public void VerifyNeatPopulationStats()
        {
            IRandomSource rng = RandomDefaults.CreateRandomSource(0);

            // Create test population and apply speciation strategy.
            NeatPopulation <double> neatPop = CreateNeatPopulation(30, 10.0, rng);

            // Loop the species; assign the same fitness to genomes within each species.
            for (int i = 0; i < neatPop.SpeciesArray.Length; i++)
            {
                double fitness = (i + 1) * 10.0;
                neatPop.SpeciesArray[i].GenomeList.ForEach(x => x.FitnessInfo = new FitnessInfo(fitness));
            }

            // Calc NeatPopulation statistics.
            neatPop.UpdateStats(PrimaryFitnessInfoComparer.Singleton, rng);

            // Validate expected mean fitness for each species.
            for (int i = 0; i < neatPop.SpeciesArray.Length; i++)
            {
                double expectedMeanFitness = (i + 1) * 10.0;
                Assert.Equal(expectedMeanFitness, neatPop.SpeciesArray[i].Stats.MeanFitness);
            }

            // Validate SumSpeciesMeanFitness.
            double expectedSumSpeciesMeanFitness = 10.0 + 20.0 + 30.0;

            Assert.Equal(expectedSumSpeciesMeanFitness, neatPop.NeatPopulationStats.SumSpeciesMeanFitness);

            // Validate BestGenomeSpeciesIdx.
            Assert.Equal(2, neatPop.NeatPopulationStats.BestGenomeSpeciesIdx);

            // Assign a high fitness to one of the genomes, and check that BestGenomeSpeciesIdx is updated accordingly.
            neatPop.SpeciesArray[0].GenomeList[2].FitnessInfo = new FitnessInfo(100.0);

            // Note. The species must be re-initialised in order for the fit genome to be sorted correctly within its
            // containing species.
            InitialiseSpecies(neatPop);

            neatPop.UpdateStats(PrimaryFitnessInfoComparer.Singleton, rng);
            Assert.Equal(0, neatPop.NeatPopulationStats.BestGenomeSpeciesIdx);

            // Perform the same test again with the best genome in the second species.
            neatPop.SpeciesArray[1].GenomeList[3].FitnessInfo = new FitnessInfo(200.0);
            InitialiseSpecies(neatPop);
            neatPop.UpdateStats(PrimaryFitnessInfoComparer.Singleton, rng);
            Assert.Equal(1, neatPop.NeatPopulationStats.BestGenomeSpeciesIdx);
        }
        public void VerifyPopulationStats()
        {
            IRandomSource rng = RandomDefaults.CreateRandomSource(0);

            // Create a test population.
            NeatPopulation <double> pop = CreateNeatPopulation(100, 10.0, rng);

            // Modify a few genome fitnesses.
            pop.GenomeList[10].FitnessInfo = new FitnessInfo(100.0);
            pop.GenomeList[20].FitnessInfo = new FitnessInfo(0.0);

            // Initialise the NEAT population species; the wider population stats are dependent on this occurring.
            InitialiseSpecies(pop);

            // Calc/update stats.
            pop.UpdateStats(PrimaryFitnessInfoComparer.Singleton, rng);

            // Validate stats.
            // Fitness stats.
            PopulationStatistics stats = pop.Stats;

            Assert.Equal(10, stats.BestGenomeIndex);
            Assert.Equal(100.0, stats.BestFitness.PrimaryFitness);
            Assert.Equal(10.8, stats.MeanFitness);
            Assert.Equal(1, stats.BestFitnessHistory.Length);
            Assert.Equal(100.0, stats.BestFitnessHistory.Total);

            // Complexity stats.
            Assert.Equal(6.0, stats.BestComplexity);
            Assert.Equal(6.0, stats.MeanComplexity);
            Assert.Equal(1, stats.MeanComplexityHistory.Length);
            Assert.Equal(6.0, stats.MeanComplexityHistory.Total);
        }
    public void UpdateSpeciesAllocationSizes()
    {
        IRandomSource rng = RandomDefaults.CreateRandomSource(0);

        NeatEvolutionAlgorithmSettings eaSettings = new()
        {
            SpeciesCount = 4
        };

        // Create population.
        NeatPopulation <double> neatPop = CreateNeatPopulation(100, eaSettings.SpeciesCount, 2, 2, 1.0);

        // Manually set-up some species.
        var speciesArr = neatPop.SpeciesArray;

        speciesArr[0].GenomeList.AddRange(neatPop.GenomeList.Take(25));
        speciesArr[1].GenomeList.AddRange(neatPop.GenomeList.Skip(25).Take(25));
        speciesArr[2].GenomeList.AddRange(neatPop.GenomeList.Skip(50).Take(25));
        speciesArr[3].GenomeList.AddRange(neatPop.GenomeList.Skip(75).Take(25));

        // Manually assign fitness scores to the genomes.
        speciesArr[0].GenomeList.ForEach(x => x.FitnessInfo = new FitnessInfo(100.0));
        speciesArr[1].GenomeList.ForEach(x => x.FitnessInfo = new FitnessInfo(200.0));
        speciesArr[2].GenomeList.ForEach(x => x.FitnessInfo = new FitnessInfo(400.0));
        speciesArr[3].GenomeList.ForEach(x => x.FitnessInfo = new FitnessInfo(800.0));

        // Invoke species target size calcs.
        neatPop.UpdateStats(PrimaryFitnessInfoComparer.Singleton, rng);
        SpeciesAllocationCalcs <double> .UpdateSpeciesAllocationSizes(
            neatPop, eaSettings, RandomDefaults.CreateRandomSource());

        // Species target sizes should be relative to the species mean fitness.
        double totalMeanFitness = 1500.0;
        double popSize          = 100.0;

        Assert.Equal((100.0 / totalMeanFitness) * popSize, speciesArr[0].Stats.TargetSizeReal);
        Assert.Equal((200.0 / totalMeanFitness) * popSize, speciesArr[1].Stats.TargetSizeReal);
        Assert.Equal((400.0 / totalMeanFitness) * popSize, speciesArr[2].Stats.TargetSizeReal);
        Assert.Equal((800.0 / totalMeanFitness) * popSize, speciesArr[3].Stats.TargetSizeReal);

        // Note. Discretized target sizes will generally be equal to ceil(TargetSizeReal) or floor(TargetSizeReal),
        // but may not be due to the target size adjustment logic that is used to ensure that sum(TargetSizeInt) is equal
        // to the required population size.

        // Check that sum(TargetSizeInt) is equal to the required population size.
        Assert.Equal(speciesArr.Sum(x => x.Stats.TargetSizeInt), neatPop.GenomeList.Count);
    }