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
@@ -11,6 +11,8 @@ Warning:
11
11
  super().do method to increment the generation counter _before_ conducting the termination check.
12
12
  """
13
13
 
14
+ import time
15
+ from abc import abstractmethod
14
16
  from collections.abc import Sequence
15
17
 
16
18
  from desdeo.tools.message import (
@@ -20,7 +22,7 @@ from desdeo.tools.message import (
20
22
  Message,
21
23
  TerminatorMessageTopics,
22
24
  )
23
- from desdeo.tools.patterns import Subscriber
25
+ from desdeo.tools.patterns import Publisher, Subscriber
24
26
 
25
27
 
26
28
  class BaseTerminator(Subscriber):
@@ -52,21 +54,22 @@ class BaseTerminator(Subscriber):
52
54
  """Return the message topics that the terminator is interested in."""
53
55
  return [EvaluatorMessageTopics.NEW_EVALUATIONS, GeneratorMessageTopics.NEW_EVALUATIONS]
54
56
 
55
- def __init__(self, **kwargs):
57
+ def __init__(self, publisher: Publisher):
56
58
  """Initialize a termination criterion."""
57
- super().__init__(**kwargs)
59
+ super().__init__(publisher=publisher, verbosity=1)
58
60
  self.current_generation: int = 1
59
61
  self.current_evaluations: int = 0
60
62
  self.max_generations: int = 0
61
63
  self.max_evaluations: int = 0
62
64
 
63
- def check(self) -> bool | None:
65
+ def check(self) -> bool:
64
66
  """Check if the termination criterion is reached.
65
67
 
66
68
  Returns:
67
69
  bool: True if the termination criterion is reached, False otherwise.
68
70
  """
69
71
  self.current_generation += 1
72
+ self.notify()
70
73
 
71
74
  def state(self) -> Sequence[Message]:
72
75
  """Return the state of the termination criterion."""
@@ -109,7 +112,7 @@ class BaseTerminator(Subscriber):
109
112
  """
110
113
  if not isinstance(message, IntMessage):
111
114
  return
112
- if not isinstance(message.topic, EvaluatorMessageTopics) or isinstance(message.topic, GeneratorMessageTopics):
115
+ if not (isinstance(message.topic, EvaluatorMessageTopics) or isinstance(message.topic, GeneratorMessageTopics)):
113
116
  return
