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.
Files changed (58) hide show
  1. {opytimark-3.0.0 → opytimark-3.0.2}/PKG-INFO +25 -1
  2. {opytimark-3.0.0 → opytimark-3.0.2}/README.md +24 -0
  3. opytimark-3.0.2/opytimark/__init__.py +12 -0
  4. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/core/__init__.py +2 -1
  5. opytimark-3.0.2/opytimark/core/benchmark.py +296 -0
  6. opytimark-3.0.2/opytimark/core/cec_benchmark.py +240 -0
  7. opytimark-3.0.2/opytimark/data/__init__.py +2 -0
  8. opytimark-3.0.2/opytimark/logging.py +18 -0
  9. opytimark-3.0.2/opytimark/markers/__init__.py +2 -0
  10. opytimark-3.0.2/opytimark/markers/boolean.py +188 -0
  11. opytimark-3.0.2/opytimark/markers/cec/__init__.py +2 -0
  12. opytimark-3.0.2/opytimark/markers/cec/year_2005.py +1319 -0
  13. opytimark-3.0.2/opytimark/markers/cec/year_2008.py +338 -0
  14. opytimark-3.0.2/opytimark/markers/cec/year_2010.py +1352 -0
  15. opytimark-3.0.2/opytimark/markers/cec/year_2013.py +1388 -0
  16. opytimark-3.0.2/opytimark/markers/many_dimensional.py +635 -0
  17. opytimark-3.0.2/opytimark/markers/n_dimensional.py +2633 -0
  18. opytimark-3.0.2/opytimark/markers/one_dimensional.py +84 -0
  19. opytimark-3.0.2/opytimark/markers/two_dimensional.py +2876 -0
  20. opytimark-3.0.2/opytimark/typing.py +17 -0
  21. opytimark-3.0.2/opytimark/utils/__init__.py +2 -0
  22. opytimark-3.0.2/opytimark/utils/constants.py +8 -0
  23. opytimark-3.0.2/opytimark/utils/decorator.py +120 -0
  24. opytimark-3.0.2/opytimark/utils/exception.py +76 -0
  25. opytimark-3.0.2/opytimark/utils/loader.py +139 -0
  26. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/PKG-INFO +25 -1
  27. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/SOURCES.txt +4 -1
  28. {opytimark-3.0.0 → opytimark-3.0.2}/pyproject.toml +8 -1
  29. opytimark-3.0.2/tests/test_conventions.py +277 -0
  30. opytimark-3.0.0/opytimark/__init__.py +0 -5
  31. opytimark-3.0.0/opytimark/core/benchmark.py +0 -91
  32. opytimark-3.0.0/opytimark/core/cec_benchmark.py +0 -133
  33. opytimark-3.0.0/opytimark/data/__init__.py +0 -1
  34. opytimark-3.0.0/opytimark/markers/__init__.py +0 -4
  35. opytimark-3.0.0/opytimark/markers/boolean.py +0 -93
  36. opytimark-3.0.0/opytimark/markers/cec/__init__.py +0 -3
  37. opytimark-3.0.0/opytimark/markers/cec/year_2005.py +0 -1130
  38. opytimark-3.0.0/opytimark/markers/cec/year_2008.py +0 -310
  39. opytimark-3.0.0/opytimark/markers/cec/year_2010.py +0 -1048
  40. opytimark-3.0.0/opytimark/markers/cec/year_2013.py +0 -1262
  41. opytimark-3.0.0/opytimark/markers/many_dimensional.py +0 -515
  42. opytimark-3.0.0/opytimark/markers/n_dimensional.py +0 -1950
  43. opytimark-3.0.0/opytimark/markers/one_dimensional.py +0 -55
  44. opytimark-3.0.0/opytimark/markers/two_dimensional.py +0 -2099
  45. opytimark-3.0.0/opytimark/utils/__init__.py +0 -1
  46. opytimark-3.0.0/opytimark/utils/constants.py +0 -7
  47. opytimark-3.0.0/opytimark/utils/decorator.py +0 -70
  48. opytimark-3.0.0/opytimark/utils/exception.py +0 -30
  49. opytimark-3.0.0/opytimark/utils/loader.py +0 -74
  50. {opytimark-3.0.0 → opytimark-3.0.2}/LICENSE +0 -0
  51. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/data/2005.tar.gz +0 -0
  52. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/data/2008.tar.gz +0 -0
  53. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/data/2010.tar.gz +0 -0
  54. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark/data/2013.tar.gz +0 -0
  55. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/dependency_links.txt +0 -0
  56. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/requires.txt +0 -0
  57. {opytimark-3.0.0 → opytimark-3.0.2}/opytimark.egg-info/top_level.txt +0 -0
  58. {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.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
- """A core package for all common opytimark modules."""
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,2 @@
1
+ # Copyright (c) 2020-2026 Gustavo de Rosa.
2
+ # Licensed under the Apache License, Version 2.0.
@@ -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)
@@ -0,0 +1,2 @@
1
+ # Copyright (c) 2020-2026 Gustavo de Rosa.
2
+ # Licensed under the Apache License, Version 2.0.