1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444 |
- """
- Functions to operate on polynomials.
- """
- __all__ = ['poly', 'roots', 'polyint', 'polyder', 'polyadd',
- 'polysub', 'polymul', 'polydiv', 'polyval', 'poly1d',
- 'polyfit', 'RankWarning']
- import functools
- import re
- import warnings
- import numpy.core.numeric as NX
- from numpy.core import (isscalar, abs, finfo, atleast_1d, hstack, dot, array,
- ones)
- from numpy.core import overrides
- from numpy.core.overrides import set_module
- from numpy.lib.twodim_base import diag, vander
- from numpy.lib.function_base import trim_zeros
- from numpy.lib.type_check import iscomplex, real, imag, mintypecode
- from numpy.linalg import eigvals, lstsq, inv
- array_function_dispatch = functools.partial(
- overrides.array_function_dispatch, module='numpy')
- @set_module('numpy')
- class RankWarning(UserWarning):
- """
- Issued by `polyfit` when the Vandermonde matrix is rank deficient.
- For more information, a way to suppress the warning, and an example of
- `RankWarning` being issued, see `polyfit`.
- """
- pass
- def _poly_dispatcher(seq_of_zeros):
- return seq_of_zeros
- @array_function_dispatch(_poly_dispatcher)
- def poly(seq_of_zeros):
- """
- Find the coefficients of a polynomial with the given sequence of roots.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- Returns the coefficients of the polynomial whose leading coefficient
- is one for the given sequence of zeros (multiple roots must be included
- in the sequence as many times as their multiplicity; see Examples).
- A square matrix (or array, which will be treated as a matrix) can also
- be given, in which case the coefficients of the characteristic polynomial
- of the matrix are returned.
- Parameters
- ----------
- seq_of_zeros : array_like, shape (N,) or (N, N)
- A sequence of polynomial roots, or a square array or matrix object.
- Returns
- -------
- c : ndarray
- 1D array of polynomial coefficients from highest to lowest degree:
- ``c[0] * x**(N) + c[1] * x**(N-1) + ... + c[N-1] * x + c[N]``
- where c[0] always equals 1.
- Raises
- ------
- ValueError
- If input is the wrong shape (the input must be a 1-D or square
- 2-D array).
- See Also
- --------
- polyval : Compute polynomial values.
- roots : Return the roots of a polynomial.
- polyfit : Least squares polynomial fit.
- poly1d : A one-dimensional polynomial class.
- Notes
- -----
- Specifying the roots of a polynomial still leaves one degree of
- freedom, typically represented by an undetermined leading
- coefficient. [1]_ In the case of this function, that coefficient -
- the first one in the returned array - is always taken as one. (If
- for some reason you have one other point, the only automatic way
- presently to leverage that information is to use ``polyfit``.)
- The characteristic polynomial, :math:`p_a(t)`, of an `n`-by-`n`
- matrix **A** is given by
- :math:`p_a(t) = \\mathrm{det}(t\\, \\mathbf{I} - \\mathbf{A})`,
- where **I** is the `n`-by-`n` identity matrix. [2]_
- References
- ----------
- .. [1] M. Sullivan and M. Sullivan, III, "Algebra and Trignometry,
- Enhanced With Graphing Utilities," Prentice-Hall, pg. 318, 1996.
- .. [2] G. Strang, "Linear Algebra and Its Applications, 2nd Edition,"
- Academic Press, pg. 182, 1980.
- Examples
- --------
- Given a sequence of a polynomial's zeros:
- >>> np.poly((0, 0, 0)) # Multiple root example
- array([1., 0., 0., 0.])
- The line above represents z**3 + 0*z**2 + 0*z + 0.
- >>> np.poly((-1./2, 0, 1./2))
- array([ 1. , 0. , -0.25, 0. ])
- The line above represents z**3 - z/4
- >>> np.poly((np.random.random(1)[0], 0, np.random.random(1)[0]))
- array([ 1. , -0.77086955, 0.08618131, 0. ]) # random
- Given a square array object:
- >>> P = np.array([[0, 1./3], [-1./2, 0]])
- >>> np.poly(P)
- array([1. , 0. , 0.16666667])
- Note how in all cases the leading coefficient is always 1.
- """
- seq_of_zeros = atleast_1d(seq_of_zeros)
- sh = seq_of_zeros.shape
- if len(sh) == 2 and sh[0] == sh[1] and sh[0] != 0:
- seq_of_zeros = eigvals(seq_of_zeros)
- elif len(sh) == 1:
- dt = seq_of_zeros.dtype
- # Let object arrays slip through, e.g. for arbitrary precision
- if dt != object:
- seq_of_zeros = seq_of_zeros.astype(mintypecode(dt.char))
- else:
- raise ValueError("input must be 1d or non-empty square 2d array.")
- if len(seq_of_zeros) == 0:
- return 1.0
- dt = seq_of_zeros.dtype
- a = ones((1,), dtype=dt)
- for k in range(len(seq_of_zeros)):
- a = NX.convolve(a, array([1, -seq_of_zeros[k]], dtype=dt),
- mode='full')
- if issubclass(a.dtype.type, NX.complexfloating):
- # if complex roots are all complex conjugates, the roots are real.
- roots = NX.asarray(seq_of_zeros, complex)
- if NX.all(NX.sort(roots) == NX.sort(roots.conjugate())):
- a = a.real.copy()
- return a
- def _roots_dispatcher(p):
- return p
- @array_function_dispatch(_roots_dispatcher)
- def roots(p):
- """
- Return the roots of a polynomial with coefficients given in p.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- The values in the rank-1 array `p` are coefficients of a polynomial.
- If the length of `p` is n+1 then the polynomial is described by::
- p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n]
- Parameters
- ----------
- p : array_like
- Rank-1 array of polynomial coefficients.
- Returns
- -------
- out : ndarray
- An array containing the roots of the polynomial.
- Raises
- ------
- ValueError
- When `p` cannot be converted to a rank-1 array.
- See also
- --------
- poly : Find the coefficients of a polynomial with a given sequence
- of roots.
- polyval : Compute polynomial values.
- polyfit : Least squares polynomial fit.
- poly1d : A one-dimensional polynomial class.
- Notes
- -----
- The algorithm relies on computing the eigenvalues of the
- companion matrix [1]_.
- References
- ----------
- .. [1] R. A. Horn & C. R. Johnson, *Matrix Analysis*. Cambridge, UK:
- Cambridge University Press, 1999, pp. 146-7.
- Examples
- --------
- >>> coeff = [3.2, 2, 1]
- >>> np.roots(coeff)
- array([-0.3125+0.46351241j, -0.3125-0.46351241j])
- """
- # If input is scalar, this makes it an array
- p = atleast_1d(p)
- if p.ndim != 1:
- raise ValueError("Input must be a rank-1 array.")
- # find non-zero array entries
- non_zero = NX.nonzero(NX.ravel(p))[0]
- # Return an empty array if polynomial is all zeros
- if len(non_zero) == 0:
- return NX.array([])
- # find the number of trailing zeros -- this is the number of roots at 0.
- trailing_zeros = len(p) - non_zero[-1] - 1
- # strip leading and trailing zeros
- p = p[int(non_zero[0]):int(non_zero[-1])+1]
- # casting: if incoming array isn't floating point, make it floating point.
- if not issubclass(p.dtype.type, (NX.floating, NX.complexfloating)):
- p = p.astype(float)
- N = len(p)
- if N > 1:
- # build companion matrix and find its eigenvalues (the roots)
- A = diag(NX.ones((N-2,), p.dtype), -1)
- A[0,:] = -p[1:] / p[0]
- roots = eigvals(A)
- else:
- roots = NX.array([])
- # tack any zeros onto the back of the array
- roots = hstack((roots, NX.zeros(trailing_zeros, roots.dtype)))
- return roots
- def _polyint_dispatcher(p, m=None, k=None):
- return (p,)
- @array_function_dispatch(_polyint_dispatcher)
- def polyint(p, m=1, k=None):
- """
- Return an antiderivative (indefinite integral) of a polynomial.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- The returned order `m` antiderivative `P` of polynomial `p` satisfies
- :math:`\\frac{d^m}{dx^m}P(x) = p(x)` and is defined up to `m - 1`
- integration constants `k`. The constants determine the low-order
- polynomial part
- .. math:: \\frac{k_{m-1}}{0!} x^0 + \\ldots + \\frac{k_0}{(m-1)!}x^{m-1}
- of `P` so that :math:`P^{(j)}(0) = k_{m-j-1}`.
- Parameters
- ----------
- p : array_like or poly1d
- Polynomial to integrate.
- A sequence is interpreted as polynomial coefficients, see `poly1d`.
- m : int, optional
- Order of the antiderivative. (Default: 1)
- k : list of `m` scalars or scalar, optional
- Integration constants. They are given in the order of integration:
- those corresponding to highest-order terms come first.
- If ``None`` (default), all constants are assumed to be zero.
- If `m = 1`, a single scalar can be given instead of a list.
- See Also
- --------
- polyder : derivative of a polynomial
- poly1d.integ : equivalent method
- Examples
- --------
- The defining property of the antiderivative:
- >>> p = np.poly1d([1,1,1])
- >>> P = np.polyint(p)
- >>> P
- poly1d([ 0.33333333, 0.5 , 1. , 0. ]) # may vary
- >>> np.polyder(P) == p
- True
- The integration constants default to zero, but can be specified:
- >>> P = np.polyint(p, 3)
- >>> P(0)
- 0.0
- >>> np.polyder(P)(0)
- 0.0
- >>> np.polyder(P, 2)(0)
- 0.0
- >>> P = np.polyint(p, 3, k=[6,5,3])
- >>> P
- poly1d([ 0.01666667, 0.04166667, 0.16666667, 3. , 5. , 3. ]) # may vary
- Note that 3 = 6 / 2!, and that the constants are given in the order of
- integrations. Constant of the highest-order polynomial term comes first:
- >>> np.polyder(P, 2)(0)
- 6.0
- >>> np.polyder(P, 1)(0)
- 5.0
- >>> P(0)
- 3.0
- """
- m = int(m)
- if m < 0:
- raise ValueError("Order of integral must be positive (see polyder)")
- if k is None:
- k = NX.zeros(m, float)
- k = atleast_1d(k)
- if len(k) == 1 and m > 1:
- k = k[0]*NX.ones(m, float)
- if len(k) < m:
- raise ValueError(
- "k must be a scalar or a rank-1 array of length 1 or >m.")
- truepoly = isinstance(p, poly1d)
- p = NX.asarray(p)
- if m == 0:
- if truepoly:
- return poly1d(p)
- return p
- else:
- # Note: this must work also with object and integer arrays
- y = NX.concatenate((p.__truediv__(NX.arange(len(p), 0, -1)), [k[0]]))
- val = polyint(y, m - 1, k=k[1:])
- if truepoly:
- return poly1d(val)
- return val
- def _polyder_dispatcher(p, m=None):
- return (p,)
- @array_function_dispatch(_polyder_dispatcher)
- def polyder(p, m=1):
- """
- Return the derivative of the specified order of a polynomial.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- Parameters
- ----------
- p : poly1d or sequence
- Polynomial to differentiate.
- A sequence is interpreted as polynomial coefficients, see `poly1d`.
- m : int, optional
- Order of differentiation (default: 1)
- Returns
- -------
- der : poly1d
- A new polynomial representing the derivative.
- See Also
- --------
- polyint : Anti-derivative of a polynomial.
- poly1d : Class for one-dimensional polynomials.
- Examples
- --------
- The derivative of the polynomial :math:`x^3 + x^2 + x^1 + 1` is:
- >>> p = np.poly1d([1,1,1,1])
- >>> p2 = np.polyder(p)
- >>> p2
- poly1d([3, 2, 1])
- which evaluates to:
- >>> p2(2.)
- 17.0
- We can verify this, approximating the derivative with
- ``(f(x + h) - f(x))/h``:
- >>> (p(2. + 0.001) - p(2.)) / 0.001
- 17.007000999997857
- The fourth-order derivative of a 3rd-order polynomial is zero:
- >>> np.polyder(p, 2)
- poly1d([6, 2])
- >>> np.polyder(p, 3)
- poly1d([6])
- >>> np.polyder(p, 4)
- poly1d([0])
- """
- m = int(m)
- if m < 0:
- raise ValueError("Order of derivative must be positive (see polyint)")
- truepoly = isinstance(p, poly1d)
- p = NX.asarray(p)
- n = len(p) - 1
- y = p[:-1] * NX.arange(n, 0, -1)
- if m == 0:
- val = p
- else:
- val = polyder(y, m - 1)
- if truepoly:
- val = poly1d(val)
- return val
- def _polyfit_dispatcher(x, y, deg, rcond=None, full=None, w=None, cov=None):
- return (x, y, w)
- @array_function_dispatch(_polyfit_dispatcher)
- def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False):
- """
- Least squares polynomial fit.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- Fit a polynomial ``p(x) = p[0] * x**deg + ... + p[deg]`` of degree `deg`
- to points `(x, y)`. Returns a vector of coefficients `p` that minimises
- the squared error in the order `deg`, `deg-1`, ... `0`.
- The `Polynomial.fit <numpy.polynomial.polynomial.Polynomial.fit>` class
- method is recommended for new code as it is more stable numerically. See
- the documentation of the method for more information.
- Parameters
- ----------
- x : array_like, shape (M,)
- x-coordinates of the M sample points ``(x[i], y[i])``.
- y : array_like, shape (M,) or (M, K)
- y-coordinates of the sample points. Several data sets of sample
- points sharing the same x-coordinates can be fitted at once by
- passing in a 2D-array that contains one dataset per column.
- deg : int
- Degree of the fitting polynomial
- rcond : float, optional
- Relative condition number of the fit. Singular values smaller than
- this relative to the largest singular value will be ignored. The
- default value is len(x)*eps, where eps is the relative precision of
- the float type, about 2e-16 in most cases.
- full : bool, optional
- Switch determining nature of return value. When it is False (the
- default) just the coefficients are returned, when True diagnostic
- information from the singular value decomposition is also returned.
- w : array_like, shape (M,), optional
- Weights to apply to the y-coordinates of the sample points. For
- gaussian uncertainties, use 1/sigma (not 1/sigma**2).
- cov : bool or str, optional
- If given and not `False`, return not just the estimate but also its
- covariance matrix. By default, the covariance are scaled by
- chi2/dof, where dof = M - (deg + 1), i.e., the weights are presumed
- to be unreliable except in a relative sense and everything is scaled
- such that the reduced chi2 is unity. This scaling is omitted if
- ``cov='unscaled'``, as is relevant for the case that the weights are
- 1/sigma**2, with sigma known to be a reliable estimate of the
- uncertainty.
- Returns
- -------
- p : ndarray, shape (deg + 1,) or (deg + 1, K)
- Polynomial coefficients, highest power first. If `y` was 2-D, the
- coefficients for `k`-th data set are in ``p[:,k]``.
- residuals, rank, singular_values, rcond
- Present only if `full` = True. Residuals is sum of squared residuals
- of the least-squares fit, the effective rank of the scaled Vandermonde
- coefficient matrix, its singular values, and the specified value of
- `rcond`. For more details, see `linalg.lstsq`.
- V : ndarray, shape (M,M) or (M,M,K)
- Present only if `full` = False and `cov`=True. The covariance
- matrix of the polynomial coefficient estimates. The diagonal of
- this matrix are the variance estimates for each coefficient. If y
- is a 2-D array, then the covariance matrix for the `k`-th data set
- are in ``V[:,:,k]``
- Warns
- -----
- RankWarning
- The rank of the coefficient matrix in the least-squares fit is
- deficient. The warning is only raised if `full` = False.
- The warnings can be turned off by
- >>> import warnings
- >>> warnings.simplefilter('ignore', np.RankWarning)
- See Also
- --------
- polyval : Compute polynomial values.
- linalg.lstsq : Computes a least-squares fit.
- scipy.interpolate.UnivariateSpline : Computes spline fits.
- Notes
- -----
- The solution minimizes the squared error
- .. math ::
- E = \\sum_{j=0}^k |p(x_j) - y_j|^2
- in the equations::
- x[0]**n * p[0] + ... + x[0] * p[n-1] + p[n] = y[0]
- x[1]**n * p[0] + ... + x[1] * p[n-1] + p[n] = y[1]
- ...
- x[k]**n * p[0] + ... + x[k] * p[n-1] + p[n] = y[k]
- The coefficient matrix of the coefficients `p` is a Vandermonde matrix.
- `polyfit` issues a `RankWarning` when the least-squares fit is badly
- conditioned. This implies that the best fit is not well-defined due
- to numerical error. The results may be improved by lowering the polynomial
- degree or by replacing `x` by `x` - `x`.mean(). The `rcond` parameter
- can also be set to a value smaller than its default, but the resulting
- fit may be spurious: including contributions from the small singular
- values can add numerical noise to the result.
- Note that fitting polynomial coefficients is inherently badly conditioned
- when the degree of the polynomial is large or the interval of sample points
- is badly centered. The quality of the fit should always be checked in these
- cases. When polynomial fits are not satisfactory, splines may be a good
- alternative.
- References
- ----------
- .. [1] Wikipedia, "Curve fitting",
- https://en.wikipedia.org/wiki/Curve_fitting
- .. [2] Wikipedia, "Polynomial interpolation",
- https://en.wikipedia.org/wiki/Polynomial_interpolation
- Examples
- --------
- >>> import warnings
- >>> x = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
- >>> y = np.array([0.0, 0.8, 0.9, 0.1, -0.8, -1.0])
- >>> z = np.polyfit(x, y, 3)
- >>> z
- array([ 0.08703704, -0.81349206, 1.69312169, -0.03968254]) # may vary
- It is convenient to use `poly1d` objects for dealing with polynomials:
- >>> p = np.poly1d(z)
- >>> p(0.5)
- 0.6143849206349179 # may vary
- >>> p(3.5)
- -0.34732142857143039 # may vary
- >>> p(10)
- 22.579365079365115 # may vary
- High-order polynomials may oscillate wildly:
- >>> with warnings.catch_warnings():
- ... warnings.simplefilter('ignore', np.RankWarning)
- ... p30 = np.poly1d(np.polyfit(x, y, 30))
- ...
- >>> p30(4)
- -0.80000000000000204 # may vary
- >>> p30(5)
- -0.99999999999999445 # may vary
- >>> p30(4.5)
- -0.10547061179440398 # may vary
- Illustration:
- >>> import matplotlib.pyplot as plt
- >>> xp = np.linspace(-2, 6, 100)
- >>> _ = plt.plot(x, y, '.', xp, p(xp), '-', xp, p30(xp), '--')
- >>> plt.ylim(-2,2)
- (-2, 2)
- >>> plt.show()
- """
- order = int(deg) + 1
- x = NX.asarray(x) + 0.0
- y = NX.asarray(y) + 0.0
- # check arguments.
- if deg < 0:
- raise ValueError("expected deg >= 0")
- if x.ndim != 1:
- raise TypeError("expected 1D vector for x")
- if x.size == 0:
- raise TypeError("expected non-empty vector for x")
- if y.ndim < 1 or y.ndim > 2:
- raise TypeError("expected 1D or 2D array for y")
- if x.shape[0] != y.shape[0]:
- raise TypeError("expected x and y to have same length")
- # set rcond
- if rcond is None:
- rcond = len(x)*finfo(x.dtype).eps
- # set up least squares equation for powers of x
- lhs = vander(x, order)
- rhs = y
- # apply weighting
- if w is not None:
- w = NX.asarray(w) + 0.0
- if w.ndim != 1:
- raise TypeError("expected a 1-d array for weights")
- if w.shape[0] != y.shape[0]:
- raise TypeError("expected w and y to have the same length")
- lhs *= w[:, NX.newaxis]
- if rhs.ndim == 2:
- rhs *= w[:, NX.newaxis]
- else:
- rhs *= w
- # scale lhs to improve condition number and solve
- scale = NX.sqrt((lhs*lhs).sum(axis=0))
- lhs /= scale
- c, resids, rank, s = lstsq(lhs, rhs, rcond)
- c = (c.T/scale).T # broadcast scale coefficients
- # warn on rank reduction, which indicates an ill conditioned matrix
- if rank != order and not full:
- msg = "Polyfit may be poorly conditioned"
- warnings.warn(msg, RankWarning, stacklevel=4)
- if full:
- return c, resids, rank, s, rcond
- elif cov:
- Vbase = inv(dot(lhs.T, lhs))
- Vbase /= NX.outer(scale, scale)
- if cov == "unscaled":
- fac = 1
- else:
- if len(x) <= order:
- raise ValueError("the number of data points must exceed order "
- "to scale the covariance matrix")
- # note, this used to be: fac = resids / (len(x) - order - 2.0)
- # it was deciced that the "- 2" (originally justified by "Bayesian
- # uncertainty analysis") is not was the user expects
- # (see gh-11196 and gh-11197)
- fac = resids / (len(x) - order)
- if y.ndim == 1:
- return c, Vbase * fac
- else:
- return c, Vbase[:,:, NX.newaxis] * fac
- else:
- return c
- def _polyval_dispatcher(p, x):
- return (p, x)
- @array_function_dispatch(_polyval_dispatcher)
- def polyval(p, x):
- """
- Evaluate a polynomial at specific values.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- If `p` is of length N, this function returns the value:
- ``p[0]*x**(N-1) + p[1]*x**(N-2) + ... + p[N-2]*x + p[N-1]``
- If `x` is a sequence, then ``p(x)`` is returned for each element of ``x``.
- If `x` is another polynomial then the composite polynomial ``p(x(t))``
- is returned.
- Parameters
- ----------
- p : array_like or poly1d object
- 1D array of polynomial coefficients (including coefficients equal
- to zero) from highest degree to the constant term, or an
- instance of poly1d.
- x : array_like or poly1d object
- A number, an array of numbers, or an instance of poly1d, at
- which to evaluate `p`.
- Returns
- -------
- values : ndarray or poly1d
- If `x` is a poly1d instance, the result is the composition of the two
- polynomials, i.e., `x` is "substituted" in `p` and the simplified
- result is returned. In addition, the type of `x` - array_like or
- poly1d - governs the type of the output: `x` array_like => `values`
- array_like, `x` a poly1d object => `values` is also.
- See Also
- --------
- poly1d: A polynomial class.
- Notes
- -----
- Horner's scheme [1]_ is used to evaluate the polynomial. Even so,
- for polynomials of high degree the values may be inaccurate due to
- rounding errors. Use carefully.
- If `x` is a subtype of `ndarray` the return value will be of the same type.
- References
- ----------
- .. [1] I. N. Bronshtein, K. A. Semendyayev, and K. A. Hirsch (Eng.
- trans. Ed.), *Handbook of Mathematics*, New York, Van Nostrand
- Reinhold Co., 1985, pg. 720.
- Examples
- --------
- >>> np.polyval([3,0,1], 5) # 3 * 5**2 + 0 * 5**1 + 1
- 76
- >>> np.polyval([3,0,1], np.poly1d(5))
- poly1d([76])
- >>> np.polyval(np.poly1d([3,0,1]), 5)
- 76
- >>> np.polyval(np.poly1d([3,0,1]), np.poly1d(5))
- poly1d([76])
- """
- p = NX.asarray(p)
- if isinstance(x, poly1d):
- y = 0
- else:
- x = NX.asanyarray(x)
- y = NX.zeros_like(x)
- for i in range(len(p)):
- y = y * x + p[i]
- return y
- def _binary_op_dispatcher(a1, a2):
- return (a1, a2)
- @array_function_dispatch(_binary_op_dispatcher)
- def polyadd(a1, a2):
- """
- Find the sum of two polynomials.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- Returns the polynomial resulting from the sum of two input polynomials.
- Each input must be either a poly1d object or a 1D sequence of polynomial
- coefficients, from highest to lowest degree.
- Parameters
- ----------
- a1, a2 : array_like or poly1d object
- Input polynomials.
- Returns
- -------
- out : ndarray or poly1d object
- The sum of the inputs. If either input is a poly1d object, then the
- output is also a poly1d object. Otherwise, it is a 1D array of
- polynomial coefficients from highest to lowest degree.
- See Also
- --------
- poly1d : A one-dimensional polynomial class.
- poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval
- Examples
- --------
- >>> np.polyadd([1, 2], [9, 5, 4])
- array([9, 6, 6])
- Using poly1d objects:
- >>> p1 = np.poly1d([1, 2])
- >>> p2 = np.poly1d([9, 5, 4])
- >>> print(p1)
- 1 x + 2
- >>> print(p2)
- 2
- 9 x + 5 x + 4
- >>> print(np.polyadd(p1, p2))
- 2
- 9 x + 6 x + 6
- """
- truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
- a1 = atleast_1d(a1)
- a2 = atleast_1d(a2)
- diff = len(a2) - len(a1)
- if diff == 0:
- val = a1 + a2
- elif diff > 0:
- zr = NX.zeros(diff, a1.dtype)
- val = NX.concatenate((zr, a1)) + a2
- else:
- zr = NX.zeros(abs(diff), a2.dtype)
- val = a1 + NX.concatenate((zr, a2))
- if truepoly:
- val = poly1d(val)
- return val
- @array_function_dispatch(_binary_op_dispatcher)
- def polysub(a1, a2):
- """
- Difference (subtraction) of two polynomials.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- Given two polynomials `a1` and `a2`, returns ``a1 - a2``.
- `a1` and `a2` can be either array_like sequences of the polynomials'
- coefficients (including coefficients equal to zero), or `poly1d` objects.
- Parameters
- ----------
- a1, a2 : array_like or poly1d
- Minuend and subtrahend polynomials, respectively.
- Returns
- -------
- out : ndarray or poly1d
- Array or `poly1d` object of the difference polynomial's coefficients.
- See Also
- --------
- polyval, polydiv, polymul, polyadd
- Examples
- --------
- .. math:: (2 x^2 + 10 x - 2) - (3 x^2 + 10 x -4) = (-x^2 + 2)
- >>> np.polysub([2, 10, -2], [3, 10, -4])
- array([-1, 0, 2])
- """
- truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
- a1 = atleast_1d(a1)
- a2 = atleast_1d(a2)
- diff = len(a2) - len(a1)
- if diff == 0:
- val = a1 - a2
- elif diff > 0:
- zr = NX.zeros(diff, a1.dtype)
- val = NX.concatenate((zr, a1)) - a2
- else:
- zr = NX.zeros(abs(diff), a2.dtype)
- val = a1 - NX.concatenate((zr, a2))
- if truepoly:
- val = poly1d(val)
- return val
- @array_function_dispatch(_binary_op_dispatcher)
- def polymul(a1, a2):
- """
- Find the product of two polynomials.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- Finds the polynomial resulting from the multiplication of the two input
- polynomials. Each input must be either a poly1d object or a 1D sequence
- of polynomial coefficients, from highest to lowest degree.
- Parameters
- ----------
- a1, a2 : array_like or poly1d object
- Input polynomials.
- Returns
- -------
- out : ndarray or poly1d object
- The polynomial resulting from the multiplication of the inputs. If
- either inputs is a poly1d object, then the output is also a poly1d
- object. Otherwise, it is a 1D array of polynomial coefficients from
- highest to lowest degree.
- See Also
- --------
- poly1d : A one-dimensional polynomial class.
- poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval
- convolve : Array convolution. Same output as polymul, but has parameter
- for overlap mode.
- Examples
- --------
- >>> np.polymul([1, 2, 3], [9, 5, 1])
- array([ 9, 23, 38, 17, 3])
- Using poly1d objects:
- >>> p1 = np.poly1d([1, 2, 3])
- >>> p2 = np.poly1d([9, 5, 1])
- >>> print(p1)
- 2
- 1 x + 2 x + 3
- >>> print(p2)
- 2
- 9 x + 5 x + 1
- >>> print(np.polymul(p1, p2))
- 4 3 2
- 9 x + 23 x + 38 x + 17 x + 3
- """
- truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
- a1, a2 = poly1d(a1), poly1d(a2)
- val = NX.convolve(a1, a2)
- if truepoly:
- val = poly1d(val)
- return val
- def _polydiv_dispatcher(u, v):
- return (u, v)
- @array_function_dispatch(_polydiv_dispatcher)
- def polydiv(u, v):
- """
- Returns the quotient and remainder of polynomial division.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- The input arrays are the coefficients (including any coefficients
- equal to zero) of the "numerator" (dividend) and "denominator"
- (divisor) polynomials, respectively.
- Parameters
- ----------
- u : array_like or poly1d
- Dividend polynomial's coefficients.
- v : array_like or poly1d
- Divisor polynomial's coefficients.
- Returns
- -------
- q : ndarray
- Coefficients, including those equal to zero, of the quotient.
- r : ndarray
- Coefficients, including those equal to zero, of the remainder.
- See Also
- --------
- poly, polyadd, polyder, polydiv, polyfit, polyint, polymul, polysub
- polyval
- Notes
- -----
- Both `u` and `v` must be 0-d or 1-d (ndim = 0 or 1), but `u.ndim` need
- not equal `v.ndim`. In other words, all four possible combinations -
- ``u.ndim = v.ndim = 0``, ``u.ndim = v.ndim = 1``,
- ``u.ndim = 1, v.ndim = 0``, and ``u.ndim = 0, v.ndim = 1`` - work.
- Examples
- --------
- .. math:: \\frac{3x^2 + 5x + 2}{2x + 1} = 1.5x + 1.75, remainder 0.25
- >>> x = np.array([3.0, 5.0, 2.0])
- >>> y = np.array([2.0, 1.0])
- >>> np.polydiv(x, y)
- (array([1.5 , 1.75]), array([0.25]))
- """
- truepoly = (isinstance(u, poly1d) or isinstance(v, poly1d))
- u = atleast_1d(u) + 0.0
- v = atleast_1d(v) + 0.0
- # w has the common type
- w = u[0] + v[0]
- m = len(u) - 1
- n = len(v) - 1
- scale = 1. / v[0]
- q = NX.zeros((max(m - n + 1, 1),), w.dtype)
- r = u.astype(w.dtype)
- for k in range(0, m-n+1):
- d = scale * r[k]
- q[k] = d
- r[k:k+n+1] -= d*v
- while NX.allclose(r[0], 0, rtol=1e-14) and (r.shape[-1] > 1):
- r = r[1:]
- if truepoly:
- return poly1d(q), poly1d(r)
- return q, r
- _poly_mat = re.compile(r"\*\*([0-9]*)")
- def _raise_power(astr, wrap=70):
- n = 0
- line1 = ''
- line2 = ''
- output = ' '
- while True:
- mat = _poly_mat.search(astr, n)
- if mat is None:
- break
- span = mat.span()
- power = mat.groups()[0]
- partstr = astr[n:span[0]]
- n = span[1]
- toadd2 = partstr + ' '*(len(power)-1)
- toadd1 = ' '*(len(partstr)-1) + power
- if ((len(line2) + len(toadd2) > wrap) or
- (len(line1) + len(toadd1) > wrap)):
- output += line1 + "\n" + line2 + "\n "
- line1 = toadd1
- line2 = toadd2
- else:
- line2 += partstr + ' '*(len(power)-1)
- line1 += ' '*(len(partstr)-1) + power
- output += line1 + "\n" + line2
- return output + astr[n:]
- @set_module('numpy')
- class poly1d:
- """
- A one-dimensional polynomial class.
- .. note::
- This forms part of the old polynomial API. Since version 1.4, the
- new polynomial API defined in `numpy.polynomial` is preferred.
- A summary of the differences can be found in the
- :doc:`transition guide </reference/routines.polynomials>`.
- A convenience class, used to encapsulate "natural" operations on
- polynomials so that said operations may take on their customary
- form in code (see Examples).
- Parameters
- ----------
- c_or_r : array_like
- The polynomial's coefficients, in decreasing powers, or if
- the value of the second parameter is True, the polynomial's
- roots (values where the polynomial evaluates to 0). For example,
- ``poly1d([1, 2, 3])`` returns an object that represents
- :math:`x^2 + 2x + 3`, whereas ``poly1d([1, 2, 3], True)`` returns
- one that represents :math:`(x-1)(x-2)(x-3) = x^3 - 6x^2 + 11x -6`.
- r : bool, optional
- If True, `c_or_r` specifies the polynomial's roots; the default
- is False.
- variable : str, optional
- Changes the variable used when printing `p` from `x` to `variable`
- (see Examples).
- Examples
- --------
- Construct the polynomial :math:`x^2 + 2x + 3`:
- >>> p = np.poly1d([1, 2, 3])
- >>> print(np.poly1d(p))
- 2
- 1 x + 2 x + 3
- Evaluate the polynomial at :math:`x = 0.5`:
- >>> p(0.5)
- 4.25
- Find the roots:
- >>> p.r
- array([-1.+1.41421356j, -1.-1.41421356j])
- >>> p(p.r)
- array([ -4.44089210e-16+0.j, -4.44089210e-16+0.j]) # may vary
- These numbers in the previous line represent (0, 0) to machine precision
- Show the coefficients:
- >>> p.c
- array([1, 2, 3])
- Display the order (the leading zero-coefficients are removed):
- >>> p.order
- 2
- Show the coefficient of the k-th power in the polynomial
- (which is equivalent to ``p.c[-(i+1)]``):
- >>> p[1]
- 2
- Polynomials can be added, subtracted, multiplied, and divided
- (returns quotient and remainder):
- >>> p * p
- poly1d([ 1, 4, 10, 12, 9])
- >>> (p**3 + 4) / p
- (poly1d([ 1., 4., 10., 12., 9.]), poly1d([4.]))
- ``asarray(p)`` gives the coefficient array, so polynomials can be
- used in all functions that accept arrays:
- >>> p**2 # square of polynomial
- poly1d([ 1, 4, 10, 12, 9])
- >>> np.square(p) # square of individual coefficients
- array([1, 4, 9])
- The variable used in the string representation of `p` can be modified,
- using the `variable` parameter:
- >>> p = np.poly1d([1,2,3], variable='z')
- >>> print(p)
- 2
- 1 z + 2 z + 3
- Construct a polynomial from its roots:
- >>> np.poly1d([1, 2], True)
- poly1d([ 1., -3., 2.])
- This is the same polynomial as obtained by:
- >>> np.poly1d([1, -1]) * np.poly1d([1, -2])
- poly1d([ 1, -3, 2])
- """
- __hash__ = None
- @property
- def coeffs(self):
- """ The polynomial coefficients """
- return self._coeffs
- @coeffs.setter
- def coeffs(self, value):
- # allowing this makes p.coeffs *= 2 legal
- if value is not self._coeffs:
- raise AttributeError("Cannot set attribute")
- @property
- def variable(self):
- """ The name of the polynomial variable """
- return self._variable
- # calculated attributes
- @property
- def order(self):
- """ The order or degree of the polynomial """
- return len(self._coeffs) - 1
- @property
- def roots(self):
- """ The roots of the polynomial, where self(x) == 0 """
- return roots(self._coeffs)
- # our internal _coeffs property need to be backed by __dict__['coeffs'] for
- # scipy to work correctly.
- @property
- def _coeffs(self):
- return self.__dict__['coeffs']
- @_coeffs.setter
- def _coeffs(self, coeffs):
- self.__dict__['coeffs'] = coeffs
- # alias attributes
- r = roots
- c = coef = coefficients = coeffs
- o = order
- def __init__(self, c_or_r, r=False, variable=None):
- if isinstance(c_or_r, poly1d):
- self._variable = c_or_r._variable
- self._coeffs = c_or_r._coeffs
- if set(c_or_r.__dict__) - set(self.__dict__):
- msg = ("In the future extra properties will not be copied "
- "across when constructing one poly1d from another")
- warnings.warn(msg, FutureWarning, stacklevel=2)
- self.__dict__.update(c_or_r.__dict__)
- if variable is not None:
- self._variable = variable
- return
- if r:
- c_or_r = poly(c_or_r)
- c_or_r = atleast_1d(c_or_r)
- if c_or_r.ndim > 1:
- raise ValueError("Polynomial must be 1d only.")
- c_or_r = trim_zeros(c_or_r, trim='f')
- if len(c_or_r) == 0:
- c_or_r = NX.array([0], dtype=c_or_r.dtype)
- self._coeffs = c_or_r
- if variable is None:
- variable = 'x'
- self._variable = variable
- def __array__(self, t=None):
- if t:
- return NX.asarray(self.coeffs, t)
- else:
- return NX.asarray(self.coeffs)
- def __repr__(self):
- vals = repr(self.coeffs)
- vals = vals[6:-1]
- return "poly1d(%s)" % vals
- def __len__(self):
- return self.order
- def __str__(self):
- thestr = "0"
- var = self.variable
- # Remove leading zeros
- coeffs = self.coeffs[NX.logical_or.accumulate(self.coeffs != 0)]
- N = len(coeffs)-1
- def fmt_float(q):
- s = '%.4g' % q
- if s.endswith('.0000'):
- s = s[:-5]
- return s
- for k in range(len(coeffs)):
- if not iscomplex(coeffs[k]):
- coefstr = fmt_float(real(coeffs[k]))
- elif real(coeffs[k]) == 0:
- coefstr = '%sj' % fmt_float(imag(coeffs[k]))
- else:
- coefstr = '(%s + %sj)' % (fmt_float(real(coeffs[k])),
- fmt_float(imag(coeffs[k])))
- power = (N-k)
- if power == 0:
- if coefstr != '0':
- newstr = '%s' % (coefstr,)
- else:
- if k == 0:
- newstr = '0'
- else:
- newstr = ''
- elif power == 1:
- if coefstr == '0':
- newstr = ''
- elif coefstr == 'b':
- newstr = var
- else:
- newstr = '%s %s' % (coefstr, var)
- else:
- if coefstr == '0':
- newstr = ''
- elif coefstr == 'b':
- newstr = '%s**%d' % (var, power,)
- else:
- newstr = '%s %s**%d' % (coefstr, var, power)
- if k > 0:
- if newstr != '':
- if newstr.startswith('-'):
- thestr = "%s - %s" % (thestr, newstr[1:])
- else:
- thestr = "%s + %s" % (thestr, newstr)
- else:
- thestr = newstr
- return _raise_power(thestr)
- def __call__(self, val):
- return polyval(self.coeffs, val)
- def __neg__(self):
- return poly1d(-self.coeffs)
- def __pos__(self):
- return self
- def __mul__(self, other):
- if isscalar(other):
- return poly1d(self.coeffs * other)
- else:
- other = poly1d(other)
- return poly1d(polymul(self.coeffs, other.coeffs))
- def __rmul__(self, other):
- if isscalar(other):
- return poly1d(other * self.coeffs)
- else:
- other = poly1d(other)
- return poly1d(polymul(self.coeffs, other.coeffs))
- def __add__(self, other):
- other = poly1d(other)
- return poly1d(polyadd(self.coeffs, other.coeffs))
- def __radd__(self, other):
- other = poly1d(other)
- return poly1d(polyadd(self.coeffs, other.coeffs))
- def __pow__(self, val):
- if not isscalar(val) or int(val) != val or val < 0:
- raise ValueError("Power to non-negative integers only.")
- res = [1]
- for _ in range(val):
- res = polymul(self.coeffs, res)
- return poly1d(res)
- def __sub__(self, other):
- other = poly1d(other)
- return poly1d(polysub(self.coeffs, other.coeffs))
- def __rsub__(self, other):
- other = poly1d(other)
- return poly1d(polysub(other.coeffs, self.coeffs))
- def __div__(self, other):
- if isscalar(other):
- return poly1d(self.coeffs/other)
- else:
- other = poly1d(other)
- return polydiv(self, other)
- __truediv__ = __div__
- def __rdiv__(self, other):
- if isscalar(other):
- return poly1d(other/self.coeffs)
- else:
- other = poly1d(other)
- return polydiv(other, self)
- __rtruediv__ = __rdiv__
- def __eq__(self, other):
- if not isinstance(other, poly1d):
- return NotImplemented
- if self.coeffs.shape != other.coeffs.shape:
- return False
- return (self.coeffs == other.coeffs).all()
- def __ne__(self, other):
- if not isinstance(other, poly1d):
- return NotImplemented
- return not self.__eq__(other)
- def __getitem__(self, val):
- ind = self.order - val
- if val > self.order:
- return 0
- if val < 0:
- return 0
- return self.coeffs[ind]
- def __setitem__(self, key, val):
- ind = self.order - key
- if key < 0:
- raise ValueError("Does not support negative powers.")
- if key > self.order:
- zr = NX.zeros(key-self.order, self.coeffs.dtype)
- self._coeffs = NX.concatenate((zr, self.coeffs))
- ind = 0
- self._coeffs[ind] = val
- return
- def __iter__(self):
- return iter(self.coeffs)
- def integ(self, m=1, k=0):
- """
- Return an antiderivative (indefinite integral) of this polynomial.
- Refer to `polyint` for full documentation.
- See Also
- --------
- polyint : equivalent function
- """
- return poly1d(polyint(self.coeffs, m=m, k=k))
- def deriv(self, m=1):
- """
- Return a derivative of this polynomial.
- Refer to `polyder` for full documentation.
- See Also
- --------
- polyder : equivalent function
- """
- return poly1d(polyder(self.coeffs, m=m))
- # Stuff to do on module import
- warnings.simplefilter('always', RankWarning)
|