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,88 @@
1
+ from desdeo.problem.schema import (
2
+ Constant,
3
+ Constraint,
4
+ Objective,
5
+ Problem,
6
+ Variable,
7
+ )
8
+
9
+ def binh_and_korn(maximize: tuple[bool] = (False, False)) -> Problem:
10
+ """Create a pydantic dataclass representation of the Binh and Korn problem.
11
+
12
+ The function has two objective functions, two variables, and two constraint functions.
13
+ For testing purposes, it can be chosen whether the firs and second objective should
14
+ be maximized instead.
15
+
16
+ Arguments:
17
+ maximize (tuple[bool]): whether the first or second objective should be
18
+ maximized or not. Defaults to (False, False).
19
+
20
+ References:
21
+ Binh T. and Korn U. (1997) MOBES: A Multiobjective Evolution Strategy for Constrained Optimization Problems.
22
+ In: Proceedings of the Third International Conference on Genetic Algorithms. Czech Republic. pp. 176-182.
23
+ """
24
+ # These constants are for demonstrative purposes.
25
+ constant_1 = Constant(name="Four", symbol="c_1", value=4)
26
+ constant_2 = Constant(name="Five", symbol="c_2", value=5)
27
+
28
+ variable_1 = Variable(
29
+ name="The first variable", symbol="x_1", variable_type="real", lowerbound=0, upperbound=5, initial_value=2.5
30
+ )
31
+ variable_2 = Variable(
32
+ name="The second variable", symbol="x_2", variable_type="real", lowerbound=0, upperbound=3, initial_value=1.5
33
+ )
34
+
35
+ objective_1 = Objective(
36
+ name="Objective 1",
37
+ symbol="f_1",
38
+ func=f"{'-' if maximize[0] else ''}(c_1 * x_1**2 + c_1*x_2**2)",
39
+ # func=["Add", ["Multiply", "c_1", ["Square", "x_1"]], ["Multiply", "c_1", ["Square", "x_2"]]],
40
+ maximize=maximize[0],
41
+ ideal=0,
42
+ nadir=140 if not maximize[0] else -140,
43
+ is_linear=False,
44
+ is_convex=True,
45
+ is_twice_differentiable=True,
46
+ )
47
+ objective_2 = Objective(
48
+ name="Objective 2",
49
+ symbol="f_2",
50
+ # func=["Add", ["Square", ["Subtract", "x_1", "c_2"]], ["Square", ["Subtract", "x_2", "c_2"]]],
51
+ func=f"{'-' if maximize[1] else ''}((x_1 - c_2)**2 + (x_2 - c_2)**2)",
52
+ maximize=maximize[1],
53
+ ideal=0,
54
+ nadir=50 if not maximize[0] else -50,
55
+ is_linear=False,
56
+ is_convex=True,
57
+ is_twice_differentiable=True,
58
+ )
59
+
60
+ constraint_1 = Constraint(
61
+ name="Constraint 1",
62
+ symbol="g_1",
63
+ cons_type="<=",
64
+ func=["Add", ["Square", ["Subtract", "x_1", "c_2"]], ["Square", "x_2"], -25],
65
+ is_linear=False,
66
+ is_convex=True,
67
+ is_twice_differentiable=True,
68
+ )
69
+
70
+ constraint_2 = Constraint(
71
+ name="Constraint 2",
72
+ symbol="g_2",
73
+ cons_type="<=",
74
+ func=["Add", ["Negate", ["Square", ["Subtract", "x_1", 8]]], ["Negate", ["Square", ["Add", "x_2", 3]]], 7.7],
75
+ is_linear=False,
76
+ is_convex=True,
77
+ is_twice_differentiable=True,
78
+ )
79
+
80
+ return Problem(
81
+ name="The Binh and Korn function",
82
+ description="The two-objective problem used in the paper by Binh and Korn.",
83
+ constants=[constant_1, constant_2],
84
+ variables=[variable_1, variable_2],
85
+ objectives=[objective_1, objective_2],
86
+ constraints=[constraint_1, constraint_2],
87
+ is_twice_differentiable=True,
88
+ )
@@ -0,0 +1,102 @@
1
+ import numpy as np
2
+
3
+ from desdeo.problem.schema import (
4
+ ExtraFunction,
5
+ Objective,
6
+ Problem,
7
+ Variable,
8
+ VariableTypeEnum,
9
+ )
10
+
11
+ def dtlz2(n_variables: int, n_objectives: int) -> Problem:
12
+ r"""Defines the DTLZ2 test problem.
13
+
14
+ The objective functions for DTLZ2 are defined as follows, for $i = 1$ to $M$:
15
+
16
+ \begin{equation}
17
+ \underset{\mathbf{x}}{\operatorname{min}}
18
+ f_i(\mathbf{x}) = (1+g(\mathbf{x}_M)) \prod_{j=1}^{M-i} \cos\left(x_j \frac{\pi}{2}\right) \times
19
+ \begin{cases}
20
+ 1 & \text{if } i=1 \\
21
+ \sin\left(x_{(M-i+1)}\frac{\pi}{2}\right) & \text{otherwise},
22
+ \end{cases}
23
+ \end{equation}
24
+
25
+ where
26
+
27
+ \begin{equation}
28
+ g(\mathbf{x}_M) = \sum_{x_i \in \mathbf{x}_M} \left( x_i - 0.5 \right)^2,
29
+ \end{equation}
30
+
31
+ and $\mathbf{x}_M$ represents the last $n-k$ dimensions of the decision vector.
32
+ Pareto optimal solutions to the DTLZ2 problem consist of $x_i = 0.5$ for
33
+ all $x_i \in\mathbf{x}_{M}$, and $\sum{i=1}^{M} f_i^2 = 1$.
34
+
35
+ Args:
36
+ n_variables (int): number of variables.
37
+ n_objectives (int): number of objective functions.
38
+
39
+ Returns:
40
+ Problem: an instance of the DTLZ2 problem with `n_variables` variables and `n_objectives` objective
41
+ functions.
42
+
43
+ References:
44
+ Deb, K., Thiele, L., Laumanns, M., Zitzler, E. (2005). Scalable Test
45
+ Problems for Evolutionary Multiobjective Optimization. In: Abraham, A.,
46
+ Jain, L., Goldberg, R. (eds) Evolutionary Multiobjective Optimization.
47
+ Advanced Information and Knowledge Processing. Springer.
48
+ """
49
+ # function g
50
+ g_symbol = "g"
51
+ g_expr = " + ".join([f"(x_{i} - 0.5)**2" for i in range(n_objectives, n_variables + 1)])
52
+ g_expr = "1 + " + g_expr
53
+
54
+ objectives = []
55
+ for m in range(1, n_objectives + 1):
56
+ # function f_m
57
+ prod_expr = " * ".join([f"Cos(0.5 * {np.pi} * x_{i})" for i in range(1, n_objectives - m + 1)])
58
+ if m > 1:
59
+ prod_expr += f"{' * ' if prod_expr != "" else ""}Sin(0.5 * {np.pi} * x_{n_objectives - m + 1})"
60
+ if prod_expr == "":
61
+ prod_expr = "1" # When m == n_objectives, the product is empty, implying f_M = g.
62
+ f_m_expr = f"({g_symbol}) * ({prod_expr})"
63
+
64
+ objectives.append(
65
+ Objective(
66
+ name=f"f_{m}",
67
+ symbol=f"f_{m}",
68
+ func=f_m_expr,
69
+ maximize=False,
70
+ ideal=0,
71
+ nadir=1, # Assuming the range of g and the trigonometric functions
72
+ is_convex=False,
73
+ is_linear=False,
74
+ is_twice_differentiable=True,
75
+ )
76
+ )
77
+
78
+ variables = [
79
+ Variable(
80
+ name=f"x_{i}",
81
+ symbol=f"x_{i}",
82
+ variable_type=VariableTypeEnum.real,
83
+ lowerbound=0,
84
+ upperbound=1,
85
+ initial_value=1.0,
86
+ )
87
+ for i in range(1, n_variables + 1)
88
+ ]
89
+
90
+ extras = [
91
+ ExtraFunction(
92
+ name="g", symbol=g_symbol, func=g_expr, is_convex=False, is_linear=False, is_twice_differentiable=True
93
+ ),
94
+ ]
95
+
96
+ return Problem(
97
+ name="dtlz2",
98
+ description="The DTLZ2 test problem.",
99
+ variables=variables,
100
+ objectives=objectives,
101
+ extra_funcs=extras,
102
+ )
@@ -0,0 +1,275 @@
1
+ from pathlib import Path
2
+
3
+ import numpy as np
4
+ import polars as pl
5
+
6
+ from desdeo.problem.schema import (
7
+ Constraint,
8
+ ConstraintTypeEnum,
9
+ DiscreteRepresentation,
10
+ Objective,
11
+ ObjectiveTypeEnum,
12
+ Problem,
13
+ TensorConstant,
14
+ TensorVariable,
15
+ Variable,
16
+ VariableTypeEnum,
17
+ )
18
+
19
+ def forest_problem(simulation_results: str, treatment_key: str, holding: int = 1, comparing: bool = False) -> Problem:
20
+ r"""Defines a test forest problem that has TensorConstants and TensorVariables.
21
+
22
+ The problem has TensorConstants V, W and P as vectors taking values from a data file and
23
+ TensorVariables X_n, where n is the number of units in the data, as vectors matching the constants in shape.
24
+ The variables are binary and each variable vector X_i has one variable with the value 1 while others have value 0.
25
+ The variable with the value 1 for each vector X_i represents the optimal plan for the corresponding unit i.
26
+ The three objective functions f_1, f_2, f_3 represent the net present value, wood volume at the end of
27
+ the planning period, and the profit from harvesting.
28
+ All of the objective functions are to be maximized.
29
+ The problem is defined as follows:
30
+
31
+ \begin{align}
32
+ \max_{\mathbf{x}} & \quad \sum_{j=1}^N\sum_{i \in I_j} v_{ij} x_{ij} & \\
33
+ & \quad \sum_{j=1}^N\sum_{i \in I_j} w_{ij} x_{ij} & \\
34
+ & \quad \sum_{j=1}^N\sum_{i \in I_j} p_{ij} x_{ij} & \\
35
+ \text{s.t.} & \quad \sum\limits_{i \in I_j} x_{ij} = 1, & \forall j = 1 \ldots N \\
36
+ & \quad x_{ij}\in \{0,1\}& \forall j = 1 \ldots N, ~\forall i\in I_j,
37
+ \end{align}
38
+
39
+ where $x_{ij}$ are decision variables representing the choice of implementing management plan $i$ in stand $j$,
40
+ and $I_j$ is the set of available management plans for stand $j$. For each plan $i$ in stand $j$
41
+ the net present value, wood volume at the end of the planning period, and the profit from harvesting
42
+ are represented by $v_{ij}$, $w_{ij}$, and $p_{ij}$ respectively.
43
+
44
+ Args:
45
+ simulation_results (str): Location of the simulation results file.
46
+ treatment_key (str): Location of the file with the treatment information.
47
+ holding (int, optional): The number of the holding to be optimized. Defaults to 1.
48
+ comparing (bool, optional): Determines if solutions are to be compared to those from the rahti app.
49
+ Defaults to False.
50
+
51
+ Returns:
52
+ Problem: An instance of the test forest problem.
53
+ """
54
+ df = pl.read_csv(simulation_results, schema_overrides={"unit": pl.Float64})
55
+ df_key = pl.read_csv(treatment_key, schema_overrides={"unit": pl.Float64})
56
+
57
+ selected_df_v = df.filter(pl.col("holding") == holding).select(["unit", "schedule", "npv_5_percent"])
58
+ unique_units = selected_df_v.unique(["unit"], maintain_order=True).get_column("unit")
59
+ selected_df_v.group_by(["unit", "schedule"])
60
+ rows_by_key = selected_df_v.rows_by_key(key=["unit", "schedule"])
61
+ v_array = np.zeros((selected_df_v["unit"].n_unique(), selected_df_v["schedule"].n_unique()))
62
+ for i in range(np.shape(v_array)[0]):
63
+ for j in range(np.shape(v_array)[1]):
64
+ if (unique_units[i], j) in rows_by_key:
65
+ v_array[i][j] = rows_by_key[(unique_units[i], j)][0]
66
+
67
+ # determine whether the results are to be compared to those from the rahti app (for testing purposes)
68
+ # if compared, the stock values are calculated by substacting the value after 2025 period from
69
+ # the value after the 2035 period (in other words, last value - first value)
70
+ if comparing:
71
+ selected_df_w = df.filter(pl.col("holding") == holding).select(["unit", "schedule", "stock_2025", "stock_2035"])
72
+ selected_df_w.group_by(["unit", "schedule"])
73
+ rows_by_key = selected_df_w.rows_by_key(key=["unit", "schedule"])
74
+ selected_df_key_w = df_key.select(["unit", "schedule", "treatment"])
75
+ selected_df_key_w.group_by(["unit", "schedule"])
76
+ rows_by_key_df_key = selected_df_key_w.rows_by_key(key=["unit", "schedule"])
77
+ w_array = np.zeros((selected_df_w["unit"].n_unique(), selected_df_w["schedule"].n_unique()))
78
+ for i in range(np.shape(w_array)[0]):
79
+ for j in range(np.shape(w_array)[1]):
80
+ if len(rows_by_key_df_key[(unique_units[i], j)]) == 0:
81
+ continue
82
+ if (unique_units[i], j) in rows_by_key:
83
+ w_array[i][j] = rows_by_key[(unique_units[i], j)][0][1] - rows_by_key[(unique_units[i], j)][0][0]
84
+ else:
85
+ selected_df_w = df.filter(pl.col("holding") == holding).select(["unit", "schedule", "stock_2035"])
86
+ selected_df_w.group_by(["unit", "schedule"])
87
+ rows_by_key = selected_df_w.rows_by_key(key=["unit", "schedule"])
88
+ selected_df_key_w = df_key.select(["unit", "schedule", "treatment"])
89
+ selected_df_key_w.group_by(["unit", "schedule"])
90
+ rows_by_key_df_key = selected_df_key_w.rows_by_key(key=["unit", "schedule"])
91
+ w_array = np.zeros((selected_df_w["unit"].n_unique(), selected_df_w["schedule"].n_unique()))
92
+ for i in range(np.shape(w_array)[0]):
93
+ for j in range(np.shape(w_array)[1]):
94
+ if len(rows_by_key_df_key[(unique_units[i], j)]) == 0:
95
+ continue
96
+ if (unique_units[i], j) in rows_by_key:
97
+ w_array[i][j] = rows_by_key[(unique_units[i], j)][0][0]
98
+
99
+ selected_df_p = df.filter(pl.col("holding") == holding).select(
100
+ ["unit", "schedule", "harvest_value_period_2025", "harvest_value_period_2030", "harvest_value_period_2035"]
101
+ )
102
+ selected_df_p.group_by(["unit", "schedule"])
103
+ rows_by_key = selected_df_p.rows_by_key(key=["unit", "schedule"])
104
+ p_array = np.zeros((selected_df_p["unit"].n_unique(), selected_df_p["schedule"].n_unique()))
105
+ for i in range(np.shape(p_array)[0]):
106
+ for j in range(np.shape(p_array)[1]):
107
+ if (unique_units[i], j) in rows_by_key:
108
+ p_array[i][j] = sum(rows_by_key[(unique_units[i], j)][0])
109
+
110
+ constants = []
111
+ variables = []
112
+ constraints = []
113
+ f_1_func = []
114
+ f_2_func = []
115
+ f_3_func = []
116
+ # define the constants V, W and P, decision variable X, constraints, and objective function expressions in one loop
117
+ for i in range(np.shape(v_array)[0]):
118
+ # Constants V, W and P
119
+ v = TensorConstant(
120
+ name=f"V_{i+1}",
121
+ symbol=f"V_{i+1}",
122
+ shape=[np.shape(v_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
123
+ values=v_array[i].tolist(),
124
+ )
125
+ constants.append(v)
126
+ w = TensorConstant(
127
+ name=f"W_{i+1}",
128
+ symbol=f"W_{i+1}",
129
+ shape=[np.shape(w_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
130
+ values=w_array[i].tolist(),
131
+ )
132
+ constants.append(w)
133
+ p = TensorConstant(
134
+ name=f"P_{i+1}",
135
+ symbol=f"P_{i+1}",
136
+ shape=[np.shape(p_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
137
+ values=p_array[i].tolist(),
138
+ )
139
+
140
+ # Decision variable X
141
+ constants.append(p)
142
+ x = TensorVariable(
143
+ name=f"X_{i+1}",
144
+ symbol=f"X_{i+1}",
145
+ variable_type=VariableTypeEnum.binary,
146
+ shape=[np.shape(v_array)[1]], # NOTE: vectors have to be of form [2] instead of [2,1] or [1,2]
147
+ lowerbounds=np.shape(v_array)[1] * [0],
148
+ upperbounds=np.shape(v_array)[1] * [1],
149
+ initial_values=np.shape(v_array)[1] * [0],
150
+ )
151
+ variables.append(x)
152
+
153
+ # Constraints
154
+ con = Constraint(
155
+ name=f"x_con_{i+1}",
156
+ symbol=f"x_con_{i+1}",
157
+ cons_type=ConstraintTypeEnum.EQ,
158
+ func=f"Sum(X_{i+1}) - 1",
159
+ is_linear=True,
160
+ is_convex=False, # not checked
161
+ is_twice_differentiable=True,
162
+ )
163
+ constraints.append(con)
164
+
165
+ # Objective function expressions
166
+ exprs = f"V_{i+1}@X_{i+1}"
167
+ f_1_func.append(exprs)
168
+
169
+ exprs = f"W_{i+1}@X_{i+1}"
170
+ f_2_func.append(exprs)
171
+
172
+ exprs = f"P_{i+1}@X_{i+1}"
173
+ f_3_func.append(exprs)
174
+
175
+ # form the objective function sums
176
+ f_1_func = " + ".join(f_1_func)
177
+ f_2_func = " + ".join(f_2_func)
178
+ f_3_func = " + ".join(f_3_func)
179
+
180
+ f_1 = Objective(
181
+ name="Net present value",
182
+ symbol="f_1",
183
+ func=f_1_func,
184
+ maximize=True,
185
+ objective_type=ObjectiveTypeEnum.analytical,
186
+ is_linear=True,
187
+ is_convex=False, # not checked
188
+ is_twice_differentiable=True,
189
+ )
190
+
191
+ f_2 = Objective(
192
+ name="Wood stock volume",
193
+ symbol="f_2",
194
+ func=f_2_func,
195
+ maximize=True,
196
+ objective_type=ObjectiveTypeEnum.analytical,
197
+ is_linear=True,
198
+ is_convex=False, # not checked
199
+ is_twice_differentiable=True,
200
+ )
201
+
202
+ f_3 = Objective(
203
+ name="Harvest value",
204
+ symbol="f_3",
205
+ func=f_3_func,
206
+ maximize=True,
207
+ objective_type=ObjectiveTypeEnum.analytical,
208
+ is_linear=True,
209
+ is_convex=False, # not checked
210
+ is_twice_differentiable=True,
211
+ )
212
+
213
+ return Problem(
214
+ name="Forest problem",
215
+ description="A test forest problem.",
216
+ constants=constants,
217
+ variables=variables,
218
+ objectives=[f_1, f_2, f_3],
219
+ constraints=constraints,
220
+ )
221
+
222
+
223
+ def forest_problem_discrete() -> Problem:
224
+ """Implements the forest problem using Pareto front representation.
225
+
226
+ Returns:
227
+ Problem: A problem instance representing the forest problem.
228
+ """
229
+ filename = "datasets/forest_holding_4.csv"
230
+
231
+ path = Path(__file__).parent.parent.parent.parent / filename
232
+
233
+ obj_names = ["stock", "harvest_value", "npv"]
234
+
235
+ var_name = "index"
236
+
237
+ data = pl.read_csv(
238
+ path, has_header=True, columns=["stock", "harvest_value", "npv"], separator=";", decimal_comma=True
239
+ )
240
+
241
+ variables = [
242
+ Variable(
243
+ name=var_name,
244
+ symbol=var_name,
245
+ variable_type=VariableTypeEnum.integer,
246
+ lowerbound=0,
247
+ upperbound=len(data) - 1,
248
+ initial_value=0,
249
+ )
250
+ ]
251
+
252
+ objectives = [
253
+ Objective(
254
+ name=obj_name,
255
+ symbol=obj_name,
256
+ objective_type=ObjectiveTypeEnum.data_based,
257
+ ideal=data[obj_name].max(),
258
+ nadir=data[obj_name].min(),
259
+ maximize=True,
260
+ )
261
+ for obj_name in obj_names
262
+ ]
263
+
264
+ discrete_def = DiscreteRepresentation(
265
+ variable_values={"index": list(range(len(data)))},
266
+ objective_values=data[[obj.symbol for obj in objectives]].to_dict(),
267
+ )
268
+
269
+ return Problem(
270
+ name="Finnish Forest Problem (Discrete)",
271
+ description="Defines a forest problem with three objectives: stock, harvest value, and net present value.",
272
+ variables=variables,
273
+ objectives=objectives,
274
+ discrete_representation=discrete_def,
275
+ )
@@ -0,0 +1,163 @@
1
+ from desdeo.problem.schema import (
2
+ Constant,
3
+ Constraint,
4
+ ConstraintTypeEnum,
5
+ Objective,
6
+ ObjectiveTypeEnum,
7
+ Problem,
8
+ TensorConstant,
9
+ TensorVariable,
10
+ Variable,
11
+ VariableTypeEnum,
12
+ )
13
+
14
+ def simple_knapsack() -> Problem:
15
+ r"""Defines a simple multiobjective knapsack problem.
16
+
17
+ Given a set of 4 items, each with a weight and three values corresponding to
18
+ different objectives, the problem is defined as follows:
19
+
20
+ - Item 1: weight = 2, values = (5, 10, 15)
21
+ - Item 2: weight = 3, values = (4, 7, 9)
22
+ - Item 3: weight = 1, values = (3, 5, 8)
23
+ - Item 4: weight = 4, values = (2, 3, 5)
24
+
25
+ The problem is then to maximize the following functions:
26
+
27
+ \begin{align*}
28
+ f_1(x) &= 5x_1 + 4x_2 + 3x_3 + 2x_4 \\
29
+ f_2(x) &= 10x_1 + 7x_2 + 5x_3 + 3x_4 \\
30
+ f_3(x) &= 15x_1 + 9x_2 + 8x_3 + 5x_4 \\
31
+ \text{s.t.}\quad & 2x_1 + 3x_2 + 1x_3 + 4x_4 \leq 7 \\
32
+ & x_i \in \{0,1\} \quad \text{for} \quad i = 1, 2, 3, 4,
33
+ \end{align*}
34
+
35
+ where the inequality constraint is a weight constraint. The problem is a binary variable problem.
36
+
37
+ Returns:
38
+ Problem: the simple knapsack problem.
39
+ """
40
+ variables = [
41
+ Variable(
42
+ name=f"x_{i}",
43
+ symbol=f"x_{i}",
44
+ variable_type=VariableTypeEnum.binary,
45
+ lowerbound=0,
46
+ upperbound=1,
47
+ initial_value=0,
48
+ )
49
+ for i in range(1, 5)
50
+ ]
51
+
52
+ exprs = {
53
+ "f1": "5*x_1 + 4*x_2 + 3*x_3 + 2*x_4",
54
+ "f2": "10*x_1 + 7*x_2 + 5*x_3 + 3*x_4",
55
+ "f3": "15*x_1 + 9*x_2 + 8*x_3 + 5*x_4",
56
+ }
57
+
58
+ ideals = {"f1": 15, "f2": 25, "f3": 37}
59
+
60
+ objectives = [
61
+ Objective(
62
+ name=f"f_{i}",
63
+ symbol=f"f_{i}",
64
+ func=exprs[f"f{i}"],
65
+ maximize=True,
66
+ ideal=ideals[f"f{i}"],
67
+ nadir=0,
68
+ objective_type=ObjectiveTypeEnum.analytical,
69
+ is_linear=True,
70
+ is_convex=True,
71
+ is_twice_differentiable=True,
72
+ )
73
+ for i in range(1, 4)
74
+ ]
75
+
76
+ constraints = [
77
+ Constraint(
78
+ name="Weight constraint",
79
+ symbol="g_w",
80
+ cons_type=ConstraintTypeEnum.LTE,
81
+ func="2*x_1 + 3*x_2 + 1*x_3 + 4*x_4 - 7",
82
+ is_linear=True,
83
+ is_convex=True,
84
+ is_twice_differentiable=True,
85
+ )
86
+ ]
87
+
88
+ return Problem(
89
+ name="Simple knapsack",
90
+ description="A simple knapsack problem with three objectives to be maximized.",
91
+ variables=variables,
92
+ objectives=objectives,
93
+ constraints=constraints,
94
+ )
95
+
96
+
97
+ def simple_knapsack_vectors():
98
+ """Define a simple variant of the knapsack problem that utilizes vectors (TensorVariable and TensorConstant)."""
99
+ n_items = 4
100
+ weight_values = [2, 3, 4, 5]
101
+ profit_values = [3, 5, 6, 8]
102
+ efficiency_values = [4, 2, 7, 3]
103
+
104
+ max_weight = Constant(name="Maximum weights", symbol="w_max", value=5)
105
+
106
+ weights = TensorConstant(name="Weights of the items", symbol="W", shape=[len(weight_values)], values=weight_values)
107
+ profits = TensorConstant(name="Profits", symbol="P", shape=[len(profit_values)], values=profit_values)
108
+ efficiencies = TensorConstant(
109
+ name="Efficiencies", symbol="E", shape=[len(efficiency_values)], values=efficiency_values
110
+ )
111
+
112
+ choices = TensorVariable(
113
+ name="Chosen items",
114
+ symbol="X",
115
+ shape=[n_items],
116
+ variable_type="binary",
117
+ lowerbounds=n_items * [0],
118
+ upperbounds=n_items * [1],
119
+ initial_values=n_items * [1],
120
+ )
121
+
122
+ profit_objective = Objective(
123
+ name="max profit",
124
+ symbol="f_1",
125
+ func="P@X",
126
+ maximize=True,
127
+ ideal=8,
128
+ nadir=0,
129
+ is_linear=True,
130
+ is_convex=False,
131
+ is_twice_differentiable=False,
132
+ )
133
+
134
+ efficiency_objective = Objective(
135
+ name="max efficiency",
136
+ symbol="f_2",
137
+ func="E@X",
138
+ maximize=True,
139
+ ideal=7,
140
+ nadir=0,
141
+ is_linear=True,
142
+ is_convex=False,
143
+ is_twice_differentiable=False,
144
+ )
145
+
146
+ weight_constraint = Constraint(
147
+ name="Weight constraint",
148
+ symbol="g_1",
149
+ cons_type="<=",
150
+ func="W@X - w_max",
151
+ is_linear=True,
152
+ is_convex=False,
153
+ is_twice_differentiable=False,
154
+ )
155
+
156
+ return Problem(
157
+ name="Simple two-objective Knapsack problem",
158
+ description="A simple variant of the classic combinatorial problem.",
159
+ constants=[max_weight, weights, profits, efficiencies],
160
+ variables=[choices],
161
+ objectives=[profit_objective, efficiency_objective],
162
+ constraints=[weight_constraint],
163
+ )