desdeo 1.1.3__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.1.3.dist-info → desdeo-2.0.0.dist-info}/WHEEL +1 -1
- desdeo-1.1.3.dist-info/METADATA +0 -18
- desdeo-1.1.3.dist-info/RECORD +0 -4
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"""Classes for evaluating the objectives and constraints of the individuals in the population."""
|
|
2
|
+
|
|
3
|
+
import warnings
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
|
|
6
|
+
import polars as pl
|
|
7
|
+
|
|
8
|
+
from desdeo.problem import Evaluator, Problem
|
|
9
|
+
from desdeo.tools.message import (
|
|
10
|
+
EvaluatorMessageTopics,
|
|
11
|
+
GenericMessage,
|
|
12
|
+
IntMessage,
|
|
13
|
+
Message,
|
|
14
|
+
PolarsDataFrameMessage,
|
|
15
|
+
)
|
|
16
|
+
from desdeo.tools.patterns import Subscriber
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class EMOEvaluator(Subscriber):
|
|
20
|
+
"""Base class for evaluating the objectives and constraints of the individuals in the population.
|
|
21
|
+
|
|
22
|
+
This class should be inherited by the classes that implement the evaluation of the objectives
|
|
23
|
+
and constraints of the individuals in the population.
|
|
24
|
+
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
@property
|
|
28
|
+
def provided_topics(self) -> dict[int, Sequence[EvaluatorMessageTopics]]:
|
|
29
|
+
"""The topics provided by the Evaluator."""
|
|
30
|
+
return {
|
|
31
|
+
0: [],
|
|
32
|
+
1: [EvaluatorMessageTopics.NEW_EVALUATIONS],
|
|
33
|
+
2: [
|
|
34
|
+
EvaluatorMessageTopics.NEW_EVALUATIONS,
|
|
35
|
+
EvaluatorMessageTopics.VERBOSE_OUTPUTS,
|
|
36
|
+
],
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def interested_topics(self):
|
|
41
|
+
"""The topics that the Evaluator is interested in."""
|
|
42
|
+
return []
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
problem: Problem,
|
|
47
|
+
verbosity: int = 1,
|
|
48
|
+
**kwargs,
|
|
49
|
+
):
|
|
50
|
+
"""Initialize the EMOEvaluator class."""
|
|
51
|
+
super().__init__(**kwargs)
|
|
52
|
+
self.problem = problem
|
|
53
|
+
# TODO(@light-weaver, @gialmisi): This can be so much more efficient.
|
|
54
|
+
self.evaluator = lambda x: Evaluator(problem).evaluate(
|
|
55
|
+
{name.symbol: x[name.symbol].to_list() for name in problem.get_flattened_variables()}, flat=True
|
|
56
|
+
)
|
|
57
|
+
self.variable_symbols = [name.symbol for name in problem.variables]
|
|
58
|
+
self.population: pl.DataFrame
|
|
59
|
+
self.outs: pl.DataFrame
|
|
60
|
+
self.verbosity: int = verbosity
|
|
61
|
+
self.new_evals: int = 0
|
|
62
|
+
|
|
63
|
+
def evaluate(self, population: pl.DataFrame) -> pl.DataFrame:
|
|
64
|
+
"""Evaluate and return the objectives.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
population (pl.Dataframe): The set of decision variables to evaluate.
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
pl.Dataframe: A dataframe of objective vectors, target vectors, and constraint vectors.
|
|
71
|
+
"""
|
|
72
|
+
self.population = population
|
|
73
|
+
out = self.evaluator(population)
|
|
74
|
+
# remove variable_symbols from the output
|
|
75
|
+
self.out = out.drop(self.variable_symbols)
|
|
76
|
+
self.new_evals = len(population)
|
|
77
|
+
# merge the objectives and targets
|
|
78
|
+
|
|
79
|
+
self.notify()
|
|
80
|
+
return self.out
|
|
81
|
+
|
|
82
|
+
def state(self) -> Sequence[Message]:
|
|
83
|
+
"""The state of the evaluator sent to the Publisher."""
|
|
84
|
+
if self.population is None or self.out is None or self.population is None or self.verbosity == 0:
|
|
85
|
+
return []
|
|
86
|
+
if self.verbosity == 1:
|
|
87
|
+
return [
|
|
88
|
+
IntMessage(
|
|
89
|
+
topic=EvaluatorMessageTopics.NEW_EVALUATIONS,
|
|
90
|
+
value=self.new_evals,
|
|
91
|
+
source=self.__class__.__name__,
|
|
92
|
+
)
|
|
93
|
+
]
|
|
94
|
+
|
|
95
|
+
if isinstance(self.population, pl.DataFrame):
|
|
96
|
+
message = PolarsDataFrameMessage(
|
|
97
|
+
topic=EvaluatorMessageTopics.VERBOSE_OUTPUTS,
|
|
98
|
+
value=pl.concat([self.population, self.out], how="horizontal"),
|
|
99
|
+
source=self.__class__.__name__,
|
|
100
|
+
)
|
|
101
|
+
else:
|
|
102
|
+
warnings.warn("Population is not a Polars DataFrame. Defaulting to providing OUTPUTS only.", stacklevel=2)
|
|
103
|
+
message = PolarsDataFrameMessage(
|
|
104
|
+
topic=EvaluatorMessageTopics.VERBOSE_OUTPUTS,
|
|
105
|
+
value=self.out,
|
|
106
|
+
source=self.__class__.__name__,
|
|
107
|
+
)
|
|
108
|
+
return [
|
|
109
|
+
IntMessage(
|
|
110
|
+
topic=EvaluatorMessageTopics.NEW_EVALUATIONS,
|
|
111
|
+
value=self.new_evals,
|
|
112
|
+
source=self.__class__.__name__,
|
|
113
|
+
),
|
|
114
|
+
message,
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
def update(self, *_, **__):
|
|
118
|
+
"""Update the parameters of the evaluator."""
|
|
@@ -0,0 +1,356 @@
|
|
|
1
|
+
"""Class for generating initial population for the evolutionary optimization algorithms."""
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
|
|
6
|
+
import numpy as np
|
|
7
|
+
import polars as pl
|
|
8
|
+
from scipy.stats.qmc import LatinHypercube
|
|
9
|
+
|
|
10
|
+
from desdeo.emo.operators.evaluator import EMOEvaluator
|
|
11
|
+
from desdeo.problem import Problem, VariableTypeEnum
|
|
12
|
+
from desdeo.tools.message import GeneratorMessageTopics, IntMessage, Message, PolarsDataFrameMessage
|
|
13
|
+
from desdeo.tools.patterns import Subscriber
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseGenerator(Subscriber):
|
|
17
|
+
"""Base class for generating initial population for the evolutionary optimization algorithms.
|
|
18
|
+
|
|
19
|
+
This class should be inherited by the classes that implement the initial population generation
|
|
20
|
+
for the evolutionary optimization algorithms.
|
|
21
|
+
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
@property
|
|
25
|
+
def provided_topics(self) -> dict[int, Sequence[GeneratorMessageTopics]]:
|
|
26
|
+
"""Return the topics provided by the generator.
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
dict[int, Sequence[GeneratorMessageTopics]]: The topics provided by the generator.
|
|
30
|
+
"""
|
|
31
|
+
return {
|
|
32
|
+
0: [],
|
|
33
|
+
1: [GeneratorMessageTopics.NEW_EVALUATIONS],
|
|
34
|
+
2: [
|
|
35
|
+
GeneratorMessageTopics.NEW_EVALUATIONS,
|
|
36
|
+
GeneratorMessageTopics.VERBOSE_OUTPUTS,
|
|
37
|
+
],
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def interested_topics(self):
|
|
42
|
+
"""Return the message topics that the generator is interested in."""
|
|
43
|
+
return []
|
|
44
|
+
|
|
45
|
+
def __init__(self, problem: Problem, **kwargs):
|
|
46
|
+
"""Initialize the BaseGenerator class."""
|
|
47
|
+
super().__init__(**kwargs)
|
|
48
|
+
self.problem = problem
|
|
49
|
+
self.variable_symbols = [var.symbol for var in problem.get_flattened_variables()]
|
|
50
|
+
self.bounds = np.array([[var.lowerbound, var.upperbound] for var in problem.get_flattened_variables()])
|
|
51
|
+
self.population: pl.DataFrame = None
|
|
52
|
+
self.out: pl.DataFrame = None
|
|
53
|
+
|
|
54
|
+
@abstractmethod
|
|
55
|
+
def do(self) -> tuple[pl.DataFrame, pl.DataFrame]:
|
|
56
|
+
"""Generate the initial population.
|
|
57
|
+
|
|
58
|
+
This method should be implemented by the inherited classes.
|
|
59
|
+
|
|
60
|
+
Returns:
|
|
61
|
+
tuple[pl.DataFrame, pl.DataFrame]: The initial population as the first element,
|
|
62
|
+
the corresponding objectives, the constraint violations, and the targets as the
|
|
63
|
+
second element.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def state(self) -> Sequence[Message]:
|
|
67
|
+
"""Return the state of the generator.
|
|
68
|
+
|
|
69
|
+
This method should be implemented by the inherited classes.
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
dict: The state of the generator.
|
|
73
|
+
"""
|
|
74
|
+
if self.population is None or self.out is None or self.verbosity == 0:
|
|
75
|
+
return []
|
|
76
|
+
if self.verbosity == 1:
|
|
77
|
+
return [
|
|
78
|
+
IntMessage(
|
|
79
|
+
topic=GeneratorMessageTopics.NEW_EVALUATIONS,
|
|
80
|
+
value=self.population.shape[0],
|
|
81
|
+
source=self.__class__.__name__,
|
|
82
|
+
),
|
|
83
|
+
]
|
|
84
|
+
# verbosity == 2
|
|
85
|
+
return [
|
|
86
|
+
PolarsDataFrameMessage(
|
|
87
|
+
topic=GeneratorMessageTopics.VERBOSE_OUTPUTS,
|
|
88
|
+
value=pl.concat([self.population, self.out], how="horizontal"),
|
|
89
|
+
source=self.__class__.__name__,
|
|
90
|
+
),
|
|
91
|
+
IntMessage(
|
|
92
|
+
topic=GeneratorMessageTopics.NEW_EVALUATIONS,
|
|
93
|
+
value=self.population.shape[0],
|
|
94
|
+
source=self.__class__.__name__,
|
|
95
|
+
),
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class RandomGenerator(BaseGenerator):
|
|
100
|
+
"""Class for generating random initial population for the evolutionary optimization algorithms.
|
|
101
|
+
|
|
102
|
+
This class generates the initial population by randomly sampling the points from the variable bounds. The
|
|
103
|
+
distribution of the points is uniform. If the seed is not provided, the seed is set to 0.
|
|
104
|
+
"""
|
|
105
|
+
|
|
106
|
+
def __init__(self, problem: Problem, evaluator: EMOEvaluator, n_points: int, seed: int, **kwargs):
|
|
107
|
+
"""Initialize the RandomGenerator class.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
problem (Problem): The problem to solve.
|
|
111
|
+
evaluator (BaseEvaluator): The evaluator to evaluate the population.
|
|
112
|
+
n_points (int): The number of points to generate for the initial population.
|
|
113
|
+
seed (int): The seed for the random number generator.
|
|
114
|
+
kwargs: Additional keyword arguments. Check the Subscriber class for more information.
|
|
115
|
+
At the very least, the publisher argument should be provided.
|
|
116
|
+
"""
|
|
117
|
+
super().__init__(problem, **kwargs)
|
|
118
|
+
self.n_points = n_points
|
|
119
|
+
self.evaluator = evaluator
|
|
120
|
+
self.rng = np.random.default_rng(seed)
|
|
121
|
+
self.seed = seed
|
|
122
|
+
|
|
123
|
+
def do(self) -> tuple[pl.DataFrame, pl.DataFrame]:
|
|
124
|
+
"""Generate the initial population.
|
|
125
|
+
|
|
126
|
+
This method should be implemented by the inherited classes.
|
|
127
|
+
|
|
128
|
+
Returns:
|
|
129
|
+
tuple[pl.DataFrame, pl.DataFrame]: The initial population as the first element,
|
|
130
|
+
the corresponding objectives, the constraint violations, and the targets as the second element.
|
|
131
|
+
"""
|
|
132
|
+
if self.population is not None and self.out is not None:
|
|
133
|
+
self.notify()
|
|
134
|
+
return self.population, self.out
|
|
135
|
+
self.population = pl.from_numpy(
|
|
136
|
+
self.rng.uniform(low=self.bounds[:, 0], high=self.bounds[:, 1], size=(self.n_points, self.bounds.shape[0])),
|
|
137
|
+
schema=self.variable_symbols,
|
|
138
|
+
)
|
|
139
|
+
self.out = self.evaluator.evaluate(self.population)
|
|
140
|
+
self.notify()
|
|
141
|
+
return self.population, self.out
|
|
142
|
+
|
|
143
|
+
def update(self, message) -> None:
|
|
144
|
+
"""Update the generator based on the message."""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class LHSGenerator(RandomGenerator):
|
|
148
|
+
"""Class for generating Latin Hypercube Sampling (LHS) initial population for the MOEAs.
|
|
149
|
+
|
|
150
|
+
This class generates the initial population by using the Latin Hypercube Sampling (LHS) method.
|
|
151
|
+
If the seed is not provided, the seed is set to 0.
|
|
152
|
+
"""
|
|
153
|
+
|
|
154
|
+
def __init__(self, problem: Problem, evaluator: EMOEvaluator, n_points: int, seed: int, **kwargs):
|
|
155
|
+
"""Initialize the LHSGenerator class.
|
|
156
|
+
|
|
157
|
+
Args:
|
|
158
|
+
problem (Problem): The problem to solve.
|
|
159
|
+
evaluator (BaseEvaluator): The evaluator to evaluate the population.
|
|
160
|
+
n_points (int): The number of points to generate for the initial population.
|
|
161
|
+
seed (int): The seed for the random number generator.
|
|
162
|
+
kwargs: Additional keyword arguments. Check the Subscriber class for more information.
|
|
163
|
+
At the very least, the publisher argument should be provided.
|
|
164
|
+
"""
|
|
165
|
+
super().__init__(problem, evaluator, n_points, seed, **kwargs)
|
|
166
|
+
self.lhsrng = LatinHypercube(d=len(self.variable_symbols), seed=seed)
|
|
167
|
+
self.seed = seed
|
|
168
|
+
|
|
169
|
+
def do(self) -> tuple[pl.DataFrame, pl.DataFrame]:
|
|
170
|
+
"""Generate the initial population.
|
|
171
|
+
|
|
172
|
+
This method should be implemented by the inherited classes.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
tuple[pl.DataFrame, pl.DataFrame]: The initial population as the first element,
|
|
176
|
+
the corresponding objectives, the constraint violations, and the targets as the second element.
|
|
177
|
+
"""
|
|
178
|
+
if self.population is not None and self.out is not None:
|
|
179
|
+
self.notify()
|
|
180
|
+
return self.population, self.out
|
|
181
|
+
self.population = pl.from_numpy(
|
|
182
|
+
self.lhsrng.random(n=self.n_points) * (self.bounds[:, 1] - self.bounds[:, 0]) + self.bounds[:, 0],
|
|
183
|
+
schema=self.variable_symbols,
|
|
184
|
+
)
|
|
185
|
+
self.out = self.evaluator.evaluate(self.population)
|
|
186
|
+
self.notify()
|
|
187
|
+
return self.population, self.out
|
|
188
|
+
|
|
189
|
+
def update(self, message) -> None:
|
|
190
|
+
"""Update the generator based on the message."""
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class RandomBinaryGenerator(BaseGenerator):
|
|
194
|
+
"""Class for generating random initial population for problems with binary variables.
|
|
195
|
+
|
|
196
|
+
This class generates an initial population by randomly setting variable values to be either 0 or 1.
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
def __init__(self, problem: Problem, evaluator: EMOEvaluator, n_points: int, seed: int, **kwargs):
|
|
200
|
+
"""Initialize the RandomBinaryGenerator class.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
problem (Problem): The problem to solve.
|
|
204
|
+
evaluator (BaseEvaluator): The evaluator to evaluate the population.
|
|
205
|
+
n_points (int): The number of points to generate for the initial population.
|
|
206
|
+
seed (int): The seed for the random number generator.
|
|
207
|
+
kwargs: Additional keyword arguments. Check the Subscriber class for more information.
|
|
208
|
+
At the very least, the publisher argument should be provided.
|
|
209
|
+
"""
|
|
210
|
+
super().__init__(problem, **kwargs)
|
|
211
|
+
self.n_points = n_points
|
|
212
|
+
self.evaluator = evaluator
|
|
213
|
+
self.rng = np.random.default_rng(seed)
|
|
214
|
+
self.seed = seed
|
|
215
|
+
|
|
216
|
+
def do(self) -> tuple[pl.DataFrame, pl.DataFrame]:
|
|
217
|
+
"""Generate the initial population.
|
|
218
|
+
|
|
219
|
+
Returns:
|
|
220
|
+
tuple[pl.DataFrame, pl.DataFrame]: The initial population as the first element,
|
|
221
|
+
the corresponding objectives, the constraint violations, and the targets as the second element.
|
|
222
|
+
"""
|
|
223
|
+
if self.population is not None and self.out is not None:
|
|
224
|
+
self.notify()
|
|
225
|
+
return self.population, self.out
|
|
226
|
+
|
|
227
|
+
self.population = pl.from_numpy(
|
|
228
|
+
self.rng.integers(low=0, high=2, size=(self.n_points, self.bounds.shape[0])).astype(dtype=np.float64),
|
|
229
|
+
schema=self.variable_symbols,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
self.out = self.evaluator.evaluate(self.population)
|
|
233
|
+
self.notify()
|
|
234
|
+
return self.population, self.out
|
|
235
|
+
|
|
236
|
+
def update(self, message) -> None:
|
|
237
|
+
"""Update the generator based on the message."""
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
class RandomIntegerGenerator(BaseGenerator):
|
|
241
|
+
"""Class for generating random initial population for problems with integer variables.
|
|
242
|
+
|
|
243
|
+
This class generates an initial population by randomly setting variable values to be integers between the bounds of
|
|
244
|
+
the variables.
|
|
245
|
+
"""
|
|
246
|
+
|
|
247
|
+
def __init__(self, problem: Problem, evaluator: EMOEvaluator, n_points: int, seed: int, **kwargs):
|
|
248
|
+
"""Initialize the RandomIntegerGenerator class.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
problem (Problem): The problem to solve.
|
|
252
|
+
evaluator (BaseEvaluator): The evaluator to evaluate the population.
|
|
253
|
+
n_points (int): The number of points to generate for the initial population.
|
|
254
|
+
seed (int): The seed for the random number generator.
|
|
255
|
+
kwargs: Additional keyword arguments. Check the Subscriber class for more information.
|
|
256
|
+
At the very least, the publisher argument should be provided.
|
|
257
|
+
"""
|
|
258
|
+
super().__init__(problem, **kwargs)
|
|
259
|
+
self.n_points = n_points
|
|
260
|
+
self.evaluator = evaluator
|
|
261
|
+
self.rng = np.random.default_rng(seed)
|
|
262
|
+
self.seed = seed
|
|
263
|
+
|
|
264
|
+
def do(self) -> tuple[pl.DataFrame, pl.DataFrame]:
|
|
265
|
+
"""Generate the initial population.
|
|
266
|
+
|
|
267
|
+
Returns:
|
|
268
|
+
tuple[pl.DataFrame, pl.DataFrame]: The initial population as the first element,
|
|
269
|
+
the corresponding objectives, the constraint violations, and the targets as the second element.
|
|
270
|
+
"""
|
|
271
|
+
if self.population is not None and self.out is not None:
|
|
272
|
+
self.notify()
|
|
273
|
+
return self.population, self.out
|
|
274
|
+
|
|
275
|
+
self.population = pl.from_numpy(
|
|
276
|
+
self.rng.integers(
|
|
277
|
+
low=self.bounds[:, 0],
|
|
278
|
+
high=self.bounds[:, 1],
|
|
279
|
+
size=(self.n_points, self.bounds.shape[0]),
|
|
280
|
+
endpoint=True,
|
|
281
|
+
).astype(dtype=float),
|
|
282
|
+
schema=self.variable_symbols,
|
|
283
|
+
)
|
|
284
|
+
|
|
285
|
+
self.out = self.evaluator.evaluate(self.population)
|
|
286
|
+
self.notify()
|
|
287
|
+
return self.population, self.out
|
|
288
|
+
|
|
289
|
+
def update(self, message) -> None:
|
|
290
|
+
"""Update the generator based on the message."""
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
class RandomMixedIntegerGenerator(BaseGenerator):
|
|
294
|
+
"""Class for generating random initial population for problems with mixed-integer variables.
|
|
295
|
+
|
|
296
|
+
This class generates an initial population by randomly setting variable
|
|
297
|
+
values to be integers or floats between the bounds of the variables.
|
|
298
|
+
"""
|
|
299
|
+
|
|
300
|
+
def __init__(self, problem: Problem, evaluator: EMOEvaluator, n_points: int, seed: int, **kwargs):
|
|
301
|
+
"""Initialize the RandomMixedIntegerGenerator class.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
problem (Problem): The problem to solve.
|
|
305
|
+
evaluator (BaseEvaluator): The evaluator to evaluate the population.
|
|
306
|
+
n_points (int): The number of points to generate for the initial population.
|
|
307
|
+
seed (int): The seed for the random number generator.
|
|
308
|
+
kwargs: Additional keyword arguments. Check the Subscriber class for more information.
|
|
309
|
+
At the very least, the publisher argument should be provided.
|
|
310
|
+
"""
|
|
311
|
+
super().__init__(problem, **kwargs)
|
|
312
|
+
self.var_symbol_types = {
|
|
313
|
+
VariableTypeEnum.real: [
|
|
314
|
+
var.symbol for var in problem.variables if var.variable_type == VariableTypeEnum.real
|
|
315
|
+
],
|
|
316
|
+
VariableTypeEnum.integer: [
|
|
317
|
+
var.symbol
|
|
318
|
+
for var in problem.variables
|
|
319
|
+
if var.variable_type in [VariableTypeEnum.integer, VariableTypeEnum.binary]
|
|
320
|
+
],
|
|
321
|
+
}
|
|
322
|
+
self.n_points = n_points
|
|
323
|
+
self.evaluator = evaluator
|
|
324
|
+
self.rng = np.random.default_rng(seed)
|
|
325
|
+
self.seed = seed
|
|
326
|
+
|
|
327
|
+
def do(self) -> tuple[pl.DataFrame, pl.DataFrame]:
|
|
328
|
+
"""Generate the initial population.
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
tuple[pl.DataFrame, pl.DataFrame]: The initial population as the first element,
|
|
332
|
+
the corresponding objectives, the constraint violations, and the targets as the second element.
|
|
333
|
+
"""
|
|
334
|
+
if self.population is not None and self.out is not None:
|
|
335
|
+
self.notify()
|
|
336
|
+
return self.population, self.out
|
|
337
|
+
|
|
338
|
+
tmp = {
|
|
339
|
+
var.symbol: self.rng.integers(
|
|
340
|
+
low=var.lowerbound, high=var.upperbound, size=self.n_points, endpoint=True
|
|
341
|
+
).astype(dtype=float)
|
|
342
|
+
if var.variable_type in [VariableTypeEnum.binary, VariableTypeEnum.integer]
|
|
343
|
+
else self.rng.uniform(low=var.lowerbound, high=var.upperbound, size=self.n_points).astype(dtype=float)
|
|
344
|
+
for var in self.problem.variables
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
# combine
|
|
348
|
+
# self.population
|
|
349
|
+
self.population = pl.DataFrame(tmp)
|
|
350
|
+
|
|
351
|
+
self.out = self.evaluator.evaluate(self.population)
|
|
352
|
+
self.notify()
|
|
353
|
+
return self.population, self.out
|
|
354
|
+
|
|
355
|
+
def update(self, message) -> None:
|
|
356
|
+
"""Update the generator based on the message."""
|