comparison DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/lib/polynomial.py @ 87:2a2c65a20a8b

Add Python libs and headers
author Chris Cannam
date Wed, 25 Feb 2015 14:05:22 +0000
parents
children
comparison
equal deleted inserted replaced
86:413a9d26189e 87:2a2c65a20a8b
1 """
2 Functions to operate on polynomials.
3
4 """
5 from __future__ import division, absolute_import, print_function
6
7 __all__ = ['poly', 'roots', 'polyint', 'polyder', 'polyadd',
8 'polysub', 'polymul', 'polydiv', 'polyval', 'poly1d',
9 'polyfit', 'RankWarning']
10
11 import re
12 import warnings
13 import numpy.core.numeric as NX
14
15 from numpy.core import isscalar, abs, finfo, atleast_1d, hstack, dot
16 from numpy.lib.twodim_base import diag, vander
17 from numpy.lib.function_base import trim_zeros, sort_complex
18 from numpy.lib.type_check import iscomplex, real, imag
19 from numpy.linalg import eigvals, lstsq, inv
20
21 class RankWarning(UserWarning):
22 """
23 Issued by `polyfit` when the Vandermonde matrix is rank deficient.
24
25 For more information, a way to suppress the warning, and an example of
26 `RankWarning` being issued, see `polyfit`.
27
28 """
29 pass
30
31 def poly(seq_of_zeros):
32 """
33 Find the coefficients of a polynomial with the given sequence of roots.
34
35 Returns the coefficients of the polynomial whose leading coefficient
36 is one for the given sequence of zeros (multiple roots must be included
37 in the sequence as many times as their multiplicity; see Examples).
38 A square matrix (or array, which will be treated as a matrix) can also
39 be given, in which case the coefficients of the characteristic polynomial
40 of the matrix are returned.
41
42 Parameters
43 ----------
44 seq_of_zeros : array_like, shape (N,) or (N, N)
45 A sequence of polynomial roots, or a square array or matrix object.
46
47 Returns
48 -------
49 c : ndarray
50 1D array of polynomial coefficients from highest to lowest degree:
51
52 ``c[0] * x**(N) + c[1] * x**(N-1) + ... + c[N-1] * x + c[N]``
53 where c[0] always equals 1.
54
55 Raises
56 ------
57 ValueError
58 If input is the wrong shape (the input must be a 1-D or square
59 2-D array).
60
61 See Also
62 --------
63 polyval : Evaluate a polynomial at a point.
64 roots : Return the roots of a polynomial.
65 polyfit : Least squares polynomial fit.
66 poly1d : A one-dimensional polynomial class.
67
68 Notes
69 -----
70 Specifying the roots of a polynomial still leaves one degree of
71 freedom, typically represented by an undetermined leading
72 coefficient. [1]_ In the case of this function, that coefficient -
73 the first one in the returned array - is always taken as one. (If
74 for some reason you have one other point, the only automatic way
75 presently to leverage that information is to use ``polyfit``.)
76
77 The characteristic polynomial, :math:`p_a(t)`, of an `n`-by-`n`
78 matrix **A** is given by
79
80 :math:`p_a(t) = \\mathrm{det}(t\\, \\mathbf{I} - \\mathbf{A})`,
81
82 where **I** is the `n`-by-`n` identity matrix. [2]_
83
84 References
85 ----------
86 .. [1] M. Sullivan and M. Sullivan, III, "Algebra and Trignometry,
87 Enhanced With Graphing Utilities," Prentice-Hall, pg. 318, 1996.
88
89 .. [2] G. Strang, "Linear Algebra and Its Applications, 2nd Edition,"
90 Academic Press, pg. 182, 1980.
91
92 Examples
93 --------
94 Given a sequence of a polynomial's zeros:
95
96 >>> np.poly((0, 0, 0)) # Multiple root example
97 array([1, 0, 0, 0])
98
99 The line above represents z**3 + 0*z**2 + 0*z + 0.
100
101 >>> np.poly((-1./2, 0, 1./2))
102 array([ 1. , 0. , -0.25, 0. ])
103
104 The line above represents z**3 - z/4
105
106 >>> np.poly((np.random.random(1.)[0], 0, np.random.random(1.)[0]))
107 array([ 1. , -0.77086955, 0.08618131, 0. ]) #random
108
109 Given a square array object:
110
111 >>> P = np.array([[0, 1./3], [-1./2, 0]])
112 >>> np.poly(P)
113 array([ 1. , 0. , 0.16666667])
114
115 Or a square matrix object:
116
117 >>> np.poly(np.matrix(P))
118 array([ 1. , 0. , 0.16666667])
119
120 Note how in all cases the leading coefficient is always 1.
121
122 """
123 seq_of_zeros = atleast_1d(seq_of_zeros)
124 sh = seq_of_zeros.shape
125 if len(sh) == 2 and sh[0] == sh[1] and sh[0] != 0:
126 seq_of_zeros = eigvals(seq_of_zeros)
127 elif len(sh) == 1:
128 pass
129 else:
130 raise ValueError("input must be 1d or non-empty square 2d array.")
131
132 if len(seq_of_zeros) == 0:
133 return 1.0
134
135 a = [1]
136 for k in range(len(seq_of_zeros)):
137 a = NX.convolve(a, [1, -seq_of_zeros[k]], mode='full')
138
139 if issubclass(a.dtype.type, NX.complexfloating):
140 # if complex roots are all complex conjugates, the roots are real.
141 roots = NX.asarray(seq_of_zeros, complex)
142 pos_roots = sort_complex(NX.compress(roots.imag > 0, roots))
143 neg_roots = NX.conjugate(sort_complex(
144 NX.compress(roots.imag < 0, roots)))
145 if (len(pos_roots) == len(neg_roots) and
146 NX.alltrue(neg_roots == pos_roots)):
147 a = a.real.copy()
148
149 return a
150
151 def roots(p):
152 """
153 Return the roots of a polynomial with coefficients given in p.
154
155 The values in the rank-1 array `p` are coefficients of a polynomial.
156 If the length of `p` is n+1 then the polynomial is described by::
157
158 p[0] * x**n + p[1] * x**(n-1) + ... + p[n-1]*x + p[n]
159
160 Parameters
161 ----------
162 p : array_like
163 Rank-1 array of polynomial coefficients.
164
165 Returns
166 -------
167 out : ndarray
168 An array containing the complex roots of the polynomial.
169
170 Raises
171 ------
172 ValueError
173 When `p` cannot be converted to a rank-1 array.
174
175 See also
176 --------
177 poly : Find the coefficients of a polynomial with a given sequence
178 of roots.
179 polyval : Evaluate a polynomial at a point.
180 polyfit : Least squares polynomial fit.
181 poly1d : A one-dimensional polynomial class.
182
183 Notes
184 -----
185 The algorithm relies on computing the eigenvalues of the
186 companion matrix [1]_.
187
188 References
189 ----------
190 .. [1] R. A. Horn & C. R. Johnson, *Matrix Analysis*. Cambridge, UK:
191 Cambridge University Press, 1999, pp. 146-7.
192
193 Examples
194 --------
195 >>> coeff = [3.2, 2, 1]
196 >>> np.roots(coeff)
197 array([-0.3125+0.46351241j, -0.3125-0.46351241j])
198
199 """
200 # If input is scalar, this makes it an array
201 p = atleast_1d(p)
202 if len(p.shape) != 1:
203 raise ValueError("Input must be a rank-1 array.")
204
205 # find non-zero array entries
206 non_zero = NX.nonzero(NX.ravel(p))[0]
207
208 # Return an empty array if polynomial is all zeros
209 if len(non_zero) == 0:
210 return NX.array([])
211
212 # find the number of trailing zeros -- this is the number of roots at 0.
213 trailing_zeros = len(p) - non_zero[-1] - 1
214
215 # strip leading and trailing zeros
216 p = p[int(non_zero[0]):int(non_zero[-1])+1]
217
218 # casting: if incoming array isn't floating point, make it floating point.
219 if not issubclass(p.dtype.type, (NX.floating, NX.complexfloating)):
220 p = p.astype(float)
221
222 N = len(p)
223 if N > 1:
224 # build companion matrix and find its eigenvalues (the roots)
225 A = diag(NX.ones((N-2,), p.dtype), -1)
226 A[0,:] = -p[1:] / p[0]
227 roots = eigvals(A)
228 else:
229 roots = NX.array([])
230
231 # tack any zeros onto the back of the array
232 roots = hstack((roots, NX.zeros(trailing_zeros, roots.dtype)))
233 return roots
234
235 def polyint(p, m=1, k=None):
236 """
237 Return an antiderivative (indefinite integral) of a polynomial.
238
239 The returned order `m` antiderivative `P` of polynomial `p` satisfies
240 :math:`\\frac{d^m}{dx^m}P(x) = p(x)` and is defined up to `m - 1`
241 integration constants `k`. The constants determine the low-order
242 polynomial part
243
244 .. math:: \\frac{k_{m-1}}{0!} x^0 + \\ldots + \\frac{k_0}{(m-1)!}x^{m-1}
245
246 of `P` so that :math:`P^{(j)}(0) = k_{m-j-1}`.
247
248 Parameters
249 ----------
250 p : {array_like, poly1d}
251 Polynomial to differentiate.
252 A sequence is interpreted as polynomial coefficients, see `poly1d`.
253 m : int, optional
254 Order of the antiderivative. (Default: 1)
255 k : {None, list of `m` scalars, scalar}, optional
256 Integration constants. They are given in the order of integration:
257 those corresponding to highest-order terms come first.
258
259 If ``None`` (default), all constants are assumed to be zero.
260 If `m = 1`, a single scalar can be given instead of a list.
261
262 See Also
263 --------
264 polyder : derivative of a polynomial
265 poly1d.integ : equivalent method
266
267 Examples
268 --------
269 The defining property of the antiderivative:
270
271 >>> p = np.poly1d([1,1,1])
272 >>> P = np.polyint(p)
273 >>> P
274 poly1d([ 0.33333333, 0.5 , 1. , 0. ])
275 >>> np.polyder(P) == p
276 True
277
278 The integration constants default to zero, but can be specified:
279
280 >>> P = np.polyint(p, 3)
281 >>> P(0)
282 0.0
283 >>> np.polyder(P)(0)
284 0.0
285 >>> np.polyder(P, 2)(0)
286 0.0
287 >>> P = np.polyint(p, 3, k=[6,5,3])
288 >>> P
289 poly1d([ 0.01666667, 0.04166667, 0.16666667, 3. , 5. , 3. ])
290
291 Note that 3 = 6 / 2!, and that the constants are given in the order of
292 integrations. Constant of the highest-order polynomial term comes first:
293
294 >>> np.polyder(P, 2)(0)
295 6.0
296 >>> np.polyder(P, 1)(0)
297 5.0
298 >>> P(0)
299 3.0
300
301 """
302 m = int(m)
303 if m < 0:
304 raise ValueError("Order of integral must be positive (see polyder)")
305 if k is None:
306 k = NX.zeros(m, float)
307 k = atleast_1d(k)
308 if len(k) == 1 and m > 1:
309 k = k[0]*NX.ones(m, float)
310 if len(k) < m:
311 raise ValueError(
312 "k must be a scalar or a rank-1 array of length 1 or >m.")
313
314 truepoly = isinstance(p, poly1d)
315 p = NX.asarray(p)
316 if m == 0:
317 if truepoly:
318 return poly1d(p)
319 return p
320 else:
321 # Note: this must work also with object and integer arrays
322 y = NX.concatenate((p.__truediv__(NX.arange(len(p), 0, -1)), [k[0]]))
323 val = polyint(y, m - 1, k=k[1:])
324 if truepoly:
325 return poly1d(val)
326 return val
327
328 def polyder(p, m=1):
329 """
330 Return the derivative of the specified order of a polynomial.
331
332 Parameters
333 ----------
334 p : poly1d or sequence
335 Polynomial to differentiate.
336 A sequence is interpreted as polynomial coefficients, see `poly1d`.
337 m : int, optional
338 Order of differentiation (default: 1)
339
340 Returns
341 -------
342 der : poly1d
343 A new polynomial representing the derivative.
344
345 See Also
346 --------
347 polyint : Anti-derivative of a polynomial.
348 poly1d : Class for one-dimensional polynomials.
349
350 Examples
351 --------
352 The derivative of the polynomial :math:`x^3 + x^2 + x^1 + 1` is:
353
354 >>> p = np.poly1d([1,1,1,1])
355 >>> p2 = np.polyder(p)
356 >>> p2
357 poly1d([3, 2, 1])
358
359 which evaluates to:
360
361 >>> p2(2.)
362 17.0
363
364 We can verify this, approximating the derivative with
365 ``(f(x + h) - f(x))/h``:
366
367 >>> (p(2. + 0.001) - p(2.)) / 0.001
368 17.007000999997857
369
370 The fourth-order derivative of a 3rd-order polynomial is zero:
371
372 >>> np.polyder(p, 2)
373 poly1d([6, 2])
374 >>> np.polyder(p, 3)
375 poly1d([6])
376 >>> np.polyder(p, 4)
377 poly1d([ 0.])
378
379 """
380 m = int(m)
381 if m < 0:
382 raise ValueError("Order of derivative must be positive (see polyint)")
383
384 truepoly = isinstance(p, poly1d)
385 p = NX.asarray(p)
386 n = len(p) - 1
387 y = p[:-1] * NX.arange(n, 0, -1)
388 if m == 0:
389 val = p
390 else:
391 val = polyder(y, m - 1)
392 if truepoly:
393 val = poly1d(val)
394 return val
395
396 def polyfit(x, y, deg, rcond=None, full=False, w=None, cov=False):
397 """
398 Least squares polynomial fit.
399
400 Fit a polynomial ``p(x) = p[0] * x**deg + ... + p[deg]`` of degree `deg`
401 to points `(x, y)`. Returns a vector of coefficients `p` that minimises
402 the squared error.
403
404 Parameters
405 ----------
406 x : array_like, shape (M,)
407 x-coordinates of the M sample points ``(x[i], y[i])``.
408 y : array_like, shape (M,) or (M, K)
409 y-coordinates of the sample points. Several data sets of sample
410 points sharing the same x-coordinates can be fitted at once by
411 passing in a 2D-array that contains one dataset per column.
412 deg : int
413 Degree of the fitting polynomial
414 rcond : float, optional
415 Relative condition number of the fit. Singular values smaller than
416 this relative to the largest singular value will be ignored. The
417 default value is len(x)*eps, where eps is the relative precision of
418 the float type, about 2e-16 in most cases.
419 full : bool, optional
420 Switch determining nature of return value. When it is False (the
421 default) just the coefficients are returned, when True diagnostic
422 information from the singular value decomposition is also returned.
423 w : array_like, shape (M,), optional
424 weights to apply to the y-coordinates of the sample points.
425 cov : bool, optional
426 Return the estimate and the covariance matrix of the estimate
427 If full is True, then cov is not returned.
428
429 Returns
430 -------
431 p : ndarray, shape (M,) or (M, K)
432 Polynomial coefficients, highest power first. If `y` was 2-D, the
433 coefficients for `k`-th data set are in ``p[:,k]``.
434
435 residuals, rank, singular_values, rcond :
436 Present only if `full` = True. Residuals of the least-squares fit,
437 the effective rank of the scaled Vandermonde coefficient matrix,
438 its singular values, and the specified value of `rcond`. For more
439 details, see `linalg.lstsq`.
440
441 V : ndarray, shape (M,M) or (M,M,K)
442 Present only if `full` = False and `cov`=True. The covariance
443 matrix of the polynomial coefficient estimates. The diagonal of
444 this matrix are the variance estimates for each coefficient. If y
445 is a 2-D array, then the covariance matrix for the `k`-th data set
446 are in ``V[:,:,k]``
447
448
449 Warns
450 -----
451 RankWarning
452 The rank of the coefficient matrix in the least-squares fit is
453 deficient. The warning is only raised if `full` = False.
454
455 The warnings can be turned off by
456
457 >>> import warnings
458 >>> warnings.simplefilter('ignore', np.RankWarning)
459
460 See Also
461 --------
462 polyval : Computes polynomial values.
463 linalg.lstsq : Computes a least-squares fit.
464 scipy.interpolate.UnivariateSpline : Computes spline fits.
465
466 Notes
467 -----
468 The solution minimizes the squared error
469
470 .. math ::
471 E = \\sum_{j=0}^k |p(x_j) - y_j|^2
472
473 in the equations::
474
475 x[0]**n * p[0] + ... + x[0] * p[n-1] + p[n] = y[0]
476 x[1]**n * p[0] + ... + x[1] * p[n-1] + p[n] = y[1]
477 ...
478 x[k]**n * p[0] + ... + x[k] * p[n-1] + p[n] = y[k]
479
480 The coefficient matrix of the coefficients `p` is a Vandermonde matrix.
481
482 `polyfit` issues a `RankWarning` when the least-squares fit is badly
483 conditioned. This implies that the best fit is not well-defined due
484 to numerical error. The results may be improved by lowering the polynomial
485 degree or by replacing `x` by `x` - `x`.mean(). The `rcond` parameter
486 can also be set to a value smaller than its default, but the resulting
487 fit may be spurious: including contributions from the small singular
488 values can add numerical noise to the result.
489
490 Note that fitting polynomial coefficients is inherently badly conditioned
491 when the degree of the polynomial is large or the interval of sample points
492 is badly centered. The quality of the fit should always be checked in these
493 cases. When polynomial fits are not satisfactory, splines may be a good
494 alternative.
495
496 References
497 ----------
498 .. [1] Wikipedia, "Curve fitting",
499 http://en.wikipedia.org/wiki/Curve_fitting
500 .. [2] Wikipedia, "Polynomial interpolation",
501 http://en.wikipedia.org/wiki/Polynomial_interpolation
502
503 Examples
504 --------
505 >>> x = np.array([0.0, 1.0, 2.0, 3.0, 4.0, 5.0])
506 >>> y = np.array([0.0, 0.8, 0.9, 0.1, -0.8, -1.0])
507 >>> z = np.polyfit(x, y, 3)
508 >>> z
509 array([ 0.08703704, -0.81349206, 1.69312169, -0.03968254])
510
511 It is convenient to use `poly1d` objects for dealing with polynomials:
512
513 >>> p = np.poly1d(z)
514 >>> p(0.5)
515 0.6143849206349179
516 >>> p(3.5)
517 -0.34732142857143039
518 >>> p(10)
519 22.579365079365115
520
521 High-order polynomials may oscillate wildly:
522
523 >>> p30 = np.poly1d(np.polyfit(x, y, 30))
524 /... RankWarning: Polyfit may be poorly conditioned...
525 >>> p30(4)
526 -0.80000000000000204
527 >>> p30(5)
528 -0.99999999999999445
529 >>> p30(4.5)
530 -0.10547061179440398
531
532 Illustration:
533
534 >>> import matplotlib.pyplot as plt
535 >>> xp = np.linspace(-2, 6, 100)
536 >>> _ = plt.plot(x, y, '.', xp, p(xp), '-', xp, p30(xp), '--')
537 >>> plt.ylim(-2,2)
538 (-2, 2)
539 >>> plt.show()
540
541 """
542 order = int(deg) + 1
543 x = NX.asarray(x) + 0.0
544 y = NX.asarray(y) + 0.0
545
546 # check arguments.
547 if deg < 0:
548 raise ValueError("expected deg >= 0")
549 if x.ndim != 1:
550 raise TypeError("expected 1D vector for x")
551 if x.size == 0:
552 raise TypeError("expected non-empty vector for x")
553 if y.ndim < 1 or y.ndim > 2:
554 raise TypeError("expected 1D or 2D array for y")
555 if x.shape[0] != y.shape[0]:
556 raise TypeError("expected x and y to have same length")
557
558 # set rcond
559 if rcond is None:
560 rcond = len(x)*finfo(x.dtype).eps
561
562 # set up least squares equation for powers of x
563 lhs = vander(x, order)
564 rhs = y
565
566 # apply weighting
567 if w is not None:
568 w = NX.asarray(w) + 0.0
569 if w.ndim != 1:
570 raise TypeError("expected a 1-d array for weights")
571 if w.shape[0] != y.shape[0]:
572 raise TypeError("expected w and y to have the same length")
573 lhs *= w[:, NX.newaxis]
574 if rhs.ndim == 2:
575 rhs *= w[:, NX.newaxis]
576 else:
577 rhs *= w
578
579 # scale lhs to improve condition number and solve
580 scale = NX.sqrt((lhs*lhs).sum(axis=0))
581 lhs /= scale
582 c, resids, rank, s = lstsq(lhs, rhs, rcond)
583 c = (c.T/scale).T # broadcast scale coefficients
584
585 # warn on rank reduction, which indicates an ill conditioned matrix
586 if rank != order and not full:
587 msg = "Polyfit may be poorly conditioned"
588 warnings.warn(msg, RankWarning)
589
590 if full:
591 return c, resids, rank, s, rcond
592 elif cov:
593 Vbase = inv(dot(lhs.T, lhs))
594 Vbase /= NX.outer(scale, scale)
595 # Some literature ignores the extra -2.0 factor in the denominator, but
596 # it is included here because the covariance of Multivariate Student-T
597 # (which is implied by a Bayesian uncertainty analysis) includes it.
598 # Plus, it gives a slightly more conservative estimate of uncertainty.
599 fac = resids / (len(x) - order - 2.0)
600 if y.ndim == 1:
601 return c, Vbase * fac
602 else:
603 return c, Vbase[:,:, NX.newaxis] * fac
604 else:
605 return c
606
607
608 def polyval(p, x):
609 """
610 Evaluate a polynomial at specific values.
611
612 If `p` is of length N, this function returns the value:
613
614 ``p[0]*x**(N-1) + p[1]*x**(N-2) + ... + p[N-2]*x + p[N-1]``
615
616 If `x` is a sequence, then `p(x)` is returned for each element of `x`.
617 If `x` is another polynomial then the composite polynomial `p(x(t))`
618 is returned.
619
620 Parameters
621 ----------
622 p : array_like or poly1d object
623 1D array of polynomial coefficients (including coefficients equal
624 to zero) from highest degree to the constant term, or an
625 instance of poly1d.
626 x : array_like or poly1d object
627 A number, a 1D array of numbers, or an instance of poly1d, "at"
628 which to evaluate `p`.
629
630 Returns
631 -------
632 values : ndarray or poly1d
633 If `x` is a poly1d instance, the result is the composition of the two
634 polynomials, i.e., `x` is "substituted" in `p` and the simplified
635 result is returned. In addition, the type of `x` - array_like or
636 poly1d - governs the type of the output: `x` array_like => `values`
637 array_like, `x` a poly1d object => `values` is also.
638
639 See Also
640 --------
641 poly1d: A polynomial class.
642
643 Notes
644 -----
645 Horner's scheme [1]_ is used to evaluate the polynomial. Even so,
646 for polynomials of high degree the values may be inaccurate due to
647 rounding errors. Use carefully.
648
649 References
650 ----------
651 .. [1] I. N. Bronshtein, K. A. Semendyayev, and K. A. Hirsch (Eng.
652 trans. Ed.), *Handbook of Mathematics*, New York, Van Nostrand
653 Reinhold Co., 1985, pg. 720.
654
655 Examples
656 --------
657 >>> np.polyval([3,0,1], 5) # 3 * 5**2 + 0 * 5**1 + 1
658 76
659 >>> np.polyval([3,0,1], np.poly1d(5))
660 poly1d([ 76.])
661 >>> np.polyval(np.poly1d([3,0,1]), 5)
662 76
663 >>> np.polyval(np.poly1d([3,0,1]), np.poly1d(5))
664 poly1d([ 76.])
665
666 """
667 p = NX.asarray(p)
668 if isinstance(x, poly1d):
669 y = 0
670 else:
671 x = NX.asarray(x)
672 y = NX.zeros_like(x)
673 for i in range(len(p)):
674 y = x * y + p[i]
675 return y
676
677 def polyadd(a1, a2):
678 """
679 Find the sum of two polynomials.
680
681 Returns the polynomial resulting from the sum of two input polynomials.
682 Each input must be either a poly1d object or a 1D sequence of polynomial
683 coefficients, from highest to lowest degree.
684
685 Parameters
686 ----------
687 a1, a2 : array_like or poly1d object
688 Input polynomials.
689
690 Returns
691 -------
692 out : ndarray or poly1d object
693 The sum of the inputs. If either input is a poly1d object, then the
694 output is also a poly1d object. Otherwise, it is a 1D array of
695 polynomial coefficients from highest to lowest degree.
696
697 See Also
698 --------
699 poly1d : A one-dimensional polynomial class.
700 poly, polyadd, polyder, polydiv, polyfit, polyint, polysub, polyval
701
702 Examples
703 --------
704 >>> np.polyadd([1, 2], [9, 5, 4])
705 array([9, 6, 6])
706
707 Using poly1d objects:
708
709 >>> p1 = np.poly1d([1, 2])
710 >>> p2 = np.poly1d([9, 5, 4])
711 >>> print p1
712 1 x + 2
713 >>> print p2
714 2
715 9 x + 5 x + 4
716 >>> print np.polyadd(p1, p2)
717 2
718 9 x + 6 x + 6
719
720 """
721 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
722 a1 = atleast_1d(a1)
723 a2 = atleast_1d(a2)
724 diff = len(a2) - len(a1)
725 if diff == 0:
726 val = a1 + a2
727 elif diff > 0:
728 zr = NX.zeros(diff, a1.dtype)
729 val = NX.concatenate((zr, a1)) + a2
730 else:
731 zr = NX.zeros(abs(diff), a2.dtype)
732 val = a1 + NX.concatenate((zr, a2))
733 if truepoly:
734 val = poly1d(val)
735 return val
736
737 def polysub(a1, a2):
738 """
739 Difference (subtraction) of two polynomials.
740
741 Given two polynomials `a1` and `a2`, returns ``a1 - a2``.
742 `a1` and `a2` can be either array_like sequences of the polynomials'
743 coefficients (including coefficients equal to zero), or `poly1d` objects.
744
745 Parameters
746 ----------
747 a1, a2 : array_like or poly1d
748 Minuend and subtrahend polynomials, respectively.
749
750 Returns
751 -------
752 out : ndarray or poly1d
753 Array or `poly1d` object of the difference polynomial's coefficients.
754
755 See Also
756 --------
757 polyval, polydiv, polymul, polyadd
758
759 Examples
760 --------
761 .. math:: (2 x^2 + 10 x - 2) - (3 x^2 + 10 x -4) = (-x^2 + 2)
762
763 >>> np.polysub([2, 10, -2], [3, 10, -4])
764 array([-1, 0, 2])
765
766 """
767 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
768 a1 = atleast_1d(a1)
769 a2 = atleast_1d(a2)
770 diff = len(a2) - len(a1)
771 if diff == 0:
772 val = a1 - a2
773 elif diff > 0:
774 zr = NX.zeros(diff, a1.dtype)
775 val = NX.concatenate((zr, a1)) - a2
776 else:
777 zr = NX.zeros(abs(diff), a2.dtype)
778 val = a1 - NX.concatenate((zr, a2))
779 if truepoly:
780 val = poly1d(val)
781 return val
782
783
784 def polymul(a1, a2):
785 """
786 Find the product of two polynomials.
787
788 Finds the polynomial resulting from the multiplication of the two input
789 polynomials. Each input must be either a poly1d object or a 1D sequence
790 of polynomial coefficients, from highest to lowest degree.
791
792 Parameters
793 ----------
794 a1, a2 : array_like or poly1d object
795 Input polynomials.
796
797 Returns
798 -------
799 out : ndarray or poly1d object
800 The polynomial resulting from the multiplication of the inputs. If
801 either inputs is a poly1d object, then the output is also a poly1d
802 object. Otherwise, it is a 1D array of polynomial coefficients from
803 highest to lowest degree.
804
805 See Also
806 --------
807 poly1d : A one-dimensional polynomial class.
808 poly, polyadd, polyder, polydiv, polyfit, polyint, polysub,
809 polyval
810 convolve : Array convolution. Same output as polymul, but has parameter
811 for overlap mode.
812
813 Examples
814 --------
815 >>> np.polymul([1, 2, 3], [9, 5, 1])
816 array([ 9, 23, 38, 17, 3])
817
818 Using poly1d objects:
819
820 >>> p1 = np.poly1d([1, 2, 3])
821 >>> p2 = np.poly1d([9, 5, 1])
822 >>> print p1
823 2
824 1 x + 2 x + 3
825 >>> print p2
826 2
827 9 x + 5 x + 1
828 >>> print np.polymul(p1, p2)
829 4 3 2
830 9 x + 23 x + 38 x + 17 x + 3
831
832 """
833 truepoly = (isinstance(a1, poly1d) or isinstance(a2, poly1d))
834 a1, a2 = poly1d(a1), poly1d(a2)
835 val = NX.convolve(a1, a2)
836 if truepoly:
837 val = poly1d(val)
838 return val
839
840 def polydiv(u, v):
841 """
842 Returns the quotient and remainder of polynomial division.
843
844 The input arrays are the coefficients (including any coefficients
845 equal to zero) of the "numerator" (dividend) and "denominator"
846 (divisor) polynomials, respectively.
847
848 Parameters
849 ----------
850 u : array_like or poly1d
851 Dividend polynomial's coefficients.
852
853 v : array_like or poly1d
854 Divisor polynomial's coefficients.
855
856 Returns
857 -------
858 q : ndarray
859 Coefficients, including those equal to zero, of the quotient.
860 r : ndarray
861 Coefficients, including those equal to zero, of the remainder.
862
863 See Also
864 --------
865 poly, polyadd, polyder, polydiv, polyfit, polyint, polymul, polysub,
866 polyval
867
868 Notes
869 -----
870 Both `u` and `v` must be 0-d or 1-d (ndim = 0 or 1), but `u.ndim` need
871 not equal `v.ndim`. In other words, all four possible combinations -
872 ``u.ndim = v.ndim = 0``, ``u.ndim = v.ndim = 1``,
873 ``u.ndim = 1, v.ndim = 0``, and ``u.ndim = 0, v.ndim = 1`` - work.
874
875 Examples
876 --------
877 .. math:: \\frac{3x^2 + 5x + 2}{2x + 1} = 1.5x + 1.75, remainder 0.25
878
879 >>> x = np.array([3.0, 5.0, 2.0])
880 >>> y = np.array([2.0, 1.0])
881 >>> np.polydiv(x, y)
882 (array([ 1.5 , 1.75]), array([ 0.25]))
883
884 """
885 truepoly = (isinstance(u, poly1d) or isinstance(u, poly1d))
886 u = atleast_1d(u) + 0.0
887 v = atleast_1d(v) + 0.0
888 # w has the common type
889 w = u[0] + v[0]
890 m = len(u) - 1
891 n = len(v) - 1
892 scale = 1. / v[0]
893 q = NX.zeros((max(m - n + 1, 1),), w.dtype)
894 r = u.copy()
895 for k in range(0, m-n+1):
896 d = scale * r[k]
897 q[k] = d
898 r[k:k+n+1] -= d*v
899 while NX.allclose(r[0], 0, rtol=1e-14) and (r.shape[-1] > 1):
900 r = r[1:]
901 if truepoly:
902 return poly1d(q), poly1d(r)
903 return q, r
904
905 _poly_mat = re.compile(r"[*][*]([0-9]*)")
906 def _raise_power(astr, wrap=70):
907 n = 0
908 line1 = ''
909 line2 = ''
910 output = ' '
911 while True:
912 mat = _poly_mat.search(astr, n)
913 if mat is None:
914 break
915 span = mat.span()
916 power = mat.groups()[0]
917 partstr = astr[n:span[0]]
918 n = span[1]
919 toadd2 = partstr + ' '*(len(power)-1)
920 toadd1 = ' '*(len(partstr)-1) + power
921 if ((len(line2) + len(toadd2) > wrap) or
922 (len(line1) + len(toadd1) > wrap)):
923 output += line1 + "\n" + line2 + "\n "
924 line1 = toadd1
925 line2 = toadd2
926 else:
927 line2 += partstr + ' '*(len(power)-1)
928 line1 += ' '*(len(partstr)-1) + power
929 output += line1 + "\n" + line2
930 return output + astr[n:]
931
932
933 class poly1d(object):
934 """
935 A one-dimensional polynomial class.
936
937 A convenience class, used to encapsulate "natural" operations on
938 polynomials so that said operations may take on their customary
939 form in code (see Examples).
940
941 Parameters
942 ----------
943 c_or_r : array_like
944 The polynomial's coefficients, in decreasing powers, or if
945 the value of the second parameter is True, the polynomial's
946 roots (values where the polynomial evaluates to 0). For example,
947 ``poly1d([1, 2, 3])`` returns an object that represents
948 :math:`x^2 + 2x + 3`, whereas ``poly1d([1, 2, 3], True)`` returns
949 one that represents :math:`(x-1)(x-2)(x-3) = x^3 - 6x^2 + 11x -6`.
950 r : bool, optional
951 If True, `c_or_r` specifies the polynomial's roots; the default
952 is False.
953 variable : str, optional
954 Changes the variable used when printing `p` from `x` to `variable`
955 (see Examples).
956
957 Examples
958 --------
959 Construct the polynomial :math:`x^2 + 2x + 3`:
960
961 >>> p = np.poly1d([1, 2, 3])
962 >>> print np.poly1d(p)
963 2
964 1 x + 2 x + 3
965
966 Evaluate the polynomial at :math:`x = 0.5`:
967
968 >>> p(0.5)
969 4.25
970
971 Find the roots:
972
973 >>> p.r
974 array([-1.+1.41421356j, -1.-1.41421356j])
975 >>> p(p.r)
976 array([ -4.44089210e-16+0.j, -4.44089210e-16+0.j])
977
978 These numbers in the previous line represent (0, 0) to machine precision
979
980 Show the coefficients:
981
982 >>> p.c
983 array([1, 2, 3])
984
985 Display the order (the leading zero-coefficients are removed):
986
987 >>> p.order
988 2
989
990 Show the coefficient of the k-th power in the polynomial
991 (which is equivalent to ``p.c[-(i+1)]``):
992
993 >>> p[1]
994 2
995
996 Polynomials can be added, subtracted, multiplied, and divided
997 (returns quotient and remainder):
998
999 >>> p * p
1000 poly1d([ 1, 4, 10, 12, 9])
1001
1002 >>> (p**3 + 4) / p
1003 (poly1d([ 1., 4., 10., 12., 9.]), poly1d([ 4.]))
1004
1005 ``asarray(p)`` gives the coefficient array, so polynomials can be
1006 used in all functions that accept arrays:
1007
1008 >>> p**2 # square of polynomial
1009 poly1d([ 1, 4, 10, 12, 9])
1010
1011 >>> np.square(p) # square of individual coefficients
1012 array([1, 4, 9])
1013
1014 The variable used in the string representation of `p` can be modified,
1015 using the `variable` parameter:
1016
1017 >>> p = np.poly1d([1,2,3], variable='z')
1018 >>> print p
1019 2
1020 1 z + 2 z + 3
1021
1022 Construct a polynomial from its roots:
1023
1024 >>> np.poly1d([1, 2], True)
1025 poly1d([ 1, -3, 2])
1026
1027 This is the same polynomial as obtained by:
1028
1029 >>> np.poly1d([1, -1]) * np.poly1d([1, -2])
1030 poly1d([ 1, -3, 2])
1031
1032 """
1033 coeffs = None
1034 order = None
1035 variable = None
1036 __hash__ = None
1037
1038 def __init__(self, c_or_r, r=0, variable=None):
1039 if isinstance(c_or_r, poly1d):
1040 for key in c_or_r.__dict__.keys():
1041 self.__dict__[key] = c_or_r.__dict__[key]
1042 if variable is not None:
1043 self.__dict__['variable'] = variable
1044 return
1045 if r:
1046 c_or_r = poly(c_or_r)
1047 c_or_r = atleast_1d(c_or_r)
1048 if len(c_or_r.shape) > 1:
1049 raise ValueError("Polynomial must be 1d only.")
1050 c_or_r = trim_zeros(c_or_r, trim='f')
1051 if len(c_or_r) == 0:
1052 c_or_r = NX.array([0.])
1053 self.__dict__['coeffs'] = c_or_r
1054 self.__dict__['order'] = len(c_or_r) - 1
1055 if variable is None:
1056 variable = 'x'
1057 self.__dict__['variable'] = variable
1058
1059 def __array__(self, t=None):
1060 if t:
1061 return NX.asarray(self.coeffs, t)
1062 else:
1063 return NX.asarray(self.coeffs)
1064
1065 def __repr__(self):
1066 vals = repr(self.coeffs)
1067 vals = vals[6:-1]
1068 return "poly1d(%s)" % vals
1069
1070 def __len__(self):
1071 return self.order
1072
1073 def __str__(self):
1074 thestr = "0"
1075 var = self.variable
1076
1077 # Remove leading zeros
1078 coeffs = self.coeffs[NX.logical_or.accumulate(self.coeffs != 0)]
1079 N = len(coeffs)-1
1080
1081 def fmt_float(q):
1082 s = '%.4g' % q
1083 if s.endswith('.0000'):
1084 s = s[:-5]
1085 return s
1086
1087 for k in range(len(coeffs)):
1088 if not iscomplex(coeffs[k]):
1089 coefstr = fmt_float(real(coeffs[k]))
1090 elif real(coeffs[k]) == 0:
1091 coefstr = '%sj' % fmt_float(imag(coeffs[k]))
1092 else:
1093 coefstr = '(%s + %sj)' % (fmt_float(real(coeffs[k])),
1094 fmt_float(imag(coeffs[k])))
1095
1096 power = (N-k)
1097 if power == 0:
1098 if coefstr != '0':
1099 newstr = '%s' % (coefstr,)
1100 else:
1101 if k == 0:
1102 newstr = '0'
1103 else:
1104 newstr = ''
1105 elif power == 1:
1106 if coefstr == '0':
1107 newstr = ''
1108 elif coefstr == 'b':
1109 newstr = var
1110 else:
1111 newstr = '%s %s' % (coefstr, var)
1112 else:
1113 if coefstr == '0':
1114 newstr = ''
1115 elif coefstr == 'b':
1116 newstr = '%s**%d' % (var, power,)
1117 else:
1118 newstr = '%s %s**%d' % (coefstr, var, power)
1119
1120 if k > 0:
1121 if newstr != '':
1122 if newstr.startswith('-'):
1123 thestr = "%s - %s" % (thestr, newstr[1:])
1124 else:
1125 thestr = "%s + %s" % (thestr, newstr)
1126 else:
1127 thestr = newstr
1128 return _raise_power(thestr)
1129
1130 def __call__(self, val):
1131 return polyval(self.coeffs, val)
1132
1133 def __neg__(self):
1134 return poly1d(-self.coeffs)
1135
1136 def __pos__(self):
1137 return self
1138
1139 def __mul__(self, other):
1140 if isscalar(other):
1141 return poly1d(self.coeffs * other)
1142 else:
1143 other = poly1d(other)
1144 return poly1d(polymul(self.coeffs, other.coeffs))
1145
1146 def __rmul__(self, other):
1147 if isscalar(other):
1148 return poly1d(other * self.coeffs)
1149 else:
1150 other = poly1d(other)
1151 return poly1d(polymul(self.coeffs, other.coeffs))
1152
1153 def __add__(self, other):
1154 other = poly1d(other)
1155 return poly1d(polyadd(self.coeffs, other.coeffs))
1156
1157 def __radd__(self, other):
1158 other = poly1d(other)
1159 return poly1d(polyadd(self.coeffs, other.coeffs))
1160
1161 def __pow__(self, val):
1162 if not isscalar(val) or int(val) != val or val < 0:
1163 raise ValueError("Power to non-negative integers only.")
1164 res = [1]
1165 for _ in range(val):
1166 res = polymul(self.coeffs, res)
1167 return poly1d(res)
1168
1169 def __sub__(self, other):
1170 other = poly1d(other)
1171 return poly1d(polysub(self.coeffs, other.coeffs))
1172
1173 def __rsub__(self, other):
1174 other = poly1d(other)
1175 return poly1d(polysub(other.coeffs, self.coeffs))
1176
1177 def __div__(self, other):
1178 if isscalar(other):
1179 return poly1d(self.coeffs/other)
1180 else:
1181 other = poly1d(other)
1182 return polydiv(self, other)
1183
1184 __truediv__ = __div__
1185
1186 def __rdiv__(self, other):
1187 if isscalar(other):
1188 return poly1d(other/self.coeffs)
1189 else:
1190 other = poly1d(other)
1191 return polydiv(other, self)
1192
1193 __rtruediv__ = __rdiv__
1194
1195 def __eq__(self, other):
1196 if self.coeffs.shape != other.coeffs.shape:
1197 return False
1198 return (self.coeffs == other.coeffs).all()
1199
1200 def __ne__(self, other):
1201 return not self.__eq__(other)
1202
1203 def __setattr__(self, key, val):
1204 raise ValueError("Attributes cannot be changed this way.")
1205
1206 def __getattr__(self, key):
1207 if key in ['r', 'roots']:
1208 return roots(self.coeffs)
1209 elif key in ['c', 'coef', 'coefficients']:
1210 return self.coeffs
1211 elif key in ['o']:
1212 return self.order
1213 else:
1214 try:
1215 return self.__dict__[key]
1216 except KeyError:
1217 raise AttributeError(
1218 "'%s' has no attribute '%s'" % (self.__class__, key))
1219
1220 def __getitem__(self, val):
1221 ind = self.order - val
1222 if val > self.order:
1223 return 0
1224 if val < 0:
1225 return 0
1226 return self.coeffs[ind]
1227
1228 def __setitem__(self, key, val):
1229 ind = self.order - key
1230 if key < 0:
1231 raise ValueError("Does not support negative powers.")
1232 if key > self.order:
1233 zr = NX.zeros(key-self.order, self.coeffs.dtype)
1234 self.__dict__['coeffs'] = NX.concatenate((zr, self.coeffs))
1235 self.__dict__['order'] = key
1236 ind = 0
1237 self.__dict__['coeffs'][ind] = val
1238 return
1239
1240 def __iter__(self):
1241 return iter(self.coeffs)
1242
1243 def integ(self, m=1, k=0):
1244 """
1245 Return an antiderivative (indefinite integral) of this polynomial.
1246
1247 Refer to `polyint` for full documentation.
1248
1249 See Also
1250 --------
1251 polyint : equivalent function
1252
1253 """
1254 return poly1d(polyint(self.coeffs, m=m, k=k))
1255
1256 def deriv(self, m=1):
1257 """
1258 Return a derivative of this polynomial.
1259
1260 Refer to `polyder` for full documentation.
1261
1262 See Also
1263 --------
1264 polyder : equivalent function
1265
1266 """
1267 return poly1d(polyder(self.coeffs, m=m))
1268
1269 # Stuff to do on module import
1270
1271 warnings.simplefilter('always', RankWarning)