better-optimize 0.1__tar.gz → 0.1.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.
Files changed (19) hide show
  1. {better_optimize-0.1 → better_optimize-0.1.2}/PKG-INFO +1 -1
  2. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize/__init__.py +2 -1
  3. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize/_version.py +3 -3
  4. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize/basinhopping.py +49 -1
  5. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize/minimize.py +3 -0
  6. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize/root.py +3 -0
  7. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize/wrapper.py +7 -4
  8. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize.egg-info/PKG-INFO +1 -1
  9. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize.egg-info/requires.txt +1 -1
  10. {better_optimize-0.1 → better_optimize-0.1.2}/pyproject.toml +1 -1
  11. {better_optimize-0.1 → better_optimize-0.1.2}/LICENSE +0 -0
  12. {better_optimize-0.1 → better_optimize-0.1.2}/README.md +0 -0
  13. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize/constants.py +0 -0
  14. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize/utilities.py +0 -0
  15. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize.egg-info/SOURCES.txt +0 -0
  16. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize.egg-info/dependency_links.txt +0 -0
  17. {better_optimize-0.1 → better_optimize-0.1.2}/better_optimize.egg-info/top_level.txt +0 -0
  18. {better_optimize-0.1 → better_optimize-0.1.2}/setup.cfg +0 -0
  19. {better_optimize-0.1 → better_optimize-0.1.2}/setup.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better_optimize
3
- Version: 0.1
3
+ Version: 0.1.2
4
4
  Summary: A drop-in replacement for scipy optimize functions with quality of life improvements
5
5
  License: MIT License
6
6
  Project-URL: Homepage, https://github.com/jessegrabowski/better_optimize
@@ -1,6 +1,7 @@
1
1
  import logging
2
2
 
3
3
  from better_optimize._version import get_versions
4
+ from better_optimize.basinhopping import basinhopping
4
5
  from better_optimize.minimize import minimize
5
6
  from better_optimize.root import root
6
7
 
@@ -14,4 +15,4 @@ if not logging.root.handlers:
14
15
 
15
16
 
16
17
  __version__ = get_versions()["version"]
17
- __all__ = ["minimize", "root"]
18
+ __all__ = ["minimize", "root", "basinhopping"]
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-04-07T14:48:10+0200",
11
+ "date": "2025-05-14T08:34:39+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "21632a7508f32aa1de12b701842d56e3eca319e2",
15
- "version": "0.1"
14
+ "full-revisionid": "b9f19e0aad67eca088085f337a4627b537f71e3c",
15
+ "version": "0.1.2"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -17,6 +17,7 @@ from scipy.optimize._basinhopping import (
17
17
  Metropolis,
18
18
  MinimizerWrapper,
19
19
  RandomDisplacement,
20
+ Storage,
20
21
  check_random_state,
21
22
  )
22
23
 
@@ -57,6 +58,35 @@ def initialize_progress_bar(progressbar, use_jac=True, use_hess=False):
57
58
  )
58
59
 
59
60
 
