123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452 |
- """
- 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 zero in seq_of_zeros:
- a = NX.convolve(a, array([1, -zero], 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. If not None, the weight ``w[i]`` applies to the unsquared
- residual ``y[i] - y_hat[i]`` at ``x[i]``. Ideally the weights are
- chosen so that the errors of the products ``w[i]*y[i]`` all have the
- same variance. When using inverse-variance weighting, use
- ``w[i] = 1/sigma(y[i])``. The default value is None.
- 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
- w = 1/sigma, 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
- These values are only returned if ``full == True``
- - residuals -- sum of squared residuals of the least squares fit
- - rank -- the effective rank of the scaled Vandermonde
- coefficient matrix
- - singular_values -- singular values of the scaled Vandermonde
- coefficient matrix
- - rcond -- value of `rcond`.
- For more details, see `numpy.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 what 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 pv in p:
- y = y * x + pv
- 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, coeff in enumerate(coeffs):
- if not iscomplex(coeff):
- coefstr = fmt_float(real(coeff))
- elif real(coeff) == 0:
- coefstr = '%sj' % fmt_float(imag(coeff))
- else:
- coefstr = '(%s + %sj)' % (fmt_float(real(coeff)),
- fmt_float(imag(coeff)))
- 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 self.coeffs.dtype.type(0)
- if val < 0:
- return self.coeffs.dtype.type(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)
|