def calc(self, problem, evaluator, solution, values, eps, hessian): jacobian_estm, hessian_estm = self.jacobian, self.hessian jac_approx = Population.new(X=jacobian_estm.points(solution.X, eps)) evaluator.eval(problem, jac_approx) for value in values: f, F = solution.get(value), jac_approx.get(value) dF = np.array([ jacobian_estm.calc(f[m], F[:, m], eps) for m in range(F.shape[1]) ]) solution.set("d" + value, dF) # if the hessian should be calculated as well if hessian: hess_approx = Population.new( X=self.hessian.points(solution.X, eps)) evaluator.eval(problem, hess_approx) for value in values: f, F, FF = solution.get(value), jac_approx.get( value), hess_approx.get(value) ddF = np.array([ hessian_estm.calc(f[m], F[:, m], FF[:, m], eps) for m in range(FF.shape[1]) ]) solution.set("dd" + value, ddF)
def _initialize_infill(self): self.step_size = self.alpha if self.point is None: return Population.new(X=np.copy(self.problem.xl[None, :])) else: return Population.create(self.point)
def _infill(self): pop, mu, _lambda = self.pop, self.pop_size, self.n_offsprings xl, xu = self.problem.bounds() X, sigma = pop.get("X", "sigma") # cycle through the elites individuals for create the solutions I = np.arange(_lambda) % mu # transform X and sigma to the shape of number of offsprings X, sigma = X[I], sigma[I] # copy the original sigma to sigma prime to be modified Xp, sigmap = np.copy(X), np.copy(sigma) # for the best individuals do differential variation to provide a direction to search in Xp[:mu - 1] = X[:mu - 1] + self.gamma * (X[0] - X[1:mu]) # update the sigma values for elite and non-elite individuals sigmap[mu - 1:] = np.minimum( self.sigma_max, es_sigma(sigma[mu - 1:], self.tau, self.taup)) # execute the evolutionary strategy to calculate the offspring solutions Xp[mu - 1:] = X[mu - 1:] + sigmap[mu - 1:] * np.random.normal( size=sigmap[mu - 1:].shape) # repair the individuals which are not feasible by sampling from sigma again Xp = es_mut_repair(Xp, X, sigmap, xl, xu, 10) # now update the sigma values of the non-elites only sigmap[mu:] = sigma[mu:] + self.alpha * (sigmap[mu:] - sigma[mu:]) # create the population to proceed further off = Population.new(X=Xp, sigma=sigmap) return off
def test_survival(): problem = DTLZ2(n_obj=3) for k in range(1, 11): print("TEST RVEA GEN", k) ref_dirs = np.loadtxt( path_to_test_resource('rvea', f"ref_dirs_{k}.txt")) F = np.loadtxt(path_to_test_resource('rvea', f"F_{k}.txt")) pop = Population.new(F=F) algorithm = RVEA(ref_dirs) algorithm.setup(problem, termination=('n_gen', 500)) algorithm.n_gen = k algorithm.pop = pop survival = APDSurvival(ref_dirs) survivors = survival.do(problem, algorithm.pop, n_survive=len(pop), algorithm=algorithm, return_indices=True) apd = pop[survivors].get("apd") correct_apd = np.loadtxt(path_to_test_resource('rvea', f"apd_{k}.txt")) np.testing.assert_allclose(apd, correct_apd)
def _backward(self, X, inplace=False, **kwargs): assert isinstance(X, np.ndarray) and X.ndim == 2 if inplace: self.obj.set(self.attr, X) return self.obj else: return Population.new(**{self.attr: X})
def _infill(self): pop, mu, _lambda = self.pop, self.pop_size, self.n_offsprings xl, xu = self.problem.bounds() X, sigma = pop.get("X", "sigma") # cycle through the elites individuals for create the solutions I = np.arange(_lambda) % mu # transform X and sigma to the shape of number of offsprings X, sigma = X[I], sigma[I] # get the sigma only of the elites to be used sigmap = es_intermediate_recomb(sigma) # calculate the new sigma based on tau and tau prime sigmap = np.minimum(self.sigma_max, es_sigma(sigmap, self.tau, self.taup)) # execute the evolutionary strategy to calculate the offspring solutions Xp = X + sigmap * np.random.normal(size=sigmap.shape) # if gamma is not none do the differential variation overwrite Xp and sigmap for the first mu-1 individuals if self.gamma is not None: Xp[:mu - 1] = X[:mu - 1] + self.gamma * (X[0] - X[1:mu]) sigmap[:mu - 1] = sigma[:mu - 1] # if we have bounds to consider -> repair the individuals which are out of bounds if self.problem.has_bounds(): Xp = es_mut_repair(Xp, X, sigmap, xl, xu, 10) # create the population to proceed further off = Population.new(X=Xp, sigma=sigmap) return off
def do(self, problem, n_samples, pop=Population(), **kwargs): """ Sample new points with problem information if necessary. Parameters ---------- problem : :class:`~pymoo.core.problem.Problem` The problem to which points should be sampled. (lower and upper bounds, discrete, binary, ...) n_samples : int Number of samples pop : :class:`~pymoo.core.population.Population` The sampling results are stored in a population. The template of the population can be changed. If 'none' simply a numpy array is returned. Returns ------- X : numpy.array Samples points in a two dimensional array """ val = self._do(problem, n_samples, **kwargs) if pop is None: return val return Population.new("X", val)
def __init__(self, ref_dirs, n_neighbors=20, decomposition=Tchebicheff2(), prob_neighbor_mating=0.9, sampling=FloatRandomSampling(), crossover=SimulatedBinaryCrossover(prob=1.0, eta=20), mutation=PolynomialMutation(prob=None, eta=20), display=MultiObjectiveDisplay(), **kwargs): """ Parameters ---------- ref_dirs n_neighbors decomposition prob_neighbor_mating display kwargs """ self.ref_dirs = ref_dirs self.pc_capacity = len(ref_dirs) self.pc_pop = Population.new() self.npc_pop = Population.new() self.n_neighbors = min(len(ref_dirs), n_neighbors) self.prob_neighbor_mating = prob_neighbor_mating self.decomp = decomposition # initialise the neighborhood of subproblems based on the distances of weight vectors self.neighbors = np.argsort(cdist(self.ref_dirs, self.ref_dirs), axis=1, kind='quicksort')[:, :self.n_neighbors] self.selection = NeighborhoodSelection(self.neighbors, prob=prob_neighbor_mating) super().__init__(pop_size=len(ref_dirs), sampling=sampling, crossover=crossover, mutation=mutation, eliminate_duplicates=DefaultDuplicateElimination(), display=display, advance_after_initialization=False, **kwargs)
def _local_infill(self): X = np.array(self.next_X) self.send_array_to_yield = X.ndim > 1 X = np.atleast_2d(X) # evaluate the population self.pop = Population.new("X", self.norm.backward(X)) return self.pop
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, parents, **kwargs): X = pop.get("X")[parents.T].copy() _, n_matings, n_var = X.shape M = mut_binomial(n_matings, n_var, self.bias, at_least_once=True) Xp = X[0] Xp[~M] = X[1][~M] return Population.new(X=Xp)
def to_solution_set(X, F=None, G=None): if isinstance(X, np.ndarray): if X.ndim == 1: X = Solution(X=X, F=F, G=G) else: X = Population.new(X=X, F=F, G=G) if isinstance(X, Individual): X = SolutionSet.create(X) return X
def do(self, problem, pop, parents, **kwargs): X = pop.get("X")[parents.T].copy() assert len( X.shape ) == 3, "Please provide a three-dimensional matrix n_parents x pop_size x n_vars." n_parents, n_matings, n_var = X.shape # a mask over matings that need to be repeated m = np.arange(n_matings) # if the user provides directly an F value to use F = self.F if self.F is not None else rnd_F(m) # prepare the out to be set Xp = de_differential(X[:, m], F) # if the problem has boundaries to be considered if problem.has_bounds(): for k in range(self.n_iter): # find the individuals which are still infeasible m = is_out_of_bounds_by_problem(problem, Xp) F = rnd_F(m) # actually execute the differential equation Xp[m] = de_differential(X[:, m], F) # if still infeasible do a random initialization Xp = repair_random_init(Xp, X[0], *problem.bounds()) if self.variant == "bin": M = mut_binomial(n_matings, n_var, self.CR, at_least_once=self.at_least_once) elif self.variant == "exp": M = mut_exp(n_matings, n_var, self.CR, at_least_once=self.at_least_once) else: raise Exception(f"Unknown variant: {self.variant}") # take the first parents (this is already a copy) X = X[0] # set the corresponding values from the donor vector X[M] = Xp[M] return Population.new("X", X)
def test_preevaluated(): evaluator = Evaluator() pop = Population.new("X", X) evaluator.eval(problem, pop) pop[range(30)].set("evaluated", None) evaluator = Evaluator() evaluator.eval(problem, pop) np.testing.assert_allclose(F, pop.get("F")) assert evaluator.n_eval == 30
def do(self, problem, pop, parents, **kwargs): """ This method executes the crossover on the parents. This class wraps the implementation of the class that implements the crossover. Parameters ---------- problem: class The problem to be solved. Provides information such as lower and upper bounds or feasibility conditions for custom crossovers. pop : Population The population as an object parents: numpy.array The select parents of the population for the crossover kwargs : dict Any additional data that might be necessary to perform the crossover. E.g. constants of an algorithm. Returns ------- offsprings : Population The off as a matrix. n_children rows and the number of columns is equal to the variable length of the problem. """ if self.n_parents != parents.shape[1]: raise ValueError( 'Exception during crossover: Number of parents differs from defined at crossover.' ) # get the design space matrix form the population and parents X = pop.get("X")[parents.T].copy() # now apply the crossover probability do_crossover = np.random.random(len(parents)) < self.prob # execute the crossover _X = self._do(problem, X, **kwargs) X[:, do_crossover, :] = _X[:, do_crossover, :] # flatten the array to become a 2d-array X = X.reshape(-1, X.shape[-1]) # create a population object off = Population.new("X", X) return off
def _infill(self): problem, particles, pbest = self.problem, self.particles, self.pop (X, V) = particles.get("X", "V") P_X = pbest.get("X") sbest = self._social_best() S_X = sbest.get("X") Xp, Vp = pso_equation(X, P_X, S_X, V, self.V_max, self.w, self.c1, self.c2) # if the problem has boundaries to be considered if problem.has_bounds(): for k in range(20): # find the individuals which are still infeasible m = is_out_of_bounds_by_problem(problem, Xp) # actually execute the differential equation Xp[m], Vp[m] = pso_equation(X[m], P_X[m], S_X[m], V[m], self.V_max, self.w, self.c1, self.c2) # if still infeasible do a random initialization Xp = repair_random_init(Xp, X, *problem.bounds()) # create the offspring population off = Population.new(X=Xp, V=Vp) # try to improve the current best with a pertubation if self.pertube_best: k = FitnessSurvival().do(problem, pbest, n_survive=1, return_indices=True)[0] eta = int(np.random.uniform(20, 30)) mutant = PolynomialMutation(eta).do(problem, pbest[[k]])[0] off[k].set("X", mutant.X) self.repair.do(problem, off) self.sbest = sbest.copy() return off
def do(self, problem, n_samples, **kwargs): # provide a whole population object - (individuals might be already evaluated) if isinstance(self.sampling, Population): pop = self.sampling else: if isinstance(self.sampling, np.ndarray): pop = Population.new(X=self.sampling) else: pop = self.sampling.do(problem, n_samples, **kwargs) # repair all solutions that are not already evaluated not_eval_yet = [k for k in range(len(pop)) if pop[k].F is None] if len(not_eval_yet) > 0: pop[not_eval_yet] = self.repair.do(problem, pop[not_eval_yet], **kwargs) # filter duplicate in the population pop = self.eliminate_duplicates.do(pop) return pop
def pop_from_sampling(problem, sampling, n_initial_samples, pop=None): # the population type can be different - (different type of individuals) if pop is None: pop = Population() # provide a whole population object - (individuals might be already evaluated) if isinstance(sampling, Population): pop = sampling else: # if just an X array create a pop if isinstance(sampling, np.ndarray): pop = pop.new("X", sampling) elif isinstance(sampling, Sampling): # use the sampling pop = sampling.do(problem, n_initial_samples, pop=pop) else: return None return pop
def mutation(mutation, X, xl=0, xu=1, type_var=np.double, **kwargs): problem = get_problem_func(X.shape[1], xl, xu, type_var)(**kwargs) return mutation.do(problem, Population.new("X", X), **kwargs).get("X")
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 test_evaluate_pop(): evaluator = Evaluator() pop = Population.new("X", X) evaluator.eval(problem, pop) np.testing.assert_allclose(F, pop.get("F")) assert evaluator.n_eval == len(X)
def _local_infill(self): X = self.norm.backward(np.array(self.es.ask())) return Population.new("X", X)