Пример #1
0
class Population(object):
    """
    This class implements the core NEAT algorithm.  It maintains a list of Species instances,
    each of which contains a collection of Genome instances.
    """
    def __init__(self, config, initial_population=None):
        """
        :param config: Either a config.Config object or path to a configuration file.
        :param initial_population: Either an initial set of Genome instances to be used
               as the initial population, or None, in which case a randomized set of Genomes
               will be created automatically based on the configuration parameters.
        """

        # If config is not a Config object, assume it is a path to the config file.
        if not isinstance(config, Config):
            config = Config(config)

        # Configure statistics and reporting as requested by the user.
        self.reporters = ReporterSet()
        if config.collect_statistics:
            self.statistics = StatisticsReporter()
            self.add_reporter(self.statistics)
        else:
            self.statistics = None

        if config.report:
            self.add_reporter(StdOutReporter())

        self.config = config
        self.species_indexer = Indexer(1)
        self.genome_indexer = Indexer(1)
        self.reproduction = config.reproduction_type(self.config,
                                                     self.reporters,
                                                     self.genome_indexer)

        self.species = []
        self.generation = -1
        self.total_evaluations = 0

        # Create a population if one is not given, then partition into species.
        if initial_population is None:
            initial_population = self._create_population()
        self._speciate(initial_population)

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def load_checkpoint(self, filename):
        '''Resumes the simulation from a previous saved point.'''
        self.reporters.loading_checkpoint(filename)
        with gzip.open(filename) as f:
            (self.species, self.generation, random_state) = pickle.load(f)

            random.setstate(random_state)

    def save_checkpoint(self, filename=None, checkpoint_type="user"):
        """ Save the current simulation state. """
        if filename is None:
            filename = 'neat-checkpoint-{0}'.format(self.generation)

        self.reporters.saving_checkpoint(checkpoint_type, filename)

        with gzip.open(filename, 'w', compresslevel=5) as f:
            data = (self.species, self.generation, random.getstate())
            pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)

    def _create_population(self):
        # Create a collection of unconnected genomes with no hidden nodes.
        new_population = []

        # TODO: The genotype class should know how to do everything below, based
        # solely on what's in the config object. This allows users to completely
        # replace the initial population creation scheme if they choose.
        for i in range(self.config.pop_size):
            g_id = self.genome_indexer.get_next()
            g = self.config.genotype.create_unconnected(g_id, self.config)
            new_population.append(g)

        # Add hidden nodes if requested.
        if self.config.hidden_nodes > 0:
            for g in new_population:
                g.add_hidden_nodes(self.config.hidden_nodes)

        # Add connections based on initial connectivity type.
        if self.config.initial_connection == 'fs_neat':
            for g in new_population:
                g.connect_fs_neat()
        elif self.config.initial_connection == 'fully_connected':
            for g in new_population:
                g.connect_full()
        elif self.config.initial_connection == 'partial':
            for g in new_population:
                g.connect_partial(self.config.connection_fraction)

        return new_population

    def _speciate(self, population):
        """
        Place genomes into species by genetic similarity.

        Note that this method assumes the current representatives of the species are from the old
        generation, and that after speciation has been performed, the old representatives should be
        dropped and replaced with representatives from the new generation.  If you violate this
        assumption, you should make sure other necessary parts of the code are updated to reflect
        the new behavior.
        """
        for individual in population:
            # Find the species with the most similar representative.
            min_distance = None
            closest_species = None
            for s in self.species:
                distance = individual.distance(s.representative)
                if distance < self.config.compatibility_threshold \
                    and (min_distance is None or distance < min_distance):
                    closest_species = s
                    min_distance = distance

            if closest_species:
                closest_species.add(individual)
            else:
                # No species is similar enough, create a new species for this individual.
                self.species.append(
                    Species(individual, self.species_indexer.get_next()))

        # Only keep non-empty species.
        self.species = [s for s in self.species if s.members]

        # Select a random current member as the new representative.
        for s in self.species:
            s.representative = random.choice(s.members)

    def run(self, fitness_function, n):
        """
        Runs NEAT's genetic algorithm for n generations.

        The user-provided fitness_function should take one argument, a list of all genomes in the population,
        and its return value is ignored.  This function is free to maintain external state, perform evaluations
        in parallel, and probably any other thing you want.  The only requirement is that each individual's
        fitness member must be set to a floating point value after this function returns.

        It is assumed that fitness_function does not modify the list of genomes, or the genomes themselves, apart
        from updating the fitness member.
        """

        # Remember start time for saving timed checkpoints.
        last_checkpoint = time.time()

        for g in range(n):
            self.generation += 1

            self.reporters.start_generation(self.generation)

            # Collect a list of all members from all species.
            population = []
            for s in self.species:
                population.extend(s.members)

            # Evaluate all individuals in the population using the user-provided function.
            # TODO: Add an option to only evaluate each genome once, to reduce number of
            # fitness evaluations in cases where the fitness is known to be the same if the
            # genome doesn't change--in these cases, evaluating unmodified elites in each
            # generation is a waste of time.
            fitness_function(population)
            self.total_evaluations += len(population)

            # Gather and report statistics.
            best = max(population)
            self.reporters.post_evaluate(population, self.species, best)

            # Save the best genome from the current generation if requested.
            if self.config.save_best:
                with open('best_genome_' + str(self.generation), 'wb') as f:
                    pickle.dump(best, f)

            # End if the fitness threshold is reached.
            if best.fitness >= self.config.max_fitness_threshold:
                self.reporters.found_solution(self.generation, best)
                break

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

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

                # If requested by the user, create a completely new population,
                # otherwise raise an exception.
                if self.config.reset_on_extinction:
                    new_population = self._create_population()
                else:
                    raise CompleteExtinctionException()

            # Update species age.
            for s in self.species:
                s.age += 1

            # Divide the new population into species.
            self._speciate(new_population)

            # Save checkpoints if necessary.
            if self.config.checkpoint_time_interval is not None:
                timed_checkpoint_due = last_checkpoint + 60 * self.config.checkpoint_time_interval
                if time.time() >= timed_checkpoint_due:
                    self.save_checkpoint(checkpoint_type="timed")
                    last_checkpoint = time.time()

            if self.config.checkpoint_gen_interval is not None \
                    and self.generation % self.config.checkpoint_gen_interval == 0:
                self.save_checkpoint(checkpoint_type="generation")

            self.reporters.end_generation()
Пример #2
0
class My_Population(Population):
    """
       This class implements the core evolution algorithm:
           1. Evaluate fitness of all genomes.
           2. Check to see if the termination criterion is satisfied; exit if it is.
           3. Generate the next generation from the current population.
           4. Partition the new generation into species based on genetic similarity.
           5. Go to 1.
       """
    def __init__(self, config, initial_state=None):
        super().__init__(config, initial_state)
        self.reporters = ReporterSet()
        self.config = config
        stagnation = config.stagnation_type(config.stagnation_config,
                                            self.reporters)
        self.reproduction = config.reproduction_type(
            config.reproduction_config, self.reporters, stagnation)
        if config.fitness_criterion == 'max':
            self.fitness_criterion = max
        elif config.fitness_criterion == 'min':
            self.fitness_criterion = min
        elif config.fitness_criterion == 'mean':
            self.fitness_criterion = mean
        elif not config.no_fitness_termination:
            raise RuntimeError("Unexpected fitness_criterion: {0!r}".format(
                config.fitness_criterion))

        if initial_state is None:
            # Create a population from scratch, then partition into species.
            self.population = self.reproduction.create_new(
                config.genome_type, config.genome_config, config.pop_size)
            self.species = config.species_set_type(config.species_set_config,
                                                   self.reporters)
            self.generation = 0
            self.species.speciate(config, self.population, self.generation)
        else:
            self.population, self.species, self.generation = initial_state

        self.best_genome = None

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def run(self, fitness_function, n=None):
        """
        Runs NEAT's genetic algorithm for at most n generations.  If n
        is None, run until solution is found or extinction occurs.

        The user-provided fitness_function must take only two arguments:
            1. The population as a list of (genome id, genome) tuples.
            2. The current configuration object.

        The return value of the fitness function is ignored, but it must assign
        a Python float to the `fitness` member of each genome.

        The fitness function is free to maintain external state, perform
        evaluations in parallel, etc.

        It is assumed that fitness_function does not modify the list of genomes,
        the genomes themselves (apart from updating the fitness member),
        or the configuration object.
        """

        list_of_best = list()

        if self.config.no_fitness_termination and (n is None):
            raise RuntimeError(
                "Cannot have no generational limit with no fitness termination"
            )

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

            self.reporters.start_generation(self.generation)

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

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

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

            list_of_best.append(best)

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

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

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

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

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

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

            self.generation += 1

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

        return self.best_genome, list_of_best
