| import warnings |
| from . import _minpack |
|
|
| import numpy as np |
| from numpy import (atleast_1d, triu, shape, transpose, zeros, prod, greater, |
| asarray, inf, |
| finfo, inexact, issubdtype, dtype) |
| from scipy import linalg |
| from scipy.linalg import svd, cholesky, solve_triangular, LinAlgError |
| from scipy._lib._util import _asarray_validated, _lazywhere, _contains_nan |
| from scipy._lib._util import getfullargspec_no_self as _getfullargspec |
| from ._optimize import OptimizeResult, _check_unknown_options, OptimizeWarning |
| from ._lsq import least_squares |
| |
| from ._lsq.least_squares import prepare_bounds |
| from scipy.optimize._minimize import Bounds |
|
|
| __all__ = ['fsolve', 'leastsq', 'fixed_point', 'curve_fit'] |
|
|
|
|
| def _check_func(checker, argname, thefunc, x0, args, numinputs, |
| output_shape=None): |
| res = atleast_1d(thefunc(*((x0[:numinputs],) + args))) |
| if (output_shape is not None) and (shape(res) != output_shape): |
| if (output_shape[0] != 1): |
| if len(output_shape) > 1: |
| if output_shape[1] == 1: |
| return shape(res) |
| msg = f"{checker}: there is a mismatch between the input and output " \ |
| f"shape of the '{argname}' argument" |
| func_name = getattr(thefunc, '__name__', None) |
| if func_name: |
| msg += f" '{func_name}'." |
| else: |
| msg += "." |
| msg += f'Shape should be {output_shape} but it is {shape(res)}.' |
| raise TypeError(msg) |
| if issubdtype(res.dtype, inexact): |
| dt = res.dtype |
| else: |
| dt = dtype(float) |
| return shape(res), dt |
|
|
|
|
| def fsolve(func, x0, args=(), fprime=None, full_output=0, |
| col_deriv=0, xtol=1.49012e-8, maxfev=0, band=None, |
| epsfcn=None, factor=100, diag=None): |
| """ |
| Find the roots of a function. |
| |
| Return the roots of the (non-linear) equations defined by |
| ``func(x) = 0`` given a starting estimate. |
| |
| Parameters |
| ---------- |
| func : callable ``f(x, *args)`` |
| A function that takes at least one (possibly vector) argument, |
| and returns a value of the same length. |
| x0 : ndarray |
| The starting estimate for the roots of ``func(x) = 0``. |
| args : tuple, optional |
| Any extra arguments to `func`. |
| fprime : callable ``f(x, *args)``, optional |
| A function to compute the Jacobian of `func` with derivatives |
| across the rows. By default, the Jacobian will be estimated. |
| full_output : bool, optional |
| If True, return optional outputs. |
| col_deriv : bool, optional |
| Specify whether the Jacobian function computes derivatives down |
| the columns (faster, because there is no transpose operation). |
| xtol : float, optional |
| The calculation will terminate if the relative error between two |
| consecutive iterates is at most `xtol`. |
| maxfev : int, optional |
| The maximum number of calls to the function. If zero, then |
| ``100*(N+1)`` is the maximum where N is the number of elements |
| in `x0`. |
| band : tuple, optional |
| If set to a two-sequence containing the number of sub- and |
| super-diagonals within the band of the Jacobi matrix, the |
| Jacobi matrix is considered banded (only for ``fprime=None``). |
| epsfcn : float, optional |
| A suitable step length for the forward-difference |
| approximation of the Jacobian (for ``fprime=None``). If |
| `epsfcn` is less than the machine precision, it is assumed |
| that the relative errors in the functions are of the order of |
| the machine precision. |
| factor : float, optional |
| A parameter determining the initial step bound |
| (``factor * || diag * x||``). Should be in the interval |
| ``(0.1, 100)``. |
| diag : sequence, optional |
| N positive entries that serve as a scale factors for the |
| variables. |
| |
| Returns |
| ------- |
| x : ndarray |
| The solution (or the result of the last iteration for |
| an unsuccessful call). |
| infodict : dict |
| A dictionary of optional outputs with the keys: |
| |
| ``nfev`` |
| number of function calls |
| ``njev`` |
| number of Jacobian calls |
| ``fvec`` |
| function evaluated at the output |
| ``fjac`` |
| the orthogonal matrix, q, produced by the QR |
| factorization of the final approximate Jacobian |
| matrix, stored column wise |
| ``r`` |
| upper triangular matrix produced by QR factorization |
| of the same matrix |
| ``qtf`` |
| the vector ``(transpose(q) * fvec)`` |
| |
| ier : int |
| An integer flag. Set to 1 if a solution was found, otherwise refer |
| to `mesg` for more information. |
| mesg : str |
| If no solution is found, `mesg` details the cause of failure. |
| |
| See Also |
| -------- |
| root : Interface to root finding algorithms for multivariate |
| functions. See the ``method='hybr'`` in particular. |
| |
| Notes |
| ----- |
| ``fsolve`` is a wrapper around MINPACK's hybrd and hybrj algorithms. |
| |
| Examples |
| -------- |
| Find a solution to the system of equations: |
| ``x0*cos(x1) = 4, x1*x0 - x1 = 5``. |
| |
| >>> import numpy as np |
| >>> from scipy.optimize import fsolve |
| >>> def func(x): |
| ... return [x[0] * np.cos(x[1]) - 4, |
| ... x[1] * x[0] - x[1] - 5] |
| >>> root = fsolve(func, [1, 1]) |
| >>> root |
| array([6.50409711, 0.90841421]) |
| >>> np.isclose(func(root), [0.0, 0.0]) # func(root) should be almost 0.0. |
| array([ True, True]) |
| |
| """ |
| def _wrapped_func(*fargs): |
| """ |
| Wrapped `func` to track the number of times |
| the function has been called. |
| """ |
| _wrapped_func.nfev += 1 |
| return func(*fargs) |
|
|
| _wrapped_func.nfev = 0 |
|
|
| options = {'col_deriv': col_deriv, |
| 'xtol': xtol, |
| 'maxfev': maxfev, |
| 'band': band, |
| 'eps': epsfcn, |
| 'factor': factor, |
| 'diag': diag} |
|
|
| res = _root_hybr(_wrapped_func, x0, args, jac=fprime, **options) |
| res.nfev = _wrapped_func.nfev |
|
|
| if full_output: |
| x = res['x'] |
| info = {k: res.get(k) |
| for k in ('nfev', 'njev', 'fjac', 'r', 'qtf') if k in res} |
| info['fvec'] = res['fun'] |
| return x, info, res['status'], res['message'] |
| else: |
| status = res['status'] |
| msg = res['message'] |
| if status == 0: |
| raise TypeError(msg) |
| elif status == 1: |
| pass |
| elif status in [2, 3, 4, 5]: |
| warnings.warn(msg, RuntimeWarning, stacklevel=2) |
| else: |
| raise TypeError(msg) |
| return res['x'] |
|
|
|
|
| def _root_hybr(func, x0, args=(), jac=None, |
| col_deriv=0, xtol=1.49012e-08, maxfev=0, band=None, eps=None, |
| factor=100, diag=None, **unknown_options): |
| """ |
| Find the roots of a multivariate function using MINPACK's hybrd and |
| hybrj routines (modified Powell method). |
| |
| Options |
| ------- |
| col_deriv : bool |
| Specify whether the Jacobian function computes derivatives down |
| the columns (faster, because there is no transpose operation). |
| xtol : float |
| The calculation will terminate if the relative error between two |
| consecutive iterates is at most `xtol`. |
| maxfev : int |
| The maximum number of calls to the function. If zero, then |
| ``100*(N+1)`` is the maximum where N is the number of elements |
| in `x0`. |
| band : tuple |
| If set to a two-sequence containing the number of sub- and |
| super-diagonals within the band of the Jacobi matrix, the |
| Jacobi matrix is considered banded (only for ``jac=None``). |
| eps : float |
| A suitable step length for the forward-difference |
| approximation of the Jacobian (for ``jac=None``). If |
| `eps` is less than the machine precision, it is assumed |
| that the relative errors in the functions are of the order of |
| the machine precision. |
| factor : float |
| A parameter determining the initial step bound |
| (``factor * || diag * x||``). Should be in the interval |
| ``(0.1, 100)``. |
| diag : sequence |
| N positive entries that serve as a scale factors for the |
| variables. |
| |
| """ |
| _check_unknown_options(unknown_options) |
| epsfcn = eps |
|
|
| x0 = asarray(x0).flatten() |
| n = len(x0) |
| if not isinstance(args, tuple): |
| args = (args,) |
| shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n,)) |
| if epsfcn is None: |
| epsfcn = finfo(dtype).eps |
| Dfun = jac |
| if Dfun is None: |
| if band is None: |
| ml, mu = -10, -10 |
| else: |
| ml, mu = band[:2] |
| if maxfev == 0: |
| maxfev = 200 * (n + 1) |
| retval = _minpack._hybrd(func, x0, args, 1, xtol, maxfev, |
| ml, mu, epsfcn, factor, diag) |
| else: |
| _check_func('fsolve', 'fprime', Dfun, x0, args, n, (n, n)) |
| if (maxfev == 0): |
| maxfev = 100 * (n + 1) |
| retval = _minpack._hybrj(func, Dfun, x0, args, 1, |
| col_deriv, xtol, maxfev, factor, diag) |
|
|
| x, status = retval[0], retval[-1] |
|
|
| errors = {0: "Improper input parameters were entered.", |
| 1: "The solution converged.", |
| 2: "The number of calls to function has " |
| "reached maxfev = %d." % maxfev, |
| 3: f"xtol={xtol:f} is too small, no further improvement " |
| "in the approximate\n solution is possible.", |
| 4: "The iteration is not making good progress, as measured " |
| "by the \n improvement from the last five " |
| "Jacobian evaluations.", |
| 5: "The iteration is not making good progress, " |
| "as measured by the \n improvement from the last " |
| "ten iterations.", |
| 'unknown': "An error occurred."} |
|
|
| info = retval[1] |
| info['fun'] = info.pop('fvec') |
| sol = OptimizeResult(x=x, success=(status == 1), status=status, |
| method="hybr") |
| sol.update(info) |
| try: |
| sol['message'] = errors[status] |
| except KeyError: |
| sol['message'] = errors['unknown'] |
|
|
| return sol |
|
|
|
|
| LEASTSQ_SUCCESS = [1, 2, 3, 4] |
| LEASTSQ_FAILURE = [5, 6, 7, 8] |
|
|
|
|
| def leastsq(func, x0, args=(), Dfun=None, full_output=False, |
| col_deriv=False, ftol=1.49012e-8, xtol=1.49012e-8, |
| gtol=0.0, maxfev=0, epsfcn=None, factor=100, diag=None): |
| """ |
| Minimize the sum of squares of a set of equations. |
| |
| :: |
| |
| x = arg min(sum(func(y)**2,axis=0)) |
| y |
| |
| Parameters |
| ---------- |
| func : callable |
| Should take at least one (possibly length ``N`` vector) argument and |
| returns ``M`` floating point numbers. It must not return NaNs or |
| fitting might fail. ``M`` must be greater than or equal to ``N``. |
| x0 : ndarray |
| The starting estimate for the minimization. |
| args : tuple, optional |
| Any extra arguments to func are placed in this tuple. |
| Dfun : callable, optional |
| A function or method to compute the Jacobian of func with derivatives |
| across the rows. If this is None, the Jacobian will be estimated. |
| full_output : bool, optional |
| If ``True``, return all optional outputs (not just `x` and `ier`). |
| col_deriv : bool, optional |
| If ``True``, specify that the Jacobian function computes derivatives |
| down the columns (faster, because there is no transpose operation). |
| ftol : float, optional |
| Relative error desired in the sum of squares. |
| xtol : float, optional |
| Relative error desired in the approximate solution. |
| gtol : float, optional |
| Orthogonality desired between the function vector and the columns of |
| the Jacobian. |
| maxfev : int, optional |
| The maximum number of calls to the function. If `Dfun` is provided, |
| then the default `maxfev` is 100*(N+1) where N is the number of elements |
| in x0, otherwise the default `maxfev` is 200*(N+1). |
| epsfcn : float, optional |
| A variable used in determining a suitable step length for the forward- |
| difference approximation of the Jacobian (for Dfun=None). |
| Normally the actual step length will be sqrt(epsfcn)*x |
| If epsfcn is less than the machine precision, it is assumed that the |
| relative errors are of the order of the machine precision. |
| factor : float, optional |
| A parameter determining the initial step bound |
| (``factor * || diag * x||``). Should be in interval ``(0.1, 100)``. |
| diag : sequence, optional |
| N positive entries that serve as a scale factors for the variables. |
| |
| Returns |
| ------- |
| x : ndarray |
| The solution (or the result of the last iteration for an unsuccessful |
| call). |
| cov_x : ndarray |
| The inverse of the Hessian. `fjac` and `ipvt` are used to construct an |
| estimate of the Hessian. A value of None indicates a singular matrix, |
| which means the curvature in parameters `x` is numerically flat. To |
| obtain the covariance matrix of the parameters `x`, `cov_x` must be |
| multiplied by the variance of the residuals -- see curve_fit. Only |
| returned if `full_output` is ``True``. |
| infodict : dict |
| a dictionary of optional outputs with the keys: |
| |
| ``nfev`` |
| The number of function calls |
| ``fvec`` |
| The function evaluated at the output |
| ``fjac`` |
| A permutation of the R matrix of a QR |
| factorization of the final approximate |
| Jacobian matrix, stored column wise. |
| Together with ipvt, the covariance of the |
| estimate can be approximated. |
| ``ipvt`` |
| An integer array of length N which defines |
| a permutation matrix, p, such that |
| fjac*p = q*r, where r is upper triangular |
| with diagonal elements of nonincreasing |
| magnitude. Column j of p is column ipvt(j) |
| of the identity matrix. |
| ``qtf`` |
| The vector (transpose(q) * fvec). |
| |
| Only returned if `full_output` is ``True``. |
| mesg : str |
| A string message giving information about the cause of failure. |
| Only returned if `full_output` is ``True``. |
| ier : int |
| An integer flag. If it is equal to 1, 2, 3 or 4, the solution was |
| found. Otherwise, the solution was not found. In either case, the |
| optional output variable 'mesg' gives more information. |
| |
| See Also |
| -------- |
| least_squares : Newer interface to solve nonlinear least-squares problems |
| with bounds on the variables. See ``method='lm'`` in particular. |
| |
| Notes |
| ----- |
| "leastsq" is a wrapper around MINPACK's lmdif and lmder algorithms. |
| |
| cov_x is a Jacobian approximation to the Hessian of the least squares |
| objective function. |
| This approximation assumes that the objective function is based on the |
| difference between some observed target data (ydata) and a (non-linear) |
| function of the parameters `f(xdata, params)` :: |
| |
| func(params) = ydata - f(xdata, params) |
| |
| so that the objective function is :: |
| |
| min sum((ydata - f(xdata, params))**2, axis=0) |
| params |
| |
| The solution, `x`, is always a 1-D array, regardless of the shape of `x0`, |
| or whether `x0` is a scalar. |
| |
| Examples |
| -------- |
| >>> from scipy.optimize import leastsq |
| >>> def func(x): |
| ... return 2*(x-3)**2+1 |
| >>> leastsq(func, 0) |
| (array([2.99999999]), 1) |
| |
| """ |
| x0 = asarray(x0).flatten() |
| n = len(x0) |
| if not isinstance(args, tuple): |
| args = (args,) |
| shape, dtype = _check_func('leastsq', 'func', func, x0, args, n) |
| m = shape[0] |
|
|
| if n > m: |
| raise TypeError(f"Improper input: func input vector length N={n} must" |
| f" not exceed func output vector length M={m}") |
|
|
| if epsfcn is None: |
| epsfcn = finfo(dtype).eps |
|
|
| if Dfun is None: |
| if maxfev == 0: |
| maxfev = 200*(n + 1) |
| retval = _minpack._lmdif(func, x0, args, full_output, ftol, xtol, |
| gtol, maxfev, epsfcn, factor, diag) |
| else: |
| if col_deriv: |
| _check_func('leastsq', 'Dfun', Dfun, x0, args, n, (n, m)) |
| else: |
| _check_func('leastsq', 'Dfun', Dfun, x0, args, n, (m, n)) |
| if maxfev == 0: |
| maxfev = 100 * (n + 1) |
| retval = _minpack._lmder(func, Dfun, x0, args, full_output, |
| col_deriv, ftol, xtol, gtol, maxfev, |
| factor, diag) |
|
|
| errors = {0: ["Improper input parameters.", TypeError], |
| 1: ["Both actual and predicted relative reductions " |
| f"in the sum of squares\n are at most {ftol:f}", None], |
| 2: ["The relative error between two consecutive " |
| f"iterates is at most {xtol:f}", None], |
| 3: ["Both actual and predicted relative reductions in " |
| f"the sum of squares\n are at most {ftol:f} and the " |
| "relative error between two consecutive " |
| f"iterates is at \n most {xtol:f}", None], |
| 4: ["The cosine of the angle between func(x) and any " |
| f"column of the\n Jacobian is at most {gtol:f} in " |
| "absolute value", None], |
| 5: ["Number of calls to function has reached " |
| "maxfev = %d." % maxfev, ValueError], |
| 6: [f"ftol={ftol:f} is too small, no further reduction " |
| "in the sum of squares\n is possible.", |
| ValueError], |
| 7: [f"xtol={xtol:f} is too small, no further improvement in " |
| "the approximate\n solution is possible.", |
| ValueError], |
| 8: [f"gtol={gtol:f} is too small, func(x) is orthogonal to the " |
| "columns of\n the Jacobian to machine precision.", ValueError]} |
|
|
| |
| info = retval[-1] |
|
|
| if full_output: |
| cov_x = None |
| if info in LEASTSQ_SUCCESS: |
| |
| |
| |
| |
| |
| |
| |
| perm = retval[1]['ipvt'] |
| n = len(perm) |
| r = triu(transpose(retval[1]['fjac'])[:n, :]) |
| inv_triu = linalg.get_lapack_funcs('trtri', (r,)) |
| try: |
| |
| invR, trtri_info = inv_triu(r) |
| if trtri_info != 0: |
| raise LinAlgError(f'trtri returned info {trtri_info}') |
| invR[perm] = invR.copy() |
| cov_x = invR @ invR.T |
| except (LinAlgError, ValueError): |
| pass |
| return (retval[0], cov_x) + retval[1:-1] + (errors[info][0], info) |
| else: |
| if info in LEASTSQ_FAILURE: |
| warnings.warn(errors[info][0], RuntimeWarning, stacklevel=2) |
| elif info == 0: |
| raise errors[info][1](errors[info][0]) |
| return retval[0], info |
|
|
|
|
| def _lightweight_memoizer(f): |
| |
| |
| |
| def _memoized_func(params): |
| if _memoized_func.skip_lookup: |
| return f(params) |
|
|
| if np.all(_memoized_func.last_params == params): |
| return _memoized_func.last_val |
| elif _memoized_func.last_params is not None: |
| _memoized_func.skip_lookup = True |
|
|
| val = f(params) |
|
|
| if _memoized_func.last_params is None: |
| _memoized_func.last_params = np.copy(params) |
| _memoized_func.last_val = val |
|
|
| return val |
|
|
| _memoized_func.last_params = None |
| _memoized_func.last_val = None |
| _memoized_func.skip_lookup = False |
| return _memoized_func |
|
|
|
|
| def _wrap_func(func, xdata, ydata, transform): |
| if transform is None: |
| def func_wrapped(params): |
| return func(xdata, *params) - ydata |
| elif transform.size == 1 or transform.ndim == 1: |
| def func_wrapped(params): |
| return transform * (func(xdata, *params) - ydata) |
| else: |
| |
| |
| |
| |
| |
| |
| |
| |
| def func_wrapped(params): |
| return solve_triangular(transform, func(xdata, *params) - ydata, lower=True) |
| return func_wrapped |
|
|
|
|
| def _wrap_jac(jac, xdata, transform): |
| if transform is None: |
| def jac_wrapped(params): |
| return jac(xdata, *params) |
| elif transform.ndim == 1: |
| def jac_wrapped(params): |
| return transform[:, np.newaxis] * np.asarray(jac(xdata, *params)) |
| else: |
| def jac_wrapped(params): |
| return solve_triangular(transform, |
| np.asarray(jac(xdata, *params)), |
| lower=True) |
| return jac_wrapped |
|
|
|
|
| def _initialize_feasible(lb, ub): |
| p0 = np.ones_like(lb) |
| lb_finite = np.isfinite(lb) |
| ub_finite = np.isfinite(ub) |
|
|
| mask = lb_finite & ub_finite |
| p0[mask] = 0.5 * (lb[mask] + ub[mask]) |
|
|
| mask = lb_finite & ~ub_finite |
| p0[mask] = lb[mask] + 1 |
|
|
| mask = ~lb_finite & ub_finite |
| p0[mask] = ub[mask] - 1 |
|
|
| return p0 |
|
|
|
|
| def curve_fit(f, xdata, ydata, p0=None, sigma=None, absolute_sigma=False, |
| check_finite=None, bounds=(-np.inf, np.inf), method=None, |
| jac=None, *, full_output=False, nan_policy=None, |
| **kwargs): |
| """ |
| Use non-linear least squares to fit a function, f, to data. |
| |
| Assumes ``ydata = f(xdata, *params) + eps``. |
| |
| Parameters |
| ---------- |
| f : callable |
| The model function, f(x, ...). It must take the independent |
| variable as the first argument and the parameters to fit as |
| separate remaining arguments. |
| xdata : array_like |
| The independent variable where the data is measured. |
| Should usually be an M-length sequence or an (k,M)-shaped array for |
| functions with k predictors, and each element should be float |
| convertible if it is an array like object. |
| ydata : array_like |
| The dependent data, a length M array - nominally ``f(xdata, ...)``. |
| p0 : array_like, optional |
| Initial guess for the parameters (length N). If None, then the |
| initial values will all be 1 (if the number of parameters for the |
| function can be determined using introspection, otherwise a |
| ValueError is raised). |
| sigma : None or scalar or M-length sequence or MxM array, optional |
| Determines the uncertainty in `ydata`. If we define residuals as |
| ``r = ydata - f(xdata, *popt)``, then the interpretation of `sigma` |
| depends on its number of dimensions: |
| |
| - A scalar or 1-D `sigma` should contain values of standard deviations of |
| errors in `ydata`. In this case, the optimized function is |
| ``chisq = sum((r / sigma) ** 2)``. |
| |
| - A 2-D `sigma` should contain the covariance matrix of |
| errors in `ydata`. In this case, the optimized function is |
| ``chisq = r.T @ inv(sigma) @ r``. |
| |
| .. versionadded:: 0.19 |
| |
| None (default) is equivalent of 1-D `sigma` filled with ones. |
| absolute_sigma : bool, optional |
| If True, `sigma` is used in an absolute sense and the estimated parameter |
| covariance `pcov` reflects these absolute values. |
| |
| If False (default), only the relative magnitudes of the `sigma` values matter. |
| The returned parameter covariance matrix `pcov` is based on scaling |
| `sigma` by a constant factor. This constant is set by demanding that the |
| reduced `chisq` for the optimal parameters `popt` when using the |
| *scaled* `sigma` equals unity. In other words, `sigma` is scaled to |
| match the sample variance of the residuals after the fit. Default is False. |
| Mathematically, |
| ``pcov(absolute_sigma=False) = pcov(absolute_sigma=True) * chisq(popt)/(M-N)`` |
| check_finite : bool, optional |
| If True, check that the input arrays do not contain nans of infs, |
| and raise a ValueError if they do. Setting this parameter to |
| False may silently produce nonsensical results if the input arrays |
| do contain nans. Default is True if `nan_policy` is not specified |
| explicitly and False otherwise. |
| bounds : 2-tuple of array_like or `Bounds`, optional |
| Lower and upper bounds on parameters. Defaults to no bounds. |
| There are two ways to specify the bounds: |
| |
| - Instance of `Bounds` class. |
| |
| - 2-tuple of array_like: Each element of the tuple must be either |
| an array with the length equal to the number of parameters, or a |
| scalar (in which case the bound is taken to be the same for all |
| parameters). Use ``np.inf`` with an appropriate sign to disable |
| bounds on all or some parameters. |
| |
| method : {'lm', 'trf', 'dogbox'}, optional |
| Method to use for optimization. See `least_squares` for more details. |
| Default is 'lm' for unconstrained problems and 'trf' if `bounds` are |
| provided. The method 'lm' won't work when the number of observations |
| is less than the number of variables, use 'trf' or 'dogbox' in this |
| case. |
| |
| .. versionadded:: 0.17 |
| jac : callable, string or None, optional |
| Function with signature ``jac(x, ...)`` which computes the Jacobian |
| matrix of the model function with respect to parameters as a dense |
| array_like structure. It will be scaled according to provided `sigma`. |
| If None (default), the Jacobian will be estimated numerically. |
| String keywords for 'trf' and 'dogbox' methods can be used to select |
| a finite difference scheme, see `least_squares`. |
| |
| .. versionadded:: 0.18 |
| full_output : boolean, optional |
| If True, this function returns additional information: `infodict`, |
| `mesg`, and `ier`. |
| |
| .. versionadded:: 1.9 |
| nan_policy : {'raise', 'omit', None}, optional |
| Defines how to handle when input contains nan. |
| The following options are available (default is None): |
| |
| * 'raise': throws an error |
| * 'omit': performs the calculations ignoring nan values |
| * None: no special handling of NaNs is performed |
| (except what is done by check_finite); the behavior when NaNs |
| are present is implementation-dependent and may change. |
| |
| Note that if this value is specified explicitly (not None), |
| `check_finite` will be set as False. |
| |
| .. versionadded:: 1.11 |
| **kwargs |
| Keyword arguments passed to `leastsq` for ``method='lm'`` or |
| `least_squares` otherwise. |
| |
| Returns |
| ------- |
| popt : array |
| Optimal values for the parameters so that the sum of the squared |
| residuals of ``f(xdata, *popt) - ydata`` is minimized. |
| pcov : 2-D array |
| The estimated approximate covariance of popt. The diagonals provide |
| the variance of the parameter estimate. To compute one standard |
| deviation errors on the parameters, use |
| ``perr = np.sqrt(np.diag(pcov))``. Note that the relationship between |
| `cov` and parameter error estimates is derived based on a linear |
| approximation to the model function around the optimum [1]_. |
| When this approximation becomes inaccurate, `cov` may not provide an |
| accurate measure of uncertainty. |
| |
| How the `sigma` parameter affects the estimated covariance |
| depends on `absolute_sigma` argument, as described above. |
| |
| If the Jacobian matrix at the solution doesn't have a full rank, then |
| 'lm' method returns a matrix filled with ``np.inf``, on the other hand |
| 'trf' and 'dogbox' methods use Moore-Penrose pseudoinverse to compute |
| the covariance matrix. Covariance matrices with large condition numbers |
| (e.g. computed with `numpy.linalg.cond`) may indicate that results are |
| unreliable. |
| infodict : dict (returned only if `full_output` is True) |
| a dictionary of optional outputs with the keys: |
| |
| ``nfev`` |
| The number of function calls. Methods 'trf' and 'dogbox' do not |
| count function calls for numerical Jacobian approximation, |
| as opposed to 'lm' method. |
| ``fvec`` |
| The residual values evaluated at the solution, for a 1-D `sigma` |
| this is ``(f(x, *popt) - ydata)/sigma``. |
| ``fjac`` |
| A permutation of the R matrix of a QR |
| factorization of the final approximate |
| Jacobian matrix, stored column wise. |
| Together with ipvt, the covariance of the |
| estimate can be approximated. |
| Method 'lm' only provides this information. |
| ``ipvt`` |
| An integer array of length N which defines |
| a permutation matrix, p, such that |
| fjac*p = q*r, where r is upper triangular |
| with diagonal elements of nonincreasing |
| magnitude. Column j of p is column ipvt(j) |
| of the identity matrix. |
| Method 'lm' only provides this information. |
| ``qtf`` |
| The vector (transpose(q) * fvec). |
| Method 'lm' only provides this information. |
| |
| .. versionadded:: 1.9 |
| mesg : str (returned only if `full_output` is True) |
| A string message giving information about the solution. |
| |
| .. versionadded:: 1.9 |
| ier : int (returned only if `full_output` is True) |
| An integer flag. If it is equal to 1, 2, 3 or 4, the solution was |
| found. Otherwise, the solution was not found. In either case, the |
| optional output variable `mesg` gives more information. |
| |
| .. versionadded:: 1.9 |
| |
| Raises |
| ------ |
| ValueError |
| if either `ydata` or `xdata` contain NaNs, or if incompatible options |
| are used. |
| |
| RuntimeError |
| if the least-squares minimization fails. |
| |
| OptimizeWarning |
| if covariance of the parameters can not be estimated. |
| |
| See Also |
| -------- |
| least_squares : Minimize the sum of squares of nonlinear functions. |
| scipy.stats.linregress : Calculate a linear least squares regression for |
| two sets of measurements. |
| |
| Notes |
| ----- |
| Users should ensure that inputs `xdata`, `ydata`, and the output of `f` |
| are ``float64``, or else the optimization may return incorrect results. |
| |
| With ``method='lm'``, the algorithm uses the Levenberg-Marquardt algorithm |
| through `leastsq`. Note that this algorithm can only deal with |
| unconstrained problems. |
| |
| Box constraints can be handled by methods 'trf' and 'dogbox'. Refer to |
| the docstring of `least_squares` for more information. |
| |
| Parameters to be fitted must have similar scale. Differences of multiple |
| orders of magnitude can lead to incorrect results. For the 'trf' and |
| 'dogbox' methods, the `x_scale` keyword argument can be used to scale |
| the parameters. |
| |
| References |
| ---------- |
| .. [1] K. Vugrin et al. Confidence region estimation techniques for nonlinear |
| regression in groundwater flow: Three case studies. Water Resources |
| Research, Vol. 43, W03423, :doi:`10.1029/2005WR004804` |
| |
| Examples |
| -------- |
| >>> import numpy as np |
| >>> import matplotlib.pyplot as plt |
| >>> from scipy.optimize import curve_fit |
| |
| >>> def func(x, a, b, c): |
| ... return a * np.exp(-b * x) + c |
| |
| Define the data to be fit with some noise: |
| |
| >>> xdata = np.linspace(0, 4, 50) |
| >>> y = func(xdata, 2.5, 1.3, 0.5) |
| >>> rng = np.random.default_rng() |
| >>> y_noise = 0.2 * rng.normal(size=xdata.size) |
| >>> ydata = y + y_noise |
| >>> plt.plot(xdata, ydata, 'b-', label='data') |
| |
| Fit for the parameters a, b, c of the function `func`: |
| |
| >>> popt, pcov = curve_fit(func, xdata, ydata) |
| >>> popt |
| array([2.56274217, 1.37268521, 0.47427475]) |
| >>> plt.plot(xdata, func(xdata, *popt), 'r-', |
| ... label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)) |
| |
| Constrain the optimization to the region of ``0 <= a <= 3``, |
| ``0 <= b <= 1`` and ``0 <= c <= 0.5``: |
| |
| >>> popt, pcov = curve_fit(func, xdata, ydata, bounds=(0, [3., 1., 0.5])) |
| >>> popt |
| array([2.43736712, 1. , 0.34463856]) |
| >>> plt.plot(xdata, func(xdata, *popt), 'g--', |
| ... label='fit: a=%5.3f, b=%5.3f, c=%5.3f' % tuple(popt)) |
| |
| >>> plt.xlabel('x') |
| >>> plt.ylabel('y') |
| >>> plt.legend() |
| >>> plt.show() |
| |
| For reliable results, the model `func` should not be overparametrized; |
| redundant parameters can cause unreliable covariance matrices and, in some |
| cases, poorer quality fits. As a quick check of whether the model may be |
| overparameterized, calculate the condition number of the covariance matrix: |
| |
| >>> np.linalg.cond(pcov) |
| 34.571092161547405 # may vary |
| |
| The value is small, so it does not raise much concern. If, however, we were |
| to add a fourth parameter ``d`` to `func` with the same effect as ``a``: |
| |
| >>> def func2(x, a, b, c, d): |
| ... return a * d * np.exp(-b * x) + c # a and d are redundant |
| >>> popt, pcov = curve_fit(func2, xdata, ydata) |
| >>> np.linalg.cond(pcov) |
| 1.13250718925596e+32 # may vary |
| |
| Such a large value is cause for concern. The diagonal elements of the |
| covariance matrix, which is related to uncertainty of the fit, gives more |
| information: |
| |
| >>> np.diag(pcov) |
| array([1.48814742e+29, 3.78596560e-02, 5.39253738e-03, 2.76417220e+28]) # may vary |
| |
| Note that the first and last terms are much larger than the other elements, |
| suggesting that the optimal values of these parameters are ambiguous and |
| that only one of these parameters is needed in the model. |
| |
| If the optimal parameters of `f` differ by multiple orders of magnitude, the |
| resulting fit can be inaccurate. Sometimes, `curve_fit` can fail to find any |
| results: |
| |
| >>> ydata = func(xdata, 500000, 0.01, 15) |
| >>> try: |
| ... popt, pcov = curve_fit(func, xdata, ydata, method = 'trf') |
| ... except RuntimeError as e: |
| ... print(e) |
| Optimal parameters not found: The maximum number of function evaluations is |
| exceeded. |
| |
| If parameter scale is roughly known beforehand, it can be defined in |
| `x_scale` argument: |
| |
| >>> popt, pcov = curve_fit(func, xdata, ydata, method = 'trf', |
| ... x_scale = [1000, 1, 1]) |
| >>> popt |
| array([5.00000000e+05, 1.00000000e-02, 1.49999999e+01]) |
| """ |
| if p0 is None: |
| |
| sig = _getfullargspec(f) |
| args = sig.args |
| if len(args) < 2: |
| raise ValueError("Unable to determine number of fit parameters.") |
| n = len(args) - 1 |
| else: |
| p0 = np.atleast_1d(p0) |
| n = p0.size |
|
|
| if isinstance(bounds, Bounds): |
| lb, ub = bounds.lb, bounds.ub |
| else: |
| lb, ub = prepare_bounds(bounds, n) |
| if p0 is None: |
| p0 = _initialize_feasible(lb, ub) |
|
|
| bounded_problem = np.any((lb > -np.inf) | (ub < np.inf)) |
| if method is None: |
| if bounded_problem: |
| method = 'trf' |
| else: |
| method = 'lm' |
|
|
| if method == 'lm' and bounded_problem: |
| raise ValueError("Method 'lm' only works for unconstrained problems. " |
| "Use 'trf' or 'dogbox' instead.") |
|
|
| if check_finite is None: |
| check_finite = True if nan_policy is None else False |
|
|
| |
| if check_finite: |
| ydata = np.asarray_chkfinite(ydata, float) |
| else: |
| ydata = np.asarray(ydata, float) |
|
|
| if isinstance(xdata, (list, tuple, np.ndarray)): |
| |
| |
| if check_finite: |
| xdata = np.asarray_chkfinite(xdata, float) |
| else: |
| xdata = np.asarray(xdata, float) |
|
|
| if ydata.size == 0: |
| raise ValueError("`ydata` must not be empty!") |
|
|
| |
| |
| if not check_finite and nan_policy is not None: |
| if nan_policy == "propagate": |
| raise ValueError("`nan_policy='propagate'` is not supported " |
| "by this function.") |
|
|
| policies = [None, 'raise', 'omit'] |
| x_contains_nan, nan_policy = _contains_nan(xdata, nan_policy, |
| policies=policies) |
| y_contains_nan, nan_policy = _contains_nan(ydata, nan_policy, |
| policies=policies) |
|
|
| if (x_contains_nan or y_contains_nan) and nan_policy == 'omit': |
| |
| has_nan = np.isnan(xdata) |
| has_nan = has_nan.any(axis=tuple(range(has_nan.ndim-1))) |
| has_nan |= np.isnan(ydata) |
|
|
| xdata = xdata[..., ~has_nan] |
| ydata = ydata[~has_nan] |
|
|
| |
| if sigma is not None: |
| sigma = np.asarray(sigma) |
| if sigma.ndim == 1: |
| sigma = sigma[~has_nan] |
| elif sigma.ndim == 2: |
| sigma = sigma[~has_nan, :] |
| sigma = sigma[:, ~has_nan] |
|
|
| |
| if sigma is not None: |
| sigma = np.asarray(sigma) |
|
|
| |
| if sigma.size == 1 or sigma.shape == (ydata.size,): |
| transform = 1.0 / sigma |
| |
| |
| elif sigma.shape == (ydata.size, ydata.size): |
| try: |
| |
| transform = cholesky(sigma, lower=True) |
| except LinAlgError as e: |
| raise ValueError("`sigma` must be positive definite.") from e |
| else: |
| raise ValueError("`sigma` has incorrect shape.") |
| else: |
| transform = None |
|
|
| func = _lightweight_memoizer(_wrap_func(f, xdata, ydata, transform)) |
|
|
| if callable(jac): |
| jac = _lightweight_memoizer(_wrap_jac(jac, xdata, transform)) |
| elif jac is None and method != 'lm': |
| jac = '2-point' |
|
|
| if 'args' in kwargs: |
| |
| |
| |
| raise ValueError("'args' is not a supported keyword argument.") |
|
|
| if method == 'lm': |
| |
| if ydata.size != 1 and n > ydata.size: |
| raise TypeError(f"The number of func parameters={n} must not" |
| f" exceed the number of data points={ydata.size}") |
| res = leastsq(func, p0, Dfun=jac, full_output=1, **kwargs) |
| popt, pcov, infodict, errmsg, ier = res |
| ysize = len(infodict['fvec']) |
| cost = np.sum(infodict['fvec'] ** 2) |
| if ier not in [1, 2, 3, 4]: |
| raise RuntimeError("Optimal parameters not found: " + errmsg) |
| else: |
| |
| if 'max_nfev' not in kwargs: |
| kwargs['max_nfev'] = kwargs.pop('maxfev', None) |
|
|
| res = least_squares(func, p0, jac=jac, bounds=bounds, method=method, |
| **kwargs) |
|
|
| if not res.success: |
| raise RuntimeError("Optimal parameters not found: " + res.message) |
|
|
| infodict = dict(nfev=res.nfev, fvec=res.fun) |
| ier = res.status |
| errmsg = res.message |
|
|
| ysize = len(res.fun) |
| cost = 2 * res.cost |
| popt = res.x |
|
|
| |
| _, s, VT = svd(res.jac, full_matrices=False) |
| threshold = np.finfo(float).eps * max(res.jac.shape) * s[0] |
| s = s[s > threshold] |
| VT = VT[:s.size] |
| pcov = np.dot(VT.T / s**2, VT) |
|
|
| warn_cov = False |
| if pcov is None or np.isnan(pcov).any(): |
| |
| pcov = zeros((len(popt), len(popt)), dtype=float) |
| pcov.fill(inf) |
| warn_cov = True |
| elif not absolute_sigma: |
| if ysize > p0.size: |
| s_sq = cost / (ysize - p0.size) |
| pcov = pcov * s_sq |
| else: |
| pcov.fill(inf) |
| warn_cov = True |
|
|
| if warn_cov: |
| warnings.warn('Covariance of the parameters could not be estimated', |
| category=OptimizeWarning, stacklevel=2) |
|
|
| if full_output: |
| return popt, pcov, infodict, errmsg, ier |
| else: |
| return popt, pcov |
|
|
|
|
| def check_gradient(fcn, Dfcn, x0, args=(), col_deriv=0): |
| """Perform a simple check on the gradient for correctness. |
| |
| """ |
|
|
| x = atleast_1d(x0) |
| n = len(x) |
| x = x.reshape((n,)) |
| fvec = atleast_1d(fcn(x, *args)) |
| m = len(fvec) |
| fvec = fvec.reshape((m,)) |
| ldfjac = m |
| fjac = atleast_1d(Dfcn(x, *args)) |
| fjac = fjac.reshape((m, n)) |
| if col_deriv == 0: |
| fjac = transpose(fjac) |
|
|
| xp = zeros((n,), float) |
| err = zeros((m,), float) |
| fvecp = None |
| _minpack._chkder(m, n, x, fvec, fjac, ldfjac, xp, fvecp, 1, err) |
|
|
| fvecp = atleast_1d(fcn(xp, *args)) |
| fvecp = fvecp.reshape((m,)) |
| _minpack._chkder(m, n, x, fvec, fjac, ldfjac, xp, fvecp, 2, err) |
|
|
| good = (prod(greater(err, 0.5), axis=0)) |
|
|
| return (good, err) |
|
|
|
|
| def _del2(p0, p1, d): |
| return p0 - np.square(p1 - p0) / d |
|
|
|
|
| def _relerr(actual, desired): |
| return (actual - desired) / desired |
|
|
|
|
| def _fixed_point_helper(func, x0, args, xtol, maxiter, use_accel): |
| p0 = x0 |
| for i in range(maxiter): |
| p1 = func(p0, *args) |
| if use_accel: |
| p2 = func(p1, *args) |
| d = p2 - 2.0 * p1 + p0 |
| p = _lazywhere(d != 0, (p0, p1, d), f=_del2, fillvalue=p2) |
| else: |
| p = p1 |
| relerr = _lazywhere(p0 != 0, (p, p0), f=_relerr, fillvalue=p) |
| if np.all(np.abs(relerr) < xtol): |
| return p |
| p0 = p |
| msg = "Failed to converge after %d iterations, value is %s" % (maxiter, p) |
| raise RuntimeError(msg) |
|
|
|
|
| def fixed_point(func, x0, args=(), xtol=1e-8, maxiter=500, method='del2'): |
| """ |
| Find a fixed point of the function. |
| |
| Given a function of one or more variables and a starting point, find a |
| fixed point of the function: i.e., where ``func(x0) == x0``. |
| |
| Parameters |
| ---------- |
| func : function |
| Function to evaluate. |
| x0 : array_like |
| Fixed point of function. |
| args : tuple, optional |
| Extra arguments to `func`. |
| xtol : float, optional |
| Convergence tolerance, defaults to 1e-08. |
| maxiter : int, optional |
| Maximum number of iterations, defaults to 500. |
| method : {"del2", "iteration"}, optional |
| Method of finding the fixed-point, defaults to "del2", |
| which uses Steffensen's Method with Aitken's ``Del^2`` |
| convergence acceleration [1]_. The "iteration" method simply iterates |
| the function until convergence is detected, without attempting to |
| accelerate the convergence. |
| |
| References |
| ---------- |
| .. [1] Burden, Faires, "Numerical Analysis", 5th edition, pg. 80 |
| |
| Examples |
| -------- |
| >>> import numpy as np |
| >>> from scipy import optimize |
| >>> def func(x, c1, c2): |
| ... return np.sqrt(c1/(x+c2)) |
| >>> c1 = np.array([10,12.]) |
| >>> c2 = np.array([3, 5.]) |
| >>> optimize.fixed_point(func, [1.2, 1.3], args=(c1,c2)) |
| array([ 1.4920333 , 1.37228132]) |
| |
| """ |
| use_accel = {'del2': True, 'iteration': False}[method] |
| x0 = _asarray_validated(x0, as_inexact=True) |
| return _fixed_point_helper(func, x0, args, xtol, maxiter, use_accel) |
|
|