scijit.optimize.fmin_bfgs¶
- scijit.optimize.fmin_bfgs(f, x0, fprime=None, args=(), gtol=1e-05, norm=inf, epsilon=1.4901161193847656e-08, maxiter=None, full_output=0, disp=1, retall=0, callback=None, xrtol=0, c1=0.0001, c2=0.9, hess_inv0=None)¶
Minimize a function with the quasi-Newton BFGS method.
Reaches the same core as
minimize(method='BFGS'). Callable from Python and from inside@njit; both entries run the same compiled core.- Parameters:
- fcallable
A plain
@njitf(x, *args) -> float.- x0array_like
Initial guess. Any rank is flattened.
- fprimecallable or None, optional
A plain
@njitfprime(x, *args) -> 1-D float64 array.None(default) selects forward differences with stepepsilon. A result whose length is notlen(x0)raisesValueError.- argstuple or ndarray, optional
Extra parameters. A tuple is unpacked into separate arguments after x, so
args=(a, b)callsf(x, a, b)andfprime(x, a, b). Its elements may be of any type a compiled call accepts, arrays and strings included. Default(), which callsf(x). An ndarray or a list arrives as ONE argument instead,f(x, args). See Notes.- gtolfloat, optional
Stop when
vecnorm(g, norm) <= gtol. Default 1e-5.- normfloat, optional
Order of the gradient norm.
inf(default) ismax|g_i|.- epsilonfloat or ndarray, optional
Absolute step for the forward-difference gradient. An array gives a per-element step and must have length
len(x0)or length 1.- maxiterint, float or None, optional
Iteration cap.
None(default) meansN*200.np.infis accepted and means unbounded.- full_outputbool, optional
False(default) returnsxopt.Truereturns(xopt, fopt, gopt, Bopt, func_calls, grad_calls, warnflag). Compile-time constant inside@njit.- dispint, optional
1(default) prints a summary. The status line appears only on success and becomes anOptimizeWarningotherwise; the counters print either way.0is silent, and is what aprangesweep wants: see Notes.- retallbool, optional
Append
allvecsto the return. Compile-time constant.- callbackcallable or None, optional
Called once per iteration as
callback(xk), with a copy of the current iterate. A plain Python function may instead be writtencallback(intermediate_result)and is handed an object carryingxandfun. RaisingStopIterationstops the run and returns the current best. From inside@njitthe callback is an@njitcallback(xk).- xrtolfloat, optional
Relative tolerance on
x. Stop when the step is shorter thanxrtol * (xrtol + |xk|_2). Default 0.- c1, c2float, optional
Armijo and curvature parameters. Defaults 1e-4 and 0.9.
0 < c1 < c2 < 1is required.- hess_inv0ndarray or None, optional
Initial inverse-Hessian estimate, shape
(N, N).None(default) is the identity. A non-symmetric or non-positive-definite matrix raisesValueError.
- Returns:
- xoptndarray
The minimizer.
- foptfloat
f(xopt). Withfull_output.- goptndarray
Gradient at
xopt. Withfull_output.- Boptndarray, shape (N, N)
The inverse-Hessian estimate at
xopt. Withfull_output.- func_callsint
Objective evaluations, including those consumed by a finite-difference gradient. With
full_output.- grad_callsint
Gradient evaluations. With
full_output.- warnflagint
0converged,1maxiterreached,2precision loss,3NaN encountered. Withfull_output.- allvecslist of ndarray
xkafter each iteration,nit + 1entries of shape(N,), the first being x0. Withretall. From inside@njitthis is anumba.typed.List.
- Raises:
- ValueError
If callback is neither
Nonenor a callable nor an@njitfunction; if fprime returns a gradient whose length is notlen(x0); or if hess_inv0 is not(N, N).
See also
scipy.optimize.fmin_bfgsThe scipy routine this mirrors.
scijit.optimize.fmin_cgNo stored matrix; O(N) memory.
scijit.optimize.fmin_l_bfgs_bBounds, and limited memory at large N.
scijit.optimize.minimizeDispatches over the Fortran-backed methods.
Notes
Under retall,
allvecsis a python list of 1-D arrays, as scipy’s is. From inside@njitit is anumba.typed.Listof the same arrays.args is unpacked into the argument lists of f and fprime,
f(x, *args), which is scipy’s contract.DELIBERATELY DIFFERENT: an ndarray or a list args reaches f and fprime as ONE argument,
f(x, args). 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:f(x, *args)on an ndarray inside@njitis aTypingError. An objective written to scipy’s shape refuses the ndarray rather than reading it wrongly.full_output and retall must be compile-time literals inside
@njit: they select the return type.callback takes a plain Python function or an
@njitfunction, and both halt onStopIteration. The Python spelling reaches the compiled core through anumba.objmodeblock over a module-level slot: it takes the GIL once per iteration, and two concurrent solves that both install one overwrite each other, so aprangeloop over it serializes and is not reentrant.Two differences from scipy follow from the compiled path. A plain Python callable cannot be an argument of a compiled function, so a call written inside
@njitpasses the@njitspelling and a Python one is refused by numba. And numba matches no exception class,except StopIterationbeing aTypingErroron 0.66, so any exception raised inside an@njitcallback stops the run the wayStopIterationdoes, where scipy propagates it. A Python callback keeps scipy’s behaviour: onlyStopIterationhalts, and anything else reaches the caller.dispreproduces scipy’s summary and scipy’s warning, at scipy’s default, so a bare call writes to stdout and warns on a limit path. The warning takes the GIL through anumba.objmodeblock and the printing through Python’s stdout, so withdispset aprangeloop over this routine still gives the right answer but serializes.disp=0is the parallel path.A gradient whose length is not
len(x0)raises. scipy 1.18 raises on the same input,ValueError: shapes (2,2) and (1,) not aligned, from the inverse-Hessian product. The one case it misses is a wrong-length gradient small enough for the gradient-norm test to halt at iteration 0, where it returns x at the starting point withwarnflag0.Pure
@njit, so it is safe to call from anumba.prangeloop withdisp=0and with callback left atNoneor given as an@njitfunction.https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.fmin_bfgs.html
Examples
>>> import numpy as np >>> from numba import njit >>> from scijit.optimize import fmin_bfgs >>> @njit ... def rosen(x): ... s = 0.0 ... for i in range(x.size - 1): ... s += 100.0 * (x[i + 1] - x[i] ** 2) ** 2 + (1.0 - x[i]) ** 2 ... return s >>> @njit ... def rosen_g(x): ... g = np.zeros(x.size) ... for i in range(x.size - 1): ... g[i] += -400.0 * x[i] * (x[i + 1] - x[i] ** 2) - 2.0 * (1.0 - x[i]) ... g[i + 1] += 200.0 * (x[i + 1] - x[i] ** 2) ... return g >>> @njit ... def run(): ... return fmin_bfgs(rosen, np.array([0.0, 0.0]), rosen_g, ... disp=0) >>> np.round(run(), 6) array([0.999999, 0.999998])