dfbench 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 (122) hide show
  1. dfbench/__init__.py +48 -0
  2. dfbench/algorithms/__init__.py +219 -0
  3. dfbench/algorithms/derivative_free/__init__.py +23 -0
  4. dfbench/algorithms/derivative_free/_dfo_common.py +150 -0
  5. dfbench/algorithms/derivative_free/_scipy_common.py +124 -0
  6. dfbench/algorithms/derivative_free/nelder_mead.py +97 -0
  7. dfbench/algorithms/derivative_free/omads_mads.py +385 -0
  8. dfbench/algorithms/derivative_free/pdfo/__init__.py +11 -0
  9. dfbench/algorithms/derivative_free/pdfo/lincoa.py +139 -0
  10. dfbench/algorithms/derivative_free/pdfo/newuoa.py +127 -0
  11. dfbench/algorithms/derivative_free/pdfo/uobyqa.py +122 -0
  12. dfbench/algorithms/derivative_free/powell.py +94 -0
  13. dfbench/algorithms/derivative_free/pybobyqa.py +170 -0
  14. dfbench/algorithms/evolutionary/__init__.py +35 -0
  15. dfbench/algorithms/evolutionary/cmaes_sep_cma.py +192 -0
  16. dfbench/algorithms/evolutionary/evosax_es.py +336 -0
  17. dfbench/algorithms/evolutionary/evox_es.py +307 -0
  18. dfbench/algorithms/evolutionary/evox_pso.py +198 -0
  19. dfbench/algorithms/evolutionary/jax_es.py +361 -0
  20. dfbench/algorithms/evolutionary/nevergrad/__init__.py +11 -0
  21. dfbench/algorithms/evolutionary/nevergrad/_common.py +66 -0
  22. dfbench/algorithms/evolutionary/nevergrad/ngopt.py +126 -0
  23. dfbench/algorithms/evolutionary/nevergrad/oneplusone.py +130 -0
  24. dfbench/algorithms/evolutionary/nevergrad/tbpsa.py +143 -0
  25. dfbench/algorithms/evolutionary/pycma_cmaes.py +599 -0
  26. dfbench/algorithms/evolutionary/random_search.py +92 -0
  27. dfbench/algorithms/generative/vae_sampling.py +488 -0
  28. dfbench/algorithms/global_search/__init__.py +12 -0
  29. dfbench/algorithms/global_search/basin_hopping.py +142 -0
  30. dfbench/algorithms/global_search/dual_annealing.py +133 -0
  31. dfbench/algorithms/gradient_based/__init__.py +151 -0
  32. dfbench/algorithms/gradient_based/adam_gd.py +87 -0
  33. dfbench/algorithms/gradient_based/custom_jax.py +867 -0
  34. dfbench/algorithms/gradient_based/lbfgs_gd.py +157 -0
  35. dfbench/algorithms/gradient_based/na_adam_gd.py +266 -0
  36. dfbench/algorithms/gradient_based/optax/__init__.py +68 -0
  37. dfbench/algorithms/gradient_based/optax/_common.py +198 -0
  38. dfbench/algorithms/gradient_based/optax/adabelief.py +32 -0
  39. dfbench/algorithms/gradient_based/optax/adadelta.py +31 -0
  40. dfbench/algorithms/gradient_based/optax/adafactor.py +31 -0
  41. dfbench/algorithms/gradient_based/optax/adagrad.py +30 -0
  42. dfbench/algorithms/gradient_based/optax/adam.py +28 -0
  43. dfbench/algorithms/gradient_based/optax/adamax.py +32 -0
  44. dfbench/algorithms/gradient_based/optax/adamaxw.py +33 -0
  45. dfbench/algorithms/gradient_based/optax/adamw.py +33 -0
  46. dfbench/algorithms/gradient_based/optax/adan.py +35 -0
  47. dfbench/algorithms/gradient_based/optax/amsgrad.py +32 -0
  48. dfbench/algorithms/gradient_based/optax/lamb.py +33 -0
  49. dfbench/algorithms/gradient_based/optax/lion.py +33 -0
  50. dfbench/algorithms/gradient_based/optax/lookahead.py +129 -0
  51. dfbench/algorithms/gradient_based/optax/nadam.py +32 -0
  52. dfbench/algorithms/gradient_based/optax/nadamw.py +33 -0
  53. dfbench/algorithms/gradient_based/optax/noisy_sgd.py +33 -0
  54. dfbench/algorithms/gradient_based/optax/novograd.py +35 -0
  55. dfbench/algorithms/gradient_based/optax/oadam.py +33 -0
  56. dfbench/algorithms/gradient_based/optax/ogd.py +28 -0
  57. dfbench/algorithms/gradient_based/optax/polyak_sgd.py +111 -0
  58. dfbench/algorithms/gradient_based/optax/radam.py +33 -0
  59. dfbench/algorithms/gradient_based/optax/rmsprop.py +33 -0
  60. dfbench/algorithms/gradient_based/optax/rprop.py +35 -0
  61. dfbench/algorithms/gradient_based/optax/sam.py +147 -0
  62. dfbench/algorithms/gradient_based/optax/schedule_free_adam.py +45 -0
  63. dfbench/algorithms/gradient_based/optax/sgd.py +73 -0
  64. dfbench/algorithms/gradient_based/optax/sign.py +56 -0
  65. dfbench/algorithms/gradient_based/optax/sm3.py +28 -0
  66. dfbench/algorithms/gradient_based/optax/sophia.py +140 -0
  67. dfbench/algorithms/gradient_based/optax/yogi.py +34 -0
  68. dfbench/algorithms/gradient_based/optax_lbfgs.py +121 -0
  69. dfbench/algorithms/gradient_based/sa_gd.py +241 -0
  70. dfbench/algorithms/gradient_based/scipy/__init__.py +31 -0
  71. dfbench/algorithms/gradient_based/scipy/_common.py +398 -0
  72. dfbench/algorithms/gradient_based/scipy/bfgs.py +54 -0
  73. dfbench/algorithms/gradient_based/scipy/cobyla.py +65 -0
  74. dfbench/algorithms/gradient_based/scipy/cobyqa.py +65 -0
  75. dfbench/algorithms/gradient_based/scipy/dogleg.py +70 -0
  76. dfbench/algorithms/gradient_based/scipy/lbfgsb.py +62 -0
  77. dfbench/algorithms/gradient_based/scipy/newton_cg.py +58 -0
  78. dfbench/algorithms/gradient_based/scipy/nonlinear_cg.py +52 -0
  79. dfbench/algorithms/gradient_based/scipy/slsqp.py +52 -0
  80. dfbench/algorithms/gradient_based/scipy/sr1.py +82 -0
  81. dfbench/algorithms/gradient_based/scipy/tnc.py +62 -0
  82. dfbench/algorithms/gradient_based/scipy/trust_constr.py +75 -0
  83. dfbench/algorithms/gradient_based/scipy/trust_krylov.py +68 -0
  84. dfbench/algorithms/gradient_based/scipy/trust_ncg.py +71 -0
  85. dfbench/algorithms/surrogate_based/__init__.py +46 -0
  86. dfbench/algorithms/surrogate_based/ax_baxus.py +237 -0
  87. dfbench/algorithms/surrogate_based/ax_saasbo.py +162 -0
  88. dfbench/algorithms/surrogate_based/botorch/__init__.py +24 -0
  89. dfbench/algorithms/surrogate_based/botorch/_botorch_common.py +179 -0
  90. dfbench/algorithms/surrogate_based/botorch/botorch_bo.py +295 -0
  91. dfbench/algorithms/surrogate_based/botorch/botorch_gebo.py +225 -0
  92. dfbench/algorithms/surrogate_based/botorch/botorch_linebo.py +217 -0
  93. dfbench/algorithms/surrogate_based/botorch/botorch_qkg.py +160 -0
  94. dfbench/algorithms/surrogate_based/botorch/botorch_qnei.py +167 -0
  95. dfbench/algorithms/surrogate_based/botorch/botorch_rembo.py +181 -0
  96. dfbench/algorithms/surrogate_based/botorch/botorch_turbo.py +489 -0
  97. dfbench/algorithms/surrogate_based/hebo_bo.py +118 -0
  98. dfbench/algorithms/surrogate_based/restir.py +371 -0
  99. dfbench/algorithms/surrogate_based/smac_bo.py +131 -0
  100. dfbench/algorithms/surrogate_based/turbo_lbfgs.py +276 -0
  101. dfbench/benchmark/__init__.py +8 -0
  102. dfbench/benchmark/benchmark.py +923 -0
  103. dfbench/benchmark/metrics.py +369 -0
  104. dfbench/core/_init_env.py +16 -0
  105. dfbench/core/algorithm.py +205 -0
  106. dfbench/core/config.py +51 -0
  107. dfbench/core/display.py +526 -0
  108. dfbench/core/objective.py +2110 -0
  109. dfbench/core/problem.py +66 -0
  110. dfbench/core/utils.py +44 -0
  111. dfbench/problems/__init__.py +34 -0
  112. dfbench/problems/base_problem.py +334 -0
  113. dfbench/problems/uifo/__init__.py +15 -0
  114. dfbench/problems/uifo/uifo_problem.py +589 -0
  115. dfbench/problems/voyager/__init__.py +13 -0
  116. dfbench/problems/voyager/constrained_voyager_problem.py +258 -0
  117. dfbench/problems/voyager/voyager_problem.py +197 -0
  118. dfbench/problems/voyager/voyager_tuning_problem.py +183 -0
  119. dfbench-0.1.0.dist-info/METADATA +554 -0
  120. dfbench-0.1.0.dist-info/RECORD +122 -0
  121. dfbench-0.1.0.dist-info/WHEEL +4 -0
  122. dfbench-0.1.0.dist-info/licenses/LICENSE +21 -0
