scijit.optimize.newton¶
- scijit.optimize.newton(f, x0, fprime=None, args=(), tol=1.48e-08, maxiter=50, fprime2=None, x1=None, rtol=0.0, full_output=False, disp=True)¶
Newton-Raphson, secant and Halley root-finder.
Which derivatives are supplied selects the method: no fprime runs the secant method, fprime alone runs Newton-Raphson, fprime with fprime2 runs Halley’s method.
Callback style A for every callback: plain
@njitfunctions of one float, passed as first-class arguments:@njit def f(x): return x * x - 2.0 @njit def fp(x): return 2.0 * x res = newton(f, 1.0, fp) res.root, res.converged
- Parameters:
- f@njit function
f(x) -> float Function whose root is wanted.
- x0float or complex
Starting guess. A complex x0 runs the same iteration over
complex128and returns a complex root, on all three methods. An ARRAY of any shape runs a different algorithm over every element at once, described under Notes; the callbacks then receive the whole array and return one of the same shape. A 0-d array is a scalar start.- fprime@njit function
fprime(x) -> float, or None, optional First derivative.
None(default) runs the secant method with x1 as the second starting guess. Positional third.- argstuple, optional
Extra arguments, unpacked into every call as
f(x, *args)and into fprime and fprime2 the same way. Must be a tuple; anything else raisesTypeError. Default().- tolfloat, optional
Absolute step tolerance; convergence when
|p - p0| <= tol + rtol * |p0|. Default 1.48e-8.- maxiterint, optional
Iteration cap. Default 50.
maxiter < 1raisesValueError.- fprime2@njit function
fprime2(x) -> float, or None, optional Second derivative. Supplying it alongside fprime runs Halley’s method. Supplying it without fprime raises
ValueError.- x1float, complex or None, optional
Second starting guess for the secant method, used only when fprime is
None. The defaultNoneseeds it from x0,x0 * (1 + 1e-4)moved a further1e-4away from zero. Any float is taken as the seed, NaN included.x1 == x0raisesValueError.- rtolfloat, optional
Relative step tolerance. Default 0.0.
- full_outputbool, optional
False(default) returns the root alone.Truereturns(x, RootResults). Inside@njitit must be a compile-time constant; see Notes.- dispbool, optional
True(default) raisesRuntimeErrorwhen the iteration limit is reached.Falsereturnsconverged=Falseinstead.
- f@njit function
- Returns:
- xfloat or complex
The estimated root, when
full_outputis False. Complex when x0 is.- (x, res)tuple of (float or complex, RootResults)
When
full_outputis True. res fields are reached by attribute, by index or by unpacking.- rootfloat or complex
Best estimate.
- iterationsint
Iterations used. An immediate exact hit, where
f(x0) == 0, reports 0.- function_callsint
Evaluations of f, plus the derivative evaluations, counted on the same counter.
- convergedbool
True if a tolerance test was met before maxiter.
- flagstr
'converged', or'convergence error'.- methodstr
The method that produced the result, by name.
- resArrayNewtonResult
When x0 is an array of one dimension or more, and full_output is True. root, converged and zero_der are each shaped like x0. Under
full_output=Falsethat route returns the root member alone.
- Raises:
- ValueError
If fprime2 is given without fprime; if
tol <= 0; ifmaxiter < 1; or ifx1 == x0.- RuntimeError
If maxiter is reached, or the derivative is zero so the next step is undefined, or two secant iterates coincide. All three are suppressed by
disp=False. On an array x0 the iteration limit raises only when EVERY element failed, and disp does not suppress it.
- Warns:
- RuntimeWarning
Under
disp=False, on two exits:"Derivative was zero."and"Tolerance of {p1 - p0} reached.". Plain non-convergence does not warn.An array x0 warns from four more, whatever disp is:
"all derivatives were zero"and"some derivatives were zero"when a derivative was supplied,"RMS of {rms:g} reached"on the secant, and"some failed to converge after {maxiter} iterations"when part of the array reached the limit.
See also
scipy.optimize.newtonThe scipy routine this mirrors.
scijit.optimize.root_scalarThe same methods, by name.
scijit.optimize.brentqBracketed, and guaranteed to converge.
Notes
full_output selects the RETURN SHAPE, and a compiled function has one return type per signature, so inside
@njitthe flag has to be readable when the call compiles. A literal, an omitted default and a module-level constant all are; a variable is not, and raises TypingError naming the constraint. From Python a runtime value is fine.The result is a namedtuple, where scipy’s is a
dictsubclass. See RootResults.Which of the three methods runs follows the VALUE of fprime and fprime2 from Python, as it does in scipy: one variable holding either a function or
Nonereaches both methods and reports the one it ran. Inside@njitthe choice follows the TYPE instead, so it is settled when the call compiles, and a variable that may hold either does not type. Both spellings work there written on their own.fprime2 without fprime raises. scipy’s Halley block sits inside
if fprime is not None, so scipy ignores a lone fprime2, runs the secant loop and reportsmethod='secant'.An array x0 runs a different algorithm, not the scalar loop repeated. Every element advances together, an element whose derivative is zero stops being updated while the others continue, and the outcome is reported per element in converged and zero_der. x1, rtol and disp are not read on that route, and the result carries no
iterationsorfunction_calls. Its secant seed iseps ** 0.33, where the scalar secant seeds with1e-4.A LENGTH-1 array takes the array route here. scipy switches on
np.size(x0) > 1, so a length-1 array takes its scalar route instead. Underfull_outputscipy returns(array, RootResults)for that one size and this returns ArrayNewtonResult. The size of an array is a run-time value while its number of dimensions is not, and a compiled function has one return type per signature. A 0-d array is the scalar route on both sides.An integer array x0 returns float64. scipy returns the input integer dtype in the single case where no element is ever updated, which is an x0 whose every element is already an exact root.
Newton’s method is not bracketed, so a bad start can diverge. There is no warning on divergence, only the iteration limit.
Pure
@njit, safe to call from anumba.prangeloop.https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.newton.html
Examples
>>> from numba import njit >>> from scijit.optimize import newton >>> @njit ... def f(x): ... return x * x - 2.0 >>> @njit ... def fp(x): ... return 2.0 * x >>> @njit ... def run(): ... return newton(f, 1.0, fp) >>> round(run(), 12) 1.414213562373 >>> @njit ... def run_full(): ... return newton(f, 1.0, fp, full_output=True) >>> x, res = run_full() >>> round(x, 12), res.converged, res.iterations (1.414213562373, True, 5)
With no derivative, which runs the secant method:
>>> @njit ... def run_secant(): ... return newton(f, 1.0) >>> round(run_secant(), 12) 1.414213562373
An array of starting points, solved together. The callbacks receive the whole array:
>>> import numpy as np >>> @njit ... def run_vec(): ... return newton(f, np.array([1.0, 10.0, -3.0]), fp) >>> np.round(run_vec(), 12) array([ 1.41421356, 1.41421356, -1.41421356]) >>> @njit ... def run_vec_full(): ... return newton(f, np.array([1.0, 10.0, -3.0]), fp, ... full_output=True) >>> res = run_vec_full() >>> res.converged, res.zero_der (array([ True, True, True]), array([False, False, False]))