class Population(object): """ This class implements the core NEAT algorithm. It maintains a list of Species instances, each of which contains a collection of Genome instances. """ def __init__(self, config, initial_population=None): """ :param config: Either a config.Config object or path to a configuration file. :param initial_population: Either an initial set of Genome instances to be used as the initial population, or None, in which case a randomized set of Genomes will be created automatically based on the configuration parameters. """ # If config is not a Config object, assume it is a path to the config file. if not isinstance(config, Config): config = Config(config) # Configure statistics and reporting as requested by the user. self.reporters = ReporterSet() if config.collect_statistics: self.statistics = StatisticsReporter() self.add_reporter(self.statistics) else: self.statistics = None if config.report: self.add_reporter(StdOutReporter()) self.config = config self.species_indexer = Indexer(1) self.genome_indexer = Indexer(1) self.reproduction = config.reproduction_type(self.config, self.reporters, self.genome_indexer) self.species = [] self.generation = -1 self.total_evaluations = 0 # Create a population if one is not given, then partition into species. if initial_population is None: initial_population = self._create_population() self._speciate(initial_population) def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) def load_checkpoint(self, filename): '''Resumes the simulation from a previous saved point.''' self.reporters.loading_checkpoint(filename) with gzip.open(filename) as f: (self.species, self.generation, random_state) = pickle.load(f) random.setstate(random_state) def save_checkpoint(self, filename=None, checkpoint_type="user"): """ Save the current simulation state. """ if filename is None: filename = 'neat-checkpoint-{0}'.format(self.generation) self.reporters.saving_checkpoint(checkpoint_type, filename) with gzip.open(filename, 'w', compresslevel=5) as f: data = (self.species, self.generation, random.getstate()) pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL) def _create_population(self): # Create a collection of unconnected genomes with no hidden nodes. new_population = [] # TODO: The genotype class should know how to do everything below, based # solely on what's in the config object. This allows users to completely # replace the initial population creation scheme if they choose. for i in range(self.config.pop_size): g_id = self.genome_indexer.get_next() g = self.config.genotype.create_unconnected(g_id, self.config) new_population.append(g) # Add hidden nodes if requested. if self.config.hidden_nodes > 0: for g in new_population: g.add_hidden_nodes(self.config.hidden_nodes) # Add connections based on initial connectivity type. if self.config.initial_connection == 'fs_neat': for g in new_population: g.connect_fs_neat() elif self.config.initial_connection == 'fully_connected': for g in new_population: g.connect_full() elif self.config.initial_connection == 'partial': for g in new_population: g.connect_partial(self.config.connection_fraction) return new_population 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 run(self, fitness_function, n): """ Runs NEAT's genetic algorithm for n generations. The user-provided fitness_function should take one argument, a list of all genomes in the population, and its return value is ignored. This function is free to maintain external state, perform evaluations in parallel, and probably any other thing you want. The only requirement is that each individual's fitness member must be set to a floating point value after this function returns. It is assumed that fitness_function does not modify the list of genomes, or the genomes themselves, apart from updating the fitness member. """ # Remember start time for saving timed checkpoints. last_checkpoint = time.time() for g in range(n): self.generation += 1 self.reporters.start_generation(self.generation) # Collect a list of all members from all species. population = [] for s in self.species: population.extend(s.members) # Evaluate all individuals in the population using the user-provided function. # TODO: Add an option to only evaluate each genome once, to reduce number of # fitness evaluations in cases where the fitness is known to be the same if the # genome doesn't change--in these cases, evaluating unmodified elites in each # generation is a waste of time. fitness_function(population) self.total_evaluations += len(population) # Gather and report statistics. best = max(population) self.reporters.post_evaluate(population, self.species, best) # Save the best genome from the current generation if requested. if self.config.save_best: with open('best_genome_' + str(self.generation), 'wb') as f: pickle.dump(best, f) # End if the fitness threshold is reached. if best.fitness >= self.config.max_fitness_threshold: self.reporters.found_solution(self.generation, best) break # Create the next generation from the current generation. self.species, new_population = self.reproduction.reproduce( self.species, self.config.pop_size) # Check for complete extinction if not self.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: new_population = self._create_population() else: raise CompleteExtinctionException() # Update species age. for s in self.species: s.age += 1 # Divide the new population into species. self._speciate(new_population) # Save checkpoints if necessary. if self.config.checkpoint_time_interval is not None: timed_checkpoint_due = last_checkpoint + 60 * self.config.checkpoint_time_interval if time.time() >= timed_checkpoint_due: self.save_checkpoint(checkpoint_type="timed") last_checkpoint = time.time() if self.config.checkpoint_gen_interval is not None \ and self.generation % self.config.checkpoint_gen_interval == 0: self.save_checkpoint(checkpoint_type="generation") self.reporters.end_generation()
class My_Population(Population): """ This class implements the core evolution algorithm: 1. Evaluate fitness of all genomes. 2. Check to see if the termination criterion is satisfied; exit if it is. 3. Generate the next generation from the current population. 4. Partition the new generation into species based on genetic similarity. 5. Go to 1. """ def __init__(self, config, initial_state=None): super().__init__(config, initial_state) self.reporters = ReporterSet() self.config = config stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type( config.reproduction_config, self.reporters, stagnation) if config.fitness_criterion == 'max': self.fitness_criterion = max elif config.fitness_criterion == 'min': self.fitness_criterion = min elif config.fitness_criterion == 'mean': self.fitness_criterion = mean elif not config.no_fitness_termination: raise RuntimeError("Unexpected fitness_criterion: {0!r}".format( config.fitness_criterion)) if initial_state is None: # Create a population from scratch, then partition into species. self.population = self.reproduction.create_new( config.genome_type, config.genome_config, config.pop_size) self.species = config.species_set_type(config.species_set_config, self.reporters) self.generation = 0 self.species.speciate(config, self.population, self.generation) else: self.population, self.species, self.generation = initial_state self.best_genome = None def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) 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. """ list_of_best = list() 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 list_of_best.append(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, list_of_best
class TotalPopulation(object): def __init__(self, config, config_ghost, initial_state=None): self.reporters = ReporterSet() self.reporters_g_1 = ReporterSet() self.reporters_g_2 = ReporterSet() self.config = config self.config_ghost = config_ghost stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type( config.reproduction_config, self.reporters, stagnation) self.reproduction_ghost = self.config_ghost.reproduction_type( config_ghost.reproduction_config, self.reporters_g_1, stagnation) if config.fitness_criterion == 'max': self.fitness_criterion = max elif config.fitness_criterion == 'min': self.fitness_criterion = min elif config.fitness_criterion == 'mean': self.fitness_criterion = mean else: raise Exception("Unexpected fitness_criterion: {0!r}".format( config.fitness_criterion)) if initial_state is None: # Create a population from scratch, then partition into species. self.pacman_population = self.reproduction.create_new( config.genome_type, config.genome_config, config.pop_size) self.pacman_species = config.species_set_type( config, self.reporters) self.ghost_population_1 = self.reproduction_ghost.create_new( config_ghost.genome_type, config_ghost.genome_config, config_ghost.pop_size) self.ghost_population_2 = self.reproduction_ghost.create_new( config_ghost.genome_type, config_ghost.genome_config, config_ghost.pop_size) self.ghost_species_1 = config.species_set_type( config_ghost, self.reporters_g_1) self.ghost_species_2 = config.species_set_type( config_ghost, self.reporters_g_2) self.generation = 0 self.pacman_species.speciate(config, self.pacman_population, self.generation) self.ghost_species_1.speciate(config_ghost, self.ghost_population_1, self.generation) self.ghost_species_2.speciate(config_ghost, self.ghost_population_2, self.generation) else: self.pacman_population, self.pacman_species, self.generation = initial_state self.ghost_population_1, self.ghost_species_1, self.generation = initial_state self.ghost_population_2, self.ghost_species_2, self.generation = initial_state self.best_genome_pacman = None self.best_genome_ghost_1 = None self.best_genome_ghost_2 = None def add_reporter_pacman(self, reporter): self.reporters.add(reporter) def remove_reporter_pacman(self, reporter): self.reporters.remove(reporter) def add_reporter_ghost_1(self, reporter): self.reporters_g_1.add(reporter) def remove_reporter_ghost_1(self, reporter): self.reporters_g_1.remove(reporter) def add_reporter_ghost_2(self, reporter): self.reporters_g_2.add(reporter) def remove_reporter_ghost_2(self, reporter): self.reporters_g_2.remove(reporter) def run(self, evaluator, 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. """ k = 0 while n is None or k < n: k += 1 self.reporters.start_generation(self.generation) self.reporters_g_1.start_generation(self.generation) self.reporters_g_2.start_generation(self.generation) # Run the fitness evaluation function that will use all three populations evaluator(list(iteritems(self.pacman_population)), list(iteritems(self.ghost_population_1)), list(iteritems(self.ghost_population_2)), self.config, self.config_ghost) # Gather and report statistics. bestp = None for g in itervalues(self.pacman_population): if bestp is None or g.fitness > bestp.fitness: bestp = g bestg1 = None for g in itervalues(self.ghost_population_1): if bestg1 is None or g.fitness > bestg1.fitness: bestg1 = g bestg2 = None for g in itervalues(self.ghost_population_2): if not g.fitness: continue if bestg2 is None or g.fitness > bestg2.fitness: bestg2 = g self.reporters.post_evaluate(self.config, self.pacman_population, self.pacman_species, bestp) self.reporters_g_1.post_evaluate(self.config_ghost, self.ghost_population_1, self.ghost_species_1, bestg1) self.reporters_g_2.post_evaluate(self.config_ghost, self.ghost_population_2, self.ghost_species_2, bestg2) # Track the best genome ever seen. if self.best_genome_pacman is None or bestp.fitness > self.best_genome_pacman.fitness: self.best_genome_pacman = bestp if self.best_genome_ghost_1 is None or bestg1.fitness > self.best_genome_ghost_1.fitness: self.best_genome_ghost_1 = bestg1 if self.best_genome_ghost_2 is None or bestg2.fitness > self.best_genome_ghost_2.fitness: self.best_genome_ghost_2 = bestg2 # End if the fitness threshold is reached. fv = self.fitness_criterion( g.fitness for g in itervalues(self.pacman_population)) if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, bestp) break # Create the next generation from the current generation. self.pacman_population = self.reproduction.reproduce( self.config, self.pacman_species, self.config.pop_size, self.generation) self.ghost_population_1 = self.reproduction_ghost.reproduce( self.config_ghost, self.ghost_species_1, self.config_ghost.pop_size, self.generation) self.ghost_population_2 = self.reproduction_ghost.reproduce( self.config_ghost, self.ghost_species_2, self.config_ghost.pop_size, self.generation) # Check for complete extinction. if not self.pacman_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.pacman_population = self.reproduction.create_new( self.config.genome_type, self.config.genome_config, self.config.pop_size) else: raise CompleteExtinctionException() if not self.ghost_species_1.species: self.reporters_g_1.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config_ghost.reset_on_extinction: self.ghost_population_1 = self.reproduction_ghost.create_new( self.config_ghost.genome_type, self.config_ghost.genome_config, self.config_ghost.pop_size) else: raise CompleteExtinctionException() if not self.ghost_species_2.species: self.reporters_g_2.complete_extinction() # If requested by the user, create a completely new population, # otherwise raise an exception. if self.config_ghost.reset_on_extinction: self.ghost_population_2 = self.reproduction_ghost.create_new( self.config_ghost.genome_type, self.config_ghost.genome_config, self.config_ghost.pop_size) else: raise CompleteExtinctionException() # Divide the new population into species. self.pacman_species.speciate(self.config, self.pacman_population, self.generation) self.ghost_species_1.speciate(self.config_ghost, self.ghost_population_1, self.generation) self.ghost_species_2.speciate(self.config_ghost, self.ghost_population_2, self.generation) self.reporters.end_generation(self.config, self.pacman_population, self.pacman_species) self.reporters_g_1.end_generation(self.config_ghost, self.ghost_population_1, self.ghost_species_1) self.reporters_g_2.end_generation(self.config_ghost, self.ghost_population_2, self.ghost_species_2) self.generation += 1 return self.best_genome_pacman, self.best_genome_ghost_1, self.best_genome_ghost_2
class Population(object): """ This class implements the core evolution algorithm: 1. Evaluate fitness of all genomes. 2. Check to see if the termination criterion is satisfied; exit if it is. 3. Generate the next generation from the current population. 4. Partition the new generation into species based on genetic similarity. 5. Go to 1. """ def __init__(self, config, num_pop=None, initial_state=None): self.reporters = ReporterSet() self.config = config if num_pop: self.global_fitnesses = GlobalFitnesses(num_pop) else: self.global_fitnesses = GlobalFitnesses(0) stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type( config.reproduction_config, self.reporters, stagnation) if config.fitness_criterion == 'max': self.fitness_criterion = max elif config.fitness_criterion == 'min': self.fitness_criterion = min elif config.fitness_criterion == 'mean': self.fitness_criterion = mean elif not config.no_fitness_termination: raise RuntimeError("Unexpected fitness_criterion: {0!r}".format( config.fitness_criterion)) if initial_state is None: # Create a population from scratch, then partition into species. self.population = self.reproduction.create_new( config.genome_type, config.genome_config, config.pop_size) self.species = config.species_set_type(config.species_set_config, self.reporters) self.generation = 0 self.species.speciate(config, self.population, self.generation) else: self.population, self.species, self.generation = initial_state self.best_genome = None def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) 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. """ start_time = time.time() if self.config.no_fitness_termination and (n is None): raise RuntimeError( "Cannot have no generational limit with no fitness termination" ) k = 0 while time.time() - start_time < 500: 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 self.fitness_criterion == "min": if best is None or g.fitness < best.fitness: best = g else: 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.fitness_criterion == "min": if self.best_genome is None or best.fitness < self.best_genome.fitness: self.best_genome = best else: if self.best_genome is None or best.fitness > self.best_genome.fitness: self.best_genome = best ''' # Create the next generation from the current generation. current_best_seen, self.population = self.reproduction.reproduce( self.config, self.species, self.config.pop_size, self.generation, fitness_function) if self.fitness_criterion == min: if self.best_genome is None or current_best_seen.fitness < self.best_genome.fitness: self.best_genome = current_best_seen else: if self.best_genome is None or current_best_seen.fitness > self.best_genome.fitness: self.best_genome = current_best_seen print("--- %s seconds ---" % (time.time() - start_time)) print(self.best_genome.key, self.best_genome.fitness, self.best_genome.size()) print(current_best_seen.key, current_best_seen.fitness, current_best_seen.size()) self.global_fitnesses.add_fitness(self.best_genome.fitness) fv = self.best_genome.fitness if self.fitness_criterion == min: if fv <= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, self.best_genome) break else: if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, self.best_genome) break # 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) print("--- %s seconds ---" % (time.time() - start_time)) print(self.global_fitnesses.get_fitnesses()) self.global_fitnesses.save_fig() return self.best_genome
class Population(object): """ This class implements the core evolution algorithm: 1. Evaluate fitness of all genomes. 2. Check to see if the termination criterion is satisfied; exit if it is. 3. Generate the next generation from the current population. 4. Partition the new generation into species based on genetic similarity. 5. Go to 1. """ def __init__(self, config, initial_state=None): self.reporters = ReporterSet() self.config = config stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type(config.reproduction_config, self.reporters, stagnation) if config.fitness_criterion == 'max': self.fitness_criterion = max elif config.fitness_criterion == 'min': self.fitness_criterion = min elif config.fitness_criterion == 'mean': self.fitness_criterion = mean elif not config.no_fitness_termination: raise RuntimeError( "Unexpected fitness_criterion: {0!r}".format(config.fitness_criterion)) if initial_state is None: # Create a population from scratch, then partition into species. self.population = self.reproduction.create_new(config.genome_type, config.genome_config, config.pop_size) self.species = config.species_set_type(config.species_set_config, self.reporters) self.generation = 0 self.species.speciate(config, self.population, self.generation) else: self.population, self.species, self.generation = initial_state self.best_genome = None def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) def run(self, fitness_function, trafic, SUMO, 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. """ number_episodes=n send_iterator=0 if self.config.no_fitness_termination and (n is None): raise RuntimeError("Cannot have no generational limit with no fitness termination") """Initialise simulation =======================================================================================""" traci.route.add(trafic.vehicle_ego.RouteID, trafic.vehicle_ego.Route) if trafic.vehicle2_exist: traci.route.add(trafic.vehicle_2.RouteID, trafic.vehicle_2.Route) if trafic.vehicle3_exist: traci.route.add(trafic.vehicle_3.RouteID, trafic.vehicle_3.Route) traci.vehicletype.setSpeedFactor(typeID='traffic_vehicle', factor=5.0) cum_reward = np.zeros((number_episodes, 1)) best_cum_reward = -1000000 reward_mean100 = np.zeros((number_episodes, 1)) length_episode = np.zeros((number_episodes, 1)) data_export = np.zeros((number_episodes, 2)) restart_step = 0 # counter for calculating the reset timing when the simulation time gets close to 24 days cum_reward_evaluation = [0] # list for cum reward of evaluation episodes evaluation = False sub_ego = {} # traci.vehicle.setSpeedMode(trafic.vehicle_ego.ID, 16) if trafic.training: trafic.vehicle_ego.depart_speed = np.random.randint(0, 30, size=number_episodes) else: trafic.vehicle_ego.depart_speed = ego_depart_speed traci.trafficlight.setProgram(tlsID='junction1', programID=TLS_ID) k = 0 while n is None or k < n: k += 1 episode=k try: # for keyboard interrupt """Check if total simulation time is close to 24 days ======================================================""" # TraCI time inputs have a maximum value of ~24days --> restart SUMO to reset time if np.sum(length_episode[restart_step:])*SUMO.sim['timestep'] > 2000000: print('Almost 24 days of simulation time reached! Restarting SUMO and continue with next episode...') traci.close() traci.start(['sumo', '-c', 'SUMO_config.sumocfg']) traci.route.add(trafic.vehicle_ego.RouteID, trafic.vehicle_ego.Route) if trafic.vehicle2_exist: traci.route.add(trafic.vehicle_2.RouteID, trafic.vehicle_2.Route) if trafic.vehicle3_exist: traci.route.add(trafic.vehicle_3.RouteID, trafic.vehicle_3.Route) restart_step = episode print('Episode: ', episode, '/', number_episodes) send_iterator+=1 if send_iterator==10: msg='Episode: '+ str(episode)+ '/'+ str(number_episodes) andIsendtomyself(msg) send_iterator=0 """Initialise episode ==================================================================================""" # SUMO.init_vars_episode() # dynamics_ego.reset_variables() # if controller == 'DQN' or controller == 'DDPG' or controller == 'hybrid_a' or controller == 'DDPG_v': # nn_controller.reset_variables() # if controller == 'ACC' or controller == 'hybrid_a': # acc_controller.create_mode_map() # if exploration_policy == 'ACC': # explo_policy.create_mode_map() # if (controller == 'DDPG' or controller == 'hybrid_a' or controller == 'DDPG_v') and ((episode+1) % 5 == 0): # perform an evaluation episode (without exploration noise) every x episodes to observe the cum reward progress # evaluation = True """Anmerkung: Hier werden einige Variationen des Verkehrsszenarios für meine Trainingsepisoden definiert, wenn 'training = True' gesetzt ist. Im Fall 'training = False' oder 'evaluation = True' (Evaluierungsepisoden unter gleichen Randbedingungen) wird immer eine # Episode mit gleichen Randbedingungen (z.B. Geschwindigkeitsprofil vorausfahrendes Fahrzeug) gesetzt""" # if trafic.evaluation: # traci.vehicle.add(trafic.vehicle_ego.ID, trafic.vehicle_ego.RouteID, departSpeed='0', # typeID='ego_vehicle') # Ego vehicle # traci.trafficlight.setPhase('junction1', 0) # set traffic light phase to 0 for evaluation (same conditions) # else: # traci.vehicle.add(trafic.vehicle_ego.ID, trafic.vehicle_ego.RouteID, departSpeed=np.array2string(trafic.vehicle_ego.depart_speed[episode]), typeID='ego_vehicle') # Ego vehicle if not trafic.training: traci.trafficlight.setPhase('junction1', 0) if trafic.training and not evaluation and trafic.vehicle3_exist: trafic.vehicle3 = np.random.choice([True, False], p=[0.95, 0.05]) traci.lane.setMaxSpeed('gneE01_0', np.random.choice([8.33, 13.89, 19.44, 25.])) traci.lane.setMaxSpeed('gneE02_0', np.random.choice([8.33, 13.89, 19.44, 25.])) traci.lane.setMaxSpeed('startedge_0', np.random.choice([8.33, 13.89, 19.44, 25.])) SUMO.create_v_profile_prec(a=SUMO.prec_train_amplitude[episode-1], c=SUMO.prec_train_mean[episode-1]) else: trafic.vehicle3 = vehicle3_exist traci.lane.setMaxSpeed('startedge_0', 13.89) # 13.89 traci.lane.setMaxSpeed('gneE01_0', 19.44) # 19.44 traci.lane.setMaxSpeed('gneE02_0', 13.89) # 13.89 traci.lane.setMaxSpeed('stopedge_0', 8.33) # 8.33 trafic.episoden_variante=np.random.rand()*240. # if trafic.vehicle2_exist: # traci.vehicle.add(vehicle_2.ID, vehicle_2.RouteID, typeID='traffic_vehicle') # preceding vehicle 1 # if trafic.vehicle3: # traci.vehicle.add(trafic.vehicle_3.ID, trafic.vehicle_3.RouteID, typeID='traffic_vehicle') # preceding vehicle 2 # if trafic.training and not evaluation: # traci.vehicle.moveTo(trafic.vehicle_3.ID, 'gneE01_0', np.random.rand()*240.) # else: # traci.vehicle.moveTo(trafic.vehicle_3.ID, 'gneE01_0', 0.) # # traci.simulationStep() # to spawn vehicles ## if controller != 'SUMO': ## traci.vehicle.setSpeedMode(trafic.vehicle_ego.ID, 16) # only emergency stopping at red traffic lights --> episode ends # if trafic.vehicle2_exist: # traci.vehicle.setSpeedMode(trafic.vehicle_2.ID, 17) # if trafic.vehicle3: # traci.vehicle.setSpeedMode(trafic.vehicle_3.ID, 17) # # SUMO.currentvehiclelist = traci.vehicle.getIDList() # # # SUMO subscriptions # traci.vehicle.subscribeLeader(trafic.vehicle_ego.ID, 10000) # traci.vehicle.subscribe(trafic.vehicle_ego.ID, [traci.constants.VAR_SPEED, traci.constants.VAR_BEST_LANES, traci.constants.VAR_FUELCONSUMPTION, # traci.constants.VAR_NEXT_TLS, traci.constants.VAR_ALLOWED_SPEED, traci.constants.VAR_LANE_ID]) # self.reporters.start_generation(self.generation) # print(self.population[49+k]) # Evaluate all genomes using the user-provided function. pool=Pool(processes=os.cpu_count()) pool.starmap(fitness_function, zip(list(iteritems(self.population)), repeat(self.config), repeat(episode))) # print(self.fitness) # Gather and report statistics. best = None for g in itervalues(self.population): if best is None or g.fitness > best.fitness: best = g # print(best.fitness, best.size(),self.species.get_species_id(best.key),best.key) 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) # # print('Cumulative Reward:', cum_reward[episode]) # if evaluation: # cum_reward_evaluation.append(cum_reward[episode]) # evaluation = False # if cum_reward[episode] > best_cum_reward: # nn_controller.save_models(savefile_best_actor+'_'+str(episode), savefile_best_critic+'_'+str(episode)) # best_cum_reward = cum_reward[episode] # # if training and (controller == 'DQN' or controller == 'hybrid_a' or controller == 'DDPG' or controller == 'DDPG_v') and liveplot: # reward_mean100[episode] = nn_controller.reward_mean_100_running(cum_reward, episode) # nn_controller.weight_observer(episode) # plot_running(reward_mean100, episode, cum_reward_evaluation) # data_export[:, 0] = cum_reward[:, 0] # data_export[:, 1] = length_episode[:, 0] # if training: # try: # if (episode+1) % 25 == 0: # ==> save rewards every 50 episodes # np.savetxt(savefile_reward, data_export) # if (episode+1) % 25 == 0: # save model every 50 episodes # nn_controller.save_models(savefile_model_actor, savefile_model_critic) # # except OSError: # print('File saving failed') # pass # # if acc_controller: # acc_controller.reset_integral_error() # except KeyboardInterrupt: print('Manual interrupt') break traci.close() # traci.start(['sumo-gui', '-c', 'SUMO_config.sumocfg']) # sbr.result(self.best_genome, self.config, trafic) # sbr.eval_genomes(self.best_genome, self.config, 0, SUMO, trafic) now=datetime.now() nowstr=now.strftime('%Y%m%d%H%M%S') with open('H:\\MT\\Python\\NEAT und SUMO\\saved models\\'+'best_genome_neat'+nowstr , 'wb') as f: pickle.dump(self.best_genome, f) return self.best_genome
class Population(object): """ This class implements the core evolution algorithm: 1. Evaluate fitness of all genomes. 2. Check to see if the termination criterion is satisfied; exit if it is. 3. Generate the next generation from the current population. 4. Partition the new generation into species based on genetic similarity. 5. Go to 1. """ def __init__(self, config, initial_state=None): self.reporters = ReporterSet() self.config = config stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type( config.reproduction_config, self.reporters, stagnation) if config.fitness_criterion == 'max': self.fitness_criterion = max elif config.fitness_criterion == 'min': self.fitness_criterion = min elif config.fitness_criterion == 'mean': self.fitness_criterion = mean elif not config.no_fitness_termination: raise RuntimeError("Unexpected fitness_criterion: {0!r}".format( config.fitness_criterion)) if initial_state is None: # Create a population from scratch, then partition into species. self.population = self.reproduction.create_new( config.genome_type, config.genome_config, config.pop_size) self.species = config.species_set_type(config.species_set_config, self.reporters) self.generation = 0 self.species.speciate(config, self.population, self.generation) else: self.population, self.species, self.generation = initial_state if bool(self.config.novelty_search_config.novelty_search_enabled ) == True: self.novelty = config.novelty_search_type() self.best_genome = None def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) 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 if bool(self.config.novelty_search_config.novelty_search_enabled ) == True: all_behaviors = [] all_populations = [] while n is None or k < n: k += 1 self.reporters.start_generation(self.generation) # Invoke novelty search if enabled in parameters, else proceed normally with objective function if bool(self.config.novelty_search_config.novelty_search_enabled ) == True: # print('Novelty Search Enabled!') # fitness function should input values for genome.behavior, NOT genome.fitness fitness_function(list(self.population.items()), self.config, iteration=k - 1) # Novelty Search function is called, which carries out two steps: # Step 1: Euclidean distance/KNN is called to calculate novelty of each genome in the population and genome.fitness = novelty metric. # Step 2: Genome behavior is compared to behavior of novel archives. If a genome is novel enough, it is appended to the novel archive list novelMembers, population_behavior = self.novelty.calculateNovelty( list(self.population.items()), self.config, iteration=k) all_behaviors.append(population_behavior) all_populations.append(list(self.population.values())) else: # print('Novelty Search Disabled!') # Evaluate all genomes using the user-provided function. # fitness_function(list(self.population.items()), self.config) fitness_function(list(self.population.items()), self.config, iteration=k - 1) # Gather and report statistics. best = None for g in self.population.values(): if g.fitness is None: raise RuntimeError( "Fitness not assigned to genome {}".format(g.key)) 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: # if self.best_genome != None: # print('potential best fitness: ', best.fitness) # print('current best fitness: ', self.best_genome.fitness) # print('best.fitness > self.best_genome.fitness', best.fitness > self.best_genome.fitness) # self.best_genome = copy.deepcopy(best) self.best_genome = best # print('BEST GENOME FITNESS: ', self.best_genome.fitness) if not self.config.no_fitness_termination: # End if the fitness threshold is reached. fv = self.fitness_criterion(g.fitness for g in self.population.values()) if fv >= self.config.fitness_threshold: self.reporters.found_solution(self.config, self.generation, best) break # Create the next generation from the current generation. old_population = self.population 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) # If novelty search is disabled if not self.config.novelty_search_config.novelty_search_enabled: return self.best_genome else: return self.best_genome, novelMembers, all_behaviors, all_populations
class Population(object): """ This class implements the core evolution algorithm: 1. Evaluate fitness of all genomes. 2. Check to see if the termination criterion is satisfied; exit if it is. 3. Generate the next generation from the current population. 4. Partition the new generation into species based on genetic similarity. 5. Go to 1. """ def __init__(self, config, initial_state=None): print("Path:", sys.path) self.reporters = ReporterSet() self.config = config stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type( config.reproduction_config, self.reporters, stagnation) if config.fitness_criterion == 'max': self.fitness_criterion = max elif config.fitness_criterion == 'min': self.fitness_criterion = min elif config.fitness_criterion == 'mean': self.fitness_criterion = mean elif not config.no_fitness_termination: raise RuntimeError("Unexpected fitness_criterion: {0!r}".format( config.fitness_criterion)) if initial_state is None: # Create a population from scratch, then partition into species. self.population = self.reproduction.create_new( config.genome_type, config.genome_config, config.pop_size) self.species = config.species_set_type(config.species_set_config, self.reporters) self.generation = 0 self.species.speciate(config, self.population, self.generation) self.k = 0 else: self.population, self.species, self.generation = initial_state self.best_genome = None def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) def start_compute(self, current_genome_list): """ 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: raise RuntimeError( "Cannot have no generational limit with no fitness termination" ) self.dump_genomes_pop(current_genome_list) def dump_genomes_pop(self, current_genome_list): print("Dumping pop and genomes of size: ", len(current_genome_list)) if self.config.no_fitness_termination: print("no fitness termination") self.reporters.found_solution(self.config, self.generation, self.best_genome) for genome in itervalues(self.population): genome.fitness = 0 self.reporters.start_generation(self.generation) pop_loc = neat_path + "pklDumps/pop.pkl" with open(pop_loc, 'wb') as popPklDump: print("Dumping population object to: ", neat_path, "pklDumps/pop") pickle.dump(self, popPklDump) print("Dumping current genomes") del current_genome_list[:] for index, genome in iteritems(self.population): print("Currently dumping: ", genome.key) current_genome_list.append(genome.key) with open( neat_path + "pklDumps/genome_" + str(genome.key) + ".pkl", 'wb') as nnPklDump: pickle.dump(genome, nnPklDump) print("Dump complete") if self.best_genome is not None: print("best genome now: ", self.best_genome.key) else: print("Best genome null") # Called when ghc is done with all the neural nets in this generation def continue_processing(self, current_genome_list): print("Continue processing") # self.population = {} for genome_key in current_genome_list: print("Currently opening: ", genome_key) with open( neat_path + "pklDumps/genome_" + str(genome_key) + ".pkl", 'rb') as nnPklRead: current_gen = pickle.load(nnPklRead) self.population[genome_key].fitness = current_gen.fitness 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) print("Chosen current best as: ", best.key) # Track the best genome ever seen. if self.best_genome is None or best.fitness > self.best_genome.fitness: self.best_genome = best print("Chosen overall best as: ", self.best_genome.key) 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) return # 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 # continue to top of loop for next k self.dump_genomes_pop(current_genome_list) def get_winner(self): print("Get winner", self.best_genome.key) return self.best_genome
class Population(object): """ This class implements the core NEAT algorithm. It maintains a list of Species instances, each of which contains a collection of Genome instances. """ def __init__(self, config, initial_population=None): """ :param config: Either a config.Config object or path to a configuration file. :param initial_population: Either an initial set of Genome instances to be used as the initial population, or None, in which case a randomized set of Genomes will be created automatically based on the configuration parameters. """ # If config is not a Config object, assume it is a path to the config file. if not isinstance(config, Config): config = Config(config) # Configure statistics and reporting as requested by the user. self.reporters = ReporterSet() if config.collect_statistics: self.statistics = StatisticsReporter() self.add_reporter(self.statistics) else: self.statistics = None if config.report: self.add_reporter(StdOutReporter()) self.config = config self.species_indexer = Indexer(1) self.genome_indexer = Indexer(1) self.innovation_indexer = InnovationIndexer(0) self.reproduction = config.reproduction_type( self.config, self.reporters, self.genome_indexer, self.innovation_indexer ) self.species = [] self.generation = -1 self.total_evaluations = 0 # Create a population if one is not given, then partition into species. if initial_population is None: initial_population = self._create_population() self._speciate(initial_population) def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) def load_checkpoint(self, filename): """Resumes the simulation from a previous saved point.""" self.reporters.loading_checkpoint(filename) with gzip.open(filename) as f: (self.species, self.generation, random_state) = pickle.load(f) random.setstate(random_state) def save_checkpoint(self, filename=None, checkpoint_type="user"): """ Save the current simulation state. """ if filename is None: filename = "neat-checkpoint-{0}".format(self.generation) self.reporters.saving_checkpoint(checkpoint_type, filename) with gzip.open(filename, "w", compresslevel=5) as f: data = (self.species, self.generation, random.getstate()) pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL) def _create_population(self): # Create a collection of unconnected genomes with no hidden nodes. new_population = [] # TODO: The genotype class should know how to do everything below, based # solely on what's in the config object. This allows users to completely # replace the initial population creation scheme if they choose. for i in range(self.config.pop_size): g_id = self.genome_indexer.next() g = self.config.genotype.create_unconnected(g_id, self.config) new_population.append(g) # Add hidden nodes if requested. if self.config.hidden_nodes > 0: for g in new_population: g.add_hidden_nodes(self.config.hidden_nodes) # Add connections based on initial connectivity type. if self.config.initial_connection == "fs_neat": for g in new_population: g.connect_fs_neat(self.innovation_indexer) elif self.config.initial_connection == "fully_connected": for g in new_population: g.connect_full(self.innovation_indexer) elif self.config.initial_connection == "partial": for g in new_population: g.connect_partial(self.innovation_indexer, self.config.connection_fraction) return new_population 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: if 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.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 run(self, fitness_function, n): """ Runs NEAT's genetic algorithm for n generations. The user-provided fitness_function should take one argument, a list of all genomes in the population, and its return value is ignored. This function is free to maintain external state, perform evaluations in parallel, and probably any other thing you want. The only requirement is that each individual's fitness member must be set to a floating point value after this function returns. It is assumed that fitness_function does not modify the list of genomes, or the genomes themselves, apart from updating the fitness member. """ # Remember start time for saving timed checkpoints. last_checkpoint = time.time() for g in range(n): self.generation += 1 self.reporters.start_generation(self.generation) # Collect a list of all members from all species. population = [] for s in self.species: population.extend(s.members) # Evaluate all individuals in the population using the user-provided function. # TODO: Add an option to only evaluate each genome once, to reduce number of # fitness evaluations in cases where the fitness is known to be the same if the # genome doesn't change--in these cases, evaluating unmodified elites in each # generation is a waste of time. fitness_function(population) self.total_evaluations += len(population) # Gather and report statistics. best = max(population) self.reporters.post_evaluate(population, self.species, best) # Save the best genome from the current generation if requested. if self.config.save_best: with open("best_genome_" + str(self.generation), "wb") as f: pickle.dump(best, f) # End if the fitness threshold is reached. if best.fitness >= self.config.max_fitness_threshold: self.reporters.found_solution(self.generation, best) break # Create the next generation from the current generation. self.species, new_population = self.reproduction.reproduce(self.species, self.config.pop_size) # Check for complete extinction if not self.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: new_population = self._create_population() else: raise CompleteExtinctionException() # Update species age. for s in self.species: s.age += 1 # Divide the new population into species. self._speciate(new_population) # Save checkpoints if necessary. if self.config.checkpoint_time_interval is not None: timed_checkpoint_due = last_checkpoint + 60 * self.config.checkpoint_time_interval if time.time() >= timed_checkpoint_due: self.save_checkpoint(checkpoint_type="timed") last_checkpoint = time.time() if ( self.config.checkpoint_gen_interval is not None and self.generation % self.config.checkpoint_gen_interval == 0 ): self.save_checkpoint(checkpoint_type="generation") self.reporters.end_generation()
class Population(object): """ This class implements the core NEAT algorithm. """ def __init__(self, config, initial_state=None): self.reporters = ReporterSet() self.config = config stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type( config.reproduction_config, self.reporters, stagnation) if initial_state is None: # Create a population from scratch, then partition into species. self.population = self.reproduction.create_new( config.genome_type, config.genome_config, config.pop_size) self.species = config.species_set_type(config) self.species.speciate(config, self.population) self.generation = -1 else: self.population, self.species, self.generation = initial_state self.best_genome = None def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) def run(self, fitness_function, n): """ Runs NEAT's genetic algorithm for at most n generations. The user-provided fitness_function should take two arguments, a list of all genomes in the population, and its return value is ignored. This function is free to maintain external state, perform evaluations in parallel, and probably any other thing you want. Each individual genome's fitness member must be set to a floating point value after this function returns. 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. """ for g in range(n): self.generation += 1 self.reporters.start_generation(self.generation) # Evaluate all individuals in the population using the user-provided function. # TODO: Add an option to only evaluate each genome once, to reduce number of # fitness evaluations in cases where the fitness is known to be the same if the # genome doesn't change--in these cases, evaluating unmodified elites in each # generation is a waste of time. The user can always take care of this in their # fitness function in the time being if they wish. 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 # End if the fitness threshold is reached. if best.fitness >= self.config.max_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) # 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.pop_size) else: raise CompleteExtinctionException() # Update species age. # TODO: Wouldn't it be easier to remember creation time? for s in itervalues(self.species.species): s.age += 1 # Divide the new population into species. self.species.speciate(self.config, self.population) self.reporters.end_generation(self.config, self.population, self.species) return self.best_genome
class Population(object): """ This class implements the core NEAT algorithm. It maintains a list of Species instances, each of which contains a collection of Genome instances. """ def __init__(self, config, initial_population=None): """ :param config: Either a config.Config object or path to a configuration file. :param initial_population: Either an initial set of Genome instances to be used as the initial population, or None, in which case a randomized set of Genomes will be created automatically based on the configuration parameters. """ # If config is not a Config object, assume it is a path to the config file. if not isinstance(config, Config): config = Config(config) # Configure statistics and reporting as requested by the user. self.reporters = ReporterSet() if config.collect_statistics: self.statistics = StatisticsReporter() self.add_reporter(self.statistics) else: self.statistics = None if config.report: self.add_reporter(StdOutReporter()) self.config = config self.reproduction = config.reproduction_type(config, self.reporters) self.species = SpeciesSet(config) self.generation = -1 self.total_evaluations = 0 # Create a population if one is not given, then partition into species. if initial_population is None: initial_population = self.reproduction.create_new(config.pop_size) self.species.speciate(initial_population) def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) def load_checkpoint(self, filename): '''Resumes the simulation from a previous saved point.''' self.reporters.loading_checkpoint(filename) with gzip.open(filename) as f: (self.species, self.generation, random_state) = pickle.load(f) random.setstate(random_state) def save_checkpoint(self, filename=None, checkpoint_type="user"): """ Save the current simulation state. """ if filename is None: filename = 'neat-checkpoint-{0}'.format(self.generation) self.reporters.saving_checkpoint(checkpoint_type, filename) with gzip.open(filename, 'w', compresslevel=5) as f: data = (self.species, self.generation, random.getstate()) pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL) def run(self, fitness_function, n): """ Runs NEAT's genetic algorithm for at most n generations. The user-provided fitness_function should take one argument, a list of all genomes in the population, and its return value is ignored. This function is free to maintain external state, perform evaluations in parallel, and probably any other thing you want. The only requirement is that each individual's fitness member must be set to a floating point value after this function returns. It is assumed that fitness_function does not modify the list of genomes, or the genomes themselves, apart from updating the fitness member. """ # Remember start time for saving timed checkpoints. last_checkpoint = time.time() for g in range(n): self.generation += 1 self.reporters.start_generation(self.generation) # Collect a list of all members from all species. population = [] for s in self.species.species: population.extend(s.members) # Evaluate all individuals in the population using the user-provided function. # TODO: Add an option to only evaluate each genome once, to reduce number of # fitness evaluations in cases where the fitness is known to be the same if the # genome doesn't change--in these cases, evaluating unmodified elites in each # generation is a waste of time. The user can always take care of this in their # fitness function in the time being if they wish. fitness_function(population) self.total_evaluations += len(population) # Gather and report statistics. best = max(population) self.reporters.post_evaluate(population, self.species.species, best) # Save the best genome from the current generation if requested. if self.config.save_best: with open('best_genome_' + str(self.generation), 'wb') as f: pickle.dump(best, f) # End if the fitness threshold is reached. if best.fitness >= self.config.max_fitness_threshold: self.reporters.found_solution(self.generation, best) break # Create the next generation from the current generation. new_population = self.reproduction.reproduce(self.species, self.config.pop_size) # 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: new_population = self.reproduction.create_new(self.config.pop_size) else: raise CompleteExtinctionException() # Update species age. for s in self.species.species: s.age += 1 # Divide the new population into species. self.species.speciate(new_population) # Save checkpoints if necessary. if self.config.checkpoint_time_interval is not None: timed_checkpoint_due = last_checkpoint + 60 * self.config.checkpoint_time_interval if time.time() >= timed_checkpoint_due: self.save_checkpoint(checkpoint_type="timed") last_checkpoint = time.time() if self.config.checkpoint_gen_interval is not None \ and self.generation % self.config.checkpoint_gen_interval == 0: self.save_checkpoint(checkpoint_type="generation") self.reporters.end_generation()
class Population(object): """ This class implements the core NEAT algorithm. It maintains a list of Species instances, each of which contains a collection of Genome instances. """ def __init__(self, config, initial_population=None): """ :param config: Either a config.Config object or path to a configuration file. :param initial_population: Either an initial set of Genome instances to be used as the initial population, or None, in which case a randomized set of Genomes will be created automatically based on the configuration parameters. """ # If config is not a Config object, assume it is a path to the config file. if not isinstance(config, Config): config = Config(config) # Configure statistics and reporting as requested by the user. self.reporters = ReporterSet() if config.collect_statistics: self.statistics = StatisticsReporter() self.add_reporter(self.statistics) else: self.statistics = None if config.report: self.add_reporter(StdOutReporter()) self.config = config self.reproduction = config.reproduction_type(config, self.reporters) self.species = SpeciesSet(config) self.generation = -1 self.total_evaluations = 0 # Create a population if one is not given, then partition into species. if initial_population is None: initial_population = self.reproduction.create_new(config.pop_size) self.species.speciate(initial_population) def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) def load_checkpoint(self, filename): '''Resumes the simulation from a previous saved point.''' self.reporters.loading_checkpoint(filename) with gzip.open(filename) as f: (self.species, self.generation, random_state) = pickle.load(f) random.setstate(random_state) def save_checkpoint(self, filename=None, checkpoint_type="user"): """ Save the current simulation state. """ if filename is None: filename = 'neat-checkpoint-{0}'.format(self.generation) self.reporters.saving_checkpoint(checkpoint_type, filename) with gzip.open(filename, 'w', compresslevel=5) as f: data = (self.species, self.generation, random.getstate()) pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL) def run(self, fitness_function, n): """ Runs NEAT's genetic algorithm for at most n generations. The user-provided fitness_function should take one argument, a list of all genomes in the population, and its return value is ignored. This function is free to maintain external state, perform evaluations in parallel, and probably any other thing you want. The only requirement is that each individual's fitness member must be set to a floating point value after this function returns. It is assumed that fitness_function does not modify the list of genomes, or the genomes themselves, apart from updating the fitness member. """ # Remember start time for saving timed checkpoints. last_checkpoint = time.time() for g in range(n): self.generation += 1 self.reporters.start_generation(self.generation) # Collect a list of all members from all species. population = [] for s in self.species.species: population.extend(s.members) # Evaluate all individuals in the population using the user-provided function. # TODO: Add an option to only evaluate each genome once, to reduce number of # fitness evaluations in cases where the fitness is known to be the same if the # genome doesn't change--in these cases, evaluating unmodified elites in each # generation is a waste of time. The user can always take care of this in their # fitness function in the time being if they wish. fitness_function(population) self.total_evaluations += len(population) # Gather and report statistics. best = max(population) self.reporters.post_evaluate(population, self.species.species, best) # Save the best genome from the current generation if requested. if self.config.save_best: with open('best_genome_' + str(self.generation), 'wb') as f: pickle.dump(best, f) # End if the fitness threshold is reached. if best.fitness >= self.config.max_fitness_threshold: self.reporters.found_solution(self.generation, best) break # Create the next generation from the current generation. new_population = self.reproduction.reproduce( self.species, self.config.pop_size) # 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: new_population = self.reproduction.create_new( self.config.pop_size) else: raise CompleteExtinctionException() # Update species age. for s in self.species.species: s.age += 1 # Divide the new population into species. self.species.speciate(new_population) # Save checkpoints if necessary. if self.config.checkpoint_time_interval is not None: timed_checkpoint_due = last_checkpoint + 60 * self.config.checkpoint_time_interval if time.time() >= timed_checkpoint_due: self.save_checkpoint(checkpoint_type="timed") last_checkpoint = time.time() if self.config.checkpoint_gen_interval is not None \ and self.generation % self.config.checkpoint_gen_interval == 0: self.save_checkpoint(checkpoint_type="generation") self.reporters.end_generation()
class Population(object): """ This class implements the core evolution algorithm: 1. Evaluate fitness of all genomes. 2. Check to see if the termination criterion is satisfied; exit if it is. 3. Generate the next generation from the current population. 4. Partition the new generation into species based on genetic similarity. 5. Go to 1. """ def __init__(self, config, initial_state=None): self.reporters = ReporterSet() self.config = config stagnation = config.stagnation_type(config.stagnation_config, self.reporters) self.reproduction = config.reproduction_type( config.reproduction_config, self.reporters, stagnation) if config.fitness_criterion == 'max': self.fitness_criterion = max elif config.fitness_criterion == 'min': self.fitness_criterion = min elif config.fitness_criterion == 'mean': self.fitness_criterion = mean elif not config.no_fitness_termination: raise RuntimeError("Unexpected fitness_criterion: {0!r}".format( config.fitness_criterion)) if initial_state is None: # Create a population from scratch, then partition into species. self.population = self.reproduction.create_new( config.genome_type, config.genome_config, config.pop_size) self.species = config.species_set_type(config.species_set_config, self.reporters) self.generation = 0 self.species.speciate(config, self.population, self.generation) else: self.population, self.species, self.generation = initial_state self.best_genome = None def add_reporter(self, reporter): self.reporters.add(reporter) def remove_reporter(self, reporter): self.reporters.remove(reporter) 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 dominates(self, one, other): """Return true if each objective of *one* is not strictly worse than the corresponding objective of *other* and at least one objective is strictly better. """ not_equal = False for self_wvalue, other_wvalue in zip(one, other): if self_wvalue > other_wvalue: not_equal = True elif self_wvalue < other_wvalue: return False return not_equal def sortNondominatedNSGA2(self, k, first_front_only=False): """Sort the first *k* *individuals* into different nondomination levels using the "Fast Nondominated Sorting Approach" proposed by Deb et al., see [Deb2002]_. This algorithm has a time complexity of :math:`O(MN^2)`, where :math:`M` is the number of objectives and :math:`N` the number of individuals. :param individuals: A list of individuals to select from. :param k: The number of individuals to select. :param first_front_only: If :obj:`True` sort only the first front and exit. :returns: A list of Pareto fronts (lists), the first list includes nondominated individuals. .. [Deb2002] Deb, Pratab, Agarwal, and Meyarivan, "A fast elitist non-dominated sorting genetic algorithm for multi-objective optimization: NSGA-II", 2002. """ if k == 0: return [] map_fit_ind = defaultdict(list) for key in self.population: ind = self.population[key] map_fit_ind[ind.fitness_mult].append(ind) fits = list(map_fit_ind.keys()) current_front = [] next_front = [] dominating_fits = defaultdict(int) dominated_fits = defaultdict(list) # Rank first Pareto front for i, fit_i in enumerate(fits): for fit_j in fits[i + 1:]: if self.dominates(fit_i, fit_j): dominating_fits[fit_j] += 1 dominated_fits[fit_i].append(fit_j) elif self.dominates(fit_j, fit_i): dominating_fits[fit_i] += 1 dominated_fits[fit_j].append(fit_i) if dominating_fits[fit_i] == 0: current_front.append(fit_i) fronts = [[]] for fit in current_front: fronts[-1].extend(map_fit_ind[fit]) pareto_sorted = len(fronts[-1]) # Rank the next front until all individuals are sorted or # the given number of individual are sorted. if not first_front_only: N = min(len(self.population), k) while pareto_sorted < N: fronts.append([]) for fit_p in current_front: for fit_d in dominated_fits[fit_p]: dominating_fits[fit_d] -= 1 if dominating_fits[fit_d] == 0: next_front.append(fit_d) pareto_sorted += len(map_fit_ind[fit_d]) fronts[-1].extend(map_fit_ind[fit_d]) current_front = next_front next_front = [] return fronts def assignCrowdingDist(self, individuals): """Assign a crowding distance to each individual's fitness. It is done per front. """ if len(individuals) == 0: return distances = [0.0] * len(individuals) crowd = [(ind.fitness_mult, i) for i, ind in enumerate(individuals)] nobj = len(individuals[0].fitness_mult) for i in range(nobj): crowd.sort(key=lambda element: element[0][i]) distances[crowd[0][1]] = float("inf") distances[crowd[-1][1]] = float("inf") if crowd[-1][0][i] == crowd[0][0][i]: continue norm = nobj * float(crowd[-1][0][i] - crowd[0][0][i]) for prev, cur, next in zip(crowd[:-2], crowd[1:-1], crowd[2:]): distances[cur[1]] += (next[0][i] - prev[0][i]) / norm # find max and min distance max_val = -float("inf") min_val = float("inf") flag_plus_inf = False flag_minus_inf = False for dist in distances: if dist != float("inf") and max_val < dist: max_val = dist pass if dist != -float("inf") and min_val > dist: min_val = dist pass if dist == float("inf"): flag_plus_inf = True elif dist == -float("inf"): flag_minus_inf = True pass # set values equal to inf to be max + 0.5 # set values equal to -inf to be max - 0.5 # and rescale the rest if flag_plus_inf: max_val += 0.5 if flag_minus_inf: min_val -= 0.5 for i in range(0, len(distances)): if distances[i] == float("inf"): distances[i] = 1. elif distances[i] == -float("inf"): distances[i] = 0. else: distances[i] = (distances[i] - min_val) / (max_val - min_val) pass pass for i, dist in enumerate(distances): individuals[i].crowding_dist = dist / 2 pass pass def run_NSGA2(self, fitness_function, n=None, multi=False, algo='NSGA-2'): # algo possible values: NSGA-2, PO - pareto-optimality """ 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) # if multi-objective, set fitness if multi: if algo == 'NSGA-2': # sort NSGA-2 fronts = self.sortNondominatedNSGA2(len(self.population)) pass # assign crowding distance for front in fronts: self.assignCrowdingDist(front) # now assign fitness value num_fronts = len(fronts) for i in range(0, num_fronts): front = fronts[i] for el in front: el.fitness = (num_fronts - i) + el.crowding_dist pass pass pass # 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) self.reporters.end_generation(self.config, self.population, self.species) break # Create the next generation from the current generation. new_population = self.reproduction.reproduce( self.config, self.species, self.config.pop_size, self.generation) fitness_function(list(iteritems(new_population)), self.config) self.population = {**self.population, **new_population} # if multi-objective, set fitness if multi: if algo == 'NSGA-2': # sort NSGA-2 fronts = self.sortNondominatedNSGA2(len(self.population)) pass # assign crowding distance for front in fronts: self.assignCrowdingDist(front) # now assign fitness value num_fronts = len(fronts) for i in range(0, num_fronts): front = fronts[i] for el in front: el.fitness = (num_fronts - i) + el.crowding_dist pass pass pass new_population = list(iteritems(self.population)) new_population.sort(reverse=True, key=lambda x: x[1].fitness) self.population = dict(new_population[:self.config.pop_size]) # fitness_function(list(iteritems(self.population)), self.config) # 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