Ejemplo n.º 1
0
 def initialize(self):
     super().initialize()
     # Generate a new population
     self.population = Population(
         size=self.population_size,
         spawning_pool=self.spawning_pool,
     )
     for individual in self.population:
         individual.fitness_method = self.fitness
     # Clear the best individuals historical cache
     self.best_individuals.clear()
Ejemplo n.º 2
0
class SimpleGA(GeneticAlgorithm):
    """ Simple implementation of a GeneticAlgorithm

    This subclass implements the basic behavior of a genetic algorithm with some
    degree of configuration.
    """

    def __init__(
            self,
            stop_condition,
            population_size,
            spawning_pool,
            fitness,
            selection,
            recombination,
            replacement,
            mutation=None,
            diversity=None,
            p_recombination=0.9,
            p_mutation=0.1,
            replacement_rate=1.0,
    ):
        """ Initializes this instance.

        :param stop_condition: The condition to be met in order to stop the
            genetic algorithm.
        :param population_size: The size this population should have.
        :param spawning_pool: The object that generates individuals.
        :param fitness: The method to evaluate individuals. It's expected to be
            a callable that returns a float value where the higher the value,
            the better the individual. Instances of subclasses of class Fitness
            can be used for this purpose.
        :param selection: The method to select individuals of the population to
            recombine.
        :param replacement: The method that will add and remove individuals from
            the population given the set of old individuals (i.e. the ones on
            the population before the evolution step) and new individuals (i.e.
            the offspring).
        :param recombination: The method to recombine parents in order to
            generate an offspring with characteristics of the parents. If none,
            no recombination will be applied.
        :param mutation: The method to mutate an individual. If none, no
            mutation over the individual will be applied. If not provided, no
            mutation is performed.
        :param diversity: The method to compute the diversity of a sequence of
            individuals generated by this SpawningPool instance. Is expected to
            be a function that generates a diversity representation given a
            subset of individuals. Instances of subclasses of class Diversity
            can be used for this purpose.
        :param p_recombination: The odds for recombination method to be
            performed over a set of selected individuals to generate progeny. If
            not performed, progeny will be the parents. Must be a value between
            0 and 1 (both included). If not provided, defaults to 1.0.
        :param p_mutation: The odds for mutation method to be performed over a
            progeny. It's applied once for each individual. If not performed the
            individuals will not be modified. Must be a value between 0 and 1
            (both included). If not provided, it defaults to 0.0 (no mutation is
            performed).
        :param replacement_rate: The rate of individuals to be replaced in each
            step of the algorithm. Must be a float value in the (0, 1] interval.
        :raises WrongValueForIntervalError: If any of the bounded values fall
            out of their respective intervals.
        :raises NotAProbabilityError: If a value was expected to be a
            probability and it wasn't.
        :raises UnexpectedClassError: If any of the input variables doesn't
            follow the contract required (i.e. doesn't inherit from a predefined
            class).
        """
        super().__init__(stop_condition=stop_condition)

        self.population_size = population_size
        self.spawning_pool = spawning_pool
        self.fitness = fitness
        self.offspring_size = int(math.ceil(population_size * replacement_rate))
        self.selection = selection
        self.recombination = recombination
        self.mutation = mutation or NoMutation()
        self.diversity = diversity
        self.replacement = replacement
        self.replacement_rate = replacement_rate
        self.p_recombination = p_recombination
        self.p_mutation = p_mutation

        self.selection_size = len(
            inspect.signature(recombination.__call__).parameters
        )
        self.population = None
        self.best_individuals = []

    def initialize(self):
        super().initialize()
        # Generate a new population
        self.population = Population(
            size=self.population_size,
            spawning_pool=self.spawning_pool,
        )
        for individual in self.population:
            individual.fitness_method = self.fitness
        # Clear the best individuals historical cache
        self.best_individuals.clear()

    def step(self):
        offspring = []
        while len(offspring) < self.offspring_size:
            # Selection
            parents = self.selection(self.population, self.selection_size)
            # Recombination
            if take_chances(self.p_recombination):
                progeny = self.recombination(*parents)
            else:
                progeny = [i.clone() for i in parents]
            # Mutation
            individuals_who_fit = min(
                len(progeny),
                self.offspring_size - len(offspring)
            )
            progeny = [
                self.mutation(individual, self.p_mutation)
                for individual in random.sample(progeny, individuals_who_fit)
                ]
            # Add progeny to the offspring
            offspring.extend(progeny)

        # Once offspring is generated, a replace step is performed
        self.replacement(self.population, offspring)

        # We store the best individual for further information
        if self.generation < len(self.best_individuals):
            self.best_individuals[self.generation] = self.population.best()
        else:
            self.best_individuals.append(self.population.best())

    def best(self, generation=None):
        if self.best_individuals:
            generation = generation or -1
            if generation > len(self.best_individuals) - 1:
                raise PyneticsError()
            else:
                return self.best_individuals[generation]
        else:
            return None