diff options
author | Jarrod Millman <millman@berkeley.edu> | 2010-02-17 23:53:04 +0000 |
---|---|---|
committer | Jarrod Millman <millman@berkeley.edu> | 2010-02-17 23:53:04 +0000 |
commit | e2bb09430d90c73a7be6e47ea8c4528f094f693f (patch) | |
tree | 3ded297a6cbe634446d6a54afc4e95c8c71553e6 /numpy/polynomial | |
parent | dcc721a5bddde3afd4ce47d7a7b76ec6c7102b92 (diff) | |
download | numpy-e2bb09430d90c73a7be6e47ea8c4528f094f693f.tar.gz |
more docstring updates from pydoc website (thanks to everyone who contributed!)
Diffstat (limited to 'numpy/polynomial')
-rw-r--r-- | numpy/polynomial/__init__.py | 15 | ||||
-rw-r--r-- | numpy/polynomial/chebyshev.py | 500 | ||||
-rw-r--r-- | numpy/polynomial/polynomial.py | 512 | ||||
-rw-r--r-- | numpy/polynomial/polytemplate.py | 10 | ||||
-rw-r--r-- | numpy/polynomial/polyutils.py | 237 |
5 files changed, 900 insertions, 374 deletions
diff --git a/numpy/polynomial/__init__.py b/numpy/polynomial/__init__.py index 6aedbef47..7e755ca52 100644 --- a/numpy/polynomial/__init__.py +++ b/numpy/polynomial/__init__.py @@ -1,3 +1,18 @@ +""" +A sub-package for efficiently dealing with polynomials. + +Within the documentation for this sub-package, a "finite power series," +i.e., a polynomial (also referred to simply as a "series") is represented +by a 1-D numpy array of the polynomial's coefficients, ordered from lowest +order term to highest. For example, array([1,2,3]) represents +``P_0 + 2*P_1 + 3*P_2``, where P_n is the n-th order basis polynomial +applicable to the specific module in question, e.g., `polynomial` (which +"wraps" the "standard" basis) or `chebyshev`. For optimal performance, +all operations on polynomials, including evaluation at an argument, are +implemented as operations on the coefficients. Additional (module-specific) +information can be found in the docstring for the module of interest. + +""" from polynomial import * from chebyshev import * from polyutils import * diff --git a/numpy/polynomial/chebyshev.py b/numpy/polynomial/chebyshev.py index e25ed8a6f..662f664a9 100644 --- a/numpy/polynomial/chebyshev.py +++ b/numpy/polynomial/chebyshev.py @@ -1,50 +1,58 @@ -"""Functions for dealing with Chebyshev series. +""" +Objects for dealing with Chebyshev series. -This module provide s a number of functions that are useful in dealing with -Chebyshev series as well as a ``Chebyshev`` class that encapsuletes the usual -arithmetic operations. All the Chebyshev series are assumed to be ordered -from low to high, thus ``array([1,2,3])`` will be treated as the series -``T_0 + 2*T_1 + 3*T_2`` +This module provides a number of objects (mostly functions) useful for +dealing with Chebyshev series, including a `Chebyshev` class that +encapsulates the usual arithmetic operations. (General information +on how this module represents and works with such polynomials is in the +docstring for its "parent" sub-package, `numpy.polynomial`). Constants --------- -- chebdomain -- Chebyshev series default domain -- chebzero -- Chebyshev series that evaluates to 0. -- chebone -- Chebyshev series that evaluates to 1. -- chebx -- Chebyshev series of the identity map (x). +- `chebdomain` -- Chebyshev series default domain, [-1,1]. +- `chebzero` -- (Coefficients of the) Chebyshev series that evaluates + identically to 0. +- `chebone` -- (Coefficients of the) Chebyshev series that evaluates + identically to 1. +- `chebx` -- (Coefficients of the) Chebyshev series for the identity map, + ``f(x) = x``. Arithmetic ---------- -- chebadd -- add a Chebyshev series to another. -- chebsub -- subtract a Chebyshev series from another. -- chebmul -- multiply a Chebyshev series by another -- chebdiv -- divide one Chebyshev series by another. -- chebval -- evaluate a Chebyshev series at given points. +- `chebadd` -- add two Chebyshev series. +- `chebsub` -- subtract one Chebyshev series from another. +- `chebmul` -- multiply two Chebyshev series. +- `chebdiv` -- divide one Chebyshev series by another. +- `chebval` -- evaluate a Chebyshev series at given points. Calculus -------- -- chebder -- differentiate a Chebyshev series. -- chebint -- integrate a Chebyshev series. +- `chebder` -- differentiate a Chebyshev series. +- `chebint` -- integrate a Chebyshev series. Misc Functions -------------- -- chebfromroots -- create a Chebyshev series with specified roots. -- chebroots -- find the roots of a Chebyshev series. -- chebvander -- Vandermode like matrix for Chebyshev polynomials. -- chebfit -- least squares fit returning a Chebyshev series. -- chebtrim -- trim leading coefficients from a Chebyshev series. -- chebline -- Chebyshev series of given straight line -- cheb2poly -- convert a Chebyshev series to a polynomial. -- poly2cheb -- convert a polynomial to a Chebyshev series. +- `chebfromroots` -- create a Chebyshev series with specified roots. +- `chebroots` -- find the roots of a Chebyshev series. +- `chebvander` -- Vandermonde-like matrix for Chebyshev polynomials. +- `chebfit` -- least-squares fit returning a Chebyshev series. +- `chebtrim` -- trim leading coefficients from a Chebyshev series. +- `chebline` -- Chebyshev series of given straight line. +- `cheb2poly` -- convert a Chebyshev series to a polynomial. +- `poly2cheb` -- convert a polynomial to a Chebyshev series. Classes ------- -- Chebyshev -- Chebyshev series class. +- `Chebyshev` -- A Chebyshev series class. + +See also +-------- +`numpy.polynomial` Notes ----- The implementations of multiplication, division, integration, and -differentiation use the algebraic identities: +differentiation use the algebraic identities [1]_: .. math :: T_n(x) = \\frac{z^n + z^{-n}}{2} \\\\ @@ -55,8 +63,14 @@ where .. math :: x = \\frac{z + z^{-1}}{2}. These identities allow a Chebyshev series to be expressed as a finite, -symmetric Laurent series. These sorts of Laurent series are referred to as -z-series in this module. +symmetric Laurent series. In this module, this sort of Laurent series +is referred to as a "z-series." + +References +---------- +.. [1] A. T. Benjamin, et al., "Combinatorial Trigonometry with Chebyshev + Polynomials," *Journal of Statistical Planning and Inference 14*, 2008 + (preprint: http://www.math.hmc.edu/~benjamin/papers/CombTrig.pdf, pg. 4) """ from __future__ import division @@ -289,20 +303,24 @@ def _zseries_int(zs) : def poly2cheb(pol) : - """Convert a polynomial to a Chebyshev series. + """ + poly2cheb(pol) - Convert a series containing polynomial coefficients ordered by degree - from low to high to an equivalent Chebyshev series ordered from low to - high. + Convert a polynomial to a Chebyshev series. - Inputs - ------ + Convert an array representing the coefficients of a polynomial (relative + to the "standard" basis) ordered from lowest degree to highest, to an + array of the coefficients of the equivalent Chebyshev series, ordered + from lowest to highest degree. + + Parameters + ---------- pol : array_like - 1-d array containing the polynomial coeffients + 1-d array containing the polynomial coefficients Returns ------- - cseries : ndarray + cs : ndarray 1-d array containing the coefficients of the equivalent Chebyshev series. @@ -310,6 +328,23 @@ def poly2cheb(pol) : -------- cheb2poly + Notes + ----- + Note that a consequence of the input needing to be array_like and that + the output is an ndarray, is that if one is going to use this function + to convert a Polynomial instance, P, to a Chebyshev instance, T, the + usage is ``T = Chebyshev(poly2cheb(P.coef))``; see Examples below. + + Examples + -------- + >>> from numpy import polynomial as P + >>> p = P.Polynomial(np.arange(4)) + >>> p + Polynomial([ 0., 1., 2., 3.], [-1., 1.]) + >>> c = P.Chebyshev(P.poly2cheb(p.coef)) + >>> c + Chebyshev([ 1. , 3.25, 1. , 0.75], [-1., 1.]) + """ [pol] = pu.as_series([pol]) pol = pol[::-1] @@ -322,28 +357,50 @@ def poly2cheb(pol) : def cheb2poly(cs) : - """Convert a Chebyshev series to a polynomial. + """ + cheb2poly(cs) - Covert a series containing Chebyshev series coefficients orderd from - low to high to an equivalent polynomial ordered from low to - high by degree. + Convert a Chebyshev series to a polynomial. - Inputs - ------ + Convert an array representing the coefficients of a Chebyshev series, + ordered from lowest degree to highest, to an array of the coefficients + of the equivalent polynomial (relative to the "standard" basis) ordered + from lowest to highest degree. + + Parameters + ---------- cs : array_like - 1-d array containing the Chebyshev series coeffients ordered from - low to high. + 1-d array containing the Chebyshev series coefficients, ordered + from lowest order term to highest. Returns ------- pol : ndarray 1-d array containing the coefficients of the equivalent polynomial - ordered from low to high by degree. + (relative to the "standard" basis) ordered from lowest order term + to highest. See Also -------- poly2cheb + Notes + ----- + Note that a consequence of the input needing to be array_like and that + the output is an ndarray, is that if one is going to use this function + to convert a Chebyshev instance, T, to a Polynomial instance, P, the + usage is ``P = Polynomial(cheb2poly(T.coef))``; see Examples below. + + Examples + -------- + >>> from numpy import polynomial as P + >>> c = P.Chebyshev(np.arange(4)) + >>> c + Chebyshev([ 0., 1., 2., 3.], [-1., 1.]) + >>> p = P.Polynomial(P.cheb2poly(c.coef)) + >>> p + Polynomial([ -2., -8., 4., 12.], [-1., 1.]) + """ [cs] = pu.as_series([cs]) pol = np.zeros(len(cs), dtype=cs.dtype) @@ -373,19 +430,33 @@ chebone = np.array([1]) chebx = np.array([0,1]) def chebline(off, scl) : - """Chebyshev series whose graph is a straight line + """ + Chebyshev series whose graph is a straight line. + - The line has the formula ``off + scl*x`` - Parameters: - ----------- + Parameters + ---------- off, scl : scalars The specified line is given by ``off + scl*x``. - Returns: + Returns + ------- + y : ndarray + This module's representation of the Chebyshev series for + ``off + scl*x``. + + See Also + -------- + polyline + + Examples -------- - series : 1d ndarray - The Chebyshev series representation of ``off + scl*x``. + >>> import numpy.polynomial.chebyshev as C + >>> C.chebline(3,2) + array([3, 2]) + >>> C.chebval(-3, C.chebline(3,2)) # should be -3 + -3.0 """ if scl != 0 : @@ -394,25 +465,55 @@ def chebline(off, scl) : return np.array([off]) def chebfromroots(roots) : - """Generate a Chebyschev series with given roots. + """ + Generate a Chebyshev series with the given roots. - Generate a Chebyshev series whose roots are given by `roots`. The - resulting series is the produet `(x - roots[0])*(x - roots[1])*...` + Return the array of coefficients for the C-series whose roots (a.k.a. + "zeros") are given by *roots*. The returned array of coefficients is + ordered from lowest order "term" to highest, and zeros of multiplicity + greater than one must be included in *roots* a number of times equal + to their multiplicity (e.g., if `2` is a root of multiplicity three, + then [2,2,2] must be in *roots*). - Inputs - ------ + Parameters + ---------- roots : array_like - 1-d array containing the roots in sorted order. + Sequence containing the roots. Returns ------- - series : ndarray - 1-d array containing the coefficients of the Chebeshev series - ordered from low to high. + out : ndarray + 1-d array of the C-series' coefficients, ordered from low to + high. If all roots are real, ``out.dtype`` is a float type; + otherwise, ``out.dtype`` is a complex type, even if all the + coefficients in the result are real (see Examples below). See Also -------- - chebroots + polyfromroots + + Notes + ----- + What is returned are the :math:`c_i` such that: + + .. math:: + + \\sum_{i=0}^{n} c_i*T_i(x) = \\prod_{i=0}^{n} (x - roots[i]) + + where ``n == len(roots)`` and :math:`T_i(x)` is the `i`-th Chebyshev + (basis) polynomial over the domain `[-1,1]`. Note that, unlike + `polyfromroots`, due to the nature of the C-series basis set, the + above identity *does not* imply :math:`c_n = 1` identically (see + Examples). + + Examples + -------- + >>> import numpy.polynomial.chebyshev as C + >>> C.chebfromroots((-1,0,1)) # x^3 - x relative to the standard basis + array([ 0. , -0.25, 0. , 0.25]) + >>> j = complex(0,1) + >>> C.chebfromroots((-j,j)) # x^2 + 1 relative to the standard basis + array([ 1.5+0.j, 0.0+0.j, 0.5+0.j]) """ if len(roots) == 0 : @@ -427,27 +528,43 @@ def chebfromroots(roots) : def chebadd(c1, c2): - """Add one Chebyshev series to another. + """ + Add one Chebyshev series to another. - Returns the sum of two Chebyshev series `c1` + `c2`. The arguments are - sequences of coefficients ordered from low to high, i.e., [1,2,3] is - the series "T_0 + 2*T_1 + 3*T_2". + Returns the sum of two Chebyshev series `c1` + `c2`. The arguments + are sequences of coefficients ordered from lowest order term to + highest, i.e., [1,2,3] represents the series ``T_0 + 2*T_1 + 3*T_2``. Parameters ---------- c1, c2 : array_like - 1d arrays of Chebyshev series coefficients ordered from low to + 1-d arrays of Chebyshev series coefficients ordered from low to high. Returns ------- out : ndarray - Chebyshev series of the sum. + Array representing the Chebyshev series of their sum. See Also -------- chebsub, chebmul, chebdiv, chebpow + Notes + ----- + Unlike multiplication, division, etc., the sum of two Chebyshev series + is a Chebyshev series (without having to "reproject" the result onto + the basis set) so addition, just like that of "standard" polynomials, + is simply "component-wise." + + Examples + -------- + >>> from numpy.polynomial import chebyshev as C + >>> c1 = (1,2,3) + >>> c2 = (3,2,1) + >>> C.chebadd(c1,c2) + array([ 4., 4., 4.]) + """ # c1, c2 are trimmed copies [c1, c2] = pu.as_series([c1, c2]) @@ -461,29 +578,44 @@ def chebadd(c1, c2): def chebsub(c1, c2): - """Subtract one Chebyshev series from another. + """ + Subtract one Chebyshev series from another. - Returns the difference of two Chebyshev series `c1` - `c2`. The - sequences of coefficients are ordered from low to high, i.e., [1,2,3] - is the series ``T_0 + 2*T_1 + 3*T_2.`` + Returns the difference of two Chebyshev series `c1` - `c2`. The + sequences of coefficients are from lowest order term to highest, i.e., + [1,2,3] represents the series ``T_0 + 2*T_1 + 3*T_2``. Parameters ---------- c1, c2 : array_like - 1d arrays of Chebyshev series coefficients ordered from low to + 1-d arrays of Chebyshev series coefficients ordered from low to high. Returns ------- out : ndarray - Chebyshev series of the difference. + Of Chebyshev series coefficients representing their difference. See Also -------- chebadd, chebmul, chebdiv, chebpow + Notes + ----- + Unlike multiplication, division, etc., the difference of two Chebyshev + series is a Chebyshev series (without having to "reproject" the result + onto the basis set) so subtraction, just like that of "standard" + polynomials, is simply "component-wise." + Examples -------- + >>> from numpy.polynomial import chebyshev as C + >>> c1 = (1,2,3) + >>> c2 = (3,2,1) + >>> C.chebsub(c1,c2) + array([-2., 0., 2.]) + >>> C.chebsub(c2,c1) # -C.chebsub(c1,c2) + array([ 2., 0., -2.]) """ # c1, c2 are trimmed copies @@ -499,27 +631,44 @@ def chebsub(c1, c2): def chebmul(c1, c2): - """Multiply one Chebyshev series by another. + """ + Multiply one Chebyshev series by another. - Returns the product of two Chebyshev series `c1` * `c2`. The arguments - are sequences of coefficients ordered from low to high, i.e., [1,2,3] - is the series ``T_0 + 2*T_1 + 3*T_2.`` + Returns the product of two Chebyshev series `c1` * `c2`. The arguments + are sequences of coefficients, from lowest order "term" to highest, + e.g., [1,2,3] represents the series ``T_0 + 2*T_1 + 3*T_2``. Parameters ---------- c1, c2 : array_like - 1d arrays of chebyshev series coefficients ordered from low to + 1-d arrays of Chebyshev series coefficients ordered from low to high. Returns ------- out : ndarray - Chebyshev series of the product. + Of Chebyshev series coefficients representing their product. See Also -------- chebadd, chebsub, chebdiv, chebpow + Notes + ----- + In general, the (polynomial) product of two C-series results in terms + that are not in the Chebyshev polynomial basis set. Thus, to express + the product as a C-series, it is typically necessary to "re-project" + the product onto said basis set, which typically produces + "un-intuitive" (but correct) results; see Examples section below. + + Examples + -------- + >>> from numpy.polynomial import chebyshev as C + >>> c1 = (1,2,3) + >>> c2 = (3,2,1) + >>> C.chebmul(c1,c2) # multiplication requires "reprojection" + array([ 6.5, 12. , 12. , 4. , 1.5]) + """ # c1, c2 are trimmed copies [c1, c2] = pu.as_series([c1, c2]) @@ -531,29 +680,49 @@ def chebmul(c1, c2): def chebdiv(c1, c2): - """Divide one Chebyshev series by another. + """ + Divide one Chebyshev series by another. - Returns the quotient of two Chebyshev series `c1` / `c2`. The arguments - are sequences of coefficients ordered from low to high, i.e., [1,2,3] - is the series ``T_0 + 2*T_1 + 3*T_2.`` + Returns the quotient-with-remainder of two Chebyshev series + `c1` / `c2`. The arguments are sequences of coefficients from lowest + order "term" to highest, e.g., [1,2,3] represents the series + ``T_0 + 2*T_1 + 3*T_2``. Parameters ---------- c1, c2 : array_like - 1d arrays of chebyshev series coefficients ordered from low to + 1-d arrays of Chebyshev series coefficients ordered from low to high. Returns ------- - [quo, rem] : ndarray - Chebyshev series of the quotient and remainder. + [quo, rem] : ndarrays + Of Chebyshev series coefficients representing the quotient and + remainder. See Also -------- chebadd, chebsub, chebmul, chebpow + Notes + ----- + In general, the (polynomial) division of one C-series by another + results in quotient and remainder terms that are not in the Chebyshev + polynomial basis set. Thus, to express these results as C-series, it + is typically necessary to "re-project" the results onto said basis + set, which typically produces "un-intuitive" (but correct) results; + see Examples section below. + Examples -------- + >>> from numpy.polynomial import chebyshev as C + >>> c1 = (1,2,3) + >>> c2 = (3,2,1) + >>> C.chebdiv(c1,c2) # quotient "intuitive," remainder not + (array([ 3.]), array([-8., -4.])) + >>> c2 = (0,1,2,3) + >>> C.chebdiv(c2,c1) # neither "intuitive" + (array([ 0., 2.]), array([-2., -4.])) """ # c1, c2 are trimmed copies @@ -627,24 +796,25 @@ def chebpow(cs, pow, maxpower=16) : return _zseries_to_cseries(prd) def chebder(cs, m=1, scl=1) : - """Differentiate a Chebyshev series. + """ + Differentiate a Chebyshev series. - Returns the series `cs` differentiated `m` times. At each iteration the - result is multiplied by `scl`. The scaling factor is for use in a - linear change of variable. The argument `cs` is a sequence of - coefficients ordered from low to high. i.e., [1,2,3] is the series + Returns the series `cs` differentiated `m` times. At each iteration the + result is multiplied by `scl` (the scaling factor is for use in a linear + change of variable). The argument `cs` is the sequence of coefficients + from lowest order "term" to highest, e.g., [1,2,3] represents the series ``T_0 + 2*T_1 + 3*T_2``. Parameters ---------- cs: array_like - 1d array of chebyshev series coefficients ordered from low to high. + 1-d array of Chebyshev series coefficients ordered from low to high. m : int, optional - Order of differentiation, must be non-negative. (default: 1) + Number of derivatives taken, must be non-negative. (Default: 1) scl : scalar, optional - The result of each derivation is multiplied by `scl`. The end - result is multiplication by `scl`**`m`. This is for use in a linear - change of variable. (default: 1) + Each differentiation is multiplied by `scl`. The end result is + multiplication by ``scl**m``. This is for use in a linear change of + variable. (Default: 1) Returns ------- @@ -655,8 +825,25 @@ def chebder(cs, m=1, scl=1) : -------- chebint + Notes + ----- + In general, the result of differentiating a C-series needs to be + "re-projected" onto the C-series basis set. Thus, typically, the + result of this function is "un-intuitive," albeit correct; see Examples + section below. + Examples -------- + >>> from numpy.polynomial import chebyshev as C + >>> cs = (1,2,3,4) + >>> C.chebder(cs) + array([ 14., 12., 24.]) + >>> C.chebder(cs,3) + array([ 96.]) + >>> C.chebder(cs,scl=-1) + array([-14., -12., -24.]) + >>> C.chebder(cs,2,-1) + array([ 12., 96.]) """ # cs is a trimmed copy @@ -678,49 +865,80 @@ def chebder(cs, m=1, scl=1) : def chebint(cs, m=1, k=[], lbnd=0, scl=1) : - """Integrate a Chebyshev series. - - Returns the series integrated from `lbnd` to x `m` times. At each - iteration the resulting series is multiplied by `scl` and an - integration constant specified by `k` is added. The scaling factor is - for use in a linear change of variable. The argument `cs` is a sequence - of coefficients ordered from low to high. i.e., [1,2,3] is the series - ``T_0 + 2*T_1 + 3*T_2``. - + """ + Integrate a Chebyshev series. + + Returns, as a C-series, the input C-series `cs`, integrated `m` times + from `lbnd` to `x`. At each iteration the resulting series is + **multiplied** by `scl` and an integration constant, `k`, is added. + The scaling factor is for use in a linear change of variable. ("Buyer + beware": note that, depending on what one is doing, one may want `scl` + to be the reciprocal of what one might expect; for more information, + see the Notes section below.) The argument `cs` is a sequence of + coefficients, from lowest order C-series "term" to highest, e.g., + [1,2,3] represents the series :math:`T_0(x) + 2T_1(x) + 3T_2(x)`. Parameters ---------- - cs: array_like - 1d array of chebyshev series coefficients ordered from low to high. + cs : array_like + 1-d array of C-series coefficients, ordered from low to high. m : int, optional - Order of integration, must be positeve. (default: 1) + Order of integration, must be positive. (Default: 1) k : {[], list, scalar}, optional - Integration constants. The value of the first integral at zero is - the first value in the list, the value of the second integral at - zero is the second value in the list, and so on. If ``[]`` - (default), all constants are set zero. If `m = 1`, a single scalar - can be given instead of a list. + Integration constant(s). The value of the first integral at zero + is the first value in the list, the value of the second integral + at zero is the second value, etc. If ``k == []`` (the default), + all constants are set to zero. If ``m == 1``, a single scalar can + be given instead of a list. lbnd : scalar, optional - The lower bound of the integral. (default: 0) + The lower bound of the integral. (Default: 0) scl : scalar, optional - Following each integration the result is multiplied by `scl` before - the integration constant is added. (default: 1) + Following each integration the result is *multiplied* by `scl` + before the integration constant is added. (Default: 1) Returns ------- - der : ndarray - Chebyshev series of the integral. + S : ndarray + C-series coefficients of the integral. Raises ------ ValueError + If ``m < 1``, ``len(k) > m``, ``np.isscalar(lbnd) == False``, or + ``np.isscalar(scl) == False``. See Also -------- chebder + Notes + ----- + Note that the result of each integration is *multiplied* by `scl`. + Why is this important to note? Say one is making a linear change of + variable :math:`u = ax + b` in an integral relative to `x`. Then + :math:`dx = du/a`, so one will need to set `scl` equal to :math:`1/a` + - perhaps not what one would have first thought. + + Also note that, in general, the result of integrating a C-series needs + to be "re-projected" onto the C-series basis set. Thus, typically, + the result of this function is "un-intuitive," albeit correct; see + Examples section below. + Examples -------- + >>> from numpy.polynomial import chebyshev as C + >>> cs = (1,2,3) + >>> C.chebint(cs) + array([ 0.5, -0.5, 0.5, 0.5]) + >>> C.chebint(cs,3) + array([ 0.03125 , -0.1875 , 0.04166667, -0.05208333, 0.01041667, + 0.00625 ]) + >>> C.chebint(cs, k=3) + array([ 3.5, -0.5, 0.5, 0.5]) + >>> C.chebint(cs,lbnd=-2) + array([ 8.5, -0.5, 0.5, 0.5]) + >>> C.chebint(cs,scl=-2) + array([-1., 1., -1., -1.]) """ if np.isscalar(k) : @@ -839,10 +1057,11 @@ def chebvander(x, deg) : return v def chebfit(x, y, deg, rcond=None, full=False): - """Least squares fit of Chebyshev series to data. + """ + Least squares fit of Chebyshev series to data. - Fit a Chebyshev series ``p(x) = p[0] * T_{deq}(x) + ... + p[deg] * - T_{0}(x)`` of degree `deg` to points `(x, y)`. Returns a vector of + Fit a Chebyshev series ``p(x) = p[0] * T_{0}(x) + ... + p[deg] * + T_{deg}(x)`` of degree `deg` to points `(x, y)`. Returns a vector of coefficients `p` that minimises the squared error. Parameters @@ -900,7 +1119,7 @@ def chebfit(x, y, deg, rcond=None, full=False): The solution are the coefficients ``c[i]`` of the Chebyshev series ``T(x)`` that minimizes the squared error - ``E = \sum_j |y_j - T(x_j)|^2``. + ``E = \\sum_j |y_j - T(x_j)|^2``. This problem is solved by setting up as the overdetermined matrix equation @@ -971,24 +1190,45 @@ def chebfit(x, y, deg, rcond=None, full=False): def chebroots(cs): - """Roots of a Chebyshev series. + """ + Compute the roots of a Chebyshev series. - Compute the roots of the Chebyshev series `cs`. The argument `cs` is a - sequence of coefficients ordered from low to high. i.e., [1,2,3] is the - series ``T_0 + 2*T_1 + 3*T_2``. + Return the roots (a.k.a "zeros") of the C-series represented by `cs`, + which is the sequence of the C-series' coefficients from lowest order + "term" to highest, e.g., [1,2,3] represents the C-series + ``T_0 + 2*T_1 + 3*T_2``. Parameters ---------- cs : array_like - 1D array of Chebyshev coefficients ordered from low to high. + 1-d array of C-series coefficients ordered from low to high. Returns ------- out : ndarray - An array containing the complex roots of the chebyshev series. + Array of the roots. If all the roots are real, then so is the + dtype of ``out``; otherwise, ``out``'s dtype is complex. + + See Also + -------- + polyroots + + Notes + ----- + Algorithm(s) used: + + Remember: because the C-series basis set is different from the + "standard" basis set, the results of this function *may* not be what + one is expecting. Examples -------- + >>> import numpy.polynomial as P + >>> import numpy.polynomial.chebyshev as C + >>> P.polyroots((-1,1,-1,1)) # x^3 - x^2 + x - 1 has two complex roots + array([ -4.99600361e-16-1.j, -4.99600361e-16+1.j, 1.00000e+00+0.j]) + >>> C.chebroots((-1,1,-1,1)) # T3 - T2 + T1 - T0 has only real roots + array([ -5.00000000e-01, 2.60860684e-17, 1.00000000e+00]) """ # cs is a trimmed copy diff --git a/numpy/polynomial/polynomial.py b/numpy/polynomial/polynomial.py index d5681f9ce..60b28c41f 100644 --- a/numpy/polynomial/polynomial.py +++ b/numpy/polynomial/polynomial.py @@ -1,43 +1,49 @@ -"""Functions for dealing with polynomials. +""" +Objects for dealing with polynomials. -This module provides a number of functions that are useful in dealing with -polynomials as well as a ``Polynomial`` class that encapsuletes the usual -arithmetic operations. All arrays of polynomial coefficients are assumed to -be ordered from low to high degree, thus `array([1,2,3])` will be treated -as the polynomial ``1 + 2*x + 3*x**2`` +This module provides a number of objects (mostly functions) useful for +dealing with polynomials, including a `Polynomial` class that +encapsulates the usual arithmetic operations. (General information +on how this module represents and works with polynomial objects is in +the docstring for its "parent" sub-package, `numpy.polynomial`). Constants --------- -- polydomain -- Polynomial default domain -- polyzero -- Polynomial that evaluates to 0. -- polyone -- Polynomial that evaluates to 1. -- polyx -- Polynomial of the identity map (x). +- `polydomain` -- Polynomial default domain, [-1,1]. +- `polyzero` -- (Coefficients of the) "zero polynomial." +- `polyone` -- (Coefficients of the) constant polynomial 1. +- `polyx` -- (Coefficients of the) identity map polynomial, ``f(x) = x``. Arithmetic ---------- -- polyadd -- add a polynomial to another. -- polysub -- subtract a polynomial from another. -- polymul -- multiply a polynomial by another -- polydiv -- divide one polynomial by another. -- polyval -- evaluate a polynomial at given points. +- `polyadd` -- add two polynomials. +- `polysub` -- subtract one polynomial from another. +- `polymul` -- multiply two polynomials. +- `polydiv` -- divide one polynomial by another. +- `polyval` -- evaluate a polynomial at given points. Calculus -------- -- polyder -- differentiate a polynomial. -- polyint -- integrate a polynomial. +- `polyder` -- differentiate a polynomial. +- `polyint` -- integrate a polynomial. Misc Functions -------------- -- polyfromroots -- create a polynomial with specified roots. -- polyroots -- find the roots of a polynomial. -- polyvander -- Vandermode like matrix for powers. -- polyfit -- least squares fit returning a polynomial. -- polytrim -- trim leading coefficients from a polynomial. -- polyline -- Polynomial of given straight line +- `polyfromroots` -- create a polynomial with specified roots. +- `polyroots` -- find the roots of a polynomial. +- `polyvander` -- Vandermonde-like matrix for powers. +- `polyfit` -- least-squares fit returning a polynomial. +- `polytrim` -- trim leading coefficients from a polynomial. +- `polyline` -- Given a straight line, return the equivalent polynomial + object. Classes ------- -- Polynomial -- polynomial class. +- `Polynomial` -- polynomial class. + +See also +-------- +`numpy.polynomial` """ from __future__ import division @@ -77,19 +83,31 @@ polyx = np.array([0,1]) # def polyline(off, scl) : - """Polynomial whose graph is a straight line. - - The line has the formula ``off + scl*x`` + """ + Returns an array representing a linear polynomial. - Parameters: - ----------- + Parameters + ---------- off, scl : scalars - The specified line is given by ``off + scl*x``. + The "y-intercept" and "slope" of the line, respectively. + + Returns + ------- + y : ndarray + This module's representation of the linear polynomial ``off + + scl*x``. - Returns: + See Also + -------- + chebline + + Examples -------- - series : 1d ndarray - The polynomial equal to ``off + scl*x``. + >>> from numpy import polynomial as P + >>> P.polyline(1,-1) + array([ 1, -1]) + >>> P.polyval(1, P.polyline(1,-1)) # should be 0 + 0.0 """ if scl != 0 : @@ -98,25 +116,53 @@ def polyline(off, scl) : return np.array([off]) def polyfromroots(roots) : - """Generate a polynomial with given roots. + """ + Generate a polynomial with the given roots. - Generate a polynomial whose roots are given by `roots`. The resulting - series is the produet `(x - roots[0])*(x - roots[1])*...` + Return the array of coefficients for the polynomial whose leading + coefficient (i.e., that of the highest order term) is `1` and whose + roots (a.k.a. "zeros") are given by *roots*. The returned array of + coefficients is ordered from lowest order term to highest, and zeros + of multiplicity greater than one must be included in *roots* a number + of times equal to their multiplicity (e.g., if `2` is a root of + multiplicity three, then [2,2,2] must be in *roots*). - Inputs - ------ + Parameters + ---------- roots : array_like - 1-d array containing the roots in sorted order. + Sequence containing the roots. Returns ------- - series : ndarray - 1-d array containing the coefficients of the Chebeshev series - ordered from low to high. + out : ndarray + 1-d array of the polynomial's coefficients, ordered from low to + high. If all roots are real, ``out.dtype`` is a float type; + otherwise, ``out.dtype`` is a complex type, even if all the + coefficients in the result are real (see Examples below). See Also -------- - polyroots + chebfromroots + + Notes + ----- + What is returned are the :math:`a_i` such that: + + .. math:: + + \\sum_{i=0}^{n} a_ix^i = \\prod_{i=0}^{n} (x - roots[i]) + + where ``n == len(roots)``; note that this implies that `1` is always + returned for :math:`a_n`. + + Examples + -------- + >>> import numpy.polynomial as P + >>> P.polyfromroots((-1,0,1)) # x(x - 1)(x + 1) = x^3 - x + array([ 0., -1., 0., 1.]) + >>> j = complex(0,1) + >>> P.polyfromroots((-j,j)) # complex returned, though values are real + array([ 1.+0.j, 0.+0.j, 1.+0.j]) """ if len(roots) == 0 : @@ -131,27 +177,37 @@ def polyfromroots(roots) : def polyadd(c1, c2): - """Add one polynomial to another. + """ + Add one polynomial to another. - Returns the sum of two polynomials `c1` + `c2`. The arguments are - sequences of coefficients ordered from low to high, i.e., [1,2,3] is - the polynomial ``1 + 2*x + 3*x**2"``. + Returns the sum of two polynomials `c1` + `c2`. The arguments are + sequences of coefficients from lowest order term to highest, i.e., + [1,2,3] represents the polynomial ``1 + 2*x + 3*x**2"``. Parameters ---------- c1, c2 : array_like - 1d arrays of polynomial coefficients ordered from low to - high. + 1-d arrays of polynomial coefficients ordered from low to high. Returns ------- out : ndarray - polynomial of the sum. + The coefficient array representing their sum. See Also -------- polysub, polymul, polydiv, polypow + Examples + -------- + >>> from numpy import polynomial as P + >>> c1 = (1,2,3) + >>> c2 = (3,2,1) + >>> sum = P.polyadd(c1,c2); sum + array([ 4., 4., 4.]) + >>> P.polyval(2, sum) # 4 + 4(2) + 4(2**2) + 28.0 + """ # c1, c2 are trimmed copies [c1, c2] = pu.as_series([c1, c2]) @@ -165,22 +221,23 @@ def polyadd(c1, c2): def polysub(c1, c2): - """Subtract one polynomial from another. + """ + Subtract one polynomial from another. - Returns the difference of two polynomials `c1` - `c2`. The arguments - are sequences of coefficients ordered from low to high, i.e., [1,2,3] - is the polynomial ``1 + 2*x + 3*x**2``. + Returns the difference of two polynomials `c1` - `c2`. The arguments + are sequences of coefficients from lowest order term to highest, i.e., + [1,2,3] represents the polynomial ``1 + 2*x + 3*x**2``. Parameters ---------- c1, c2 : array_like - 1d arrays of polynomial coefficients ordered from low to + 1-d arrays of polynomial coefficients ordered from low to high. Returns ------- out : ndarray - polynomial of the difference. + Of coefficients representing their difference. See Also -------- @@ -188,6 +245,13 @@ def polysub(c1, c2): Examples -------- + >>> from numpy import polynomial as P + >>> c1 = (1,2,3) + >>> c2 = (3,2,1) + >>> P.polysub(c1,c2) + array([-2., 0., 2.]) + >>> P.polysub(c2,c1) # -P.polysub(c1,c2) + array([ 2., 0., -2.]) """ # c1, c2 are trimmed copies @@ -203,27 +267,36 @@ def polysub(c1, c2): def polymul(c1, c2): - """Multiply one polynomial by another. + """ + Multiply one polynomial by another. - Returns the product of two polynomials `c1` * `c2`. The arguments - are sequences of coefficients ordered from low to high, i.e., [1,2,3] - is the polynomial ``1 + 2*x + 3*x**2.`` + Returns the product of two polynomials `c1` * `c2`. The arguments are + sequences of coefficients, from lowest order term to highest, e.g., + [1,2,3] represents the polynomial ``1 + 2*x + 3*x**2.`` Parameters ---------- c1, c2 : array_like - 1d arrays of polyyshev series coefficients ordered from low to - high. + 1-d arrays of coefficients representing a polynomial, relative to the + "standard" basis, and ordered from lowest order term to highest. Returns ------- out : ndarray - polynomial of the product. + Of the coefficients of their product. See Also -------- polyadd, polysub, polydiv, polypow + Examples + -------- + >>> import numpy.polynomial as P + >>> c1 = (1,2,3) + >>> c2 = (3,2,1) + >>> P.polymul(c1,c2) + array([ 3., 8., 14., 8., 3.]) + """ # c1, c2 are trimmed copies [c1, c2] = pu.as_series([c1, c2]) @@ -232,22 +305,22 @@ def polymul(c1, c2): def polydiv(c1, c2): - """Divide one polynomial by another. + """ + Divide one polynomial by another. - Returns the quotient of two polynomials `c1` / `c2`. The arguments are - sequences of coefficients ordered from low to high, i.e., [1,2,3] is - the series ``1 + 2*x + 3*x**2.`` + Returns the quotient-with-remainder of two polynomials `c1` / `c2`. + The arguments are sequences of coefficients, from lowest order term + to highest, e.g., [1,2,3] represents ``1 + 2*x + 3*x**2``. Parameters ---------- c1, c2 : array_like - 1d arrays of chebyshev series coefficients ordered from low to - high. + 1-d arrays of polynomial coefficients ordered from low to high. Returns ------- - [quo, rem] : ndarray - polynomial of the quotient and remainder. + [quo, rem] : ndarrays + Of coefficient series representing the quotient and remainder. See Also -------- @@ -255,6 +328,13 @@ def polydiv(c1, c2): Examples -------- + >>> import numpy.polynomial as P + >>> c1 = (1,2,3) + >>> c2 = (3,2,1) + >>> P.polydiv(c1,c2) + (array([ 3.]), array([-8., -4.])) + >>> P.polydiv(c2,c1) + (array([ 0.33333333]), array([ 2.66666667, 1.33333333])) """ # c1, c2 are trimmed copies @@ -331,27 +411,30 @@ def polypow(cs, pow, maxpower=None) : return prd def polyder(cs, m=1, scl=1) : - """Differentiate a polynomial. + """ + Differentiate a polynomial. - Returns the polynomial `cs` differentiated `m` times. The argument `cs` - is a sequence of coefficients ordered from low to high. i.e., [1,2,3] - is the series ``1 + 2*x + 3*x**2.`` + Returns the polynomial `cs` differentiated `m` times. At each + iteration the result is multiplied by `scl` (the scaling factor is for + use in a linear change of variable). The argument `cs` is the sequence + of coefficients from lowest order term to highest, e.g., [1,2,3] + represents the polynomial ``1 + 2*x + 3*x**2``. Parameters ---------- cs: array_like - 1d array of chebyshev series coefficients ordered from low to high. + 1-d array of polynomial coefficients ordered from low to high. m : int, optional - Order of differentiation, must be non-negative. (default: 1) + Number of derivatives taken, must be non-negative. (Default: 1) scl : scalar, optional - The result of each derivation is multiplied by `scl`. The end - result is multiplication by `scl`**`m`. This is for use in a linear - change of variable. (default: 1) + Each differentiation is multiplied by `scl`. The end result is + multiplication by ``scl**m``. This is for use in a linear change + of variable. (Default: 1) Returns ------- der : ndarray - polynomial of the derivative. + Polynomial of the derivative. See Also -------- @@ -359,6 +442,16 @@ def polyder(cs, m=1, scl=1) : Examples -------- + >>> from numpy import polynomial as P + >>> cs = (1,2,3,4) # 1 + 2x + 3x**2 + 4x**3 + >>> P.polyder(cs) # (d/dx)(cs) = 2 + 6x + 12x**2 + array([ 2., 6., 12.]) + >>> P.polyder(cs,3) # (d**3/dx**3)(cs) = 24 + array([ 24.]) + >>> P.polyder(cs,scl=-1) # (d/d(-x))(cs) = -2 - 6x - 12x**2 + array([ -2., -6., -12.]) + >>> P.polyder(cs,2,-1) # (d**2/d(-x)**2)(cs) = 6 + 24x + array([ 6., 24.]) """ # cs is a trimmed copy @@ -380,49 +473,75 @@ def polyder(cs, m=1, scl=1) : return cs[i+1:].copy() def polyint(cs, m=1, k=[], lbnd=0, scl=1) : - """Integrate a polynomial. - - Returns the polynomial `cs` integrated from `lbnd` to x `m` times. At - each iteration the resulting series is multiplied by `scl` and an - integration constant specified by `k` is added. The scaling factor is - for use in a linear change of variable. The argument `cs` is a sequence - of coefficients ordered from low to high. i.e., [1,2,3] is the - polynomial ``1 + 2*x + 3*x**2``. - + """ + Integrate a polynomial. + + Returns the polynomial `cs`, integrated `m` times from `lbnd` to `x`. + At each iteration the resulting series is **multiplied** by `scl` and + an integration constant, `k`, is added. The scaling factor is for use + in a linear change of variable. ("Buyer beware": note that, depending + on what one is doing, one may want `scl` to be the reciprocal of what + one might expect; for more information, see the Notes section below.) + The argument `cs` is a sequence of coefficients, from lowest order + term to highest, e.g., [1,2,3] represents the polynomial + ``1 + 2*x + 3*x**2``. Parameters ---------- cs : array_like - 1d array of chebyshev series coefficients ordered from low to high. + 1-d array of polynomial coefficients, ordered from low to high. m : int, optional - Order of integration, must be positeve. (default: 1) + Order of integration, must be positive. (Default: 1) k : {[], list, scalar}, optional - Integration constants. The value of the first integral at zero is - the first value in the list, the value of the second integral at - zero is the second value in the list, and so on. If ``[]`` - (default), all constants are set zero. If `m = 1`, a single scalar - can be given instead of a list. + Integration constant(s). The value of the first integral at zero + is the first value in the list, the value of the second integral + at zero is the second value, etc. If ``k == []`` (the default), + all constants are set to zero. If ``m == 1``, a single scalar can + be given instead of a list. lbnd : scalar, optional - The lower bound of the integral. (default: 0) + The lower bound of the integral. (Default: 0) scl : scalar, optional - Following each integration the result is multiplied by `scl` before - the integration constant is added. (default: 1) + Following each integration the result is *multiplied* by `scl` + before the integration constant is added. (Default: 1) Returns ------- - der : ndarray - polynomial of the integral. + S : ndarray + Coefficients of the integral. Raises ------ ValueError + If ``m < 1``, ``len(k) > m``, ``np.isscalar(lbnd) == False``, or + ``np.isscalar(scl) == False``. See Also -------- polyder + Notes + ----- + Note that the result of each integration is *multiplied* by `scl`. + Why is this important to note? Say one is making a linear change of + variable :math:`u = ax + b` in an integral relative to `x`. Then + :math:`dx = du/a`, so one will need to set `scl` equal to :math:`1/a` + - perhaps not what one would have first thought. + Examples -------- + >>> from numpy import polynomial as P + >>> cs = (1,2,3) + >>> P.polyint(cs) # should return array([0, 1, 1, 1]) + array([ 0., 1., 1., 1.]) + >>> P.polyint(cs,3) # should return array([0, 0, 0, 1/6, 1/12, 1/20]) + array([ 0. , 0. , 0. , 0.16666667, 0.08333333, + 0.05 ]) + >>> P.polyint(cs,k=3) # should return array([3, 1, 1, 1]) + array([ 3., 1., 1., 1.]) + >>> P.polyint(cs,lbnd=-2) # should return array([6, 1, 1, 1]) + array([ 6., 1., 1., 1.]) + >>> P.polyint(cs,scl=-2) # should return array([0, -2, -2, -2]) + array([ 0., -2., -2., -2.]) """ if np.isscalar(k) : @@ -448,7 +567,8 @@ def polyint(cs, m=1, k=[], lbnd=0, scl=1) : return ret def polyval(x, cs): - """Evaluate a polynomial. + """ + Evaluate a polynomial. If `cs` is of length `n`, this function returns : @@ -476,16 +596,10 @@ def polyval(x, cs): -------- polyfit - Examples - -------- - Notes ----- The evaluation uses Horner's method. - Examples - -------- - """ # cs is a trimmed copy [cs] = pu.as_series([cs]) @@ -529,50 +643,56 @@ def polyvander(x, deg) : return v def polyfit(x, y, deg, rcond=None, full=False): - """Least squares fit of polynomial to data. + """ + Least-squares fit of a polynomial to data. - Fit a polynomial ``p(x) = p[0] * T_{deq}(x) + ... + p[deg] * - T_{0}(x)`` of degree `deg` to points `(x, y)`. Returns a vector of - coefficients `p` that minimises the squared error. + Fit a polynomial ``c0 + c1*x + c2*x**2 + ... + c[deg]*x**deg`` to + points (`x`, `y`). Returns a 1-d (if `y` is 1-d) or 2-d (if `y` is 2-d) + array of coefficients representing, from lowest order term to highest, + the polynomial(s) which minimize the total square error. 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. + x : array_like, shape (`M`,) + x-coordinates of the `M` sample (data) points ``(x[i], y[i])``. + y : array_like, shape (`M`,) or (`M`, `K`) + y-coordinates of the sample points. Several sets of sample points + sharing the same x-coordinates can be (independently) fit with one + call to `polyfit` by passing in for `y` a 2-d array that contains + one data set per column. deg : int - Degree of the fitting polynomial + Degree of the polynomial(s) to be fit. 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. + Relative condition number of the fit. Singular values smaller + than `rcond`, relative to the largest singular value, will be + ignored. The default value is ``len(x)*eps``, where `eps` is the + relative precision of the platform's 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. + Switch determining the nature of the return value. When ``False`` + (the default) just the coefficients are returned; when ``True``, + diagnostic information from the singular value decomposition (used + to solve the fit's matrix equation) is also returned. Returns ------- - coef : ndarray, shape (M,) or (M, K) - Polynomial coefficients ordered from low to high. If `y` was 2-D, - the coefficients for the data in column k of `y` are in column - `k`. + coef : ndarray, shape (`deg` + 1,) or (`deg` + 1, `K`) + Polynomial coefficients ordered from low to high. If `y` was 2-d, + the coefficients in column `k` of `coef` represent the polynomial + fit to the data in `y`'s `k`-th column. - [residuals, rank, singular_values, rcond] : present when `full` = True - Residuals of the least-squares fit, the effective rank of the - scaled Vandermonde matrix and its singular values, and the - specified value of `rcond`. For more details, see `linalg.lstsq`. + [residuals, rank, singular_values, rcond] : present when `full` == True + Sum of the squared residuals (SSR) of the least-squares fit; the + effective rank of the scaled Vandermonde matrix; its singular + values; and the specified value of `rcond`. For more information, + see `linalg.lstsq`. - Warns - ----- + Raises + ------ 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 + Raised if the matrix in the least-squares fit is rank deficient. + The warning is only raised if `full` == False. The warnings can + be turned off by: >>> import warnings >>> warnings.simplefilter('ignore', RankWarning) @@ -587,41 +707,60 @@ def polyfit(x, y, deg, rcond=None, full=False): Notes ----- - The solution are the coefficients ``c[i]`` of the polynomial ``P(x)`` - that minimizes the squared error - - ``E = \sum_j |y_j - P(x_j)|^2``. - - This problem is solved by setting up as the overdetermined matrix - equation - - ``V(x)*c = y``, - - where ``V`` is the Vandermonde matrix of `x`, the elements of ``c`` are - the coefficients to be solved for, and the elements of `y` are the - observed values. This equation is then solved using the singular value - decomposition of ``V``. - - If some of the singular values of ``V`` are so small that they are - neglected, then a `RankWarning` will be issued. This means that the - coeficient values may be poorly determined. Using a lower order fit - will usually get rid of the warning. The `rcond` parameter can also be - set to a value smaller than its default, but the resulting fit may be - spurious and have large contributions from roundoff error. - - Fits using double precision and polynomials tend to fail at about - degree 20. Fits using Chebyshev series are generally better - conditioned, but much can still depend on the distribution of the - sample points and the smoothness of the data. If the quality of the fit - is inadequate splines may be a good alternative. - - References - ---------- - .. [1] Wikipedia, "Curve fitting", - http://en.wikipedia.org/wiki/Curve_fitting + The solutions are the coefficients ``c[i]`` of the polynomial ``P(x)`` + that minimizes the total squared error: + + .. math :: E = \\sum_j (y_j - P(x_j))^2 + + This problem is solved by setting up the (typically) over-determined + matrix equation: + + .. math :: V(x)*c = y + + where `V` is the Vandermonde matrix of `x`, the elements of `c` are the + coefficients to be solved for, and the elements of `y` are the observed + values. This equation is then solved using the singular value + decomposition of `V`. + + If some of the singular values of `V` are so small that they are + neglected (and `full` == ``False``), a `RankWarning` will be raised. + This means that the coefficient values may be poorly determined. + Fitting to a lower order polynomial will usually get rid of the warning + (but may not be what you want, of course; if you have independent + reason(s) for choosing the degree which isn't working, you may have to: + a) reconsider those reasons, and/or b) reconsider the quality of your + data). The `rcond` parameter can also be set to a value smaller than + its default, but the resulting fit may be spurious and have large + contributions from roundoff error. + + Polynomial fits using double precision tend to "fail" at about + (polynomial) degree 20. Fits using Chebyshev series are generally + better conditioned, but much can still depend on the distribution of + the sample points and the smoothness of the data. If the quality of + the fit is inadequate, splines may be a good alternative. Examples -------- + >>> from numpy import polynomial as P + >>> x = np.linspace(-1,1,51) # x "data": [-1, -0.96, ..., 0.96, 1] + >>> y = x**3 - x + np.random.randn(len(x)) # x^3 - x + N(0,1) "noise" + >>> c, stats = P.polyfit(x,y,3,full=True) + >>> c # c[0], c[2] should be approx. 0, c[1] approx. -1, c[3] approx. 1 + array([ 0.01909725, -1.30598256, -0.00577963, 1.02644286]) + >>> stats # note the large SSR, explaining the rather poor results + [array([ 38.06116253]), 4, array([ 1.38446749, 1.32119158, 0.50443316, + 0.28853036]), 1.1324274851176597e-014] + + Same thing without the added noise + + >>> y = x**3 - x + >>> c, stats = P.polyfit(x,y,3,full=True) + >>> c # c[0], c[2] should be "very close to 0", c[1] ~= -1, c[3] ~= 1 + array([ -1.73362882e-17, -1.00000000e+00, -2.67471909e-16, + 1.00000000e+00]) + >>> stats # note the minuscule SSR + [array([ 7.46346754e-31]), 4, array([ 1.38446749, 1.32119158, + 0.50443316, 0.28853036]), 1.1324274851176597e-014] """ order = int(deg) + 1 @@ -662,24 +801,39 @@ def polyfit(x, y, deg, rcond=None, full=False): def polyroots(cs): - """Roots of a polynomial. + """ + Compute the roots of a polynomial. - Compute the roots of the Chebyshev series `cs`. The argument `cs` is a - sequence of coefficients ordered from low to high. i.e., [1,2,3] is the - polynomial ``1 + 2*x + 3*x**2``. + Return the roots (a.k.a. "zeros") of the "polynomial" `cs`, the + polynomial's coefficients from lowest order term to highest + (e.g., [1,2,3] represents the polynomial ``1 + 2*x + 3*x**2``). Parameters ---------- - cs : array_like of shape(M,) - 1D array of polynomial coefficients ordered from low to high. + cs : array_like of shape (M,) + 1-d array of polynomial coefficients ordered from low to high. Returns ------- out : ndarray - An array containing the complex roots of the polynomial series. + Array of the roots of the polynomial. If all the roots are real, + then so is the dtype of ``out``; otherwise, ``out``'s dtype is + complex. + + See Also + -------- + chebroots Examples -------- + >>> import numpy.polynomial as P + >>> P.polyroots(P.polyfromroots((-1,0,1))) + array([-1., 0., 1.]) + >>> P.polyroots(P.polyfromroots((-1,0,1))).dtype + dtype('float64') + >>> j = complex(0,1) + >>> P.polyroots(P.polyfromroots((-j,0,j))) + array([ 0.00000000e+00+0.j, 0.00000000e+00+1.j, 2.77555756e-17-1.j]) """ # cs is a trimmed copy diff --git a/numpy/polynomial/polytemplate.py b/numpy/polynomial/polytemplate.py index 512fd9201..29691496b 100644 --- a/numpy/polynomial/polytemplate.py +++ b/numpy/polynomial/polytemplate.py @@ -1,4 +1,12 @@ -"""Template for the Chebyshev and Polynomial classes. +""" +Template for the Chebyshev and Polynomial classes. + +This module houses a Python string module Template object (see, e.g., +http://docs.python.org/library/string.html#template-strings) used by +the `polynomial` and `chebyshev` modules to implement their respective +`Polynomial` and `Chebyshev` classes. It provides a mechanism for easily +creating additional specific polynomial classes (e.g., Legendre, Jacobi, +etc.) in the future, such that all these classes will have a common API. """ import string diff --git a/numpy/polynomial/polyutils.py b/numpy/polynomial/polyutils.py index 4d9516a03..5c65e03c2 100644 --- a/numpy/polynomial/polyutils.py +++ b/numpy/polynomial/polyutils.py @@ -1,30 +1,34 @@ -"""Utililty functions for polynomial modules. +""" +Utililty objects for the polynomial modules. -This modules provides errors, warnings, and a polynomial base class along -with some common routines that are used in both the polynomial and -chebyshev modules. +This module provides: error and warning objects; a polynomial base class; +and some routines used in both the `polynomial` and `chebyshev` modules. -Errors ------- -- PolyError -- base class for errors -- PolyDomainError -- mismatched domains +Error objects +------------- +- `PolyError` -- base class for this sub-package's errors. +- `PolyDomainError` -- raised when domains are "mismatched." -Warnings --------- -- RankWarning -- issued by least squares fits to warn of deficient rank +Warning objects +--------------- +- `RankWarning` -- raised by a least-squares fit when a rank-deficient + matrix is encountered. -Base Class +Base class ---------- -- PolyBase -- Base class for the Polynomial and Chebyshev classes. +- `PolyBase` -- The base class for the `Polynomial` and `Chebyshev` + classes. Functions --------- -- as_series -- turns list of array_like into 1d arrays of common type -- trimseq -- removes trailing zeros -- trimcoef -- removes trailing coefficients less than given magnitude -- getdomain -- finds appropriate domain for collection of points -- mapdomain -- maps points between domains -- mapparms -- parameters of the linear map between domains +- `as_series` -- turns a list of array_likes into 1-D arrays of common + type. +- `trimseq` -- removes trailing zeros. +- `trimcoef` -- removes trailing coefficients that are less than a given + magnitude (thereby removing the corresponding terms). +- `getdomain` -- returns a domain appropriate for a given set of abscissae. +- `mapdomain` -- maps points between domains. +- `mapparms` -- parameters of the linear map between domains. """ from __future__ import division @@ -109,29 +113,44 @@ def trimseq(seq) : def as_series(alist, trim=True) : - """Return arguments as a list of 1d arrays. + """ + Return argument as a list of 1-d arrays. - The return type will always be an array of double, complex double. or - object. + The returned list contains array(s) of dtype double, complex double, or + object. A 1-d argument of shape ``(N,)`` is parsed into ``N`` arrays of + size one; a 2-d argument of shape ``(M,N)`` is parsed into ``M`` arrays + of size ``N`` (i.e., is "parsed by row"); and a higher dimensional array + raises a Value Error if it is not first reshaped into either a 1-d or 2-d + array. Parameters ---------- - [a1, a2,...] : list of array_like. - The arrays must have no more than one dimension when converted. - trim : boolean + a : array_like + A 1- or 2-d array_like + trim : boolean, optional When True, trailing zeros are removed from the inputs. When False, the inputs are passed through intact. Returns ------- [a1, a2,...] : list of 1d-arrays - A copy of the input data as a 1d-arrays. + A copy of the input data as a list of 1-d arrays. Raises ------ ValueError : - Raised when an input can not be coverted to 1-d array or the - resulting array is empty. + Raised when `as_series` cannot convert its input to 1-d arrays, or at + least one of the resulting arrays is empty. + + Examples + -------- + >>> from numpy import polynomial as P + >>> a = np.arange(4) + >>> P.as_series(a) + [array([ 0.]), array([ 1.]), array([ 2.]), array([ 3.])] + >>> b = np.arange(6).reshape((2,3)) + >>> P.as_series(b) + [array([ 0., 1., 2.]), array([ 3., 4., 5.])] """ arrays = [np.array(a, ndmin=1, copy=0) for a in alist] @@ -161,24 +180,47 @@ def as_series(alist, trim=True) : def trimcoef(c, tol=0) : - """Remove small trailing coefficients from a polynomial series. + """ + Remove "small" "trailing" coefficients from a polynomial. + + "Small" means "small in absolute value" and is controlled by the + parameter `tol`; "trailing" means highest order coefficient(s), e.g., in + ``[0, 1, 1, 0, 0]`` (which represents ``0 + x + x**2 + 0*x**3 + 0*x**4``) + both the 3-rd and 4-th order coefficients would be "trimmed." Parameters ---------- c : array_like - 1-d array of coefficients, ordered from low to high. - tol : number - Trailing elements with absolute value less than tol are removed. + 1-d array of coefficients, ordered from lowest order to highest. + tol : number, optional + Trailing (i.e., highest order) elements with absolute value less + than or equal to `tol` (default value is zero) are removed. Returns ------- trimmed : ndarray - 1_d array with tailing zeros removed. If the resulting series would - be empty, a series containing a singel zero is returned. + 1-d array with trailing zeros removed. If the resulting series + would be empty, a series containing a single zero is returned. Raises ------ - ValueError : if tol < 0 + ValueError + If `tol` < 0 + + See Also + -------- + trimseq + + Examples + -------- + >>> from numpy import polynomial as P + >>> P.trimcoef((0,0,3,0,5,0,0)) + array([ 0., 0., 3., 0., 5.]) + >>> P.trimcoef((0,0,1e-3,0,1e-5,0,0),1e-3) # item == tol is trimmed + array([ 0.]) + >>> i = complex(0,1) # works for complex + >>> P.trimcoef((3e-4,1e-3*(1-i),5e-4,2e-5*(1+i)), 1e-3) + array([ 0.0003+0.j , 0.0010-0.001j]) """ if tol < 0 : @@ -192,29 +234,42 @@ def trimcoef(c, tol=0) : return c[:ind[-1] + 1].copy() def getdomain(x) : - """Determine suitable domain for given points. + """ + Return a domain suitable for given abscissae. + + Find a domain suitable for a polynomial or Chebyshev series + defined at the values supplied. - Find a suitable domain in which to fit a function defined at the points - `x` with a polynomial or Chebyshev series. - Parameters ---------- x : array_like - 1D array of points whose domain will be determined. + 1-d array of abscissae whose domain will be determined. Returns ------- domain : ndarray - 1D ndarray containing two values. If the inputs are complex, then - the two points are the corners of the smallest rectangle alinged - with the axes in the complex plane containing the points `x`. If - the inputs are real, then the two points are the ends of the - smallest interval containing the points `x`, + 1-d array containing two values. If the inputs are complex, then + the two returned points are the lower left and upper right corners + of the smallest rectangle (aligned with the axes) in the complex + plane containing the points `x`. If the inputs are real, then the + two points are the ends of the smallest interval containing the + points `x`. See Also -------- mapparms, mapdomain + Examples + -------- + >>> from numpy.polynomial import polyutils as pu + >>> points = np.arange(4)**2 - 5; points + array([-5, -4, -1, 4]) + >>> pu.getdomain(points) + array([-5., 4.]) + >>> c = np.exp(complex(0,1)*np.pi*np.arange(12)/6) # unit circle + >>> pu.getdomain(c) + array([-1.-1.j, 1.+1.j]) + """ [x] = as_series([x], trim=False) if x.dtype.char in np.typecodes['Complex'] : @@ -225,27 +280,45 @@ def getdomain(x) : return np.array((x.min(), x.max())) def mapparms(old, new) : - """Linear map between domains. + """ + Linear map parameters between domains. - Return the parameters of the linear map ``off + scl*x`` that maps the - `old` domain to the `new` domain. The map is defined by the requirement - that the left end of the old domain map to the left end of the new - domain, and similarly for the right ends. + Return the parameters of the linear map ``offset + scale*x`` that maps + `old` to `new` such that ``old[i] -> new[i]``, ``i = 0, 1``. Parameters ---------- old, new : array_like - The two domains should convert as 1D arrays containing two values. + Each domain must (successfully) convert to a 1-d array containing + precisely two values. Returns ------- - off, scl : scalars - The map `=``off + scl*x`` maps the first domain to the second. + offset, scale : scalars + The map ``L(x) = offset + scale*x`` maps the first domain to the + second. See Also -------- getdomain, mapdomain + Notes + ----- + Also works for complex numbers, and thus can be used to calculate the + parameters required to map any line in the complex plane to any other + line therein. + + Examples + -------- + >>> from numpy import polynomial as P + >>> P.mapparms((-1,1),(-1,1)) + (0.0, 1.0) + >>> P.mapparms((1,-1),(-1,1)) + (0.0, -1.0) + >>> i = complex(0,1) + >>> P.mapparms((-i,-1),(1,i)) + ((1+1j), (1+0j)) + """ oldlen = old[1] - old[0] newlen = new[1] - new[0] @@ -254,30 +327,66 @@ def mapparms(old, new) : return off, scl def mapdomain(x, old, new) : - """Apply linear map to input points. - - The linear map of the form ``off + scl*x`` that takes the `old` domain - to the `new` domain is applied to the points `x`. + """ + Apply linear map to input points. + + The linear map ``offset + scale*x`` that maps `old` to `new` is applied + to the points `x`. Parameters ---------- x : array_like - Points to be mapped + Points to be mapped. old, new : array_like - The two domains that determin the map. They should both convert as - 1D arrays containing two values. - + The two domains that determine the map. Each must (successfully) + convert to 1-d arrays containing precisely two values. Returns ------- - new_x : ndarray - Array of points of the same shape as the input `x` after the linear - map defined by the two domains is applied. + x_out : ndarray + Array of points of the same shape as `x`, after application of the + linear map between the two domains. See Also -------- getdomain, mapparms + Notes + ----- + Effectively, this implements: + + .. math :: + x\\_out = new[0] + m(x - old[0]) + + where + + .. math :: + m = \\frac{new[1]-new[0]}{old[1]-old[0]} + + Examples + -------- + >>> from numpy import polynomial as P + >>> old_domain = (-1,1) + >>> new_domain = (0,2*np.pi) + >>> x = np.linspace(-1,1,6); x + array([-1. , -0.6, -0.2, 0.2, 0.6, 1. ]) + >>> x_out = P.mapdomain(x, old_domain, new_domain); x_out + array([ 0. , 1.25663706, 2.51327412, 3.76991118, 5.02654825, + 6.28318531]) + >>> x - P.mapdomain(x_out, new_domain, old_domain) + array([ 0., 0., 0., 0., 0., 0.]) + + Also works for complex numbers (and thus can be used to map any line in + the complex plane to any other line therein). + + >>> i = complex(0,1) + >>> old = (-1 - i, 1 + i) + >>> new = (-1 + i, 1 - i) + >>> z = np.linspace(old[0], old[1], 6); z + array([-1.0-1.j , -0.6-0.6j, -0.2-0.2j, 0.2+0.2j, 0.6+0.6j, 1.0+1.j ]) + >>> new_z = P.mapdomain(z, old, new); new_z + array([-1.0+1.j , -0.6+0.6j, -0.2+0.2j, 0.2-0.2j, 0.6-0.6j, 1.0-1.j ]) + """ [x] = as_series([x], trim=False) off, scl = mapparms(old, new) |