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 random_permuations(n, l): from pymoo.rand import random perms = [] for i in range(n): perms.append(random.perm(l)) P = np.concatenate(perms) return P
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 # start point of crossover r = np.row_stack([random.perm(n_var-1) + 1 for _ in range(n_matings)])[:, :self.n_points] r.sort(axis=1) r = np.column_stack([r, np.full(n_matings, n_var)]) # the mask do to the crossover M = np.full((n_matings, n_var), False) # create for each individual the crossover range for i in range(n_matings): j = 0 while j < r.shape[1] - 1: a, b = r[i, j], r[i, j + 1] M[i, a:b] = True j += 2 _X = crossover_mask(X, M) return pop.new("X", _X)
def next(self, n_selected): """ Parameters ---------- n_selected: int Number of individuals to be selected. Returns ------- v: vector Selected indices of individuals as a integer vector. """ selected = np.zeros(n_selected, dtype=np.int) for i in range(n_selected): if self.counter + self.pressure >= self.pop.size(): self.perm = random.perm(self.pop.size()) self.counter = 0 selected[i] = self.f_comp( self.pop, self.perm[self.counter:self.counter + self.pressure], self.data) self.counter = self.counter + self.pressure return selected
def _mating(self, pop): # the population object to be used off = pop.new() # mating counter - counts how often the mating needs to be done to fill up n_offsprings n_matings = 0 # iterate until enough offsprings are created while len(off) < self.n_offsprings: # how many parents need to be select for the mating - depending on number of offsprings remaining n_select = math.ceil( (self.n_offsprings - len(off)) / self.crossover.n_offsprings) # select the parents for the mating - just an index array parents = self.selection.do(pop, n_select, self.crossover.n_parents, algorithm=self) # do the crossover using the parents index and the population - additional data provided if necessary _off = self.crossover.do(self.problem, pop, parents, algorithm=self) # do the mutation on the offsprings created through crossover _off = self.mutation.do(self.problem, _off, algorithm=self) # repair the individuals if necessary if self.func_repair is not None: _off = self.func_repair(self.problem, _off, algorithm=self) if self.eliminate_duplicates: is_duplicate = self.eliminate_duplicates(_off, pop, off, algorithm=self) _off = _off[np.logical_not(is_duplicate)] # if more offsprings than necessary - truncate them if len(_off) > self.n_offsprings - len(off): I = random.perm(self.n_offsprings - len(off)) _off = _off[I] # add to the offsprings and increase the mating counter off = off.merge(_off) n_matings += 1 # if no new offsprings can be generated within 100 trails -> return the current result if n_matings > 100: print( "WARNING: Recombination could not produce new offsprings which are not already in the population!" ) break return off
def _next(self, pop): # get the vectors from the population F, CV, feasible = pop.get("F", "CV", "feasible") F = parameter_less(F, CV) # create offsprings and add it to the data of the algorithm if self.var_selection == "rand": P = self.selection.do(pop, self.pop_size, self.crossover.n_parents) elif self.var_selection == "best": best = np.argmin(F[:, 0]) P = self.selection.do(pop, self.pop_size, self.crossover.n_parents - 1) P = np.column_stack([np.full(len(pop), best), P]) elif self.var_selection == "rand+best": best = np.argmin(F[:, 0]) P = self.selection.do(pop, self.pop_size, self.crossover.n_parents) use_best = random.random(len(pop)) < 0.3 P[use_best, 0] = best else: raise Exception("Unknown selection: %s" % self.var_selection) self.off = self.crossover.do(self.problem, pop, P) # do the mutation by using the offsprings self.off = self.mutation.do(self.problem, self.off, algorithm=self) # bring back to bounds if violated through crossover - bounce back strategy X = self.off.get("X") xl = np.repeat(self.problem.xl[None, :], X.shape[0], axis=0) xu = np.repeat(self.problem.xu[None, :], X.shape[0], axis=0) # otherwise bounds back into the feasible space X[X < xl] = (xl + (xl - X))[X < xl] X[X > xu] = (xu - (X - xu))[X > xu] self.off.set("X", X) # evaluate the results self.evaluator.eval(self.problem, self.off, algorithm=self) _F, _CV, _feasible = self.off.get("F", "CV", "feasible") _F = parameter_less(_F, _CV) # find the individuals which are indeed better is_better = np.where((_F <= F)[:, 0])[0] # truncate the replacements if desired if self.n_replace is not None and self.n_replace < len(is_better): is_better = is_better[random.perm(len(is_better))[:self.n_replace]] # replace the individuals in the population pop[is_better] = self.off[is_better] return pop
def next(self, n_selected): if self.perm is None or self.counter + n_selected >= self.pop.size(): self.perm = random.perm(self.pop.size()) self.counter = 0 selected = self.perm[self.counter:self.counter + n_selected] self.counter = self.counter + n_selected return selected
def niching(pop, n_remaining, niche_count, niche_of_individuals, dist_to_niche): survivors = [] # boolean array of elements that are considered for each iteration mask = np.full(len(pop), True) while len(survivors) < n_remaining: # number of individuals to select in this iteration n_select = n_remaining - len(survivors) # all niches where new individuals can be assigned to and the corresponding niche count next_niches_list = np.unique(niche_of_individuals[mask]) next_niche_count = niche_count[next_niches_list] # the minimum niche count min_niche_count = next_niche_count.min() # all niches with the minimum niche count (truncate if randomly if more niches than remaining individuals) next_niches = next_niches_list[np.where( next_niche_count == min_niche_count)[0]] next_niches = next_niches[random.perm(len(next_niches))[:n_select]] for next_niche in next_niches: # 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])] is_closest = True else: # already randomized through shuffling next_ind = next_ind[0] is_closest = False # add the selected individual to the survivors mask[next_ind] = False pop[next_ind].data["closest"] = is_closest survivors.append(int(next_ind)) # increase the corresponding niche count niche_count[next_niche] += 1 return survivors
def set_population(self, pop, data): """ Parameters ---------- pop: class The population to be selected from. data: class Any additional data that might be needed for the selection. """ self.pop = pop self.data = data self.perm = random.perm(self.pop.size()) self.counter = 0
def randomized_argsort(A, method="numpy", order='ascending'): if method == "numpy": P = random.perm(len(A)) I = np.argsort(A[P], kind='quicksort') I = P[I] elif method == "quicksort": I = quicksort(A) else: raise Exception("Randomized sort method not known.") if order == 'ascending': return I elif order == 'descending': return np.flip(I, axis=0) else: raise Exception("Unknown sorting order: ascending or descending.")
def _next(self, pop): # iterate for each member of the population for i in range(self.pop_size): # all neighbors shuffled (excluding the individual itself) neighbors = self.neighbours[i][1:][random.perm(self.n_neighbors - 1)] parents = np.concatenate([[i], neighbors[:self.crossover.n_parents - 1] ]) # do recombination and create an offspring X = self.crossover.do(self.problem, pop.X[None, parents, :], X=pop.X[[i], :]) X = self.mutation.do(self.problem, X) # evaluate the offspring F, _ = self.evaluator.eval(self.problem, X) # update the ideal point self.ideal_point = np.min(np.concatenate( [self.ideal_point[None, :], F], axis=0), axis=0) # for each offspring that was created for k in range(self.crossover.n_children): # the weights of each neighbor weights_of_neighbors = self.weights[self.neighbours[i], :] # calculate the decomposed values for each neighbour FV = tchebi(pop.F[self.neighbours[i]], weights_of_neighbors, self.ideal_point) off_FV = tchebi(F[[k], :], weights_of_neighbors, self.ideal_point) # get the absolute index in F where offspring is better than the current F (decomposed space) off_is_better = self.neighbours[i][np.where(off_FV < FV)[0]] pop.F[off_is_better, :] = F[k, :] pop.X[off_is_better, :] = X[k, :]
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 # start point of crossover r = np.row_stack([ random.perm(n_var - 1) + 1 for _ in range(n_matings) ])[:, :self.n_points] r.sort(axis=1) r = np.column_stack([r, np.full(n_matings, n_var)]) # genome defines a gene as (op, source), so we need to crossover on even values only # otherwise, we get op from 1 parent and source from the other r = r // 2 * 2 # the mask do to the crossover M = np.full((n_matings, n_var), False) # create for each individual the crossover range for i in range(n_matings): j = 0 while j < r.shape[1] - 1: a, b = r[i, j], r[i, j + 1] M[i, a:b] = True j += 2 _X = crossover_mask(X, M) new_pop = pop.new("X", _X) # print(new_pop) for i, ind in enumerate(new_pop): ind.parents = pop.get("id")[parents][i // 2] # for ind in new_pop: # print(ind.parents) return new_pop
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) not_equal = X[0] != X[1] # create for each individual the crossover range for i in range(n_matings): I = np.where(not_equal[i])[0] n = math.ceil(len(I) / 2) if n > 0: _I = I[random.perm(len(I))[:n]] M[i, _I] = True _X = crossover_mask(X, M) return pop.new("X", _X)
def random_permuations(n, l): perms = [] for i in range(n): perms.append(random.perm(size=l)) P = np.concatenate(perms) return P
def _mating(self, pop): # adjusted mating to preserve heritage # the population object to be used off = pop.new() # mating counter - counts how often the mating needs to be done to fill up n_offsprings n_matings = 0 # iterate until enough offsprings are created while len(off) < self.n_offsprings: # # how many parents need to be select for the mating - depending on number of offsprings remaining # n_select = math.ceil((self.n_offsprings - len(off)) / self.crossover.n_offsprings) # set to one to simplify sources of error. 1 means two parents n_select = 1 # select the parents for the mating - just an index array parents = self.selection.do(pop, n_select, self.crossover.n_parents, algorithm=self) # do the crossover using the parents index and the population - additional data provided if necessary _off = self.crossover.do(self.problem, pop, parents, algorithm=self) # do the mutation on the offsprings created through crossover muta_off = self.mutation.do(self.problem, _off, algorithm=self) for i, ind in enumerate(_off): mutation_map = ind.X == muta_off[i].X ind.X = muta_off[i].X ind.mutation_map = mutation_map # repair the individuals if necessary if self.repair: _off = self.repair.do(self.problem, _off, algorithm=self) if self.eliminate_duplicates: is_duplicate = self.eliminate_duplicates(_off, pop, off, algorithm=self) _off = _off[np.logical_not(is_duplicate)] # if more offsprings than necessary - truncate them if len(_off) > self.n_offsprings - len(off): I = random.perm(self.n_offsprings - len(off)) _off = _off[I] off = off.merge(_off) n_matings += 1 # if no new offsprings can be generated within 100 trails -> return the current result if n_matings > 100: print( "WARNING: Recombination could not produce new offsprings which are not already in the population!" ) break for ind in off: assert hasattr(ind, "parents") assert hasattr(ind, "mutation_map") return off