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