def create(genome): connections = [cg.key for cg in itervalues(genome.connections) if cg.enabled] layers = feed_forward_layers(genome.input_keys, genome.output_keys, connections) mapping_tuples = {} node_evals = [] # Traverse layers for layer in layers: # For each node in each layer, collect all incoming connections to the node for node in layer: incoming_connections = [] for conn_key in connections: input_node, output_node = conn_key if output_node == node: cg = genome.connections[conn_key] incoming_connections.append((input_node, cg.weight)) # Gather node gene information node_gene = genome.nodes[node] activation_function = node_gene.activation node_evals.append((node, activation_function, sum, incoming_connections)) # Gather mapping tuples for key in genome.output_keys: mapping_tuples[key] = genome.nodes[key].cppn_tuple for key in genome.bias_keys: mapping_tuples[key] = genome.nodes[key].cppn_tuple return FeedForwardCPPN(genome.input_keys, genome.output_keys, node_evals, genome.nodes, mapping_tuples)
def report_fitness(pop): ''' Report average, min, and max fitness of a population pop -- population to be reported ''' avg_fitness = 0 # Find best genome in current generation and update avg fitness for genome in itervalues(pop.population): avg_fitness += genome.fitness print("\n=================================================") print("\t\tGeneration: {}".format(pop.current_gen)) print("=================================================") print("Best Fitness \t Avg Fitness \t Champion") print("============ \t =========== \t ========") print("{:.2f} \t\t {:.2f} \t\t {}".format(pop.best_genome.fitness, avg_fitness/pop.size,pop.best_genome.key)) print("=================================================") print("Max Complexity \t Avg Complexity") print("============ \t =========== \t ========") print("{} \t\t {}".format(None, pop.avg_complexity))
def run(self, task, goal, generations=None): ''' Run evolution on a given task for a number of generations or until a goal is reached. task -- the task to be solved goal -- the goal to reach for the given task that defines a solution generations -- the max number of generations to run evolution for ''' self.current_gen = 0 reached_goal = False # Plot data best_fitnesses = [] max_complexity = [] min_complexity = [] avg_complexity = [] while self.current_gen < generations and not reached_goal: # Assess fitness of current population task(list(iteritems(self.population))) # Find best genome in current generation and update avg fitness curr_best = None curr_max_complex = None curr_min_complex = None avg_complexities = 0 for genome in itervalues(self.population): avg_complexities += genome.complexity() # Update generation's most fit if curr_best is None or genome.fitness > curr_best.fitness: curr_best = genome # Update generation's most complex if curr_max_complex is None or genome.complexity( ) > curr_max_complex.complexity(): curr_max_complex = genome # Update generation's least complex if curr_min_complex is None or genome.complexity( ) < curr_min_complex.complexity(): curr_min_complex = genome # Update global best genome if possible if self.best_genome is None or curr_best.fitness > self.best_genome.fitness: self.best_genome = curr_best # Update global most and least complex genomes if self.max_complex_genome is None or curr_max_complex.complexity( ) > self.max_complex_genome.complexity(): self.max_complex_genome = curr_max_complex if self.min_complex_genome is None or curr_min_complex.complexity( ) < self.min_complex_genome.complexity(): self.min_complex_genome = curr_min_complex self.max_dict[self.current_gen] = self.max_complex_genome # Reporters report_fitness(self) report_species(self.species, self.current_gen) report_output(self) best_fitnesses.append(self.best_genome.fitness) max_complexity.append(self.max_complex_genome.complexity()) min_complexity.append(self.min_complex_genome.complexity()) avg_complexity.append( (avg_complexities + 0.0) / len(self.population)) self.avg_complex = (avg_complexities + 0.0) / len(self.population) avg_complexities = 0 # Reached fitness goal, we can stop if self.best_genome.fitness >= goal: reached_goal = True # Create new unspeciated popuation based on current population's fitness self.population = self.reproduction.reproduce_with_species( self.species, self.size, self.current_gen) # Check for species extinction (species did not perform well) if not self.species.species: print("!!! Species went extinct !!!") self.population = self.reproduction.create_new_population( self.size) # Speciate new population self.species.speciate(self.population, self.current_gen) self.current_gen += 1 generations = range(self.current_gen) plot_fitness(generations, best_fitnesses) return self.best_genome
def get_fitnesses(self): return [m.fitness for m in itervalues(self.members)]
def reproduce_with_species(self, species_set, pop_size, generation): ''' Creates and speciates genomes. species_set -- set of current species pop_size -- population size generation -- current generation ''' all_fitnesses = [] remaining_species = [] # Traverse species and grab fitnesses from non-stagnated species for sid, species, species_is_stagnant in self.stagnation.update( species_set, generation): if species_is_stagnant: print("!!! Species {} Stagnated !!!".format(sid)) # self.reporters.species_stagnant(sid, species) pass else: # Add fitnesses of members of current species all_fitnesses.extend(member.fitness for member in itervalues(species.members)) remaining_species.append(species) # No species if not remaining_species: species_set.species = {} return {} # Find min/max fitness across entire population min_population_fitness = min(all_fitnesses) max_population_fitness = max(all_fitnesses) # Do not allow the fitness range to be zero, as we divide by it below. population_fitness_range = max( 1.0, max_population_fitness - min_population_fitness) # Compute adjusted fitness and record minimum species size for species in remaining_species: # Determine current species average fitness mean_species_fitness = mean( [member.fitness for member in itervalues(species.members)]) max_species_fitness = max( [member.fitness for member in itervalues(species.members)]) # Determine current species adjusted fitness and update it species_adjusted_fitness = ( mean_species_fitness - min_population_fitness) / population_fitness_range species.adjusted_fitness = species_adjusted_fitness species.max_fitness = max_species_fitness adjusted_fitnesses = [ species.adjusted_fitness for species in remaining_species ] avg_adjusted_fitness = mean(adjusted_fitnesses) # Compute the number of new members for each species in the new generation. previous_sizes = [ len(species.members) for species in remaining_species ] min_species_size = max(2, self.species_elitism) spawn_amounts = self.compute_species_sizes(adjusted_fitnesses, previous_sizes, pop_size, min_species_size) new_population = {} species_set.species = {} for spawn, species in zip(spawn_amounts, remaining_species): # If elitism is enabled, each species always at least gets to retain its elites. spawn = max(spawn, self.species_elitism) assert spawn > 0 # The species has at least one member for the next generation, so retain it. old_species_members = list(iteritems(species.members)) # Update species with blank slate species.members = {} # Update species in species set accordingly species_set.species[species.key] = species # Sort old species members in order of descending fitness. old_species_members.sort(reverse=True, key=lambda x: x[1].fitness) # Clone elites to new generation. if self.species_elitism > 0: for member_key, member in old_species_members[:self. species_elitism]: new_population[member_key] = member spawn -= 1 # If the species only has room for the elites, move onto next species if spawn <= 0: continue # Only allow fraction of species members to reproduce reproduction_cutoff = int( ceil(self.species_reproduction_threshold * len(old_species_members))) # Use at least two parents no matter what the threshold fraction result is. reproduction_cutoff = max(reproduction_cutoff, 2) old_species_members = old_species_members[:reproduction_cutoff] # Randomly choose parents and produce the number of offspring allotted to the species. # NOTE: Asexual reproduction for now while spawn > 0: spawn -= 1 parent1_key, parent1 = random.choice(old_species_members) # parent2_key, parent2 = random.choice(old_species_members) child_key = next(self.genome_indexer) child = Genome(child_key) # child.crossover(parent1, parent2) child.copy(parent1, generation) child.mutate(generation) new_population[child_key] = child return new_population