/// <summary> /// Determines if a neuron is still needed. If all links to/from a neuron /// have been removed, then the neuron is no longer needed. /// </summary> /// <param name="target">The target genome.</param> /// <param name="neuronID">The neuron id to check for.</param> /// <returns>Returns true, if the neuron is still needed.</returns> public bool IsNeuronNeeded(NEATGenome target, long neuronID) { // do not remove bias or input neurons or output foreach (NEATNeuronGene gene in target.NeuronsChromosome) { if (gene.Id == neuronID) { NEATNeuronGene neuron = gene; if ((neuron.NeuronType == NEATNeuronType.Input) || (neuron.NeuronType == NEATNeuronType.Bias) || (neuron.NeuronType == NEATNeuronType.Output)) { return(true); } } } // Now check to see if the neuron is used in any links foreach (NEATLinkGene gene in target.LinksChromosome) { NEATLinkGene linkGene = gene; if (linkGene.FromNeuronId == neuronID) { return(true); } if (linkGene.ToNeuronId == neuronID) { return(true); } } return(false); }
/// <inheritdoc/> public override void PerformOperation(EncogRandom rnd, IGenome[] parents, int parentIndex, IGenome[] offspring, int offspringIndex) { var target = ObtainGenome(parents, parentIndex, offspring, offspringIndex); if (target.LinksChromosome.Count < MinLink) { // don't remove from small genomes return; } // determine the target and remove var index = RangeRandomizer.RandomInt(0, target .LinksChromosome.Count - 1); NEATLinkGene targetGene = target.LinksChromosome[index]; target.LinksChromosome.Remove(targetGene); // if this orphaned any nodes, then kill them too! if (!IsNeuronNeeded(target, targetGene.FromNeuronId)) { RemoveNeuron(target, targetGene.FromNeuronId); } if (!IsNeuronNeeded(target, targetGene.ToNeuronId)) { RemoveNeuron(target, targetGene.ToNeuronId); } }
/// <summary> /// Create a link between two neuron id's. Create or find any necessary /// innovation records. /// </summary> /// <param name="target">The target genome.</param> /// <param name="neuron1Id">The id of the source neuron.</param> /// <param name="neuron2Id">The id of the target neuron.</param> /// <param name="weight">The weight of this new link.</param> public void CreateLink(NEATGenome target, long neuron1Id, long neuron2Id, double weight) { // first, does this link exist? (and if so, hopefully disabled, // otherwise we have a problem) foreach (NEATLinkGene linkGene in target.LinksChromosome) { if ((linkGene.FromNeuronId == neuron1Id) && (linkGene.ToNeuronId == neuron2Id)) { // bring the link back, at the new weight linkGene.Enabled = true; linkGene.Weight = weight; return; } } // check to see if this innovation has already been tried NEATInnovation innovation = ((NEATPopulation)target .Population).Innovations.FindInnovation(neuron1Id, neuron2Id); // now create this link var lg = new NEATLinkGene(neuron1Id, neuron2Id, true, innovation.InnovationId, weight); target.LinksChromosome.Add(lg); }
/// <inheritdoc/> public void MutateWeight(EncogRandom rnd, NEATLinkGene linkGene, double weightRange) { double delta = rnd.NextGaussian() * _sigma; double w = linkGene.Weight + delta; w = NEATPopulation.ClampWeight(w, weightRange); linkGene.Weight = w; }
/// <inheritdoc/> public IMLMethod Decode(IGenome genome) { var neatGenome = (NEATGenome)genome; var pop = (NEATPopulation)neatGenome.Population; IList <NEATNeuronGene> neuronsChromosome = neatGenome.NeuronsChromosome; IList <NEATLinkGene> linksChromosome = neatGenome.LinksChromosome; if (neuronsChromosome[0].NeuronType != NEATNeuronType.Bias) { throw new NeuralNetworkError( "The first neuron must be the bias neuron, this genome is invalid."); } var links = new List <NEATLink>(); var afs = new IActivationFunction[neuronsChromosome.Count]; for (int i = 0; i < afs.Length; i++) { afs[i] = neuronsChromosome[i].ActivationFunction; } IDictionary <long, int> lookup = new Dictionary <long, int>(); for (int i = 0; i < neuronsChromosome.Count; i++) { NEATNeuronGene neuronGene = neuronsChromosome[i]; lookup[neuronGene.Id] = i; } // loop over connections for (int i = 0; i < linksChromosome.Count; i++) { NEATLinkGene linkGene = linksChromosome[i]; if (linkGene.Enabled) { links.Add(new NEATLink(lookup[linkGene.FromNeuronId], lookup[linkGene.ToNeuronId], linkGene.Weight)); } } links.Sort(); NEATNetwork network = new NEATNetwork(neatGenome.InputCount, neatGenome.OutputCount, links, afs); network.ActivationCycles = pop.ActivationCycles; return(network); }
/// <inheritdoc/> public IList <NEATLinkGene> SelectLinks(EncogRandom rnd, NEATGenome genome) { IList <NEATLinkGene> result = new List <NEATLinkGene>(); int cnt = Math.Min(_linkCount, genome.LinksChromosome.Count); while (result.Count < cnt) { int idx = rnd.Next(genome.LinksChromosome.Count); NEATLinkGene link = genome.LinksChromosome[idx]; if (!result.Contains(link)) { result.Add(link); } } return(result); }
/// <inheritdoc/> public IList <NEATLinkGene> SelectLinks(EncogRandom rnd, NEATGenome genome) { IList <NEATLinkGene> result = new List <NEATLinkGene>(); bool mutated = false; foreach (var linkGene in genome.LinksChromosome) { if (rnd.NextDouble() < _proportion) { mutated = true; result.Add(linkGene); } } if (!mutated) { int idx = rnd.Next(genome.LinksChromosome.Count); NEATLinkGene linkGene = genome.LinksChromosome[idx]; result.Add(linkGene); } return(result); }
/// <inheritdoc/> public override void PerformOperation(EncogRandom rnd, IGenome[] parents, int parentIndex, IGenome[] offspring, int offspringIndex) { var target = ObtainGenome(parents, parentIndex, offspring, offspringIndex); var countTrysToFindOldLink = Owner.MaxTries; var pop = ((NEATPopulation)target.Population); // the link to split NEATLinkGene splitLink = null; int sizeBias = ((NEATGenome)parents[0]).InputCount + ((NEATGenome)parents[0]).OutputCount + 10; // if there are not at least int upperLimit; if (target.LinksChromosome.Count < sizeBias) { upperLimit = target.NumGenes - 1 - (int)Math.Sqrt(target.NumGenes); } else { upperLimit = target.NumGenes - 1; } while ((countTrysToFindOldLink--) > 0) { // choose a link, use the square root to prefer the older links int i = RangeRandomizer.RandomInt(0, upperLimit); NEATLinkGene link = target.LinksChromosome[i]; // get the from neuron long fromNeuron = link.FromNeuronId; if ((link.Enabled) && (target.NeuronsChromosome [GetElementPos(target, fromNeuron)] .NeuronType != NEATNeuronType.Bias)) { splitLink = link; break; } } if (splitLink == null) { return; } splitLink.Enabled = false; long from = splitLink.FromNeuronId; long to = splitLink.ToNeuronId; NEATInnovation innovation = ((NEATPopulation)Owner.Population).Innovations .FindInnovationSplit(from, to); // add the splitting neuron IActivationFunction af = ((NEATPopulation)Owner.Population).ActivationFunctions.Pick(new Random()); target.NeuronsChromosome.Add( new NEATNeuronGene(NEATNeuronType.Hidden, af, innovation .NeuronId, innovation.InnovationId)); // add the other two sides of the link CreateLink(target, from, innovation.NeuronId, splitLink.Weight); CreateLink(target, innovation.NeuronId, to, pop.WeightRange); target.SortGenes(); }
/// <summary> /// Read the object. /// </summary> /// <param name="mask0">The stream to read the object from.</param> /// <returns>The object that was loaded.</returns> public virtual Object Read(Stream mask0) { var result = new NEATPopulation(); var innovationList = new NEATInnovationList { Population = result }; result.Innovations = innovationList; var ins0 = new EncogReadHelper(mask0); IDictionary <Int32, ISpecies> speciesMap = new Dictionary <Int32, ISpecies>(); IDictionary <ISpecies, Int32> leaderMap = new Dictionary <ISpecies, Int32>(); IDictionary <Int32, IGenome> genomeMap = new Dictionary <Int32, IGenome>(); EncogFileSection section; while ((section = ins0.ReadNextSection()) != null) { if (section.SectionName.Equals("NEAT-POPULATION") && section.SubSectionName.Equals("INNOVATIONS")) { foreach (String line in section.Lines) { IList <String> cols = EncogFileSection.SplitColumns(line); var innovation = new NEATInnovation { InnovationID = Int32.Parse(cols[0]), InnovationType = StringToInnovationType(cols[1]), NeuronType = StringToNeuronType(cols[2]), SplitX = CSVFormat.EgFormat.Parse(cols[3]), SplitY = CSVFormat.EgFormat.Parse(cols[4]), NeuronID = Int32.Parse(cols[5]), FromNeuronID = Int32.Parse(cols[6]), ToNeuronID = Int32.Parse(cols[7]) }; result.Innovations.Add(innovation); } } else if (section.SectionName.Equals("NEAT-POPULATION") && section.SubSectionName.Equals("SPECIES")) { foreach (String line in section.Lines) { String[] cols = line.Split(','); var species = new BasicSpecies { SpeciesID = Int32.Parse(cols[0]), Age = Int32.Parse(cols[1]), BestScore = CSVFormat.EgFormat.Parse(cols[2]), GensNoImprovement = Int32.Parse(cols[3]), SpawnsRequired = CSVFormat.EgFormat .Parse(cols[4]) }; species.SpawnsRequired = CSVFormat.EgFormat .Parse(cols[5]); leaderMap[(species)] = (Int32.Parse(cols[6])); result.Species.Add(species); speciesMap[((int)species.SpeciesID)] = (species); } } else if (section.SectionName.Equals("NEAT-POPULATION") && section.SubSectionName.Equals("GENOMES")) { NEATGenome lastGenome = null; foreach (String line in section.Lines) { IList <String> cols = EncogFileSection.SplitColumns(line); if (cols[0].Equals("g", StringComparison.InvariantCultureIgnoreCase)) { lastGenome = new NEATGenome { NeuronsChromosome = new Chromosome(), LinksChromosome = new Chromosome() }; lastGenome.Chromosomes.Add(lastGenome.NeuronsChromosome); lastGenome.Chromosomes.Add(lastGenome.LinksChromosome); lastGenome.GenomeID = Int32.Parse(cols[1]); lastGenome.SpeciesID = Int32.Parse(cols[2]); lastGenome.AdjustedScore = CSVFormat.EgFormat .Parse(cols[3]); lastGenome.AmountToSpawn = CSVFormat.EgFormat .Parse(cols[4]); lastGenome.NetworkDepth = Int32.Parse(cols[5]); lastGenome.Score = CSVFormat.EgFormat.Parse(cols[6]); result.Add(lastGenome); genomeMap[(int)lastGenome.GenomeID] = lastGenome; } else if (cols[0].Equals("n", StringComparison.InvariantCultureIgnoreCase)) { var neuronGene = new NEATNeuronGene { Id = Int32.Parse(cols[1]), NeuronType = StringToNeuronType(cols[2]), Enabled = Int32.Parse(cols[3]) > 0, InnovationId = Int32.Parse(cols[4]), ActivationResponse = CSVFormat.EgFormat .Parse(cols[5]), SplitX = CSVFormat.EgFormat.Parse(cols[6]), SplitY = CSVFormat.EgFormat.Parse(cols[7]) }; lastGenome.Neurons.Add(neuronGene); } else if (cols[0].Equals("l", StringComparison.InvariantCultureIgnoreCase)) { var linkGene = new NEATLinkGene(); linkGene.Id = Int32.Parse(cols[1]); linkGene.Enabled = Int32.Parse(cols[2]) > 0; linkGene.Recurrent = Int32.Parse(cols[3]) > 0; linkGene.FromNeuronID = Int32.Parse(cols[4]); linkGene.ToNeuronID = Int32.Parse(cols[5]); linkGene.Weight = CSVFormat.EgFormat.Parse(cols[6]); linkGene.InnovationId = Int32.Parse(cols[7]); lastGenome.Links.Add(linkGene); } } } else if (section.SectionName.Equals("NEAT-POPULATION") && section.SubSectionName.Equals("CONFIG")) { IDictionary <String, String> paras = section.ParseParams(); result.NeatActivationFunction = EncogFileSection .ParseActivationFunction(paras, NEATPopulation.PropertyNEATActivation); result.OutputActivationFunction = EncogFileSection .ParseActivationFunction(paras, NEATPopulation.PropertyOutputActivation); result.Snapshot = EncogFileSection.ParseBoolean(paras, PersistConst.Snapshot); result.InputCount = EncogFileSection.ParseInt(paras, PersistConst.InputCount); result.OutputCount = EncogFileSection.ParseInt(paras, PersistConst.OutputCount); result.OldAgePenalty = EncogFileSection.ParseDouble(paras, PopulationConst.PropertyOldAgePenalty); result.OldAgeThreshold = EncogFileSection.ParseInt(paras, PopulationConst.PropertyOldAgeThreshold); result.PopulationSize = EncogFileSection.ParseInt(paras, PopulationConst.PropertyPopulationSize); result.SurvivalRate = EncogFileSection.ParseDouble(paras, PopulationConst.PropertySurvivalRate); result.YoungBonusAgeThreshhold = EncogFileSection.ParseInt( paras, PopulationConst.PropertyYoungAgeThreshold); result.YoungScoreBonus = EncogFileSection.ParseDouble(paras, PopulationConst.PropertyYoungAgeBonus); result.GenomeIDGenerate.CurrentID = EncogFileSection.ParseInt(paras, PopulationConst. PropertyNextGenomeID); result.InnovationIDGenerate.CurrentID = EncogFileSection.ParseInt(paras, PopulationConst. PropertyNextInnovationID); result.GeneIDGenerate.CurrentID = EncogFileSection.ParseInt(paras, PopulationConst. PropertyNextGeneID); result.SpeciesIDGenerate.CurrentID = EncogFileSection.ParseInt(paras, PopulationConst. PropertyNextSpeciesID); } } // now link everything up // first put all the genomes into correct species foreach (IGenome genome in result.Genomes) { var neatGenome = (NEATGenome)genome; var speciesId = (int)neatGenome.SpeciesID; if (speciesMap.ContainsKey(speciesId)) { ISpecies s = speciesMap[speciesId]; s.Members.Add(neatGenome); } neatGenome.InputCount = result.InputCount; neatGenome.OutputCount = result.OutputCount; } // set the species leader links foreach (ISpecies species in leaderMap.Keys) { int leaderID = leaderMap[species]; IGenome leader = genomeMap[leaderID]; species.Leader = leader; ((BasicSpecies)species).Population = result; } return(result); }
/// <inheritdoc/> public void MutateWeight(EncogRandom rnd, NEATLinkGene linkGene, double weightRange) { linkGene.Weight = RangeRandomizer.Randomize(rnd, -weightRange, weightRange); }
/// <inheritdoc/> public Object Read(Stream istream) { long nextInnovationId = 0; long nextGeneId = 0; var result = new NEATPopulation(); var innovationList = new NEATInnovationList { Population = result }; result.Innovations = innovationList; var reader = new EncogReadHelper(istream); EncogFileSection section; while ((section = reader.ReadNextSection()) != null) { if (section.SectionName.Equals("NEAT-POPULATION") && section.SubSectionName.Equals("INNOVATIONS")) { foreach (String line in section.Lines) { IList <String> cols = EncogFileSection .SplitColumns(line); var innovation = new NEATInnovation(); var innovationId = int.Parse(cols[1]); innovation.InnovationId = innovationId; innovation.NeuronId = int.Parse(cols[2]); result.Innovations.Innovations[cols[0]] = innovation; nextInnovationId = Math.Max(nextInnovationId, innovationId + 1); } } else if (section.SectionName.Equals("NEAT-POPULATION") && section.SubSectionName.Equals("SPECIES")) { NEATGenome lastGenome = null; BasicSpecies lastSpecies = null; foreach (String line in section.Lines) { IList <String> cols = EncogFileSection.SplitColumns(line); if (String.Compare(cols[0], "s", StringComparison.OrdinalIgnoreCase) == 0) { lastSpecies = new BasicSpecies { Population = result, Age = int.Parse(cols[1]), BestScore = CSVFormat.EgFormat.Parse(cols[2]), GensNoImprovement = int.Parse(cols[3]) }; result.Species.Add(lastSpecies); } else if (String.Compare(cols[0], "g", StringComparison.OrdinalIgnoreCase) == 0) { bool isLeader = lastGenome == null; lastGenome = new NEATGenome { InputCount = result.InputCount, OutputCount = result.OutputCount, Species = lastSpecies, AdjustedScore = CSVFormat.EgFormat.Parse(cols[1]), Score = CSVFormat.EgFormat.Parse(cols[2]), BirthGeneration = int.Parse(cols[3]) }; lastSpecies.Add(lastGenome); if (isLeader) { lastSpecies.Leader = lastGenome; } } else if (String.Compare(cols[0], "n", StringComparison.OrdinalIgnoreCase) == 0) { var neuronGene = new NEATNeuronGene(); int geneId = int.Parse(cols[1]); neuronGene.Id = geneId; IActivationFunction af = EncogFileSection.ParseActivationFunction(cols[2]); neuronGene.ActivationFunction = af; neuronGene.NeuronType = PersistNEATPopulation.StringToNeuronType(cols[3]); neuronGene.InnovationId = int.Parse(cols[4]); lastGenome.NeuronsChromosome.Add(neuronGene); nextGeneId = Math.Max(geneId + 1, nextGeneId); } else if (String.Compare(cols[0], "l", StringComparison.OrdinalIgnoreCase) == 0) { var linkGene = new NEATLinkGene { Id = int.Parse(cols[1]), Enabled = (int.Parse(cols[2]) > 0), FromNeuronId = int.Parse(cols[3]), ToNeuronId = int.Parse(cols[4]), Weight = CSVFormat.EgFormat.Parse(cols[5]), InnovationId = int.Parse(cols[6]) }; lastGenome.LinksChromosome.Add(linkGene); } } } else if (section.SectionName.Equals("NEAT-POPULATION") && section.SubSectionName.Equals("CONFIG")) { IDictionary <string, string> prm = section.ParseParams(); string afStr = prm[NEATPopulation.PropertyNEATActivation]; if (String.Compare(afStr, TypeCppn, StringComparison.OrdinalIgnoreCase) == 0) { HyperNEATGenome.BuildCPPNActivationFunctions(result.ActivationFunctions); } else { result.NEATActivationFunction = EncogFileSection.ParseActivationFunction(prm, NEATPopulation.PropertyNEATActivation); } result.ActivationCycles = EncogFileSection.ParseInt(prm, PersistConst.ActivationCycles); result.InputCount = EncogFileSection.ParseInt(prm, PersistConst.InputCount); result.OutputCount = EncogFileSection.ParseInt(prm, PersistConst.OutputCount); result.PopulationSize = EncogFileSection.ParseInt(prm, NEATPopulation.PropertyPopulationSize); result.SurvivalRate = EncogFileSection.ParseDouble(prm, NEATPopulation.PropertySurvivalRate); result.ActivationCycles = EncogFileSection.ParseInt(prm, NEATPopulation.PropertyCycles); } } // set factories if (result.IsHyperNEAT) { result.GenomeFactory = new FactorHyperNEATGenome(); result.CODEC = new HyperNEATCODEC(); } else { result.GenomeFactory = new FactorNEATGenome(); result.CODEC = new NEATCODEC(); } // set the next ID's result.InnovationIDGenerate.CurrentID = nextInnovationId; result.GeneIdGenerate.CurrentID = nextGeneId; // find first genome, which should be the best genome if (result.Species.Count > 0) { ISpecies species = result.Species[0]; if (species.Members.Count > 0) { result.BestGenome = species.Members[0]; } } return(result); }
/// <inheritdoc/> public void PerformOperation(EncogRandom rnd, IGenome[] parents, int parentIndex, IGenome[] offspring, int offspringIndex) { var mom = (NEATGenome)parents[parentIndex + 0]; var dad = (NEATGenome)parents[parentIndex + 1]; var best = FavorParent(rnd, mom, dad); var notBest = (best == mom) ? mom : dad; var selectedLinks = new List <NEATLinkGene>(); var selectedNeurons = new List <NEATNeuronGene>(); int curMom = 0; // current gene index from mom int curDad = 0; // current gene index from dad NEATLinkGene selectedGene = null; // add in the input and bias, they should always be here int alwaysCount = ((NEATGenome)parents[0]).InputCount + ((NEATGenome)parents[0]).OutputCount + 1; for (int i = 0; i < alwaysCount; i++) { AddNeuronId(i, selectedNeurons, best, notBest); } while ((curMom < mom.NumGenes) || (curDad < dad.NumGenes)) { NEATLinkGene momGene = null; // the mom gene object NEATLinkGene dadGene = null; // the dad gene object long momInnovation = -1; long dadInnovation = -1; // grab the actual objects from mom and dad for the specified // indexes // if there are none, then null if (curMom < mom.NumGenes) { momGene = mom.LinksChromosome[curMom]; momInnovation = momGene.InnovationId; } if (curDad < dad.NumGenes) { dadGene = dad.LinksChromosome[curDad]; dadInnovation = dadGene.InnovationId; } // now select a gene for mom or dad. This gene is for the baby if ((momGene == null) && (dadGene != null)) { if (best == dad) { selectedGene = dadGene; } curDad++; } else if ((dadGene == null) && (momGene != null)) { if (best == mom) { selectedGene = momGene; } curMom++; } else if (momInnovation < dadInnovation) { if (best == mom) { selectedGene = momGene; } curMom++; } else if (dadInnovation < momInnovation) { if (best == dad) { selectedGene = dadGene; } curDad++; } else if (dadInnovation == momInnovation) { selectedGene = rnd.NextDouble() < 0.5f ? momGene : dadGene; curMom++; curDad++; } if (selectedGene != null) { if (selectedLinks.Count == 0) { selectedLinks.Add(selectedGene); } else { if (selectedLinks[selectedLinks.Count - 1] .InnovationId != selectedGene .InnovationId) { selectedLinks.Add(selectedGene); } } // Check if we already have the nodes referred to in // SelectedGene. // If not, they need to be added. AddNeuronId(selectedGene.FromNeuronId, selectedNeurons, best, notBest); AddNeuronId(selectedGene.ToNeuronId, selectedNeurons, best, notBest); } } // now create the required nodes. First sort them into order selectedNeurons.Sort(); // finally, create the genome var factory = (INEATGenomeFactory)_owner .Population.GenomeFactory; var babyGenome = factory.Factor(selectedNeurons, selectedLinks, mom.InputCount, mom.OutputCount); babyGenome.BirthGeneration = _owner.IterationNumber; babyGenome.Population = _owner.Population; babyGenome.SortGenes(); offspring[offspringIndex] = babyGenome; }