desdeo 2.0.0__py3-none-any.whl → 2.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- desdeo/adm/ADMAfsar.py +551 -0
- desdeo/adm/ADMChen.py +414 -0
- desdeo/adm/BaseADM.py +119 -0
- desdeo/adm/__init__.py +11 -0
- desdeo/api/__init__.py +6 -6
- desdeo/api/app.py +38 -28
- desdeo/api/config.py +65 -44
- desdeo/api/config.toml +23 -12
- desdeo/api/db.py +10 -8
- desdeo/api/db_init.py +12 -6
- desdeo/api/models/__init__.py +220 -20
- desdeo/api/models/archive.py +16 -27
- desdeo/api/models/emo.py +128 -0
- desdeo/api/models/enautilus.py +69 -0
- desdeo/api/models/gdm/gdm_aggregate.py +139 -0
- desdeo/api/models/gdm/gdm_base.py +69 -0
- desdeo/api/models/gdm/gdm_score_bands.py +114 -0
- desdeo/api/models/gdm/gnimbus.py +138 -0
- desdeo/api/models/generic.py +104 -0
- desdeo/api/models/generic_states.py +401 -0
- desdeo/api/models/nimbus.py +158 -0
- desdeo/api/models/preference.py +44 -6
- desdeo/api/models/problem.py +274 -64
- desdeo/api/models/session.py +4 -1
- desdeo/api/models/state.py +419 -52
- desdeo/api/models/user.py +7 -6
- desdeo/api/models/utopia.py +25 -0
- desdeo/api/routers/_EMO.backup +309 -0
- desdeo/api/routers/_NIMBUS.py +6 -3
- desdeo/api/routers/emo.py +497 -0
- desdeo/api/routers/enautilus.py +237 -0
- desdeo/api/routers/gdm/gdm_aggregate.py +234 -0
- desdeo/api/routers/gdm/gdm_base.py +420 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_manager.py +398 -0
- desdeo/api/routers/gdm/gdm_score_bands/gdm_score_bands_routers.py +377 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_manager.py +698 -0
- desdeo/api/routers/gdm/gnimbus/gnimbus_routers.py +591 -0
- desdeo/api/routers/generic.py +233 -0
- desdeo/api/routers/nimbus.py +705 -0
- desdeo/api/routers/problem.py +201 -4
- desdeo/api/routers/reference_point_method.py +20 -44
- desdeo/api/routers/session.py +50 -26
- desdeo/api/routers/user_authentication.py +180 -26
- desdeo/api/routers/utils.py +187 -0
- desdeo/api/routers/utopia.py +230 -0
- desdeo/api/schema.py +10 -4
- desdeo/api/tests/conftest.py +94 -2
- desdeo/api/tests/test_enautilus.py +330 -0
- desdeo/api/tests/test_models.py +550 -72
- desdeo/api/tests/test_routes.py +902 -43
- desdeo/api/utils/_database.py +263 -0
- desdeo/api/utils/database.py +28 -266
- desdeo/api/utils/emo_database.py +40 -0
- desdeo/core.py +7 -0
- desdeo/emo/__init__.py +154 -24
- desdeo/emo/hooks/archivers.py +18 -2
- desdeo/emo/methods/EAs.py +128 -5
- desdeo/emo/methods/bases.py +9 -56
- desdeo/emo/methods/templates.py +111 -0
- desdeo/emo/operators/crossover.py +544 -42
- desdeo/emo/operators/evaluator.py +10 -14
- desdeo/emo/operators/generator.py +127 -24
- desdeo/emo/operators/mutation.py +212 -41
- desdeo/emo/operators/scalar_selection.py +202 -0
- desdeo/emo/operators/selection.py +956 -214
- desdeo/emo/operators/termination.py +124 -16
- desdeo/emo/options/__init__.py +108 -0
- desdeo/emo/options/algorithms.py +435 -0
- desdeo/emo/options/crossover.py +164 -0
- desdeo/emo/options/generator.py +131 -0
- desdeo/emo/options/mutation.py +260 -0
- desdeo/emo/options/repair.py +61 -0
- desdeo/emo/options/scalar_selection.py +66 -0
- desdeo/emo/options/selection.py +127 -0
- desdeo/emo/options/templates.py +383 -0
- desdeo/emo/options/termination.py +143 -0
- desdeo/gdm/__init__.py +22 -0
- desdeo/gdm/gdmtools.py +45 -0
- desdeo/gdm/score_bands.py +114 -0
- desdeo/gdm/voting_rules.py +50 -0
- desdeo/mcdm/__init__.py +23 -1
- desdeo/mcdm/enautilus.py +338 -0
- desdeo/mcdm/gnimbus.py +484 -0
- desdeo/mcdm/nautilus_navigator.py +7 -6
- desdeo/mcdm/reference_point_method.py +70 -0
- desdeo/problem/__init__.py +16 -11
- desdeo/problem/evaluator.py +4 -5
- desdeo/problem/external/__init__.py +18 -0
- desdeo/problem/external/core.py +356 -0
- desdeo/problem/external/pymoo_provider.py +266 -0
- desdeo/problem/external/runtime.py +44 -0
- desdeo/problem/gurobipy_evaluator.py +37 -12
- desdeo/problem/infix_parser.py +1 -16
- desdeo/problem/json_parser.py +7 -11
- desdeo/problem/pyomo_evaluator.py +25 -6
- desdeo/problem/schema.py +73 -55
- desdeo/problem/simulator_evaluator.py +65 -15
- desdeo/problem/testproblems/__init__.py +26 -11
- desdeo/problem/testproblems/benchmarks_server.py +120 -0
- desdeo/problem/testproblems/cake_problem.py +185 -0
- desdeo/problem/testproblems/dmitry_forest_problem_discrete.py +71 -0
- desdeo/problem/testproblems/forest_problem.py +77 -69
- desdeo/problem/testproblems/multi_valued_constraints.py +119 -0
- desdeo/problem/testproblems/{river_pollution_problem.py → river_pollution_problems.py} +28 -22
- desdeo/problem/testproblems/single_objective.py +289 -0
- desdeo/problem/testproblems/zdt_problem.py +4 -1
- desdeo/problem/utils.py +1 -1
- desdeo/tools/__init__.py +39 -21
- desdeo/tools/desc_gen.py +22 -0
- desdeo/tools/generics.py +22 -2
- desdeo/tools/group_scalarization.py +3090 -0
- desdeo/tools/indicators_binary.py +107 -1
- desdeo/tools/indicators_unary.py +3 -16
- desdeo/tools/message.py +33 -2
- desdeo/tools/non_dominated_sorting.py +4 -3
- desdeo/tools/patterns.py +9 -7
- desdeo/tools/pyomo_solver_interfaces.py +49 -36
- desdeo/tools/reference_vectors.py +118 -351
- desdeo/tools/scalarization.py +340 -1413
- desdeo/tools/score_bands.py +491 -328
- desdeo/tools/utils.py +117 -49
- desdeo/tools/visualizations.py +67 -0
- desdeo/utopia_stuff/utopia_problem.py +1 -1
- desdeo/utopia_stuff/utopia_problem_old.py +1 -1
- {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info}/METADATA +47 -30
- desdeo-2.1.1.dist-info/RECORD +180 -0
- {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info}/WHEEL +1 -1
- desdeo-2.0.0.dist-info/RECORD +0 -120
- /desdeo/api/utils/{logger.py → _logger.py} +0 -0
- {desdeo-2.0.0.dist-info → desdeo-2.1.1.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""JSON Schema for generator options."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Literal
|
|
6
|
+
|
|
7
|
+
import polars as pl
|
|
8
|
+
from pydantic import BaseModel, Field
|
|
9
|
+
|
|
10
|
+
from desdeo.emo.operators.evaluator import EMOEvaluator
|
|
11
|
+
from desdeo.emo.operators.generator import (
|
|
12
|
+
ArchiveGenerator,
|
|
13
|
+
BaseGenerator,
|
|
14
|
+
LHSGenerator,
|
|
15
|
+
RandomBinaryGenerator,
|
|
16
|
+
RandomGenerator,
|
|
17
|
+
RandomIntegerGenerator,
|
|
18
|
+
RandomMixedIntegerGenerator,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from desdeo.problem import Problem
|
|
23
|
+
from desdeo.tools.patterns import Publisher
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BaseGeneratorOptions(BaseModel):
|
|
27
|
+
"""Options for all generators."""
|
|
28
|
+
|
|
29
|
+
n_points: int = Field(gt=0, description="The number of points to generate for the initial population.")
|
|
30
|
+
"""The number of points to generate for the initial population."""
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class LHSGeneratorOptions(BaseGeneratorOptions):
|
|
34
|
+
"""Options for Latin Hypercube Sampling (LHS) generator."""
|
|
35
|
+
|
|
36
|
+
name: Literal["LHSGenerator"] = Field(default="LHSGenerator", frozen=True, description="The name of the generator.")
|
|
37
|
+
"""The name of the generator."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class RandomBinaryGeneratorOptions(BaseGeneratorOptions):
|
|
41
|
+
"""Options for Random Binary generator."""
|
|
42
|
+
|
|
43
|
+
name: Literal["RandomBinaryGenerator"] = Field(
|
|
44
|
+
default="RandomBinaryGenerator", frozen=True, description="The name of the generator."
|
|
45
|
+
)
|
|
46
|
+
"""The name of the generator."""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class RandomGeneratorOptions(BaseGeneratorOptions):
|
|
50
|
+
"""Options for Random generator."""
|
|
51
|
+
|
|
52
|
+
name: Literal["RandomGenerator"] = Field(
|
|
53
|
+
default="RandomGenerator", frozen=True, description="The name of the generator."
|
|
54
|
+
)
|
|
55
|
+
"""The name of the generator."""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class RandomIntegerGeneratorOptions(BaseGeneratorOptions):
|
|
59
|
+
"""Options for Random Integer generator."""
|
|
60
|
+
|
|
61
|
+
name: Literal["RandomIntegerGenerator"] = Field(
|
|
62
|
+
default="RandomIntegerGenerator", frozen=True, description="The name of the generator."
|
|
63
|
+
)
|
|
64
|
+
"""The name of the generator."""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class RandomMixedIntegerGeneratorOptions(BaseGeneratorOptions):
|
|
68
|
+
"""Options for Random Mixed Integer generator."""
|
|
69
|
+
|
|
70
|
+
name: Literal["RandomMixedIntegerGenerator"] = Field(
|
|
71
|
+
default="RandomMixedIntegerGenerator", frozen=True, description="The name of the generator."
|
|
72
|
+
)
|
|
73
|
+
"""The name of the generator."""
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class ArchiveGeneratorOptions(BaseModel):
|
|
77
|
+
"""Options for Archive generator."""
|
|
78
|
+
|
|
79
|
+
model_config = {"arbitrary_types_allowed": True, "use_attribute_docstrings": True}
|
|
80
|
+
|
|
81
|
+
name: Literal["ArchiveGenerator"] = "ArchiveGenerator"
|
|
82
|
+
"""The name of the generator."""
|
|
83
|
+
solutions: pl.DataFrame
|
|
84
|
+
"""The initial solutions to populate the archive with."""
|
|
85
|
+
outputs: pl.DataFrame
|
|
86
|
+
"""The corresponding outputs of the initial solutions."""
|
|
87
|
+
|
|
88
|
+
GeneratorOptions = (
|
|
89
|
+
LHSGeneratorOptions
|
|
90
|
+
| RandomBinaryGeneratorOptions
|
|
91
|
+
| RandomGeneratorOptions
|
|
92
|
+
| RandomIntegerGeneratorOptions
|
|
93
|
+
| RandomMixedIntegerGeneratorOptions
|
|
94
|
+
| ArchiveGeneratorOptions
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def generator_constructor(
|
|
99
|
+
problem: Problem,
|
|
100
|
+
options: GeneratorOptions,
|
|
101
|
+
publisher: Publisher,
|
|
102
|
+
verbosity: int,
|
|
103
|
+
seed: int,
|
|
104
|
+
evaluator: EMOEvaluator,
|
|
105
|
+
) -> BaseGenerator:
|
|
106
|
+
"""Construct a generator based on the provided options.
|
|
107
|
+
|
|
108
|
+
Args:
|
|
109
|
+
problem (Problem): The optimization problem to solve.
|
|
110
|
+
options (GeneratorOptions): The options for the generator.
|
|
111
|
+
publisher (Publisher): The publisher for the generator.
|
|
112
|
+
verbosity (int): The verbosity level for the generator.
|
|
113
|
+
seed (int): The random seed for the generator.
|
|
114
|
+
evaluator (EMOEvaluator): The evaluator to use for evaluating solutions.
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
BaseGenerator: The constructed generator.
|
|
118
|
+
"""
|
|
119
|
+
generator_types = {
|
|
120
|
+
"LHSGenerator": LHSGenerator,
|
|
121
|
+
"RandomBinaryGenerator": RandomBinaryGenerator,
|
|
122
|
+
"RandomGenerator": RandomGenerator,
|
|
123
|
+
"RandomIntegerGenerator": RandomIntegerGenerator,
|
|
124
|
+
"RandomMixedIntegerGenerator": RandomMixedIntegerGenerator,
|
|
125
|
+
"ArchiveGenerator": ArchiveGenerator,
|
|
126
|
+
}
|
|
127
|
+
options: dict = options.model_dump()
|
|
128
|
+
name = options.pop("name")
|
|
129
|
+
return generator_types[name](
|
|
130
|
+
problem, **options, publisher=publisher, verbosity=verbosity, seed=seed, evaluator=evaluator
|
|
131
|
+
)
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
"""JSON Schema for mutation operator options."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
from desdeo.emo.operators.mutation import (
|
|
10
|
+
BaseMutation,
|
|
11
|
+
BinaryFlipMutation,
|
|
12
|
+
BoundedPolynomialMutation,
|
|
13
|
+
IntegerRandomMutation,
|
|
14
|
+
MixedIntegerRandomMutation,
|
|
15
|
+
MPTMutation,
|
|
16
|
+
NonUniformMutation,
|
|
17
|
+
PowerMutation,
|
|
18
|
+
SelfAdaptiveGaussianMutation,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
from desdeo.problem import Problem
|
|
23
|
+
from desdeo.tools.patterns import Publisher
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class BoundedPolynomialMutationOptions(BaseModel):
|
|
27
|
+
"""Options for Bounded Polynomial Mutation."""
|
|
28
|
+
|
|
29
|
+
model_config = {"use_attribute_docstrings": True}
|
|
30
|
+
|
|
31
|
+
name: Literal["BoundedPolynomialMutation"] = Field(
|
|
32
|
+
default="BoundedPolynomialMutation",
|
|
33
|
+
frozen=True,
|
|
34
|
+
)
|
|
35
|
+
"""The name of the mutation operator."""
|
|
36
|
+
mutation_probability: float | None = Field(default=None, ge=0.0, le=1.0)
|
|
37
|
+
"""
|
|
38
|
+
The probability of mutation. Defaults to None, which sets the mutation probability to
|
|
39
|
+
1/<number of decision variables>.
|
|
40
|
+
"""
|
|
41
|
+
distribution_index: float = Field(default=20.0, gt=0.0)
|
|
42
|
+
"""The distribution index."""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class BinaryFlipMutationOptions(BaseModel):
|
|
46
|
+
"""Options for Binary Flip Mutation."""
|
|
47
|
+
|
|
48
|
+
name: Literal["BinaryFlipMutation"] = Field(
|
|
49
|
+
default="BinaryFlipMutation", frozen=True, description="The name of the mutation operator."
|
|
50
|
+
)
|
|
51
|
+
"""The name of the mutation operator."""
|
|
52
|
+
mutation_probability: float | None = Field(
|
|
53
|
+
default=None,
|
|
54
|
+
ge=0.0,
|
|
55
|
+
le=1.0,
|
|
56
|
+
description=(
|
|
57
|
+
"The probability of mutation. Defaults to None, which sets the "
|
|
58
|
+
"mutation probability to 1/<number of decision variables>."
|
|
59
|
+
),
|
|
60
|
+
)
|
|
61
|
+
"""
|
|
62
|
+
The probability of mutation. Defaults to None, which sets the mutation probability to
|
|
63
|
+
1/<number of decision variables>.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class IntegerRandomMutationOptions(BaseModel):
|
|
68
|
+
"""Options for Integer Random Mutation."""
|
|
69
|
+
|
|
70
|
+
name: Literal["IntegerRandomMutation"] = Field(
|
|
71
|
+
default="IntegerRandomMutation", frozen=True, description="The name of the mutation operator."
|
|
72
|
+
)
|
|
73
|
+
"""The name of the mutation operator."""
|
|
74
|
+
mutation_probability: float | None = Field(
|
|
75
|
+
default=None,
|
|
76
|
+
ge=0.0,
|
|
77
|
+
le=1.0,
|
|
78
|
+
description=(
|
|
79
|
+
"The probability of mutation. Defaults to None, which sets the "
|
|
80
|
+
"mutation probability to 1/<number of decision variables>."
|
|
81
|
+
),
|
|
82
|
+
)
|
|
83
|
+
"""
|
|
84
|
+
The probability of mutation. Defaults to None, which sets the mutation probability to
|
|
85
|
+
1/<number of decision variables>.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class MixedIntegerRandomMutationOptions(BaseModel):
|
|
90
|
+
"""Options for Mixed Integer Random Mutation."""
|
|
91
|
+
|
|
92
|
+
name: Literal["MixedIntegerRandomMutation"] = Field(
|
|
93
|
+
default="MixedIntegerRandomMutation", frozen=True, description="The name of the mutation operator."
|
|
94
|
+
)
|
|
95
|
+
"""The name of the mutation operator."""
|
|
96
|
+
mutation_probability: float | None = Field(
|
|
97
|
+
default=None,
|
|
98
|
+
ge=0.0,
|
|
99
|
+
le=1.0,
|
|
100
|
+
description=(
|
|
101
|
+
"The probability of mutation. Defaults to None, which sets the "
|
|
102
|
+
"mutation probability to 1/<number of decision variables>."
|
|
103
|
+
),
|
|
104
|
+
)
|
|
105
|
+
"""
|
|
106
|
+
The probability of mutation. Defaults to None, which sets the mutation probability to
|
|
107
|
+
1/<number of decision variables>.
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class MPTMutationOptions(BaseModel):
|
|
112
|
+
"""Options for MPT Mutation."""
|
|
113
|
+
|
|
114
|
+
name: Literal["MPTMutation"] = Field(
|
|
115
|
+
default="MPTMutation", frozen=True, description="The name of the mutation operator."
|
|
116
|
+
)
|
|
117
|
+
"""The name of the mutation operator."""
|
|
118
|
+
mutation_probability: float | None = Field(
|
|
119
|
+
default=None,
|
|
120
|
+
ge=0.0,
|
|
121
|
+
le=1.0,
|
|
122
|
+
description=(
|
|
123
|
+
"The probability of mutation. Defaults to None, which sets the "
|
|
124
|
+
"mutation probability to 1/<number of decision variables>."
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
"""
|
|
128
|
+
The probability of mutation. Defaults to None, which sets the mutation probability to
|
|
129
|
+
1/<number of decision variables>.
|
|
130
|
+
"""
|
|
131
|
+
mutation_exponent: float = Field(
|
|
132
|
+
default=2.0, ge=0.0, description="Controls strength of small mutation (larger means smaller mutations)."
|
|
133
|
+
)
|
|
134
|
+
"""Controls strength of small mutation (larger means smaller mutations)."""
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
class NonUniformMutationOptions(BaseModel):
|
|
138
|
+
"""Options for Non-Uniform Mutation."""
|
|
139
|
+
|
|
140
|
+
name: Literal["NonUniformMutation"] = Field(
|
|
141
|
+
default="NonUniformMutation", frozen=True, description="The name of the mutation operator."
|
|
142
|
+
)
|
|
143
|
+
"""The name of the mutation operator."""
|
|
144
|
+
mutation_probability: float | None = Field(
|
|
145
|
+
default=None,
|
|
146
|
+
ge=0.0,
|
|
147
|
+
le=1.0,
|
|
148
|
+
description=(
|
|
149
|
+
"The probability of mutation. Defaults to None, which sets the "
|
|
150
|
+
"mutation probability to 1/<number of decision variables>."
|
|
151
|
+
),
|
|
152
|
+
)
|
|
153
|
+
"""
|
|
154
|
+
The probability of mutation. Defaults to None, which sets the mutation probability to
|
|
155
|
+
1/<number of decision variables>.
|
|
156
|
+
"""
|
|
157
|
+
max_generations: int = Field(
|
|
158
|
+
gt=0, description="Maximum number of generations in the evolutionary run. Used to scale mutation decay."
|
|
159
|
+
)
|
|
160
|
+
b: float = Field(
|
|
161
|
+
default=5.0,
|
|
162
|
+
ge=0.0,
|
|
163
|
+
description=(
|
|
164
|
+
"Non-uniform mutation decay parameter. Higher values cause"
|
|
165
|
+
"faster reduction in mutation strength over generations."
|
|
166
|
+
),
|
|
167
|
+
)
|
|
168
|
+
"""Non-uniform mutation decay parameter. Higher values cause
|
|
169
|
+
faster reduction in mutation strength over generations."""
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class SelfAdaptiveGaussianMutationOptions(BaseModel):
|
|
173
|
+
"""Options for Self-Adaptive Gaussian Mutation."""
|
|
174
|
+
|
|
175
|
+
name: Literal["SelfAdaptiveGaussianMutation"] = Field(
|
|
176
|
+
default="SelfAdaptiveGaussianMutation", frozen=True, description="The name of the mutation operator."
|
|
177
|
+
)
|
|
178
|
+
"""The name of the mutation operator."""
|
|
179
|
+
mutation_probability: float | None = Field(
|
|
180
|
+
default=None,
|
|
181
|
+
ge=0.0,
|
|
182
|
+
le=1.0,
|
|
183
|
+
description=(
|
|
184
|
+
"The probability of mutation. Defaults to None, which sets the "
|
|
185
|
+
"mutation probability to 1/<number of decision variables>."
|
|
186
|
+
),
|
|
187
|
+
)
|
|
188
|
+
"""
|
|
189
|
+
The probability of mutation. Defaults to None, which sets the mutation probability to
|
|
190
|
+
1/<number of decision variables>.
|
|
191
|
+
"""
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class PowerMutationOptions(BaseModel):
|
|
195
|
+
"""Options for Power Mutation."""
|
|
196
|
+
|
|
197
|
+
name: Literal["PowerMutation"] = Field(
|
|
198
|
+
default="PowerMutation", frozen=True, description="The name of the mutation operator."
|
|
199
|
+
)
|
|
200
|
+
"""The name of the mutation operator."""
|
|
201
|
+
mutation_probability: float | None = Field(
|
|
202
|
+
default=None,
|
|
203
|
+
ge=0.0,
|
|
204
|
+
le=1.0,
|
|
205
|
+
description=(
|
|
206
|
+
"The probability of mutation. Defaults to None, which sets the "
|
|
207
|
+
"mutation probability to 1/<number of decision variables>."
|
|
208
|
+
),
|
|
209
|
+
)
|
|
210
|
+
"""
|
|
211
|
+
The probability of mutation. Defaults to None, which sets the mutation probability to
|
|
212
|
+
1/<number of decision variables>.
|
|
213
|
+
"""
|
|
214
|
+
p: float = Field(
|
|
215
|
+
default=1.5, ge=0.0, description="Power distribution parameter. Controls the perturbation magnitude."
|
|
216
|
+
)
|
|
217
|
+
"""Power distribution parameter. Controls the perturbation magnitude."""
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
MutationOptions = (
|
|
221
|
+
BoundedPolynomialMutationOptions
|
|
222
|
+
| BinaryFlipMutationOptions
|
|
223
|
+
| IntegerRandomMutationOptions
|
|
224
|
+
| MixedIntegerRandomMutationOptions
|
|
225
|
+
| MPTMutationOptions
|
|
226
|
+
| NonUniformMutationOptions
|
|
227
|
+
| SelfAdaptiveGaussianMutationOptions
|
|
228
|
+
| PowerMutationOptions
|
|
229
|
+
)
|
|
230
|
+
"""All possible mutation operator options."""
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def mutation_constructor(
|
|
234
|
+
problem: Problem, publisher: Publisher, seed: int, verbosity: int, options: MutationOptions
|
|
235
|
+
) -> BaseMutation:
|
|
236
|
+
"""Construct a mutation operator.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
problem (Problem): The optimization problem to solve.
|
|
240
|
+
publisher (Publisher): The publisher for communication.
|
|
241
|
+
seed (int): The random seed for reproducibility.
|
|
242
|
+
verbosity (int): The verbosity level of the output.
|
|
243
|
+
options (MutationOptions): The options for the mutation operator.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
BaseCrossover: The constructed crossover operator.
|
|
247
|
+
"""
|
|
248
|
+
mutation_types = {
|
|
249
|
+
"BoundedPolynomialMutation": BoundedPolynomialMutation,
|
|
250
|
+
"BinaryFlipMutation": BinaryFlipMutation,
|
|
251
|
+
"IntegerRandomMutation": IntegerRandomMutation,
|
|
252
|
+
"MixedIntegerRandomMutation": MixedIntegerRandomMutation,
|
|
253
|
+
"MPTMutation": MPTMutation,
|
|
254
|
+
"NonUniformMutation": NonUniformMutation,
|
|
255
|
+
"SelfAdaptiveGaussianMutation": SelfAdaptiveGaussianMutation,
|
|
256
|
+
"PowerMutation": PowerMutation,
|
|
257
|
+
}
|
|
258
|
+
options: dict = options.model_dump()
|
|
259
|
+
name = options.pop("name")
|
|
260
|
+
return mutation_types[name](problem=problem, publisher=publisher, seed=seed, verbosity=verbosity, **options)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"""JSON Schema for repair operator options."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Callable
|
|
4
|
+
from typing import Literal
|
|
5
|
+
|
|
6
|
+
import polars as pl
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
from desdeo.problem import Problem
|
|
10
|
+
from desdeo.tools.utils import repair
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NoRepairOptions(BaseModel):
|
|
14
|
+
"""Options for No Repair."""
|
|
15
|
+
|
|
16
|
+
model_config = ConfigDict(use_attribute_docstrings=True)
|
|
17
|
+
|
|
18
|
+
name: Literal["NoRepair"] = Field(default="NoRepair")
|
|
19
|
+
"""Do not apply any repair to the solutions."""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class ClipRepairOptions(BaseModel):
|
|
23
|
+
"""Options for Clip Repair."""
|
|
24
|
+
|
|
25
|
+
model_config = ConfigDict(use_attribute_docstrings=True)
|
|
26
|
+
|
|
27
|
+
name: Literal["ClipRepair"] = Field(default="ClipRepair")
|
|
28
|
+
"""Clip the solutions to be within the variable bounds."""
|
|
29
|
+
|
|
30
|
+
lower_bounds: dict[str, float] | None = None
|
|
31
|
+
"""Lower bounds for the decision variables. If none, the lower bounds from the problem will be used."""
|
|
32
|
+
upper_bounds: dict[str, float] | None = None
|
|
33
|
+
"""Upper bounds for the decision variables. If none, the upper bounds from the problem will be used."""
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
RepairOptions = ClipRepairOptions | NoRepairOptions
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def repair_constructor(options: RepairOptions, problem: Problem) -> Callable[[pl.DataFrame], pl.DataFrame]:
|
|
40
|
+
"""Get the repair operator based on the provided options.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
options (RepairOptions): The repair options.
|
|
44
|
+
problem (Problem): The optimization problem to solve.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
callable: The repair operator function.
|
|
48
|
+
"""
|
|
49
|
+
if options.name == "NoRepair":
|
|
50
|
+
return lambda x: x # No repair, return input as is
|
|
51
|
+
if options.name == "ClipRepair":
|
|
52
|
+
if options.lower_bounds is None:
|
|
53
|
+
lower_bounds = {var.symbol: var.lowerbound for var in problem.get_flattened_variables()}
|
|
54
|
+
else:
|
|
55
|
+
lower_bounds = options.lower_bounds
|
|
56
|
+
if options.upper_bounds is None:
|
|
57
|
+
upper_bounds = {var.symbol: var.upperbound for var in problem.get_flattened_variables()}
|
|
58
|
+
else:
|
|
59
|
+
upper_bounds = options.upper_bounds
|
|
60
|
+
return repair(lower_bounds=lower_bounds, upper_bounds=upper_bounds)
|
|
61
|
+
raise ValueError(f"Unknown repair operator: {options.name}")
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"""JSON Schema for scalar selector operator options."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, Field
|
|
8
|
+
|
|
9
|
+
from desdeo.emo.operators.scalar_selection import BaseScalarSelector, TournamentSelection
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from desdeo.tools.patterns import Publisher
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class TournamentSelectionOptions(BaseModel):
|
|
16
|
+
"""Options for tournament selection operator."""
|
|
17
|
+
|
|
18
|
+
name: Literal["TournamentSelection"] = Field(
|
|
19
|
+
default="TournamentSelection", frozen=True, description="The name of the scalar selection operator."
|
|
20
|
+
)
|
|
21
|
+
"""The name of the scalar selection operator."""
|
|
22
|
+
tournament_size: int = Field(
|
|
23
|
+
default=2,
|
|
24
|
+
description="The number of individuals participating in the tournament.",
|
|
25
|
+
)
|
|
26
|
+
"""The number of individuals participating in the tournament."""
|
|
27
|
+
winner_size: int = Field(
|
|
28
|
+
gt=1,
|
|
29
|
+
description="The number of winners to select (equivalent to population size).",
|
|
30
|
+
)
|
|
31
|
+
"""The number of winners to select (equivalent to population size)."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class RouletteWheelSelectionOptions(TournamentSelectionOptions):
|
|
35
|
+
"""Options for roulette wheel selection operator."""
|
|
36
|
+
|
|
37
|
+
name: Literal["RouletteWheelSelection"] = Field(
|
|
38
|
+
default="RouletteWheelSelection", frozen=True, description="The name of the scalar selection operator."
|
|
39
|
+
)
|
|
40
|
+
"""The name of the scalar selection operator."""
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
ScalarSelectionOptions = TournamentSelectionOptions | RouletteWheelSelectionOptions
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def scalar_selector_constructor(
|
|
47
|
+
options: ScalarSelectionOptions, seed: int, publisher: Publisher, verbosity: int
|
|
48
|
+
) -> BaseScalarSelector:
|
|
49
|
+
"""Construct a scalar selector operator based on the provided options."""
|
|
50
|
+
if options.name == "TournamentSelection":
|
|
51
|
+
return TournamentSelection(
|
|
52
|
+
tournament_size=options.tournament_size,
|
|
53
|
+
winner_size=options.winner_size,
|
|
54
|
+
publisher=publisher,
|
|
55
|
+
verbosity=verbosity,
|
|
56
|
+
)
|
|
57
|
+
if options.name == "RouletteWheelSelection":
|
|
58
|
+
return TournamentSelection( # It implements both (and more)
|
|
59
|
+
winner_size=options.winner_size,
|
|
60
|
+
seed=seed, # By providing seed tournament selection behaves like roulette wheel
|
|
61
|
+
publisher=publisher,
|
|
62
|
+
verbosity=verbosity,
|
|
63
|
+
tournament_size=options.tournament_size,
|
|
64
|
+
)
|
|
65
|
+
else:
|
|
66
|
+
raise ValueError(f"Unknown scalar selection operator: {options.name}")
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"""JSON Schema for selection operator options."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Literal
|
|
6
|
+
|
|
7
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
8
|
+
|
|
9
|
+
from desdeo.emo.operators.selection import (
|
|
10
|
+
BaseSelector,
|
|
11
|
+
IBEASelector,
|
|
12
|
+
NSGA2Selector,
|
|
13
|
+
NSGA3Selector,
|
|
14
|
+
ParameterAdaptationStrategy,
|
|
15
|
+
ReferenceVectorOptions,
|
|
16
|
+
RVEASelector,
|
|
17
|
+
)
|
|
18
|
+
from desdeo.tools.indicators_binary import self_epsilon, self_hv
|
|
19
|
+
|
|
20
|
+
if TYPE_CHECKING:
|
|
21
|
+
from desdeo.problem import Problem
|
|
22
|
+
from desdeo.tools.patterns import Publisher
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RVEASelectorOptions(BaseModel):
|
|
26
|
+
"""Options for RVEA Selection."""
|
|
27
|
+
|
|
28
|
+
model_config = ConfigDict(use_enum_values=True)
|
|
29
|
+
|
|
30
|
+
name: Literal["RVEASelector"] = Field(
|
|
31
|
+
default="RVEASelector", frozen=True, description="The name of the selection operator."
|
|
32
|
+
)
|
|
33
|
+
"""The name of the selection operator."""
|
|
34
|
+
reference_vector_options: ReferenceVectorOptions = Field(
|
|
35
|
+
default=ReferenceVectorOptions(), description="Options for the reference vectors."
|
|
36
|
+
)
|
|
37
|
+
"""Options for the reference vectors."""
|
|
38
|
+
parameter_adaptation_strategy: ParameterAdaptationStrategy = Field(
|
|
39
|
+
default=ParameterAdaptationStrategy.GENERATION_BASED, description="The parameter adaptation strategy to use."
|
|
40
|
+
)
|
|
41
|
+
"""Whether the angle penalized distance is adapted per generation or per function evaluation."""
|
|
42
|
+
alpha: float = Field(default=2.0, gt=0.0, description="The alpha parameter in the angle penalized distance.")
|
|
43
|
+
"""The alpha parameter in the angle penalized distance."""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class NSGA3SelectorOptions(BaseModel):
|
|
47
|
+
"""Options for NSGA-III Selection."""
|
|
48
|
+
|
|
49
|
+
name: Literal["NSGA3Selector"] = Field(
|
|
50
|
+
default="NSGA3Selector", frozen=True, description="The name of the selection operator."
|
|
51
|
+
)
|
|
52
|
+
"""The name of the selection operator."""
|
|
53
|
+
reference_vector_options: ReferenceVectorOptions = Field(
|
|
54
|
+
default=ReferenceVectorOptions(), description="Options for the reference vectors."
|
|
55
|
+
)
|
|
56
|
+
"""Options for the reference vectors."""
|
|
57
|
+
invert_reference_vectors: bool = Field(
|
|
58
|
+
default=False, description="Whether to invert the reference vectors (inverted triangle)."
|
|
59
|
+
)
|
|
60
|
+
"""Whether to invert the reference vectors (inverted triangle)."""
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class NSGA2SelectorOptions(BaseModel):
|
|
64
|
+
"""Options for NSGA-II Selection."""
|
|
65
|
+
|
|
66
|
+
name: Literal["NSGA2Selector"] = Field(
|
|
67
|
+
default="NSGA2Selector", frozen=True, description="The name of the selection operator."
|
|
68
|
+
)
|
|
69
|
+
"""The name of the selection operator."""
|
|
70
|
+
population_size: int = Field(gt=0, description="The population size.")
|
|
71
|
+
"""The population size."""
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class IBEASelectorOptions(BaseModel):
|
|
75
|
+
"""Options for IBEA Selection."""
|
|
76
|
+
|
|
77
|
+
name: Literal["IBEASelector"] = Field(
|
|
78
|
+
default="IBEASelector", frozen=True, description="The name of the selection operator."
|
|
79
|
+
)
|
|
80
|
+
"""The name of the selection operator."""
|
|
81
|
+
population_size: int = Field(gt=0, description="The population size.")
|
|
82
|
+
"""The population size."""
|
|
83
|
+
kappa: float = Field(default=0.05, description="The kappa parameter for IBEA.")
|
|
84
|
+
"""The kappa parameter for IBEA."""
|
|
85
|
+
binary_indicator: Literal["eps", "hv"] = Field(default="eps", description="The binary indicator for IBEA.")
|
|
86
|
+
"""The binary indicator for IBEA."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
SelectorOptions = RVEASelectorOptions | NSGA2SelectorOptions | NSGA3SelectorOptions | IBEASelectorOptions
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def selection_constructor(
|
|
93
|
+
problem: Problem, options: SelectorOptions, publisher: Publisher, verbosity: int, seed: int
|
|
94
|
+
) -> BaseSelector:
|
|
95
|
+
"""Construct a selection operator from given options.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
problem (Problem): The optimization problem.
|
|
99
|
+
options (SelectorOptions): The options for the selection operator.
|
|
100
|
+
publisher (Publisher): The publisher to use for the operator.
|
|
101
|
+
verbosity (int): The verbosity level.
|
|
102
|
+
seed (int): The random seed.
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
BaseSelector: The constructed selection operator.
|
|
106
|
+
|
|
107
|
+
Raises:
|
|
108
|
+
ValueError: If an unknown selection operator name is provided.
|
|
109
|
+
"""
|
|
110
|
+
selection_types = {
|
|
111
|
+
"RVEASelector": RVEASelector,
|
|
112
|
+
"NSGA2Selector": NSGA2Selector,
|
|
113
|
+
"NSGA3Selector": NSGA3Selector,
|
|
114
|
+
"IBEASelector": IBEASelector,
|
|
115
|
+
}
|
|
116
|
+
options: dict = options.model_dump()
|
|
117
|
+
name = options.pop("name")
|
|
118
|
+
if name == "IBEASelector":
|
|
119
|
+
indi = options.pop("binary_indicator")
|
|
120
|
+
match indi:
|
|
121
|
+
case "eps":
|
|
122
|
+
options["binary_indicator"] = self_epsilon
|
|
123
|
+
case "hv":
|
|
124
|
+
options["binary_indicator"] = self_hv
|
|
125
|
+
case _:
|
|
126
|
+
raise ValueError(f"Unknown binary indicator: {indi}")
|
|
127
|
+
return selection_types[name](problem=problem, publisher=publisher, seed=seed, verbosity=verbosity, **options)
|