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.
Files changed (182) hide show
  1. desdeo/__init__.py +8 -8
  2. desdeo/adm/ADMAfsar.py +551 -0
  3. desdeo/adm/ADMChen.py +414 -0
  4. desdeo/adm/BaseADM.py +119 -0
  5. desdeo/adm/__init__.py +11 -0
  6. desdeo/api/README.md +73 -0
  7. desdeo/api/__init__.py +15 -0
  8. desdeo/api/app.py +50 -0
  9. desdeo/api/config.py +90 -0
  10. desdeo/api/config.toml +64 -0
  11. desdeo/api/db.py +27 -0
  12. desdeo/api/db_init.py +85 -0
  13. desdeo/api/db_models.py +164 -0
  14. desdeo/api/malaga_db_init.py +27 -0
  15. desdeo/api/models/__init__.py +266 -0
  16. desdeo/api/models/archive.py +23 -0
  17. desdeo/api/models/emo.py +128 -0
  18. desdeo/api/models/enautilus.py +69 -0
  19. desdeo/api/models/gdm/gdm_aggregate.py +139 -0
  20. desdeo/api/models/gdm/gdm_base.py +69 -0
  21. desdeo/api/models/gdm/gdm_score_bands.py +114 -0
  22. desdeo/api/models/gdm/gnimbus.py +138 -0
  23. desdeo/api/models/generic.py +104 -0
  24. desdeo/api/models/generic_states.py +401 -0
  25. desdeo/api/models/nimbus.py +158 -0
  26. desdeo/api/models/preference.py +128 -0
  27. desdeo/api/models/problem.py +717 -0
  28. desdeo/api/models/reference_point_method.py +18 -0
  29. desdeo/api/models/session.py +49 -0
  30. desdeo/api/models/state.py +463 -0
  31. desdeo/api/models/user.py +52 -0
  32. desdeo/api/models/utopia.py +25 -0
  33. desdeo/api/routers/_EMO.backup +309 -0
  34. desdeo/api/routers/_NAUTILUS.py +245 -0
  35. desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
  36. desdeo/api/routers/_NIMBUS.py +765 -0
  37. desdeo/api/routers/__init__.py +5 -0
  38. desdeo/api/routers/emo.py +497 -0
  39. desdeo/api/routers/enautilus.py +237 -0
  40. desdeo/api/routers/gdm/gdm_aggregate.py +234 -0
  41. desdeo/api/routers/gdm/gdm_base.py +420 -0
  42. desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py +398 -0
  43. desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py +377 -0
  44. desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py +698 -0
  45. desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py +591 -0
  46. desdeo/api/routers/generic.py +233 -0
  47. desdeo/api/routers/nimbus.py +705 -0
  48. desdeo/api/routers/problem.py +307 -0
  49. desdeo/api/routers/reference_point_method.py +93 -0
  50. desdeo/api/routers/session.py +100 -0
  51. desdeo/api/routers/test.py +16 -0
  52. desdeo/api/routers/user_authentication.py +520 -0
  53. desdeo/api/routers/utils.py +187 -0
  54. desdeo/api/routers/utopia.py +230 -0
  55. desdeo/api/schema.py +100 -0
  56. desdeo/api/tests/__init__.py +0 -0
  57. desdeo/api/tests/conftest.py +151 -0
  58. desdeo/api/tests/test_enautilus.py +330 -0
  59. desdeo/api/tests/test_models.py +1179 -0
  60. desdeo/api/tests/test_routes.py +1075 -0
  61. desdeo/api/utils/_database.py +263 -0
  62. desdeo/api/utils/_logger.py +29 -0
  63. desdeo/api/utils/database.py +36 -0
  64. desdeo/api/utils/emo_database.py +40 -0
  65. desdeo/core.py +34 -0
  66. desdeo/emo/__init__.py +159 -0
  67. desdeo/emo/hooks/archivers.py +188 -0
  68. desdeo/emo/methods/EAs.py +541 -0
  69. desdeo/emo/methods/__init__.py +0 -0
  70. desdeo/emo/methods/bases.py +12 -0
  71. desdeo/emo/methods/templates.py +111 -0
  72. desdeo/emo/operators/__init__.py +1 -0
  73. desdeo/emo/operators/crossover.py +1282 -0
  74. desdeo/emo/operators/evaluator.py +114 -0
  75. desdeo/emo/operators/generator.py +459 -0
  76. desdeo/emo/operators/mutation.py +1224 -0
  77. desdeo/emo/operators/scalar_selection.py +202 -0
  78. desdeo/emo/operators/selection.py +1778 -0
  79. desdeo/emo/operators/termination.py +286 -0
  80. desdeo/emo/options/__init__.py +108 -0
  81. desdeo/emo/options/algorithms.py +435 -0
  82. desdeo/emo/options/crossover.py +164 -0
  83. desdeo/emo/options/generator.py +131 -0
  84. desdeo/emo/options/mutation.py +260 -0
  85. desdeo/emo/options/repair.py +61 -0
  86. desdeo/emo/options/scalar_selection.py +66 -0
  87. desdeo/emo/options/selection.py +127 -0
  88. desdeo/emo/options/templates.py +383 -0
  89. desdeo/emo/options/termination.py +143 -0
  90. desdeo/explanations/__init__.py +6 -0
  91. desdeo/explanations/explainer.py +100 -0
  92. desdeo/explanations/utils.py +90 -0
  93. desdeo/gdm/__init__.py +22 -0
  94. desdeo/gdm/gdmtools.py +45 -0
  95. desdeo/gdm/score_bands.py +114 -0
  96. desdeo/gdm/voting_rules.py +50 -0
  97. desdeo/mcdm/__init__.py +41 -0
  98. desdeo/mcdm/enautilus.py +338 -0
  99. desdeo/mcdm/gnimbus.py +484 -0
  100. desdeo/mcdm/nautili.py +345 -0
  101. desdeo/mcdm/nautilus.py +477 -0
  102. desdeo/mcdm/nautilus_navigator.py +656 -0
  103. desdeo/mcdm/nimbus.py +417 -0
  104. desdeo/mcdm/pareto_navigator.py +269 -0
  105. desdeo/mcdm/reference_point_method.py +186 -0
  106. desdeo/problem/__init__.py +83 -0
  107. desdeo/problem/evaluator.py +561 -0
  108. desdeo/problem/external/__init__.py +18 -0
  109. desdeo/problem/external/core.py +356 -0
  110. desdeo/problem/external/pymoo_provider.py +266 -0
  111. desdeo/problem/external/runtime.py +44 -0
  112. desdeo/problem/gurobipy_evaluator.py +562 -0
  113. desdeo/problem/infix_parser.py +341 -0
  114. desdeo/problem/json_parser.py +944 -0
  115. desdeo/problem/pyomo_evaluator.py +487 -0
  116. desdeo/problem/schema.py +1829 -0
  117. desdeo/problem/simulator_evaluator.py +348 -0
  118. desdeo/problem/sympy_evaluator.py +244 -0
  119. desdeo/problem/testproblems/__init__.py +88 -0
  120. desdeo/problem/testproblems/benchmarks_server.py +120 -0
  121. desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
  122. desdeo/problem/testproblems/cake_problem.py +185 -0
  123. desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
  124. desdeo/problem/testproblems/dtlz2_problem.py +102 -0
  125. desdeo/problem/testproblems/forest_problem.py +283 -0
  126. desdeo/problem/testproblems/knapsack_problem.py +163 -0
  127. desdeo/problem/testproblems/mcwb_problem.py +831 -0
  128. desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +83 -0
  129. desdeo/problem/testproblems/momip_problem.py +172 -0
  130. desdeo/problem/testproblems/multi_valued_constraints.py +119 -0
  131. desdeo/problem/testproblems/nimbus_problem.py +143 -0
  132. desdeo/problem/testproblems/pareto_navigator_problem.py +89 -0
  133. desdeo/problem/testproblems/re_problem.py +492 -0
  134. desdeo/problem/testproblems/river_pollution_problems.py +440 -0
  135. desdeo/problem/testproblems/rocket_injector_design_problem.py +140 -0
  136. desdeo/problem/testproblems/simple_problem.py +351 -0
  137. desdeo/problem/testproblems/simulator_problem.py +92 -0
  138. desdeo/problem/testproblems/single_objective.py +289 -0
  139. desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
  140. desdeo/problem/testproblems/zdt_problem.py +274 -0
  141. desdeo/problem/utils.py +245 -0
  142. desdeo/tools/GenerateReferencePoints.py +181 -0
  143. desdeo/tools/__init__.py +120 -0
  144. desdeo/tools/desc_gen.py +22 -0
  145. desdeo/tools/generics.py +165 -0
  146. desdeo/tools/group_scalarization.py +3090 -0
  147. desdeo/tools/gurobipy_solver_interfaces.py +258 -0
  148. desdeo/tools/indicators_binary.py +117 -0
  149. desdeo/tools/indicators_unary.py +362 -0
  150. desdeo/tools/interaction_schema.py +38 -0
  151. desdeo/tools/intersection.py +54 -0
  152. desdeo/tools/iterative_pareto_representer.py +99 -0
  153. desdeo/tools/message.py +265 -0
  154. desdeo/tools/ng_solver_interfaces.py +199 -0
  155. desdeo/tools/non_dominated_sorting.py +134 -0
  156. desdeo/tools/patterns.py +283 -0
  157. desdeo/tools/proximal_solver.py +99 -0
  158. desdeo/tools/pyomo_solver_interfaces.py +477 -0
  159. desdeo/tools/reference_vectors.py +229 -0
  160. desdeo/tools/scalarization.py +2065 -0
  161. desdeo/tools/scipy_solver_interfaces.py +454 -0
  162. desdeo/tools/score_bands.py +627 -0
  163. desdeo/tools/utils.py +388 -0
  164. desdeo/tools/visualizations.py +67 -0
  165. desdeo/utopia_stuff/__init__.py +0 -0
  166. desdeo/utopia_stuff/data/1.json +15 -0
  167. desdeo/utopia_stuff/data/2.json +13 -0
  168. desdeo/utopia_stuff/data/3.json +15 -0
  169. desdeo/utopia_stuff/data/4.json +17 -0
  170. desdeo/utopia_stuff/data/5.json +15 -0
  171. desdeo/utopia_stuff/from_json.py +40 -0
  172. desdeo/utopia_stuff/reinit_user.py +38 -0
  173. desdeo/utopia_stuff/utopia_db_init.py +212 -0
  174. desdeo/utopia_stuff/utopia_problem.py +403 -0
  175. desdeo/utopia_stuff/utopia_problem_old.py +415 -0
  176. desdeo/utopia_stuff/utopia_reference_solutions.py +79 -0
  177. desdeo-2.1.0.dist-info/METADATA +186 -0
  178. desdeo-2.1.0.dist-info/RECORD +180 -0
  179. {desdeo-1.2.dist-info → desdeo-2.1.0.dist-info}/WHEEL +1 -1
  180. desdeo-2.1.0.dist-info/licenses/LICENSE +21 -0
  181. desdeo-1.2.dist-info/METADATA +0 -16
  182. desdeo-1.2.dist-info/RECORD +0 -4
