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 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 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): """ Receives a genome and returns its phenotype (a RecurrentNetwork). """ 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.append((node_key, activation_function, aggregation_function, node.bias, node.response, inputs)) return RecurrentNetwork(genome_config.input_keys, genome_config.output_keys, node_evals)
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)) res = open("result.csv", "a") res.write('{0:3.5f},{1:3.5f} \n'.format(best_genome.fitness, fit_mean)) res.close() # andrew add if (best_genome.fitness > self.bestFitness): self.bestFitness = best_genome.fitness best = open("best.txt", "a") best.write('\nBest genome:\n{!s}'.format(best_genome)) best.close()
def reset(self): """Reset all neurons to their default state.""" for n in itervalues(self.neurons): n.reset()
def reproduce(self, config, species, pop_size, generation): """ Handles creation of genomes, either from scratch or by sexual or asexual reproduction from parents. """ # TODO: I don't like this modification of the species and stagnation objects, # because it requires internal knowledge of the objects. # Filter out stagnated species, collect the set of non-stagnated # species members, and compute their average adjusted fitness. # The average adjusted fitness scheme (normalized to the interval # [0, 1]) allows the use of negative fitness values without # interfering with the shared fitness scheme. all_fitnesses = [] remaining_species = [] for stag_sid, stag_s, stagnant in self.stagnation.update( species, generation): if stagnant: self.reporters.species_stagnant(stag_sid, stag_s) else: all_fitnesses.extend(m.fitness for m in itervalues(stag_s.members)) remaining_species.append(stag_s) # The above comment was not quite what was happening - now getting fitnesses # only from members of non-stagnated species. #print("#########################################################################{0}".format(remaining_species.__len__())) # No species left. if not remaining_species: species.species = {} return {} # was [] # Find minimum/maximum fitness across the entire population, for use in # species adjusted fitness computation. min_fitness = min(all_fitnesses) max_fitness = max(all_fitnesses) #print('max_fitness ',max_fitness) # Do not allow the fitness range to be zero, as we divide by it below. # TODO: The ``1.0`` below is rather arbitrary, and should be configurable. fitness_range = max(1.0, max_fitness - min_fitness) for afs in remaining_species: # Compute adjusted fitness. msf = mean([m.fitness for m in itervalues(afs.members)]) af = (msf - min_fitness) / fitness_range afs.adjusted_fitness = af adjusted_fitnesses = [s.adjusted_fitness for s in remaining_species] avg_adjusted_fitness = mean(adjusted_fitnesses) # type: float self.reporters.info( "Average adjusted fitness: {:.3f}".format(avg_adjusted_fitness)) # Compute the number of new members for each species in the new generation. previous_sizes = [len(s.members) for s in remaining_species] min_species_size = self.reproduction_config.min_species_size # Isn't the effective min_species_size going to be max(min_species_size, # self.reproduction_config.elitism)? That would probably produce more accurate tracking # of population sizes and relative fitnesses... doing. TODO: document. min_species_size = max(min_species_size, self.reproduction_config.elitism) spawn_amounts = self.compute_spawn(adjusted_fitnesses, previous_sizes, pop_size, min_species_size) new_population = {} species.species = {} for spawn, s in zip(spawn_amounts, remaining_species): # If elitism is enabled, each species always at least gets to retain its elites. spawn = max(spawn, self.reproduction_config.elitism) assert spawn > 0 # The species has at least one member for the next generation, so retain it. old_members = list(iteritems(s.members)) s.members = {} species.species[s.key] = s # Sort members in order of descending fitness. old_members.sort(reverse=True, key=lambda x: x[1].fitness) # Transfer elites to new generation. if self.reproduction_config.elitism > 0: #print('elitism ', self.reproduction_config.elitism) # i是个体的id, m 是 个体 for i, m in old_members[:self.reproduction_config.elitism]: new_population[i] = m print('fitness ', m.fitness, ' ', i) spawn -= 1 if spawn <= 0: continue # Only use the survival threshold fraction to use as parents for the next generation. repro_cutoff = int( math.ceil(self.reproduction_config.survival_threshold * len(old_members))) # Use at least two parents no matter what the threshold fraction result is. repro_cutoff = max(repro_cutoff, 2) old_members = old_members[:repro_cutoff] # Randomly choose parents and produce the number of offspring allotted to the species. while spawn > 0: spawn -= 1 parent1_id, parent1 = random.choice(old_members) parent2_id, parent2 = random.choice(old_members) # Note that if the parents are not distinct, crossover will produce a # genetically identical clone of the parent (but with a different ID). gid = next(self.genome_indexer) child = config.genome_type(gid) child.configure_crossover(parent1, parent2, config.genome_config) child.mutate(config.genome_config) new_population[gid] = child self.ancestors[gid] = (parent1_id, parent2_id) return new_population
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" ) parallel = ParallelEvaluator(num_workers=5, eval_function=fitness_function) if self.generation > 0: # start from saved_state self.reporters.start_generation(self.generation) # 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. self.best_genome.append((best, best.fitness)) # 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 k = 0 while n is None or k < n: k += 1 self.checkpointer.start_generation(self.generation) self.reporters.start_generation(self.generation) # Evaluate all genomes using the user-provided function. #fitness_function(list(iteritems(self.population)), self.config) #print(self.population) #print(self.species) genomes_l = [] for genome_id, genome in list(iteritems(self.population)): if self.has_eval.__contains__(genome_id): continue else: self.has_eval[genome_id] = 1 genomes_l.append((genome_id, genome)) #genomes = parallel.evaluate(list(iteritems(self.population)), self.config) genomes = parallel.evaluate(genomes_l, self.config) #print('---') for genome_id, genome in genomes: #print(genome_id, genome.fitness) self.population[genome_id] = genome self.species.speciate(self.config, self.population, self.generation) #print(self.population) # Gather and report statistics. best = None #idxx = 0 for g in itervalues(self.population): #idxx += 1 #print(g.fitness) #if g.fitness is None: #print(idxx) 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. self.best_genome.append((best, best.fitness)) # 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 #print(self.generation, self.checkpointer.last_generation_checkpoint, self.checkpointer.generation_interval ) if self.generation - self.checkpointer.last_generation_checkpoint >= \ self.checkpointer.generation_interval: #pass print('save checkpoint') self.checkpointer.end_generation(self.config, self.population, self.species) #print(self.species) # 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[-1][0]) 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)]