Exemplo n.º 1
0
    def evaluate_and_evolve(
        self,
        pop: Population,
        n: int = 1,
        parallel=True,
        save_interval: int = 1,
    ):
        """
        Evaluate the population on the same set of games.
        
        :param pop: Population object
        :param n: Number of generations
        :param parallel: Parallel the code (disable parallelization for debugging purposes)
        :param save_interval: Indicates how often a population gets saved
        """
        multi_env = get_multi_env(pop=pop, game_config=self.game_config)
        msg = f"Repetitive evaluating on games: {self.games} for {n} iterations"
        pop.log(msg, print_result=False)

        # Iterate and evaluate over the games
        saved = True
        for iteration in range(n):
            # Set and randomize the games
            multi_env.set_games(self.games, noise=True)

            # Prepare the generation's reporters for the generation
            pop.reporters.start_generation(gen=pop.generation, logger=pop.log)

            # Fetch the dictionary of genomes
            genomes = list(iteritems(pop.population))

            if parallel:
                # Initialize the evaluation-pool
                pool = mp.Pool(mp.cpu_count() - self.unused_cpu)
                manager = mp.Manager()
                return_dict = manager.dict()

                for genome in genomes:
                    pool.apply_async(func=multi_env.eval_genome,
                                     args=(genome, return_dict))
                pool.close()  # Close the pool
                pool.join(
                )  # Postpone continuation until everything is finished
            else:
                return_dict = dict()
                for genome in tqdm(genomes, desc="sequential training"):
                    multi_env.eval_genome(genome, return_dict)

            # Calculate the fitness from the given return_dict
            fitness = calc_pop_fitness(
                fitness_cfg=pop.config.evaluation,
                game_cfg=self.game_config.game,
                game_obs=return_dict,
                gen=pop.generation,
            )
            for i, genome in genomes:
                genome.fitness = fitness[i]

            # Gather and report statistics
            best = None
            for g in itervalues(pop.population):
                if best is None or g.fitness > best.fitness: best = g
            pop.reporters.post_evaluate(population=pop.population,
                                        species=pop.species,
                                        best_genome=best,
                                        logger=pop.log)

            # Update the population's best_genome
            genomes = sorted(pop.population.items(),
                             key=lambda x: x[1].fitness,
                             reverse=True)
            pop.best_fitness[pop.generation] = genomes[0][1].fitness
            pop.best_genome_hist[pop.generation] = genomes[0]
            pop.best_genome = best

            # Let population evolve
            pop.evolve()

            # End generation
            pop.reporters.end_generation(population=pop.population,
                                         name=str(pop),
                                         species_set=pop.species,
                                         logger=pop.log)

            # Save the population
            if (iteration + 1) % save_interval == 0:
                pop.save()
                saved = True
            else:
                saved = False

        # Make sure that last iterations saves
        if not saved: pop.save()
Exemplo n.º 2
0
    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.

        # 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)
        # 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:
                for i, m in old_members[:self.reproduction_config.elitism]:
                    new_population[i] = m
                    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)

                if parent1.fitness > parent2.fitness:
                    parent1.mutate(config.genome_config)
                    new_population[parent1_id] = parent1
                    self.ancestors[parent1_id] = (parent1_id, parent1_id)
                else:
                    parent2.mutate(config.genome_config)
                    new_population[parent2_id] = parent2
                    self.ancestors[parent2_id] = (parent2_id, parent2_id)
                '''
                Check cppnon sets of both parents
                If they're the same, perfrom crossover of non-cppnon-nodes and connections
                Else,
                Find parent with highest fitness
                Transfer those nodes to child
                perfrom crossover of non-cppnon-nodes and connections
                '''

        return new_population
Exemplo n.º 3
0
    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:
            if not (self.state):
                print("Training: break")
                self.reached_limit  = False
                break
            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)
                    self.reached_limit  = False
                    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
