sigmaepsilon.math 2.2.1__tar.gz → 2.2.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.
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/PKG-INFO +5 -3
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/pyproject.toml +1 -1
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/optimize/__init__.py +2 -1
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/optimize/bga.py +19 -9
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/optimize/ga.py +140 -49
- sigmaepsilon_math-2.2.2/src/sigmaepsilon/math/optimize/state.py +78 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/LICENSE +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/README.md +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/__init__.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/__init__.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/functions.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/lagrange.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/ls.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/ls_approx.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/ls_poly.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/ls_preproc.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/mls.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/arraysetops.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/decorate.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/downloads.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/__init__.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/function.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/functions.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/metafunction.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/relation.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/symutils.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/testfunction.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/utils.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/graph/__init__.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/graph/graph.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/graph/utils.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/hist.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/knn.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/__init__.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/abstract.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/exceptions.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/frame.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/imap.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/logical.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/meta.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/solve.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/sparse/__init__.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/sparse/csr.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/sparse/jaggedarray.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/sparse/utils.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/tensor.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/testing.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/tr.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/utils.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/vector.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/logical.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/mathtypes.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/metautils.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/numint.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/optimize/lp.py +0 -0
- {sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/utils.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: sigmaepsilon.math
|
|
3
|
-
Version: 2.2.
|
|
3
|
+
Version: 2.2.2
|
|
4
4
|
Summary: A Python Library for Applied Mathematics in Physical Sciences.
|
|
5
|
-
Home-page: https://github.com/sigma-epsilon/sigmaepsilon.math
|
|
6
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
7
|
Keywords: engineering,mathematics,optimization,vector,tensor,vector algebra,tensor algebra,symbolic mathematics,nonlinear optimization
|
|
8
8
|
Author: Bence Balogh
|
|
9
9
|
Author-email: bencebalogh@sigmaepsilon.com
|
|
@@ -18,6 +18,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
|
18
18
|
Classifier: Programming Language :: Python :: 3.11
|
|
19
19
|
Classifier: Programming Language :: Python :: 3.12
|
|
20
20
|
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
22
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
22
23
|
Requires-Dist: awkward (>2.0.8)
|
|
23
24
|
Requires-Dist: numba (>=0.56.4)
|
|
@@ -29,6 +30,7 @@ Requires-Dist: sigmaepsilon-deepdict (>=3.0.1,<4.0.0)
|
|
|
29
30
|
Requires-Dist: sympy (>=1.11.1)
|
|
30
31
|
Requires-Dist: zipp (>=3.19.1)
|
|
31
32
|
Project-URL: Documentation, https://sigmaepsilonmath.readthedocs.io/en/latest/?badge=latest
|
|
33
|
+
Project-URL: Homepage, https://github.com/sigma-epsilon/sigmaepsilon.math
|
|
32
34
|
Project-URL: Repository, https://github.com/sigma-epsilon/sigmaepsilon.math
|
|
33
35
|
Description-Content-Type: text/markdown
|
|
34
36
|
|
|
@@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api"
|
|
|
4
4
|
|
|
5
5
|
[tool.poetry]
|
|
6
6
|
name = "sigmaepsilon.math"
|
|
7
|
-
version = "2.2.
|
|
7
|
+
version = "2.2.2"
|
|
8
8
|
description = "A Python Library for Applied Mathematics in Physical Sciences."
|
|
9
9
|
classifiers=[ # https://pypi.org/classifiers/
|
|
10
10
|
"Development Status :: 5 - Production/Stable",
|
|
@@ -52,15 +52,13 @@ class BinaryGeneticAlgorithm(GeneticAlgorithm):
|
|
|
52
52
|
ftol: float, Optional
|
|
53
53
|
Torelance for floating point operations. Default is 1e-12.
|
|
54
54
|
maxage: int, Optional
|
|
55
|
-
The age is the number of generations a candidate spends at the top
|
|
56
|
-
(being the best candidate)
|
|
57
|
-
of stopping criterion. Default is 5.
|
|
55
|
+
The age is the maximum number of generations a candidate spends at the top
|
|
56
|
+
(being the best candidate) before termination. Default is 5.
|
|
58
57
|
minimize: bool, Optional
|
|
59
58
|
If True, the objective function is minimized. Default is False.
|
|
60
59
|
|
|
61
60
|
See Also
|
|
62
61
|
--------
|
|
63
|
-
:class:`~sigmaepsilon.math.optimize.ga.GeneticAlgorithm`
|
|
64
62
|
:class:`~sigmaepsilon.math.optimize.ga.Genom`
|
|
65
63
|
|
|
66
64
|
Examples
|
|
@@ -80,7 +78,7 @@ class BinaryGeneticAlgorithm(GeneticAlgorithm):
|
|
|
80
78
|
>>> _ = bga.solve()
|
|
81
79
|
>>> champion = bga.champion
|
|
82
80
|
>>> x = champion.phenotype
|
|
83
|
-
>>> fx = champion.
|
|
81
|
+
>>> fx = champion.fitness
|
|
84
82
|
|
|
85
83
|
"""
|
|
86
84
|
|
|
@@ -167,16 +165,28 @@ class BinaryGeneticAlgorithm(GeneticAlgorithm):
|
|
|
167
165
|
p = np.random.rand(self.dim * self.length)
|
|
168
166
|
return np.where(p > self.p_m, child, 1 - child)
|
|
169
167
|
|
|
170
|
-
def select(self, genotypes: ndarray, phenotypes: ndarray | None = None) -> ndarray:
|
|
168
|
+
def select(self, genotypes: ndarray | None = None, phenotypes: ndarray | None = None) -> ndarray:
|
|
171
169
|
"""
|
|
172
170
|
Organizes a tournament and returns the genotypes of the winners.
|
|
171
|
+
|
|
172
|
+
.. note::
|
|
173
|
+
Providing either ``genotypes`` or ``phenotypes`` (or both) is not currently supported
|
|
174
|
+
and will raise a NotImplementedError. Only the default case (both None) is implemented.
|
|
173
175
|
"""
|
|
174
|
-
|
|
175
|
-
|
|
176
|
+
if (genotypes is None) and (phenotypes is None):
|
|
177
|
+
fitness = self.fitness
|
|
178
|
+
genotypes = self.genotypes
|
|
179
|
+
else:
|
|
180
|
+
raise NotImplementedError(
|
|
181
|
+
"Selection with either 'genotypes' or 'phenotypes' (or both) provided is not implemented. "
|
|
182
|
+
"This branch is reached when at least one of these arguments is given to 'select', "
|
|
183
|
+
"but only the default case (both None) is currently supported."
|
|
184
|
+
)
|
|
185
|
+
winners, others = self.divide(fitness)
|
|
176
186
|
winners = winners.tolist()
|
|
177
187
|
while len(winners) < int(self.nPop / 2):
|
|
178
188
|
candidates = np.random.choice(others, 3, replace=False)
|
|
179
|
-
argsort = np.argsort([
|
|
189
|
+
argsort = np.argsort([fitness[ID] for ID in candidates])
|
|
180
190
|
winner = argsort[0] if self._minimize else argsort[-1]
|
|
181
191
|
winners.append(candidates[winner])
|
|
182
192
|
return np.array([genotypes[w] for w in winners], dtype=float)
|
|
@@ -1,22 +1,25 @@
|
|
|
1
1
|
from abc import abstractmethod
|
|
2
2
|
from typing import Iterable, Callable, Tuple, Generator
|
|
3
3
|
from types import NoneType
|
|
4
|
+
from numbers import Number
|
|
4
5
|
import operator
|
|
6
|
+
from enum import Enum, unique
|
|
5
7
|
|
|
6
8
|
import numpy as np
|
|
7
9
|
from numpy import ndarray
|
|
8
10
|
from pydantic import BaseModel, Field
|
|
9
11
|
|
|
10
12
|
from ..function import Function
|
|
13
|
+
from .state import OptimizerState
|
|
11
14
|
|
|
12
15
|
__all__ = ["GeneticAlgorithm", "Genom"]
|
|
13
16
|
|
|
14
17
|
|
|
15
|
-
def even(n):
|
|
18
|
+
def even(n: Number) -> bool:
|
|
16
19
|
return n % 2 == 0
|
|
17
20
|
|
|
18
21
|
|
|
19
|
-
def odd(n):
|
|
22
|
+
def odd(n: Number) -> bool:
|
|
20
23
|
return not even(n)
|
|
21
24
|
|
|
22
25
|
|
|
@@ -27,7 +30,7 @@ class Genom(BaseModel):
|
|
|
27
30
|
|
|
28
31
|
phenotype: list[float] = Field(default_factory=list)
|
|
29
32
|
genotype: list[int] = Field(default_factory=list)
|
|
30
|
-
|
|
33
|
+
fitness: float
|
|
31
34
|
age: int = Field(default=0)
|
|
32
35
|
index: int = Field(default=-1)
|
|
33
36
|
|
|
@@ -45,28 +48,38 @@ class Genom(BaseModel):
|
|
|
45
48
|
raise TypeError(
|
|
46
49
|
f"This operation is not supported between instances of {type(other)} and {type(self)}."
|
|
47
50
|
)
|
|
48
|
-
return np.all(self.
|
|
51
|
+
return np.all(self.fitness > other.fitness)
|
|
49
52
|
|
|
50
53
|
def __lt__(self, other):
|
|
51
54
|
if not isinstance(other, Genom):
|
|
52
55
|
raise TypeError(
|
|
53
56
|
f"This operation is not supported between instances of {type(other)} and {type(self)}."
|
|
54
57
|
)
|
|
55
|
-
return np.all(self.
|
|
58
|
+
return np.all(self.fitness < other.fitness)
|
|
56
59
|
|
|
57
60
|
def __ge__(self, other):
|
|
58
61
|
if not isinstance(other, Genom):
|
|
59
62
|
raise TypeError(
|
|
60
63
|
f"This operation is not supported between instances of {type(other)} and {type(self)}."
|
|
61
64
|
)
|
|
62
|
-
return np.all(self.
|
|
65
|
+
return np.all(self.fitness >= other.fitness)
|
|
63
66
|
|
|
64
67
|
def __le__(self, other):
|
|
65
68
|
if not isinstance(other, Genom):
|
|
66
69
|
raise TypeError(
|
|
67
70
|
f"This operation is not supported between instances of {type(other)} and {type(self)}."
|
|
68
71
|
)
|
|
69
|
-
return np.all(self.
|
|
72
|
+
return np.all(self.fitness <= other.fitness)
|
|
73
|
+
|
|
74
|
+
@property
|
|
75
|
+
def fittness(self) -> float: # pragma: no cover
|
|
76
|
+
import warnings
|
|
77
|
+
warnings.warn(
|
|
78
|
+
"'fittness' was a typo and is deprecated; it will be removed in a future version. Use 'fitness' instead.",
|
|
79
|
+
DeprecationWarning,
|
|
80
|
+
stacklevel=2,
|
|
81
|
+
)
|
|
82
|
+
return self.fitness
|
|
70
83
|
|
|
71
84
|
|
|
72
85
|
class GeneticAlgorithm:
|
|
@@ -112,9 +125,8 @@ class GeneticAlgorithm:
|
|
|
112
125
|
ftol: float, Optional
|
|
113
126
|
Torelance for floating point operations. Default is 1e-12.
|
|
114
127
|
maxage: int, Optional
|
|
115
|
-
The age is the number of generations a candidate spends at the top
|
|
116
|
-
(being the best candidate).
|
|
117
|
-
of stopping criterion. Default is 5.
|
|
128
|
+
The age is the maximum number of generations a candidate spends at the top
|
|
129
|
+
(being the best candidate). Default is 5.
|
|
118
130
|
minimize: bool, Optional
|
|
119
131
|
If True, the objective function is minimized. Default is False.
|
|
120
132
|
|
|
@@ -124,14 +136,23 @@ class GeneticAlgorithm:
|
|
|
124
136
|
genetic algorithm can be wery demanding on the computational side. If the objective
|
|
125
137
|
function takes a lot of time to evaluate, it is probably not a good idea to use a heuristic
|
|
126
138
|
approach, unless you have a dedicated evaluator that is able to run efficiently for a large
|
|
127
|
-
number of problems. If you want to customize the way the
|
|
128
|
-
the :func:`evaluate` method.
|
|
139
|
+
number of problems or if the long running time is not an issue. If you want to customize the way the
|
|
140
|
+
objective is evaluated, override the :func:`evaluate` method.
|
|
129
141
|
|
|
130
142
|
See also
|
|
131
143
|
--------
|
|
132
144
|
:class:`~sigmaepsilon.math.optimize.bga.BinaryGeneticAlgorithm`
|
|
133
145
|
"""
|
|
134
146
|
|
|
147
|
+
@unique
|
|
148
|
+
class Status(Enum):
|
|
149
|
+
"""Status codes for the genetic algorithm."""
|
|
150
|
+
|
|
151
|
+
INITIALIZED = 0
|
|
152
|
+
MAX_ITERATIONS_REACHED = 1
|
|
153
|
+
CONVERGED = 2
|
|
154
|
+
ERROR = -1
|
|
155
|
+
|
|
135
156
|
__slots__ = [
|
|
136
157
|
"fnc",
|
|
137
158
|
"ranges",
|
|
@@ -142,17 +163,18 @@ class GeneticAlgorithm:
|
|
|
142
163
|
"nPop",
|
|
143
164
|
"_genotypes",
|
|
144
165
|
"_phenotypes",
|
|
145
|
-
"
|
|
166
|
+
"_fitness",
|
|
146
167
|
"_champion",
|
|
147
168
|
"_evolver",
|
|
148
169
|
"maxiter",
|
|
149
170
|
"miniter",
|
|
150
171
|
"elitism",
|
|
151
172
|
"maxage",
|
|
152
|
-
"nIter",
|
|
153
173
|
"_is_symbolic_Function",
|
|
154
174
|
"_celebrate_op",
|
|
155
175
|
"_minimize",
|
|
176
|
+
"_state",
|
|
177
|
+
"_status",
|
|
156
178
|
]
|
|
157
179
|
|
|
158
180
|
def __init__(
|
|
@@ -190,11 +212,13 @@ class GeneticAlgorithm:
|
|
|
190
212
|
self.p_m = None
|
|
191
213
|
self._genotypes = None
|
|
192
214
|
self._pnenotypes = None
|
|
193
|
-
self.
|
|
215
|
+
self._fitness = None
|
|
194
216
|
self._champion: Genom | NoneType = None
|
|
195
217
|
self._celebrate_op = None
|
|
196
218
|
self._minimize = False
|
|
197
219
|
self.elitism = None
|
|
220
|
+
self._state = None
|
|
221
|
+
self._status = None
|
|
198
222
|
self.set_solution_params(
|
|
199
223
|
p_c=p_c,
|
|
200
224
|
p_m=p_m,
|
|
@@ -206,6 +230,18 @@ class GeneticAlgorithm:
|
|
|
206
230
|
)
|
|
207
231
|
self.reset()
|
|
208
232
|
|
|
233
|
+
@property
|
|
234
|
+
def state(self) -> OptimizerState:
|
|
235
|
+
"""
|
|
236
|
+
Returns the state of the optimizer.
|
|
237
|
+
"""
|
|
238
|
+
return self._state
|
|
239
|
+
|
|
240
|
+
@property
|
|
241
|
+
def nIter(self) -> int: # pragma: no cover
|
|
242
|
+
"""For backwards compatibility. Returns the number of iterations performed."""
|
|
243
|
+
return self.state.n_iter
|
|
244
|
+
|
|
209
245
|
@property
|
|
210
246
|
def champion(self) -> Genom:
|
|
211
247
|
"""
|
|
@@ -221,13 +257,13 @@ class GeneticAlgorithm:
|
|
|
221
257
|
return self._genotypes
|
|
222
258
|
|
|
223
259
|
@genotypes.setter
|
|
224
|
-
def genotypes(self, value: Iterable):
|
|
260
|
+
def genotypes(self, value: Iterable) -> None:
|
|
225
261
|
"""
|
|
226
262
|
Sets the genotypes of the population.
|
|
227
263
|
"""
|
|
228
264
|
self._genotypes = value
|
|
229
265
|
self._phenotypes = None
|
|
230
|
-
self.
|
|
266
|
+
self._fitness = None
|
|
231
267
|
|
|
232
268
|
@property
|
|
233
269
|
def phenotypes(self) -> Iterable:
|
|
@@ -240,25 +276,46 @@ class GeneticAlgorithm:
|
|
|
240
276
|
self._phenotypes = self.decode(genotypes)
|
|
241
277
|
return self._phenotypes
|
|
242
278
|
|
|
279
|
+
|
|
243
280
|
@property
|
|
244
|
-
def
|
|
281
|
+
def fitness(self) -> ndarray:
|
|
245
282
|
"""
|
|
246
|
-
Returns the actual
|
|
283
|
+
Returns the actual fitness values of the population, or the fitness
|
|
247
284
|
of the population described by the argument `phenotypes`.
|
|
248
285
|
"""
|
|
249
|
-
if self.
|
|
250
|
-
|
|
251
|
-
|
|
286
|
+
if self._fitness is not None:
|
|
287
|
+
return self._fitness
|
|
288
|
+
|
|
289
|
+
self._fitness = self.evaluate(self.phenotypes)
|
|
290
|
+
return self._fitness
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def fittness(self) -> ndarray: # pragma: no cover
|
|
294
|
+
import warnings
|
|
295
|
+
warnings.warn(
|
|
296
|
+
"'fittness' was a typo and is deprecated; it will be removed in a future version. Use 'fitness' instead.",
|
|
297
|
+
DeprecationWarning,
|
|
298
|
+
stacklevel=2,
|
|
299
|
+
)
|
|
300
|
+
return self.fitness
|
|
252
301
|
|
|
253
302
|
def reset(self) -> "GeneticAlgorithm":
|
|
254
303
|
"""
|
|
255
304
|
Resets the solver and returns the object. Only use it if you want to have
|
|
256
305
|
a completely clean sheet. Also, the function is called for every object at
|
|
257
306
|
instantiation.
|
|
307
|
+
|
|
308
|
+
Note
|
|
309
|
+
----
|
|
310
|
+
This method resets the internal state of the genetic algorithm, including
|
|
311
|
+
the population and champion. Use this method when you want to start a new
|
|
312
|
+
optimization process from scratch.
|
|
258
313
|
"""
|
|
259
314
|
self._evolver = self.evolver()
|
|
260
315
|
self._evolver.send(None)
|
|
261
316
|
self._champion = None
|
|
317
|
+
self._state = OptimizerState()
|
|
318
|
+
self._status = GeneticAlgorithm.Status.INITIALIZED
|
|
262
319
|
return self
|
|
263
320
|
|
|
264
321
|
def set_solution_params(self, **kwargs) -> "GeneticAlgorithm":
|
|
@@ -327,7 +384,7 @@ class GeneticAlgorithm:
|
|
|
327
384
|
_ = yield
|
|
328
385
|
yield self.genotypes
|
|
329
386
|
while True:
|
|
330
|
-
genotypes = self.select(
|
|
387
|
+
genotypes = self.select()
|
|
331
388
|
self.genotypes = self.populate(genotypes)
|
|
332
389
|
yield self.genotypes
|
|
333
390
|
|
|
@@ -344,7 +401,7 @@ class GeneticAlgorithm:
|
|
|
344
401
|
|
|
345
402
|
def solve(self, recycle: bool = False, **kwargs) -> Genom:
|
|
346
403
|
"""
|
|
347
|
-
Solves the problem and returns the
|
|
404
|
+
Solves the problem and returns the champion.
|
|
348
405
|
|
|
349
406
|
.. note::
|
|
350
407
|
This class is designed for maximizing the objective function. To minimize it,
|
|
@@ -365,17 +422,39 @@ class GeneticAlgorithm:
|
|
|
365
422
|
The best candidate.
|
|
366
423
|
|
|
367
424
|
"""
|
|
368
|
-
|
|
425
|
+
if not recycle:
|
|
426
|
+
self.reset()
|
|
369
427
|
self.set_solution_params(**kwargs)
|
|
370
428
|
|
|
371
|
-
|
|
429
|
+
finished = False
|
|
430
|
+
self._status = GeneticAlgorithm.Status.INITIALIZED
|
|
431
|
+
|
|
432
|
+
try:
|
|
433
|
+
while not finished:
|
|
434
|
+
self.evolve(1)
|
|
435
|
+
self.state.n_iter += 1
|
|
436
|
+
min_iter_reached = self.state.n_iter >= self.miniter
|
|
437
|
+
max_iter_reached = self.state.n_iter >= self.maxiter
|
|
438
|
+
finished = (
|
|
439
|
+
self.stopping_criteria() or max_iter_reached
|
|
440
|
+
) and min_iter_reached
|
|
441
|
+
|
|
442
|
+
if finished:
|
|
443
|
+
if max_iter_reached:
|
|
444
|
+
self._status = GeneticAlgorithm.Status.MAX_ITERATIONS_REACHED
|
|
445
|
+
elif self.stopping_criteria():
|
|
446
|
+
self._status = GeneticAlgorithm.Status.CONVERGED
|
|
447
|
+
self._state.success = True
|
|
448
|
+
else: # pragma: no cover
|
|
449
|
+
self._state.success = False
|
|
450
|
+
except Exception as e:
|
|
451
|
+
self._state.success = False
|
|
452
|
+
self._state.message = str(e)
|
|
453
|
+
self._status = GeneticAlgorithm.Status.ERROR
|
|
454
|
+
raise e
|
|
455
|
+
finally:
|
|
456
|
+
self._state.stage = self._status.value
|
|
372
457
|
|
|
373
|
-
while (not finished and nIter < self.maxiter) or (nIter < self.miniter):
|
|
374
|
-
self.evolve(1)
|
|
375
|
-
finished = self.stopping_criteria()
|
|
376
|
-
nIter += 1
|
|
377
|
-
|
|
378
|
-
self.nIter = nIter
|
|
379
458
|
return self.champion
|
|
380
459
|
|
|
381
460
|
def evaluate(self, phenotypes: Iterable | None = None) -> ndarray:
|
|
@@ -391,11 +470,19 @@ class GeneticAlgorithm:
|
|
|
391
470
|
The phenotypes the objective function is to be evaluated for.
|
|
392
471
|
Default is None.
|
|
393
472
|
"""
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
473
|
+
try:
|
|
474
|
+
phenotypes = self.phenotypes if phenotypes is None else phenotypes
|
|
475
|
+
if self._is_symbolic_Function:
|
|
476
|
+
result = self.fnc(phenotypes.T)
|
|
477
|
+
else:
|
|
478
|
+
result = np.array([self.fnc(x) for x in phenotypes], dtype=float)
|
|
479
|
+
self.state.n_fev += len(phenotypes)
|
|
480
|
+
except Exception as e: # pragma: no cover
|
|
481
|
+
result = None
|
|
482
|
+
raise RuntimeError(
|
|
483
|
+
"Error during evaluation of the objective function."
|
|
484
|
+
) from e
|
|
485
|
+
return result
|
|
399
486
|
|
|
400
487
|
def best_phenotype(self) -> ndarray:
|
|
401
488
|
"""
|
|
@@ -420,13 +507,13 @@ class GeneticAlgorithm:
|
|
|
420
507
|
the :func:`champion` property.
|
|
421
508
|
|
|
422
509
|
"""
|
|
423
|
-
|
|
510
|
+
fitness = self.fitness
|
|
424
511
|
argfunc = np.argmin if self._minimize else np.argmax
|
|
425
|
-
index = argfunc(
|
|
512
|
+
index = argfunc(fitness)
|
|
426
513
|
return Genom(
|
|
427
514
|
phenotype=self.phenotypes[index],
|
|
428
515
|
genotype=self.genotypes[index],
|
|
429
|
-
|
|
516
|
+
fitness=fitness[index],
|
|
430
517
|
index=index,
|
|
431
518
|
)
|
|
432
519
|
|
|
@@ -443,17 +530,19 @@ class GeneticAlgorithm:
|
|
|
443
530
|
if has_new_champion:
|
|
444
531
|
self._champion = genom
|
|
445
532
|
self._champion.age = 0
|
|
533
|
+
self.state.x = self.champion.phenotype
|
|
534
|
+
self.state.fun = self.champion.fitness
|
|
446
535
|
self._champion.age += 1
|
|
447
536
|
|
|
448
|
-
def divide(self,
|
|
537
|
+
def divide(self, fitness: ndarray | None = None) -> tuple[ndarray, ndarray]:
|
|
449
538
|
"""
|
|
450
539
|
Divides population to elit and others, and returns the corresponding
|
|
451
540
|
index arrays.
|
|
452
541
|
|
|
453
542
|
Parameters
|
|
454
543
|
----------
|
|
455
|
-
|
|
456
|
-
|
|
544
|
+
fitness: numpy.ndarray, Optional
|
|
545
|
+
Fitness values. If not provided, values from the latest
|
|
457
546
|
evaluation are used. Default is None.
|
|
458
547
|
|
|
459
548
|
Returns
|
|
@@ -463,14 +552,14 @@ class GeneticAlgorithm:
|
|
|
463
552
|
list
|
|
464
553
|
Indices of the members of the others.
|
|
465
554
|
"""
|
|
466
|
-
|
|
467
|
-
assert
|
|
555
|
+
fitness = self.fitness if fitness is None else fitness
|
|
556
|
+
assert fitness is not None, "No available fitness data detected."
|
|
468
557
|
|
|
469
558
|
if self.elitism is None:
|
|
470
559
|
return [], list(range(self.nPop))
|
|
471
560
|
|
|
472
561
|
if self.elitism is not None:
|
|
473
|
-
argsort = np.argsort(
|
|
562
|
+
argsort = np.argsort(fitness)
|
|
474
563
|
if not self._minimize:
|
|
475
564
|
argsort = argsort[::-1]
|
|
476
565
|
|
|
@@ -566,9 +655,11 @@ class GeneticAlgorithm:
|
|
|
566
655
|
...
|
|
567
656
|
|
|
568
657
|
@abstractmethod
|
|
569
|
-
def select(
|
|
658
|
+
def select(
|
|
659
|
+
self, genotypes: ndarray, phenotypes: ndarray
|
|
660
|
+
) -> ndarray:
|
|
570
661
|
"""
|
|
571
|
-
Ought to implement
|
|
572
|
-
tournament or other.
|
|
662
|
+
Ought to implement some kind of selection mechanism, e.g., a roulette wheel,
|
|
663
|
+
tournament, or other. Both `genotypes` and `phenotypes` must be provided.
|
|
573
664
|
"""
|
|
574
665
|
...
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from types import NoneType
|
|
2
|
+
from pydantic import BaseModel, Field
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.optimize import OptimizeResult
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
__all__ = ["OptimizerState"]
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class OptimizerState(BaseModel):
|
|
11
|
+
"""
|
|
12
|
+
Model representing the state of an optimizer.
|
|
13
|
+
|
|
14
|
+
Attributes
|
|
15
|
+
----------
|
|
16
|
+
x : list[float] | NoneType
|
|
17
|
+
The current solution vector, or None if not set.
|
|
18
|
+
fun : float | NoneType
|
|
19
|
+
The value of the objective function at the current solution, or None if not set.
|
|
20
|
+
n_fev : int
|
|
21
|
+
The number of function evaluations performed.
|
|
22
|
+
n_jev : int
|
|
23
|
+
The number of Jacobian evaluations performed.
|
|
24
|
+
n_hev : int
|
|
25
|
+
The number of Hessian evaluations performed.
|
|
26
|
+
n_iter : int
|
|
27
|
+
The number of iterations completed.
|
|
28
|
+
success : bool
|
|
29
|
+
Whether or not the optimizer exited successfully.
|
|
30
|
+
message : str
|
|
31
|
+
A message describing the cause of the termination.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
x: list[float] | NoneType = Field(
|
|
35
|
+
default=None, description="The current solution vector."
|
|
36
|
+
)
|
|
37
|
+
fun: float | NoneType = Field(
|
|
38
|
+
default=None,
|
|
39
|
+
description="The value of the objective function at the current solution.",
|
|
40
|
+
)
|
|
41
|
+
n_fev: int = Field(
|
|
42
|
+
default=0, description="The number of function evaluations performed."
|
|
43
|
+
)
|
|
44
|
+
n_jev: int = Field(
|
|
45
|
+
default=0, description="The number of Jacobian evaluations performed."
|
|
46
|
+
)
|
|
47
|
+
n_hev: int = Field(
|
|
48
|
+
default=0, description="The number of Hessian evaluations performed."
|
|
49
|
+
)
|
|
50
|
+
n_iter: int = Field(default=0, description="The number of iterations completed.")
|
|
51
|
+
success: bool = Field(
|
|
52
|
+
default=True, description="Indicates if the optimizer exited successfully."
|
|
53
|
+
)
|
|
54
|
+
message: str = Field(
|
|
55
|
+
default="", description="Description of the cause of termination."
|
|
56
|
+
)
|
|
57
|
+
stage: int | NoneType = Field(
|
|
58
|
+
default=None,
|
|
59
|
+
description=(
|
|
60
|
+
"Termination status of the optimizer. "
|
|
61
|
+
"Its value depends on the underlying solver. "
|
|
62
|
+
"Refer to the solver being used for more details."
|
|
63
|
+
),
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
def to_scipy(self) -> OptimizeResult:
|
|
67
|
+
"""Returns the state as a SciPy OptimizeResult object."""
|
|
68
|
+
return OptimizeResult(
|
|
69
|
+
x=np.array(self.x) if self.x is not None else None,
|
|
70
|
+
fun=self.fun,
|
|
71
|
+
status=self.stage,
|
|
72
|
+
nfev=self.n_fev,
|
|
73
|
+
njev=self.n_jev,
|
|
74
|
+
nhev=self.n_hev,
|
|
75
|
+
success=self.success,
|
|
76
|
+
message=self.message,
|
|
77
|
+
nit=self.n_iter,
|
|
78
|
+
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/__init__.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/functions.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/lagrange.py
RENAMED
|
File without changes
|
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/ls_approx.py
RENAMED
|
File without changes
|
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/approx/ls_preproc.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/__init__.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/function.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/functions.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/metafunction.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/relation.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/symutils.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/function/testfunction.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/__init__.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/abstract.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/sparse/__init__.py
RENAMED
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/sparse/csr.py
RENAMED
|
File without changes
|
|
File without changes
|
{sigmaepsilon_math-2.2.1 → sigmaepsilon_math-2.2.2}/src/sigmaepsilon/math/linalg/sparse/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|