/// <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.StochasticRound(targetSizeReal, rng); // Total up discretized target sizes. totalTargetSizeInt += stats.TargetSizeInt; } return(totalTargetSizeInt); }
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 (totalMeanFitness == 0.0) { 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.StochasticRound(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); }
private static void AllocateEliteSelectionOffspringCounts( Species <T> species, NeatEvolutionAlgorithmSettings eaSettings, bool isBestGenomeSpecies, IRandomSource rng) { SpeciesStats stats = species.Stats; // Special case - zero target size. if (stats.TargetSizeInt == 0) { Debug.Assert(!isBestGenomeSpecies, "Zero target size assigned to specie that contains the best genome."); stats.EliteSizeInt = 0; stats.OffspringCount = 0; stats.OffspringAsexualCount = 0; stats.OffspringSexualCount = 0; stats.SelectionSizeInt = 0; return; } // Calculate the elite size as a proportion of the current species size. // Note. We discretize the real size with a probabilistic handling of the fractional part. double eliteSizeReal = species.GenomeList.Count * eaSettings.ElitismProportion; int eliteSizeInt = (int)NumericsUtils.StochasticRound(eliteSizeReal, rng); // Ensure eliteSizeInt is no larger than the current target size. (I.e. the value was // calculated as a proportion of the current size, not the new target size). stats.EliteSizeInt = Math.Min(eliteSizeInt, stats.TargetSizeInt); // Special case: ensure the species with the best genome preserves that genome. // Note. This is done even for a target size of one, which would mean that no offspring are // produced from the best genome, apart from the (usually small) chance of a cross-species mating. if (isBestGenomeSpecies && stats.EliteSizeInt == 0) { stats.EliteSizeInt = 1; } // Determine how many offspring to produce for the species. stats.OffspringCount = stats.TargetSizeInt - stats.EliteSizeInt; // Determine the split between asexual and sexual reproduction. Again using probabilistic // rounding to compensate for any rounding bias. double offspringAsexualCountReal = stats.OffspringCount * eaSettings.OffspringAsexualProportion; stats.OffspringAsexualCount = (int)NumericsUtils.StochasticRound(offspringAsexualCountReal, rng); stats.OffspringSexualCount = stats.OffspringCount - stats.OffspringAsexualCount; // Calculate the selectionSize. The number of the species' fittest genomes that are selected from // to create offspring. // We ensure this is at least one; if TargetSizeInt is zero then it doesn't matter because no genomes will be // selected from this species to produce offspring, except for cross-species mating, hence the minimum of one is // a useful general approach. double selectionSizeReal = species.GenomeList.Count * eaSettings.SelectionProportion; stats.SelectionSizeInt = Math.Max(1, (int)NumericsUtils.StochasticRound(selectionSizeReal, rng)); }
public void StochasticRound() { IRandomSource rng = RandomDefaults.CreateRandomSource(0); for (int i = 0; i < 1_000_000; i++) { double valReal = 100 * rng.NextDouble(); double valRound = NumericsUtils.StochasticRound(valReal, rng); Assert.True(valRound == Math.Floor(valReal) || valRound == Math.Ceiling(valReal)); } }
/// <summary> /// Select a subset of items from a superset of a given size. /// </summary> /// <param name="supersetCount">The size of the superset to select from.</param> /// <param name="rng">Random source.</param> /// <returns>An array of indexes that are the selected items.</returns> public int[] SelectSubset(int supersetCount, IRandomSource rng) { // Note. Ideally we'd return a sorted list of indexes to improve performance of the code that consumes them, // however, the sampling process inherently produces samples in randomized order, thus the decision of whether // to sort or not depends on the cost to the code using the samples. I.e. don't sort here! int selectionCount = (int)NumericsUtils.StochasticRound(supersetCount * _selectionProportion, rng); int[] idxArr = new int[selectionCount]; DiscreteDistribution.SampleUniformWithoutReplacement(rng, supersetCount, idxArr); return(idxArr); }
/// <summary> /// Creates a single randomly initialised genome. /// </summary> private NeatGenome <T> CreateGenome() { // Determine how many connections to create in the new genome, as a proportion of all possible connections // between the input and output nodes. int connectionCount = (int)NumericsUtils.StochasticRound(_connectionDefArr.Length * _connectionsProportion, _rng); // Ensure there is at least one connection. connectionCount = Math.Max(1, connectionCount); // Select a random subset of all possible connections between the input and output nodes. int[] sampleArr = new int[connectionCount]; DiscreteDistribution.SampleUniformWithoutReplacement( _rng, _connectionDefArr.Length, sampleArr); // Sort the samples. // Note. This results in the neural net connections being sorted by sourceID then targetID. Array.Sort(sampleArr); // Create the connection gene arrays and populate them. var connGenes = new ConnectionGenes <T>(connectionCount); var connArr = connGenes._connArr; var weightArr = connGenes._weightArr; for (int i = 0; i < sampleArr.Length; i++) { DirectedConnection cdef = _connectionDefArr[sampleArr[i]]; connArr[i] = new DirectedConnection( cdef.SourceId, cdef.TargetId); weightArr[i] = _connWeightDist.Sample(_rng); } // Get create a new genome with a new ID, birth generation of zero. int id = _genomeIdSeq.Next(); return(_genomeBuilder.Create(id, 0, connGenes)); }
private void CreateSpeciesOffspringSexual( Species <T>[] speciesArr, Species <T> species, DiscreteDistribution speciesDistUpdated, DiscreteDistribution?[] genomeDistArr, DiscreteDistribution genomeDist, int offspringCount, List <NeatGenome <T> > offspringList, double interspeciesMatingProportion, IRandomSource rng, out int offspringInterspeciesCount) { // Calc the number of offspring to create via inter-species sexual reproduction. int offspringCountSexualInter; if (interspeciesMatingProportion == 0.0) { offspringInterspeciesCount = offspringCountSexualInter = 0; } else { offspringInterspeciesCount = offspringCountSexualInter = (int)NumericsUtils.StochasticRound(interspeciesMatingProportion * offspringCount, rng); } // Calc the number of offspring to create via intra-species sexual reproduction. int offspringCountSexualIntra = offspringCount - offspringCountSexualInter; // Get genome list for the current species. var genomeList = species.GenomeList; // Produce the required number of offspring from intra-species sexual reproduction. for (int i = 0; i < offspringCountSexualIntra; i++) { // Select/sample parent A from the species. int genomeIdx = DiscreteDistribution.Sample(rng, genomeDist); var parentGenomeA = genomeList[genomeIdx]; // Create a new distribution with parent A removed from the set of possibilities. DiscreteDistribution genomeDistUpdated = genomeDist.RemoveOutcome(genomeIdx); // Select/sample parent B from the species. genomeIdx = DiscreteDistribution.Sample(rng, genomeDistUpdated); var parentGenomeB = genomeList[genomeIdx]; // Create a child genome and add it to offspringList. var childGenome = _reproductionSexual.CreateGenome(parentGenomeA, parentGenomeB, rng); offspringList.Add(childGenome); } // Produce the required number of offspring from inter-species sexual reproduction. for (int i = 0; i < offspringCountSexualInter; i++) { // Select/sample parent A from the current species. int genomeIdx = DiscreteDistribution.Sample(rng, genomeDist); var parentGenomeA = genomeList[genomeIdx]; // Select another species to select parent B from. int speciesIdx = DiscreteDistribution.Sample(rng, speciesDistUpdated); Species <T> speciesB = speciesArr[speciesIdx]; // Select parent B from species B. DiscreteDistribution genomeDistB = genomeDistArr[speciesIdx] !; genomeIdx = DiscreteDistribution.Sample(rng, genomeDistB); var parentGenomeB = speciesB.GenomeList[genomeIdx]; // Ensure parentA is the fittest of the two parents. if (_fitnessComparer.Compare(parentGenomeA.FitnessInfo, parentGenomeB.FitnessInfo) < 0) { VariableUtils.Swap(ref parentGenomeA !, ref parentGenomeB !); } // Create a child genome and add it to offspringList. var childGenome = _reproductionSexual.CreateGenome(parentGenomeA, parentGenomeB, rng); offspringList.Add(childGenome); } }