def _advance(self, infills=None, **kwargs): self.norm.update(infills) pop = Population.merge(Population.merge(self.pop, infills), self.archive) F = pop.get("F") if self.problem.has_constraints(): G = pop.get("G") C = np.maximum(0, G) C = C / np.maximum(1, C.max(axis=0)) CV = C.sum(axis=1) else: CV = np.zeros(len(pop)) F = F - self.norm.ideal(only_feas=False) + 1e-6 indicator = np.full((len(pop), len(pop)), np.inf) for i in range(len(pop)): Ci = CV[i] Fi = F[i] if Ci == 0: Ir = np.log(Fi / F) MaxIr = np.max(Ir, axis=1) MinIr = np.min(Ir, axis=1) CVA = MaxIr J = MaxIr <= 0 CVA[J] = MinIr[J] val = CVA else: IC = (Ci + 1e-6) / (CV + 1e-6) CVF = np.max(np.maximum(Fi, F) / np.minimum(Fi, F), axis=1) val = np.log(np.maximum(CVF, IC)) indicator[:, i] = val indicator[i, i] = np.inf feas = np.where(CV <= 0)[0] if len(feas) <= self.pop_size: self.archive = pop[np.argsort(CV)[:self.pop_size]] else: feas_F = pop[feas].get("F") - self.norm.ideal( only_feas=True) + 1e-6 I = Selection_Operator_of_PREA(feas_F, indicator[feas][:, feas], self.pop_size) self.archive = pop[feas[I]] I = Indicator_based_CHT(F, indicator, self.ref_dirs, self.pop_size) self.pop = pop[I] self.Ra = 1 - self.n_gen / self.termination.n_max_gen
def update_ep(EP, Offsprings, nEP, nds): """ Update the external population archive """ # merge population and keep only the first non-dominated front EP = Population.merge(EP, Offsprings) EP = EP[nds.do(EP.get("F"), only_non_dominated_front=True)] N, M = EP.get("F").shape # Delete the overcrowded solutions D = cdist(EP.get("F"), EP.get("F")) # prevent selection of a point with itself D[np.eye(len(D), dtype=np.bool)] = np.inf removed = np.zeros(N, dtype=np.bool) while sum(removed) < N - nEP: remain = np.flatnonzero(~removed) subDis = np.sort(D[np.ix_(remain, remain)], axis=1) # compute viscinity distance among the closest neighbors prodDist = np.prod(subDis[:, 0:min(M, len(remain))], axis=1) # select the point with the smallest viscinity distance to its neighbors and set it as removed worst = np.argmin(prodDist) removed[remain[worst]] = True return EP[~removed]
def update_PCpop(pc_pop, off): pc_objs = pc_pop.get("F") off_objs = off.get("F") n = pc_objs.shape[0] del_ind = [] for i in range(n): flag = Dominator.get_relation(off_objs[0, :], pc_objs[i, :]) if flag == 1: # off dominates pc_pop[i] del_ind.append(i) break elif flag == -1: # pc_pop[i] dominates off return pc_pop else: # flag == 0 # off and pc_pop[i] are nondominated break if len(del_ind) > 0: pc_index = np.arange(n) # Delete element at index positions given by the list 'del_ind' pc_index = np.delete(pc_index, del_ind) pc_pop = pc_pop[pc_index.tolist()] pc_pop = Population.merge(pc_pop, off) return pc_pop
def test_restricted_mating_selection(ref_dirs, evaluator): np.random.seed(200) selection = RestrictedMating(func_comp=comp_by_cv_dom_then_random) problem = C3DTLZ4(n_var=12, n_obj=3) ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'preCA.x')) CA = Population.create(ca_x) evaluator.eval(problem, CA) da_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'preDA.x')) DA = Population.create(da_x) evaluator.eval(problem, DA) Hm = Population.merge(CA, DA) n_pop = len(CA) _, rank = NonDominatedSorting().do(Hm.get('F'), return_rank=True) Pc = (rank[:n_pop] == 0).sum() / len(Hm) Pd = (rank[n_pop:] == 0).sum() / len(Hm) P = selection.do(Hm, len(CA)) assert P.shape == (91, 2) if Pc > Pd: assert (P[:, 0] < n_pop).all() else: assert (P[:, 0] >= n_pop).all() assert (P[:, 1] >= n_pop).any() assert (P[:, 1] < n_pop).any()
def test_association(ref_dirs, evaluator): problem = C1DTLZ3(n_var=12, n_obj=3) ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', 'case3', 'preCA.x')) CA = Population.create(ca_x) evaluator.eval(problem, CA) da_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', 'case3', 'preDA.x')) DA = Population.create(da_x) evaluator.eval(problem, DA) off_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', 'case3', 'offspring.x')) off = Population.create(off_x) evaluator.eval(problem, off) true_assoc = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', 'case3', 'feasible_rank0.txt')) true_niche = true_assoc[:, 1] true_id = true_assoc[:, 0] sorted_id = np.argsort(true_id) survival = CADASurvival(ref_dirs) mixed = Population.merge(CA, off) survival.ideal_point = np.min(np.vstack((DA.get("F"), mixed.get("F"))), axis=0) fronts = NonDominatedSorting().do(mixed.get("F"), n_stop_if_ranked=len(ref_dirs)) I = np.concatenate(fronts) niche, _ = survival._associate(mixed[I]) sorted_I = np.argsort(I) assert (niche[sorted_I] == true_niche[sorted_id]).all()
def _next(self): sol = self.opt[0] x = sol.get("X") jac, hess = self.gradient_and_hessian(sol) method = "cholesky" func = direction_cholesky if method == "cholesky" else direction_inv direction, decrement, adapted, success = func(jac, hess) # in case we can not calculate the newton direction fall back to the gradient approach if not success: direction = -jac decrement = np.linalg.norm(direction) ** 2 if self.damped: line = LineSearchProblem(self.problem, sol, direction) _next = WolfeSearch().setup(line, evaluator=self.evaluator).run() # _next = wolfe_line_search(self.problem, sol, direction, evaluator=self.evaluator) # if the newton step was not successful, then try the gradient if adapted and (sol.F[0] - _next.F[0]) < 1e-16: _next = inexact_line_search(self.problem, sol, -jac, evaluator=self.evaluator) else: _x = x + direction _next = Solution(X=_x) if isinstance(_next, Individual): _next = Population.create(_next) self.pop = Population.merge(self.opt, _next) if decrement / 2 <= self.eps: self.termination.force_termination = True
def _advance(self, infills=None, **kwargs): assert infills is not None, "This algorithms uses the AskAndTell interface thus infills must to be provided." pop = Population.merge(self.pop, infills) self.pop, self.da = self.survival.do(self.problem, pop, self.da, self.pop_size, algorithm=self)
def test_update_da(ref_dirs, evaluator): problem = C1DTLZ3(n_var=12, n_obj=3) for i in range(2): ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', f'case{i + 1}', 'preCA.x')) CA = Population.create(ca_x) evaluator.eval(problem, CA) da_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', f'case{i + 1}', 'preDA.x')) DA = Population.create(da_x) evaluator.eval(problem, DA) off_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', f'case{i + 1}', 'offspring.x')) off = Population.create(off_x) evaluator.eval(problem, off) survival = CADASurvival(ref_dirs) mixed = Population.merge(CA, off) survival.ideal_point = np.min(np.vstack((DA.get("F"), mixed.get("F"))), axis=0) post_ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c1dtlz3', f'case{i + 1}', 'postCA.x')) CA = Population.create(post_ca_x) evaluator.eval(problem, CA) Hd = Population.merge(DA, off) pDA = survival._updateDA(CA, Hd, 91) true_S1 = [151, 35, 6, 63, 67, 24, 178, 106, 134, 172, 148, 159, 41, 173, 145, 77, 62, 40, 127, 61, 130, 27, 171, 115, 52, 176, 22, 75, 55, 87, 36, 149, 154, 47, 78, 170, 90, 15, 53, 175, 179, 165, 56, 89, 132, 82, 141, 39, 32, 25, 131, 14, 72, 65, 177, 140, 66, 143, 34, 81, 103, 99, 147, 168, 51, 26, 70, 94, 54, 97, 158, 107, 29, 120, 50, 108, 157, 11, 85, 174, 80, 0, 95, 13, 142, 101, 156, 19, 8, 98, 20] true_S2 = [78, 173, 59, 21, 101, 52, 36, 94, 17, 20, 37, 96, 90, 129, 150, 136, 162, 70, 146, 75, 138, 154, 65, 179, 98, 32, 97, 11, 26, 107, 12, 128, 95, 170, 24, 171, 40, 180, 14, 44, 49, 43, 130, 23, 60, 79, 148, 62, 87, 56, 157, 73, 104, 45, 177, 74, 15, 152, 164, 28, 80, 113, 41, 33, 158, 57, 77, 34, 114, 118, 18, 54, 53, 145, 93, 115, 121, 174, 142, 39, 13, 105, 10, 69, 120, 55, 6, 153, 91, 137, 46] if i == 0: assert np.all(pDA == Hd[true_S1]) else: assert np.all(pDA == Hd[true_S2])
def _do(self, problem, pop, off, algorithm=None, **kwargs): if self.cnt is None: self.cnt = np.zeros(len(pop), dtype=int) cnt = self.cnt cv = pop.get("CV")[:, 0] # cnt = self.cnt cnt = algorithm.n_gen - pop.get("n_gen") - 1 # make sure we never replace the best solution if we would consider feasibility first best = FitnessSurvival().do(problem, Population.merge(pop, off), n_survive=1)[0] eps = np.zeros(len(pop)) for k, t in enumerate(cnt): # cycle = (t // (4 * self.t)) # max_eps = (2 ** cycle) * self.eps max_eps = self.eps t = t % (4 * self.t) if t < self.t: eps[k] = cv[k] + (max_eps - cv[k]) * (t / self.t) elif t < 2 * self.t: eps[k] = max_eps elif t < 3 * self.t: eps[k] = max_eps * (1 - ((t % self.t) / self.t)) else: eps[k] = 0.0 eps_is_zero = np.where(eps <= 0)[0] # print(len(eps_is_zero)) repl = np.full(len(pop), False) for k in range(len(pop)): if pop[k] == best: repl[k] = False elif off[k] == best: repl[k] = True else: if rel_eps_constr(pop[k], off[k], eps[k]) <= 0: repl[k] = True # self.cnt[repl] = 0 # self.cnt[~repl] += 1 return repl
def _advance(self, infills=None, **kwargs): # if not all solutions suggested by infill() are evaluated we create a more semi (mu+lambda) algorithm if len(infills) < self.pop_size: infills = Population.merge(infills, self.pop) self.pop = self.survival.do(self.problem, infills, n_survive=self.pop_size)
def crossover(crossover, a, b, c=None, xl=0, xu=1, type_var=np.double, **kwargs): n = a.shape[0] _pop = Population.merge(Population.new("X", a), Population.new("X", b)) _P = np.column_stack([np.arange(n), np.arange(n) + n]) if c is not None: _pop = Population.merge(_pop, Population.new("X", c)) _P = np.column_stack([_P, np.arange(n) + 2 * n]) problem = get_problem_func(a.shape[1], xl, xu, type_var)(**kwargs) return crossover.do(problem, _pop, _P, **kwargs).get("X")
def do(self, problem, pop, *args, n_survive=None, return_indices=False, **kwargs): # make sure the population has at least one individual if len(pop) == 0: return pop if n_survive is None: n_survive = len(pop) n_survive = min(n_survive, len(pop)) # if the split should be done beforehand if self.filter_infeasible and problem.n_constr > 0: # split feasible and infeasible solutions feas, infeas = split_by_feasibility(pop, eps=0.0, sort_infeasbible_by_cv=True) if len(feas) == 0: survivors = Population() else: survivors = self._do(problem, pop[feas], *args, n_survive=min(len(feas), n_survive), **kwargs) # calculate how many individuals are still remaining to be filled up with infeasible ones n_remaining = n_survive - len(survivors) # if infeasible solutions needs to be added if n_remaining > 0: survivors = Population.merge(survivors, pop[infeas[:n_remaining]]) else: survivors = self._do(problem, pop, *args, n_survive=n_survive, **kwargs) if return_indices: H = {} for k, ind in enumerate(pop): H[ind] = k return [H[survivor] for survivor in survivors] else: return survivors
def _infill(self): pop = self.pop # actually do the mating given the elite selection and biased crossover off = self.mating.do(self.problem, pop, n_offsprings=self.n_offsprings, algorithm=self) # create the mutants randomly to fill the population with mutants = FloatRandomSampling().do(self.problem, self.n_mutants, algorithm=self) # evaluate all the new solutions return Population.merge(off, mutants)
def _advance(self, infills=None): pop = self.pop # get all the elites from the current population elites = np.where(pop.get("type") == "elite")[0] # finally merge everything together and sort by fitness pop = Population.merge(pop[elites], infills) # the do survival selection - set the elites for the next round self.pop = self.survival.do(self.problem, pop, n_survive=len(pop), algorithm=self)
def _advance(self, infills=None, **kwargs): # merge the offsprings with the current population if infills is not None: self.pop = Population.merge(self.pop, infills) # execute the survival to find the fittest solutions self.pop = self.survival.do(self.problem, self.pop, n_survive=self.pop_size, algorithm=self)
def do(self, _, pop, da, n_survive=None, **kwargs): # Offspring are last of merged population off = pop[-n_survive:] # Update ideal point self.ideal_point = np.min(np.vstack((self.ideal_point, off.get("F"))), axis=0) # Update CA pop = self._updateCA(pop, n_survive) # Update DA Hd = Population.merge(da, off) da = self._updateDA(pop, Hd, n_survive) return pop, da
def _advance(self, infills=None, **kwargs): assert infills is not None, "This algorithms uses the AskAndTell interface thus infills must to be provided." pop = Population.merge(self.pop, infills) F, CV = pop.get("F", "CV") f, cv = F[:, 0, ], CV[:, 0] S = hierarchical_sort(f, cv) pop, f, cv, feas = pop[S], f[S], cv[S], cv[S] <= 0 survivors = list(range(30)) n_remaining = self.pop_size - len(survivors) if feas.sum() > 0: f_min, cv_min = f[feas].min(), 0.0 else: f_min, cv_min = np.inf, cv[~feas].min() I = np.where(np.logical_and(~feas, f < f_min))[0] # survivors.extend(I[:n_remaining]) survivors.extend(I[cv[I].argsort()][:n_remaining]) if len(survivors) < n_remaining: I = np.where(np.logical_and(~feas, f >= f_min))[0] survivors.extend(I[f[I].argsort()][:n_remaining]) # survivors.extend(np.random.choice(I, size=n_remaining)) if self.n_gen > 1000: import matplotlib.pyplot as plt plt.scatter(cv[~feas], f[~feas], facecolor="none", edgecolors="red", alpha=0.5, s=20) plt.scatter(cv[feas], f[feas], facecolor="none", edgecolors="blue", alpha=0.5, s=40) plt.scatter(cv[survivors], f[survivors], color="black", s=3, alpha=0.9) plt.show() self.pop = pop[survivors] self.pop.set("below", self.pop.get("F")[:, 0] <= f_min)
def step(self): alpha, max_alpha = self.alpha, self.problem.xu[0] if alpha > max_alpha: alpha = max_alpha infill = Individual(X=np.array([alpha])) self.evaluator.eval(self.problem, infill) self.pop = Population.merge(self.pop, infill)[-10:] if is_better(self.point, infill, eps=0.0) or alpha == max_alpha: self.termination.force_termination = True return self.point = infill self.alpha *= 2
def _infill(self, **kwargs): pop, archive, Ra = self.pop, self.archive, self.Ra N = self.pop_size Nt = int(np.floor(Ra * N)) from_pop = pop[permutation(len(pop))[:Nt]] from_archive = pop[permutation(len(archive))[:N - Nt]] pool = Population.merge(from_pop, from_archive) # first do the differential evolution mating off = self.mating.do(self.problem, pool, self.n_offsprings, algorithm=self, ideal=self.norm.ideal(only_feas=False)) return off
def _local_initialize_infill(self): self.alpha, self.beta, self.gamma, self.delta = self.func_params(self.problem) # the corresponding x values of the provided or best found solution x0 = self.x0.X # if lower and upper bounds are given take 5% of the range and add if self.problem.has_bounds(): self.simplex_scaling = 0.05 * (self.problem.xu - self.problem.xl) # no bounds are given do it based on x0 - MATLAB procedure else: self.simplex_scaling = 0.05 * self.x0.X # some value needs to be added if x0 is zero self.simplex_scaling[self.simplex_scaling == 0] = 0.00025 # initialize the simplex simplex = pop_from_array_or_individual(self.initialize_simplex(x0)) return Population.merge(self.x0, simplex)
def _infill(self): # the offspring population to finally evaluate and attach to the population infills = Population() # find the potential optimal solution in the current population potential_optimal = self._potential_optimal() # for each of those solutions execute the division move for current in potential_optimal: # find the largest dimension the solution has not been evaluated yet nxl, nxu = norm_bounds(current, self.problem) k = np.argmax(nxu - nxl) # the delta value to be used to get left and right - this is one sixth of the range xl, xu = current.get("xl"), current.get("xu") delta = (xu[k] - xl[k]) / 6 # create the left individual left_x = np.copy(current.X) left_x[k] = xl[k] + delta left = Individual(X=left_x) # create the right individual right_x = np.copy(current.X) right_x[k] = xu[k] - delta right = Individual(X=right_x) # update the boundaries for all the points accordingly for ind in [current, left, right]: update_bounds(ind, xl, xu, k, delta) # create the offspring population, evaluate and attach to current population _infill = Population.create(left, right) _infill.set("depth", current.get("depth") + 1) infills = Population.merge(infills, _infill) return infills
def do(self, problem, pop, n_offsprings, **kwargs): # the population object to be used off = pop.new() # infill counter - counts how often the mating needs to be done to fill up n_offsprings n_infills = 0 # iterate until enough offsprings are created while len(off) < n_offsprings: # how many offsprings are remaining to be created n_remaining = n_offsprings - len(off) # do the mating _off = self._do(problem, pop, n_remaining, **kwargs) # repair the individuals if necessary - disabled if repair is NoRepair _off = self.repair.do(problem, _off, **kwargs) # eliminate the duplicates - disabled if it is NoRepair _off = self.eliminate_duplicates.do(_off, pop, off) # if more offsprings than necessary - truncate them randomly if len(off) + len(_off) > n_offsprings: # IMPORTANT: Interestingly, this makes a difference in performance n_remaining = n_offsprings - len(off) _off = _off[:n_remaining] # add to the offsprings and increase the mating counter off = Population.merge(off, _off) n_infills += 1 # if no new offsprings can be generated within a pre-specified number of generations if n_infills >= self.n_max_iterations: break return off
def test_update(ref_dirs, evaluator): problem = C3DTLZ4(n_var=12, n_obj=3) ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'preCA.x')) CA = Population.create(ca_x) evaluator.eval(problem, CA) da_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'preDA.x')) DA = Population.create(da_x) evaluator.eval(problem, DA) off_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'offspring.x')) off = Population.create(off_x) evaluator.eval(problem, off) post_ca_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'postCA.x')) true_pCA = Population.create(post_ca_x) evaluator.eval(problem, true_pCA) post_da_x = np.loadtxt(path_to_test_resource('ctaea', 'c3dtlz4', 'case2', 'postDA.x')) true_pDA = Population.create(post_da_x) evaluator.eval(problem, true_pDA) survival = CADASurvival(ref_dirs) mixed = Population.merge(CA, off) survival.ideal_point = np.array([0., 0., 0.]) pCA, pDA = survival.do(problem, mixed, DA, len(ref_dirs)) pCA_X = set([tuple(x) for x in pCA.get("X")]) tpCA_X = set([tuple(x) for x in true_pCA.get("X")]) pDA_X = set([tuple(x) for x in pDA.get("X")]) tpDA_X = set([tuple(x) for x in true_pDA.get("X")]) assert pCA_X == tpCA_X assert pDA_X == tpDA_X
def _advance(self, infills=None): self.pop = infills if self.opt is None else Population.merge(infills, self.opt)
def _advance(self, **kwargs): repair, crossover, mutation = self.repair, self.mating.crossover, self.mating.mutation pc_pop = self.pc_pop.copy(deep=True) npc_pop = self.npc_pop.copy(deep=True) ############################################################## # PC evolving ############################################################## # Normalise both poulations according to the PC individuals pc_pop, npc_pop = normalize_bothpop(pc_pop, npc_pop) PCObj = pc_pop.get("F") NPCObj = npc_pop.get("F") pc_size = PCObj.shape[0] npc_size = NPCObj.shape[0] ###################################################### # Calculate the Euclidean distance among individuals ###################################################### d = np.zeros((pc_size, pc_size)) d = cdist(PCObj, PCObj, 'euclidean') d[d == 0] = np.inf # Determine the size of the niche if pc_size == 1: radius = 0 else: radius = determine_radius(d, pc_size, self.pc_capacity) # calculate the radius for individual exploration r = pc_size / self.pc_capacity * radius ######################################################## # find the promising individuals in PC for exploration ######################################################## # promising_num: record how many promising individuals in PC promising_num = 0 # count: record how many NPC individuals are located in each PC individual's niche count = np.array([]) d2 = np.zeros((pc_size, npc_size)) d2 = cdist(PCObj, NPCObj, 'euclidean') # Count of True elements in each row (each individual in PC) of 2D Numpy Array count = np.count_nonzero(d2 <= r, axis=1) # Check if the niche has no NPC individual or has only one NPC individual # Record the indices of promising individuals. # Since np.nonzero() returns a tuple of arrays, we change the type of promising_index to a numpy.ndarray. promising_index = np.nonzero(count <= 1) promising_index = np.asarray(promising_index).flatten() # Record total number of promising individuals in PC for exploration promising_num = len(promising_index) ######################################## # explore these promising individuals ######################################## original_size = pc_size off = Individual() if promising_num > 0: for i in range(promising_num): if original_size > 1: parents = Population.new(2) # The explored individual is considered as one parent parents[0] = pc_pop[promising_index[i]] # The remaining parent will be selected randomly from the PC population rnd = np.random.permutation(pc_size) for j in rnd: if j != promising_index[i]: parents[1] = pc_pop[j] break index = np.array([0, 1]) parents_shape = index[None, :] # do recombination and create an offspring off = crossover.do(self.problem, parents, parents_shape)[0] else: off = pc_pop[0] # mutation off = Population.create(off) off = mutation.do(self.problem, off) # evaluate the offspring self.evaluator.eval(self.problem, off) # update the PC population by the offspring self.pc_pop = update_PCpop(self.pc_pop, off) # update the ideal point self.ideal = np.min(np.vstack([self.ideal, off.get("F")]), axis=0) # update at most one solution in NPC population self.npc_pop = update_NPCpop(self.npc_pop, off, self.ideal, self.ref_dirs, self.decomp) ######################################################## # NPC evolution based on MOEA/D ######################################################## # iterate for each member of the population in random order for i in np.random.permutation(len(self.npc_pop)): # get the parents using the neighborhood selection P = self.selection.do(self.npc_pop, 1, self.mating.crossover.n_parents, k=[i]) # perform a mating using the default operators (recombination & mutation) - if more than one offspring just pick the first off = self.mating.do(self.problem, self.npc_pop, 1, parents=P)[0] off = Population.create(off) # evaluate the offspring self.evaluator.eval(self.problem, off, algorithm=self) # update the PC population by the offspring self.pc_pop = update_PCpop(self.pc_pop, off) # update the ideal point self.ideal = np.min(np.vstack([self.ideal, off.get("F")]), axis=0) # now actually do the replacement of the individual is better self.npc_pop = self._replace(i, off) ######################################################## # population maintenance operation in the PC evolution ######################################################## current_pop = Population.merge(self.pc_pop, self.npc_pop) current_pop = Population.merge(current_pop, self.pop) # filter duplicate in the population pc_pop = self.eliminate_duplicates.do(current_pop) pc_size = len(pc_pop) if (pc_size > self.pc_capacity): # get the objective space values and objects pc_objs = pc_pop.get("F") fronts, rank = NonDominatedSorting().do(pc_objs, return_rank=True) front_0_index = fronts[0] # put the nondominated individuals of the NPC population into the PC population self.pc_pop = pc_pop[front_0_index] if len(self.pc_pop) > self.pc_capacity: self.pc_pop = maintain_PCpop(self.pc_pop, self.pc_capacity) self.pop = self.pc_pop.copy(deep=True)
def _set_optimum(self): pop = self.pop if self.opt is None else Population.merge( self.opt, self.pop) self.opt = filter_optimum(pop, least_infeasible=True)
def _updateCA(self, pop, n_survive): """Update the Convergence archive (CA)""" CV = pop.get("CV").flatten() Sc = pop[CV == 0] # Feasible population if len(Sc) == n_survive: # Exactly n_survive feasible individuals F = Sc.get("F") fronts, rank = NonDominatedSorting().do(F, return_rank=True) Sc.set('rank', rank) self.opt = Sc[fronts[0]] return Sc elif len(Sc) < n_survive: # Not enough feasible individuals remainder = n_survive - len(Sc) # Solve sub-problem CV, tche SI = pop[CV > 0] f1 = SI.get("CV") _, f2 = self._associate(SI) sub_F = np.column_stack([f1, f2]) fronts = NonDominatedSorting().do(sub_F, n_stop_if_ranked=remainder) I = [] for front in fronts: if len(I) + len(front) <= remainder: I.extend(front) else: n_missing = remainder - len(I) last_front_CV = np.argsort(f1.flatten()[front]) I.extend(front[last_front_CV[:n_missing]]) SI = SI[I] S = Population.merge(Sc, SI) F = S.get("F") fronts, rank = NonDominatedSorting().do(F, return_rank=True) S.set('rank', rank) self.opt = S[fronts[0]] return S else: # Too many feasible individuals F = Sc.get("F") # Filter by non-dominated sorting fronts, rank = NonDominatedSorting().do(F, return_rank=True, n_stop_if_ranked=n_survive) I = np.concatenate(fronts) S, rank, F = Sc[I], rank[I], F[I] if len(S) > n_survive: # Remove individual in most crowded niche and with worst fitness niche_of_individuals, FV = self._associate(S) index, count = np.unique(niche_of_individuals, return_counts=True) survivors = np.full(S.shape[0], True) while survivors.sum() > n_survive: crowdest_niches, = np.where(count == count.max()) worst_idx = None worst_niche = None worst_fit = -1 for crowdest_niche in crowdest_niches: crowdest, = np.where( (niche_of_individuals == index[crowdest_niche]) & survivors) niche_worst = crowdest[FV[crowdest].argmax()] dist_to_max_fit = cdist(F[[niche_worst], :], F).flatten() dist_to_max_fit[niche_worst] = np.inf dist_to_max_fit[~survivors] = np.inf min_d_to_max_fit = dist_to_max_fit.min() dist_in_niche = squareform(pdist(F[crowdest])) np.fill_diagonal(dist_in_niche, np.inf) delta_d = dist_in_niche - min_d_to_max_fit min_d_i = np.unravel_index( np.argmin(delta_d, axis=None), dist_in_niche.shape) if (delta_d[min_d_i] < 0) or ( delta_d[min_d_i] == 0 and (FV[crowdest[list(min_d_i)]] > niche_worst).any()): min_d_i = list(min_d_i) np.random.shuffle(min_d_i) closest = crowdest[min_d_i] niche_worst = closest[np.argmax(FV[closest])] if FV[niche_worst] > worst_fit: worst_fit = FV[niche_worst] worst_idx = niche_worst worst_niche = crowdest_niche survivors[worst_idx] = False count[worst_niche] -= 1 S, rank = S[survivors], rank[survivors] S.set('rank', rank) self.opt = S[rank == 0] return S
def update_weight(pop, W, Z, EP, nus): """ Delete crowded weight vectors and add new ones """ N, M = pop.get("F").shape # Update the current population by EP # Calculate the function value of each solution in Population or EP on each subproblem in W combined = Population.merge(pop, EP) combinedF = np.abs(combined.get("F") - Z) g = np.zeros((len(combined), W.shape[0])) for i in range(W.shape[0]): g[:, i] = np.max(combinedF * W[i, :], axis=1) # Choose the best solution for each subproblem best = np.argmin(g, axis=0) pop = combined[best] ###################################### # Delete the overcrowded subproblems # ###################################### D = cdist(pop.get("F"), pop.get("F")) # avoid selection of solutions with themselves D[np.eye(len(D), dtype=np.bool)] = np.inf deleted = np.zeros(len(pop), dtype=np.bool) while np.sum(deleted) < min(nus, len(EP)): remain = np.flatnonzero(~deleted) subD = D[np.ix_(remain, remain)] subDis = np.sort(subD, axis=1) # use viscinity distance to find the most crowded vector among the remaining ones and set it to be deleted worst = np.argmin(np.prod(subDis[:, 0:min(M, len(remain))], axis=1)) deleted[remain[worst]] = True pop = pop[~deleted] W = W[~deleted, :] ###################################### # Add new subproblems # ###################################### # Determine the new solutions be added combined = Population.merge(pop, EP) selected = np.zeros(len(combined), dtype=np.bool) selected[:len( pop)] = True # keep all solutions from pop and add solutions from EP D = cdist(combined.get("F"), combined.get("F")) D[np.eye(len(D), dtype=np.bool)] = np.inf while np.sum(selected) < min(N, len(selected)): # get the farthest solutions from already selected solutions using viscinity distance and select it subDis = np.sort(D[np.ix_(~selected, selected)], axis=1) best = np.argmax(np.prod(subDis[:, 0:min(M, subDis.shape[1])], axis=1)) remain = np.flatnonzero(~selected) selected[remain[best]] = True # Add new subproblems to W newF = EP[selected[len(pop):]].get("F") # transform the weights using the WS transformation described in the paper # we don't care about NaNs as they will be eliminated later anyway with np.errstate(divide="ignore", invalid='ignore'): temp = 1. / (newF - Z) W = np.vstack([W, temp / np.sum(temp, axis=1)[:, None]]) # Add new solutions pop = combined[selected] return pop, W
def _infill(self): Hm = Population.merge(self.pop, self.da) return self.mating.do(self.problem, Hm, n_offsprings=self.n_offsprings, algorithm=self)
def step(self): problem, sol = self.problem, self.opt[0] self.evaluator.eval(self.problem, sol, evaluate_values_of=["dF"]) dF = sol.get("dF")[0] print(sol) if np.linalg.norm(dF)**2 < 1e-8: self.termination.force_termination = True return direction = self.direction(dF) line = LineSearchProblem(self.problem, sol, direction, strict_bounds=self.strict_bounds) alpha = self.alpha if self.strict_bounds: if problem.has_bounds(): line.xu = np.array([ max_alpha(sol.X, direction, *problem.bounds(), mode="all_hit_bounds") ]) # remember the step length from the last run alpha = min(alpha, line.xu[0]) if alpha == 0: self.termination.force_termination = True return # make the solution to be the starting point of the univariate search x0 = sol.copy(deep=True) x0.set("__X__", x0.get("X")) x0.set("X", np.zeros(1)) # determine the brackets to be searched in exp = ExponentialSearch(delta=alpha).setup(line, evaluator=self.evaluator, termination=("n_iter", 20), x0=x0) a, b = exp.run().pop[-2:] # search in the brackets res = GoldenSectionSearch().setup(line, evaluator=self.evaluator, termination=("n_iter", 20), a=a, b=b).run() infill = res.opt[0] # set the alpha value and revert the X to be the multi-variate one infill.set("X", infill.get("__X__")) self.alpha = infill.get("alpha")[0] # keep always a few historical solutions self.pop = Population.merge(self.pop, infill)[-10:]