Exemplo n.º 4
0
def run():
    # Load the config file, which is assumed to live in
    # the same directory as this script.
    local_dir = os.path.dirname(__file__)
    config_path = os.path.join(local_dir, my_config)
    config = neat.Config(LanderGenome, neat.DefaultReproduction,
                         neat.DefaultSpeciesSet, neat.DefaultStagnation,
                         config_path)

    pop = neat.Population(config)
    stats = neat.StatisticsReporter()
    pop.add_reporter(stats)
    pop2 = neat.Population(config)
    stats2 = neat.StatisticsReporter()
    pop.add_reporter(stats)
    pop2.add_reporter(stats2)
    pop.add_reporter(neat.StdOutReporter(True))
    # Checkpoint every 25 generations or 900 seconds.
    rep = neat.Checkpointer(25, 900)
    pop.add_reporter(rep)
    # Training set index
    avg_score_v = -10000000.0
    avg_score_v_ant = avg_score_v
    avg_score = avg_score_v

    # asigna un gen_best para poder cargar los demás desde syn
    for g in itervalues(pop.population):
        gen_best = g
        g.fitness = -10000000.0

    # Run until the winner from a generation is able to solve the environment
    # or the user interrupts the process.
    ec = PooledErrorCompute()
    temp = 0
    best_fitness = -2000.0

    pop_size = len(pop.population)
    # sets the nuber of continuous iterations
    num_iterations = round(200 / len(pop.population)) + 1
    while 1:
        try:
            if temp > 0:
                # Calcula training y validation fitness
                best_genomes = stats.best_unique_genomes(3)
                solved = True
                best_scores = []
                observation = env_t.reset()
                score = 0.0
                step = 0
                gen_best_nn = neat.nn.FeedForwardNetwork.create(
                    gen_best, config)

                while 1:
                    step += 1
                    output = gen_best_nn.activate(nn_format(observation))
                    action = (np.argmax(output[0:2]), output[3], output[4],
                              output[5])  # buy,sell or
                    observation, reward, done, info = env_t.step(action)
                    score += reward

                    env_t.render()
                    if done:
                        break
                ec.episode_score.append(score)
                ec.episode_length.append(step)
                best_scores.append(score)
                avg_score = sum(best_scores) / len(best_scores)
                print("Training Set Score =", score, " avg_score=", avg_score)

                # Calculate the real-validation set score
                best_genomes = stats.best_unique_genomes(3)
                solved = True
                best_scores = []
                observation = env_v.reset()
                score = 0.0
                step = 0
                gen_best_nn = neat.nn.FeedForwardNetwork.create(
                    gen_best, config)
                while 1:
                    step += 1
                    output = gen_best_nn.activate(nn_format(observation))
                    action = (np.argmax(output[0:2]), output[3], output[4],
                              output[5])  # buy,sell or
                    observation, reward, done, info = env_v.step(action)
                    score += reward
                    #env_v.render()
                    if done:
                        break
                best_scores.append(score)
                avg_score_v = sum(best_scores) / len(best_scores)
                print("Validation Set Score = ", avg_score_v)
                print(
                    "*********************************************************"
                )
                # Calcula el best_fitness (PARA SYNC)como el promedio del score de training y el promedio del fitness de los reps.
                reps_local = []
                reps = [gen_best]
                accum = 0.0
                countr = 0
                for sid, s in iteritems(pop.species.species):
                    if pop.population[
                            s.representative.key].fitness is not None:
                        accum = accum + pop.population[
                            s.representative.key].fitness
                        countr = countr + 1
                if countr > 0:
                    best_fitness = (3 * avg_score + (accum / countr)) / 4
                else:
                    best_fitness = (avg_score)
                #FIN de calculo de real validation

            if temp >= 0:
                # TODO: FUNCION DE SINCRONIZACION CON SINGULARITY
                # Lee en pop2 el último checkpoint desde syn
                # Hace request de getLastParam(process_hash,use_current) a syn TODO: HACER PROCESS CONFIGURABLE Y POR HASH no por id
                res = requests.get(
                    my_url +
                    "/processes/1?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph"
                )
                cont = res.json()
                print('\ncurrent_block_performance =',
                      cont['result'][0]['current_block_performance'])
                print('\nlast_optimum_id =',
                      cont['result'][0]['last_optimum_id'])
                last_optimum_id = cont['result'][0]['last_optimum_id']

                # Si el perf reportado pop2_champion_fitness > pop1_champion_fitness de validation training
                print("\nPerformance = ", best_fitness)
                print(
                    "*********************************************************"
                )
                if cont['result'][0][
                        'current_block_performance'] > best_fitness:
                    # hace request GetParameter(id)
                    res_p = requests.get(
                        my_url + "/parameters/" + str(last_optimum_id) +
                        "?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph"
                    )
                    cont_param = res_p.json()
                    # descarga el checkpoint del link de la respuesta si cont.parameter_link
                    print('Parameter Downloaded')
                    print('\nmigrations =')
                    if cont_param['result'][0]['parameter_link'] is not None:
                        genom_data = requests.get(
                            cont_param['result'][0]['parameter_link']).content
                        with open('remote_reps', 'wb') as handler:
                            handler.write(genom_data)
                            handler.close()
                        # carga genom descargado en nueva población pop2
                        with open('remote_reps', 'rb') as f:
                            remote_reps = pickle.load(f)
                        # OP.MIGRATION: Reemplaza el peor de la especie pop1 más cercana por el nuevo chmpion de pop2 como http://neo.lcc.uma.es/Articles/WRH98.pdf
                        # para cada elemento de remote_reps, busca el closer, si remote fitness > local, lo reemplaza
                        for i in range(len(remote_reps)):
                            closer = None
                            min_dist = None
                            # initialize for less fit search
                            less_fit = None
                            less_fitness = 10000
                            for g in itervalues(pop.population):
                                if g not in remote_reps:
                                    dist = g.distance(remote_reps[i],
                                                      config.genome_config)
                                    if dist is None:
                                        dist = 100000000
                                else:
                                    dist = 100000000
                                # do not count already migrated remote_reps
                                if closer is None or min_dist is None:
                                    closer = deepcopy(g)
                                    min_dist = dist
                                if dist < min_dist:
                                    closer = deepcopy(g)
                                    min_dist = dist
                                if g.fitness is None:
                                    g.fitness = -10
                                if g.fitness < less_fitness:
                                    less_fitness = g.fitness
                                    less_fit = deepcopy(g)
                            # For the best genom in position 0
                            if i == 0:
                                tmp_genom = deepcopy(remote_reps[i])
                                # Hack: overwrites original genome key with the replacing one
                                tmp_genom.key = closer.key
                                pop.population[closer.key] = deepcopy(
                                    tmp_genom)
                                print(" gen_best=", closer.key)
                                pop.best_genome = deepcopy(tmp_genom)
                                gen_best = deepcopy(tmp_genom)
                            else:
                                # si el remote fitness>local, reemplazar el remote de pop2 en pop1
                                if closer is None:
                                    # busca el pop con el menor fitness
                                    closer = less_fit
                                if closer is not None:
                                    if closer not in remote_reps:
                                        if closer.fitness is None:
                                            closer.fitness = less_fitness
                                        if closer.fitness is not None and remote_reps[
                                                i].fitness is not None:
                                            if remote_reps[
                                                    i].fitness > closer.fitness:
                                                tmp_genom = deepcopy(
                                                    remote_reps[i])
                                                # Hack: overwrites original genome key with the replacing one
                                                tmp_genom.key = closer.key
                                                pop.population[
                                                    closer.key] = deepcopy(
                                                        tmp_genom)
                                                print("Replaced=", closer.key)
                                                # actualiza gen_best y best_genome al remoto
                                                pop.best_genome = deepcopy(
                                                    tmp_genom)
                                                gen_best = deepcopy(tmp_genom)
                                        if closer.fitness is None:
                                            tmp_genom = deepcopy(
                                                remote_reps[i])
                                            # Hack: overwrites original genome key with the replacing one
                                            tmp_genom.key = len(
                                                pop.population) + 1
                                            pop.population[
                                                tmp_genom.key] = tmp_genom
                                            print(
                                                "Created Por closer.fitness=NONE : ",
                                                tmp_genom.key)
                                            # actualiza gen_best y best_genome al remoto
                                            pop.best_genome = deepcopy(
                                                tmp_genom)
                                            gen_best = deepcopy(tmp_genom)
                                    else:
                                        #si closer está en remote_reps es porque no hay ningun otro cercano así que lo adiciona
                                        tmp_genom = deepcopy(remote_reps[i])
                                        # Hack: overwrites original genome key with the replacing one
                                        tmp_genom.key = len(pop.population) + 1
                                        pop.population[
                                            tmp_genom.key] = tmp_genom
                                        print(
                                            "Created por Closer in rempte_reps=",
                                            tmp_genom.key)
                                        # actualiza gen_best y best_genome al remoto
                                        pop.best_genome = deepcopy(tmp_genom)
                                        gen_best = deepcopy(tmp_genom)

                        #ejecuta speciate
                        pop.species.speciate(config, pop.population,
                                             pop.generation)
                        print("\nSpeciation after migration done")
                # Si el perf reportado es menor pero no igual al de pop1
                if cont['result'][0][
                        'current_block_performance'] < best_fitness:
                    # Obtiene remote_reps
                    # hace request GetParameter(id)
                    remote_reps = None
                    res_p = requests.get(
                        my_url + "/parameters/" + str(last_optimum_id) +
                        "?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph"
                    )
                    cont_param = res_p.json()
                    # descarga el checkpoint del link de la respuesta si cont.parameter_link
                    print('\nNEW OPTIMUM - cont_param =', cont_param)
                    #print('\nmigrations =')
                    if cont_param['result'][0]['parameter_link'] is not None:
                        genom_data = requests.get(
                            cont_param['result'][0]['parameter_link']).content
                        with open('remote_reps', 'wb') as handler:
                            handler.write(genom_data)
                            handler.close()
                        # carga genom descargado en nueva población pop2
                        with open('remote_reps', 'rb') as f:
                            remote_reps = pickle.load(f)
                #Guarda los mejores reps
                    reps_local = []
                    reps = [gen_best]
                    # Para cada especie, adiciona su representative a reps
                    for sid, s in iteritems(pop.species.species):
                        #print("\ns=",s)
                        if s.representative not in reps_local:
                            reps_local.append(
                                pop.population[s.representative.key])
                            reps_local[len(reps_local) - 1] = deepcopy(
                                pop.population[s.representative.key])
                    # TODO: Conservar los mejores reps, solo reemplazarlos por los mas cercanos
                    if remote_reps is None:
                        for l in reps_local:
                            reps.append(l)
                            reps[len(reps) - 1] = deepcopy(l)
                    else:
                        # para cada reps_local l
                        for l in reps_local:
                            # busca el closer a l en reps_remote
                            for i in range(len(remote_reps)):
                                closer = None
                                min_dist = None
                                for g in reps_local:
                                    if g not in remote_reps:
                                        dist = g.distance(
                                            remote_reps[i],
                                            config.genome_config)
                                    else:
                                        dist = 100000000
                                    # do not count already migrated remote_reps
                                    if closer is None or min_dist is None:
                                        closer = deepcopy(g)
                                        min_dist = dist
                                    if dist < min_dist:
                                        closer = deepcopy(g)
                                        min_dist = dist
                #           si closer is in reps
                            if closer in reps:
                                #               adiciona l a reps si ya no estaba en reps
                                if l not in reps:
                                    reps.append(l)
                                    reps[len(reps) - 1] = deepcopy(l)
                #           sino
                            else:
                                #               si l tiene más fitness que closer,
                                if closer.fitness is not None and l.fitness is not None:
                                    if l.fitness > pop.population[
                                            closer.key].fitness:
                                        #                       adiciona l a reps si ya no estaba en reps
                                        if l not in reps:
                                            reps.append(l)
                                            reps[len(reps) - 1] = deepcopy(l)
                #               sino
                                    else:
                                        #                      adiciona closer a reps si ya no estaba en reps
                                        if l not in reps:
                                            reps.append(
                                                pop.population[closer.key])
                                            reps[len(reps) - 1] = deepcopy(
                                                pop.population[closer.key])
                                            # Guarda checkpoint de los representatives de cada especie y lo copia a ubicación para servir vía syn.
                                            # rep.save_checkpoint(config,pop,neat.DefaultSpeciesSet,rep.current_generation)
                    print("\nreps=", reps)
                    filename = '{0}{1}'.format("reps-", rep.current_generation)
                    with open(filename, 'wb') as f:
                        pickle.dump(reps, f)
                    #
                    # Hace request de CreateParam a syn
                    form_data = {
                        "process_hash":
                        "ph",
                        "app_hash":
                        "ah",
                        "parameter_link":
                        my_url + "/genoms/" + filename,
                        "parameter_text":
                        pop.best_genome.key,
                        "parameter_blob":
                        "",
                        "validation_hash":
                        "",
                        "hash":
                        "h",
                        "performance":
                        best_fitness,
                        "redir":
                        "1",
                        "username":
                        "******",
                        "pass_hash":
                        "$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q"
                    }
                    # TODO: COLOCAR DIRECCION CONFIGURABLE
                    res = requests.post(
                        my_url +
                        "/parameters?username=harveybc&pass_hash=$2a$04$ntNHmofQoMoajG89mTEM2uSR66jKXBgRQJnCgqfNN38aq9UkN4Y6q&process_hash=ph",
                        data=form_data)
                    res_json = res.json()
                # TODO FIN: FUNCION DE SINCRONIZACION CON SINGULARITY
            temp = temp + 1

            # EVALUATE THE GENOMES WITH THE SUBSET TRAINING DATASET
            gen_best = pop.run(ec.evaluate_genomes, num_iterations)
            # TODO:
            # VERIFY IF THERE ARE PENDING EVALUATIONS
            # EVALUATE NUM_EVALUATIONS PENDING EVALUATIONS

            #print(gen_best)
            #visualize.plot_stats(stats, ylog=False, view=False, filename="fitness.svg")
            #plt.plot(ec.episode_score, 'g-', label='score')
            #plt.plot(ec.episode_length, 'b-', label='length')
            #plt.grid()
            #plt.legend(loc='best')
            #plt.savefig("scores.svg")
            #plt.close()

            #mfs = sum(stats.get_fitness_mean()[-5:]) / 5.0
            #print("Average mean fitness over last 5 generations: {0}".format(mfs))

            #mfs = sum(stats.get_fitness_stat(min)[-5:]) / 5.0
            #print("Average min fitness over last 3 generations: {0}".format(mfs))
            if avg_score < 2000000000:
                solved = False

            if solved:
                print("Solved.")

                # Save the winners.
                for n, g in enumerate(best_genomes):
                    name = 'winner-{0}'.format(n)
                    with open(name + '.pickle', 'wb') as f:
                        pickle.dump(g, f)

                    visualize.draw_net(config,
                                       g,
                                       view=False,
                                       filename=name + "-net.gv")
                    visualize.draw_net(config,
                                       g,
                                       view=False,
                                       filename=name + "-net-enabled.gv",
                                       show_disabled=False)
                    visualize.draw_net(config,
                                       g,
                                       view=False,
                                       filename=name +
                                       "-net-enabled-pruned.gv",
                                       show_disabled=False,
                                       prune_unused=True)

                break
        except KeyboardInterrupt:
            print("User break.")
            break

    env.close()
