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