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.
Files changed (50) hide show
  1. evograd/__init__.py +67 -0
  2. evograd/algorithms/__init__.py +138 -0
  3. evograd/algorithms/cmaes.py +1365 -0
  4. evograd/algorithms/de.py +895 -0
  5. evograd/algorithms/ga.py +532 -0
  6. evograd/algorithms/pso.py +648 -0
  7. evograd/algorithms/shade.py +1165 -0
  8. evograd/benchmarks/functions/__init__.py +229 -0
  9. evograd/benchmarks/functions/base.py +217 -0
  10. evograd/benchmarks/functions/cec2017/__init__.py +250 -0
  11. evograd/benchmarks/functions/cec2017/basic.py +413 -0
  12. evograd/benchmarks/functions/cec2017/composition.py +580 -0
  13. evograd/benchmarks/functions/cec2017/data.pkl +0 -0
  14. evograd/benchmarks/functions/cec2017/data.py +350 -0
  15. evograd/benchmarks/functions/cec2017/hybrid.py +406 -0
  16. evograd/benchmarks/functions/cec2017/simple.py +326 -0
  17. evograd/benchmarks/functions/classical.py +649 -0
  18. evograd/benchmarks/functions/smoothed_funnel.py +476 -0
  19. evograd/benchmarks/functions/transforms.py +463 -0
  20. evograd/benchmarks/run_benchmark_functions.py +1208 -0
  21. evograd/core/__init__.py +73 -0
  22. evograd/core/algorithm.py +778 -0
  23. evograd/core/maximize.py +269 -0
  24. evograd/core/minimize.py +740 -0
  25. evograd/core/problem.py +444 -0
  26. evograd/core/result.py +571 -0
  27. evograd/core/termination.py +602 -0
  28. evograd/operators/__init__.py +178 -0
  29. evograd/operators/crossover.py +1117 -0
  30. evograd/operators/mutation.py +1098 -0
  31. evograd/operators/relaxations.py +175 -0
  32. evograd/operators/repair.py +601 -0
  33. evograd/operators/sampling.py +577 -0
  34. evograd/operators/selection.py +981 -0
  35. evograd/operators/survival.py +1000 -0
  36. evograd/tests/__init__.py +11 -0
  37. evograd/tests/run_all.py +78 -0
  38. evograd/tests/test_core.py +528 -0
  39. evograd/tests/test_ga.py +572 -0
  40. evograd/tests/test_operators.py +662 -0
  41. evograd/tests/test_per_individual.py +326 -0
  42. evograd/tests/test_utils.py +328 -0
  43. evograd/utils/__init__.py +97 -0
  44. evograd/utils/callbacks.py +926 -0
  45. evograd/utils/device.py +502 -0
  46. evograd/utils/duplicates.py +421 -0
  47. evograd_diff-0.1.0.dist-info/METADATA +439 -0
  48. evograd_diff-0.1.0.dist-info/RECORD +50 -0
  49. evograd_diff-0.1.0.dist-info/WHEEL +4 -0
  50. evograd_diff-0.1.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,413 @@