Пример #3
0
class TotalPopulation(object):
    def __init__(self, config, config_ghost, initial_state=None):
        self.reporters = ReporterSet()
        self.reporters_g_1 = ReporterSet()
        self.reporters_g_2 = ReporterSet()

        self.config = config
        self.config_ghost = config_ghost

        stagnation = config.stagnation_type(config.stagnation_config,
                                            self.reporters)
        self.reproduction = config.reproduction_type(
            config.reproduction_config, self.reporters, stagnation)
        self.reproduction_ghost = self.config_ghost.reproduction_type(
            config_ghost.reproduction_config, self.reporters_g_1, stagnation)

        if config.fitness_criterion == 'max':
            self.fitness_criterion = max
        elif config.fitness_criterion == 'min':
            self.fitness_criterion = min
        elif config.fitness_criterion == 'mean':
            self.fitness_criterion = mean
        else:
            raise Exception("Unexpected fitness_criterion: {0!r}".format(
                config.fitness_criterion))

        if initial_state is None:
            # Create a population from scratch, then partition into species.
            self.pacman_population = self.reproduction.create_new(
                config.genome_type, config.genome_config, config.pop_size)
            self.pacman_species = config.species_set_type(
                config, self.reporters)

            self.ghost_population_1 = self.reproduction_ghost.create_new(
                config_ghost.genome_type, config_ghost.genome_config,
                config_ghost.pop_size)
            self.ghost_population_2 = self.reproduction_ghost.create_new(
                config_ghost.genome_type, config_ghost.genome_config,
                config_ghost.pop_size)
            self.ghost_species_1 = config.species_set_type(
                config_ghost, self.reporters_g_1)
            self.ghost_species_2 = config.species_set_type(
                config_ghost, self.reporters_g_2)

            self.generation = 0
            self.pacman_species.speciate(config, self.pacman_population,
                                         self.generation)
            self.ghost_species_1.speciate(config_ghost,
                                          self.ghost_population_1,
                                          self.generation)
            self.ghost_species_2.speciate(config_ghost,
                                          self.ghost_population_2,
                                          self.generation)
        else:
            self.pacman_population, self.pacman_species, self.generation = initial_state
            self.ghost_population_1, self.ghost_species_1, self.generation = initial_state
            self.ghost_population_2, self.ghost_species_2, self.generation = initial_state

        self.best_genome_pacman = None
        self.best_genome_ghost_1 = None
        self.best_genome_ghost_2 = None

    def add_reporter_pacman(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter_pacman(self, reporter):
        self.reporters.remove(reporter)

    def add_reporter_ghost_1(self, reporter):
        self.reporters_g_1.add(reporter)

    def remove_reporter_ghost_1(self, reporter):
        self.reporters_g_1.remove(reporter)

    def add_reporter_ghost_2(self, reporter):
        self.reporters_g_2.add(reporter)

    def remove_reporter_ghost_2(self, reporter):
        self.reporters_g_2.remove(reporter)

    def run(self, evaluator, n=None):
        """
        Runs NEAT's genetic algorithm for at most n generations.  If n
        is None, run until solution is found or extinction occurs.
        The user-provided fitness_function must take only two arguments:
            1. The population as a list of (genome id, genome) tuples.
            2. The current configuration object.
        The return value of the fitness function is ignored, but it must assign
        a Python float to the `fitness` member of each genome.
        The fitness function is free to maintain external state, perform
        evaluations in parallel, etc.
        It is assumed that fitness_function does not modify the list of genomes,
        the genomes themselves (apart from updating the fitness member),
        or the configuration object.
        """
        k = 0
        while n is None or k < n:
            k += 1

            self.reporters.start_generation(self.generation)
            self.reporters_g_1.start_generation(self.generation)
            self.reporters_g_2.start_generation(self.generation)

            # Run the fitness evaluation function that will use all three populations
            evaluator(list(iteritems(self.pacman_population)),
                      list(iteritems(self.ghost_population_1)),
                      list(iteritems(self.ghost_population_2)), self.config,
                      self.config_ghost)

            # Gather and report statistics.
            bestp = None
            for g in itervalues(self.pacman_population):
                if bestp is None or g.fitness > bestp.fitness:
                    bestp = g

            bestg1 = None
            for g in itervalues(self.ghost_population_1):
                if bestg1 is None or g.fitness > bestg1.fitness:
                    bestg1 = g

            bestg2 = None
            for g in itervalues(self.ghost_population_2):
                if not g.fitness:
                    continue
                if bestg2 is None or g.fitness > bestg2.fitness:
                    bestg2 = g

            self.reporters.post_evaluate(self.config, self.pacman_population,
                                         self.pacman_species, bestp)
            self.reporters_g_1.post_evaluate(self.config_ghost,
                                             self.ghost_population_1,
                                             self.ghost_species_1, bestg1)
            self.reporters_g_2.post_evaluate(self.config_ghost,
                                             self.ghost_population_2,
                                             self.ghost_species_2, bestg2)

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

            if self.best_genome_ghost_1 is None or bestg1.fitness > self.best_genome_ghost_1.fitness:
                self.best_genome_ghost_1 = bestg1

            if self.best_genome_ghost_2 is None or bestg2.fitness > self.best_genome_ghost_2.fitness:
                self.best_genome_ghost_2 = bestg2

            # End if the fitness threshold is reached.
            fv = self.fitness_criterion(
                g.fitness for g in itervalues(self.pacman_population))
            if fv >= self.config.fitness_threshold:
                self.reporters.found_solution(self.config, self.generation,
                                              bestp)
                break

            # Create the next generation from the current generation.
            self.pacman_population = self.reproduction.reproduce(
                self.config, self.pacman_species, self.config.pop_size,
                self.generation)
            self.ghost_population_1 = self.reproduction_ghost.reproduce(
                self.config_ghost, self.ghost_species_1,
                self.config_ghost.pop_size, self.generation)
            self.ghost_population_2 = self.reproduction_ghost.reproduce(
                self.config_ghost, self.ghost_species_2,
                self.config_ghost.pop_size, self.generation)

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

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

            if not self.ghost_species_1.species:
                self.reporters_g_1.complete_extinction()

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

            if not self.ghost_species_2.species:
                self.reporters_g_2.complete_extinction()

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

            # Divide the new population into species.
            self.pacman_species.speciate(self.config, self.pacman_population,
                                         self.generation)
            self.ghost_species_1.speciate(self.config_ghost,
                                          self.ghost_population_1,
                                          self.generation)
            self.ghost_species_2.speciate(self.config_ghost,
                                          self.ghost_population_2,
                                          self.generation)

            self.reporters.end_generation(self.config, self.pacman_population,
                                          self.pacman_species)
            self.reporters_g_1.end_generation(self.config_ghost,
                                              self.ghost_population_1,
                                              self.ghost_species_1)
            self.reporters_g_2.end_generation(self.config_ghost,
                                              self.ghost_population_2,
                                              self.ghost_species_2)

            self.generation += 1

        return self.best_genome_pacman, self.best_genome_ghost_1, self.best_genome_ghost_2
Пример #4
0
class Population(object):
    """
    This class implements the core evolution algorithm:
        1. Evaluate fitness of all genomes.
        2. Check to see if the termination criterion is satisfied; exit if it is.
        3. Generate the next generation from the current population.
        4. Partition the new generation into species based on genetic similarity.
        5. Go to 1.
    """
    def __init__(self, config, num_pop=None, initial_state=None):
        self.reporters = ReporterSet()
        self.config = config
        if num_pop:
            self.global_fitnesses = GlobalFitnesses(num_pop)
        else:
            self.global_fitnesses = GlobalFitnesses(0)
        stagnation = config.stagnation_type(config.stagnation_config,
                                            self.reporters)
        self.reproduction = config.reproduction_type(
            config.reproduction_config, self.reporters, stagnation)
        if config.fitness_criterion == 'max':
            self.fitness_criterion = max
        elif config.fitness_criterion == 'min':
            self.fitness_criterion = min
        elif config.fitness_criterion == 'mean':
            self.fitness_criterion = mean
        elif not config.no_fitness_termination:
            raise RuntimeError("Unexpected fitness_criterion: {0!r}".format(
                config.fitness_criterion))

        if initial_state is None:
            # Create a population from scratch, then partition into species.
            self.population = self.reproduction.create_new(
                config.genome_type, config.genome_config, config.pop_size)
            self.species = config.species_set_type(config.species_set_config,
                                                   self.reporters)
            self.generation = 0
            self.species.speciate(config, self.population, self.generation)
        else:
            self.population, self.species, self.generation = initial_state

        self.best_genome = None

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def run(self, fitness_function, n=None):
        """
        Runs NEAT's genetic algorithm for at most n generations.  If n
        is None, run until solution is found or extinction occurs.

        The user-provided fitness_function must take only two arguments:
            1. The population as a list of (genome id, genome) tuples.
            2. The current configuration object.

        The return value of the fitness function is ignored, but it must assign
        a Python float to the `fitness` member of each genome.

        The fitness function is free to maintain external state, perform
        evaluations in parallel, etc.

        It is assumed that fitness_function does not modify the list of genomes,
        the genomes themselves (apart from updating the fitness member),
        or the configuration object.
        """

        start_time = time.time()

        if self.config.no_fitness_termination and (n is None):
            raise RuntimeError(
                "Cannot have no generational limit with no fitness termination"
            )

        k = 0
        while time.time() - start_time < 500:
            k += 1

            self.reporters.start_generation(self.generation)

            # Evaluate all genomes using the user-provided function.
            fitness_function(list(iteritems(self.population)), self.config)
            '''
            # Gather and report statistics.
            best = None
            for g in itervalues(self.population):
                if self.fitness_criterion == "min":
                    if best is None or g.fitness < best.fitness:
                        best = g
                else:
                    if best is None or g.fitness > best.fitness:
                        best = g
            self.reporters.post_evaluate(
                self.config, self.population, self.species, best)
            '''
            '''
            # Track the best genome ever seen.
            if self.fitness_criterion == "min":
                if self.best_genome is None or best.fitness < self.best_genome.fitness:
                    self.best_genome = best
            else:
                if self.best_genome is None or best.fitness > self.best_genome.fitness:
                    self.best_genome = best

            '''
            # Create the next generation from the current generation.
            current_best_seen, self.population = self.reproduction.reproduce(
                self.config, self.species, self.config.pop_size,
                self.generation, fitness_function)

            if self.fitness_criterion == min:
                if self.best_genome is None or current_best_seen.fitness < self.best_genome.fitness:
                    self.best_genome = current_best_seen

            else:
                if self.best_genome is None or current_best_seen.fitness > self.best_genome.fitness:
                    self.best_genome = current_best_seen

            print("--- %s seconds ---" % (time.time() - start_time))
            print(self.best_genome.key, self.best_genome.fitness,
                  self.best_genome.size())
            print(current_best_seen.key, current_best_seen.fitness,
                  current_best_seen.size())
            self.global_fitnesses.add_fitness(self.best_genome.fitness)

            fv = self.best_genome.fitness
            if self.fitness_criterion == min:
                if fv <= self.config.fitness_threshold:
                    self.reporters.found_solution(self.config, self.generation,
                                                  self.best_genome)
                    break
            else:
                if fv >= self.config.fitness_threshold:
                    self.reporters.found_solution(self.config, self.generation,
                                                  self.best_genome)
                    break

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

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

            self.generation += 1

        if self.config.no_fitness_termination:
            self.reporters.found_solution(self.config, self.generation,
                                          self.best_genome)
        print("--- %s seconds ---" % (time.time() - start_time))
        print(self.global_fitnesses.get_fitnesses())
        self.global_fitnesses.save_fig()
        return self.best_genome
Пример #5
0
class Population(object):
    """
    This class implements the core evolution algorithm:
        1. Evaluate fitness of all genomes.
        2. Check to see if the termination criterion is satisfied; exit if it is.
        3. Generate the next generation from the current population.
        4. Partition the new generation into species based on genetic similarity.
        5. Go to 1.
    """

    def __init__(self, config, initial_state=None):
        self.reporters = ReporterSet()
        self.config = config
        stagnation = config.stagnation_type(config.stagnation_config, self.reporters)
        self.reproduction = config.reproduction_type(config.reproduction_config,
                                                     self.reporters,
                                                     stagnation)
        if config.fitness_criterion == 'max':
            self.fitness_criterion = max
        elif config.fitness_criterion == 'min':
            self.fitness_criterion = min
        elif config.fitness_criterion == 'mean':
            self.fitness_criterion = mean
        elif not config.no_fitness_termination:
            raise RuntimeError(
                "Unexpected fitness_criterion: {0!r}".format(config.fitness_criterion))
        
        if initial_state is None:
            # Create a population from scratch, then partition into species.
            self.population = self.reproduction.create_new(config.genome_type,
                                                           config.genome_config,
                                                           config.pop_size)
            self.species = config.species_set_type(config.species_set_config, self.reporters)
            self.generation = 0
            self.species.speciate(config, self.population, self.generation)
        else:
            self.population, self.species, self.generation = initial_state

        self.best_genome = None

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def run(self, fitness_function, trafic, SUMO, n=None):
        """
        Runs NEAT's genetic algorithm for at most n generations.  If n
        is None, run until solution is found or extinction occurs.
       
        The user-provided fitness_function must take only two arguments:
            1. The population as a list of (genome id, genome) tuples.
            2. The current configuration object.
       
        The return value of the fitness function is ignored, but it must assign
        a Python float to the `fitness` member of each genome.
       
        The fitness function is free to maintain external state, perform
        evaluations in parallel, etc.
       
        It is assumed that fitness_function does not modify the list of genomes,
        the genomes themselves (apart from updating the fitness member),
        or the configuration object.
        """
        number_episodes=n
        send_iterator=0
        
        if self.config.no_fitness_termination and (n is None):
            raise RuntimeError("Cannot have no generational limit with no fitness termination")

        """Initialise simulation ======================================================================================="""
        traci.route.add(trafic.vehicle_ego.RouteID, trafic.vehicle_ego.Route)
        if trafic.vehicle2_exist:
               traci.route.add(trafic.vehicle_2.RouteID, trafic.vehicle_2.Route)
                     
        if trafic.vehicle3_exist:
              traci.route.add(trafic.vehicle_3.RouteID, trafic.vehicle_3.Route)
              traci.vehicletype.setSpeedFactor(typeID='traffic_vehicle', factor=5.0)
              cum_reward = np.zeros((number_episodes, 1))
              best_cum_reward = -1000000
              reward_mean100 = np.zeros((number_episodes, 1))
              length_episode = np.zeros((number_episodes, 1))
              data_export = np.zeros((number_episodes, 2))
              restart_step = 0  # counter for calculating the reset timing when the simulation time gets close to 24 days
              cum_reward_evaluation = [0]  # list for cum reward of evaluation episodes
              evaluation = False
              sub_ego = {}
              
#        traci.vehicle.setSpeedMode(trafic.vehicle_ego.ID, 16)             
        if trafic.training:
              trafic.vehicle_ego.depart_speed = np.random.randint(0, 30, size=number_episodes)
        else:
              trafic.vehicle_ego.depart_speed = ego_depart_speed
              traci.trafficlight.setProgram(tlsID='junction1', programID=TLS_ID)

       

           
        k = 0
        while n is None or k < n:
               k += 1
               episode=k
               try:  # for keyboard interrupt
                   """Check if total simulation time is close to 24 days ======================================================"""
                   # TraCI time inputs have a maximum value of ~24days --> restart SUMO to reset time
                   if np.sum(length_episode[restart_step:])*SUMO.sim['timestep'] > 2000000:
                       print('Almost 24 days of simulation time reached! Restarting SUMO and continue with next episode...')
                       traci.close()
                       traci.start(['sumo', '-c', 'SUMO_config.sumocfg'])
                       traci.route.add(trafic.vehicle_ego.RouteID, trafic.vehicle_ego.Route)
                       if trafic.vehicle2_exist:
                           traci.route.add(trafic.vehicle_2.RouteID, trafic.vehicle_2.Route)
                       if trafic.vehicle3_exist:
                           traci.route.add(trafic.vehicle_3.RouteID, trafic.vehicle_3.Route)
                       restart_step = episode
                   print('Episode: ', episode, '/', number_episodes)
                   send_iterator+=1
                   if send_iterator==10:
                       msg='Episode: '+ str(episode)+ '/'+ str(number_episodes)
                       andIsendtomyself(msg)
                       send_iterator=0
       
                   """Initialise episode =================================================================================="""
#                   SUMO.init_vars_episode()
#                   dynamics_ego.reset_variables()
#                   if controller == 'DQN' or controller == 'DDPG' or controller == 'hybrid_a' or controller == 'DDPG_v':
#                       nn_controller.reset_variables()
#                   if controller == 'ACC' or controller == 'hybrid_a':
#                       acc_controller.create_mode_map()
#                   if exploration_policy == 'ACC':
#                       explo_policy.create_mode_map()
#                   if (controller == 'DDPG' or controller == 'hybrid_a' or controller == 'DDPG_v') and ((episode+1) % 5 == 0):  # perform an evaluation episode (without exploration noise) every x episodes to observe the cum reward progress
#                       evaluation = True
       
                   """Anmerkung: Hier werden einige Variationen des Verkehrsszenarios für meine Trainingsepisoden definiert, wenn 'training = True'
                   gesetzt ist. Im Fall 'training = False' oder 'evaluation = True' (Evaluierungsepisoden unter gleichen Randbedingungen) wird immer eine
#                   Episode mit gleichen Randbedingungen (z.B. Geschwindigkeitsprofil vorausfahrendes Fahrzeug) gesetzt"""
#                   if trafic.evaluation:
#                       traci.vehicle.add(trafic.vehicle_ego.ID, trafic.vehicle_ego.RouteID, departSpeed='0',
#                                         typeID='ego_vehicle')  # Ego vehicle
#                       traci.trafficlight.setPhase('junction1', 0)  # set traffic light phase to 0 for evaluation (same conditions)
#                   else:
#                       traci.vehicle.add(trafic.vehicle_ego.ID, trafic.vehicle_ego.RouteID, departSpeed=np.array2string(trafic.vehicle_ego.depart_speed[episode]), typeID='ego_vehicle')  # Ego vehicle
                   if not trafic.training:
                           traci.trafficlight.setPhase('junction1', 0)
                   if trafic.training and not evaluation and trafic.vehicle3_exist:
                       trafic.vehicle3 = np.random.choice([True, False], p=[0.95, 0.05])
                       traci.lane.setMaxSpeed('gneE01_0', np.random.choice([8.33, 13.89, 19.44, 25.]))
                       traci.lane.setMaxSpeed('gneE02_0', np.random.choice([8.33, 13.89, 19.44, 25.]))
                       traci.lane.setMaxSpeed('startedge_0', np.random.choice([8.33, 13.89, 19.44, 25.]))
                       SUMO.create_v_profile_prec(a=SUMO.prec_train_amplitude[episode-1], c=SUMO.prec_train_mean[episode-1])
                   else:
                       trafic.vehicle3 = vehicle3_exist
                       traci.lane.setMaxSpeed('startedge_0', 13.89)  # 13.89
                       traci.lane.setMaxSpeed('gneE01_0', 19.44)  # 19.44
                       traci.lane.setMaxSpeed('gneE02_0', 13.89)  # 13.89
                       traci.lane.setMaxSpeed('stopedge_0', 8.33)  # 8.33
                       
                   trafic.episoden_variante=np.random.rand()*240.
#                   if trafic.vehicle2_exist:
#                       traci.vehicle.add(vehicle_2.ID, vehicle_2.RouteID, typeID='traffic_vehicle')  # preceding vehicle 1
#                   if trafic.vehicle3:
#                       traci.vehicle.add(trafic.vehicle_3.ID, trafic.vehicle_3.RouteID, typeID='traffic_vehicle')  # preceding vehicle 2
#                       if trafic.training and not evaluation:
#                           traci.vehicle.moveTo(trafic.vehicle_3.ID, 'gneE01_0', np.random.rand()*240.)
#                       else:
#                           traci.vehicle.moveTo(trafic.vehicle_3.ID, 'gneE01_0', 0.)
#       
#                   traci.simulationStep()  # to spawn vehicles
##                   if controller != 'SUMO':
##                       traci.vehicle.setSpeedMode(trafic.vehicle_ego.ID, 16)  # only emergency stopping at red traffic lights --> episode ends
#                   if trafic.vehicle2_exist:
#                       traci.vehicle.setSpeedMode(trafic.vehicle_2.ID, 17)
#                   if trafic.vehicle3:
#                       traci.vehicle.setSpeedMode(trafic.vehicle_3.ID, 17)
#       
#                   SUMO.currentvehiclelist = traci.vehicle.getIDList()
#       
#                   # SUMO subscriptions
#                   traci.vehicle.subscribeLeader(trafic.vehicle_ego.ID, 10000)
#                   traci.vehicle.subscribe(trafic.vehicle_ego.ID, [traci.constants.VAR_SPEED, traci.constants.VAR_BEST_LANES, traci.constants.VAR_FUELCONSUMPTION,
#                                                            traci.constants.VAR_NEXT_TLS, traci.constants.VAR_ALLOWED_SPEED, traci.constants.VAR_LANE_ID])
#                   
                   self.reporters.start_generation(self.generation)
#                   print(self.population[49+k])
                   # Evaluate all genomes using the user-provided function.
                   pool=Pool(processes=os.cpu_count())
                   pool.starmap(fitness_function, zip(list(iteritems(self.population)), repeat(self.config), repeat(episode)))
#                   print(self.fitness)
                   # Gather and report statistics.
                   best = None
                   for g in itervalues(self.population):
                       if best is None or g.fitness > best.fitness:
                           best = g
#                   print(best.fitness, best.size(),self.species.get_species_id(best.key),best.key)
                   self.reporters.post_evaluate(self.config, self.population, self.species, best)
              
                   # Track the best genome ever seen.
                   if self.best_genome is None or best.fitness > self.best_genome.fitness:
                       self.best_genome = best
              
                   if not self.config.no_fitness_termination:
                       # End if the fitness threshold is reached.
                       fv = self.fitness_criterion(g.fitness for g in itervalues(self.population))
                       if fv >= self.config.fitness_threshold:
                           self.reporters.found_solution(self.config, self.generation, best)
                           break
              
                   # Create the next generation from the current generation.
                   self.population = self.reproduction.reproduce(self.config, self.species,
                                                                 self.config.pop_size, self.generation)
              
                   # Check for complete extinction.
                   if not self.species.species:
                       self.reporters.complete_extinction()
              
                       # If requested by the user, create a completely new population,
                       # otherwise raise an exception.
                       if self.config.reset_on_extinction:
                           self.population = self.reproduction.create_new(self.config.genome_type,
                                                                          self.config.genome_config,
                                                                          self.config.pop_size)
                       else:
                           raise CompleteExtinctionException()
              
                   # Divide the new population into species.
                   self.species.speciate(self.config, self.population, self.generation)
              
                   self.reporters.end_generation(self.config, self.population, self.species)
              
                   self.generation += 1
              
                   if self.config.no_fitness_termination:
                          self.reporters.found_solution(self.config, self.generation, self.best_genome)
#                   
#                   print('Cumulative Reward:', cum_reward[episode])
#                   if evaluation:
#                       cum_reward_evaluation.append(cum_reward[episode])
#                       evaluation = False
#                       if cum_reward[episode] > best_cum_reward:
#                           nn_controller.save_models(savefile_best_actor+'_'+str(episode), savefile_best_critic+'_'+str(episode))
#                           best_cum_reward = cum_reward[episode]
#                           
#                   if training and (controller == 'DQN' or controller == 'hybrid_a' or controller == 'DDPG' or controller == 'DDPG_v') and liveplot:
#                       reward_mean100[episode] = nn_controller.reward_mean_100_running(cum_reward, episode)
#                       nn_controller.weight_observer(episode)
#                       plot_running(reward_mean100, episode, cum_reward_evaluation)
#                   data_export[:, 0] = cum_reward[:, 0]
#                   data_export[:, 1] = length_episode[:, 0]
#                   if training:
#                       try:
#                           if (episode+1) % 25 == 0:  # ==> save rewards every 50 episodes
#                               np.savetxt(savefile_reward, data_export)
#                           if (episode+1) % 25 == 0:  # save model every 50 episodes
#                               nn_controller.save_models(savefile_model_actor, savefile_model_critic)
#                               
#                       except OSError:
#                           print('File saving failed')
#                           pass
#                           
#                   if acc_controller:
#                       acc_controller.reset_integral_error()
#                       
               except KeyboardInterrupt:
                   print('Manual interrupt')
                   break
        traci.close()
#        traci.start(['sumo-gui', '-c', 'SUMO_config.sumocfg'])
#        sbr.result(self.best_genome, self.config,  trafic)
#        sbr.eval_genomes(self.best_genome, self.config, 0, SUMO, trafic)
        now=datetime.now()
        nowstr=now.strftime('%Y%m%d%H%M%S')
        with open('H:\\MT\\Python\\NEAT und SUMO\\saved models\\'+'best_genome_neat'+nowstr , 'wb') as f:
            pickle.dump(self.best_genome, f)
        return self.best_genome
Пример #6
0
class Population(object):
    """
    This class implements the core evolution algorithm:
        1. Evaluate fitness of all genomes.
        2. Check to see if the termination criterion is satisfied; exit if it is.
        3. Generate the next generation from the current population.
        4. Partition the new generation into species based on genetic similarity.
        5. Go to 1.
    """
    def __init__(self, config, initial_state=None):
        self.reporters = ReporterSet()
        self.config = config
        stagnation = config.stagnation_type(config.stagnation_config,
                                            self.reporters)
        self.reproduction = config.reproduction_type(
            config.reproduction_config, self.reporters, stagnation)
        if config.fitness_criterion == 'max':
            self.fitness_criterion = max
        elif config.fitness_criterion == 'min':
            self.fitness_criterion = min
        elif config.fitness_criterion == 'mean':
            self.fitness_criterion = mean
        elif not config.no_fitness_termination:
            raise RuntimeError("Unexpected fitness_criterion: {0!r}".format(
                config.fitness_criterion))

        if initial_state is None:
            # Create a population from scratch, then partition into species.
            self.population = self.reproduction.create_new(
                config.genome_type, config.genome_config, config.pop_size)
            self.species = config.species_set_type(config.species_set_config,
                                                   self.reporters)
            self.generation = 0
            self.species.speciate(config, self.population, self.generation)

        else:
            self.population, self.species, self.generation = initial_state

        if bool(self.config.novelty_search_config.novelty_search_enabled
                ) == True:
            self.novelty = config.novelty_search_type()

        self.best_genome = None

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def run(self, fitness_function, n=None):
        """
        Runs NEAT's genetic algorithm for at most n generations.  If n
        is None, run until solution is found or extinction occurs.

        The user-provided fitness_function must take only two arguments:
            1. The population as a list of (genome id, genome) tuples.
            2. The current configuration object.

        The return value of the fitness function is ignored, but it must assign
        a Python float to the `fitness` member of each genome.

        The fitness function is free to maintain external state, perform
        evaluations in parallel, etc.

        It is assumed that fitness_function does not modify the list of genomes,
        the genomes themselves (apart from updating the fitness member),
        or the configuration object.
        """

        if self.config.no_fitness_termination and (n is None):
            raise RuntimeError(
                "Cannot have no generational limit with no fitness termination"
            )

        k = 0

        if bool(self.config.novelty_search_config.novelty_search_enabled
                ) == True:
            all_behaviors = []
            all_populations = []
        while n is None or k < n:
            k += 1
            self.reporters.start_generation(self.generation)

            # Invoke novelty search if enabled in parameters, else proceed normally with objective function
            if bool(self.config.novelty_search_config.novelty_search_enabled
                    ) == True:
                # print('Novelty Search Enabled!')
                # fitness function should input values for genome.behavior, NOT genome.fitness
                fitness_function(list(self.population.items()),
                                 self.config,
                                 iteration=k - 1)

                # Novelty Search function is called, which carries out two steps:
                # Step 1: Euclidean distance/KNN is called to calculate novelty of each genome in the population and genome.fitness = novelty metric.
                # Step 2: Genome behavior is compared to behavior of novel archives. If a genome is novel enough, it is appended to the novel archive list
                novelMembers, population_behavior = self.novelty.calculateNovelty(
                    list(self.population.items()), self.config, iteration=k)
                all_behaviors.append(population_behavior)
                all_populations.append(list(self.population.values()))
            else:
                # print('Novelty Search Disabled!')
                # Evaluate all genomes using the user-provided function.
                # fitness_function(list(self.population.items()), self.config)
                fitness_function(list(self.population.items()),
                                 self.config,
                                 iteration=k - 1)

            # Gather and report statistics.
            best = None
            for g in self.population.values():
                if g.fitness is None:
                    raise RuntimeError(
                        "Fitness not assigned to genome {}".format(g.key))

                if best is None or g.fitness > best.fitness:
                    best = g
            self.reporters.post_evaluate(self.config, self.population,
                                         self.species, best)

            # Track the best genome ever seen.
            if self.best_genome is None or best.fitness > self.best_genome.fitness:
                # if self.best_genome != None:
                #     print('potential best fitness: ', best.fitness)
                #     print('current best fitness: ', self.best_genome.fitness)
                #     print('best.fitness > self.best_genome.fitness', best.fitness > self.best_genome.fitness)
                # self.best_genome = copy.deepcopy(best)
                self.best_genome = best
                # print('BEST GENOME FITNESS: ', self.best_genome.fitness)

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

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

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

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

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

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

            self.generation += 1

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

        # If novelty search is disabled
        if not self.config.novelty_search_config.novelty_search_enabled:
            return self.best_genome
        else:
            return self.best_genome, novelMembers, all_behaviors, all_populations
Пример #7
0
class Population(object):
    """
    This class implements the core evolution algorithm:
        1. Evaluate fitness of all genomes.
        2. Check to see if the termination criterion is satisfied; exit if it is.
        3. Generate the next generation from the current population.
        4. Partition the new generation into species based on genetic similarity.
        5. Go to 1.
    """
    def __init__(self, config, initial_state=None):
        print("Path:", sys.path)
        self.reporters = ReporterSet()
        self.config = config
        stagnation = config.stagnation_type(config.stagnation_config,
                                            self.reporters)
        self.reproduction = config.reproduction_type(
            config.reproduction_config, self.reporters, stagnation)
        if config.fitness_criterion == 'max':
            self.fitness_criterion = max
        elif config.fitness_criterion == 'min':
            self.fitness_criterion = min
        elif config.fitness_criterion == 'mean':
            self.fitness_criterion = mean
        elif not config.no_fitness_termination:
            raise RuntimeError("Unexpected fitness_criterion: {0!r}".format(
                config.fitness_criterion))

        if initial_state is None:
            # Create a population from scratch, then partition into species.
            self.population = self.reproduction.create_new(
                config.genome_type, config.genome_config, config.pop_size)
            self.species = config.species_set_type(config.species_set_config,
                                                   self.reporters)
            self.generation = 0
            self.species.speciate(config, self.population, self.generation)
            self.k = 0
        else:
            self.population, self.species, self.generation = initial_state

        self.best_genome = None

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def start_compute(self, current_genome_list):
        """
        Runs NEAT's genetic algorithm for at most n generations.  If n
        is None, run until solution is found or extinction occurs.

        The user-provided fitness_function must take only two arguments:
            1. The population as a list of (genome id, genome) tuples.
            2. The current configuration object.

        The return value of the fitness function is ignored, but it must assign
        a Python float to the `fitness` member of each genome.

        The fitness function is free to maintain external state, perform
        evaluations in parallel, etc.

        It is assumed that fitness_function does not modify the list of genomes,
        the genomes themselves (apart from updating the fitness member),
        or the configuration object.
        """
        if self.config.no_fitness_termination:
            raise RuntimeError(
                "Cannot have no generational limit with no fitness termination"
            )
        self.dump_genomes_pop(current_genome_list)

    def dump_genomes_pop(self, current_genome_list):
        print("Dumping pop and genomes of size: ", len(current_genome_list))
        if self.config.no_fitness_termination:
            print("no fitness termination")
            self.reporters.found_solution(self.config, self.generation,
                                          self.best_genome)

        for genome in itervalues(self.population):
            genome.fitness = 0

        self.reporters.start_generation(self.generation)

        pop_loc = neat_path + "pklDumps/pop.pkl"

        with open(pop_loc, 'wb') as popPklDump:
            print("Dumping population object to: ", neat_path, "pklDumps/pop")
            pickle.dump(self, popPklDump)

        print("Dumping current genomes")
        del current_genome_list[:]
        for index, genome in iteritems(self.population):
            print("Currently dumping: ", genome.key)
            current_genome_list.append(genome.key)
            with open(
                    neat_path + "pklDumps/genome_" + str(genome.key) + ".pkl",
                    'wb') as nnPklDump:
                pickle.dump(genome, nnPklDump)
        print("Dump complete")
        if self.best_genome is not None:
            print("best genome now: ", self.best_genome.key)
        else:
            print("Best genome null")

    # Called when ghc is done with all the neural nets in this generation
    def continue_processing(self, current_genome_list):
        print("Continue processing")
        # self.population = {}
        for genome_key in current_genome_list:
            print("Currently opening: ", genome_key)
            with open(
                    neat_path + "pklDumps/genome_" + str(genome_key) + ".pkl",
                    'rb') as nnPklRead:
                current_gen = pickle.load(nnPklRead)
                self.population[genome_key].fitness = current_gen.fitness
        best = None
        for g in itervalues(self.population):
            if best is None or g.fitness > best.fitness:
                best = g
        self.reporters.post_evaluate(self.config, self.population,
                                     self.species, best)

        print("Chosen current best as: ", best.key)

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

        print("Chosen overall best as: ", self.best_genome.key)

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

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

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

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

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

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

        self.generation += 1

        # continue to top of loop for next k
        self.dump_genomes_pop(current_genome_list)

    def get_winner(self):
        print("Get winner", self.best_genome.key)
        return self.best_genome
Пример #8
0
class Population(object):
    """
    This class implements the core NEAT algorithm.  It maintains a list of Species instances,
    each of which contains a collection of Genome instances.
    """

    def __init__(self, config, initial_population=None):
        """
        :param config: Either a config.Config object or path to a configuration file.
        :param initial_population: Either an initial set of Genome instances to be used
               as the initial population, or None, in which case a randomized set of Genomes
               will be created automatically based on the configuration parameters.
        """

        # If config is not a Config object, assume it is a path to the config file.
        if not isinstance(config, Config):
            config = Config(config)

        # Configure statistics and reporting as requested by the user.
        self.reporters = ReporterSet()
        if config.collect_statistics:
            self.statistics = StatisticsReporter()
            self.add_reporter(self.statistics)
        else:
            self.statistics = None

        if config.report:
            self.add_reporter(StdOutReporter())

        self.config = config
        self.species_indexer = Indexer(1)
        self.genome_indexer = Indexer(1)
        self.innovation_indexer = InnovationIndexer(0)
        self.reproduction = config.reproduction_type(
            self.config, self.reporters, self.genome_indexer, self.innovation_indexer
        )

        self.species = []
        self.generation = -1
        self.total_evaluations = 0

        # Create a population if one is not given, then partition into species.
        if initial_population is None:
            initial_population = self._create_population()
        self._speciate(initial_population)

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def load_checkpoint(self, filename):
        """Resumes the simulation from a previous saved point."""
        self.reporters.loading_checkpoint(filename)
        with gzip.open(filename) as f:
            (self.species, self.generation, random_state) = pickle.load(f)

            random.setstate(random_state)

    def save_checkpoint(self, filename=None, checkpoint_type="user"):
        """ Save the current simulation state. """
        if filename is None:
            filename = "neat-checkpoint-{0}".format(self.generation)

        self.reporters.saving_checkpoint(checkpoint_type, filename)

        with gzip.open(filename, "w", compresslevel=5) as f:
            data = (self.species, self.generation, random.getstate())
            pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)

    def _create_population(self):
        # Create a collection of unconnected genomes with no hidden nodes.
        new_population = []

        # TODO: The genotype class should know how to do everything below, based
        # solely on what's in the config object. This allows users to completely
        # replace the initial population creation scheme if they choose.
        for i in range(self.config.pop_size):
            g_id = self.genome_indexer.next()
            g = self.config.genotype.create_unconnected(g_id, self.config)
            new_population.append(g)

        # Add hidden nodes if requested.
        if self.config.hidden_nodes > 0:
            for g in new_population:
                g.add_hidden_nodes(self.config.hidden_nodes)

        # Add connections based on initial connectivity type.
        if self.config.initial_connection == "fs_neat":
            for g in new_population:
                g.connect_fs_neat(self.innovation_indexer)
        elif self.config.initial_connection == "fully_connected":
            for g in new_population:
                g.connect_full(self.innovation_indexer)
        elif self.config.initial_connection == "partial":
            for g in new_population:
                g.connect_partial(self.innovation_indexer, self.config.connection_fraction)

        return new_population

    def _speciate(self, population):
        """
        Place genomes into species by genetic similarity.

        Note that this method assumes the current representatives of the species are from the old
        generation, and that after speciation has been performed, the old representatives should be
        dropped and replaced with representatives from the new generation.  If you violate this
        assumption, you should make sure other necessary parts of the code are updated to reflect
        the new behavior.
        """
        for individual in population:
            # Find the species with the most similar representative.
            min_distance = None
            closest_species = None
            for s in self.species:
                distance = individual.distance(s.representative)
                if distance < self.config.compatibility_threshold:
                    if min_distance is None or distance < min_distance:
                        closest_species = s
                        min_distance = distance

            if closest_species:
                closest_species.add(individual)
            else:
                # No species is similar enough, create a new species for this individual.
                self.species.append(Species(individual, self.species_indexer.next()))

        # Only keep non-empty species.
        self.species = [s for s in self.species if s.members]

        # Select a random current member as the new representative.
        for s in self.species:
            s.representative = random.choice(s.members)

    def run(self, fitness_function, n):
        """
        Runs NEAT's genetic algorithm for n generations.

        The user-provided fitness_function should take one argument, a list of all genomes in the population,
        and its return value is ignored.  This function is free to maintain external state, perform evaluations
        in parallel, and probably any other thing you want.  The only requirement is that each individual's
        fitness member must be set to a floating point value after this function returns.

        It is assumed that fitness_function does not modify the list of genomes, or the genomes themselves, apart
        from updating the fitness member.
        """

        # Remember start time for saving timed checkpoints.
        last_checkpoint = time.time()

        for g in range(n):
            self.generation += 1

            self.reporters.start_generation(self.generation)

            # Collect a list of all members from all species.
            population = []
            for s in self.species:
                population.extend(s.members)

            # Evaluate all individuals in the population using the user-provided function.
            # TODO: Add an option to only evaluate each genome once, to reduce number of
            # fitness evaluations in cases where the fitness is known to be the same if the
            # genome doesn't change--in these cases, evaluating unmodified elites in each
            # generation is a waste of time.
            fitness_function(population)
            self.total_evaluations += len(population)

            # Gather and report statistics.
            best = max(population)
            self.reporters.post_evaluate(population, self.species, best)

            # Save the best genome from the current generation if requested.
            if self.config.save_best:
                with open("best_genome_" + str(self.generation), "wb") as f:
                    pickle.dump(best, f)

            # End if the fitness threshold is reached.
            if best.fitness >= self.config.max_fitness_threshold:
                self.reporters.found_solution(self.generation, best)
                break

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

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

                # If requested by the user, create a completely new population,
                # otherwise raise an exception.
                if self.config.reset_on_extinction:
                    new_population = self._create_population()
                else:
                    raise CompleteExtinctionException()

            # Update species age.
            for s in self.species:
                s.age += 1

            # Divide the new population into species.
            self._speciate(new_population)

            # Save checkpoints if necessary.
            if self.config.checkpoint_time_interval is not None:
                timed_checkpoint_due = last_checkpoint + 60 * self.config.checkpoint_time_interval
                if time.time() >= timed_checkpoint_due:
                    self.save_checkpoint(checkpoint_type="timed")
                    last_checkpoint = time.time()

            if (
                self.config.checkpoint_gen_interval is not None
                and self.generation % self.config.checkpoint_gen_interval == 0
            ):
                self.save_checkpoint(checkpoint_type="generation")

            self.reporters.end_generation()
