desdeo 1.1.3__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.1.3.dist-info → desdeo-2.0.0.dist-info}/WHEEL +1 -1
- desdeo-1.1.3.dist-info/METADATA +0 -18
- desdeo-1.1.3.dist-info/RECORD +0 -4
|
@@ -0,0 +1,701 @@
|
|
|
1
|
+
"""Tests related to the SQLModels."""
|
|
2
|
+
|
|
3
|
+
from sqlmodel import Session, select
|
|
4
|
+
|
|
5
|
+
from desdeo.api.models import (
|
|
6
|
+
ArchiveEntryBase,
|
|
7
|
+
ArchiveEntryDB,
|
|
8
|
+
Bounds,
|
|
9
|
+
ConstantDB,
|
|
10
|
+
ConstraintDB,
|
|
11
|
+
DiscreteRepresentationDB,
|
|
12
|
+
ExtraFunctionDB,
|
|
13
|
+
InteractiveSessionDB,
|
|
14
|
+
ObjectiveDB,
|
|
15
|
+
PreferenceDB,
|
|
16
|
+
ProblemDB,
|
|
17
|
+
ReferencePoint,
|
|
18
|
+
RPMState,
|
|
19
|
+
ScalarizationFunctionDB,
|
|
20
|
+
SimulatorDB,
|
|
21
|
+
StateDB,
|
|
22
|
+
TensorConstantDB,
|
|
23
|
+
TensorVariableDB,
|
|
24
|
+
User,
|
|
25
|
+
VariableDB,
|
|
26
|
+
)
|
|
27
|
+
from desdeo.mcdm import rpm_solve_solutions
|
|
28
|
+
from desdeo.problem.schema import (
|
|
29
|
+
Constant,
|
|
30
|
+
Constraint,
|
|
31
|
+
ConstraintTypeEnum,
|
|
32
|
+
DiscreteRepresentation,
|
|
33
|
+
ExtraFunction,
|
|
34
|
+
Objective,
|
|
35
|
+
ObjectiveTypeEnum,
|
|
36
|
+
Problem,
|
|
37
|
+
ScalarizationFunction,
|
|
38
|
+
Simulator,
|
|
39
|
+
TensorConstant,
|
|
40
|
+
TensorVariable,
|
|
41
|
+
Variable,
|
|
42
|
+
VariableTypeEnum,
|
|
43
|
+
)
|
|
44
|
+
from desdeo.problem.testproblems import (
|
|
45
|
+
binh_and_korn,
|
|
46
|
+
dtlz2,
|
|
47
|
+
momip_ti2,
|
|
48
|
+
momip_ti7,
|
|
49
|
+
nimbus_test_problem,
|
|
50
|
+
pareto_navigator_test_problem,
|
|
51
|
+
re21,
|
|
52
|
+
re22,
|
|
53
|
+
re23,
|
|
54
|
+
re24,
|
|
55
|
+
river_pollution_problem,
|
|
56
|
+
river_pollution_problem_discrete,
|
|
57
|
+
simple_data_problem,
|
|
58
|
+
simple_knapsack,
|
|
59
|
+
simple_knapsack_vectors,
|
|
60
|
+
simple_linear_test_problem,
|
|
61
|
+
simple_scenario_test_problem,
|
|
62
|
+
simple_test_problem,
|
|
63
|
+
spanish_sustainability_problem,
|
|
64
|
+
zdt1,
|
|
65
|
+
)
|
|
66
|
+
from desdeo.tools import available_solvers
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def compare_models(
|
|
70
|
+
model_1,
|
|
71
|
+
model_2,
|
|
72
|
+
unordered_fields=None,
|
|
73
|
+
) -> bool:
|
|
74
|
+
"""Compares two Pydantic models.
|
|
75
|
+
|
|
76
|
+
Args:
|
|
77
|
+
model_1 (Any): Pydantic model 1.
|
|
78
|
+
model_2 (Any): Pydantic model 2.
|
|
79
|
+
unordered_fields (list[str]): field names that are unordered and should be compared for
|
|
80
|
+
having the same contents.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
bool: Whether the two models have identical contents.
|
|
84
|
+
"""
|
|
85
|
+
if unordered_fields is None:
|
|
86
|
+
unordered_fields = [
|
|
87
|
+
"variables",
|
|
88
|
+
"constants",
|
|
89
|
+
"objectives",
|
|
90
|
+
"constraints",
|
|
91
|
+
"extra_funcs",
|
|
92
|
+
"simulators",
|
|
93
|
+
"scenario_keys",
|
|
94
|
+
]
|
|
95
|
+
|
|
96
|
+
dict_1 = model_1.model_dump()
|
|
97
|
+
dict_2 = model_2.model_dump()
|
|
98
|
+
|
|
99
|
+
for field in unordered_fields:
|
|
100
|
+
if field in dict_1 and field in dict_2 and isinstance(dict_1[field], list) and isinstance(dict_2[field], list):
|
|
101
|
+
if len(dict_1[field]) != len(dict_2[field]):
|
|
102
|
+
return False
|
|
103
|
+
|
|
104
|
+
for key_1, key_2 in zip(dict_1, dict_2, strict=True):
|
|
105
|
+
if key_1 not in dict_2 or key_2 not in dict_1:
|
|
106
|
+
return False
|
|
107
|
+
|
|
108
|
+
if dict_1[key_1] != dict_1[key_2]:
|
|
109
|
+
return False
|
|
110
|
+
|
|
111
|
+
if dict_2[key_1] != dict_2[key_2]:
|
|
112
|
+
return False
|
|
113
|
+
|
|
114
|
+
del dict_1[field], dict_2[field]
|
|
115
|
+
|
|
116
|
+
return dict_1 == dict_2
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def test_tensor_constant(session_and_user: dict[str, Session | list[User]]):
|
|
120
|
+
"""Test that a tensor constant can be transformed to an SQLModel and back after adding it to the database."""
|
|
121
|
+
session = session_and_user["session"]
|
|
122
|
+
|
|
123
|
+
t_tensor = TensorConstant(name="tensor", symbol="T", shape=[2, 2], values=[[1, 2], [3, 4]])
|
|
124
|
+
t_tensor_dump = t_tensor.model_dump()
|
|
125
|
+
t_tensor_dump["problem_id"] = 1
|
|
126
|
+
|
|
127
|
+
db_tensor = TensorConstantDB.model_validate(t_tensor_dump)
|
|
128
|
+
|
|
129
|
+
session.add(db_tensor)
|
|
130
|
+
session.commit()
|
|
131
|
+
|
|
132
|
+
statement = select(TensorConstantDB).where(TensorConstantDB.problem_id == 1)
|
|
133
|
+
from_db_tensor = session.exec(statement).first()
|
|
134
|
+
|
|
135
|
+
# check that original added TensorConstant and fetched match
|
|
136
|
+
assert db_tensor == from_db_tensor
|
|
137
|
+
|
|
138
|
+
from_db_tensor_dump = from_db_tensor.model_dump()
|
|
139
|
+
t_tensor_validated = TensorConstant.model_validate(from_db_tensor_dump)
|
|
140
|
+
|
|
141
|
+
assert t_tensor_validated == t_tensor
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def test_constant(session_and_user: dict[str, Session | list[User]]):
|
|
145
|
+
"""Test that a scalar constant can be transformed to an SQLModel and back after adding it to the database."""
|
|
146
|
+
session = session_and_user["session"]
|
|
147
|
+
|
|
148
|
+
constant = Constant(name="constant", symbol="c", value=69.420)
|
|
149
|
+
constant_dump = constant.model_dump()
|
|
150
|
+
constant_dump["problem_id"] = 1
|
|
151
|
+
|
|
152
|
+
db_constant = ConstantDB.model_validate(constant_dump)
|
|
153
|
+
|
|
154
|
+
session.add(db_constant)
|
|
155
|
+
session.commit()
|
|
156
|
+
|
|
157
|
+
statement = select(ConstantDB).where(ConstantDB.problem_id == 1)
|
|
158
|
+
from_db_constant = session.exec(statement).first()
|
|
159
|
+
|
|
160
|
+
assert db_constant == from_db_constant
|
|
161
|
+
|
|
162
|
+
from_db_constant_dump = from_db_constant.model_dump()
|
|
163
|
+
constant_validated = Constant.model_validate(from_db_constant_dump)
|
|
164
|
+
|
|
165
|
+
assert constant_validated == constant
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def test_variable(session_and_user: dict[str, Session | list[User]]):
|
|
169
|
+
"""Test that a scalar variable can be transformed to an SQLModel and back after adding it to the database."""
|
|
170
|
+
session = session_and_user["session"]
|
|
171
|
+
|
|
172
|
+
variable = Variable(
|
|
173
|
+
name="test variable",
|
|
174
|
+
symbol="x_1",
|
|
175
|
+
initial_value=69,
|
|
176
|
+
lowerbound=42,
|
|
177
|
+
upperbound=420,
|
|
178
|
+
variable_type=VariableTypeEnum.integer,
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
variable_dump = variable.model_dump()
|
|
182
|
+
variable_dump["problem_id"] = 1
|
|
183
|
+
|
|
184
|
+
db_variable = VariableDB.model_validate(variable_dump)
|
|
185
|
+
|
|
186
|
+
session.add(db_variable)
|
|
187
|
+
session.commit()
|
|
188
|
+
session.refresh(db_variable)
|
|
189
|
+
|
|
190
|
+
from_db_variable = session.get(VariableDB, db_variable.id)
|
|
191
|
+
|
|
192
|
+
assert db_variable == from_db_variable
|
|
193
|
+
|
|
194
|
+
from_db_variable_dump = from_db_variable.model_dump()
|
|
195
|
+
variable_validated = Variable.model_validate(from_db_variable_dump)
|
|
196
|
+
|
|
197
|
+
assert variable_validated == variable
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def test_tensor_variable(session_and_user: dict[str, Session | list[User]]):
|
|
201
|
+
"""Test that a tensor variable can be transformed to an SQLModel and back after adding it to the database."""
|
|
202
|
+
session = session_and_user["session"]
|
|
203
|
+
|
|
204
|
+
t_variable = TensorVariable(
|
|
205
|
+
name="test variable",
|
|
206
|
+
symbol="X",
|
|
207
|
+
shape=[2, 2],
|
|
208
|
+
initial_values=[[1, 2], [3, 4]],
|
|
209
|
+
lowerbounds=[[0, 1], [1, 0]],
|
|
210
|
+
upperbounds=[[99, 89], [88, 77]],
|
|
211
|
+
variable_type=VariableTypeEnum.integer,
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
t_variable_dump = t_variable.model_dump()
|
|
215
|
+
t_variable_dump["problem_id"] = 69
|
|
216
|
+
|
|
217
|
+
db_t_variable = TensorVariableDB.model_validate(t_variable_dump)
|
|
218
|
+
|
|
219
|
+
session.add(db_t_variable)
|
|
220
|
+
session.commit()
|
|
221
|
+
session.refresh(db_t_variable)
|
|
222
|
+
|
|
223
|
+
from_db_t_variable = session.get(TensorVariableDB, db_t_variable.id)
|
|
224
|
+
|
|
225
|
+
assert db_t_variable == from_db_t_variable
|
|
226
|
+
|
|
227
|
+
from_db_t_variable_dump = from_db_t_variable.model_dump()
|
|
228
|
+
t_variable_validated = TensorVariable.model_validate(from_db_t_variable_dump)
|
|
229
|
+
|
|
230
|
+
assert t_variable_validated == t_variable
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def test_objective(session_and_user: dict[str, Session | list[User]]):
|
|
234
|
+
"""Test that an objective can be transformed to an SQLModel and back after adding it to the database."""
|
|
235
|
+
session = session_and_user["session"]
|
|
236
|
+
|
|
237
|
+
objective = Objective(
|
|
238
|
+
name="Test Objective",
|
|
239
|
+
symbol="f_1",
|
|
240
|
+
func="x_1 + x_2 + Sin(y)",
|
|
241
|
+
objective_type=ObjectiveTypeEnum.analytical,
|
|
242
|
+
ideal=10.5,
|
|
243
|
+
nadir=20.0,
|
|
244
|
+
maximize=False,
|
|
245
|
+
scenario_keys=["s_1", "s_2"],
|
|
246
|
+
unit="m",
|
|
247
|
+
is_convex=False,
|
|
248
|
+
is_linear=True,
|
|
249
|
+
is_twice_differentiable=True,
|
|
250
|
+
simulator_path="/dev/null",
|
|
251
|
+
surrogates=["/var/log", "/dev/sda/sda1"],
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
objective_dump = objective.model_dump()
|
|
255
|
+
objective_dump["problem_id"] = 420 # yes
|
|
256
|
+
|
|
257
|
+
db_objective = ObjectiveDB.model_validate(objective_dump)
|
|
258
|
+
|
|
259
|
+
session.add(db_objective)
|
|
260
|
+
session.commit()
|
|
261
|
+
session.refresh(db_objective)
|
|
262
|
+
|
|
263
|
+
from_db_objective = session.get(ObjectiveDB, db_objective.id)
|
|
264
|
+
|
|
265
|
+
assert db_objective == from_db_objective
|
|
266
|
+
|
|
267
|
+
from_db_objective_dump = from_db_objective.model_dump()
|
|
268
|
+
objective_validated = Objective.model_validate(from_db_objective_dump)
|
|
269
|
+
|
|
270
|
+
assert objective_validated == objective
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
def test_constraint(session_and_user: dict[str, Session | list[User]]):
|
|
274
|
+
"""Test that an constraint can be transformed to an SQLModel and back after adding it to the database."""
|
|
275
|
+
session = session_and_user["session"]
|
|
276
|
+
|
|
277
|
+
constraint = Constraint(
|
|
278
|
+
name="Test Constraint",
|
|
279
|
+
symbol="g_1",
|
|
280
|
+
func="x_1 + x_1 + x_1 - 10",
|
|
281
|
+
cons_type=ConstraintTypeEnum.LTE,
|
|
282
|
+
is_convex=True,
|
|
283
|
+
is_linear=False,
|
|
284
|
+
is_twice_differentiable=False,
|
|
285
|
+
scenario_keys=["Abloy", "MasterLock", "MasterLockToOpenMasterLock"],
|
|
286
|
+
simulator_path="/dev/null/aaaaaaaaaa",
|
|
287
|
+
surrogates=["/var/log", "/dev/sda/sda1/no"],
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
constraint_dump = constraint.model_dump()
|
|
291
|
+
constraint_dump["problem_id"] = 72
|
|
292
|
+
|
|
293
|
+
db_constraint = ConstraintDB.model_validate(constraint_dump)
|
|
294
|
+
|
|
295
|
+
session.add(db_constraint)
|
|
296
|
+
session.commit()
|
|
297
|
+
session.refresh(db_constraint)
|
|
298
|
+
|
|
299
|
+
from_db_constraint = session.get(ConstraintDB, db_constraint.id)
|
|
300
|
+
|
|
301
|
+
assert db_constraint == from_db_constraint
|
|
302
|
+
|
|
303
|
+
from_db_constraint_dump = from_db_constraint.model_dump()
|
|
304
|
+
constraint_validated = Constraint.model_validate(from_db_constraint_dump)
|
|
305
|
+
|
|
306
|
+
assert constraint_validated == constraint
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def test_scalarization_function(session_and_user: dict[str, Session | list[User]]):
|
|
310
|
+
"""Test that a scalarization function can be transformed to an SQLModel and back after adding it to the database."""
|
|
311
|
+
session = session_and_user["session"]
|
|
312
|
+
|
|
313
|
+
scalarization = ScalarizationFunction(
|
|
314
|
+
name="Test ScalarizationFunction",
|
|
315
|
+
symbol="s_1",
|
|
316
|
+
func="x_1 + x_1 + x_1 - 10 - 99999 + Sin(y_3)",
|
|
317
|
+
is_convex=True,
|
|
318
|
+
is_linear=True,
|
|
319
|
+
is_twice_differentiable=False,
|
|
320
|
+
scenario_keys=["Abloy", "MasterLock", "MasterLockToOpenMasterLock", "MyHandsHurt"],
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
scalarization_dump = scalarization.model_dump()
|
|
324
|
+
scalarization_dump["problem_id"] = 2
|
|
325
|
+
|
|
326
|
+
db_scalarization = ScalarizationFunctionDB.model_validate(scalarization_dump)
|
|
327
|
+
|
|
328
|
+
session.add(db_scalarization)
|
|
329
|
+
session.commit()
|
|
330
|
+
session.refresh(db_scalarization)
|
|
331
|
+
|
|
332
|
+
from_db_scalarization = session.get(ScalarizationFunctionDB, db_scalarization.id)
|
|
333
|
+
|
|
334
|
+
assert db_scalarization == from_db_scalarization
|
|
335
|
+
|
|
336
|
+
from_db_scalarization_dump = from_db_scalarization.model_dump()
|
|
337
|
+
scalarization_validated = ScalarizationFunction.model_validate(from_db_scalarization_dump)
|
|
338
|
+
|
|
339
|
+
assert scalarization_validated == scalarization
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def test_extra_function(session_and_user: dict[str, Session | list[User]]):
|
|
343
|
+
"""Test that an extra function can be transformed to an SQLModel and back after adding it to the database."""
|
|
344
|
+
session = session_and_user["session"]
|
|
345
|
+
|
|
346
|
+
extra = ExtraFunction(
|
|
347
|
+
name="Test ExtraFunction",
|
|
348
|
+
symbol="extra_1",
|
|
349
|
+
func="x_1 + x_2 + x_9000 - 10 - 99999 + Cos(y_3)",
|
|
350
|
+
is_convex=False,
|
|
351
|
+
is_linear=False,
|
|
352
|
+
is_twice_differentiable=True,
|
|
353
|
+
scenario_keys=["Abloy", "MasterLock", "MasterLockToOpenMasterLock", "MyHandsHurt", "RunningOutOfIdeas"],
|
|
354
|
+
)
|
|
355
|
+
|
|
356
|
+
extra_dump = extra.model_dump()
|
|
357
|
+
extra_dump["problem_id"] = 5
|
|
358
|
+
|
|
359
|
+
db_extra = ExtraFunctionDB.model_validate(extra_dump)
|
|
360
|
+
|
|
361
|
+
session.add(db_extra)
|
|
362
|
+
session.commit()
|
|
363
|
+
session.refresh(db_extra)
|
|
364
|
+
|
|
365
|
+
from_db_extra = session.get(ExtraFunctionDB, db_extra.id)
|
|
366
|
+
|
|
367
|
+
assert db_extra == from_db_extra
|
|
368
|
+
|
|
369
|
+
from_db_extra_dump = from_db_extra.model_dump()
|
|
370
|
+
extra_validated = ExtraFunction.model_validate(from_db_extra_dump)
|
|
371
|
+
|
|
372
|
+
assert extra_validated == extra
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def test_discrete_representation(session_and_user: dict[str, Session | list[User]]):
|
|
376
|
+
"""Test that a DiscreteRepresentation can be transformed to an SQLModel and back after adding it to the database."""
|
|
377
|
+
session = session_and_user["session"]
|
|
378
|
+
|
|
379
|
+
discrete = DiscreteRepresentation(
|
|
380
|
+
variable_values={"x_1": [1, 2, 3, 4, 5], "x_2": [6, 7, 8, 9, 10]},
|
|
381
|
+
objective_values={"f_1": [0.5, 1.0, 2.0, 3.5, 9], "f_2": [-1, -2, -3, -4, -5]},
|
|
382
|
+
non_dominated=True,
|
|
383
|
+
)
|
|
384
|
+
|
|
385
|
+
discrete_dump = discrete.model_dump()
|
|
386
|
+
discrete_dump["problem_id"] = 3
|
|
387
|
+
|
|
388
|
+
db_discrete = DiscreteRepresentationDB.model_validate(discrete_dump)
|
|
389
|
+
|
|
390
|
+
session.add(db_discrete)
|
|
391
|
+
session.commit()
|
|
392
|
+
session.refresh(db_discrete)
|
|
393
|
+
|
|
394
|
+
from_db_discrete = session.get(DiscreteRepresentationDB, db_discrete.id)
|
|
395
|
+
|
|
396
|
+
assert db_discrete == from_db_discrete
|
|
397
|
+
|
|
398
|
+
from_db_discrete_dump = from_db_discrete.model_dump()
|
|
399
|
+
discrete_validated = DiscreteRepresentation.model_validate(from_db_discrete_dump)
|
|
400
|
+
|
|
401
|
+
assert discrete_validated == discrete
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def test_simulator(session_and_user: dict[str, Session | list[User]]):
|
|
405
|
+
"""Test that a Simulator can be transformed to an SQLModel and back after adding it to the database."""
|
|
406
|
+
session = session_and_user["session"]
|
|
407
|
+
|
|
408
|
+
simulator = Simulator(
|
|
409
|
+
file="/my/favorite/simulator.exe",
|
|
410
|
+
name="simulator",
|
|
411
|
+
symbol="simu",
|
|
412
|
+
parameter_options={"param1": 69, "nice": True},
|
|
413
|
+
)
|
|
414
|
+
|
|
415
|
+
simulator_dump = simulator.model_dump()
|
|
416
|
+
simulator_dump["problem_id"] = 2
|
|
417
|
+
|
|
418
|
+
db_simulator = SimulatorDB.model_validate(simulator_dump)
|
|
419
|
+
|
|
420
|
+
session.add(db_simulator)
|
|
421
|
+
session.commit()
|
|
422
|
+
session.refresh(db_simulator)
|
|
423
|
+
|
|
424
|
+
from_db_simulator = session.get(SimulatorDB, db_simulator.id)
|
|
425
|
+
|
|
426
|
+
assert db_simulator == from_db_simulator
|
|
427
|
+
|
|
428
|
+
from_db_simulator_dump = from_db_simulator.model_dump()
|
|
429
|
+
simulator_validated = Simulator.model_validate(from_db_simulator_dump)
|
|
430
|
+
|
|
431
|
+
assert simulator_validated == simulator
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def test_from_pydantic(session_and_user: dict[str, Session | list[User]]):
|
|
435
|
+
"""Test that a problem can be added and fetched from the database correctly."""
|
|
436
|
+
session = session_and_user["session"]
|
|
437
|
+
user = session_and_user["user"]
|
|
438
|
+
|
|
439
|
+
problem_binh = binh_and_korn()
|
|
440
|
+
|
|
441
|
+
problemdb = ProblemDB.from_problem(problem_binh, user=user)
|
|
442
|
+
session.add(problemdb)
|
|
443
|
+
session.commit()
|
|
444
|
+
session.refresh(problemdb)
|
|
445
|
+
|
|
446
|
+
from_db_problem = session.get(ProblemDB, problemdb.id)
|
|
447
|
+
|
|
448
|
+
assert compare_models(problemdb, from_db_problem)
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def test_from_problem_to_d_and_back(session_and_user: dict[str, Session | list[User]]):
|
|
452
|
+
"""Test that Problem converts to ProblemDB and back."""
|
|
453
|
+
session = session_and_user["session"]
|
|
454
|
+
user = session_and_user["user"]
|
|
455
|
+
|
|
456
|
+
problems = [
|
|
457
|
+
binh_and_korn(),
|
|
458
|
+
river_pollution_problem(),
|
|
459
|
+
simple_knapsack(),
|
|
460
|
+
simple_data_problem(),
|
|
461
|
+
simple_scenario_test_problem(),
|
|
462
|
+
re24(),
|
|
463
|
+
simple_knapsack_vectors(),
|
|
464
|
+
spanish_sustainability_problem(),
|
|
465
|
+
zdt1(10),
|
|
466
|
+
dtlz2(5, 3),
|
|
467
|
+
momip_ti2(),
|
|
468
|
+
momip_ti7(),
|
|
469
|
+
nimbus_test_problem(),
|
|
470
|
+
pareto_navigator_test_problem(),
|
|
471
|
+
river_pollution_problem_discrete(),
|
|
472
|
+
simple_test_problem(),
|
|
473
|
+
simple_linear_test_problem(),
|
|
474
|
+
re21(),
|
|
475
|
+
re22(),
|
|
476
|
+
re23(),
|
|
477
|
+
]
|
|
478
|
+
|
|
479
|
+
for problem in problems:
|
|
480
|
+
# convert to SQLModel
|
|
481
|
+
problem_db = ProblemDB.from_problem(problem, user=user)
|
|
482
|
+
|
|
483
|
+
session.add(problem_db)
|
|
484
|
+
session.commit()
|
|
485
|
+
session.refresh(problem_db)
|
|
486
|
+
|
|
487
|
+
from_db = session.get(ProblemDB, problem_db.id)
|
|
488
|
+
|
|
489
|
+
# Back to pure pydantic
|
|
490
|
+
problem_db = Problem.from_problemdb(from_db)
|
|
491
|
+
|
|
492
|
+
# check that problems are equal
|
|
493
|
+
assert compare_models(problem, problem_db)
|
|
494
|
+
|
|
495
|
+
|
|
496
|
+
def test_archive_entry(session_and_user: dict[str, Session | list[User]]):
|
|
497
|
+
"""Test that the archive works as intended."""
|
|
498
|
+
session = session_and_user["session"]
|
|
499
|
+
user = session_and_user["user"]
|
|
500
|
+
|
|
501
|
+
problem = dtlz2(n_variables=5, n_objectives=3)
|
|
502
|
+
problem_db = ProblemDB.from_problem(problem, user)
|
|
503
|
+
|
|
504
|
+
session.add(problem_db)
|
|
505
|
+
session.commit()
|
|
506
|
+
session.refresh(problem_db)
|
|
507
|
+
|
|
508
|
+
variable_values = {"x_1": 0.3, "x_2": 0.8, "x_3": 0.1, "x_4": 0.6, "x_5": 0.9}
|
|
509
|
+
objective_values = {"f_1": 1.2, "f_2": 0.9, "f_3": 1.5}
|
|
510
|
+
constraint_values = {"g_1": 0.1}
|
|
511
|
+
extra_func_values = {"extra_1": 5000, "extra_2": 600000}
|
|
512
|
+
|
|
513
|
+
archive_entry = ArchiveEntryBase(variable_values=variable_values, objective_values=objective_values)
|
|
514
|
+
|
|
515
|
+
archive_entry_db = ArchiveEntryDB.model_validate(
|
|
516
|
+
archive_entry,
|
|
517
|
+
update={
|
|
518
|
+
"user_id": user.id,
|
|
519
|
+
"problem_id": problem_db.id,
|
|
520
|
+
"constraint_values": constraint_values,
|
|
521
|
+
"extra_func_values": extra_func_values,
|
|
522
|
+
},
|
|
523
|
+
)
|
|
524
|
+
|
|
525
|
+
session.add(archive_entry_db)
|
|
526
|
+
session.commit()
|
|
527
|
+
session.refresh(archive_entry_db)
|
|
528
|
+
|
|
529
|
+
from_db = session.get(ArchiveEntryDB, archive_entry_db.id)
|
|
530
|
+
|
|
531
|
+
assert from_db.user_id == user.id
|
|
532
|
+
assert from_db.problem_id == problem_db.id
|
|
533
|
+
assert from_db == user.archive[0]
|
|
534
|
+
assert compare_models(from_db.problem, problem_db)
|
|
535
|
+
assert from_db.variable_values == variable_values
|
|
536
|
+
assert from_db.objective_values == objective_values
|
|
537
|
+
assert from_db.constraint_values == constraint_values
|
|
538
|
+
assert from_db.extra_func_values == extra_func_values
|
|
539
|
+
|
|
540
|
+
|
|
541
|
+
def test_preference_models(session_and_user: dict[str, Session | list[User]]):
|
|
542
|
+
"""Test that the archive works as intended."""
|
|
543
|
+
session = session_and_user["session"]
|
|
544
|
+
user = session_and_user["user"]
|
|
545
|
+
|
|
546
|
+
problem = ProblemDB.from_problem(dtlz2(5, 3), user=user)
|
|
547
|
+
|
|
548
|
+
session.add(problem)
|
|
549
|
+
session.commit()
|
|
550
|
+
session.refresh(problem)
|
|
551
|
+
|
|
552
|
+
aspiration_levels = {"f_1": 0.1, "f_2": 5, "f_3": -3.1}
|
|
553
|
+
lower_bounds = {"f_1": -4.1, "f_2": 0, "f_3": 2.2}
|
|
554
|
+
upper_bounds = {"f_1": 2.1, "f_2": 0.1, "f_3": 12.2}
|
|
555
|
+
|
|
556
|
+
reference_point = ReferencePoint(aspiration_levels=aspiration_levels)
|
|
557
|
+
bounds = Bounds(lower_bounds=lower_bounds, upper_bounds=upper_bounds)
|
|
558
|
+
|
|
559
|
+
reference_point_db = PreferenceDB(user_id=user.id, problem_id=problem.id, preference=reference_point)
|
|
560
|
+
bounds_db = PreferenceDB(user_id=user.id, problem_id=problem.id, preference=bounds)
|
|
561
|
+
|
|
562
|
+
session.add(reference_point_db)
|
|
563
|
+
session.add(bounds_db)
|
|
564
|
+
session.commit()
|
|
565
|
+
session.refresh(reference_point_db)
|
|
566
|
+
session.refresh(bounds_db)
|
|
567
|
+
|
|
568
|
+
from_db_ref_point = session.get(PreferenceDB, reference_point_db.id)
|
|
569
|
+
from_db_bounds = session.get(PreferenceDB, bounds_db.id)
|
|
570
|
+
|
|
571
|
+
assert from_db_ref_point.preference.aspiration_levels == aspiration_levels
|
|
572
|
+
assert from_db_bounds.preference.lower_bounds == lower_bounds
|
|
573
|
+
assert from_db_bounds.preference.upper_bounds == upper_bounds
|
|
574
|
+
|
|
575
|
+
assert from_db_ref_point.problem == problem
|
|
576
|
+
assert from_db_ref_point.problem == problem
|
|
577
|
+
assert from_db_bounds.problem == problem
|
|
578
|
+
|
|
579
|
+
assert from_db_bounds.user == user
|
|
580
|
+
assert from_db_ref_point.user == user
|
|
581
|
+
|
|
582
|
+
assert from_db_bounds.solutions == []
|
|
583
|
+
assert from_db_ref_point.solutions == []
|
|
584
|
+
|
|
585
|
+
|
|
586
|
+
def test_rpm_state(session_and_user: dict[str, Session | list[User]]):
|
|
587
|
+
"""Test the RPM state that it works correctly."""
|
|
588
|
+
session = session_and_user["session"]
|
|
589
|
+
user = session_and_user["user"]
|
|
590
|
+
problem_db = user.problems[0]
|
|
591
|
+
|
|
592
|
+
# create interactive session
|
|
593
|
+
isession = InteractiveSessionDB(user_id=user.id)
|
|
594
|
+
|
|
595
|
+
session.add(isession)
|
|
596
|
+
session.commit()
|
|
597
|
+
session.refresh(isession)
|
|
598
|
+
|
|
599
|
+
# use the reference point method
|
|
600
|
+
asp_levels_1 = {"f_1": 0.4, "f_2": 0.8, "f_3": 0.6}
|
|
601
|
+
|
|
602
|
+
problem = Problem.from_problemdb(problem_db)
|
|
603
|
+
|
|
604
|
+
scalarization_options = None
|
|
605
|
+
solver = "pyomo_bonmin"
|
|
606
|
+
solver_options = None
|
|
607
|
+
|
|
608
|
+
results = rpm_solve_solutions(
|
|
609
|
+
problem,
|
|
610
|
+
asp_levels_1,
|
|
611
|
+
scalarization_options=scalarization_options,
|
|
612
|
+
solver=available_solvers[solver],
|
|
613
|
+
solver_options=solver_options,
|
|
614
|
+
)
|
|
615
|
+
|
|
616
|
+
# create preferences
|
|
617
|
+
|
|
618
|
+
rp_1 = ReferencePoint(aspiration_levels=asp_levels_1)
|
|
619
|
+
preferences = PreferenceDB(user_id=user.id, problem_id=problem_db.id, preference=rp_1)
|
|
620
|
+
|
|
621
|
+
session.add(preferences)
|
|
622
|
+
session.commit()
|
|
623
|
+
session.refresh(preferences)
|
|
624
|
+
|
|
625
|
+
# create state
|
|
626
|
+
|
|
627
|
+
rpm_state = RPMState(
|
|
628
|
+
scalarization_options=scalarization_options,
|
|
629
|
+
solver=solver,
|
|
630
|
+
solver_options=solver_options,
|
|
631
|
+
solver_results=results,
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
state_1 = StateDB(
|
|
635
|
+
problem_id=problem_db.id, preference_id=preferences.id, session_id=isession.id, parent_id=None, state=rpm_state
|
|
636
|
+
)
|
|
637
|
+
|
|
638
|
+
session.add(state_1)
|
|
639
|
+
session.commit()
|
|
640
|
+
session.refresh(state_1)
|
|
641
|
+
|
|
642
|
+
asp_levels_2 = {"f_1": 0.6, "f_2": 0.4, "f_3": 0.5}
|
|
643
|
+
|
|
644
|
+
problem = Problem.from_problemdb(problem_db)
|
|
645
|
+
|
|
646
|
+
scalarization_options = None
|
|
647
|
+
solver = "pyomo_bonmin"
|
|
648
|
+
solver_options = None
|
|
649
|
+
|
|
650
|
+
results = rpm_solve_solutions(
|
|
651
|
+
problem,
|
|
652
|
+
asp_levels_2,
|
|
653
|
+
scalarization_options=scalarization_options,
|
|
654
|
+
solver=available_solvers[solver],
|
|
655
|
+
solver_options=solver_options,
|
|
656
|
+
)
|
|
657
|
+
|
|
658
|
+
# create preferences
|
|
659
|
+
|
|
660
|
+
rp_2 = ReferencePoint(aspiration_levels=asp_levels_2)
|
|
661
|
+
preferences = PreferenceDB(user_id=user.id, problem_id=problem_db.id, preference=rp_2)
|
|
662
|
+
|
|
663
|
+
session.add(preferences)
|
|
664
|
+
session.commit()
|
|
665
|
+
session.refresh(preferences)
|
|
666
|
+
|
|
667
|
+
# create state
|
|
668
|
+
|
|
669
|
+
rpm_state = RPMState(
|
|
670
|
+
scalarization_options=scalarization_options,
|
|
671
|
+
solver=solver,
|
|
672
|
+
solver_options=solver_options,
|
|
673
|
+
solver_results=results,
|
|
674
|
+
)
|
|
675
|
+
|
|
676
|
+
state_2 = StateDB(
|
|
677
|
+
problem_id=problem_db.id,
|
|
678
|
+
preference_id=preferences.id,
|
|
679
|
+
session_id=isession.id,
|
|
680
|
+
parent_id=state_1.id,
|
|
681
|
+
state=rpm_state,
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
session.add(state_2)
|
|
685
|
+
session.commit()
|
|
686
|
+
session.refresh(state_2)
|
|
687
|
+
|
|
688
|
+
assert state_1.parent is None
|
|
689
|
+
assert state_2.parent == state_1
|
|
690
|
+
assert len(state_1.children) == 1
|
|
691
|
+
assert state_1.children[0] == state_2
|
|
692
|
+
|
|
693
|
+
assert state_1.preference.preference == rp_1
|
|
694
|
+
assert state_2.preference.preference == rp_2
|
|
695
|
+
|
|
696
|
+
assert state_2.problem == problem_db
|
|
697
|
+
assert state_2.session.user == user
|
|
698
|
+
|
|
699
|
+
assert state_2.children == []
|
|
700
|
+
assert state_2.parent.problem == problem_db
|
|
701
|
+
assert state_2.parent.session.user == user
|