def continuous_ga(model, weights, hparams, NGEN=40, nb_indiv=80, nb_threads=1, cxpb=0.7, mutpb=0.4, mu=0, sigma=0.3, indpb=0.05, alpha=0.1, tournsize=3, stric_interval=False, log_file=None, recover_file=None, **kwargs): """ Main function to run the genetic algorithm for continuous optimization Parameters: model (python callable) evaluation function weights (tuple): weights of the metrics of the python callable hparams (python dict with specific structure): parameter space, the format is presented in the __main__ function NGEN (Integer): Number of generations/iterations nb_indiv (Integer):Number of individuals in the population nb_thread (Integer): number of processes that will run in parallel (allows a lot of speed up if the test function is costly) cxpb (Float between 0 and 1): probability of crossing two individuals mutpb (Float between 0 and 1): probability of mutating one individual mu (Float): expectancy of the mutation sigma (Float): variance of the mutation indpb (float between 0 and 1): probability of mutating one gene tournsize (Integer): size of the tournament for selection strict_interval (Bool): If True the solutions are constrained to the design space defined in hparams, if False the GA can evolve outside the bounds log_file (str): string of the log file to create to register all the executions and the relations between individuals recover_file (str): If you want to resume the computation from a log_file **kwargs: extra arguments forwarded to the model Outputs: pops (list of populations(list of individuals)) this object is written in the pickle file but you can have it unpickled as the output of the algorithm """ #if we are resuming the computation from a log_file if recover_file: with open(recover_file, 'rb') as pickle_file: hparams = pickle.load(pickle_file) translator = Continous_DNA(hparams, stric_interval=stric_interval) pops = pickle.load(pickle_file) #creating Individual and Fitness class creation_deap_classes(creator, weights) #toolbox object from deap: allows to define functions in one line for operations on the individual toolbox = base.Toolbox() #registering the operators for running the algorithm creation_tools(toolbox, model, translator, creator, weights, alpha, mu, sigma, indpb, tournsize, nb_threads, **kwargs) #recovering the population from the log_file population = recover_last_gen(pops, creator, translator) #Performing selection and cross and mutation to create the new generation population = toolbox.select([ indiv for indiv in population if translator.is_dna_viable(indiv) ], k=nb_indiv) population = algorithms.varAnd(population, toolbox, cxpb=cxpb, mutpb=mutpb) #otherwise initialize from else: translator = Continous_DNA(hparams, stric_interval=stric_interval) #creating Individual and Fitness class creation_deap_classes(creator, weights) #toolbox object from deap: allows to define functions in one line for operations on the individual toolbox = base.Toolbox() creation_tools(toolbox, model, translator, creator, weights, alpha, mu, sigma, indpb, tournsize, nb_threads, **kwargs) #we are now creating the population population = toolbox.population(n=nb_indiv) pops = [] init_integer = len(pops) #Starting the main loop for the genetic algorithm for gen in range(init_integer, NGEN): print('Generation: ' + str(gen + 1)) #creating ids if needed for l, ind in enumerate(population): ind.age += 1 if ind.mutated != None or ind.parents != None or gen == 0: ind.id = str(gen + 1) + "." + str(l) #Evaluation of the individuals fits = toolbox.map(toolbox.evaluate, population) #assigning fitnesses for fit, ind in zip(fits, population): ind.fitness.values = fit # Printing the best individual top1 = tools.selBest(population, k=1) print(translator.dna_to_phen(top1[0])) print(top1[0].fitness.values) #Registering the information in the pickle file pops.append([{ "phen": translator.dna_to_phen(indiv), "fits": indiv.fitness.values, "age": indiv.age, "id": indiv.id, "parents": indiv.parents, "mutated": indiv.mutated } for indiv in population if translator.is_dna_viable(indiv)]) #Selection of the individuals population = toolbox.select( [indiv for indiv in population if translator.is_dna_viable(indiv)], k=len(population)) #Crossing and mutating population = algorithms.varAnd(population, toolbox, cxpb=cxpb, mutpb=mutpb) if log_file: with open(log_file, 'wb') as pickle_file: pickle.dump(hparams, pickle_file, protocol=pickle.HIGHEST_PROTOCOL) pickle.dump(pops, pickle_file, protocol=pickle.HIGHEST_PROTOCOL) #at the end we return all the executions even though they might have been registered in the pickle file return pops
def run_evolution(model, hparams, NGEN=40, mut_rate=0.05, tourn_size=3, cxpb=0.5, mutpb=0.3, nb_indiv=80, nb_threads=1, log_file=None, recover_file=None, gray_code=False, **kwargs): """ Arguments: model (python callable): black box function to be optimized: python function that returns the fitness as first argument it must take one configuration of the hyperparameters hparams (python dictionnary): parameter space defined in the following format: hparams={ "character1":[1,2,3], "character2":["relu","sig"] } mut_rate (float between 0 and 1): the probability of mutating one gene tourn_size (int): the size of the tournament cxpb (float between 0 and 1): probability of crossing two individuals, otherwise the individuals are kept as such mutpb (float between 0 and 1): probability to mutate an individual nb_indiv (int): number of individuals per generation nb_thread (int): number of processes needed for the GA to run log_file (str): name of the file for saving the generations recover_file (str): name of the log_file that you want to recover from gray_code (bool): binary encoding technique: if True gray code is used, binary encoding if False **kwargs is all the extra arguments that need to be passed to the model (a data reader for instance) Outputs: pops (list of populations(list of individuals)) this object is written in the pickle file but you can have it unpickled as the output of the algorithm """ if recover_file: with open(recover_file, 'rb') as pickle_file: hparams = pickle.load(pickle_file) translator = DNA_creator(hparams, gray_code=gray_code) pops = pickle.load(pickle_file) #creating Individual and Fitness class creation_deap_classes(creator) #toolbox object from deap: allows to define functions in one line for operations on the individual toolbox = base.Toolbox() #registering the operators for running the algorithm creation_tools(toolbox, model, translator, creator, mutpb, tourn_size, nb_threads, **kwargs) population = recover_last_gen(pops, creator, translator) #Performing selection and cross and mutation to create the new generation population = toolbox.select([ indiv for indiv in population if translator.is_dna_viable(indiv) ], k=nb_indiv) population = algorithms.varAnd(population, toolbox, cxpb=cxpb, mutpb=mutpb) else: translator = DNA_creator(hparams, gray_code=gray_code) #creating Individual and Fitness class creation_deap_classes(creator) #toolbox object from deap: allows to define functions in one line for operations on the individual toolbox = base.Toolbox() #registering the operators for running the algorithm creation_tools(toolbox, model, translator, creator, mutpb, tourn_size, nb_threads, **kwargs) #we are now creating the population population = toolbox.population(n=nb_indiv) pops = [] init_integer = len(pops) #We are running the genetic algorithm for gen in range(init_integer, NGEN): print('Generation: ' + str(gen + 1)) #creating ids if needed for l, ind in enumerate(population): ind.age += 1 if ind.mutated != None or ind.parents != None or gen == 0: ind.id = str(gen + 1) + "." + str(l) #Evaluation of the individuals fits = toolbox.map(toolbox.evaluate, population) #assigning fitness for fit, ind in zip(fits, population): ind.fitness.values = fit # Printing the best individual top1 = tools.selBest(population, k=1) print(translator.dna_to_phen(top1[0])) print(top1[0].fitness.values) #Registering the information in the pickle file pops.append([{ "phen": translator.dna_to_phen(indiv), "fits": indiv.fitness.values, "age": indiv.age, "id": indiv.id, "parents": indiv.parents, "mutated": indiv.mutated } for indiv in population if translator.is_dna_viable(indiv)]) #Selection of the individuals population = toolbox.select( [indiv for indiv in population if translator.is_dna_viable(indiv)], k=len(population)) population = algorithms.varAnd(population, toolbox, cxpb=cxpb, mutpb=mutpb) if log_file: with open(log_file, 'wb') as pickle_file: pickle.dump(hparams, pickle_file, protocol=pickle.HIGHEST_PROTOCOL) pickle.dump(pops, pickle_file, protocol=pickle.HIGHEST_PROTOCOL) return pops
def run_constrained_hybrid_ga(model, weights, hparams, NGEN=40, nb_indiv=80, nb_threads=1, cxpb=0.7, mutpb=0.4, discrete_cx=tools.cxTwoPoint, discrete_cx_kwargs={}, continuous_cx=tools.cxBlend, continuous_cx_kwargs={"alpha": 0.1}, discrete_mut=tools.mutFlipBit, discrete_mut_kwargs={"indpb": 0.05}, continuous_mut=tools.mutGaussian, continuous_mut_kwargs={ "mu": 0., "sigma": 0.1, "indpb": 0.05 }, selection_op=tools.selTournament, selection_kwargs={"tournsize": 3}, stric_interval=False, log_file=None, recover_file=None, gray_code=True, constraints=[], **kwargs): if recover_file: with open(recover_file, 'rb') as pickle_file: hparams = pickle.load(pickle_file) translator = HybridDNA(hparams, stric_interval=stric_interval, gray_code=gray_code) pops = pickle.load(pickle_file) creator.create("FitnessHidden", FitnessReg, weights=weights) creator.create("FitnessMax", ConstrainedFitness, base_fit=creator.FitnessHidden) #weight is the weights given to the different fitnesses (incase you have a multiobjective optimization to make) creator.create("Individual", list, fitness=creator.FitnessMax, parents=None, mutated=None, id=None, age=0) #toolbox object from deap: allows to define functions in one line for operations on the individual toolbox = base.Toolbox() toolbox.register("individual", generate, translator=translator, creator=creator) toolbox.register("population", tools.initRepeat, list, toolbox.individual) #funtion that retruns the fitness from an individual #the output is a tuple to match the format of the weights given just above in fitnessMax definition #deap basic funtion to cross a population toolbox.register("evaluate", evaluate, model=model, translator=translator, weights=weights, **kwargs) toolbox.register("mate", decorator_cross(hybrid_cx), discrete_cx=discrete_cx, discrete_cx_kwargs=discrete_cx_kwargs, continuous_cx=continuous_cx, continuous_cx_kwargs=continuous_cx_kwargs) toolbox.register("mutate", decorator_mut(hybrid_mut), discrete_mut=discrete_mut, discrete_mut_kwargs=discrete_mut_kwargs, continuous_mut=continuous_mut, continuous_mut_kwargs=continuous_mut_kwargs) toolbox.register("select", decorator_selection(selection_op), **selection_kwargs) population = recover_last_gen(pops, creator, translator) #nb_indiv=len(population) #Performing selection and cross and mutation to create the new generation population = toolbox.select([ indiv for indiv in population if translator.is_dna_viable(indiv) ], k=nb_indiv) population = algorithms.varAnd(population, toolbox, cxpb=cxpb, mutpb=mutpb) else: translator = HybridDNA(hparams, stric_interval=stric_interval, gray_code=gray_code) creator.create("FitnessHidden", FitnessReg, weights=weights) creator.create("FitnessMax", ConstrainedFitness, base_fit=creator.FitnessHidden) #weight is the weights given to the different fitnesses (incase you have a multiobjective optimization to make) creator.create("Individual", list, fitness=creator.FitnessMax, parents=None, mutated=None, id=None, age=0) #toolbox object from deap: allows to define functions in one line for operations on the individual toolbox = base.Toolbox() toolbox.register("individual", generate, translator=translator, creator=creator) toolbox.register("population", tools.initRepeat, list, toolbox.individual) #funtion that retruns the fitness from an individual #the output is a tuple to match the format of the weights given just above in fitnessMax definition #deap basic funtion to cross a population toolbox.register("evaluate", evaluate, model=model, translator=translator, weights=weights, **kwargs) toolbox.register("mate", decorator_cross(hybrid_cx), discrete_cx=discrete_cx, discrete_cx_kwargs=discrete_cx_kwargs, continuous_cx=continuous_cx, continuous_cx_kwargs=continuous_cx_kwargs) toolbox.register("mutate", decorator_mut(hybrid_mut), discrete_mut=discrete_mut, discrete_mut_kwargs=discrete_mut_kwargs, continuous_mut=continuous_mut, continuous_mut_kwargs=continuous_mut_kwargs) toolbox.register("select", decorator_selection(selection_op), **selection_kwargs) #we are now creating the population population = toolbox.population(n=nb_indiv) pops = [] init_integer = len(pops) if nb_threads > 1: pool = multiprocessing.Pool(processes=nb_threads) toolbox.register("map", pool.map) #We are running the genetical algorithm for gen in range(init_integer, NGEN): print('Generation: ' + str(gen + 1)) #creating ids if needed for l, ind in enumerate(population): ind.age += 1 if ind.mutated != None or ind.parents != None or gen == 0: ind.id = str(gen + 1) + "." + str(l) #Evaluation of the individuals fits = toolbox.map(toolbox.evaluate, population) for fit, ind in zip(fits, population): ind.fitness.values = fit for ind in population: if translator.is_dna_viable(ind): phen = translator.dna_to_phen(ind) ind.fitness.constraints = [ const(phen, ind.fitness.values) for const in constraints ] else: ind.fitness.constraints = [False for _ in constraints] # Printing the best individual top1 = tools.selBest(population, k=1) print(translator.dna_to_phen(top1[0])) print(top1[0].fitness.values) #Registering the information in the pickle file pops.append([{ "phen": translator.dna_to_phen(indiv), "fits": indiv.fitness.values, "age": indiv.age, "id": indiv.id, "parents": indiv.parents, "mutated": indiv.mutated, "constraints": ind.fitness.constraints } for indiv in population if translator.is_dna_viable(indiv)]) #Selection of the individuals population = toolbox.select( [indiv for indiv in population if translator.is_dna_viable(indiv)], k=len(population)) population = algorithms.varAnd(population, toolbox, cxpb=cxpb, mutpb=mutpb) if log_file: with open(log_file, 'wb') as pickle_file: pickle.dump(hparams, pickle_file, protocol=pickle.HIGHEST_PROTOCOL) pickle.dump(pops, pickle_file, protocol=pickle.HIGHEST_PROTOCOL) return pops
def run_hybrid_ga(model, weights, hparams, NGEN=40, nb_indiv=80, nb_threads=1, cxpb=0.7, mutpb=0.4, discrete_cx=tools.cxTwoPoint, discrete_cx_kwargs={}, continuous_cx=tools.cxBlend, continuous_cx_kwargs={"alpha": 0.1}, discrete_mut=tools.mutFlipBit, discrete_mut_kwargs={"indpb": 0.05}, continuous_mut=tools.mutGaussian, continuous_mut_kwargs={ "mu": 0., "sigma": 0.1, "indpb": 0.05 }, selection_op=tools.selTournament, selection_kwargs={"tournsize": 3}, stric_interval=False, log_file=None, recover_file=None, gray_code=True, **kwargs): if recover_file: with open(recover_file, 'rb') as pickle_file: hparams = pickle.load(pickle_file) translator = HybridDNA(hparams, stric_interval=stric_interval, gray_code=gray_code) pops = pickle.load(pickle_file) #Creation of class FitnessMax and individual creation_deap_classes(creator, weights) #toolbox object from deap: allows to define functions in one line for operations on the individual toolbox = base.Toolbox() #creation of operators creation_tools(toolbox, model, translator, creator, weights, discrete_cx, discrete_cx_kwargs, continuous_cx, continuous_cx_kwargs, discrete_mut, discrete_mut_kwargs, continuous_mut, continuous_mut_kwargs, selection_op, selection_kwargs, nb_threads, **kwargs) population = recover_last_gen(pops, creator, translator) #Performing selection and cross and mutation to create the new generation population = toolbox.select([ indiv for indiv in population if translator.is_dna_viable(indiv) ], k=nb_indiv) population = algorithms.varAnd(population, toolbox, cxpb=cxpb, mutpb=mutpb) else: translator = HybridDNA(hparams, stric_interval=stric_interval, gray_code=gray_code) #Creation of class FitnessMax and individual creation_deap_classes(creator, weights) #toolbox object from deap: allows to define functions in one line for operations on the individual toolbox = base.Toolbox() #creation of operators creation_tools(toolbox, model, translator, creator, weights, discrete_cx, discrete_cx_kwargs, continuous_cx, continuous_cx_kwargs, discrete_mut, discrete_mut_kwargs, continuous_mut, continuous_mut_kwargs, selection_op, selection_kwargs, nb_threads, **kwargs) #we are now creating the population population = toolbox.population(n=nb_indiv) pops = [] init_integer = len(pops) #We are running the genetical algorithm for gen in range(init_integer, NGEN): print('Generation: ' + str(gen + 1)) #creating ids if needed for l, ind in enumerate(population): ind.age += 1 if ind.mutated != None or ind.parents != None or gen == 0: ind.id = str(gen + 1) + "." + str(l) #Evaluation of the individuals fits = toolbox.map(toolbox.evaluate, population) for fit, ind in zip(fits, population): ind.fitness.values = fit # Printing the best individual top1 = tools.selBest(population, k=1) print(translator.dna_to_phen(top1[0])) print(top1[0].fitness.values) #Registering the information in the pickle file pops.append([{ "phen": translator.dna_to_phen(indiv), "fits": indiv.fitness.values, "age": indiv.age, "id": indiv.id, "parents": indiv.parents, "mutated": indiv.mutated } for indiv in population if translator.is_dna_viable(indiv)]) #Selection of the individuals population = toolbox.select( [indiv for indiv in population if translator.is_dna_viable(indiv)], k=len(population)) population = algorithms.varAnd(population, toolbox, cxpb=cxpb, mutpb=mutpb) if log_file: with open(log_file, 'wb') as pickle_file: pickle.dump(hparams, pickle_file, protocol=pickle.HIGHEST_PROTOCOL) pickle.dump(pops, pickle_file, protocol=pickle.HIGHEST_PROTOCOL) return pops