scijit.optimize.fmin_l_bfgs_b¶
- scijit.optimize.fmin_l_bfgs_b(func, x0, fprime=None, args=(), approx_grad=0, bounds=None, m=10, factr=10000000.0, pgtol=1e-05, epsilon=1e-08, maxfun=15000, maxiter=15000, callback=None, maxls=20, iprint=-1)¶
Minimize a function of many variables subject to simple bounds.
L-BFGS-B stores a limited number of past gradient differences instead of a full Hessian approximation, which is what makes it usable at large n.
Callable from Python and from inside
@njit. Both entries run the same compiled driver. One argument SPELLING is python-only, named under Notes.- Parameters:
- funccallable
A plain
@njitfunction. Which protocol it must follow depends on the two arguments below: withfprime=Noneandapprox_grad=0it isf(x, *args) -> (value, gradient); with fprime given orapprox_grad=1it isf(x, *args) -> value.- x0array_like
Initial guess. Any rank is flattened, and it is clipped into bounds before the first evaluation.
- fprimecallable or None, optional
jac(x, *args) -> gradient.Noneor a plain@njitfunction, decided at compile time.- argstuple or ndarray, optional
Extra values for func and fprime. A tuple is unpacked into their argument lists,
f(x, *args); the entries may be of any types the callbacks accept.()(default) callsf(x).- approx_gradbool, optional
Truecomputes the gradient by forward differences with absolute step epsilon. Compile-time constant.- boundssequence of (min, max) pairs, (n, 2) ndarray, or None, optional
None(default) is unconstrained. A sequence of pairs and an(n, 2)array both work, from Python and inside@njit.Noneinside a pair means “no bound in that direction”;-np.infandnp.infare the equivalent spelling. Pairs may mix the two, and a list holding mixed pairs is written at the@njitcall site rather than passed in as an argument; see Notes. The pairs are read by transposition, so the number of rows must belen(x0)and each row must hold exactly two entries.- mint, optional
Number of stored gradient corrections. Default 10.
- factrfloat, optional
Convergence tolerance on f, in units of machine epsilon. Default 1e7. The absolute tolerance is
factr * np.finfo(float).eps.- pgtolfloat, optional
Convergence tolerance on the projected gradient. Default 1e-5.
- epsilonfloat or ndarray, optional
Absolute step for approx_grad. Default 1e-8. An array gives one step per coordinate; a size-1 array applies to every coordinate. Read only when approx_grad is set.
- maxfun, maxiterint, optional
Evaluation and iteration budgets. Both default to 15000.
- callbackcallable, optional
Called once per iteration, as
callback(xk)orcallback(intermediate_result). Two spellings are served: a plain Python callable, which halts the solve when it raisesStopIteration, and a numba@njitcallback(xk), which halts when it raises any exception. See Notes.- maxlsint, optional
Maximum line-search steps per iteration. Default 20. Must be positive.
- iprintint, optional
Verbosity passed straight to the Fortran, whose output goes to the process stdout rather than through Python. Default
-1, silent.
- Returns:
- xndarray, shape (n,)
The minimizer.
- ffloat
func at x.
- dLbfgsbInfo
Namedtuple with fields
grad,task,funcalls,nitandwarnflag, reached as attributes or by position.warnflagis0converged,1a limit was reached and2anything else;gradandtaskare meaningful whatever it is.
- Raises:
- ValueError
If callback is a
@cfunc, a raw function address or a non-callable; if bounds has a length other thanlen(x0), or rows that are not pairs; if any lower bound exceeds its upper bound; if maxls is below 1; if epsilon does not broadcast against x0 while approx_grad is set; or if func or fprime returns a gradient whose length is notlen(x0).- TypeError
If a row of bounds is a number rather than a pair.
- numba.core.errors.TypingError
Inside
@njit, where the refusal is decided by TYPE: a Python callback, a list of pairs that mixesNonewith a number, and an approx_grad or fprime whose value is not known when the call compiles.
See also
scipy.optimize.fmin_l_bfgs_bThe scipy routine this mirrors.
scijit.optimize.minimizeDispatches here for
method='L-BFGS-B'.scijit.optimize.fmin_slsqpConstrained minimization by SLSQP.
scijit.optimize.fmin_bfgsUnbounded BFGS, a pure
@njitport.
Notes
d is a namedtuple carrying scipy’s five field names in scipy’s order.
d['warnflag'],d.warnflagandd[4]all reach the flag, andd.keys(),d.values(),d.items(),d.get(),'task' in danddict(d)all work. Unpacking yields the five VALUES, where unpacking scipy’s dict yields the five field names.An ndarray or a list args reaches func and fprime as ONE argument,
f(x, args), where scipy unpacks those two element by element. The arity of a compiled call is fixed when it compiles, so a sequence whose length is known only at run time cannot be unpacked.At
len(x0) == 0the reported funcalls is 0 where scipy reports 1: scipy evaluates func once at x0 before the solver runs and this reports the calls it made, which is none. f comes back as a float where scipy returns the 0-d array it started with.Whether fprime is
None, and the value of approx_grad, are compile-time constants inside@njit: they select which callback protocol gets compiled, so neither can vary at run time.A Python callback is reached from compiled code through a module-level slot and a
numba.objmodeblock, so it takes the GIL and pays an interpreter round trip once per iteration. It is also notprange-safe, because the slot is module state that two concurrent solves share. Inside@njitonly the@njitspelling is accepted, since a Python callable cannot cross into compiled code as an argument.The two spellings differ in what halts the solve. The Python one halts on
StopIteration, which is scipy’s contract; anything else it raises reaches the caller. The@njitone halts on ANY exception, because numba matches no exception class:except StopIterationdoes not compile.A MIXED list of pairs such as
[(None, 5.0), (0.0, 1.0)]is written at the@njitcall site, not passed in as an argument. numba unifies theNoneand the float into an optional type for a literal, and refuses to unbox a heterogeneous list handed over as an argument:TypeError: can't unbox heterogeneous list. The same pairs as a tuple cross either way, and so do a list whose pairs are uniform, an(n, 2)array, and the-np.inf/np.infspelling.A gradient whose length is not
len(x0)raises. scipy 1.18 raises at no length and no value. What it returns depends on the gradient it was handed: on a 2-variable problem with a length-1 gradient it reportswarnflag0 at the starting point for0.0and-1e-6, andwarnflag2 with the first coordinate moved for-1.0and-2.0. The coordinates the short gradient does not cover are not written, measured coming back as-1.177e-310.iprint has no scipy counterpart, and sits last in the signature so that every position before it is scipy’s. scipy 1.18 removed both
iprintanddispfrom this function and from theminimizeoptions dict.Safe to call from a
numba.prangeloop with callback atNoneor an@njitfunction: the solver is reverse communication, so its state lives in caller-owned arrays. A Python callback is not, and serializes the loop.https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_l_bfgs_b.html
Examples
>>> import numpy as np >>> from numba import njit >>> from scijit.optimize import fmin_l_bfgs_b >>> @njit ... def fg(x): ... f = (x[0] - 1.0) ** 2 + (x[1] - 2.5) ** 2 ... return f, np.array([2.0 * (x[0] - 1.0), 2.0 * (x[1] - 2.5)]) >>> @njit ... def run(): ... return fmin_l_bfgs_b(fg, np.array([0.0, 0.0])) >>> x, f, d = run() >>> x array([1. , 2.5]) >>> f 0.0 >>> d.warnflag, d.nit (0, 2)