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 }