/// <summary> /// Copy another gene to this one. /// </summary> /// <param name="gene">The other gene.</param> public override void Copy(IGene gene) { NEATNeuronGene other = (NEATNeuronGene)gene; activationResponse = other.activationResponse; Id = other.Id; neuronType = other.neuronType; recurrent = other.recurrent; splitX = other.splitX; splitY = other.splitY; }
/// <summary> /// Get the specified neuron's index. /// </summary> /// <param name="neuronID">The neuron id to check for.</param> /// <returns>The index.</returns> private int GetElementPos(long neuronID) { for (int i = 0; i < Neurons.Genes.Count; i++) { NEATNeuronGene neuronGene = (NEATNeuronGene)neuronsChromosome.Genes[i]; if (neuronGene.Id == neuronID) { return(i); } } return(-1); }
/// <summary> /// Construct an innovation. /// </summary> /// <param name="neuronGene">The neuron gene.</param> /// <param name="innovationID">The innovation id.</param> /// <param name="neuronID">The neuron id.</param> public NEATInnovation(NEATNeuronGene neuronGene, long innovationID, long neuronID) { this.neuronID = neuronID; InnovationID = innovationID; splitX = neuronGene.SplitX; splitY = neuronGene.SplitY; neuronType = neuronGene.NeuronType; innovationType = NEATInnovationType.NewNeuron; fromNeuronID = -1; toNeuronID = -1; }
/// <summary> /// Mutate the activation response. /// </summary> /// <param name="mutateRate">The mutation rate.</param> /// <param name="maxPertubation">The maximum to perturb it by.</param> public void MutateActivationResponse(double mutateRate, double maxPertubation) { foreach (IGene gene in neuronsChromosome.Genes) { if (ThreadSafeRandom.NextDouble() < mutateRate) { NEATNeuronGene neuronGene = (NEATNeuronGene)gene; neuronGene.ActivationResponse = neuronGene .ActivationResponse + RangeRandomizer.Randomize(-1, 1) * maxPertubation; } } }
/// <summary> /// Do we already have this neuron id? /// </summary> /// <param name="id">The id to check for.</param> /// <returns>True if we already have this neuron id.</returns> public bool AlreadyHaveThisNeuronID(long id) { foreach (IGene gene in neuronsChromosome.Genes) { NEATNeuronGene neuronGene = (NEATNeuronGene)gene; if (neuronGene.Id == id) { 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> /// Choose a random neuron. /// </summary> /// <param name="includeInput">Should the input neurons be included.</param> /// <returns>The random neuron.</returns> private NEATNeuronGene ChooseRandomNeuron(bool includeInput) { int start; if (includeInput) { start = 0; } else { start = inputCount + 1; } int neuronPos = RangeRandomizer.RandomInt(start, Neurons .Genes.Count - 1); NEATNeuronGene neuronGene = (NEATNeuronGene)neuronsChromosome.Genes[neuronPos]; return(neuronGene); }
/// <summary> /// Create a new neuron gene from an id. /// </summary> /// <param name="neuronID">The neuron id.</param> /// <returns>The neuron gene.</returns> public NEATNeuronGene CreateNeuronFromID(long neuronID) { NEATNeuronGene result = new NEATNeuronGene(NEATNeuronType.Hidden, 0, 0, 0); foreach (IInnovation i in Innovations) { NEATInnovation innovation = (NEATInnovation)i; if (innovation.NeuronID == neuronID) { result.NeuronType = innovation.NeuronType; result.Id = innovation.NeuronID; result.SplitY = innovation.SplitY; result.SplitX = innovation.SplitX; return(result); } } return(result); }
/// <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> /// 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> /// Create a new neuron gene from an id. /// </summary> /// <param name="neuronID">The neuron id.</param> /// <returns>The neuron gene.</returns> public NEATNeuronGene CreateNeuronFromID(long neuronID) { NEATNeuronGene result = new NEATNeuronGene(NEATNeuronType.Hidden, 0, 0, 0); foreach (IInnovation i in Innovations) { NEATInnovation innovation = (NEATInnovation)i; if (innovation.NeuronID == neuronID) { result.NeuronType = innovation.NeuronType; result.Id = innovation.NeuronID; result.SplitY = innovation.SplitY; result.SplitX = innovation.SplitX; return result; } } return result; }
/// <summary> /// Construct an innovation. /// </summary> /// <param name="neuronGene">The neuron gene.</param> /// <param name="innovationID">The innovation id.</param> /// <param name="neuronID">The neuron id.</param> public NEATInnovation(NEATNeuronGene neuronGene, long innovationID, long neuronID) { this.neuronID = neuronID; InnovationID = innovationID; splitX = neuronGene.SplitX; splitY = neuronGene.SplitY; neuronType = neuronGene.NeuronType; innovationType = NEATInnovationType.NewNeuron; fromNeuronID = -1; toNeuronID = -1; }