desdeo 1.2__py3-none-any.whl → 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- desdeo/__init__.py +8 -8
- desdeo/api/README.md +73 -0
- desdeo/api/__init__.py +15 -0
- desdeo/api/app.py +40 -0
- desdeo/api/config.py +69 -0
- desdeo/api/config.toml +53 -0
- desdeo/api/db.py +25 -0
- desdeo/api/db_init.py +79 -0
- desdeo/api/db_models.py +164 -0
- desdeo/api/malaga_db_init.py +27 -0
- desdeo/api/models/__init__.py +66 -0
- desdeo/api/models/archive.py +34 -0
- desdeo/api/models/preference.py +90 -0
- desdeo/api/models/problem.py +507 -0
- desdeo/api/models/reference_point_method.py +18 -0
- desdeo/api/models/session.py +46 -0
- desdeo/api/models/state.py +96 -0
- desdeo/api/models/user.py +51 -0
- desdeo/api/routers/_NAUTILUS.py +245 -0
- desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
- desdeo/api/routers/_NIMBUS.py +762 -0
- desdeo/api/routers/__init__.py +5 -0
- desdeo/api/routers/problem.py +110 -0
- desdeo/api/routers/reference_point_method.py +117 -0
- desdeo/api/routers/session.py +76 -0
- desdeo/api/routers/test.py +16 -0
- desdeo/api/routers/user_authentication.py +366 -0
- desdeo/api/schema.py +94 -0
- desdeo/api/tests/__init__.py +0 -0
- desdeo/api/tests/conftest.py +59 -0
- desdeo/api/tests/test_models.py +701 -0
- desdeo/api/tests/test_routes.py +216 -0
- desdeo/api/utils/database.py +274 -0
- desdeo/api/utils/logger.py +29 -0
- desdeo/core.py +27 -0
- desdeo/emo/__init__.py +29 -0
- desdeo/emo/hooks/archivers.py +172 -0
- desdeo/emo/methods/EAs.py +418 -0
- desdeo/emo/methods/__init__.py +0 -0
- desdeo/emo/methods/bases.py +59 -0
- desdeo/emo/operators/__init__.py +1 -0
- desdeo/emo/operators/crossover.py +780 -0
- desdeo/emo/operators/evaluator.py +118 -0
- desdeo/emo/operators/generator.py +356 -0
- desdeo/emo/operators/mutation.py +1053 -0
- desdeo/emo/operators/selection.py +1036 -0
- desdeo/emo/operators/termination.py +178 -0
- desdeo/explanations/__init__.py +6 -0
- desdeo/explanations/explainer.py +100 -0
- desdeo/explanations/utils.py +90 -0
- desdeo/mcdm/__init__.py +19 -0
- desdeo/mcdm/nautili.py +345 -0
- desdeo/mcdm/nautilus.py +477 -0
- desdeo/mcdm/nautilus_navigator.py +655 -0
- desdeo/mcdm/nimbus.py +417 -0
- desdeo/mcdm/pareto_navigator.py +269 -0
- desdeo/mcdm/reference_point_method.py +116 -0
- desdeo/problem/__init__.py +79 -0
- desdeo/problem/evaluator.py +561 -0
- desdeo/problem/gurobipy_evaluator.py +562 -0
- desdeo/problem/infix_parser.py +341 -0
- desdeo/problem/json_parser.py +944 -0
- desdeo/problem/pyomo_evaluator.py +468 -0
- desdeo/problem/schema.py +1808 -0
- desdeo/problem/simulator_evaluator.py +298 -0
- desdeo/problem/sympy_evaluator.py +244 -0
- desdeo/problem/testproblems/__init__.py +73 -0
- desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
- desdeo/problem/testproblems/dtlz2_problem.py +102 -0
- desdeo/problem/testproblems/forest_problem.py +275 -0
- desdeo/problem/testproblems/knapsack_problem.py +163 -0
- desdeo/problem/testproblems/mcwb_problem.py +831 -0
- desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +83 -0
- desdeo/problem/testproblems/momip_problem.py +172 -0
- desdeo/problem/testproblems/nimbus_problem.py +143 -0
- desdeo/problem/testproblems/pareto_navigator_problem.py +89 -0
- desdeo/problem/testproblems/re_problem.py +492 -0
- desdeo/problem/testproblems/river_pollution_problem.py +434 -0
- desdeo/problem/testproblems/rocket_injector_design_problem.py +140 -0
- desdeo/problem/testproblems/simple_problem.py +351 -0
- desdeo/problem/testproblems/simulator_problem.py +92 -0
- desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
- desdeo/problem/testproblems/zdt_problem.py +271 -0
- desdeo/problem/utils.py +245 -0
- desdeo/tools/GenerateReferencePoints.py +181 -0
- desdeo/tools/__init__.py +102 -0
- desdeo/tools/generics.py +145 -0
- desdeo/tools/gurobipy_solver_interfaces.py +258 -0
- desdeo/tools/indicators_binary.py +11 -0
- desdeo/tools/indicators_unary.py +375 -0
- desdeo/tools/interaction_schema.py +38 -0
- desdeo/tools/intersection.py +54 -0
- desdeo/tools/iterative_pareto_representer.py +99 -0
- desdeo/tools/message.py +234 -0
- desdeo/tools/ng_solver_interfaces.py +199 -0
- desdeo/tools/non_dominated_sorting.py +133 -0
- desdeo/tools/patterns.py +281 -0
- desdeo/tools/proximal_solver.py +99 -0
- desdeo/tools/pyomo_solver_interfaces.py +464 -0
- desdeo/tools/reference_vectors.py +462 -0
- desdeo/tools/scalarization.py +3138 -0
- desdeo/tools/scipy_solver_interfaces.py +454 -0
- desdeo/tools/score_bands.py +464 -0
- desdeo/tools/utils.py +320 -0
- desdeo/utopia_stuff/__init__.py +0 -0
- desdeo/utopia_stuff/data/1.json +15 -0
- desdeo/utopia_stuff/data/2.json +13 -0
- desdeo/utopia_stuff/data/3.json +15 -0
- desdeo/utopia_stuff/data/4.json +17 -0
- desdeo/utopia_stuff/data/5.json +15 -0
- desdeo/utopia_stuff/from_json.py +40 -0
- desdeo/utopia_stuff/reinit_user.py +38 -0
- desdeo/utopia_stuff/utopia_db_init.py +212 -0
- desdeo/utopia_stuff/utopia_problem.py +403 -0
- desdeo/utopia_stuff/utopia_problem_old.py +415 -0
- desdeo/utopia_stuff/utopia_reference_solutions.py +79 -0
- desdeo-2.0.0.dist-info/LICENSE +21 -0
- desdeo-2.0.0.dist-info/METADATA +168 -0
- desdeo-2.0.0.dist-info/RECORD +120 -0
- {desdeo-1.2.dist-info → desdeo-2.0.0.dist-info}/WHEEL +1 -1
- desdeo-1.2.dist-info/METADATA +0 -16
- desdeo-1.2.dist-info/RECORD +0 -4
desdeo/tools/__init__.py
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"""Imports available form the desdeo-tools package."""
|
|
2
|
+
|
|
3
|
+
__all__ = [
|
|
4
|
+
"BaseSolver",
|
|
5
|
+
"BonminOptions",
|
|
6
|
+
"IpoptOptions",
|
|
7
|
+
"GurobipySolver",
|
|
8
|
+
"NevergradGenericOptions",
|
|
9
|
+
"NevergradGenericSolver",
|
|
10
|
+
"PersistentGurobipySolver",
|
|
11
|
+
"ProximalSolver",
|
|
12
|
+
"PyomoBonminSolver",
|
|
13
|
+
"PyomoCBCSolver",
|
|
14
|
+
"PyomoGurobiSolver",
|
|
15
|
+
"PyomoIpoptSolver",
|
|
16
|
+
"ScipyDeSolver",
|
|
17
|
+
"ScipyMinimizeSolver",
|
|
18
|
+
"SolverOptions",
|
|
19
|
+
"SolverResults",
|
|
20
|
+
"ScalarizationError",
|
|
21
|
+
"add_asf_diff",
|
|
22
|
+
"add_asf_generic_nondiff",
|
|
23
|
+
"add_asf_generic_diff",
|
|
24
|
+
"add_asf_nondiff",
|
|
25
|
+
"add_epsilon_constraints",
|
|
26
|
+
"add_guess_sf_diff",
|
|
27
|
+
"add_guess_sf_nondiff",
|
|
28
|
+
"add_group_asf",
|
|
29
|
+
"add_group_asf_diff",
|
|
30
|
+
"add_group_guess_sf",
|
|
31
|
+
"add_group_guess_sf_diff",
|
|
32
|
+
"add_group_nimbus_sf",
|
|
33
|
+
"add_group_nimbus_sf_diff",
|
|
34
|
+
"add_group_stom_sf",
|
|
35
|
+
"add_group_stom_sf_diff",
|
|
36
|
+
"add_nimbus_sf_diff",
|
|
37
|
+
"add_nimbus_sf_nondiff",
|
|
38
|
+
"add_objective_as_scalarization",
|
|
39
|
+
"add_stom_sf_diff",
|
|
40
|
+
"add_stom_sf_nondiff",
|
|
41
|
+
"add_weighted_sums",
|
|
42
|
+
"available_nevergrad_optimizers",
|
|
43
|
+
"available_solvers",
|
|
44
|
+
"find_compatible_solvers",
|
|
45
|
+
"get_corrected_ideal_and_nadir",
|
|
46
|
+
"get_corrected_reference_point",
|
|
47
|
+
"guess_best_solver",
|
|
48
|
+
"payoff_table_method",
|
|
49
|
+
]
|
|
50
|
+
|
|
51
|
+
from desdeo.tools.generics import BaseSolver, SolverOptions, SolverResults
|
|
52
|
+
from desdeo.tools.gurobipy_solver_interfaces import (
|
|
53
|
+
GurobipySolver,
|
|
54
|
+
PersistentGurobipySolver,
|
|
55
|
+
)
|
|
56
|
+
from desdeo.tools.ng_solver_interfaces import (
|
|
57
|
+
NevergradGenericOptions,
|
|
58
|
+
NevergradGenericSolver,
|
|
59
|
+
available_nevergrad_optimizers,
|
|
60
|
+
)
|
|
61
|
+
from desdeo.tools.proximal_solver import ProximalSolver
|
|
62
|
+
from desdeo.tools.pyomo_solver_interfaces import (
|
|
63
|
+
BonminOptions,
|
|
64
|
+
IpoptOptions,
|
|
65
|
+
PyomoBonminSolver,
|
|
66
|
+
PyomoCBCSolver,
|
|
67
|
+
PyomoGurobiSolver,
|
|
68
|
+
PyomoIpoptSolver,
|
|
69
|
+
)
|
|
70
|
+
from desdeo.tools.scalarization import (
|
|
71
|
+
ScalarizationError,
|
|
72
|
+
add_asf_diff,
|
|
73
|
+
add_asf_generic_diff,
|
|
74
|
+
add_asf_generic_nondiff,
|
|
75
|
+
add_asf_nondiff,
|
|
76
|
+
add_epsilon_constraints,
|
|
77
|
+
add_group_asf,
|
|
78
|
+
add_group_asf_diff,
|
|
79
|
+
add_group_guess_sf,
|
|
80
|
+
add_group_guess_sf_diff,
|
|
81
|
+
add_group_nimbus_sf,
|
|
82
|
+
add_group_nimbus_sf_diff,
|
|
83
|
+
add_group_stom_sf,
|
|
84
|
+
add_group_stom_sf_diff,
|
|
85
|
+
add_guess_sf_diff,
|
|
86
|
+
add_guess_sf_nondiff,
|
|
87
|
+
add_nimbus_sf_diff,
|
|
88
|
+
add_nimbus_sf_nondiff,
|
|
89
|
+
add_objective_as_scalarization,
|
|
90
|
+
add_stom_sf_diff,
|
|
91
|
+
add_stom_sf_nondiff,
|
|
92
|
+
add_weighted_sums,
|
|
93
|
+
)
|
|
94
|
+
from desdeo.tools.scipy_solver_interfaces import ScipyDeSolver, ScipyMinimizeSolver
|
|
95
|
+
from desdeo.tools.utils import (
|
|
96
|
+
available_solvers,
|
|
97
|
+
find_compatible_solvers,
|
|
98
|
+
get_corrected_ideal_and_nadir,
|
|
99
|
+
get_corrected_reference_point,
|
|
100
|
+
guess_best_solver,
|
|
101
|
+
payoff_table_method,
|
|
102
|
+
)
|
desdeo/tools/generics.py
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"""Defines generic classes, functions, and objects utilized in the tools module."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from typing import Any, TypeVar
|
|
5
|
+
|
|
6
|
+
from pydantic import BaseModel, Field
|
|
7
|
+
|
|
8
|
+
from desdeo.problem import (
|
|
9
|
+
Constraint,
|
|
10
|
+
Objective,
|
|
11
|
+
Problem,
|
|
12
|
+
ScalarizationFunction,
|
|
13
|
+
Variable,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class SolverError(Exception):
|
|
18
|
+
"""Raised when an error with a solver is encountered."""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SolverResults(BaseModel):
|
|
22
|
+
"""Defines a schema for a dataclass to store the results of a solver."""
|
|
23
|
+
|
|
24
|
+
optimal_variables: dict[str, int | float | list] = Field(description="The optimal decision variables found.")
|
|
25
|
+
optimal_objectives: dict[str, float | list[float]] = Field(
|
|
26
|
+
description="The objective function values corresponding to the optimal decision variables found."
|
|
27
|
+
)
|
|
28
|
+
constraint_values: dict[str, float | list[float]] | None = Field(
|
|
29
|
+
description=(
|
|
30
|
+
"The constraint values of the problem. A negative value means the constraint is respected, "
|
|
31
|
+
"a positive one means it has been breached."
|
|
32
|
+
),
|
|
33
|
+
default=None,
|
|
34
|
+
)
|
|
35
|
+
extra_func_values: dict[str, float | list[float]] | None = Field(
|
|
36
|
+
description=("The extra function values of the problem."), default=None
|
|
37
|
+
)
|
|
38
|
+
scalarization_values: dict[str, float | list[float]] | None = Field(
|
|
39
|
+
description=("The scalarization function values of the problem."), default=None
|
|
40
|
+
)
|
|
41
|
+
success: bool = Field(description="A boolean flag indicating whether the optimization was successful or not.")
|
|
42
|
+
message: str = Field(description="Description of the cause of termination.")
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BaseSolver(ABC):
|
|
46
|
+
"""Defines a schema for a solver base class."""
|
|
47
|
+
|
|
48
|
+
evaluator: object
|
|
49
|
+
problem: Problem
|
|
50
|
+
|
|
51
|
+
def __init__(self, problem: Problem, options: dict[str, any] | None = None):
|
|
52
|
+
"""Initializer for the persistent solver.
|
|
53
|
+
|
|
54
|
+
Args:
|
|
55
|
+
problem (Problem): The problem for the solver.
|
|
56
|
+
options (dict[str,any]): Dictionary of parameters to set.
|
|
57
|
+
What these should be depends on the solver used.
|
|
58
|
+
"""
|
|
59
|
+
self.problem = problem
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def solve(self, target: str) -> SolverResults:
|
|
63
|
+
"""Solves the current problem with the specified target.
|
|
64
|
+
|
|
65
|
+
Args:
|
|
66
|
+
target (str): a str representing the symbol of the target function.
|
|
67
|
+
|
|
68
|
+
Returns:
|
|
69
|
+
SolverResults: The results of the solver
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class PersistentSolver:
|
|
74
|
+
"""Defines a schema for a persistent solver class.
|
|
75
|
+
|
|
76
|
+
Can be used when reinitializing the solver every time the problem is changed is not practical.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
evaluator: object
|
|
80
|
+
problem: Problem
|
|
81
|
+
|
|
82
|
+
def __init__(self, problem: Problem, options: dict[str, any] | None = None):
|
|
83
|
+
"""Initializer for the persistent solver.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
problem (Problem): The problem for the solver.
|
|
87
|
+
options (dict[str,any]): Dictionary of parameters to set.
|
|
88
|
+
What these should be depends on the solver used.
|
|
89
|
+
"""
|
|
90
|
+
self.problem = problem
|
|
91
|
+
|
|
92
|
+
def add_constraint(self, constraint: Constraint | list[Constraint]):
|
|
93
|
+
"""Add a constraint expression to the solver.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
constraint (Constraint): the constraint function expression.
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
def add_objective(self, objective: Objective):
|
|
100
|
+
"""Adds an objective function expression to the solver.
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
objective (Objective): an objective function expression to be added.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def add_scalarization_function(self, scalarization: ScalarizationFunction):
|
|
107
|
+
"""Adds a scalrization expression to the solver.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
scalarization (ScalarizationFunction): A scalarization function to be added.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def add_variable(self, variable: Variable):
|
|
114
|
+
"""Add a variable to the solver.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
variable (Variable): The definition of the variable to be added.
|
|
118
|
+
"""
|
|
119
|
+
|
|
120
|
+
def remove_constraint(self, symbol: str):
|
|
121
|
+
"""Removes a constraint from the solver.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
symbol (str): a str representing the symbol of the constraint to be removed.
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
def remove_variable(self, symbol: str):
|
|
128
|
+
"""Removes a variable from the model.
|
|
129
|
+
|
|
130
|
+
Args:
|
|
131
|
+
symbol (str): a str representing the symbol of the variable to be removed.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
def solve(self, target: str) -> SolverResults:
|
|
135
|
+
"""Solves the current problem with the specified target.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
target (str): a str representing the symbol of the target function.
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
SolverResults: The results of the solver
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
SolverOptions = TypeVar("SolverOptions", bound=Any)
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""Defines solver interfaces for gurobipy."""
|
|
2
|
+
|
|
3
|
+
import gurobipy as gp
|
|
4
|
+
|
|
5
|
+
from desdeo.problem import (
|
|
6
|
+
Constraint,
|
|
7
|
+
GurobipyEvaluator,
|
|
8
|
+
Objective,
|
|
9
|
+
Problem,
|
|
10
|
+
ScalarizationFunction,
|
|
11
|
+
TensorVariable,
|
|
12
|
+
Variable,
|
|
13
|
+
)
|
|
14
|
+
from desdeo.tools.generics import BaseSolver, PersistentSolver, SolverResults
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def parse_gurobipy_optimizer_results(problem: Problem, evaluator: GurobipyEvaluator) -> SolverResults:
|
|
18
|
+
"""Parses results from GurobipyEvaluator's model into DESDEO SolverResults.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
problem (Problem): the problem being solved.
|
|
22
|
+
evaluator (GurobipyEvaluator): the evaluator utilized to solve the problem.
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
SolverResults: DESDEO solver results.
|
|
26
|
+
"""
|
|
27
|
+
results = evaluator.get_values()
|
|
28
|
+
|
|
29
|
+
variable_values = {var.symbol: results[var.symbol] for var in problem.variables}
|
|
30
|
+
objective_values = {obj.symbol: results[obj.symbol] for obj in problem.objectives}
|
|
31
|
+
constraint_values = (
|
|
32
|
+
{con.symbol: results[con.symbol] for con in problem.constraints} if problem.constraints is not None else None
|
|
33
|
+
)
|
|
34
|
+
extra_func_values = (
|
|
35
|
+
{extra.symbol: results[extra.symbol] for extra in problem.extra_funcs}
|
|
36
|
+
if problem.extra_funcs is not None
|
|
37
|
+
else None
|
|
38
|
+
)
|
|
39
|
+
scalarization_values = (
|
|
40
|
+
{scal.symbol: results[scal.symbol] for scal in problem.scalarization_funcs}
|
|
41
|
+
if problem.scalarization_funcs is not None
|
|
42
|
+
else None
|
|
43
|
+
)
|
|
44
|
+
success = evaluator.model.status == gp.GRB.OPTIMAL
|
|
45
|
+
if evaluator.model.status == gp.GRB.OPTIMAL:
|
|
46
|
+
status = "Optimal solution found."
|
|
47
|
+
elif evaluator.model.status == gp.GRB.INFEASIBLE:
|
|
48
|
+
status = "Model is infeasible."
|
|
49
|
+
elif evaluator.model.status == gp.GRB.UNBOUNDED:
|
|
50
|
+
status = "Model is unbounded."
|
|
51
|
+
elif evaluator.model.status == gp.GRB.INF_OR_UNBD:
|
|
52
|
+
status = "Model is either infeasible or unbounded."
|
|
53
|
+
else:
|
|
54
|
+
status = f"Optimization ended with status: {evaluator.model.status}"
|
|
55
|
+
msg = f"Gurobipy solver status is: '{status}'"
|
|
56
|
+
|
|
57
|
+
return SolverResults(
|
|
58
|
+
optimal_variables=variable_values,
|
|
59
|
+
optimal_objectives=objective_values,
|
|
60
|
+
constraint_values=constraint_values,
|
|
61
|
+
extra_func_values=extra_func_values,
|
|
62
|
+
scalarization_values=scalarization_values,
|
|
63
|
+
success=success,
|
|
64
|
+
message=msg,
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class GurobipySolver(BaseSolver):
|
|
69
|
+
"""Creates a gurobipy solver that utilizes gurobi's own Python implementation."""
|
|
70
|
+
|
|
71
|
+
def __init__(self, problem: Problem, options: dict[str, any] | None = None):
|
|
72
|
+
"""The solver is initialized by supplying a problem and options.
|
|
73
|
+
|
|
74
|
+
Unlike with Pyomo you do not need to have gurobi installed on your system
|
|
75
|
+
for this to work. Suitable for solving mixed-integer linear and quadratic optimization
|
|
76
|
+
problems.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
problem (Problem): the problem to be solved.
|
|
80
|
+
options (dict[str,any]): Dictionary of Gurobi parameters to set.
|
|
81
|
+
You probably don't need to set any of these and can just use the defaults.
|
|
82
|
+
For available parameters see https://www.gurobi.com/documentation/current/refman/parameters.html
|
|
83
|
+
"""
|
|
84
|
+
self.evaluator = GurobipyEvaluator(problem)
|
|
85
|
+
self.problem = problem
|
|
86
|
+
|
|
87
|
+
if options is not None:
|
|
88
|
+
for key, value in options.items():
|
|
89
|
+
self.evaluator.model.setParam(key, value)
|
|
90
|
+
|
|
91
|
+
def solve(self, target: str) -> SolverResults:
|
|
92
|
+
"""Solve the problem for the given target.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
target (str): the symbol of the function to be optimized, and which is
|
|
96
|
+
defined in the problem given when initializing the solver.
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
SolverResults: the results of the optimization.
|
|
100
|
+
"""
|
|
101
|
+
self.evaluator.set_optimization_target(target)
|
|
102
|
+
self.evaluator.model.optimize()
|
|
103
|
+
return parse_gurobipy_optimizer_results(self.problem, self.evaluator)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class PersistentGurobipySolver(PersistentSolver):
|
|
107
|
+
"""A persistent solver class utlizing gurobipy.
|
|
108
|
+
|
|
109
|
+
Use this instead of create_gurobipy_solver when re-initializing the
|
|
110
|
+
solver every time the problem is changed is not practical.
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
evaluator: GurobipyEvaluator
|
|
114
|
+
|
|
115
|
+
def __init__(self, problem: Problem, options: dict[str, any] | None = None):
|
|
116
|
+
"""Initializer for the persistent solver.
|
|
117
|
+
|
|
118
|
+
Args:
|
|
119
|
+
problem (Problem): the problem to be transformed in a GurobipyModel.
|
|
120
|
+
options (dict[str,any]): Dictionary of Gurobi parameters to set.
|
|
121
|
+
You probably don't need to set any of these and can just use the defaults.
|
|
122
|
+
For available parameters see https://www.gurobi.com/documentation/current/refman/parameters.html
|
|
123
|
+
"""
|
|
124
|
+
self.problem = problem
|
|
125
|
+
self.evaluator = GurobipyEvaluator(problem)
|
|
126
|
+
if options is not None:
|
|
127
|
+
for key, value in options.items():
|
|
128
|
+
self.evaluator.model.setParam(key, value)
|
|
129
|
+
|
|
130
|
+
def add_constraint(self, constraint: Constraint | list[Constraint]) -> gp.Constr | list[gp.Constr]:
|
|
131
|
+
"""Add one or more constraint expressions to the solver.
|
|
132
|
+
|
|
133
|
+
If adding a lot of constraints or dealing with a large model, this function
|
|
134
|
+
may end up being very slow compared to adding the constraints to the model
|
|
135
|
+
stored in the evaluator directly.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
constraint (Constraint): the constraint function expression or a list of
|
|
139
|
+
constraint function expressions.
|
|
140
|
+
|
|
141
|
+
Raises:
|
|
142
|
+
GurobipyEvaluatorError: when an unsupported constraint type is encountered.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
gurobipy.Constr: The gurobipy constraint that was added or a list of gurobipy
|
|
146
|
+
constraints if the constraint argument was a list.
|
|
147
|
+
"""
|
|
148
|
+
if isinstance(constraint, list):
|
|
149
|
+
cons_list = list[gp.Constr]
|
|
150
|
+
for cons in constraint:
|
|
151
|
+
cons_list.append(self.evaluator.add_constraint(cons))
|
|
152
|
+
return cons_list
|
|
153
|
+
|
|
154
|
+
return self.evaluator.add_constraint(constraint)
|
|
155
|
+
|
|
156
|
+
def add_objective(self, objective: Objective | list[Objective]):
|
|
157
|
+
"""Adds an objective function expression to the solver.
|
|
158
|
+
|
|
159
|
+
Does not yet add any actual gurobipy optimization objectives, only adds them to the dict
|
|
160
|
+
containing the expressions of the objectives. The objective expressions are stored in the
|
|
161
|
+
evaluator and the evaluator must add the appropiate gurobipy objective before solving.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
objective (Objective): an objective function expression or a list of objective function
|
|
165
|
+
expressions to be added.
|
|
166
|
+
"""
|
|
167
|
+
if not isinstance(objective, list):
|
|
168
|
+
objective = [objective]
|
|
169
|
+
|
|
170
|
+
for obj in objective:
|
|
171
|
+
self.evaluator.add_objective(obj)
|
|
172
|
+
|
|
173
|
+
def add_scalarization_function(self, scalarization: ScalarizationFunction | list[ScalarizationFunction]):
|
|
174
|
+
"""Adds a scalrization expression to the solver.
|
|
175
|
+
|
|
176
|
+
Scalarizations work identically to objectives, except they are stored in a different
|
|
177
|
+
dict in the evaluator. If you want to solve the problem using a scalarization, the
|
|
178
|
+
evaluator needs to set it as an optimization target first.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
scalarization (ScalarizationFunction): A scalarization function or a list of
|
|
182
|
+
scalarization functions to be added.
|
|
183
|
+
"""
|
|
184
|
+
if not isinstance(scalarization, list):
|
|
185
|
+
scalarization = [scalarization]
|
|
186
|
+
|
|
187
|
+
for scal in scalarization:
|
|
188
|
+
self.evaluator.add_scalarization_function(scal)
|
|
189
|
+
|
|
190
|
+
def add_variable(
|
|
191
|
+
self, variable: Variable | TensorVariable | list[Variable] | list[TensorVariable]
|
|
192
|
+
) -> gp.Var | gp.MVar | list[gp.Var] | list[gp.MVar]:
|
|
193
|
+
"""Add one or more variables to the solver.
|
|
194
|
+
|
|
195
|
+
If adding a lot of variables or dealing with a large model, this function
|
|
196
|
+
may end up being very slow compared to adding the variables to the model
|
|
197
|
+
stored in the evaluator directly.
|
|
198
|
+
|
|
199
|
+
Args:
|
|
200
|
+
variable (Variable): The definition of the variable or a list of variables to be added.
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
GurobipyEvaluatorError: when a problem in extracting the variables is encountered.
|
|
204
|
+
I.e., the variables are of a non supported type.
|
|
205
|
+
|
|
206
|
+
Returns:
|
|
207
|
+
gp.Var: the variable that was added to the model or a list of variables if
|
|
208
|
+
variable argument was a list.
|
|
209
|
+
"""
|
|
210
|
+
if isinstance(variable, list):
|
|
211
|
+
var_list = list[gp.Var | gp.MVar]
|
|
212
|
+
for var in variable:
|
|
213
|
+
var_list.append(self.evaluator.add_variable(var))
|
|
214
|
+
return var_list
|
|
215
|
+
|
|
216
|
+
return self.evaluator.add_variable(variable)
|
|
217
|
+
|
|
218
|
+
def remove_constraint(self, symbol: str | list[str]):
|
|
219
|
+
"""Removes a constraint from the solver.
|
|
220
|
+
|
|
221
|
+
If removing a lot of constraints or dealing with a very large model this function
|
|
222
|
+
may be slow because of the model.update() calls. Accessing the model stored in the
|
|
223
|
+
evaluator directly may be faster.
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
symbol (str): a str representing the symbol of the constraint to be removed.
|
|
227
|
+
Can also be a list of multiple symbols.
|
|
228
|
+
"""
|
|
229
|
+
if not isinstance(symbol, list):
|
|
230
|
+
symbol = [symbol]
|
|
231
|
+
for s in symbol:
|
|
232
|
+
self.evaluator.remove_constraint(s)
|
|
233
|
+
|
|
234
|
+
def remove_variable(self, symbol: str | list[str]):
|
|
235
|
+
"""Removes a variable from the model.
|
|
236
|
+
|
|
237
|
+
If removing a lot of variables or dealing with a very large model this function
|
|
238
|
+
may be slow because of the model.update() calls. Accessing the model stored in
|
|
239
|
+
the evaluator directly may be faster.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
symbol (str): a str representing the symbol of the variable to be removed.
|
|
243
|
+
Can also be a list of multiple symbols.
|
|
244
|
+
"""
|
|
245
|
+
self.evaluator.remove_variable(symbol)
|
|
246
|
+
|
|
247
|
+
def solve(self, target: str) -> SolverResults:
|
|
248
|
+
"""Solves the current problem with the specified target.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
target (str): a str representing the symbol of the target function.
|
|
252
|
+
|
|
253
|
+
Returns:
|
|
254
|
+
SolverResults: The results of the solver
|
|
255
|
+
"""
|
|
256
|
+
self.evaluator.set_optimization_target(target)
|
|
257
|
+
self.evaluator.model.optimize()
|
|
258
|
+
return parse_gurobipy_optimizer_results(self.problem, self.evaluator)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""This module implements unary indicators that can be used to compare two solution sets.
|
|
2
|
+
|
|
3
|
+
It assumes that the solution set has been normalized just that _some_ ideal point (not necessarily the ideal point
|
|
4
|
+
of the set) is the origin and _some_ nadir point (not necessarily the nadir point of the set) is (1, 1, ..., 1).
|
|
5
|
+
The normalized solution set is assumed to be inside the bounding box [0, 1]^k where k is the number of objectives.
|
|
6
|
+
If these conditions are not met, the results of the indicators will not be meaningful.
|
|
7
|
+
|
|
8
|
+
Additionally, the set may be assumed to only contain mutually non-dominated solutions, depending on the indicator.
|
|
9
|
+
|
|
10
|
+
For now, we rely on pymoo for the implementation of many of the indicators.
|
|
11
|
+
"""
|