Пример #1
0
def selNSGA2(individuals, k):
    """Apply NSGA-II selection operator on the *individuals*. Usually, the
    size of *individuals* will be larger than *k* because any individual
    present in *individuals* will appear in the returned list at most once.
    Having the size of *individuals* equals to *k* will have no effect other
    than sorting the population according to a non-domination scheme. The list
    returned contains references to the input *individuals*. For more details
    on the NSGA-II operator see [Deb2002]_.

    :param individuals: A list of individuals to select from.
    :param k: The number of individuals to select.
    :returns: A list of selected individuals.

    .. [Deb2002] Deb, Pratab, Agarwal, and Meyarivan, "A fast elitist
       non-dominated sorting genetic algorithm for multi-objective
       optimization: NSGA-II", 2002.
    """
    pareto_fronts = sortFastND(individuals, k)
    for front in pareto_fronts:
        assignCrowdingDist(front)

    chosen = list(chain(*pareto_fronts[:-1]))
    k = k - len(chosen)
    if k > 0:
        sorted_front = sorted(pareto_fronts[-1], key=attrgetter("fitness.crowding_dist"), reverse=True)
        chosen.extend(sorted_front[:k])

    return chosen, pareto_fronts[0]
Пример #2
0
    def _run_optimization(self, toolbox, generate_individual, 
                           evaluate_population, attr_list, keys, obj_function, 
                           pop_size, reporting_interval, weights, 
                           nr_of_generations, crossover_rate, mutation_rate,
                           levers, caching, **kwargs):
        '''
        Helper function that runs the actual optimization
                
        :param toolbox: 
        :param generate_individual: helper function for generating an 
                                    individual
        :param evaluate_population: helper function for evaluating the 
                                    population
        :param attr_list: list of attributes (alleles)
        :param keys: the names of the attributes in the same order as attr_list
        :param obj_function: the objective function
        :param pop_size: the size of the population
        :param reporting_interval: the interval for reporting progress, passed
                                   on to perform_experiments
        :param weights: the weights on the outcomes
        :param nr_of_generations: number of generations for which the GA will 
                                  be run
        :param crossover_rate: the crossover rate of the GA
        :param mutation_rate: the muation rate of the GA
        :param levers: a dictionary with param keys as keys, and as values
                       info used in mutation.
        
        '''
        # figure out whether we are doing single or multi-objective 
        # optimization
        #TODO raise error if not specified
        single_obj = True
        if len(weights) >1: 
            single_obj=False
        
        # Structure initializers
        toolbox.register("individual", 
                         generate_individual, 
                         creator.Individual, #@UndefinedVariable
                         attr_list, keys=keys) 
        toolbox.register("population", tools.initRepeat, list, 
                         toolbox.individual)
    
        # Operator registering
        toolbox.register("evaluate", obj_function)
        toolbox.register("crossover", tools.cxOnePoint)
        toolbox.register("mutate", mut_polynomial_bounded)
       
        if single_obj:
            toolbox.register("select", tools.selTournament)
        else:
            toolbox.register("select", tools.selNSGA2)

        # generate population
        # for some stupid reason, DEAP demands a multiple of four for 
        # population size in case of NSGA-2 
        pop_size = closest_multiple_of_four(pop_size)
        info("population size restricted to %s " % (pop_size))
        pop = toolbox.population(pop_size)
        
        debug("Start of evolution")
        
        # Evaluate the entire population
        evaluate_population(pop, reporting_interval, toolbox, self)
        
        if not single_obj:
            # This is just to assign the crowding distance to the individuals
            tools.assignCrowdingDist(pop)
    
        #some statistics logging
        stats_callback = NSGA2StatisticsCallback(weights=weights,
                                    nr_of_generations=nr_of_generations,
                                    crossover_rate=crossover_rate, 
                                    mutation_rate=mutation_rate, 
                                    pop_size=pop_size, 
                                    caching=caching)
        stats_callback(pop)
        stats_callback.log_stats(0)

        # Begin the generational process
        for gen in range(nr_of_generations):
            pop = self._run_geneneration(pop, crossover_rate, mutation_rate, 
                                          toolbox, reporting_interval, levers, 
                                          evaluate_population, keys, 
                                          single_obj, stats_callback, 
                                          caching, **kwargs)
            stats_callback(pop)
            stats_callback.log_stats(gen)    
        info("-- End of (successful) evolution --")

        return stats_callback, pop