/// <summary> /// Handle specific case where all genomes/species have a zero fitness. /// </summary> private static int CalcSpeciesTargetSizesInner_ZeroTotalMeanFitness(NeatPopulation <T> pop, IRandomSource rng) { // Assign all species an equal targetSize. Species <T>[] speciesArr = pop.SpeciesArray; double popSizeReal = pop.GenomeList.Count; double targetSizeReal = popSizeReal / speciesArr.Length; // Keep a total of all allocated target sizes, typically this will vary slightly from the // required target population size due to rounding of each real valued target size. int totalTargetSizeInt = 0; for (int i = 0; i < speciesArr.Length; i++) { SpeciesStats stats = speciesArr[i].Stats; stats.TargetSizeReal = targetSizeReal; // Stochastic rounding will result in equal allocation if targetSizeReal is a whole // number, otherwise it will help to distribute allocations fairly. stats.TargetSizeInt = (int)NumericsUtils.ProbabilisticRound(targetSizeReal, rng); // Total up discretized target sizes. totalTargetSizeInt += stats.TargetSizeInt; } return(totalTargetSizeInt); }
private static void AdjustSpeciesTargetSizes( NeatPopulation <T> pop, int totalTargetSizeInt, IRandomSource rng) { // Discretized target sizes may total up to a value that is not equal to the required population size. // Here we check this and if the total does not match the required population size then we adjust the // species' targetSizeInt values to compensate for the difference. int targetSizeDeltaInt = totalTargetSizeInt - pop.PopulationSize; if (targetSizeDeltaInt < 0) { // Target size is too low; adjust up. AdjustSpeciesTargetSizesUp(pop.SpeciesArray, targetSizeDeltaInt, rng); } else if (targetSizeDeltaInt > 0) { // Target size is too high; adjust down. AdjustSpeciesTargetSizesDown(pop.SpeciesArray, targetSizeDeltaInt, rng); } // Ensure a non-zero target size for the species that contains the best genome. AdjustSpeciesTargetSizes_AccommodateBestGenomeSpecies(pop, rng); // Assert that Sum(TargetSizeInt) == popSize. Debug.Assert(pop.SpeciesArray.Sum(x => x.Stats.TargetSizeInt) == pop.PopulationSize); }
private static void UpdateSpeciesTargetSizes( NeatPopulation <T> pop, IRandomSource rng) { double totalMeanFitness = pop.NeatPopulationStats.SumSpeciesMeanFitness; int totalTargetSizeInt = 0; // Handle specific case where all genomes/species have a zero fitness. // Assign all species an equal targetSize. if (0.0 == totalMeanFitness) { totalTargetSizeInt = CalcSpeciesTargetSizesInner_ZeroTotalMeanFitness(pop, rng); } else { // Calculate the new target size of each species using fitness sharing. double popSizeReal = pop.GenomeList.Count; Species <T>[] speciesArr = pop.SpeciesArray; // The size of each specie is based on its fitness relative to the other species. for (int i = 0; i < speciesArr.Length; i++) { SpeciesStats stats = speciesArr[i].Stats; stats.TargetSizeReal = (stats.MeanFitness / totalMeanFitness) * popSizeReal; // Discretize targetSize (stochastic rounding). stats.TargetSizeInt = (int)NumericsUtils.ProbabilisticRound(stats.TargetSizeReal, rng); // Total up discretized target sizes. totalTargetSizeInt += stats.TargetSizeInt; } } // Adjust each species' target allocation such that the sum total matches the required population size. AdjustSpeciesTargetSizes(pop, totalTargetSizeInt, rng); }
/// <summary> /// Create a new instance of <see cref="NeatEvolutionAlgorithm{T}"/> for the given neat experiment. /// </summary> /// <param name="neatExperiment">A neat experiment instance; this conveys everything required to create a new evolution algorithm instance that is ready to be run.</param> /// <returns>A new instance of <see cref="NeatEvolutionAlgorithm{T}"/>.</returns> public static NeatEvolutionAlgorithm <double> CreateNeatEvolutionAlgorithm( INeatExperiment <double> neatExperiment) { // Create a genomeList evaluator based on the experiment's configuration settings. var genomeListEvaluator = CreateGenomeListEvaluator(neatExperiment); // Create a MetaNeatGenome. var metaNeatGenome = CreateMetaNeatGenome(neatExperiment); // Create an initial population of genomes. NeatPopulation <double> neatPop = NeatPopulationFactory <double> .CreatePopulation( metaNeatGenome, connectionsProportion : neatExperiment.InitialInterconnectionsProportion, popSize : neatExperiment.PopulationSize); // Create a speciation strategy based on the experiment's configuration settings. var speciationStrategy = CreateSpeciationStrategy(neatExperiment); // Create an instance of the default connection weight mutation scheme. var weightMutationScheme = WeightMutationSchemeFactory.CreateDefaultScheme(neatExperiment.ConnectionWeightScale); // Pull all of the parts together into an evolution algorithm instance. var ea = new NeatEvolutionAlgorithm <double>( neatExperiment.NeatEvolutionAlgorithmSettings, genomeListEvaluator, speciationStrategy, neatPop, neatExperiment.ComplexityRegulationStrategy, neatExperiment.ReproductionAsexualSettings, neatExperiment.ReproductionSexualSettings, weightMutationScheme); return(ea); }
/// <summary> /// Create a new instance of <see cref="NeatEvolutionAlgorithm{T}"/> for the given neat experiment, and neat population. /// </summary> /// <param name="neatExperiment">A neat experiment instance; this conveys everything required to create a new evolution algorithm instance that is ready to be run.</param> /// <param name="neatPop">A pre constructed/loaded neat population; this must be compatible with the provided neat experiment, otherwise an exception will be thrown.</param> /// <returns>A new instance of <see cref="NeatEvolutionAlgorithm{T}"/>.</returns> public static NeatEvolutionAlgorithm <double> CreateNeatEvolutionAlgorithm( INeatExperiment <double> neatExperiment, NeatPopulation <double> neatPop) { // Validate MetaNeatGenome and NeatExperiment are compatible; normally the former should have been created based on the latter, but this is not enforced. MetaNeatGenome <double> metaNeatGenome = neatPop.MetaNeatGenome; ValidateCompatible(neatExperiment, metaNeatGenome); // Create a genomeList evaluator based on the experiment's configuration settings. var genomeListEvaluator = CreateGenomeListEvaluator(neatExperiment); // Create a speciation strategy based on the experiment's configuration settings. var speciationStrategy = CreateSpeciationStrategy(neatExperiment); // Create an instance of the default connection weight mutation scheme. var weightMutationScheme = WeightMutationSchemeFactory.CreateDefaultScheme(neatExperiment.ConnectionWeightScale); // Pull all of the parts together into an evolution algorithm instance. var ea = new NeatEvolutionAlgorithm <double>( neatExperiment.NeatEvolutionAlgorithmSettings, genomeListEvaluator, speciationStrategy, neatPop, neatExperiment.ComplexityRegulationStrategy, neatExperiment.ReproductionAsexualSettings, neatExperiment.ReproductionSexualSettings, weightMutationScheme); return(ea); }
/// <summary> /// Calc and store species target sizes based on relative mean fitness of each species, i.e. as per NEAT fitness sharing method. /// Then calc and store the elite, selection and offspring allocations/counts, per species. /// </summary> public static void UpdateSpeciesAllocationSizes( NeatPopulation <T> pop, NeatEvolutionAlgorithmSettings eaSettings, IRandomSource rng) { // Calculate the new target size of each species using fitness sharing. UpdateSpeciesTargetSizes(pop, rng); // Calculate elite, selection and offspring counts, per species. UpdateEliteSelectionOffspringCounts(pop, eaSettings, rng); }
private static void AdjustSpeciesTargetSizes_AccommodateBestGenomeSpecies( NeatPopulation <T> pop, IRandomSource rng) { // Test if the best genome is in a species with a zero target size allocation. int bestGenomeSpeciesIdx = pop.NeatPopulationStats.BestGenomeSpeciesIdx; Species <T>[] speciesArr = pop.SpeciesArray; if (speciesArr[bestGenomeSpeciesIdx].Stats.TargetSizeInt > 0) { // Nothing to do. The best genome is in a species with a non-zero allocation. return; } // Set the target size of the best genome species to a allow the best genome to survive to the next generation. speciesArr[bestGenomeSpeciesIdx].Stats.TargetSizeInt++; // Adjust down the target size of one of the other species to compensate. // Pick a species at random (but not the champ species). Note that this may result in a species with a zero // target size, this is OK at this stage. We handle allocations of zero elsewhere. // Create an array of shuffled indexes to select from, i.e. all of the species except for the one with the best genome in it. int speciesCount = speciesArr.Length; int[] speciesIdxArr = new int[speciesCount - 1]; for (int i = 0; i < bestGenomeSpeciesIdx; i++) { speciesIdxArr[i] = i; } for (int i = bestGenomeSpeciesIdx + 1; i < speciesCount; i++) { speciesIdxArr[i - 1] = i; } SortUtils.Shuffle(speciesIdxArr, rng); // Loop the species indexes. bool success = false; foreach (int speciesIdx in speciesIdxArr) { if (speciesArr[speciesIdx].Stats.TargetSizeInt > 0) { speciesArr[speciesIdx].Stats.TargetSizeInt--; success = true; break; } } if (!success) { throw new Exception("All species have a zero target size."); } }
/// <summary> /// For each species, allocate the EliteSizeInt, OffspringCount (broken down into OffspringAsexualCount and OffspringSexualCount), /// and SelectionSizeInt values. /// </summary> /// <param name="pop"></param> /// <param name="eaSettings"></param> /// <param name="rng"></param> private static void UpdateEliteSelectionOffspringCounts( NeatPopulation <T> pop, NeatEvolutionAlgorithmSettings eaSettings, IRandomSource rng) { Species <T>[] speciesArr = pop.SpeciesArray; // Loop the species, calculating and storing the various size/count properties. int bestGenomeSpeciesIdx = pop.NeatPopulationStats.BestGenomeSpeciesIdx; for (int i = 0; i < speciesArr.Length; i++) { bool isBestGenomeSpecies = (bestGenomeSpeciesIdx == i); AllocateEliteSelectionOffspringCounts(speciesArr[i], eaSettings, isBestGenomeSpecies, rng); } }