/// <summary> /// Main constructor for evolution. Requires a configuration instance /// </summary> /// <param name="config"></param> public Evolution(EvolutionConfig config) { // Setup new lists genomes = new List<Genome>(); species = new List<Species>(); // Reset basic variables evaluationIndex = 0; speciesFitnessSum = 0; generationNumber = 0; topScore = 0; // Set the configuration evoConfig = config; // Create new random genomes for (int i = 0; i < config.PopulationSize; i++) genomes.Add(new Genome(config)); // Mutate each genenome, add add them to a species foreach (Genome g in genomes) { g.Mutate(); AddGenomeToSpecies(g); } // Setup netwrok evaluationNetwork = new NeuralNetwork(genomes[0]); }
/// <summary> /// After the network has been used, we need to evaluate the network, assign a fitness score and move on. /// </summary> /// <param name="fitnessScore"></param> public void EvaluateNetwork(double fitnessScore) { // Update the fitness of this genome genomes[evaluationIndex++].Fitness = fitnessScore; // When the last genome was evaluated, setup the next generation if (evaluationIndex >= genomes.Count()) { NextGeneration(); onGenerationEnded(EventArgs.Empty); } else // Setup next network evaluationNetwork = new NeuralNetwork(genomes[evaluationIndex]); }
/// <summary> /// This constructor creates a new generation from a previous generation /// </summary> /// <param name="evoConfig"></param> /// <param name="previousGeneration"></param> public void NextGeneration() { // Clear the list of genomes (we want to update them) genomes.Clear(); List<Species> nextSpecies = new List<Species>(species); foreach (Species s in species) { // 'Prune' the species s.KeepTopHalf(); // Remove old 'dead' species that have not made any progress in a while s.UpdateStaleness(); if (s.Staleness >= evoConfig.StaleSpecies && s.TopFitness < species.Max().TopFitness) nextSpecies.Remove(s); } // Update species species = nextSpecies; nextSpecies = new List<Species>(species); UpdateSpeciesFitnessSum(); // Remove species that are not going to be bred because they have low fitness sums foreach (Species s in species) { if (evoConfig.PopulationSize * s.AverageFitness / speciesFitnessSum < 1 && s.TopFitness < species.Max().TopFitness) nextSpecies.Remove(s); } // Update species species = nextSpecies; UpdateSpeciesFitnessSum(); // Breed new children List<Genome> children = new List<Genome>(); foreach (Species s in species) { // Estimate how many children should be bred per species int breedCount = (int) (evoConfig.PopulationSize * s.AverageFitness / speciesFitnessSum); // Make new children for (int i = 0; i < breedCount; i++) children.Add(Genome.BreedChild(evoConfig, s)); // Keep the best from each species s.KeepTop(1); // Add them to the list of genomes foreach(Genome g in s.genomes) genomes.Add(g); } // Find the top score topScore = genomes.Max(g => g.Fitness); // Since we only estimated how many children should be bred per species, we can't be sure we actually fit the number required (decimals) // So we need to add more children by randomly selecting species and breeding them again while (children.Count < evoConfig.PopulationSize) children.Add(Genome.BreedChild(evoConfig, species.ElementAt( evoConfig.rng.Next(species.Count())))); // Assign each child a species (most likely within its same species, but sometimes they get adopted) foreach (Genome g in children) { AddGenomeToSpecies(g); // Add to global genome list genomes.Add(g); } // Reset evaluation index; evaluationIndex = 0; // Setup netwrok evaluationNetwork = new NeuralNetwork(genomes[0]); // Increment generation counter generationNumber++; }