def do(pop, n_survive): # 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, n_stop_if_ranked=n_survive) for k, front in enumerate(fronts): # calculate the crowding distance of the front crowding_of_front = calc_crowding_distance(F[front, :]) # save rank and crowding in the individual class for j, i in enumerate(front): pop[i].set('rank', k) pop[i].set('crowding', crowding_of_front[j]) # current front sorted by crowding distance if splitting if len(survivors) + len(front) > n_survive: I = randomized_argsort(crowding_of_front, order='descending', method='numpy') I = I[:(n_survive - len(survivors))] # otherwise take the whole front unsorted else: I = np.arange(len(front)) # extend the survivors by all or selected individuals survivors.extend(front[I]) return pop[survivors]
def _do(self, pop, n_survive, D=None, **kwargs): # attributes to be set after the survival F = pop.get("F") # 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.worst_point = np.max(np.vstack((self.worst_point, F)), axis=0) # calculate the fronts of the population fronts, rank = NonDominatedSorting().do(F, return_rank=True, n_stop_if_ranked=n_survive) self.nadir_point = get_nadir_point_from_fronts(F, fronts, self.ideal_point) # consider only the population until we come to the splitting front I = np.concatenate(fronts) pop, rank, F = pop[I], rank[I], F[I] # update the front indices for the current population counter = 0 for i in range(len(fronts)): for j in range(len(fronts[i])): fronts[i][j] = counter counter += 1 last_front = fronts[-1] # associate individuals to niches niche_of_individuals, dist_to_niche = associate_to_niches( F, self.ref_dirs, self.ideal_point, self.nadir_point) pop.set('rank', rank, 'niche', niche_of_individuals, 'dist_to_niche', dist_to_niche) # if we need to select individuals to survive if len(pop) > n_survive: # if there is only one front if len(fronts) == 1: n_remaining = n_survive until_last_front = np.array([], dtype=np.int) niche_count = np.zeros(len(self.ref_dirs), dtype=np.int) # if some individuals already survived else: until_last_front = np.concatenate(fronts[:-1]) niche_count = calc_niche_count( len(self.ref_dirs), niche_of_individuals[until_last_front]) n_remaining = n_survive - len(until_last_front) S = niching(F[last_front, :], n_remaining, niche_count, niche_of_individuals[last_front], dist_to_niche[last_front]) survivors = np.concatenate( (until_last_front, last_front[S].tolist())) pop = pop[survivors] return pop
def eval(self, problem=None, x=None, archive=None, **kwargs): if x.ndim == 1: n = 1 x = np.expand_dims(x, 0) else: n = x.shape[0] f = np.zeros((n, problem.n_obj)) g = np.zeros((n, np.max([problem.n_constr, 1]))) if self.n_eval + x.shape[0] > self.n_max_eval: rest = self.n_max_eval - self.n_eval I = np.random.permutation(x.shape[0]) I = I[:rest] x = x[I] f = f[I] g = g[I] problem._evaluate_high_fidelity(x=x, f=f, g=g) archive['x'] = np.concatenate((x, archive['x']), axis=0) archive['f'] = np.concatenate((f, archive['f']), axis=0) archive['g'] = np.concatenate((g, archive['g']), axis=0) cv = np.copy(archive['g']) index = np.any(archive['g'] > 0, axis=1) cv[archive['g'] <= 0] = 0 cv = np.sum(cv, axis=1) acv = np.sum(archive['g'], axis=1) acv[index] = np.copy(cv[index]) archive['feasible_index'] = cv <= 0 archive['feasible_index'] = np.vstack( np.asarray(archive['feasible_index']).flatten()) archive['cv'] = np.vstack(np.asarray(cv).flatten()) archive['acv'] = np.vstack(np.asarray(acv).flatten()) feasible = archive['f'][archive['feasible_index'][:, 0], :] if feasible.size > 0: nd = NonDominatedSorting() index = nd.do(F=feasible, only_non_dominated_front=True) archive['non_dominated_front'] = archive['x'][index] else: archive['non_dominated_front'] = np.empty([0, f.size]) self.n_eval = archive["x"].shape[0] return archive
def test_rank_and_crowding_distance(self): for i, D in enumerate(self.data): survivor_and_last_front = np.where(D['rank'] != -1.0)[0] crowding = D['crowding'][survivor_and_last_front] rank = D['rank'][survivor_and_last_front].astype(np.int) F = D['F'][survivor_and_last_front, :] fronts, _rank = NonDominatedSorting().do(F, return_rank=True) _rank += 1 _crowding = np.full(len(F), np.nan) for front in fronts: _crowding[front] = calc_crowding_distance(F[front]) is_equal = np.all(rank == _rank) if not is_equal: index = np.where(rank == _rank) print(index) print(D['rank'][index]) print(D['F'][index]) self.assertTrue(is_equal) is_equal = np.all(np.abs(_crowding - crowding) < 0.001) if not is_equal: index = np.where(np.abs(_crowding - crowding) > 0.001)[0] index = index[np.argsort(rank[index])] # only an error if it is not a duplicate F value for i_not_equal in index: if len( np.where(np.all(F[i_not_equal, :] == F, axis=1))[0]) == 1: print("-" * 30) print("Generation: ", i) print("Is rank equal: ", np.all(rank == _rank)) print(index) print(rank[index]) print(F[index]) print( np.concatenate( [_crowding[:, None], crowding[:, None]], axis=1)[index, :]) print() self.assertTrue(is_equal)
def calc_normalized_constraints(self, G): # update the ideal point for constraints if self.min_constraints is None: self.min_constraints = np.full(G.shape[1], np.inf) self.min_constraints = np.min(np.vstack((self.min_constraints, G)), axis=0) # update the nadir point for constraints non_dominated = NonDominatedSorting().do(G, return_rank=True, only_non_dominated_front=True) if self.max_constraints is None: self.max_constraints = np.full(G.shape[1], np.inf) self.max_constraints = np.min(np.vstack((self.max_constraints, np.max(G[non_dominated, :], axis=0))), axis=0) return normalize(G, self.min_constraints, self.max_constraints)
def _calc(self, F): non_dom = NonDominatedSorting().do(F, only_non_dominated_front=True) _F = F[non_dom, :] if self.normalize: hv = _HyperVolume(np.ones(F.shape[1])) _F = normalize(_F, x_min=np.min(self.pf, axis=0), x_max=np.max(self.pf, axis=0)) else: hv = _HyperVolume(np.max(self.pf, axis=0)) val = hv.compute(_F) return val
def _calc(self, F): # only consider the non-dominated solutions for HV non_dom = NonDominatedSorting().do(F, only_non_dominated_front=True) _F = np.copy(F[non_dom, :]) if self.normalize: # because we normalize now the reference point is (1,...1) ref_point = np.ones(F.shape[1]) hv = _HyperVolume(ref_point) _F = normalize(_F, x_min=self.ideal_point, x_max=self.nadir_point) else: hv = _HyperVolume(self.ref_point) val = hv.compute(_F) return val
def solve(fd: FoodDistribution): problem = SolverProblem(fd) method = nsga2(pop_size=70) t = time.time() print('start') res = minimize(problem, method, termination=('n_gen', 20), seed=2, save_history=True, disp=True) print('end: ', time.time() - t) plt.figure(1) plt.clf() for g, a in enumerate(res.history): a.opt = a.pop.copy() a.opt = a.opt[a.opt.collect(lambda ind: ind.feasible)[:, 0]] I = NonDominatedSorting().do(a.opt.get("F"), only_non_dominated_front=True) a.opt = a.opt[I] X, F, CV, G = a.opt.get("X", "F", "CV", "G") plt.figure(1) plt.scatter(F[:, 0], F[:, 1], c=[[g / len(res.history), 0, 0]], s=5**2) plt.figure(2) plt.clf() plt.scatter(F[:, 0], F[:, 1], s=5**2) plt.xlabel('remaining demand') plt.ylabel('cost') plt.savefig('../g_{}.eps'.format(g), format='eps') plt.figure(1) #plotting.plot(res.F, no_fill=True, show=False) plt.xlabel('remaining demand') plt.ylabel('cost') plt.show() plt.savefig('../scatter.eps', format='eps') return res.X
def _do(self, pop, n_survive, D=None, **kwargs): # attributes to be set after the survival F = pop.get("F") # 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.worst_point = np.max(np.vstack((self.worst_point, F)), axis=0) # calculate the fronts of the population fronts, rank = NonDominatedSorting().do(F, return_rank=True, n_stop_if_ranked=n_survive) non_dominated, last_front = fronts[0], fronts[-1] # find the extreme points for normalization self.extreme_points = get_extreme_points_c( F[non_dominated, :], self.ideal_point, extreme_points=self.extreme_points) # find the intercepts for normalization and do backup if gaussian elimination fails worst_of_population = np.max(F, axis=0) worst_of_front = np.max(F[non_dominated, :], axis=0) self.nadir_point = get_nadir_point(self.extreme_points, self.ideal_point, self.worst_point, worst_of_population, worst_of_front) # consider only the population until we come to the splitting front I = np.concatenate(fronts) pop, rank, F = pop[I], rank[I], F[I] # update the front indices for the current population counter = 0 for i in range(len(fronts)): for j in range(len(fronts[i])): fronts[i][j] = counter counter += 1 # normalize the whole population by the estimations made N = normalize(F, self.ideal_point, self.nadir_point) # the final indices of surviving individuals survivors = [] for k, front in enumerate(fronts): # calculate the crowding distance of the front crowding_of_front = calc_asf_crowding_distance(N[front, :]) # save rank and crowding in the individual class for j, i in enumerate(front): pop[i].set("rank", k) pop[i].set("crowding", crowding_of_front[j]) # current front sorted by crowding distance if splitting if len(survivors) + len(front) > n_survive: I = randomized_argsort(crowding_of_front, order='descending', method='numpy') I = I[:(n_survive - len(survivors))] # otherwise take the whole front unsorted else: I = np.arange(len(front)) # extend the survivors by all or selected individuals survivors.extend(front[I]) return pop[survivors]
def test_run(self): ref_dirs = np.array(self.data['ref_dir']) survival = ReferenceLineSurvival(ref_dirs) D = self.data['hist'][0] pop = Population() pop.X = np.array(D['before_X']) pop.F = np.array(D['before_F']) _, _rank = NonDominatedSorting(epsilon=1e-10).do(pop.F, return_rank=True) rank = np.array(D['before_rank']) if not np.all(_rank + 1 == rank): print("") self.assertTrue(np.all(_rank + 1 == rank)) survival.do(pop, pop.size()) for i, D in enumerate(self.data['hist']): out = {} vars = {'out': out} pop = Population() pop.X = np.array(D['before_X']) pop.F = np.array(D['before_F']) off = Population() off.X = np.array(D['off_X']) off.F = np.array(D['off_F']) pop.merge(off) cand = Population() cand.X = np.array(D['cand_X']) cand.F = np.array(D['cand_F']) Configuration.rand.randint = MagicMock() Configuration.rand.randint.side_effect = D['rnd_niching'] fronts = [] ranks = np.array(D['cand_rank']) for r in np.unique(ranks): fronts.append(np.where(ranks == r)[0].tolist()) NonDominatedSorting.do = MagicMock() NonDominatedSorting.do.return_value = [ np.array(front) for front in fronts ], ranks - 1 cand_copy = cand.copy() # necessary because only candidates are provided if survival.ideal_point is None: survival.ideal_point = np.min(pop.F, axis=0) else: survival.ideal_point = np.min(np.concatenate( [survival.ideal_point[None, :], pop.F], axis=0), axis=0) survival.do(cand, pop.size() / 2, **vars) is_equal = np.all( survival.extreme_points == np.array(D['extreme'])) self.assertTrue(is_equal) is_equal = np.all(survival.ideal_point == np.array(D['ideal'])) self.assertTrue(is_equal) is_equal = np.all( np.abs(survival.intercepts - np.array(D['intercepts'])) < 0.0001) if not is_equal: print(i) print(survival.intercepts, np.array(D['intercepts'])) self.assertTrue(is_equal) niche_of_individuals, dist_to_niche = associate_to_niches( cand_copy.F, ref_dirs, survival.ideal_point, survival.intercepts) for r, v in enumerate(D['ref_dir']): self.assertTrue(np.all(ref_dirs[niche_of_individuals[r]] == v)) is_equal = np.all( np.abs(dist_to_niche - np.array(D['perp_dist'])) < 0.000001) if not is_equal: print(i) self.assertTrue(is_equal) surv_pop = Population() surv_pop.X = np.array(D['X']) surv_pop.F = np.array(D['F']) for k in range(surv_pop.size()): is_equal = np.any(np.all(surv_pop.X[k, :] == cand.X, axis=1)) if not is_equal: print(i) print(k) self.assertTrue(is_equal) for k in range(cand.size()): is_equal = np.any(np.all(cand.F[k, :] == surv_pop.F, axis=1)) self.assertTrue(is_equal)
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) # 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 opt = pop.copy() 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
def _filter_fast(self): filtered_pop = NonDominatedSorting.get_non_dominated( self.whole_pop, self.curr_pop) return filtered_pop
def seeking(P, gamma=210): P_F = P.get('F') idx_front_0 = NonDominatedSorting().do(P_F, n_stop_if_ranked=len(P), only_non_dominated_front=True) PS = P[idx_front_0].copy() PF = P_F[idx_front_0].copy() # Normalize validation error for calculating angle between two individuals PF_norm = PF.copy() mi_f0 = np.min(PF[:, 0]) ma_f0 = np.max(PF[:, 0]) mi_f1 = np.min(PF[:, 1]) ma_f1 = np.max(PF[:, 1]) PF_norm[:, 0] = (PF[:, 0] - mi_f0) / (ma_f0 - mi_f0) PF_norm[:, 1] = (PF[:, 1] - mi_f1) / (ma_f1 - mi_f1) new_idx = np.argsort(PF[:, 0]) PS = PS[new_idx] PF = PF[new_idx] PF_norm = PF_norm[new_idx] idx_front_0 = idx_front_0[new_idx] angle = [np.array([360, 0])] for i in range(1, len(PF) - 1): l = None u = None for m in range(i - 1, -1, -1): if np.sum(np.abs(PF[m] - PF[i])) != 0: l = m break for m in range(i + 1, len(PF), 1): if np.sum(np.abs(PF[m] - PF[i])) != 0: u = m break if l is None or u is None: angle.append(np.array([0, i])) else: position = above_or_below(PF[i], PF[l], PF[u]) if position == -1: angle.append( np.array([ calculating_angle(p_middle=PF_norm[i], p_top=PF_norm[l], p_bot=PF_norm[u]), i ])) else: angle.append(np.array([0, i])) angle.append(np.array([360, len(PS) - 1])) angle = np.array(angle) angle = angle[np.argsort(angle[:, 0])] angle = angle[angle[:, 0] > gamma] idx_potential_solutions = np.array(angle[:, 1], dtype=np.int) return PS, idx_potential_solutions, idx_front_0
def improve_potential_solutions(self, P): P_F = P.get('F') front_0 = NonDominatedSorting().do(P_F, n_stop_if_ranked=len(P), only_non_dominated_front=True) PF = P[front_0].copy() PF_F = P_F[front_0].copy() # normalize val_error for calculating angle between two individuals nPF_F = P_F[front_0].copy() mi_f0 = np.min(PF_F[:, 0]) ma_f0 = np.max(PF_F[:, 0]) mi_f1 = np.min(PF_F[:, 1]) ma_f1 = np.max(PF_F[:, 1]) nPF_F[:, 0] = (PF_F[:, 0] - mi_f0) / (ma_f0 - mi_f0) nPF_F[:, 1] = (PF_F[:, 1] - mi_f1) / (ma_f1 - mi_f1) new_idx = np.argsort(PF_F[:, 0]) # --> use for sort PF = PF[new_idx] PF_F = PF_F[new_idx] nPF_F = nPF_F[new_idx] front_0 = front_0[new_idx] angle = [np.array([360, 0])] for i in range(1, len(PF_F) - 1): l = None u = None for m in range(i - 1, -1, -1): if np.sum(np.abs(PF_F[m] - PF_F[i])) != 0: l = m break for m in range(i + 1, len(PF_F), 1): if np.sum(np.abs(PF_F[m] - PF_F[i])) != 0: u = m break if l is None or u is None: angle.append(np.array([0, i])) else: tren_hay_duoi = kiem_tra_p1_nam_phia_tren_hay_duoi_p2_p3( PF_F[i], PF_F[l], PF_F[u]) if tren_hay_duoi == 'duoi': angle.append( np.array([ cal_angle(p_middle=nPF_F[i], p_top=nPF_F[l], p_bot=nPF_F[u]), i ])) else: angle.append(np.array([0, i])) angle.append(np.array([360, len(PF) - 1])) angle = np.array(angle) angle = angle[np.argsort(angle[:, 0])] angle = angle[angle[:, 0] > 210] idx_S = np.array(angle[:, 1], dtype=np.int) S = PF[idx_S].copy() S = self.local_search_on_X(P, X=S, ls_on_knee_solutions=True) PF[idx_S] = S P[front_0] = PF return P
def _do(self, pop, n_survive, D=None, **kwargs): # attributes to be set after the survival F = pop.get("F") # find or usually update the new ideal point - from feasible solutions self.ideal_point = np.min(np.vstack( (self.ideal_point, F, self.ref_points)), axis=0) self.worst_point = np.max(np.vstack( (self.worst_point, F, self.ref_points)), axis=0) # calculate the fronts of the population fronts, rank = NonDominatedSorting().do(F, return_rank=True, n_stop_if_ranked=n_survive) non_dominated, last_front = fronts[0], fronts[-1] # find the extreme points for normalization self.extreme_points = get_extreme_points_c( np.vstack([F[non_dominated], self.ref_points]), self.ideal_point, extreme_points=self.extreme_points) # find the intercepts for normalization and do backup if gaussian elimination fails worst_of_population = np.max(F, axis=0) worst_of_front = np.max(F[non_dominated, :], axis=0) self.nadir_point = get_nadir_point(self.extreme_points, self.ideal_point, self.worst_point, worst_of_population, worst_of_front) # consider only the population until we come to the splitting front I = np.concatenate(fronts) pop, rank, F = pop[I], rank[I], F[I] # update the front indices for the current population counter = 0 for i in range(len(fronts)): for j in range(len(fronts[i])): fronts[i][j] = counter counter += 1 last_front = fronts[-1] unit_ref_points = (self.ref_points - self.ideal_point) / ( self.nadir_point - self.ideal_point) ref_dirs = get_ref_dirs_from_points(unit_ref_points, self.aspiration_ref_dirs, mu=self.mu) self.ref_dirs = denormalize(ref_dirs, self.ideal_point, self.nadir_point) # associate individuals to niches niche_of_individuals, dist_to_niche = associate_to_niches( F, ref_dirs, self.ideal_point, self.nadir_point) pop.set('rank', rank, 'niche', niche_of_individuals, 'dist_to_niche', dist_to_niche) # if we need to select individuals to survive if len(pop) > n_survive: # if there is only one front if len(fronts) == 1: n_remaining = n_survive until_last_front = np.array([], dtype=np.int) niche_count = np.zeros(len(ref_dirs), dtype=np.int) # if some individuals already survived else: until_last_front = np.concatenate(fronts[:-1]) niche_count = calc_niche_count( len(ref_dirs), niche_of_individuals[until_last_front]) n_remaining = n_survive - len(until_last_front) S = niching(pop[last_front], n_remaining, niche_count, niche_of_individuals[last_front], dist_to_niche[last_front]) survivors = np.concatenate( (until_last_front, last_front[S].tolist())) pop = pop[survivors] return pop
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: # select the closest solution idx = not_selected[0] # 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 _do(self, pop, n_survive, algorithm=None, **kwargs): if True: if self.archive is None: self.archive = pop else: self.archive = self.archive.merge(pop) # get the function values of the current archive for operations F = self.archive.get("F") # filter out all the duplicate solutions I = np.logical_not(default_is_duplicate(F)) self.archive, F = self.archive[I], F[I] # get only the non-dominated solutions I = NonDominatedSorting().do(F, only_non_dominated_front=True) self.archive, F = self.archive[I], F[I] if len(self.archive) > self.n_archive: cd = calc_crowding_distance(F) self.archive = self.archive[np.argsort(cd)[::-1] [:self.n_archive]] # attributes to be set after the survival pop.merge(self.archive) F = pop.get("F") # 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.worst_point = np.max(np.vstack((self.worst_point, F)), axis=0) # calculate the fronts of the population fronts, rank = NonDominatedSorting(epsilon=1e-10).do( F, return_rank=True, n_stop_if_ranked=n_survive) non_dominated, last_front = fronts[0], fronts[-1] # find the extreme points for normalization self.extreme_points = non_dominated[get_extreme_points( F[non_dominated, :], self.ideal_point)] # find the intercepts for normalization and do backup if gaussian elimination fails worst_of_population = np.max(F, axis=0) worst_of_front = np.max(F[non_dominated, :], axis=0) self.nadir_point = get_nadir_point(F[self.extreme_points], self.ideal_point, self.worst_point, worst_of_population, worst_of_front) # associate individuals to niches pop.set('rank', rank) # if we need to select individuals to survive if len(pop) > n_survive: for i in range(len(fronts)): fronts[i] = np.array( [j for j in fronts[i] if j not in self.extreme_points]) survivors = np.unique(self.extreme_points).tolist() for k, front in enumerate(fronts): # current front sorted by crowding distance if splitting if len(survivors) + len(front) > n_survive: I = selection(survivors, list(front), F, (n_survive - len(survivors))) survivors.extend(I) # otherwise take the whole front unsorted else: # extend the survivors by all or selected individuals survivors.extend(front) pop = pop[survivors] return pop
def _seeking(self, pop, **kwargs): pop_F = pop.get('F') pos_front_0 = NonDominatedSorting().do(pop_F, n_stop_if_ranked=len(pop), only_non_dominated_front=True) pareto_set = pop[pos_front_0].copy() pf = pop_F[pos_front_0].copy() # Normalize validation error for calculating angle between two individuals pf_norm = pf.copy() mi_f0 = np.min(pf[:, 0]) ma_f0 = np.max(pf[:, 0]) mi_f1 = np.min(pf[:, 1]) ma_f1 = np.max(pf[:, 1]) pf_norm[:, 0] = (pf[:, 0] - mi_f0) / (ma_f0 - mi_f0) pf_norm[:, 1] = (pf[:, 1] - mi_f1) / (ma_f1 - mi_f1) new_idx = np.argsort(pf[:, 0]) pareto_set = pareto_set[new_idx] pf = pf[new_idx] pf_norm = pf_norm[new_idx] pos_front_0 = pos_front_0[new_idx] angle = [np.array([360, 0])] for i in range(1, len(pf) - 1): l = None u = None for m in range(i - 1, -1, -1): if np.sum(np.abs(pf[m] - pf[i])) != 0: l = m break for m in range(i + 1, len(pf), 1): if np.sum(np.abs(pf[m] - pf[i])) != 0: u = m break if l is None or u is None: angle.append(np.array([0, i])) else: position = above_or_below(pf[i], pf[l], pf[u]) if position == -1: angle.append( np.array([ cal_angle(p_middle=pf_norm[i], p_top=pf_norm[l], p_bot=pf_norm[u]), i ])) else: angle.append(np.array([0, i])) angle.append(np.array([360, len(pareto_set) - 1])) angle = np.array(angle) angle = angle[np.argsort(angle[:, 0])] angle = angle[angle[:, 0] > self.gamma] pos_potential_sols = np.array(angle[:, 1], dtype=np.int) return pareto_set, pos_potential_sols, pos_front_0