61
+ class AllowFailureStorage(Storage):
62
+ """
63
+ Subclass of Storage that allows the minimizer to fail, but still updates the global minimum
64
+ if the new point is better than the current global minimum.
65
+ """
66
+
67
+ def update(self, minres):
68
+ if minres.fun < self.minres.fun:
69
+ self._add(minres)
70
+ return True
71
+ else:
72
+ return False
73
+
74
+
75
+ class AllowFailureBasinHoppingRunner(BasinHoppingRunner):
76
+ """
77
+ A subclass of BasinHoppingRunner that allows the minimizer to fail, but still updates the
78
+ global minimum if the new point is better than the current global minimum.
79
+ """
80
+
81
+ def __init__(
82
+ self, x0, minimizer, step_taking, accept_tests, accept_on_minimizer_fail=False, disp=False
83
+ ):
84
+ super().__init__(x0, minimizer, step_taking, accept_tests, disp=disp)
85
+
86
+ if accept_on_minimizer_fail:
87
+ self.storage = AllowFailureStorage(self.storage.minres)
88
+
89
+
60
90
  def basinhopping(
61
91
  func: Callable[..., float | np.ndarray],
62
92
  x0: Sequence[float],
@@ -66,6 +96,7 @@ def basinhopping(
66
96
  minimizer_kwargs: dict | None = None,
67
97
  take_step: Callable | None = None,
68
98
  accept_test: Callable | None = None,
99
+ accept_on_minimizer_fail: bool = False,
69
100
  callback: Callable | None = None,
70
101
  interval: int = 50,
71
102
  progressbar: bool = True,
@@ -97,6 +128,9 @@ def basinhopping(
97
128
  Custom step-taking routine
98
129
  accept_test: Callable, optional
99
130
  Custom accept/reject routine
131
+ accept_on_minimizer_fail: bool
132
+ Accept the new point even if the minimizer fails. Will also update the global minimum if the point is
133
+ better than the current global minimum, even if the minimizer fails.
100
134
  callback: Callable, optional
101
135
  User-supplied function to call after each iteration
102
136
  interval: int
@@ -214,7 +248,14 @@ def basinhopping(
214
248
  if niter_success is None:
215
249
  niter_success = niter + 2
216
250
 
217
- bh = BasinHoppingRunner(x0, wrapped_minimizer, take_step_wrapped, accept_tests, disp=verbose)
251
+ bh = AllowFailureBasinHoppingRunner(
252
+ x0,
253
+ wrapped_minimizer,
254
+ take_step_wrapped,
255
+ accept_tests,
256
+ accept_on_minimizer_fail=accept_on_minimizer_fail,
257
+ disp=verbose,
258
+ )
218
259
 
219
260
  # The wrapped minimizer is called once during construction of
220
261
  # BasinHoppingRunner, so run the callback
@@ -224,7 +265,11 @@ def basinhopping(
224
265
  # start main iteration loop
225
266
  count, i = 0, 0
226
267
  message = ["requested number of basinhopping iterations completed" " successfully"]
268
+
227
269
  grad_norm_at_min = 0.0
270
+ if use_jac:
271
+ _, grad_val = func(bh.x)
272
+ grad_norm_at_min = np.linalg.norm(grad_val)
228
273
 
229
274
  with progress:
230
275
  for i in range(niter):
@@ -282,3 +327,6 @@ def basinhopping(
282
327
  res.success = res.lowest_optimization_result.success
283
328
 
284
329
  return res
330
+
331
+
332
+ __all__ = ["basinhopping"]
@@ -113,3 +113,6 @@ def minimize(
113
113
 
114
114
  optimizer_result = optimizer_early_stopping_wrapper(f_optim)
115
115
  return optimizer_result
116
+
117
+
118
+ __all__ = ["minimize"]
@@ -100,3 +100,6 @@ def root(
100
100
 
101
101
  optimizer_result = optimizer_early_stopping_wrapper(f_optim)
102
102
  return optimizer_result
103
+
104
+
105
+ __all__ = ["root"]
@@ -15,6 +15,7 @@ from rich.progress import (
15
15
  TimeElapsedColumn,
16
16
  )
17
17
  from rich.table import Column
18
+ from scipy.optimize import OptimizeResult
18
19
 
19
20
  from better_optimize.utilities import ToggleableProgress
20
21
 
@@ -188,10 +189,12 @@ def optimizer_early_stopping_wrapper(f_optim: partial):
188
189
  except (KeyboardInterrupt, StopIteration):
189
190
  # Teardown the progress bar if necessary, then forward the error
190
191
  final_value = objective.previous_x
191
- res = {"x": final_value, "fun": objective.step(final_value)}
192
- _log.warning(
193
- "Execution interrupted and callback failed! Returning partial results as a dictionary "
194
- "(not a scipy.optimize.OptimizeResult)."
192
+ res = OptimizeResult(
193
+ x=final_value,
194
+ fun=objective.previous_x,
195
+ success=False,
196
+ message="`StopIteration` or `KeyboardInterrupt` raised -- optimization stopped "
197
+ "prematurely.",
195
198
  )
196
199
  except Exception as e:
197
200
  raise e
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: better-optimize
3
- Version: 0.1
3
+ Version: 0.1.2
4
4
  Summary: A drop-in replacement for scipy optimize functions with quality of life improvements
5
5
  License: MIT License
6
6
  Project-URL: Homepage, https://github.com/jessegrabowski/better_optimize
@@ -1,5 +1,5 @@
1
1
  numpy
2
- scipy
2
+ scipy>=1.15
3
3
  rich
4
4
 
5
5
  [tests]
@@ -27,7 +27,7 @@ classifiers =[
27
27
 
28
28
  dependencies = [
29
29
  'numpy',
30
- 'scipy',
30
+ 'scipy>=1.15',
31
31
  'rich',
32
32
  ]
33
33
 
File without changes
File without changes
File without changes
File without changes