Exemplo n.º 5
0
 def __init__(self, name, **default_dict):
     self.name = name
     for n, default in iteritems(default_dict):
         self._config_items[n] = [self._config_items[n][0], default]
     for n in iterkeys(self._config_items):
         setattr(self, n + "_name", self.config_item_name(n))
Exemplo n.º 6
0
def process():
    global program_state, p
    global config
    global obj_topo, road_topo
    global CAR_ROW_START, CAR_COL_START, CAR_SHAPE_COL, sand_stall
    global frame, new_distance, distance_stall, old_distance, base_distance
    global old_time, new_time, base_time
    global fitness_val, genome_fitness, generation_fitness, generation_fitness_index, generation_average_fitness
    global genome_rank, generation_rank, generation_rank_index
    global generation_count, genome_count, genome_list, genome, current_net, best_genome
    global key_press
    global winner

    print(program_state)

    # Stop the data processing if the agent is currently training
    if program_state == STATE_BUFFERING:
        time.sleep(0.1)
        pass

    elif program_state == STATE_BEGIN:
        program_state = STATE_BUFFERING

        # Report the current generation
        p.reporters.start_generation(p.generation)
        genome_list = list(iteritems(p.population))
        program_state = STATE_PRESSING_F1
        time.sleep(0.1)
        pass

    elif program_state == STATE_PRESSING_F1:
        time.sleep(0.1)
        pass

    elif program_state == STATE_RESETTED:
        program_state = STATE_BUFFERING

        # Grab the genome if there is still genome to check
        if genome_count < NUM_GENOMES:
            _, genome = genome_list[genome_count]
            current_net = neat.nn.FeedForwardNetwork.create(genome, config)
            program_state = STATE_EVALUATING
        else:
            genome_count = 0
            program_state = STATE_POST_PROCESSING
        pass

        # Sleep for the stability of the algorithm
        time.sleep(0.1)

    elif program_state == STATE_EVALUATING:

        old_distance = new_distance
        old_time = new_time

        # Get the data from the request
        start = time.time()
        data = request.body.read().decode('utf8')
        data = json.loads(data)

        #
        pixels = data["Pixels"]
        speed = data["Speed"]
        new_time = data["Time"]
        if new_time > BASE_TIME: new_time = 0
        new_distance = data["Distance"]
        if new_distance == BASE_DISTANCE: new_distance = 0
        rank = data["Rank"]
        nitro = data["Nitro"]

        # Determine a set of background color, we will try to remove this color from the game.
        road_topo = topo_from_original_array(pixels)[: TOPO_SHAPE[0] // 2, :]
        end = time.time()

        # Calculate decision from current net
        input_arr = road_topo.flatten()
        key_press = generate_key_presses(current_net, input_arr) + "0" # Don't press F1 to reset

        # Update frame counter and distance_stall and time
        if new_distance < old_distance: base_distance += BASE_DISTANCE
        if new_distance == old_distance:
            distance_stall += 1
        else:
            distance_stall = 0
        frame += 1
        if new_time < old_time: base_time += BASE_TIME
        if not road_topo[CAR_ROW_START + SHAPE_ROW, CAR_COL_START: CAR_COL_START + SHAPE_COL].any():
            sand_stall += 1
        else:
            sand_stall = 0

        fitness_val = (base_distance + new_distance) * DIST_C \
                      - (base_time + new_time) * TIME_C \
                      - rank * RANK_C \
                      + BASE_FITNESS

        # Print necessary information
        os.system('cls')
        print("Current generation: " + str(generation_count) + "/" + str(NUM_GENERATIONS))
        print("----")
        print_topo(road_topo)
        print(end - start)
        print_key_presses(key_press, BUTTONS)
        print("Speed:    " + str(speed))
        print("Time:     " + str(base_time + new_time))
        print("Distance: " + str(base_distance + new_distance))
        print("Rank:     " + str(rank))
        print("Nitro:    " + str(nitro))
        print("Current genome: " + str(genome_count + 1) + "/" + str(NUM_GENOMES))
        print("Fitness value: " + str(fitness_val))
        print('--------------')
        print('| Gen | Best Agent | Score      | Rank | Avg Score   |')
        print('|----------------------------------------------------|')
        for i in range(len(generation_fitness)):
            print("| {:3d} | {:10d} | {:10d} | {:4d} | {:11.3f} |".format( \
                i, generation_fitness_index[i], generation_fitness[i], generation_rank[i], generation_average_fitness[i]))

        # Stop the running if stall limit is reached
        if distance_stall > STALL_LIMIT:
            genome.fitness = fitness_val
            print("- Evaluated Genome " + str(genome_count) + ": " + str(fitness_val))

            # Add a bunch of statistical variables
            genome_fitness.append(fitness_val)
            genome_rank.append(rank)

            # Reset
            reset_variables()
            genome_count += 1
            program_state = STATE_PRESSING_F1

        # Return something cuz a post go with a get
        pass

    elif program_state == STATE_POST_PROCESSING:

        program_state = STATE_BUFFERING

        # Gather and report statistics.
        best = None
        for g in itervalues(p.population):
            if best is None or g.fitness > best.fitness:
                best = g
        p.reporters.post_evaluate(p.config, p.population, p.species, best)

        # Track the best gen ome ever seen.
        if p.best_genome is None or best.fitness > p.best_genome.fitness:
            p.best_genome = best

        # End if the fitness threshold is reached.
        fv = p.fitness_criterion(g.fitness for g in itervalues(p.population))
        if fv >= p.config.fitness_threshold:
            p.reporters.found_solution(p.config, p.generation, best)
            program_state = STATE_RETURN_BEST

        # Create the next generation from the current generation.
        p.population = p.reproduction.reproduce(p.config, p.species,
                                                p.config.pop_size, p.generation)

        # Check for complete extinction.
        if not p.species.species:
            p.reporters.complete_extinction()

            # If requested by the user, create a completely new population,
            # otherwise raise an exception.

            if p.config.reset_on_extinction:
                p.population = p.reproduction.create_new(p.config.genome_type,
                                                         p.config.genome_config,
                                                         p.config.pop_size)
            else:
                raise neat.CompleteExtinctionException()

        # Divide the new population into species.
        p.species.speciate(p.config, p.population, p.generation)
        p.reporters.end_generation(p.config, p.population, p.species)
        p.generation += 1

        # Add the best genome value into the statistics
        generation_fitness.append(max(genome_fitness))
        generation_average_fitness.append(np.average(genome_fitness))
        generation_fitness_index.append(np.argmax(genome_fitness))
        genome_fitness = []

        generation_rank.append(min(genome_rank))
        generation_rank_index.append(np.argmin(genome_rank))
        genome_rank = []

        # Increment generation count and switch state
        np.savetxt("best_genome_by_generation.npy",generation_fitness_index)
        generation_count += 1
        if generation_count == NUM_GENERATIONS: program_state = STATE_RETURN_BEST
        else: program_state = STATE_BEGIN
        pass

    elif program_state == STATE_RETURN_BEST:
        best_genome = p.best_genome
        program_state = STATE_TEST_BEST
        pass

    elif program_state == STATE_TEST_BEST:
        print("Should start testing stuff now")
        pass

    return("Finished")
Exemplo n.º 7
0
    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 = []

        old_population = []

        for stag_sid, stag_s, stagnant in self.stagnation.update(
                species, generation):

            old_population.append(itervalues(stag_s.members))

            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.

        # 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)
        # 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)

        # Bandit Update
        # TODO Reconsider reward scheme to be invariant of actual fitness value
        # Percentage improvement to fitness?
        # Fitness improvement as a fraction of best improvement? - seems good, then one mutation is always 1, rest are < 1
        # How to reward or perceive "failure"? Same as fraction but for decreases?
        # 1 if best arm in generation else 0?
        if generation > 0:
            mutation_delta = []

            for mutant, old_fitness, mutations in self.records[-1]:
                fit_delta = mutant.fitness - old_fitness

                for m in mutations:
                    mutation_delta.append((m, fit_delta))
            # print(mutation_delta)
            self.bandit.update_all(mutation_delta)
            #         if m not in mutations_deltas:
            #             mutations_deltas[m] = [fit_delta]
            #         else:
            #             mutations_deltas[m].append(fit_delta)

            # for mutations, deltas in mutations_deltas.items():
            #     self.bandit.update(mutations, deltas) # len deltas can be removed, the number of rewards is the number of plays

            # print([(g.key, g.fitness-f, m) for g,f,m in self.records[-1]])

        self.reporters.post_reproduction(config, self.bandit, None)

        self.records.append([])

        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:
                for i, m in old_members[:self.reproduction_config.elitism]:
                    new_population[i] = m
                    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 = choice(old_members)
                parent2_id, parent2 = (parent1_id, parent1)

                gid = next(self.genome_indexer)
                child = config.genome_type(gid)

                child.configure_crossover(parent1, parent2,
                                          config.genome_config)

                mutation_directives = self.bandit.play(generation)
                child.mutate(config.genome_config, mutation_directives)

                new_population[gid] = child
                self.ancestors[gid] = (parent1_id, parent2_id)

                self.records[-1].append(
                    (child, parent1.fitness, mutation_directives))

        return new_population
Exemplo n.º 8
0
    def get_population(self):

        return list(iteritems(self.population))
Exemplo n.º 9
0
    def speciate(self, config: Config, population, generation, logger=None):
        """
        Place genomes into species by genetic similarity.

        :note: 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.
        """
        assert isinstance(population, dict)
        unspeciated = set(
            iterkeys(population))  # Initially the full population
        distances = GenomeDistanceCache(
            config.genome)  # Cache memorizing distances between genomes
        # distances.warm_up(population)  # No warm-up since only comparison with representatives! (expensive!)
        new_representatives = dict(
        )  # Updated representatives for each specie (least difference with previous)
        members = dict(
        )  # Dictionary containing for each specie its corresponding members

        # Update the representatives for each specie as the genome closest to the previous representative
        for sid, s in iteritems(self.species):
            candidates = []
            for gid in unspeciated:
                genome = population[gid]
                d = distances(s.representative, genome)
                candidates.append((d, genome))

            # Sort the genomes based on their distance towards the specie and take the closest genome
            sorted_repr = sorted(candidates, key=lambda x: x[0])
            new_repr = sorted_repr[0][1]
            new_representatives[sid] = new_repr.key
            members[sid] = [new_repr.key]
            unspeciated.remove(new_repr.key)

        # Partition the population into species based on their genetic similarity
        while unspeciated:
            # Pull unspeciated genome
            gid = unspeciated.pop()
            genome = population[gid]

            # Find the species with the most similar representative
            specie_distance = []
            candidates = []
            for sid, rid in iteritems(new_representatives):
                rep = population[rid]
                d = distances(rep, genome)
                specie_distance.append((d, sid))
                if d < config.population.get_compatibility_threshold(
                        n_species=len(new_representatives)):
                    candidates.append((d, sid))

            # There are species close enough; add genome to most similar specie
            if candidates:
                _, sid = min(candidates, key=lambda x: x[0])
                members[sid].append(gid)
            else:  # No specie is similar enough, create new specie with this genome as its representative
                sid = next(self.indexer)
                new_representatives[sid] = gid
                members[sid] = [gid]

        # Update the species collection based on the newly defined speciation
        self.genome_to_species = dict()
        for sid, rid in iteritems(new_representatives):
            s = self.species.get(sid)

            # One of the newly defined species
            if s is None:
                s = Species(sid, generation)
                self.species[sid] = s

            # Append the newly added members to the genome_to_species mapping
            specie_members = members[sid]
            for gid in specie_members:
                self.genome_to_species[gid] = sid

            # Update the specie's current representative and members
            member_dict = dict((genome_id, population[genome_id])
                               for genome_id in specie_members)
            s.update(representative=population[rid], members=member_dict)

        # Report over the current species (distances)
        self.reporters.info(
            f'Genetic distance:'
            f'\n\t- Maximum: {max(itervalues(distances.distances)):.5f}'
            f'\n\t- Mean: {mean(itervalues(distances.distances)):.5f}'
            f'\n\t- Minimum: {min(itervalues(distances.distances)):.5f}'
            f'\n\t- Standard deviation: {stdev(itervalues(distances.distances)):.5f}',
            logger=logger,
            print_result=False,
        )
        print(
            f'Maximum genetic distance: {round(max(itervalues(distances.distances)), 3)}'
        )  # Print reduced version

        # Report over the most complex genome
        most_complex = sorted([(g.size(), gid)
                               for gid, g in population.items()],
                              key=lambda x: x[0],
                              reverse=True)[0]
        gid = most_complex[1]
        size = most_complex[0]
        specie_id = self.genome_to_species[gid]
        distance = distances(population[gid],
                             self.species[specie_id].representative)
        self.reporters.info(
            f"Most complex genome '{gid}' "
            f"of size {size} "
            f"belongs to specie '{specie_id}' "
            f"with distance to representative of {round(distance, 3)}",
            logger=logger,
        )
Exemplo n.º 10
0
    def run(self, fitness_function, self2, n=None):
        if self.config.no_fitness_termination and (n is None):
            raise RuntimeError(
                "Cannot have no generational limit with no fitness termination"
            )
        if self2.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)
            self2.reporters.start_generation(self2.generation)

            # Evaluate all genomes using the user-provided function.
            fitness_function(list(iteritems(self.population)),
                             list(iteritems(self2.population)), self.config)

            # Gather and report statistics.
            best1 = None
            for g in itervalues(self.population):
                if best1 is None or g.fitness > best1.fitness:
                    best1 = g
            self.reporters.post_evaluate(self.config, self.population,
                                         self.species, best1)

            best2 = None
            for g in itervalues(self2.population):
                if best2 is None or g.fitness > best2.fitness:
                    best2 = g
            self2.reporters.post_evaluate(self2.config, self2.population,
                                          self2.species, best2)

            # Track the best genome ever seen.
            if self.best_genome is None or best1.fitness > self.best_genome.fitness:
                self.best_genome = best1

            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,
                                                  best1)
                    break

            #Do the same with self2 <--- balances out config version
            if self2.best_genome is None or best2.fitness > self2.best_genome.fitness:
                self2.best_genome = best2

            if not self2.config.no_fitness_termination:
                # End if the fitness threshold is reached.
                fv = self2.fitness_criterion(
                    g.fitness for g in itervalues(self2.population))
                if fv >= self2.config.fitness_threshold:
                    self2.reporters.found_solution(self2.config,
                                                   self2.generation, best2)
                    break

            # Create the next generation from the current generation.
            self.population = self.reproduction.reproduce(
                self.config, self.species, self.config.pop_size,
                self.generation)
            self2.population = self2.reproduction.reproduce(
                self2.config, self2.species, self2.config.pop_size,
                self2.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()

            if not self2.species.species:
                self2.reporters.complete_extinction()

                # If requested by the user, create a completely new population,
                # otherwise raise an exception.
                if self2.config.reset_on_extinction:
                    self2.population = self2.reproduction.create_new(
                        self2.config.genome_type, self2.config.genome_config,
                        self2.config.pop_size)
                else:
                    raise CompleteExtinctionException()

            # Divide the new population into species.
            self.species.speciate(self.config, self.population,
                                  self.generation)
            self2.species.speciate(self2.config, self2.population,
                                   self2.generation)

            self.reporters.end_generation(self.config, self.population,
                                          self.species)
            self2.reporters.end_generation(self2.config, self2.population,
                                           self2.species)

            self.generation += 1
            self2.generation += 1

        if self.config.no_fitness_termination:
            self.reporters.found_solution(self.config, self.generation,
                                          self.best_genome)
        if self2.config.no_fitness_termination:
            self2.reporters.found_solution(self2.config, self2.generation,
                                           self2.best_genome)

        return (self.best_genome, self2.best_genome)
Exemplo n.º 11
0
    output = net.activate(input_arr)
    s = ""
    for val in output:
        if val < THRESHOLD: s += "0"
        else: s += "1"
    return s


# ---------------
# PREPARE NETWORK
# ---------------

# best_agents = np.load("./data/from_tung/best_genome_by_generation.npy")
p = neat.Checkpointer.restore_checkpoint('./data/from_tung/neat-checkpoint-' +
                                         str(CHOSEN_GEN))
genome_list = list(iteritems(p.population))
_, genome = genome_list[CHOSEN_GENOME]
current_net = neat.nn.FeedForwardNetwork.create(genome, p.config)
visualize.draw_net(p.config,
                   genome,
                   filename="Generation_" + str(CHOSEN_GEN) + "_Genome_" +
                   str(CHOSEN_GENOME),
                   fmt="pdf")

# --------------
# NEURAL NETWORK
# --------------

while (True):
    r = requests.get("http://127.0.0.1:37979/get_input")
    data = r.json()
Exemplo n.º 12
0
    def speciate(self, config, 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.
        """
        assert type(population) is dict

        compatibility_threshold = config.species_set_config[
            'compatibility_threshold']

        # Reset all species member lists.
        for s in itervalues(self.species):
            s.members.clear()
        self.to_species.clear()

        # Partition population into species based on genetic similarity.
        distances = []
        for key, individual in iteritems(population):
            # Find the species with the most similar representative.
            min_distance = sys.float_info.max
            closest_species = None
            closest_species_id = None
            for sid, s in iteritems(self.species):
                rep = s.representative
                distance = individual.distance(rep, config.genome_config)
                distances.append(distance)
                compatible = distance < compatibility_threshold

                if compatible and distance < min_distance:
                    closest_species = s
                    closest_species_id = sid
                    min_distance = distance

            if closest_species is not None:
                closest_species.add(key, individual)
                self.to_species[key] = closest_species_id
            else:
                # No species is similar enough, create a new species for this individual.
                sid = self.indexer.get_next()
                self.species[sid] = Species(sid, key, individual)
                self.to_species[key] = sid

        self.reporters.info('Mean genetic distance {0}, std dev {1}'.format(
            mean(distances), stdev(distances)))

        # Only keep non-empty species.
        empty_species_ids = []
        for sid, s in iteritems(self.species):
            if not s.members:
                empty_species_ids.append(sid)

        for sid in empty_species_ids:
            del self.species[sid]

        # Select a random current member as the new representative.
        for s in itervalues(self.species):
            s.representative = random.choice(list(s.members.values()))
Exemplo n.º 13
0
    selection_type = sys.argv[1]
    base_dir = sys.argv[2]

    read_state_sequences = dict()

    with open(base_dir + 'state_usages.csv', 'r') as csv_file:
        csv_reader = csv.reader(csv_file)

        for row in csv_reader:

            if selection_type == 'scenario':
                key = int(int(filter(str.isdigit, row[0])))
                index = int(row[1])
            elif selection_type == 'controller':
                key = int(row[1])
                index = int(int(filter(str.isdigit, row[0])))
            else:
                print('Unknown selection type')
                exit(1)

            if key not in read_state_sequences:
                read_state_sequences[key] = []

            read_state_sequences[key].append((index, int(row[2]), [int(x) for x in row[3:]]))

    for key, state_sequences in iteritems(read_state_sequences):

        visualizer = StateUsageVisualizer(base_dir, selection_type, key)
        visualizer.state_sequences = state_sequences    # Cheat to not have to append them all.
        visualizer.finalize()
Exemplo n.º 14
0
    def distance(self, other, config)
        """
        Returns the genetic distance between this genome and the other. This distance value
        is used to compute genome compatibility for speciation.
        """

        # Compute node gene distance component.
        node_distance = 0.0
        if self.nodes or other.nodes:
            disjoint_nodes = 0
            for k2 in iterkeys(other.nodes):
                if k2 not in self.nodes:
                    disjoint_nodes += 1

            for k1, n1 in iteritems(self.nodes):
                n2 = other.nodes.get(k1)
                if n2 is None:
                    disjoint_nodes += 1
                else:
                    # Homologous genes compute their own distance value.
                    node_distance += n1.distance(n2, config)

            max_nodes = max(len(self.nodes), len(other.nodes))
            node_distance = (node_distance +
                             (config.compatibility_disjoint_coefficient *
                              disjoint_nodes)) / max_nodes

        # Compute connection gene differences.
        connection_distance = 0.0
        if self.connections.keys() != [None] or other.connections.keys() != [None]:
Exemplo n.º 15
0
    def reproduce(self,
                  config: Config,
                  species: DefaultSpecies,
                  generation: int,
                  logger=None):
        """Handles creation of genomes, either from scratch or by sexual or asexual reproduction from parents."""
        # Check is one of the species has become stagnant (i.e. must be removed)
        remaining_fitness = []
        remaining_species = []
        for stag_sid, stag_s, stagnant in self.stagnation.update(
                config=config, species_set=species, gen=generation):
            # If specie is stagnant, then remove
            if stagnant:
                self.reporters.species_stagnant(stag_sid,
                                                stag_s,
                                                logger=logger)

            # Add the specie to remaining_species and save each of its members' fitness
            else:
                remaining_fitness.extend(m.fitness
                                         for m in itervalues(stag_s.members))
                remaining_species.append(stag_s)

        # If no species is left then force hard-reset
        if not remaining_species:
            species.species = dict()
            return dict()

        # Calculate the adjusted fitness, normalized by the minimum fitness across the entire population
        for specie in remaining_species:
            # Adjust a specie's fitness in a fitness sharing manner. A specie's fitness gets normalized by the number of
            #  members it has, this to ensure that a better performing specie does not takes over the population
            # A specie's fitness is determined by its most fit genome
            specie_fitness = max([m.fitness for m in specie.members.values()])
            specie_size = len(specie.members)
            specie.adjusted_fitness = specie_fitness / max(
                specie_size, config.population.min_specie_size)

        # Minimum specie-size is defined by the number of elites and the minimal number of genomes in a population
        spawn_amounts = self.compute_spawn(
            adjusted_fitness=[s.adjusted_fitness for s in remaining_species],
            previous_sizes=[len(s.members) for s in remaining_species],
            pop_size=config.population.pop_size,
            min_species_size=max(config.population.min_specie_size,
                                 config.population.genome_elitism))

        # Setup the next generation by filling in the new species with their elites and offspring
        new_population = dict()
        species.species = dict()
        for spawn_amount, specie in zip(spawn_amounts, remaining_species):
            # If elitism is enabled, each species will always at least gets to retain its elites
            spawn_amount = max(spawn_amount, config.population.genome_elitism)
            assert spawn_amount > 0

            # Get all the specie's old (evaluated) members
            old_members = list(iteritems(
                specie.members))  # Temporarily save members of last generation
            specie.members = dict()  # Reset members
            species.species[specie.key] = specie

            # Sort members in order of descending fitness (i.e. most fit members in front)
            old_members.sort(reverse=True, key=lambda x: x[1].fitness)

            # Make sure that all the specie's elites are added to the new generation
            if config.population.genome_elitism > 0:
                # Add the specie's elites to the global population
                for i, m in old_members[:config.population.genome_elitism]:
                    new_population[i] = m
                    spawn_amount -= 1

                # Add the specie's past elites as well if requested
                for i in range(
                        min(len(specie.elite_list),
                            config.population.genome_elite_stagnation - 1)):
                    gid, g = specie.elite_list[-(i + 1)]
                    if gid not in new_population:  # Only add genomes not yet present in the population
                        new_population[gid] = g
                        spawn_amount -= 1

                # Update the specie's elite_list
                specie.elite_list.append(old_members[0])

            # Check if the specie has the right to add more genomes to the population
            if spawn_amount <= 0: continue

            # Only use the survival threshold fraction to use as parents for the next generation, use at least all the
            #  elite of a population as parents
            reproduction_cutoff = max(
                round(config.population.parent_selection * len(old_members)),
                config.population.genome_elitism)

            # Since asexual reproduction, at least one parent must be chosen
            reproduction_cutoff = max(reproduction_cutoff, 1)
            parents = old_members[:reproduction_cutoff]

            # Add the elites again to the parent-set such that these have a greater likelihood of being chosen
            parents += old_members[:config.population.genome_elitism]

            # Fill the specie with offspring based, which is a mutation of the chosen parent
            while spawn_amount > 0:
                spawn_amount -= 1

                # Init genome dummy (values are overwritten later)
                gid = next(self.genome_indexer)
                child: Genome = Genome(gid,
                                       num_outputs=config.genome.num_outputs,
                                       bot_config=config.bot)

                # Choose the parents, note that if the parents are not distinct, crossover will produce a genetically
                #  identical clone of the parent (but with a different ID)
                p1_id, p1 = choice(parents)
                child.connections = copy.deepcopy(p1.connections)
                child.nodes = copy.deepcopy(p1.nodes)

                # Mutate the child
                child.mutate(config.genome)

                # Ensure that the child is connected
                while len(child.get_used_connections()) == 0:
                    child.mutate_add_connection(config.genome)

                # Add the child to the global population
                new_population[gid] = child

        return new_population
Exemplo n.º 16
0
    def create(genome, config):
        """ Receives a genome and returns its phenotype (a GRUNetwork). """
        genome_config = config.genome_config
        required = required_for_output(genome_config.input_keys,
                                       genome_config.output_keys,
                                       genome.connections, genome.nodes)

        connections = [
            cg.key for cg in itervalues(genome.connections) if cg.enabled
        ]

        node_evals = []

        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 = []
        gate_list = set()

        # Add the gates first for proper computation order
        for node_key, inputs in iteritems(node_inputs):
            ng = genome.nodes[node_key]

            if type(ng) is GRUNodeGene:
                for gate_key in [ng.read_gate, ng.forget_gate]:
                    if type(gate_key) is int and gate_key not in gate_list:
                        gate_list = gate_list.union(gate_key)
                        gate_g = genome.nodes[gate_key]
                        activation_function = genome_config.activation_defs.get(
                            gate_g.activation)
                        aggregation_function = genome_config.aggregation_function_defs.get(
                            gate_g.aggregation)
                        node_evals.append(
                            (gate_key, activation_function,
                             aggregation_function, gate_g.bias,
                             gate_g.response, inputs,
                             [gate_g.read_gate, gate_g.forget_gate]
                             if type(gate_g) is GRUNodeGene else None))

        for node_key, inputs in iteritems(node_inputs):
            if node_key not in gate_list:
                ng = genome.nodes[node_key]
                activation_function = genome_config.activation_defs.get(
                    ng.activation)
                aggregation_function = genome_config.aggregation_function_defs.get(
                    ng.aggregation)

                node_evals.append((node_key, activation_function,
                                   aggregation_function, ng.bias, ng.response,
                                   inputs, [ng.read_gate, ng.forget_gate]
                                   if type(ng) is GRUNodeGene else None))

        nw = GRUNetwork(genome_config.input_keys, genome_config.output_keys,
                        node_evals, gate_list)
        nw.genome = genome
        return nw
Exemplo n.º 17
0
    def reproduce(self, config, species, pop_size, generation):
        # TODO: I don't like this modification of the species and stagnation objects,
        # because it requires internal knowledge of the objects.

        # Find minimum/maximum fitness across the entire population, for use in
        # species adjusted fitness computation.
        all_fitnesses = []
        for sid, s in iteritems(species.species):
            all_fitnesses.extend(m.fitness for m in itervalues(s.members))
        min_fitness = min(all_fitnesses)
        max_fitness = max(all_fitnesses)
        # Do not allow the fitness range to be zero, as we divide by it below.
        fitness_range = max(1.0, max_fitness - min_fitness)

        # 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.
        num_remaining = 0
        species_fitness = []
        avg_adjusted_fitness = 0.0
        for sid, s, stagnant in self.stagnation.update(species, generation):
            if stagnant:
                self.reporters.species_stagnant(sid, s)
            else:
                num_remaining += 1

                # Compute adjusted fitness.
                msf = mean([m.fitness for m in itervalues(s.members)])
                s.adjusted_fitness = (msf - min_fitness) / fitness_range
                species_fitness.append((sid, s, s.fitness))
                avg_adjusted_fitness += s.adjusted_fitness

        # No species left.
        if 0 == num_remaining:
            species.species = {}
            return []

        avg_adjusted_fitness /= len(species_fitness)
        self.reporters.info(
            "Average adjusted fitness: {:.3f}".format(avg_adjusted_fitness))

        # Compute the number of new individuals to create for the new generation.
        spawn_amounts = []
        for sid, s, sfitness in species_fitness:
            spawn = len(s.members)
            if sfitness > avg_adjusted_fitness:
                spawn = max(spawn + 2, spawn * 1.1)
            else:
                spawn = max(spawn * 0.9, 2)
            spawn_amounts.append(spawn)

        # Normalize the spawn amounts so that the next generation is roughly
        # the population size requested by the user.
        total_spawn = sum(spawn_amounts)
        norm = pop_size / total_spawn
        spawn_amounts = [int(round(n * norm)) for n in spawn_amounts]

        new_population = {}
        species.species = {}
        for spawn, (sid, s, sfitness) in zip(spawn_amounts, species_fitness):
            # If elitism is enabled, each species always at least gets to retain its elites.
            spawn = max(spawn, self.elitism)

            if spawn <= 0:
                continue

            # The species has at least one member for the next generation, so retain it.
            old_members = list(iteritems(s.members))
            s.members = {}
            species.species[sid] = 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.elitism > 0:
                for i, m in old_members[:self.elitism]:
                    new_population[i] = m
                    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.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 = self.genome_indexer.get_next()
                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
Exemplo n.º 18
0
 def build_population_nn(self):
     nn = {}
     for index, pop in list(iteritems(self.population.population)):
         nn[index] = self.create_net(pop)
     self.rt_population_nn = nn
    def run(self, fitness_function, n=None):
        # Variables needed to save archive on each update
        winner_name_prefix = "archive_checkpoint_" + self.winner_name

        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)

            not_evaluated = {}
            evaluated = []
            # len(population) = 2*pop_size
            for gid, g in self.population.items():
                if g.fitness is None:
                    not_evaluated[gid] = g
                else:
                    evaluated.append((gid, g))

            # Evaluate all genomes using the user-provided function.
            fitness_function(list(iteritems(not_evaluated)), self.config)
            self.KNNdistances(self.population, self.novelty_archive, self.n_neighbors)

            if len(evaluated) != 0:
                self.species.speciate(self.config, self.population, self.generation)
                dim = 0
                max_spec_dim = 0
                max_sid = -1
                for sid, s in iteritems(self.species.species):

                    s.members = self.get_best_n_members(s.members, math.ceil(len(s.members) / 2))
                    d = len(s.members)
                    if d > max_spec_dim:
                        max_spec_dim = d
                        max_sid = sid
                    dim += d
                diff = dim - self.config.pop_size
                if diff > 0 and diff > max_spec_dim:
                    s = self.species.species[max_sid]
                    s.members = self.get_best_n_members(s.members, len(s.members) - diff)

                new_population = {}
                for sid, s in iteritems(self.species.species):
                    for gid, g in s.members.items():
                        new_population[gid] = g
                self.population = new_population

            self.species.speciate(self.config, self.population, 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()

            # Gather and report statistics.
            best = None
            for g in itervalues(self.population):
                if best is None or g.dist > best.dist:
                    best = g
                if g.dist > self.novelty_threshold:
                    if g not in self.novelty_archive:
                        self.novelty_archive.append(g)
                        print("Distanza di aggiunta: ", g.dist)
                        self.n_add_archive += 1
                        self.last_archive_modified = self.generation
                        with open(winner_name_prefix, "wb") as f:
                            pickle.dump(self.novelty_archive, f)

            self.reporters.post_evaluate(self.config, self.population, self.species, best)

            if not self.config.no_fitness_termination:
                # End if the fitness threshold is reached.
                fv = self.fitness_criterion(g.dist 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)

            time_diff = self.generation - self.last_archive_modified

            if time_diff > 60:
                self.novelty_threshold -= self.novelty_threshold * 0.05

            if self.n_add_archive > 4 and time_diff <= 30:
                self.novelty_threshold += self.novelty_threshold * 0.05
                self.n_add_archive = 0

            if time_diff > 30:
                self.n_add_archive = 0

            self.reporters.info("Novelty's archive size: {}\n".format(len(self.novelty_archive)))
            if len(self.novelty_archive) > 0:
                self.reporters.info("Archive's best: {}".format(
                    max(self.novelty_archive, key=lambda x: x.fitness[0]).fitness[0]))
            else:
                self.reporters.info("Archive's best: 0")
            self.generation += 1

        if self.config.no_fitness_termination:
            self.reporters.found_solution(self.config, self.generation, self.best_genome)

        return self.novelty_archive
Exemplo n.º 20
0
    def rt_iterate(self):
        self.population.reporters.start_generation(self.population.generation)

        # Gather and report statistics.
        best = None
        worst = None
        for g in itervalues(self.population.population):
            if not hasattr(g, 'birth'):
                g.birth = self.population.generation

            if g.fitness is None:
                continue

            if best is None or g.fitness > best.fitness:
                best = g
            if (worst is None or g.fitness < worst.fitness) and \
                    self.population.generation - g.birth > self.population.config.stagnation_config.max_stagnation:
                worst = g

        population_with_fitness = dict([
            p for p in iteritems(self.population.population)
            if p[1].fitness is not None
        ])
        if population_with_fitness:
            self.population.reporters.post_evaluate(self.config,
                                                    population_with_fitness,
                                                    self.population.species,
                                                    best)

        # Track the best genome ever seen.
        if self.population.best_genome is None or best.fitness > self.population.best_genome.fitness:
            self.population.best_genome = best

        if not self.population.config.no_fitness_termination:
            # End if the fitness threshold is reached.
            fv = self.population.fitness_criterion(
                g.fitness for g in itervalues(population_with_fitness))
            if fv >= self.population.config.fitness_threshold:
                self.population.reporters.found_solution(
                    self.population.config, self.population.generation, best)
                return

        # remove worst genome and replace with new one
        if worst is not None:
            new_genomes = self.population.reproduction.reproduce(
                self.population.config, self.population.species, 1,
                self.population.generation)
            new_key = np.max(
                [g.key for g in itervalues(self.population.population)]) + 1
            for g in itervalues(new_genomes):
                del self.population.population[worst.key]
                g.key = new_key
                self.population.population[new_key] = g
                print(new_key)
                break

        # Divide the new population into species.
        if population_with_fitness:
            self.population.species.speciate(self.population.config,
                                             population_with_fitness,
                                             self.population.generation)

        # self.population.reporters.end_generation(self.population.config, self.population.population,
        #                                          self.population.species)

        self.population.generation += 1
Exemplo n.º 21
0
from MyCheckpoint import MyCheckpointer
from GenomeVisualizer import GenomeVisualizer

root_path = '../logs/meandist_collision100/1525807840'
generation = 1029
top_x = 5

config_path = '{}/neat_config'.format(root_path)
pop_path = '{}/pop-gen-{}'.format(root_path, generation)

neat_conf = neat.Config(neat.DefaultGenome, neat.DefaultReproduction,
                        neat.DefaultSpeciesSet, neat.DefaultStagnation,
                        config_path)

pop = MyCheckpointer.restore_checkpoint(pop_path)
genomes = list(iteritems(pop.population))


def cond(item):
    return item[1].fitness


sorted_genoms = sorted(genomes, reverse=True, key=cond)
genomes = sorted_genoms[0:top_x]

for i in range(0, len(genomes)):
    genome_viz_file = '{}/genome-gen-{}-top-{}-viz'.format(
        root_path, generation, i)
    GenomeVisualizer.draw_net(neat_conf,
                              genomes[i][1],
                              False,
Exemplo n.º 22
0
def neat_manual_looping(pop, generation_limit=None):
    if pop.config.no_fitness_termination and (generation_limit is None):
        raise RuntimeError(
            "Cannot have no generational limit with no fitness termination")

    yield

    k = 0
    while generation_limit is None or k < generation_limit:
        k += 1

        pop.reporters.start_generation(pop.generation)

        genomes = list(iteritems(pop.population))

        finesses = yield genomes

        for (genome_id, genome), fitness in zip(genomes, finesses):
            genome.fitness = fitness

        # Gather and report statistics.
        best = None
        for g in itervalues(pop.population):
            if best is None or g.fitness > best.fitness:
                best = g
        pop.reporters.post_evaluate(pop.config, pop.population, pop.species,
                                    best)

        # Track the best genome ever seen.
        if pop.best_genome is None or best.fitness > pop.best_genome.fitness:
            pop.best_genome = best

        if not pop.config.no_fitness_termination:
            # End if the fitness threshold is reached.
            fv = pop.fitness_criterion(g.fitness
                                       for g in itervalues(pop.population))
            if fv >= pop.config.fitness_threshold:
                pop.reporters.found_solution(pop.config, pop.generation, best)
                break

        # Create the next generation from the current generation.
        pop.population = pop.reproduction.reproduce(pop.config, pop.species,
                                                    pop.config.pop_size,
                                                    pop.generation)

        # Check for complete extinction.
        if not pop.species.species:
            pop.reporters.complete_extinction()

            # If requested by the user, create a completely new population,
            # otherwise raise an exception.
            if pop.config.reset_on_extinction:
                pop.population = pop.reproduction.create_new(
                    pop.config.genome_type, pop.config.genome_config,
                    pop.config.pop_size)
            else:
                raise CompleteExtinctionException()

        # Divide the new population into species.
        pop.species.speciate(pop.config, pop.population, pop.generation)

        pop.reporters.end_generation(pop.config, pop.population, pop.species)

        pop.generation += 1

    if pop.config.no_fitness_termination:
        pop.reporters.found_solution(pop.config, pop.generation,
                                     pop.best_genome)
Exemplo n.º 23
0
def train(
    population: Population,
    game_config: Config,
    games: list,
    iterations: int,
    unused_cpu: int = 0,
    save_interval: int = 10,
):
    """Train the population on the requested number of iterations. Manual adaptation of main's train()."""

    population.log("\n===> TRAINING <===\n")

    multi_env = get_multi_env(pop=population, game_config=game_config)
    msg = f"Repetitive evaluating on games: {games} for {iterations} iterations"
    population.log(msg, print_result=False)

    # Iterate and evaluate over the games
    saved = True
    for iteration in range(iterations):
        # Set and randomize the games
        multi_env.set_games(games, noise=True)

        # Prepare the generation's reporters for the generation
        population.reporters.start_generation(gen=population.generation,
                                              logger=population.log)

        # Fetch the dictionary of genomes
        genomes = list(iteritems(population.population))

        # Initialize the evaluation-pool
        pool = mp.Pool(mp.cpu_count() - unused_cpu)
        manager = mp.Manager()
        return_dict = manager.dict()

        for genome in genomes:
            pool.apply_async(func=multi_env.eval_genome,
                             args=(genome, return_dict))
        pool.close()  # Close the pool
        pool.join()  # Postpone continuation until everything is finished

        # Calculate the fitness from the given return_dict
        fitness = calc_pop_fitness(
            fitness_cfg=population.config.evaluation,
            game_cfg=game_config.game,
            game_obs=return_dict,
            gen=population.generation,
        )
        for i, genome in genomes:
            genome.fitness = fitness[i]

        # Gather and report statistics
        best = None
        for g in itervalues(population.population):
            if best is None or g.fitness > best.fitness: best = g
        population.reporters.post_evaluate(population=population.population,
                                           species=population.species,
                                           best_genome=best,
                                           logger=population.log)

        # Update the population's best_genome
        genomes = sorted(population.population.items(),
                         key=lambda x: x[1].fitness,
                         reverse=True)
        population.best_fitness[population.generation] = genomes[0][1].fitness
        population.best_genome_hist[population.generation] = genomes[0]
        population.best_genome = best

        # Let population evolve
        population.evolve()

        # Update the genomes such all have one hidden node
        for g in population.population.values():
            n_hidden, _ = g.size()
            while n_hidden < 1:
                g.mutate_add_connection(population.config.genome)
                n_hidden, _ = g.size()

        # End generation
        population.reporters.end_generation(population=population.population,
                                            name=str(population),
                                            species_set=population.species,
                                            logger=population.log)

        # Save the population
        if (iteration + 1) % save_interval == 0:
            population.save()
            saved = True
        else:
            saved = False

    # Make sure that last iterations saves
    if not saved: population.save()
Exemplo n.º 24
0
    def reproduce(self, config, species, pop_size, generation, exp, syllabus):
        all_fitnesses = []
        for sid, s in iteritems(species.species):
            all_fitnesses.extend(m.fitness for m in itervalues(s.members))
        min_fitness = min(all_fitnesses)
        max_fitness = max(all_fitnesses)
        fitness_range = max(1.0, max_fitness - min_fitness)
        num_remaining = 0
        species_fitness = []
        avg_adjusted_fitness = 0.0
        for sid, s, stagnant in self.stagnation.update(species, generation):
            if stagnant:
                self.reporters.species_stagnant(sid, s)
            else:
                num_remaining += 1
                msf = mean([m.fitness for m in itervalues(s.members)])
                s.adjusted_fitness = (msf - min_fitness) / fitness_range
                species_fitness.append((sid, s, s.fitness))
                avg_adjusted_fitness += s.adjusted_fitness
        if 0 == num_remaining:
            species.species = {}
            return []
        avg_adjusted_fitness /= len(species_fitness)
        self.reporters.info(
            "Average adjusted fitness: {:.3f}".format(avg_adjusted_fitness))
        spawn_amounts = []
        for sid, s, sfitness in species_fitness:
            spawn = len(s.members)
            if sfitness > avg_adjusted_fitness:
                spawn = max(spawn + 2, spawn * 1.1)
            else:
                spawn = max(spawn * 0.9, 2)
            spawn_amounts.append(spawn)
        total_spawn = sum(spawn_amounts)
        norm = pop_size / total_spawn
        spawn_amounts = [int(round(n * norm)) for n in spawn_amounts]
        new_population = {}
        species.species = {}

        learning_function = None
        if exp.learning_function == 'BP':
            learning_function = backpropagation
        elif exp.learning_function == 'BT':
            learning_function = batch

        has_learning = not learning_function is None
        if has_learning:
            (writer, lessons) = syllabus
            if exp.syllabus_source == 'EXP':
                lessons_to_learn = random.sample(
                    lessons, min(exp.syllabus_size, len(lessons)))
            elif exp.syllabus_source == 'RND':
                lessons_to_learn = lessons

        for spawn, (sid, s, sfitness) in zip(spawn_amounts, species_fitness):
            spawn = max(spawn, self.elitism)
            if spawn <= 0:
                continue
            old_members = list(iteritems(s.members))
            s.members = {}
            species.species[sid] = s
            old_members.sort(reverse=True, key=lambda x: x[1].fitness)
            if self.elitism > 0:
                for i, m in old_members[:self.elitism]:
                    new_population[i] = m
                    spawn -= 1
            if spawn <= 0:
                continue
            repro_cutoff = int(
                math.ceil(self.survival_threshold * len(old_members)))
            repro_cutoff = max(repro_cutoff, 2)
            old_members = old_members[:repro_cutoff]

            if has_learning and (exp.learning_target == 'Ambos'
                                 or exp.learning_target == 'Pais'):
                for (_, g) in old_members:
                    if g != writer:
                        learning_function(g, lessons_to_learn,
                                          exp.learning_rate)

            while spawn > 0:
                spawn -= 1
                parent1_id, parent1 = random.choice(old_members)
                parent2_id, parent2 = random.choice(old_members)
                gid = self.genome_indexer.next()
                child = config.genome_type(gid)
                child.configure_crossover(parent1, parent2,
                                          config.genome_config)
                child.mutate(config.genome_config)
                child.net = FeedForwardNetwork.create(child, self.config)

                if has_learning and (exp.learning_target == 'Ambos'
                                     or exp.learning_target == 'Filhos'):
                    if exp.children_inclusion == 'Inicial' or (
                            exp.children_inclusion == 'Tardia'
                            and generation >= exp.generations / 2):
                        learning_function(child, lessons_to_learn,
                                          exp.learning_rate)

                new_population[gid] = child
                self.ancestors[gid] = (parent1_id, parent2_id)
        return new_population
    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))
