/// <summary> /// Handles iterating a generation of the population. /// All evaluations must have been done by this point. /// </summary> /// <param name="rando">A random number generator.</param> public void Epoch(Random rando) { foreach (var spec in Species) { spec.SetChampions(); } SortSpeciesByChampionFitness(); Genome generationChampion = Species.Last().Fitness.Champion; if (generationChampion.Fitness.Score >= Fitness.ChampionScore) { GenerationPrime = Generation; } Fitness.GenerationalChampion = generationChampion; Dictionary <Species, int> ExpectedChildren = FindExpectedChildren(); foreach (var spec in Species) { int numberOfParents = (int)Math.Floor(spec.Size() * Const.SurvivalThresh + 1); spec.SortGenomesByFitness(); spec.Genomes.RemoveRange(0, spec.Size() - numberOfParents); } List <Genome> nextGenerationGenomes = new List <Genome>(); foreach (var specCountPair in ExpectedChildren) { nextGenerationGenomes.AddRange(specCountPair.Key.Reproduce(specCountPair.Value, GenerationalInnovations, Species, rando)); } ClearParentGeneration(); SpeciateNewGeneration(nextGenerationGenomes); RemoveDeadSpecies(); ResetSpeciesTemplates(); AgeSpecies(); Generation++; HistoricalInnovations.AddRange(GenerationalInnovations); GenerationalInnovations.Clear(); }
/// <summary> /// Finds the expected children of each species. /// </summary> /// <returns>A dictionary of each species that will definitely have children.</returns> private Dictionary <Species, int> FindExpectedChildren() { Dictionary <Species, int> expectedChildren = new Dictionary <Species, int>(); Species mostExpectingSpecies = null; int totalChildren = 0; //Original NEAT calls this delta-coding. //Original NEAT does not check this first. It overrides the 'else' statement logic. Seemed unneccessary. if (Generation - GenerationPrime >= Const.PopulationDropoffGenerations) { GenerationPrime = Generation; int halfPopulationSize = TargetPopulationSize / 2; if (Species.Count == 1) { expectedChildren[Species.Last()] = TargetPopulationSize; totalChildren = TargetPopulationSize; Species.Last().AgePrime = Species.Last().Age; mostExpectingSpecies = Species.Last(); } else { expectedChildren[Species.Last()] = halfPopulationSize; Species.Last().AgePrime = Species.Last().Age; mostExpectingSpecies = Species.Last(); expectedChildren[Species[Species.Count - 1]] = halfPopulationSize; Species[Species.Count - 1].AgePrime = Species[Species.Count - 1].Age; totalChildren = 2 * halfPopulationSize; } } else { if (Generation % Const.SpeciesStagnationEpochCycle == 0) { foreach (var spec in Species) { if (spec.Age > Const.OverTheHillThresh) { spec.OnDeathBed = true; break; } } } double sumOfTotalAdjustedScore = 0.0; foreach (var spec in Species) { spec.AdjustFitness(); sumOfTotalAdjustedScore += spec.Fitness.TotalAdjustedScore; } int mostExpected = 0; foreach (var spec in Species) { int children = 0; //Warning: There is no check in the original NEAT that handles this case. if (sumOfTotalAdjustedScore != 0) { children = (int)(spec.Fitness.TotalAdjustedScore * TargetPopulationSize / sumOfTotalAdjustedScore); } if (children > 0) { expectedChildren.Add(spec, children); totalChildren += children; } if (children > mostExpected) { mostExpectingSpecies = spec; mostExpected = children; } } } if (totalChildren > 0) { expectedChildren[mostExpectingSpecies] += TargetPopulationSize - totalChildren; } else { expectedChildren.Add(Species.Last(), TargetPopulationSize); } return(expectedChildren); }