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