Пример #9
0
class Population(object):
    """ This class implements the core NEAT algorithm. """
    def __init__(self, config, initial_state=None):
        self.reporters = ReporterSet()
        self.config = config
        stagnation = config.stagnation_type(config.stagnation_config,
                                            self.reporters)
        self.reproduction = config.reproduction_type(
            config.reproduction_config, self.reporters, stagnation)

        if initial_state is None:
            # Create a population from scratch, then partition into species.
            self.population = self.reproduction.create_new(
                config.genome_type, config.genome_config, config.pop_size)
            self.species = config.species_set_type(config)
            self.species.speciate(config, self.population)
            self.generation = -1
        else:
            self.population, self.species, self.generation = initial_state

        self.best_genome = None

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def run(self, fitness_function, n):
        """
        Runs NEAT's genetic algorithm for at most n generations.

        The user-provided fitness_function should take two arguments, a list of all genomes in the population,
        and its return value is ignored.  This function is free to maintain external state, perform evaluations
        in parallel, and probably any other thing you want.  Each individual genome's fitness member must be set
        to a floating point value after this function returns.

        It is assumed that fitness_function does not modify the list of genomes, the genomes themselves (apart
        from updating the fitness member), or the configuration object.
        """
        for g in range(n):
            self.generation += 1

            self.reporters.start_generation(self.generation)

            # Evaluate all individuals in the population using the user-provided function.
            # TODO: Add an option to only evaluate each genome once, to reduce number of
            # fitness evaluations in cases where the fitness is known to be the same if the
            # genome doesn't change--in these cases, evaluating unmodified elites in each
            # generation is a waste of time.  The user can always take care of this in their
            # fitness function in the time being if they wish.
            fitness_function(list(iteritems(self.population)), self.config)

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

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

            # End if the fitness threshold is reached.
            if best.fitness >= self.config.max_fitness_threshold:
                self.reporters.found_solution(self.config, self.generation,
                                              best)
                break

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

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

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

            # Update species age.
            # TODO: Wouldn't it be easier to remember creation time?
            for s in itervalues(self.species.species):
                s.age += 1

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

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

        return self.best_genome