114
117
  if (
115
118
  message.topic == EvaluatorMessageTopics.NEW_EVALUATIONS # NOQA: PLR1714
@@ -121,23 +124,19 @@ class BaseTerminator(Subscriber):
121
124
  class MaxGenerationsTerminator(BaseTerminator):
122
125
  """A class for a termination criterion based on the number of generations."""
123
126
 
124
- def __init__(self, max_generations: int, **kwargs):
127
+ def __init__(self, max_generations: int, publisher: Publisher):
125
128
  """Initialize a termination criterion based on the number of generations.
126
129
 
127
130
  Args:
128
131
  max_generations (int): the maximum number of generations.
129
- kwargs: Additional keyword arguments. These are passed to the Subscriber class. At the very least, the
130
- publisher must be passed. See the Subscriber class for more information.
132
+ publisher (Publisher): The publisher to which the terminator will publish its state.
131
133
  """
132
- super().__init__(**kwargs)
134
+ super().__init__(publisher=publisher)
133
135
  self.max_generations = max_generations
134
136
 
135
137
  def check(self) -> bool:
136
138
  """Check if the termination criterion based on the number of generations is reached.
137
139
 
138
- Args:
139
- new_generation (bool, optional): Increment the generation counter. Defaults to True.
140
-
141
140
  Returns:
142
141
  bool: True if the termination criterion is reached, False otherwise.
143
142
  """
@@ -151,24 +150,24 @@ class MaxGenerationsTerminator(BaseTerminator):
151
150
  class MaxEvaluationsTerminator(BaseTerminator):
152
151
  """A class for a termination criterion based on the number of evaluations."""
153
152
 
154
- def __init__(self, max_evaluations: int, **kwargs):
153
+ def __init__(self, max_evaluations: int, publisher: Publisher):
155
154
  """Initialize a termination criterion based on the number of evaluations.
156
155
 
157
156
  Looks for messages with key "num_evaluations" to update the number of evaluations.
158
157
 
159
158
  Args:
160
159
  max_evaluations (int): the maximum number of evaluations.
161
- kwargs: Additional keyword arguments. These are passed to the Subscriber class. At the very least, the
160
+ publisher (Publisher): The publisher to which the terminator will publish its state.
162
161
  publisher must be passed. See the Subscriber class for more information.
163
162
  """
164
- super().__init__(**kwargs)
163
+ super().__init__(publisher=publisher)
165
164
  if not isinstance(max_evaluations, int) or max_evaluations < 0:
166
165
  raise ValueError("max_evaluations must be a non-negative integer")
167
166
  self.max_evaluations = max_evaluations
168
167
  self.current_evaluations = 0
169
168
 
170
169
  def check(self) -> bool:
171
- """Check if the termination criterion based on the number of evaluations is reached.
170
+ """Check if the termination criterion based on the number of generations is reached.
172
171
 
173
172
  Returns:
174
173
  bool: True if the termination criterion is reached, False otherwise.
@@ -176,3 +175,112 @@ class MaxEvaluationsTerminator(BaseTerminator):
176
175
  super().check()
177
176
  self.notify()
178
177
  return self.current_evaluations >= self.max_evaluations
178
+
179
+
180
+ class CompositeTerminator(BaseTerminator):
181
+ """Combines multiple terminators using logical AND or OR."""
182
+
183
+ def __init__(self, terminators: list[BaseTerminator], publisher: Publisher, mode: str = "any"):
184
+ """Initialize a composite termination criterion.
185
+
186
+ Args:
187
+ terminators (list[BaseTerminator]): List of BaseTerminator instances.
188
+ publisher (Publisher): Publisher for passing messages.
189
+ mode (str): "any" (terminate if any) or "all" (terminate if all). By default, "any".
190
+ """
191
+ super().__init__(publisher=publisher)
192
+ self.terminators = terminators
193
+ for t in self.terminators:
194
+ t.notify = lambda: None # Reset the notify method so that individual terminators do not send notifications
195
+ types = [type(t) for t in self.terminators]
196
+ # Assert that all terminators are unique
197
+ if len(types) != len(set(types)):
198
+ raise ValueError("All terminators must be unique.")
199
+ max_generations = [t.max_generations for t in self.terminators if isinstance(t, MaxGenerationsTerminator)]
200
+ if max_generations:
201
+ self.max_generations = max(max_generations)
202
+ max_evaluations = [t.max_evaluations for t in self.terminators if isinstance(t, MaxEvaluationsTerminator)]
203
+ if max_evaluations:
204
+ self.max_evaluations = max(max_evaluations)
205
+ self.mode = mode
206
+
207
+ def check(self) -> bool:
208
+ """Check if the termination criterion is reached.
209
+
210
+ Returns:
211
+ bool: True if the termination criterion is reached, False otherwise.
212
+ """
213
+ super().check()
214
+ results = [t.check() for t in self.terminators]
215
+ if self.mode == "all":
216
+ return all(results)
217
+ return any(results)
218
+
219
+
220
+ class ExternalCheckTerminator(BaseTerminator):
221
+ """A termination criterion that checks an external condition."""
222
+
223
+ def __init__(self, check_function, publisher: Publisher):
224
+ """Initialize the external check terminator.
225
+
226
+ Args:
227
+ check_function (callable): A function that returns True if the termination condition is met.
228
+ publisher (Publisher): The publisher to send messages to.
229
+ """
230
+ super().__init__(publisher=publisher)
231
+ self.check_function = check_function
232
+
233
+ def check(self) -> bool:
234
+ """Check if the termination condition is met.
235
+
236
+ Returns:
237
+ bool: True if the termination condition is met, False otherwise.
238
+ """
239
+ super().check()
240
+ self.notify()
241
+ return self.check_function()
242
+
243
+
244
+ class MaxTimeTerminator(BaseTerminator):
245
+ """A termination criterion based on the maximum elapsed time."""
246
+
247
+ @property
248
+ def provided_topics(self) -> dict[int, Sequence[TerminatorMessageTopics]]:
249
+ """Return the topics provided by the terminator.
250
+
251
+ Returns:
252
+ dict[int, Sequence[TerminatorMessageTopics]]: The topics provided by the terminator.
253
+ """
254
+ return {
255
+ 0: [],
256
+ 1: [
257
+ TerminatorMessageTopics.GENERATION,
258
+ TerminatorMessageTopics.EVALUATION,
259
+ ],
260
+ }
261
+
262
+ def __init__(self, max_time_in_seconds: float, publisher: Publisher):
263
+ """Initialize the maximum time terminator.
264
+
265
+ Args:
266
+ max_time_in_seconds (float): The maximum elapsed time in seconds.
267
+ publisher (Publisher): The publisher to which the terminator will publish its state.
268
+ """
269
+ super().__init__(publisher=publisher)
270
+ if not isinstance(max_time_in_seconds, float) or max_time_in_seconds < 0:
271
+ raise ValueError("max_time must be a non-negative float")
272
+ self.max_time = max_time_in_seconds
273
+ self.start_time = None
274
+
275
+ def check(self) -> bool:
276
+ """Check if the termination criterion based on the maximum elapsed time is reached.
277
+
278
+ Returns:
279
+ bool: True if the termination criterion is reached, False otherwise.
280
+ """
281
+ super().check()
282
+ self.notify()
283
+ if self.start_time is None:
284
+ self.start_time = time.perf_counter()
285
+ elapsed_time = time.perf_counter() - self.start_time
286
+ return elapsed_time >= self.max_time
@@ -0,0 +1,108 @@
1
+ from .crossover import (
2
+ BlendAlphaCrossoverOptions,
3
+ BoundedExponentialCrossoverOptions,
4
+ CrossoverOptions,
5
+ LocalCrossoverOptions,
6
+ SimulatedBinaryCrossoverOptions,
7
+ SingleArithmeticCrossoverOptions,
8
+ SinglePointBinaryCrossoverOptions,
9
+ UniformIntegerCrossoverOptions,
10
+ UniformMixedIntegerCrossoverOptions,
11
+ crossover_constructor,
12
+ )
13
+ from .generator import (
14
+ GeneratorOptions,
15
+ LHSGeneratorOptions,
16
+ RandomBinaryGeneratorOptions,
17
+ RandomGeneratorOptions,
18
+ RandomIntegerGeneratorOptions,
19
+ RandomMixedIntegerGeneratorOptions,
20
+ generator_constructor,
21
+ )
22
+ from .mutation import (
23
+ BinaryFlipMutationOptions,
24
+ BoundedPolynomialMutationOptions,
25
+ IntegerRandomMutationOptions,
26
+ MixedIntegerRandomMutationOptions,
27
+ MPTMutationOptions,
28
+ MutationOptions,
29
+ NonUniformMutationOptions,
30
+ PowerMutationOptions,
31
+ SelfAdaptiveGaussianMutationOptions,
32
+ mutation_constructor,
33
+ )
34
+ from .repair import RepairOptions, repair_constructor
35
+ from .scalar_selection import (
36
+ RouletteWheelSelectionOptions,
37
+ ScalarSelectionOptions,
38
+ TournamentSelectionOptions,
39
+ scalar_selector_constructor,
40
+ )
41
+ from .selection import (
42
+ IBEASelectorOptions,
43
+ NSGA2SelectorOptions,
44
+ NSGA3SelectorOptions,
45
+ ReferenceVectorOptions,
46
+ RVEASelectorOptions,
47
+ SelectorOptions,
48
+ selection_constructor,
49
+ )
50
+ from .termination import (
51
+ CompositeTerminatorOptions,
52
+ ExternalCheckTerminatorOptions,
53
+ MaxEvaluationsTerminatorOptions,
54
+ MaxGenerationsTerminatorOptions,
55
+ MaxTimeTerminatorOptions,
56
+ TerminatorOptions,
57
+ terminator_constructor,
58
+ )
59
+
60
+ __all__ = [ # noqa: RUF022
61
+ "BinaryFlipMutationOptions",
62
+ "BlendAlphaCrossoverOptions",
63
+ "BoundedExponentialCrossoverOptions",
64
+ "BoundedPolynomialMutationOptions",
65
+ "CompositeTerminatorOptions",
66
+ "CrossoverOptions",
67
+ "ExternalCheckTerminatorOptions",
68
+ "GeneratorOptions",
69
+ "IBEASelectorOptions",
70
+ "IntegerRandomMutationOptions",
71
+ "LHSGeneratorOptions",
72
+ "LocalCrossoverOptions",
73
+ "MaxEvaluationsTerminatorOptions",
74
+ "MaxGenerationsTerminatorOptions",
75
+ "MaxTimeTerminatorOptions",
76
+ "MixedIntegerRandomMutationOptions",
77
+ "MPTMutationOptions",
78
+ "MutationOptions",
79
+ "NSGA2SelectorOptions",
80
+ "NSGA3SelectorOptions",
81
+ "NonUniformMutationOptions",
82
+ "PowerMutationOptions",
83
+ "RandomBinaryGeneratorOptions",
84
+ "RandomGeneratorOptions",
85
+ "RandomIntegerGeneratorOptions",
86
+ "RandomMixedIntegerGeneratorOptions",
87
+ "ReferenceVectorOptions",
88
+ "RouletteWheelSelectionOptions",
89
+ "RVEASelectorOptions",
90
+ "ScalarSelectionOptions",
91
+ "scalar_selector_constructor",
92
+ "SelfAdaptiveGaussianMutationOptions",
93
+ "SelectorOptions",
94
+ "SimulatedBinaryCrossoverOptions",
95
+ "SingleArithmeticCrossoverOptions",
96
+ "SinglePointBinaryCrossoverOptions",
97
+ "TerminatorOptions",
98
+ "terminator_constructor",
99
+ "TournamentSelectionOptions",
100
+ "UniformIntegerCrossoverOptions",
101
+ "UniformMixedIntegerCrossoverOptions",
102
+ "crossover_constructor",
103
+ "generator_constructor",
104
+ "mutation_constructor",
105
+ "selection_constructor",
106
+ "RepairOptions",
107
+ "repair_constructor",
108
+ ]