def _run_geneneration(self, pop, crossover_rate, mutation_rate, toolbox, reporting_interval, allele_dict, evaluate_population, keys, single_obj, stats_callback, caching, **kwargs): ''' Helper function for runing a single generation. :param pop: :param crossover_rate: :param mutation_rate: :param toolbox: :param reporting_interval: :param allele_dict: :param evaluate_population: :param keys: :param single_obj: ''' # Variate the population pop_size = len(pop) a = pop[0:closest_multiple_of_four(len(pop))] if single_obj: offspring = toolbox.select(pop, pop_size, min(pop_size, 10)) else: offspring = tools.selTournamentDCD(a, len(pop)) offspring = [toolbox.clone(ind) for ind in offspring] no_name=False for child1, child2 in zip(offspring[::2], offspring[1::2]): # Apply crossover if random.random() < crossover_rate: keys = sorted(child1.keys()) try: keys.pop(keys.index("name")) except ValueError: no_name = True child1_temp = [child1[key] for key in keys] child2_temp = [child2[key] for key in keys] toolbox.crossover(child1_temp, child2_temp) if not no_name: for child, child_temp in zip((child1, child2), (child1_temp,child2_temp)): name = "" for key, value in zip(keys, child_temp): child[key] = value name += " "+str(child[key]) child['name'] = name else: for child, child_temp in zip((child1, child2), (child1_temp,child2_temp)): for key, value in zip(keys, child_temp): child[key] = value #apply mutation toolbox.mutate(child1, mutation_rate, allele_dict, keys, 0.05) toolbox.mutate(child2, mutation_rate, allele_dict, keys, 0.05) for entry in (child1, child2): del entry.fitness.values if caching: for entry in (child1, child2): try: ind = stats_callback.tried_solutions[entry] except KeyError: del entry.fitness.values continue entry.fitness = ind.fitness # Evaluate the individuals with an invalid fitness invalid_ind = [ind for ind in offspring if not ind.fitness.valid] evaluate_population(invalid_ind, reporting_interval, toolbox, self) # Select the next generation population if single_obj: pop = offspring else: pop = toolbox.select(pop + offspring, pop_size) return pop
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