desdeo 2.0.0__py3-none-any.whl → 2.1.1__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 (130) 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 +16 -11
  87. desdeo/problem/evaluator.py +4 -5
  88. desdeo/problem/external/__init__.py +18 -0
  89. desdeo/problem/external/core.py +356 -0
  90. desdeo/problem/external/pymoo_provider.py +266 -0
  91. desdeo/problem/external/runtime.py +44 -0
  92. desdeo/problem/gurobipy_evaluator.py +37 -12
  93. desdeo/problem/infix_parser.py +1 -16
  94. desdeo/problem/json_parser.py +7 -11
  95. desdeo/problem/pyomo_evaluator.py +25 -6
  96. desdeo/problem/schema.py +73 -55
  97. desdeo/problem/simulator_evaluator.py +65 -15
  98. desdeo/problem/testproblems/__init__.py +26 -11
  99. desdeo/problem/testproblems/benchmarks_server.py +120 -0
  100. desdeo/problem/testproblems/cake_problem.py +185 -0
  101. desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
  102. desdeo/problem/testproblems/forest_problem.py +77 -69
  103. desdeo/problem/testproblems/multi_valued_constraints.py +119 -0
  104. desdeo/problem/testproblems/{river_pollution_problem.py → river_pollution_problems.py} +28 -22
  105. desdeo/problem/testproblems/single_objective.py +289 -0
  106. desdeo/problem/testproblems/zdt_problem.py +4 -1
  107. desdeo/problem/utils.py +1 -1
  108. desdeo/tools/__init__.py +39 -21
  109. desdeo/tools/desc_gen.py +22 -0
  110. desdeo/tools/generics.py +22 -2
  111. desdeo/tools/group_scalarization.py +3090 -0
  112. desdeo/tools/indicators_binary.py +107 -1
  113. desdeo/tools/indicators_unary.py +3 -16
  114. desdeo/tools/message.py +33 -2
  115. desdeo/tools/non_dominated_sorting.py +4 -3
  116. desdeo/tools/patterns.py +9 -7
  117. desdeo/tools/pyomo_solver_interfaces.py +49 -36
  118. desdeo/tools/reference_vectors.py +118 -351
  119. desdeo/tools/scalarization.py +340 -1413
  120. desdeo/tools/score_bands.py +491 -328
  121. desdeo/tools/utils.py +117 -49
  122. desdeo/tools/visualizations.py +67 -0
  123. desdeo/utopia_stuff/utopia_problem.py +1 -1
  124. desdeo/utopia_stuff/utopia_problem_old.py +1 -1
  125. {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info}/METADATA +47 -30
  126. desdeo-2.1.1.dist-info/RECORD +180 -0
  127. {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info}/WHEEL +1 -1
  128. desdeo-2.0.0.dist-info/RECORD +0 -120
  129. /desdeo/api/utils/{logger.py → _logger.py} +0 -0
  130. {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info/licenses}/LICENSE +0 -0
desdeo/emo/__init__.py CHANGED
@@ -1,29 +1,159 @@
1
1
  """Imports available from the desdeo emo."""
2
2
 
3
3
  __all__ = [
4
- "rvea",
5
- "nsga3",
6
- "template1",
7
- "NSGAIII_select",
8
- "RVEASelector",
9
- "SimulatedBinaryCrossover",
10
- "BoundedPolynomialMutation",
11
- "LHSGenerator",
12
- "RandomGenerator",
13
- "EMOEvaluator",
14
- "MaxEvaluationsTerminator",
15
- "MaxGenerationsTerminator",
16
- "Archive",
17
- "FeasibleArchive",
18
- "NonDominatedArchive",
4
+ "algorithms",
5
+ "crossover",
6
+ "generator",
7
+ "mutation",
8
+ "other",
9
+ "preference_handling",
10
+ "repair",
11
+ "scalar_selection",
12
+ "selection",
13
+ "templates",
14
+ "termination",
19
15
  ]