dfbench/__init__.py ADDED
@@ -0,0 +1,48 @@
1
+ """Differometor Benchmark package.
2
+
3
+ Provides optimization algorithms, problem definitions, and benchmarking tools.
4
+
5
+ Usage:
6
+ ### Core classes
7
+ `from dfbench import Objective`
8
+
9
+ ### Protocols
10
+ `from dfbench import ContinuousProblem, OptimizationAlgorithm, AlgorithmType`
11
+
12
+ ### Algorithms (hierarchical)
13
+ `from dfbench.algorithms import AdamGD, EvoxES, BotorchBO`
14
+
15
+ ### Problems (hierarchical)
16
+ `from dfbench.problems import VoyagerProblem, UIFOProblem`
17
+
18
+ ### Benchmarking (hierarchical)
19
+ `from dfbench.benchmark import Benchmark, AlgorithmConfig`
20
+ """
21
+
22
+ # Initialize environment variables first
23
+ import dfbench.core._init_env # noqa: F401
24
+
25
+ # Import protocols
26
+ from dfbench.core.problem import ContinuousProblem
27
+ from dfbench.core.algorithm import OptimizationAlgorithm, AlgorithmType
28
+
29
+ # Import core utilities
30
+ from dfbench.core.config import create_parser
31
+ from dfbench.core.utils import t2j, j2t
32
+
33
+ # Import Objective for external use
34
+ from dfbench.core.objective import Objective
35
+
36
+
37
+ __all__ = [
38
+ # Core
39
+ "Objective",
40
+ # Protocols
41
+ "ContinuousProblem",
42
+ "OptimizationAlgorithm",
43
+ "AlgorithmType",
44
+ # Utilities
45
+ "create_parser",
46
+ "t2j",
47
+ "j2t",
48
+ ]
@@ -0,0 +1,219 @@
1
+ """Optimization algorithms."""
2
+
3
+ from dfbench.algorithms.derivative_free.nelder_mead import NelderMead
4
+ from dfbench.algorithms.derivative_free.omads_mads import OmadsMADS, OmadsOrthoMADS
5
+ from dfbench.algorithms.derivative_free.powell import Powell
6
+ from dfbench.algorithms.global_search.basin_hopping import BasinHopping
7
+ from dfbench.algorithms.global_search.dual_annealing import DualAnnealing
8
+ from dfbench.algorithms.evolutionary.evox_es import EvoxES
9
+ from dfbench.algorithms.evolutionary.evox_pso import EvoxPSO
10
+ from dfbench.algorithms.evolutionary.nevergrad.ngopt import NevergradNGOpt
11
+ from dfbench.algorithms.evolutionary.nevergrad.oneplusone import NevergradOnePlusOne
12
+ from dfbench.algorithms.evolutionary.nevergrad.tbpsa import NevergradTBPSA
13
+ from dfbench.algorithms.evolutionary.random_search import RandomSearch
14
+ from dfbench.algorithms.evolutionary.pycma_cmaes import (
15
+ PyCMACMAES,
16
+ PyCMAActiveCMAES,
17
+ PyCMAIPOP,
18
+ PyCMABIPOP,
19
+ )
20
+ from dfbench.algorithms.evolutionary.cmaes_sep_cma import CMAESSepCMA
21
+ from dfbench.algorithms.evolutionary.evosax_es import EvosaxMAES, EvosaxLMMAES
22
+ from dfbench.algorithms.evolutionary.jax_es import JAXOnePlusOneES, JAXMuLambdaES
23
+ from dfbench.algorithms.gradient_based.adam_gd import AdamGD
24
+ from dfbench.algorithms.gradient_based.custom_jax import (
25
+ ARCJAX,
26
+ ASAMJAX,
27
+ AdamToLBFGSJAX,
28
+ EntropySGDJAX,
29
+ GDRestartsJAX,
30
+ GaussianSmoothingGDJAX,
31
+ NoisyAdamJAX,
32
+ OAdamJAX,
33
+ OGDJAX,
34
+ PerturbedGDJAX,
35
+ SGHMCJAX,
36
+ SGLDJAX,
37
+ )
38
+ from dfbench.algorithms.gradient_based.scipy.bfgs import BFGS
39
+ from dfbench.algorithms.gradient_based.scipy.cobyla import COBYLA
40
+ from dfbench.algorithms.gradient_based.scipy.cobyqa import COBYQA
41
+ from dfbench.algorithms.gradient_based.scipy.dogleg import Dogleg
42
+ from dfbench.algorithms.gradient_based.lbfgs_gd import LBFGSGD
43
+ from dfbench.algorithms.gradient_based.scipy.lbfgsb import LBFGSB
44
+ from dfbench.algorithms.gradient_based.na_adam_gd import NAAdamGD
45
+ from dfbench.algorithms.gradient_based.scipy.newton_cg import NewtonCG
46
+ from dfbench.algorithms.gradient_based.scipy.nonlinear_cg import NonlinearCG
47
+ from dfbench.algorithms.gradient_based.sa_gd import SAGD
48
+ from dfbench.algorithms.gradient_based.scipy.slsqp import SLSQP
49
+ from dfbench.algorithms.gradient_based.scipy.sr1 import SR1
50
+ from dfbench.algorithms.gradient_based.scipy.tnc import TNC
51
+ from dfbench.algorithms.gradient_based.scipy.trust_constr import TrustConstr
52
+ from dfbench.algorithms.gradient_based.scipy.trust_krylov import TrustKrylov
53
+ from dfbench.algorithms.gradient_based.scipy.trust_ncg import TrustNCG
54
+ from dfbench.algorithms.surrogate_based.botorch.botorch_bo import BotorchBO
55
+ from dfbench.algorithms.surrogate_based.botorch.botorch_turbo import BotorchTuRBO
56
+ from dfbench.algorithms.surrogate_based.restir import ReSTIR
57
+ from dfbench.algorithms.surrogate_based.ax_baxus import BAxUS
58
+ from dfbench.algorithms.surrogate_based.botorch.botorch_qnei import BotorchqNEI
59
+ from dfbench.algorithms.surrogate_based.botorch.botorch_qkg import BotorchqKG
60
+ from dfbench.algorithms.surrogate_based.botorch.botorch_rembo import REMBO
61
+ from dfbench.algorithms.surrogate_based.botorch.botorch_gebo import GEBO
62
+ from dfbench.algorithms.surrogate_based.botorch.botorch_linebo import LineBO
63
+ from dfbench.algorithms.surrogate_based.hebo_bo import HEBO
64
+ from dfbench.algorithms.surrogate_based.turbo_lbfgs import TuRBOLBFGS
65
+ from dfbench.algorithms.generative.vae_sampling import VAESampling
66
+ from dfbench.algorithms.derivative_free.pdfo.uobyqa import PDFOUOBYQA
67
+ from dfbench.algorithms.derivative_free.pdfo.newuoa import PDFONEWUOA
68
+ from dfbench.algorithms.derivative_free.pdfo.lincoa import PDFOLINCOA
69
+ from dfbench.algorithms.derivative_free.pybobyqa import PyBOBYQA
70
+
71
+ # Optax
72
+ from dfbench.algorithms.gradient_based.optax.adam import OptaxAdam
73
+ from dfbench.algorithms.gradient_based.optax.adamw import OptaxAdamW
74
+ from dfbench.algorithms.gradient_based.optax.adabelief import OptaxAdaBelief
75
+ from dfbench.algorithms.gradient_based.optax.adafactor import OptaxAdafactor
76
+ from dfbench.algorithms.gradient_based.optax.amsgrad import OptaxAMSGrad
77
+ from dfbench.algorithms.gradient_based.optax.adagrad import OptaxAdaGrad
78
+ from dfbench.algorithms.gradient_based.optax.adadelta import OptaxAdaDelta
79
+ from dfbench.algorithms.gradient_based.optax.adamax import OptaxAdaMax
80
+ from dfbench.algorithms.gradient_based.optax.adamaxw import OptaxAdaMaxW
81
+ from dfbench.algorithms.gradient_based.optax.adan import OptaxAdan
82
+ from dfbench.algorithms.gradient_based.optax.lion import OptaxLion
83
+ from dfbench.algorithms.gradient_based.optax.lamb import OptaxLAMB
84
+ from dfbench.algorithms.gradient_based.optax.nadam import OptaxNadam
85
+ from dfbench.algorithms.gradient_based.optax.nadamw import OptaxNadamW
86
+ from dfbench.algorithms.gradient_based.optax.rmsprop import OptaxRMSProp
87
+ from dfbench.algorithms.gradient_based.optax.rprop import OptaxRProp
88
+ from dfbench.algorithms.gradient_based.optax.radam import OptaxRAdam
89
+ from dfbench.algorithms.gradient_based.optax.sgd import OptaxSGD, OptaxSGDM, OptaxNAG
90
+ from dfbench.algorithms.gradient_based.optax.noisy_sgd import OptaxNoisySGD
91
+ from dfbench.algorithms.gradient_based.optax.polyak_sgd import OptaxPolyakSGD
92
+ from dfbench.algorithms.gradient_based.optax.sam import OptaxSAM
93
+ from dfbench.algorithms.gradient_based.optax.sophia import OptaxSophia
94
+ from dfbench.algorithms.gradient_based.optax.lookahead import OptaxLookahead
95
+ from dfbench.algorithms.gradient_based.optax.schedule_free_adam import OptaxScheduleFreeAdam
96
+ from dfbench.algorithms.gradient_based.optax.yogi import OptaxYogi
97
+ from dfbench.algorithms.gradient_based.optax.novograd import OptaxNovoGrad
98
+ from dfbench.algorithms.gradient_based.optax.ogd import OptaxOGD
99
+ from dfbench.algorithms.gradient_based.optax.oadam import OptaxOAdam
100
+ from dfbench.algorithms.gradient_based.optax.sign import OptaxSignSGD, OptaxSignum
101
+ from dfbench.algorithms.gradient_based.optax.sm3 import OptaxSM3
102
+ from dfbench.algorithms.gradient_based.optax_lbfgs import OptaxLBFGS
103
+
104
+ # External-package algorithms: imported only when their dependencies exist.
105
+ try:
106
+ from dfbench.algorithms.surrogate_based.ax_saasbo import AxSAASBO
107
+ except ImportError:
108
+ pass
109
+
110
+ try:
111
+ from dfbench.algorithms.surrogate_based.smac_bo import SMAC
112
+ except ImportError:
113
+ pass
114
+
115
+ __all__ = [
116
+ "BasinHopping",
117
+ "DualAnnealing",
118
+ "NelderMead",
119
+ "OmadsMADS",
120
+ "OmadsOrthoMADS",
121
+ "Powell",
122
+ "EvoxES",
123
+ "EvoxPSO",
124
+ "NevergradNGOpt",
125
+ "NevergradOnePlusOne",
126
+ "NevergradTBPSA",
127
+ "RandomSearch",
128
+ "PyCMACMAES",
129
+ "PyCMAActiveCMAES",
130
+ "PyCMAIPOP",
131
+ "PyCMABIPOP",
132
+ "CMAESSepCMA",
133
+ "EvosaxMAES",
134
+ "EvosaxLMMAES",
135
+ "JAXOnePlusOneES",
136
+ "JAXMuLambdaES",
137
+ "AdamGD",
138
+ "SGLDJAX",
139
+ "ASAMJAX",
140
+ "AdamToLBFGSJAX",
141
+ "EntropySGDJAX",
142
+ "SGHMCJAX",
143
+ "ARCJAX",
144
+ "OGDJAX",
145
+ "OAdamJAX",
146
+ "PerturbedGDJAX",
147
+ "NoisyAdamJAX",
148
+ "GDRestartsJAX",
149
+ "GaussianSmoothingGDJAX",
150
+ "BFGS",
151
+ "COBYLA",
152
+ "COBYQA",
153
+ "Dogleg",
154
+ "LBFGSGD",
155
+ "LBFGSB",
156
+ "NAAdamGD",
157
+ "NewtonCG",
158
+ "NonlinearCG",
159
+ "SAGD",
160
+ "SLSQP",
161
+ "SR1",
162
+ "TNC",
163
+ "TrustConstr",
164
+ "TrustKrylov",
165
+ "TrustNCG",
166
+ "BotorchBO",
167
+ "BotorchTuRBO",
168
+ "ReSTIR",
169
+ "AxSAASBO",
170
+ "BAxUS",
171
+ "BotorchqNEI",
172
+ "BotorchqKG",
173
+ "REMBO",
174
+ "GEBO",
175
+ "LineBO",
176
+ "HEBO",
177
+ "SMAC",
178
+ "TuRBOLBFGS",
179
+ "VAESampling",
180
+ "PDFOUOBYQA",
181
+ "PDFONEWUOA",
182
+ "PDFOLINCOA",
183
+ "PyBOBYQA",
184
+ # Optax batch
185
+ "OptaxAdam",
186
+ "OptaxAdamW",
187
+ "OptaxAdaBelief",
188
+ "OptaxAdafactor",
189
+ "OptaxAMSGrad",
190
+ "OptaxAdaGrad",
191
+ "OptaxAdaDelta",
192
+ "OptaxAdaMax",
193
+ "OptaxAdaMaxW",
194
+ "OptaxAdan",
195
+ "OptaxLion",
196
+ "OptaxLAMB",
197
+ "OptaxNadam",
198
+ "OptaxNadamW",
199
+ "OptaxRMSProp",
200
+ "OptaxRProp",
201
+ "OptaxRAdam",
202
+ "OptaxSGD",
203
+ "OptaxSGDM",
204
+ "OptaxNAG",
205
+ "OptaxNoisySGD",
206
+ "OptaxPolyakSGD",
207
+ "OptaxSAM",
208
+ "OptaxSophia",
209
+ "OptaxLookahead",
210
+ "OptaxScheduleFreeAdam",
211
+ "OptaxYogi",
212
+ "OptaxNovoGrad",
213
+ "OptaxOGD",
214
+ "OptaxOAdam",
215
+ "OptaxSignSGD",
216
+ "OptaxSignum",
217
+ "OptaxSM3",
218
+ "OptaxLBFGS",
219
+ ]
@@ -0,0 +1,23 @@
1
+ """Derivative-free optimization algorithms.
2
+
3
+ Includes mesh-based direct search (OMADS), Powell-style trust-region methods
4
+ (PDFO: UOBYQA, NEWUOA, LINCOA; plus Py-BOBYQA), and SciPy classics
5
+ (Nelder-Mead, Powell).
6
+ """
7
+
8
+ from dfbench.algorithms.derivative_free.nelder_mead import NelderMead
9
+ from dfbench.algorithms.derivative_free.omads_mads import OmadsMADS, OmadsOrthoMADS
10
+ from dfbench.algorithms.derivative_free.pdfo import PDFOLINCOA, PDFONEWUOA, PDFOUOBYQA
11
+ from dfbench.algorithms.derivative_free.powell import Powell
12
+ from dfbench.algorithms.derivative_free.pybobyqa import PyBOBYQA
13
+
14
+ __all__ = [
15
+ "NelderMead",
16
+ "OmadsMADS",
17
+ "OmadsOrthoMADS",
18
+ "PDFOLINCOA",
19
+ "PDFONEWUOA",
20
+ "PDFOUOBYQA",
21
+ "Powell",
22
+ "PyBOBYQA",
23
+ ]
@@ -0,0 +1,150 @@
1
+ """Common derivative-free optimization wrapper utilities.
2
+
3
+ Provides shared infrastructure for wrapping DFO solvers (PDFO, Py-BOBYQA, etc.)
4
+ so they integrate cleanly with the Objective / OptimizationAlgorithm interface.
5
+
6
+ Key responsibilities:
7
+ - Convert between JAX arrays and NumPy arrays expected by solvers.
8
+ - Evaluate ``obj.value(...)`` in bounded space and log results.
9
+ - Enforce Objective budgets via a callback / early-termination mechanism.
10
+ - Support multistart restarts with fresh random starting points.
11
+ - Handle NaN / Inf returns and solver failure codes gracefully.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import numpy as np
17
+ import jax.numpy as jnp
18
+ from jax import random
19
+ from jaxtyping import Array, Float
20
+
21
+ from dfbench.core.objective import Objective
22
+
23
+
24
+ # Large but finite penalty returned when the solver evaluates an infeasible
25
+ # or NaN-producing point. Using ``np.inf`` would crash most DFO solvers.
26
+ _NAN_PENALTY = 1e30
27
+
28
+
29
+ class _BudgetExhausted(Exception):
30
+ """Raised inside a DFO callback when the Objective budget is exhausted.
31
+
32
+ For pure-Python solvers (e.g. Py-BOBYQA) this propagates immediately and
33
+ terminates the optimisation loop. For Fortran-backed solvers (PDFO/prima)
34
+ f2py stores the exception and re-raises it after the current Fortran call
35
+ returns, which also causes prima to abort early.
36
+ """
37
+
38
+
39
+ def dfo_objective_wrapper(
40
+ obj: Objective,
41
+ ) -> callable:
42
+ """Return a NumPy-compatible scalar function that evaluates ``obj.value``.
43
+
44
+ The returned callable accepts a 1-D ``np.ndarray``, converts it to a JAX
45
+ array, evaluates through ``obj.value`` (which logs the evaluation), and
46
+ returns a Python float.
47
+
48
+ NaN / Inf losses are replaced with ``_NAN_PENALTY`` so that DFO solvers
49
+ do not crash. When the Objective budget is exceeded the wrapper raises
50
+ ``_BudgetExhausted`` instead of calling the (expensive) physics evaluation.
51
+
52
+ Returns:
53
+ ``fun(x: np.ndarray) -> float``
54
+ """
55
+
56
+ def _fun(x: np.ndarray) -> float:
57
+ if obj.budget_exceeded:
58
+ raise _BudgetExhausted
59
+ params = jnp.asarray(x)
60
+ loss = obj.value(params)
61
+ loss_f = float(loss)
62
+ if not np.isfinite(loss_f):
63
+ return _NAN_PENALTY
64
+ return loss_f
65
+
66
+ return _fun
67
+
68
+
69
+ def random_bounded_start(
70
+ obj: Objective,
71
+ key,
72
+ ) -> tuple[np.ndarray, "jax.Array"]:
73
+ """Sample a uniformly random starting point within problem bounds.
74
+
75
+ Args:
76
+ obj: Objective (must be in bounded mode).
77
+ key: JAX PRNG key; a new sub-key is split off and the updated key
78
+ is returned alongside the sample.
79
+
80
+ Returns:
81
+ (x0, new_key) – x0 is a 1-D ``np.ndarray`` of shape ``(n_params,)``.
82
+ """
83
+ key, subkey = random.split(key)
84
+ lower, upper = obj.problem.bounds
85
+ x0_jax = random.uniform(subkey, shape=(obj.n_params,), minval=lower, maxval=upper)
86
+ return np.asarray(x0_jax, dtype=np.float64), key
87
+
88
+
89
+ def clip_to_bounds(x: np.ndarray, obj: Objective) -> np.ndarray:
90
+ """Clip a parameter vector to the problem bounds (in-place safe).
91
+
92
+ Returns:
93
+ Clipped copy of *x*.
94
+ """
95
+ lower = np.asarray(obj.problem.bounds[0], dtype=np.float64)
96
+ upper = np.asarray(obj.problem.bounds[1], dtype=np.float64)
97
+ return np.clip(x, lower, upper)
98
+
99
+
100
+ def solver_bounds_np(obj: Objective) -> tuple[np.ndarray, np.ndarray]:
101
+ """Return ``(lower, upper)`` as plain NumPy float64 arrays."""
102
+ lower = np.asarray(obj.problem.bounds[0], dtype=np.float64)
103
+ upper = np.asarray(obj.problem.bounds[1], dtype=np.float64)
104
+ return lower, upper
105
+
106
+
107
+ def multistart_loop(
108
+ obj: Objective,
109
+ key,
110
+ solve_fn: callable,
111
+ n_restarts: int = 1,
112
+ init_params: Float[Array, "..."] | None = None,
113
+ ) -> None:
114
+ """Run *solve_fn* up to *n_restarts* times with fresh random starts.
115
+
116
+ ``solve_fn(x0: np.ndarray)`` should call the DFO solver once from
117
+ starting point *x0*. It may raise or return; either way the best
118
+ result is tracked by the Objective automatically.
119
+
120
+ The first restart uses *init_params* (if provided); subsequent restarts
121
+ sample uniformly within bounds.
122
+
123
+ Args:
124
+ obj: The Objective instance (already logging).
125
+ key: JAX PRNG key for sampling restarts.
126
+ solve_fn: ``(x0: np.ndarray) -> None`` – runs one solver call.
127
+ n_restarts: Total number of solver invocations.
128
+ init_params: Optional starting point for the first restart.
129
+ """
130
+ for i in range(n_restarts):
131
+ if obj.budget_exceeded:
132
+ break
133
+
134
+ if i == 0 and init_params is not None:
135
+ x0 = np.asarray(init_params, dtype=np.float64)
136
+ else:
137
+ x0, key = random_bounded_start(obj, key)
138
+
139
+ try:
140
+ solve_fn(x0)
141
+ except _BudgetExhausted:
142
+ break
143
+ except Exception as exc: # noqa: BLE001
144
+ if obj.budget_exceeded:
145
+ # Exception was likely triggered (directly or indirectly) by
146
+ # _BudgetExhausted propagating through the solver; stop quietly.
147
+ break
148
+ # DFO solvers may raise on degenerate geometry, singular models,
149
+ # etc. Log and continue with next restart.
150
+ print(f"[DFO restart {i+1}/{n_restarts}] solver raised {type(exc).__name__}: {exc}")
@@ -0,0 +1,124 @@
1
+ """Shared utilities for SciPy-based derivative-free optimizers.
2
+
3
+ Provides numpy↔JAX wrappers, bounds conversion, and budget-aware
4
+ callbacks so individual algorithm modules stay DRY.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import numpy as np
10
+ import jax.numpy as jnp
11
+ from scipy.optimize import Bounds
12
+
13
+ from dfbench.core.objective import Objective
14
+
15
+
16
+ class SciPyBudgetExceeded(RuntimeError):
17
+ """Raised inside a SciPy objective/callback when the Objective budget is spent."""
18
+
19
+
20
+ # Large but finite penalty returned when the objective produces NaN/Inf.
21
+ # Using ``np.inf`` causes SciPy's derivative-free solvers (Nelder-Mead,
22
+ # Powell, basin-hopping, dual-annealing) to terminate prematurely or crash.
23
+ _NAN_PENALTY = 1e30
24
+
25
+
26
+ # ---------------------------------------------------------------------------
27
+ # Bounds helpers
28
+ # ---------------------------------------------------------------------------
29
+
30
+
31
+ def scipy_bounds(obj: Objective) -> Bounds:
32
+ """Convert problem bounds to :class:`scipy.optimize.Bounds`."""
33
+ lb = np.asarray(obj.problem.bounds[0], dtype=np.float64)
34
+ ub = np.asarray(obj.problem.bounds[1], dtype=np.float64)
35
+ return Bounds(lb, ub)
36
+
37
+
38
+ def scipy_bounds_list(obj: Objective) -> list[tuple[float, float]]:
39
+ """Return bounds as ``[(lb0, ub0), …]`` (format used by *dual_annealing*)."""
40
+ lb = np.asarray(obj.problem.bounds[0], dtype=np.float64)
41
+ ub = np.asarray(obj.problem.bounds[1], dtype=np.float64)
42
+ return list(zip(lb.tolist(), ub.tolist()))
43
+
44
+
45
+ # ---------------------------------------------------------------------------
46
+ # Objective wrappers
47
+ # ---------------------------------------------------------------------------
48
+
49
+
50
+ def make_scipy_fun(obj: Objective):
51
+ """Return a numpy-in / float-out wrapper around ``obj.value()``.
52
+
53
+ Raises :class:`SciPyBudgetExceeded` when the evaluation budget is spent so
54
+ the calling SciPy routine can be interrupted cleanly.
55
+ """
56
+
57
+ def fun(x_np: np.ndarray) -> float:
58
+ if obj.budget_exceeded:
59
+ raise SciPyBudgetExceeded
60
+ loss = float(obj.value(jnp.asarray(x_np)))
61
+ if not np.isfinite(loss):
62
+ return _NAN_PENALTY
63
+ return loss
64
+
65
+ return fun
66
+
67
+
68
+ def make_scipy_fun_and_grad(obj: Objective):
69
+ """Return a wrapper calling ``obj.value_and_grad()`` → ``(float, ndarray)``.
70
+
71
+ Intended for use with ``jac=True`` in :func:`scipy.optimize.minimize` so
72
+ that value and gradient are obtained in a single Objective call, avoiding
73
+ double-counted evaluations.
74
+ """
75
+
76
+ def fun(x_np: np.ndarray) -> tuple[float, np.ndarray]:
77
+ if obj.budget_exceeded:
78
+ raise SciPyBudgetExceeded
79
+ loss, grad = obj.value_and_grad(jnp.asarray(x_np))
80
+ loss_f = float(loss)
81
+ grad_np = np.asarray(grad, dtype=np.float64)
82
+ if not np.isfinite(loss_f) or not np.all(np.isfinite(grad_np)):
83
+ return _NAN_PENALTY, np.zeros_like(x_np, dtype=np.float64)
84
+ return loss_f, grad_np
85
+
86
+ return fun
87
+
88
+
89
+ # ---------------------------------------------------------------------------
90
+ # Callbacks
91
+ # ---------------------------------------------------------------------------
92
+
93
+
94
+ def make_budget_callback(obj: Objective):
95
+ """Return a ``callback(xk)`` that raises :class:`SciPyBudgetExceeded`."""
96
+
97
+ def callback(xk, *_args, **_kwargs):
98
+ if obj.budget_exceeded:
99
+ raise SciPyBudgetExceeded
100
+
101
+ return callback
102
+
103
+
104
+ # ---------------------------------------------------------------------------
105
+ # Bounded step-taker for basin-hopping
106
+ # ---------------------------------------------------------------------------
107
+
108
+
109
+ class BoundedStep:
110
+ """Random displacement clipped to problem bounds.
111
+
112
+ Drop-in replacement for SciPy's default ``RandomDisplacement`` that
113
+ guarantees the perturbed point remains feasible.
114
+ """
115
+
116
+ def __init__(self, stepsize: float, lb: np.ndarray, ub: np.ndarray) -> None:
117
+ self.stepsize = stepsize
118
+ self.lb = lb
119
+ self.ub = ub
120
+
121
+ def __call__(self, x: np.ndarray) -> np.ndarray:
122
+ scale = self.stepsize * (self.ub - self.lb)
123
+ x_new = x + np.random.uniform(-scale, scale, size=x.shape)
124
+ return np.clip(x_new, self.lb, self.ub)
@@ -0,0 +1,97 @@
1
+ """Nelder-Mead simplex algorithm via :func:`scipy.optimize.minimize`."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import numpy as np
6
+ import jax.numpy as jnp
7
+ from jaxtyping import Array, Float
8
+ from scipy.optimize import minimize
9
+
10
+ from dfbench.core.algorithm import AlgorithmType, OptimizationAlgorithm
11
+ from dfbench.core.objective import Objective
12
+ from dfbench.algorithms.derivative_free._scipy_common import (
13
+ SciPyBudgetExceeded,
14
+ make_scipy_fun,
15
+ scipy_bounds,
16
+ make_budget_callback,
17
+ )
18
+
19
+
20
+ class NelderMead(OptimizationAlgorithm):
21
+ """Nelder-Mead simplex derivative-free local optimizer.
22
+
23
+ Uses ``scipy.optimize.minimize(method='Nelder-Mead')`` in **bounded**
24
+ parameter space (requires SciPy >= 1.7).
25
+
26
+ Suitable as a local refinement method or for low-dimensional problems
27
+ where gradient information is unavailable or unreliable.
28
+
29
+ Attributes:
30
+ algorithm_str: ``"nelder_mead"``
31
+ algorithm_type: :attr:`AlgorithmType.EVOLUTIONARY`
32
+ """
33
+
34
+ algorithm_str: str = "nelder_mead"
35
+ algorithm_type: AlgorithmType = AlgorithmType.EVOLUTIONARY
36
+
37
+ def __init__(self) -> None:
38
+ pass
39
+
40
+ def optimize(
41
+ self,
42
+ problem_objective: Objective,
43
+ init_params: Float[Array, "..."] | None = None,
44
+ random_seed: int | None = None,
45
+ xatol: float = 1e-8,
46
+ fatol: float = 1e-8,
47
+ adaptive: bool = True,
48
+ ) -> None:
49
+ """Run Nelder-Mead simplex optimization.
50
+
51
+ Args:
52
+ problem_objective: Pre-configured Objective instance.
53
+ init_params: Starting point. If *None*, sampled uniformly in bounds.
54
+ random_seed: Seed for reproducibility (controls initial point).
55
+ xatol: Absolute parameter tolerance for convergence.
56
+ fatol: Absolute function-value tolerance for convergence.
57
+ adaptive: Adapt simplex parameters to dimensionality (recommended
58
+ for n > 1).
59
+ """
60
+ obj = problem_objective
61
+
62
+ random_seed, _key = self.prepare(
63
+ obj, unbounded=False, random_seed=random_seed
64
+ )
65
+
66
+ if init_params is None:
67
+ params = obj.random_params_bounded()
68
+ else:
69
+ params = init_params
70
+
71
+ # JIT warmup
72
+ _ = obj.value(params)
73
+
74
+ obj.start_logging()
75
+
76
+ fun = make_scipy_fun(obj)
77
+ x0 = np.asarray(params, dtype=np.float64)
78
+ bounds = scipy_bounds(obj)
79
+ callback = make_budget_callback(obj)
80
+ maxfev = obj.evals_left if obj.evals_left is not None else 10_000
81
+
82
+ try:
83
+ minimize(
84
+ fun,
85
+ x0,
86
+ method="Nelder-Mead",
87
+ bounds=bounds,
88
+ callback=callback,
89
+ options={
90
+ "maxfev": maxfev,
91
+ "xatol": xatol,
92
+ "fatol": fatol,
93
+ "adaptive": adaptive,
94
+ },
95
+ )
96
+ except SciPyBudgetExceeded:
97
+ pass