Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/numpy/lib/polynomial.py : 21%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2Functions to operate on polynomials.
4"""
5__all__ = ['poly', 'roots', 'polyint', 'polyder', 'polyadd',
6 'polysub', 'polymul', 'polydiv', 'polyval', 'poly1d',
7 'polyfit', 'RankWarning']
9import functools
10import re
11import warnings
12import numpy.core.numeric as NX
14from numpy.core import (isscalar, abs, finfo, atleast_1d, hstack, dot, array,
15 ones)
16from numpy.core import overrides
17from numpy.core.overrides import set_module
18from numpy.lib.twodim_base import diag, vander
19from numpy.lib.function_base import trim_zeros
20from numpy.lib.type_check import iscomplex, real, imag, mintypecode
21from numpy.linalg import eigvals, lstsq, inv
24array_function_dispatch = functools.partial(
25 overrides.array_function_dispatch, module='numpy')
28@set_module('numpy')
29class RankWarning(UserWarning):
30 """
31 Issued by `polyfit` when the Vandermonde matrix is rank deficient.
33 For more information, a way to suppress the warning, and an example of
34 `RankWarning` being issued, see `polyfit`.
36 """
37 pass
40def _poly_dispatcher(seq_of_zeros):
41 return seq_of_zeros
44@array_function_dispatch(_poly_dispatcher)
45def poly(seq_of_zeros):
46 """
47 Find the coefficients of a polynomial with the given sequence of roots.
49 Returns the coefficients of the polynomial whose leading coefficient
50 is one for the given sequence of zeros (multiple roots must be included
51 in the sequence as many times as their multiplicity; see Examples).
52 A square matrix (or array, which will be treated as a matrix) can also
53 be given, in which case the coefficients of the characteristic polynomial
54 of the matrix are returned.
56 Parameters
57 ----------
58 seq_of_zeros : array_like, shape (N,) or (N, N)
59 A sequence of polynomial roots, or a square array or matrix object.
61 Returns
62 -------
63 c : ndarray
64 1D array of polynomial coefficients from highest to lowest degree:
66 ``c[0] * x**(N) + c[1] * x**(N-1) + ... + c[N-1] * x + c[N]``
67 where c[0] always equals 1.
69 Raises
70 ------
71 ValueError
72 If input is the wrong shape (the input must be a 1-D or square
73 2-D array).
75 See Also
76 --------
77 polyval : Compute polynomial values.
78 roots : Return the roots of a polynomial.
79 polyfit : Least squares polynomial fit.
80 poly1d : A one-dimensional polynomial class.
82 Notes
83 -----
84 Specifying the roots of a polynomial still leaves one degree of
85 freedom, typically represented by an undetermined leading
86 coefficient. [1]_ In the case of this function, that coefficient -
87 the first one in the returned array - is always taken as one. (If
88 for some reason you have one other point, the only automatic way
89 presently to leverage that information is to use ``polyfit``.)
91 The characteristic polynomial, :math:`p_a(t)`, of an `n`-by-`n`
92 matrix **A** is given by
94 :math:`p_a(t) = \\mathrm{det}(t\\, \\mathbf{I} - \\mathbf{A})`,
96 where **I** is the `n`-by-`n` identity matrix. [2]_
98 References
99 ----------
100 .. [1] M. Sullivan and M. Sullivan, III, "Algebra and Trignometry,
101 Enhanced With Graphing Utilities," Prentice-Hall, pg. 318, 1996.
103 .. [2] G. Strang, "Linear Algebra and Its Applications, 2nd Edition,"
104 Academic Press, pg. 182, 1980.
106 Examples
107 --------
108 Given a sequence of a polynomial's zeros:
110 >>> np.poly((0, 0, 0)) # Multiple root example
111 array([1., 0., 0., 0.])
113 The line above represents z**3 + 0*z**2 + 0*z + 0.
115 >>> np.poly((-1./2, 0, 1./2))
116 array([ 1. , 0. , -0.25, 0. ])
118 The line above represents z**3 - z/4
120 >>> np.poly((np.random.random(1)[0], 0, np.random.random(1)[0]))
121 array([ 1. , -0.77086955, 0.08618131, 0. ]) # random
123 Given a square array object:
125 >>> P = np.array([[0, 1./3], [-1./2, 0]])
126 >>> np.poly(P)
127 array([1. , 0. , 0.16666667])
129 Note how in all cases the leading coefficient is always 1.
131 """
132 seq_of_zeros = atleast_1d(seq_of_zeros)
133 sh = seq_of_zeros.shape
135 if len(sh) == 2 and sh[0] == sh[1] and sh[0] != 0:
136 seq_of_zeros = eigvals(seq_of_zeros)
137 elif len(sh) == 1:
138 dt = seq_of_zeros.dtype
139 # Let object arrays slip through, e.g. for arbitrary precision
140 if dt != object:
141 seq_of_zeros = seq_of_zeros.astype(mintypecode(dt.char))
142 else:
143 raise ValueError("input must be 1d or non-empty square 2d array.")
145 if len(seq_of_zeros) == 0:
146 return 1.0
147 dt = seq_of_zeros.dtype
148 a = ones((1,), dtype=dt)
149 for k in range(len(seq_of_zeros)):
150 a = NX.convolve(a, array([1, -seq_of_zeros[k]], dtype=dt),
151 mode='full')
153 if issubclass(a.dtype.type, NX.complexfloating):
154 # if complex roots are all complex conjugates, the roots are real.
155 roots = NX.asarray(seq_of_zeros, complex)
156 if NX.all(NX.sort(roots) == NX.sort(roots.conjugate())):
157 a = a.real.copy()
159 return a
162def _roots_dispatcher(p):
163 return p
166@array_function_dispatch(_roots_dispatcher)
167def roots(p):
168 """
169 Return the roots of a polynomial with coefficients given in p.
171 The values in the rank-1 array `p` are coefficients of a polynomial.
172 If the length of `p` is n+1 then the polynomial is described by::
174 p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n]
176 Parameters
177 ----------
178 p : array_like
179 Rank-1 array of polynomial coefficients.
181 Returns
182 -------
183 out : ndarray
184 An array containing the roots of the polynomial.
186 Raises
187 ------
188 ValueError
189 When `p` cannot be converted to a rank-1 array.
191 See also
192 --------
193 poly : Find the coefficients of a polynomial with a given sequence
194 of roots.
195 polyval : Compute polynomial values.
196 polyfit : Least squares polynomial fit.
197 poly1d : A one-dimensional polynomial class.
199 Notes
200 -----
201 The algorithm relies on computing the eigenvalues of the
202 companion matrix [1]_.
204 References
205 ----------
206 .. [1] R. A. Horn & C. R. Johnson, *Matrix Analysis*. Cambridge, UK:
207 Cambridge University Press, 1999, pp. 146-7.
209 Examples
210 --------
211 >>> coeff = [3.2, 2, 1]
212 >>> np.roots(coeff)
213 array([-0.3125+0.46351241j, -0.3125-0.46351241j])
215 """
216 # If input is scalar, this makes it an array
217 p = atleast_1d(p)
218 if p.ndim != 1:
219 raise ValueError("Input must be a rank-1 array.")
221 # find non-zero array entries
222 non_zero = NX.nonzero(NX.ravel(p))[0]
224 # Return an empty array if polynomial is all zeros
225 if len(non_zero) == 0:
226 return NX.array([])
228 # find the number of trailing zeros -- this is the number of roots at 0.
229 trailing_zeros = len(p) - non_zero[-1] - 1
231 # strip leading and trailing zeros
232 p = p[int(non_zero[0]):int(non_zero[-1])+1]
234 # casting: if incoming array isn't floating point, make it floating point.
235 if not issubclass(p.dtype.type, (NX.floating, NX.complexfloating)):
236 p = p.astype(float)
238 N = len(p)
239 if N > 1:
240 # build companion matrix and find its eigenvalues (the roots)
241 A = diag(NX.ones((N-2,), p.dtype), -1)
242 A[0,:] = -p[1:] / p[0]
243 roots = eigvals(A)
244 else:
245 roots = NX.array([])
247 # tack any zeros onto the back of the array
248 roots = hstack((roots, NX.zeros(trailing_zeros, roots.dtype)))
249 return roots
252def _polyint_dispatcher(p, m=None, k=None):
253 return (p,)
256@array_function_dispatch(_polyint_dispatcher)
257def polyint(p, m=1, k=None):
258 """
259 Return an antiderivative (indefinite integral) of a polynomial.
261 The returned order `m` antiderivative `P` of polynomial `p` satisfies
262 :math:`\\frac{d^m}{dx^m}P(x) = p(x)` and is defined up to `m - 1`
263 integration constants `k`. The constants determine the low-order
264 polynomial part
266 .. math:: \\frac{k_{m-1}}{0!} x^0 + \\ldots + \\frac{k_0}{(m-1)!}x^{m-1}
268 of `P` so that :math:`P^{(j)}(0) = k_{m-j-1}`.
270 Parameters
271 ----------
272 p : array_like or poly1d
273 Polynomial to integrate.
274 A sequence is interpreted as polynomial coefficients, see `poly1d`.
275 m : int, optional
276 Order of the antiderivative. (Default: 1)
277 k : list of `m` scalars or scalar, optional
278 Integration constants. They are given in the order of integration:
279 those corresponding to highest-order terms come first.
281 If ``None`` (default), all constants are assumed to be zero.
282 If `m = 1`, a single scalar can be given instead of a list.
284 See Also
285 --------
286 polyder : derivative of a polynomial
287 poly1d.integ : equivalent method
289 Examples
290 --------
291 The defining property of the antiderivative:
293 >>> p = np.poly1d([1,1,1])
294 >>> P = np.polyint(p)
295 >>> P
296 poly1d([ 0.33333333, 0.5 , 1. , 0. ]) # may vary
297 >>> np.polyder(P) == p
298 True
300 The integration constants default to zero, but can be specified:
302 >>> P = np.polyint(p, 3)
303 >>> P(0)
304 0.0
305 >>> np.polyder(P)(0)
306 0.0
307 >>> np.polyder(P, 2)(0)
308 0.0
309 >>> P = np.polyint(p, 3, k=[6,5,3])
310 >>> P
311 poly1d([ 0.01666667, 0.04166667, 0.16666667, 3. , 5. , 3. ]) # may vary
313 Note that 3 = 6 / 2!, and that the constants are given in the order of
314 integrations. Constant of the highest-order polynomial term comes first:
316 >>> np.polyder(P, 2)(0)
317 6.0
318 >>> np.polyder(P, 1)(0)
319 5.0
320 >>> P(0)
321 3.0
323 """
324 m = int(m)
325 if m < 0:
326 raise ValueError("Order of integral must be positive (see polyder)")
327 if k is None:
328 k = NX.zeros(m, float)
329 k = atleast_1d(k)
330 if len(k) == 1 and m > 1:
331 k = k[0]*NX.ones(m, float)
332 if len(k) < m:
333 raise ValueError(
334 "k must be a scalar or a rank-1 array of length 1 or >m.")
336 truepoly = isinstance(p, poly1d)
337 p = NX.asarray(p)
338 if m == 0:
339 if truepoly:
340 return poly1d(p)
341 return p
342 else:
343 # Note: this must work also with object and integer arrays
344 y = NX.concatenate((p.__truediv__(NX.arange(len(p), 0, -1)), [k[0]]))
345 val = polyint(y, m - 1, k=k[1:])
346 if truepoly:
347 return poly1d(val)
348 return val
351def _polyder_dispatcher(p, m=None):
352 return (p,)
355@array_function_dispatch(_polyder_dispatcher)
356def polyder(p, m=1):
357 """
358 Return the derivative of the specified order of a polynomial.
360 Parameters
361 ----------
362 p : poly1d or sequence
363 Polynomial to differentiate.
364 A sequence is interpreted as polynomial coefficients, see `poly1d`.
365 m : int, optional
366 Order of differentiation (default: 1)
368 Returns
369 -------
370 der : poly1d
371 A new polynomial representing the derivative.
373 See Also
374 --------
375 polyint : Anti-derivative of a polynomial.
376 poly1d : Class for one-dimensional polynomials.
378 Examples
379 --------
380 The derivative of the polynomial :math:`x^3 + x^2 + x^1 + 1` is:
382 >>> p = np.poly1d([1,1,1,1])
383 >>> p2 = np.polyder(p)
384 >>> p2
385 poly1d([3, 2, 1])
387 which evaluates to:
389 >>> p2(2.)
390 17.0
392 We can verify this, approximating the derivative with
393 ``(f(x + h) - f(x))/h``:
395 >>> (p(2. + 0.001) - p(2.)) / 0.001
396 17.007000999997857
398 The fourth-order derivative of a 3rd-order polynomial is zero:
400 >>> np.polyder(p, 2)
401 poly1d([6, 2])
402 >>> np.polyder(p, 3)
403 poly1d([6])
404 >>> np.polyder(p, 4)
405 poly1d([0.])
407 """
408 m = int(m)
409 if m < 0:
410 raise ValueError("Order of derivative must be positive (see polyint)")
412 truepoly = isinstance(p, poly1d)
413 p = NX.asarray(p)
414 n = len(p) - 1
415 y = p[:-1] * NX.arange(n, 0, -1)
416 if m == 0:
417 val = p
418 else:
419 val = polyder(y, m - 1)
420 if truepoly:
421 val = poly1d(val)
422 return val
425def _polyfit_dispatcher(x, y, deg, rcond=None, full=None, w=None, cov=None):
426 return (x, y, w)
429@array_function_dispatch(_polyfit_dispatcher)
430def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False):
431 """
432 Least squares polynomial fit.
434 Fit a polynomial ``p(x) = p[0] * x**deg + ... + p[deg]`` of degree `deg`
435 to points `(x, y)`. Returns a vector of coefficients `p` that minimises
436 the squared error in the order `deg`, `deg-1`, ... `0`.
438 The `Polynomial.fit <numpy.polynomial.polynomial.Polynomial.fit>` class
439 method is recommended for new code as it is more stable numerically. See
440 the documentation of the method for more information.
442 Parameters
443 ----------
444 x : array_like, shape (M,)
445 x-coordinates of the M sample points ``(x[i], y[i])``.
446 y : array_like, shape (M,) or (M, K)
447 y-coordinates of the sample points. Several data sets of sample
448 points sharing the same x-coordinates can be fitted at once by
449 passing in a 2D-array that contains one dataset per column.
450 deg : int
451 Degree of the fitting polynomial
452 rcond : float, optional
453 Relative condition number of the fit. Singular values smaller than
454 this relative to the largest singular value will be ignored. The
455 default value is len(x)*eps, where eps is the relative precision of
456 the float type, about 2e-16 in most cases.
457 full : bool, optional
458 Switch determining nature of return value. When it is False (the
459 default) just the coefficients are returned, when True diagnostic
460 information from the singular value decomposition is also returned.
461 w : array_like, shape (M,), optional
462 Weights to apply to the y-coordinates of the sample points. For
463 gaussian uncertainties, use 1/sigma (not 1/sigma**2).
464 cov : bool or str, optional
465 If given and not `False`, return not just the estimate but also its
466 covariance matrix. By default, the covariance are scaled by
467 chi2/sqrt(N-dof), i.e., the weights are presumed to be unreliable
468 except in a relative sense and everything is scaled such that the
469 reduced chi2 is unity. This scaling is omitted if ``cov='unscaled'``,
470 as is relevant for the case that the weights are 1/sigma**2, with
471 sigma known to be a reliable estimate of the uncertainty.
473 Returns
474 -------
475 p : ndarray, shape (deg + 1,) or (deg + 1, K)
476 Polynomial coefficients, highest power first. If `y` was 2-D, the
477 coefficients for `k`-th data set are in ``p[:,k]``.
479 residuals, rank, singular_values, rcond
480 Present only if `full` = True. Residuals is sum of squared residuals
481 of the least-squares fit, the effective rank of the scaled Vandermonde
482 coefficient matrix, its singular values, and the specified value of
483 `rcond`. For more details, see `linalg.lstsq`.
485 V : ndarray, shape (M,M) or (M,M,K)
486 Present only if `full` = False and `cov`=True. The covariance
487 matrix of the polynomial coefficient estimates. The diagonal of
488 this matrix are the variance estimates for each coefficient. If y
489 is a 2-D array, then the covariance matrix for the `k`-th data set
490 are in ``V[:,:,k]``
493 Warns
494 -----
495 RankWarning
496 The rank of the coefficient matrix in the least-squares fit is
497 deficient. The warning is only raised if `full` = False.
499 The warnings can be turned off by
501 >>> import warnings
502 >>> warnings.simplefilter('ignore', np.RankWarning)
504 See Also
505 --------
506 polyval : Compute polynomial values.
507 linalg.lstsq : Computes a least-squares fit.
508 scipy.interpolate.UnivariateSpline : Computes spline fits.
510 Notes
511 -----
512 The solution minimizes the squared error
514 .. math ::
515 E = \\sum_{j=0}^k |p(x_j) - y_j|^2
517 in the equations::
519 x[0]**n * p[0] + ... + x[0] * p[n-1] + p[n] = y[0]
520 x[1]**n * p[0] + ... + x[1] * p[n-1] + p[n] = y[1]
521 ...
522 x[k]**n * p[0] + ... + x[k] * p[n-1] + p[n] = y[k]
524 The coefficient matrix of the coefficients `p` is a Vandermonde matrix.
526 `polyfit` issues a `RankWarning` when the least-squares fit is badly
527 conditioned. This implies that the best fit is not well-defined due
528 to numerical error. The results may be improved by lowering the polynomial
529 degree or by replacing `x` by `x` - `x`.mean(). The `rcond` parameter
530 can also be set to a value smaller than its default, but the resulting
531 fit may be spurious: including contributions from the small singular
532 values can add numerical noise to the result.
534 Note that fitting polynomial coefficients is inherently badly conditioned
535 when the degree of the polynomial is large or the interval of sample points
536 is badly centered. The quality of the fit should always be checked in these
537 cases. When polynomial fits are not satisfactory, splines may be a good
538 alternative.
540 References
541 ----------
542 .. [1] Wikipedia, "Curve fitting",
543 https://en.wikipedia.org/wiki/Curve_fitting
544 .. [2] Wikipedia, "Polynomial interpolation",
545 https://en.wikipedia.org/wiki/Polynomial_interpolation
547 Examples
548 --------
549 >>> import warnings
550 >>> x = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
551 >>> y = np.array([0.0, 0.8, 0.9, 0.1, -0.8, -1.0])
552 >>> z = np.polyfit(x, y, 3)
553 >>> z
554 array([ 0.08703704, -0.81349206, 1.69312169, -0.03968254]) # may vary
556 It is convenient to use `poly1d` objects for dealing with polynomials:
558 >>> p = np.poly1d(z)
559 >>> p(0.5)
560 0.6143849206349179 # may vary
561 >>> p(3.5)
562 -0.34732142857143039 # may vary
563 >>> p(10)
564 22.579365079365115 # may vary
566 High-order polynomials may oscillate wildly:
568 >>> with warnings.catch_warnings():
569 ... warnings.simplefilter('ignore', np.RankWarning)
570 ... p30 = np.poly1d(np.polyfit(x, y, 30))
571 ...
572 >>> p30(4)
573 -0.80000000000000204 # may vary
574 >>> p30(5)
575 -0.99999999999999445 # may vary
576 >>> p30(4.5)
577 -0.10547061179440398 # may vary
579 Illustration:
581 >>> import matplotlib.pyplot as plt
582 >>> xp = np.linspace(-2, 6, 100)
583 >>> _ = plt.plot(x, y, '.', xp, p(xp), '-', xp, p30(xp), '--')
584 >>> plt.ylim(-2,2)
585 (-2, 2)
586 >>> plt.show()
588 """
589 order = int(deg) + 1
590 x = NX.asarray(x) + 0.0
591 y = NX.asarray(y) + 0.0
593 # check arguments.
594 if deg < 0:
595 raise ValueError("expected deg >= 0")
596 if x.ndim != 1:
597 raise TypeError("expected 1D vector for x")
598 if x.size == 0:
599 raise TypeError("expected non-empty vector for x")
600 if y.ndim < 1 or y.ndim > 2:
601 raise TypeError("expected 1D or 2D array for y")
602 if x.shape[0] != y.shape[0]:
603 raise TypeError("expected x and y to have same length")
605 # set rcond
606 if rcond is None:
607 rcond = len(x)*finfo(x.dtype).eps
609 # set up least squares equation for powers of x
610 lhs = vander(x, order)
611 rhs = y
613 # apply weighting
614 if w is not None:
615 w = NX.asarray(w) + 0.0
616 if w.ndim != 1:
617 raise TypeError("expected a 1-d array for weights")
618 if w.shape[0] != y.shape[0]:
619 raise TypeError("expected w and y to have the same length")
620 lhs *= w[:, NX.newaxis]
621 if rhs.ndim == 2:
622 rhs *= w[:, NX.newaxis]
623 else:
624 rhs *= w
626 # scale lhs to improve condition number and solve
627 scale = NX.sqrt((lhs*lhs).sum(axis=0))
628 lhs /= scale
629 c, resids, rank, s = lstsq(lhs, rhs, rcond)
630 c = (c.T/scale).T # broadcast scale coefficients
632 # warn on rank reduction, which indicates an ill conditioned matrix
633 if rank != order and not full:
634 msg = "Polyfit may be poorly conditioned"
635 warnings.warn(msg, RankWarning, stacklevel=4)
637 if full:
638 return c, resids, rank, s, rcond
639 elif cov:
640 Vbase = inv(dot(lhs.T, lhs))
641 Vbase /= NX.outer(scale, scale)
642 if cov == "unscaled":
643 fac = 1
644 else:
645 if len(x) <= order:
646 raise ValueError("the number of data points must exceed order "
647 "to scale the covariance matrix")
648 # note, this used to be: fac = resids / (len(x) - order - 2.0)
649 # it was deciced that the "- 2" (originally justified by "Bayesian
650 # uncertainty analysis") is not was the user expects
651 # (see gh-11196 and gh-11197)
652 fac = resids / (len(x) - order)
653 if y.ndim == 1:
654 return c, Vbase * fac
655 else:
656 return c, Vbase[:,:, NX.newaxis] * fac
657 else:
658 return c
661def _polyval_dispatcher(p, x):
662 return (p, x)
665@array_function_dispatch(_polyval_dispatcher)
666def polyval(p, x):
667 """
668 Evaluate a polynomial at specific values.
670 If `p` is of length N, this function returns the value:
672 ``p[0]*x**(N-1) + p[1]*x**(N-2) + ... + p[N-2]*x + p[N-1]``
674 If `x` is a sequence, then `p(x)` is returned for each element of `x`.
675 If `x` is another polynomial then the composite polynomial `p(x(t))`
676 is returned.
678 Parameters
679 ----------
680 p : array_like or poly1d object
681 1D array of polynomial coefficients (including coefficients equal
682 to zero) from highest degree to the constant term, or an
683 instance of poly1d.
684 x : array_like or poly1d object
685 A number, an array of numbers, or an instance of poly1d, at
686 which to evaluate `p`.
688 Returns
689 -------
690 values : ndarray or poly1d
691 If `x` is a poly1d instance, the result is the composition of the two
692 polynomials, i.e., `x` is "substituted" in `p` and the simplified
693 result is returned. In addition, the type of `x` - array_like or
694 poly1d - governs the type of the output: `x` array_like => `values`
695 array_like, `x` a poly1d object => `values` is also.
697 See Also
698 --------
699 poly1d: A polynomial class.
701 Notes
702 -----
703 Horner's scheme [1]_ is used to evaluate the polynomial. Even so,
704 for polynomials of high degree the values may be inaccurate due to
705 rounding errors. Use carefully.
707 If `x` is a subtype of `ndarray` the return value will be of the same type.
709 References
710 ----------
711 .. [1] I. N. Bronshtein, K. A. Semendyayev, and K. A. Hirsch (Eng.
712 trans. Ed.), *Handbook of Mathematics*, New York, Van Nostrand
713 Reinhold Co., 1985, pg. 720.
715 Examples
716 --------
717 >>> np.polyval([3,0,1], 5) # 3 * 5**2 + 0 * 5**1 + 1
718 76
719 >>> np.polyval([3,0,1], np.poly1d(5))
720 poly1d([76.])
721 >>> np.polyval(np.poly1d([3,0,1]), 5)
722 76
723 >>> np.polyval(np.poly1d([3,0,1]), np.poly1d(5))
724 poly1d([76.])
726 """
727 p = NX.asarray(p)
728 if isinstance(x, poly1d):
729 y = 0
730 else:
731 x = NX.asanyarray(x)
732 y = NX.zeros_like(x)
733 for i in range(len(p)):
734 y = y * x + p[i]
735 return y
738def _binary_op_dispatcher(a1, a2):
739 return (a1, a2)
742@array_function_dispatch(_binary_op_dispatcher)
743def polyadd(a1, a2):
744 """
745 Find the sum of two polynomials.
747 Returns the polynomial resulting from the sum of two input polynomials.
748 Each input must be either a poly1d object or a 1D sequence of polynomial
749 coefficients, from highest to lowest degree.
751 Parameters
752 ----------
753 a1, a2 : array_like or poly1d object
754 Input polynomials.
756 Returns
757 -------
758 out : ndarray or poly1d object
759 The sum of the inputs. If either input is a poly1d object, then the
760 output is also a poly1d object. Otherwise, it is a 1D array of
761 polynomial coefficients from highest to lowest degree.
763 See Also
764 --------
765 poly1d : A one-dimensional polynomial class.
766 poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval
768 Examples
769 --------
770 >>> np.polyadd([1, 2], [9, 5, 4])
771 array([9, 6, 6])
773 Using poly1d objects:
775 >>> p1 = np.poly1d([1, 2])
776 >>> p2 = np.poly1d([9, 5, 4])
777 >>> print(p1)
778 1 x + 2
779 >>> print(p2)
780 2
781 9 x + 5 x + 4
782 >>> print(np.polyadd(p1, p2))
783 2
784 9 x + 6 x + 6
786 """
787 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
788 a1 = atleast_1d(a1)
789 a2 = atleast_1d(a2)
790 diff = len(a2) - len(a1)
791 if diff == 0:
792 val = a1 + a2
793 elif diff > 0:
794 zr = NX.zeros(diff, a1.dtype)
795 val = NX.concatenate((zr, a1)) + a2
796 else:
797 zr = NX.zeros(abs(diff), a2.dtype)
798 val = a1 + NX.concatenate((zr, a2))
799 if truepoly:
800 val = poly1d(val)
801 return val
804@array_function_dispatch(_binary_op_dispatcher)
805def polysub(a1, a2):
806 """
807 Difference (subtraction) of two polynomials.
809 Given two polynomials `a1` and `a2`, returns ``a1 - a2``.
810 `a1` and `a2` can be either array_like sequences of the polynomials'
811 coefficients (including coefficients equal to zero), or `poly1d` objects.
813 Parameters
814 ----------
815 a1, a2 : array_like or poly1d
816 Minuend and subtrahend polynomials, respectively.
818 Returns
819 -------
820 out : ndarray or poly1d
821 Array or `poly1d` object of the difference polynomial's coefficients.
823 See Also
824 --------
825 polyval, polydiv, polymul, polyadd
827 Examples
828 --------
829 .. math:: (2 x^2 + 10 x - 2) - (3 x^2 + 10 x -4) = (-x^2 + 2)
831 >>> np.polysub([2, 10, -2], [3, 10, -4])
832 array([-1, 0, 2])
834 """
835 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
836 a1 = atleast_1d(a1)
837 a2 = atleast_1d(a2)
838 diff = len(a2) - len(a1)
839 if diff == 0:
840 val = a1 - a2
841 elif diff > 0:
842 zr = NX.zeros(diff, a1.dtype)
843 val = NX.concatenate((zr, a1)) - a2
844 else:
845 zr = NX.zeros(abs(diff), a2.dtype)
846 val = a1 - NX.concatenate((zr, a2))
847 if truepoly:
848 val = poly1d(val)
849 return val
852@array_function_dispatch(_binary_op_dispatcher)
853def polymul(a1, a2):
854 """
855 Find the product of two polynomials.
857 Finds the polynomial resulting from the multiplication of the two input
858 polynomials. Each input must be either a poly1d object or a 1D sequence
859 of polynomial coefficients, from highest to lowest degree.
861 Parameters
862 ----------
863 a1, a2 : array_like or poly1d object
864 Input polynomials.
866 Returns
867 -------
868 out : ndarray or poly1d object
869 The polynomial resulting from the multiplication of the inputs. If
870 either inputs is a poly1d object, then the output is also a poly1d
871 object. Otherwise, it is a 1D array of polynomial coefficients from
872 highest to lowest degree.
874 See Also
875 --------
876 poly1d : A one-dimensional polynomial class.
877 poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval
878 convolve : Array convolution. Same output as polymul, but has parameter
879 for overlap mode.
881 Examples
882 --------
883 >>> np.polymul([1, 2, 3], [9, 5, 1])
884 array([ 9, 23, 38, 17, 3])
886 Using poly1d objects:
888 >>> p1 = np.poly1d([1, 2, 3])
889 >>> p2 = np.poly1d([9, 5, 1])
890 >>> print(p1)
891 2
892 1 x + 2 x + 3
893 >>> print(p2)
894 2
895 9 x + 5 x + 1
896 >>> print(np.polymul(p1, p2))
897 4 3 2
898 9 x + 23 x + 38 x + 17 x + 3
900 """
901 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
902 a1, a2 = poly1d(a1), poly1d(a2)
903 val = NX.convolve(a1, a2)
904 if truepoly:
905 val = poly1d(val)
906 return val
909def _polydiv_dispatcher(u, v):
910 return (u, v)
913@array_function_dispatch(_polydiv_dispatcher)
914def polydiv(u, v):
915 """
916 Returns the quotient and remainder of polynomial division.
918 The input arrays are the coefficients (including any coefficients
919 equal to zero) of the "numerator" (dividend) and "denominator"
920 (divisor) polynomials, respectively.
922 Parameters
923 ----------
924 u : array_like or poly1d
925 Dividend polynomial's coefficients.
927 v : array_like or poly1d
928 Divisor polynomial's coefficients.
930 Returns
931 -------
932 q : ndarray
933 Coefficients, including those equal to zero, of the quotient.
934 r : ndarray
935 Coefficients, including those equal to zero, of the remainder.
937 See Also
938 --------
939 poly, polyadd, polyder, polydiv, polyfit, polyint, polymul, polysub
940 polyval
942 Notes
943 -----
944 Both `u` and `v` must be 0-d or 1-d (ndim = 0 or 1), but `u.ndim` need
945 not equal `v.ndim`. In other words, all four possible combinations -
946 ``u.ndim = v.ndim = 0``, ``u.ndim = v.ndim = 1``,
947 ``u.ndim = 1, v.ndim = 0``, and ``u.ndim = 0, v.ndim = 1`` - work.
949 Examples
950 --------
951 .. math:: \\frac{3x^2 + 5x + 2}{2x + 1} = 1.5x + 1.75, remainder 0.25
953 >>> x = np.array([3.0, 5.0, 2.0])
954 >>> y = np.array([2.0, 1.0])
955 >>> np.polydiv(x, y)
956 (array([1.5 , 1.75]), array([0.25]))
958 """
959 truepoly = (isinstance(u, poly1d) or isinstance(u, poly1d))
960 u = atleast_1d(u) + 0.0
961 v = atleast_1d(v) + 0.0
962 # w has the common type
963 w = u[0] + v[0]
964 m = len(u) - 1
965 n = len(v) - 1
966 scale = 1. / v[0]
967 q = NX.zeros((max(m - n + 1, 1),), w.dtype)
968 r = u.astype(w.dtype)
969 for k in range(0, m-n+1):
970 d = scale * r[k]
971 q[k] = d
972 r[k:k+n+1] -= d*v
973 while NX.allclose(r[0], 0, rtol=1e-14) and (r.shape[-1] > 1):
974 r = r[1:]
975 if truepoly:
976 return poly1d(q), poly1d(r)
977 return q, r
979_poly_mat = re.compile(r"[*][*]([0-9]*)")
980def _raise_power(astr, wrap=70):
981 n = 0
982 line1 = ''
983 line2 = ''
984 output = ' '
985 while True:
986 mat = _poly_mat.search(astr, n)
987 if mat is None:
988 break
989 span = mat.span()
990 power = mat.groups()[0]
991 partstr = astr[n:span[0]]
992 n = span[1]
993 toadd2 = partstr + ' '*(len(power)-1)
994 toadd1 = ' '*(len(partstr)-1) + power
995 if ((len(line2) + len(toadd2) > wrap) or
996 (len(line1) + len(toadd1) > wrap)):
997 output += line1 + "\n" + line2 + "\n "
998 line1 = toadd1
999 line2 = toadd2
1000 else:
1001 line2 += partstr + ' '*(len(power)-1)
1002 line1 += ' '*(len(partstr)-1) + power
1003 output += line1 + "\n" + line2
1004 return output + astr[n:]
1007@set_module('numpy')
1008class poly1d:
1009 """
1010 A one-dimensional polynomial class.
1012 A convenience class, used to encapsulate "natural" operations on
1013 polynomials so that said operations may take on their customary
1014 form in code (see Examples).
1016 Parameters
1017 ----------
1018 c_or_r : array_like
1019 The polynomial's coefficients, in decreasing powers, or if
1020 the value of the second parameter is True, the polynomial's
1021 roots (values where the polynomial evaluates to 0). For example,
1022 ``poly1d([1, 2, 3])`` returns an object that represents
1023 :math:`x^2 + 2x + 3`, whereas ``poly1d([1, 2, 3], True)`` returns
1024 one that represents :math:`(x-1)(x-2)(x-3) = x^3 - 6x^2 + 11x -6`.
1025 r : bool, optional
1026 If True, `c_or_r` specifies the polynomial's roots; the default
1027 is False.
1028 variable : str, optional
1029 Changes the variable used when printing `p` from `x` to `variable`
1030 (see Examples).
1032 Examples
1033 --------
1034 Construct the polynomial :math:`x^2 + 2x + 3`:
1036 >>> p = np.poly1d([1, 2, 3])
1037 >>> print(np.poly1d(p))
1038 2
1039 1 x + 2 x + 3
1041 Evaluate the polynomial at :math:`x = 0.5`:
1043 >>> p(0.5)
1044 4.25
1046 Find the roots:
1048 >>> p.r
1049 array([-1.+1.41421356j, -1.-1.41421356j])
1050 >>> p(p.r)
1051 array([ -4.44089210e-16+0.j, -4.44089210e-16+0.j]) # may vary
1053 These numbers in the previous line represent (0, 0) to machine precision
1055 Show the coefficients:
1057 >>> p.c
1058 array([1, 2, 3])
1060 Display the order (the leading zero-coefficients are removed):
1062 >>> p.order
1063 2
1065 Show the coefficient of the k-th power in the polynomial
1066 (which is equivalent to ``p.c[-(i+1)]``):
1068 >>> p[1]
1069 2
1071 Polynomials can be added, subtracted, multiplied, and divided
1072 (returns quotient and remainder):
1074 >>> p * p
1075 poly1d([ 1, 4, 10, 12, 9])
1077 >>> (p**3 + 4) / p
1078 (poly1d([ 1., 4., 10., 12., 9.]), poly1d([4.]))
1080 ``asarray(p)`` gives the coefficient array, so polynomials can be
1081 used in all functions that accept arrays:
1083 >>> p**2 # square of polynomial
1084 poly1d([ 1, 4, 10, 12, 9])
1086 >>> np.square(p) # square of individual coefficients
1087 array([1, 4, 9])
1089 The variable used in the string representation of `p` can be modified,
1090 using the `variable` parameter:
1092 >>> p = np.poly1d([1,2,3], variable='z')
1093 >>> print(p)
1094 2
1095 1 z + 2 z + 3
1097 Construct a polynomial from its roots:
1099 >>> np.poly1d([1, 2], True)
1100 poly1d([ 1., -3., 2.])
1102 This is the same polynomial as obtained by:
1104 >>> np.poly1d([1, -1]) * np.poly1d([1, -2])
1105 poly1d([ 1, -3, 2])
1107 """
1108 __hash__ = None
1110 @property
1111 def coeffs(self):
1112 """ The polynomial coefficients """
1113 return self._coeffs
1115 @coeffs.setter
1116 def coeffs(self, value):
1117 # allowing this makes p.coeffs *= 2 legal
1118 if value is not self._coeffs:
1119 raise AttributeError("Cannot set attribute")
1121 @property
1122 def variable(self):
1123 """ The name of the polynomial variable """
1124 return self._variable
1126 # calculated attributes
1127 @property
1128 def order(self):
1129 """ The order or degree of the polynomial """
1130 return len(self._coeffs) - 1
1132 @property
1133 def roots(self):
1134 """ The roots of the polynomial, where self(x) == 0 """
1135 return roots(self._coeffs)
1137 # our internal _coeffs property need to be backed by __dict__['coeffs'] for
1138 # scipy to work correctly.
1139 @property
1140 def _coeffs(self):
1141 return self.__dict__['coeffs']
1142 @_coeffs.setter
1143 def _coeffs(self, coeffs):
1144 self.__dict__['coeffs'] = coeffs
1146 # alias attributes
1147 r = roots
1148 c = coef = coefficients = coeffs
1149 o = order
1151 def __init__(self, c_or_r, r=False, variable=None):
1152 if isinstance(c_or_r, poly1d):
1153 self._variable = c_or_r._variable
1154 self._coeffs = c_or_r._coeffs
1156 if set(c_or_r.__dict__) - set(self.__dict__):
1157 msg = ("In the future extra properties will not be copied "
1158 "across when constructing one poly1d from another")
1159 warnings.warn(msg, FutureWarning, stacklevel=2)
1160 self.__dict__.update(c_or_r.__dict__)
1162 if variable is not None:
1163 self._variable = variable
1164 return
1165 if r:
1166 c_or_r = poly(c_or_r)
1167 c_or_r = atleast_1d(c_or_r)
1168 if c_or_r.ndim > 1:
1169 raise ValueError("Polynomial must be 1d only.")
1170 c_or_r = trim_zeros(c_or_r, trim='f')
1171 if len(c_or_r) == 0:
1172 c_or_r = NX.array([0.])
1173 self._coeffs = c_or_r
1174 if variable is None:
1175 variable = 'x'
1176 self._variable = variable
1178 def __array__(self, t=None):
1179 if t:
1180 return NX.asarray(self.coeffs, t)
1181 else:
1182 return NX.asarray(self.coeffs)
1184 def __repr__(self):
1185 vals = repr(self.coeffs)
1186 vals = vals[6:-1]
1187 return "poly1d(%s)" % vals
1189 def __len__(self):
1190 return self.order
1192 def __str__(self):
1193 thestr = "0"
1194 var = self.variable
1196 # Remove leading zeros
1197 coeffs = self.coeffs[NX.logical_or.accumulate(self.coeffs != 0)]
1198 N = len(coeffs)-1
1200 def fmt_float(q):
1201 s = '%.4g' % q
1202 if s.endswith('.0000'):
1203 s = s[:-5]
1204 return s
1206 for k in range(len(coeffs)):
1207 if not iscomplex(coeffs[k]):
1208 coefstr = fmt_float(real(coeffs[k]))
1209 elif real(coeffs[k]) == 0:
1210 coefstr = '%sj' % fmt_float(imag(coeffs[k]))
1211 else:
1212 coefstr = '(%s + %sj)' % (fmt_float(real(coeffs[k])),
1213 fmt_float(imag(coeffs[k])))
1215 power = (N-k)
1216 if power == 0:
1217 if coefstr != '0':
1218 newstr = '%s' % (coefstr,)
1219 else:
1220 if k == 0:
1221 newstr = '0'
1222 else:
1223 newstr = ''
1224 elif power == 1:
1225 if coefstr == '0':
1226 newstr = ''
1227 elif coefstr == 'b':
1228 newstr = var
1229 else:
1230 newstr = '%s %s' % (coefstr, var)
1231 else:
1232 if coefstr == '0':
1233 newstr = ''
1234 elif coefstr == 'b':
1235 newstr = '%s**%d' % (var, power,)
1236 else:
1237 newstr = '%s %s**%d' % (coefstr, var, power)
1239 if k > 0:
1240 if newstr != '':
1241 if newstr.startswith('-'):
1242 thestr = "%s - %s" % (thestr, newstr[1:])
1243 else:
1244 thestr = "%s + %s" % (thestr, newstr)
1245 else:
1246 thestr = newstr
1247 return _raise_power(thestr)
1249 def __call__(self, val):
1250 return polyval(self.coeffs, val)
1252 def __neg__(self):
1253 return poly1d(-self.coeffs)
1255 def __pos__(self):
1256 return self
1258 def __mul__(self, other):
1259 if isscalar(other):
1260 return poly1d(self.coeffs * other)
1261 else:
1262 other = poly1d(other)
1263 return poly1d(polymul(self.coeffs, other.coeffs))
1265 def __rmul__(self, other):
1266 if isscalar(other):
1267 return poly1d(other * self.coeffs)
1268 else:
1269 other = poly1d(other)
1270 return poly1d(polymul(self.coeffs, other.coeffs))
1272 def __add__(self, other):
1273 other = poly1d(other)
1274 return poly1d(polyadd(self.coeffs, other.coeffs))
1276 def __radd__(self, other):
1277 other = poly1d(other)
1278 return poly1d(polyadd(self.coeffs, other.coeffs))
1280 def __pow__(self, val):
1281 if not isscalar(val) or int(val) != val or val < 0:
1282 raise ValueError("Power to non-negative integers only.")
1283 res = [1]
1284 for _ in range(val):
1285 res = polymul(self.coeffs, res)
1286 return poly1d(res)
1288 def __sub__(self, other):
1289 other = poly1d(other)
1290 return poly1d(polysub(self.coeffs, other.coeffs))
1292 def __rsub__(self, other):
1293 other = poly1d(other)
1294 return poly1d(polysub(other.coeffs, self.coeffs))
1296 def __div__(self, other):
1297 if isscalar(other):
1298 return poly1d(self.coeffs/other)
1299 else:
1300 other = poly1d(other)
1301 return polydiv(self, other)
1303 __truediv__ = __div__
1305 def __rdiv__(self, other):
1306 if isscalar(other):
1307 return poly1d(other/self.coeffs)
1308 else:
1309 other = poly1d(other)
1310 return polydiv(other, self)
1312 __rtruediv__ = __rdiv__
1314 def __eq__(self, other):
1315 if not isinstance(other, poly1d):
1316 return NotImplemented
1317 if self.coeffs.shape != other.coeffs.shape:
1318 return False
1319 return (self.coeffs == other.coeffs).all()
1321 def __ne__(self, other):
1322 if not isinstance(other, poly1d):
1323 return NotImplemented
1324 return not self.__eq__(other)
1327 def __getitem__(self, val):
1328 ind = self.order - val
1329 if val > self.order:
1330 return 0
1331 if val < 0:
1332 return 0
1333 return self.coeffs[ind]
1335 def __setitem__(self, key, val):
1336 ind = self.order - key
1337 if key < 0:
1338 raise ValueError("Does not support negative powers.")
1339 if key > self.order:
1340 zr = NX.zeros(key-self.order, self.coeffs.dtype)
1341 self._coeffs = NX.concatenate((zr, self.coeffs))
1342 ind = 0
1343 self._coeffs[ind] = val
1344 return
1346 def __iter__(self):
1347 return iter(self.coeffs)
1349 def integ(self, m=1, k=0):
1350 """
1351 Return an antiderivative (indefinite integral) of this polynomial.
1353 Refer to `polyint` for full documentation.
1355 See Also
1356 --------
1357 polyint : equivalent function
1359 """
1360 return poly1d(polyint(self.coeffs, m=m, k=k))
1362 def deriv(self, m=1):
1363 """
1364 Return a derivative of this polynomial.
1366 Refer to `polyder` for full documentation.
1368 See Also
1369 --------
1370 polyder : equivalent function
1372 """
1373 return poly1d(polyder(self.coeffs, m=m))
1375# Stuff to do on module import
1377warnings.simplefilter('always', RankWarning)