1 /** 2 * Copyright: © 2012-2014 Anton Gushcha 3 * License: Subject to the terms of the MIT license, as written in the included LICENSE file. 4 * Authors: NCrashed <ncrashed@gmail.com>, 5 * LeMarwin <lemarwin42@gmail.com>, 6 * Nazgull09 <nazgull90@gmail.com> 7 */ 8 module devol.evolutor; 9 10 import std.random; 11 import std.stdio; 12 import std.array; 13 import std.math; 14 import std.conv; 15 import std.array; 16 import std.algorithm; 17 import std.range; 18 import devol.typemng; 19 import devol.operatormng; 20 import devol.std.random; 21 import devol.population; 22 23 /// Статистический тест случайного выбора элемента 24 enum RANDOM_ELEM_STAT_TEST = 0; 25 /// Статистический тест случайного выбора листа 26 enum RANDOM_LEAF_STAT_TEST = 0; 27 /// Тест замены случайного элемента 28 enum RANDOM_ELEM_REPLACE_TEST = 0; 29 /// Тест обмена случайными элементами 30 enum RANDOM_ELEM_SWAP_TEST = 0; 31 /// Тест мутации 32 enum MUTATION_TEST = 0; 33 /// Тест кроссинговера 34 enum CROSSINGOVER_TEST = 0; 35 36 public 37 { 38 import devol.std.typepod; 39 import devol.programtype; 40 } 41 42 class Evolutor 43 { 44 class GenExeption : Exception 45 { 46 ErrorType error; 47 Type t; 48 49 enum ErrorType 50 { 51 SEARCH_OPERATOR 52 } 53 54 this(string s, ErrorType errt, Type et) 55 { 56 super(s); 57 error = errt; 58 t = et; 59 } 60 } 61 62 static int MaxProgramDepth = 30; 63 64 static bool getChance(float val) 65 { 66 return uniform!"[]"(0.0,1.0) <= val; 67 } 68 69 void generateInitProgram(IndAbstract pInd, ProgTypeAbstract ptype) 70 { 71 Line[] buff = new Line[0]; 72 foreach(i; 0..uniform!("[]")(ptype.progMinSize,ptype.progMaxSize)) 73 { 74 version(Verbose) writeln("Generating line ", i); 75 76 try buff ~= generateLine( pInd, ptype ); 77 catch( GenExeption e) 78 { 79 } 80 } 81 pInd.program = buff; 82 } 83 84 /// Генерация линии 85 Line generateLine( IndAbstract pInd, ProgTypeAbstract ptype ) 86 { 87 auto line = new Line(); 88 auto opmng = OperatorMng.getSingleton(); 89 auto tmng = TypeMng.getSingleton(); 90 auto scopetype = cast(TypeScope)(tmng.getType("TypeScope")); 91 auto voidtype = cast(TypeVoid)(tmng.getType("TypeVoid")); 92 93 auto op = opmng.getRndOperator(); 94 if (op is null) return line; 95 96 line.operator = op; 97 98 uint i = 0; 99 foreach( j,arg; line ) 100 { 101 if (arg.type.name == voidtype.name && getChance(ptype.newScopeGenChance)) 102 { 103 auto ascope = scopetype.getNewArg(); 104 uint s = uniform!"[]"(ptype.scopeMinSize, ptype.scopeMaxSize); 105 scope(success) line[j] = ascope; 106 107 version(Verbose) writeln("Generating scope"); 108 foreach(i; 0..s) 109 { 110 ascope.addElement( generateLine( pInd, ptype, voidtype ) ); 111 } 112 } else if (getChance(ptype.newOpGenChance) 113 || (op.style == ArgsStyle.CONTROL_STYLE && i != 0) 114 || arg.type.name == voidtype.name) 115 { 116 version(Verbose) writeln("Generating line"); 117 auto aline = new Line; 118 scope(success) line[j] = aline; 119 120 aline = generateLine( pInd, ptype, arg.type ); 121 } 122 123 i++; 124 } 125 126 return line; 127 } 128 129 /// Генерация типизированной линии 130 Line generateLine( IndAbstract pInd, ProgTypeAbstract ptype, 131 Type rtype, uint depth = 0) 132 { 133 auto line = new Line(); 134 auto opmng = OperatorMng.getSingleton(); 135 auto tmng = TypeMng.getSingleton(); 136 auto scopetype = cast(TypeScope)(tmng.getType("TypeScope")); 137 auto voidtype = cast(TypeVoid)(tmng.getType("TypeVoid")); 138 139 Operator op; 140 if ( cast(TypeVoid)rtype is null && cast(TypeLine)rtype is null && cast(TypeScope)rtype is null ) 141 { 142 op = opmng.getRndOperator(rtype); 143 if (op is null) 144 { 145 throw new Evolutor.GenExeption( 146 "Cannot choose operator for type " ~ rtype.name, 147 GenExeption.ErrorType.SEARCH_OPERATOR, 148 rtype 149 ); 150 } 151 } else 152 { 153 version(Verbose) writeln("Type is container, generating any op."); 154 op = opmng.getRndOperator(); 155 if (op is null) 156 { 157 throw new Evolutor.GenExeption( 158 "Cannot choose operator for any type! May be there aren't any one?", 159 GenExeption.ErrorType.SEARCH_OPERATOR, 160 rtype 161 ); 162 } 163 } 164 165 line.operator = op; 166 167 if (depth >= MaxProgramDepth) return line; 168 169 uint i = 0; 170 foreach( i,arg; line ) 171 { 172 if (arg.type.name == voidtype.name && getChance(ptype.newScopeGenChance)) 173 { 174 auto ascope = scopetype.getNewArg(); 175 uint s = uniform!"[]"(ptype.scopeMinSize, ptype.scopeMaxSize); 176 177 foreach(j; 0..s) 178 { 179 ascope.addElement( generateLine( pInd, ptype, voidtype, depth+1 ) ); 180 } 181 line[i] = ascope; 182 } else if (getChance(ptype.newOpGenChance) 183 || (op.style == ArgsStyle.CONTROL_STYLE && i != 0) 184 || arg.type.name == voidtype.name) 185 { 186 auto aline = new Line; 187 aline = generateLine( pInd, ptype, arg.type, 188 depth+1 ); 189 line[i] = aline; 190 } 191 i++; 192 } 193 return line; 194 } 195 196 /// Получение случайного элемента дерева. Равномерное распределение. 197 /** 198 * Вероятности выбрать любой элемент дерева будет равные. Данный метод 199 * не подойдет, если нужно заменить элемента дерева. 200 * @param cont Узел дерева, в котором нужно выбрать. 201 * @see replaceRandomElementStd 202 */ 203 Argument getRandomElementStd(Container cont) 204 { 205 auto chances = new double[0]; 206 Argument ret = null; 207 208 do 209 { 210 ulong childs = cont.children; 211 212 chances ~= 1./childs; 213 foreach( arg; cont ) 214 { 215 chances ~= cast(double)arg.children/cast(double)childs; 216 } 217 218 randomRange!( 219 (int k) 220 { 221 if (k==0) 222 ret = cont; 223 else 224 if (cast(Container)(cont[k-1]) is null) 225 ret = cont[k-1]; 226 else 227 cont = cast(Container)(cont[k-1]); 228 } 229 )(chances); 230 231 chances.clear(); 232 } while(ret is null); 233 234 return ret; 235 } 236 237 /// Замена случайного элемента дерева. Равномерное распределение. 238 /** 239 * Вероятности заменить любой элемент дерева будет равные. 240 * @note Данный метод не подходит для замены самого первого переданного узла, его 241 * замену реализует пользователь этого метода отдельно. 242 * @param cont Узел дерева, в котором нужно заменить. 243 * @param generator Делегат-генератор, делегат, который создаст типизированный аргумент. 244 */ 245 void replaceRandomElementStd(Container cont, Argument delegate(Type) generator) 246 { 247 auto chances = new double[0]; 248 bool end = false; 249 Container prevCont = null; 250 uint prevContI = -1; 251 252 do 253 { 254 ulong childs = cont.children; 255 256 chances ~= 1./childs; 257 foreach( arg; cont ) 258 { 259 chances ~= cast(double)arg.children/cast(double)childs; 260 } 261 262 randomRange!( 263 (int k) 264 { 265 if (k==0) 266 { 267 if (prevCont !is null) 268 { 269 Type t; 270 if (cast(Line)(prevCont[prevContI]) !is null) 271 { 272 auto line = cast(Line)(prevCont[prevContI]); 273 t = line.operator.rtype; 274 } 275 else if (cast(ArgScope)(prevCont[prevContI]) !is null) 276 t = new TypeVoid; 277 prevCont[prevContI] = generator(t); 278 } 279 end = true; 280 } 281 else 282 if (cast(Container)(cont[k-1]) is null) 283 { 284 cont[k-1] = generator(cont[k-1].type); 285 end = true; 286 } 287 else 288 { 289 prevCont = cont; 290 prevContI = k-1; 291 cont = cast(Container)(cont[k-1]); 292 } 293 } 294 )(chances); 295 296 chances.clear(); 297 } while(!end); 298 } 299 300 /// Получение случайного листа по равномерному распределению 301 /** 302 * Вероятность выбрать любой лист дерева будет одинаковой. 303 */ 304 Argument getRandomLeafStd(Container cont) 305 { 306 bool normalize( ref double[] mass ) 307 { 308 if (mass.length == 0) return false; 309 310 double summ = 0; 311 foreach( val; mass) 312 summ += val; 313 314 if (summ == 0) 315 return false; 316 317 foreach( ref val; mass) 318 val /= summ; 319 return true; 320 } 321 322 auto chances = new double[0]; 323 Argument ret = null; 324 325 do 326 { 327 ulong leafs = cont.leafs; 328 if(cont.length == 0) return cont; 329 330 auto builder = appender!(double[]); 331 foreach( arg; cont ) 332 { 333 builder.put(cast(double)arg.leafs/cast(double)leafs); 334 } 335 chances = builder.data; 336 337 if (!normalize(chances)) 338 { 339 return cont; 340 } 341 version(Verbose) 342 { 343 writeln(cont.tostring(0)); 344 writeln("Chances distribution over leafs: ", chances); 345 } 346 347 randomRange!( 348 (int k) 349 { 350 if (cast(Container)(cont[k]) is null) 351 ret = cont[k]; 352 else 353 cont = cast(Container)(cont[k]); 354 } 355 )(chances); 356 357 chances.clear(); 358 } while(ret is null); 359 360 return ret; 361 } 362 363 /// Обмен элементами между деревьями 364 /** 365 * Выбирается поддерево из каждого контейнера и меняются местами. Выбор 366 * идет на основе равномерного распределения. 367 * @param cont1 Первый контейнер 368 * @param cont2 Второй контейнер 369 * @note Обмен корневыми элементами невозможен в рамках данного метода, 370 * его реализацей занимайтесь сами. 371 */ 372 bool swapRandomElements(Container cont1, Container cont2, ProgTypeAbstract ptype) 373 { 374 bool validType(Argument a, Argument b) 375 { 376 if (cast(ArgScope)a !is null && cast(ArgScope)b !is null) 377 return true; 378 else if (cast(Line)a !is null && cast(ArgScope)b !is null) 379 return (cast(Line)a).operator.rtype.name == "TypeVoid"; 380 else if (cast(Line)b !is null && cast(ArgScope)a !is null) 381 return (cast(Line)b).operator.rtype.name == "TypeVoid"; 382 else if (cast(Line)a !is null && cast(Line)b !is null) 383 return (cast(Line)a).operator.rtype == (cast(Line)b).operator.rtype; 384 else 385 return a.type == b.type; 386 387 } 388 389 struct SwapStruct 390 { 391 Container parentCont; 392 uint place; 393 } 394 /// Формирование массива возможных замен 395 SwapStruct[] checkExistens(Container checkCont, Argument swapArg) 396 { 397 auto ret = new SwapStruct[0]; 398 uint i = 0; 399 foreach(Argument arg; checkCont ) 400 { 401 if (cast(Container)(arg) !is null) 402 { 403 if ( (cast(Line)(arg) !is null && (cast(Line)arg).operator.rtype.name == swapArg.type.name) || 404 (cast(ArgScope)arg !is null && cast(ArgScope)swapArg !is null) ) 405 { 406 SwapStruct st; 407 st.parentCont = checkCont; 408 st.place = i; 409 ret ~= st; 410 } 411 ret ~= checkExistens( cast(Container)(arg), swapArg); 412 } 413 else 414 if ( arg.type.name == swapArg.type.name ) 415 { 416 SwapStruct st; 417 st.parentCont = checkCont; 418 st.place = i; 419 ret ~= st; 420 } 421 i++; 422 } 423 return ret; 424 } 425 426 /// Поиск второго подходящего поддерева и обмен. 427 bool innerSwap( Container parentCont, int place ) 428 { 429 auto candidates = checkExistens( cont2, parentCont[place] ); 430 if (candidates.length == 0) return false; 431 432 auto candidate = candidates[uniform(0,candidates.length)]; 433 auto temp = parentCont[place]; 434 parentCont[place] = candidate.parentCont[candidate.place]; 435 candidate.parentCont[candidate.place] = temp; 436 return true; 437 } 438 439 440 auto chances = new double[0]; 441 bool end = false; 442 Container cont = cont1; 443 Container prevCont = null; 444 uint prevContI = -1; 445 446 447 do 448 { 449 ulong childs = cont.children; 450 451 chances ~= 1./childs; 452 foreach( arg; cont ) 453 { 454 chances ~= cast(double)arg.children/cast(double)childs; 455 } 456 457 randomRange!( 458 (int k) 459 { 460 if (k==0) 461 { 462 version(Verbose) writeln("Selected to stop. Finded 1st tree"); 463 if (prevCont !is null) 464 { 465 innerSwap(prevCont, prevContI); 466 } 467 end = true; 468 } 469 else 470 if (cast(Container)(cont[k-1]) is null) 471 { 472 version(Verbose) writeln("Selected leaf. Finded 1st tree"); 473 innerSwap(cont, k-1); 474 475 end = true; 476 } 477 else 478 { 479 version(Verbose) writeln("Going down"); 480 prevCont = cont; 481 prevContI = k-1; 482 cont = cast(Container)(cont[k-1]); 483 } 484 } 485 )(chances); 486 chances.clear(); 487 } while(!end); 488 489 return true; 490 } 491 492 /// Стандартная мутация 493 void mutationStd( IndAbstract pInd, ProgTypeAbstract ptype) 494 { 495 if (pInd.program.length == 0) return; 496 497 auto voidtype = cast(TypeVoid)(TypeMng.getSingleton.getType("TypeVoid")); 498 499 size_t k = uniform(0, pInd.program.length); 500 Line line = pInd.program[k]; 501 auto chances = new double[3]; 502 503 /// Сначала проверим на глобальную мутацию 504 chances[0] = ptype.mutationAddLineChance(); 505 chances[1] = ptype.mutationRemoveLineChance(); 506 chances[2] = 1 - chances[0] - chances[1]; 507 508 bool local = false; 509 randomRange!( 510 (int k) 511 { 512 if (k==0) // mutationAddLineChance 513 { 514 try 515 { 516 pInd.program = pInd.program ~ generateLine(pInd, ptype); 517 } catch( GenExeption e) 518 { 519 520 } 521 return; 522 } else if (k==1) // mutationRemoveLineChance 523 { 524 if( k != pInd.program.length -1) 525 pInd.program = pInd.program[0..k] ~ pInd.program[k+1..$]; 526 else 527 pInd.program = pInd.program[0..k]; 528 return; 529 } 530 local = true; 531 } 532 )(chances); 533 534 if (!local) return; 535 /// Локальная мутация 536 chances[0] = ptype.mutationChangeChance(); 537 chances[1] = ptype.mutationReplaceChance(); 538 chances[2] = ptype.mutationDeleteChance(); 539 540 version(Verbose) writeln("Mutation chances: ", chances); 541 542 randomRange!( 543 (int t) 544 { 545 switch(t) 546 { 547 case 0: // mutationChangeChance 548 { 549 version(Verbose) writeln("Change"); 550 if (line.length > 0 && line.leafs > 0) 551 { 552 auto arg = getRandomLeafStd(line); 553 version(Verbose) writeln("Got argument: ", arg.tostring(0)); 554 if (arg !is null) 555 arg.randomChange(ptype.maxMutationChange); 556 } 557 break; 558 } 559 case 1: // mutationReplaceChance 560 { 561 version(Verbose) writeln("Replace"); 562 if ( line.children > 1 ) 563 replaceRandomElementStd(line, 564 (Type t) 565 { 566 try 567 { 568 return cast(Argument)generateLine(pInd, ptype, t); 569 } catch( GenExeption e) 570 { 571 auto arg = t.getNewArg(); 572 arg.randomChange(ptype.maxMutationChange); 573 return arg; 574 } 575 }); 576 577 break; 578 } 579 case 2: // mutationDeleteChance 580 { 581 version(Verbose) writeln("Delete"); 582 replaceRandomElementStd(line, 583 (Type t) 584 { 585 if(t.name == voidtype.name) 586 { 587 try return generateLine(pInd, ptype); 588 catch( GenExeption e) 589 { 590 Argument arg = t.getNewArg(); 591 arg.randomChange(ptype.maxMutationChange); 592 return arg; 593 } 594 } 595 else 596 { 597 Argument arg = t.getNewArg(); 598 arg.randomChange(ptype.maxMutationChange); 599 return arg; 600 } 601 }); 602 break; 603 } 604 default: 605 } 606 } 607 )(chances); 608 } 609 610 /// Стандартный кроссинговер 611 bool crossingoverStd(IndAbstract pIndA, IndAbstract pIndB, ProgTypeAbstract ptype) 612 { 613 if (pIndA.program.length == 0 || pIndB.program.length == 0) return false; 614 version(Verbose) writeln("Starting crossingover"); 615 616 ulong length = pIndA.program.length; 617 if (pIndB.program.length < length) 618 length = pIndB.program.length; 619 version(Verbose) writeln("Selected length:", length); 620 621 foreach(ulong i; 0..length/2+1) 622 { 623 version(Verbose) writeln("Starting ",i," swapping"); 624 size_t kA = uniform(0,pIndA.program.length); 625 size_t kB = uniform(0,pIndB.program.length); 626 Line lineA = pIndA.program[kA]; 627 Line lineB = pIndB.program[kB]; 628 629 /// Перемена местами двух деревьев полностью 630 if ( uniform!"[]"(0,1) < 1./cast(double)(lineA.children+lineB.children)) 631 { 632 version(Verbose) writeln("Swapping roots"); 633 swap(pIndA.program[kA], pIndB.program[kB]); 634 } else /// Обмен поддеревьями 635 { 636 version(Verbose) writeln("Swapping subtrees"); 637 if (!swapRandomElements( lineA, lineB, ptype )) 638 return false; 639 640 } 641 } 642 return true; 643 } 644 645 /// Создание следующей популяции 646 /** 647 * Создание популяции на основе вычисленной приспособленности. 648 * @param pop Популяция, из которой будет браться материал для 649 * следующей популяции. 650 * @param ptype Тип программы, в котором записаны все настройки 651 * процесса эволюции. 652 * @return Новая популяция. 653 */ 654 PopType formNextPopulation(PopType)(PopType pop, ProgTypeAbstract ptype) 655 { 656 if (pop.length == 0) return pop; 657 658 //Вычисляем среднюю приспособленность 659 version(Verbose) writeln("Вычисляем сумму приспособленность: "); 660 double averFitness = 0; 661 foreach( ind; pop) 662 averFitness += ind.fitness; 663 664 auto newPop = pop.dup; 665 newPop.clear(); 666 version(Verbose) writeln( "averFitness = ", averFitness ); 667 668 // Копируем лучших индивидов 669 version(Verbose) writeln("Копируем лучших индивидов"); 670 int k = cast(int)round((ptype.copyingPart()*pop.length)); 671 version(Verbose) writeln("Будет выбрано ", k, " лучших муравьев"); 672 673 auto sortedInds = new pop.IndividType[0]; 674 foreach( ind; pop) 675 sortedInds ~= cast(pop.IndividType)ind; 676 677 sort!("a.fitness > b.fitness")(sortedInds); 678 foreach(i; 0..k) 679 { 680 version(Verbose) writeln("Добавляем ", i, " из лучших"); 681 newPop.addIndivid(cast(newPop.IndividType)(sortedInds[i].dup)); 682 } 683 684 version(Verbose) 685 { 686 write("Отсортированные индивиды по фитнес: ["); 687 foreach(ind; sortedInds) 688 write(ind.fitness,","); 689 writeln("]"); 690 writeln("Размер новой популяции ", newPop.length); 691 } 692 693 // Формируем шансы для операций 694 auto opChances = new double[2]; 695 opChances[0] = ptype.mutationChance(); 696 opChances[1] = ptype.crossingoverChance(); 697 version(Verbose) writeln("Шансы на операции: ", opChances); 698 699 // Формируем шансы индивидов 700 auto indChances = new double[0]; 701 if(averFitness == 0) 702 { 703 indChances = (1.0/cast(double)pop.length).repeat.take(pop.length).array; 704 assert(indChances.length == pop.length); 705 } 706 else 707 { 708 foreach( ind; pop ) 709 { 710 indChances ~= cast(double)(ind.fitness)/cast(double)(averFitness); 711 } 712 } 713 version(Verbose) writeln("Шансы индивидов: ", indChances); 714 715 version(Verbose) writeln("Начинаем формировать новую популяцию:"); 716 while( newPop.length < pop.length ) 717 { 718 int opSelected; 719 randomRange!((int m){opSelected = m;})(opChances); 720 721 if (opSelected == 0) // mutationChance 722 { 723 version(Verbose) writeln("Выбрана мутация"); 724 randomRange!( 725 (int s) 726 { 727 version(Verbose) writeln("Выбран индивид №", s); 728 auto ind = cast(pop.IndividType)(pop[s].dup); 729 version(Verbose) writeln("Был: ", ind.programString()); 730 mutationStd( ind, ptype); 731 version(Verbose) writeln("Стал: ", ind.programString()); 732 newPop.addIndivid( ind ); 733 } 734 )(indChances); 735 } else // crossingoverChance 736 { 737 // Замечен странный баг с вложенными лямбдами, поэтому передаю занчения вверх 738 version(Verbose) writeln("Выбран кроссинговер"); 739 int iInd1; 740 int iInd2; 741 randomRange!((int s1){iInd1 = s1;})(indChances); 742 randomRange!((int s2){iInd2 = s2;})(indChances); 743 auto pIndA = cast(pop.IndividType)(pop[iInd1].dup); 744 auto pIndB = cast(pop.IndividType)(pop[iInd2].dup); 745 746 version(Verbose) writeln("Выбраны индивиды №", iInd1, " и №", iInd2); 747 version(Verbose) writeln("Был: ", pIndA.programString()); 748 version(Verbose) writeln("Был: ", pIndB.programString()); 749 crossingoverStd(pIndA, pIndB, ptype); 750 version(Verbose) writeln("Стал: ", pIndA.programString()); 751 version(Verbose) writeln("Стал: ", pIndB.programString()); 752 753 newPop.addIndivid( pIndA ); 754 if (newPop.length < pop.length) 755 newPop.addIndivid( pIndB ); 756 } 757 } 758 759 performGenomeTruncation(newPop, ptype); 760 761 return newPop; 762 } 763 764 void performGenomeTruncation(IndAbstract pInd, ProgTypeAbstract ptype) 765 { 766 auto voidtype = cast(TypeVoid)(TypeMng.getSingleton.getType("TypeVoid")); 767 768 void destructiveMutation() 769 { 770 size_t k = uniform(0, pInd.program.length); 771 Line line = pInd.program[k]; 772 773 if(getChance(ptype.mutationRemoveLineChance)) 774 { 775 version(Verbose) writeln("Removing global line"); 776 777 if( k != pInd.program.length -1) 778 { 779 pInd.program = pInd.program[0..k] ~ pInd.program[k+1..$]; 780 } 781 else 782 { 783 pInd.program = pInd.program[0..k]; 784 } 785 786 return; 787 } 788 789 version(Verbose) writeln("Replacing local argument"); 790 replaceRandomElementStd(line, 791 (Type t) 792 { 793 if(t.name == voidtype.name) 794 { 795 try return generateLine(pInd, ptype); 796 catch( GenExeption e) 797 { 798 Argument arg = t.getNewArg(); 799 arg.randomChange(ptype.maxMutationChange); 800 return arg; 801 } 802 } 803 else 804 { 805 Argument arg = t.getNewArg(); 806 arg.randomChange(ptype.maxMutationChange); 807 return arg; 808 } 809 }); 810 } 811 812 auto gensize = pInd.getGenomeSize; 813 if(gensize >= ptype.maxGenomeSize) 814 { 815 do 816 { 817 version(Verbose) writeln("Individ genome size is: ", gensize, ". Performing truncation."); 818 819 destructiveMutation(); 820 gensize = pInd.getGenomeSize; 821 } 822 while(gensize >= ptype.maxGenomeSize); 823 } 824 else if(gensize >= ptype.deleteMutationRiseGenomeSize) 825 { 826 version(Verbose) writeln("Individ genome size is: ", gensize, ". Performing soft truncation."); 827 828 // linear interpolation 829 double chance = (gensize - ptype.deleteMutationRiseGenomeSize) / ptype.maxGenomeSize; 830 if(chance.isNaN) return; 831 832 if(getChance(chance)) 833 { 834 destructiveMutation(); 835 } 836 } 837 } 838 839 void performGenomeTruncation(PopType)(PopType pop, ProgTypeAbstract ptype) 840 { 841 foreach(ind; pop) 842 { 843 performGenomeTruncation(ind, ptype); 844 } 845 } 846 } 847 848 //====================================================================== 849 // Статистические тесты 850 //====================================================================== 851 static if (RANDOM_ELEM_STAT_TEST) 852 { 853 unittest 854 { 855 import std.process; 856 class VoidOp : Operator 857 { 858 this() 859 { 860 mRetType = new TypePod!int; 861 super("v","",ArgsStyle.CLASSIC_STYLE); 862 863 ArgInfo a1; 864 a1.type = mRetType; 865 a1.min = "-1000"; 866 a1.max = "+1000"; 867 868 args ~= a1; 869 args ~= a1; 870 args ~= a1; 871 } 872 873 Argument apply(IndAbstract ind, Line line, WorldAbstract world) 874 { 875 auto arg = mRetType.getNewArg(); 876 return arg; 877 } 878 879 } 880 881 auto tm = new TypeMng; 882 auto em = new Evolutor; 883 tm.registerType!(TypePod!int); 884 885 auto op = new VoidOp; 886 auto nline0 = new Line; 887 auto nline1 = new Line; 888 auto nline2 = new Line; 889 auto nline3 = new Line; 890 891 892 nline0.operator = op; 893 nline1.operator = op; 894 nline2.operator = op; 895 nline3.operator = op; 896 897 nline0[0] = nline1; 898 nline0[1] = op.mRetType.getNewArg("1","1",[]); 899 nline0[2] = op.mRetType.getNewArg("2","2",[]); 900 901 nline1[0] = op.mRetType.getNewArg("3","3",[]); 902 nline1[1] = nline2; 903 nline1[2] = nline3; 904 905 nline2[0] = op.mRetType.getNewArg("4","4",[]); 906 nline2[1] = op.mRetType.getNewArg("5","5",[]); 907 nline2[2] = op.mRetType.getNewArg("6","6",[]); 908 909 nline3[0] = op.mRetType.getNewArg("7","7",[]); 910 nline3[1] = op.mRetType.getNewArg("8","8",[]); 911 nline3[2] = op.mRetType.getNewArg("9","9",[]); 912 913 double[Argument] stat; 914 915 foreach(arg; nline0) 916 stat[arg] = 0; 917 foreach(arg; nline1) 918 stat[arg] = 0; 919 foreach(arg; nline2) 920 stat[arg] = 0; 921 foreach(arg; nline3) 922 stat[arg] = 0; 923 924 Argument a; 925 ProgTypeAbstract ptype; 926 ulong count = cast(ulong)1e6; 927 928 foreach(ulong i; 0..count) 929 { 930 version(linux) 931 system("clear"); 932 933 a = em.getRandomElementStd(nline0); 934 stat[a] += 1.; 935 936 foreach(key,val; stat) 937 { 938 writeln(key.tostring, " : ", val/i); 939 } 940 writeln( "Выполнено ", cast(double)i/count*100, "%"); 941 } 942 version(linux) 943 system("clear"); 944 writeln("Результаты теста: "); 945 foreach(key,val; stat) 946 { 947 writeln(key.tostring, " : ", val/count); 948 } 949 getchar(); 950 } 951 } 952 953 static if (RANDOM_ELEM_REPLACE_TEST) 954 { 955 unittest 956 { 957 import std.process; 958 import ant.progtype; 959 960 class VoidOp : Operator 961 { 962 this() 963 { 964 mRetType = new TypePod!int; 965 super("v","",ArgsStyle.CLASSIC_STYLE); 966 967 ArgInfo a1; 968 a1.type = mRetType; 969 a1.min = "-1000"; 970 a1.max = "+1000"; 971 972 args ~= a1; 973 args ~= a1; 974 args ~= a1; 975 } 976 977 Argument apply(IndAbstract ind, Line line, WorldAbstract world) 978 { 979 auto arg = mRetType.getNewArg(); 980 return arg; 981 } 982 983 } 984 985 auto tm = new TypeMng; 986 auto em = new Evolutor; 987 //tm.registerType!(TypePod!int); 988 989 AntProgType ptype = new AntProgType(); 990 Ant pInd = new Ant(); 991 992 auto op = new VoidOp; 993 auto nline0 = new Line; 994 auto nline1 = new Line; 995 auto nline2 = new Line; 996 auto nline3 = new Line; 997 998 999 nline0.operator = op; 1000 nline1.operator = op; 1001 nline2.operator = op; 1002 nline3.operator = op; 1003 1004 nline0[0] = nline1; 1005 nline0[1] = op.mRetType.getNewArg("1","1",[]); 1006 nline0[2] = op.mRetType.getNewArg("2","2",[]); 1007 1008 nline1[0] = op.mRetType.getNewArg("3","3",[]); 1009 nline1[1] = nline2; 1010 nline1[2] = nline3; 1011 1012 nline2[0] = op.mRetType.getNewArg("4","4",[]); 1013 nline2[1] = op.mRetType.getNewArg("5","5",[]); 1014 nline2[2] = op.mRetType.getNewArg("6","6",[]); 1015 1016 nline3[0] = op.mRetType.getNewArg("7","7",[]); 1017 nline3[1] = op.mRetType.getNewArg("8","8",[]); 1018 nline3[2] = op.mRetType.getNewArg("9","9",[]); 1019 1020 //pInd.program[0] = nline0; 1021 char ans; 1022 1023 do 1024 { 1025 version(linux) 1026 system("clear"); 1027 1028 writeln("Было: "); 1029 writeln(nline0.tostring); 1030 em.replaceRandomElementStd(nline0, (Type t){return cast(Argument)em.generateLine(pInd, ptype, t);}); 1031 //em.replaceRandomElementStd(nline0, (Type t){return new ArgVoid;}); 1032 writeln("Стало: "); 1033 writeln(nline0.tostring); 1034 1035 write("Для остновки введите 'n': "); 1036 ans = readln()[0]; 1037 } while(ans != 'n' && ans != 'N'); 1038 1039 } 1040 } 1041 1042 static if (RANDOM_LEAF_STAT_TEST) 1043 { 1044 unittest 1045 { 1046 import std.process; 1047 class VoidOp : Operator 1048 { 1049 this() 1050 { 1051 mRetType = new TypePod!int; 1052 super("v","",ArgsStyle.CLASSIC_STYLE); 1053 1054 ArgInfo a1; 1055 a1.type = mRetType; 1056 a1.min = "-1000"; 1057 a1.max = "+1000"; 1058 1059 args ~= a1; 1060 args ~= a1; 1061 args ~= a1; 1062 } 1063 1064 Argument apply(IndAbstract ind, Line line, WorldAbstract world) 1065 { 1066 auto arg = mRetType.getNewArg(); 1067 return arg; 1068 } 1069 1070 } 1071 1072 auto tm = new TypeMng; 1073 auto em = new Evolutor; 1074 tm.registerType!(TypePod!int); 1075 1076 auto op = new VoidOp; 1077 auto nline0 = new Line; 1078 auto nline1 = new Line; 1079 auto nline2 = new Line; 1080 auto nline3 = new Line; 1081 1082 1083 nline0.operator = op; 1084 nline1.operator = op; 1085 nline2.operator = op; 1086 nline3.operator = op; 1087 1088 nline0[0] = nline1; 1089 nline0[1] = op.mRetType.getNewArg("1","1",[]); 1090 nline0[2] = op.mRetType.getNewArg("2","2",[]); 1091 1092 nline1[0] = op.mRetType.getNewArg("3","3",[]); 1093 nline1[1] = nline2; 1094 nline1[2] = nline3; 1095 1096 nline2[0] = op.mRetType.getNewArg("4","4",[]); 1097 nline2[1] = op.mRetType.getNewArg("5","5",[]); 1098 nline2[2] = op.mRetType.getNewArg("6","6",[]); 1099 1100 nline3[0] = op.mRetType.getNewArg("7","7",[]); 1101 nline3[1] = op.mRetType.getNewArg("8","8",[]); 1102 nline3[2] = op.mRetType.getNewArg("9","9",[]); 1103 1104 double[Argument] stat; 1105 1106 foreach(arg; nline0) 1107 stat[arg] = 0; 1108 foreach(arg; nline1) 1109 stat[arg] = 0; 1110 foreach(arg; nline2) 1111 stat[arg] = 0; 1112 foreach(arg; nline3) 1113 stat[arg] = 0; 1114 1115 Argument a; 1116 ProgTypeAbstract ptype; 1117 ulong count = cast(ulong)1e6; 1118 1119 foreach(ulong i; 0..count) 1120 { 1121 version(linux) 1122 system("clear"); 1123 1124 a = em.getRandomLeafStd(nline0); 1125 stat[a] += 1.; 1126 1127 foreach(key,val; stat) 1128 { 1129 writeln(key.tostring, " : ", val/i); 1130 } 1131 writeln( "Выполнено ", cast(double)i/count*100, "%"); 1132 } 1133 version(linux) 1134 system("clear"); 1135 writeln("Результаты теста: "); 1136 foreach(key,val; stat) 1137 { 1138 writeln(key.tostring, " : ", val/count); 1139 } 1140 getchar(); 1141 } 1142 } 1143 1144 static if (RANDOM_ELEM_SWAP_TEST) 1145 { 1146 unittest 1147 { 1148 import std.process; 1149 import ant.progtype; 1150 1151 class VoidOp : Operator 1152 { 1153 this() 1154 { 1155 mRetType = new TypePod!int; 1156 super("v","",ArgsStyle.CLASSIC_STYLE); 1157 1158 ArgInfo a1; 1159 a1.type = mRetType; 1160 a1.min = "-1000"; 1161 a1.max = "+1000"; 1162 1163 args ~= a1; 1164 args ~= a1; 1165 args ~= a1; 1166 } 1167 1168 Argument apply(IndAbstract ind, Line line, WorldAbstract world) 1169 { 1170 auto arg = mRetType.getNewArg(); 1171 return arg; 1172 } 1173 1174 } 1175 1176 auto tm = new TypeMng; 1177 auto em = new Evolutor; 1178 //tm.registerType!(TypePod!int); 1179 1180 AntProgType ptype = new AntProgType(); 1181 Ant pInd = new Ant(); 1182 1183 auto op = new VoidOp; 1184 auto nline0 = new Line; 1185 auto nline1 = new Line; 1186 auto nline2 = new Line; 1187 auto nline3 = new Line; 1188 1189 1190 nline0.operator = op; 1191 nline1.operator = op; 1192 nline2.operator = op; 1193 nline3.operator = op; 1194 1195 nline0[0] = nline1; 1196 nline0[1] = op.mRetType.getNewArg("1","1",[]); 1197 nline0[2] = op.mRetType.getNewArg("2","2",[]); 1198 1199 nline1[0] = op.mRetType.getNewArg("3","3",[]); 1200 nline1[1] = nline2; 1201 nline1[2] = nline3; 1202 1203 nline2[0] = op.mRetType.getNewArg("4","4",[]); 1204 nline2[1] = op.mRetType.getNewArg("5","5",[]); 1205 nline2[2] = op.mRetType.getNewArg("6","6",[]); 1206 1207 nline3[0] = op.mRetType.getNewArg("7","7",[]); 1208 nline3[1] = op.mRetType.getNewArg("8","8",[]); 1209 nline3[2] = op.mRetType.getNewArg("9","9",[]); 1210 1211 pInd.program = pInd.program ~ nline0; 1212 char ans; 1213 1214 Line lineA = nline0; 1215 Line lineB = nline0.dup; 1216 1217 do 1218 { 1219 version(linux) 1220 system("clear"); 1221 1222 writeln("Было: "); 1223 writeln("Линия А: ", lineA.tostring); 1224 writeln("Линия B: ", lineB.tostring); 1225 1226 em.swapRandomElements( lineA, lineB, ptype ); 1227 //em.mutationStd(pInd, ptype); 1228 1229 writeln("Стало: "); 1230 writeln("Линия А: ", lineA.tostring); 1231 writeln("Линия B: ", lineB.tostring); 1232 1233 write("Для остновки введите 'n': "); 1234 ans = readln()[0]; 1235 } while(ans != 'n' && ans != 'N'); 1236 1237 } 1238 } 1239 1240 static if (MUTATION_TEST) 1241 { 1242 unittest 1243 { 1244 import std.process; 1245 import ant.progtype; 1246 1247 class VoidOp : Operator 1248 { 1249 this() 1250 { 1251 mRetType = new TypePod!int; 1252 super("v","",ArgsStyle.CLASSIC_STYLE); 1253 1254 ArgInfo a1; 1255 a1.type = mRetType; 1256 a1.min = "-1000"; 1257 a1.max = "+1000"; 1258 1259 args ~= a1; 1260 args ~= a1; 1261 args ~= a1; 1262 } 1263 1264 Argument apply(IndAbstract ind, Line line, WorldAbstract world) 1265 { 1266 auto arg = mRetType.getNewArg(); 1267 return arg; 1268 } 1269 1270 } 1271 1272 auto tm = new TypeMng; 1273 auto em = new Evolutor; 1274 //tm.registerType!(TypePod!int); 1275 1276 AntProgType ptype = new AntProgType(); 1277 Ant pInd = new Ant(); 1278 1279 auto op = new VoidOp; 1280 auto nline0 = new Line; 1281 auto nline1 = new Line; 1282 auto nline2 = new Line; 1283 auto nline3 = new Line; 1284 1285 1286 nline0.operator = op; 1287 nline1.operator = op; 1288 nline2.operator = op; 1289 nline3.operator = op; 1290 1291 nline0[0] = nline1; 1292 nline0[1] = op.mRetType.getNewArg("1","1",[]); 1293 nline0[2] = op.mRetType.getNewArg("2","2",[]); 1294 1295 nline1[0] = op.mRetType.getNewArg("3","3",[]); 1296 nline1[1] = nline2; 1297 nline1[2] = nline3; 1298 1299 nline2[0] = op.mRetType.getNewArg("4","4",[]); 1300 nline2[1] = op.mRetType.getNewArg("5","5",[]); 1301 nline2[2] = op.mRetType.getNewArg("6","6",[]); 1302 1303 nline3[0] = op.mRetType.getNewArg("7","7",[]); 1304 nline3[1] = op.mRetType.getNewArg("8","8",[]); 1305 nline3[2] = op.mRetType.getNewArg("9","9",[]); 1306 1307 pInd.program = pInd.program ~ nline0; 1308 char ans; 1309 1310 do 1311 { 1312 version(linux) 1313 system("clear"); 1314 1315 writeln("Было: "); 1316 writeln(nline0.tostring); 1317 em.mutationStd( pInd, ptype ); 1318 1319 writeln("Стало: "); 1320 writeln(nline0.tostring); 1321 1322 write("Для остновки введите 'n': "); 1323 ans = readln()[0]; 1324 } while(ans != 'n' && ans != 'N'); 1325 1326 } 1327 } 1328 1329 static if (CROSSINGOVER_TEST) 1330 { 1331 unittest 1332 { 1333 import std.process; 1334 import ant.progtype; 1335 1336 class VoidOp : Operator 1337 { 1338 this() 1339 { 1340 mRetType = new TypePod!int; 1341 super("v","",ArgsStyle.CLASSIC_STYLE); 1342 1343 ArgInfo a1; 1344 a1.type = mRetType; 1345 a1.min = "-1000"; 1346 a1.max = "+1000"; 1347 1348 args ~= a1; 1349 args ~= a1; 1350 args ~= a1; 1351 } 1352 1353 Argument apply(IndAbstract ind, Line line, WorldAbstract world) 1354 { 1355 auto arg = mRetType.getNewArg(); 1356 return arg; 1357 } 1358 1359 } 1360 1361 auto tm = new TypeMng; 1362 auto em = new Evolutor; 1363 //tm.registerType!(TypePod!int); 1364 1365 AntProgType ptype = new AntProgType(); 1366 Ant pIndA = new Ant(); 1367 Ant pIndB = new Ant(); 1368 1369 auto op = new VoidOp; 1370 auto nline0 = new Line; 1371 auto nline1 = new Line; 1372 auto nline2 = new Line; 1373 auto nline3 = new Line; 1374 1375 1376 nline0.operator = op; 1377 nline1.operator = op; 1378 nline2.operator = op; 1379 nline3.operator = op; 1380 1381 nline0[0] = nline1; 1382 nline0[1] = op.mRetType.getNewArg("1","1",[]); 1383 nline0[2] = op.mRetType.getNewArg("2","2",[]); 1384 1385 nline1[0] = op.mRetType.getNewArg("3","3",[]); 1386 nline1[1] = nline2; 1387 nline1[2] = nline3; 1388 1389 nline2[0] = op.mRetType.getNewArg("4","4",[]); 1390 nline2[1] = op.mRetType.getNewArg("5","5",[]); 1391 nline2[2] = op.mRetType.getNewArg("6","6",[]); 1392 1393 nline3[0] = op.mRetType.getNewArg("7","7",[]); 1394 nline3[1] = op.mRetType.getNewArg("8","8",[]); 1395 nline3[2] = op.mRetType.getNewArg("9","9",[]); 1396 1397 pIndA.program = pIndA.program ~ nline0.dup; 1398 pIndB.program = pIndB.program ~ nline1.dup; 1399 1400 char ans; 1401 1402 do 1403 { 1404 version(linux) 1405 system("clear"); 1406 1407 writeln("Было: "); 1408 writeln("Индивид А: ",pIndA.programString()); 1409 writeln("Индивид B: ",pIndB.programString()); 1410 1411 em.crossingoverStd( pIndA, pIndB, ptype ); 1412 1413 writeln("Стало: "); 1414 writeln("Индивид А: ",pIndA.programString()); 1415 writeln("Индивид B: ",pIndB.programString()); 1416 1417 write("Для остновки введите 'n': "); 1418 ans = readln()[0]; 1419 } while(ans != 'n' && ans != 'N'); 1420 1421 } 1422 }