opytimark 3.0.0__tar.gz → 3.0.2__tar.gz
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.
- {opytimark-3.0.0 → opytimark-3.0.2}/PKG-INFO +25 -1
- {opytimark-3.0.0 → opytimark-3.0.2}/README.md +24 -0
- opytimark-3.0.2/opytimark/__init__.py +12 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/core/__init__.py +2 -1
- opytimark-3.0.2/opytimark/core/benchmark.py +296 -0
- opytimark-3.0.2/opytimark/core/cec_benchmark.py +240 -0
- opytimark-3.0.2/opytimark/data/__init__.py +2 -0
- opytimark-3.0.2/opytimark/logging.py +18 -0
- opytimark-3.0.2/opytimark/markers/__init__.py +2 -0
- opytimark-3.0.2/opytimark/markers/boolean.py +188 -0
- opytimark-3.0.2/opytimark/markers/cec/__init__.py +2 -0
- opytimark-3.0.2/opytimark/markers/cec/year_2005.py +1319 -0
- opytimark-3.0.2/opytimark/markers/cec/year_2008.py +338 -0
- opytimark-3.0.2/opytimark/markers/cec/year_2010.py +1352 -0
- opytimark-3.0.2/opytimark/markers/cec/year_2013.py +1388 -0
- opytimark-3.0.2/opytimark/markers/many_dimensional.py +635 -0
- opytimark-3.0.2/opytimark/markers/n_dimensional.py +2633 -0
- opytimark-3.0.2/opytimark/markers/one_dimensional.py +84 -0
- opytimark-3.0.2/opytimark/markers/two_dimensional.py +2876 -0
- opytimark-3.0.2/opytimark/typing.py +17 -0
- opytimark-3.0.2/opytimark/utils/__init__.py +2 -0
- opytimark-3.0.2/opytimark/utils/constants.py +8 -0
- opytimark-3.0.2/opytimark/utils/decorator.py +120 -0
- opytimark-3.0.2/opytimark/utils/exception.py +76 -0
- opytimark-3.0.2/opytimark/utils/loader.py +139 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/PKG-INFO +25 -1
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/SOURCES.txt +4 -1
- {opytimark-3.0.0 → opytimark-3.0.2}/pyproject.toml +8 -1
- opytimark-3.0.2/tests/test_conventions.py +277 -0
- opytimark-3.0.0/opytimark/__init__.py +0 -5
- opytimark-3.0.0/opytimark/core/benchmark.py +0 -91
- opytimark-3.0.0/opytimark/core/cec_benchmark.py +0 -133
- opytimark-3.0.0/opytimark/data/__init__.py +0 -1
- opytimark-3.0.0/opytimark/markers/__init__.py +0 -4
- opytimark-3.0.0/opytimark/markers/boolean.py +0 -93
- opytimark-3.0.0/opytimark/markers/cec/__init__.py +0 -3
- opytimark-3.0.0/opytimark/markers/cec/year_2005.py +0 -1130
- opytimark-3.0.0/opytimark/markers/cec/year_2008.py +0 -310
- opytimark-3.0.0/opytimark/markers/cec/year_2010.py +0 -1048
- opytimark-3.0.0/opytimark/markers/cec/year_2013.py +0 -1262
- opytimark-3.0.0/opytimark/markers/many_dimensional.py +0 -515
- opytimark-3.0.0/opytimark/markers/n_dimensional.py +0 -1950
- opytimark-3.0.0/opytimark/markers/one_dimensional.py +0 -55
- opytimark-3.0.0/opytimark/markers/two_dimensional.py +0 -2099
- opytimark-3.0.0/opytimark/utils/__init__.py +0 -1
- opytimark-3.0.0/opytimark/utils/constants.py +0 -7
- opytimark-3.0.0/opytimark/utils/decorator.py +0 -70
- opytimark-3.0.0/opytimark/utils/exception.py +0 -30
- opytimark-3.0.0/opytimark/utils/loader.py +0 -74
- {opytimark-3.0.0 → opytimark-3.0.2}/LICENSE +0 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/data/2005.tar.gz +0 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/data/2008.tar.gz +0 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/data/2010.tar.gz +0 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/data/2013.tar.gz +0 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/dependency_links.txt +0 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/requires.txt +0 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/top_level.txt +0 -0
- {opytimark-3.0.0 → opytimark-3.0.2}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: opytimark
|
|
3
|
-
Version: 3.0.
|
|
3
|
+
Version: 3.0.2
|
|
4
4
|
Summary: Python Optimization Benchmarking Functions
|
|
5
5
|
Author-email: Gustavo Rosa <gustavo.rosa@unesp.br>
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -43,6 +43,14 @@ Opytimark supports Python 3.11 or newer. Read the full API reference at
|
|
|
43
43
|
|
|
44
44
|
## Installation
|
|
45
45
|
|
|
46
|
+
Opytimark is published on PyPI. Add it to a project managed by uv with:
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
uv add opytimark
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For a consumer installation in an existing Python environment, pip is also supported:
|
|
53
|
+
|
|
46
54
|
```bash
|
|
47
55
|
pip install opytimark
|
|
48
56
|
```
|
|
@@ -60,8 +68,24 @@ print(value)
|
|
|
60
68
|
|
|
61
69
|
More examples are available in [`examples/`](examples).
|
|
62
70
|
|
|
71
|
+
## Reproducibility
|
|
72
|
+
|
|
73
|
+
Imports do not reset NumPy's random state. For reproducible noisy or randomized
|
|
74
|
+
benchmarks, call `np.random.seed(your_seed)` explicitly before the experiment.
|
|
75
|
+
|
|
76
|
+
The 3.0.1 CEC conditioning, group-rotation, and composition corrections
|
|
77
|
+
change some fitness values relative to 3.0.0. See
|
|
78
|
+
[numerical behavior](docs/numerical_behavior.rst) before comparing old and new
|
|
79
|
+
optimization results.
|
|
80
|
+
|
|
81
|
+
Library diagnostics use Python logging rather than writing to standard output.
|
|
82
|
+
Applications control log handlers, output streams, and levels.
|
|
83
|
+
|
|
63
84
|
## Development
|
|
64
85
|
|
|
86
|
+
Follow the [project conventions](CONVENTIONS.md) when changing Python code,
|
|
87
|
+
scientific documentation, or tests.
|
|
88
|
+
|
|
65
89
|
Install [uv](https://docs.astral.sh/uv/), clone the repository, then run:
|
|
66
90
|
|
|
67
91
|
```bash
|
|
@@ -14,6 +14,14 @@ Opytimark supports Python 3.11 or newer. Read the full API reference at
|
|
|
14
14
|
|
|
15
15
|
## Installation
|
|
16
16
|
|
|
17
|
+
Opytimark is published on PyPI. Add it to a project managed by uv with:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
uv add opytimark
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
For a consumer installation in an existing Python environment, pip is also supported:
|
|
24
|
+
|
|
17
25
|
```bash
|
|
18
26
|
pip install opytimark
|
|
19
27
|
```
|
|
@@ -31,8 +39,24 @@ print(value)
|
|
|
31
39
|
|
|
32
40
|
More examples are available in [`examples/`](examples).
|
|
33
41
|
|
|
42
|
+
## Reproducibility
|
|
43
|
+
|
|
44
|
+
Imports do not reset NumPy's random state. For reproducible noisy or randomized
|
|
45
|
+
benchmarks, call `np.random.seed(your_seed)` explicitly before the experiment.
|
|
46
|
+
|
|
47
|
+
The 3.0.1 CEC conditioning, group-rotation, and composition corrections
|
|
48
|
+
change some fitness values relative to 3.0.0. See
|
|
49
|
+
[numerical behavior](docs/numerical_behavior.rst) before comparing old and new
|
|
50
|
+
optimization results.
|
|
51
|
+
|
|
52
|
+
Library diagnostics use Python logging rather than writing to standard output.
|
|
53
|
+
Applications control log handlers, output streams, and levels.
|
|
54
|
+
|
|
34
55
|
## Development
|
|
35
56
|
|
|
57
|
+
Follow the [project conventions](CONVENTIONS.md) when changing Python code,
|
|
58
|
+
scientific documentation, or tests.
|
|
59
|
+
|
|
36
60
|
Install [uv](https://docs.astral.sh/uv/), clone the repository, then run:
|
|
37
61
|
|
|
38
62
|
```bash
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# Copyright (c) 2020-2026 Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
|
|
4
|
+
from logging import NullHandler
|
|
5
|
+
|
|
6
|
+
from opytimark.logging import get_logger
|
|
7
|
+
|
|
8
|
+
__version__ = "3.0.2"
|
|
9
|
+
|
|
10
|
+
_logger = get_logger(__name__)
|
|
11
|
+
if not any(isinstance(handler, NullHandler) for handler in _logger.handlers):
|
|
12
|
+
_logger.addHandler(NullHandler())
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
# Copyright (c) 2020-2026 Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
2
3
|
|
|
3
4
|
from opytimark.core.benchmark import Benchmark
|
|
4
5
|
from opytimark.core.cec_benchmark import CECBenchmark, CECCompositeBenchmark
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
# Copyright (c) 2020-2026 Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
|
|
4
|
+
from numpy.typing import ArrayLike
|
|
5
|
+
|
|
6
|
+
import opytimark.utils.exception as e
|
|
7
|
+
from opytimark.typing import BenchmarkValue
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class _Missing:
|
|
11
|
+
def __repr__(self) -> str:
|
|
12
|
+
return "<default>"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_MISSING = _Missing()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _validate_type(name: str, value: object, expected_type: type) -> None:
|
|
19
|
+
if not isinstance(value, expected_type):
|
|
20
|
+
raise e.TypeError(
|
|
21
|
+
f"`{name}` must be of type {expected_type.__name__}, but got {value!r} ({type(value).__name__})."
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class Benchmark:
|
|
26
|
+
"""
|
|
27
|
+
Base class for callable benchmarks with validated, mutable metadata.
|
|
28
|
+
|
|
29
|
+
"""
|
|
30
|
+
|
|
31
|
+
_defaults: tuple[str, int, bool, bool, bool, bool, bool] = ("Benchmark", 1, False, False, False, False, False)
|
|
32
|
+
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
name: str | _Missing = _MISSING,
|
|
36
|
+
dims: int | _Missing = _MISSING,
|
|
37
|
+
continuous: bool | _Missing = _MISSING,
|
|
38
|
+
convex: bool | _Missing = _MISSING,
|
|
39
|
+
differentiable: bool | _Missing = _MISSING,
|
|
40
|
+
multimodal: bool | _Missing = _MISSING,
|
|
41
|
+
separable: bool | _Missing = _MISSING,
|
|
42
|
+
) -> None:
|
|
43
|
+
"""Initialize benchmark metadata using the subclass defaults for omitted arguments.
|
|
44
|
+
|
|
45
|
+
Args:
|
|
46
|
+
name: Benchmark identifier used in diagnostics and CEC data-file selection.
|
|
47
|
+
dims: Dimension metadata interpreted by the benchmark's input validator.
|
|
48
|
+
continuous: Whether the benchmark is continuous.
|
|
49
|
+
convex: Whether the benchmark is convex.
|
|
50
|
+
differentiable: Whether the benchmark is differentiable.
|
|
51
|
+
multimodal: Whether the benchmark has multiple modes.
|
|
52
|
+
separable: Whether coordinates can be optimized independently.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
TypeError: A metadata value has the wrong type.
|
|
56
|
+
ValueError: The dimension count is below -1 or equal to zero.
|
|
57
|
+
|
|
58
|
+
Notes:
|
|
59
|
+
Omitted values come from the subclass's `_defaults` tuple.
|
|
60
|
+
Explicit values, including None, pass through property validation.
|
|
61
|
+
Validators may require an exact size, impose an upper bound, or select supported CEC rotations.
|
|
62
|
+
|
|
63
|
+
"""
|
|
64
|
+
|
|
65
|
+
supplied = (
|
|
66
|
+
name,
|
|
67
|
+
dims,
|
|
68
|
+
continuous,
|
|
69
|
+
convex,
|
|
70
|
+
differentiable,
|
|
71
|
+
multimodal,
|
|
72
|
+
separable,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
for field, value, default in zip(
|
|
76
|
+
(
|
|
77
|
+
"name",
|
|
78
|
+
"dims",
|
|
79
|
+
"continuous",
|
|
80
|
+
"convex",
|
|
81
|
+
"differentiable",
|
|
82
|
+
"multimodal",
|
|
83
|
+
"separable",
|
|
84
|
+
),
|
|
85
|
+
supplied,
|
|
86
|
+
self._defaults,
|
|
87
|
+
):
|
|
88
|
+
setattr(self, field, default if value is _MISSING else value)
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def name(self) -> str:
|
|
92
|
+
"""Return the benchmark identifier.
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Validated benchmark name.
|
|
96
|
+
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
return self._name
|
|
100
|
+
|
|
101
|
+
@name.setter
|
|
102
|
+
def name(self, name: str) -> None:
|
|
103
|
+
"""Set the benchmark identifier.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
name: Benchmark name used in diagnostics.
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
TypeError: The name is not a string.
|
|
110
|
+
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
_validate_type("name", name, str)
|
|
114
|
+
|
|
115
|
+
self._name = name
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def dims(self) -> int:
|
|
119
|
+
"""Return the benchmark's dimension metadata.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Positive dimension metadata or -1 for a variable-dimensional benchmark.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
|
|
126
|
+
return self._dims
|
|
127
|
+
|
|
128
|
+
@dims.setter
|
|
129
|
+
def dims(self, dims: int) -> None:
|
|
130
|
+
"""Set the benchmark's dimension metadata.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
dims: Positive dimension metadata or -1 for a variable-dimensional benchmark.
|
|
134
|
+
|
|
135
|
+
Raises:
|
|
136
|
+
TypeError: The dimension count is not an integer.
|
|
137
|
+
ValueError: The dimension count is below -1 or equal to zero.
|
|
138
|
+
|
|
139
|
+
"""
|
|
140
|
+
|
|
141
|
+
_validate_type("dims", dims, int)
|
|
142
|
+
if not (dims >= -1 and dims != 0):
|
|
143
|
+
raise e.ValueError(f"`dims` must be -1 or a positive integer, but got {dims!r}.")
|
|
144
|
+
|
|
145
|
+
self._dims = dims
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def continuous(self) -> bool:
|
|
149
|
+
"""Return whether the benchmark is continuous.
|
|
150
|
+
|
|
151
|
+
Returns:
|
|
152
|
+
True when the benchmark is marked continuous.
|
|
153
|
+
|
|
154
|
+
"""
|
|
155
|
+
|
|
156
|
+
return self._continuous
|
|
157
|
+
|
|
158
|
+
@continuous.setter
|
|
159
|
+
def continuous(self, continuous: bool) -> None:
|
|
160
|
+
"""Set the benchmark's continuity flag.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
continuous: Whether the benchmark is continuous.
|
|
164
|
+
|
|
165
|
+
Raises:
|
|
166
|
+
TypeError: The continuity flag is not a boolean.
|
|
167
|
+
|
|
168
|
+
"""
|
|
169
|
+
|
|
170
|
+
_validate_type("continuous", continuous, bool)
|
|
171
|
+
|
|
172
|
+
self._continuous = continuous
|
|
173
|
+
|
|
174
|
+
@property
|
|
175
|
+
def convex(self) -> bool:
|
|
176
|
+
"""Return whether the benchmark is convex.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
True when the benchmark is marked convex.
|
|
180
|
+
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
return self._convex
|
|
184
|
+
|
|
185
|
+
@convex.setter
|
|
186
|
+
def convex(self, convex: bool) -> None:
|
|
187
|
+
"""Set the benchmark's convexity flag.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
convex: Whether the benchmark is convex.
|
|
191
|
+
|
|
192
|
+
Raises:
|
|
193
|
+
TypeError: The convexity flag is not a boolean.
|
|
194
|
+
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
_validate_type("convex", convex, bool)
|
|
198
|
+
|
|
199
|
+
self._convex = convex
|
|
200
|
+
|
|
201
|
+
@property
|
|
202
|
+
def differentiable(self) -> bool:
|
|
203
|
+
"""Return whether the benchmark is differentiable.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
True when the benchmark is marked differentiable.
|
|
207
|
+
|
|
208
|
+
"""
|
|
209
|
+
|
|
210
|
+
return self._differentiable
|
|
211
|
+
|
|
212
|
+
@differentiable.setter
|
|
213
|
+
def differentiable(self, differentiable: bool) -> None:
|
|
214
|
+
"""Set the benchmark's differentiability flag.
|
|
215
|
+
|
|
216
|
+
Args:
|
|
217
|
+
differentiable: Whether the benchmark is differentiable.
|
|
218
|
+
|
|
219
|
+
Raises:
|
|
220
|
+
TypeError: The differentiability flag is not a boolean.
|
|
221
|
+
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
_validate_type("differentiable", differentiable, bool)
|
|
225
|
+
|
|
226
|
+
self._differentiable = differentiable
|
|
227
|
+
|
|
228
|
+
@property
|
|
229
|
+
def multimodal(self) -> bool:
|
|
230
|
+
"""Return whether the benchmark has multiple modes.
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
True when the benchmark is marked multimodal.
|
|
234
|
+
|
|
235
|
+
"""
|
|
236
|
+
|
|
237
|
+
return self._multimodal
|
|
238
|
+
|
|
239
|
+
@multimodal.setter
|
|
240
|
+
def multimodal(self, multimodal: bool) -> None:
|
|
241
|
+
"""Set the benchmark's multimodality flag.
|
|
242
|
+
|
|
243
|
+
Args:
|
|
244
|
+
multimodal: Whether the benchmark has multiple modes.
|
|
245
|
+
|
|
246
|
+
Raises:
|
|
247
|
+
TypeError: The multimodality flag is not a boolean.
|
|
248
|
+
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
_validate_type("multimodal", multimodal, bool)
|
|
252
|
+
|
|
253
|
+
self._multimodal = multimodal
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def separable(self) -> bool:
|
|
257
|
+
"""Return whether coordinates can be optimized independently.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
True when the benchmark is marked separable.
|
|
261
|
+
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
return self._separable
|
|
265
|
+
|
|
266
|
+
@separable.setter
|
|
267
|
+
def separable(self, separable: bool) -> None:
|
|
268
|
+
"""Set the benchmark's separability flag.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
separable: Whether coordinates can be optimized independently.
|
|
272
|
+
|
|
273
|
+
Raises:
|
|
274
|
+
TypeError: The separability flag is not a boolean.
|
|
275
|
+
|
|
276
|
+
"""
|
|
277
|
+
|
|
278
|
+
_validate_type("separable", separable, bool)
|
|
279
|
+
|
|
280
|
+
self._separable = separable
|
|
281
|
+
|
|
282
|
+
def __call__(self, x: ArrayLike) -> BenchmarkValue:
|
|
283
|
+
"""Evaluate the benchmark through a concrete implementation.
|
|
284
|
+
|
|
285
|
+
Args:
|
|
286
|
+
x: Numeric coordinates supplied to the benchmark.
|
|
287
|
+
|
|
288
|
+
Returns:
|
|
289
|
+
Benchmark value produced by a subclass implementation.
|
|
290
|
+
|
|
291
|
+
Raises:
|
|
292
|
+
NotImplementedError: The base benchmark has no numerical implementation.
|
|
293
|
+
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
raise NotImplementedError("`Benchmark.__call__` must be implemented by a subclass.")
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
# Copyright (c) 2020-2026 Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
|
|
4
|
+
from collections.abc import Callable, Iterable, Sequence
|
|
5
|
+
from typing import Any
|
|
6
|
+
|
|
7
|
+
import numpy as np
|
|
8
|
+
from numpy.typing import NDArray
|
|
9
|
+
|
|
10
|
+
import opytimark.utils.decorator as d
|
|
11
|
+
import opytimark.utils.loader as ld
|
|
12
|
+
from opytimark.core.benchmark import _MISSING, Benchmark, _Missing, _validate_type
|
|
13
|
+
from opytimark.typing import BenchmarkValue
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class CECBenchmark(Benchmark):
|
|
17
|
+
"""
|
|
18
|
+
Base class for CEC benchmarks backed by auxiliary data.
|
|
19
|
+
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
_year: str = ""
|
|
23
|
+
_auxiliary_data: tuple[str, ...] = ()
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
name: str | _Missing = _MISSING,
|
|
28
|
+
year: str | _Missing = _MISSING,
|
|
29
|
+
auxiliary_data: Iterable[str] | _Missing = _MISSING,
|
|
30
|
+
dims: int | _Missing = _MISSING,
|
|
31
|
+
continuous: bool | _Missing = _MISSING,
|
|
32
|
+
convex: bool | _Missing = _MISSING,
|
|
33
|
+
differentiable: bool | _Missing = _MISSING,
|
|
34
|
+
multimodal: bool | _Missing = _MISSING,
|
|
35
|
+
separable: bool | _Missing = _MISSING,
|
|
36
|
+
) -> None:
|
|
37
|
+
"""Initialize CEC metadata and load the requested auxiliary arrays.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
name: Benchmark name used as the auxiliary-data filename prefix.
|
|
41
|
+
year: CEC edition identifying the auxiliary-data archive.
|
|
42
|
+
auxiliary_data: Iterable of auxiliary variable names to load as instance attributes.
|
|
43
|
+
dims: Required or maximum dimension count for the benchmark's input validator.
|
|
44
|
+
continuous: Whether the benchmark is continuous.
|
|
45
|
+
convex: Whether the benchmark is convex.
|
|
46
|
+
differentiable: Whether the benchmark is differentiable.
|
|
47
|
+
multimodal: Whether the benchmark has multiple modes.
|
|
48
|
+
separable: Whether coordinates can be optimized independently.
|
|
49
|
+
|
|
50
|
+
Raises:
|
|
51
|
+
TypeError: A metadata value has the wrong type or auxiliary_data is not iterable.
|
|
52
|
+
ValueError: The dimension count or auxiliary data is invalid.
|
|
53
|
+
OSError: Requested auxiliary data cannot be read or downloaded.
|
|
54
|
+
|
|
55
|
+
Notes:
|
|
56
|
+
Omitted arguments use the subclass's `_defaults`, `_year`, and `_auxiliary_data` values.
|
|
57
|
+
Explicit None values are not treated as omitted.
|
|
58
|
+
Local auxiliary data takes precedence over bundled data, with remote loading as the final fallback.
|
|
59
|
+
Mutating metadata after initialization does not reload auxiliary data.
|
|
60
|
+
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
super().__init__(
|
|
64
|
+
name,
|
|
65
|
+
dims,
|
|
66
|
+
continuous,
|
|
67
|
+
convex,
|
|
68
|
+
differentiable,
|
|
69
|
+
multimodal,
|
|
70
|
+
separable,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self.year = self._year if year is _MISSING else year
|
|
74
|
+
|
|
75
|
+
data = self._auxiliary_data if auxiliary_data is _MISSING else auxiliary_data
|
|
76
|
+
self._load_auxiliary_data(self.name, self.year, data)
|
|
77
|
+
|
|
78
|
+
@property
|
|
79
|
+
def year(self) -> str:
|
|
80
|
+
"""Return the declared CEC edition.
|
|
81
|
+
|
|
82
|
+
Returns:
|
|
83
|
+
Validated CEC edition string.
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
return self._year_value
|
|
88
|
+
|
|
89
|
+
@year.setter
|
|
90
|
+
def year(self, year: str) -> None:
|
|
91
|
+
"""Set the CEC edition without reloading auxiliary arrays.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
year: CEC edition identifying the auxiliary-data archive.
|
|
95
|
+
|
|
96
|
+
Raises:
|
|
97
|
+
TypeError: The year is not a string.
|
|
98
|
+
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
_validate_type("year", year, str)
|
|
102
|
+
|
|
103
|
+
self._year_value = year
|
|
104
|
+
|
|
105
|
+
def _load_auxiliary_data(self, name: str, year: str, data: Iterable[str]) -> None:
|
|
106
|
+
for variable in data:
|
|
107
|
+
setattr(self, variable, ld.load_cec_auxiliary(f"{name}_{variable}", year))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
class CECCompositeBenchmark(CECBenchmark):
|
|
111
|
+
"""
|
|
112
|
+
Base class for shifted, scaled, and rotated CEC composite benchmarks.
|
|
113
|
+
|
|
114
|
+
"""
|
|
115
|
+
|
|
116
|
+
_bias = 1
|
|
117
|
+
|
|
118
|
+
def __init__(
|
|
119
|
+
self,
|
|
120
|
+
name: str | _Missing = _MISSING,
|
|
121
|
+
year: str | _Missing = _MISSING,
|
|
122
|
+
auxiliary_data: Iterable[str] | _Missing = _MISSING,
|
|
123
|
+
sigma: Sequence[float] | NDArray[Any] = (),
|
|
124
|
+
l: Sequence[float] | NDArray[Any] = (),
|
|
125
|
+
functions: Sequence[Callable[[NDArray[Any]], BenchmarkValue]] = (),
|
|
126
|
+
bias: BenchmarkValue | _Missing = _MISSING,
|
|
127
|
+
dims: int | _Missing = _MISSING,
|
|
128
|
+
continuous: bool | _Missing = _MISSING,
|
|
129
|
+
convex: bool | _Missing = _MISSING,
|
|
130
|
+
differentiable: bool | _Missing = _MISSING,
|
|
131
|
+
multimodal: bool | _Missing = _MISSING,
|
|
132
|
+
separable: bool | _Missing = _MISSING,
|
|
133
|
+
) -> None:
|
|
134
|
+
"""Initialize a CEC composition while retaining caller-supplied component objects.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
name: Benchmark name used as the auxiliary-data filename prefix.
|
|
138
|
+
year: CEC edition identifying the auxiliary-data archive.
|
|
139
|
+
auxiliary_data: Iterable of auxiliary variable names to load as instance attributes.
|
|
140
|
+
sigma: Component weight widths in evaluation order.
|
|
141
|
+
l: Component coordinate scales in evaluation order.
|
|
142
|
+
functions: Component callables evaluated in the supplied order.
|
|
143
|
+
bias: Overall additive bias, using the subclass's _bias value when omitted.
|
|
144
|
+
dims: Dimension metadata retained for compatibility with the shared benchmark constructor.
|
|
145
|
+
continuous: Whether the benchmark is continuous.
|
|
146
|
+
convex: Whether the benchmark is convex.
|
|
147
|
+
differentiable: Whether the benchmark is differentiable.
|
|
148
|
+
multimodal: Whether the benchmark has multiple modes.
|
|
149
|
+
separable: Whether coordinates can be optimized independently.
|
|
150
|
+
|
|
151
|
+
Raises:
|
|
152
|
+
TypeError: A metadata value has the wrong type or auxiliary_data is not iterable.
|
|
153
|
+
ValueError: The dimension count or auxiliary data is invalid.
|
|
154
|
+
OSError: Requested auxiliary data cannot be read or downloaded.
|
|
155
|
+
|
|
156
|
+
Notes:
|
|
157
|
+
Metadata and auxiliary-data defaults follow CECBenchmark.
|
|
158
|
+
Component sequences are retained without conversion or length validation.
|
|
159
|
+
Initialization supplies C = 2000 and the ten historical component biases.
|
|
160
|
+
|
|
161
|
+
"""
|
|
162
|
+
|
|
163
|
+
super().__init__(
|
|
164
|
+
name,
|
|
165
|
+
year,
|
|
166
|
+
auxiliary_data,
|
|
167
|
+
dims,
|
|
168
|
+
continuous,
|
|
169
|
+
convex,
|
|
170
|
+
differentiable,
|
|
171
|
+
multimodal,
|
|
172
|
+
separable,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
self._initialize_composition(
|
|
176
|
+
sigma,
|
|
177
|
+
l,
|
|
178
|
+
functions,
|
|
179
|
+
self._bias if bias is _MISSING else bias,
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
def _initialize_composition(
|
|
183
|
+
self,
|
|
184
|
+
sigma: Sequence[float] | NDArray[Any],
|
|
185
|
+
scale: Sequence[float] | NDArray[Any],
|
|
186
|
+
functions: Sequence[Callable[[NDArray[Any]], BenchmarkValue]],
|
|
187
|
+
bias: BenchmarkValue,
|
|
188
|
+
) -> None:
|
|
189
|
+
self.sigma = sigma
|
|
190
|
+
self.l = scale
|
|
191
|
+
self.f = functions
|
|
192
|
+
self.bias = bias
|
|
193
|
+
self.C = 2000
|
|
194
|
+
self.f_bias = (0, 100, 200, 300, 400, 500, 600, 700, 800, 900)
|
|
195
|
+
|
|
196
|
+
@d.check_exact_dimension_and_auxiliary_matrix
|
|
197
|
+
def __call__(self, x: NDArray[Any]) -> BenchmarkValue:
|
|
198
|
+
"""Evaluate the weighted CEC composition and add its overall bias.
|
|
199
|
+
|
|
200
|
+
Args:
|
|
201
|
+
x: Numeric coordinates with first-axis length 2, 10, 30, or 50.
|
|
202
|
+
|
|
203
|
+
Returns:
|
|
204
|
+
Weighted component fitness plus the overall bias, without converting the result type.
|
|
205
|
+
|
|
206
|
+
Raises:
|
|
207
|
+
SizeError: The input's first-axis length is unsupported.
|
|
208
|
+
|
|
209
|
+
Notes:
|
|
210
|
+
Positional and keyword inputs are normalized without forcing a floating-point dtype.
|
|
211
|
+
A singleton second axis is removed, other axes are retained, and the input is not mutated.
|
|
212
|
+
The matching M2, M10, M30, or M50 rotation array is selected as M.
|
|
213
|
+
Each component is evaluated at its scaled, rotated reference point before its shifted input.
|
|
214
|
+
Relative exponential weights are attenuated before normalization to avoid numerical underflow.
|
|
215
|
+
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
return self._evaluate_composition(x) + self.bias
|
|
219
|
+
|
|
220
|
+
def _evaluate_composition(self, x: NDArray[Any]) -> BenchmarkValue:
|
|
221
|
+
dimension = x.shape[0]
|
|
222
|
+
log_weights = np.zeros(len(self.f))
|
|
223
|
+
fitness = np.zeros(len(self.f))
|
|
224
|
+
reference = 5 * np.ones(dimension)
|
|
225
|
+
|
|
226
|
+
for index, function in enumerate(self.f):
|
|
227
|
+
start = index * dimension
|
|
228
|
+
end = start + dimension
|
|
229
|
+
shifted = x - self.o[index][:dimension]
|
|
230
|
+
log_weights[index] = -np.sum(shifted**2) / (2 * dimension * self.sigma[index] ** 2)
|
|
231
|
+
normalizer = function(np.matmul(reference / self.l[index], self.M[start:end]))
|
|
232
|
+
fitness[index] = self.C * function(np.matmul(shifted / self.l[index], self.M[start:end])) / normalizer
|
|
233
|
+
|
|
234
|
+
# Relative exponentials avoid underflow before weight normalization
|
|
235
|
+
maximum = np.max(log_weights)
|
|
236
|
+
weights = np.exp(log_weights - maximum)
|
|
237
|
+
weights[log_weights != maximum] *= -np.expm1(10 * maximum)
|
|
238
|
+
weights /= np.sum(weights)
|
|
239
|
+
|
|
240
|
+
return np.matmul(weights, fitness + self.f_bias)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Copyright (c) 2020-2026 Gustavo de Rosa.
|
|
2
|
+
# Licensed under the Apache License, Version 2.0.
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def get_logger(name: str) -> logging.Logger:
|
|
8
|
+
"""Return a logger without configuring application output.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
name: Fully qualified name of the module requesting the logger.
|
|
12
|
+
|
|
13
|
+
Returns:
|
|
14
|
+
Standard-library logger with application-controlled handlers, levels, and propagation.
|
|
15
|
+
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
return logging.getLogger(name)
|