def optimize(self): current_population = self._generate_population() gene_tracker = GeneTracker(current_population[0].nodes_count) all_species = [Species(current_population[0])] best_fitness = None best_genome = None current_generation = 0 while current_generation < self.max_generations: fitness = self._calculate_fitness(current_population) all_species = self._determine_species(current_population, fitness, all_species) species_fitness = [species.fitness for species in all_species] next_population = [] best_fitness, best_genome = self._save_best( best_fitness, best_genome, all_species, next_population) self._fill_population(gene_tracker, all_species, species_fitness, next_population) [species.clear() for species in all_species] current_population = next_population current_generation += 1 if self.print_progress: print( f"generation: {current_generation}, best fitness: {best_fitness}" ) return build_network(best_genome)
def speciate(self, config, population, generation): assert isinstance(population, dict) self.genome_to_species = {} # Add all the genomes to the right species set. for gid, genome in iteritems(population): num_states = len(genome.states) if num_states not in self.species: self.species[num_states] = Species(num_states, generation) self.species[num_states].members[gid] = genome self.genome_to_species[gid] = num_states # Remove empty species sets. empty = [] for sid, species in iteritems(self.species): if not species.members: empty.append(sid) for sid in empty: self.species.pop(sid) self.reporters.info('Removing species with {0} states from species set'.format(sid)) self.reporters.info('Speciated into {0} species'.format(len(self.species)))
def _speciate(self, population): """ Place genomes into species by genetic similarity. Note that this method assumes the current representatives of the species are from the old generation, and that after speciation has been performed, the old representatives should be dropped and replaced with representatives from the new generation. If you violate this assumption, you should make sure other necessary parts of the code are updated to reflect the new behavior. """ for individual in population: # Find the species with the most similar representative. min_distance = None closest_species = None for s in self.species: distance = individual.distance(s.representative) if distance < self.config.compatibility_threshold \ and (min_distance is None or distance < min_distance): closest_species = s min_distance = distance if closest_species: closest_species.add(individual) else: # No species is similar enough, create a new species for this individual. self.species.append( Species(individual, self.species_indexer.get_next())) # Only keep non-empty species. self.species = [s for s in self.species if s.members] # Select a random current member as the new representative. for s in self.species: s.representative = random.choice(s.members)
def speciate(self, genome, generation): """ Places Genome into proper species - index :param genome: Genome be speciated :param generation: Number of generation this speciation is occuring at :return: None """ for species in self.species: if Species.species_distance(genome, species.model_genome) <= self.Config.SPECIATION_THRESHOLD: genome.species = species.id species.members.append(genome) return # Did not match any current species. Create a new one new_species = Species(len(self.species), genome, generation) genome.species = new_species.id new_species.members.append(genome) self.species.append(new_species)
def __init__(self, seed_creature=None, n_pops=150): """Create a population for NEAT. Arguments: seed_creature: The creature that will be used to creature the initial generation. n_pops: How many creatures should be in the population. """ self.n_pops = n_pops self.species = set() if seed_creature: self.creatures = [seed_creature.copy() for _ in range(n_pops)] genesis_species = Species() genesis_species.assign_members(self.creatures) self.species.add(genesis_species) else: self.creatures = []
def _mutate_topology(self, genotype: Genotype, species: Species = None): species_template = ( {}, {}) if species is None else species.get_mutate_template() add_node = np.random.uniform() < self.prop_top_add_node if add_node: genotype.mutate_add_node(self.config, self.innovation_map, species_template) if np.random.uniform() < self.prop_top_add_edge and not add_node: genotype.mutate_add_edge(self.config, self.innovation_map, species_template)
def _determine_species(self, current_population, fitness, all_species): new_all_species = [] for i in range(len(current_population)): genome = current_population[i] genome_fitness = fitness[i] for species in all_species: if calculate_genomes_distance(genome, species.representative, self.c1, self.c2, self.c3, self.n) < self.dt: species.add_genome(genome, genome_fitness) break else: new_species = Species() new_species.add_genome(genome, genome_fitness) new_all_species.append(new_species) all_species = [ species for species in all_species if species.count != 0 ] all_species.extend(new_all_species) [species.finalize() for species in all_species] return all_species
def speciate(self): """Place creatures in the creatures into a species, or create a new species if no suitable species exists. """ print('Segregating Communities...', end='') # Adding these lines slows down convergence a lot. for species in self.species: species.members.clear() for creature in self.creatures: for species in self.species: if creature.distance(species.representative) < \ Species.compatibility_threshold: species.add(creature) break else: new_species = Species() new_species.add(creature) new_species.representative = creature self.species.add(new_species) self.species = set(filter(lambda s: len(s) > 0, self.species))
def from_json(config): """Load an instance of the NEAT algorithm from JSON. Arguments: config: the JSON dictionary loaded from file. Returns: an instance of the NEAT algorithm. """ population = Population(None, config['n_pops']) population.species = set( Species.from_json(s_config) for s_config in config['species']) population.creatures = [] for species in population.species: population.creatures += species.members return population
def gen_species(self): s: Species for s in self.species.data: s.reset() c: Client for c in self.clients.data: if c.species: continue found = False for s in self.species.data: if s.put(c): # successfully found matching species found = True break if not found: self.species.add(Species(c)) for s in self.species.data: s.evaluate_score()
def test_json(self): """Test whether a species can be saved to and loaded from JSON.""" species = Species() species.assign_members([Creature(4, 1) for _ in range(100)]) species.allotted_offspring_quota = 93 dump = json.dumps(species.to_json()) species_load = Species.from_json(json.loads(dump)) self.assertEqual(len(species), len(species_load)) self.assertTrue( species.representative.distance(species_load.representative) < 1e-8 ) self.assertEqual(species.name, species_load.name) self.assertEqual(species.id, species_load.id) self.assertEqual(species.allotted_offspring_quota, species_load.allotted_offspring_quota) self.assertTrue( species.champion.distance(species_load.champion) < 1e-8)
def speciate(self): # Randomly assign representative genome [species.assign_genome() for species in self.species] # Clear species [species.clear() for species in self.species] # Assign organisms to species for organism in self.population: for species in self.species: if species.is_compatible_with(organism): species.add_organism(organism) break # Create new species else: self.species.append(Species(self.config, organism)) # Remove any species without organisms self.species = [species for species in self.species if species.organisms]
def speciate(self): """ Seperates the organisms in the population into species """ # We first remove the organisms attached to each species from the last generation, # but we do not remove the the representative of each species from the last generation for species in self.species: species.organisms.clear() for organism in self.organisms: # For each organism we try to find a species whose representative is close enough found_species = False for s_idx, species in enumerate(self.species): # Calculate how compatible the species representative is to our organism compat_dist = organism.genome.get_compatibility_distance( species.representative) if compat_dist < SPECIATION_THRESHOLD: # If it is below the threshold, then the organism belongs to this species found_species = True species.organisms.append(organism) organism.species = s_idx break if not found_species: # If this organism is different enough such that it is not compatible with any of # the existing species, we create a new species with the organism as the # representative organism.species = len(self.species) self.species.append( Species(organism, self.global_species_counter)) # Remove species which have no organisms in this population. We iterate backwards so we can # delete from the list while iterating for i in range(len(self.species) - 1, -1, -1): if len(self.species[i].organisms) == 0: self.species.pop(i)
def speciate(self): compatibility_threshold = self.config.genome_params.compatibility_threshold species = [] # assign members to each of the species population = deepcopy(self.population) for s in self.species: for idx, p in enumerate(population): distance = p.compatibility_distance(s.champion, p) if distance < compatibility_threshold: population.pop(idx) s.update(p) species.append(s) # divide newborn population or unspeciated individuals into species for p in population: distances = [] for s in species: distance = p.compatibility_distance(s.champion, p) if distance < compatibility_threshold: distances.append((distance, s)) # check how py checks for empty list |if distances|, # might be slow if it computes len first if distances: _, s = min(distances, key=lambda d: d[0]) s.update(p) else: s = Species() s.update(p) species.append(s) # this is obviously dumb here, will change for s in species: s.update_avg_fitness() self.species.clear() self.species = species
def run(self): for generation in range(1, self.Config.NUMBER_OF_GENERATIONS): # Get Fitness of Every Genome for genome in self.population: genome.fitness = max(0, self.Config.fitness_fn(genome)) best_genome = utils.get_best_genome(self.population) # Reproduce all_fitnesses = [] remaining_species = [] for species, is_stagnant in Species.stagnation( self.species, generation): if is_stagnant: self.species.remove(species) else: all_fitnesses.extend(g.fitness for g in species.members) remaining_species.append(species) min_fitness = min(all_fitnesses) max_fitness = max(all_fitnesses) fit_range = max(1.0, (max_fitness - min_fitness)) for species in remaining_species: # Set adjusted fitness avg_species_fitness = np.mean( [g.fitness for g in species.members]) species.adjusted_fitness = (avg_species_fitness - min_fitness) / fit_range adj_fitnesses = [s.adjusted_fitness for s in remaining_species] adj_fitness_sum = sum(adj_fitnesses) # Get the number of offspring for each species new_population = [] for species in remaining_species: if species.adjusted_fitness > 0: size = max( 2, int((species.adjusted_fitness / adj_fitness_sum) * self.Config.POPULATION_SIZE)) else: size = 2 # sort current members in order of descending fitness cur_members = species.members cur_members.sort(key=lambda g: g.fitness, reverse=True) species.members = [] # reset # save top individual in species new_population.append(cur_members[0]) size -= 1 # Only allow top x% to reproduce purge_index = int(self.Config.PERCENTAGE_TO_SAVE * len(cur_members)) purge_index = max(2, purge_index) cur_members = cur_members[:purge_index] for i in range(size): parent_1 = random.choice(cur_members) parent_2 = random.choice(cur_members) child = crossover(parent_1, parent_2, self.Config) mutate(child, self.Config) new_population.append(child) # Set new population self.population = new_population Population.current_gen_innovation = [] # Speciate for genome in self.population: self.speciate(genome, generation) if best_genome.fitness >= self.Config.FITNESS_THRESHOLD: return best_genome, generation # Generation Stats if self.Config.VERBOSE: logger.info(f'Finished Generation {generation}') logger.info(f'Best Genome Fitness: {best_genome.fitness}') logger.info( f'Best Genome Length {len(best_genome.connection_genes)}\n' ) return None, None
def speciate(self, config, population, generation): """ Place genomes into species by genetic similarity. Note that this method assumes the current representatives of the species are from the old generation, and that after speciation has been performed, the old representatives should be dropped and replaced with representatives from the new generation. If you violate this assumption, you should make sure other necessary parts of the code are updated to reflect the new behavior. """ assert isinstance(population, dict) compatibility_threshold = self.species_set_config.compatibility_threshold # Find the best representatives for each existing species. unspeciated = set(iterkeys(population)) distances = GenomeDistanceCache(config.genome_config) new_representatives = {} new_members = {} for sid, s in iteritems(self.species): candidates = [] for gid in unspeciated: g = population[gid] d = distances(s.representative, g) candidates.append((d, g)) # The new representative is the genome closest to the current representative. if len(candidates) > 0: ignored_rdist, new_rep = min(candidates, key=lambda x: x[0]) new_rid = new_rep.key new_representatives[sid] = new_rid new_members[sid] = [new_rid] unspeciated.remove(new_rid) # Partition population into species based on genetic similarity. while unspeciated: gid = unspeciated.pop() g = population[gid] # Find the species with the most similar representative. candidates = [] for sid, rid in iteritems(new_representatives): rep = population[rid] d = distances(rep, g) if d < compatibility_threshold: candidates.append((d, sid)) if len(candidates) > 0: ignored_sdist, sid = min(candidates, key=lambda x: x[0]) new_members[sid].append(gid) else: # No species is similar enough, create a new species, using # this genome as its representative. sid = next(self.indexer) new_representatives[sid] = gid new_members[sid] = [gid] # Update species collection based on new speciation. self.genome_to_species = {} for sid, rid in iteritems(new_representatives): s = self.species.get(sid) if s is None: s = Species(sid, generation) self.species[sid] = s members = new_members[sid] for gid in members: self.genome_to_species[gid] = sid member_dict = dict((gid, population[gid]) for gid in members) s.update(population[rid], member_dict) gdmean = mean(itervalues(distances.distances)) gdstdev = stdev(itervalues(distances.distances)) self.reporters.info( 'Mean genetic distance {0:.3f}, standard deviation {1:.3f}'.format(gdmean, gdstdev))
def run(self, pool=None, shared_data=None): allgenfitnesses = [] for generation in range(1, self.Config.NUMBER_OF_GENERATIONS): # ****** BYME: Neuro-evolution accures here ******* # Get Fitness of Every Genome if pool != None: ll = len(self.population) args = zip(list(it.repeat(self.Config.fitness_fn, ll)), \ self.population, list(it.repeat(shared_data, ll)), \ list(it.repeat(generation, ll)), range(ll)) fitnesses = list(pool.map(pool_func, args)) for genome, fitness in zip(self.population, fitnesses): genome.fitness = fitness else: for genome in tqdm(self.population): genome.fitness = max(0, self.Config.fitness_fn(genome)) allfitnesses_onegen = [g.fitness for g in self.population] allfitnesses_onegen.sort() allgenfitnesses.append(allfitnesses_onegen) best_genome = utils.get_best_genome(self.population) draw_net(best_genome, view=False, \ filename="./images/solution-best-g%d"%(generation), show_disabled=True) # Reproduce all_fitnesses = [] remaining_species = [] for species, is_stagnant in Species.stagnation( self.species, generation): if is_stagnant: self.species.remove(species) else: all_fitnesses.extend(g.fitness for g in species.members) remaining_species.append(species) min_fitness = min(all_fitnesses) max_fitness = max(all_fitnesses) fit_range = max(1.0, (max_fitness - min_fitness)) for species in remaining_species: # Set adjusted fitness avg_species_fitness = np.mean( [g.fitness for g in species.members]) species.adjusted_fitness = (avg_species_fitness - min_fitness) / fit_range adj_fitnesses = [s.adjusted_fitness for s in remaining_species] adj_fitness_sum = sum(adj_fitnesses) # Get the number of offspring for each species new_population = [] for species in remaining_species: if species.adjusted_fitness > 0: size = max( 2, int((species.adjusted_fitness / adj_fitness_sum) * self.Config.POPULATION_SIZE)) else: size = 2 # sort current members in order of descending fitness cur_members = species.members cur_members.sort(key=lambda g: g.fitness, reverse=True) species.members = [] # reset # save top individual in species new_population.append(cur_members[0]) size -= 1 # Only allow top x% to reproduce purge_index = int(self.Config.PERCENTAGE_TO_SAVE * len(cur_members)) purge_index = max(2, purge_index) cur_members = cur_members[:purge_index] for i in range(size): parent_1 = random.choice(cur_members) parent_2 = random.choice(cur_members) child = crossover(parent_1, parent_2, self.Config) mutate(child, self.Config) new_population.append(child) # Set new population self.population = new_population Population.current_gen_innovation = [] # Speciate for genome in self.population: self.speciate(genome, generation) if best_genome.fitness >= self.Config.FITNESS_THRESHOLD: o = open("allgenfitnesses.txt", "w") for allfitnesses_onegen in allgenfitnesses: o.write(str(allfitnesses_onegen) + "\n") o.close() return best_genome, generation # Generation Stats if self.Config.VERBOSE: print('Finished Generation', generation) print('Best Genome Fitness:', best_genome.fitness) if hasattr(best_genome, "avgloss"): print('Best Genome Loss:', best_genome.avgloss) print('Best Genome Length', len(best_genome.connection_genes)) print() o = open("allgenfitnesses.txt", "w") for allfitnesses_onegen in allgenfitnesses: o.write(str(allfitnesses_onegen) + "\n") o.close() return None, None
def speciate(self): self.archive.reset_stats() self._adjust_fitness() for species in self.species: species.reset(self.species, self.population) self.species = [ s for s in self.species if s.representative is not None ] representatives = [ s.representative for s in self.species if s.representative is not None ] for genotype in self.population: if genotype in representatives: continue genotype.curr_orig_species_id = genotype.species_id species_found = False best_species = -1 best_diffs = None for i in range(len(self.species)): is_compatible, diffs = self.species[i].calculate_compatibility( genotype) if is_compatible: species_found = True if best_diffs is None or diffs < best_diffs: best_diffs = diffs best_species = i if species_found: self.species[best_species].add_member(genotype, best_diffs) else: self.species.append( Species(genotype, self._next_species_id(), self.config.compatibility_max_diffs, self.config.species_elitism)) self.species = [ species for species in self.species if not species.is_empty() ] # For Console Observer self.specs_before = len(self.species) self.comp_before = self._get_interspecies_disjoints() for species in self.species: species.evaluate_fitness() best_member_species = self.species[np.argsort( [s.get_best_member().score for s in self.species])[-1]] best_member = best_member_species.get_best_member() print("BEST MEMBER", best_member, best_member_species) # Remove Extinct # if len(self.species) >= self.config.species_max: # self.species = [species for species in self.species if not species.is_extinct() or species == best_member_species] # Limit max species new_species = [best_member_species] # type: List[Species] # species_score = np.argsort([s.fitness * best_member.calculate_compatibility(s.representative, self.config.compatibility_max_diffs) for s in self.species])[::-1] # if len(self.species) < self.config.species_max: # valid_species = [s for s in self.species] # else: # valid_species = [s for s in self.species if # s.calculate_compatibility(best_member_species.representative)[1] < self.config.compatibility_max_diffs * 3 and # s.get_best_member().fitness >= best_member_species.get_best_member().fitness * 0.5] valid_species = [ s for s in self.species if s.calculate_compatibility(best_member_species.representative)[1] < self.config.compatibility_species_max_diffs and s.get_best_member().fitness >= best_member_species.get_best_member().fitness * 0.75 ] species_score = np.argsort([ s.get_best_member().fitness + s.fitness for s in valid_species ])[::-1] for i in species_score: if self.species[i] not in new_species: new_species.append(self.species[i]) if len(new_species) == self.config.species_max: break self.species = new_species speices_id = {s.id for s in self.species} unspeciated_genotypes = [ g for g in self.population if g.species_id not in speices_id ] for genotype in unspeciated_genotypes: genotype.species_id = genotype.curr_orig_species_id species_found = False best_species = None best_diffs = None for i in range(len(self.species)): is_compatible, diffs = self.species[i].calculate_compatibility( genotype) if is_compatible: species_found = True if best_species is None or diffs < best_diffs: best_diffs = diffs best_species = i if species_found: self.species[best_species].add_member(genotype, best_diffs) # if len(self.species) >= self.config.species_max and self._get_interspecies_disjoints() > self.config.compatibility_species_max_diffs: # if len(self.species) >= self.config.species_max: # representatives = [s.representative for s in self.species] # representatives_sizes = [len(r.edges) for r in representatives] # avg_size = sum(representatives_sizes) / len(representatives_sizes) # # best_member_species = self.species[np.argsort([s.get_best_member().score for s in self.species])[-1]] # # self.species = [s for s in self.species # if s.calculate_compatibility(best_member_species.representative)[1] < self.config.compatibility_max_diffs * 3] # if self._get_interspecies_disjoints() > max(avg_size * 0.2, self.config.compatibility_max_diffs * 3): # pop_test = [] # for s in self.species: # pop_test.extend(s.get_elite()) # # results, avg_results, seeds = self.get_evaluator().test(pop_test, self.test_seed, evals=True, allow_penalty=False) # # avg_results = [avg_results[i] + pop_test[i].score for i in range(len(pop_test))] # best_genotype = pop_test[avg_results.index(max(avg_results))] # # best_member_species = Species(best_genotype, self._next_species_id(), self.config.compatibility_max_diffs, self.config.species_elitism) # # self.species = [best_member_species] # self.archive.add(self.population) # best_member_species.restart_to_best(self.config, best_member_species.get_best_member(), self.population) # self.get_evaluator().calculate_score(self.population, self.seed) # self._adjust_fitness() # self.mutaiton_manager.after_merge = True for species in self.species: species.evaluate_fitness() species.sort_members_by_score() speciated_genotypes = [] [ speciated_genotypes.append(g) for s in self.species for g in s.members if g not in speciated_genotypes ] # Add unspeciated genotypes self.archive.add( [g for g in self.population if g not in speciated_genotypes]) # For Console Observer self.compatibility = [ g.species_diff for s in self.species for g in s.members ] self.comp_after = self._get_interspecies_disjoints() self.specs_after = len(self.species)
def sort(self, population, species, pop_size, generation): ## Stagnation happens only on the child Q(t) population, before merging, # so the species have a chance to avoid stagnation if they're doing # generally fine # Filter out stagnated species genomes, collect the set of non-stagnated remaining_species = {} # remaining species for stag_sid, stag_s, stagnant in self.stagnation.update( species, generation): # stagnant species: remove genomes from child population if stagnant: self.reporters.species_stagnant(stag_sid, stag_s) population = { id: g for id, g in population.items() if g not in stag_s.members } # non stagnant species: append species to parent species dictionary else: remaining_species[stag_sid] = stag_s # No genomes left. if not remaining_species: species.species = {} return {} ## NSGA-II : step 1 : merge and sort # Merge populations P(t)+Q(t) and sort by non-dominated fronts child_pop = [g for _, g in population.items()] + self.parent_pop # Merge parent P(t) species and child (Qt) species, # so all non-stagnated genomes are covered by species.species species.species = remaining_species for id, sp in self.parent_species.items(): if (id in species.species): species.species[id].members.update(sp.members) else: species.species[id] = sp ## Non-Dominated Sorting (of P(t)+Q(t)) # algorithm data S = {} # genomes dominated by key genome n = {} # counter of genomes dominating key genome F = [] # current dominance front self.fronts = [] # clear dominance fronts # calculate dominance of every genome to every other genome - O(MN²) for p in range(len(child_pop)): S[p] = [] n[p] = 0 for q in range(len(child_pop)): if (p == q): continue # p dominates q if (child_pop[p].fitness.dominates(child_pop[q].fitness)): S[p].append(q) # q dominates p elif (child_pop[q].fitness.dominates(child_pop[p].fitness)): n[p] += 1 # if genome is non-dominated, set rank and add to front if (n[p] == 0): child_pop[p].fitness.rank = 0 F.append(p) # assemble dominance fronts - O(N²) i = 0 # dominance front iterator while (len(F) > 0): # store front self.fronts.append([child_pop[f] for f in F]) # new dominance front Q = [] # for each genome in current front for p in F: # for each genome q dominated by p for q in S[p]: # decrease dominate counter of q n[q] -= 1 # if q reached new front if n[q] == 0: child_pop[q].fitness.rank = -(i + 1) Q.append(q) # iterate front i += 1 F = Q ## NSGA-II : step 2 : pareto selection # Create new parent population P(t+1) from the best fronts # Sort each front by Crowding Distance, to be used on Tournament self.parent_pop = [] for front in self.fronts: ## Calculate crowd-distance of fitnesses # First set distance to zero for genome in front: genome.dist = 0 # List of fitnesses to be used for distance calculation fitnesses = [f.fitness for f in front] # Iterate each fitness parameter (values) for m in range(len(fitnesses[0].values)): # Sort fitnesses by parameter fitnesses.sort(key=lambda f: f.values[m]) # Get scale for normalizing values scale = (fitnesses[-1].values[m] - fitnesses[0].values[m]) # Set edges distance to infinite, to ensure are picked by the next step # This helps keeping the population diverse fitnesses[0].dist = float('inf') fitnesses[-1].dist = float('inf') # Increment distance values for each fitness if (scale > 0): for i in range(1, len(fitnesses) - 1): fitnesses[i].dist += abs( fitnesses[i + 1].values[0] - fitnesses[i - 1].values[0]) / scale ## Sort front by crowd distance # In case distances are equal (mostly on 'inf' values), use the first value to sort front.sort(key=lambda g: (g.fitness.dist, g.fitness.values[0]), reverse=True) ## Assemble new parent population P(t+1) # front fits entirely on the parent population, just append it if (len(self.parent_pop) + len(front) <= pop_size): self.parent_pop += front if (len(self.parent_pop) == pop_size): break # front exceeds parent population, append only what's necessary to reach pop_size else: self.parent_pop += front[:pop_size - len(self.parent_pop)] break ## NSGA-II : post step 2 : Clean Species # Remove the genomes that haven't passed the crowding-distance step # (The ones stagnated are already not on this dict) # Also rebuild SpeciesSet.genome_to_species species.genome_to_species = {} for _, sp in species.species.items(): sp.members = { id: g for id, g in sp.members.items() if g in self.parent_pop } # map genome to species for id, g in sp.members.items(): species.genome_to_species[id] = sp.key # Remove empty species species.species = { id: sp for id, sp in species.species.items() if len(sp.members) > 0 } # self.parent_species should be a deepcopy of the species dictionary, # in order to avoid being modified by the species.speciate() method # the species in here are used to keep track of parent_genomes on next sort self.parent_species = {} for id, sp in species.species.items(): self.parent_species[id] = Species(id, sp.created) self.parent_species[id].members = dict(sp.members) self.parent_species[id].representative = sp.representative ## NSGA-II : end : return parent population P(t+1) to be assigned to child population container Q(t+1) # this container will be used on the Tournament at NSGA2Reproduction.reproduce() # to create the real Q(t+1) population return {g.key: g for g in self.parent_pop}