/// <summary> /// Constructs a NEAT client with the given pedigree, number of organisms, evaluation function, and every constant. /// </summary> /// <param name="pedigree">The pedigree used to track all genes in this NEAT client.</param> /// <param name="numOrganisms">The total number of organisms this NEAT client will train.</param> /// <param name="evaluateFunction">The function to evaluate organisms' neural networks and give them a fitness score.</param> /// <param name="compatibility_distance">The initial compatibility distance.</param> /// <param name="CD_function">The function to adjust the compatibility distance by. Good options are the <see cref="NEAT.NEATClient.NERO_CD_Function(int)"/> and the /// <see cref="NEAT.NEATClient.Constant_CD_Function(int)"/></param> /// <param name="surviving_percentage">The percentage of organisms that will survive on <see cref="NEAT.NEATClient.Kill"/>.</param> public NEATClient(Pedigree pedigree, int numOrganisms, EvaluateFunction evaluateFunction, double compatibility_distance, CompatibilityDistanceFunction CD_function, double surviving_percentage) { #region Internal Setters Pedigree = pedigree; NumOrganisms = numOrganisms; Organisms = new List <Organism>(numOrganisms); Species = new HashSet <Species>(); this.evaluateFunction = evaluateFunction; CompatibilityDistance = compatibility_distance; this.CD_function = CD_function; SurvivingPercentage = surviving_percentage; #endregion Internal Setters #region Initial Conditions //Create organisms: for (int i = 0; i < NumOrganisms; ++i) { Organisms.Add(new Organism(Pedigree.CreateGenome())); } //Create Species: Species first_species = new Species(); Species.Add(first_species); first_species.AddOrganism(Organisms[0]); //Prepares first species to house every organism. Speciate(); #endregion Initial Conditions }
/// <summary> /// Reproduces the surviving species based on their fitness scores. Chooses the organisms in the species to mate based on fitness scores as well. Replaces the previous generation /// with the newly created organisms. Removes extinct species if necessary. Adjusts compatibility distance after replacement. /// See <see cref="NEAT.NEATClient.Evolve"/> before using! /// </summary> /// <remarks> /// Decides the amount of offspring (nk) each species (k) should be allotted via the following equation: /// <para/> /// nk = P * (Fk / Ft) /// <para/> /// Where: /// <list type="bullet"> /// <item>k: The species.</item> /// <item>nk: The number of new organisms for species k in the next generation.</item> /// <item>P: The desired new population count.</item> /// <item>Fk: The average fitness score of the species.</item> /// <item>Ft: The sum of all fitness score averages of every species.</item> /// </list> /// </remarks> public void ReproduceAndReplace() { double total_speciesFitness = Species.Sum(x => x.AverageFitnessScore); Dictionary <Species, ReproduceAndReplace_Node> stored_distributionsSpecies = new Dictionary <Species, ReproduceAndReplace_Node>(Species.Count); List <Organism> next_generation = new List <Organism>(NumOrganisms); //All of the new organisms. HashSet <Species> next_species = new HashSet <Species>(Species.Count); #region Reproduction foreach (Species species in Species) { //Calculate the number of organisms every species will have: int num_alloted_offspring = (int)(NumOrganisms * (species.AverageFitnessScore / total_speciesFitness)); //Prepare new species for placement: Species creation_species = new Species(); next_species.Add(creation_species); //Prepare ScoredDistribution for selecting organisms to mate: ScoredDistribution <Organism> internal_organisms = new ScoredDistribution <Organism>(species.Size, Pedigree.Random); foreach (Organism organism in species) { internal_organisms.Add(organism, organism.FitnessScore); } stored_distributionsSpecies.Add(species, new ReproduceAndReplace_Node(internal_organisms, creation_species)); //Actaully mate the organisms: for (int i = 0; i < num_alloted_offspring; ++i) { Organism random_organism_1 = internal_organisms.ChooseValue(); Organism random_organism_2 = internal_organisms.ChooseValue(random_organism_1); //We don't want any self-replication... if (random_organism_2 == null) { random_organism_2 = random_organism_1; //Unless that's the only option ;) Only happens when the organism is the only one in the species. } Organism baby = new Organism(random_organism_1.Genome.Crossover(random_organism_1.FitnessScore, random_organism_2.Genome, random_organism_2.FitnessScore, Pedigree.Random)); next_generation.Add(baby); creation_species.AddOrganism(baby); } } while (next_generation.Count < NumOrganisms) //We need more organisms, give it to some random species. Super rare that this doesn't happen. { ReproduceAndReplace_Node chosen_last_node = stored_distributionsSpecies[Species.RandomValue(Pedigree.Random)]; ScoredDistribution <Organism> last_round_distribution = chosen_last_node.scoredDistribution_organisms; Species last_round_species = chosen_last_node.new_species; Organism random_organism_1 = last_round_distribution.ChooseValue(); Organism random_organism_2 = last_round_distribution.ChooseValue(random_organism_1); //We don't want any self-replication... if (random_organism_2 == null) { random_organism_2 = random_organism_1; //Unless that's the only option ;) Only happens when the organism is the only one in the species. } Organism baby = new Organism(random_organism_1.Genome.Crossover(random_organism_1.FitnessScore, random_organism_2.Genome, random_organism_2.FitnessScore, Pedigree.Random)); next_generation.Add(baby); last_round_species.AddOrganism(baby); } #endregion Reproduction #region Replacement //Organisms: Organisms.Clear(); Organisms.AddRange(next_generation); //Species: Species.Clear(); foreach (Species species in next_species) { Species.Add(species); } #endregion Replacement RemoveExtinctions(); CompatibilityDistance += CD_function.Invoke(Species.Count); }
/// <summary> /// Separates the current organisms into the correct species. Makes new species and deletes extinct species as needed. See <see cref="NEAT.NEATClient.Evolve"/> before using! /// </summary> public void Speciate() { foreach (Organism organism in Organisms) { bool found_species = false; foreach (Species species in Species) { Organism random_organism = species.GetRandomOrganism(Pedigree.Random, organism); if (random_organism == null) //The organism we're at is the only organism in the species we're at. See if we should leave it in this species. { bool kill_species = false; IEnumerable <Species> all_other_species = Species.Where(x => x != species); foreach (Species other_species in all_other_species) { Organism other_random_organism = other_species.GetRandomOrganism(Pedigree.Random); //No need to exclude the organism because it can't be here. if (organism.Genome.Distance(other_random_organism.Genome) < CompatibilityDistance) //The organism can fit in another species, put it there. { organism.Species.RemoveOrganism(organism); other_species.AddOrganism(organism); kill_species = true; break; } } if (kill_species) //This species no longer has any organisms, it is now extinct. { Species.Remove(species); } found_species = true; break; } if (organism.Genome.Distance(random_organism.Genome) < CompatibilityDistance) { if (organism.Species != random_organism.Species) //If the species to set is actually different. { Species original_species = organism.Species; original_species?.RemoveOrganism(organism); species.AddOrganism(organism); if (original_species?.Size == 0) //This species just went extinct. { Species.Remove(original_species); } } found_species = true; break; } } if (!found_species) { Species original_species = organism.Species; original_species?.RemoveOrganism(organism); Species new_species = new Species(); Species.Add(new_species); new_species.AddOrganism(organism); if (original_species?.Size == 0) //This species just went extinct. { Species.Remove(original_species); } } } }