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.
Files changed (122) hide show
  1. desdeo/__init__.py +8 -8
  2. desdeo/api/README.md +73 -0
  3. desdeo/api/__init__.py +15 -0
  4. desdeo/api/app.py +40 -0
  5. desdeo/api/config.py +69 -0
  6. desdeo/api/config.toml +53 -0
  7. desdeo/api/db.py +25 -0
  8. desdeo/api/db_init.py +79 -0
  9. desdeo/api/db_models.py +164 -0
  10. desdeo/api/malaga_db_init.py +27 -0
  11. desdeo/api/models/__init__.py +66 -0
  12. desdeo/api/models/archive.py +34 -0
  13. desdeo/api/models/preference.py +90 -0
  14. desdeo/api/models/problem.py +507 -0
  15. desdeo/api/models/reference_point_method.py +18 -0
  16. desdeo/api/models/session.py +46 -0
  17. desdeo/api/models/state.py +96 -0
  18. desdeo/api/models/user.py +51 -0
  19. desdeo/api/routers/_NAUTILUS.py +245 -0
  20. desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
  21. desdeo/api/routers/_NIMBUS.py +762 -0
  22. desdeo/api/routers/__init__.py +5 -0
  23. desdeo/api/routers/problem.py +110 -0
  24. desdeo/api/routers/reference_point_method.py +117 -0
  25. desdeo/api/routers/session.py +76 -0
  26. desdeo/api/routers/test.py +16 -0
  27. desdeo/api/routers/user_authentication.py +366 -0
  28. desdeo/api/schema.py +94 -0
  29. desdeo/api/tests/__init__.py +0 -0
  30. desdeo/api/tests/conftest.py +59 -0
  31. desdeo/api/tests/test_models.py +701 -0
  32. desdeo/api/tests/test_routes.py +216 -0
  33. desdeo/api/utils/database.py +274 -0
  34. desdeo/api/utils/logger.py +29 -0
  35. desdeo/core.py +27 -0
  36. desdeo/emo/__init__.py +29 -0
  37. desdeo/emo/hooks/archivers.py +172 -0
  38. desdeo/emo/methods/EAs.py +418 -0
  39. desdeo/emo/methods/__init__.py +0 -0
  40. desdeo/emo/methods/bases.py +59 -0
  41. desdeo/emo/operators/__init__.py +1 -0
  42. desdeo/emo/operators/crossover.py +780 -0
  43. desdeo/emo/operators/evaluator.py +118 -0
  44. desdeo/emo/operators/generator.py +356 -0
  45. desdeo/emo/operators/mutation.py +1053 -0
  46. desdeo/emo/operators/selection.py +1036 -0
  47. desdeo/emo/operators/termination.py +178 -0
  48. desdeo/explanations/__init__.py +6 -0
  49. desdeo/explanations/explainer.py +100 -0
  50. desdeo/explanations/utils.py +90 -0
  51. desdeo/mcdm/__init__.py +19 -0
  52. desdeo/mcdm/nautili.py +345 -0
  53. desdeo/mcdm/nautilus.py +477 -0
  54. desdeo/mcdm/nautilus_navigator.py +655 -0
  55. desdeo/mcdm/nimbus.py +417 -0
  56. desdeo/mcdm/pareto_navigator.py +269 -0
  57. desdeo/mcdm/reference_point_method.py +116 -0
  58. desdeo/problem/__init__.py +79 -0
  59. desdeo/problem/evaluator.py +561 -0
  60. desdeo/problem/gurobipy_evaluator.py +562 -0
  61. desdeo/problem/infix_parser.py +341 -0
  62. desdeo/problem/json_parser.py +944 -0
  63. desdeo/problem/pyomo_evaluator.py +468 -0
  64. desdeo/problem/schema.py +1808 -0
  65. desdeo/problem/simulator_evaluator.py +298 -0
  66. desdeo/problem/sympy_evaluator.py +244 -0
  67. desdeo/problem/testproblems/__init__.py +73 -0
  68. desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
  69. desdeo/problem/testproblems/dtlz2_problem.py +102 -0
  70. desdeo/problem/testproblems/forest_problem.py +275 -0
  71. desdeo/problem/testproblems/knapsack_problem.py +163 -0
  72. desdeo/problem/testproblems/mcwb_problem.py +831 -0
  73. desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +83 -0
  74. desdeo/problem/testproblems/momip_problem.py +172 -0
  75. desdeo/problem/testproblems/nimbus_problem.py +143 -0
  76. desdeo/problem/testproblems/pareto_navigator_problem.py +89 -0
  77. desdeo/problem/testproblems/re_problem.py +492 -0
  78. desdeo/problem/testproblems/river_pollution_problem.py +434 -0
  79. desdeo/problem/testproblems/rocket_injector_design_problem.py +140 -0
  80. desdeo/problem/testproblems/simple_problem.py +351 -0
  81. desdeo/problem/testproblems/simulator_problem.py +92 -0
  82. desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
  83. desdeo/problem/testproblems/zdt_problem.py +271 -0
  84. desdeo/problem/utils.py +245 -0
  85. desdeo/tools/GenerateReferencePoints.py +181 -0
  86. desdeo/tools/__init__.py +102 -0
  87. desdeo/tools/generics.py +145 -0
  88. desdeo/tools/gurobipy_solver_interfaces.py +258 -0
  89. desdeo/tools/indicators_binary.py +11 -0
  90. desdeo/tools/indicators_unary.py +375 -0
  91. desdeo/tools/interaction_schema.py +38 -0
  92. desdeo/tools/intersection.py +54 -0
  93. desdeo/tools/iterative_pareto_representer.py +99 -0
  94. desdeo/tools/message.py +234 -0
  95. desdeo/tools/ng_solver_interfaces.py +199 -0
  96. desdeo/tools/non_dominated_sorting.py +133 -0
  97. desdeo/tools/patterns.py +281 -0
  98. desdeo/tools/proximal_solver.py +99 -0
  99. desdeo/tools/pyomo_solver_interfaces.py +464 -0
  100. desdeo/tools/reference_vectors.py +462 -0
  101. desdeo/tools/scalarization.py +3138 -0
  102. desdeo/tools/scipy_solver_interfaces.py +454 -0
  103. desdeo/tools/score_bands.py +464 -0
  104. desdeo/tools/utils.py +320 -0
  105. desdeo/utopia_stuff/__init__.py +0 -0
  106. desdeo/utopia_stuff/data/1.json +15 -0
  107. desdeo/utopia_stuff/data/2.json +13 -0
  108. desdeo/utopia_stuff/data/3.json +15 -0
  109. desdeo/utopia_stuff/data/4.json +17 -0
  110. desdeo/utopia_stuff/data/5.json +15 -0
  111. desdeo/utopia_stuff/from_json.py +40 -0
  112. desdeo/utopia_stuff/reinit_user.py +38 -0
  113. desdeo/utopia_stuff/utopia_db_init.py +212 -0
  114. desdeo/utopia_stuff/utopia_problem.py +403 -0
  115. desdeo/utopia_stuff/utopia_problem_old.py +415 -0
  116. desdeo/utopia_stuff/utopia_reference_solutions.py +79 -0
  117. desdeo-2.0.0.dist-info/LICENSE +21 -0
  118. desdeo-2.0.0.dist-info/METADATA +168 -0
  119. desdeo-2.0.0.dist-info/RECORD +120 -0
  120. {desdeo-1.1.3.dist-info → desdeo-2.0.0.dist-info}/WHEEL +1 -1
  121. desdeo-1.1.3.dist-info/METADATA +0 -18
  122. desdeo-1.1.3.dist-info/RECORD +0 -4
