desdeo 1.2__py3-none-any.whl → 2.1.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/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/README.md +73 -0
- desdeo/api/__init__.py +15 -0
- desdeo/api/app.py +50 -0
- desdeo/api/config.py +90 -0
- desdeo/api/config.toml +64 -0
- desdeo/api/db.py +27 -0
- desdeo/api/db_init.py +85 -0
- desdeo/api/db_models.py +164 -0
- desdeo/api/malaga_db_init.py +27 -0
- desdeo/api/models/__init__.py +266 -0
- desdeo/api/models/archive.py +23 -0
- 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 +128 -0
- desdeo/api/models/problem.py +717 -0
- desdeo/api/models/reference_point_method.py +18 -0
- desdeo/api/models/session.py +49 -0
- desdeo/api/models/state.py +463 -0
- desdeo/api/models/user.py +52 -0
- desdeo/api/models/utopia.py +25 -0
- desdeo/api/routers/_EMO.backup +309 -0
- desdeo/api/routers/_NAUTILUS.py +245 -0
- desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
- desdeo/api/routers/_NIMBUS.py +765 -0
- desdeo/api/routers/__init__.py +5 -0
- 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 +307 -0
- desdeo/api/routers/reference_point_method.py +93 -0
- desdeo/api/routers/session.py +100 -0
- desdeo/api/routers/test.py +16 -0
- desdeo/api/routers/user_authentication.py +520 -0
- desdeo/api/routers/utils.py +187 -0
- desdeo/api/routers/utopia.py +230 -0
- desdeo/api/schema.py +100 -0
- desdeo/api/tests/__init__.py +0 -0
- desdeo/api/tests/conftest.py +151 -0
- desdeo/api/tests/test_enautilus.py +330 -0
- desdeo/api/tests/test_models.py +1179 -0
- desdeo/api/tests/test_routes.py +1075 -0
- desdeo/api/utils/_database.py +263 -0
- desdeo/api/utils/_logger.py +29 -0
- desdeo/api/utils/database.py +36 -0
- desdeo/api/utils/emo_database.py +40 -0
- desdeo/core.py +34 -0
- desdeo/emo/__init__.py +159 -0
- desdeo/emo/hooks/archivers.py +188 -0
- desdeo/emo/methods/EAs.py +541 -0
- desdeo/emo/methods/__init__.py +0 -0
- desdeo/emo/methods/bases.py +12 -0
- desdeo/emo/methods/templates.py +111 -0
- desdeo/emo/operators/__init__.py +1 -0
- desdeo/emo/operators/crossover.py +1282 -0
- desdeo/emo/operators/evaluator.py +114 -0
- desdeo/emo/operators/generator.py +459 -0
- desdeo/emo/operators/mutation.py +1224 -0
- desdeo/emo/operators/scalar_selection.py +202 -0
- desdeo/emo/operators/selection.py +1778 -0
- desdeo/emo/operators/termination.py +286 -0
- 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/explanations/__init__.py +6 -0
- desdeo/explanations/explainer.py +100 -0
- desdeo/explanations/utils.py +90 -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 +41 -0
- desdeo/mcdm/enautilus.py +338 -0
- desdeo/mcdm/gnimbus.py +484 -0
- desdeo/mcdm/nautili.py +345 -0
- desdeo/mcdm/nautilus.py +477 -0
- desdeo/mcdm/nautilus_navigator.py +656 -0
- desdeo/mcdm/nimbus.py +417 -0
- desdeo/mcdm/pareto_navigator.py +269 -0
- desdeo/mcdm/reference_point_method.py +186 -0
- desdeo/problem/__init__.py +83 -0
- desdeo/problem/evaluator.py +561 -0
- 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 +562 -0
- desdeo/problem/infix_parser.py +341 -0
- desdeo/problem/json_parser.py +944 -0
- desdeo/problem/pyomo_evaluator.py +487 -0
- desdeo/problem/schema.py +1829 -0
- desdeo/problem/simulator_evaluator.py +348 -0
- desdeo/problem/sympy_evaluator.py +244 -0
- desdeo/problem/testproblems/__init__.py +88 -0
- desdeo/problem/testproblems/benchmarks_server.py +120 -0
- desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
- desdeo/problem/testproblems/cake_problem.py +185 -0
- desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
- desdeo/problem/testproblems/dtlz2_problem.py +102 -0
- desdeo/problem/testproblems/forest_problem.py +283 -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/multi_valued_constraints.py +119 -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_problems.py +440 -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/single_objective.py +289 -0
- desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
- desdeo/problem/testproblems/zdt_problem.py +274 -0
- desdeo/problem/utils.py +245 -0
- desdeo/tools/GenerateReferencePoints.py +181 -0
- desdeo/tools/__init__.py +120 -0
- desdeo/tools/desc_gen.py +22 -0
- desdeo/tools/generics.py +165 -0
- desdeo/tools/group_scalarization.py +3090 -0
- desdeo/tools/gurobipy_solver_interfaces.py +258 -0
- desdeo/tools/indicators_binary.py +117 -0
- desdeo/tools/indicators_unary.py +362 -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 +265 -0
- desdeo/tools/ng_solver_interfaces.py +199 -0
- desdeo/tools/non_dominated_sorting.py +134 -0
- desdeo/tools/patterns.py +283 -0
- desdeo/tools/proximal_solver.py +99 -0
- desdeo/tools/pyomo_solver_interfaces.py +477 -0
- desdeo/tools/reference_vectors.py +229 -0
- desdeo/tools/scalarization.py +2065 -0
- desdeo/tools/scipy_solver_interfaces.py +454 -0
- desdeo/tools/score_bands.py +627 -0
- desdeo/tools/utils.py +388 -0
- desdeo/tools/visualizations.py +67 -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.1.0.dist-info/METADATA +186 -0
- desdeo-2.1.0.dist-info/RECORD +180 -0
- {desdeo-1.2.dist-info → desdeo-2.1.0.dist-info}/WHEEL +1 -1
- desdeo-2.1.0.dist-info/licenses/LICENSE +21 -0
- desdeo-1.2.dist-info/METADATA +0 -16
- desdeo-1.2.dist-info/RECORD +0 -4
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
from math import pi
|
|
2
|
+
|
|
3
|
+
from desdeo.problem.schema import (
|
|
4
|
+
ExtraFunction,
|
|
5
|
+
Objective,
|
|
6
|
+
Problem,
|
|
7
|
+
Variable,
|
|
8
|
+
)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def zdt1(number_of_variables: int) -> Problem:
|
|
12
|
+
r"""Defines the ZDT1 test problem.
|
|
13
|
+
|
|
14
|
+
The problem has a variable number of decision variables and two objective functions to be minimized as
|
|
15
|
+
follows:
|
|
16
|
+
|
|
17
|
+
\begin{align*}
|
|
18
|
+
\min\quad f_1(\textbf{x}) &= x_1 \\
|
|
19
|
+
\min\quad f_2(\textbf{x}) &= g(\textbf{x}) \cdot h(f_1(\textbf{x}), g(\textbf{x}))\\
|
|
20
|
+
g(\textbf{x}) &= 1 + \frac{9}{n-1} \sum_{i=2}^{n} x_i \\
|
|
21
|
+
h(f_1, g) &= 1 - \sqrt{\frac{f_1}{g}}, \\
|
|
22
|
+
\end{align*}
|
|
23
|
+
|
|
24
|
+
where $f_1$ and $f_2$ are objective functions, $x_1,\dots,x_n$ are decision variable, $n$
|
|
25
|
+
is the number of decision variables,
|
|
26
|
+
and $g$ and $h$ are auxiliary functions.
|
|
27
|
+
"""
|
|
28
|
+
n = number_of_variables
|
|
29
|
+
|
|
30
|
+
# function f_1
|
|
31
|
+
f1_symbol = "f_1"
|
|
32
|
+
f1_expr = "x_1"
|
|
33
|
+
|
|
34
|
+
# function g
|
|
35
|
+
g_symbol = "g"
|
|
36
|
+
g_expr_1 = f"1 + (9 / ({n} - 1))"
|
|
37
|
+
g_expr_2 = "(" + " + ".join([f"x_{i}" for i in range(2, n + 1)]) + ")"
|
|
38
|
+
g_expr = g_expr_1 + " * " + g_expr_2
|
|
39
|
+
|
|
40
|
+
# function h(f, g)
|
|
41
|
+
h_symbol = "h"
|
|
42
|
+
h_expr = f"1 - Sqrt(({f1_expr}) / ({g_expr}))"
|
|
43
|
+
|
|
44
|
+
# function f_2
|
|
45
|
+
f2_symbol = "f_2"
|
|
46
|
+
f2_expr = f"{g_symbol} * {h_symbol}"
|
|
47
|
+
|
|
48
|
+
variables = [
|
|
49
|
+
Variable(name=f"x_{i}", symbol=f"x_{i}", variable_type="real", lowerbound=0, upperbound=1, initial_value=0.5)
|
|
50
|
+
for i in range(1, n + 1)
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
objectives = [
|
|
54
|
+
Objective(
|
|
55
|
+
name="f_1",
|
|
56
|
+
symbol=f1_symbol,
|
|
57
|
+
func=f1_expr,
|
|
58
|
+
maximize=False,
|
|
59
|
+
ideal=0,
|
|
60
|
+
nadir=1,
|
|
61
|
+
is_convex=True,
|
|
62
|
+
is_linear=True,
|
|
63
|
+
is_twice_differentiable=True,
|
|
64
|
+
),
|
|
65
|
+
Objective(
|
|
66
|
+
name="f_2",
|
|
67
|
+
symbol=f2_symbol,
|
|
68
|
+
func=f2_expr,
|
|
69
|
+
maximize=False,
|
|
70
|
+
ideal=0,
|
|
71
|
+
nadir=1,
|
|
72
|
+
is_convex=True,
|
|
73
|
+
is_linear=False,
|
|
74
|
+
is_twice_differentiable=True,
|
|
75
|
+
),
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
extras = [
|
|
79
|
+
ExtraFunction(
|
|
80
|
+
name="g", symbol=g_symbol, func=g_expr, is_convex=True, is_linear=True, is_twice_differentiable=True
|
|
81
|
+
),
|
|
82
|
+
ExtraFunction(
|
|
83
|
+
name="h", symbol=h_symbol, func=h_expr, is_convex=True, is_linear=False, is_twice_differentiable=True
|
|
84
|
+
),
|
|
85
|
+
]
|
|
86
|
+
|
|
87
|
+
return Problem(
|
|
88
|
+
name="zdt1",
|
|
89
|
+
description="The ZDT1 test problem.",
|
|
90
|
+
variables=variables,
|
|
91
|
+
objectives=objectives,
|
|
92
|
+
extra_funcs=extras,
|
|
93
|
+
is_convex=True,
|
|
94
|
+
is_linear=False,
|
|
95
|
+
is_twice_differentiable=True,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def zdt2(n_variables: int) -> Problem:
|
|
100
|
+
r"""Defines the ZDT2 test problem.
|
|
101
|
+
|
|
102
|
+
The problem has a variable number of decision variables and two objective functions to be minimized as
|
|
103
|
+
follows:
|
|
104
|
+
|
|
105
|
+
\begin{align*}
|
|
106
|
+
\min\quad f_1(\textbf{x}) &= x_1 \\
|
|
107
|
+
\min\quad f_2(\textbf{x}) &= g(\textbf{x}) \cdot h(f_1(\textbf{x}), g(\textbf{x}))\\
|
|
108
|
+
g(\textbf{x}) &= 1 + \frac{9}{n-1} \sum_{i=2}^{n} x_i \\
|
|
109
|
+
h(f_1, g) &= 1 - \left({\frac{f_1}{g}}\right)^2, \\
|
|
110
|
+
\end{align*}
|
|
111
|
+
|
|
112
|
+
where $f_1$ and $f_2$ are objective functions, $x_1,\dots,x_n$ are decision variable, $n$
|
|
113
|
+
is the number of decision variables,
|
|
114
|
+
and $g$ and $h$ are auxiliary functions.
|
|
115
|
+
"""
|
|
116
|
+
n = n_variables
|
|
117
|
+
|
|
118
|
+
# function f_1
|
|
119
|
+
f1_symbol = "f_1"
|
|
120
|
+
f1_expr = "x_1"
|
|
121
|
+
|
|
122
|
+
# function g
|
|
123
|
+
g_symbol = "g"
|
|
124
|
+
g_expr_1 = f"1 + (9 / ({n} - 1))"
|
|
125
|
+
g_expr_2 = "(" + " + ".join([f"x_{i}" for i in range(2, n + 1)]) + ")"
|
|
126
|
+
g_expr = g_expr_1 + " * " + g_expr_2
|
|
127
|
+
|
|
128
|
+
# function h(f, g)
|
|
129
|
+
h_symbol = "h"
|
|
130
|
+
h_expr = f"1 - (({f1_expr}) / ({g_expr})) ** 2"
|
|
131
|
+
|
|
132
|
+
# function f_2
|
|
133
|
+
f2_symbol = "f_2"
|
|
134
|
+
f2_expr = f"{g_symbol} * {h_symbol}"
|
|
135
|
+
|
|
136
|
+
variables = [
|
|
137
|
+
Variable(name=f"x_{i}", symbol=f"x_{i}", variable_type="real", lowerbound=0, upperbound=1, initial_value=0.5)
|
|
138
|
+
for i in range(1, n + 1)
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
objectives = [
|
|
142
|
+
Objective(
|
|
143
|
+
name="f_1",
|
|
144
|
+
symbol=f1_symbol,
|
|
145
|
+
func=f1_expr,
|
|
146
|
+
maximize=False,
|
|
147
|
+
ideal=0,
|
|
148
|
+
nadir=1,
|
|
149
|
+
is_convex=True,
|
|
150
|
+
is_linear=True,
|
|
151
|
+
is_twice_differentiable=True,
|
|
152
|
+
),
|
|
153
|
+
Objective(
|
|
154
|
+
name="f_2",
|
|
155
|
+
symbol=f2_symbol,
|
|
156
|
+
func=f2_expr,
|
|
157
|
+
maximize=False,
|
|
158
|
+
ideal=0,
|
|
159
|
+
nadir=1,
|
|
160
|
+
is_convex=False,
|
|
161
|
+
is_linear=False,
|
|
162
|
+
is_twice_differentiable=True,
|
|
163
|
+
),
|
|
164
|
+
]
|
|
165
|
+
|
|
166
|
+
extras = [
|
|
167
|
+
ExtraFunction(
|
|
168
|
+
name="g", symbol=g_symbol, func=g_expr, is_convex=True, is_linear=True, is_twice_differentiable=True
|
|
169
|
+
),
|
|
170
|
+
ExtraFunction(
|
|
171
|
+
name="h", symbol=h_symbol, func=h_expr, is_convex=False, is_linear=False, is_twice_differentiable=True
|
|
172
|
+
),
|
|
173
|
+
]
|
|
174
|
+
|
|
175
|
+
return Problem(
|
|
176
|
+
name="zdt2",
|
|
177
|
+
description="The ZDT2 test problem.",
|
|
178
|
+
variables=variables,
|
|
179
|
+
objectives=objectives,
|
|
180
|
+
extra_funcs=extras,
|
|
181
|
+
is_convex=False,
|
|
182
|
+
is_linear=False,
|
|
183
|
+
is_twice_differentiable=True,
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def zdt3(
|
|
188
|
+
n_variables: int,
|
|
189
|
+
) -> Problem:
|
|
190
|
+
r"""Defines the ZDT3 test problem.
|
|
191
|
+
|
|
192
|
+
The problem has a variable number of decision variables and two objective functions to be minimized as
|
|
193
|
+
follows:
|
|
194
|
+
|
|
195
|
+
\begin{align*}
|
|
196
|
+
\min\quad f_1(x) &= x_1 \\
|
|
197
|
+
\min\quad f_2(x) &= g(\textbf{x}) \cdot h(f_1(\textbf{x}), g(\textbf{x}))\\
|
|
198
|
+
g(\textbf{x}) &= 1 + \frac{9}{n-1} \sum_{i=2}^{n} x_i \\
|
|
199
|
+
h(f_1, g) &= 1 - \sqrt{\frac{f_1}{g}} - \frac{f_1}{g} \sin(10\pi f_1)), \\
|
|
200
|
+
\end{align*}
|
|
201
|
+
|
|
202
|
+
where $f_2$ and $f_2$ are objective functions, $x_1,\dots,x_n$ are decision variable, $n$
|
|
203
|
+
is the number of decision variables,
|
|
204
|
+
and $g$ and $h$ are auxiliary functions.
|
|
205
|
+
"""
|
|
206
|
+
n = n_variables
|
|
207
|
+
|
|
208
|
+
# function f_1
|
|
209
|
+
f1_symbol = "f_1"
|
|
210
|
+
f1_expr = "x_1"
|
|
211
|
+
|
|
212
|
+
# function g
|
|
213
|
+
g_symbol = "g"
|
|
214
|
+
g_expr_1 = f"1 + (9 / ({n} - 1))"
|
|
215
|
+
g_expr_2 = "(" + " + ".join([f"x_{i}" for i in range(2, n + 1)]) + ")"
|
|
216
|
+
g_expr = g_expr_1 + " * " + g_expr_2
|
|
217
|
+
|
|
218
|
+
# function h(f, g)
|
|
219
|
+
h_symbol = "h"
|
|
220
|
+
h_expr = f"1 - Sqrt(({f1_expr}) / ({g_expr})) - (({f1_expr}) / ({g_expr})) * Sin (10 * {pi} * {f1_expr}) "
|
|
221
|
+
|
|
222
|
+
# function f_2
|
|
223
|
+
f2_symbol = "f_2"
|
|
224
|
+
f2_expr = f"{g_symbol} * {h_symbol}"
|
|
225
|
+
|
|
226
|
+
variables = [
|
|
227
|
+
Variable(name=f"x_{i}", symbol=f"x_{i}", variable_type="real", lowerbound=0, upperbound=1, initial_value=0.5)
|
|
228
|
+
for i in range(1, n + 1)
|
|
229
|
+
]
|
|
230
|
+
|
|
231
|
+
objectives = [
|
|
232
|
+
Objective(
|
|
233
|
+
name="f_1",
|
|
234
|
+
symbol=f1_symbol,
|
|
235
|
+
func=f1_expr,
|
|
236
|
+
maximize=False,
|
|
237
|
+
ideal=0,
|
|
238
|
+
nadir=1,
|
|
239
|
+
is_convex=True,
|
|
240
|
+
is_linear=True,
|
|
241
|
+
is_twice_differentiable=True,
|
|
242
|
+
),
|
|
243
|
+
Objective(
|
|
244
|
+
name="f_2",
|
|
245
|
+
symbol=f2_symbol,
|
|
246
|
+
func=f2_expr,
|
|
247
|
+
maximize=False,
|
|
248
|
+
ideal=-1,
|
|
249
|
+
nadir=1,
|
|
250
|
+
is_convex=False,
|
|
251
|
+
is_linear=False,
|
|
252
|
+
is_twice_differentiable=True,
|
|
253
|
+
),
|
|
254
|
+
]
|
|
255
|
+
|
|
256
|
+
extras = [
|
|
257
|
+
ExtraFunction(
|
|
258
|
+
name="g", symbol=g_symbol, func=g_expr, is_convex=True, is_linear=True, is_twice_differentiable=True
|
|
259
|
+
),
|
|
260
|
+
ExtraFunction(
|
|
261
|
+
name="h", symbol=h_symbol, func=h_expr, is_convex=False, is_linear=False, is_twice_differentiable=True
|
|
262
|
+
),
|
|
263
|
+
]
|
|
264
|
+
|
|
265
|
+
return Problem(
|
|
266
|
+
name="zdt3",
|
|
267
|
+
description="The ZDT3 test problem.",
|
|
268
|
+
variables=variables,
|
|
269
|
+
objectives=objectives,
|
|
270
|
+
extra_funcs=extras,
|
|
271
|
+
is_convex=False,
|
|
272
|
+
is_linear=False,
|
|
273
|
+
is_twice_differentiable=True,
|
|
274
|
+
)
|
desdeo/problem/utils.py
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
"""Various utilities used across the framework related to the Problem formulation."""
|
|
2
|
+
|
|
3
|
+
import itertools
|
|
4
|
+
import warnings
|
|
5
|
+
from functools import reduce
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
import polars as pl
|
|
9
|
+
|
|
10
|
+
from desdeo.problem import Problem, TensorConstant, TensorVariable, Variable
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ProblemUtilsError(Exception):
|
|
14
|
+
"""Raised when an exception occurs in one of the utils function.
|
|
15
|
+
|
|
16
|
+
Raised when an exception occurs in one of the utils functions defined in the Problem module.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def objective_dict_to_numpy_array(problem: Problem, objective_dict: dict[str, float]) -> np.ndarray:
|
|
21
|
+
"""Takes a dict with an objective vector and returns a numpy array.
|
|
22
|
+
|
|
23
|
+
Takes a dict with the keys being objective function symbols and the values
|
|
24
|
+
being the corresponding objective function values. Returns a numpy array
|
|
25
|
+
with the objective function values in the same order they have been defined
|
|
26
|
+
in the original problem.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
problem (Problem): the problem the objective dict belongs to.
|
|
30
|
+
objective_dict (dict[str, float]): the dict with the objective function values.
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
np.ndarray: a numpy array with the objective function values in the order they are
|
|
34
|
+
present in problem.
|
|
35
|
+
"""
|
|
36
|
+
if isinstance(objective_dict[problem.objectives[0].symbol], list):
|
|
37
|
+
if len(objective_dict[problem.objectives[0].symbol]) != 1:
|
|
38
|
+
raise ValueError("The objective_dict has multiple values for an objective function")
|
|
39
|
+
return np.array([objective_dict[objective.symbol][0] for objective in problem.objectives])
|
|
40
|
+
return np.array([objective_dict[objective.symbol] for objective in problem.objectives])
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def numpy_array_to_objective_dict(problem: Problem, numpy_array: np.ndarray) -> dict[str, float]:
|
|
44
|
+
"""Takes a numpy array with objective function values and return a dict.
|
|
45
|
+
|
|
46
|
+
The reverse of objective_dict_to_numpy_array.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
problem (Problem): the problem the numpy array represents an objective vector of.
|
|
50
|
+
numpy_array (np.ndarray): the objective vector as a numpy array. The
|
|
51
|
+
array is squeezed, i.e., axes or length one are removed: [[42]] -> [42].
|
|
52
|
+
|
|
53
|
+
Returns:
|
|
54
|
+
dict[str, float]: a dict with keys being objective function symbols and value being
|
|
55
|
+
objective function values.
|
|
56
|
+
"""
|
|
57
|
+
return {objective.symbol: np.squeeze(numpy_array).tolist()[i] for i, objective in enumerate(problem.objectives)}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def unflatten_variable_array(problem: Problem, var_array: np.ndarray) -> dict[str, float | list]:
|
|
61
|
+
"""Unflatten a numpy array representing decision variable values.
|
|
62
|
+
|
|
63
|
+
Unflatten a numpy array that represent decision variable values. It is assumed
|
|
64
|
+
that the unflattened values follow a C-like order when it comes to unflattening
|
|
65
|
+
values for `TensorVariable`s. Note that `var_array` must be of dimension 1.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
problem (Problem): the problem instance the decision variables are associated with.
|
|
69
|
+
var_array (np.ndarray): a flat 1D array of numerical values representing
|
|
70
|
+
decision variable values.
|
|
71
|
+
|
|
72
|
+
Raises:
|
|
73
|
+
ValueError: `var_array` is of some other dimension that 1.
|
|
74
|
+
IndexError: `var_array` has too few elements given the variables defined in
|
|
75
|
+
the instance of `Problem`.
|
|
76
|
+
TypeError: unsupported variable type encountered in the variables defined in the
|
|
77
|
+
instance of `Problem`.
|
|
78
|
+
|
|
79
|
+
Returns:
|
|
80
|
+
dict[str, float | list]: a dict with keys equal to the symbols of the variables
|
|
81
|
+
defined in the `Problem` instance, and values equal to the decision variable
|
|
82
|
+
values as they were defined in `var_array`.
|
|
83
|
+
"""
|
|
84
|
+
if (dimension := var_array.ndim) != 1:
|
|
85
|
+
msg = f"The given variable array must have a dimension of 1. Current {dimension=}"
|
|
86
|
+
raise ValueError(msg)
|
|
87
|
+
|
|
88
|
+
var_dict = {}
|
|
89
|
+
array_i = 0
|
|
90
|
+
for var in problem.variables:
|
|
91
|
+
if array_i >= len(var_array):
|
|
92
|
+
msg = (
|
|
93
|
+
"End of variable array reached before all variables in the problem were iterated over. "
|
|
94
|
+
f"The variable array is too short with length={len(var_array)}."
|
|
95
|
+
)
|
|
96
|
+
raise IndexError(msg)
|
|
97
|
+
|
|
98
|
+
if isinstance(var, Variable):
|
|
99
|
+
# regular variable, just pick it
|
|
100
|
+
var_dict[var.symbol] = var_array[array_i].item()
|
|
101
|
+
array_i += 1
|
|
102
|
+
continue
|
|
103
|
+
|
|
104
|
+
if isinstance(var, TensorVariable):
|
|
105
|
+
# tensor variable, pick row-wise from var_array
|
|
106
|
+
slice_length = reduce(lambda x1, x2: x1 * x2, var.shape) # product of dimensions
|
|
107
|
+
flat_values = var_array[array_i : array_i + slice_length]
|
|
108
|
+
var_dict[var.symbol] = np.reshape(flat_values, var.shape, order="C").tolist()
|
|
109
|
+
array_i += slice_length
|
|
110
|
+
continue
|
|
111
|
+
|
|
112
|
+
msg = f"Unsupported variable type {type(var)} encountered."
|
|
113
|
+
raise TypeError(msg)
|
|
114
|
+
|
|
115
|
+
# check if values remain in var_array
|
|
116
|
+
if array_i < len(var_array):
|
|
117
|
+
# some values remain, warn user, but do not raise an error
|
|
118
|
+
msg = f"Warning, the variable array had some values that were not unflattened: f{["...", *var_array[array_i:]]}"
|
|
119
|
+
warnings.warn(msg, stacklevel=2)
|
|
120
|
+
|
|
121
|
+
# return the variable dict
|
|
122
|
+
return var_dict
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def flatten_variable_dict(problem: Problem, variable_dict: dict[str, float | list]) -> np.ndarray:
|
|
126
|
+
"""Flatten a dictionary representing variable values of an instance of `Problem` into a numpy array.
|
|
127
|
+
|
|
128
|
+
Flattens a dictionary representing variable values of an instance of `Problem` into a numpy array.
|
|
129
|
+
The flattening follows a C-like order. Support the flattening of both `Variable` and `TensorVariable`
|
|
130
|
+
types. Note that it is assumed that no more than one value is defined for each symbol in `variable_dict`
|
|
131
|
+
that correspond to the required shape of the underlying variable type.
|
|
132
|
+
|
|
133
|
+
Args:
|
|
134
|
+
problem (Problem): the problem instance the decision variables are associated with.
|
|
135
|
+
variable_dict (dict[str, float | list]): a dictionary with its keys being the symbols
|
|
136
|
+
of the variables defined in the instance of `Problem`, and the values corresponding
|
|
137
|
+
to the variables' values.
|
|
138
|
+
|
|
139
|
+
Raises:
|
|
140
|
+
ValueError: the `variable_dict` does not contain as its keys one or more of the symbols
|
|
141
|
+
defined for the variables in the instance of `Problem`.
|
|
142
|
+
TypeError: unsupported variable type encountered in the variables defined in the
|
|
143
|
+
instance of `Problem`.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
np.ndarray: a 1D numpy array with the variable values unflattened in C-like order.
|
|
147
|
+
"""
|
|
148
|
+
tmp = []
|
|
149
|
+
for var in problem.variables:
|
|
150
|
+
if isinstance(var, Variable):
|
|
151
|
+
# just a regular variable
|
|
152
|
+
if var.symbol not in variable_dict:
|
|
153
|
+
msg = f"The variable_dict is missing values for the variable {var.symbol}."
|
|
154
|
+
raise ValueError(msg)
|
|
155
|
+
tmp.append([variable_dict[var.symbol]])
|
|
156
|
+
continue
|
|
157
|
+
|
|
158
|
+
if isinstance(var, TensorVariable):
|
|
159
|
+
# tensor variable
|
|
160
|
+
if var.symbol in variable_dict:
|
|
161
|
+
# tensor variable is defined in the dict as a tensor
|
|
162
|
+
tmp = [*tmp, np.array(variable_dict[var.symbol]).flatten(order="C")]
|
|
163
|
+
continue
|
|
164
|
+
if any(key.startswith(f"{var.symbol}_") for key in variable_dict):
|
|
165
|
+
# tensor variable flattened in the dict
|
|
166
|
+
indices = itertools.product(*[range(1, s + 1) for s in var.shape])
|
|
167
|
+
flat_symbols = [f"{var.symbol}_{'_'.join(map(str, index))}" for index in indices]
|
|
168
|
+
tmp = [*tmp, np.array([variable_dict[s] for s in flat_symbols])]
|
|
169
|
+
continue
|
|
170
|
+
|
|
171
|
+
msg = f"The variable dict is missing values for the variable {var.symbol}."
|
|
172
|
+
raise ValueError(msg)
|
|
173
|
+
|
|
174
|
+
msg = f"Unsupported variable type {type(var)} encountered."
|
|
175
|
+
raise TypeError(msg)
|
|
176
|
+
|
|
177
|
+
return np.concatenate(tmp)
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def get_nadir_dict(problem: Problem) -> dict[str, float]:
|
|
181
|
+
"""Return a dict representing a problem's nadir point.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
problem (Problem): the problem with the nadir point.
|
|
185
|
+
|
|
186
|
+
Returns:
|
|
187
|
+
dict[str, float]: key are objective funciton symbols, values are nadir values.
|
|
188
|
+
"""
|
|
189
|
+
return {objective.symbol: objective.nadir for objective in problem.objectives}
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def get_ideal_dict(problem: Problem) -> dict[str, float]:
|
|
193
|
+
"""Return a dict representing a problem's ideal point.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
problem (Problem): the problem with the ideal point.
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
dict[str, float]: key are objective funciton symbols, values are ideal values.
|
|
200
|
+
"""
|
|
201
|
+
return {objective.symbol: objective.ideal for objective in problem.objectives}
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def tensor_constant_from_dataframe(
|
|
205
|
+
df: pl.DataFrame, name: str, symbol: str, n_rows: int, column_names: list[str]
|
|
206
|
+
) -> TensorConstant:
|
|
207
|
+
"""Create a TensorConstant from a Polars dataframe.
|
|
208
|
+
|
|
209
|
+
Args:
|
|
210
|
+
df (pl.DataFrame): a Polars dataframe with at least the columns in `column_names`
|
|
211
|
+
name (str): name attribute of the created TensorConstant.
|
|
212
|
+
symbol (str): symbol attribute of the created TensorConstant.
|
|
213
|
+
n_rows (int): the number of rows to read from the dataframe.
|
|
214
|
+
column_names (list[str]): the column names in the dataframe from which
|
|
215
|
+
the constant values will be picked.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
TensorConstant: A TensorConstant instance with values taken from the a given
|
|
219
|
+
Polars dataframe. The shape of the TensorConstant will be
|
|
220
|
+
(`n_rows`, len(`column_names`)).
|
|
221
|
+
|
|
222
|
+
Note:
|
|
223
|
+
In the argument `shape` the first element must be either less or equal to the
|
|
224
|
+
number of rows in `df`. The second element in `shape` must be equal
|
|
225
|
+
to the number of element in `column_names`.
|
|
226
|
+
"""
|
|
227
|
+
if n_rows > df.shape[0]:
|
|
228
|
+
# not enough rows in df
|
|
229
|
+
msg = f"Requested {n_rows} rows, but the dataframe has only {df.shape[0]} rows."
|
|
230
|
+
raise ProblemUtilsError(msg)
|
|
231
|
+
|
|
232
|
+
if len(column_names) > df.shape[1]:
|
|
233
|
+
# not enough cols in df
|
|
234
|
+
msg = f"Requested {len(column_names)} columns, but the dataframe has only {df.shape[1]} columns."
|
|
235
|
+
raise ProblemUtilsError(msg)
|
|
236
|
+
|
|
237
|
+
for col in column_names:
|
|
238
|
+
if col not in df.columns:
|
|
239
|
+
msg = f"The requested column '{col}' is not found in the given dataframe with columns {df.columns}."
|
|
240
|
+
raise ProblemUtilsError(msg)
|
|
241
|
+
|
|
242
|
+
selected_df = df.select(column_names).head(n_rows)
|
|
243
|
+
selected_values = [selected_df.to_dict()[col_name].to_list() for col_name in column_names]
|
|
244
|
+
|
|
245
|
+
return TensorConstant(name=name, symbol=symbol, shape=[n_rows, len(column_names)], values=selected_values)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"""Generate reference points for the IPA algorithm."""
|
|
2
|
+
|
|
3
|
+
from itertools import product
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
from numba import njit
|
|
7
|
+
from scipy.spatial import ConvexHull
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def normalize(vectors):
|
|
11
|
+
"""Normalize a set of vectors.
|
|
12
|
+
|
|
13
|
+
The length of the returned vectors will be 1.
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
vectors : np.ndarray
|
|
18
|
+
Set of vectors of any length, except zero.
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
if len(np.asarray(vectors).shape) == 1:
|
|
22
|
+
return vectors / np.linalg.norm(vectors)
|
|
23
|
+
norm = np.linalg.norm(vectors, axis=1)
|
|
24
|
+
return vectors / norm[:, np.newaxis]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def householder(vector):
|
|
28
|
+
"""Return reflection matrix via householder transformation."""
|
|
29
|
+
identity_mat = np.eye(len(vector))
|
|
30
|
+
v = vector[np.newaxis]
|
|
31
|
+
denominator = np.matmul(v, v.T)
|
|
32
|
+
numerator = np.matmul(v.T, v)
|
|
33
|
+
rot_mat = identity_mat - (2 * numerator / denominator)
|
|
34
|
+
return rot_mat
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def rotate(initial_vector, rotated_vector, other_vectors):
|
|
38
|
+
"""Calculate the rotation matrix that rotates the initial_vector to the
|
|
39
|
+
rotated_vector. Apply that rotation on other_vectors and return.
|
|
40
|
+
Uses Householder reflections twice to achieve this."""
|
|
41
|
+
|
|
42
|
+
init_vec_norm = normalize(initial_vector)
|
|
43
|
+
rot_vec_norm = normalize(np.asarray(rotated_vector))
|
|
44
|
+
middle_vec_norm = normalize(init_vec_norm + rot_vec_norm)
|
|
45
|
+
first_reflector = init_vec_norm - middle_vec_norm
|
|
46
|
+
second_reflector = middle_vec_norm - rot_vec_norm
|
|
47
|
+
Q1 = householder(first_reflector)
|
|
48
|
+
Q2 = householder(second_reflector)
|
|
49
|
+
reflection_matrix = np.matmul(Q2, Q1)
|
|
50
|
+
rotated_vectors = np.matmul(other_vectors, np.transpose(reflection_matrix))
|
|
51
|
+
return rotated_vectors
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def get_reference_hull(num_dims):
|
|
55
|
+
"""Get the convex hull of the valid reference points for IPA.
|
|
56
|
+
|
|
57
|
+
This algorithm generates the vertices of the unit hypercube in the (num_dims)-dimensional space.
|
|
58
|
+
Then, the vertices are projected onto the plane perpendicular to the largest space diagonal (vertex first parallel
|
|
59
|
+
projection) and rotated such that the plane is perpendular to one of the axes. Then, the points are are flattened
|
|
60
|
+
to (num_dims-1)-dimensional space. A convex hull is then constructed from the projected vertices, and a bounding box
|
|
61
|
+
is constructed around the convex hull.
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
num_dims (int): The number of dimensions of the space in which the reference points are generated.
|
|
65
|
+
|
|
66
|
+
Returns:
|
|
67
|
+
np.ndarray: A (2) x (num_dims-1) array of the bounding box. Reference points are guaranteed to be within
|
|
68
|
+
this box. However, not all points within this box are valid reference points.
|
|
69
|
+
np.ndarray: A (num_dims-1) x (num_dims-1) array of the coefficients of the hyperplanes defining the convex hull
|
|
70
|
+
of the bounds of the reference points. A point is a valid reference point if it lies within the convex hull.
|
|
71
|
+
np.ndarray: A (num_dims-1) array of the constants of the hyperplanes defining the convex hull. See above.
|
|
72
|
+
scipy.spatial.ConvexHull: The convex hull of the projected vertices/valid reference points.
|
|
73
|
+
"""
|
|
74
|
+
vertices = np.array(list(product([0, 1], repeat=num_dims)))
|
|
75
|
+
|
|
76
|
+
# Project vertices onto plane perpendicular to largest space diagonal, rotate to make one of the objectives zero.
|
|
77
|
+
# Then flatten to (num_dims-1) dimensions.
|
|
78
|
+
rotated_vertices = rotate_in(vertices)
|
|
79
|
+
|
|
80
|
+
bounding_box = np.array([np.min(rotated_vertices, axis=0), np.max(rotated_vertices, axis=0)])
|
|
81
|
+
|
|
82
|
+
hull = ConvexHull(rotated_vertices)
|
|
83
|
+
|
|
84
|
+
A, b = get_hull_equations(hull)
|
|
85
|
+
|
|
86
|
+
return bounding_box, A, b, hull
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def rotate_in(vertices):
|
|
90
|
+
"""
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
vertices (_type_): _description_
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
_type_: _description_
|
|
97
|
+
"""
|
|
98
|
+
num_dims = len(vertices[0])
|
|
99
|
+
rotated_vertices = rotate([1] * num_dims, ([0] * (num_dims - 1) + [1]), vertices)
|
|
100
|
+
rotated_vertices = rotated_vertices[:, :-1]
|
|
101
|
+
return rotated_vertices
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def rotate_out(points):
|
|
105
|
+
points = np.atleast_2d(points)
|
|
106
|
+
num_points, num_dims = points.shape
|
|
107
|
+
points_rotated = np.hstack((points, np.ones((len(points), 1))))
|
|
108
|
+
points_rotated = rotate(([0] * (num_dims) + [1]), [1] * (num_dims + 1), points_rotated)
|
|
109
|
+
# Move (along nadir-ideal direction) the plane of these points such that it passes through nadir
|
|
110
|
+
points_rotated = points_rotated + 1 - 1 / np.sqrt(num_dims + 1)
|
|
111
|
+
return points_rotated
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def get_hull_equations(hull):
|
|
115
|
+
"""Get the equations of the hyperplanes defining the convex hull.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
hull (scipy.spatial.ConvexHull): A convex hull.
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
np.ndarray: A (num_dims-1) x num_hyperplanes array of the coefficients of the hyperplanes defining the convex
|
|
122
|
+
hull.
|
|
123
|
+
np.ndarray: A (num_hyperplanes) array of the constants of the hyperplanes defining the convex hull.
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
return np.ascontiguousarray(hull.equations[:, :-1].T), np.ascontiguousarray(hull.equations[:, -1].T)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def generate_points(
|
|
130
|
+
num_points: int, num_dims: int
|
|
131
|
+
) -> tuple[
|
|
132
|
+
np.ndarray,
|
|
133
|
+
np.ndarray,
|
|
134
|
+
]:
|
|
135
|
+
"""Generate reference points for the IPA algorithm.
|
|
136
|
+
|
|
137
|
+
Creates a (large) number of reference points on a plane perpendicular to the largest space diagonal of the unit
|
|
138
|
+
hypercube in the num_dims-dimensional space. First, the vertices of the unit hypercube are generated. Then, the
|
|
139
|
+
vertices are projected onto the plane perpendicular to the largest space diagonal (vertex first parallel
|
|
140
|
+
projection) and rotated such that the plane is perpendular to one of the axes, making all objective values zero.
|
|
141
|
+
A convex hull is then constructed from the projected vertices, and a bounding box is constructed
|
|
142
|
+
around the convex hull. Finally, points are generated uniformly within the bounding box until num_points points
|
|
143
|
+
are generated _inside_ the convex hull. Note that the number of dimensions must be at least 2. Also, the number of
|
|
144
|
+
dimensions of the reference points is one less than the number of dimensions of the objective space. This is because
|
|
145
|
+
the reference points are generated on the projected plane.
|
|
146
|
+
|
|
147
|
+
Args:
|
|
148
|
+
num_points (int): The number of reference points to generate.
|
|
149
|
+
num_dims (int): The number of dimensions of the space in which the reference points are generated.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
np.ndarray: A (num_points) x (num_dims-1) array of reference points.
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
bounding_box, A, b, _ = get_reference_hull(num_dims)
|
|
156
|
+
points = numba_random_gen(num_points, bounding_box, A, b)
|
|
157
|
+
# Project vertices onto plane perpendicular to largest space diagonal
|
|
158
|
+
points_rotated = rotate_out(points)
|
|
159
|
+
return points, points_rotated
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@njit()
|
|
163
|
+
def numba_random_gen(num_points: int, bounding_box: np.ndarray, A: np.ndarray, b: np.ndarray) -> np.ndarray:
|
|
164
|
+
"""Generates num_points random points within the convex hull defined by A and b."""
|
|
165
|
+
num_dims_ = bounding_box.shape[1]
|
|
166
|
+
points = np.zeros((num_points, num_dims_))
|
|
167
|
+
|
|
168
|
+
eps = np.finfo(np.float32).eps
|
|
169
|
+
|
|
170
|
+
counter = 0
|
|
171
|
+
|
|
172
|
+
while counter < num_points:
|
|
173
|
+
point = np.zeros(num_dims_)
|
|
174
|
+
for i in range(num_dims_):
|
|
175
|
+
# Generate a random point within the bounding box
|
|
176
|
+
point[i] = np.random.uniform(bounding_box[0, i], bounding_box[1, i])
|
|
177
|
+
if np.all(point @ A + b < eps):
|
|
178
|
+
# If the point is inside the convex hull, add it to the list of points
|
|
179
|
+
points[counter] = point
|
|
180
|
+
counter += 1
|
|
181
|
+
return points
|