/// <summary> /// Add an innovation. /// </summary> /// /// <param name="innovation">The innovation to add.</param> public void Add(IInnovation innovation) { _list.Add(innovation); }
public void Add(IInnovation innovation) { this._list.Add(innovation); }
/// <summary> /// Crosses two parents to derive a new genome for a new child <see cref="INeatNetwork"/> /// </summary> /// <param name="left"></param> /// <param name="right"></param> /// <param name="fitnessState"></param> /// <returns></returns> public IInnovation[] DeriveGenome(INeatNetwork left, INeatNetwork right, FitnessState fitnessState) { // align the genomes so we can select and create a new genome var GenomeMatches = AlignGenomes(left, right); // per https://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.28.5457&rep=rep1&type=pdf pg 12 // Matching genes are inherited randomly, whereas disjoint genes(those that do not match in the middle) and excess genes (those that do not match in the end) are inherited from the more fit parent. Whith equal fitnesses, disjoint and excess genes are also inherited randomly. // if the right length is larger that means any excess in the genome belongs to the right int additionalGenomeSize = 0; bool inheritWholeExcess = false; // check to see who is responsible for the excess genes bool rightExcess = right.Innovations.Length > left.Innovations.Length; bool inheritRandomExcess = false; int amountOfDisjointGenesToInherit = 0; int amountOfExcessGenesToInherit = 0; // determine if there is any excess or disjoint genes to inherit if (GenomeMatches.Excess.Length != 0 || GenomeMatches.LeftDisjoint.Length != 0 || GenomeMatches.RightDisjoint.Length != 0) { switch (fitnessState) { // if the right is more fit and the excess belongs to the right inherit the excess and the right disjoints case FitnessState.RightMoreFit: inheritWholeExcess = rightExcess; if (inheritWholeExcess) { additionalGenomeSize = GenomeMatches.Excess.Length + GenomeMatches.RightDisjoint.Length; } break; // if the left is more fit and the excess belongs to the left inherit the excess and the left disjoints case FitnessState.LeftMoreFit: inheritWholeExcess = !rightExcess; if (inheritWholeExcess) { additionalGenomeSize = GenomeMatches.Excess.Length + GenomeMatches.LeftDisjoint.Length; } break; // if both are equally fit randomly inherit excess and disjoint genes case FitnessState.EqualFitness: inheritRandomExcess = true; inheritWholeExcess = false; // determine the max disjoint to inherit if (Helpers.Random.NextUDouble() >= 0.5d && GenomeMatches.LeftDisjoint.Length > 0) { amountOfDisjointGenesToInherit = Helpers.Random.Next(0, GenomeMatches.LeftDisjoint.Length); } else if (GenomeMatches.RightDisjoint.Length > 0) { amountOfDisjointGenesToInherit = Helpers.Random.Next(0, GenomeMatches.RightDisjoint.Length); } amountOfExcessGenesToInherit = Helpers.Random.Next(0, GenomeMatches.Excess.Length + 1); additionalGenomeSize = amountOfExcessGenesToInherit + amountOfDisjointGenesToInherit; break; } } // divide the length by 2 since the array scheme holds both left and right aligned genes int startingIndex = GenomeMatches.Aligned.Length >> 1; // becuase we do not know the IInnovation[] result = new IInnovation[startingIndex + additionalGenomeSize]; Span <IInnovation> newGenome = new(result); // get the rolls for the entire genome at once to avoid costly semaphore hits double[] rolls = Helpers.Random.NextUDoubleArray(newGenome.Length).Result; for (int i = 0; i < startingIndex; i++) { // randomly select genes if (rolls[i] > 0.5d) { // select the left gene newGenome[i] = GenomeMatches.Aligned[i << 1]; // left right left right left right // 0 1 2 3 4 5 // left = i * 2 } else { // select the right gene newGenome[i] = GenomeMatches.Aligned[(i << 1) + 1]; // left right left right left right // 0 1 2 3 4 5 // right = i * 2 + 1 } } // now that we have inserted all of the randomly chosen aligned genes we should add all of the excess and disjoint(if we determined earlier that it's appropriate // // copy the excess over to the destination array, we dont have to figure out who it belongs to yet becuase we only get 1 parents excess form the align genes method if (inheritWholeExcess) { // the size of the destination array has already been assigned so we dont have to resize and carve out another coiontiguous block of memory for the array Span <IInnovation> excess = new(GenomeMatches.Excess); excess.CopyTo(newGenome.Slice(startingIndex, excess.Length)); // now copy over the disjointed values if (rightExcess) { Span <IInnovation> disjointed = new(GenomeMatches.RightDisjoint); disjointed.CopyTo(newGenome.Slice(startingIndex + excess.Length)); } else { Span <IInnovation> disjointed = new(GenomeMatches.LeftDisjoint); disjointed.CopyTo(newGenome.Slice(startingIndex + excess.Length)); } } else if (inheritRandomExcess) { // since we should inherit random genes from both parents start off by inheriting the whole excess Span <IInnovation> excess = new(GenomeMatches.Excess); for (int i = 0; i < amountOfExcessGenesToInherit; i++) { newGenome[i + startingIndex] = excess[(i + Helpers.Random.Next(0, 101)) % amountOfExcessGenesToInherit]; } int index = startingIndex + amountOfExcessGenesToInherit; // now randomly inherit excess genes for (int i = 0; i < amountOfDisjointGenesToInherit; i++) { if (Helpers.Random.NextUDouble() >= 0.5d) { // left if (GenomeMatches.LeftDisjoint.Length - 1 > i) { newGenome[index++] = GenomeMatches.LeftDisjoint[i]; } else if (GenomeMatches.RightDisjoint.Length - 1 > i) { newGenome[index++] = GenomeMatches.RightDisjoint[i]; } } else { // right if (GenomeMatches.RightDisjoint.Length - 1 > i) { newGenome[index++] = GenomeMatches.RightDisjoint[i]; } else if (GenomeMatches.LeftDisjoint.Length - 1 > i) { newGenome[index++] = GenomeMatches.LeftDisjoint[i]; } } } } return(result); }
/// <summary> /// Hashes the provided innovation so that it can be compared with others in the genome /// </summary> /// <param name="innovation"></param> /// <returns></returns> public string Hash(IInnovation innovation) => Hash(innovation.InputNode, innovation.OutputNode);
/// <summary> /// Returns exception: /// <code> /// Circular innovation Id(12) From(3) -> To(1). Innovations can not input from nodes above them in the network or output to nodes that are below them in the network. /// </code> /// </summary> /// <param name="innovation"></param> /// <returns></returns> public static Exception CircularInnovationReference(IInnovation innovation) { return(new StackOverflowException($"Circular innovation Id({innovation?.Id}) From({innovation?.InputNode}) -> To({innovation?.OutputNode}). Innovations can not input from nodes above them in the network or output to nodes that are below them in the network.")); }