@@ -0,0 +1,202 @@
1
+ """Classs for scalar selection operators."""
2
+
3
+ from abc import abstractmethod
4
+ from collections.abc import Sequence
5
+
6
+ import numpy as np
7
+ import polars as pl
8
+
9
+ from desdeo.emo.operators.selection import SolutionType
10
+ from desdeo.tools.message import Message, SelectorMessageTopics
11
+ from desdeo.tools.patterns import Publisher, Subscriber
12
+
13
+
14
+ class BaseScalarSelector(Subscriber):
15
+ """A base class for selection operators."""
16
+
17
+ @property
18
+ def provided_topics(self):
19
+ return {
20
+ 0: [],
21
+ 1: [],
22
+ 2: [],
23
+ }
24
+
25
+ @property
26
+ def interested_topics(self):
27
+ return [
28
+ SelectorMessageTopics.SELECTED_FITNESS,
29
+ ]
30
+
31
+ def __init__(self, verbosity: int, publisher: Publisher):
32
+ """Initialize a selection operator."""
33
+ super().__init__(verbosity=verbosity, publisher=publisher)
34
+ self.fitness: np.ndarray | None = None
35
+
36
+ @abstractmethod
37
+ def _do(
38
+ self,
39
+ solutions: tuple[SolutionType, pl.DataFrame],
40
+ fitness: np.ndarray | None = None,
41
+ ) -> tuple[SolutionType, pl.DataFrame]:
42
+ """Perform the selection operation.
43
+
44
+ Args:
45
+ solutions (tuple[SolutionType, pl.DataFrame]): the decision variables as the first element.
46
+ The second element is the objective values, targets, and constraint violations.
47
+ fitness (np.ndarray | None, optional): The fitness values of the solutions. If None, the fitness is
48
+ calculated from the messages sent by the publisher.
49
+
50
+ Returns:
51
+ SolutionType: The selected decision variables.
52
+ """
53
+
54
+ def do(
55
+ self,
56
+ solutions: tuple[SolutionType, pl.DataFrame],
57
+ fitness: np.ndarray | None = None,
58
+ ) -> tuple[SolutionType, pl.DataFrame]:
59
+ """Perform the selection operation.
60
+
61
+ Args:
62
+ solutions (tuple[SolutionType, pl.DataFrame]): the decision variables as the first element.
63
+ The second element is the objective values, targets, and constraint violations.
64
+ fitness (np.ndarray | None, optional): The fitness values of the solutions. If None, the fitness is
65
+ calculated from the messages sent by the publisher.
66
+
67
+ Returns:
68
+ SolutionType: The selected decision variables.
69
+ """
70
+ if fitness is not None and self.fitness is not None:
71
+ raise RuntimeError("The fitness is being set twice.")
72
+ if fitness is None and self.fitness is None:
73
+ raise RuntimeError(
74
+ "The fitness is not set. Either pass it as an argument or make sure the publisher sends it."
75
+ )
76
+ if fitness is None:
77
+ fitness = self.fitness
78
+ if len(fitness) != len(solutions[0]):
79
+ raise ValueError(
80
+ f"The length of the fitness array ({len(fitness)}) does not match"
81
+ f" the number of solutions ({len(solutions[0])})."
82
+ )
83
+ returnval = self._do(solutions, fitness)
84
+ self.fitness = None # Reset fitness after selection
85
+ return returnval
86
+
87
+ def update(self, message: Message) -> None:
88
+ """Update the operator with a message.
89
+
90
+ Args:
91
+ message (Message): The message to update the operator with.
92
+ """
93
+ if message.topic == SelectorMessageTopics.SELECTED_FITNESS and isinstance(message.value, np.ndarray):
94
+ self.fitness = message.value
95
+ else:
96
+ raise ValueError(f"Unknown message topic: {message.topic}")
97
+
98
+ def state(self) -> Sequence[Message]:
99
+ return []
100
+
101
+
102
+ class TournamentSelection(BaseScalarSelector):
103
+ """A tournament selection operator."""
104
+
105
+ def __init__(
106
+ self,
107
+ *,
108
+ winner_size: int,
109
+ verbosity: int,
110
+ publisher: Publisher,
111
+ tournament_size: int = 2,
112
+ seed: int | None = None,
113
+ selection_probability: float | None = None,
114
+ ) -> None:
115
+ """Initialize the tournament selection operator.
116
+
117
+ Args:
118
+ winner_size (int): The number of winners to select.
119
+ verbosity (int): The verbosity level of the operator.
120
+ publisher (Publisher): The publisher to send messages to.
121
+ tournament_size (int, optional): The size of the tournament. Defaults to 2, which corresponds to binary
122
+ tournament.
123
+ seed (int | None, optional): The seed for the random number generator. If None, the deterministic tournament
124
+ selection is used, i.e., the solution with the highest fitness in the tournament is always selected.
125
+ Otherwise the selection is stochastic, and the solution is selected with a probability proportional to
126
+ its fitness. Defaults to None.
127
+ selection_probability (float | None, optional): The probability of selecting a solution in the tournament.
128
+ If None, but a seed is provided, then the probabilities are proportional to the fitness values of the
129
+ solutions in the tournament. If None and no seed is provided, then the selection is deterministic.
130
+ If a value is provided, and the seed is not None, then the selection is stochastic, and the
131
+ probabilities of choosing the k-best solution in the tournament is given by p * (1 - p) ** (k - 1),
132
+ where p is the selection probability. Note that doing selection with a probability proportional to
133
+ fitness is equivalent to roulette wheel selection.
134
+ """
135
+ super().__init__(verbosity=verbosity, publisher=publisher)
136
+ self.winner_size = winner_size
137
+ self.tournament_size = tournament_size
138
+ self.seed = seed
139
+ self.rng = np.random.default_rng(seed)
140
+ self.selection_probability = selection_probability
141
+ if self.seed is None and self.selection_probability is not None:
142
+ raise ValueError(
143
+ "If selection_probability is provided, seed must also be provided to ensure stochastic selection."
144
+ )
145
+
146
+ @staticmethod
147
+ def deterministic_select(indices: np.ndarray, fitness: np.ndarray) -> int:
148
+ """Select the index of the solution with the highest fitness from the given indices.
149
+
150
+ Args:
151
+ indices (np.ndarray): The indices of the solutions to select from.
152
+ fitness (np.ndarray): The fitness values of the solutions.
153
+
154
+ Returns:
155
+ int: The index of the solution with the highest fitness.
156
+ """
157
+ return indices[np.argmax(fitness)]
158
+
159
+ def stochastic_select(self, indices: np.ndarray, fitness: np.ndarray) -> int:
160
+ """Select the index of the solution with a probability proportional to its fitness from the given indices.
161
+
162
+ Args:
163
+ indices (np.ndarray): The indices of the solutions to select from.
164
+ fitness (np.ndarray): The fitness values of the solutions.
165
+
166
+ Returns:
167
+ int: The index of the selected solution.
168
+ """
169
+ if self.selection_probability is None:
170
+ probabilities = fitness / np.sum(fitness)
171
+ probabilities = np.cumsum(probabilities)
172
+ else:
173
+ indices = indices[np.argsort(fitness)[::-1]] # Sort indices by fitness in descending order
174
+ probabilities = np.array(
175
+ [self.selection_probability * (1 - self.selection_probability) ** i for i in range(len(indices))]
176
+ )
177
+ random_value = self.rng.random()
178
+ selected_index = np.searchsorted(probabilities, random_value)
179
+ return indices[selected_index]
180
+
181
+ def _do(
182
+ self, solutions: tuple[SolutionType, pl.DataFrame], fitness: np.ndarray | None = None
183
+ ) -> tuple[SolutionType, pl.DataFrame]:
184
+ """Perform the tournament selection operation.
185
+
186
+ Args:
187
+ solutions (tuple[SolutionType, pl.DataFrame]): The decision variables and their outputs.
188
+ fitness (np.ndarray | None, optional): The fitness values of the solutions.
189
+
190
+ Returns:
191
+ tuple[SolutionType, pl.DataFrame]: The selected decision variables and their outputs.
192
+ """
193
+ selected_indices = np.zeros(self.winner_size, dtype=int)
194
+ for i in range(self.winner_size):
195
+ tournament_indices = self.rng.choice(range(len(solutions[0])), size=self.tournament_size, replace=True)
196
+ if self.seed is None:
197
+ selected_indices[i] = self.deterministic_select(tournament_indices, fitness[tournament_indices])
198
+ else:
199
+ selected_indices[i] = self.stochastic_select(tournament_indices, fitness[tournament_indices])
200
+ selected_solutions = solutions[0][selected_indices]
201
+ selected_outputs = solutions[1][selected_indices]
202
+ return selected_solutions, selected_outputs