def evolve_generation_speciated(self): # Clean out our previous species array species_list = [] self.current_generation = {} # Find the fitness of all individuals of the previous generation, # And also assign a species to all of them for genome in self.previous_generation.values(): genome.fitness = self.fitness_function(genome) genome_added = False for species in species_list: cd = Genome.compatibility_distance(self.previous_generation[species.representor], genome, self.config.PARAM_C1, self.config.PARAM_C2, self.config.PARAM_C3) if cd <= self.config.COMPATIBILITY_THRESHOLD: species.add_genome(genome.id, genome.fitness) genome_added = True break if not genome_added: species_list.append(Species(genome.id, genome.fitness)) species_list.sort(key=lambda species: species.total_fitness, reverse=True) # Recalculate the adjusted fitness based on species for species in species_list: for genome_id in species.genomes: self.previous_generation[genome_id].fitness /= species.count_genomes # Kill off low performing individuals fitness_sorted_genome_ids = [genome[0] for genome in sorted(self.previous_generation.items(), key=lambda genome: genome[1].fitness, reverse=True)] parents = fitness_sorted_genome_ids[:int(self.config.SELECTION_RATIO * self.config.POPULATION_SIZE)] for species in species_list: for genome_id in species.genomes: if genome_id not in parents: species.genomes.remove(genome_id) species = [species for species in species_list if species] # Allocate child count per-species and create children total_fitness = reduce(lambda a, b: a + b, [s.total_fitness for s in species_list]) if total_fitness > 0.0: allocation_ratio = self.config.POPULATION_SIZE / total_fitness per_species_allocation = [(index, int(species.total_fitness * allocation_ratio)) for index, species in enumerate(species_list)] # Create offspring to fill up remaining space by random mating and mutations based on species size for index, child_count in per_species_allocation: for _ in range(child_count): parent1 = self.previous_generation[self.random.choice(species_list[index].genomes)] parent2 = self.previous_generation[self.random.choice([genome for genome in species_list[index].genomes if genome != parent1])] child = Genome.generate_offspring(parent1, self.max_id, self.random, self.node_classes, self.innovator, self.config, parent2) self.current_generation[child.id] = child present_population_size = len(self.current_generation) while present_population_size <= self.config.POPULATION_SIZE: parent = self.previous_generation[self.random.choice(parents)] child = Genome.generate_offspring(parent, self.max_id, self.random, self.node_classes, self.innovator, self.config) self.current_generation[child.id] = child present_population_size += 1 self.max_id += 1 # Update all lists and perform logging self.previous_generation = self.current_generation