Пример #10
0
class Population(object):
    """
    This class implements the core NEAT algorithm.  It maintains a list of Species instances,
    each of which contains a collection of Genome instances.
    """

    def __init__(self, config, initial_population=None):
        """
        :param config: Either a config.Config object or path to a configuration file.
        :param initial_population: Either an initial set of Genome instances to be used
               as the initial population, or None, in which case a randomized set of Genomes
               will be created automatically based on the configuration parameters.
        """

        # If config is not a Config object, assume it is a path to the config file.
        if not isinstance(config, Config):
            config = Config(config)

        # Configure statistics and reporting as requested by the user.
        self.reporters = ReporterSet()
        if config.collect_statistics:
            self.statistics = StatisticsReporter()
            self.add_reporter(self.statistics)
        else:
            self.statistics = None

        if config.report:
            self.add_reporter(StdOutReporter())

        self.config = config
        self.reproduction = config.reproduction_type(config, self.reporters)

        self.species = SpeciesSet(config)
        self.generation = -1
        self.total_evaluations = 0

        # Create a population if one is not given, then partition into species.
        if initial_population is None:
            initial_population = self.reproduction.create_new(config.pop_size)
        self.species.speciate(initial_population)

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def load_checkpoint(self, filename):
        '''Resumes the simulation from a previous saved point.'''
        self.reporters.loading_checkpoint(filename)
        with gzip.open(filename) as f:
            (self.species,
             self.generation,
             random_state) = pickle.load(f)

            random.setstate(random_state)

    def save_checkpoint(self, filename=None, checkpoint_type="user"):
        """ Save the current simulation state. """
        if filename is None:
            filename = 'neat-checkpoint-{0}'.format(self.generation)

        self.reporters.saving_checkpoint(checkpoint_type, filename)

        with gzip.open(filename, 'w', compresslevel=5) as f:
            data = (self.species,
                    self.generation,
                    random.getstate())
            pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)


    def run(self, fitness_function, n):
        """
        Runs NEAT's genetic algorithm for at most n generations.

        The user-provided fitness_function should take one argument, a list of all genomes in the population,
        and its return value is ignored.  This function is free to maintain external state, perform evaluations
        in parallel, and probably any other thing you want.  The only requirement is that each individual's
        fitness member must be set to a floating point value after this function returns.

        It is assumed that fitness_function does not modify the list of genomes, or the genomes themselves, apart
        from updating the fitness member.
        """

        # Remember start time for saving timed checkpoints.
        last_checkpoint = time.time()

        for g in range(n):
            self.generation += 1

            self.reporters.start_generation(self.generation)

            # Collect a list of all members from all species.
            population = []
            for s in self.species.species:
                population.extend(s.members)

            # Evaluate all individuals in the population using the user-provided function.
            # TODO: Add an option to only evaluate each genome once, to reduce number of
            # fitness evaluations in cases where the fitness is known to be the same if the
            # genome doesn't change--in these cases, evaluating unmodified elites in each
            # generation is a waste of time.  The user can always take care of this in their
            # fitness function in the time being if they wish.
            fitness_function(population)
            self.total_evaluations += len(population)

            # Gather and report statistics.
            best = max(population)
            self.reporters.post_evaluate(population, self.species.species, best)

            # Save the best genome from the current generation if requested.
            if self.config.save_best:
                with open('best_genome_' + str(self.generation), 'wb') as f:
                    pickle.dump(best, f)

            # End if the fitness threshold is reached.
            if best.fitness >= self.config.max_fitness_threshold:
                self.reporters.found_solution(self.generation, best)
                break

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

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

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

            # Update species age.
            for s in self.species.species:
                s.age += 1

            # Divide the new population into species.
            self.species.speciate(new_population)

            # Save checkpoints if necessary.
            if self.config.checkpoint_time_interval is not None:
                timed_checkpoint_due = last_checkpoint + 60 * self.config.checkpoint_time_interval
                if time.time() >= timed_checkpoint_due:
                    self.save_checkpoint(checkpoint_type="timed")
                    last_checkpoint = time.time()

            if self.config.checkpoint_gen_interval is not None \
                    and self.generation % self.config.checkpoint_gen_interval == 0:
                self.save_checkpoint(checkpoint_type="generation")

            self.reporters.end_generation()
