desdeo 1.2__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.2.dist-info → desdeo-2.0.0.dist-info}/WHEEL +1 -1
  121. desdeo-1.2.dist-info/METADATA +0 -16
  122. desdeo-1.2.dist-info/RECORD +0 -4
@@ -0,0 +1,172 @@
1
+ """A collection of archivers for storing solutions evaluated during evolution."""
2
+
3
+ from collections.abc import Sequence
4
+
5
+ import polars as pl
6
+
7
+ from desdeo.problem import Problem
8
+ from desdeo.tools.message import (
9
+ EvaluatorMessageTopics,
10
+ GeneratorMessageTopics,
11
+ Message,
12
+ MessageTopics,
13
+ SelectorMessageTopics,
14
+ TerminatorMessageTopics,
15
+ )
16
+ from desdeo.tools.non_dominated_sorting import non_dominated, non_dominated_merge
17
+ from desdeo.tools.patterns import Publisher, Subscriber
18
+
19
+
20
+ class BaseArchive(Subscriber):
21
+ """Base class for archivers."""
22
+
23
+ @property
24
+ def interested_topics(self) -> Sequence[MessageTopics]:
25
+ """Return the message topics that the archiver is interested in."""
26
+ return [
27
+ GeneratorMessageTopics.VERBOSE_OUTPUTS,
28
+ EvaluatorMessageTopics.VERBOSE_OUTPUTS,
29
+ SelectorMessageTopics.SELECTED_VERBOSE_OUTPUTS,
30
+ TerminatorMessageTopics.GENERATION,
31
+ ]
32
+
33
+ @property
34
+ def provided_topics(self) -> dict[int, Sequence[MessageTopics]]:
35
+ """Return the topics provided by the archiver."""
36
+ return {0: []}
37
+
38
+ def __init__(self, *, problem: Problem, publisher: Publisher):
39
+ """Initialize the base archiver.
40
+
41
+ Args:
42
+ problem (Problem): The problem being solved.
43
+ publisher (Publisher): The publisher object.
44
+ """
45
+ super().__init__(publisher, verbosity=0)
46
+ self.solutions: pl.DataFrame = None
47
+ self.selections: pl.DataFrame = None
48
+ self.problem = problem
49
+ self.generation_number = 1
50
+
51
+ def state(self) -> Sequence[Message]:
52
+ """Return the state of the archiver."""
53
+ return []
54
+
55
+ def update(self, message: Message) -> None:
56
+ """Updae the archiver with new data.
57
+
58
+ Takes care of common archiving jobs. Make sure to run this for every archiver.
59
+
60
+ Args:
61
+ message (Message): Message from the publisher.
62
+ """
63
+ if message.topic == TerminatorMessageTopics.GENERATION:
64
+ self.generation_number = message.value
65
+ return
66
+ if message.topic == SelectorMessageTopics.SELECTED_VERBOSE_OUTPUTS:
67
+ data: pl.DataFrame = message.value
68
+ data = data.with_columns(generation=self.generation_number)
69
+ if self.selections is None:
70
+ self.selections = data
71
+ else:
72
+ self.selections = pl.concat([self.selections, data], how="vertical")
73
+ return
74
+
75
+
76
+ class FeasibleArchive(BaseArchive):
77
+ """An archiver that stores all feasible solutions evaluated during evolution."""
78
+
79
+ def __init__(self, *, problem: Problem, publisher: Publisher):
80
+ """Initialize the archiver.
81
+
82
+ Args:
83
+ problem (Problem): The problem being solved.
84
+ publisher (Publisher): The publisher object.
85
+ """
86
+ super().__init__(problem=problem, publisher=publisher)
87
+
88
+ if problem.constraints is None:
89
+ raise ValueError("The problem has no constraints.")
90
+ self.cons_symb = [x.symbol for x in problem.constraints]
91
+
92
+ def update(self, message: Message) -> None:
93
+ """Update the archiver with the new data.
94
+
95
+ Args:
96
+ message (Message): Message from the publisher.
97
+ """
98
+ if (
99
+ message.topic == TerminatorMessageTopics.GENERATION # NOQA: PLR1714
100
+ or message.topic == SelectorMessageTopics.SELECTED_VERBOSE_OUTPUTS
101
+ ):
102
+ super().update(message)
103
+ return
104
+ data = message.value
105
+ feasible_mask = (data[self.cons_symb] <= 0).to_numpy().all(axis=1)
106
+ feasible_data = data.filter(feasible_mask)
107
+ feasible_data = data.with_columns(generation=self.generation_number)
108
+ if self.solutions is None:
109
+ self.solutions = feasible_data
110
+ else:
111
+ self.solutions = pl.concat([self.solutions, feasible_data])
112
+
113
+
114
+ class Archive(BaseArchive):
115
+ """An archiver that stores the solutions evaluated during evolution."""
116
+
117
+ def update(self, message: Message) -> None:
118
+ """Update the archiver with the new data.
119
+
120
+ Args:
121
+ message (Message): Message from the publisher.
122
+ """
123
+ if (
124
+ message.topic == TerminatorMessageTopics.GENERATION # NOQA: PLR1714
125
+ or message.topic == SelectorMessageTopics.SELECTED_VERBOSE_OUTPUTS
126
+ ):
127
+ super().update(message)
128
+ return
129
+ data = message.value
130
+ data = data.with_columns(generation=self.generation_number)
131
+ if self.solutions is None:
132
+ self.solutions = data
133
+ else:
134
+ self.solutions = pl.concat([self.solutions, data])
135
+
136
+
137
+ class NonDominatedArchive(Archive):
138
+ """An archiver that stores only the non-dominated solutions evaluated during evolution."""
139
+
140
+ def __init__(self, *, problem: Problem, publisher: Publisher):
141
+ """Initialize the archiver.
142
+
143
+ Args:
144
+ problem (Problem): The problem being solved.
145
+ publisher (Publisher): The publisher object.
146
+ """
147
+ super().__init__(problem=problem, publisher=publisher)
148
+ self.targets = [f"{x.symbol}_min" for x in problem.objectives]
149
+
150
+ def update(self, message: Message) -> None:
151
+ """Update the archiver with the new data.
152
+
153
+ Args:
154
+ message (Message): Message from the publisher.
155
+ """
156
+ if (
157
+ message.topic == TerminatorMessageTopics.GENERATION # NOQA: PLR1714
158
+ or message.topic == SelectorMessageTopics.SELECTED_VERBOSE_OUTPUTS
159
+ ):
160
+ super().update(message)
161
+ return
162
+ data = message.value
163
+ data = data.with_columns(generation=self.generation_number)
164
+ if type(data) is not pl.DataFrame:
165
+ raise ValueError("Data should be a polars DataFrame")
166
+ if self.solutions is None:
167
+ non_dom_mask = non_dominated(data[self.targets].to_numpy())
168
+ self.solutions = data.filter(non_dom_mask)
169
+ else:
170
+ to_add = data.filter(non_dominated(data[self.targets].to_numpy()))
171
+ mask1, mask2 = non_dominated_merge(self.solutions[self.targets].to_numpy(), to_add[self.targets].to_numpy())
172
+ self.solutions = pl.concat([self.solutions.filter(mask1), to_add.filter(mask2)])
@@ -0,0 +1,418 @@
1
+ """Implements common evolutionary algorithms for multi-objective optimization."""
2
+
3
+ from collections.abc import Callable
4
+ from functools import partial
5
+
6
+ from desdeo.emo.methods.bases import EMOResult, template1
7
+ from desdeo.emo.operators.crossover import SimulatedBinaryCrossover, UniformMixedIntegerCrossover
8
+ from desdeo.emo.operators.evaluator import EMOEvaluator
9
+ from desdeo.emo.operators.generator import LHSGenerator, RandomMixedIntegerGenerator
10
+ from desdeo.emo.operators.mutation import BoundedPolynomialMutation, MixedIntegerRandomMutation
11
+ from desdeo.emo.operators.selection import NSGAIII_select, ReferenceVectorOptions, RVEASelector
12
+ from desdeo.emo.operators.termination import MaxEvaluationsTerminator, MaxGenerationsTerminator
13
+ from desdeo.problem import Problem
14
+ from desdeo.tools.patterns import Publisher
15
+
16
+
17
+ def rvea(
18
+ *,
19
+ problem: Problem,
20
+ seed: int = 0,
21
+ n_generations=100,
22
+ max_evaluations: int | None = None,
23
+ reference_vector_options: ReferenceVectorOptions = None,
24
+ forced_verbosity: int | None = None,
25
+ ) -> tuple[Callable[[], EMOResult], Publisher]:
26
+ """Implements the Reference Vector Guided Evolutionary Algorithm (RVEA), as well as its interactive version.
27
+
28
+ References:
29
+ R. Cheng, Y. Jin, M. Olhofer and B. Sendhoff, "A Reference Vector Guided Evolutionary Algorithm for Many-
30
+ Objective Optimization," in IEEE Transactions on Evolutionary Computation, vol. 20, no. 5, pp. 773-791,
31
+ Oct. 2016, doi: 10.1109/TEVC.2016.2519378.
32
+
33
+ J. Hakanen, T. Chugh, K. Sindhya, Y. Jin, and K. Miettinen, “Connections of reference vectors and different
34
+ types of preference information in interactive multiobjective evolutionary algorithms,” in 2016 IEEE Symposium
35
+ Series on Computational Intelligence (SSCI), Athens, Greece: IEEE, Dec. 2016, pp. 1-8.
36
+
37
+
38
+ Args:
39
+ problem (Problem): The problem to be solved.
40
+ seed (int, optional): The seed for the random number generator. Defaults to 0.
41
+ n_generations (int, optional): The number of generations to run the algorithm. Defaults to 100.
42
+ max_evaluations (int, optional): The maximum number of evaluations to run the algorithm. If None, the algorithm
43
+ will run for n_generations. Defaults to None. If both n_generations and max_evaluations are provided, the
44
+ algorithm will run until max_evaluations is reached.
45
+ reference_vector_options (ReferenceVectorOptions, optional): The options for the reference vectors. Defaults to
46
+ None. See the ReferenceVectorOptions class for the defaults. This option can be used to run an interactive
47
+ version of the algorithm, using preferences provided by the user.
48
+ forced_verbosity (int, optional): If not None, the verbosity of the algorithm is forced to this value. Defaults
49
+ to None.
50
+
51
+ Returns:
52
+ tuple[Callable[[], EMOResult], Publisher]: A tuple containing the function to run the algorithm and the
53
+ publisher object. The publisher object can be used to subscribe to the topics of the algorithm components,
54
+ as well as to inject additional functionality such as archiving the results.
55
+ """
56
+ publisher = Publisher()
57
+ evaluator = EMOEvaluator(
58
+ problem=problem,
59
+ publisher=publisher,
60
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
61
+ )
62
+
63
+ selector = RVEASelector(
64
+ problem=problem,
65
+ publisher=publisher,
66
+ reference_vector_options=reference_vector_options,
67
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
68
+ )
69
+
70
+ # Note that the initial population size is equal to the number of reference vectors
71
+ n_points = selector.reference_vectors.shape[0]
72
+
73
+ generator = LHSGenerator(
74
+ problem=problem,
75
+ evaluator=evaluator,
76
+ publisher=publisher,
77
+ n_points=n_points,
78
+ seed=seed,
79
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
80
+ )
81
+ crossover = SimulatedBinaryCrossover(
82
+ problem=problem,
83
+ publisher=publisher,
84
+ seed=seed,
85
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
86
+ )
87
+ mutation = BoundedPolynomialMutation(
88
+ problem=problem,
89
+ publisher=publisher,
90
+ seed=seed,
91
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
92
+ )
93
+
94
+ if max_evaluations is not None:
95
+ terminator = MaxEvaluationsTerminator(max_evaluations, publisher=publisher)
96
+ else:
97
+ terminator = MaxGenerationsTerminator(n_generations, publisher=publisher)
98
+
99
+ components = [evaluator, generator, crossover, mutation, selector, terminator]
100
+ [publisher.auto_subscribe(x) for x in components]
101
+ [publisher.register_topics(x.provided_topics[x.verbosity], x.__class__.__name__) for x in components]
102
+
103
+ return (
104
+ partial(
105
+ template1,
106
+ evaluator=evaluator,
107
+ crossover=crossover,
108
+ mutation=mutation,
109
+ generator=generator,
110
+ selection=selector,
111
+ terminator=terminator,
112
+ ),
113
+ publisher,
114
+ )
115
+
116
+
117
+ def nsga3(
118
+ *,
119
+ problem: Problem,
120
+ seed: int = 0,
121
+ n_generations: int = 100,
122
+ max_evaluations: int | None = None,
123
+ reference_vector_options: ReferenceVectorOptions = None,
124
+ forced_verbosity: int | None = None,
125
+ ) -> tuple[Callable[[], EMOResult], Publisher]:
126
+ """Implements the NSGA-III algorithm as well as its interactive version.
127
+
128
+ References:
129
+ K. Deb and H. Jain, “An Evolutionary Many-Objective Optimization Algorithm Using Reference-Point-Based
130
+ Nondominated Sorting Approach, Part I: Solving Problems With Box Constraints,” IEEE Transactions on Evolutionary
131
+ Computation, vol. 18, no. 4, pp. 577-601, Aug. 2014.
132
+
133
+ J. Hakanen, T. Chugh, K. Sindhya, Y. Jin, and K. Miettinen, “Connections of reference vectors and different
134
+ types of preference information in interactive multiobjective evolutionary algorithms,” in 2016 IEEE Symposium
135
+ Series on Computational Intelligence (SSCI), Athens, Greece: IEEE, Dec. 2016, pp. 1-8.
136
+
137
+ Args:
138
+ problem (Problem): The problem to be solved.
139
+ seed (int, optional): The seed for the random number generator. Defaults to 0.
140
+ n_generations (int, optional): The number of generations to run the algorithm. Defaults to 100.
141
+ max_evaluations (int, optional): The maximum number of evaluations to run the algorithm. If None, the algorithm
142
+ will run for n_generations. Defaults to None. If both n_generations and max_evaluations are provided, the
143
+ algorithm will run until max_evaluations is reached.
144
+ reference_vector_options (ReferenceVectorOptions, optional): The options for the reference vectors. Defaults to
145
+ None. See the ReferenceVectorOptions class for the defaults. This option can be used to run an interactive
146
+ version of the algorithm, using preferences provided by the user.
147
+ forced_verbosity (int, optional): If not None, the verbosity of the algorithm is forced to this value. Defaults
148
+ to None.
149
+
150
+ Returns:
151
+ tuple[Callable[[], EMOResult], Publisher]: A tuple containing the function to run the algorithm and the
152
+ publisher object. The publisher object can be used to subscribe to the topics of the algorithm components,
153
+ as well as to inject additional functionality such as archiving the results.
154
+ """
155
+ publisher = Publisher()
156
+ evaluator = EMOEvaluator(
157
+ problem=problem,
158
+ publisher=publisher,
159
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
160
+ )
161
+
162
+ selector = NSGAIII_select(
163
+ problem=problem,
164
+ publisher=publisher,
165
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
166
+ reference_vector_options=reference_vector_options,
167
+ )
168
+
169
+ # Note that the initial population size is equal to the number of reference vectors
170
+ n_points = selector.reference_vectors.shape[0]
171
+
172
+ generator = LHSGenerator(
173
+ problem=problem,
174
+ evaluator=evaluator,
175
+ publisher=publisher,
176
+ n_points=n_points,
177
+ seed=seed,
178
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
179
+ )
180
+ crossover = SimulatedBinaryCrossover(
181
+ problem=problem,
182
+ publisher=publisher,
183
+ seed=seed,
184
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
185
+ )
186
+ mutation = BoundedPolynomialMutation(
187
+ problem=problem,
188
+ publisher=publisher,
189
+ seed=seed,
190
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
191
+ )
192
+
193
+ if max_evaluations is not None:
194
+ terminator = MaxEvaluationsTerminator(max_evaluations, publisher=publisher)
195
+ else:
196
+ terminator = MaxGenerationsTerminator(
197
+ n_generations,
198
+ publisher=publisher,
199
+ )
200
+
201
+ components = [evaluator, generator, crossover, mutation, selector, terminator]
202
+ [publisher.auto_subscribe(x) for x in components]
203
+ [publisher.register_topics(x.provided_topics[x.verbosity], x.__class__.__name__) for x in components]
204
+
205
+ return (
206
+ partial(
207
+ template1,
208
+ evaluator=evaluator,
209
+ crossover=crossover,
210
+ mutation=mutation,
211
+ generator=generator,
212
+ selection=selector,
213
+ terminator=terminator,
214
+ ),
215
+ publisher,
216
+ )
217
+
218
+
219
+ def nsga3_mixed_integer(
220
+ *,
221
+ problem: Problem,
222
+ seed: int = 0,
223
+ n_generations: int = 100,
224
+ max_evaluations: int | None = None,
225
+ reference_vector_options: ReferenceVectorOptions = None,
226
+ forced_verbosity: int | None = None,
227
+ ) -> tuple[Callable[[], EMOResult], Publisher]:
228
+ """Implements the NSGA-III algorithm as well as its interactive version for mixed-integer problems.
229
+
230
+ References:
231
+ K. Deb and H. Jain, “An Evolutionary Many-Objective Optimization Algorithm Using Reference-Point-Based
232
+ Nondominated Sorting Approach, Part I: Solving Problems With Box Constraints,” IEEE Transactions on Evolutionary
233
+ Computation, vol. 18, no. 4, pp. 577-601, Aug. 2014.
234
+
235
+ J. Hakanen, T. Chugh, K. Sindhya, Y. Jin, and K. Miettinen, “Connections of reference vectors and different
236
+ types of preference information in interactive multiobjective evolutionary algorithms,” in 2016 IEEE Symposium
237
+ Series on Computational Intelligence (SSCI), Athens, Greece: IEEE, Dec. 2016, pp. 1-8.
238
+
239
+ Args:
240
+ problem (Problem): The problem to be solved.
241
+ seed (int, optional): The seed for the random number generator. Defaults to 0.
242
+ n_generations (int, optional): The number of generations to run the algorithm. Defaults to 100.
243
+ max_evaluations (int, optional): The maximum number of evaluations to run the algorithm. If None, the algorithm
244
+ will run for n_generations. Defaults to None. If both n_generations and max_evaluations are provided, the
245
+ algorithm will run until max_evaluations is reached.
246
+ reference_vector_options (ReferenceVectorOptions, optional): The options for the reference vectors. Defaults to
247
+ None. See the ReferenceVectorOptions class for the defaults. This option can be used to run an interactive
248
+ version of the algorithm, using preferences provided by the user.
249
+ forced_verbosity (int, optional): If not None, the verbosity of the algorithm is forced to this value. Defaults
250
+ to None.
251
+
252
+ Returns:
253
+ tuple[Callable[[], EMOResult], Publisher]: A tuple containing the function to run the algorithm and the
254
+ publisher object. The publisher object can be used to subscribe to the topics of the algorithm components,
255
+ as well as to inject additional functionality such as archiving the results.
256
+ """
257
+ publisher = Publisher()
258
+ evaluator = EMOEvaluator(
259
+ problem=problem,
260
+ publisher=publisher,
261
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
262
+ )
263
+
264
+ selector = NSGAIII_select(
265
+ problem=problem,
266
+ publisher=publisher,
267
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
268
+ reference_vector_options=reference_vector_options,
269
+ )
270
+
271
+ # Note that the initial population size is equal to the number of reference vectors
272
+ n_points = selector.reference_vectors.shape[0]
273
+
274
+ generator = RandomMixedIntegerGenerator(
275
+ problem=problem,
276
+ evaluator=evaluator,
277
+ publisher=publisher,
278
+ n_points=n_points,
279
+ seed=seed,
280
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
281
+ )
282
+ crossover = UniformMixedIntegerCrossover(
283
+ problem=problem,
284
+ publisher=publisher,
285
+ seed=seed,
286
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
287
+ )
288
+ mutation = MixedIntegerRandomMutation(
289
+ problem=problem,
290
+ publisher=publisher,
291
+ seed=seed,
292
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
293
+ )
294
+
295
+ if max_evaluations is not None:
296
+ terminator = MaxEvaluationsTerminator(max_evaluations, publisher=publisher)
297
+ else:
298
+ terminator = MaxGenerationsTerminator(
299
+ n_generations,
300
+ publisher=publisher,
301
+ )
302
+
303
+ components = [evaluator, generator, crossover, mutation, selector, terminator]
304
+ [publisher.auto_subscribe(x) for x in components]
305
+ [publisher.register_topics(x.provided_topics[x.verbosity], x.__class__.__name__) for x in components]
306
+
307
+ return (
308
+ partial(
309
+ template1,
310
+ evaluator=evaluator,
311
+ crossover=crossover,
312
+ mutation=mutation,
313
+ generator=generator,
314
+ selection=selector,
315
+ terminator=terminator,
316
+ ),
317
+ publisher,
318
+ )
319
+
320
+
321
+ def rvea_mixed_integer(
322
+ *,
323
+ problem: Problem,
324
+ seed: int = 0,
325
+ n_generations=100,
326
+ max_evaluations: int | None = None,
327
+ reference_vector_options: ReferenceVectorOptions = None,
328
+ forced_verbosity: int | None = None,
329
+ ) -> tuple[Callable[[], EMOResult], Publisher]:
330
+ """Implements the mixed-integer variant of RVEA, as well as its interactive version.
331
+
332
+ References:
333
+ R. Cheng, Y. Jin, M. Olhofer and B. Sendhoff, "A Reference Vector Guided Evolutionary Algorithm for Many-
334
+ Objective Optimization," in IEEE Transactions on Evolutionary Computation, vol. 20, no. 5, pp. 773-791,
335
+ Oct. 2016, doi: 10.1109/TEVC.2016.2519378.
336
+
337
+ J. Hakanen, T. Chugh, K. Sindhya, Y. Jin, and K. Miettinen, “Connections of reference vectors and different
338
+ types of preference information in interactive multiobjective evolutionary algorithms,” in 2016 IEEE Symposium
339
+ Series on Computational Intelligence (SSCI), Athens, Greece: IEEE, Dec. 2016, pp. 1-8.
340
+
341
+
342
+ Args:
343
+ problem (Problem): The problem to be solved.
344
+ seed (int, optional): The seed for the random number generator. Defaults to 0.
345
+ n_generations (int, optional): The number of generations to run the algorithm. Defaults to 100.
346
+ max_evaluations (int, optional): The maximum number of evaluations to run the algorithm. If None, the algorithm
347
+ will run for n_generations. Defaults to None. If both n_generations and max_evaluations are provided, the
348
+ algorithm will run until max_evaluations is reached.
349
+ reference_vector_options (ReferenceVectorOptions, optional): The options for the reference vectors. Defaults to
350
+ None. See the ReferenceVectorOptions class for the defaults. This option can be used to run an interactive
351
+ version of the algorithm, using preferences provided by the user.
352
+ forced_verbosity (int, optional): If not None, the verbosity of the algorithm is forced to this value. Defaults
353
+ to None.
354
+
355
+ Returns:
356
+ tuple[Callable[[], EMOResult], Publisher]: A tuple containing the function to run the algorithm and the
357
+ publisher object. The publisher object can be used to subscribe to the topics of the algorithm components,
358
+ as well as to inject additional functionality such as archiving the results.
359
+ """
360
+ publisher = Publisher()
361
+ evaluator = EMOEvaluator(
362
+ problem=problem,
363
+ publisher=publisher,
364
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
365
+ )
366
+
367
+ selector = RVEASelector(
368
+ problem=problem,
369
+ publisher=publisher,
370
+ reference_vector_options=reference_vector_options,
371
+ verbosity=forced_verbosity if forced_verbosity is not None else 2,
372
+ )
373
+
374
+ # Note that the initial population size is equal to the number of reference vectors
375
+ n_points = selector.reference_vectors.shape[0]
376
+
377
+ generator = RandomMixedIntegerGenerator(
378
+ problem=problem,
379
+ evaluator=evaluator,
380
+ publisher=publisher,
381
+ n_points=n_points,
382
+ seed=seed,
383
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
384
+ )
385
+ crossover = UniformMixedIntegerCrossover(
386
+ problem=problem,
387
+ publisher=publisher,
388
+ seed=seed,
389
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
390
+ )
391
+ mutation = MixedIntegerRandomMutation(
392
+ problem=problem,
393
+ publisher=publisher,
394
+ seed=seed,
395
+ verbosity=forced_verbosity if forced_verbosity is not None else 1,
396
+ )
397
+
398
+ if max_evaluations is not None:
399
+ terminator = MaxEvaluationsTerminator(max_evaluations, publisher=publisher)
400
+ else:
401
+ terminator = MaxGenerationsTerminator(n_generations, publisher=publisher)
402
+
403
+ components = [evaluator, generator, crossover, mutation, selector, terminator]
404
+ [publisher.auto_subscribe(x) for x in components]
405
+ [publisher.register_topics(x.provided_topics[x.verbosity], x.__class__.__name__) for x in components]
406
+
407
+ return (
408
+ partial(
409
+ template1,
410
+ evaluator=evaluator,
411
+ crossover=crossover,
412
+ mutation=mutation,
413
+ generator=generator,
414
+ selection=selector,
415
+ terminator=terminator,
416
+ ),
417
+ publisher,
418
+ )
File without changes
@@ -0,0 +1,59 @@
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
+ import numpy as np
7
+ import polars as pl
8
+ from pydantic import BaseModel, ConfigDict, Field
9
+
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)
@@ -0,0 +1 @@
1
+