def payoff_table_method( problem: MOProblem, initial_guess: Optional[np.ndarray] = None, solver_method: Optional[Union[ScalarMethod, str]] = "scipy_de", ) -> Tuple[np.ndarray, np.ndarray]: """Uses the payoff table method to solve for the ideal and nadir points of a MOProblem. Call through to payoff_table_method_general. Args: problem (MOProblem): The problem defined as a MOProblem class instance. initial_guess (Optional[np.ndarray]): The initial guess of decision variables to be used in the solver. If None, uses the lower bounds defined for the variables in MOProblem. Defaults to None. solver_method (Optional[Union[ScalarMethod, str]]): The method used to minimize the invidual problems in the payoff table method. Defaults to 'scipy_de'. Returns: Tuple[np.ndarray, np.ndarray]: The ideal and nadir points """ if problem.n_of_constraints > 0: constraints = lambda x: problem.evaluate(x).constraints.squeeze() else: constraints = None return payoff_table_method_general( lambda xs: problem.evaluate(xs).objectives, problem.n_of_objectives, problem.get_variable_bounds(), constraints, initial_guess, solver_method, )
def solve_pareto_front_representation( problem: MOProblem, step: Optional[Union[np.ndarray, float]] = 0.1, eps: Optional[float] = 1e-6, solver_method: Optional[Union[ScalarMethod, str]] = "scipy_de", ) -> Tuple[np.ndarray, np.ndarray]: """Pass through to solve_pareto_front_representation_general when the problem for which the front is being calculated for is defined as an MOProblem object. Computes a representation of a Pareto efficient front from a multiobjective minimizatino problem. Does so by generating an evenly spaced set of reference points (in the objective space), in the space spanned by the supplied ideal and nadir points. The generated reference points are then used to formulate achievement scalaraization problems, which when solved, yield a representation of a Pareto efficient solution. Args: problem (MOProblem): The multiobjective minimization problem for which the front is to be solved for. step (Optional[Union[np.ndarray, float]], optional): Either a float or an array of floats. If a single float is given, generates reference points with the objectives having values a step apart between the ideal and nadir points. If an array of floats is given, use the steps defined in the array for each objective's values. Default to 0.1. eps (Optional[float], optional): An offset to be added to the nadir value to keep the nadir inside the range when generating reference points. Defaults to 1e-6. solver_method (Optional[Union[ScalarMethod, str]], optional): The method used to minimize the achievement scalarization problems arising when calculating Pareto efficient solutions. Defaults to "scipy_de". Returns: Tuple[np.ndarray, np.ndarray]: A tuple containing representations of the Pareto optimal variable values, and the corresponsing objective values. """ if problem.n_of_constraints > 0: constraints = lambda x: problem.evaluate(x).constraints.squeeze() else: constraints = None var_values, obj_values = solve_pareto_front_representation_general( lambda x: problem.evaluate(x).objectives, problem.n_of_objectives, problem.get_variable_bounds(), step, eps, problem.ideal * problem._max_multiplier, problem.nadir * problem._max_multiplier, constraints, solver_method, ) return var_values, obj_values * problem._max_multiplier
def __init__( # parameters of the class self, problem: MOProblem, scalarization_function: MOEADSF = Tchebycheff(), n_neighbors: int = 20, population_params: Dict = None, initial_population: Population = None, lattice_resolution: int = None, use_repair: bool = True, n_parents: int = 2, a_priori: bool = False, interact: bool = False, use_surrogates: bool = False, n_iterations: int = 10, n_gen_per_iter: int = 100, total_function_evaluations: int = 0, ): super().__init__( # parameters for decomposition based approach problem=problem, population_size=None, population_params=population_params, initial_population=initial_population, lattice_resolution=lattice_resolution, a_priori=a_priori, interact=interact, use_surrogates=use_surrogates, n_iterations=n_iterations, n_gen_per_iter=n_gen_per_iter, total_function_evaluations=total_function_evaluations, ) self.population_size = self.population.pop_size self.problem = problem self.scalarization_function = scalarization_function self.n_neighbors = n_neighbors self.use_repair = use_repair self.n_parents = n_parents self.population.mutation = BP_mutation( problem.get_variable_lower_bounds(), problem.get_variable_upper_bounds(), 0.5, 20, ) self.population.recombination = SBX_xover(1.0, 20) selection_operator = MOEAD_select(self.population, SF_type=self.scalarization_function) self.selection_operator = selection_operator # Compute the distance between each pair of reference vectors distance_matrix_vectors = distance_matrix( self.reference_vectors.values, self.reference_vectors.values) # Get the closest vectors to obtain the neighborhoods self.neighborhoods = np.argsort(distance_matrix_vectors, axis=1, kind="quicksort")[:, :n_neighbors] self.population.update_ideal() self._ideal_point = self.population.ideal_objective_vector
def __init__(self, problem: MOProblem, scalar_method: Optional[ScalarMethod] = None): # check if ideal and nadir are defined if problem.ideal is None or problem.nadir is None: # TODO: use same method as defined in scalar_method ideal, nadir = payoff_table_method(problem) self._ideal = ideal self._nadir = nadir else: self._ideal = problem.ideal self._nadir = problem.nadir self._scalar_method = scalar_method # generate Pareto optimal starting point asf = SimpleASF(np.ones(self._ideal.shape)) scalarizer = Scalarizer( lambda x: problem.evaluate(x).objectives, asf, scalarizer_args={"reference_point": np.atleast_2d(self._ideal)}, ) if problem.n_of_constraints > 0: _con_eval = lambda x: problem.evaluate(x).constraints.squeeze() else: _con_eval = None solver = ScalarMinimizer( scalarizer, problem.get_variable_bounds(), constraint_evaluator=_con_eval, method=self._scalar_method, ) # TODO: fix tools to check for scipy methods in general and delete me! solver._use_scipy = True res = solver.minimize(problem.get_variable_upper_bounds() / 2) if res["success"]: self._current_solution = res["x"] self._current_objectives = problem.evaluate( self._current_solution).objectives.squeeze() self._archive_solutions = [] self._archive_objectives = [] self._state = "classify" super().__init__(problem)
print(f"Loop {counter} of {total_count}") counter += 1 # build problem var_names = ["x" + str(i + 1) for i in range(n_var)] obj_names = ["f" + str(i + 1) for i in range(n_obj)] prob = wfg_problem( name=problem_name, n_of_objectives=n_obj, n_of_variables=n_var ) variables = variable_builder( names=var_names, initial_values=prob.lower, lower_bounds=prob.lower, upper_bounds=prob.upper, ) objective = VectorObjective(name=obj_names, evaluator=prob.eval) problem = MOProblem([objective], variables, None) problem.ideal = prob.ideal problem.nadir = ( abs(np.random.normal(size=n_obj, scale=0.15)) + 1 ) * prob.nadir true_nadir = prob.nadir scalar = asf(ideal=problem.ideal, nadir=true_nadir) # a posteriori a_post_rvea = RVEA( problem=problem, interact=False, n_gen_per_iter=gen, n_iterations=4 ) a_post_nsga = NSGAIII( problem=problem, interact=False, n_gen_per_iter=gen, n_iterations=4
objkaikki = VectorObjective("obj", objectives) # variables var_names = ["x1", "x2" ] # Make sure that the variable names are meaningful to you. initial_values = np.array([0.5, 0.5]) lower_bounds = [0.3, 0.3] upper_bounds = [1.0, 1.0] bounds = np.stack((lower_bounds, upper_bounds)) variables = variable_builder(var_names, initial_values, lower_bounds, upper_bounds) # problem prob = MOProblem(objectives=[obj1, obj2, obj3, obj4], variables=variables) # objectives "seperately" # solved in Nautilus.py ideal = np.array([-6.34, -3.44487179, -7.5, 0]) nadir = np.array([-4.751, -2.86054116, -0.32111111, 9.70666666]) # start solving method = ReferencePointMethod(problem=prob, ideal=ideal, nadir=nadir) # Pareto optimal solution: [-6.30, -3.26, -2.60, 3.63] print("Let's start solving\n") req = method.start() rp = np.array([-5.0, -3.0, -3.0, 5.0]) req.response = { "reference_point": rp,
return (x[0] + x[1]) - 0.5 f1 = _ScalarObjective(name="f1", evaluator=f_1) f2 = _ScalarObjective(name="f2", evaluator=f_2) f3 = _ScalarObjective(name="f3", evaluator=f_3) f4 = _ScalarObjective(name="f4", evaluator=f_4) f5 = _ScalarObjective(name="f5", evaluator=f_5) varsl = variable_builder( ["x_1", "x_2"], initial_values=[0.5, 0.5], lower_bounds=[0.3, 0.3], upper_bounds=[1.0, 1.0], ) c1 = ScalarConstraint("c1", 2, 5, evaluator=c_1) problem = MOProblem(variables=varsl, objectives=[f1, f2, f3, f4, f5], constraints=[c1]) method = NIMBUS(problem, scalar_method="scipy_de") reqs = method.request_classification()[0] response = {} response["classifications"] = ["<", "<=", "=", ">=", "0"] response["levels"] = [-6, -3, -5, 8, 0.349] response["number_of_solutions"] = 3 reqs.response = response res_1 = method.iterate(reqs)[0] res_1.response = {"indices": []} res_2 = method.iterate(res_1)[0] response = {}
def test_problem_builder(name: str, n_of_variables: int = None, n_of_objectives: int = None) -> MOProblem: """Build test problems. Currently supported: ZDT1-4, ZDT6, and DTLZ1-7. Args: name (str): Name of the problem in all caps. For example: "ZDT1", "DTLZ4", etc. n_of_variables (int, optional): Number of variables. Required for DTLZ problems, but can be skipped for ZDT problems as they only support one variable value. n_of_objectives (int, optional): Required for DTLZ problems, but can be skipped for ZDT problems as they only support one variable value. Raises: ProblemError: When one of many issues occur while building the MOProblem instance. Returns: MOProblem: The test problem object """ problems = { "ZDT1": zdt.ZDT1, "ZDT2": zdt.ZDT2, "ZDT3": zdt.ZDT3, "ZDT4": zdt.ZDT4, "ZDT5": zdt.ZDT5, "ZDT6": zdt.ZDT6, "DTLZ1": dtlz.DTLZ1, "DTLZ2": dtlz.DTLZ2, "DTLZ3": dtlz.DTLZ3, "DTLZ4": dtlz.DTLZ4, "DTLZ5": dtlz.DTLZ5, "DTLZ6": dtlz.DTLZ6, "DTLZ7": dtlz.DTLZ7, } num_var = {"ZDT1": 30, "ZDT2": 30, "ZDT3": 30, "ZDT4": 10, "ZDT6": 10} if not (name in problems.keys()): msg = ( "Specified Problem not yet supported.\n The supported problems are:" + str(problems.keys())) raise ProblemError(msg) if "ZDT" in name: if n_of_variables is None: n_of_variables = num_var[name] if n_of_objectives is None: n_of_objectives = 2 if not (n_of_variables == num_var[name]): msg = (name + " problem has been limited to " + str(num_var[name]) + " variables. Number of variables recieved = " + str(n_of_variables)) raise ProblemError(msg) if not (n_of_objectives == 2): msg = ("ZDT problems can only have 2 objectives. " + "Number of objectives recieved = " + str(n_of_objectives)) raise ProblemError(msg) obj_func = problems[name]() elif "DTLZ" in name: if (n_of_variables is None) or (n_of_objectives is None): msg = ("Please provide both number of variables and objectives" + " for the DTLZ problems") raise ProblemError(msg) obj_func = problems[name](n_of_objectives, n_of_variables) else: msg = "How did you end up here?" raise ProblemError(msg) lower_limits = obj_func.min_bounds upper_limits = obj_func.max_bounds var_names = ["x" + str(i + 1) for i in range(n_of_variables)] obj_names = ["f" + str(i + 1) for i in range(n_of_objectives)] variables = variable_builder( names=var_names, initial_values=lower_limits, lower_bounds=lower_limits, upper_bounds=upper_limits, ) # Because optproblems can only handle one objective at a time def modified_obj_func(x): if isinstance(x, list): if len(x) == n_of_variables: return [obj_func(x)] elif len(x[0]) == n_of_variables: return list(map(obj_func, x)) else: if x.ndim == 1: return [obj_func(x)] elif x.ndim == 2: return list(map(obj_func, x)) raise TypeError("Unforseen problem, contact developer") objective = VectorObjective(name=obj_names, evaluator=modified_obj_func) problem = MOProblem([objective], variables, None) return problem
def __init__( self, problem: MOProblem, ideal: np.ndarray, nadir: np.ndarray, epsilon: float = 1e-6, objective_names: Optional[List[str]] = None, minimize: Optional[List[int]] = None, ): if not ideal.shape == nadir.shape: raise NautilusException( "The dimensions of the ideal and nadir point do not match.") if objective_names: if not len(objective_names) == ideal.shape[0]: raise NautilusException( "The supplied objective names must have a length equal to " "the numbr of objectives.") self._objective_names = objective_names else: self._objective_names = [ f"f{i + 1}" for i in range(ideal.shape[0]) ] if minimize: if not len(objective_names) == ideal.shape[0]: raise NautilusException( "The minimize list must have " "as many elements as there are objectives.") self._minimize = minimize else: self._minimize = [1 for _ in range(ideal.shape[0])] # initialize method with problem super().__init__(problem) self._problem = problem self._objectives: Callable = lambda x: self._problem.evaluate( x).objectives self._variable_bounds: Union[np.ndarray, None] = problem.get_variable_bounds() self._constraints: Optional[ Callable] = lambda x: self._problem.evaluate(x).constraints # Used to calculate the utopian point from the ideal point self._epsilon = epsilon self._ideal = ideal self._nadir = nadir # calculate utopian vector self._utopian = [ideal_i - self._epsilon for ideal_i in self._ideal] # bounds of the reachable region self._lower_bounds: List[np.ndarray] = [] self._upper_bounds: List[np.ndarray] = [] # current iteration step number self._step_number = 1 # iteration points self._zs: np.ndarray = [] # solutions, objectives, and distances for each iteration self._xs: np.ndarray = [] self._fs: np.ndarray = [] self._ds: np.ndarray = [] # The current reference point self._q: Union[None, np.ndarray] = None # preference information self._preference_method = None self._preference_info = None self._preferential_factors = None # number of total iterations and iterations left self._n_iterations = None self._n_iterations_left = None # flags for the iteration phase # not utilized atm self._use_previous_preference: bool = False self._step_back: bool = False self._short_step: bool = False self._first_iteration: bool = True # evolutionary method for minimizing self._method_de: ScalarMethod = ScalarMethod( lambda x, _, **y: differential_evolution(x, **y), method_args={ "disp": False, "polish": False, "tol": 0.000001, "popsize": 10, "maxiter": 50000 }, use_scipy=True)
# define the variables, bounds -5 <= x_1 and x_2 <= 5 varsl = variable_builder(["x_1", "x_2", "x_3"], initial_values=[0.5, 0.5, 0.5], lower_bounds=[-5, -5, -5], upper_bounds=[5, 5, 5]) # define constraints def c_1(x, f=None): x = x.squeeze() # x_1 < 2 return x[0] + 2 # name of constraints, num variables, num objectives, evaluator c1 = ScalarConstraint("c1", len(varsl), len(objl), evaluator=c_1) problem = MOProblem(variables=varsl, objectives=objl, constraints=[c1]) max_bool = list( map(lambda x: True if x < 0 else False, problem._max_multiplier)) max_multiplier = problem._max_multiplier # define ideal and nadir ideal = max_multiplier * np.array([-15, 15, 15]) nadir = max_multiplier * np.array([15, -15, -15]) problem.ideal = ideal problem.nadir = nadir # GLOBAL global method method = NIMBUS(problem, scalar_method="scipy_de")
f1 = _ScalarObjective(name="f1", evaluator=f_1, maximize=[True]) f2 = _ScalarObjective(name="f2", evaluator=f_2, maximize=[True]) f3 = _ScalarObjective(name="f3", evaluator=f_3, maximize=[True]) f4 = _ScalarObjective(name="f4", evaluator=f_4) f5 = _ScalarObjective(name="f5", evaluator=f_5) varsl = variable_builder( ["x_1", "x_2"], initial_values=[0.5, 0.5], lower_bounds=[0.3, 0.3], upper_bounds=[1.0, 1.0], ) problem = MOProblem(variables=varsl, objectives=[f1, f2, f3, f4, f5]) evolver = RVEA(problem, interact=True, n_iterations=10, n_gen_per_iter=100) _, pref = evolver.iterate() n_iteration = 1 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.bind(("10.0.2.15", 5005)) # sock.bind(("127.0.0.1", 5005)) sock.listen(1) print("Waiting for ComVis to connect...") connection, client_addr = sock.accept() print("Connection estabilished!")
lower_bounds=[-5, -5, -5], upper_bounds=[5, 5, 5], ) f1 = _ScalarObjective(name="f1", evaluator=f_1) f2 = _ScalarObjective(name="f2", evaluator=f_2) #For AI_DM ranges are always defined as IDEAL, NADIR. f1_range = [-20, -14] f2_range = [-14, 0.5] x1 = np.linspace(min(f1_range), max(f1_range), 1000) x2 = np.linspace(min(f2_range), max(f2_range), 1000) y = np.linspace(0, 0, 1000) problem = MOProblem(variables=varsl, objectives=[f1, f2], ideal=np.array([-20, -12]), nadir=np.array([-14, 0.5])) from desdeo_mcdm.interactive.NIMBUS import NIMBUS from scipy.optimize import minimize, differential_evolution scalar_method = ScalarMethod( lambda x, _, **y: differential_evolution(x, **y), use_scipy=True, method_args={ "polish": True, "disp": True }) method = NIMBUS(problem, scalar_method)
def __init__( self, problem: MOProblem, ideal: np.ndarray, nadir: np.ndarray, epsilon: float = 1e-6, objective_names: Optional[List[str]] = None, minimize: Optional[List[int]] = None, ): if not ideal.shape == nadir.shape: raise RPMException( "The dimensions of the ideal and nadir point do not match.") if objective_names: if not len(objective_names) == ideal.shape[0]: raise RPMException( "The supplied objective names must have a leangth equal to " "the number of objectives.") self._objective_names = objective_names else: self._objective_names = [ f"f{i + 1}" for i in range(ideal.shape[0]) ] if minimize: if not len(objective_names) == ideal.shape[0]: raise RPMException("The minimize list must have " "as many elements as there are objectives.") self._minimize = minimize else: self._minimize = [1 for _ in range(ideal.shape[0])] # initialize method with problem super().__init__(problem) self._problem = problem self._objectives: Callable = lambda x: self._problem.evaluate( x).objectives self._variable_bounds: Union[np.ndarray, None] = problem.get_variable_bounds() self._constraints: Optional[ Callable] = lambda x: self._problem.evaluate(x).constraints self._ideal = ideal self._nadir = nadir self._utopian = ideal - epsilon self._n_objectives = self._ideal.shape[0] # current iteration step number self._h = 1 # solutions in decision and objective space, distances and referation points for each iteration self._xs = [None] * 10 self._fs = [None] * 10 self._ds = [None] * 10 self._qs = [None] * 10 # perturbed reference points self._pqs = [None] * 10 # additional solutions self._axs = [None] * 10 self._afs = [None] * 10 # current reference point self._q: Union[None, np.ndarray] = None # weighting vector for achievement function self._w: np.ndarray = [] # evolutionary method for minimizing self._method_de: ScalarMethod = ScalarMethod( lambda x, _, **y: differential_evolution(x, **y), method_args={ "disp": False, "polish": False, "tol": 0.000001, "popsize": 10, "maxiter": 50000 }, use_scipy=True, )
def __init__(self, problem: MOProblem, assign_type: str = "RandomDesign", pop_size=None, recombination_type=None, crossover_type="simulated_binary_crossover", mutation_type="bounded_polynomial_mutation", *args): """Initialize the population. Parameters ---------- problem : BaseProblem An object of the class Problem assign_type : str, optional Define the method of creation of population. If 'assign_type' is 'RandomDesign' the population is generated randomly. If 'assign_type' is 'LHSDesign', the population is generated via Latin Hypercube Sampling. If 'assign_type' is 'custom', the population is imported from file. If assign_type is 'empty', create blank population. 'EvoNN' and 'EvoDN2' will create neural networks or deep neural networks, respectively, for population . plotting : bool, optional (the default is True, which creates the plots) pop_size : int Population size recombination_type, crossover_type, mutation_type : str Recombination functions. If recombination_type is specified, crossover and mutation will be handled by the same function. If None, they are done separately. """ self.assign_type = assign_type self.num_var = problem.n_of_variables self.lower_limits = np.asarray(problem.get_variable_lower_bounds()) self.upper_limits = np.asarray(problem.get_variable_upper_bounds()) self.hyp = 0 self.non_dom = 0 self.pop_size = pop_size # Fix to remove the following assumptions self.recombination_funcs = { "biogp_xover": biogp_xover, "biogp_mut": biogp_mutation, "evodn2_xover_mutation": evodn2_xover_mutation, "evonn_xover_mutation": evonn_xover_mutation, "bounded_polynomial_mutation": bounded_polynomial_mutation, "simulated_binary_crossover": simulated_binary_crossover, } self.crossover_type = crossover_type self.mutation_type = mutation_type self.recombination = self.recombination_funcs.get( recombination_type, None) if recombination_type is None: self.crossover = self.recombination_funcs.get(crossover_type, None) self.mutation = self.recombination_funcs.get(mutation_type, None) self.problem = problem self.filename = (problem.name + "_" + str(problem.n_of_objectives) ) # Used for plotting self.plotting = plotting self.individuals = [] self.objectives = np.empty((0, self.problem.n_of_objectives), float) if problem.minimize is not None: self.fitness = self.objectives[:, self.problem.minimize] self.ideal_fitness = np.full((1, self.fitness.shape[1]), np.inf) self.worst_fitness = -1 * self.ideal_fitness else: self.fitness = np.empty((0, self.problem.num_of_objectives), float) self.ideal_fitness = np.full((1, self.problem.num_of_objectives), np.inf) self.worst_fitness = -1 * self.ideal_fitness self.constraint_violation = np.empty( (0, self.problem.num_of_constraints), float) self.archive = pd.DataFrame( columns=["generation", "decision_variables", "objective_values"]) if not assign_type == "empty": individuals = create_new_individuals(assign_type, problem, pop_size=self.pop_size) self.add(individuals) if self.plotting: self.figure = [] self.plot_init_()
return -0.5 + (x[:, 0] - 0.5)**2 + (x[:, 1] - 0.5)**2 list_vars = variable_builder(["x", "y"], initial_values=[0, 0], lower_bounds=[0, 0], upper_bounds=[np.pi, np.pi]) f1 = _ScalarObjective(name="f1", evaluator=f_1) f2 = _ScalarObjective(name="f2", evaluator=f_2) c1 = ScalarConstraint("c1", 2, 2, c_1) c2 = ScalarConstraint("c2", 2, 2, c_2) problem = MOProblem(variables=list_vars, objectives=[f1, f2], constraints=[c1, c2]) evolver = RVEA(problem) """ figure = animate_init_(evolver.population.objectives, filename="MOPC4.html") """ while evolver.continue_evolution(): evolver.iterate() """figure = animate_next_( evolver.population.objectives, figure, filename="MOPC4.html", generation=evolver._iteration_counter, ) """