/// <summary> /// Tell how different are 2 genomes. This is used in speciation. /// </summary> /// <returns>A normalized float.</returns> public static float GeneticDistance(NEATConfig config, Genome genome1, Genome genome2) { var distance = 0f; var comparisonInfo = new GenomeComparisonInfo(genome1, genome2); var genesCountInBiggest = Mathf.Max(genome1.Genes.Count, genome2.Genes.Count); var disjointDist = comparisonInfo.Disjoint.Count * config.speciesCompatibility.disjointImpact; var excessDist = comparisonInfo.Excess.Count * config.speciesCompatibility.excessImpact; distance += disjointDist / genesCountInBiggest; distance += excessDist / genesCountInBiggest; float totalWeightDif = 0; foreach (var gene in comparisonInfo.Matching) { var weight1 = Mathf.Abs(genome1.GetGene(gene).Weight); var weight2 = Mathf.Abs(genome2.GetGene(gene).Weight); var diff = Mathf.Abs(weight1 - weight2) / Mathf.Max(weight1, weight2); totalWeightDif += diff; } distance += totalWeightDif / comparisonInfo.Matching.Count * config.speciesCompatibility.weightMeanDifCoef; distance /= config.speciesCompatibility.TotalImpactSum; return(distance); }
/// <summary> /// Feed inputs into the neural network. /// Starting from the output Neurons, solve all necessary neurons, /// including recurent sequences. /// The input and bias neurons are considered solved. /// </summary> /// <param name="inputs"> The given inner values of the Input layer. </param> public float[] FeedNeuralNetwork(NEATConfig config, float[] inputs) { var inputCount = InputCount; if (inputs.Length != inputCount) { throw new System.Exception(string.Format("Invalid number of inputs, {0} given {1} expected", inputs.Length, inputCount)); } int i = 0; foreach (var inputNeuron in InputNeurons) { inputNeuron.InnerValue = inputs[i++]; } solvedNeurons.Clear(); foreach (var outNeuron in OutputNeurons) { FeedNeuralNetworkRecursively(config.activationFunction, outNeuron, solvedNeurons); } return(CollectNetworkOutputs()); }
/// <summary> /// Groups all the genomes in Species, so that they can /// develop their own structures, without being 'distracted' /// by other local maximums. /// </summary> public void Speciate(NEATConfig config, List <Genome> genomes) { foreach (var genome in genomes) { var compatibleSpecies = GetGenomeSpecies(config, SpeciesList, genome); if (compatibleSpecies == null) { var newSpecies = new Species(); newSpecies.NewGenomes.Add(genome); SpeciesList.Add(newSpecies); } else { compatibleSpecies.NewGenomes.Add(genome); } } foreach (var speciesI in SpeciesList) { speciesI.Genomes = speciesI.NewGenomes; speciesI.NewGenomes = new List <Genome>(); speciesI.Generation++; } EliminateEmptySpecies(); AssignSpeciesToGenomes(SpeciesList); }
public static void MutateConnection(NEATConfig config, Genome genome) { foreach (var gene in genome.Genes) { gene.Mutate(config); } }
/// <summary> /// Mutates the given genome. /// First, try to mutate the structure. Only one /// structural mutation can happen at a time. /// Then, mutate the genome's weights, each /// genome has a chance to be modified. /// </summary> /// <param name="gradient">The mutation chances are multiplied by this gradient.</param> /// <returns>The same genome, but mutated.</returns> public static Genome Mutate(NEATConfig config, Genome genome, float gradient = 1f) { float rand = Random.Range(0, 1f); // Mutate structure. It's a weighted random choice. if (rand <= config.structMutation.connectionAddProb * gradient) { MutateAddGene(config, genome); } else if ((rand -= config.structMutation.connectionAddProb) <= config.structMutation.connectionDelProb * gradient) { MutateDelGene(genome); } else if ((rand -= config.structMutation.connectionDelProb) <= config.structMutation.nodeAddProb * gradient) { MutateAddNode(genome); } else if ((rand -= config.structMutation.nodeAddProb) <= config.structMutation.nodeDelProb * gradient) { MutateDelNode(genome); } // Mutate weight values. foreach (var gene in genome.Genes) { if (Random.Range(0, 1.0f) <= config.weightMutation.prob * gradient) { gene.Mutate(config); } } return(genome); }
public static Gene MutateAddGene(NEATConfig config, Genome genome) { List <Neuron> possibleStartingNodes = new List <Neuron>(genome.Neurons); while (possibleStartingNodes.Count > 0) { Neuron startingNode = possibleStartingNodes[Random.Range(0, possibleStartingNodes.Count)]; var alreadyConnectedNodes = startingNode.OutNeurons; Neuron[] possibleEndNodes = genome.Neurons.Where(x => x.Type != ENeuronType.Input && x.Type != ENeuronType.Bias && !alreadyConnectedNodes.Contains(x) ).ToArray(); if (possibleEndNodes.Length != 0) { Neuron endNeuron = possibleEndNodes[Random.Range(0, possibleEndNodes.Length)]; var newConnection = new Gene( startingNode, endNeuron, (config == null) ? 0 : config.NewRandomWeight()); startingNode.OutGenes.Add(newConnection); endNeuron.InGenes.Add(newConnection); genome.Genes.Add(newConnection); return(newConnection); } else { possibleStartingNodes.Remove(startingNode); } } return(null); }
/// <summary> /// Get N genomes from this species. /// The best genome is simply copied. /// A part of the genomes are soft copied. Soft, means that the mutation /// is less than normal. /// The rest, are a result of a crossover between weighted random chosen /// genomes. The random is weighted, based on the fitness. The parents can't /// be the same. /// </summary> public Genome[] GetNGenomesForNextGeneration(NEATConfig config, int n) { var result = new Genome[n]; Genomes = Genomes.OrderByDescending(x => x.Fitness).ToList(); for (int i = 0; i < n; i++) { if (i == 0) { result[i] = new Genome(Genomes[0]); } else if (i <= config.partOfGenomesToCopyForNextGenerations * n || Genomes.Count == 1) { result[i] = new Genome(FitnessWeightedRandomChoice(Genomes)).Mutate(config, 0.8f); } else { var parent1 = FitnessWeightedRandomChoice(Genomes, config.weightedRandomGradient); var parent2 = FitnessWeightedRandomChoice(Genomes.Where(x => x != parent1), config.weightedRandomGradient); result[i] = Genome.Crossover(config, parent1, parent2).Mutate(config); } } return(result); }
public Genome(NEATConfig config, PackedGenome packedGenome) { SpeciesId = null; Fitness = 0; CreateNetwork(config.inputCount, config.outputCount, packedGenome.genes.Select(x => new Gene(x)) ); }
/// <summary> /// There is a config.WeightMutateNewValProb chance to /// reinitialize the Weight. /// </summary> public void Mutate(NEATConfig config) { if (Random.Range(0, 1.0f) < config.weightMutation.NewValProb) { Weight = config.NewRandomWeight(); } else { Weight += config.GetAWeightMutation(); } }
public Population(int genomeCount, int inCount, int outCount, NEATConfig config) { SpeciesCtrl = new SpeciesControl(); GenomeCount = genomeCount; InputCount = inCount; OutputCount = outCount; Config = config; Genomes = new List <Genome>(GenomeCount); Populate(); }
public static Genome LoadGenome(NEATConfig config, string filePath) { if (!File.Exists(filePath)) { throw new Exception(filePath + " doesn't exist."); } var bf = new BinaryFormatter(); FileStream file = File.Open(filePath, FileMode.Open); var packedGenome = (PackedGenome)bf.Deserialize(file); file.Close(); return(new Genome(config, packedGenome)); }
/// <summary> /// Create a new offspring by combining the 2 parents' genes. /// The Matching genes are already randomly chosen from parent1 or p2. /// </summary> /// <returns>A fresh Genome (not mutated yet).</returns> public static Genome Crossover(NEATConfig config, Genome parent1, Genome parent2) { var fittestParent = (parent1.Fitness > parent2.Fitness) ? parent1 : parent2; var genesToInherit = new List <Gene>(fittestParent.Genes.Count); var comparisonInfo = new GenomeComparisonInfo(parent1, parent2); // The matching genes are chosen randomly from target1 or target2 genesToInherit.AddRange(comparisonInfo.Matching.Select(x => new Gene(x))); // Add the disjoint genes from the fittests. genesToInherit.AddRange(comparisonInfo.Disjoint.Where(x => x.Value == fittestParent) .Select(x => new Gene(x.Key))); // Add the excess genes from the fittests. genesToInherit.AddRange(comparisonInfo.Excess.Where(x => x.Value == fittestParent) .Select(x => new Gene(x.Key))); return(new Genome(fittestParent, genesToInherit)); }
private string _mutationLogs = ""; // Mutation Logs public Evaluator(NEATConfig config, Genome startingGenome, InnovationCounter nodeInnovation, InnovationCounter connectionInnovation) { _config = config; GenerationNumber = 0; _nodeInnovation = nodeInnovation; _connectionInnovation = connectionInnovation; _genomes = new List <Genome>(); for (int i = 0; i < _config.populationSize; ++i) { _genomes.Add(Genome.GenomeRandomWeights(startingGenome, _r)); } _nextEvaluationGenomes = new List <Genome>(); _species = new List <Species>(); _genomesSpecies = new Dictionary <Genome, Species>(); BestFitness = -1f; BestGenome = null; }
/// <summary> /// Determines the species in which the given genome is located. /// Two genomes are in the same species if their genetic distance /// is less than the given compatibility treshold. /// </summary> private Species GetGenomeSpecies(NEATConfig config, List <Species> species, Genome genome) { if (species == null) { return(null); } foreach (var speciesI in species) { if (speciesI.Genomes.Count == 0) { continue; } var threshold = config.speciesCompatibility.threshold; var geneticDist = Genome.GeneticDistance(config, genome, speciesI.Representative); if (geneticDist <= threshold) { return(speciesI); } } return(null); }
public Genome Mutate(NEATConfig config, float gradient = 1f) { return(Mutator.Mutate(config, this, gradient)); }
public bool DidntImproveInLastGenerations(NEATConfig config) { return(GenerationsSinceFitnesImprovement >= config.maxStagnation); }