def create(genome, config): """ Receives a genome and returns its phenotype (a FeedForwardNetwork). """ # Gather expressed connections. connections = [ cg.key for cg in itervalues(genome.connections) if cg.enabled ] layers = feed_forward_layers(config.genome_config.input_keys, config.genome_config.output_keys, connections) node_evals = [] for layer in layers: for node in layer: inputs = [] node_expr = [] # currently unused for conn_key in connections: inode, onode = conn_key if onode == node: cg = genome.connections[conn_key] inputs.append((inode, cg.weight)) node_expr.append("v[{}] * {:.7e}".format( inode, cg.weight)) ng = genome.nodes[node] aggregation_function = config.genome_config.aggregation_function_defs.get( ng.aggregation) activation_function = config.genome_config.activation_defs.get( ng.activation) node_evals.append( (node, activation_function, aggregation_function, ng.bias, ng.response, inputs)) return FeedForwardNetwork(config.genome_config.input_keys, config.genome_config.output_keys, node_evals)
def create(genome, config): """ Receives a genome and returns its phenotype (a neural network). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # Gather inputs and expressed connections. node_inputs = {} for cg in itervalues(genome.connections): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if o not in node_inputs: node_inputs[o] = [(i, cg.weight)] else: node_inputs[o].append((i, cg.weight)) neurons = {} for node_key in required: ng = genome.nodes[node_key] inputs = node_inputs.get(node_key, []) neurons[node_key] = IZNeuron(ng.bias, ng.a, ng.b, ng.c, ng.d, inputs) genome_config = config.genome_config return IZNN(neurons, genome_config.input_keys, genome_config.output_keys)
def create(genome, config, time_constant): """ Receives a genome and returns its phenotype (a CTRNN). """ genome_config = config.genome_config required = required_for_output(genome_config.input_keys, genome_config.output_keys, genome.connections) # Gather inputs and expressed connections. node_inputs = {} for cg in itervalues(genome.connections): if not cg.enabled: continue i, o = cg.key if o not in required and i not in required: continue if o not in node_inputs: node_inputs[o] = [(i, cg.weight)] else: node_inputs[o].append((i, cg.weight)) node_evals = {} for node_key, inputs in iteritems(node_inputs): node = genome.nodes[node_key] activation_function = genome_config.activation_defs.get(node.activation) aggregation_function = genome_config.aggregation_function_defs.get(node.aggregation) node_evals[node_key] = CTRNNNodeEval(time_constant, activation_function, aggregation_function, node.bias, node.response, inputs) return CTRNN(genome_config.input_keys, genome_config.output_keys, node_evals)
def advance(self, dt_msec): for n in itervalues(self.neurons): n.current = n.bias for i, w in n.inputs: ineuron = self.neurons.get(i) if ineuron is not None: ivalue = ineuron.fired else: ivalue = self.input_values[i] n.current += ivalue * w for n in itervalues(self.neurons): n.advance(dt_msec) return [self.neurons[i].fired for i in self.outputs]
def post_evaluate(self, config, population, species, best_genome): # pylint: disable=no-self-use fitnesses = [c.fitness for c in itervalues(population)] fit_mean = mean(fitnesses) fit_std = stdev(fitnesses) best_species_id = species.get_species_id(best_genome.key) print('Population\'s average fitness: {0:3.5f} stdev: {1:3.5f}'.format( fit_mean, fit_std)) print('Best fitness: {0:3.5f} - size: {1!r} - species {2} - id {3}'. format(best_genome.fitness, best_genome.size(), best_species_id, best_genome.key))
def update(self, species_set, generation): result = [] for s in itervalues(species_set.species): # If any member of the species is selected (i.e., has a fitness above zero), # mark the species as improved. for m in s.members.values(): if m.fitness > 0: s.last_improved = generation break stagnant_time = generation - s.last_improved is_stagnant = stagnant_time >= self.max_stagnation result.append((s.key, s, is_stagnant)) return result
def run(self, fitness_function, n=None): """ Runs NEAT's genetic algorithm for at most n generations. If n is None, run until solution is found or extinction occurs. The user-provided fitness_function must take only two arguments: 1. The population as a list of (genome id, genome) tuples. 2. The current configuration object. The return value of the fitness function is ignored, but it must assign a Python float to the `fitness` member of each genome. The fitness function is free to maintain external state, perform evaluations in parallel, etc. It is assumed that fitness_function does not modify the list of genomes, the genomes themselves (apart from updating the fitness member), or the configuration object. """ if self.config.no_fitness_termination and (n is None): raise RuntimeError( "Cannot have no generational limit with no fitness termination" ) k = 0 while n is None or k < n: k += 1 self.reporters.start_generation(self.generation) # Evaluate all genomes using the user-provided function. fitness_function(list(iteritems(self.population)), self.config) # Gather and report statistics. best = None for g in itervalues(self.population): if best is None or g.fitness > best.fitness: best = g self.reporters.post_evaluate(self.config, self.population, self.species, best) # Track the best genome ever seen. if self.best_genome is None or best.fitness > self.best_genome.fitness: self.best_genome = best if not self.config.no_fitness_termination: # End if the fitness threshold is reached. fv = self.fitness_criterion( g.fitness for g in itervalues(self.population)) if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, best) break # Create the next generation from the current generation. self.population = self.reproduction.reproduce( self.config, self.species, self.config.pop_size, self.generation) # Check for complete extinction. if not self.species.species: self.reporters.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config.reset_on_extinction: self.population = self.reproduction.create_new( self.config.genome_type, self.config.genome_config, self.config.pop_size) else: raise CompleteExtinctionException() # Divide the new population into species. self.species.speciate(self.config, self.population, self.generation) self.reporters.end_generation(self.config, self.population, self.species) self.generation += 1 if self.config.no_fitness_termination: self.reporters.found_solution(self.config, self.generation, self.best_genome) return self.best_genome
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. 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 candidates: 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 get_fitnesses(self): return [m.fitness for m in itervalues(self.members)]
def reset(self): """Reset all neurons to their default state.""" for n in itervalues(self.neurons): n.reset()