1
+ """
2
+ CEC 2017 Basic Functions.
3
+
4
+ Core mathematical functions used by CEC 2017 benchmark suite.
5
+ These are stateless functions that operate on already-transformed inputs.
6
+
7
+ Reference: CEC 2017 Competition on Real-Parameter Single Objective Optimization
8
+ """
9
+
10
+ import torch
11
+ from torch import Tensor
12
+ from typing import Optional
13
+
14
+
15
+ def bent_cigar(x: Tensor) -> Tensor:
16
+ """
17
+ Bent Cigar function.
18
+ f(x) = x_1^2 + 10^7 * sum(x_i^2) for i > 1
19
+ """
20
+ return x[..., 0] ** 2 + 10e6 * (x[..., 1:] ** 2).sum(dim=-1)
21
+
22
+
23
+ def sum_diff_pow(x: Tensor) -> Tensor:
24
+ """
25
+ Sum of Different Powers function.
26
+ f(x) = sum(|x_i|^(i+1))
27
+ """
28
+ n = x.shape[-1]
29
+ i = torch.arange(1, n + 1, device=x.device, dtype=x.dtype)
30
+ return (torch.abs(x) ** i).sum(dim=-1)
31
+
32
+
33
+ def zakharov(x: Tensor) -> Tensor:
34
+ """
35
+ Zakharov function.
36
+ f(x) = sum(x_i^2) + (0.5 * sum(i * x_i))^2 + (0.5 * sum(i * x_i))^4
37
+ """
38
+ n = x.shape[-1]
39
+ i = torch.arange(1, n + 1, device=x.device, dtype=x.dtype)
40
+ sum_sq = (x ** 2).sum(dim=-1)
41
+ sum_ix = (i * x).sum(dim=-1)
42
+ term = 0.5 * sum_ix
43
+ return sum_sq + term ** 2 + term ** 4
44
+
45
+
46
+ def rosenbrock(x: Tensor) -> Tensor:
47
+ """
48
+ Rosenbrock function (CEC scaled version).
49
+ Scales input by 0.02048 and shifts by 1.
50
+ f(x) = sum(100*(x_{i+1} - x_i^2)^2 + (x_i - 1)^2)
51
+ """
52
+ x = 0.02048 * x + 1.0
53
+ x_i = x[..., :-1]
54
+ x_ip1 = x[..., 1:]
55
+ term1 = 100 * (x_ip1 - x_i ** 2) ** 2
56
+ term2 = (x_i - 1) ** 2
57
+ return (term1 + term2).sum(dim=-1)
58
+
59
+
60
+ def rastrigin(x: Tensor) -> Tensor:
61
+ """
62
+ Rastrigin function (CEC scaled version).
63
+ Scales input by 0.0512.
64
+ f(x) = sum(x_i^2 - 10*cos(2*pi*x_i) + 10)
65
+ """
66
+ x = 0.0512 * x
67
+ return (x ** 2 - 10 * torch.cos(2 * torch.pi * x) + 10).sum(dim=-1)
68
+
69
+
70
+ def expanded_schaffers_f6(x: Tensor) -> Tensor:
71
+ """
72
+ Expanded Schaffer's F6 function.
73
+ f(x) = sum(0.5 + (sin^2(sqrt(x_i^2 + x_{i+1}^2)) - 0.5) / (1 + 0.001*(x_i^2 + x_{i+1}^2))^2)
74
+ """
75
+ x_i = x[..., :-1]
76
+ x_ip1 = x[..., 1:]
77
+ t = x_i ** 2 + x_ip1 ** 2
78
+ sin_term = torch.sin(torch.sqrt(t)) ** 2 - 0.5
79
+ denom = (1 + 0.001 * t) ** 2
80
+ return (0.5 + sin_term / denom).sum(dim=-1)
81
+
82
+
83
+ def lunacek_bi_rastrigin(
84
+ x: Tensor,
85
+ shift: Optional[Tensor] = None,
86
+ rotation: Optional[Tensor] = None,
87
+ ) -> Tensor:
88
+ """
89
+ Lunacek Bi-Rastrigin function.
90
+ A special case that requires shift and rotation to be passed directly.
91
+ """
92
+ nx = x.shape[-1]
93
+ batch_shape = x.shape[:-1]
94
+
95
+ if shift is None:
96
+ shift = torch.zeros(nx, device=x.device, dtype=x.dtype)
97
+
98
+ # Ensure shift has correct shape for broadcasting
99
+ shift = shift.to(x.device, x.dtype)
100
+ if shift.dim() == 1:
101
+ shift = shift.unsqueeze(0) # [1, nx]
102
+
103
+ # Calculate coefficients
104
+ mu0 = 2.5
105
+ s = 1 - 1 / (2 * ((nx + 20) ** 0.5) - 8.2)
106
+ mu1 = -((mu0 * mu0 - 1) / s) ** 0.5
107
+
108
+ # Shift and scale
109
+ y = 0.1 * (x - shift)
110
+
111
+ tmpx = 2 * y.clone()
112
+ # Flip sign where shift < 0
113
+ mask = (shift < 0).expand_as(tmpx)
114
+ tmpx = torch.where(mask, -tmpx, tmpx)
115
+
116
+ z = tmpx.clone()
117
+ tmpx = tmpx + mu0
118
+
119
+ # Term 1: sum((tmpx - mu0)^2)
120
+ t1 = ((tmpx - mu0) ** 2).sum(dim=-1)
121
+
122
+ # Term 2: s * sum((tmpx - mu1)^2) + nx
123
+ t2 = s * ((tmpx - mu1) ** 2).sum(dim=-1) + nx
124
+
125
+ # Apply rotation if provided
126
+ if rotation is not None:
127
+ rotation = rotation.to(x.device, x.dtype)
128
+ # z @ R.T for batch matrix multiplication
129
+ z = z @ rotation.T
130
+
131
+ # Cosine term
132
+ cos_term = torch.cos(2.0 * torch.pi * z).sum(dim=-1)
133
+
134
+ # Result
135
+ result = torch.minimum(t1, t2) + 10.0 * (nx - cos_term)
136
+ return result
137
+
138
+
139
+ def non_cont_rastrigin(
140
+ x: Tensor,
141
+ shift: Optional[Tensor] = None,
142
+ rotation: Optional[Tensor] = None,
143
+ ) -> Tensor:
144
+ """
145
+ Non-Continuous Rastrigin function.
146
+ A special case that requires shift and rotation to be passed directly.
147
+ """
148
+ nx = x.shape[-1]
149
+
150
+ if shift is None:
151
+ shift = torch.zeros(nx, device=x.device, dtype=x.dtype)
152
+
153
+ shift = shift.to(x.device, x.dtype)
154
+ if shift.dim() == 1:
155
+ shift = shift.unsqueeze(0)
156
+
157
+ shifted = x - shift
158
+
159
+ # Apply non-continuity
160
+ x_mod = x.clone()
161
+ mask = torch.abs(shifted) > 0.5
162
+ x_mod = torch.where(
163
+ mask,
164
+ shift + torch.floor(2 * shifted + 0.5) * 0.5,
165
+ x_mod
166
+ )
167
+
168
+ # Scale
169
+ z = 0.0512 * shifted
170
+
171
+ # Apply rotation if provided
172
+ if rotation is not None:
173
+ rotation = rotation.to(x.device, x.dtype)
174
+ z = z @ rotation.T
175
+
176
+ # Rastrigin formula
177
+ result = (z ** 2 - 10 * torch.cos(2 * torch.pi * z) + 10).sum(dim=-1)
178
+ return result
179
+
180
+
181
+ def levy(x: Tensor) -> Tensor:
182
+ """
183
+ Levy function (CEC version without scaling).
184
+ """
185
+ w = 1.0 + 0.25 * (x - 1.0)
186
+
187
+ term1 = torch.sin(torch.pi * w[..., 0]) ** 2
188
+
189
+ w_inner = w[..., :-1]
190
+ term2 = ((w_inner - 1) ** 2 * (1 + 10 * torch.sin(torch.pi * w_inner + 1) ** 2)).sum(dim=-1)
191
+
192
+ term3 = (w[..., -1] - 1) ** 2 * (1 + torch.sin(2 * torch.pi * w[..., -1]) ** 2)
193
+
194
+ return term1 + term2 + term3
195
+
196
+
197
+ def modified_schwefel(x: Tensor) -> Tensor:
198
+ """
199
+ Modified Schwefel function.
200
+ """
201
+ nx = x.shape[-1]
202
+ x = 10.0 * x # Scale to search range
203
+
204
+ z = x + 420.9687462275036
205
+
206
+ # Initialize result
207
+ result = z * torch.sin(torch.sqrt(torch.abs(z)))
208
+
209
+ # Handle z < -500
210
+ mask1 = z < -500
211
+ zm1 = torch.fmod(torch.abs(z), 500.0) - 500
212
+ penalty1 = (z + 500) ** 2 / (10000 * nx)
213
+ result = torch.where(mask1, zm1 * torch.sin(torch.sqrt(torch.abs(zm1))) - penalty1, result)
214
+
215
+ # Handle z > 500
216
+ mask2 = z > 500
217
+ zm2 = 500 - torch.fmod(torch.abs(z), 500.0)
218
+ penalty2 = (z - 500) ** 2 / (10000 * nx)
219
+ result = torch.where(mask2, zm2 * torch.sin(torch.sqrt(torch.abs(zm2))) - penalty2, result)
220
+
221
+ return 418.9829 * nx - result.sum(dim=-1)
222
+
223
+
224
+ def high_conditioned_elliptic(x: Tensor) -> Tensor:
225
+ """
226
+ High Conditioned Elliptic function.
227
+ f(x) = sum(10^(6*(i-1)/(n-1)) * x_i^2)
228
+ """
229
+ n = x.shape[-1]
230
+ i = torch.arange(n, device=x.device, dtype=x.dtype)
231
+ factor = 6.0 / max(n - 1, 1)
232
+ weights = 10 ** (i * factor)
233
+ return (weights * x ** 2).sum(dim=-1)
234
+
235
+
236
+ def discus(x: Tensor) -> Tensor:
237
+ """
238
+ Discus function.
239
+ f(x) = 10^6 * x_1^2 + sum(x_i^2) for i > 1
240
+ """
241
+ return 1e6 * x[..., 0] ** 2 + (x[..., 1:] ** 2).sum(dim=-1)
242
+
243
+
244
+ def ackley(x: Tensor) -> Tensor:
245
+ """
246
+ Ackley function.
247
+ """
248
+ n = x.shape[-1]
249
+ sum_sq = (x ** 2).sum(dim=-1)
250
+ sum_cos = torch.cos(2 * torch.pi * x).sum(dim=-1)
251
+ return -20 * torch.exp(-0.2 * torch.sqrt(sum_sq / n)) - torch.exp(sum_cos / n) + 20 + torch.e
252
+
253
+
254
+ def weierstrass(x: Tensor) -> Tensor:
255
+ """
256
+ Weierstrass function (CEC scaled version).
257
+ Scales input by 0.005.
258
+ """
259
+ x = 0.005 * x
260
+ nx = x.shape[-1]
261
+
262
+ k = torch.arange(0, 21, device=x.device, dtype=x.dtype)
263
+ a_k = 0.5 ** k
264
+ b_k = torch.pi * (3.0 ** k)
265
+
266
+ # Compute constant term
267
+ const = (a_k * torch.cos(b_k)).sum()
268
+
269
+ # Compute sum over dimensions
270
+ # Shape: [..., nx, 21]
271
+ inner = 2 * (x.unsqueeze(-1) + 0.5) * b_k
272
+ result = (a_k * torch.cos(inner)).sum(dim=-1).sum(dim=-1)
273
+
274
+ return result - nx * const
275
+
276
+
277
+ def griewank(x: Tensor) -> Tensor:
278
+ """
279
+ Griewank function (CEC scaled version).
280
+ Scales input by 6.0.
281
+ """
282
+ x = 6.0 * x
283
+ nx = x.shape[-1]
284
+ i = torch.arange(1, nx + 1, device=x.device, dtype=x.dtype)
285
+
286
+ sum_term = (x ** 2).sum(dim=-1) / 4000
287
+ prod_term = torch.cos(x / torch.sqrt(i)).prod(dim=-1)
288
+
289
+ return sum_term - prod_term + 1
290
+
291
+
292
+ def katsuura(x: Tensor) -> Tensor:
293
+ """
294
+ Katsuura function (CEC scaled version).
295
+ Scales input by 0.05.
296
+ """
297
+ x = 0.05 * x
298
+ nx = x.shape[-1]
299
+ pw = 10.0 / (nx ** 1.2)
300
+
301
+ # j = 1, 2, ..., 32
302
+ j = torch.arange(1, 33, device=x.device, dtype=x.dtype)
303
+ tj = 2.0 ** j # [32]
304
+
305
+ # tjx: [..., nx, 32]
306
+ tjx = tj * x.unsqueeze(-1)
307
+ t = torch.abs(tjx - torch.round(tjx)) / tj
308
+ tsm = t.sum(dim=-1) # [..., nx]
309
+
310
+ # Product term
311
+ i = torch.arange(1, nx + 1, device=x.device, dtype=x.dtype)
312
+ prd = ((1 + i * tsm) ** pw).prod(dim=-1)
313
+
314
+ df = 10.0 / (nx * nx)
315
+ return df * prd - df
316
+
317
+
318
+ def happy_cat(x: Tensor) -> Tensor:
319
+ """
320
+ Happy Cat function (CEC scaled version).
321
+ Scales input by 0.05 and shifts by -1.
322
+ """
323
+ x = 0.05 * x - 1
324
+ nx = x.shape[-1]
325
+
326
+ sum_x = x.sum(dim=-1)
327
+ sum_sq = (x ** 2).sum(dim=-1)
328
+
329
+ return torch.abs(sum_sq - nx) ** 0.25 + (0.5 * sum_sq + sum_x) / nx + 0.5
330
+
331
+
332
+ def h_g_bat(x: Tensor) -> Tensor:
333
+ """
334
+ HGBat function (CEC scaled version).
335
+ Scales input by 0.05 and shifts by -1.
336
+ """
337
+ x = 0.05 * x - 1
338
+ nx = x.shape[-1]
339
+
340
+ sum_x = x.sum(dim=-1)
341
+ sum_sq = (x ** 2).sum(dim=-1)
342
+
343
+ return torch.abs(sum_sq ** 2 - sum_x ** 2) ** 0.5 + (0.5 * sum_sq + sum_x) / nx + 0.5
344
+
345
+
346
+ def expanded_griewanks_plus_rosenbrock(x: Tensor) -> Tensor:
347
+ """
348
+ Expanded Griewank's plus Rosenbrock function (CEC scaled version).
349
+ Scales input by 0.05 and shifts by 1.
350
+ """
351
+ x = 0.05 * x + 1
352
+
353
+ # Rosenbrock part for consecutive pairs
354
+ x_i = x[..., :-1]
355
+ x_ip1 = x[..., 1:]
356
+ tmp1 = x_i ** 2 - x_ip1
357
+ tmp2 = x_i - 1.0
358
+ temp = 100 * tmp1 ** 2 + tmp2 ** 2
359
+
360
+ # Griewank applied to Rosenbrock result
361
+ grie = temp ** 2 / 4000 - torch.cos(temp) + 1
362
+
363
+ # Wrap-around term (last to first)
364
+ tmp1_wrap = x[..., -1:] ** 2 - x[..., :1]
365
+ tmp2_wrap = x[..., -1:] - 1.0
366
+ temp_wrap = 100 * tmp1_wrap ** 2 + tmp2_wrap ** 2
367
+ grie_wrap = temp_wrap ** 2 / 4000 - torch.cos(temp_wrap) + 1
368
+
369
+ return grie.sum(dim=-1) + grie_wrap.sum(dim=-1)
370
+
371
+
372
+ def schaffers_f7(x: Tensor) -> Tensor:
373
+ """
374
+ Schaffer's F7 function.
375
+ """
376
+ nx = x.shape[-1]
377
+
378
+ x_i = x[..., :-1]
379
+ x_ip1 = x[..., 1:]
380
+
381
+ si = torch.sqrt(x_i ** 2 + x_ip1 ** 2)
382
+ tmp = torch.sin(50 * si ** 0.2)
383
+
384
+ # Note: Original CEC code has tmp squared (appears to be intentional)
385
+ sm = (torch.sqrt(si) * (tmp ** 2 + 1)).sum(dim=-1)
386
+
387
+ denom = (nx - 1) ** 2
388
+ return (sm ** 2) / denom
389
+
390
+
391
+ # Registry of all basic functions
392
+ BASIC_FUNCTIONS = {
393
+ "bent_cigar": bent_cigar,
394
+ "sum_diff_pow": sum_diff_pow,
395
+ "zakharov": zakharov,
396
+ "rosenbrock": rosenbrock,
397
+ "rastrigin": rastrigin,
398
+ "expanded_schaffers_f6": expanded_schaffers_f6,
399
+ "lunacek_bi_rastrigin": lunacek_bi_rastrigin,
400
+ "non_cont_rastrigin": non_cont_rastrigin,
401
+ "levy": levy,
402
+ "modified_schwefel": modified_schwefel,
403
+ "high_conditioned_elliptic": high_conditioned_elliptic,
404
+ "discus": discus,
405
+ "ackley": ackley,
406
+ "weierstrass": weierstrass,
407
+ "griewank": griewank,
408
+ "katsuura": katsuura,
409
+ "happy_cat": happy_cat,
410
+ "h_g_bat": h_g_bat,
411
+ "expanded_griewanks_plus_rosenbrock": expanded_griewanks_plus_rosenbrock,
412
+ "schaffers_f7": schaffers_f7,
413
+ }