123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502 |
- """
- Unified interfaces to root finding algorithms for real or complex
- scalar functions.
- Functions
- ---------
- - root : find a root of a scalar function.
- """
- import numpy as np
- from . import _zeros_py as optzeros
- __all__ = ['root_scalar']
- ROOT_SCALAR_METHODS = ['bisect', 'brentq', 'brenth', 'ridder', 'toms748',
- 'newton', 'secant', 'halley']
- class MemoizeDer:
- """Decorator that caches the value and derivative(s) of function each
- time it is called.
- This is a simplistic memoizer that calls and caches a single value
- of `f(x, *args)`.
- It assumes that `args` does not change between invocations.
- It supports the use case of a root-finder where `args` is fixed,
- `x` changes, and only rarely, if at all, does x assume the same value
- more than once."""
- def __init__(self, fun):
- self.fun = fun
- self.vals = None
- self.x = None
- self.n_calls = 0
- def __call__(self, x, *args):
- r"""Calculate f or use cached value if available"""
- # Derivative may be requested before the function itself, always check
- if self.vals is None or x != self.x:
- fg = self.fun(x, *args)
- self.x = x
- self.n_calls += 1
- self.vals = fg[:]
- return self.vals[0]
- def fprime(self, x, *args):
- r"""Calculate f' or use a cached value if available"""
- if self.vals is None or x != self.x:
- self(x, *args)
- return self.vals[1]
- def fprime2(self, x, *args):
- r"""Calculate f'' or use a cached value if available"""
- if self.vals is None or x != self.x:
- self(x, *args)
- return self.vals[2]
- def ncalls(self):
- return self.n_calls
- def root_scalar(f, args=(), method=None, bracket=None,
- fprime=None, fprime2=None,
- x0=None, x1=None,
- xtol=None, rtol=None, maxiter=None,
- options=None):
- """
- Find a root of a scalar function.
- Parameters
- ----------
- f : callable
- A function to find a root of.
- args : tuple, optional
- Extra arguments passed to the objective function and its derivative(s).
- method : str, optional
- Type of solver. Should be one of
- - 'bisect' :ref:`(see here) <optimize.root_scalar-bisect>`
- - 'brentq' :ref:`(see here) <optimize.root_scalar-brentq>`
- - 'brenth' :ref:`(see here) <optimize.root_scalar-brenth>`
- - 'ridder' :ref:`(see here) <optimize.root_scalar-ridder>`
- - 'toms748' :ref:`(see here) <optimize.root_scalar-toms748>`
- - 'newton' :ref:`(see here) <optimize.root_scalar-newton>`
- - 'secant' :ref:`(see here) <optimize.root_scalar-secant>`
- - 'halley' :ref:`(see here) <optimize.root_scalar-halley>`
- bracket: A sequence of 2 floats, optional
- An interval bracketing a root. `f(x, *args)` must have different
- signs at the two endpoints.
- x0 : float, optional
- Initial guess.
- x1 : float, optional
- A second guess.
- fprime : bool or callable, optional
- If `fprime` is a boolean and is True, `f` is assumed to return the
- value of the objective function and of the derivative.
- `fprime` can also be a callable returning the derivative of `f`. In
- this case, it must accept the same arguments as `f`.
- fprime2 : bool or callable, optional
- If `fprime2` is a boolean and is True, `f` is assumed to return the
- value of the objective function and of the
- first and second derivatives.
- `fprime2` can also be a callable returning the second derivative of `f`.
- In this case, it must accept the same arguments as `f`.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- options : dict, optional
- A dictionary of solver options. E.g., ``k``, see
- :obj:`show_options()` for details.
- Returns
- -------
- sol : RootResults
- The solution represented as a ``RootResults`` object.
- Important attributes are: ``root`` the solution , ``converged`` a
- boolean flag indicating if the algorithm exited successfully and
- ``flag`` which describes the cause of the termination. See
- `RootResults` for a description of other attributes.
- See also
- --------
- show_options : Additional options accepted by the solvers
- root : Find a root of a vector function.
- Notes
- -----
- This section describes the available solvers that can be selected by the
- 'method' parameter.
- The default is to use the best method available for the situation
- presented.
- If a bracket is provided, it may use one of the bracketing methods.
- If a derivative and an initial value are specified, it may
- select one of the derivative-based methods.
- If no method is judged applicable, it will raise an Exception.
- Arguments for each method are as follows (x=required, o=optional).
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- | method | f | args | bracket | x0 | x1 | fprime | fprime2 | xtol | rtol | maxiter | options |
- +===============================================+===+======+=========+====+====+========+=========+======+======+=========+=========+
- | :ref:`bisect <optimize.root_scalar-bisect>` | x | o | x | | | | | o | o | o | o |
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- | :ref:`brentq <optimize.root_scalar-brentq>` | x | o | x | | | | | o | o | o | o |
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- | :ref:`brenth <optimize.root_scalar-brenth>` | x | o | x | | | | | o | o | o | o |
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- | :ref:`ridder <optimize.root_scalar-ridder>` | x | o | x | | | | | o | o | o | o |
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- | :ref:`toms748 <optimize.root_scalar-toms748>` | x | o | x | | | | | o | o | o | o |
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- | :ref:`newton <optimize.root_scalar-newton>` | x | o | | x | | x | | o | o | o | o |
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- | :ref:`secant <optimize.root_scalar-secant>` | x | o | | x | x | | | o | o | o | o |
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- | :ref:`halley <optimize.root_scalar-halley>` | x | o | | x | | x | x | o | o | o | o |
- +-----------------------------------------------+---+------+---------+----+----+--------+---------+------+------+---------+---------+
- Examples
- --------
- Find the root of a simple cubic
- >>> from scipy import optimize
- >>> def f(x):
- ... return (x**3 - 1) # only one real root at x = 1
- >>> def fprime(x):
- ... return 3*x**2
- The `brentq` method takes as input a bracket
- >>> sol = optimize.root_scalar(f, bracket=[0, 3], method='brentq')
- >>> sol.root, sol.iterations, sol.function_calls
- (1.0, 10, 11)
- The `newton` method takes as input a single point and uses the
- derivative(s).
- >>> sol = optimize.root_scalar(f, x0=0.2, fprime=fprime, method='newton')
- >>> sol.root, sol.iterations, sol.function_calls
- (1.0, 11, 22)
- The function can provide the value and derivative(s) in a single call.
- >>> def f_p_pp(x):
- ... return (x**3 - 1), 3*x**2, 6*x
- >>> sol = optimize.root_scalar(
- ... f_p_pp, x0=0.2, fprime=True, method='newton'
- ... )
- >>> sol.root, sol.iterations, sol.function_calls
- (1.0, 11, 11)
- >>> sol = optimize.root_scalar(
- ... f_p_pp, x0=0.2, fprime=True, fprime2=True, method='halley'
- ... )
- >>> sol.root, sol.iterations, sol.function_calls
- (1.0, 7, 8)
- """
- if not isinstance(args, tuple):
- args = (args,)
- if options is None:
- options = {}
- # fun also returns the derivative(s)
- is_memoized = False
- if fprime2 is not None and not callable(fprime2):
- if bool(fprime2):
- f = MemoizeDer(f)
- is_memoized = True
- fprime2 = f.fprime2
- fprime = f.fprime
- else:
- fprime2 = None
- if fprime is not None and not callable(fprime):
- if bool(fprime):
- f = MemoizeDer(f)
- is_memoized = True
- fprime = f.fprime
- else:
- fprime = None
- # respect solver-specific default tolerances - only pass in if actually set
- kwargs = {}
- for k in ['xtol', 'rtol', 'maxiter']:
- v = locals().get(k)
- if v is not None:
- kwargs[k] = v
- # Set any solver-specific options
- if options:
- kwargs.update(options)
- # Always request full_output from the underlying method as _root_scalar
- # always returns a RootResults object
- kwargs.update(full_output=True, disp=False)
- # Pick a method if not specified.
- # Use the "best" method available for the situation.
- if not method:
- if bracket:
- method = 'brentq'
- elif x0 is not None:
- if fprime:
- if fprime2:
- method = 'halley'
- else:
- method = 'newton'
- else:
- method = 'secant'
- if not method:
- raise ValueError('Unable to select a solver as neither bracket '
- 'nor starting point provided.')
- meth = method.lower()
- map2underlying = {'halley': 'newton', 'secant': 'newton'}
- try:
- methodc = getattr(optzeros, map2underlying.get(meth, meth))
- except AttributeError as e:
- raise ValueError('Unknown solver %s' % meth) from e
- if meth in ['bisect', 'ridder', 'brentq', 'brenth', 'toms748']:
- if not isinstance(bracket, (list, tuple, np.ndarray)):
- raise ValueError('Bracket needed for %s' % method)
- a, b = bracket[:2]
- r, sol = methodc(f, a, b, args=args, **kwargs)
- elif meth in ['secant']:
- if x0 is None:
- raise ValueError('x0 must not be None for %s' % method)
- if x1 is None:
- raise ValueError('x1 must not be None for %s' % method)
- if 'xtol' in kwargs:
- kwargs['tol'] = kwargs.pop('xtol')
- r, sol = methodc(f, x0, args=args, fprime=None, fprime2=None,
- x1=x1, **kwargs)
- elif meth in ['newton']:
- if x0 is None:
- raise ValueError('x0 must not be None for %s' % method)
- if not fprime:
- raise ValueError('fprime must be specified for %s' % method)
- if 'xtol' in kwargs:
- kwargs['tol'] = kwargs.pop('xtol')
- r, sol = methodc(f, x0, args=args, fprime=fprime, fprime2=None,
- **kwargs)
- elif meth in ['halley']:
- if x0 is None:
- raise ValueError('x0 must not be None for %s' % method)
- if not fprime:
- raise ValueError('fprime must be specified for %s' % method)
- if not fprime2:
- raise ValueError('fprime2 must be specified for %s' % method)
- if 'xtol' in kwargs:
- kwargs['tol'] = kwargs.pop('xtol')
- r, sol = methodc(f, x0, args=args, fprime=fprime, fprime2=fprime2, **kwargs)
- else:
- raise ValueError('Unknown solver %s' % method)
- if is_memoized:
- # Replace the function_calls count with the memoized count.
- # Avoids double and triple-counting.
- n_calls = f.n_calls
- sol.function_calls = n_calls
- return sol
- def _root_scalar_brentq_doc():
- r"""
- Options
- -------
- args : tuple, optional
- Extra arguments passed to the objective function.
- bracket: A sequence of 2 floats, optional
- An interval bracketing a root. `f(x, *args)` must have different
- signs at the two endpoints.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- options: dict, optional
- Specifies any method-specific options not covered above
- """
- pass
- def _root_scalar_brenth_doc():
- r"""
- Options
- -------
- args : tuple, optional
- Extra arguments passed to the objective function.
- bracket: A sequence of 2 floats, optional
- An interval bracketing a root. `f(x, *args)` must have different
- signs at the two endpoints.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- options: dict, optional
- Specifies any method-specific options not covered above.
- """
- pass
- def _root_scalar_toms748_doc():
- r"""
- Options
- -------
- args : tuple, optional
- Extra arguments passed to the objective function.
- bracket: A sequence of 2 floats, optional
- An interval bracketing a root. `f(x, *args)` must have different
- signs at the two endpoints.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- options: dict, optional
- Specifies any method-specific options not covered above.
- """
- pass
- def _root_scalar_secant_doc():
- r"""
- Options
- -------
- args : tuple, optional
- Extra arguments passed to the objective function.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- x0 : float, required
- Initial guess.
- x1 : float, required
- A second guess.
- options: dict, optional
- Specifies any method-specific options not covered above.
- """
- pass
- def _root_scalar_newton_doc():
- r"""
- Options
- -------
- args : tuple, optional
- Extra arguments passed to the objective function and its derivative.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- x0 : float, required
- Initial guess.
- fprime : bool or callable, optional
- If `fprime` is a boolean and is True, `f` is assumed to return the
- value of derivative along with the objective function.
- `fprime` can also be a callable returning the derivative of `f`. In
- this case, it must accept the same arguments as `f`.
- options: dict, optional
- Specifies any method-specific options not covered above.
- """
- pass
- def _root_scalar_halley_doc():
- r"""
- Options
- -------
- args : tuple, optional
- Extra arguments passed to the objective function and its derivatives.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- x0 : float, required
- Initial guess.
- fprime : bool or callable, required
- If `fprime` is a boolean and is True, `f` is assumed to return the
- value of derivative along with the objective function.
- `fprime` can also be a callable returning the derivative of `f`. In
- this case, it must accept the same arguments as `f`.
- fprime2 : bool or callable, required
- If `fprime2` is a boolean and is True, `f` is assumed to return the
- value of 1st and 2nd derivatives along with the objective function.
- `fprime2` can also be a callable returning the 2nd derivative of `f`.
- In this case, it must accept the same arguments as `f`.
- options: dict, optional
- Specifies any method-specific options not covered above.
- """
- pass
- def _root_scalar_ridder_doc():
- r"""
- Options
- -------
- args : tuple, optional
- Extra arguments passed to the objective function.
- bracket: A sequence of 2 floats, optional
- An interval bracketing a root. `f(x, *args)` must have different
- signs at the two endpoints.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- options: dict, optional
- Specifies any method-specific options not covered above.
- """
- pass
- def _root_scalar_bisect_doc():
- r"""
- Options
- -------
- args : tuple, optional
- Extra arguments passed to the objective function.
- bracket: A sequence of 2 floats, optional
- An interval bracketing a root. `f(x, *args)` must have different
- signs at the two endpoints.
- xtol : float, optional
- Tolerance (absolute) for termination.
- rtol : float, optional
- Tolerance (relative) for termination.
- maxiter : int, optional
- Maximum number of iterations.
- options: dict, optional
- Specifies any method-specific options not covered above.
- """
- pass
|