/// <summary> /// Copy from another gene. /// </summary> /// <param name="gene">The other gene.</param> public override void Copy(IGene gene) { NEATLinkGene other = (NEATLinkGene)gene; this.Enabled = other.Enabled; fromNeuronID = other.fromNeuronID; toNeuronID = other.toNeuronID; this.InnovationId = other.InnovationId; recurrent = other.recurrent; weight = other.weight; }
/// <summary> /// Determine if this is a duplicate link. /// </summary> /// <param name="fromNeuronID">The from neuron id.</param> /// <param name="toNeuronID">The to neuron id.</param> /// <returns>True if this is a duplicate link.</returns> public bool IsDuplicateLink(long fromNeuronID, long toNeuronID) { foreach (IGene gene in this.Links.Genes) { NEATLinkGene linkGene = (NEATLinkGene)gene; if ((linkGene.FromNeuronID == fromNeuronID) && (linkGene.ToNeuronID == toNeuronID)) { return(true); } } return(false); }
/// <summary> /// Convert the genes to an actual network. /// </summary> public override void Decode() { NEATPattern pattern = new NEATPattern(); IList <NEATNeuron> neurons = pattern.Neurons; foreach (IGene gene in Neurons.Genes) { NEATNeuronGene neuronGene = (NEATNeuronGene)gene; NEATNeuron neuron = new NEATNeuron( neuronGene.NeuronType, neuronGene.Id, neuronGene .SplitY, neuronGene.SplitX, neuronGene .ActivationResponse); neurons.Add(neuron); } // now to create the links. foreach (IGene gene in Links.Genes) { NEATLinkGene linkGene = (NEATLinkGene)gene; if (linkGene.Enabled) { int element = GetElementPos(linkGene.FromNeuronID); NEATNeuron fromNeuron = neurons[element]; element = GetElementPos(linkGene.ToNeuronID); NEATNeuron toNeuron = neurons[element]; NEATLink link = new NEATLink(linkGene.Weight, fromNeuron, toNeuron, linkGene.IsRecurrent); fromNeuron.OutputboundLinks.Add(link); toNeuron.InboundLinks.Add(link); } } pattern.NEATActivation = (((NEATTraining)GA).NeatActivationFunction); pattern.ActivationFunction = (((NEATTraining)GA).OutputActivationFunction); pattern.InputNeurons = (inputCount); pattern.OutputNeurons = (outputCount); pattern.Snapshot = ((NEATTraining)GA).Snapshot; Organism = pattern.Generate(); }
/// <summary> /// Mutate the weights. /// </summary> /// <param name="mutateRate">The mutation rate.</param> /// <param name="probNewMutate">The probability of a whole new weight.</param> /// <param name="maxPertubation">The max perturbation.</param> public void MutateWeights(double mutateRate, double probNewMutate, double maxPertubation) { foreach (IGene gene in linksChromosome.Genes) { NEATLinkGene linkGene = (NEATLinkGene)gene; if (ThreadSafeRandom.NextDouble() < mutateRate) { if (ThreadSafeRandom.NextDouble() < probNewMutate) { linkGene.Weight = RangeRandomizer.Randomize(-1, 1); } else { linkGene.Weight += RangeRandomizer.Randomize(-1, 1) * maxPertubation; } } } }
/// <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> /// 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> /// Mutate the genome by adding a neuron. /// </summary> /// <param name="mutationRate">The mutation rate.</param> /// <param name="numTrysToFindOldLink">The number of tries to find a link to split.</param> public void AddNeuron(double mutationRate, int numTrysToFindOldLink) { // should we add a neuron? if (ThreadSafeRandom.NextDouble() > mutationRate) { return; } // the link to split NEATLinkGene splitLink = null; int sizeThreshold = inputCount + outputCount + 10; // if there are not at least int upperLimit; if (linksChromosome.Genes.Count < sizeThreshold) { upperLimit = NumGenes - 1 - (int)Math.Sqrt(NumGenes); } else { upperLimit = NumGenes - 1; } while ((numTrysToFindOldLink--) > 0) { // choose a link, use the square root to prefer the older links int i = RangeRandomizer.RandomInt(0, upperLimit); NEATLinkGene link = (NEATLinkGene)linksChromosome.Genes[i]; // get the from neuron long fromNeuron = link.FromNeuronID; if ((link.Enabled) && (!link.IsRecurrent) && (((NEATNeuronGene)Neurons.Genes[ GetElementPos(fromNeuron)]).NeuronType != NEATNeuronType.Bias)) { splitLink = link; break; } } if (splitLink == null) { return; } splitLink.Enabled = false; double originalWeight = splitLink.Weight; long from = splitLink.FromNeuronID; long to = splitLink.ToNeuronID; NEATNeuronGene fromGene = (NEATNeuronGene)Neurons.Genes[ GetElementPos(from)]; NEATNeuronGene toGene = (NEATNeuronGene)Neurons.Genes[ GetElementPos(to)]; double newDepth = (fromGene.SplitY + toGene.SplitY) / 2; double newWidth = (fromGene.SplitX + toGene.SplitX) / 2; // has this innovation already been tried? NEATInnovation innovation = ((NEATTraining)GA).Innovations.CheckInnovation( from, to, NEATInnovationType.NewNeuron); // prevent chaining if (innovation != null) { long neuronID = innovation.NeuronID; if (AlreadyHaveThisNeuronID(neuronID)) { innovation = null; } } if (innovation == null) { // this innovation has not been tried, create it long newNeuronID = ((NEATTraining)GA).Innovations .CreateNewInnovation(from, to, NEATInnovationType.NewNeuron, NEATNeuronType.Hidden, newWidth, newDepth); neuronsChromosome.Genes.Add(new NEATNeuronGene(NEATNeuronType.Hidden, newNeuronID, newDepth, newWidth)); // add the first link long link1ID = ((NEATTraining)GA).Population.AssignInnovationID(); ((NEATTraining)GA).Innovations.CreateNewInnovation(from, newNeuronID, NEATInnovationType.NewLink); NEATLinkGene link1 = new NEATLinkGene(from, newNeuronID, true, link1ID, 1.0, false); linksChromosome.Genes.Add(link1); // add the second link long link2ID = ((NEATTraining)GA).Population.AssignInnovationID(); ((NEATTraining)GA).Innovations.CreateNewInnovation(newNeuronID, to, NEATInnovationType.NewLink); NEATLinkGene link2 = new NEATLinkGene(newNeuronID, to, true, link2ID, originalWeight, false); linksChromosome.Genes.Add(link2); } else { // existing innovation long newNeuronID = innovation.NeuronID; NEATInnovation innovationLink1 = ((NEATTraining)GA).Innovations .CheckInnovation(from, newNeuronID, NEATInnovationType.NewLink); NEATInnovation innovationLink2 = ((NEATTraining)GA).Innovations .CheckInnovation(newNeuronID, to, NEATInnovationType.NewLink); if ((innovationLink1 == null) || (innovationLink2 == null)) { throw new NeuralNetworkError("NEAT Error"); } NEATLinkGene link1 = new NEATLinkGene(from, newNeuronID, true, innovationLink1.InnovationID, 1.0, false); NEATLinkGene link2 = new NEATLinkGene(newNeuronID, to, true, innovationLink2.InnovationID, originalWeight, false); linksChromosome.Genes.Add(link1); linksChromosome.Genes.Add(link2); NEATNeuronGene newNeuron = new NEATNeuronGene( NEATNeuronType.Hidden, newNeuronID, newDepth, newWidth); neuronsChromosome.Genes.Add(newNeuron); } return; }
/// <summary> /// Mutate the genome by adding a link to this genome. /// </summary> /// <param name="mutationRate">The mutation rate.</param> /// <param name="chanceOfLooped">The chance of a self-connected neuron.</param> /// <param name="numTrysToFindLoop">The number of tries to find a loop.</param> /// <param name="numTrysToAddLink">The number of tries to add a link.</param> public void AddLink(double mutationRate, double chanceOfLooped, int numTrysToFindLoop, int numTrysToAddLink) { // should we even add the link if (ThreadSafeRandom.NextDouble() > mutationRate) { return; } // the link will be between these two neurons long neuron1ID = -1; long neuron2ID = -1; bool recurrent = false; // a self-connected loop? if (ThreadSafeRandom.NextDouble() < chanceOfLooped) { // try to find(randomly) a neuron to add a self-connected link to while ((numTrysToFindLoop--) > 0) { NEATNeuronGene neuronGene = ChooseRandomNeuron(false); // no self-links on input or bias neurons if (!neuronGene.Recurrent && (neuronGene.NeuronType != NEATNeuronType.Bias) && (neuronGene.NeuronType != NEATNeuronType.Input)) { neuron1ID = neuron2ID = neuronGene.Id; neuronGene.Recurrent = true; recurrent = true; numTrysToFindLoop = 0; } } } else { // try to add a regular link while ((numTrysToAddLink--) > 0) { NEATNeuronGene neuron1 = ChooseRandomNeuron(true); NEATNeuronGene neuron2 = ChooseRandomNeuron(false); if (!IsDuplicateLink(neuron1ID, neuron2ID) && (neuron1.Id != neuron2.Id) && (neuron2.NeuronType != NEATNeuronType.Bias)) { neuron1ID = -1; neuron2ID = -1; break; } } } // did we fail to find a link if ((neuron1ID < 0) || (neuron2ID < 0)) { return; } // check to see if this innovation has already been tried NEATInnovation innovation = ((NEATTraining)GA).Innovations .CheckInnovation(neuron1ID, neuron1ID, NEATInnovationType.NewLink); // see if this is a recurrent(backwards) link NEATNeuronGene neuronGene2 = (NEATNeuronGene)neuronsChromosome.Genes [GetElementPos(neuron1ID)]; if (neuronGene2.SplitY > neuronGene2.SplitY) { recurrent = true; } // is this a new innovation? if (innovation == null) { // new innovation ((NEATTraining)GA).Innovations.CreateNewInnovation(neuron1ID, neuron2ID, NEATInnovationType.NewLink); long id2 = ((NEATTraining)GA).Population.AssignInnovationID(); NEATLinkGene linkGene = new NEATLinkGene(neuron1ID, neuron2ID, true, id2, RangeRandomizer.Randomize(-1, 1), recurrent); linksChromosome.Genes.Add(linkGene); } else { // existing innovation NEATLinkGene linkGene = new NEATLinkGene(neuron1ID, neuron2ID, true, innovation.InnovationID, RangeRandomizer.Randomize(-1, 1), recurrent); linksChromosome.Genes.Add(linkGene); } }
/// <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> /// Mutate the genome by adding a neuron. /// </summary> /// <param name="mutationRate">The mutation rate.</param> /// <param name="numTrysToFindOldLink">The number of tries to find a link to split.</param> public void AddNeuron(double mutationRate, int numTrysToFindOldLink) { // should we add a neuron? if (ThreadSafeRandom.NextDouble() > mutationRate) { return; } // the link to split NEATLinkGene splitLink = null; int sizeThreshold = inputCount + outputCount + 10; // if there are not at least int upperLimit; if (linksChromosome.Genes.Count < sizeThreshold) { upperLimit = NumGenes - 1 - (int)Math.Sqrt(NumGenes); } else { upperLimit = NumGenes - 1; } while ((numTrysToFindOldLink--) > 0) { // choose a link, use the square root to prefer the older links int i = RangeRandomizer.RandomInt(0, upperLimit); NEATLinkGene link = (NEATLinkGene)linksChromosome.Genes[i]; // get the from neuron long fromNeuron = link.FromNeuronID; if ((link.Enabled) && (!link.IsRecurrent) && (((NEATNeuronGene)Neurons.Genes[ GetElementPos(fromNeuron)]).NeuronType != NEATNeuronType.Bias)) { splitLink = link; break; } } if (splitLink == null) { return; } splitLink.Enabled = false; double originalWeight = splitLink.Weight; long from = splitLink.FromNeuronID; long to = splitLink.ToNeuronID; NEATNeuronGene fromGene = (NEATNeuronGene)Neurons.Genes[ GetElementPos(from)]; NEATNeuronGene toGene = (NEATNeuronGene)Neurons.Genes[ GetElementPos(to)]; double newDepth = (fromGene.SplitY + toGene.SplitY) / 2; double newWidth = (fromGene.SplitX + toGene.SplitX) / 2; // has this innovation already been tried? NEATInnovation innovation = ((NEATTraining)GA).Innovations.CheckInnovation( from, to, NEATInnovationType.NewNeuron); // prevent chaining if (innovation != null) { long neuronID = innovation.NeuronID; if (AlreadyHaveThisNeuronID(neuronID)) { innovation = null; } } if (innovation == null) { // this innovation has not been tried, create it long newNeuronID = ((NEATTraining)GA).Innovations .CreateNewInnovation(from, to, NEATInnovationType.NewNeuron, NEATNeuronType.Hidden, newWidth, newDepth); neuronsChromosome.Genes.Add(new NEATNeuronGene(NEATNeuronType.Hidden, newNeuronID, newDepth, newWidth)); // add the first link long link1ID = ((NEATTraining)GA).Population.AssignInnovationID(); ((NEATTraining)GA).Innovations.CreateNewInnovation(from, newNeuronID, NEATInnovationType.NewLink); NEATLinkGene link1 = new NEATLinkGene(from, newNeuronID, true, link1ID, 1.0, false); linksChromosome.Genes.Add(link1); // add the second link long link2ID = ((NEATTraining)GA).Population.AssignInnovationID(); ((NEATTraining)GA).Innovations.CreateNewInnovation(newNeuronID, to, NEATInnovationType.NewLink); NEATLinkGene link2 = new NEATLinkGene(newNeuronID, to, true, link2ID, originalWeight, false); linksChromosome.Genes.Add(link2); } else { // existing innovation long newNeuronID = innovation.NeuronID; NEATInnovation innovationLink1 = ((NEATTraining)GA).Innovations .CheckInnovation(from, newNeuronID, NEATInnovationType.NewLink); NEATInnovation innovationLink2 = ((NEATTraining)GA).Innovations .CheckInnovation(newNeuronID, to, NEATInnovationType.NewLink); if ((innovationLink1 == null) || (innovationLink2 == null)) { throw new NeuralNetworkError("NEAT Error"); } NEATLinkGene link1 = new NEATLinkGene(from, newNeuronID, true, innovationLink1.InnovationID, 1.0, false); NEATLinkGene link2 = new NEATLinkGene(newNeuronID, to, true, innovationLink2.InnovationID, originalWeight, false); linksChromosome.Genes.Add(link1); linksChromosome.Genes.Add(link2); NEATNeuronGene newNeuron = new NEATNeuronGene( NEATNeuronType.Hidden, newNeuronID, newDepth, newWidth); neuronsChromosome.Genes.Add(newNeuron); } return; }
/// <summary> /// Mutate the genome by adding a link to this genome. /// </summary> /// <param name="mutationRate">The mutation rate.</param> /// <param name="chanceOfLooped">The chance of a self-connected neuron.</param> /// <param name="numTrysToFindLoop">The number of tries to find a loop.</param> /// <param name="numTrysToAddLink">The number of tries to add a link.</param> public void AddLink(double mutationRate, double chanceOfLooped, int numTrysToFindLoop, int numTrysToAddLink) { // should we even add the link if (ThreadSafeRandom.NextDouble() > mutationRate) { return; } // the link will be between these two neurons long neuron1ID = -1; long neuron2ID = -1; bool recurrent = false; // a self-connected loop? if (ThreadSafeRandom.NextDouble() < chanceOfLooped) { // try to find(randomly) a neuron to add a self-connected link to while ((numTrysToFindLoop--) > 0) { NEATNeuronGene neuronGene = ChooseRandomNeuron(false); // no self-links on input or bias neurons if (!neuronGene.Recurrent && (neuronGene.NeuronType != NEATNeuronType.Bias) && (neuronGene.NeuronType != NEATNeuronType.Input)) { neuron1ID = neuron2ID = neuronGene.Id; neuronGene.Recurrent = true; recurrent = true; numTrysToFindLoop = 0; } } } else { // try to add a regular link while ((numTrysToAddLink--) > 0) { NEATNeuronGene neuron1 = ChooseRandomNeuron(true); NEATNeuronGene neuron2 = ChooseRandomNeuron(false); if (!IsDuplicateLink(neuron1ID, neuron2ID) && (neuron1.Id != neuron2.Id) && (neuron2.NeuronType != NEATNeuronType.Bias)) { neuron1ID = -1; neuron2ID = -1; break; } } } // did we fail to find a link if ((neuron1ID < 0) || (neuron2ID < 0)) { return; } // check to see if this innovation has already been tried NEATInnovation innovation = ((NEATTraining)GA).Innovations .CheckInnovation(neuron1ID, neuron1ID, NEATInnovationType.NewLink); // see if this is a recurrent(backwards) link NEATNeuronGene neuronGene2 = (NEATNeuronGene)neuronsChromosome.Genes [GetElementPos(neuron1ID)]; if (neuronGene2.SplitY > neuronGene2.SplitY) { recurrent = true; } // is this a new innovation? if (innovation == null) { // new innovation ((NEATTraining)GA).Innovations.CreateNewInnovation(neuron1ID, neuron2ID, NEATInnovationType.NewLink); long id2 = ((NEATTraining)GA).Population.AssignInnovationID(); NEATLinkGene linkGene = new NEATLinkGene(neuron1ID, neuron2ID, true, id2, RangeRandomizer.Randomize(-1, 1), recurrent); linksChromosome.Genes.Add(linkGene); } else { // existing innovation NEATLinkGene linkGene = new NEATLinkGene(neuron1ID, neuron2ID, true, innovation.InnovationID, RangeRandomizer.Randomize(-1, 1), recurrent); linksChromosome.Genes.Add(linkGene); } }
/// <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); }