desdeo 2.0.0__py3-none-any.whl → 2.1.1__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/adm/ADMAfsar.py +551 -0
- desdeo/adm/ADMChen.py +414 -0
- desdeo/adm/BaseADM.py +119 -0
- desdeo/adm/__init__.py +11 -0
- desdeo/api/__init__.py +6 -6
- desdeo/api/app.py +38 -28
- desdeo/api/config.py +65 -44
- desdeo/api/config.toml +23 -12
- desdeo/api/db.py +10 -8
- desdeo/api/db_init.py +12 -6
- desdeo/api/models/__init__.py +220 -20
- desdeo/api/models/archive.py +16 -27
- desdeo/api/models/emo.py +128 -0
- desdeo/api/models/enautilus.py +69 -0
- desdeo/api/models/gdm/gdm_aggregate.py +139 -0
- desdeo/api/models/gdm/gdm_base.py +69 -0
- desdeo/api/models/gdm/gdm_score_bands.py +114 -0
- desdeo/api/models/gdm/gnimbus.py +138 -0
- desdeo/api/models/generic.py +104 -0
- desdeo/api/models/generic_states.py +401 -0
- desdeo/api/models/nimbus.py +158 -0
- desdeo/api/models/preference.py +44 -6
- desdeo/api/models/problem.py +274 -64
- desdeo/api/models/session.py +4 -1
- desdeo/api/models/state.py +419 -52
- desdeo/api/models/user.py +7 -6
- desdeo/api/models/utopia.py +25 -0
- desdeo/api/routers/_EMO.backup +309 -0
- desdeo/api/routers/_NIMBUS.py +6 -3
- desdeo/api/routers/emo.py +497 -0
- desdeo/api/routers/enautilus.py +237 -0
- desdeo/api/routers/gdm/gdm_aggregate.py +234 -0
- desdeo/api/routers/gdm/gdm_base.py +420 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py +398 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py +377 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py +698 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py +591 -0
- desdeo/api/routers/generic.py +233 -0
- desdeo/api/routers/nimbus.py +705 -0
- desdeo/api/routers/problem.py +201 -4
- desdeo/api/routers/reference_point_method.py +20 -44
- desdeo/api/routers/session.py +50 -26
- desdeo/api/routers/user_authentication.py +180 -26
- desdeo/api/routers/utils.py +187 -0
- desdeo/api/routers/utopia.py +230 -0
- desdeo/api/schema.py +10 -4
- desdeo/api/tests/conftest.py +94 -2
- desdeo/api/tests/test_enautilus.py +330 -0
- desdeo/api/tests/test_models.py +550 -72
- desdeo/api/tests/test_routes.py +902 -43
- desdeo/api/utils/_database.py +263 -0
- desdeo/api/utils/database.py +28 -266
- desdeo/api/utils/emo_database.py +40 -0
- desdeo/core.py +7 -0
- desdeo/emo/__init__.py +154 -24
- desdeo/emo/hooks/archivers.py +18 -2
- desdeo/emo/methods/EAs.py +128 -5
- desdeo/emo/methods/bases.py +9 -56
- desdeo/emo/methods/templates.py +111 -0
- desdeo/emo/operators/crossover.py +544 -42
- desdeo/emo/operators/evaluator.py +10 -14
- desdeo/emo/operators/generator.py +127 -24
- desdeo/emo/operators/mutation.py +212 -41
- desdeo/emo/operators/scalar_selection.py +202 -0
- desdeo/emo/operators/selection.py +956 -214
- desdeo/emo/operators/termination.py +124 -16
- desdeo/emo/options/__init__.py +108 -0
- desdeo/emo/options/algorithms.py +435 -0
- desdeo/emo/options/crossover.py +164 -0
- desdeo/emo/options/generator.py +131 -0
- desdeo/emo/options/mutation.py +260 -0
- desdeo/emo/options/repair.py +61 -0
- desdeo/emo/options/scalar_selection.py +66 -0
- desdeo/emo/options/selection.py +127 -0
- desdeo/emo/options/templates.py +383 -0
- desdeo/emo/options/termination.py +143 -0
- desdeo/gdm/__init__.py +22 -0
- desdeo/gdm/gdmtools.py +45 -0
- desdeo/gdm/score_bands.py +114 -0
- desdeo/gdm/voting_rules.py +50 -0
- desdeo/mcdm/__init__.py +23 -1
- desdeo/mcdm/enautilus.py +338 -0
- desdeo/mcdm/gnimbus.py +484 -0
- desdeo/mcdm/nautilus_navigator.py +7 -6
- desdeo/mcdm/reference_point_method.py +70 -0
- desdeo/problem/__init__.py +16 -11
- desdeo/problem/evaluator.py +4 -5
- desdeo/problem/external/__init__.py +18 -0
- desdeo/problem/external/core.py +356 -0
- desdeo/problem/external/pymoo_provider.py +266 -0
- desdeo/problem/external/runtime.py +44 -0
- desdeo/problem/gurobipy_evaluator.py +37 -12
- desdeo/problem/infix_parser.py +1 -16
- desdeo/problem/json_parser.py +7 -11
- desdeo/problem/pyomo_evaluator.py +25 -6
- desdeo/problem/schema.py +73 -55
- desdeo/problem/simulator_evaluator.py +65 -15
- desdeo/problem/testproblems/__init__.py +26 -11
- desdeo/problem/testproblems/benchmarks_server.py +120 -0
- desdeo/problem/testproblems/cake_problem.py +185 -0
- desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
- desdeo/problem/testproblems/forest_problem.py +77 -69
- desdeo/problem/testproblems/multi_valued_constraints.py +119 -0
- desdeo/problem/testproblems/{river_pollution_problem.py → river_pollution_problems.py} +28 -22
- desdeo/problem/testproblems/single_objective.py +289 -0
- desdeo/problem/testproblems/zdt_problem.py +4 -1
- desdeo/problem/utils.py +1 -1
- desdeo/tools/__init__.py +39 -21
- desdeo/tools/desc_gen.py +22 -0
- desdeo/tools/generics.py +22 -2
- desdeo/tools/group_scalarization.py +3090 -0
- desdeo/tools/indicators_binary.py +107 -1
- desdeo/tools/indicators_unary.py +3 -16
- desdeo/tools/message.py +33 -2
- desdeo/tools/non_dominated_sorting.py +4 -3
- desdeo/tools/patterns.py +9 -7
- desdeo/tools/pyomo_solver_interfaces.py +49 -36
- desdeo/tools/reference_vectors.py +118 -351
- desdeo/tools/scalarization.py +340 -1413
- desdeo/tools/score_bands.py +491 -328
- desdeo/tools/utils.py +117 -49
- desdeo/tools/visualizations.py +67 -0
- desdeo/utopia_stuff/utopia_problem.py +1 -1
- desdeo/utopia_stuff/utopia_problem_old.py +1 -1
- {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info}/METADATA +47 -30
- desdeo-2.1.1.dist-info/RECORD +180 -0
- {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info}/WHEEL +1 -1
- desdeo-2.0.0.dist-info/RECORD +0 -120
- /desdeo/api/utils/{logger.py → _logger.py} +0 -0
- {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info/licenses}/LICENSE +0 -0
desdeo/tools/utils.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"""General utilities related to solvers."""
|
|
2
2
|
|
|
3
3
|
import shutil
|
|
4
|
+
from collections.abc import Callable
|
|
4
5
|
|
|
5
6
|
import numpy as np
|
|
7
|
+
import polars as pl
|
|
6
8
|
|
|
7
9
|
from desdeo.problem import (
|
|
8
10
|
ObjectiveTypeEnum,
|
|
@@ -14,27 +16,60 @@ from desdeo.problem import (
|
|
|
14
16
|
variable_dimension_enumerate,
|
|
15
17
|
)
|
|
16
18
|
from desdeo.tools.generics import BaseSolver
|
|
17
|
-
from desdeo.tools.gurobipy_solver_interfaces import GurobipySolver
|
|
18
|
-
from desdeo.tools.ng_solver_interfaces import NevergradGenericSolver
|
|
19
|
+
from desdeo.tools.gurobipy_solver_interfaces import GurobipySolver, PersistentGurobipySolver
|
|
20
|
+
from desdeo.tools.ng_solver_interfaces import NevergradGenericOptions, NevergradGenericSolver
|
|
19
21
|
from desdeo.tools.proximal_solver import ProximalSolver
|
|
20
22
|
from desdeo.tools.pyomo_solver_interfaces import (
|
|
23
|
+
BonminOptions,
|
|
24
|
+
CbcOptions,
|
|
25
|
+
IpoptOptions,
|
|
21
26
|
PyomoBonminSolver,
|
|
22
27
|
PyomoCBCSolver,
|
|
23
28
|
PyomoGurobiSolver,
|
|
24
29
|
PyomoIpoptSolver,
|
|
25
30
|
)
|
|
26
|
-
from desdeo.tools.scipy_solver_interfaces import
|
|
31
|
+
from desdeo.tools.scipy_solver_interfaces import (
|
|
32
|
+
ScipyDeOptions,
|
|
33
|
+
ScipyDeSolver,
|
|
34
|
+
ScipyMinimizeOptions,
|
|
35
|
+
ScipyMinimizeSolver,
|
|
36
|
+
)
|
|
27
37
|
|
|
28
38
|
available_solvers = {
|
|
29
|
-
"scipy_minimize":
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
"
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"
|
|
39
|
+
"scipy_minimize": {
|
|
40
|
+
"constructor": ScipyMinimizeSolver,
|
|
41
|
+
"options": ScipyMinimizeOptions,
|
|
42
|
+
},
|
|
43
|
+
"scipy_de": {
|
|
44
|
+
"constructor": ScipyDeSolver,
|
|
45
|
+
"options": ScipyDeOptions,
|
|
46
|
+
},
|
|
47
|
+
"proximal": {
|
|
48
|
+
"constructor": ProximalSolver,
|
|
49
|
+
"options": None,
|
|
50
|
+
},
|
|
51
|
+
"nevergrad": {"constructor": NevergradGenericSolver, "options": NevergradGenericOptions},
|
|
52
|
+
"pyomo_bonmin": {
|
|
53
|
+
"constructor": PyomoBonminSolver,
|
|
54
|
+
"options": BonminOptions,
|
|
55
|
+
},
|
|
56
|
+
"pyomo_cbc": {
|
|
57
|
+
"constructor": PyomoCBCSolver,
|
|
58
|
+
"options": CbcOptions,
|
|
59
|
+
},
|
|
60
|
+
"pyomo_ipopt": {
|
|
61
|
+
"constructor": PyomoIpoptSolver,
|
|
62
|
+
"options": IpoptOptions,
|
|
63
|
+
},
|
|
64
|
+
"pyomo_gurobi": {"constructor": PyomoGurobiSolver, "options": None},
|
|
65
|
+
"gurobipy": {
|
|
66
|
+
"constructor": GurobipySolver,
|
|
67
|
+
"options": None,
|
|
68
|
+
},
|
|
69
|
+
"gurobipy_persistent": {
|
|
70
|
+
"constructor": PersistentGurobipySolver,
|
|
71
|
+
"options": None,
|
|
72
|
+
},
|
|
38
73
|
}
|
|
39
74
|
|
|
40
75
|
|
|
@@ -66,7 +101,7 @@ def find_compatible_solvers(problem: Problem) -> list[BaseSolver]:
|
|
|
66
101
|
if all_data_based and has_discrete and var_dim == VariableDimensionEnum.scalar:
|
|
67
102
|
# problem has only data-based objectives and a discrete definition is available
|
|
68
103
|
# return ProximalSolver as it is the only solver for data-based problems at the moment
|
|
69
|
-
return [available_solvers["proximal"]]
|
|
104
|
+
return [available_solvers["proximal"]["constructor"]]
|
|
70
105
|
|
|
71
106
|
# check if the problem is differentiable and if it is mixed integer
|
|
72
107
|
if (
|
|
@@ -78,7 +113,7 @@ def find_compatible_solvers(problem: Problem) -> list[BaseSolver]:
|
|
|
78
113
|
VariableDomainTypeEnum.mixed,
|
|
79
114
|
]
|
|
80
115
|
):
|
|
81
|
-
solvers.append(available_solvers["pyomo_bonmin"]) # bonmin has to be installed
|
|
116
|
+
solvers.append(available_solvers["pyomo_bonmin"]["constructor"]) # bonmin has to be installed
|
|
82
117
|
|
|
83
118
|
# check if the problem is differentiable and continuous
|
|
84
119
|
if (
|
|
@@ -86,22 +121,22 @@ def find_compatible_solvers(problem: Problem) -> list[BaseSolver]:
|
|
|
86
121
|
and shutil.which("ipopt")
|
|
87
122
|
and problem.variable_domain in [VariableDomainTypeEnum.continuous]
|
|
88
123
|
):
|
|
89
|
-
solvers.append(available_solvers["pyomo_ipopt"]) # ipopt has to be installed
|
|
124
|
+
solvers.append(available_solvers["pyomo_ipopt"]["constructor"]) # ipopt has to be installed
|
|
90
125
|
|
|
91
126
|
# check if the problem is linear
|
|
92
127
|
if problem.is_linear:
|
|
93
|
-
solvers.append(available_solvers["gurobipy"])
|
|
128
|
+
solvers.append(available_solvers["gurobipy"]["constructor"])
|
|
94
129
|
if problem.is_linear and shutil.which("gurobi"):
|
|
95
|
-
solvers.append(available_solvers["pyomo_gurobi"]) # gurobi has to be installed
|
|
130
|
+
solvers.append(available_solvers["pyomo_gurobi"]["constructor"]) # gurobi has to be installed
|
|
96
131
|
if problem.is_linear and shutil.which("cbc"):
|
|
97
|
-
solvers.append(available_solvers["pyomo_cbc"])
|
|
132
|
+
solvers.append(available_solvers["pyomo_cbc"]["constructor"])
|
|
98
133
|
|
|
99
134
|
# check if problem's variables are all scalars
|
|
100
135
|
if var_dim == VariableDimensionEnum.scalar:
|
|
101
136
|
# nevergrad and scipy solvers work with all(?) problems with only scalar valued variables
|
|
102
|
-
solvers.append(available_solvers["nevergrad"])
|
|
103
|
-
solvers.append(available_solvers["scipy_minimize"])
|
|
104
|
-
solvers.append(available_solvers["scipy_de"])
|
|
137
|
+
solvers.append(available_solvers["nevergrad"]["constructor"])
|
|
138
|
+
solvers.append(available_solvers["scipy_minimize"]["constructor"])
|
|
139
|
+
solvers.append(available_solvers["scipy_de"]["constructor"])
|
|
105
140
|
return solvers
|
|
106
141
|
|
|
107
142
|
|
|
@@ -125,12 +160,17 @@ def guess_best_solver(problem: Problem) -> BaseSolver: # noqa: PLR0911
|
|
|
125
160
|
# check if problem has a discrete definition
|
|
126
161
|
has_discrete = problem.discrete_representation is not None
|
|
127
162
|
|
|
128
|
-
|
|
163
|
+
# TODO: when figured out what solver is best for problems with tensor variables: it seems that e.g. the
|
|
164
|
+
# forest problems don't work with pyomo_cbc. So VERY MUCH a quick fix to get something working, as those types of
|
|
165
|
+
# problems have been ok with gurobipy.
|
|
166
|
+
|
|
167
|
+
# if True in [isinstance(variable, TensorVariable) for variable in problem.variables]:
|
|
168
|
+
if False:
|
|
129
169
|
if problem.is_linear and shutil.which("cbc"):
|
|
130
|
-
return available_solvers["pyomo_cbc"]
|
|
170
|
+
return available_solvers["pyomo_cbc"]["constructor"]
|
|
131
171
|
|
|
132
172
|
if problem.is_linear:
|
|
133
|
-
return available_solvers["gurobipy"]
|
|
173
|
+
return available_solvers["gurobipy"]["constructor"]
|
|
134
174
|
|
|
135
175
|
# check if the problem is differentiable and if it is mixed integer
|
|
136
176
|
if (
|
|
@@ -142,7 +182,7 @@ def guess_best_solver(problem: Problem) -> BaseSolver: # noqa: PLR0911
|
|
|
142
182
|
VariableDomainTypeEnum.mixed,
|
|
143
183
|
]
|
|
144
184
|
):
|
|
145
|
-
return available_solvers["pyomo_bonmin"]
|
|
185
|
+
return available_solvers["pyomo_bonmin"]["constructor"]
|
|
146
186
|
|
|
147
187
|
# check if the problem is differentiable and continuous
|
|
148
188
|
if (
|
|
@@ -150,23 +190,28 @@ def guess_best_solver(problem: Problem) -> BaseSolver: # noqa: PLR0911
|
|
|
150
190
|
and shutil.which("ipopt")
|
|
151
191
|
and problem.variable_domain in [VariableDomainTypeEnum.continuous]
|
|
152
192
|
):
|
|
153
|
-
return available_solvers["pyomo_ipopt"]
|
|
193
|
+
return available_solvers["pyomo_ipopt"]["constructor"]
|
|
154
194
|
|
|
155
195
|
if all_data_based and has_discrete:
|
|
156
196
|
# problem has only data-based objectives and a discrete definition is available
|
|
157
197
|
# guess proximal solver is best
|
|
158
|
-
return available_solvers["proximal"]
|
|
198
|
+
return available_solvers["proximal"]["constructor"]
|
|
159
199
|
|
|
160
200
|
# check if the problem is linear
|
|
161
201
|
if problem.is_linear:
|
|
162
|
-
return available_solvers["gurobipy"]
|
|
202
|
+
return available_solvers["gurobipy"]["constructor"]
|
|
163
203
|
|
|
164
204
|
# check if the problem is differentiable and if it is mixed integer
|
|
165
|
-
if
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
205
|
+
if (
|
|
206
|
+
problem.is_twice_differentiable
|
|
207
|
+
and shutil.which("bonmin")
|
|
208
|
+
and problem.variable_domain
|
|
209
|
+
in [
|
|
210
|
+
VariableDomainTypeEnum.integer,
|
|
211
|
+
VariableDomainTypeEnum.mixed,
|
|
212
|
+
]
|
|
213
|
+
):
|
|
214
|
+
return available_solvers["pyomo_bonmin"]["constructor"]
|
|
170
215
|
|
|
171
216
|
# check if the problem is differentiable and continuous
|
|
172
217
|
if (
|
|
@@ -174,10 +219,10 @@ def guess_best_solver(problem: Problem) -> BaseSolver: # noqa: PLR0911
|
|
|
174
219
|
and shutil.which("ipopt")
|
|
175
220
|
and problem.variable_domain in [VariableDomainTypeEnum.continuous]
|
|
176
221
|
):
|
|
177
|
-
return available_solvers["pyomo_ipopt"]
|
|
222
|
+
return available_solvers["pyomo_ipopt"]["constructor"]
|
|
178
223
|
|
|
179
224
|
# else, guess nevergrad heuristics to be the best
|
|
180
|
-
return available_solvers["nevergrad"]
|
|
225
|
+
return available_solvers["nevergrad"]["constructor"]
|
|
181
226
|
|
|
182
227
|
# thigs to check: variable types, does the problem have constraint, constraint types, etc...
|
|
183
228
|
|
|
@@ -215,6 +260,7 @@ def get_corrected_ideal_and_nadir(problem: Problem) -> tuple[dict[str, float | N
|
|
|
215
260
|
|
|
216
261
|
return ideal_point, nadir_point
|
|
217
262
|
|
|
263
|
+
|
|
218
264
|
def get_corrected_ideal(problem: Problem) -> dict[str, float | None]:
|
|
219
265
|
"""Compute the corrected ideal point depending if an objective function is to be maximized or not.
|
|
220
266
|
|
|
@@ -235,12 +281,11 @@ def get_corrected_ideal(problem: Problem) -> dict[str, float | None]:
|
|
|
235
281
|
msg = "Some of the objectives have not a defined ideal value."
|
|
236
282
|
raise ValueError(msg)
|
|
237
283
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
284
|
+
return {
|
|
285
|
+
objective.symbol: objective.ideal if not objective.maximize else -objective.ideal
|
|
286
|
+
for objective in problem.objectives
|
|
241
287
|
}
|
|
242
288
|
|
|
243
|
-
return ideal_point
|
|
244
289
|
|
|
245
290
|
def get_corrected_nadir(problem: Problem) -> dict[str, float | None]:
|
|
246
291
|
"""Compute the corrected nadir point depending if an objective function is to be maximized or not.
|
|
@@ -262,28 +307,27 @@ def get_corrected_nadir(problem: Problem) -> dict[str, float | None]:
|
|
|
262
307
|
msg = "Some of the objectives have not a defined nadir value."
|
|
263
308
|
raise ValueError(msg)
|
|
264
309
|
|
|
265
|
-
|
|
310
|
+
return {
|
|
266
311
|
objective.symbol: objective.nadir if not objective.maximize else -objective.nadir
|
|
267
312
|
for objective in problem.objectives
|
|
268
313
|
}
|
|
269
314
|
|
|
270
|
-
return nadir_point
|
|
271
315
|
|
|
272
|
-
def
|
|
273
|
-
"""
|
|
316
|
+
def flip_maximized_objective_values(problem: Problem, objective_values: dict[str, float]) -> dict[str, float]:
|
|
317
|
+
"""Flips the objective values if the objective function is to be maximized.
|
|
274
318
|
|
|
275
|
-
|
|
276
|
-
related to maximized objective functions by -1.
|
|
319
|
+
Flips the objective values if the objective function is to be maximized by multiplying
|
|
320
|
+
the values related to maximized objective functions by -1.
|
|
277
321
|
|
|
278
322
|
Args:
|
|
279
|
-
problem (Problem): the problem the
|
|
280
|
-
|
|
323
|
+
problem (Problem): the problem the objective values are related to.
|
|
324
|
+
objective_values (dict[str, float]): the objective values to be flipped.
|
|
281
325
|
|
|
282
326
|
Returns:
|
|
283
|
-
dict[str, float]: the
|
|
327
|
+
dict[str, float]: the flipped objective values.
|
|
284
328
|
"""
|
|
285
329
|
return {
|
|
286
|
-
obj.symbol:
|
|
330
|
+
obj.symbol: objective_values[obj.symbol] * -1 if obj.maximize else objective_values[obj.symbol]
|
|
287
331
|
for obj in problem.objectives
|
|
288
332
|
}
|
|
289
333
|
|
|
@@ -318,3 +362,27 @@ def payoff_table_method(problem: Problem, solver: BaseSolver = None) -> tuple[di
|
|
|
318
362
|
else:
|
|
319
363
|
nadir.append(np.max(po_table.T[i]))
|
|
320
364
|
return numpy_array_to_objective_dict(problem, ideal), numpy_array_to_objective_dict(problem, nadir)
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def repair(lower_bounds: dict[str, float], upper_bounds: dict[str, float]) -> Callable[[pl.DataFrame], pl.DataFrame]:
|
|
368
|
+
"""Repairs the offspring by clipping the values to be within the specified bounds.
|
|
369
|
+
|
|
370
|
+
Useful in evolutionary algorithms where offspring may go out of bounds due to crossover or mutation operations.
|
|
371
|
+
|
|
372
|
+
Args:
|
|
373
|
+
offspring (pl.DataFrame): The offspring to be repaired.
|
|
374
|
+
lower_bounds (dict[str, float]): The lower bounds for each variable.
|
|
375
|
+
upper_bounds (dict[str, float]): The upper bounds for each variable.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Callable[[pl.DataFrame], pl.DataFrame]: A function that takes a DataFrame and returns a repaired DataFrame.
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
def actual_repair(offspring: pl.DataFrame) -> pl.DataFrame:
|
|
382
|
+
for var in offspring.columns:
|
|
383
|
+
offspring = offspring.with_columns(
|
|
384
|
+
pl.col(var).clip(lower_bound=lower_bounds[var], upper_bound=upper_bounds[var])
|
|
385
|
+
)
|
|
386
|
+
return offspring
|
|
387
|
+
|
|
388
|
+
return actual_repair
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import plotly.graph_objects as go
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def scatter_plot_comparison(
|
|
5
|
+
*results, x_key="f_1", y_key="f_2", z_key=None, reference_point=None, names=None
|
|
6
|
+
):
|
|
7
|
+
"""
|
|
8
|
+
Plots multiple sets of data in a scatter plot (2D or 3D depending on the data).
|
|
9
|
+
|
|
10
|
+
Parameters:
|
|
11
|
+
*results: Variable number of result objects, each containing outputs with keys for x, y, and optionally z.
|
|
12
|
+
x_key: Key for the x-axis data in the outputs.
|
|
13
|
+
y_key: Key for the y-axis data in the outputs.
|
|
14
|
+
z_key: Key for the z-axis data in the outputs (optional, for 3D plots).
|
|
15
|
+
reference_point: A dictionary with keys matching x_key, y_key, and optionally z_key, representing the reference point to plot.
|
|
16
|
+
names: List of names for each data set to display in the legend.
|
|
17
|
+
"""
|
|
18
|
+
traces = []
|
|
19
|
+
is_3d = z_key is not None and all(z_key in result.outputs for result in results)
|
|
20
|
+
|
|
21
|
+
if names is None:
|
|
22
|
+
names = [f"Dataset {i+1}" for i in range(len(results))]
|
|
23
|
+
|
|
24
|
+
for i, (result, name) in enumerate(zip(results, names)):
|
|
25
|
+
color = f"hsl({i * 360 / len(results)}, 100%, 50%)" # Generate distinct colors
|
|
26
|
+
if is_3d:
|
|
27
|
+
trace = go.Scatter3d(
|
|
28
|
+
x=result.outputs[x_key],
|
|
29
|
+
y=result.outputs[y_key],
|
|
30
|
+
z=result.outputs[z_key],
|
|
31
|
+
mode="markers",
|
|
32
|
+
marker=dict(size=4, color=color, symbol="circle"),
|
|
33
|
+
name=name,
|
|
34
|
+
)
|
|
35
|
+
else:
|
|
36
|
+
trace = go.Scatter(
|
|
37
|
+
x=result.outputs[x_key],
|
|
38
|
+
y=result.outputs[y_key],
|
|
39
|
+
mode="markers",
|
|
40
|
+
marker=dict(size=8, color=color, symbol="circle"),
|
|
41
|
+
name=name,
|
|
42
|
+
)
|
|
43
|
+
traces.append(trace)
|
|
44
|
+
|
|
45
|
+
if reference_point:
|
|
46
|
+
if is_3d:
|
|
47
|
+
ref_trace = go.Scatter3d(
|
|
48
|
+
x=[reference_point[x_key]],
|
|
49
|
+
y=[reference_point[y_key]],
|
|
50
|
+
z=[reference_point[z_key]],
|
|
51
|
+
mode="markers",
|
|
52
|
+
marker=dict(size=4, color="black", symbol="circle"),
|
|
53
|
+
name="Reference Point",
|
|
54
|
+
)
|
|
55
|
+
else:
|
|
56
|
+
ref_trace = go.Scatter(
|
|
57
|
+
x=[reference_point[x_key]],
|
|
58
|
+
y=[reference_point[y_key]],
|
|
59
|
+
mode="markers",
|
|
60
|
+
marker=dict(size=8, color="black", symbol="circle"),
|
|
61
|
+
name="Reference Point",
|
|
62
|
+
)
|
|
63
|
+
traces.append(ref_trace)
|
|
64
|
+
|
|
65
|
+
fig = go.Figure(data=traces)
|
|
66
|
+
|
|
67
|
+
return fig
|
|
@@ -359,7 +359,7 @@ def utopia_problem_old(problem_name: str = "Forest problem", holding: int = 1) -
|
|
|
359
359
|
objectives=[f_1, f_2, f_3],
|
|
360
360
|
constraints=constraints,
|
|
361
361
|
),
|
|
362
|
-
solver=available_solvers["gurobipy"],
|
|
362
|
+
solver=available_solvers["gurobipy"]["constructor"],
|
|
363
363
|
)
|
|
364
364
|
|
|
365
365
|
print(ideals)
|
|
@@ -1,39 +1,48 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: desdeo
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.1.1
|
|
4
4
|
Summary: DESDEO is a modular and open source framework for interactive multiobjective optimization.
|
|
5
|
-
License: MIT
|
|
5
|
+
License-Expression: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
|
+
Keywords: optimization,decision-support,multiobjective optimization,evolutionary optimization,interactive methods
|
|
6
8
|
Author: Giovanni Misitano
|
|
7
9
|
Author-email: giovanni.a.misitano@jyu.fi
|
|
8
|
-
Requires-Python: >=3.12,<3.
|
|
9
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
10
|
+
Requires-Python: >=3.12,<3.14
|
|
10
11
|
Classifier: Programming Language :: Python :: 3
|
|
11
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
12
|
-
|
|
13
|
-
Requires-Dist:
|
|
14
|
-
Requires-Dist:
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
Requires-Dist:
|
|
19
|
-
Requires-Dist:
|
|
20
|
-
Requires-Dist:
|
|
21
|
-
Requires-Dist:
|
|
22
|
-
Requires-Dist:
|
|
23
|
-
Requires-Dist:
|
|
24
|
-
Requires-Dist:
|
|
25
|
-
Requires-Dist:
|
|
26
|
-
Requires-Dist:
|
|
27
|
-
Requires-Dist:
|
|
28
|
-
Requires-Dist:
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Requires-Dist: bayesian-optimization (>=1.0)
|
|
15
|
+
Requires-Dist: coco-experiment (>=2.8.2)
|
|
16
|
+
Requires-Dist: cvxpy[scip] (>=1.6.4)
|
|
17
|
+
Requires-Dist: greenlet (>=3.1.1)
|
|
18
|
+
Requires-Dist: gurobipy (>=12.0.0)
|
|
19
|
+
Requires-Dist: moocore (>=0.1.7)
|
|
20
|
+
Requires-Dist: nevergrad (>=1.0.12)
|
|
21
|
+
Requires-Dist: numba (>=0.61.0)
|
|
22
|
+
Requires-Dist: numpy (>=2.2.0)
|
|
23
|
+
Requires-Dist: plotly (>=6.0)
|
|
24
|
+
Requires-Dist: polars (==1.30)
|
|
25
|
+
Requires-Dist: pyarrow (>=20.0.0)
|
|
26
|
+
Requires-Dist: pydantic (>=2.9)
|
|
27
|
+
Requires-Dist: pydantic-settings (>=2.9)
|
|
28
|
+
Requires-Dist: pymoo (>=0.6.1.2)
|
|
29
|
+
Requires-Dist: pyomo (>=6.8)
|
|
30
|
+
Requires-Dist: pyparsing (>=3.0)
|
|
31
|
+
Requires-Dist: requests (>=2.32.0)
|
|
32
|
+
Requires-Dist: scipy (>=1.11.4)
|
|
33
|
+
Requires-Dist: shap (>=0.47.0)
|
|
34
|
+
Requires-Dist: sympy (>=1.0)
|
|
35
|
+
Requires-Dist: tsp-solver (>=0.1)
|
|
36
|
+
Requires-Dist: websockets (>=15.0.1)
|
|
37
|
+
Project-URL: Bug tracker, https://github.com/industrial-optimization-group/DESDEO/issues
|
|
38
|
+
Project-URL: Documentation, https://desdeo.readthedocs.io/en/latest/
|
|
39
|
+
Project-URL: Homepage, https://github.com/industrial-optimization-group/DESDEO
|
|
40
|
+
Project-URL: Repository, https://github.com/industrial-optimization-group/DESDEO
|
|
29
41
|
Description-Content-Type: text/markdown
|
|
30
42
|
|
|
43
|
+
[](https://discord.gg/uGCEgQTJyY) [](https://desdeo.readthedocs.io/en/latest/) 
|
|
44
|
+
|
|
31
45
|
# DESDEO: the open-source software framework for interactive multiobjective optimization
|
|
32
|
-
|
|
33
|
-
[](https://desdeo.readthedocs.io/en/latest/) 
|
|
34
|
-
|
|
35
|
-
[](https://discord.gg/TgSnUmzv5M)
|
|
36
|
-
|
|
37
46
|
## Introduction
|
|
38
47
|
|
|
39
48
|
DESDEO is an open-source framework for interactive multiobjective optimization
|
|
@@ -108,19 +117,27 @@ currently the case with the web-API.
|
|
|
108
117
|
|
|
109
118
|
## Installation instructions
|
|
110
119
|
|
|
111
|
-
|
|
120
|
+
DESDEO is available on PyPI to be installed via pip:
|
|
121
|
+
|
|
122
|
+
```bash
|
|
123
|
+
pip install desdeo
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
However, some of DESDEO's features rely on 3rd party optimizers, which should be available on your system.
|
|
127
|
+
To read more on these, and on instructions on how to install the latest version of DESDEO directly from Github,
|
|
128
|
+
[check out the documentation](https://desdeo.readthedocs.io/en/latest/howtoguides/installing/).
|
|
112
129
|
|
|
113
130
|
## Documentation
|
|
114
131
|
|
|
115
132
|
Care has been taken to make sure DESDEO is well documented, making it accessible
|
|
116
133
|
to both newcomers and seasoned users alike. [The documentation of DESDEO is
|
|
117
|
-
available online.](https://desdeo.readthedocs.io/en/
|
|
134
|
+
available online.](https://desdeo.readthedocs.io/en/latest/)
|
|
118
135
|
|
|
119
136
|
## Contributing
|
|
120
137
|
|
|
121
138
|
As DESDEO is an open source-project, anybody is welcome to contribute.
|
|
122
139
|
An extensive tutorial to get started contributing to DESDEO
|
|
123
|
-
[is available in the documentation](https://desdeo.readthedocs.io/en/
|
|
140
|
+
[is available in the documentation](https://desdeo.readthedocs.io/en/latest/tutorials/contributing/).
|
|
124
141
|
Be sure to check it out!
|
|
125
142
|
|
|
126
143
|
For additional support for contributing to DESDEO,
|