defoundry 0.1.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.
@@ -0,0 +1,357 @@
1
+ Metadata-Version: 2.4
2
+ Name: defoundry
3
+ Version: 0.1.0
4
+ Summary: Composable Differential Evolution optimizer
5
+ Author: Honza
6
+ License-Expression: MIT
7
+ Project-URL: Repository, https://github.com/vrbaj/differential_evolution
8
+ Project-URL: Issues, https://github.com/vrbaj/differential_evolution/issues
9
+ Keywords: optimization,differential-evolution,global-optimization
10
+ Classifier: Programming Language :: Python :: 3
11
+ Classifier: Programming Language :: Python :: 3 :: Only
12
+ Classifier: Programming Language :: Python :: 3.11
13
+ Classifier: Programming Language :: Python :: 3.12
14
+ Classifier: Programming Language :: Python :: 3.13
15
+ Classifier: Programming Language :: Python :: 3.14
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Typing :: Typed
18
+ Classifier: Topic :: Scientific/Engineering :: Mathematics
19
+ Requires-Python: >=3.11
20
+ Description-Content-Type: text/markdown
21
+ License-File: LICENSE
22
+ Provides-Extra: test
23
+ Requires-Dist: pytest>=7; extra == "test"
24
+ Requires-Dist: mypy>=1.10; extra == "test"
25
+ Requires-Dist: ruff>=0.6; extra == "test"
26
+ Provides-Extra: release
27
+ Requires-Dist: build>=1.2; extra == "release"
28
+ Requires-Dist: twine>=6; extra == "release"
29
+ Provides-Extra: docs
30
+ Requires-Dist: Sphinx<9,>=8; extra == "docs"
31
+ Requires-Dist: sphinxcontrib-bibtex<3,>=2.6; extra == "docs"
32
+ Requires-Dist: sphinx-rtd-theme<4,>=3; extra == "docs"
33
+ Dynamic: license-file
34
+
35
+ # DEFoundry — Differential Evolution
36
+
37
+ This repository now provides a small, explicit Differential Evolution package with independently composable mutation strategies and crossover operators.
38
+
39
+ ## What changed
40
+
41
+ - The optimizer is now a real Python package: `differential_evolution/`
42
+ - Mutation and crossover are separate components
43
+ - Randomness is explicit and reproducible
44
+ - A deterministic test suite verifies formulas and DE invariants
45
+ - The original `main.py`, `population_initialization.py`, and `testing_functions.py` remain as compatibility shims
46
+ - Full `SHADE` and `LSHADE` optimizers are available for success-history adaptive DE
47
+
48
+ ## Installation
49
+
50
+ DEFoundry requires Python 3.11 or later and has no mandatory runtime dependencies.
51
+ The distribution name is `defoundry`; the Python import is `differential_evolution`.
52
+
53
+ ```bash
54
+ python3 -m pip install defoundry
55
+ ```
56
+
57
+ For development, install from a source checkout with `python3 -m pip install -e .`.
58
+ Maintainers can follow the [release guide](https://github.com/vrbaj/differential_evolution/blob/master/RELEASE.md)
59
+ to validate and publish distributions.
60
+
61
+ ## Core concepts
62
+
63
+ - target vector: the current population member being evolved
64
+ - donor vector: the output of a mutation strategy
65
+ - trial vector: the result of applying a crossover operator to the target and donor
66
+
67
+ The optimizer orchestrates:
68
+
69
+ 1. population initialization
70
+ 2. donor-vector mutation
71
+ 3. target/donor crossover
72
+ 4. boundary handling
73
+ 5. objective evaluation
74
+ 6. greedy one-to-one selection
75
+
76
+ ## Built-in mutation strategies
77
+
78
+ - `Rand1`
79
+ - `Rand2`
80
+ - `Best1`
81
+ - `Best2`
82
+ - `CurrentToBest1`
83
+ - `CurrentToBest2`
84
+ - `CurrentToRand1`
85
+ - `CurrentToRand2`
86
+ - `TrigonometricMutation`
87
+ - `DirectedMutation`
88
+ - `NeighborhoodSearchMutation`
89
+
90
+ ## Built-in scale-factor controllers
91
+
92
+ - `ConstantScaleFactor`
93
+ - `RandomizedScaleFactor`
94
+ - `AdaptiveScaleFactor`
95
+
96
+ ## Built-in crossover-rate controllers
97
+
98
+ - `ConstantCrossoverRate`
99
+ - `AdaptiveCrossoverRate`
100
+
101
+ These can be passed anywhere a mutation strategy expects `scale` or `difference_scale`.
102
+
103
+ ```python
104
+ from differential_evolution import AdaptiveScaleFactor, Rand1
105
+
106
+ mutation = Rand1(scale=AdaptiveScaleFactor(initial=0.5, tau=0.1, lower=0.1, upper=0.9))
107
+ ```
108
+
109
+ For the Brest et al. paper's full `jDE` control-parameter scheme, pair `AdaptiveScaleFactor` with `AdaptiveCrossoverRate`, or use the convenience helper:
110
+
111
+ ```python
112
+ from differential_evolution import DifferentialEvolution, jde_rand_1_bin, sphere_function
113
+
114
+ components = jde_rand_1_bin()
115
+ optimizer = DifferentialEvolution(
116
+ objective=sphere_function,
117
+ bounds=[(-5.0, 5.0), (-5.0, 5.0)],
118
+ population_size=20,
119
+ mutation=components.mutation,
120
+ crossover=components.crossover,
121
+ max_generations=100,
122
+ seed=123,
123
+ )
124
+ ```
125
+
126
+ ## Built-in crossover operators
127
+
128
+ - `BinomialCrossover`
129
+ - `ExponentialCrossover`
130
+ - `IdentityCrossover`
131
+
132
+ `IdentityCrossover` is useful for canonical current-to-rand style runs where no extra crossover is desired.
133
+
134
+ ## Built-in diversity measures
135
+
136
+ - `PopulationDiameter`
137
+ - `PopulationRadius`
138
+ - `AverageDistanceAroundPopulationCenter`
139
+ - `AverageDistanceAroundAllIndividuals`
140
+ - `PopulationCoherence`
141
+ - `DimensionalVariance`
142
+ - `AggregatedDistribution`
143
+ - `AveragePairwiseDistance`
144
+
145
+ You can disable diversity tracking by leaving `diversity_measures=None`, or track several measures at once:
146
+
147
+ ```python
148
+ from differential_evolution import (
149
+ AveragePairwiseDistance,
150
+ DifferentialEvolution,
151
+ PopulationDiameter,
152
+ Rand1,
153
+ BinomialCrossover,
154
+ sphere_function,
155
+ )
156
+
157
+ optimizer = DifferentialEvolution(
158
+ objective=sphere_function,
159
+ bounds=[(-5.0, 5.0), (-5.0, 5.0)],
160
+ population_size=20,
161
+ mutation=Rand1(scale=0.8),
162
+ crossover=BinomialCrossover(crossover_rate=0.9),
163
+ diversity_measures=[
164
+ PopulationDiameter(),
165
+ AveragePairwiseDistance(),
166
+ "population_coherence",
167
+ ],
168
+ max_generations=50,
169
+ seed=123,
170
+ )
171
+ result = optimizer.run()
172
+ print(result.diversity_history["population_diameter"])
173
+ ```
174
+
175
+ `PopulationCoherence` compares the movement of the population center between consecutive generations with the average movement of individuals. Its first recorded value is `0.0` because there is no previous generation yet.
176
+
177
+ `AggregatedDistribution` is implemented here as a project-specific marginal dispersion statistic: each coordinate is normalized to `[0, 1]`, binned, and summarized by the variance-to-mean ratio of occupancy counts.
178
+
179
+ ## Built-in initializers
180
+
181
+ - `RandomInitializer`
182
+ - `TentInitializer`
183
+ - `OppositionInitializer`
184
+ - `QuasiOppositionInitializer`
185
+ - `SobolInitializer`
186
+
187
+ `SobolInitializer` supports up to 40 dimensions and `2**30` sequence points.
188
+ It applies a seed-dependent digital shift by default; pass `scramble=False`
189
+ for the unshifted sequence. See the current
190
+ [algorithm reference](https://github.com/vrbaj/differential_evolution/blob/master/docs/algorithm_reference.rst#initialization).
191
+
192
+ ## Population reduction
193
+
194
+ The optimizer can optionally reduce population size during the run:
195
+
196
+ - `LinearPopulationReduction`
197
+ - `HyperbolicTangentPopulationReduction`
198
+
199
+ ```python
200
+ from differential_evolution import (
201
+ BinomialCrossover,
202
+ DifferentialEvolution,
203
+ LinearPopulationReduction,
204
+ Rand1,
205
+ sphere_function,
206
+ )
207
+
208
+ optimizer = DifferentialEvolution(
209
+ objective=sphere_function,
210
+ bounds=[(-5.0, 5.0), (-5.0, 5.0)],
211
+ population_size=20,
212
+ mutation=Rand1(scale=0.8),
213
+ crossover=BinomialCrossover(crossover_rate=0.9),
214
+ population_schedule=LinearPopulationReduction(min_population_size=4),
215
+ max_evaluations=2000,
216
+ seed=123,
217
+ )
218
+ ```
219
+
220
+ `LinearPopulationReduction` follows the L-SHADE idea of shrinking linearly with used function-evaluation budget when `max_evaluations` is provided. If `max_evaluations` is omitted, it falls back to generation progress. `HyperbolicTangentPopulationReduction` is a project-specific smooth nonlinear schedule.
221
+
222
+ Supply at least one stopping limit. `max_generations` defaults to `None`, so an evaluation budget alone runs until that budget is exhausted. If both limits are supplied, the first one reached stops the run.
223
+
224
+ When the population is reduced, the worst individuals by current fitness are removed. Users must choose a minimum population size that remains compatible with the mutation strategy in use.
225
+
226
+ ## SHADE and L-SHADE
227
+
228
+ The package also includes dedicated optimizers for the full success-history adaptive algorithms:
229
+
230
+ - `SHADE`
231
+ - `LSHADE`
232
+
233
+ `SHADE` includes:
234
+
235
+ - `current-to-pbest/1`
236
+ - external archive
237
+ - Cauchy sampling of `F`
238
+ - Gaussian sampling of `CR`
239
+ - success-history memories `M_F` and `M_CR`
240
+
241
+ `LSHADE` extends `SHADE` with linear population size reduction.
242
+
243
+ ```python
244
+ from differential_evolution import LSHADE, sphere_function
245
+
246
+ optimizer = LSHADE(
247
+ objective=sphere_function,
248
+ bounds=[(-5.0, 5.0), (-5.0, 5.0)],
249
+ population_size=18,
250
+ min_population_size=4,
251
+ memory_size=6,
252
+ max_evaluations=2000,
253
+ seed=123,
254
+ )
255
+ result = optimizer.run()
256
+ ```
257
+
258
+ ## Saving runs
259
+
260
+ For post-run investigation, enable `record_snapshots=True` and save the result:
261
+
262
+ ```python
263
+ result = optimizer.run()
264
+ result.save_json("shade_run.json")
265
+ result.save_pickle("shade_run.pkl")
266
+ ```
267
+
268
+ Each snapshot stores the generation number, evaluation count, best solution, population size, optional population and fitness arrays, diversity values, and algorithm-specific extra state. For `SHADE` and `LSHADE`, the extra state includes the external archive and the parameter memories.
269
+
270
+ ## Example
271
+
272
+ ```python
273
+ from differential_evolution import (
274
+ BinomialCrossover,
275
+ DifferentialEvolution,
276
+ ExponentialCrossover,
277
+ Rand1,
278
+ Best1,
279
+ sphere_function,
280
+ )
281
+
282
+ optimizer = DifferentialEvolution(
283
+ objective=sphere_function,
284
+ bounds=[(-5.0, 5.0), (-5.0, 5.0)],
285
+ population_size=20,
286
+ mutation=Rand1(scale=0.8),
287
+ crossover=BinomialCrossover(crossover_rate=0.9),
288
+ max_generations=50,
289
+ seed=123,
290
+ )
291
+ result = optimizer.run()
292
+
293
+ optimizer_alt = DifferentialEvolution(
294
+ objective=sphere_function,
295
+ bounds=[(-5.0, 5.0), (-5.0, 5.0)],
296
+ population_size=20,
297
+ mutation=Best1(scale=0.5),
298
+ crossover=ExponentialCrossover(crossover_rate=0.8),
299
+ max_generations=50,
300
+ seed=123,
301
+ )
302
+ result_alt = optimizer_alt.run()
303
+ ```
304
+
305
+ ## Custom components
306
+
307
+ Any callable with the right signature can be injected.
308
+
309
+ ```python
310
+ class MyMutation:
311
+ def __call__(self, context):
312
+ target = context.population[context.target_index]
313
+ return [value * 0.5 for value in target]
314
+
315
+
316
+ class MyCrossover:
317
+ def __call__(self, target_vector, donor_vector, rng):
318
+ trial = list(target_vector)
319
+ trial[0] = donor_vector[0]
320
+ return trial
321
+ ```
322
+
323
+ ## Reproducibility
324
+
325
+ Use `seed=` or pass a custom RNG object. The optimizer does not use module-level global random state.
326
+
327
+ ## Compatibility
328
+
329
+ The original constructor remains available from a source checkout only:
330
+
331
+ ```python
332
+ from main import DifferentialEvolution
333
+ ```
334
+
335
+ Installed users can use `from differential_evolution.compat import DifferentialEvolution`.
336
+ Both paths emit a deprecation warning and forward into the new implementation.
337
+ Details are in [MIGRATION.md](https://github.com/vrbaj/differential_evolution/blob/master/MIGRATION.md).
338
+
339
+ ## Verification
340
+
341
+ - algorithm audit: [ALGORITHM_AUDIT.md](https://github.com/vrbaj/differential_evolution/blob/master/ALGORITHM_AUDIT.md)
342
+ - migration notes: [MIGRATION.md](https://github.com/vrbaj/differential_evolution/blob/master/MIGRATION.md)
343
+ - tests: `python3 -m unittest discover -s tests -v`
344
+ - examples: `python3 -m examples.basic_usage`, `python3 -m examples.custom_components`, `python3 -m examples.diversity_measures`, and `python3 -m examples.population_initialization`
345
+
346
+ ### Review corrections and compatibility
347
+
348
+ See [review fixes](https://github.com/vrbaj/differential_evolution/blob/master/docs/review_fixes.md) for changes and the disposition of the
349
+ reported findings. Plain DE now clips to bounds by default; SHADE/L-SHADE use
350
+ parent-aware midpoint repair. Mutation and crossover components are copied per
351
+ optimizer; inspect adapted state on `optimizer.mutation` and `optimizer.crossover`.
352
+ `run()` is single-use. `result.success` means a usable objective value was found,
353
+ not numerical convergence; `result.message` reports the exhausted limit.
354
+
355
+ Sobol initialization uses a seeded digital shift by default. Use
356
+ `SobolInitializer(scramble=False)` for the original unshifted sequence. All
357
+ fixed-dimensional benchmarks, including Ackley, require exactly two coordinates.
@@ -0,0 +1,24 @@
1
+ defoundry-0.1.0.dist-info/licenses/LICENSE,sha256=J51U_0iHTQ2nz798bkyc7rPSYITcGVsMNf2KlhZTBfA,1062
2
+ differential_evolution/__init__.py,sha256=E0DHLP7guIXv8ZmJtzimn9Og9eYvf2-ZJY_tFaMyhLA,3433
3
+ differential_evolution/_numeric.py,sha256=2AgpmOjZGXVKTAwkxkfQodL-c5vD4Iyg64Os93ccVWo,2595
4
+ differential_evolution/benchmarks.py,sha256=GfkBaliJjQmE8HVe-gVJbaWVpLKePIrZ5vNbbxjVfLA,7378
5
+ differential_evolution/boundaries.py,sha256=CnDToPfD9v2gz8IUS7D_zTMlo2126muR5gdcb1ZB_BQ,3951
6
+ differential_evolution/compat.py,sha256=Tvh1uY-VUIz2xZvDecvMpsrhZTocdpXD-S1xBzq24FA,6740
7
+ differential_evolution/crossover_rates.py,sha256=n2IqdiFZOns_DUdCc7VMqoqt8vsTSSNySJG7aDmEIn0,3644
8
+ differential_evolution/crossovers.py,sha256=MLJLVTP3VnDYsEi7bWOAOJE5kzmu69lfdfzuKZS_QGM,6391
9
+ differential_evolution/diversity.py,sha256=4zCpk_TVhzjlh7sqf7NCMwcebHUG0lLMxB-6mk3Gy_8,12769
10
+ differential_evolution/history.py,sha256=t6RI3qnn8hSFng0XFZrOqCUalhkCR4V9ZErNo14m-5U,1508
11
+ differential_evolution/initializers.py,sha256=Ni4mZ-NeU3tu4gjtu75iIu41R0UMTt62fd8u6RMo0VI,12846
12
+ differential_evolution/jde.py,sha256=S7Z6jqTxJDXgCfohyAbSbFv-nOMqWy-7A7ytKiq9QoM,1562
13
+ differential_evolution/mutation.py,sha256=JlM0kfCpx6SreEslV-M6s95gdWH5Lk9YHnGC1c4BhHM,22987
14
+ differential_evolution/optimizer.py,sha256=wLjY2xEIHrChteRrC3XFNHFbxX4Jp2VELQ4pK7REq4s,21588
15
+ differential_evolution/population_schedules.py,sha256=dHYxj8hzLwexe1Ehu7LxEqP5y0xRwrJRVi3MXg40swM,3817
16
+ differential_evolution/protocols.py,sha256=Z1ry7-PccxM7C2DqmpY4LR2bis5SgkaVdqGxNVK9vB0,1331
17
+ differential_evolution/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ differential_evolution/result.py,sha256=XtJrCtJMiHaqUxzyUIEE78KvhF3W_E9YtMBz0dyj5Cg,1337
19
+ differential_evolution/scales.py,sha256=11rUGaA45aJD8ahPzIesxghFG3Hu2-r9WyMPKkjQomQ,4898
20
+ differential_evolution/shade.py,sha256=QPCwpGxlCbbSwaXX3z66SrDhvXLjwB75Ck3WH9OcH9Y,14206
21
+ defoundry-0.1.0.dist-info/METADATA,sha256=bF00U2Ds0dnDWdFlOu7qSu_n7SIfWesVp5YB2d8nHOg,11771
22
+ defoundry-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
23
+ defoundry-0.1.0.dist-info/top_level.txt,sha256=_dSOtYkjdxsSYRfQxQONLBWvAIpRSGToBjmCJYO3f7k,23
24
+ defoundry-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (84.0.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Honza
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ differential_evolution
@@ -0,0 +1,134 @@
1
+ """Composable Differential Evolution optimizer."""
2
+
3
+ from .benchmarks import (
4
+ ackley_function,
5
+ beale_function,
6
+ booth_function,
7
+ bukin_function,
8
+ easom_function,
9
+ eggholder_function,
10
+ goldstein_price_function,
11
+ himmelblau_function,
12
+ levi_function,
13
+ mccormick_function,
14
+ matyas_function,
15
+ rastrigin_function,
16
+ schaffer_n2_function,
17
+ sphere_function,
18
+ three_hump_camel_function,
19
+ )
20
+ from .boundaries import (
21
+ ClipBoundaryHandler,
22
+ NoBoundaryHandler,
23
+ RandomResetBoundaryHandler,
24
+ MidpointBoundaryHandler,
25
+ ReflectionBoundaryHandler,
26
+ )
27
+ from .crossovers import BinomialCrossover, ExponentialCrossover, IdentityCrossover
28
+ from .crossover_rates import AdaptiveCrossoverRate, ConstantCrossoverRate
29
+ from .initializers import (
30
+ OppositionInitializer,
31
+ QuasiOppositionInitializer,
32
+ RandomInitializer,
33
+ SobolInitializer,
34
+ TentInitializer,
35
+ )
36
+ from .diversity import (
37
+ AggregatedDistribution,
38
+ AverageDistanceAroundAllIndividuals,
39
+ AverageDistanceAroundPopulationCenter,
40
+ AveragePairwiseDistance,
41
+ DimensionalVariance,
42
+ PopulationCoherence,
43
+ PopulationDiameter,
44
+ PopulationRadius,
45
+ )
46
+ from .jde import JDEComponents, jde_rand_1_bin
47
+ from .mutation import (
48
+ Best1,
49
+ Best2,
50
+ CurrentToBest1,
51
+ CurrentToBest2,
52
+ CurrentToRand1,
53
+ CurrentToRand2,
54
+ DirectedMutation,
55
+ MutationContext,
56
+ NeighborhoodSearchMutation,
57
+ Rand1,
58
+ Rand2,
59
+ TrigonometricMutation,
60
+ sample_distinct_indices,
61
+ )
62
+ from .optimizer import DifferentialEvolution
63
+ from .population_schedules import (
64
+ HyperbolicTangentPopulationReduction,
65
+ LinearPopulationReduction,
66
+ )
67
+ from .result import OptimizeResult
68
+ from .scales import AdaptiveScaleFactor, ConstantScaleFactor, RandomizedScaleFactor
69
+ from .shade import LSHADE, SHADE
70
+
71
+ __all__ = [
72
+ "LSHADE",
73
+ "SHADE",
74
+ "AdaptiveCrossoverRate",
75
+ "AdaptiveScaleFactor",
76
+ "AggregatedDistribution",
77
+ "AverageDistanceAroundAllIndividuals",
78
+ "AverageDistanceAroundPopulationCenter",
79
+ "AveragePairwiseDistance",
80
+ "Best1",
81
+ "Best2",
82
+ "BinomialCrossover",
83
+ "ClipBoundaryHandler",
84
+ "ConstantCrossoverRate",
85
+ "ConstantScaleFactor",
86
+ "CurrentToBest1",
87
+ "CurrentToBest2",
88
+ "CurrentToRand1",
89
+ "CurrentToRand2",
90
+ "DifferentialEvolution",
91
+ "DimensionalVariance",
92
+ "DirectedMutation",
93
+ "ExponentialCrossover",
94
+ "HyperbolicTangentPopulationReduction",
95
+ "IdentityCrossover",
96
+ "JDEComponents",
97
+ "LinearPopulationReduction",
98
+ "MidpointBoundaryHandler",
99
+ "MutationContext",
100
+ "NeighborhoodSearchMutation",
101
+ "NoBoundaryHandler",
102
+ "OppositionInitializer",
103
+ "OptimizeResult",
104
+ "PopulationCoherence",
105
+ "PopulationDiameter",
106
+ "PopulationRadius",
107
+ "QuasiOppositionInitializer",
108
+ "Rand1",
109
+ "Rand2",
110
+ "RandomInitializer",
111
+ "RandomResetBoundaryHandler",
112
+ "RandomizedScaleFactor",
113
+ "ReflectionBoundaryHandler",
114
+ "SobolInitializer",
115
+ "TentInitializer",
116
+ "TrigonometricMutation",
117
+ "ackley_function",
118
+ "beale_function",
119
+ "booth_function",
120
+ "bukin_function",
121
+ "easom_function",
122
+ "eggholder_function",
123
+ "goldstein_price_function",
124
+ "himmelblau_function",
125
+ "jde_rand_1_bin",
126
+ "levi_function",
127
+ "matyas_function",
128
+ "mccormick_function",
129
+ "rastrigin_function",
130
+ "sample_distinct_indices",
131
+ "schaffer_n2_function",
132
+ "sphere_function",
133
+ "three_hump_camel_function",
134
+ ]
@@ -0,0 +1,73 @@
1
+ """Overflow-safe coordinate arithmetic for finite search bounds."""
2
+
3
+ import math
4
+ from collections.abc import Sequence
5
+ from fractions import Fraction
6
+
7
+ from .protocols import RandomSource
8
+
9
+
10
+ def require_integer(value: object, name: str) -> None:
11
+ """Reject fractional counts and booleans before they reach loops or budgets."""
12
+ if isinstance(value, bool) or not isinstance(value, int):
13
+ raise TypeError(f"{name} must be an integer.")
14
+
15
+
16
+ def mean(values: Sequence[float], *, total: float | None = None) -> float:
17
+ """Preserve ordinary arithmetic, avoiding overflow of a finite-value sum."""
18
+ if total is None:
19
+ total = sum(values)
20
+ if math.isfinite(total) or not all(math.isfinite(value) for value in values):
21
+ return total / len(values)
22
+ return float(sum(Fraction(value) for value in values) / len(values))
23
+
24
+
25
+ def sum_differences(base: float, *terms: tuple[float, float, float]) -> float:
26
+ """Compute base + sum(scale * (left - right)), with an overflow fallback.
27
+
28
+ Ordinary coordinates retain their floating-point operation order. Exact
29
+ arithmetic recovers finite results lost to intermediate overflow. A result
30
+ outside float range becomes signed infinity for boundary repair.
31
+ """
32
+ value = base
33
+ for scale, left, right in terms:
34
+ value += scale * (left - right)
35
+ if math.isfinite(value):
36
+ return value
37
+ if not math.isfinite(base) or not all(
38
+ math.isfinite(number) for term in terms for number in term
39
+ ):
40
+ return value
41
+ exact = Fraction(base) + sum(
42
+ Fraction(scale) * (Fraction(left) - Fraction(right))
43
+ for scale, left, right in terms
44
+ )
45
+ try:
46
+ return float(exact)
47
+ except OverflowError:
48
+ return math.inf if exact > 0 else -math.inf
49
+
50
+
51
+ def interpolate(lower: float, upper: float, fraction: float) -> float:
52
+ width = upper - lower
53
+ if math.isfinite(width):
54
+ value = lower + fraction * width
55
+ else:
56
+ value = (1.0 - fraction) * lower + fraction * upper
57
+ return min(max(lower, upper), max(min(lower, upper), value))
58
+
59
+
60
+ def uniform(lower: float, upper: float, rng: RandomSource) -> float:
61
+ if math.isfinite(upper - lower):
62
+ return rng.uniform(lower, upper)
63
+ return interpolate(lower, upper, rng.random())
64
+
65
+
66
+ def midpoint(left: float, right: float) -> float:
67
+ total = left + right
68
+ return total / 2 if math.isfinite(total) else left / 2 + right / 2
69
+
70
+
71
+ def opposite(value: float, lower: float, upper: float) -> float:
72
+ total = lower + upper
73
+ return total - value if math.isfinite(total) else lower + (upper - value)