Пример #11
0
class Population(object):
    """
    This class implements the core NEAT algorithm.  It maintains a list of Species instances,
    each of which contains a collection of Genome instances.
    """
    def __init__(self, config, initial_population=None):
        """
        :param config: Either a config.Config object or path to a configuration file.
        :param initial_population: Either an initial set of Genome instances to be used
               as the initial population, or None, in which case a randomized set of Genomes
               will be created automatically based on the configuration parameters.
        """

        # If config is not a Config object, assume it is a path to the config file.
        if not isinstance(config, Config):
            config = Config(config)

        # Configure statistics and reporting as requested by the user.
        self.reporters = ReporterSet()
        if config.collect_statistics:
            self.statistics = StatisticsReporter()
            self.add_reporter(self.statistics)
        else:
            self.statistics = None

        if config.report:
            self.add_reporter(StdOutReporter())

        self.config = config
        self.reproduction = config.reproduction_type(config, self.reporters)

        self.species = SpeciesSet(config)
        self.generation = -1
        self.total_evaluations = 0

        # Create a population if one is not given, then partition into species.
        if initial_population is None:
            initial_population = self.reproduction.create_new(config.pop_size)
        self.species.speciate(initial_population)

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def load_checkpoint(self, filename):
        '''Resumes the simulation from a previous saved point.'''
        self.reporters.loading_checkpoint(filename)
        with gzip.open(filename) as f:
            (self.species, self.generation, random_state) = pickle.load(f)

            random.setstate(random_state)

    def save_checkpoint(self, filename=None, checkpoint_type="user"):
        """ Save the current simulation state. """
        if filename is None:
            filename = 'neat-checkpoint-{0}'.format(self.generation)

        self.reporters.saving_checkpoint(checkpoint_type, filename)

        with gzip.open(filename, 'w', compresslevel=5) as f:
            data = (self.species, self.generation, random.getstate())
            pickle.dump(data, f, protocol=pickle.HIGHEST_PROTOCOL)

    def run(self, fitness_function, n):
        """
        Runs NEAT's genetic algorithm for at most n generations.

        The user-provided fitness_function should take one argument, a list of all genomes in the population,
        and its return value is ignored.  This function is free to maintain external state, perform evaluations
        in parallel, and probably any other thing you want.  The only requirement is that each individual's
        fitness member must be set to a floating point value after this function returns.

        It is assumed that fitness_function does not modify the list of genomes, or the genomes themselves, apart
        from updating the fitness member.
        """

        # Remember start time for saving timed checkpoints.
        last_checkpoint = time.time()

        for g in range(n):
            self.generation += 1

            self.reporters.start_generation(self.generation)

            # Collect a list of all members from all species.
            population = []
            for s in self.species.species:
                population.extend(s.members)

            # Evaluate all individuals in the population using the user-provided function.
            # TODO: Add an option to only evaluate each genome once, to reduce number of
            # fitness evaluations in cases where the fitness is known to be the same if the
            # genome doesn't change--in these cases, evaluating unmodified elites in each
            # generation is a waste of time.  The user can always take care of this in their
            # fitness function in the time being if they wish.
            fitness_function(population)
            self.total_evaluations += len(population)

            # Gather and report statistics.
            best = max(population)
            self.reporters.post_evaluate(population, self.species.species,
                                         best)

            # Save the best genome from the current generation if requested.
            if self.config.save_best:
                with open('best_genome_' + str(self.generation), 'wb') as f:
                    pickle.dump(best, f)

            # End if the fitness threshold is reached.
            if best.fitness >= self.config.max_fitness_threshold:
                self.reporters.found_solution(self.generation, best)
                break

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

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

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

            # Update species age.
            for s in self.species.species:
                s.age += 1

            # Divide the new population into species.
            self.species.speciate(new_population)

            # Save checkpoints if necessary.
            if self.config.checkpoint_time_interval is not None:
                timed_checkpoint_due = last_checkpoint + 60 * self.config.checkpoint_time_interval
                if time.time() >= timed_checkpoint_due:
                    self.save_checkpoint(checkpoint_type="timed")
                    last_checkpoint = time.time()

            if self.config.checkpoint_gen_interval is not None \
                    and self.generation % self.config.checkpoint_gen_interval == 0:
                self.save_checkpoint(checkpoint_type="generation")

            self.reporters.end_generation()
