/// <summary> /// Get a list of the genes that have not been taken before. This is useful /// if you do not wish the same gene to appear more than once in a /// chromosome. /// </summary> /// <param name="source">The pool of genes to select from.</param> /// <param name="taken">An array of the taken genes.</param> /// <returns>Those genes in source that are not taken.</returns> private static IGene GetNotTaken(Chromosome source, IList<IGene> taken) { int geneLength = source.Genes.Count; for (int i = 0; i < geneLength; i++) { IGene trial = source.Genes[i]; bool found = false; foreach (IGene current in taken) { if (current.Equals(trial)) { found = true; break; } } if (!found) { taken.Add(trial); return trial; } } return null; }
/// <summary> /// Construct an innovation list. /// </summary> /// <param name="population">The population.</param> /// <param name="links">The links.</param> /// <param name="neurons">The neurons.</param> public NEATInnovationList(IPopulation population, Chromosome links, Chromosome neurons) { this.population = population; foreach (IGene gene in neurons.Genes) { NEATNeuronGene neuronGene = (NEATNeuronGene)gene; NEATInnovation innovation = new NEATInnovation(neuronGene, population.AssignInnovationID(), AssignNeuronID()); Innovations.Add(innovation); } foreach (IGene gene in links.Genes) { NEATLinkGene linkGene = (NEATLinkGene)gene; NEATInnovation innovation = new NEATInnovation(linkGene .FromNeuronID, linkGene.ToNeuronID, NEATInnovationType.NewLink, this.population.AssignInnovationID()); Innovations.Add(innovation); } }
/// <summary> /// Assuming this chromosome is the "mother" mate with the passed in /// "father". /// </summary> /// <param name="mother">The mother.</param> /// <param name="father">The father.</param> /// <param name="offspring1">The first offspring.</param> /// <param name="offspring2">The second offspring.</param> public void Mate(Chromosome mother, Chromosome father, Chromosome offspring1, Chromosome offspring2) { int geneLength = mother.Genes.Count; // the chromosome must be cut at two positions, determine them int cutpoint1 = (int)(ThreadSafeRandom.NextDouble() * (geneLength - cutLength)); int cutpoint2 = cutpoint1 + cutLength; // handle cut section for (int i = 0; i < geneLength; i++) { if (!((i < cutpoint1) || (i > cutpoint2))) { offspring1.Genes[i].Copy(father.Genes[i]); offspring2.Genes[i].Copy(mother.Genes[i]); } } // handle outer sections for (int i = 0; i < geneLength; i++) { if ((i < cutpoint1) || (i > cutpoint2)) { offspring1.Genes[i].Copy(mother.Genes[i]); offspring2.Genes[i].Copy(father.Genes[i]); } } }
/// <summary> /// Perform a perturb mutation on the specified chromosome. /// </summary> /// <param name="chromosome">The chromosome to mutate.</param> public void PerformMutation(Chromosome chromosome) { foreach (IGene gene in chromosome.Genes) { if (gene is DoubleGene) { DoubleGene doubleGene = (DoubleGene)gene; double value = doubleGene.Value; value += (perturbAmount - (ThreadSafeRandom.NextDouble()) * perturbAmount * 2); doubleGene.Value = value; } } }
/// <summary> /// Construct a neural network genome. /// </summary> /// <param name="nga">The neural genetic algorithm.</param> /// <param name="network">The network.</param> public NeuralGenome(NeuralGeneticAlgorithm nga, BasicNetwork network) : base(nga.Helper) { this.Organism = network; this.networkChromosome = new Chromosome(); // create an array of "double genes" int size = network.Structure.CalculateSize(); for (int i = 0; i < size; i++) { IGene gene = new DoubleGene(); this.networkChromosome.Genes.Add(gene); } this.Chromosomes.Add(this.networkChromosome); Encode(); }
/// <summary> /// Perform a shuffle mutation. /// </summary> /// <param name="chromosome">The chromosome to mutate.</param> public void PerformMutation(Chromosome chromosome) { int length = chromosome.Genes.Count; int iswap1 = (int)(ThreadSafeRandom.NextDouble() * length); int iswap2 = (int)(ThreadSafeRandom.NextDouble() * length); // can't be equal if (iswap1 == iswap2) { // move to the next, but // don't go out of bounds if (iswap1 > 0) { iswap1--; } else { iswap1++; } } // make sure they are in the right order if (iswap1 > iswap2) { int temp = iswap1; iswap1 = iswap2; iswap2 = temp; } IGene gene1 = chromosome.Genes[iswap1]; IGene gene2 = chromosome.Genes[iswap2]; // remove the two genes chromosome.Genes.Remove(gene1); chromosome.Genes.Remove(gene2); // insert them back in, reverse order chromosome.Genes.Insert(iswap1, gene2); chromosome.Genes.Insert(iswap2, gene1); }
/// <summary> /// Construct a genome, do not provide links and neurons. /// </summary> /// <param name="training">The owner object.</param> /// <param name="id">The genome id.</param> /// <param name="inputCount">The input count.</param> /// <param name="outputCount">The output count.</param> public NEATGenome(NEATTraining training, long id, int inputCount, int outputCount) : base(training) { GenomeID = id; AdjustedScore = 0; this.inputCount = inputCount; this.outputCount = outputCount; AmountToSpawn = 0; speciesID = 0; double inputRowSlice = 0.8 / (inputCount); neuronsChromosome = new Chromosome(); linksChromosome = new Chromosome(); for (int i = 0; i < inputCount; i++) { neuronsChromosome.Genes.Add(new NEATNeuronGene(NEATNeuronType.Input, i, 0, 0.1 + i * inputRowSlice)); } neuronsChromosome.Genes.Add(new NEATNeuronGene(NEATNeuronType.Bias, inputCount, 0, 0.9)); double outputRowSlice = 1 / (double)(outputCount + 1); for (int i = 0; i < outputCount; i++) { neuronsChromosome.Genes.Add(new NEATNeuronGene(NEATNeuronType.Output, i + inputCount + 1, 1, (i + 1) * outputRowSlice)); } for (int i = 0; i < inputCount + 1; i++) { for (int j = 0; j < outputCount; j++) { linksChromosome.Genes.Add(new NEATLinkGene( ((NEATNeuronGene)neuronsChromosome.Genes[i]).Id, ((NEATNeuronGene)Neurons.Genes[inputCount + j + 1]) .Id, true, inputCount + outputCount + 1 + NumGenes, RangeRandomizer.Randomize(-1, 1), false)); } } this.Chromosomes.Add(neuronsChromosome); this.Chromosomes.Add(linksChromosome); }
/// <summary> /// Create a NEAT gnome. /// </summary> /// <param name="training">The owner object.</param> /// <param name="genomeID">The genome id.</param> /// <param name="neurons">The neurons.</param> /// <param name="links">The links.</param> /// <param name="inputCount">The input count.</param> /// <param name="outputCount">The output count.</param> public NEATGenome(NEATTraining training, long genomeID, Chromosome neurons, Chromosome links, int inputCount, int outputCount) : base(training) { GenomeID = genomeID; linksChromosome = links; neuronsChromosome = neurons; AmountToSpawn = 0; AdjustedScore = 0; this.inputCount = inputCount; this.outputCount = outputCount; this.Chromosomes.Add(neuronsChromosome); this.Chromosomes.Add(linksChromosome); }
/// <summary> /// Construct a genome by copying another. /// </summary> /// <param name="other">The other genome.</param> public NEATGenome(NEATGenome other) : base(other.GA) { neuronsChromosome = new Chromosome(); linksChromosome = new Chromosome(); this.Chromosomes.Add(neuronsChromosome); this.Chromosomes.Add(linksChromosome); GenomeID = other.GenomeID; networkDepth = other.networkDepth; Score = other.Score; AdjustedScore = other.AdjustedScore; AmountToSpawn = other.AmountToSpawn; inputCount = other.inputCount; outputCount = other.outputCount; speciesID = other.speciesID; // copy neurons foreach (IGene gene in other.Neurons.Genes) { NEATNeuronGene oldGene = (NEATNeuronGene)gene; NEATNeuronGene newGene = new NEATNeuronGene(oldGene.NeuronType , oldGene.Id, oldGene.SplitX, oldGene.SplitY, oldGene.Recurrent, oldGene .ActivationResponse); this.neuronsChromosome.Genes.Add(newGene); } // copy links foreach (IGene gene in other.Links.Genes) { NEATLinkGene oldGene = (NEATLinkGene)gene; NEATLinkGene newGene = new NEATLinkGene(oldGene .FromNeuronID, oldGene.ToNeuronID, oldGene .Enabled, oldGene.InnovationId, oldGene .Weight, oldGene.IsRecurrent); Links.Genes.Add(newGene); } }
/// <summary> /// Assuming this chromosome is the "mother" mate with the passed in /// "father". /// </summary> /// <param name="mother">The mother.</param> /// <param name="father">The father.</param> /// <param name="offspring1">The first offspring.</param> /// <param name="offspring2">The second offspring.</param> public void Mate(Chromosome mother, Chromosome father, Chromosome offspring1, Chromosome offspring2) { int geneLength = father.Genes.Count; // the chromosome must be cut at two positions, determine them int cutpoint1 = (int)(ThreadSafeRandom.NextDouble() * (geneLength - cutLength)); int cutpoint2 = cutpoint1 + cutLength; // keep track of which genes have been taken in each of the two // offspring, defaults to false. IList<IGene> taken1 = new List<IGene>(); IList<IGene> taken2 = new List<IGene>(); // handle cut section for (int i = 0; i < geneLength; i++) { if (!((i < cutpoint1) || (i > cutpoint2))) { offspring1.Genes[i].Copy(father.Genes[i]); offspring2.Genes[i].Copy(mother.Genes[i]); taken1.Add(offspring1.Genes[i]); taken2.Add(offspring2.Genes[i]); } } // handle outer sections for (int i = 0; i < geneLength; i++) { if ((i < cutpoint1) || (i > cutpoint2)) { offspring1.Genes[i].Copy( SpliceNoRepeat.GetNotTaken(mother, taken1)); offspring2.Genes[i].Copy( SpliceNoRepeat.GetNotTaken(father, taken2)); } } }
/// <summary> /// Perform the crossover. /// </summary> /// <param name="mom">The mother.</param> /// <param name="dad">The father.</param> /// <returns>The child.</returns> public NEATGenome PerformCrossover(NEATGenome mom, NEATGenome dad) { NEATParent best; // first determine who is more fit, the mother or the father? if (mom.Score == dad.Score) { if (mom.NumGenes == dad.NumGenes) { if (ThreadSafeRandom.NextDouble() > 0) { best = NEATParent.Mom; } else { best = NEATParent.Dad; } } else { if (mom.NumGenes < dad.NumGenes) { best = NEATParent.Mom; } else { best = NEATParent.Dad; } } } else { if (Comparator.IsBetterThan(mom.Score, dad.Score)) { best = NEATParent.Mom; } else { best = NEATParent.Dad; } } Chromosome babyNeurons = new Chromosome(); Chromosome babyGenes = new Chromosome(); List<long> vecNeurons = new List<long>(); int curMom = 0; int curDad = 0; NEATLinkGene momGene; NEATLinkGene dadGene; NEATLinkGene selectedGene = null; while ((curMom < mom.NumGenes) || (curDad < dad.NumGenes)) { if (curMom < mom.NumGenes) { momGene = (NEATLinkGene)mom.Links.Genes[curMom]; } else { momGene = null; } if (curDad < dad.NumGenes) { dadGene = (NEATLinkGene)dad.Links.Genes[curDad]; } else { dadGene = null; } if ((momGene == null) && (dadGene != null)) { if (best == NEATParent.Dad) { selectedGene = dadGene; } curDad++; } else if ((dadGene == null) && (momGene != null)) { if (best == NEATParent.Mom) { selectedGene = momGene; } curMom++; } else if (momGene.InnovationId < dadGene.InnovationId) { if (best == NEATParent.Mom) { selectedGene = momGene; } curMom++; } else if (dadGene.InnovationId < momGene.InnovationId) { if (best == NEATParent.Dad) { selectedGene = dadGene; } curDad++; } else if (dadGene.InnovationId == momGene.InnovationId) { if (ThreadSafeRandom.NextDouble() < 0.5f) { selectedGene = momGene; } else { selectedGene = dadGene; } curMom++; curDad++; } if (babyGenes.Genes.Count == 0) { babyGenes.Genes.Add(selectedGene); } else { if (babyGenes.Genes[babyGenes.Genes.Count - 1] .InnovationId != selectedGene.InnovationId) { babyGenes.Genes.Add(selectedGene); } } // Check if we already have the nodes referred to in SelectedGene. // If not, they need to be added. AddNeuronID(selectedGene.FromNeuronID, vecNeurons); AddNeuronID(selectedGene.ToNeuronID, vecNeurons); } // now create the required nodes. First sort them into order vecNeurons.Sort(); for (int i = 0; i < vecNeurons.Count; i++) { babyNeurons.Genes.Add(this.Innovations.CreateNeuronFromID( vecNeurons[i])); } // finally, create the genome NEATGenome babyGenome = new NEATGenome(this, Population .AssignGenomeID(), babyNeurons, babyGenes, mom.InputCount, mom.OutputCount); return babyGenome; }