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.
- desdeo/__init__.py +8 -8
- desdeo/api/README.md +73 -0
- desdeo/api/__init__.py +15 -0
- desdeo/api/app.py +40 -0
- desdeo/api/config.py +69 -0
- desdeo/api/config.toml +53 -0
- desdeo/api/db.py +25 -0
- desdeo/api/db_init.py +79 -0
- desdeo/api/db_models.py +164 -0
- desdeo/api/malaga_db_init.py +27 -0
- desdeo/api/models/__init__.py +66 -0
- desdeo/api/models/archive.py +34 -0
- desdeo/api/models/preference.py +90 -0
- desdeo/api/models/problem.py +507 -0
- desdeo/api/models/reference_point_method.py +18 -0
- desdeo/api/models/session.py +46 -0
- desdeo/api/models/state.py +96 -0
- desdeo/api/models/user.py +51 -0
- desdeo/api/routers/_NAUTILUS.py +245 -0
- desdeo/api/routers/_NAUTILUS_navigator.py +233 -0
- desdeo/api/routers/_NIMBUS.py +762 -0
- desdeo/api/routers/__init__.py +5 -0
- desdeo/api/routers/problem.py +110 -0
- desdeo/api/routers/reference_point_method.py +117 -0
- desdeo/api/routers/session.py +76 -0
- desdeo/api/routers/test.py +16 -0
- desdeo/api/routers/user_authentication.py +366 -0
- desdeo/api/schema.py +94 -0
- desdeo/api/tests/__init__.py +0 -0
- desdeo/api/tests/conftest.py +59 -0
- desdeo/api/tests/test_models.py +701 -0
- desdeo/api/tests/test_routes.py +216 -0
- desdeo/api/utils/database.py +274 -0
- desdeo/api/utils/logger.py +29 -0
- desdeo/core.py +27 -0
- desdeo/emo/__init__.py +29 -0
- desdeo/emo/hooks/archivers.py +172 -0
- desdeo/emo/methods/EAs.py +418 -0
- desdeo/emo/methods/__init__.py +0 -0
- desdeo/emo/methods/bases.py +59 -0
- desdeo/emo/operators/__init__.py +1 -0
- desdeo/emo/operators/crossover.py +780 -0
- desdeo/emo/operators/evaluator.py +118 -0
- desdeo/emo/operators/generator.py +356 -0
- desdeo/emo/operators/mutation.py +1053 -0
- desdeo/emo/operators/selection.py +1036 -0
- desdeo/emo/operators/termination.py +178 -0
- desdeo/explanations/__init__.py +6 -0
- desdeo/explanations/explainer.py +100 -0
- desdeo/explanations/utils.py +90 -0
- desdeo/mcdm/__init__.py +19 -0
- desdeo/mcdm/nautili.py +345 -0
- desdeo/mcdm/nautilus.py +477 -0
- desdeo/mcdm/nautilus_navigator.py +655 -0
- desdeo/mcdm/nimbus.py +417 -0
- desdeo/mcdm/pareto_navigator.py +269 -0
- desdeo/mcdm/reference_point_method.py +116 -0
- desdeo/problem/__init__.py +79 -0
- desdeo/problem/evaluator.py +561 -0
- desdeo/problem/gurobipy_evaluator.py +562 -0
- desdeo/problem/infix_parser.py +341 -0
- desdeo/problem/json_parser.py +944 -0
- desdeo/problem/pyomo_evaluator.py +468 -0
- desdeo/problem/schema.py +1808 -0
- desdeo/problem/simulator_evaluator.py +298 -0
- desdeo/problem/sympy_evaluator.py +244 -0
- desdeo/problem/testproblems/__init__.py +73 -0
- desdeo/problem/testproblems/binh_and_korn_problem.py +88 -0
- desdeo/problem/testproblems/dtlz2_problem.py +102 -0
- desdeo/problem/testproblems/forest_problem.py +275 -0
- desdeo/problem/testproblems/knapsack_problem.py +163 -0
- desdeo/problem/testproblems/mcwb_problem.py +831 -0
- desdeo/problem/testproblems/mixed_variable_dimenrions_problem.py +83 -0
- desdeo/problem/testproblems/momip_problem.py +172 -0
- desdeo/problem/testproblems/nimbus_problem.py +143 -0
- desdeo/problem/testproblems/pareto_navigator_problem.py +89 -0
- desdeo/problem/testproblems/re_problem.py +492 -0
- desdeo/problem/testproblems/river_pollution_problem.py +434 -0
- desdeo/problem/testproblems/rocket_injector_design_problem.py +140 -0
- desdeo/problem/testproblems/simple_problem.py +351 -0
- desdeo/problem/testproblems/simulator_problem.py +92 -0
- desdeo/problem/testproblems/spanish_sustainability_problem.py +945 -0
- desdeo/problem/testproblems/zdt_problem.py +271 -0
- desdeo/problem/utils.py +245 -0
- desdeo/tools/GenerateReferencePoints.py +181 -0
- desdeo/tools/__init__.py +102 -0
- desdeo/tools/generics.py +145 -0
- desdeo/tools/gurobipy_solver_interfaces.py +258 -0
- desdeo/tools/indicators_binary.py +11 -0
- desdeo/tools/indicators_unary.py +375 -0
- desdeo/tools/interaction_schema.py +38 -0
- desdeo/tools/intersection.py +54 -0
- desdeo/tools/iterative_pareto_representer.py +99 -0
- desdeo/tools/message.py +234 -0
- desdeo/tools/ng_solver_interfaces.py +199 -0
- desdeo/tools/non_dominated_sorting.py +133 -0
- desdeo/tools/patterns.py +281 -0
- desdeo/tools/proximal_solver.py +99 -0
- desdeo/tools/pyomo_solver_interfaces.py +464 -0
- desdeo/tools/reference_vectors.py +462 -0
- desdeo/tools/scalarization.py +3138 -0
- desdeo/tools/scipy_solver_interfaces.py +454 -0
- desdeo/tools/score_bands.py +464 -0
- desdeo/tools/utils.py +320 -0
- desdeo/utopia_stuff/__init__.py +0 -0
- desdeo/utopia_stuff/data/1.json +15 -0
- desdeo/utopia_stuff/data/2.json +13 -0
- desdeo/utopia_stuff/data/3.json +15 -0
- desdeo/utopia_stuff/data/4.json +17 -0
- desdeo/utopia_stuff/data/5.json +15 -0
- desdeo/utopia_stuff/from_json.py +40 -0
- desdeo/utopia_stuff/reinit_user.py +38 -0
- desdeo/utopia_stuff/utopia_db_init.py +212 -0
- desdeo/utopia_stuff/utopia_problem.py +403 -0
- desdeo/utopia_stuff/utopia_problem_old.py +415 -0
- desdeo/utopia_stuff/utopia_reference_solutions.py +79 -0
- desdeo-2.0.0.dist-info/LICENSE +21 -0
- desdeo-2.0.0.dist-info/METADATA +168 -0
- desdeo-2.0.0.dist-info/RECORD +120 -0
- {desdeo-1.2.dist-info → desdeo-2.0.0.dist-info}/WHEEL +1 -1
- desdeo-1.2.dist-info/METADATA +0 -16
- 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
|
+
|