Пример #12
0
class Population(object):
    """
    This class implements the core evolution algorithm:
        1. Evaluate fitness of all genomes.
        2. Check to see if the termination criterion is satisfied; exit if it is.
        3. Generate the next generation from the current population.
        4. Partition the new generation into species based on genetic similarity.
        5. Go to 1.
    """
    def __init__(self, config, initial_state=None):
        self.reporters = ReporterSet()
        self.config = config
        stagnation = config.stagnation_type(config.stagnation_config,
                                            self.reporters)
        self.reproduction = config.reproduction_type(
            config.reproduction_config, self.reporters, stagnation)
        if config.fitness_criterion == 'max':
            self.fitness_criterion = max
        elif config.fitness_criterion == 'min':
            self.fitness_criterion = min
        elif config.fitness_criterion == 'mean':
            self.fitness_criterion = mean
        elif not config.no_fitness_termination:
            raise RuntimeError("Unexpected fitness_criterion: {0!r}".format(
                config.fitness_criterion))

        if initial_state is None:
            # Create a population from scratch, then partition into species.
            self.population = self.reproduction.create_new(
                config.genome_type, config.genome_config, config.pop_size)
            self.species = config.species_set_type(config.species_set_config,
                                                   self.reporters)
            self.generation = 0
            self.species.speciate(config, self.population, self.generation)
        else:
            self.population, self.species, self.generation = initial_state

        self.best_genome = None

    def add_reporter(self, reporter):
        self.reporters.add(reporter)

    def remove_reporter(self, reporter):
        self.reporters.remove(reporter)

    def run(self, fitness_function, n=None):
        """
        Runs NEAT's genetic algorithm for at most n generations.  If n
        is None, run until solution is found or extinction occurs.

        The user-provided fitness_function must take only two arguments:
            1. The population as a list of (genome id, genome) tuples.
            2. The current configuration object.

        The return value of the fitness function is ignored, but it must assign
        a Python float to the `fitness` member of each genome.

        The fitness function is free to maintain external state, perform
        evaluations in parallel, etc.

        It is assumed that fitness_function does not modify the list of genomes,
        the genomes themselves (apart from updating the fitness member),
        or the configuration object.
        """

        if self.config.no_fitness_termination and (n is None):
            raise RuntimeError(
                "Cannot have no generational limit with no fitness termination"
            )

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

            self.reporters.start_generation(self.generation)

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

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

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

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

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

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

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

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

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

            self.generation += 1

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

        return self.best_genome

    def dominates(self, one, other):
        """Return true if each objective of *one* is not strictly worse than
        the corresponding objective of *other* and at least one objective is
        strictly better.
        """
        not_equal = False
        for self_wvalue, other_wvalue in zip(one, other):
            if self_wvalue > other_wvalue:
                not_equal = True
            elif self_wvalue < other_wvalue:
                return False
        return not_equal

    def sortNondominatedNSGA2(self, k, first_front_only=False):
        """Sort the first *k* *individuals* into different nondomination levels
        using the "Fast Nondominated Sorting Approach" proposed by Deb et al.,
        see [Deb2002]_. This algorithm has a time complexity of :math:`O(MN^2)`,
        where :math:`M` is the number of objectives and :math:`N` the number of
        individuals.

        :param individuals: A list of individuals to select from.
        :param k: The number of individuals to select.
        :param first_front_only: If :obj:`True` sort only the first front and
                                 exit.
        :returns: A list of Pareto fronts (lists), the first list includes
                  nondominated individuals.

        .. [Deb2002] Deb, Pratab, Agarwal, and Meyarivan, "A fast elitist
           non-dominated sorting genetic algorithm for multi-objective
           optimization: NSGA-II", 2002.
        """
        if k == 0:
            return []

        map_fit_ind = defaultdict(list)
        for key in self.population:
            ind = self.population[key]
            map_fit_ind[ind.fitness_mult].append(ind)
        fits = list(map_fit_ind.keys())

        current_front = []
        next_front = []
        dominating_fits = defaultdict(int)
        dominated_fits = defaultdict(list)

        # Rank first Pareto front
        for i, fit_i in enumerate(fits):
            for fit_j in fits[i + 1:]:
                if self.dominates(fit_i, fit_j):
                    dominating_fits[fit_j] += 1
                    dominated_fits[fit_i].append(fit_j)
                elif self.dominates(fit_j, fit_i):
                    dominating_fits[fit_i] += 1
                    dominated_fits[fit_j].append(fit_i)
            if dominating_fits[fit_i] == 0:
                current_front.append(fit_i)

        fronts = [[]]
        for fit in current_front:
            fronts[-1].extend(map_fit_ind[fit])
        pareto_sorted = len(fronts[-1])

        # Rank the next front until all individuals are sorted or
        # the given number of individual are sorted.
        if not first_front_only:
            N = min(len(self.population), k)
            while pareto_sorted < N:
                fronts.append([])
                for fit_p in current_front:
                    for fit_d in dominated_fits[fit_p]:
                        dominating_fits[fit_d] -= 1
                        if dominating_fits[fit_d] == 0:
                            next_front.append(fit_d)
                            pareto_sorted += len(map_fit_ind[fit_d])
                            fronts[-1].extend(map_fit_ind[fit_d])
                current_front = next_front
                next_front = []

        return fronts

    def assignCrowdingDist(self, individuals):
        """Assign a crowding distance to each individual's fitness.
        It is done per front.
        """
        if len(individuals) == 0:
            return

        distances = [0.0] * len(individuals)
        crowd = [(ind.fitness_mult, i) for i, ind in enumerate(individuals)]

        nobj = len(individuals[0].fitness_mult)

        for i in range(nobj):
            crowd.sort(key=lambda element: element[0][i])
            distances[crowd[0][1]] = float("inf")
            distances[crowd[-1][1]] = float("inf")
            if crowd[-1][0][i] == crowd[0][0][i]:
                continue
            norm = nobj * float(crowd[-1][0][i] - crowd[0][0][i])
            for prev, cur, next in zip(crowd[:-2], crowd[1:-1], crowd[2:]):
                distances[cur[1]] += (next[0][i] - prev[0][i]) / norm

        # find max and min distance
        max_val = -float("inf")
        min_val = float("inf")
        flag_plus_inf = False
        flag_minus_inf = False
        for dist in distances:
            if dist != float("inf") and max_val < dist:
                max_val = dist
                pass
            if dist != -float("inf") and min_val > dist:
                min_val = dist
                pass
            if dist == float("inf"):
                flag_plus_inf = True
            elif dist == -float("inf"):
                flag_minus_inf = True
            pass

        # set values equal to inf to be max + 0.5
        # set values equal to -inf to be max - 0.5
        # and rescale the rest
        if flag_plus_inf:
            max_val += 0.5
        if flag_minus_inf:
            min_val -= 0.5
        for i in range(0, len(distances)):
            if distances[i] == float("inf"):
                distances[i] = 1.
            elif distances[i] == -float("inf"):
                distances[i] = 0.
            else:
                distances[i] = (distances[i] - min_val) / (max_val - min_val)
                pass
            pass

        for i, dist in enumerate(distances):
            individuals[i].crowding_dist = dist / 2
            pass
        pass

    def run_NSGA2(self, fitness_function, n=None, multi=False, algo='NSGA-2'):
        # algo possible values: NSGA-2, PO - pareto-optimality
        """
        Runs NEAT's genetic algorithm for at most n generations.  If n
        is None, run until solution is found or extinction occurs.

        The user-provided fitness_function must take only two arguments:
            1. The population as a list of (genome id, genome) tuples.
            2. The current configuration object.

        The return value of the fitness function is ignored, but it must assign
        a Python float to the `fitness` member of each genome.

        The fitness function is free to maintain external state, perform
        evaluations in parallel, etc.

        It is assumed that fitness_function does not modify the list of genomes,
        the genomes themselves (apart from updating the fitness member),
        or the configuration object.
        """

        if self.config.no_fitness_termination and (n is None):
            raise RuntimeError(
                "Cannot have no generational limit with no fitness termination"
            )

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

            self.reporters.start_generation(self.generation)

            # Evaluate all genomes using the user-provided function.
            fitness_function(list(iteritems(self.population)), self.config)
            # if multi-objective, set fitness
            if multi:
                if algo == 'NSGA-2':
                    # sort NSGA-2
                    fronts = self.sortNondominatedNSGA2(len(self.population))
                    pass
                # assign crowding distance
                for front in fronts:
                    self.assignCrowdingDist(front)
                # now assign fitness value
                num_fronts = len(fronts)
                for i in range(0, num_fronts):
                    front = fronts[i]
                    for el in front:
                        el.fitness = (num_fronts - i) + el.crowding_dist
                        pass
                    pass
                pass

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

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

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

            # Create the next generation from the current generation.
            new_population = self.reproduction.reproduce(
                self.config, self.species, self.config.pop_size,
                self.generation)
            fitness_function(list(iteritems(new_population)), self.config)
            self.population = {**self.population, **new_population}
            # if multi-objective, set fitness
            if multi:
                if algo == 'NSGA-2':
                    # sort NSGA-2
                    fronts = self.sortNondominatedNSGA2(len(self.population))
                    pass
                # assign crowding distance
                for front in fronts:
                    self.assignCrowdingDist(front)
                # now assign fitness value
                num_fronts = len(fronts)
                for i in range(0, num_fronts):
                    front = fronts[i]
                    for el in front:
                        el.fitness = (num_fronts - i) + el.crowding_dist
                        pass
                    pass
                pass
            new_population = list(iteritems(self.population))
            new_population.sort(reverse=True, key=lambda x: x[1].fitness)
            self.population = dict(new_population[:self.config.pop_size])
            # fitness_function(list(iteritems(self.population)), self.config)

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

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

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

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

            self.generation += 1

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

        return self.best_genome