def sel_nsga_iii(individuals, k): '''Implements NSGA-III selection as described in Deb, K., & Jain, H. (2014). An Evolutionary Many-Objective Optimization Algorithm Using Reference-Point-Based Nondominated Sorting Approach, Part I: Solving Problems With Box Constraints. IEEE Transactions on Evolutionary Computation, 18(4), 577–601. doi:10.1109/TEVC.2013.2281535. ''' assert len(individuals) >= k if len(individuals)==k: return individuals # Algorithm 1 steps 4--8 fronts = tools.sortLogNondominated(individuals, len(individuals)) limit = 0 res =[] for f, front in enumerate(fronts): res += front if len(res) > k: limit = f break # Algorithm 1 steps selection = [] if limit > 0: for f in range(limit): selection += fronts[f] # complete selected inividuals using the referece point based approach selection += niching_select(fronts[limit], k - len(selection)) return selection
def generate(self, ind_init): """Generate a population of :math:`\lambda` individuals of type *ind_init* from the current strategy. :param ind_init: A function object that is able to initialize an individual from a list. :returns: A list of individuals with a private attribute :attr:`_ps`. This last attribute is essential to the update function, it indicates that the individual is an offspring and the index of its parent. """ arz = numpy.random.randn(self.lambda_, self.dim) individuals = list() # Make sure every parent has a parent tag and index for i, p in enumerate(self.parents): p._ps = "p", i # Each parent produce an offspring if self.lambda_ == self.mu: for i in range(self.lambda_): # print "Z", list(arz[i]) individuals.append(ind_init(self.parents[i] + self.sigmas[i] * numpy.dot(self.A[i], arz[i]))) individuals[-1]._ps = "o", i # Parents producing an offspring are chosen at random from the first front else: ndom = tools.sortLogNondominated(self.parents, len(self.parents), first_front_only=True) for i in range(self.lambda_): j = numpy.random.randint(0, len(ndom)) _, p_idx = ndom[j]._ps individuals.append(ind_init(self.parents[p_idx] + self.sigmas[p_idx] * numpy.dot(self.A[p_idx], arz[i]))) individuals[-1]._ps = "o", p_idx return individuals
def nsgaii(model, mu, ngen, cxpb, mutpb): assert mu % 4 == 0, "Error: mu is not divisible by 4" logging.debug("Working on model " + model.name + " @ NSGAII.py::nsgaii") toolbox = base.Toolbox() toolbox.register('mate', tools.cxOnePoint) toolbox.register('mutate', tools.mutPolynomialBounded, low=0, up=1.0, eta=20.0, indpb=1.0 / model.decNum) toolbox.register('select', tools.selNSGA2) pop_df = model.init_random_pop(mu) model.eval_pd_df(pop_df, normalized=True) pop = model.pd_to_deap(pop_df) pop = toolbox.select(pop, len(pop)) for gen in range(1, ngen): # _show_pop(sorted(pop, key=lambda i: i[0])) logging.debug("Running at gen " + str(gen)) offspring = tools.selTournamentDCD(pop, len(pop)) offspring = [toolbox.clone(ind) for ind in offspring] t_offspring = list() for ind1, ind2 in zip(offspring[::2], offspring[1::2]): if random.random() <= cxpb: toolbox.mate(ind1, ind2) del ind1.fitness.values del ind2.fitness.values if random.random() <= mutpb: toolbox.mutate(ind1) toolbox.mutate(ind2) del ind1.fitness.values del ind2.fitness.values # saving new configurations if not ind1.fitness.valid: t_offspring.append(ind1) if not ind1.fitness.valid: t_offspring.append(ind2) # eval offspring_df = model.deap_to_pd(t_offspring) model.eval_pd_df(offspring_df, normalized=True) offspring = model.pd_to_deap(offspring_df) pop = toolbox.select(pop + offspring, mu) toolbox.unregister('mate') toolbox.unregister('mutate') toolbox.unregister('select') res = sortLogNondominated(pop, k=10, first_front_only=True) # k value is non-sense return model.deap_to_pd(res)
def associate(individuals, reference_points): '''Associates individuals to reference points and calculates niche number. Corresponds to Algorithm 3 of Deb & Jain (2014).''' pareto_fronts = tools.sortLogNondominated(individuals, len(individuals)) num_objs = len(individuals[0].fitness.values) for ind in individuals: rp_dists = [(rp, perpendicular_distance(ind.fitness.normalized_values, rp)) for rp in reference_points] best_rp, best_dist = sorted(rp_dists, key=lambda rpd:rpd[1])[0] ind.reference_point = best_rp ind.ref_point_distance = best_dist best_rp.associations_count +=1 # update de niche number best_rp.associations += [ind]
def plot_pareto_fronts(pop, axes, nfronts=5, showall=True): colors = seaborn.color_palette('Set1', n_colors=nfronts) fronts = tools.sortLogNondominated(pop, k=len(pop)) fitnesses = np.array([ind.fitness.values for ind in pop]) axes.scatter(fitnesses[:, 0], fitnesses[:, 1], color='black', sizes=[2], alpha=.5) for i, (color, front) in reversed(list(enumerate(zip(colors, fronts)))): fitnesses = np.array([ind.fitness.values for ind in front]) #axes.scatter(fitnesses[:,0], fitnesses[:,1], color=color, sizes=[6], label=f'Frente {i+1}') axes.plot(fitnesses[:, 0], fitnesses[:, 1], marker='o', ms=2, color=color, label=f'Frente {i+1}')
def _select(self, candidates): if len(candidates) <= self.mu: return candidates, [] pareto_fronts = tools.sortLogNondominated(candidates, len(candidates)) chosen = list() mid_front = None not_chosen = list() # Fill the next population (chosen) with the fronts until there is not enouch space # When an entire front does not fit in the space left we rely on the hypervolume # for this front # The remaining fronts are explicitely not chosen full = False for front in pareto_fronts: if len(chosen) + len(front) <= self.mu and not full: chosen += front elif mid_front is None and len(chosen) < self.mu: mid_front = front # With this front, we selected enough individuals full = True else: not_chosen += front # Separate the mid front to accept only k individuals k = self.mu - len(chosen) if k > 0: # reference point is chosen in the complete population # as the worst in each dimension +1 ref = np.array([ind.fitness.wvalues for ind in candidates]) * -1 ref = np.max(ref, axis=0) + 1 for _ in range(len(mid_front) - k): idx = self.indicator(mid_front, ref=ref) not_chosen.append(mid_front.pop(idx)) chosen += mid_front return chosen, not_chosen
def nsga_evolutionary_run(**kwargs): """ Conduct an evolutionary run using the snake and muscle model. Args: gens: generations of evolution pop_size: population size mut_prob: mutation probability """ # Establish name of the output files and write appropriate headers. out_fit_file = args.output_path + str(args.run_num) + "_fitnesses.dat" out_fronts_file = args.output_path + str(args.run_num) + "_fronts.dat" writeHeaders(out_fit_file) creator.create( "FitnessMulti", base.Fitness, weights=(1.0, -1.0, 1.0, 1.0) ) # Maximize distance, minimize touches, maximize time upright, maximize efficiency creator.create("Individual", kwargs['exp_class'], fitness=creator.FitnessMulti) # Create the toolbox for setting up DEAP functionality. toolbox = base.Toolbox() # Define an individual for use in constructing the population. toolbox.register("individual", hopper_utils.initIndividual, creator.Individual) toolbox.register("mutate", hopper_utils.mutate) toolbox.register("crossover", hopper_utils.crossover) # Create a population as a list. toolbox.register("population", tools.initRepeat, list, toolbox.individual) # Register the evaluation function. toolbox.register("evaluate", evaluate_individual) # Register the selection function. toolbox.register("select", tools.selNSGA2) # Multiprocessing component. cores = mpc.cpu_count() pool = mpc.Pool(processes=cores - 2) toolbox.register("map", pool.map) # Setup the population. pop = toolbox.population(n=kwargs['pop_size']) # Run the first generation to establish what the initial population does. # Evaluate the individuals with an invalid fitness invalid_ind = [ind for ind in pop if not ind.fitness.valid] fitnesses = toolbox.map(toolbox.evaluate, invalid_ind) for ind, fit in zip(invalid_ind, fitnesses): ind.fitness.values = fit # Check to see if a timeout error occurred, if so kill and restart children. # timeout = False # for f in fitnesses: # if f[0] == -1 and f[1] == 20000: # timeout = True # if timeout: # print(mpc.active_children()) # pool.terminate() # print(mpc.active_children()) # pool = mpc.Pool(processes=cores-2) # toolbox.register("map", pool.map) # This is just to assign the crowding distance to the individuals # no actual selection is done pop = toolbox.select(pop, len(pop)) # Log the progress of the population. (For Generation 0) writeGeneration(out_fit_file, 0, pop) # Track the progress of NSGA fronts = [] #fronts.append(','.join(str(i) for i in ind.fitness.values) for ind in pop) fronts.append( str(ind.id) + ',' + ','.join(str(i) for i in ind.fitness.values) for ind in tools.sortLogNondominated( pop, args.pop_size, first_front_only=True)) # Request new id's for the population. for ind in pop: ind.get_new_id() for gen in range(1, args.gens): # Variate the population offspring = tools.selTournamentDCD(pop, len(pop)) offspring = [toolbox.clone(ind) for ind in offspring] # Update the fronts information. #fronts.append(','.join(str(i) for i in ind.fitness.values) for ind in pop) fronts.append( str(ind.id) + ',' + ','.join(str(i) for i in ind.fitness.values) for ind in tools.sortLogNondominated( pop, args.pop_size, first_front_only=True)) # Mutate the population. for ind in offspring: hopper_utils.mutate(ind) del ind.fitness.values # Evaluate the individuals with an invalid fitness invalid_ind = [ind for ind in offspring if not ind.fitness.valid] fitnesses = toolbox.map(toolbox.evaluate, invalid_ind) for ind, fit in zip(invalid_ind, fitnesses): ind.fitness.values = fit # Select the next generation population pop = toolbox.select(pop + offspring, kwargs['pop_size']) # Request new id's for the population. for ind in pop: ind.get_new_id() print("Generation " + str(gen)) # Log the progress of the population. writeGeneration(out_fit_file, gen, pop) # Write out the fronts data. writeFronts(out_fronts_file, fronts)
from nsga2 import * from deap import tools ind1 = toolbox.individual() ind2 = toolbox.individual() ind1.fitness.values = toolbox.evaluate(ind1) ind2.fitness.values = toolbox.evaluate(ind2) import pdb pdb.set_trace() print(tools.sortLogNondominated([ind1, ind2], k=2))
def nsga_evolution_run(**kwargs): """ Base function for the main file to call. Sets up the evolutionary environment and then passes off to the type of evolutionary run that we want to conduct. Arguments: evol_type: type of evolutionary run ['norm_ga','lexicase'] output_path: where to write the files to run_num: run number of the replicate pop_size: population size """ # Seed only the evolutionary runs. random.seed(args.run_num) # Establish name of the output files and write appropriate headers. out_fit_file = kwargs['output_path'] + str( kwargs['run_num']) + "_fitnesses.dat" out_fronts_file = kwargs['output_path'] + str( kwargs['run_num']) + "_fronts.dat" writeHeaders(out_fit_file, kwargs['exp_class']) #creator.create("Fitness", base.Fitness, weights=(1.0,-1.0,1.0,1.0,-1.0,)) creator.create("Fitness", base.Fitness, weights=( 1.0, 1.0, -1.0, )) creator.create("Individual", kwargs['exp_class'], fitness=creator.Fitness) # Create the toolbox for setting up DEAP functionality. toolbox = base.Toolbox() # Define an individual for use in constructing the population. toolbox.register("individual", flex_quadruped_utils.initIndividual, creator.Individual) toolbox.register("mutate", flex_quadruped_utils.mutate) toolbox.register("mate", tools.cxTwoPoint) # Create a population as a list. toolbox.register("population", tools.initRepeat, list, toolbox.individual) # Register the evaluation function. toolbox.register("evaluate", evaluate_individual) toolbox.register("select", tools.selNSGA2) # Multiprocessing component. cores = mpc.cpu_count() pool = mpc.Pool(processes=cores - 2) toolbox.register("map", pool.map) # Crossover and mutation probability cxpb, mutpb = 0.5, 0.04 # Set the mutation value for quadruped utils flex_quadruped_utils.mutate_chance = mutpb # Setup the population. pop = toolbox.population(n=kwargs['pop_size']) # Run the first set of evaluations. invalid_ind = [ind for ind in pop if not ind.fitness.valid] fitnesses = toolbox.map(toolbox.evaluate, invalid_ind) for ind, fit in zip(invalid_ind, fitnesses): ind.fitness.values = fit # This is just to assign the crowding distance to the individuals # no actual selection is done pop = toolbox.select(pop, len(pop)) # Log the progress of the population. (For Generation 0) writeGeneration(out_fit_file, 0, pop) # Track the progress of NSGA fronts = [] #fronts.append(','.join(str(i) for i in ind.fitness.values) for ind in pop) fronts.append( str(ind.id) + ',' + ','.join(str(i) for i in ind.fitness.values) for ind in tools.sortLogNondominated( pop, args.pop_size, first_front_only=True)) # Request new id's for the population. for ind in pop: ind.get_new_id() for g in range(1, args.gens): # Variate the population offspring = tools.selTournamentDCD(pop, len(pop)) offspring = [toolbox.clone(ind) for ind in offspring] # Update the fronts information. #fronts.append(','.join(str(i) for i in ind.fitness.values) for ind in pop) fronts.append( str(ind.id) + ',' + ','.join(str(i) for i in ind.fitness.values) for ind in tools.sortLogNondominated( pop, args.pop_size, first_front_only=True)) for child1, child2 in zip(offspring[::2], offspring[1::2]): if random.random() < cxpb: # Must serialize and deserialize due to the type of object. child1_serialized, child2_serialized = toolbox.mate( child1.serialize(), child2.serialize()) child1.deserialize(child1_serialized) child2.deserialize(child2_serialized) del child1.fitness.values, child2.fitness.values # Mutate the population. for mutant in offspring: toolbox.mutate(mutant) del mutant.fitness.values # Evaluate the individuals with an invalid fitness invalid_ind = [ind for ind in offspring if not ind.fitness.valid] fitnesses = toolbox.map(toolbox.evaluate, invalid_ind) for ind, fit in zip(invalid_ind, fitnesses): ind.fitness.values = fit # Select the next generation population pop = toolbox.select(pop + offspring, kwargs['pop_size']) # Request new id's for the population. for ind in pop: ind.get_new_id() print("Generation " + str(g)) # Log the progress of the population. writeGeneration(out_fit_file, g, pop) # Write out the fronts data. writeFronts(out_fronts_file, fronts)