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
|
@@ -5,29 +5,38 @@ import subprocess
|
|
|
5
5
|
import sys
|
|
6
6
|
from inspect import getfullargspec
|
|
7
7
|
from pathlib import Path
|
|
8
|
+
from urllib.parse import urlparse
|
|
8
9
|
|
|
9
10
|
import joblib
|
|
10
11
|
import numpy as np
|
|
11
12
|
import polars as pl
|
|
12
|
-
|
|
13
|
+
import requests
|
|
13
14
|
|
|
14
15
|
from desdeo.problem import (
|
|
16
|
+
MathParser,
|
|
15
17
|
ObjectiveTypeEnum,
|
|
16
18
|
PolarsEvaluator,
|
|
17
19
|
PolarsEvaluatorModesEnum,
|
|
18
20
|
Problem,
|
|
19
21
|
)
|
|
22
|
+
from desdeo.problem.external import ProviderParams, get_resolver, supported_schemes
|
|
23
|
+
|
|
24
|
+
# external resolver to resolve providers for problems defined externally of DESDEO
|
|
25
|
+
_external_resolver = get_resolver()
|
|
20
26
|
|
|
21
27
|
|
|
22
28
|
class EvaluatorError(Exception):
|
|
23
29
|
"""Error raised when exceptions are encountered in an Evaluator."""
|
|
24
30
|
|
|
25
31
|
|
|
26
|
-
class
|
|
32
|
+
class SimulatorEvaluator:
|
|
27
33
|
"""A class for creating evaluators for simulator based and surrogate based objectives, constraints and extras."""
|
|
28
34
|
|
|
29
|
-
def __init__(
|
|
30
|
-
self,
|
|
35
|
+
def __init__( # noqa: PLR0912
|
|
36
|
+
self,
|
|
37
|
+
problem: Problem,
|
|
38
|
+
params: dict[str, dict] | ProviderParams | None = None,
|
|
39
|
+
surrogate_paths: dict[str, Path] | None = None,
|
|
31
40
|
):
|
|
32
41
|
"""Creating an evaluator for simulator based and surrogate based objectives, constraints and extras.
|
|
33
42
|
|
|
@@ -62,6 +71,15 @@ class Evaluator:
|
|
|
62
71
|
obj.symbol
|
|
63
72
|
for obj in list(filter(lambda x: x.objective_type == ObjectiveTypeEnum.surrogate, problem.objectives))
|
|
64
73
|
]
|
|
74
|
+
if problem.scalarization_funcs is not None:
|
|
75
|
+
parser = MathParser()
|
|
76
|
+
self.scalarization_funcs = [
|
|
77
|
+
(func.symbol, parser.parse(func.func))
|
|
78
|
+
for func in problem.scalarization_funcs
|
|
79
|
+
if func.symbol is not None
|
|
80
|
+
]
|
|
81
|
+
else:
|
|
82
|
+
self.scalarization_funcs = []
|
|
65
83
|
# Gather any constraints' symbols
|
|
66
84
|
if problem.constraints is not None:
|
|
67
85
|
self.analytical_symbols = self.analytical_symbols + [
|
|
@@ -93,7 +111,10 @@ class Evaluator:
|
|
|
93
111
|
|
|
94
112
|
# Gather the possible simulators
|
|
95
113
|
self.simulators = problem.simulators if problem.simulators is not None else []
|
|
114
|
+
|
|
96
115
|
# Gather the possibly given parameters
|
|
116
|
+
if params and not isinstance(params, dict):
|
|
117
|
+
params = params.model_dump()
|
|
97
118
|
self.params = {}
|
|
98
119
|
for sim in self.simulators:
|
|
99
120
|
sim_params = params.get(sim.name, {}) if params is not None else {}
|
|
@@ -136,15 +157,37 @@ class Evaluator:
|
|
|
136
157
|
for sim in self.simulators:
|
|
137
158
|
# gather the possible parameters for the simulator
|
|
138
159
|
params = self.params.get(sim.name, {})
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
160
|
+
if sim.file is not None:
|
|
161
|
+
# call the simulator with the decision variable values and parameters as dicts
|
|
162
|
+
res = subprocess.run(
|
|
163
|
+
[sys.executable, sim.file, "-d", str(xs), "-p", str(params)], capture_output=True, text=True
|
|
164
|
+
)
|
|
165
|
+
if res.returncode == 0:
|
|
166
|
+
# gather the simulation results (a dict) into the results dataframe
|
|
167
|
+
res_df = res_df.hstack(pl.DataFrame(json.loads(res.stdout)))
|
|
168
|
+
else:
|
|
169
|
+
raise EvaluatorError(res.stderr)
|
|
170
|
+
elif sim.url is not None:
|
|
171
|
+
# call the endpoint
|
|
172
|
+
try:
|
|
173
|
+
if isinstance(xs, pl.DataFrame):
|
|
174
|
+
# if xs is a polars dataframe, convert it to a dict
|
|
175
|
+
xs = xs.to_dict(as_series=False)
|
|
176
|
+
scheme = urlparse(sim.url.url).scheme
|
|
177
|
+
if scheme in supported_schemes:
|
|
178
|
+
# desdeo
|
|
179
|
+
res = _external_resolver.evaluate(sim.url.url, params, xs)
|
|
180
|
+
res_df = res_df.hstack(pl.DataFrame(res))
|
|
181
|
+
# parse res
|
|
182
|
+
else:
|
|
183
|
+
# http, https, etc...
|
|
184
|
+
res = requests.get(sim.url.url, auth=sim.url.auth, json={"d": xs, "p": params})
|
|
185
|
+
res.raise_for_status() # raise an error if the request failed
|
|
186
|
+
res_df = res_df.hstack(pl.DataFrame(res.json()))
|
|
187
|
+
except requests.RequestException as e:
|
|
188
|
+
raise EvaluatorError(
|
|
189
|
+
f"Failed to call the simulator at {sim.url}. Is the simulator server running?"
|
|
190
|
+
) from e
|
|
148
191
|
|
|
149
192
|
# Evaluate the minimization form of the objective functions
|
|
150
193
|
min_obj_columns = pl.DataFrame()
|
|
@@ -153,7 +196,11 @@ class Evaluator:
|
|
|
153
196
|
min_obj_columns = min_obj_columns.hstack(
|
|
154
197
|
res_df.select((min_max_mult * pl.col(f"{symbol}")).alias(f"{symbol}_min"))
|
|
155
198
|
)
|
|
156
|
-
|
|
199
|
+
|
|
200
|
+
res_df = res_df.hstack(min_obj_columns)
|
|
201
|
+
# If there are scalarization functions, evaluate them as well
|
|
202
|
+
scalarization_columns = res_df.select(*[expr.alias(symbol) for symbol, expr in self.scalarization_funcs])
|
|
203
|
+
return res_df.hstack(scalarization_columns)
|
|
157
204
|
|
|
158
205
|
def _evaluate_surrogates(self, xs: dict[str, list[int | float]]) -> pl.DataFrame:
|
|
159
206
|
"""Evaluate the problem for the given decision variables using the surrogate models.
|
|
@@ -194,7 +241,10 @@ class Evaluator:
|
|
|
194
241
|
min_obj_columns = min_obj_columns.hstack(
|
|
195
242
|
res.select((min_max_mult * pl.col(f"{symbol}")).alias(f"{symbol}_min"))
|
|
196
243
|
)
|
|
197
|
-
|
|
244
|
+
res_df = res.hstack(min_obj_columns)
|
|
245
|
+
# If there are scalarization functions, evaluate them as well
|
|
246
|
+
scalarization_columns = res_df.select(*[expr.alias(symbol) for symbol, expr in self.scalarization_funcs])
|
|
247
|
+
return res_df.hstack(scalarization_columns)
|
|
198
248
|
|
|
199
249
|
def _load_surrogates(self, surrogate_paths: dict[str, Path] | None = None):
|
|
200
250
|
"""Load the surrogate models from disk and store them within the evaluator.
|
|
@@ -4,15 +4,21 @@ Pre-defined problems for, e.g.,
|
|
|
4
4
|
testing and illustration purposed are defined here.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
-
__all__ = [
|
|
7
|
+
__all__ = [ # noqa: RUF022
|
|
8
8
|
"binh_and_korn",
|
|
9
9
|
"dtlz2",
|
|
10
10
|
"forest_problem",
|
|
11
11
|
"forest_problem_discrete",
|
|
12
|
-
"
|
|
13
|
-
"
|
|
12
|
+
"mcwb_equilateral_tbeam_problem",
|
|
13
|
+
"mcwb_hollow_rectangular_problem",
|
|
14
|
+
"mcwb_ragsdell1976_problem",
|
|
15
|
+
"mcwb_solid_rectangular_problem",
|
|
16
|
+
"mcwb_square_channel_problem",
|
|
17
|
+
"mcwb_tapered_channel_problem",
|
|
18
|
+
"mixed_variable_dimensions_problem",
|
|
14
19
|
"momip_ti2",
|
|
15
20
|
"momip_ti7",
|
|
21
|
+
"multi_valued_constraint_problem",
|
|
16
22
|
"nimbus_test_problem",
|
|
17
23
|
"pareto_navigator_test_problem",
|
|
18
24
|
"re21",
|
|
@@ -22,8 +28,11 @@ __all__ = [
|
|
|
22
28
|
"river_pollution_problem",
|
|
23
29
|
"river_pollution_problem_discrete",
|
|
24
30
|
"river_pollution_scenario",
|
|
31
|
+
"rocket_injector_design",
|
|
25
32
|
"simple_data_problem",
|
|
26
33
|
"simple_integer_test_problem",
|
|
34
|
+
"simple_knapsack",
|
|
35
|
+
"simple_knapsack_vectors",
|
|
27
36
|
"simple_linear_test_problem",
|
|
28
37
|
"simple_scenario_test_problem",
|
|
29
38
|
"simple_test_problem",
|
|
@@ -33,22 +42,32 @@ __all__ = [
|
|
|
33
42
|
"zdt1",
|
|
34
43
|
"zdt2",
|
|
35
44
|
"zdt3",
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"mcwb_solid_rectangular_problem"
|
|
45
|
+
"best_cake_problem",
|
|
46
|
+
"dmitry_forest_problem_disc",
|
|
39
47
|
]
|
|
40
48
|
|
|
41
49
|
|
|
42
50
|
from .binh_and_korn_problem import binh_and_korn
|
|
51
|
+
from .cake_problem import best_cake_problem
|
|
52
|
+
from .dmitry_forest_problem_discrete import dmitry_forest_problem_disc
|
|
43
53
|
from .dtlz2_problem import dtlz2
|
|
44
54
|
from .forest_problem import forest_problem, forest_problem_discrete
|
|
45
55
|
from .knapsack_problem import simple_knapsack, simple_knapsack_vectors
|
|
56
|
+
from .mcwb_problem import (
|
|
57
|
+
mcwb_equilateral_tbeam_problem,
|
|
58
|
+
mcwb_hollow_rectangular_problem,
|
|
59
|
+
mcwb_ragsdell1976_problem,
|
|
60
|
+
mcwb_solid_rectangular_problem,
|
|
61
|
+
mcwb_square_channel_problem,
|
|
62
|
+
mcwb_tapered_channel_problem,
|
|
63
|
+
)
|
|
46
64
|
from .mixed_variable_dimenrions_problem import mixed_variable_dimensions_problem
|
|
47
65
|
from .momip_problem import momip_ti2, momip_ti7
|
|
66
|
+
from .multi_valued_constraints import multi_valued_constraint_problem
|
|
48
67
|
from .nimbus_problem import nimbus_test_problem
|
|
49
68
|
from .pareto_navigator_problem import pareto_navigator_test_problem
|
|
50
69
|
from .re_problem import re21, re22, re23, re24
|
|
51
|
-
from .
|
|
70
|
+
from .river_pollution_problems import (
|
|
52
71
|
river_pollution_problem,
|
|
53
72
|
river_pollution_problem_discrete,
|
|
54
73
|
river_pollution_scenario,
|
|
@@ -67,7 +86,3 @@ from .spanish_sustainability_problem import (
|
|
|
67
86
|
spanish_sustainability_problem_discrete,
|
|
68
87
|
)
|
|
69
88
|
from .zdt_problem import zdt1, zdt2, zdt3
|
|
70
|
-
|
|
71
|
-
from .mcwb_problem import (mcwb_solid_rectangular_problem, mcwb_hollow_rectangular_problem,
|
|
72
|
-
mcwb_equilateral_tbeam_problem, mcwb_square_channel_problem, mcwb_tapered_channel_problem,
|
|
73
|
-
mcwb_ragsdell1976_problem)
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# A FastAPI server to expose pymoo benchmark problems
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
import polars as pl
|
|
5
|
+
import requests
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
from pymoo.problems import get_problem
|
|
9
|
+
|
|
10
|
+
from desdeo.problem.schema import Objective, Problem, Simulator, Url, Variable
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PymooParameters(BaseModel):
|
|
14
|
+
"""Parameters for a pymoo problem instance."""
|
|
15
|
+
|
|
16
|
+
name: str
|
|
17
|
+
n_var: int
|
|
18
|
+
n_obj: int
|
|
19
|
+
minus: bool = False
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ProblemInfo(BaseModel):
|
|
23
|
+
"""Information about a pymoo problem instance."""
|
|
24
|
+
|
|
25
|
+
lower_bounds: dict[str, float]
|
|
26
|
+
"""Lower bounds of the decision variables. Keys are the names of the decision variables, e.g. "x_1", "x_2", etc."""
|
|
27
|
+
upper_bounds: dict[str, float]
|
|
28
|
+
"""Upper bounds of the decision variables."""
|
|
29
|
+
objective_names: list[str]
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
app = FastAPI()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def get_pymoo_problem(p: PymooParameters):
|
|
36
|
+
"""Get a pymoo problem instance by name, number of variables, and number of objectives."""
|
|
37
|
+
params = p.model_dump()
|
|
38
|
+
params.pop("minus")
|
|
39
|
+
return get_problem(**params)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
@app.get("/evaluate")
|
|
43
|
+
def evaluate(d: dict[str, list[float]], p: PymooParameters) -> dict[str, Any]:
|
|
44
|
+
"""Evaluate a pymoo problem instance with given parameters and input values."""
|
|
45
|
+
problem = get_pymoo_problem(p)
|
|
46
|
+
|
|
47
|
+
xs_df = pl.DataFrame(d)
|
|
48
|
+
|
|
49
|
+
output = problem.evaluate(xs_df.to_numpy())
|
|
50
|
+
output_df = pl.DataFrame(output, schema=[f"f_{i + 1}" for i in range(problem.n_obj)])
|
|
51
|
+
|
|
52
|
+
return d | output_df.to_dict(as_series=False)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@app.get("/info")
|
|
56
|
+
def info(p: PymooParameters) -> ProblemInfo:
|
|
57
|
+
"""Get information about a pymoo problem instance, including bounds and objective names."""
|
|
58
|
+
problem = get_pymoo_problem(p)
|
|
59
|
+
bounds = problem.bounds()
|
|
60
|
+
|
|
61
|
+
return ProblemInfo(
|
|
62
|
+
lower_bounds={f"x_{i + 1}": bounds[0][i] for i in range(problem.n_var)},
|
|
63
|
+
upper_bounds={f"x_{i + 1}": bounds[1][i] for i in range(problem.n_var)},
|
|
64
|
+
objective_names=[f"f_{i + 1}" for i in range(problem.n_obj)],
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
url = "http://127.0.0.1"
|
|
69
|
+
port = 8000
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def server_problem(parameters: PymooParameters) -> Problem:
|
|
73
|
+
"""Create a Problem instance from pymoo parameters."""
|
|
74
|
+
try:
|
|
75
|
+
info = requests.get(url + f":{port}/info", json=parameters.model_dump())
|
|
76
|
+
info.raise_for_status()
|
|
77
|
+
except requests.RequestException as e:
|
|
78
|
+
raise RuntimeError("Failed to fetch problem info. Is the server running?") from e
|
|
79
|
+
info: ProblemInfo = ProblemInfo.model_validate(info.json())
|
|
80
|
+
|
|
81
|
+
simulator_url = Url(url=f"{url}:{port}/evaluate")
|
|
82
|
+
|
|
83
|
+
return Problem(
|
|
84
|
+
name=parameters.name,
|
|
85
|
+
description=f"Problem {parameters.name} with {parameters.n_var} variables and {parameters.n_obj} objectives.",
|
|
86
|
+
variables=[
|
|
87
|
+
Variable(
|
|
88
|
+
name=f"x_{i + 1}",
|
|
89
|
+
symbol=f"x_{i + 1}",
|
|
90
|
+
lowerbound=info.lower_bounds[f"x_{i + 1}"],
|
|
91
|
+
upperbound=info.upper_bounds[f"x_{i + 1}"],
|
|
92
|
+
variable_type="real",
|
|
93
|
+
)
|
|
94
|
+
for i in range(parameters.n_var)
|
|
95
|
+
],
|
|
96
|
+
objectives=[
|
|
97
|
+
Objective(
|
|
98
|
+
name=f"f_{i + 1}",
|
|
99
|
+
symbol=f"f_{i + 1}",
|
|
100
|
+
simulator_path=simulator_url,
|
|
101
|
+
objective_type="simulator",
|
|
102
|
+
maximize=parameters.minus,
|
|
103
|
+
)
|
|
104
|
+
for i in range(parameters.n_obj)
|
|
105
|
+
],
|
|
106
|
+
simulators=[
|
|
107
|
+
Simulator(
|
|
108
|
+
name="s1",
|
|
109
|
+
symbol="s1",
|
|
110
|
+
url=simulator_url,
|
|
111
|
+
parameter_options=parameters.model_dump(),
|
|
112
|
+
)
|
|
113
|
+
],
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
if __name__ == "__main__":
|
|
118
|
+
import uvicorn
|
|
119
|
+
|
|
120
|
+
uvicorn.run(app)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"""Defines the 'best cake problem'."""
|
|
2
|
+
|
|
3
|
+
from desdeo.problem.schema import (
|
|
4
|
+
Constant,
|
|
5
|
+
Objective,
|
|
6
|
+
ObjectiveTypeEnum,
|
|
7
|
+
Problem,
|
|
8
|
+
Variable,
|
|
9
|
+
VariableTypeEnum,
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
PI = 3.14159265358979323846
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
## Helper func
|
|
16
|
+
def U(z: float):
|
|
17
|
+
return 4.0 * z * (1.0 - z)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Helper funcs to return string representations
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def bowl_str(z: str, a: str, invD: str) -> str:
|
|
24
|
+
tmp: str = f"({z} - {a})*{invD}"
|
|
25
|
+
return f"({tmp}*{tmp})"
|
|
26
|
+
# return f"{clamp01_str(f"{tmp}*{tmp}")}"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def U_str(z: str) -> str:
|
|
30
|
+
tmp: str = f"(4*{z}*(1.0 - {z}))"
|
|
31
|
+
return f"({tmp}*{tmp})"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def ripple_str(t: str) -> str:
|
|
35
|
+
tmp: str = f"Sin({PI} * {t})"
|
|
36
|
+
return f"({tmp}*{tmp})"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Objective function string representations
|
|
40
|
+
def f0_str() -> str:
|
|
41
|
+
yliq: str = "(0.5*x5 + 0.3*x4 + 0.2*x3)"
|
|
42
|
+
v: str = (
|
|
43
|
+
f"(0.4 * {bowl_str('x1', 'T1', 'INV_D1')}) + "
|
|
44
|
+
f"(0.4 * {bowl_str(yliq, 'Y_LIQ_STAR', 'INV_D_YLIQ')}) + "
|
|
45
|
+
f" (0.2 * {ripple_str('((x1 + x6) - (T1 + T6))')})"
|
|
46
|
+
)
|
|
47
|
+
return f"14*({v})"
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def f1_str() -> str:
|
|
51
|
+
sbar: str = "((x2 + 0.5*x3)/1.5)"
|
|
52
|
+
w25: str = f"({U_str('x2')}*{U_str('x5')})"
|
|
53
|
+
d25: str = f"(({w25} - W25_STAR)*INV_DW25)"
|
|
54
|
+
v: str = f"(0.4*{bowl_str('x2', 'T2', 'INV_D2')}) + (0.3*{ripple_str(f'{sbar} - SBAR_STAR')}) +(0.3*{d25}*{d25})"
|
|
55
|
+
return f"14*({v})"
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def f2_str() -> str:
|
|
59
|
+
v: str = (
|
|
60
|
+
f"(0.35*{bowl_str('x6', 'T6', 'INV_D6')}) + "
|
|
61
|
+
f"(0.25*{bowl_str('x4', 'T4', 'INV_D4')}) + "
|
|
62
|
+
f"(0.4*{ripple_str('((x6 - 0.5*x4) - (T6 - 0.5*T4))')})"
|
|
63
|
+
)
|
|
64
|
+
return f"14*({v})"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def f3_str() -> str:
|
|
68
|
+
w35: str = f"({U_str('x3')}*{U_str('x5')})"
|
|
69
|
+
d35: str = f"(({w35} - W35_STAR) * INV_DW35)"
|
|
70
|
+
v: str = f"(0.3*{bowl_str('x3', 'T3', 'INV_D3')}) + (0.3*{bowl_str('x5', 'T5', 'INV_D5')}) + (0.4*({d35}*{d35}))"
|
|
71
|
+
return f"14*({v})"
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def f4_str() -> str:
|
|
75
|
+
v: str = (
|
|
76
|
+
f"(0.25*{bowl_str('x2', 'T2', 'INV_D2')}) + "
|
|
77
|
+
f"(0.25*{bowl_str('x3', 'T3', 'INV_D3')}) + "
|
|
78
|
+
f"(0.20*{ripple_str('(x4 - T4)')}) + "
|
|
79
|
+
f"(0.30*{ripple_str('((x2 - x5) - (T2 - T5))')})"
|
|
80
|
+
)
|
|
81
|
+
return f"14*({v})"
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
## The cake problem
|
|
85
|
+
def best_cake_problem() -> Problem:
|
|
86
|
+
"""Defines the best cake problem."""
|
|
87
|
+
variable_inits = [
|
|
88
|
+
("Flour", 0.70),
|
|
89
|
+
("Sugar", 0.10),
|
|
90
|
+
("Butter", 0.40),
|
|
91
|
+
("Eggs", 0.50),
|
|
92
|
+
("Milk", 0.20),
|
|
93
|
+
("Baking powder", 0.80),
|
|
94
|
+
]
|
|
95
|
+
variables = [
|
|
96
|
+
Variable(
|
|
97
|
+
name=var[0],
|
|
98
|
+
symbol=f"x{i + 1}",
|
|
99
|
+
variable_type=VariableTypeEnum.real,
|
|
100
|
+
lowerbound=0.0,
|
|
101
|
+
upperbound=1.0,
|
|
102
|
+
initial_value=var[1],
|
|
103
|
+
)
|
|
104
|
+
for i, var in enumerate(variable_inits)
|
|
105
|
+
]
|
|
106
|
+
|
|
107
|
+
constants_init = [
|
|
108
|
+
("T1", 0.60),
|
|
109
|
+
("T2", 0.35),
|
|
110
|
+
("T3", 0.25),
|
|
111
|
+
("T4", 0.30),
|
|
112
|
+
("T5", 0.35),
|
|
113
|
+
("T6", 0.40),
|
|
114
|
+
("INV_D1", 1.0 / 0.60),
|
|
115
|
+
("INV_D2", 1.0 / 0.65),
|
|
116
|
+
("INV_D3", 1.0 / 0.75),
|
|
117
|
+
("INV_D4", 1.0 / 0.70),
|
|
118
|
+
("INV_D5", 1.0 / 0.65),
|
|
119
|
+
("INV_D6", 1.0 / 0.60),
|
|
120
|
+
("Y_LIQ_STAR", 0.5 * 0.35 + 0.3 * 0.30 + 0.2 * 0.25),
|
|
121
|
+
("INV_D_YLIQ", 1.0 / 0.685),
|
|
122
|
+
("SBAR_STAR", (0.35 + 0.5 * 0.25) / 1.5),
|
|
123
|
+
("W25_STAR", U(0.35) * U(0.35)),
|
|
124
|
+
("INV_DW25", 1.0 / 0.8281),
|
|
125
|
+
("W35_STAR", U(0.25) * U(0.35)),
|
|
126
|
+
("INV_DW35", 1.0 / 0.6825),
|
|
127
|
+
]
|
|
128
|
+
|
|
129
|
+
constants = [Constant(name=const[0], symbol=const[0], value=const[1]) for const in constants_init]
|
|
130
|
+
|
|
131
|
+
objectives = [
|
|
132
|
+
Objective(
|
|
133
|
+
name="Dry/crumb error",
|
|
134
|
+
symbol="dry_crumb",
|
|
135
|
+
func=f0_str(),
|
|
136
|
+
ideal=0.0,
|
|
137
|
+
nadir=14.0,
|
|
138
|
+
objective_type=ObjectiveTypeEnum.analytical,
|
|
139
|
+
is_twice_differentiable=True, # right?
|
|
140
|
+
),
|
|
141
|
+
Objective(
|
|
142
|
+
name="Sweetness/texture off-target",
|
|
143
|
+
symbol="sweet_texture",
|
|
144
|
+
func=f1_str(),
|
|
145
|
+
ideal=0.0,
|
|
146
|
+
nadir=14.0,
|
|
147
|
+
objective_type=ObjectiveTypeEnum.analytical,
|
|
148
|
+
is_twice_differentiable=True,
|
|
149
|
+
),
|
|
150
|
+
Objective(
|
|
151
|
+
name="Rise/collapse risk",
|
|
152
|
+
symbol="rise_collapse",
|
|
153
|
+
func=f2_str(),
|
|
154
|
+
ideal=0.0,
|
|
155
|
+
nadir=14.0,
|
|
156
|
+
objective_type=ObjectiveTypeEnum.analytical,
|
|
157
|
+
is_twice_differentiable=True,
|
|
158
|
+
),
|
|
159
|
+
Objective(
|
|
160
|
+
name="Moistness/grease imbalance",
|
|
161
|
+
symbol="moistness_grease",
|
|
162
|
+
func=f3_str(),
|
|
163
|
+
ideal=0.0,
|
|
164
|
+
nadir=14.0,
|
|
165
|
+
objective_type=ObjectiveTypeEnum.analytical,
|
|
166
|
+
is_twice_differentiable=True,
|
|
167
|
+
),
|
|
168
|
+
Objective(
|
|
169
|
+
name="Browning/burn risk",
|
|
170
|
+
symbol="browning_burn",
|
|
171
|
+
func=f4_str(),
|
|
172
|
+
ideal=0.0,
|
|
173
|
+
nadir=14.0,
|
|
174
|
+
objective_type=ObjectiveTypeEnum.analytical,
|
|
175
|
+
is_twice_differentiable=True,
|
|
176
|
+
),
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
return Problem(
|
|
180
|
+
name="Cake problem",
|
|
181
|
+
description="Try to find the most delicious cake!",
|
|
182
|
+
constants=constants,
|
|
183
|
+
variables=variables,
|
|
184
|
+
objectives=objectives,
|
|
185
|
+
)
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""A forest problem with discrete representation."""
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
import polars as pl
|
|
5
|
+
|
|
6
|
+
from desdeo.problem.schema import (
|
|
7
|
+
DiscreteRepresentation,
|
|
8
|
+
Objective,
|
|
9
|
+
ObjectiveTypeEnum,
|
|
10
|
+
Problem,
|
|
11
|
+
Variable,
|
|
12
|
+
VariableTypeEnum,
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def dmitry_forest_problem_disc() -> Problem:
|
|
17
|
+
"""Implements the dmitry forest problem using Pareto front representation.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
Problem: A problem instance representing the forest problem.
|
|
21
|
+
"""
|
|
22
|
+
path = Path(__file__)
|
|
23
|
+
while not str(path).endswith("/DESDEO"):
|
|
24
|
+
path = path.parent
|
|
25
|
+
|
|
26
|
+
path = path / "tests/data/dmitry_discrete_repr/dmitry_forest_problem_non_dom_solns.csv"
|
|
27
|
+
|
|
28
|
+
obj_names = ["Rev", "HA", "Carb", "DW"]
|
|
29
|
+
|
|
30
|
+
var_name = "index"
|
|
31
|
+
|
|
32
|
+
data = pl.read_csv(
|
|
33
|
+
path, has_header=True, columns=["Rev", "HA", "Carb", "DW"], separator=",", #decimal_comma=True
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
variables = [
|
|
37
|
+
Variable(
|
|
38
|
+
name=var_name,
|
|
39
|
+
symbol=var_name,
|
|
40
|
+
variable_type=VariableTypeEnum.integer,
|
|
41
|
+
lowerbound=0,
|
|
42
|
+
upperbound=len(data) - 1,
|
|
43
|
+
initial_value=0,
|
|
44
|
+
)
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
objectives = [
|
|
48
|
+
Objective(
|
|
49
|
+
name=obj_name,
|
|
50
|
+
symbol=obj_name,
|
|
51
|
+
objective_type=ObjectiveTypeEnum.data_based,
|
|
52
|
+
ideal=data[obj_name].max(),
|
|
53
|
+
nadir=data[obj_name].min(),
|
|
54
|
+
maximize=True,
|
|
55
|
+
)
|
|
56
|
+
for obj_name in obj_names
|
|
57
|
+
]
|
|
58
|
+
|
|
59
|
+
discrete_def = DiscreteRepresentation(
|
|
60
|
+
variable_values={"index": list(range(len(data)))},
|
|
61
|
+
objective_values=data[[obj.symbol for obj in objectives]].to_dict(),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return Problem(
|
|
65
|
+
name="Dmitry Forest Problem (Discrete)",
|
|
66
|
+
description="Defines a forest problem with four objectives: revenue, habitat availability, carbon storage, and deadwood.",
|
|
67
|
+
variables=variables,
|
|
68
|
+
objectives=objectives,
|
|
69
|
+
discrete_representation=discrete_def,
|
|
70
|
+
is_twice_differentiable=False,
|
|
71
|
+
)
|