20
16
 
21
- from .hooks.archivers import Archive, FeasibleArchive, NonDominatedArchive
22
- from .methods.bases import template1
23
- from .methods.EAs import nsga3, rvea
24
- from .operators.crossover import SimulatedBinaryCrossover
25
- from .operators.evaluator import EMOEvaluator
26
- from .operators.generator import LHSGenerator, RandomGenerator
27
- from .operators.mutation import BoundedPolynomialMutation
28
- from .operators.selection import NSGAIII_select, RVEASelector
29
- from .operators.termination import MaxEvaluationsTerminator, MaxGenerationsTerminator
17
+ from types import SimpleNamespace
18
+
19
+ from .options.algorithms import (
20
+ emo_constructor,
21
+ ibea_mixed_integer_options,
22
+ ibea_options,
23
+ nsga2_options,
24
+ nsga3_mixed_integer_options,
25
+ nsga3_options,
26
+ rvea_mixed_integer_options,
27
+ rvea_options,
28
+ )
29
+ from .options.crossover import (
30
+ BlendAlphaCrossoverOptions,
31
+ BoundedExponentialCrossoverOptions,
32
+ LocalCrossoverOptions,
33
+ SimulatedBinaryCrossoverOptions,
34
+ SingleArithmeticCrossoverOptions,
35
+ SinglePointBinaryCrossoverOptions,
36
+ UniformIntegerCrossoverOptions,
37
+ UniformMixedIntegerCrossoverOptions,
38
+ )
39
+ from .options.generator import (
40
+ LHSGeneratorOptions,
41
+ RandomBinaryGeneratorOptions,
42
+ RandomGeneratorOptions,
43
+ RandomIntegerGeneratorOptions,
44
+ RandomMixedIntegerGeneratorOptions,
45
+ )
46
+ from .options.mutation import (
47
+ BinaryFlipMutationOptions,
48
+ BoundedPolynomialMutationOptions,
49
+ IntegerRandomMutationOptions,
50
+ MixedIntegerRandomMutationOptions,
51
+ MPTMutationOptions,
52
+ NonUniformMutationOptions,
53
+ PowerMutationOptions,
54
+ SelfAdaptiveGaussianMutationOptions,
55
+ )
56
+ from .options.repair import ClipRepairOptions, NoRepairOptions
57
+ from .options.scalar_selection import RouletteWheelSelectionOptions, TournamentSelectionOptions
58
+ from .options.selection import (
59
+ IBEASelectorOptions,
60
+ NSGA2SelectorOptions,
61
+ NSGA3SelectorOptions,
62
+ ReferenceVectorOptions,
63
+ RVEASelectorOptions,
64
+ )
65
+ from .options.templates import (
66
+ DesirableRangesOptions,
67
+ NonPreferredSolutionsOptions,
68
+ PreferredSolutionsOptions,
69
+ ReferencePointOptions,
70
+ Template1Options,
71
+ Template2Options,
72
+ )
73
+ from .options.termination import (
74
+ CompositeTerminatorOptions,
75
+ ExternalCheckTerminatorOptions,
76
+ MaxEvaluationsTerminatorOptions,
77
+ MaxGenerationsTerminatorOptions,
78
+ MaxTimeTerminatorOptions,
79
+ )
80
+
81
+ # Just didn't want to have a thousand imports in the main namespace
82
+ algorithms = SimpleNamespace(
83
+ rvea_options=rvea_options,
84
+ nsga2_options=nsga2_options,
85
+ nsga3_options=nsga3_options,
86
+ ibea_options=ibea_options,
87
+ rvea_mixed_integer_options=rvea_mixed_integer_options,
88
+ nsga3_mixed_integer_options=nsga3_mixed_integer_options,
89
+ ibea_mixed_integer_options=ibea_mixed_integer_options,
90
+ emo_constructor=emo_constructor,
91
+ )
92
+
93
+ termination = SimpleNamespace(
94
+ MaxEvaluationsTerminatorOptions=MaxEvaluationsTerminatorOptions,
95
+ MaxGenerationsTerminatorOptions=MaxGenerationsTerminatorOptions,
96
+ MaxTimeTerminatorOptions=MaxTimeTerminatorOptions,
97
+ CompositeTerminatorOptions=CompositeTerminatorOptions,
98
+ ExternalCheckTerminatorOptions=ExternalCheckTerminatorOptions,
99
+ )
100
+
101
+ selection = SimpleNamespace(
102
+ IBEASelectorOptions=IBEASelectorOptions,
103
+ NSGA3SelectorOptions=NSGA3SelectorOptions,
104
+ NSGA2SelectorOptions=NSGA2SelectorOptions,
105
+ RVEASelectorOptions=RVEASelectorOptions,
106
+ )
107
+
108
+ other = SimpleNamespace(
109
+ ReferenceVectorOptions=ReferenceVectorOptions,
110
+ )
111
+
112
+ scalar_selection = SimpleNamespace(
113
+ TournamentSelectionOptions=TournamentSelectionOptions,
114
+ RouletteWheelSelectionOptions=RouletteWheelSelectionOptions,
115
+ )
116
+
117
+ mutation = SimpleNamespace(
118
+ BinaryFlipMutationOptions=BinaryFlipMutationOptions,
119
+ BoundedPolynomialMutationOptions=BoundedPolynomialMutationOptions,
120
+ IntegerRandomMutationOptions=IntegerRandomMutationOptions,
121
+ MixedIntegerRandomMutationOptions=MixedIntegerRandomMutationOptions,
122
+ MPTMutationOptions=MPTMutationOptions,
123
+ NonUniformMutationOptions=NonUniformMutationOptions,
124
+ PowerMutationOptions=PowerMutationOptions,
125
+ SelfAdaptiveGaussianMutationOptions=SelfAdaptiveGaussianMutationOptions,
126
+ )
127
+
128
+ generator = SimpleNamespace(
129
+ LHSGeneratorOptions=LHSGeneratorOptions,
130
+ RandomBinaryGeneratorOptions=RandomBinaryGeneratorOptions,
131
+ RandomGeneratorOptions=RandomGeneratorOptions,
132
+ RandomIntegerGeneratorOptions=RandomIntegerGeneratorOptions,
133
+ RandomMixedIntegerGeneratorOptions=RandomMixedIntegerGeneratorOptions,
134
+ )
135
+
136
+ templates = SimpleNamespace(
137
+ Template1Options=Template1Options,
138
+ Template2Options=Template2Options,
139
+ )
140
+
141
+ repair = SimpleNamespace(NoRepairOptions=NoRepairOptions, ClipRepairOptions=ClipRepairOptions)
142
+
143
+ preference_handling = SimpleNamespace(
144
+ ReferencePointOptions=ReferencePointOptions,
145
+ DesirableRangesOptions=DesirableRangesOptions,
146
+ PreferredSolutionsOptions=PreferredSolutionsOptions,
147
+ NonPreferredSolutionsOptions=NonPreferredSolutionsOptions,
148
+ )
149
+
150
+ crossover = SimpleNamespace(
151
+ BlendAlphaCrossoverOptions=BlendAlphaCrossoverOptions,
152
+ BoundedExponentialCrossoverOptions=BoundedExponentialCrossoverOptions,
153
+ LocalCrossoverOptions=LocalCrossoverOptions,
154
+ SimulatedBinaryCrossoverOptions=SimulatedBinaryCrossoverOptions,
155
+ SingleArithmeticCrossoverOptions=SingleArithmeticCrossoverOptions,
156
+ SinglePointBinaryCrossoverOptions=SinglePointBinaryCrossoverOptions,
157
+ UniformIntegerCrossoverOptions=UniformIntegerCrossoverOptions,
158
+ UniformMixedIntegerCrossoverOptions=UniformMixedIntegerCrossoverOptions,
159
+ )
@@ -5,6 +5,7 @@ from collections.abc import Sequence
5
5
  import polars as pl
