def do(self, F, weights, _type="auto", ideal_point=None, utopian_point=None, nadir_point=None, **kwargs): _F, _weights = to_1d_array_if_possible(F), to_1d_array_if_possible(weights) if _type == "auto": if _F.ndim == 1 and _weights.ndim > 1: _type = "one_to_many" elif _F.ndim > 1 and _weights.ndim == 1: _type = "many_to_one" elif _F.ndim == 2 and _weights.ndim == 2 and _F.shape[0] == _weights.shape[0]: _type = "one_to_one" else: _type = "many_to_many" # make both at least 2d arrays F, weights = at_least_2d_array(F), at_least_2d_array(weights) # get the number of points and weights n_points, n_weights = F.shape[0], weights.shape[0] self.ideal_point = ideal_point if self.ideal_point is None: self.ideal_point = np.zeros(F.shape[1]) self.utopian_point = utopian_point if self.utopian_point is None: self.utopian_point = self.ideal_point - self.eps # set the nadir point by default to value or default self.nadir_point = nadir_point if self.nadir_point is None: self.nadir_point = self.utopian_point + np.ones(F.shape[1]) if _type == "one_to_one": D = self._do(F, weights=weights, **kwargs).flatten() elif _type == "one_to_many": F = np.repeat(F, n_weights, axis=0) D = self._do(F, weights=weights, **kwargs).flatten() elif _type == "many_to_one": weights = np.repeat(weights, n_points, axis=0) D = self._do(F, weights=weights, **kwargs).flatten() elif _type == "many_to_many": F = np.repeat(F, n_weights, axis=0) weights = np.tile(weights, (n_points, 1)) D = self._do(F, weights=weights, **kwargs).reshape(n_points, n_weights) else: raise Exception("Unknown type for decomposition: %s" % _type) return D
def _evaluate(self, x, out, *args, **kwargs): super()._evaluate(x, out, return_as_dictionary=True) F, G = at_least_2d_array(out["F"]), at_least_2d_array(out["G"]) CV = Problem.calc_constraint_violation(G) out["__F__"] = F out["__G__"] = G out["__CV__"] = CV out["F"] = out["F"] + self.penalty * CV out["G"] = None
def do(self, x, out, *args, **kwargs): self.problem.do(x, out, *args, **kwargs) if self.problem.has_constraints(): F, G = at_least_2d_array(out["F"]), at_least_2d_array(out["G"]) CV = Problem.calc_constraint_violation(G) out["__F__"] = F out["__G__"] = G out["__CV__"] = CV out["F"] = F + self.penalty * CV out["G"] = None
def pareto_front(self, *args, use_cache=True, exception_if_failing=True, **kwargs): """ Parameters ---------- args : Same problem implementation need some more information to create the Pareto front. For instance the DTLZ problem suite generates the Pareto front by usage of the reference directions. We refer to the corresponding problem for more information. exception_if_failing : bool Whether to throw an exception when generating the Pareto front has failed. use_cache : bool Whether to use the cache if the Pareto front has been generated beforehand. Returns ------- P : np.array The Pareto front of a given problem. It is only loaded or calculate the first time and then cached. For a single-objective problem only one point is returned but still in a two dimensional array. """ if not use_cache or self._pareto_front is None: try: self._pareto_front = at_least_2d_array( self._calc_pareto_front(*args, **kwargs)) self._ideal_point = np.min(self._pareto_front, axis=0) self._nadir_point = np.max(self._pareto_front, axis=0) except Exception as e: if exception_if_failing: raise e return self._pareto_front
def evaluate(self, X, *args, return_values_of=None, return_as_dictionary=False, **kwargs): # make sure the array is at least 2d. store if reshaping was necessary X, only_single_value = at_least_2d_array(X, extend_as="row", return_if_reshaped=True) assert X.shape[1] == self.n_var, f'Input dimension {X.shape[1]} are not equal to n_var {self.n_var}!' # number of function evaluations to be done n_evals = X.shape[0] # the values to be actually returned by in the end - set bu default if not providded ret_vals = default_return_values(self.has_constraints()) if return_values_of is None else return_values_of # prepare the dictionary to be filled after the evaluation out = dict_with_none(ret_vals) # do the actual evaluation for the given problem - calls in _evaluate method internally self.do(X, out, *args, **kwargs) # make sure the array is 2d before doing the shape check out_to_2d_ndarray(out) # if enabled (recommended) the output shapes are checked for inconsistencies if self.check_inconsistencies: check(self, X, out) # if the NaN values should be replaced if self.replace_nan_values_by is not None: replace_nan_values(out, self.replace_nan_values_by) # make sure F and G are in fact floats (at least try to do that, no exception will be through if it fails) out_to_float(out, ["F", "G"]) if "CV" in ret_vals or "feasible" in ret_vals: CV = calc_constr(out["G"]) if self.has_constraints() else np.zeros([n_evals, 1]) out["CV"] = CV out["feasible"] = CV <= 0 # in case the input had only one dimension, then remove always the first dimension from each output if only_single_value: out_to_1d_ndarray(out) if self.callback is not None: self.callback(X, out) # now depending on what should be returned prepare the output if return_as_dictionary: return out else: if len(ret_vals) == 1: return out[ret_vals[0]] else: return tuple([out[e] for e in ret_vals])
def set_cv(pop, feasbility=True): for ind in pop: if ind.G is None: ind.CV = np.zeros(1) else: ind.CV = Problem.calc_constraint_violation(at_least_2d_array( ind.G))[0] if feasbility: set_feasibility(pop)
def pareto_set(self, *args, use_cache=True, **kwargs): """ Returns ------- S : np.array Returns the pareto set for a problem. Points in the X space to be known to be optimal! """ if not use_cache or self._pareto_set is None: self._pareto_set = at_least_2d_array(self._calc_pareto_set(*args, **kwargs)) return self._pareto_set
def __init__(self, pf, dist_func, axis, zero_to_one=False, ideal=None, nadir=None, norm_by_dist=False, **kwargs): # the pareto front if necessary to calculate the indicator pf = at_least_2d_array(pf, extend_as="row") ideal, nadir = derive_ideal_and_nadir_from_pf(pf, ideal=ideal, nadir=nadir) super().__init__(zero_to_one=zero_to_one, ideal=ideal, nadir=nadir, **kwargs) self.dist_func = dist_func self.axis = axis self.norm_by_dist = norm_by_dist self.pf = self.normalization.forward(pf)
def set_to_bounds_if_outside(X, xl, xu): _X, only_1d = at_least_2d_array(X, return_if_reshaped=True) if xl is not None: xl = np.repeat(xl[None, :], _X.shape[0], axis=0) _X[_X < xl] = xl[_X < xl] if xu is not None: xu = np.repeat(xu[None, :], _X.shape[0], axis=0) _X[_X > xu] = xu[_X > xu] if only_1d: return _X[0, :] else: return _X
def bounce_back(X, xl, xu): only_1d = (X.ndim == 1) X = at_least_2d_array(X) xl = np.repeat(xl[None, :], X.shape[0], axis=0) xu = np.repeat(xu[None, :], X.shape[0], axis=0) # otherwise bounds back into the feasible space _range = xu - xl X[X < xl] = (xl + np.mod((xl - X), _range))[X < xl] X[X > xu] = (xu - np.mod((X - xu), _range))[X > xu] if only_1d: return X[0, :] else: return X
def set_to_bounds_if_outside(X, xl, xu): only_1d = (X.ndim == 1) X = at_least_2d_array(X) if xl is not None: xl = np.repeat(xl[None, :], X.shape[0], axis=0) X[X < xl] = xl[X < xl] if xu is not None: xu = np.repeat(xu[None, :], X.shape[0], axis=0) X[X > xu] = xu[X > xu] if only_1d: return X[0, :] else: return X
def repair_out_of_bounds(problem, X): only_1d = (X.ndim == 1) X = at_least_2d_array(X) if problem.xl is not None: xl = np.repeat(problem.xl[None, :], X.shape[0], axis=0) X[X < xl] = xl[X < xl] if problem.xu is not None: xu = np.repeat(problem.xu[None, :], X.shape[0], axis=0) X[X > xu] = xu[X > xu] if only_1d: return X[0, :] else: return X
def bounds_back(problem, X): only_1d = (X.ndim == 1) X = at_least_2d_array(X) if problem.xl is not None and problem.xu is not None: xl = np.repeat(problem.xl[None, :], X.shape[0], axis=0) xu = np.repeat(problem.xu[None, :], X.shape[0], axis=0) # otherwise bounds back into the feasible space _range = xu - xl X[X < xl] = (xl + np.mod((xl - X), _range))[X < xl] X[X > xu] = (xu - np.mod((X - xu), _range))[X > xu] if only_1d: return X[0, :] else: return X
def __init__(self, ref_point=None, pf=None, nds=True, norm_ref_point=True, ideal=None, nadir=None, **kwargs): pf = at_least_2d_array(pf, extend_as="row") ideal, nadir = derive_ideal_and_nadir_from_pf(pf, ideal=ideal, nadir=nadir) super().__init__(ideal=ideal, nadir=nadir, **kwargs) # whether the input should be checked for domination or not self.nds = nds # the reference point that shall be used - either derived from pf or provided ref_point = ref_point if ref_point is None: if pf is not None: ref_point = pf.max(axis=0) # we also have to normalize the reference point to have the same scales if norm_ref_point: ref_point = self.normalization.forward(ref_point) self.ref_point = ref_point assert self.ref_point is not None, "For Hypervolume a reference point needs to be provided!"
def do(self, X, out, *args, **kwargs): # do an elementwise evaluation and return the results ret = self.func_eval(self.func_elementwise_eval, self, X, out, *args, **kwargs) # the first element decides what keys will be set keys = list(ret[0].keys()) # now stack all the results for each of them together for key in keys: assert all([key in _out for _out in ret]), f"For some elements the {key} value has not been set." vals = [] for elem in ret: val = elem[key] if val is not None: # if it is just a float if isinstance(val, list) or isinstance(val, tuple): val = np.array(val) elif not isinstance(val, np.ndarray): val = np.full(1, val) # otherwise prepare the value to be stacked with each other by extending the dimension val = at_least_2d_array(val, extend_as="row") vals.append(val) # that means the key has never been set at all if all([val is None for val in vals]): out[key] = None else: out[key] = np.row_stack(vals) return out
def calc_pf(problem, *args, **kwargs): return at_least_2d_array(problem._calc_pareto_front(*args, **kwargs))
def _forward(self, ind, **kwargs): assert self.attr is not None and isinstance(ind, Individual) return at_least_2d_array(ind.get(self.attr), extend_as="row")