ScipyOptimizer

Contents

ScipyOptimizer#

class braintools.optim.ScipyOptimizer(loss_fun, bounds, method=None, constraints=(), tol=None, callback=None, options=None)#

SciPy-based optimizer with dict/sequence bounds compatible with Nevergrad.

This optimizer accepts the same bounds structure as braintools.optim.NevergradOptimizer and a loss function with matching signature:

  • If bounds is a sequence of (min, max) pairs, loss_fun is called positionally as loss_fun(*params).

  • If bounds is a dict mapping names to (min, max) pairs, loss_fun is called by keyword as loss_fun(**params).

Bounds can be scalars or arrays, and may optionally be provided as brainunit.Quantity. Internally, SciPy works on the numeric mantissas while your loss_fun receives values in the same structure as the bounds (without units).

Parameters:
  • loss_fun (Callable) – Objective function returning a scalar (0-D array-like). Its signature must match the structure of bounds as described above.

  • bounds (Sequence | Dict[str, Any]) – Search space bounds, as in Nevergrad: each value is (min, max) where elements are scalars or arrays, optionally u.Quantity. All elements in a pair must share shape, and units (if any) must be compatible. For dict bounds, names define keyword arguments to loss_fun.

  • method (str | None) – SciPy method (e.g., 'L-BFGS-B', 'TNC', 'SLSQP', 'Powell'). If omitted, SciPy selects a default based on constraints/bounds.

  • constraints – Passed through to scipy.optimize.minimize.

  • tol – Passed through to scipy.optimize.minimize.

  • callback – Passed through to scipy.optimize.minimize.

  • options – Passed through to scipy.optimize.minimize.

Notes

  • This wrapper flattens parameters to a 1-D vector for SciPy and unflattens results back to the same structure found in bounds.

  • Gradients are computed via JAX auto-differentiation.

Examples

Minimize a simple quadratic with tuple bounds:

>>> import jax.numpy as jnp
>>> def loss(x, y):
...     return (x - 1.0)**2 + (y + 2.0)**2
>>> bounds = [(-5.0, 5.0), (-3.0, 3.0)]
>>> opt = ScipyOptimizer(loss_fun=loss, bounds=bounds, method='L-BFGS-B')
>>> res = opt.minimize(n_iter=3)
>>> isinstance(res.x, (list, tuple))  # same structure as bounds
True

With named parameters using dict bounds:

>>> def loss(**p):
...     return (p['a'] - 1.0)**2 + (p['b'] + 2.0)**2
>>> bounds = {"a": (-5.0, 5.0), "b": (-3.0, 3.0)}
>>> opt = ScipyOptimizer(loss_fun=loss, bounds=bounds, method='TNC')
>>> res = opt.minimize(n_iter=2)
>>> isinstance(res.x, dict)
True