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,269 @@
1
+ """
2
+ Maximisation function for EvoGrad optimisation.
3
+
4
+ This module provides a maximisation interface that internally
5
+ converts to minimisation by negating the objective function.
6
+
7
+ Example:
8
+ >>> from evograd.core.problem import Problem
9
+ >>> from evograd.core.maximize import maximize
10
+ >>> from evograd.core.termination import MaxEvaluations
11
+ >>> from evograd.algorithms import GA
12
+ >>>
13
+ >>> # Define problem (we want to MAXIMIZE this)
14
+ >>> problem = Problem(
15
+ ... objective=lambda x: torch.sin(x).sum(dim=-1), # Want to maximize
16
+ ... n_var=30,
17
+ ... xl=-3.14,
18
+ ... xu=3.14,
19
+ ... )
20
+ >>>
21
+ >>> # Create algorithm
22
+ >>> algorithm = GA(pop_size=100)
23
+ >>>
24
+ >>> # Run maximisation
25
+ >>> result = maximize(
26
+ ... problem,
27
+ ... algorithm,
28
+ ... termination=MaxEvaluations(10000),
29
+ ... seed=42,
30
+ ... )
31
+ >>>
32
+ >>> # Note: result.best_fitness is the ACTUAL fitness (not negated)
33
+ >>> print(f"Best (max) fitness: {result.best_fitness}")
34
+ """
35
+
36
+ from __future__ import annotations
37
+
38
+ from typing import TYPE_CHECKING, List, Optional, Union
39
+
40
+ import torch
41
+ from torch import Tensor
42
+
43
+ from evograd.utils.callbacks import Callback
44
+ from evograd.core.minimize import minimize
45
+ from evograd.core.problem import Problem
46
+ from evograd.core.result import Result
47
+ from evograd.core.termination import Termination
48
+
49
+ if TYPE_CHECKING:
50
+ from evograd.core.algorithm import Algorithm
51
+
52
+ __all__ = [
53
+ "maximize",
54
+ ]
55
+
56
+
57
+ class _NegatedProblem(Problem):
58
+ """
59
+ Wrapper that negates the objective function for maximisation.
60
+
61
+ This is an internal class used by maximize() to convert
62
+ maximisation to minimisation.
63
+
64
+ Instead of calling super().__init__() with all parameters,
65
+ we directly copy the relevant attributes from the original
66
+ problem to avoid parameter validation issues.
67
+ """
68
+
69
+ def __init__(self, problem: Problem) -> None:
70
+ # Initialize nn.Module directly (skip Problem.__init__)
71
+ torch.nn.Module.__init__(self)
72
+
73
+ # Copy all relevant attributes from original problem
74
+ self.n_var = problem.n_var
75
+ self.n_obj = problem.n_obj
76
+ self._objective = None # We override _evaluate
77
+ self.name = f"Negated({problem.name})" if problem.name else "Negated"
78
+ self.device = problem.device
79
+ self.dtype = problem.dtype
80
+
81
+ # Register bounds as buffers (copy from original)
82
+ self.register_buffer("xl", problem.xl.clone())
83
+ self.register_buffer("xu", problem.xu.clone())
84
+
85
+ # Copy constraint info
86
+ self._constraints = problem._constraints.copy() if hasattr(problem, '_constraints') else []
87
+ self.n_ieq_constr = problem.n_ieq_constr
88
+ self.n_eq_constr = problem.n_eq_constr
89
+ self.n_constr = problem.n_constr
90
+
91
+ # Store reference to original problem
92
+ self._original_problem = problem
93
+
94
+ def _evaluate(self, x: Tensor) -> Tensor:
95
+ """Negate the original objective."""
96
+ return -self._original_problem._evaluate(x)
97
+
98
+ @property
99
+ def original_problem(self) -> Problem:
100
+ """Access the original (non-negated) problem."""
101
+ return self._original_problem
102
+
103
+
104
+ class _NegatedResult(Result):
105
+ """
106
+ Result wrapper that negates fitness values back.
107
+
108
+ Used to return the actual (non-negated) fitness values
109
+ to the user after maximisation.
110
+ """
111
+
112
+ @classmethod
113
+ def from_minimization_result(cls, result: Result) -> Result:
114
+ """Create a maximisation result from a minimisation result."""
115
+ # Negate fitness values
116
+ best_fitness = -result.best_fitness
117
+
118
+ fitness = result.fitness
119
+ if fitness is not None:
120
+ fitness = -fitness
121
+
122
+ # Negate history values
123
+ history = result.history.copy()
124
+ if "best_fitness" in history:
125
+ history["best_fitness"] = [-f for f in history["best_fitness"]]
126
+ if "mean_fitness" in history:
127
+ history["mean_fitness"] = [-f for f in history["mean_fitness"]]
128
+
129
+ return Result(
130
+ best_solution=result.best_solution,
131
+ best_fitness=best_fitness,
132
+ population=result.population,
133
+ fitness=fitness,
134
+ n_evals=result.n_evals,
135
+ n_gen=result.n_gen,
136
+ success=result.success,
137
+ termination_reason=result.termination_reason,
138
+ history=history,
139
+ hyperparams=result.hyperparams,
140
+ algorithm_state=result.algorithm_state,
141
+ problem_name=result.problem_name,
142
+ algorithm_name=result.algorithm_name,
143
+ start_time=result.start_time,
144
+ end_time=result.end_time,
145
+ elapsed_time=result.elapsed_time,
146
+ device=result.device,
147
+ extra=result.extra,
148
+ )
149
+
150
+
151
+ def maximize(
152
+ problem: Problem,
153
+ algorithm: Algorithm,
154
+ termination: Optional[Termination] = None,
155
+ seed: Optional[int] = None,
156
+ verbose: bool = True,
157
+ callback: Optional[Union[Callback, List[Callback]]] = None,
158
+ copy_algorithm: bool = False,
159
+ save_history: bool = True,
160
+ initialize: bool = True,
161
+ # Differentiable mode options
162
+ optimizer: Optional[torch.optim.Optimizer] = None,
163
+ lr_pop: Optional[float] = None,
164
+ lr_hyper: Optional[float] = None,
165
+ grad_clip_pop: Optional[float] = None,
166
+ grad_clip_hyper: Optional[float] = None,
167
+ scheduler: Optional[str] = None,
168
+ scheduler_patience: int = 50,
169
+ scheduler_factor: float = 0.5,
170
+ min_lr: float = 1e-6,
171
+ ) -> Result:
172
+ """
173
+ Maximise an objective function using a population-based algorithm.
174
+
175
+ This function converts maximisation to minimisation by negating
176
+ the objective, runs the optimisation, and then negates the results
177
+ back to return actual fitness values.
178
+
179
+ Args:
180
+ problem: Problem instance defining the objective function,
181
+ bounds, and constraints. The objective will be MAXIMISED.
182
+ algorithm: Algorithm instance (e.g., GA, DE, PSO, CMAES).
183
+ Will be initialized inside this function.
184
+ termination: When to stop optimisation. Must be a Termination
185
+ instance (e.g., MaxEvaluations(10000)). If None, uses
186
+ default (10000 evaluations).
187
+ seed: Random seed for reproducibility.
188
+ verbose: If True, print progress during optimisation.
189
+ callback: Single Callback or list of Callbacks for monitoring.
190
+ copy_algorithm: If True, create a copy of the algorithm.
191
+ save_history: If True (default), save convergence history.
192
+ initialize: If True (default), initialize the algorithm with the
193
+ problem. Set to False to continue optimization with an already
194
+ initialized algorithm (e.g., when switching problems at runtime
195
+ while preserving population state and hyperparameters).
196
+ The algorithm must have been previously initialized. When False,
197
+ the termination budget is additive (e.g., MaxEvaluations(500)
198
+ will run 500 more evaluations from the current state).
199
+
200
+ # Differentiable mode options:
201
+ optimizer: PyTorch optimizer for gradient-based updates.
202
+ lr_pop: Learning rate for population updates.
203
+ lr_hyper: Learning rate for hyperparameter updates.
204
+ grad_clip_pop: Maximum gradient norm for population clipping.
205
+ grad_clip_hyper: Maximum gradient norm for hyperparameter clipping.
206
+ scheduler: Learning rate scheduler type.
207
+ scheduler_patience: Generations before reducing LR.
208
+ scheduler_factor: Factor to multiply LR when reducing.
209
+ min_lr: Minimum learning rate.
210
+
211
+ Returns:
212
+ Result object with:
213
+ - best_solution: Best solution found
214
+ - best_fitness: Best (maximum) fitness value
215
+ - population: Final population
216
+ - fitness: Final fitness values (actual, not negated)
217
+ - history: Convergence history (with actual fitness values)
218
+
219
+ Example:
220
+ >>> from evograd.core.problem import Problem
221
+ >>> from evograd.core.maximize import maximize
222
+ >>> from evograd.core.termination import MaxEvaluations
223
+ >>> from evograd.algorithms import GA
224
+ >>>
225
+ >>> # Maximize a function
226
+ >>> problem = Problem(
227
+ ... objective=lambda x: torch.sin(x).sum(dim=-1),
228
+ ... n_var=10,
229
+ ... xl=-3.14,
230
+ ... xu=3.14,
231
+ ... )
232
+ >>>
233
+ >>> result = maximize(problem, GA(pop_size=100), termination=MaxEvaluations(10000), seed=42)
234
+ >>> print(f"Maximum value: {result.best_fitness}")
235
+
236
+ Note:
237
+ The returned fitness values are the ACTUAL values (not negated).
238
+ If you're looking for minimum fitness, use minimize() instead.
239
+ """
240
+ # Wrap problem to negate objective
241
+ negated_problem = _NegatedProblem(problem)
242
+
243
+ # Run minimisation on negated problem
244
+ result = minimize(
245
+ problem=negated_problem,
246
+ algorithm=algorithm,
247
+ termination=termination,
248
+ seed=seed,
249
+ verbose=verbose,
250
+ callback=callback,
251
+ copy_algorithm=copy_algorithm,
252
+ save_history=save_history,
253
+ initialize=initialize,
254
+ optimizer=optimizer,
255
+ lr_pop=lr_pop,
256
+ lr_hyper=lr_hyper,
257
+ grad_clip_pop=grad_clip_pop,
258
+ grad_clip_hyper=grad_clip_hyper,
259
+ scheduler=scheduler,
260
+ scheduler_patience=scheduler_patience,
261
+ scheduler_factor=scheduler_factor,
262
+ min_lr=min_lr,
263
+ )
264
+
265
+ # Fix problem name in result
266
+ result.problem_name = problem.name
267
+
268
+ # Negate fitness values back to actual values
269
+ return _NegatedResult.from_minimization_result(result)