@@ -0,0 +1,415 @@
1
+ import math
2
+ import numpy as np
3
+ from pathlib import Path
4
+ import polars as pl
5
+
6
+ from desdeo.problem.schema import (
7
+ Constant,
8
+ Constraint,
9
+ ConstraintTypeEnum,
10
+ DiscreteRepresentation,
11
+ ExtraFunction,
12
+ Objective,
13
+ ObjectiveTypeEnum,
14
+ Problem,
15
+ TensorConstant,
16
+ TensorVariable,
17
+ Variable,
18
+ VariableTypeEnum,
19
+ )
20
+ from desdeo.tools.utils import available_solvers, payoff_table_method
21
+
22
+
23
+ def utopia_problem_old(problem_name: str = "Forest problem", holding: int = 1) -> tuple[Problem, dict]:
24
+ r"""Defines a test forest problem that has TensorConstants and TensorVariables.
25
+
26
+ The problem has TensorConstants V, W and P as vectors taking values from a data file and
27
+ TensorVariables X_n, where n is the number of units in the data, as vectors matching the constants in shape.
28
+ The variables are binary and each variable vector X_i has one variable with the value 1 while others have value 0.
29
+ The variable with the value 1 for each vector X_i represents the optimal plan for the corresponding unit i.
30
+ The three objective functions f_1, f_2, f_3 represent the net present value, wood volume at the end of
31
+ the planning period, and the profit from harvesting.
32
+ All of the objective functions are to be maximized.
33
+ The problem is defined as follows:
34
+
35
+ \begin{align}
36
+ \mbox{maximize~} & \sum_{j=1}^N\sum_{i \in I_j} v_{ij} x_{ij} & \\
37
+ \mbox{maximize~} & \sum_{j=1}^N\sum_{i \in I_j} w_{ij} x_{ij} & \\
38
+ \mbox{maximize~} & \sum_{j=1}^N\sum_{i \in I_j} p_{ij} x_{ij} & \\
39
+ \nonumber\\
40
+ \mbox{subject to~} & \sum\limits_{i \in I_j} x_{ij} = 1, & \forall j = 1 \ldots N \\
41
+ & x_{ij}\in \{0,1\}& \forall j = 1 \ldots N, ~\forall i\in I_j,
42
+ \end{align}
43
+
44
+ where $x_{ij}$ are decision variables representing the choice of implementing management plan $i$ in stand $j$,
45
+ and $I_j$ is the set of available management plans for stand $j$. For each plan $i$ in stand $j$
46
+ the net present value, wood volume at the end of the planning period, and the profit from harvesting
47
+ are represented by $v_{ij}$, $w_{ij}$, and $p_{ij}$ respectively.
48
+
49
+ Args:
50
+ holding (int, optional): The number of the holding to be optimized. Defaults to 1.
51
+ comparing (bool, optional): Determines if solutions are to be compared to those from the rahti app.
52
+ Defaults to None.
53
+
54
+ Returns:
55
+ Problem: An instance of the test forest problem.
56
+ """
57
+ # TODO: remove this at some point
58
+ comparing = False
59
+
60
+ schedule_dict = {}
61
+
62
+ discounting_factor = 3 # This can be 1, 2, 3, 4 or 5. It represents %
63
+ discounting = [
64
+ (1 - 0.01 * discounting_factor) ** 3,
65
+ (1 - 0.01 * discounting_factor) ** 8,
66
+ (1 - 0.01 * discounting_factor) ** 13,
67
+ ]
68
+
69
+ df = pl.read_csv(Path("tests/data/alternatives_290124.csv"), dtypes={"unit": pl.Float64})
70
+ df_key = pl.read_csv(Path("tests/data/alternatives_key_290124.csv"), dtypes={"unit": pl.Float64})
71
+
72
+ selected_df_v = df.filter(pl.col("holding") == holding).select(
73
+ ["unit", "schedule", f"npv_{discounting_factor}_percent"]
74
+ )
75
+ unique_units = selected_df_v.unique(["unit"], maintain_order=True).get_column("unit")
76
+ selected_df_v.group_by(["unit", "schedule"])
77
+ rows_by_key = selected_df_v.rows_by_key(key=["unit", "schedule"])
78
+ v_array = np.zeros((selected_df_v["unit"].n_unique(), selected_df_v["schedule"].n_unique()))
79
+ for i in range(np.shape(v_array)[0]):
80
+ for j in range(np.shape(v_array)[1]):
81
+ if (unique_units[i], j) in rows_by_key:
82
+ v_array[i][j] = rows_by_key[(unique_units[i], j)][0][0]
83
+
84
+ # determine whether the results are to be compared to those from the rahti app (for testing purposes)
85
+ # if compared, the stock values are calculated by substacting the value after 2025 period from
86
+ # the value after the 2035 period (in other words, last value - first value)
87
+ if comparing:
88
+ selected_df_w = df.filter(pl.col("holding") == holding).select(["unit", "schedule", "stock_2025", "stock_2035"])
89
+ selected_df_w.group_by(["unit", "schedule"])
90
+ rows_by_key = selected_df_w.rows_by_key(key=["unit", "schedule"])
91
+ selected_df_key_w = df_key.select(["unit", "schedule", "treatment"])
92
+ selected_df_key_w.group_by(["unit", "schedule"])
93
+ rows_by_key_df_key = selected_df_key_w.rows_by_key(key=["unit", "schedule"])
94
+ w_array = np.zeros((selected_df_w["unit"].n_unique(), selected_df_w["schedule"].n_unique()))
95
+ for i in range(np.shape(w_array)[0]):
96
+ for j in range(np.shape(w_array)[1]):
97
+ if len(rows_by_key_df_key[(unique_units[i], j)]) == 0:
98
+ continue
99
+ if (unique_units[i], j) in rows_by_key:
100
+ w_array[i][j] = rows_by_key[(unique_units[i], j)][0][1] - rows_by_key[(unique_units[i], j)][0][0]
101
+ else:
102
+ selected_df_w = df.filter(pl.col("holding") == holding).select(["unit", "schedule", "stock_2035"])
103
+ selected_df_w.group_by(["unit", "schedule"])
104
+ rows_by_key = selected_df_w.rows_by_key(key=["unit", "schedule"])
105
+ selected_df_key_w = df_key.select(["unit", "schedule", "treatment"])
106
+ selected_df_key_w.group_by(["unit", "schedule"])
107
+ rows_by_key_df_key = selected_df_key_w.rows_by_key(key=["unit", "schedule"])
108
+ w_array = np.zeros((selected_df_w["unit"].n_unique(), selected_df_w["schedule"].n_unique()))
109
+ for i in range(np.shape(w_array)[0]):
110
+ for j in range(np.shape(w_array)[1]):
111
+ if len(rows_by_key_df_key[(unique_units[i], j)]) == 0:
112
+ continue
113
+ if (unique_units[i], j) in rows_by_key:
114
+ w_array[i][j] = rows_by_key[(unique_units[i], j)][0][0]
115
+
116
+ """
117
+ selected_df_p = df.filter(pl.col("holding") == holding).select(
118
+ ["unit", "schedule", "harvest_value_period_2025", "harvest_value_period_2030", "harvest_value_period_2035"]
119
+ )
120
+ selected_df_p.group_by(["unit", "schedule"])
121
+ rows_by_key = selected_df_p.rows_by_key(key=["unit", "schedule"])
122
+ p_array = np.zeros((selected_df_p["unit"].n_unique(), selected_df_p["schedule"].n_unique()))
123
+ discounting = [0.95**5, 0.95**10, 0.95**15]
124
+ for i in range(np.shape(p_array)[0]):
125
+ for j in range(np.shape(p_array)[1]):
126
+ if (unique_units[i], j) in rows_by_key:
127
+ p_array[i][j] = (
128
+ sum(x * y for x, y in zip(rows_by_key[(unique_units[i], j)][0], discounting, strict=True)) + 1e-6
129
+ ) # the 1E-6 is to deal with an annoying corner case, don't worry about it
130
+ v_array[i][j] += p_array[i][j]
131
+ """
132
+
133
+ selected_df_p1 = df.filter(pl.col("holding") == holding).select(["unit", "schedule", "harvest_value_period_2025"])
134
+ selected_df_p1.group_by(["unit", "schedule"])
135
+ rows_by_key = selected_df_p1.rows_by_key(key=["unit", "schedule"])
136
+ p1_array = np.zeros((selected_df_p1["unit"].n_unique(), selected_df_p1["schedule"].n_unique()))
137
+ for i in range(np.shape(p1_array)[0]):
138
+ for j in range(np.shape(p1_array)[1]):
139
+ if (unique_units[i], j) in rows_by_key:
140
+ p1_array[i][j] = rows_by_key[(unique_units[i], j)][0][0] + 1e-6
141
+
142
+ selected_df_p2 = df.filter(pl.col("holding") == holding).select(["unit", "schedule", "harvest_value_period_2030"])
143
+ selected_df_p2.group_by(["unit", "schedule"])
144
+ rows_by_key = selected_df_p2.rows_by_key(key=["unit", "schedule"])
145
+ p2_array = np.zeros((selected_df_p2["unit"].n_unique(), selected_df_p2["schedule"].n_unique()))
146
+ for i in range(np.shape(p2_array)[0]):
147
+ for j in range(np.shape(p2_array)[1]):
148
+ if (unique_units[i], j) in rows_by_key:
149
+ p2_array[i][j] = rows_by_key[(unique_units[i], j)][0][0] + 1e-6
150
+
151
+ selected_df_p3 = df.filter(pl.col("holding") == holding).select(["unit", "schedule", "harvest_value_period_2035"])
152
+ selected_df_p3.group_by(["unit", "schedule"])
153
+ rows_by_key = selected_df_p3.rows_by_key(key=["unit", "schedule"])
154
+ p3_array = np.zeros((selected_df_p3["unit"].n_unique(), selected_df_p3["schedule"].n_unique()))
155
+ for i in range(np.shape(p3_array)[0]):
156
+ for j in range(np.shape(p3_array)[1]):
157
+ if (unique_units[i], j) in rows_by_key:
158
+ p3_array[i][j] = rows_by_key[(unique_units[i], j)][0][0] + 1e-6
159
+
160
+ constants = []
161
+ variables = []
162
+ constraints = []
163
+ f_1_func = []
164
+ f_2_func = []
165
+ p1_func = []
166
+ p2_func = []
167
+ p3_func = []
168
+ # define the constants V, W and P, decision variable X, constraints, and objective function expressions in one loop
169
+ for i in range(np.shape(v_array)[0]):
170
+ # Constants V, W and P
171
+ v = TensorConstant(
172
+ name=f"V_{i+1}",
173
+ symbol=f"V_{i+1}",
174
+ shape=[np.shape(v_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
175
+ values=v_array[i].tolist(),
176
+ )
177
+ constants.append(v)
178
+ w = TensorConstant(
179
+ name=f"W_{i+1}",
180
+ symbol=f"W_{i+1}",
181
+ shape=[np.shape(w_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
182
+ values=w_array[i].tolist(),
183
+ )
184
+ constants.append(w)
185
+ p1 = TensorConstant(
186
+ name=f"P1_{i+1}",
187
+ symbol=f"P1_{i+1}",
188
+ shape=[np.shape(p1_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
189
+ values=p1_array[i].tolist(),
190
+ )
191
+ constants.append(p1)
192
+
193
+ p2 = TensorConstant(
194
+ name=f"P2_{i+1}",
195
+ symbol=f"P2_{i+1}",
196
+ shape=[np.shape(p2_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
197
+ values=p2_array[i].tolist(),
198
+ )
199
+ constants.append(p2)
200
+
201
+ p3 = TensorConstant(
202
+ name=f"P3_{i+1}",
203
+ symbol=f"P3_{i+1}",
204
+ shape=[np.shape(p3_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
205
+ values=p3_array[i].tolist(),
206
+ )
207
+ constants.append(p3)
208
+
209
+ # Decision variable X
210
+ x = TensorVariable(
211
+ name=f"X_{i+1}",
212
+ symbol=f"X_{i+1}",
213
+ variable_type=VariableTypeEnum.binary,
214
+ shape=[np.shape(v_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
215
+ lowerbounds=np.shape(v_array)[1] * [0],
216
+ upperbounds=np.shape(v_array)[1] * [1],
217
+ initial_values=np.shape(v_array)[1] * [0],
218
+ )
219
+ variables.append(x)
220
+
221
+ # Fill out the dict with information about treatments associated with X_{i+1}
222
+ treatment_list = (
223
+ df_key.filter((pl.col("holding") == holding) & (pl.col("unit") == unique_units[i]))
224
+ .get_column("treatment")
225
+ .to_list()
226
+ )
227
+ schedule_dict[f"X_{i+1}"] = dict(zip(range(len(treatment_list)), treatment_list, strict=True))
228
+ schedule_dict[f"X_{i+1}"]["unit"] = unique_units[i]
229
+
230
+ # Constraints
231
+ con = Constraint(
232
+ name=f"x_con_{i+1}",
233
+ symbol=f"x_con_{i+1}",
234
+ cons_type=ConstraintTypeEnum.EQ,
235
+ func=f"Sum(X_{i+1}) - 1",
236
+ is_twice_differentiable=True,
237
+ )
238
+ constraints.append(con)
239
+
240
+ # Objective function expressions
241
+ exprs = f"V_{i+1}@X_{i+1}"
242
+ f_1_func.append(exprs)
243
+
244
+ exprs = f"W_{i+1}@X_{i+1}"
245
+ f_2_func.append(exprs)
246
+
247
+ exprs = f"P1_{i+1}@X_{i+1}"
248
+ p1_func.append(exprs)
249
+
250
+ exprs = f"P2_{i+1}@X_{i+1}"
251
+ p2_func.append(exprs)
252
+
253
+ exprs = f"P3_{i+1}@X_{i+1}"
254
+ p3_func.append(exprs)
255
+
256
+ for i in range(1, 4):
257
+ pvar = Variable(name=f"P_{i}", symbol=f"P_{i}", variable_type=VariableTypeEnum.real, lowerbound=0)
258
+ variables.append(pvar)
259
+
260
+ vvar = Variable(name="V_end", symbol="V_end", variable_type=VariableTypeEnum.real, lowerbound=0)
261
+ variables.append(vvar)
262
+
263
+ # get the remainder value of the forest into decision variable V_end
264
+ v_func = "V_end - " + " - ".join(f_1_func)
265
+ con = Constraint(
266
+ name="v_con",
267
+ symbol="v_con",
268
+ cons_type=ConstraintTypeEnum.EQ,
269
+ func=v_func,
270
+ is_twice_differentiable=True,
271
+ )
272
+ constraints.append(con)
273
+
274
+ # These are here, so that we can get the harvesting incomes into decision variables P_i
275
+ p1_func = "P_1 - " + " - ".join(p1_func)
276
+ con = Constraint(
277
+ name="p1_con",
278
+ symbol="p1_con",
279
+ cons_type=ConstraintTypeEnum.EQ,
280
+ func=p1_func,
281
+ is_twice_differentiable=True,
282
+ )
283
+ constraints.append(con)
284
+
285
+ p2_func = "P_2 - " + " - ".join(p2_func)
286
+ con = Constraint(
287
+ name="p2_con",
288
+ symbol="p2_con",
289
+ cons_type=ConstraintTypeEnum.EQ,
290
+ func=p2_func,
291
+ is_twice_differentiable=True,
292
+ )
293
+ constraints.append(con)
294
+
295
+ p3_func = "P_3 - " + " - ".join(p3_func)
296
+ con = Constraint(
297
+ name="p3_con",
298
+ symbol="p3_con",
299
+ cons_type=ConstraintTypeEnum.EQ,
300
+ func=p3_func,
301
+ is_twice_differentiable=True,
302
+ )
303
+ constraints.append(con)
304
+
305
+ # print(v_func)
306
+ # print(p1_func)
307
+ # print(p2_func)
308
+ # print(p3_func)
309
+
310
+ # form the objective function sums
311
+ f_2_func = " + ".join(f_2_func)
312
+ f_3_func = f"{discounting[0]} * P_1 + {discounting[1]} * P_2 + {discounting[2]} * P_3"
313
+ f_1_func = "V_end + " + f_3_func
314
+
315
+ # print(f_1_func)
316
+ # print(f_2_func)
317
+ # print(f_3_func)
318
+
319
+ f_1 = Objective(
320
+ name="Net present value",
321
+ symbol="f_1",
322
+ func=f_1_func,
323
+ maximize=True,
324
+ objective_type=ObjectiveTypeEnum.analytical,
325
+ is_linear=True,
326
+ is_convex=False, # not checked
327
+ is_twice_differentiable=True,
328
+ )
329
+
330
+ f_2 = Objective(
331
+ name="Wood stock volume",
332
+ symbol="f_2",
333
+ func=f_2_func,
334
+ maximize=True,
335
+ objective_type=ObjectiveTypeEnum.analytical,
336
+ is_linear=True,
337
+ is_convex=False, # not checked
338
+ is_twice_differentiable=True,
339
+ )
340
+
341
+ f_3 = Objective(
342
+ name="Harvest value",
343
+ symbol="f_3",
344
+ func=f_3_func,
345
+ maximize=True,
346
+ objective_type=ObjectiveTypeEnum.analytical,
347
+ is_linear=True,
348
+ is_convex=False, # not checked
349
+ is_twice_differentiable=True,
350
+ )
351
+
352
+ # This is so bad, but we currently don't have a better way
353
+ ideals, nadirs = payoff_table_method(
354
+ problem=Problem(
355
+ name=problem_name,
356
+ description="A test forest problem.",
357
+ constants=constants,
358
+ variables=variables,
359
+ objectives=[f_1, f_2, f_3],
360
+ constraints=constraints,
361
+ ),
362
+ solver=available_solvers["gurobipy"],
363
+ )
364
+
365
+ print(ideals)
366
+ print(nadirs)
367
+
368
+ f_1 = Objective(
369
+ name="Nettonykyarvo / €",
370
+ symbol="f_1",
371
+ func=f_1_func,
372
+ maximize=True,
373
+ ideal=math.ceil(ideals["f_1"]),
374
+ nadir=math.floor(nadirs["f_1"]),
375
+ objective_type=ObjectiveTypeEnum.analytical,
376
+ is_linear=True,
377
+ is_convex=False, # not checked
378
+ is_twice_differentiable=True,
379
+ )
380
+
381
+ f_2 = Objective(
382
+ name="Puuston tilavuus / m^3",
383
+ symbol="f_2",
384
+ func=f_2_func,
385
+ maximize=True,
386
+ ideal=math.ceil(ideals["f_2"]),
387
+ nadir=math.floor(nadirs["f_2"]),
388
+ objective_type=ObjectiveTypeEnum.analytical,
389
+ is_linear=True,
390
+ is_convex=False, # not checked
391
+ is_twice_differentiable=True,
392
+ )
393
+
394
+ f_3 = Objective(
395
+ name="Hakkuiden tuotto / €",
396
+ symbol="f_3",
397
+ func=f_3_func,
398
+ maximize=True,
399
+ ideal=math.ceil(ideals["f_3"]),
400
+ nadir=math.floor(nadirs["f_3"]),
401
+ objective_type=ObjectiveTypeEnum.analytical,
402
+ is_linear=True,
403
+ is_convex=False, # not checked
404
+ is_twice_differentiable=True,
405
+ )
406
+
407
+ return Problem(
408
+ name=problem_name,
409
+ description="A test forest problem.",
410
+ constants=constants,
411
+ variables=variables,
412
+ objectives=[f_1, f_2, f_3],
413
+ constraints=constraints,
414
+ is_twice_differentiable=True,
415
+ ), schedule_dict
@@ -0,0 +1,79 @@
1
+ """This is a docstring."""
2
+
3
+ import json
4
+
5
+ from desdeo.api.db import SessionLocal
6
+ from desdeo.api.db_models import Method, SolutionArchive, User, Utopia
7
+ from desdeo.api.db_models import Problem as ProblemInDB
8
+ from desdeo.api.schema import Methods
9
+ from desdeo.utopia_stuff.utopia_problem import utopia_problem
10
+
11
+
12
+ def get_chosen_solution(username: str):
13
+ """Recreates the forest problem for the specified user.
14
+
15
+ Args:
16
+ username (str): username of the forest owner
17
+ """
18
+ db = SessionLocal()
19
+ user = db.query(User).filter(User.username == username).first()
20
+ if user is None:
21
+ raise Exception("User not found") # noqa: TRY002
22
+ problem_in_db = (
23
+ db.query(ProblemInDB).filter(ProblemInDB.owner == user.id, ProblemInDB.name == "Metsänhoitosuunnitelma").first()
24
+ )
25
+ # map_info = db.query(Utopia).filter(Utopia.problem == problem_in_db.id, Utopia.user == user.id)
26
+
27
+ nimbus_method = db.query(Method).filter(Method.kind == Methods.NIMBUS).first()
28
+
29
+ solution = (
30
+ db.query(SolutionArchive)
31
+ .filter(
32
+ SolutionArchive.user == user.id,
33
+ SolutionArchive.problem == problem_in_db.id,
34
+ SolutionArchive.method == nimbus_method.id,
35
+ )
36
+ .first()
37
+ )
38
+
39
+ if not solution:
40
+ return (None, None)
41
+
42
+ return (solution.objectives, solution.decision_variables)
43
+
44
+
45
+ """ with open("C:/MyTemp/data/forest_owners.json") as file: # noqa: PTH123
46
+ fo_dict = json.load(file)
47
+
48
+ print(fo_dict[username])
49
+
50
+ problem, schedule_dict = utopia_problem(
51
+ simulation_results=fo_dict[username]["simulation_results"],
52
+ treatment_key=fo_dict[username]["treatment_key"],
53
+ problem_name="Metsänhoitosuunnitelma",
54
+ )
55
+
56
+ problem_in_db.value = problem.model_dump(mode="json")
57
+ map_info.schedule_dict = schedule_dict
58
+
59
+ db.commit()"""
60
+
61
+
62
+ if __name__ == "__main__":
63
+ # print(get_chosen_solution("Iisakkila"))
64
+ with open("C:/MyTemp/data/forest_owners.json") as file: # noqa: PTH123
65
+ fo_dict = json.load(file)
66
+
67
+ reference_dict = {}
68
+ for fo in fo_dict:
69
+ objectives, decision_vars = get_chosen_solution(fo)
70
+ if objectives is None:
71
+ continue
72
+ reference_dict[fo] = {
73
+ "objectives": objectives,
74
+ "decisions_vars": decision_vars,
75
+ "simulation_results": fo_dict[fo]["simulation_results"],
76
+ }
77
+
78
+ with open("reference_solutions.json", "w") as file:
79
+ json.dump(reference_dict, file, indent=4) # indent=4 makes it nicely formatted
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 The Multiobjective Optimization Group (http://www.mit.jyu.fi/optgroup/).
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,168 @@
1
+ Metadata-Version: 2.3
2
+ Name: desdeo
3
+ Version: 2.0.0
4
+ Summary: DESDEO is a modular and open source framework for interactive multiobjective optimization.
5
+ License: MIT
6
+ Author: Giovanni Misitano
7
+ Author-email: giovanni.a.misitano@jyu.fi
8
+ Requires-Python: >=3.12,<3.13
9
+ Classifier: License :: OSI Approved :: MIT License
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3.12
12
+ Requires-Dist: bayesian-optimization (>=1.0,<2.0)
13
+ Requires-Dist: cvxpy[scip] (>=1.6.4,<2.0.0)
14
+ Requires-Dist: greenlet (>=3.1.1,<4.0.0)
15
+ Requires-Dist: gurobipy (>=12.0.0,<13.0.0)
16
+ Requires-Dist: nevergrad (>=1.0.12,<2.0.0)
17
+ Requires-Dist: numba (>=0.61.0,<0.62.0)
18
+ Requires-Dist: numpy (>=2.2.0,<3.0.0)
19
+ Requires-Dist: polars (>=1.17,<2.0)
20
+ Requires-Dist: pyarrow (>=20.0.0,<21.0.0)
21
+ Requires-Dist: pydantic (>=2.9,<3.0)
22
+ Requires-Dist: pymoo (>=0.6.1.2,<0.7.0.0)
23
+ Requires-Dist: pyomo (>=6.8,<7.0)
24
+ Requires-Dist: pyparsing (>=3.0,<4.0)
25
+ Requires-Dist: scipy (>=1.11.4,<2.0.0)
26
+ Requires-Dist: shap (>=0.47.0,<0.48.0)
27
+ Requires-Dist: sympy (>=1.0,<2.0)
28
+ Requires-Dist: tsp-solver (>=0.1,<0.2)
29
+ Description-Content-Type: text/markdown
30
+
31
+ # DESDEO: the open-source software framework for interactive multiobjective optimization
32
+
33
+ [![Documentation Status](https://img.shields.io/readthedocs/desdeo.svg?version=desdeo2&label=Documentation)](https://desdeo.readthedocs.io/en/latest/) ![Tests](https://img.shields.io/github/actions/workflow/status/industrial-optimization-group/DESDEO/unit_tests.yaml?branch=desdeo2&label=Tests)
34
+
35
+ [![Discord server](https://dcbadge.vercel.app/api/server/TgSnUmzv5M)](https://discord.gg/TgSnUmzv5M)
36
+
37
+ ## Introduction
38
+
39
+ DESDEO is an open-source framework for interactive multiobjective optimization
40
+ methods. The framework contains implementations of both scalarization- and
41
+ population-based interactive methods. There are currently no other open-source
42
+ software frameworks that focus solely on the implementation of interactive
43
+ multiobjective optimization methods.
44
+
45
+ The mission of DESDEO is to increase awareness of the benefits of interactive
46
+ multiobjective optimization methods, make interactive methods openly available,
47
+ and to function as _the_ central hub for implementations of various interactive
48
+ methods. Apart from existing methods, DESDEO offers various tools to facilitate
49
+ the development of new methods and their application as well. Another important
50
+ goal of DESDEO is to answer the needs of decision makers and practitioners when
51
+ it comes to modeling and solving real-life multiobjective optimization problems.
52
+
53
+ In the bigger picture, DESDEO will be composed of three major components:
54
+
55
+ 1. The __core-logic__, which contains the algorithmic implementation of
56
+ interactive methods, various tools related to multiobjective optimization, and
57
+ means to model a variety of multiobjective optimization problems. The core-logic
58
+ can be considered stable enough for use in research and applications.
59
+ 2. The __web-API__ (WIP), which implements a web-based application programming
60
+ interface (API) to allow the use of the various functionalities found in
61
+ DESDEO's core-logic through a web connection. The web-API implements also a
62
+ database, which is a vital component for managing and enabling
63
+ decision-support using the framework. __The
64
+ web-API is currently under heavy development, and is subject to changes.__
65
+ 3. The __web-GUI__ (WIP), which implements a web-based interface for utilizing
66
+ the interactive methods and tools for modeling and solving multiobjective
67
+ optimization problems. __The web-GUI relies heavily on the web-API, and is also being actively developed currently, and therefore subject to sudden changes.__
68
+
69
+ For developing and experimenting with interactive multiobjective optimization
70
+ methods on a "grass root" level, the __core-logic__ provides the necessary
71
+ tools. For deploying interactive methods, the __web-API__ and the __web_GUI__
72
+ play a central role.
73
+
74
+ DESDEO is an open-source project and everybody is welcome to contribute!
75
+
76
+ ## Core-logic: key features
77
+
78
+ DESDEO's core-logic offers various features that can facilitate the application and
79
+ development of new interactive multiobjective optimization methods. Some
80
+ of the key features include, but are not limited to,
81
+
82
+ - A powerful, pydantic-based, schema for modeling multiobjective optimization
83
+ problem of various kinds. Including, analytically defined problems, data-based
84
+ problems, surrogate-based problems, and simulation-based problems.
85
+ Both continuous and (mixed-)integer problems are supported as well.
86
+ - Support to interface to many popular and powerful optimization software for
87
+ solving multiobjective optimization problems. Including Gurobi, various solvers
88
+ from the COIN-OR project, and nevergrad, for instance.
89
+ - A wide assortment of modular software components for implementing existing
90
+ and new interactive multiobjective optimization methods. For example, many
91
+ scalarization functions and evolutionary operators for multiobjective
92
+ optimization are available.
93
+ - An extensive documentation suitable for both newcomers to DESDEO and
94
+ interactive multiobjective optimization in general, and seasoned veterans.
95
+
96
+ ## Web-API: key features
97
+
98
+ DESDEO's web-API is currently under active development. Once it stabilized, its
99
+ key features will be listed here. In the meantime, the interested user can
100
+ follow (and contribute!) the development progress of the web-API in [this
101
+ issue](https://github.com/industrial-optimization-group/DESDEO/issues/245).
102
+
103
+ ## Web-GUI: key features
104
+
105
+ DESDEO's web-GUI is currently in a planning stage. Once its active development
106
+ starts, an issue will be created for documenting its development, as is
107
+ currently the case with the web-API.
108
+
109
+ ## Installation instructions
110
+
111
+ For installing DESDEO, please see the documentation (TODO) for instructions.
112
+
113
+ ## Documentation
114
+
115
+ Care has been taken to make sure DESDEO is well documented, making it accessible
116
+ to both newcomers and seasoned users alike. [The documentation of DESDEO is
117
+ available online.](https://desdeo.readthedocs.io/en/desdeo2/)
118
+
119
+ ## Contributing
120
+
121
+ As DESDEO is an open source-project, anybody is welcome to contribute.
122
+ An extensive tutorial to get started contributing to DESDEO
123
+ [is available in the documentation](https://desdeo.readthedocs.io/en/desdeo2/tutorials/contributing/).
124
+ Be sure to check it out!
125
+
126
+ For additional support for contributing to DESDEO,
127
+ be sure to check out the DESDEO channels
128
+ in the MCDM Community's Discord server. You may join the server
129
+ [through this invite](https://discord.gg/TgSnUmzv5M).
130
+
131
+ ## License
132
+
133
+ DESDEO is licensed under the MIT license. For more information,
134
+ check the `LICENSE` file.
135
+
136
+ ## Citing DESDEO
137
+
138
+ To cite DESDEO, please include the following reference:
139
+
140
+ [Misitano, G., Saini, B. S., Afsar, B., Shavazipour, B., & Miettinen, K. (2021). DESDEO: The modular and open source framework for interactive multiobjective optimization. IEEE Access, 9, 148277-148295.](https://doi.org/10.1109/ACCESS.2021.3123825)
141
+
142
+ ```
143
+ @article{misitano2021desdeo,
144
+ title={DESDEO: The modular and open source framework for interactive multiobjective optimization},
145
+ author={Misitano, Giovanni and Saini, Bhupinder Singh and Afsar, Bekir and Shavazipour, Babooshka and Miettinen, Kaisa},
146
+ journal={IEEE Access},
147
+ volume={9},
148
+ pages={148277--148295},
149
+ year={2021},
150
+ publisher={IEEE}
151
+ }
152
+ ```
153
+
154
+ __Note__: A new article describing the latest iteration of the framework,
155
+ also known as _DESDEO 2.0_ is currently being prepared. The content of
156
+ this repository's master branch is considered to be _DESDEO 2.0_.
157
+
158
+ ## Funding
159
+
160
+ Currently, DESDEO's development is partly funded by two projects granted by the
161
+ [Research Council of Finland](https://www.aka.fi/en/). The most recent ones
162
+ include:
163
+
164
+ - DESIDES (project 355346)
165
+ - UTOPIA (project 352784)
166
+ - DAEMON (project 322221)
167
+
168
+