def evolve(pop: Population, pop_name: str): """Evolve with the set constraints in mind.""" # Create the next generation from the current generation pop.population = pop.reproduction.reproduce( config=pop.config, species=pop.species, generation=pop.generation, logger=pop.log, ) # Constraint each of the population's new genomes to the given topology for g in pop.population.values(): enforce_topology(pop_name, genome=g) # Check for complete extinction if not pop.species.species: pop.reporters.complete_extinction(logger=pop.log) # If requested by the user, create a completely new population, otherwise raise an exception pop.population = pop.reproduction.create_new( config=pop.config, num_genomes=pop.config.population.pop_size) # Divide the new population into species pop.species.speciate(config=pop.config, population=pop.population, generation=pop.generation, logger=pop.log) # Add to each of the species its elites pop.update_species_fitness_hist() # Increment generation count pop.generation += 1
def main(topology_id: int, iterations: int, eval_interval: int, experiment_id: int, hops: float = 0.1, weight_range: float = 1, mutate_combine: bool = False, mutate_bias: bool = True, mutate_reset: bool = True, mutate_update: bool = True, mutate_candidate: bool = True, init_population_size: int = 1000, unused_cpu: int = 2): """ Run the fifth experiment. :param topology_id: Chosen topology to investigate :param iterations: Number of training iterations performed :param eval_interval: After how much training iterations evaluation is performed :param experiment_id: ID of the experiment used to train and evaluate the population on :param hops: Hops between variable-configurations :param weight_range: Range of deviation for one's weights :param mutate_combine: Combine the best results of each mutation type :param mutate_bias: Mutate the GRU's bias values :param mutate_reset: Mutate the reset-gate's weights :param mutate_update: Mutate the update-gate's weights :param mutate_candidate: Mutate the candidate-state's weights :param init_population_size: Initial size of the randomized population :param unused_cpu: Number of CPU-cores not used """ # Get the population name = f"experiment{experiment_id}_topology{topology_id}_hops{hops}_range{weight_range}" pop = Population( name=name, folder_name='experiment5', use_backup=False, ) # Define the games specific to the experiment _, game_ids_eval = get_game_ids(experiment_id=experiment_id) # Set the genomes if population is new if pop.generation == 0: # Get the requested topology-type if topology_id == 1: topology = get_topology1 elif topology_id == 2: topology = get_topology2 else: raise Exception(f"Topology {topology_id} not supported.") # Initialize the population with a randomized population pop.population = dict() for gid in range(init_population_size): new_genome = topology(pop.config, random_init=True) new_genome.key = gid pop.population[gid] = new_genome # Perform an initial training train_population(pop=pop, games=game_ids_eval, unused_cpu=unused_cpu) pop.generation = 0 # Don't count initial training as a generation # Get the fittest genome best = None for g in pop.population.values(): if best is None or g.fitness > best.fitness: best = g pop.best_genome = best visualize_best_genome(pop=pop) # Test the initial population test_population(pop=pop, games=game_ids_eval) # Set the most fit genome as the starting-point set_population(pop=pop, hops=hops, weight_range=weight_range, mutate_bias=mutate_bias, mutate_reset=mutate_reset, mutate_update=mutate_update, mutate_candidate=mutate_candidate) # Evaluate the population for i in range(iterations): train_population(pop=pop, games=game_ids_eval, unused_cpu=unused_cpu) evaluate_fitness(pop=pop, hops=hops, weight_range=weight_range, mutate_combine=mutate_combine, mutate_bias=mutate_bias, mutate_reset=mutate_reset, mutate_update=mutate_update, mutate_candidate=mutate_candidate) visualize_best_genome(pop=pop) if pop.generation % eval_interval == 0: test_population(pop=pop, games=game_ids_eval) set_population(pop=pop, hops=hops, weight_range=weight_range, mutate_bias=mutate_bias, mutate_reset=mutate_reset, mutate_update=mutate_update, mutate_candidate=mutate_candidate)
def set_population( pop: Population, hops: float, weight_range: float, mutate_bias: bool = False, mutate_reset: bool = False, mutate_update: bool = False, mutate_candidate: bool = False, ): """Set the given population as mutations of the given genome.""" assert weight_range > hops r = int(weight_range / hops) # Re-initialize the weight around the provided genome pop.population = dict() genome_key = 0 # Create genome-mutations if mutate_bias: for i in range(3): for a in range(-r, r + 1): new_genome = copy.deepcopy(pop.best_genome) new_genome.nodes[2].bias_h[ i] = new_genome.nodes[2].bias_h[i] + a * hops new_genome.key = genome_key pop.population[genome_key] = new_genome genome_key += 1 if mutate_reset: for a in range(-r, r + 1): for b in range(-r, r + 1): new_genome = copy.deepcopy(pop.best_genome) new_genome.nodes[2].weight_xh_full[ 0, 0] = new_genome.nodes[2].weight_xh_full[0, 0] + a * hops new_genome.nodes[2].weight_hh[ 0, 0] = new_genome.nodes[2].weight_hh[0, 0] + b * hops new_genome.key = genome_key pop.population[genome_key] = new_genome genome_key += 1 if mutate_update: for a in range(-r, r + 1): for b in range(-r, r + 1): new_genome = copy.deepcopy(pop.best_genome) new_genome.nodes[2].weight_xh_full[ 1, 0] = new_genome.nodes[2].weight_xh_full[1, 0] + a * hops new_genome.nodes[2].weight_hh[ 1, 0] = new_genome.nodes[2].weight_hh[1, 0] + b * hops new_genome.key = genome_key pop.population[genome_key] = new_genome genome_key += 1 if mutate_candidate: for a in range(-r, r + 1): for b in range(-r, r + 1): new_genome = copy.deepcopy(pop.best_genome) new_genome.nodes[2].weight_xh_full[ 2, 0] = new_genome.nodes[2].weight_xh_full[2, 0] + a * hops new_genome.nodes[2].weight_hh[ 2, 0] = new_genome.nodes[2].weight_hh[2, 0] + b * hops new_genome.key = genome_key pop.population[genome_key] = new_genome genome_key += 1 # Save the updated population pop.save()