Exemplo n.º 26
0
def single_evaluation(multi_env, parallel: bool, pop: Population,
                      unused_cpu: int):
    """
    Perform a single evaluation-iteration.
    
    :param multi_env: Environment used to execute the game-simulation in
    :param parallel: Boolean indicating if training happens in parallel or not
    :param pop: Population used to evaluate on
    :param unused_cpu: Number of CPU-cores not used during evaluation
    """
    # Prepare the generation's reporters for the generation
    pop.reporters.start_generation(gen=pop.generation, logger=pop.log)

    # Fetch the dictionary of genomes
    genomes = list(iteritems(pop.population))

    if parallel:
        pbar = tqdm(total=len(genomes), desc="parallel training")

        # Initialize the evaluation-pool
        pool = mp.Pool(mp.cpu_count() - unused_cpu)
        manager = mp.Manager()
        return_dict = manager.dict()

        def cb(*_):
            """Update progressbar after finishing a single genome's evaluation."""
            pbar.update()

        for genome in genomes:
            pool.apply_async(func=multi_env.eval_genome,
                             args=(genome, return_dict),
                             callback=cb)
        pool.close()  # Close the pool
        pool.join()  # Postpone continuation until everything is finished
        pbar.close()  # Close the progressbar
    else:
        return_dict = dict()
        for genome in tqdm(genomes, desc="sequential training"):
            multi_env.eval_genome(genome, return_dict)

    # Calculate the fitness from the given return_dict
    try:
        fitness = calc_pop_fitness(
            fitness_config=pop.config.evaluation,
            game_observations=return_dict,
            game_params=multi_env.get_game_params(),
            generation=pop.generation,
        )
        for i, genome in genomes:
            genome.fitness = fitness[i]
    except Exception:  # TODO: Fix! Sometimes KeyError in fitness (path problem)
        pop.log(
            f"Exception at fitness calculation: \n{traceback.format_exc()}",
            print_result=False)
        warnings.warn(
            f"Exception at fitness calculation: \n{traceback.format_exc()}")
        # Set fitness to zero for genomes that have no fitness set yet
        for i, genome in genomes:
            if not genome.fitness: genome.fitness = 0.0

    # Gather and report statistics
    best = None
    for g in itervalues(pop.population):
        if best is None or g.fitness > best.fitness: best = g
    pop.reporters.post_evaluate(population=pop.population,
                                species=pop.species,
                                best_genome=best,
                                logger=pop.log)

    # Update the population's best_genome
    genomes = sorted(pop.population.items(),
                     key=lambda x: x[1].fitness,
                     reverse=True)
    pop.best_genome_hist[
        pop.generation] = genomes[:pop.config.population.genome_elitism]
    if pop.best_genome is None or best.fitness > pop.best_genome.fitness:
        pop.best_genome = best

    # Let population evolve
    pop.evolve()

    # End generation
    pop.reporters.end_generation(population=pop.population,
                                 name=str(pop),
                                 species_set=pop.species,
                                 logger=pop.log)
    def run(self, fitness_function, n=None):
        # Variables needed to save archive on each update
        winner_name_prefix = "archive_checkpoint_" + self.winner_name

        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)
            self.KNNdistances(self.population, self.novelty_archive,
                              self.n_neighbors)

            # Gather and report statistics.
            best = None
            for g in itervalues(self.population):
                if best is None or g.dist > best.dist:
                    best = g
                if g.dist > self.novelty_threshold:
                    if g not in self.novelty_archive:
                        self.novelty_archive.append(g)
                        print("Distanza di aggiunta: ", g.dist)
                        self.n_add_archive += 1
                        self.last_archive_modified = self.generation
                        with open(winner_name_prefix, "wb") as f:
                            pickle.dump(self.novelty_archive, f)

            self.reporters.post_evaluate(self.config, self.population,
                                         self.species, best)

            # if self.last_genome_added is None:
            #     self.last_genome_added = best

            # Track the best genome ever seen.
            # if self.last_genome_added.fitness != best.fitness and best.dist > self.novelty_threshold:
            #     print("Distanza di aggiunta: ", best.dist)
            #     self.last_genome_added = best
            #     self.last_archive_modified = self.generation
            #     self.n_add_archive += 1
            #     self.novelty_archive.append(best)
            #     with open(winner_name_prefix, "wb") as f:
            #         pickle.dump(self.novelty_archive, f)

            if not self.config.no_fitness_termination:
                # End if the fitness threshold is reached.
                fv = self.fitness_criterion(
                    g.dist 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)

            time_diff = self.generation - self.last_archive_modified

            if time_diff > 60:
                self.novelty_threshold -= self.novelty_threshold * 0.05

            if self.n_add_archive > 4 and time_diff <= 30:
                self.novelty_threshold += self.novelty_threshold * 0.05
                self.n_add_archive = 0

            if time_diff > 30:
                self.n_add_archive = 0

            self.reporters.info("Novelty's archive size: {}\n".format(
                len(self.novelty_archive)))
            self.generation += 1

        if self.config.no_fitness_termination:
            self.reporters.found_solution(self.config, self.generation,
                                          self.best_genome)

        return self.novelty_archive
