def _do(self, problem, pop, parents, **kwargs): # get the X of parents and count the matings X = pop.get("X")[parents.T] _, n_matings, n_var = X.shape # the mask do to the crossover M = np.full((n_matings, n_var), False) # start point of crossover n = random.randint(0, n_var, size=len(pop)) # the probabilities are calculated beforehand r = random.random((n_matings, n_var)) < self.prob # create for each individual the crossover range for i in range(n_matings): # the actual index where we start start = n[i] for j in range(problem.n_var): # the current position where we are pointing to current = (start + j) % problem.n_var # replace only if random value keeps being smaller than CR if r[i, current]: M[i, current] = True else: break _X = crossover_mask(X, M) return pop.new("X", _X)
def comp_by_dom_and_crowding(pop, P, crowding, **kwargs): if P.shape[1] != 2: raise ValueError("Only implemented for binary tournament!") S = np.zeros((P.shape[0], 1), dtype=np.int) for i, p in enumerate(P): rel = Dominator.get_relation(pop.F[P[i, 0], :], pop.F[P[i, 1], :]) # first by domination if rel == 1: S[i, 0] = P[i, 0] elif rel == -1: S[i, 0] = P[i, 1] # then by crowding else: if crowding[P[i, 0]] > crowding[P[i, 1]]: S[i, 0] = P[i, 0] elif crowding[P[i, 1]] > crowding[P[i, 0]]: S[i, 0] = P[i, 1] else: S[i, 0] = P[i, random.randint(0, 2)] return S
def _next(self, pop): # iterate for each member of the population in random order for i in random.perm(len(pop)): # all neighbors of this individual and corresponding weights N = self.neighbors[i, :] if random.random() < self.prob_neighbor_mating: parents = N[random.perm(self.n_neighbors)][:self.crossover.n_parents] else: parents = random.perm(self.pop_size)[:self.crossover.n_parents] # do recombination and create an offspring off = self.crossover.do(self.problem, pop, parents[None, :]) off = self.mutation.do(self.problem, off) off = off[random.randint(0, len(off))] # evaluate the offspring self.evaluator.eval(self.problem, off) # update the ideal point self.ideal_point = np.min(np.vstack([self.ideal_point, off.F]), axis=0) # calculate the decomposed values for each neighbor FV = self._decomposition.do(pop[N].get("F"), weights=self.ref_dirs[N, :], ideal_point=self.ideal_point) off_FV = self._decomposition.do(off.F[None, :], weights=self.ref_dirs[N, :], ideal_point=self.ideal_point) # get the absolute index in F where offspring is better than the current F (decomposed space) I = np.where(off_FV < FV)[0] pop[N[I]] = off return pop
def comp_by_rank_and_crowding(pop, P, **kwargs): if P.shape[1] != 2: raise ValueError("Only implemented for binary tournament!") rank = kwargs['data']['rank'] crowding = kwargs['data']['crowding'] # the winner of the tournament selection S = np.zeros((P.shape[0], 1), dtype=np.int) for i, p in enumerate(P): # first by rank if rank[P[i, 0]] < rank[P[i, 1]]: S[i, 0] = P[i, 0] elif rank[P[i, 1]] < rank[P[i, 0]]: S[i, 0] = P[i, 1] # then by crowding else: if crowding[P[i, 0]] > crowding[P[i, 1]]: S[i, 0] = P[i, 0] elif crowding[P[i, 1]] > crowding[P[i, 0]]: S[i, 0] = P[i, 1] else: S[i, 0] = P[i, random.randint(0, 2)] return S
def _do(self, p, parents, children, **kwargs): n_var = parents.shape[2] n_offsprings = parents.shape[0] # do the crossover if self.type == "binomial": # uniformly for each individual and each entry r = random.random(size=(n_offsprings, n_var)) < self.prob elif self.type == "exponential": r = np.full((n_offsprings, n_var), False) # start point of crossover n = random.randint(0, n_var, size=n_var) # length of chromosome to do the crossover L = np.argmax((random.random(n_offsprings) > self.prob), axis=1) # create for each individual the crossover range for i in range(n_offsprings): for l in range(L[i] + 1): r[i, (n[i] + l) % n_var] = True else: raise Exception( "Unknown crossover type. Either binomial or exponential.") # the so called donor vector children[:, :] = parents[:, 0] if self.variant == "DE/rand/1": trial = parents[:, 3] + self.weight * (parents[:, 1] - parents[:, 2]) else: raise Exception("DE variant %s not known." % self.variant) # set only if larger than F_CR children[r] = trial[r] # bounce back into bounds if self.bounce_back_in_bounds: smaller_than_min, larger_than_max = p.xl > children, p.xu < children children[smaller_than_min] = (p.xl + (p.xl - children))[smaller_than_min] children[larger_than_max] = (p.xu - (children - p.xu))[larger_than_max]
def sample(self, problem, pop, n_samples, **kwargs): # loop until n_samples are met m, counter = problem.n_var, 0 val = np.full((n_samples, m), np.nan) while True: for i in range(m): val[counter, i] = random.randint(low=problem.xl[i], high=problem.xu[i] + 1) if problem.is_valid(val[counter, :]): counter += 1 if counter >= n_samples: break return pop.new("X", val)
def lf_minimize(problem, method, method_args={}, termination=('n_gen', 200), **kwargs): """ Minimization of function of one or more variables, objectives and constraints. This is used as a convenience function to execute several algorithms with default settings which turned out to work for a test problems. However, evolutionary computations utilizes the idea of customizing a meta-algorithm. Customizing the algorithm using the object oriented interface is recommended to improve the convergence. Parameters ---------- problem : pymop.problem A problem object defined using the pymop framework. Either existing test problems or custom problems can be provided. please have a look at the documentation. method : string Algorithm that is used to solve the problem. method_args : dict Additional arguments to initialize the algorithm object termination : tuple The termination criterium that is used to stop the algorithm when the result is satisfying. Returns ------- res : Result The optimization result represented as a ``Result`` object. """ # create an evaluator defined by the termination criterium if not isinstance(termination, Termination): termination = get_termination(*termination, pf=kwargs.get('pf', None)) # set a random random seed if not provided if 'seed' not in kwargs: kwargs['seed'] = random.randint(1, 10000) algorithm = lf_get_alorithm(method)(**method_args) res = algorithm.solve(problem, termination, **kwargs) return res
def comp_by_rank_and_crowding(pop, indices, data): if len(indices) != 2: raise ValueError("Only implemented for binary tournament!") first = indices[0] second = indices[1] if data.rank[first] < data.rank[second]: return first elif data.rank[second] < data.rank[first]: return second else: if data.crowding[first] > data.crowding[second]: return first elif data.crowding[second] > data.crowding[first]: return second else: return indices[random.randint(0, 2)]
def minimize(problem, method, termination, **kwargs): """ Minimization of function of one or more variables, objectives and constraints. This is used as a convenience function to execute several algorithms with default settings which turned out to work for a test problems. However, evolutionary computations utilizes the idea of customizing a meta-algorithm. Customizing the algorithm using the object oriented interface is recommended to improve the convergence. Parameters ---------- problem : pymop.problem A problem object defined using the pymop framework. Either existing test problems or custom problems can be provided. please have a look at the documentation. method : :class:`~pymoo.model.algorithm.Algorithm` The algorithm object that should be used for the optimization. termination : tuple The termination criterium that is used to stop the algorithm when the result is satisfying. Returns ------- res : :class:`~pymoo.model.result.Result` The optimization result represented as an object. """ # create an evaluator defined by the termination criterium if not isinstance(termination, Termination): termination = get_termination(*termination) # set a random random seed if not provided if 'seed' not in kwargs: kwargs['seed'] = random.randint(1, 10000) res = method.solve(problem, termination, **kwargs) return res
def _do(self, problem, pop, algorithm, **kwargs): X = pop.get("X") off = algorithm.off _X = off.get("X") # do the crossover if self.variant == "bin": # uniformly for each individual and each entry r = random.random(size=(len(off), problem.n_var)) < self.CR elif self.variant == "exp": # start point of crossover r = np.full((len(off), problem.n_var), False) # start point of crossover n = random.randint(0, problem.n_var, size=len(off)) # length of chromosome to do the crossover L = random.random((len(off), problem.n_var)) < self.CR # create for each individual the crossover range for i in range(len(off)): # the actual index where we start start = n[i] for j in range(problem.n_var): # the current position where we are pointing to current = (start + j) % problem.n_var # replace only if random value keeps being smaller than CR if L[i, current]: r[i, current] = True else: break else: raise Exception( "Unknown crossover type. Either binomial or exponential.") X[r] = _X[r] return pop.new("X", X)
def comp_by_dom_and_crowding(pop, indices, data): if len(indices) != 2: raise ValueError("Only implemented for binary tournament!") first = indices[0] second = indices[1] rel = Dominator.get_relation(pop.F[first, :], pop.F[second, :]) if rel == 1: return first elif rel == -1: return second else: if data.crowding[first] > data.crowding[second]: return first elif data.crowding[second] > data.crowding[first]: return second else: return indices[random.randint(0, 2)]
def _quicksort(A, I, left, right): if left < right: index = random.randint(left, right + 1) swap(I, right, index) pivot = A[I[right]] i = left - 1 for j in range(left, right): if A[I[j]] <= pivot: i += 1 swap(I, i, j) index = i + 1 swap(I, right, index) _quicksort(A, I, left, index - 1) _quicksort(A, I, index + 1, right)
def niching(F, n_remaining, niche_count, niche_of_individuals, dist_to_niche): survivors = [] # boolean array of elements that are considered for each iteration mask = np.full(F.shape[0], True) while len(survivors) < n_remaining: # all niches where new individuals can be assigned to next_niches_list = np.unique(niche_of_individuals[mask]) # pick a niche with minimum assigned individuals - break tie if necessary next_niche_count = niche_count[next_niches_list] next_niche = np.where(next_niche_count == next_niche_count.min())[0] next_niche = next_niches_list[next_niche] next_niche = next_niche[random.randint(0, len(next_niche))] # indices of individuals that are considered and assign to next_niche next_ind = np.where( np.logical_and(niche_of_individuals == next_niche, mask))[0] # shuffle to break random tie (equal perp. dist) or select randomly next_ind = random.shuffle(next_ind) if niche_count[next_niche] == 0: next_ind = next_ind[np.argmin(dist_to_niche[next_ind])] else: # already randomized through shuffling next_ind = next_ind[0] mask[next_ind] = False survivors.append(int(next_ind)) niche_count[next_niche] += 1 return survivors
def _do(self, pop, n_survive, **kwargs): # get the objective space values and objects F = pop.get("F") # the final indices of surviving individuals survivors = [] # do the non-dominated sorting until splitting front fronts = NonDominatedSorting().do(F) if self.normalization == "ever": # find or usually update the new ideal point - from feasible solutions self.ideal_point = np.min(np.vstack((self.ideal_point, F)), axis=0) self.nadir_point = np.max(np.vstack((self.nadir_point, F)), axis=0) elif self.normalization == "front": front = fronts[0] if len(front) > 1: self.ideal_point = np.min(F[front], axis=0) self.nadir_point = np.max(F[front], axis=0) elif self.normalization == "no": self.ideal_point = np.zeros(self.n_obj) self.nadir_point = np.ones(self.n_obj) if self.extreme_points_as_reference_points: self.ref_points = np.row_stack( [self.ref_points, get_extreme_points_c(F, self.ideal_point)]) # calculate the distance matrix from ever solution to all reference point dist_to_ref_points = calc_norm_pref_distance(F, self.ref_points, self.weights, self.ideal_point, self.nadir_point) for k, front in enumerate(fronts): # save rank attributes to the individuals - rank = front here pop[front].set("rank", np.full(len(front), k)) # number of individuals remaining n_remaining = n_survive - len(survivors) # the ranking of each point regarding each reference point (two times argsort is necessary) rank_by_distance = np.argsort(np.argsort(dist_to_ref_points[front], axis=0), axis=0) # the reference point where the best ranking is coming from ref_point_of_best_rank = np.argmin(rank_by_distance, axis=1) # the actual ranking which is used as crowding ranking = rank_by_distance[np.arange(len(front)), ref_point_of_best_rank] if len(front) <= n_remaining: # we can simply copy the crowding to ranking. not epsilon selection here crowding = ranking I = np.arange(len(front)) else: # Distance from solution to every other solution and set distance to itself to infinity dist_to_others = calc_norm_pref_distance( F[front], F[front], self.weights, self.ideal_point, self.nadir_point) np.fill_diagonal(dist_to_others, np.inf) # the crowding that will be used for selection crowding = np.full(len(front), np.nan) # solutions which are not already selected - for not_selected = np.argsort(ranking) # until we have saved a crowding for each solution while len(not_selected) > 0: # randomly select an alive individual if self.survival_type == "random": idx = not_selected[random.randint( 0, len(not_selected))] elif self.survival_type == "closest": idx = not_selected[0] else: raise Exception("Unknown survival type.") # set crowding for that individual crowding[idx] = ranking[idx] # need to remove myself from not-selected array to_remove = [idx] # Group of close solutions dist = dist_to_others[idx][not_selected] group = not_selected[np.where(dist < self.epsilon)[0]] # if there exists solution with a distance less than epsilon if len(group): # discourage them by giving them a high crowding crowding[group] = ranking[group] + np.round( len(front) / 2) # remove group from not_selected array to_remove.extend(group) not_selected = np.array( [i for i in not_selected if i not in to_remove]) # now sort by the crowding (actually modified rank) ascending and let the best survive I = np.argsort(crowding)[:n_remaining] # set the crowding to all individuals pop[front].set("crowding", crowding) # extend the survivors by all or selected individuals survivors.extend(front[I]) # inverse of crowding because nsga2 does maximize it (then tournament selection can stay the same) pop.set("crowding", -pop.get("crowding")) return pop[survivors]
def sample(self, problem, n_samples, **kwargs): return random.randint(0, 2, size=(n_samples, problem.n_var))
def _do(self, pop, n_survive, data, return_only_index=False): fronts = NonDominatedRank.calc_as_fronts(pop.F, pop.G) # all indices to survive survival = [] for front in fronts: if len(survival) + len(front) > n_survive: break survival.extend(front) # filter the front to only relevant entries pop.filter(survival + front) survival = list(range(0, len(survival))) last_front = np.arange(len(survival), pop.size()) N = normalize_by_asf_interceptions(pop.F, return_bounds=False) #N = normalize(pop.F, np.zeros(pop.F.shape[1]), np.ones(pop.F.shape[1])) #N = normalize(pop.F, np.zeros(pop.F.shape[1]), np.ones(pop.F.shape[1])) # if the last front needs to be splitted n_remaining = n_survive - len(survival) if n_remaining > 0: dist_matrix = calc_perpendicular_dist_matrix(N, self.ref_dirs) niche_of_individuals = np.argmin(dist_matrix, axis=1) min_dist_matrix = dist_matrix[np.arange(len(dist_matrix)), niche_of_individuals] # for each reference direction the niche count niche_count = np.zeros(len(self.ref_dirs)) for i in niche_of_individuals[survival]: niche_count[i] += 1 # relative index now to dist and the niches min_dist_matrix = min_dist_matrix[last_front] niche_of_individuals = niche_of_individuals[last_front] # boolean array of elements that survive if true survival_last_front = np.full(len(last_front), False) while n_remaining > 0: # all niches where new individuals can be assigned to next_niches_list = np.unique( niche_of_individuals[np.logical_not(survival_last_front)]) # pick a niche with minimum assigned individuals - break tie if necessary next_niche_count = niche_count[next_niches_list] next_niche = np.where( next_niche_count == next_niche_count.min())[0] next_niche = next_niche[random.randint(0, len(next_niche))] next_niche = next_niches_list[next_niche] # indices of individuals in last front to assign niche to next_ind = np.where(niche_of_individuals[np.logical_not( survival_last_front)] == next_niche)[0] next_ind = np.where( np.logical_not(survival_last_front))[0][next_ind] if len(next_ind) == 1: next_ind = next_ind[0] elif niche_count[next_niche] == 0: next_ind = next_ind[np.argmin(min_dist_matrix[next_ind])] else: next_ind = next_ind[random.randint(0, len(next_ind))] survival_last_front[next_ind] = True niche_count[next_niche] += 1 n_remaining -= 1 survival.extend(last_front[survival_last_front]) if return_only_index: return survival # now truncate the population pop.filter(survival) return pop
def _do(self, pop, off, n_survive, return_only_index=False, **kwargs): data = kwargs['data'] fronts = NonDominatedRank.calc_as_fronts(pop.F, pop.G) # all indices to survive survival = [] for front in fronts: if len(survival) + len(front) > n_survive: break survival.extend(front) # filter the front to only relevant entries pop.filter(survival + front) survival = list(range(0, len(survival))) last_front = np.arange(len(survival), pop.size()) N, self.asf, self.extreme, F_min, F_max = normalize_by_asf_interceptions( np.vstack((pop.F, data.ref_points)), len(fronts[0]), prev_asf=self.asf, prev_S=self.extreme, return_bounds=True) z_ = (data.ref_points - F_min) / (F_max - F_min ) # Normalized reference points data.F_min, data.F_max = F_min, F_max self.ref_dirs = get_ref_dirs_from_points(z_, self.n_obj, data.ref_pop_size, alpha=data.mu, method=data.method, p=data.p) data.ref_dirs = self.ref_dirs # if the last front needs to be split n_remaining = n_survive - len(survival) if n_remaining > 0: dist_matrix = calc_perpendicular_dist_matrix(N, self.ref_dirs) niche_of_individuals = np.argmin(dist_matrix, axis=1) dist_to_niche = dist_matrix[np.arange(len(dist_matrix)), niche_of_individuals] # for each reference direction the niche count niche_count = np.zeros(len(self.ref_dirs)) for i in niche_of_individuals[survival]: niche_count[i] += 1 # relative index to dist and the niches just of the last front dist_to_niche = dist_to_niche[last_front] niche_of_individuals = niche_of_individuals[last_front] # boolean array of elements that are considered for each iteration remaining_last_front = np.full(len(last_front), True) while n_remaining > 0: # all niches where new individuals can be assigned to next_niches_list = np.unique( niche_of_individuals[remaining_last_front]) # pick a niche with minimum assigned individuals - break tie if necessary next_niche_count = niche_count[next_niches_list] next_niche = np.where( next_niche_count == next_niche_count.min())[0] next_niche = next_niche[random.randint(0, len(next_niche))] next_niche = next_niches_list[next_niche] # indices of individuals that are considered and assign to next_niche next_ind = np.where( np.logical_and(niche_of_individuals == next_niche, remaining_last_front))[0] if len(next_ind) == 1: next_ind = next_ind[0] elif niche_count[next_niche] == 0: next_ind = next_ind[np.argmin(dist_to_niche[next_ind])] else: next_ind = next_ind[random.randint(0, len(next_ind))] remaining_last_front[next_ind] = False survival.append(last_front[next_ind]) niche_count[next_niche] += 1 n_remaining -= 1 if return_only_index: return survival # now truncate the population pop.filter(survival)
def _do(self, pop, off, n_survive, return_only_index=False, **kwargs): fronts = NonDominatedRank.calc_as_fronts(pop.F, pop.G) # all indices to survive survival = [] for front in fronts: if len(survival) + len(front) > n_survive: break survival.extend(front) # filter the front to only relevant entries pop.filter(survival + front) survival = list(range(0, len(survival))) last_front = np.arange(len(survival), pop.size()) # Indices of last front members that survived survived = [] # if the last front needs to be splitted n_remaining = n_survive - len(survival) if n_remaining > 0: F_min, F_max = pop.F.min(axis=0), pop.F.max(axis=0) dist_matrix, self.ref_points = calc_ref_dist_matrix(pop.F, self.orig, weights=data.weights, n_obj=self.n_obj) point_distance_matrix = calc_dist_matrix(pop.F[last_front, :], pop.F[last_front, :], F_min=F_min, F_max=F_max) niche_of_individuals = np.argmin(dist_matrix, axis=1) min_dist_matrix = dist_matrix[np.arange(len(dist_matrix)), niche_of_individuals] # for each reference direction the niche count niche_count = np.zeros(len(self.ref_points)) for i in niche_of_individuals[survival]: niche_count[i] += 1 # relative index now to dist and the niches min_dist_matrix = min_dist_matrix[last_front] niche_of_individuals = niche_of_individuals[last_front] # boolean array of elements that survive if true survival_last_front = np.full(len(last_front), False) while n_remaining > 0: # all niches where new individuals can be assigned to next_niches_list = np.unique(niche_of_individuals[np.logical_not(survival_last_front)]) # pick a niche with minimum assigned individuals - break tie if necessary next_niche_count = niche_count[next_niches_list] next_niche = np.where(next_niche_count == next_niche_count.min())[0] next_niche = next_niche[random.randint(0, len(next_niche))] next_niche = next_niches_list[next_niche] # indices of individuals in last front to assign niche to next_ind = np.where(niche_of_individuals[np.logical_not(survival_last_front)] == next_niche)[0] next_ind = np.where(np.logical_not(survival_last_front))[0][next_ind] # Pick the closest point next_ind = next_ind[np.argmin(min_dist_matrix[next_ind])] # Find surrounding points within trust region surrounding_points = np.where(point_distance_matrix[next_ind] < self.epsilon)[0] # Clear points in trust region survival_last_front[surrounding_points] = True # Add selected point to survived population survived.append(next_ind) if np.all(survival_last_front): survival_last_front = np.full(len(last_front), False) survival_last_front[survived] = True niche_count[next_niche] += 1 n_remaining -= 1 survival.extend(last_front[survived]) if return_only_index: return survival # now truncate the population pop.filter(survival)
def _do(self, problem, pop, parents, **kwargs): # get the X of parents and count the matings X = pop.get("X")[parents.T] _, n_matings, n_var = X.shape print("Number of matings: %s" %n_matings) #print("Number of variables: %s" %n_var) #print("Number of offspring: %s" %self.n_offsprings) children = np.full((self.n_offsprings*n_matings, problem.n_var), np.inf) k=0 while k < n_matings*self.n_offsprings: x1 = [] x2 = [] #not sure about this method for selecting x1 and x2... need to preserve elitism more #while x1==x2: try: x1 = X[0][random.randint(0,n_matings-1)].copy() x1 = x1.tolist() x2 = X[1][random.randint(0,n_matings-1)].copy() x2 = x2.tolist() except: x1 = X[0][0].copy() x1 = x1.tolist() x2 = X[0][0].copy() x2 = x2.tolist() #print("X1:") #print(x1) #print("X2:") #print(x2) og_x1 = x1.copy() og_x2 = x2.copy() cycles = [] cycles_index1 = [] cycles_index2 = [] num_cyc = 0 while len(x1) > 1: cycle = [] cycle_index1 = [] cycle_index2 = [] index=0 cycle.append(x1[index]) index_1 = og_x1.index(x1[index]) cycle_index1.append(index_1) index_2 = og_x2.index(x1[index]) cycle_index2.append(index_2) x1.pop(index) while cycle[0] != x2[index]: temp = x2[index] x2.pop(index) cycle.append(temp) #note: using obj.index may be slow for long lists index = x1.index(temp) index_1 = og_x1.index(temp) cycle_index1.append(index_1) x1.pop(index) index_2 = og_x2.index(temp) cycle_index2.append(index_2) num_cyc = num_cyc + 1 x2.pop(index) cycles_index1.append(cycle_index1) cycles_index2.append(cycle_index2) cycles.append(cycle) if len(x1) == 1: cycles.append([x2[0]]) x1.pop x2.pop index_2=og_x2.index(x2[0]) index_1=og_x1.index(x1[0]) cycles_index2.append([index_2]) cycles_index1.append([index_1]) num_cyc = num_cyc + 1 child1 = np.zeros(len(og_x1)) child2 = np.zeros(len(og_x2)) i=1 #print("Num cyc %s" %num_cyc) while i<num_cyc: child1[cycles_index1[i-1]] = cycles[i-1] child2[cycles_index2[i-1]] = cycles[i-1] child1[cycles_index2[i]] = cycles[i] child2[cycles_index1[i]] = cycles[i] i +=2 if np.mod(num_cyc,2)!=0: child1[cycles_index1[i-1]] = cycles[i-1] child2[cycles_index2[i-1]] = cycles[i-1] children[k] = child1 try: children[k+1] = child2 except: pass k += 2 #The method to fill children[] is fine for now as long as k's limit is even #print("Children") children = children.astype(int) #print(children) return pop.new("X", children)
def solve(self, problem, termination, seed=None, disp=False, callback=None, save_history=False, pf=None, **kwargs): """ Solve a given problem by a given evaluator. The evaluator determines the termination condition and can either have a maximum budget, hypervolume or whatever. The problem can be any problem the algorithm is able to solve. Parameters ---------- problem: class Problem to be solved by the algorithm termination: class object that evaluates and saves the number of evaluations and determines the stopping condition seed: int Random seed for this run. Before the algorithm starts this seed is set. disp : bool If it is true than information during the algorithm execution are displayed callback : func A callback function can be passed that is executed every generation. The parameters for the function are the algorithm itself, the number of evaluations so far and the current population. def callback(algorithm): pass save_history : bool If true, a current snapshot of each generation is saved. pf : np.array The Pareto-front for the given problem. If provided performance metrics are printed during execution. Returns ------- res : dict A dictionary that saves all the results of the algorithm. Also, the history if save_history is true. """ # set the random seed for generator if seed is not None: random.seed(seed) else: seed = random.randint(0, 10000000) random.seed(seed) # the evaluator object which is counting the evaluations self.evaluator = Evaluator() self.problem = problem self.termination = termination self.pf = pf self.disp = disp self.callback = callback self.save_history = save_history # call the algorithm to solve the problem pop = self._solve(problem, termination) # get the optimal result by filtering feasible and non-dominated if self.opt is None: opt = pop.copy() else: opt = self.opt opt = opt[opt.collect(lambda ind: ind.feasible)[:, 0]] # if at least one feasible solution was found if len(opt) > 0: if problem.n_obj > 1: I = NonDominatedSorting().do(opt.get("F"), only_non_dominated_front=True) opt = opt[I] X, F, CV, G = opt.get("X", "F", "CV", "G") else: opt = opt[np.argmin(opt.get("F"))] X, F, CV, G = opt.X, opt.F, opt.CV, opt.G else: opt = None res = Result(opt, opt is None, "") res.algorithm, res.problem, res.pf = self, problem, pf res.pop = pop if opt is not None: res.X, res.F, res.CV, res.G = X, F, CV, G res.history = self.history return res