6
6
 
7
7
  from desdeo.problem import Problem
8
+ from desdeo.tools.generics import EMOResult
8
9
  from desdeo.tools.message import (
9
10
  EvaluatorMessageTopics,
10
11
  GeneratorMessageTopics,
@@ -72,6 +73,14 @@ class BaseArchive(Subscriber):
72
73
  self.selections = pl.concat([self.selections, data], how="vertical")
73
74
  return
74
75
 
76
+ @property
77
+ def results(self) -> EMOResult:
78
+ """Return the results of the archiver."""
79
+ dec_vars = [x.symbol for x in self.problem.get_flattened_variables()]
80
+ all_cols = self.solutions.columns
81
+ non_decs = [col for col in all_cols if col not in dec_vars]
82
+ return EMOResult(optimal_variables=self.solutions[dec_vars], optimal_outputs=self.solutions[non_decs])
83
+
75
84
 
76
85
  class FeasibleArchive(BaseArchive):
77
86
  """An archiver that stores all feasible solutions evaluated during evolution."""
@@ -104,7 +113,7 @@ class FeasibleArchive(BaseArchive):
104
113
  data = message.value
105
114
  feasible_mask = (data[self.cons_symb] <= 0).to_numpy().all(axis=1)
106
115
  feasible_data = data.filter(feasible_mask)
107
- feasible_data = data.with_columns(generation=self.generation_number)
116
+ feasible_data = feasible_data.with_columns(generation=self.generation_number)
108
117
  if self.solutions is None:
109
118
  self.solutions = feasible_data
110
119
  else:
@@ -135,7 +144,7 @@ class Archive(BaseArchive):
135
144
 
136
145
 
137
146
  class NonDominatedArchive(Archive):
138
- """An archiver that stores only the non-dominated solutions evaluated during evolution."""
147
+ """An archiver that stores only the feasible non-dominated solutions evaluated during evolution."""
139
148
 
140
149
  def __init__(self, *, problem: Problem, publisher: Publisher):
141
150
  """Initialize the archiver.
@@ -146,6 +155,10 @@ class NonDominatedArchive(Archive):
146
155
  """
147
156
  super().__init__(problem=problem, publisher=publisher)
148
157
  self.targets = [f"{x.symbol}_min" for x in problem.objectives]
158
+ if problem.constraints is None:
159
+ self.cons_symb = []
160
+ else:
161
+ self.cons_symb = [x.symbol for x in problem.constraints]
149
162
 
150
163
  def update(self, message: Message) -> None:
151
164
  """Update the archiver with the new data.
@@ -163,6 +176,9 @@ class NonDominatedArchive(Archive):
163
176
  data = data.with_columns(generation=self.generation_number)
164
177
  if type(data) is not pl.DataFrame:
165
178
  raise ValueError("Data should be a polars DataFrame")
179
+ if self.cons_symb:
180
+ feasible_mask = (data[self.cons_symb] <= 0).to_numpy().all(axis=1)
181
+ data = data.filter(feasible_mask)
166
182
  if self.solutions is None:
167
183
  non_dom_mask = non_dominated(data[self.targets].to_numpy())
168
184
  self.solutions = data.filter(non_dom_mask)
desdeo/emo/methods/EAs.py CHANGED
@@ -1,18 +1,33 @@
1
- """Implements common evolutionary algorithms for multi-objective optimization."""
1
+ """[Deprecated] Implements common evolutionary algorithms for multi-objective optimization.
2
+
3
+ Use desdeo.emo.options.algorithms instead.
4
+ """
2
5
 
3
6
  from collections.abc import Callable
4
7
  from functools import partial
8
+ from warnings import warn
9
+
10
+ import numpy as np
5
11
 
6
- from desdeo.emo.methods.bases import EMOResult, template1
12
+ from desdeo.emo.methods.templates import EMOResult, template1, template2
7
13
  from desdeo.emo.operators.crossover import SimulatedBinaryCrossover, UniformMixedIntegerCrossover
8
14
  from desdeo.emo.operators.evaluator import EMOEvaluator
9
15
  from desdeo.emo.operators.generator import LHSGenerator, RandomMixedIntegerGenerator
10
16
  from desdeo.emo.operators.mutation import BoundedPolynomialMutation, MixedIntegerRandomMutation
11
- from desdeo.emo.operators.selection import NSGAIII_select, ReferenceVectorOptions, RVEASelector
17
+ from desdeo.emo.operators.scalar_selection import TournamentSelection
18
+ from desdeo.emo.operators.selection import IBEASelector, NSGA3Selector, ReferenceVectorOptions, RVEASelector
12
19
  from desdeo.emo.operators.termination import MaxEvaluationsTerminator, MaxGenerationsTerminator
13
20
  from desdeo.problem import Problem
21
+ from desdeo.tools.indicators_binary import self_epsilon
14
22
  from desdeo.tools.patterns import Publisher
15
23
 
24
+ warn(
25
+ "desdeo.emo.methods.EAs is deprecated and will be removed in future versions. "
26
+ "Please use desdeo.emo.options.algorithms instead.",
27
+ category=DeprecationWarning,
28
+ stacklevel=1,
29
+ )
30
+
16
31
 
17
32
  def rvea(
18
33
  *,
@@ -159,7 +174,7 @@ def nsga3(
159
174
  verbosity=forced_verbosity if forced_verbosity is not None else 2,
160
175
  )
161
176
 
162
- selector = NSGAIII_select(
177
+ selector = NSGA3Selector(
163
178
  problem=problem,
164
179
  publisher=publisher,
165
180
  verbosity=forced_verbosity if forced_verbosity is not None else 2,
@@ -216,6 +231,114 @@ def nsga3(
216
231
  )
217
232
 
218
233
 
234
+ def ibea(
235
+ *,
236
+ problem: Problem,
237
+ population_size: int = 100,
238
+ n_generations: int = 100,
239
+ max_evaluations: int | None = None,
240
+ kappa: float = 0.05,
241
+ binary_indicator: Callable[[np.ndarray], np.ndarray] = self_epsilon,
242
+ seed: int = 0,
243
+ forced_verbosity: int | None = None,
244
+ ) -> tuple[Callable[[], EMOResult], Publisher]:
245
+ """Implements the Indicator-Based Evolutionary Algorithm (IBEA).
246
+
247
+ References:
248
+ Zitzler, E., Künzli, S. (2004). Indicator-Based Selection in Multiobjective Search. In: Yao, X., et al.
249
+ Parallel Problem Solving from Nature - PPSN VIII. PPSN 2004. Lecture Notes in Computer Science, vol 3242.
250
+ Springer, Berlin, Heidelberg. https://doi.org/10.1007/978-3-540-30217-9_84
251
+
252
+ Args:
253
+ problem (Problem): The problem to be solved.
254
+ population_size (int, optional): The size of the population. Defaults to 100.
255
+ n_generations (int, optional): The number of generations to run the algorithm. Defaults to 100.
256
+ max_evaluations (int | None, optional): The maximum number of evaluations to run the algorithm. If None, the
257
+ algorithm will run for n_generations. Defaults to None. If both n_generations and max_evaluations are
258
+ provided, the algorithm will run until max_evaluations is reached.
259
+ kappa (float, optional): The kappa value for the IBEA selection. Defaults to 0.05.
260
+ binary_indicator (Callable[[np.ndarray], np.ndarray], optional): A binary indicator function that takes the
261
+ target values and returns a binary indicator for each individual. Defaults to self_epsilon with uses
262
+ binary adaptive epsilon indicator.
263
+ seed (int, optional): The seed for the random number generator. Defaults to 0.
264
+ forced_verbosity (int | None, optional): If not None, the verbosity of the algorithm is forced to this value.
265
+ Defaults to None.
266
+
267
+ Returns:
268
+ tuple[Callable[[], EMOResult], Publisher]: A tuple containing the function to run the algorithm and the
269
+ publisher object. The publisher object can be used to subscribe to the topics of the algorithm components,
270
+ as well as to inject additional functionality such as archiving the results.
271
+ """
272
+ publisher = Publisher()
273
+ evaluator = EMOEvaluator(
274
+ problem=problem,
275
+ publisher=publisher,
276
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
277
+ )
278
+ selector = IBEASelector(
279
+ problem=problem,
280
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
281
+ publisher=publisher,
282
+ population_size=population_size,
283
+ kappa=kappa,
284
+ binary_indicator=binary_indicator,
285
+ )
286
+ generator = LHSGenerator(
287
+ problem=problem,
288
+ evaluator=evaluator,
289
+ publisher=publisher,
290
+ n_points=population_size,
291
+ seed=seed,
292
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
293
+ )
294
+ crossover = SimulatedBinaryCrossover(
295
+ problem=problem,
296
+ publisher=publisher,
297
+ seed=seed,
298
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
299
+ )
300
+ mutation = BoundedPolynomialMutation(
301
+ problem=problem,
302
+ publisher=publisher,
303
+ seed=seed,
304
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
305
+ )
306
+ if max_evaluations is not None:
307
+ terminator = MaxEvaluationsTerminator(max_evaluations, publisher=publisher)
308
+ else:
309
+ terminator = MaxGenerationsTerminator(
310
+ n_generations,
311
+ publisher=publisher,
312
+ )
313
+
314
+ scalar_selector = TournamentSelection(publisher=publisher, verbosity=0, winner_size=population_size, seed=seed)
315
+
316
+ components = [
317
+ evaluator,
318
+ generator,
319
+ crossover,
320
+ mutation,
321
+ selector,
322
+ terminator,
323
+ scalar_selector,
324
+ ]
325
+ [publisher.auto_subscribe(x) for x in components]
326
+ [publisher.register_topics(x.provided_topics[x.verbosity], x.__class__.__name__) for x in components]
327
+ return (
328
+ partial(
329
+ template2,
330
+ evaluator=evaluator,
331
+ crossover=crossover,
332
+ mutation=mutation,
333
+ generator=generator,
334
+ selection=selector,
335
+ terminator=terminator,
336
+ mate_selection=scalar_selector,
337
+ ),
338
+ publisher,
339
+ )
340
+
341
+
219
342
  def nsga3_mixed_integer(
220
343
  *,
221
344
  problem: Problem,
@@ -261,7 +384,7 @@ def nsga3_mixed_integer(
261
384
  verbosity=forced_verbosity if forced_verbosity is not None else 2,
262
385
  )
263
386
 
264
- selector = NSGAIII_select(
387
+ selector = NSGA3Selector(
265
388
  problem=problem,
266
389
  publisher=publisher,
267
390
  verbosity=forced_verbosity if forced_verbosity is not None else 2,
@@ -1,59 +1,12 @@
1
- """This module contains the basic functional implementations for the EMO methods.
1
+ """Deprecated module."""
2
2
 
3
- This can be used as a template for the implementation of the EMO methods.
4
- """
3
+ import warnings
5
4
 
6
- import numpy as np
7
- import polars as pl
8
- from pydantic import BaseModel, ConfigDict, Field
5
+ from desdeo.emo.methods.templates import EMOResult, template1, template2
9
6
 
10
- from desdeo.emo.operators.crossover import BaseCrossover
11
- from desdeo.emo.operators.evaluator import EMOEvaluator
12
- from desdeo.emo.operators.generator import BaseGenerator
13
- from desdeo.emo.operators.mutation import BaseMutation
14
- from desdeo.emo.operators.selection import BaseSelector
15
- from desdeo.emo.operators.termination import BaseTerminator
16
-
17
-
18
- class EMOResult(BaseModel):
19
- solutions: pl.DataFrame = Field(description="The decision vectors of the final population.")
20
- """The decision vectors of the final population."""
21
- outputs: pl.DataFrame = Field(
22
- description="The objective vectors, constraint vectors, and targets of the final population."
23
- )
24
- """The objective vectors, constraint vectors, and targets of the final population."""
25
-
26
- model_config = ConfigDict(arbitrary_types_allowed=True)
27
-
28
-
29
- def template1(
30
- evaluator: EMOEvaluator,
31
- crossover: BaseCrossover,
32
- mutation: BaseMutation,
33
- generator: BaseGenerator,
34
- selection: BaseSelector,
35
- terminator: BaseTerminator,
36
- ) -> EMOResult:
37
- """Implements a template that many EMO methods, such as RVEA and NSGA-III, follow.
38
-
39
- Args:
40
- evaluator (EMOEvaluator): A class that evaluates the solutions and provides the objective vectors, constraint
41
- vectors, and targets.
42
- crossover (BaseCrossover): The crossover operator.
43
- mutation (BaseMutation): The mutation operator.
44
- generator (BaseGenerator): A class that generates the initial population.
45
- selection (BaseSelector): The selection operator.
46
- terminator (BaseTerminator): The termination operator.
47
-
48
- Returns:
49
- EMOResult: The final population and their objective vectors, constraint vectors, and targets
50
- """
51
- solutions, outputs = generator.do()
52
-
53
- while not terminator.check():
54
- offspring = crossover.do(population=solutions)
55
- offspring = mutation.do(offspring, solutions)
56
- offspring_outputs = evaluator.evaluate(offspring)
57
- solutions, outputs = selection.do(parents=(solutions, outputs), offsprings=(offspring, offspring_outputs))
58
-
59
- return EMOResult(solutions=solutions, outputs=outputs)
7
+ warnings.warn(
8
+ "The EMO methods in this module are deprecated and will be removed in a future release. "
9
+ "Please use the methods in desdeo.emo.methods.templates instead.",
10
+ DeprecationWarning,
11
+ stacklevel=2,
12
+ )
@@ -0,0 +1,111 @@
1
+ """This module contains the basic functional implementations for the EMO methods.
2
+
3
+ This can be used as a template for the implementation of the EMO methods.
4
+ """
5
+
6
+ from collections.abc import Callable
7
+
8
+ import polars as pl
9
+ from pydantic import BaseModel, ConfigDict, Field
10
+
11
+ from desdeo.emo.operators.crossover import BaseCrossover
12
+ from desdeo.emo.operators.evaluator import EMOEvaluator
13
+ from desdeo.emo.operators.generator import BaseGenerator
14
+ from desdeo.emo.operators.mutation import BaseMutation
15
+ from desdeo.emo.operators.scalar_selection import BaseScalarSelector
16
+ from desdeo.emo.operators.selection import BaseSelector
17
+ from desdeo.emo.operators.termination import BaseTerminator
18
+ from desdeo.tools.generics import EMOResult
19
+
20
+
21
+ def template1(
22
+ evaluator: EMOEvaluator,
23
+ crossover: BaseCrossover,
24
+ mutation: BaseMutation,
25
+ generator: BaseGenerator,
26
+ selection: BaseSelector,
27
+ terminator: BaseTerminator,
28
+ repair: Callable[[pl.DataFrame], pl.DataFrame] = lambda x: x, # Default to identity function if no repair is needed
29
+ ) -> EMOResult:
30
+ """Implements a template that many EMO methods, such as RVEA and NSGA-III, follow.
31
+
32
+ Args:
33
+ evaluator (EMOEvaluator): A class that evaluates the solutions and provides the objective vectors, constraint
34
+ vectors, and targets.
35
+ crossover (BaseCrossover): The crossover operator.
36
+ mutation (BaseMutation): The mutation operator.
37
+ generator (BaseGenerator): A class that generates the initial population.
38
+ selection (BaseSelector): The selection operator.
39
+ terminator (BaseTerminator): The termination operator.
40
+ repair (Callable, optional): A function that repairs the offspring if they go out of bounds. Defaults to an
41
+ identity function, meaning no repair is done. See :py:func:`desdeo.tools.utils.repair` as an example of a
42
+ repair function.
43
+
44
+ Returns:
45
+ EMOResult: The final population and their objective vectors, constraint vectors, and targets
46
+ """
47
+ solutions, outputs = generator.do()
48
+
49
+ while not terminator.check():
50
+ offspring = crossover.do(population=solutions)
51
+ offspring = mutation.do(offspring, solutions)
52
+ # Repair offspring if they go out of bounds
53
+ offspring = repair(offspring)
54
+ offspring_outputs = evaluator.evaluate(offspring)
55
+ solutions, outputs = selection.do(parents=(solutions, outputs), offsprings=(offspring, offspring_outputs))
56
+
57
+ return EMOResult(optimal_variables=solutions, optimal_outputs=outputs)
58
+
59
+
60
+ def template2(
61
+ evaluator: EMOEvaluator,
62
+ crossover: BaseCrossover,
63
+ mutation: BaseMutation,
64
+ generator: BaseGenerator,
65
+ selection: BaseSelector,
66
+ mate_selection: BaseScalarSelector,
67
+ terminator: BaseTerminator,
68
+ repair: Callable[[pl.DataFrame], pl.DataFrame] = lambda x: x, # Default to identity function if no repair is needed
69
+ ) -> EMOResult:
70
+ """Implements a template that many EMO methods, such as IBEA, follow.
71
+
72
+ Args:
73
+ evaluator (EMOEvaluator): A class that evaluates the solutions and provides the objective vectors, constraint
74
+ vectors, and targets.
75
+ crossover (BaseCrossover): The crossover operator.
76
+ mutation (BaseMutation): The mutation operator.
77
+ generator (BaseGenerator): A class that generates the initial population.
78
+ selection (BaseSelector): The selection operator.
79
+ mate_selection (BaseScalarSelector): The mating selection operator, which selects parents for mating.
80
+ This is typically a scalar selector that selects parents based on their fitness.
81
+ terminator (BaseTerminator): The termination operator.
82
+ repair (Callable, optional): A function that repairs the offspring if they go out of bounds. Defaults to an
83
+ identity function, meaning no repair is done. See :py:func:`desdeo.tools.utils.repair` as an example of a
84
+ repair function.
85
+
86
+ Returns:
87
+ EMOResult: The final population and their objective vectors, constraint vectors, and targets
88
+ """
89
+ solutions, outputs = generator.do()
90
+ # This is just a hack to make all selection operators work (they require offsprings to be passed separately rn)
91
+ offspring = pl.DataFrame(
92
+ schema=solutions.schema,
93
+ )
94
+ offspring_outputs = pl.DataFrame(
95
+ schema=outputs.schema,
96
+ )
97
+
98
+ while True:
99
+ solutions, outputs = selection.do(parents=(solutions, outputs), offsprings=(offspring, offspring_outputs))
100
+ if terminator.check():
101
+ # Weird way to do looping, but IBEA does environmental selection before the loop check, and...
102
+ # does mating afterwards.
103
+ break
104
+ parents, _ = mate_selection.do((solutions, outputs))
105
+ offspring = crossover.do(population=parents)
106
+ offspring = mutation.do(offspring, solutions)
107
+ # Repair offspring if they go out of bounds
108
+ offspring = repair(offspring)
109
+ offspring_outputs = evaluator.evaluate(offspring)
110
+
111
+ return EMOResult(optimal_variables=solutions, optimal_outputs=outputs)