Exemplo n.º 28
0
    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
Exemplo n.º 29
0
    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.

        # No species left.
        if not remaining_species:
            species.species = {}
            return {}  # was []

        self.calculate_adjusted_fitnesses(remaining_species, all_fitnesses)
        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:
                for i, m in old_members[:self.reproduction_config.elitism]:
                    new_population[i] = m
                    spawn -= 1

            if spawn <= 0:
                continue

            repro_cutoff = self.calculated_cutoff(len(old_members))
            old_members = old_members[:repro_cutoff]

            # Randomly choose parents and produce the number of offspring allotted to the species.
            while spawn > 0:
                spawn -= 1

                gid, child = self.generate_child(old_members, config)
                new_population[gid] = child

        return new_population
    def run(self, fitness_function, n=None):
        #Variables needed to save winner
        winner_interval = 10
        winnername_prefix = "winner_checkpoint_"
        last_winner_checkpoint = 0

        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
            start_time_gen = time.time()
            self.reporters.start_generation(self.generation)

            # Evaluate all genomes using the user-provided function.
            fitness_function(list(iteritems(self.population)), self.config)
            if self.initial_best:
                self.species.speciate(self.config, self.population,
                                      self.generation)

            with open("output.txt", "a") as f:
                f.write("Gen: " + str(k) + " tempo: " +
                        str(round(time.time() - start_time_gen, 3)) + " sec\n")

            start_time_gen = time.time()
            # 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
            #Code to save the best after winner_interval generations
            if winner_interval is not None:
                dg = self.generation - last_winner_checkpoint
                if dg >= winner_interval:
                    filename = '{0}{1}'.format(winnername_prefix,
                                               self.generation)
                    last_winner_checkpoint = self.generation
                    with open(filename, 'wb') as f:
                        pickle.dump(self.best_genome, f)

            with open("output.txt", "a") as f:
                f.write("Gen: " + str(k) + " tempo: " +
                        str(round(time.time() - start_time_gen, 3)) + " sec\n")

        if self.config.no_fitness_termination:
            self.reporters.found_solution(self.config, self.generation,
                                          self.best_genome)

        return self.best_genome