evograd-diff 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.
- evograd/__init__.py +67 -0
- evograd/algorithms/__init__.py +138 -0
- evograd/algorithms/cmaes.py +1365 -0
- evograd/algorithms/de.py +895 -0
- evograd/algorithms/ga.py +532 -0
- evograd/algorithms/pso.py +648 -0
- evograd/algorithms/shade.py +1165 -0
- evograd/benchmarks/functions/__init__.py +229 -0
- evograd/benchmarks/functions/base.py +217 -0
- evograd/benchmarks/functions/cec2017/__init__.py +250 -0
- evograd/benchmarks/functions/cec2017/basic.py +413 -0
- evograd/benchmarks/functions/cec2017/composition.py +580 -0
- evograd/benchmarks/functions/cec2017/data.pkl +0 -0
- evograd/benchmarks/functions/cec2017/data.py +350 -0
- evograd/benchmarks/functions/cec2017/hybrid.py +406 -0
- evograd/benchmarks/functions/cec2017/simple.py +326 -0
- evograd/benchmarks/functions/classical.py +649 -0
- evograd/benchmarks/functions/smoothed_funnel.py +476 -0
- evograd/benchmarks/functions/transforms.py +463 -0
- evograd/benchmarks/run_benchmark_functions.py +1208 -0
- evograd/core/__init__.py +73 -0
- evograd/core/algorithm.py +778 -0
- evograd/core/maximize.py +269 -0
- evograd/core/minimize.py +740 -0
- evograd/core/problem.py +444 -0
- evograd/core/result.py +571 -0
- evograd/core/termination.py +602 -0
- evograd/operators/__init__.py +178 -0
- evograd/operators/crossover.py +1117 -0
- evograd/operators/mutation.py +1098 -0
- evograd/operators/relaxations.py +175 -0
- evograd/operators/repair.py +601 -0
- evograd/operators/sampling.py +577 -0
- evograd/operators/selection.py +981 -0
- evograd/operators/survival.py +1000 -0
- evograd/tests/__init__.py +11 -0
- evograd/tests/run_all.py +78 -0
- evograd/tests/test_core.py +528 -0
- evograd/tests/test_ga.py +572 -0
- evograd/tests/test_operators.py +662 -0
- evograd/tests/test_per_individual.py +326 -0
- evograd/tests/test_utils.py +328 -0
- evograd/utils/__init__.py +97 -0
- evograd/utils/callbacks.py +926 -0
- evograd/utils/device.py +502 -0
- evograd/utils/duplicates.py +421 -0
- evograd_diff-0.1.0.dist-info/METADATA +439 -0
- evograd_diff-0.1.0.dist-info/RECORD +50 -0
- evograd_diff-0.1.0.dist-info/WHEEL +4 -0
- evograd_diff-0.1.0.dist-info/licenses/LICENSE +201 -0
|
@@ -0,0 +1,439 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: evograd-diff
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PyTorch-based framework for differentiable evolutionary computation and swarm intelligence
|
|
5
|
+
Project-URL: Homepage, https://github.com/andreatangherloni/EvoGrad
|
|
6
|
+
Project-URL: Repository, https://github.com/andreatangherloni/EvoGrad
|
|
7
|
+
Project-URL: Issues, https://github.com/andreatangherloni/EvoGrad/issues
|
|
8
|
+
Author-email: Andrea Tangherloni <andrea.tangherloni@unibocconi.it>
|
|
9
|
+
License: Apache-2.0
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: CMA-ES,differentiable programming,differential evolution,evolutionary computation,genetic algorithm,metaheuristics,optimization,particle swarm optimization,pytorch
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
19
|
+
Requires-Python: >=3.12
|
|
20
|
+
Requires-Dist: matplotlib>=3.10.8
|
|
21
|
+
Requires-Dist: numpy>=2.4.4
|
|
22
|
+
Requires-Dist: pandas>=3.0.2
|
|
23
|
+
Requires-Dist: scipy>=1.17.1
|
|
24
|
+
Requires-Dist: seaborn>=0.13.2
|
|
25
|
+
Requires-Dist: torch>=2.11.0
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# EvoGrad: Metaheuristics in a Differentiable Wonderland
|
|
29
|
+
|
|
30
|
+
<p align="center">
|
|
31
|
+
<img src="https://img.shields.io/badge/python-3.9+-blue.svg" alt="Python 3.9+">
|
|
32
|
+
<img src="https://img.shields.io/badge/pytorch-2.0+-orange.svg" alt="PyTorch 2.0+">
|
|
33
|
+
<img src="https://img.shields.io/badge/license-Apache%20License%202.0-blue.svg" alt="License: Apache-2.0">
|
|
34
|
+
|
|
35
|
+
</p>
|
|
36
|
+
|
|
37
|
+
**EvoGrad** is a PyTorch-based framework for differentiable Evolutionary Computation and Swarm Intelligence. It bridges classical population-based optimisation with modern differentiable programming by enabling gradient flow through evolutionary operators.
|
|
38
|
+
|
|
39
|
+
## 🌟 Key Features
|
|
40
|
+
|
|
41
|
+
- **Fully Differentiable**: All operators support gradient computation via reparameterisation tricks (Gumbel-Softmax, Binary-Concrete, pathwise gradients)
|
|
42
|
+
- **GPU Accelerated**: Native PyTorch implementation for seamless CPU/GPU/MPS execution
|
|
43
|
+
- **Modular Design**: Dependency injection pattern inspired by [pymoo](https://pymoo.org/) for flexible operator composition
|
|
44
|
+
- **Learnable Hyperparameters**: Automatically tune algorithm parameters via backpropagation
|
|
45
|
+
- **Four Algorithms**: GA, DE, PSO, and CMA-ES with multiple variants
|
|
46
|
+
|
|
47
|
+
## 📦 Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
# Clone the repository
|
|
51
|
+
git clone https://github.com/YOUR_USERNAME/evograd.git
|
|
52
|
+
cd evograd
|
|
53
|
+
|
|
54
|
+
# Install dependencies
|
|
55
|
+
pip install torch numpy
|
|
56
|
+
|
|
57
|
+
# Install in development mode
|
|
58
|
+
pip install -e .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## 🚀 Quick Start
|
|
62
|
+
|
|
63
|
+
```python
|
|
64
|
+
import torch
|
|
65
|
+
from evograd.core import Problem, minimize
|
|
66
|
+
from evograd.algorithms import GA, DE, PSO, CMAES
|
|
67
|
+
|
|
68
|
+
# Define an optimisation problem
|
|
69
|
+
problem = Problem(
|
|
70
|
+
objective=lambda x: (x**2).sum(dim=-1), # Sphere function
|
|
71
|
+
n_var=30,
|
|
72
|
+
xl=-100.0,
|
|
73
|
+
xu=100.0,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Run with Genetic Algorithm
|
|
77
|
+
ga = GA(pop_size=100, differentiable=True)
|
|
78
|
+
result = minimize(problem, ga, max_evals=10000, seed=42)
|
|
79
|
+
print(f"GA Best: {result.best_fitness:.6f}")
|
|
80
|
+
|
|
81
|
+
# Run with Differential Evolution
|
|
82
|
+
de = DE(pop_size=100, variant="DE/rand/1/bin", adaptive=True)
|
|
83
|
+
result = minimize(problem, de, max_evals=10000, seed=42)
|
|
84
|
+
print(f"DE Best: {result.best_fitness:.6f}")
|
|
85
|
+
|
|
86
|
+
# Run with Particle Swarm Optimisation
|
|
87
|
+
pso = PSO(pop_size=100, adaptive=True, differentiable=True)
|
|
88
|
+
result = minimize(problem, pso, max_evals=10000, seed=42)
|
|
89
|
+
print(f"PSO Best: {result.best_fitness:.6f}")
|
|
90
|
+
|
|
91
|
+
# Run with CMA-ES
|
|
92
|
+
cmaes = CMAES(sigma=0.5, adaptive=True)
|
|
93
|
+
result = minimize(problem, cmaes, max_evals=10000, seed=42)
|
|
94
|
+
print(f"CMA-ES Best: {result.best_fitness:.6f}")
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## 🔧 Algorithms and Operating Modes
|
|
98
|
+
|
|
99
|
+
### Genetic Algorithm (GA)
|
|
100
|
+
|
|
101
|
+
The GA uses **operator-level differentiability**. Each operator (selection, crossover, mutation, survival) can independently be set to differentiable mode:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from evograd.algorithms import GA
|
|
105
|
+
from evograd.operators import (
|
|
106
|
+
RouletteSelection,
|
|
107
|
+
SBXCrossover,
|
|
108
|
+
PolynomialMutation,
|
|
109
|
+
MergeSurvival,
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
# Classical GA (no gradients)
|
|
113
|
+
ga = GA(pop_size=100, differentiable=False)
|
|
114
|
+
|
|
115
|
+
# Fully differentiable GA with custom operators
|
|
116
|
+
ga = GA(
|
|
117
|
+
pop_size=100,
|
|
118
|
+
selection=RouletteSelection(differentiable=True, learn_temperature=True),
|
|
119
|
+
crossover=SBXCrossover(differentiable=True, learn_eta=True, learn_prob=True),
|
|
120
|
+
mutation=PolynomialMutation(differentiable=True, learn_eta=True, learn_prob=True),
|
|
121
|
+
survival=MergeSurvival(selection=RouletteSelection(differentiable=True)),
|
|
122
|
+
differentiable=True, # Makes population learnable
|
|
123
|
+
)
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
| Parameter | Effect |
|
|
127
|
+
|-----------|--------|
|
|
128
|
+
| `differentiable=False` | Classical GA with discrete operators |
|
|
129
|
+
| `differentiable=True` | Population is an `nn.Parameter` (learnable via backprop) |
|
|
130
|
+
| Operator `differentiable=True` | Operator uses Gumbel-Softmax/Binary-Concrete for gradient flow |
|
|
131
|
+
| Operator `learn_*=True` | Operator hyperparameters become learnable `nn.Parameter` |
|
|
132
|
+
|
|
133
|
+
### Differential Evolution (DE)
|
|
134
|
+
|
|
135
|
+
DE uses **algorithm-level flags** for adaptive hyperparameters and differentiable population:
|
|
136
|
+
|
|
137
|
+
```python
|
|
138
|
+
from evograd.algorithms import DE, de_rand_1_bin, de_best_1_bin
|
|
139
|
+
|
|
140
|
+
# Classical DE
|
|
141
|
+
de = DE(pop_size=100, variant="DE/rand/1/bin", F=0.5, CR=0.9)
|
|
142
|
+
|
|
143
|
+
# Adaptive DE (learnable F, CR, selection temperature)
|
|
144
|
+
de = DE(pop_size=100, variant="DE/best/1/bin", adaptive=True)
|
|
145
|
+
|
|
146
|
+
# Differentiable population
|
|
147
|
+
de = DE(pop_size=100, variant="DE/rand/1/bin", differentiable=True)
|
|
148
|
+
|
|
149
|
+
# Both adaptive and differentiable
|
|
150
|
+
de = DE(pop_size=100, variant="DE/current-to-best/1/bin", adaptive=True, differentiable=True)
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
| `adaptive` | `differentiable` | Effect |
|
|
154
|
+
|------------|------------------|--------|
|
|
155
|
+
| False | False | Classical DE |
|
|
156
|
+
| True | False | F, CR, temperatures learnable via backprop |
|
|
157
|
+
| False | True | Population learnable via backprop |
|
|
158
|
+
| True | True | Both hyperparameters and population learnable |
|
|
159
|
+
|
|
160
|
+
**Supported Variants:**
|
|
161
|
+
- `DE/rand/1/bin`, `DE/rand/1/exp`, `DE/rand/2/bin`, `DE/rand/2/exp`
|
|
162
|
+
- `DE/best/1/bin`, `DE/best/1/exp`, `DE/best/2/bin`, `DE/best/2/exp`
|
|
163
|
+
- `DE/current-to-best/1/bin`, `DE/current-to-best/1/exp`
|
|
164
|
+
- `DE/current-to-rand/1`
|
|
165
|
+
|
|
166
|
+
### Particle Swarm Optimisation (PSO)
|
|
167
|
+
|
|
168
|
+
PSO uses the same **algorithm-level flags** as DE:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from evograd.algorithms import PSO, pso_constriction, pso_adaptive
|
|
172
|
+
|
|
173
|
+
# Classical PSO
|
|
174
|
+
pso = PSO(pop_size=100, inertia=0.7, c1=1.5, c2=1.5)
|
|
175
|
+
|
|
176
|
+
# Adaptive PSO (learnable inertia, c1, c2)
|
|
177
|
+
pso = PSO(pop_size=100, adaptive=True)
|
|
178
|
+
|
|
179
|
+
# Per-particle adaptive coefficients
|
|
180
|
+
pso = PSO(pop_size=100, adaptive=True, per_particle_coeffs=True)
|
|
181
|
+
|
|
182
|
+
# Constriction factor PSO
|
|
183
|
+
pso = pso_constriction(pop_size=100)
|
|
184
|
+
|
|
185
|
+
# Fully differentiable
|
|
186
|
+
pso = PSO(pop_size=100, adaptive=True, differentiable=True)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
| `adaptive` | `differentiable` | Effect |
|
|
190
|
+
|------------|------------------|--------|
|
|
191
|
+
| False | False | Classical PSO |
|
|
192
|
+
| True | False | Inertia, c1, c2 learnable via backprop |
|
|
193
|
+
| False | True | Particle positions learnable via backprop |
|
|
194
|
+
| True | True | Both coefficients and positions learnable |
|
|
195
|
+
|
|
196
|
+
### CMA-ES
|
|
197
|
+
|
|
198
|
+
CMA-ES supports **adaptive coefficients** and **restart strategies** (IPOP/BIPOP):
|
|
199
|
+
|
|
200
|
+
```python
|
|
201
|
+
from evograd.algorithms import CMAES, cmaes_ipop, cmaes_bipop
|
|
202
|
+
|
|
203
|
+
# Classical CMA-ES
|
|
204
|
+
cmaes = CMAES(pop_size=50, sigma=0.5)
|
|
205
|
+
|
|
206
|
+
# Adaptive CMA-ES (learnable cc, cs, c1, cmu, damps)
|
|
207
|
+
cmaes = CMAES(pop_size=50, sigma=0.5, adaptive=True)
|
|
208
|
+
|
|
209
|
+
# Differentiable mean
|
|
210
|
+
cmaes = CMAES(pop_size=50, sigma=0.5, differentiable=True)
|
|
211
|
+
|
|
212
|
+
# IPOP-CMA-ES (increasing population restarts)
|
|
213
|
+
cmaes = cmaes_ipop(restarts=9, incpopsize=2)
|
|
214
|
+
|
|
215
|
+
# BIPOP-CMA-ES (alternating small/large populations)
|
|
216
|
+
cmaes = cmaes_bipop(restarts=9)
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
| `adaptive` | `differentiable` | Effect |
|
|
220
|
+
|------------|------------------|--------|
|
|
221
|
+
| False | False | Classical CMA-ES |
|
|
222
|
+
| True | False | Adaptation coefficients learnable via backprop |
|
|
223
|
+
| False | True | Distribution mean μ learnable via backprop |
|
|
224
|
+
| True | True | Both coefficients and mean learnable |
|
|
225
|
+
|
|
226
|
+
**Restart Strategies:**
|
|
227
|
+
- **IPOP**: Restart with doubled population after convergence
|
|
228
|
+
- **BIPOP**: Alternate between small (focused) and large (exploratory) populations
|
|
229
|
+
|
|
230
|
+
## 📚 Operators Library
|
|
231
|
+
|
|
232
|
+
EvoGrad provides a comprehensive library of evolutionary operators:
|
|
233
|
+
|
|
234
|
+
### Selection
|
|
235
|
+
| Operator | Description | Differentiable |
|
|
236
|
+
|----------|-------------|----------------|
|
|
237
|
+
| `RandomSelection` | Uniform random selection | ✗ |
|
|
238
|
+
| `RouletteSelection` | Fitness-proportionate (Gumbel-Softmax) | ✓ |
|
|
239
|
+
| `TournamentSelection` | Tournament with soft winner (Gumbel-Softmax) | ✓ |
|
|
240
|
+
| `RankSelection` | Rank-based probabilities | ✓ |
|
|
241
|
+
| `StochasticUniversalSampling` | SUS with soft selection | ✓ |
|
|
242
|
+
|
|
243
|
+
### Crossover
|
|
244
|
+
| Operator | Description | Differentiable |
|
|
245
|
+
|----------|-------------|----------------|
|
|
246
|
+
| `SBXCrossover` | Simulated Binary Crossover | ✓ |
|
|
247
|
+
| `BinomialCrossover` | DE-style binomial | ✓ |
|
|
248
|
+
| `ExponentialCrossover` | DE-style exponential | ✓ |
|
|
249
|
+
| `BlendCrossover` | BLX-α crossover | ✓ |
|
|
250
|
+
| `ArithmeticCrossover` | Weighted average | ✓ |
|
|
251
|
+
| `UniformCrossover` | Gene-wise uniform swap | ✓ |
|
|
252
|
+
| `SimulatedBinaryCrossover` | Alias for SBX | ✓ |
|
|
253
|
+
|
|
254
|
+
### Mutation
|
|
255
|
+
| Operator | Description | Differentiable |
|
|
256
|
+
|----------|-------------|----------------|
|
|
257
|
+
| `PolynomialMutation` | Polynomial bounded mutation | ✓ |
|
|
258
|
+
| `GaussianMutation` | Additive Gaussian noise | ✓ |
|
|
259
|
+
| `UniformMutation` | Uniform random replacement | ✓ |
|
|
260
|
+
| `AdaptiveMutation` | Self-adaptive mutation rates | ✓ |
|
|
261
|
+
|
|
262
|
+
### Survival
|
|
263
|
+
| Operator | Description |
|
|
264
|
+
|----------|-------------|
|
|
265
|
+
| `MergeSurvival` | (μ+λ) with optional elitism |
|
|
266
|
+
| `ReplacementSurvival` | (μ,λ) generational replacement |
|
|
267
|
+
| `AgingSurvival` | Age-based replacement |
|
|
268
|
+
| `FitnessSurvival` | Pure fitness-based truncation |
|
|
269
|
+
|
|
270
|
+
### Repair
|
|
271
|
+
| Operator | Description |
|
|
272
|
+
|----------|-------------|
|
|
273
|
+
| `BoundsRepair` | Clamp to bounds |
|
|
274
|
+
| `ReflectionRepair` | Bounce off boundaries |
|
|
275
|
+
| `WrapRepair` | Toroidal wrap-around |
|
|
276
|
+
| `RandomRepair` | Random resampling |
|
|
277
|
+
|
|
278
|
+
## 🎯 Advanced Usage
|
|
279
|
+
|
|
280
|
+
### Training Neural Networks with EvoGrad
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
import torch
|
|
284
|
+
import torch.nn as nn
|
|
285
|
+
from evograd.algorithms import CMAES
|
|
286
|
+
from evograd.core import Problem
|
|
287
|
+
from evograd.core.termination import MaxEvaluations
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
# Define a simple MLP
|
|
291
|
+
class MLP(nn.Module):
|
|
292
|
+
def __init__(self):
|
|
293
|
+
super().__init__()
|
|
294
|
+
self.net = nn.Sequential(
|
|
295
|
+
nn.Linear(10, 64),
|
|
296
|
+
nn.Tanh(),
|
|
297
|
+
nn.Linear(64, 1),
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
def forward(self, x):
|
|
301
|
+
return self.net(x)
|
|
302
|
+
|
|
303
|
+
# Flatten parameters for optimisation
|
|
304
|
+
model = MLP()
|
|
305
|
+
n_params = sum(p.numel() for p in model.parameters())
|
|
306
|
+
|
|
307
|
+
def loss_fn(params):
|
|
308
|
+
# Reshape flat params back to model
|
|
309
|
+
idx = 0
|
|
310
|
+
for p in model.parameters():
|
|
311
|
+
numel = p.numel()
|
|
312
|
+
p.data.copy_(params[idx:idx+numel].view(p.shape))
|
|
313
|
+
idx += numel
|
|
314
|
+
|
|
315
|
+
# Compute loss on dummy data
|
|
316
|
+
x = torch.randn(32, 10)
|
|
317
|
+
y = torch.randn(32, 1)
|
|
318
|
+
pred = model(x)
|
|
319
|
+
return ((pred - y)**2).mean()
|
|
320
|
+
|
|
321
|
+
# Batch evaluation
|
|
322
|
+
def batch_loss(pop):
|
|
323
|
+
return torch.stack([loss_fn(p) for p in pop])
|
|
324
|
+
|
|
325
|
+
problem = Problem(
|
|
326
|
+
objective=batch_loss,
|
|
327
|
+
n_var=n_params,
|
|
328
|
+
xl=-10.0,
|
|
329
|
+
xu=10.0,
|
|
330
|
+
)
|
|
331
|
+
|
|
332
|
+
cmaes = CMAES(pop_size=50, sigma=1.0, adaptive=True)
|
|
333
|
+
result = minimize(problem, cmaes, MaxEvaluations(10000))
|
|
334
|
+
print(f"Final loss: {result.best_fitness:.6f}")
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### Callbacks for Logging
|
|
338
|
+
|
|
339
|
+
```python
|
|
340
|
+
from evograd.core import minimize
|
|
341
|
+
from evograd.utils import HistoryCallback, PrintCallback
|
|
342
|
+
|
|
343
|
+
callbacks = [
|
|
344
|
+
PrintCallback(every=10), # Print progress every 10 generations
|
|
345
|
+
HistoryCallback(), # Record full history
|
|
346
|
+
]
|
|
347
|
+
|
|
348
|
+
result = minimize(problem, algorithm, termination=MaxEvaluations(10000), callback=callbacks)
|
|
349
|
+
|
|
350
|
+
# Access history
|
|
351
|
+
print(f"Fitness over time: {result.history['best_fitness']}")
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
## 🏗️ Architecture
|
|
355
|
+
|
|
356
|
+
```
|
|
357
|
+
evograd/
|
|
358
|
+
├── algorithms/
|
|
359
|
+
│ └── cmaes.py # CMA-ES with IPOP/BIPOP
|
|
360
|
+
│ ├── de.py # Differential Evolution
|
|
361
|
+
│ ├── ga.py # Genetic Algorithm
|
|
362
|
+
│ ├── pso.py # Particle Swarm Optimisation
|
|
363
|
+
├── core/
|
|
364
|
+
│ ├── algorithm.py # Base Algorithm class
|
|
365
|
+
│ ├── maximize.py # Optimisation loop (maximisation)
|
|
366
|
+
│ ├── minimize.py # Optimisation loop (minimisation)
|
|
367
|
+
│ ├── problem.py # Problem definition
|
|
368
|
+
│ ├── result.py # Result container
|
|
369
|
+
│ └── termination.py # Stopping criteria
|
|
370
|
+
├── operators/
|
|
371
|
+
│ ├── crossover.py # Crossover operators
|
|
372
|
+
│ ├── mutation.py # Mutation operators
|
|
373
|
+
│ ├── sampling.py # Sampling operators
|
|
374
|
+
│ ├── selection.py # Selection operators
|
|
375
|
+
│ ├── survival.py # Survival/replacement
|
|
376
|
+
│ └── repair.py # Constraint handling
|
|
377
|
+
└── utils/
|
|
378
|
+
├── callbacks.py # Logging utilities
|
|
379
|
+
├── device.py # Device management
|
|
380
|
+
└── duplicates.py # Duplicate elimination
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
## 🔬 How It Works
|
|
384
|
+
|
|
385
|
+
EvoGrad makes evolutionary algorithms differentiable through:
|
|
386
|
+
|
|
387
|
+
1. **Reparameterisation Trick**: Convert random sampling into deterministic transformations of parameter-free noise:
|
|
388
|
+
```
|
|
389
|
+
x = g_θ(ε), ε ~ p(ε) → ∇_θ L ≈ ∇_θ f(g_θ(ε))
|
|
390
|
+
```
|
|
391
|
+
|
|
392
|
+
2. **Gumbel-Softmax**: Differentiable approximation for categorical selection:
|
|
393
|
+
```python
|
|
394
|
+
# Soft selection (differentiable)
|
|
395
|
+
probs = softmax((log_probs + gumbel_noise) / temperature)
|
|
396
|
+
selected = probs @ population # Weighted combination
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
3. **Binary-Concrete**: Differentiable approximation for binary masks (mutation/crossover):
|
|
400
|
+
```python
|
|
401
|
+
# Soft mask (differentiable)
|
|
402
|
+
mask = sigmoid((log(u) - log(1-u) + logits) / temperature)
|
|
403
|
+
# Straight-through estimator for hard decisions
|
|
404
|
+
hard_mask = (mask > 0.5).float() - mask.detach() + mask
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
4. **Pathwise Gradients**: For continuous distributions (Gaussian sampling in CMA-ES):
|
|
408
|
+
```python
|
|
409
|
+
# x = μ + σ * L @ z, z ~ N(0, I)
|
|
410
|
+
z = torch.randn(pop_size, n_var)
|
|
411
|
+
x = mean + sigma * (L @ z.T).T # Fully differentiable
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
## 📊 Benchmarks
|
|
415
|
+
|
|
416
|
+
TODO
|
|
417
|
+
|
|
418
|
+
## 📖 Citation
|
|
419
|
+
|
|
420
|
+
TBA
|
|
421
|
+
|
|
422
|
+
## 📄 License
|
|
423
|
+
|
|
424
|
+
This project is licensed under the Apache-2.0 License - see the [LICENSE](LICENSE) file for details.
|
|
425
|
+
|
|
426
|
+
## 🤝 Contributing
|
|
427
|
+
|
|
428
|
+
Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.
|
|
429
|
+
|
|
430
|
+
1. Fork the repository
|
|
431
|
+
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
|
|
432
|
+
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
|
433
|
+
4. Push to the branch (`git push origin feature/amazing-feature`)
|
|
434
|
+
5. Open a Pull Request
|
|
435
|
+
|
|
436
|
+
## 🙏 Acknowledgements
|
|
437
|
+
|
|
438
|
+
- Inspired by [pymoo](https://pymoo.org/) for API design
|
|
439
|
+
- Built with [PyTorch](https://pytorch.org/) for automatic differentiation
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
evograd/__init__.py,sha256=OVdolqUOenKujo0OUIL4Q0mNJCJqkyNRT9OWqJsh5kU,2141
|
|
2
|
+
evograd/algorithms/__init__.py,sha256=tckBjddiPRHdTJIujb5q5ptWhKCCfHDeN267_B7QX-c,3190
|
|
3
|
+
evograd/algorithms/cmaes.py,sha256=ToWdHWhm0uD71C8m1TQE4NtcT6JR9Hwf_I1-v63OO9s,47435
|
|
4
|
+
evograd/algorithms/de.py,sha256=eGv0pEOOtGWXG-e4BJNY1Ed4rRC8MwWd-FoPeQuslQc,32996
|
|
5
|
+
evograd/algorithms/ga.py,sha256=d_6DAjW7NPNefJWeoaQZY1Op74ijLZENzv51P2wPYfU,17646
|
|
6
|
+
evograd/algorithms/pso.py,sha256=AKq8vT6Zf7DM0HQnQtksfMeJwwH5fOzFbdJsQXvDvyo,21567
|
|
7
|
+
evograd/algorithms/shade.py,sha256=yEoYw0VgCB4uXlgshjTJdBBAb31DIjP74rvuswerl3w,41213
|
|
8
|
+
evograd/benchmarks/run_benchmark_functions.py,sha256=zAo3lFGj5wgRxHDl-1HH4RznmQoPmS9a8rmKgyotdSA,44602
|
|
9
|
+
evograd/benchmarks/functions/__init__.py,sha256=ZyMJwNUBJ9Adkt8uJU4lM2yYLJ3k-grwaqOOxkdOFTk,5046
|
|
10
|
+
evograd/benchmarks/functions/base.py,sha256=AdGSBG3ZnKjuikVtLmqLKD6DSYiqqtVXbBRTtuMcpi8,6797
|
|
11
|
+
evograd/benchmarks/functions/classical.py,sha256=GlfQ7OdprmSlo2yT0_kIEe3-iqJYofufI_pi7z4AaH0,17782
|
|
12
|
+
evograd/benchmarks/functions/smoothed_funnel.py,sha256=RYRm3G0_f34jBUP5kfLms_WlVJkc7PDOUzTBT3dje9g,15635
|
|
13
|
+
evograd/benchmarks/functions/transforms.py,sha256=yQIKMnXGgWa7eVoueT4GLLc8cfF8AqGsNb6fuxC5ZT4,13744
|
|
14
|
+
evograd/benchmarks/functions/cec2017/__init__.py,sha256=XMtpn1fN-8VQUzeasCrSv5X25N-oLkgwnObeuWx5Bkg,5840
|
|
15
|
+
evograd/benchmarks/functions/cec2017/basic.py,sha256=HHkHUuGxI7QQMMpCCuXol2Rjbzk-CGHBo2QyE_-h4KI,10962
|
|
16
|
+
evograd/benchmarks/functions/cec2017/composition.py,sha256=OikeBgiJvGMf0nbj4YkJp9pPEfwjqVGZxWcZR8x_SoU,18161
|
|
17
|
+
evograd/benchmarks/functions/cec2017/data.py,sha256=SSdacN0qIGaoUrOhtZK4pzcM0vs--24TaiS0C-tyeBs,10372
|
|
18
|
+
evograd/benchmarks/functions/cec2017/hybrid.py,sha256=MpudxERgGc8R2SoYmgkMhv5zndjdVwzJyYI2hZ7Kb40,11783
|
|
19
|
+
evograd/benchmarks/functions/cec2017/simple.py,sha256=xp1x22p02JRRdihZxzGJM-ClRPDGyVgMkppZ9ZwrP90,9272
|
|
20
|
+
evograd/core/__init__.py,sha256=CZkCkmZJp8W3-b60g0IgiWDX1a5-DtFGhpXmJiWUOXA,1877
|
|
21
|
+
evograd/core/algorithm.py,sha256=uK_WhcuXNisq4eUojimIo07nwDDOdQAIZE5pYPCoNRA,28007
|
|
22
|
+
evograd/core/maximize.py,sha256=x39c2Fwot89AJkvOyxIaKwZuGZhjSHgmUUMdpOOT5TE,9754
|
|
23
|
+
evograd/core/minimize.py,sha256=hNmn-SElGucDYHkNpqR6u4keaq3SLlRqbDtqBsVk_Pk,26954
|
|
24
|
+
evograd/core/problem.py,sha256=GYINuJfdR4ZjXQAV_rUKhg8jbusmjv3qVkPxAVUnlIQ,14858
|
|
25
|
+
evograd/core/result.py,sha256=LOyaar71CN4ma4-Q5Sb9HgyyIq7uDzsHWO-4IZTEH4w,18819
|
|
26
|
+
evograd/core/termination.py,sha256=WzNrSyjxEEWl3IxUyKlRhd_J7WvcbMv3TlKMLHant3Q,18862
|
|
27
|
+
evograd/operators/__init__.py,sha256=lGqBT-lE0KRrZ3ID6astqdIQ09xWA_rPGDRer-A1m2w,4626
|
|
28
|
+
evograd/operators/crossover.py,sha256=wCzp0eFYv_ZhGiPtsEEf5st6P_ADLGuIwEnLib3kJRs,37222
|
|
29
|
+
evograd/operators/mutation.py,sha256=fdMob0pngQZWN3wfBe_GKn0r4jobHfBjq0hrVo0JUp4,36223
|
|
30
|
+
evograd/operators/relaxations.py,sha256=r3ZDxD2YjR-Bire7bUdGz6EeNFUI4vFDK2GeP_VG6Do,5361
|
|
31
|
+
evograd/operators/repair.py,sha256=OFfN9SWx6j9S1-R0QbpQ71yTskjNcj99QnNw5R7m0G0,16950
|
|
32
|
+
evograd/operators/sampling.py,sha256=YEmM8XE-oyyImGvrhYHQ59assEvvJoZ0PuemsgMb8Hw,18865
|
|
33
|
+
evograd/operators/selection.py,sha256=H38qFAYWcyz5iVc3sTv8vM7T5cxdTaWEfPRIXr4C0gM,32665
|
|
34
|
+
evograd/operators/survival.py,sha256=r56ZctWSXUHHviXR-Gscev1mNOyWVW9R1iOPPRJN5W4,36437
|
|
35
|
+
evograd/tests/__init__.py,sha256=uC0DSDJQ8vpVxa47IAB7nEOxq4BaJICMk3PXlND0VFY,198
|
|
36
|
+
evograd/tests/run_all.py,sha256=QVMnQKc_trP-KSYfwCyYk8Gfi3sjzRXfcmdBK9XSZow,1947
|
|
37
|
+
evograd/tests/test_core.py,sha256=Qe4zsOh_KaF0fBSsqNc7igp_pI3movN9egIwWM70xrw,16871
|
|
38
|
+
evograd/tests/test_ga.py,sha256=5ipLeXk_bI42iz0SuZ1604xmnlJmv4YRpdyAepzzDDM,17157
|
|
39
|
+
evograd/tests/test_operators.py,sha256=E5oRZXGU5L7zFDiJ5vUc-va9UoIQ1qMJITqfJY0VTzI,21993
|
|
40
|
+
evograd/tests/test_per_individual.py,sha256=JRVis_dI3uuNHMGL14Vt-E-i6IOOBnOzR7lS0ztHawY,12907
|
|
41
|
+
evograd/tests/test_utils.py,sha256=SYUBt1SXZwNQXXXxIXHmC0ZSngbByHsDIRoGiouBPA4,10167
|
|
42
|
+
evograd/utils/__init__.py,sha256=DdQY1bRvpCJ-04fSa6GHK5p0LvFYXrCBfLb6zsakzIQ,2124
|
|
43
|
+
evograd/utils/callbacks.py,sha256=wMytUrw2QTJ_Xv0mHE56e-rZ46tKqYLxSO_xse5BzIQ,33302
|
|
44
|
+
evograd/utils/device.py,sha256=jESrKGJSVFu_uxNTuzpRSwTP0rNbncUZsxqH1zjscSw,14323
|
|
45
|
+
evograd/utils/duplicates.py,sha256=uDEZG9u-_XUrswvhRwwULQrlFd7ENgEL-zIJORCfMXA,13305
|
|
46
|
+
evograd/benchmarks/functions/cec2017/data.pkl,sha256=X5eOxmIOZZ6pM-IcTEOYtHuBFGfT7tPcsgRWbDllkks,24612828
|
|
47
|
+
evograd_diff-0.1.0.dist-info/METADATA,sha256=JfwSzpcKUDrzuZfgOT--T_Yl8FqHbTJlzyCnyJU1_AM,14831
|
|
48
|
+
evograd_diff-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
49
|
+
evograd_diff-0.1.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
50
|
+
evograd_diff-0.1.0.dist-info/RECORD,,
|