multistart_minimize#
- class braintools.optim.multistart_minimize(fun, x0, *, method='lbfgs', max_iter=500, tol=1e-06, bounds=None, learning_rate=0.01)#
Minimise a scalar function from many starting points in parallel.
Every start runs the same local optimiser inside a
lax.while_loop; the loop isvmap-ped over the starts and the whole run is compiled into one XLA program. A start stops when it converges, when its loss becomes non-finite, or aftermax_iteriterations, while the other starts keep going.- Parameters:
fun (
Callable[[Any],Any]) –fun(x) -> scalar. A pure, differentiable function of one startx(a pytree shaped like a single slice ofx0). Its value must be a plain or dimensionless scalar.x0 (
Any) – Starting points: every leaf has a leading axis of sizeS, the number of starts. Leaves may bebrainunitQuantities;funthen receives Quantities in the same units.method (
str|GradientTransformation) – Local optimiser.'lbfgs'isoptax.lbfgs()with its default zoom line search (learning_rateis ignored);'adam'and'gd'areoptax.adam()andoptax.sgd()with step sizelearning_rate. Any otheroptaxtransformation can be passed directly.max_iter (
int) – Maximum number of iterations per start (static).tol (
float) – Convergence tolerance. A start has converged when the largest absolute gradient entry (in the unconstrained space) is belowtol, or when the loss changes by a relative amount \(|f_k - f_{k-1}| \le \mathrm{tol}\,|f_{k-1}|\) in one iteration (so a step that leaves the loss unchanged also counts).bounds (
Any) – Box bounds mirroring the structure ofx0: a(low, high)pair orNonein place of every leaf (a bare pair for a single-leafx0). Bounds broadcast against one start of the leaf and may be Quantities in the leaf’s unit.loworhighmay beNonefor a one-sided bound. A bounded leaf is optimised in an unconstrained space throughbrainstate.nn.SigmoidT(SoftplusT/NegSoftplusTfor one-sided bounds) and mapped back, so iterates never leave the box.learning_rate (
Any) – Step size for'adam'and'gd'.
- Returns:
(x, fun, converged, n_iter, best_x, best_fun, best_index, history); seeMultiStartResult.- Return type:
- Raises:
ValueError – If
methodis unknown,max_iteris not a positive integer,x0has no leaves or leaves with different leading sizes, or a bound is malformed (wrong structure, non-finite,low >= high).TypeError – If
funreturns a value with a physical unit, or a bound’s unit does not match its leaf.
See also
multistart_initUniform starting points within bounds.
braintools.fit.FitterModel fitting built on this minimiser.
Notes
Convergence test. The gradient test uses the gradient with respect to the constrained value (the chain-rule gradient divided by the slope of the bijector), so a start near a bound, where the sigmoid is flat, is not mistaken for a stationary point. At an optimum on a bound that gradient stays non-zero, and only the loss-change test can stop the start. The loss-change test is used for
'lbfgs'only: its line search makes a negligible decrease a sign of convergence, whereas the per-step change of a fixed-step method ('adam','gd', a customoptaxtransformation) reflects the step size rather than the distance to the optimum (with a large constant offset in the loss, a relative test would stop such a method far from the minimum). These methods therefore stop on the gradient test or aftermax_iter. Conversely, for'lbfgs'on a loss with a large constant offset the relative test can stop early; lowertol(or remove the offset) then.Starts on a bound. Starting values are moved
1e-3of the interval width inside the bounds (1e-3 * max(1, |bound|)for a one-sided bound) before the bijector is inverted, so starts on (or outside) a bound are finite and still move.Non-finite losses. A start whose loss at
x0is not finite stops immediately; a start whose loss becomes non-finite during the run (it diverged) stops and keeps its last finite point. Either way it is reported withconverged=Falseandn_iter < max_iter, and it is never selected as the best while another start has a finite loss and did not diverge. If every start diverged, the lowest finite loss among them is selected; if no loss is finite,best_indexis 0 andbest_funis not finite. A start that already satisfies the convergence test atx0runs zero iterations.The function contains no host synchronisation, so it can itself be called inside
jax.jit(with the keyword options static) orjax.vmap. It is not meant to be differentiated.Examples
>>> import jax.numpy as jnp >>> from braintools.optim import multistart_minimize >>> def rosenbrock(p): ... return (1. - p['x']) ** 2 + 100. * (p['y'] - p['x'] ** 2) ** 2 >>> x0 = {'x': jnp.array([-1.5, 0., 2.]), 'y': jnp.array([2., -1., 1.])} >>> res = multistart_minimize(rosenbrock, x0, max_iter=200) >>> res.converged Array([ True, True, True], dtype=bool) >>> print(jnp.round(res.best_x['x'], 3), jnp.round(res.best_x['y'], 3)) 1.0 1.0 >>> >>> res = multistart_minimize(lambda x: jnp.sin(3. * x) + 0.1 * x ** 2, ... jnp.linspace(-2.5, 2.5, 5), bounds=(-3., 3.)) >>> print(jnp.round(res.best_x, 3)) -0.512