· 9 years ago · Jan 08, 2017, 08:50 PM
1860 lines (725 sloc) 29.7 KB
2using UnityEngine;
3using System.Collections.Generic;
4using System;
5using System.Collections;
6using System.Linq;
7
8
9//Fast neural network based on matrix operations
10public class NEATBrain : IEquatable<NEATBrain>
11{
12 private string name;
13 private int ID;
14 private int calculations;
15 private int numberOfInputNeurons;
16 private int numberOfOutputNeurons;
17 private int numberOfHiddenNeurons;
18
19 private int usedHiddenNeuronIndex;
20 private List<Gene> genome = new List<Gene>();
21 private HashSet<int> genomeHash;
22
23 private List<Neuron> network = new List<Neuron>();
24 private Neuron[] networkArray;
25 private NEATConsultor consultor;
26
27 private BehaviourGenome behaviourGenome;
28
29 public NEATBrain(int numberOfInputNeurons, int numberOfOutputNeurons, int numberOfHiddenNeurons, int ID)
30 {
31 consultor = NEATConsultor.GetInstance();
32 this.ID = ID;
33 this.numberOfHiddenNeurons = numberOfHiddenNeurons;
34 this.numberOfInputNeurons = numberOfInputNeurons;
35 this.numberOfOutputNeurons = numberOfOutputNeurons;
36 this.numberOfInputNeurons++; //bias
37
38 this.usedHiddenNeuronIndex = this.numberOfOutputNeurons + this.numberOfInputNeurons;
39
40 //InitilizeGenome();
41 GenerateRandomName();
42
43 genomeHash = new HashSet<int>();
44
45 InitilizeNeurons();
46 for (int i = 0; i < 2; i++)
47 Mutate();
48
49 MakeNetwork();
50
51 behaviourGenome = new BehaviourGenome();
52 }
53
54 public NEATBrain(NEATBrain copy, Neuron[] neuronArray)
55 {
56 consultor = NEATConsultor.GetInstance();
57 this.numberOfHiddenNeurons = copy.numberOfHiddenNeurons;
58 this.numberOfInputNeurons = copy.numberOfInputNeurons;
59 this.numberOfOutputNeurons = copy.numberOfOutputNeurons;
60 this.usedHiddenNeuronIndex = copy.usedHiddenNeuronIndex;
61
62 this.networkArray = neuronArray;
63 }
64
65 // Deep copy constructor of a given Brain
66 public NEATBrain(NEATBrain parentBrain, int ID)
67 {
68 consultor = NEATConsultor.GetInstance();
69 this.ID = ID;
70 this.calculations = parentBrain.calculations;
71 this.name = parentBrain.name;
72 this.numberOfHiddenNeurons = parentBrain.numberOfHiddenNeurons;
73 this.numberOfInputNeurons = parentBrain.numberOfInputNeurons;
74 this.numberOfOutputNeurons = parentBrain.numberOfOutputNeurons;
75 this.usedHiddenNeuronIndex = parentBrain.usedHiddenNeuronIndex;
76
77 InitilizeGenomeFromParent(parentBrain.genome);
78 InitilizeNeurons();
79 Mutate();
80 MakeNetwork();
81
82 behaviourGenome = new BehaviourGenome(parentBrain.behaviourGenome);
83 }
84
85 public void InitilizeNeurons()
86 {
87 for (int i = 0; i < numberOfInputNeurons + numberOfOutputNeurons; i++)
88 {
89 Neuron neuron = new Neuron(i, 0f);
90 network.Add(neuron);
91 }
92 }
93
94 private void MakeNetwork()
95 {
96 calculations = 0;
97 genome.Sort((x, y) => x.outNode.CompareTo(y.outNode));
98 HashSet<int> tempNeuronHash = new HashSet<int>();
99
100 for (int i = 0; i < numberOfInputNeurons + numberOfOutputNeurons; i++)
101 {
102 tempNeuronHash.Add(i);
103 }
104
105 for (int i = 0; i < genome.Count; i++)
106 {
107 Gene gene = genome[i];
108 if (!tempNeuronHash.Contains(gene.inNode))
109 {
110 Neuron neuron = new Neuron(gene.inNode,0f);
111 tempNeuronHash.Add(gene.inNode);
112 network.Add(neuron);
113 }
114
115 if (!tempNeuronHash.Contains(gene.outNode))
116 {
117 Neuron neuron = new Neuron(gene.outNode,0f);
118 tempNeuronHash.Add(gene.outNode);
119 network.Add(neuron);
120 }
121 }
122
123 network.Sort((x, y) => x.id.CompareTo(y.id));
124
125 for (int i = 0; i < genome.Count; i++)
126 {
127 Gene gene = genome[i];
128
129 if (gene.active == true)
130 {
131 network[gene.outNode].incomming.Add(gene);
132 calculations++;
133 }
134 }
135
136 networkArray = network.ToArray();
137 for (int i = 0; i < networkArray.Length; i++)
138 {
139 networkArray[i].incomming.Sort((x, y) => x.inNode.CompareTo(y.inNode));
140 networkArray[i].incommingArray = networkArray[i].incomming.ToArray();
141 }
142 }
143
144 /*private void InitilizeGenome()
145 {
146 genomeHash = new HashSet<int>();
147 for (int i = 0; i < numberOfInputNeurons; i++)
148 {
149 for (int j = numberOfInputNeurons; j < numberOfInputNeurons + numberOfOutputNeurons; j++)
150 {
151 AddGene(i, j, UnityEngine.Random.Range(-0.5f, 0.5f), true);
152 }
153 }
154 }*/
155
156 private void InitilizeGenomeFromParent(List<Gene> parentGenome)
157 {
158 genomeHash = new HashSet<int>();
159
160 for (int i = 0; i < parentGenome.Count; i++)
161 {
162 Gene gene = parentGenome[i];
163 AddGene(gene.inno, gene.inNode, gene.outNode, gene.weight, gene.active);
164 }
165 }
166
167 public List<Gene> GetGenome()
168 {
169 return genome;
170 }
171
172 public int GetUsedHiddenNeuronCount()
173 {
174 return usedHiddenNeuronIndex;
175 }
176
177 private int GetGeneHashValue(int inNode, int outNode)
178 {
179 return ((GetIntegerHashMultiplyFactor(inNode + 1, outNode + 1) * (inNode + 1)) + (outNode + 1));
180 }
181
182 private int GetIntegerHashMultiplyFactor(int int1, int int2)
183 {
184 int integer = int1 > int2 ? int1 : int2;
185
186 if (integer >= 100)
187 return 1000;
188 else if (integer >= 10)
189 return 100;
190 else
191 return 10;
192 }
193
194 private void AddGene(int inNode, int outNode, float weight, bool active)
195 {
196 int inno = consultor.GetInnovationNumber(inNode,outNode);
197 //Debug.Log(inno+" "+ inNode+" "+outNode);
198 Gene gene = new Gene(inno, inNode, outNode, weight, active);
199 genome.Add(gene);
200 int geneHashValue = GetGeneHashValue(inNode, outNode);
201 genomeHash.Add(geneHashValue);
202 }
203
204 private void AddGene(int inno, int inNode, int outNode, float weight, bool active)
205 {
206 Gene gene = new Gene(inno, inNode, outNode, weight, active);
207 genome.Add(gene);
208 int geneHashValue = GetGeneHashValue(inNode, outNode);
209 genomeHash.Add(geneHashValue);
210 }
211
212 public void RemoveGene(int index)
213 {
214 int geneHashValue = GetGeneHashValue(genome[index].inNode, genome[index].outNode);
215 genome.RemoveAt(index);
216 genomeHash.Remove(geneHashValue);
217 }
218
219 //hyperbolic tangent activation
220 private float Tanh(float value)
221 {
222 return (float)Math.Tanh(value);
223 }
224
225 //random name generation, with atlest 1 vowel per 3 letters
226 private void GenerateRandomName()
227 {
228 int nameSize = UnityEngine.Random.Range(3, 11);
229 char[] name = new char[nameSize];
230
231 int[] vowels = new int[] { 97, 101, 105, 111, 117 };
232 int vowelCounter = 1;
233 for (int i = 0; i < name.Length; i++)
234 {
235 int charNum = UnityEngine.Random.Range(97, 123);
236
237 bool isVowel = false;
238 for (int j = 0; j < vowels.Length; j++)
239 {
240 if (charNum == vowels[j])
241 {
242 isVowel = true;
243 break;
244 }
245 }
246
247 if (isVowel)
248 {
249 vowelCounter = 1;
250 }
251 else if (vowelCounter == 3)
252 {
253 vowelCounter = 1;
254 charNum = vowels[UnityEngine.Random.Range(0, vowels.Length)];
255 }
256
257 vowelCounter++;
258 name[i] = (char)charNum;
259 }
260 this.name = new string(name);
261 }
262
263 public string GetName()
264 {
265 return name;
266 }
267
268 public bool Equals(NEATBrain other)
269 {
270 if (other == null)
271 return false;
272
273 return (other.ID == this.ID);
274 }
275
276 public void SetID(int ID)
277 {
278 this.ID = ID;
279 }
280
281 public int GetNumberOfInputNeurons()
282 {
283 return numberOfInputNeurons;
284 }
285
286 public int GetNumberOfUsedHiddenNeurons()
287 {
288 return usedHiddenNeuronIndex-(numberOfInputNeurons+numberOfOutputNeurons);
289 }
290
291 public int GetNumberOfOutputNeurons()
292 {
293 return numberOfOutputNeurons;
294 }
295
296 public Neuron[] GetNetworkArray()
297 {
298 return networkArray;
299 }
300
301 public void Mutate()
302 {
303 bool initialConnection = false;
304 int[] numberOfConnectedInputNeurons = new int[numberOfInputNeurons];
305 for (int i = 0;i<genome.Count;i++)
306 {
307 Gene gene = genome[i];
308 if (gene.inNode < numberOfInputNeurons && gene.active == true)
309 {
310 numberOfConnectedInputNeurons[gene.inNode]++;
311 }
312 }
313 for (int i = 0; i < numberOfInputNeurons; i++)
314 {
315 if (numberOfConnectedInputNeurons[i] == 0)
316 {
317 int inNode = i;
318 int randomOutNode = UnityEngine.Random.Range(numberOfInputNeurons, usedHiddenNeuronIndex);
319 bool ret1 = CheckIfGeneExists(inNode, randomOutNode);
320 if (ret1 == false) //<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
321 {
322 AddGene(inNode, randomOutNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
323 }
324 initialConnection = true;
325 }
326 }
327
328 if (initialConnection == false)
329 {
330 if (genome.Count > 0)
331 {
332 float randomMutateType = UnityEngine.Random.Range(1f, 100f);
333
334 if (randomMutateType <= 1f)
335 {
336 if (genome.Count >= (numberOfInputNeurons * numberOfOutputNeurons))
337 {
338 int randomIndex = UnityEngine.Random.Range(0, genome.Count);
339 RemoveGene(randomIndex);
340 }
341 }
342 else if (randomMutateType <= 50f)
343 {
344 float randomMutateValue = UnityEngine.Random.Range(1f, 100f);
345 if (randomMutateValue <=50)
346 {
347
348 if (usedHiddenNeuronIndex < (numberOfOutputNeurons + numberOfHiddenNeurons + numberOfInputNeurons))
349 {
350 int randomIndex = UnityEngine.Random.Range(0, genome.Count);
351 Gene gene = genome[randomIndex];
352 gene.active = false;
353
354 AddGene(gene.inNode, usedHiddenNeuronIndex, /*UnityEngine.Random.Range(-0.5f, 0.5f)*/ 1f, true);
355 AddGene(usedHiddenNeuronIndex, gene.outNode, gene.weight, true);
356
357 usedHiddenNeuronIndex++;
358 }
359 }
360 else if (randomMutateValue <=100)
361 {
362 int randomInNode = UnityEngine.Random.Range(0, usedHiddenNeuronIndex);
363 int randomOutNode = UnityEngine.Random.Range(numberOfInputNeurons, usedHiddenNeuronIndex);
364 bool ret1 = CheckIfGeneExists(randomInNode, randomOutNode);
365 bool ret2 = CheckIfGeneExists(randomOutNode, randomInNode);
366 bool ret3 = CheckIfGeneExists(randomInNode, randomInNode);
367 bool ret4 = CheckIfGeneExists(randomOutNode, randomOutNode);
368
369 if (ret1 == true && ret2 == false && randomInNode >= numberOfInputNeurons)
370 {
371 AddGene(randomOutNode, randomInNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
372 }
373 else if (ret1 == false)
374 {
375 AddGene(randomInNode, randomOutNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
376 }
377 else if (ret3 == true && randomInNode >= numberOfInputNeurons)
378 {
379 //AddGene(randomInNode, randomInNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
380 }
381 else if (ret4 == false)
382 {
383 AddGene(randomOutNode, randomOutNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
384 }
385 else
386 {
387 //int randomIndex = UnityEngine.Random.Range(0, genome.Count);
388 //genome[randomIndex].active = !genome[randomIndex].active;
389 //if (genome[randomIndex].active == false)
390 //genome[randomIndex].active = true;
391 }
392 }
393 }
394 }
395 else
396 {
397 int randomInNode = UnityEngine.Random.Range(0, usedHiddenNeuronIndex);
398 int randomOutNode = UnityEngine.Random.Range(numberOfInputNeurons, usedHiddenNeuronIndex);
399 bool ret1 = CheckIfGeneExists(randomInNode, randomOutNode);
400 bool ret2 = CheckIfGeneExists(randomOutNode, randomInNode);
401 bool ret3 = CheckIfGeneExists(randomInNode, randomInNode);
402 bool ret4 = CheckIfGeneExists(randomOutNode, randomOutNode);
403
404 if (ret1 == true && ret2 == false && randomInNode >= numberOfInputNeurons)
405 {
406 AddGene(randomOutNode, randomInNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
407 }
408 else if (ret1 == false)
409 {
410 AddGene(randomInNode, randomOutNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
411 }
412 else if (ret3 == true && randomInNode >= numberOfInputNeurons)
413 {
414 //AddGene(randomInNode, randomInNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
415 }
416 else if (ret4 == false)
417 {
418 AddGene(randomOutNode, randomOutNode, UnityEngine.Random.Range(-0.5f, 0.5f), true);
419 }
420 }
421 }
422
423 MutateGenes();
424 MutateName();
425 }
426
427 private void MutateGenes()
428 {
429 int numberOfGenes = genome.Count; //number of genes
430
431 for (int i = 0; i < numberOfGenes; i++)
432 { //run through all genes
433 Gene gene = genome[i]; // get gene at index i
434 float weight = 0;
435
436 int randomNumber = UnityEngine.Random.Range(1, 101); //random number between 1 and 100
437
438 if (randomNumber <= 1)
439 { //if 1
440 //flip sign of weight
441 weight = gene.weight;
442 weight *= -1f;
443 gene.weight = weight;
444 }
445 else if (randomNumber <= 2)
446 { //if 2
447 //pick random weight between -1 and 1
448 weight = UnityEngine.Random.Range(-0.5f, 0.5f);
449 gene.weight = weight;
450 }
451 else if (randomNumber <= 3)
452 { //if 3
453 //randomly increase by 0% to 100%
454 float factor = UnityEngine.Random.Range(0f, 1f) + 1f;
455 weight = gene.weight * factor;
456 gene.weight = weight;
457 }
458 else if (randomNumber <= 4)
459 { //if 4
460 //randomly decrease by 0% to 100%
461 float factor = UnityEngine.Random.Range(0f, 1f);
462 weight = gene.weight * factor;
463 gene.weight = weight;
464 }
465 else if (randomNumber <= 5)
466 { //if 5
467 //flip activation state for gene
468 //gene.active = !gene.active;
469 }
470 }
471
472 }
473
474 public bool CheckIfGeneExists(int inNode, int outNode)
475 {
476
477 /*for (int i = 0; i < genome.Count; i++)
478 {
479 Gene gene = genome[i];
480 if (gene.inNode == inNode && gene.outNode == outNode)
481 {
482 return 1;
483 }
484 }*/
485
486 return genomeHash.Contains(GetGeneHashValue(inNode, outNode));
487 }
488
489 public int GetCalculations()
490 {
491 return calculations;
492 }
493
494 private void MutateName()
495 {
496 //Mutate name
497 int index = UnityEngine.Random.Range(0, name.Length);
498 char[] nameChar = name.ToCharArray();
499 List<char> nameCharList = new List<char>(nameChar);
500
501 int randomNumber = UnityEngine.Random.Range(0, 3);
502 if (randomNumber == 0)
503 {
504 nameChar[index] = (char)UnityEngine.Random.Range(97, 123);
505 name = new string(nameChar);
506 }
507 else if (randomNumber == 1)
508 {
509 if (nameCharList.Count >= 4)
510 {
511 nameCharList.RemoveAt(UnityEngine.Random.Range(0, nameCharList.Count));
512
513 }
514 else
515 {
516 nameCharList.Add((char)UnityEngine.Random.Range(97, 123));
517 }
518
519 nameChar = nameCharList.ToArray();
520 name = new string(nameChar);
521 }
522 else if (randomNumber == 2)
523 {
524
525 if (nameCharList.Count < 10)
526 {
527 int locationToAdd = UnityEngine.Random.Range(0, nameCharList.Count);
528 nameCharList.Insert(locationToAdd, (char)UnityEngine.Random.Range(97, 123));
529 }
530 else
531 {
532 nameCharList.RemoveAt(UnityEngine.Random.Range(0, nameCharList.Count));
533 }
534
535 nameChar = nameCharList.ToArray();
536 name = new string(nameChar);
537
538 }
539 }
540
541 public float[] GetOutput()
542 {
543 /*float[] output = new float[numberOfOutputNeurons];
544 for (int i = 0; i < output.Length; i++)
545 {
546 output[i] = nodes[numberOfInputNeurons + i];
547 }
548 return output;*/
549
550 float[] output = new float[numberOfOutputNeurons];
551 for (int i = 0; i < output.Length; i++)
552 {
553 output[i] = network[i + numberOfInputNeurons].value;
554
555 }
556
557 return output;
558 }
559
560 public float Sigmoid(float value)
561 {
562 return 2f / (1f + (float)Math.Exp(-2f * value)) - 1f;
563 }
564
565 private float activation(float value, int type)
566 {
567 switch (type)
568 {
569 case 0: return Tanh(value);
570 case 1: return Mathf.Sin(value);
571 case 2: return Sigmoid(value);
572 default: return Tanh(value);
573 }
574 }
575
576 public float[] FeedForward(float[] inputs)
577 {
578 for (int i = 0; i < inputs.Length; i++)
579 {
580 networkArray[i].value = inputs[i];
581
582 }
583
584 float[] output = new float[numberOfOutputNeurons];
585
586 float[] tempValues = new float[networkArray.Length];
587 for (int i = 0; i < tempValues.Length;i++)
588 tempValues[i] = networkArray[i].value;
589
590 networkArray[numberOfInputNeurons - 1].value = 1f;
591
592 for (int i = 0; i < networkArray.Length; i++)
593 {
594 float value = 0;
595 Neuron neuron = networkArray[i];
596 Gene[] incommingArray = neuron.incommingArray;
597
598 if (incommingArray.Length > 0)
599 {
600 for (int j = 0; j < incommingArray.Length; j++)
601 {
602 if (incommingArray[j].active == true)
603 {
604 value = value + (incommingArray[j].weight * /*network[incomming[j].inNode].value*/ tempValues[incommingArray[j].inNode]);
605 }
606 }
607 neuron.value = Tanh(value);
608 }
609 }
610
611 return GetOutput();
612 }
613
614 internal static float BrainSimilarityScore(NEATBrain net1, NEATBrain net2)
615 {
616 //Debug.Log("___________________________________________");
617 Hashtable geneHash = new Hashtable(); //hash table to be used to compared genes from the two networks
618 Gene[] geneValue; //will be used to check whether a gene exists in both networks
619
620 List<Gene> geneList1 = net1.genome; //get first network
621 List<Gene> geneList2 = net2.genome; //get second network
622
623 ICollection keysCol; //will be used to get keys from gene hash
624 int[] keys; //will be used to get keys arrray from ICollections
625
626 int numberOfGenes1 = geneList1.Count; //get number of genes in network 1
627 int numberOfGenes2 = geneList2.Count; //get number of genes in network 2
628 int largerGenomeSize = numberOfGenes1 > numberOfGenes2 ? numberOfGenes1 : numberOfGenes2; //get one that is larger between the 2 network
629 int excessGenes = 0; //number of excess genes (genes that do match and are outside the innovation number of the other network)
630 int disjointGenes = 0; //number of disjoint gene (genes that do not match in the two networks)
631 int equalGenes = 0; //number of genes both neural network have
632
633 float disjointCoefficient = 0.85f; //get disjoint coefficient from consultor
634 float excessCoefficient = 1f; //get excess coefficient from consultor
635 float averageWeightDifferenceCoefficient = 1f; //get average weight difference coefficient
636
637 float similarity = 0; //similarity of the two networks
638 float averageWeightDifference = 0; //average weight difference of the two network's equal genes
639
640 bool foundAllExcess = false; //if all excess genes are found
641 bool isFirstGeneExcess = false; //if net 1 contains the excess genes
642
643 for (int i = 0; i < geneList1.Count; i++)
644 { //run through net 1's genes
645 int innovation = geneList1[i].inno; //get innovation number of gene
646 //Debug.Log(innovation+" "+ geneList1[i].inNode+" "+ geneList1[i].outNode);
647 geneValue = new Gene[] { geneList1[i], null }; //add into the hash with innovation number as the key and gene array of size 2 as value
648
649 //try {
650 geneHash.Add(innovation, geneValue); //add into the hash with innovation number as the key and gene array of size 2 as value
651 /*}
652 catch (ArgumentException error)
653 {
654 Debug.Log(innovation+" "+ geneList1[i].inNode+" "+geneList1[i].outNode+" ERRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRROR");
655 }*/
656 }
657
658 for (int i = 0; i < geneList2.Count; i++)
659 { //run through net 2's genes
660 int innovation = geneList2[i].inno; //get innovation number of gene
661
662 if (!geneHash.ContainsKey(innovation))
663 { //if innovation key does not exist
664 geneValue = new Gene[] { null, geneList2[i] }; //create array of size 2 with new gene in the second position
665 geneHash.Add(innovation, geneValue); //add into the hash with innovation number as the key and gene array of size 2 as value
666 }
667 else
668 { //key exists
669 geneValue = (Gene[])geneHash[innovation]; //get value
670 geneValue[1] = geneList2[i]; //add into second position net 2's gene
671 }
672 }
673
674 keysCol = geneHash.Keys; //get all keys from gene hash
675 keys = new int[keysCol.Count]; //create array with size of number of keys
676 keysCol.CopyTo(keys, 0); //copy all keys from ICollections to array
677 keys = keys.OrderBy(i => i).ToArray(); //order keys in ascending order
678
679 for (int i = keys.Length - 1; i >= 0; i--)
680 { //run through all keys backwards (to get all excess gene's first)
681 geneValue = (Gene[])geneHash[keys[i]]; //get value with key
682
683 if (foundAllExcess == false)
684 { //if all excess genes have not been found
685 if (i == keys.Length - 1 && geneValue[1] == null)
686 { //this is the first itteration and second gene location is null
687 isFirstGeneExcess = true; //excess genes exit in net 1
688 }
689
690 if (isFirstGeneExcess == true && geneValue[1] == null)
691 { //excess gene exist in net 1 and there is no gene in second location of the value
692 excessGenes++; //this is an excess gene and increment excess gene
693 }
694 else if (isFirstGeneExcess == false && geneValue[0] == null)
695 { //excess gene exist in net 12 and there is no gene in first location of the value
696 excessGenes++; //this is an excess gene and increment excess gene
697 }
698 else
699 { //no excess genes
700 foundAllExcess = true; //all excess genes are found
701 }
702
703 }
704
705 if (foundAllExcess == true)
706 { //if all excess genes are found
707 if (geneValue[0] != null && geneValue[1] != null)
708 { //both gene location are not null
709 equalGenes++; //increment equal genes
710 averageWeightDifference += Mathf.Abs(geneValue[0].weight - geneValue[1].weight); //add absolute difference between 2 weight
711 }
712 else
713 { //this is disjoint gene
714 disjointGenes++; //increment disjoint
715 }
716 }
717 }
718
719 averageWeightDifference = averageWeightDifference / (float)equalGenes; //get average weight difference of equal genes
720
721 //similarity formula -> Sim = (AVG_DIFF * AVG_COFF) + (((DISJ*DISJ_COFF) + (EXSS*EXSS_COFF)) /GENOME_SIZE)
722 similarity = (averageWeightDifference * averageWeightDifferenceCoefficient) + //calculate weight difference disparity
723 (((float)disjointGenes * disjointCoefficient) / (float)largerGenomeSize) + //calculate disjoint disparity
724 (((float)excessGenes * excessCoefficient) / (float)largerGenomeSize); //calculate excess disparity
725
726 //if similairty is <= to threshold then return true, otherwise false
727 return similarity; //return boolean compare value
728
729 }
730
731 internal static NEATBrain MakeDifferentialBrain(NEATBrain net1, NEATBrain net2)
732 {
733 NEATBrain difference = null;
734 List<Neuron> differenceNeurons = new List<Neuron>();
735 Neuron[] differenceNeuronsArray;
736
737 bool net1IsSmaller = false;
738 Neuron[] net1Neurons = net1.GetNetworkArray();
739 Neuron[] net2Neurons = net2.GetNetworkArray();
740 Neuron[] tempNeurons = net1Neurons;
741 ICollection keysCol;
742 int[] keys;
743
744 if (net1Neurons.Length < net2Neurons.Length)
745 {
746 net1Neurons = net2Neurons;
747 net2Neurons = tempNeurons;
748 net1IsSmaller = true;
749 }
750
751 for (int i = 0; i < net1Neurons.Length; i++)
752 {
753 Neuron neuron = new Neuron(net1Neurons[i].id,0f);
754 Hashtable geneHash = new Hashtable();
755 for (int j = 0; j < net1Neurons[i].incommingArray.Length; j++)
756 {
757 geneHash.Add(net1Neurons[i].incommingArray[j].inno, new Gene[] { net1Neurons[i].incommingArray[j], null});
758 }
759
760 if (i <= net2Neurons.Length - 1)
761 {
762 for (int j = 0; j < net2Neurons[i].incommingArray.Length; j++)
763 {
764 if (geneHash.ContainsKey(net2Neurons[i].incommingArray[j].inno))
765 {
766 Gene[] genes = (Gene[])geneHash[net2Neurons[i].incommingArray[j].inno];
767 genes[1] = net2Neurons[i].incommingArray[j];
768 }
769 else
770 {
771 geneHash.Add(net2Neurons[i].incommingArray[j].inno, new Gene[] { null, net2Neurons[i].incommingArray[j] });
772 }
773 }
774 }
775
776 keysCol = geneHash.Keys; //get all keys from gene hash
777 keys = new int[keysCol.Count]; //create array with size of number of keys
778 keysCol.CopyTo(keys, 0); //copy all keys from ICollections to array
779 keys = keys.OrderBy(x => x).ToArray(); //order keys in ascending order
780
781 for (int j = 0; j < keys.Length; j++)
782 {
783 Gene[] genes = (Gene[])geneHash[keys[j]];
784 Gene gene = null;
785 if (genes[0] != null)
786 {
787 gene = new Gene(genes[0]);
788 if (genes[1] != null)
789 {
790 gene.weight = Mathf.Abs((gene.weight + genes[1].weight)/2f);
791 }
792 else
793 {
794 gene.weight = 100f;
795 }
796 }
797 else
798 {
799 gene = new Gene(genes[1]);
800 if (genes[0] != null)
801 {
802 gene.weight = Mathf.Abs((gene.weight + genes[0].weight) / 2f);
803 }
804 else
805 {
806 gene.weight = 100f;
807 }
808 }
809
810 //gene.weight = Mathf.Abs(gene.weight);
811 neuron.incomming.Add(gene);
812 }
813
814 differenceNeurons.Add(neuron);
815 }
816
817 differenceNeurons.Sort((x, y) => x.id.CompareTo(y.id));
818
819 differenceNeuronsArray = differenceNeurons.ToArray();
820 for (int i = 0; i < differenceNeuronsArray.Length; i++)
821 {
822 differenceNeuronsArray[i].incomming.Sort((x, y) => x.inNode.CompareTo(y.inNode));
823 differenceNeuronsArray[i].incommingArray = differenceNeuronsArray[i].incomming.ToArray();
824 }
825
826 difference = new NEATBrain(net1IsSmaller == true? net2:net1, differenceNeuronsArray);
827
828 return difference;
829 }
830
831 public BehaviourGenome GetBehaviourGenome()
832 {
833 return behaviourGenome;
834 }
835
836}