Chris@87
|
1 """
|
Chris@87
|
2 Objects for dealing with Chebyshev series.
|
Chris@87
|
3
|
Chris@87
|
4 This module provides a number of objects (mostly functions) useful for
|
Chris@87
|
5 dealing with Chebyshev series, including a `Chebyshev` class that
|
Chris@87
|
6 encapsulates the usual arithmetic operations. (General information
|
Chris@87
|
7 on how this module represents and works with such polynomials is in the
|
Chris@87
|
8 docstring for its "parent" sub-package, `numpy.polynomial`).
|
Chris@87
|
9
|
Chris@87
|
10 Constants
|
Chris@87
|
11 ---------
|
Chris@87
|
12 - `chebdomain` -- Chebyshev series default domain, [-1,1].
|
Chris@87
|
13 - `chebzero` -- (Coefficients of the) Chebyshev series that evaluates
|
Chris@87
|
14 identically to 0.
|
Chris@87
|
15 - `chebone` -- (Coefficients of the) Chebyshev series that evaluates
|
Chris@87
|
16 identically to 1.
|
Chris@87
|
17 - `chebx` -- (Coefficients of the) Chebyshev series for the identity map,
|
Chris@87
|
18 ``f(x) = x``.
|
Chris@87
|
19
|
Chris@87
|
20 Arithmetic
|
Chris@87
|
21 ----------
|
Chris@87
|
22 - `chebadd` -- add two Chebyshev series.
|
Chris@87
|
23 - `chebsub` -- subtract one Chebyshev series from another.
|
Chris@87
|
24 - `chebmul` -- multiply two Chebyshev series.
|
Chris@87
|
25 - `chebdiv` -- divide one Chebyshev series by another.
|
Chris@87
|
26 - `chebpow` -- raise a Chebyshev series to an positive integer power
|
Chris@87
|
27 - `chebval` -- evaluate a Chebyshev series at given points.
|
Chris@87
|
28 - `chebval2d` -- evaluate a 2D Chebyshev series at given points.
|
Chris@87
|
29 - `chebval3d` -- evaluate a 3D Chebyshev series at given points.
|
Chris@87
|
30 - `chebgrid2d` -- evaluate a 2D Chebyshev series on a Cartesian product.
|
Chris@87
|
31 - `chebgrid3d` -- evaluate a 3D Chebyshev series on a Cartesian product.
|
Chris@87
|
32
|
Chris@87
|
33 Calculus
|
Chris@87
|
34 --------
|
Chris@87
|
35 - `chebder` -- differentiate a Chebyshev series.
|
Chris@87
|
36 - `chebint` -- integrate a Chebyshev series.
|
Chris@87
|
37
|
Chris@87
|
38 Misc Functions
|
Chris@87
|
39 --------------
|
Chris@87
|
40 - `chebfromroots` -- create a Chebyshev series with specified roots.
|
Chris@87
|
41 - `chebroots` -- find the roots of a Chebyshev series.
|
Chris@87
|
42 - `chebvander` -- Vandermonde-like matrix for Chebyshev polynomials.
|
Chris@87
|
43 - `chebvander2d` -- Vandermonde-like matrix for 2D power series.
|
Chris@87
|
44 - `chebvander3d` -- Vandermonde-like matrix for 3D power series.
|
Chris@87
|
45 - `chebgauss` -- Gauss-Chebyshev quadrature, points and weights.
|
Chris@87
|
46 - `chebweight` -- Chebyshev weight function.
|
Chris@87
|
47 - `chebcompanion` -- symmetrized companion matrix in Chebyshev form.
|
Chris@87
|
48 - `chebfit` -- least-squares fit returning a Chebyshev series.
|
Chris@87
|
49 - `chebpts1` -- Chebyshev points of the first kind.
|
Chris@87
|
50 - `chebpts2` -- Chebyshev points of the second kind.
|
Chris@87
|
51 - `chebtrim` -- trim leading coefficients from a Chebyshev series.
|
Chris@87
|
52 - `chebline` -- Chebyshev series representing given straight line.
|
Chris@87
|
53 - `cheb2poly` -- convert a Chebyshev series to a polynomial.
|
Chris@87
|
54 - `poly2cheb` -- convert a polynomial to a Chebyshev series.
|
Chris@87
|
55
|
Chris@87
|
56 Classes
|
Chris@87
|
57 -------
|
Chris@87
|
58 - `Chebyshev` -- A Chebyshev series class.
|
Chris@87
|
59
|
Chris@87
|
60 See also
|
Chris@87
|
61 --------
|
Chris@87
|
62 `numpy.polynomial`
|
Chris@87
|
63
|
Chris@87
|
64 Notes
|
Chris@87
|
65 -----
|
Chris@87
|
66 The implementations of multiplication, division, integration, and
|
Chris@87
|
67 differentiation use the algebraic identities [1]_:
|
Chris@87
|
68
|
Chris@87
|
69 .. math ::
|
Chris@87
|
70 T_n(x) = \\frac{z^n + z^{-n}}{2} \\\\
|
Chris@87
|
71 z\\frac{dx}{dz} = \\frac{z - z^{-1}}{2}.
|
Chris@87
|
72
|
Chris@87
|
73 where
|
Chris@87
|
74
|
Chris@87
|
75 .. math :: x = \\frac{z + z^{-1}}{2}.
|
Chris@87
|
76
|
Chris@87
|
77 These identities allow a Chebyshev series to be expressed as a finite,
|
Chris@87
|
78 symmetric Laurent series. In this module, this sort of Laurent series
|
Chris@87
|
79 is referred to as a "z-series."
|
Chris@87
|
80
|
Chris@87
|
81 References
|
Chris@87
|
82 ----------
|
Chris@87
|
83 .. [1] A. T. Benjamin, et al., "Combinatorial Trigonometry with Chebyshev
|
Chris@87
|
84 Polynomials," *Journal of Statistical Planning and Inference 14*, 2008
|
Chris@87
|
85 (preprint: http://www.math.hmc.edu/~benjamin/papers/CombTrig.pdf, pg. 4)
|
Chris@87
|
86
|
Chris@87
|
87 """
|
Chris@87
|
88 from __future__ import division, absolute_import, print_function
|
Chris@87
|
89
|
Chris@87
|
90 import warnings
|
Chris@87
|
91 import numpy as np
|
Chris@87
|
92 import numpy.linalg as la
|
Chris@87
|
93
|
Chris@87
|
94 from . import polyutils as pu
|
Chris@87
|
95 from ._polybase import ABCPolyBase
|
Chris@87
|
96
|
Chris@87
|
97 __all__ = [
|
Chris@87
|
98 'chebzero', 'chebone', 'chebx', 'chebdomain', 'chebline', 'chebadd',
|
Chris@87
|
99 'chebsub', 'chebmulx', 'chebmul', 'chebdiv', 'chebpow', 'chebval',
|
Chris@87
|
100 'chebder', 'chebint', 'cheb2poly', 'poly2cheb', 'chebfromroots',
|
Chris@87
|
101 'chebvander', 'chebfit', 'chebtrim', 'chebroots', 'chebpts1',
|
Chris@87
|
102 'chebpts2', 'Chebyshev', 'chebval2d', 'chebval3d', 'chebgrid2d',
|
Chris@87
|
103 'chebgrid3d', 'chebvander2d', 'chebvander3d', 'chebcompanion',
|
Chris@87
|
104 'chebgauss', 'chebweight']
|
Chris@87
|
105
|
Chris@87
|
106 chebtrim = pu.trimcoef
|
Chris@87
|
107
|
Chris@87
|
108 #
|
Chris@87
|
109 # A collection of functions for manipulating z-series. These are private
|
Chris@87
|
110 # functions and do minimal error checking.
|
Chris@87
|
111 #
|
Chris@87
|
112
|
Chris@87
|
113 def _cseries_to_zseries(c):
|
Chris@87
|
114 """Covert Chebyshev series to z-series.
|
Chris@87
|
115
|
Chris@87
|
116 Covert a Chebyshev series to the equivalent z-series. The result is
|
Chris@87
|
117 never an empty array. The dtype of the return is the same as that of
|
Chris@87
|
118 the input. No checks are run on the arguments as this routine is for
|
Chris@87
|
119 internal use.
|
Chris@87
|
120
|
Chris@87
|
121 Parameters
|
Chris@87
|
122 ----------
|
Chris@87
|
123 c : 1-D ndarray
|
Chris@87
|
124 Chebyshev coefficients, ordered from low to high
|
Chris@87
|
125
|
Chris@87
|
126 Returns
|
Chris@87
|
127 -------
|
Chris@87
|
128 zs : 1-D ndarray
|
Chris@87
|
129 Odd length symmetric z-series, ordered from low to high.
|
Chris@87
|
130
|
Chris@87
|
131 """
|
Chris@87
|
132 n = c.size
|
Chris@87
|
133 zs = np.zeros(2*n-1, dtype=c.dtype)
|
Chris@87
|
134 zs[n-1:] = c/2
|
Chris@87
|
135 return zs + zs[::-1]
|
Chris@87
|
136
|
Chris@87
|
137
|
Chris@87
|
138 def _zseries_to_cseries(zs):
|
Chris@87
|
139 """Covert z-series to a Chebyshev series.
|
Chris@87
|
140
|
Chris@87
|
141 Covert a z series to the equivalent Chebyshev series. The result is
|
Chris@87
|
142 never an empty array. The dtype of the return is the same as that of
|
Chris@87
|
143 the input. No checks are run on the arguments as this routine is for
|
Chris@87
|
144 internal use.
|
Chris@87
|
145
|
Chris@87
|
146 Parameters
|
Chris@87
|
147 ----------
|
Chris@87
|
148 zs : 1-D ndarray
|
Chris@87
|
149 Odd length symmetric z-series, ordered from low to high.
|
Chris@87
|
150
|
Chris@87
|
151 Returns
|
Chris@87
|
152 -------
|
Chris@87
|
153 c : 1-D ndarray
|
Chris@87
|
154 Chebyshev coefficients, ordered from low to high.
|
Chris@87
|
155
|
Chris@87
|
156 """
|
Chris@87
|
157 n = (zs.size + 1)//2
|
Chris@87
|
158 c = zs[n-1:].copy()
|
Chris@87
|
159 c[1:n] *= 2
|
Chris@87
|
160 return c
|
Chris@87
|
161
|
Chris@87
|
162
|
Chris@87
|
163 def _zseries_mul(z1, z2):
|
Chris@87
|
164 """Multiply two z-series.
|
Chris@87
|
165
|
Chris@87
|
166 Multiply two z-series to produce a z-series.
|
Chris@87
|
167
|
Chris@87
|
168 Parameters
|
Chris@87
|
169 ----------
|
Chris@87
|
170 z1, z2 : 1-D ndarray
|
Chris@87
|
171 The arrays must be 1-D but this is not checked.
|
Chris@87
|
172
|
Chris@87
|
173 Returns
|
Chris@87
|
174 -------
|
Chris@87
|
175 product : 1-D ndarray
|
Chris@87
|
176 The product z-series.
|
Chris@87
|
177
|
Chris@87
|
178 Notes
|
Chris@87
|
179 -----
|
Chris@87
|
180 This is simply convolution. If symmetric/anti-symmetric z-series are
|
Chris@87
|
181 denoted by S/A then the following rules apply:
|
Chris@87
|
182
|
Chris@87
|
183 S*S, A*A -> S
|
Chris@87
|
184 S*A, A*S -> A
|
Chris@87
|
185
|
Chris@87
|
186 """
|
Chris@87
|
187 return np.convolve(z1, z2)
|
Chris@87
|
188
|
Chris@87
|
189
|
Chris@87
|
190 def _zseries_div(z1, z2):
|
Chris@87
|
191 """Divide the first z-series by the second.
|
Chris@87
|
192
|
Chris@87
|
193 Divide `z1` by `z2` and return the quotient and remainder as z-series.
|
Chris@87
|
194 Warning: this implementation only applies when both z1 and z2 have the
|
Chris@87
|
195 same symmetry, which is sufficient for present purposes.
|
Chris@87
|
196
|
Chris@87
|
197 Parameters
|
Chris@87
|
198 ----------
|
Chris@87
|
199 z1, z2 : 1-D ndarray
|
Chris@87
|
200 The arrays must be 1-D and have the same symmetry, but this is not
|
Chris@87
|
201 checked.
|
Chris@87
|
202
|
Chris@87
|
203 Returns
|
Chris@87
|
204 -------
|
Chris@87
|
205
|
Chris@87
|
206 (quotient, remainder) : 1-D ndarrays
|
Chris@87
|
207 Quotient and remainder as z-series.
|
Chris@87
|
208
|
Chris@87
|
209 Notes
|
Chris@87
|
210 -----
|
Chris@87
|
211 This is not the same as polynomial division on account of the desired form
|
Chris@87
|
212 of the remainder. If symmetric/anti-symmetric z-series are denoted by S/A
|
Chris@87
|
213 then the following rules apply:
|
Chris@87
|
214
|
Chris@87
|
215 S/S -> S,S
|
Chris@87
|
216 A/A -> S,A
|
Chris@87
|
217
|
Chris@87
|
218 The restriction to types of the same symmetry could be fixed but seems like
|
Chris@87
|
219 unneeded generality. There is no natural form for the remainder in the case
|
Chris@87
|
220 where there is no symmetry.
|
Chris@87
|
221
|
Chris@87
|
222 """
|
Chris@87
|
223 z1 = z1.copy()
|
Chris@87
|
224 z2 = z2.copy()
|
Chris@87
|
225 len1 = len(z1)
|
Chris@87
|
226 len2 = len(z2)
|
Chris@87
|
227 if len2 == 1:
|
Chris@87
|
228 z1 /= z2
|
Chris@87
|
229 return z1, z1[:1]*0
|
Chris@87
|
230 elif len1 < len2:
|
Chris@87
|
231 return z1[:1]*0, z1
|
Chris@87
|
232 else:
|
Chris@87
|
233 dlen = len1 - len2
|
Chris@87
|
234 scl = z2[0]
|
Chris@87
|
235 z2 /= scl
|
Chris@87
|
236 quo = np.empty(dlen + 1, dtype=z1.dtype)
|
Chris@87
|
237 i = 0
|
Chris@87
|
238 j = dlen
|
Chris@87
|
239 while i < j:
|
Chris@87
|
240 r = z1[i]
|
Chris@87
|
241 quo[i] = z1[i]
|
Chris@87
|
242 quo[dlen - i] = r
|
Chris@87
|
243 tmp = r*z2
|
Chris@87
|
244 z1[i:i+len2] -= tmp
|
Chris@87
|
245 z1[j:j+len2] -= tmp
|
Chris@87
|
246 i += 1
|
Chris@87
|
247 j -= 1
|
Chris@87
|
248 r = z1[i]
|
Chris@87
|
249 quo[i] = r
|
Chris@87
|
250 tmp = r*z2
|
Chris@87
|
251 z1[i:i+len2] -= tmp
|
Chris@87
|
252 quo /= scl
|
Chris@87
|
253 rem = z1[i+1:i-1+len2].copy()
|
Chris@87
|
254 return quo, rem
|
Chris@87
|
255
|
Chris@87
|
256
|
Chris@87
|
257 def _zseries_der(zs):
|
Chris@87
|
258 """Differentiate a z-series.
|
Chris@87
|
259
|
Chris@87
|
260 The derivative is with respect to x, not z. This is achieved using the
|
Chris@87
|
261 chain rule and the value of dx/dz given in the module notes.
|
Chris@87
|
262
|
Chris@87
|
263 Parameters
|
Chris@87
|
264 ----------
|
Chris@87
|
265 zs : z-series
|
Chris@87
|
266 The z-series to differentiate.
|
Chris@87
|
267
|
Chris@87
|
268 Returns
|
Chris@87
|
269 -------
|
Chris@87
|
270 derivative : z-series
|
Chris@87
|
271 The derivative
|
Chris@87
|
272
|
Chris@87
|
273 Notes
|
Chris@87
|
274 -----
|
Chris@87
|
275 The zseries for x (ns) has been multiplied by two in order to avoid
|
Chris@87
|
276 using floats that are incompatible with Decimal and likely other
|
Chris@87
|
277 specialized scalar types. This scaling has been compensated by
|
Chris@87
|
278 multiplying the value of zs by two also so that the two cancels in the
|
Chris@87
|
279 division.
|
Chris@87
|
280
|
Chris@87
|
281 """
|
Chris@87
|
282 n = len(zs)//2
|
Chris@87
|
283 ns = np.array([-1, 0, 1], dtype=zs.dtype)
|
Chris@87
|
284 zs *= np.arange(-n, n+1)*2
|
Chris@87
|
285 d, r = _zseries_div(zs, ns)
|
Chris@87
|
286 return d
|
Chris@87
|
287
|
Chris@87
|
288
|
Chris@87
|
289 def _zseries_int(zs):
|
Chris@87
|
290 """Integrate a z-series.
|
Chris@87
|
291
|
Chris@87
|
292 The integral is with respect to x, not z. This is achieved by a change
|
Chris@87
|
293 of variable using dx/dz given in the module notes.
|
Chris@87
|
294
|
Chris@87
|
295 Parameters
|
Chris@87
|
296 ----------
|
Chris@87
|
297 zs : z-series
|
Chris@87
|
298 The z-series to integrate
|
Chris@87
|
299
|
Chris@87
|
300 Returns
|
Chris@87
|
301 -------
|
Chris@87
|
302 integral : z-series
|
Chris@87
|
303 The indefinite integral
|
Chris@87
|
304
|
Chris@87
|
305 Notes
|
Chris@87
|
306 -----
|
Chris@87
|
307 The zseries for x (ns) has been multiplied by two in order to avoid
|
Chris@87
|
308 using floats that are incompatible with Decimal and likely other
|
Chris@87
|
309 specialized scalar types. This scaling has been compensated by
|
Chris@87
|
310 dividing the resulting zs by two.
|
Chris@87
|
311
|
Chris@87
|
312 """
|
Chris@87
|
313 n = 1 + len(zs)//2
|
Chris@87
|
314 ns = np.array([-1, 0, 1], dtype=zs.dtype)
|
Chris@87
|
315 zs = _zseries_mul(zs, ns)
|
Chris@87
|
316 div = np.arange(-n, n+1)*2
|
Chris@87
|
317 zs[:n] /= div[:n]
|
Chris@87
|
318 zs[n+1:] /= div[n+1:]
|
Chris@87
|
319 zs[n] = 0
|
Chris@87
|
320 return zs
|
Chris@87
|
321
|
Chris@87
|
322 #
|
Chris@87
|
323 # Chebyshev series functions
|
Chris@87
|
324 #
|
Chris@87
|
325
|
Chris@87
|
326
|
Chris@87
|
327 def poly2cheb(pol):
|
Chris@87
|
328 """
|
Chris@87
|
329 Convert a polynomial to a Chebyshev series.
|
Chris@87
|
330
|
Chris@87
|
331 Convert an array representing the coefficients of a polynomial (relative
|
Chris@87
|
332 to the "standard" basis) ordered from lowest degree to highest, to an
|
Chris@87
|
333 array of the coefficients of the equivalent Chebyshev series, ordered
|
Chris@87
|
334 from lowest to highest degree.
|
Chris@87
|
335
|
Chris@87
|
336 Parameters
|
Chris@87
|
337 ----------
|
Chris@87
|
338 pol : array_like
|
Chris@87
|
339 1-D array containing the polynomial coefficients
|
Chris@87
|
340
|
Chris@87
|
341 Returns
|
Chris@87
|
342 -------
|
Chris@87
|
343 c : ndarray
|
Chris@87
|
344 1-D array containing the coefficients of the equivalent Chebyshev
|
Chris@87
|
345 series.
|
Chris@87
|
346
|
Chris@87
|
347 See Also
|
Chris@87
|
348 --------
|
Chris@87
|
349 cheb2poly
|
Chris@87
|
350
|
Chris@87
|
351 Notes
|
Chris@87
|
352 -----
|
Chris@87
|
353 The easy way to do conversions between polynomial basis sets
|
Chris@87
|
354 is to use the convert method of a class instance.
|
Chris@87
|
355
|
Chris@87
|
356 Examples
|
Chris@87
|
357 --------
|
Chris@87
|
358 >>> from numpy import polynomial as P
|
Chris@87
|
359 >>> p = P.Polynomial(range(4))
|
Chris@87
|
360 >>> p
|
Chris@87
|
361 Polynomial([ 0., 1., 2., 3.], [-1., 1.])
|
Chris@87
|
362 >>> c = p.convert(kind=P.Chebyshev)
|
Chris@87
|
363 >>> c
|
Chris@87
|
364 Chebyshev([ 1. , 3.25, 1. , 0.75], [-1., 1.])
|
Chris@87
|
365 >>> P.poly2cheb(range(4))
|
Chris@87
|
366 array([ 1. , 3.25, 1. , 0.75])
|
Chris@87
|
367
|
Chris@87
|
368 """
|
Chris@87
|
369 [pol] = pu.as_series([pol])
|
Chris@87
|
370 deg = len(pol) - 1
|
Chris@87
|
371 res = 0
|
Chris@87
|
372 for i in range(deg, -1, -1):
|
Chris@87
|
373 res = chebadd(chebmulx(res), pol[i])
|
Chris@87
|
374 return res
|
Chris@87
|
375
|
Chris@87
|
376
|
Chris@87
|
377 def cheb2poly(c):
|
Chris@87
|
378 """
|
Chris@87
|
379 Convert a Chebyshev series to a polynomial.
|
Chris@87
|
380
|
Chris@87
|
381 Convert an array representing the coefficients of a Chebyshev series,
|
Chris@87
|
382 ordered from lowest degree to highest, to an array of the coefficients
|
Chris@87
|
383 of the equivalent polynomial (relative to the "standard" basis) ordered
|
Chris@87
|
384 from lowest to highest degree.
|
Chris@87
|
385
|
Chris@87
|
386 Parameters
|
Chris@87
|
387 ----------
|
Chris@87
|
388 c : array_like
|
Chris@87
|
389 1-D array containing the Chebyshev series coefficients, ordered
|
Chris@87
|
390 from lowest order term to highest.
|
Chris@87
|
391
|
Chris@87
|
392 Returns
|
Chris@87
|
393 -------
|
Chris@87
|
394 pol : ndarray
|
Chris@87
|
395 1-D array containing the coefficients of the equivalent polynomial
|
Chris@87
|
396 (relative to the "standard" basis) ordered from lowest order term
|
Chris@87
|
397 to highest.
|
Chris@87
|
398
|
Chris@87
|
399 See Also
|
Chris@87
|
400 --------
|
Chris@87
|
401 poly2cheb
|
Chris@87
|
402
|
Chris@87
|
403 Notes
|
Chris@87
|
404 -----
|
Chris@87
|
405 The easy way to do conversions between polynomial basis sets
|
Chris@87
|
406 is to use the convert method of a class instance.
|
Chris@87
|
407
|
Chris@87
|
408 Examples
|
Chris@87
|
409 --------
|
Chris@87
|
410 >>> from numpy import polynomial as P
|
Chris@87
|
411 >>> c = P.Chebyshev(range(4))
|
Chris@87
|
412 >>> c
|
Chris@87
|
413 Chebyshev([ 0., 1., 2., 3.], [-1., 1.])
|
Chris@87
|
414 >>> p = c.convert(kind=P.Polynomial)
|
Chris@87
|
415 >>> p
|
Chris@87
|
416 Polynomial([ -2., -8., 4., 12.], [-1., 1.])
|
Chris@87
|
417 >>> P.cheb2poly(range(4))
|
Chris@87
|
418 array([ -2., -8., 4., 12.])
|
Chris@87
|
419
|
Chris@87
|
420 """
|
Chris@87
|
421 from .polynomial import polyadd, polysub, polymulx
|
Chris@87
|
422
|
Chris@87
|
423 [c] = pu.as_series([c])
|
Chris@87
|
424 n = len(c)
|
Chris@87
|
425 if n < 3:
|
Chris@87
|
426 return c
|
Chris@87
|
427 else:
|
Chris@87
|
428 c0 = c[-2]
|
Chris@87
|
429 c1 = c[-1]
|
Chris@87
|
430 # i is the current degree of c1
|
Chris@87
|
431 for i in range(n - 1, 1, -1):
|
Chris@87
|
432 tmp = c0
|
Chris@87
|
433 c0 = polysub(c[i - 2], c1)
|
Chris@87
|
434 c1 = polyadd(tmp, polymulx(c1)*2)
|
Chris@87
|
435 return polyadd(c0, polymulx(c1))
|
Chris@87
|
436
|
Chris@87
|
437
|
Chris@87
|
438 #
|
Chris@87
|
439 # These are constant arrays are of integer type so as to be compatible
|
Chris@87
|
440 # with the widest range of other types, such as Decimal.
|
Chris@87
|
441 #
|
Chris@87
|
442
|
Chris@87
|
443 # Chebyshev default domain.
|
Chris@87
|
444 chebdomain = np.array([-1, 1])
|
Chris@87
|
445
|
Chris@87
|
446 # Chebyshev coefficients representing zero.
|
Chris@87
|
447 chebzero = np.array([0])
|
Chris@87
|
448
|
Chris@87
|
449 # Chebyshev coefficients representing one.
|
Chris@87
|
450 chebone = np.array([1])
|
Chris@87
|
451
|
Chris@87
|
452 # Chebyshev coefficients representing the identity x.
|
Chris@87
|
453 chebx = np.array([0, 1])
|
Chris@87
|
454
|
Chris@87
|
455
|
Chris@87
|
456 def chebline(off, scl):
|
Chris@87
|
457 """
|
Chris@87
|
458 Chebyshev series whose graph is a straight line.
|
Chris@87
|
459
|
Chris@87
|
460
|
Chris@87
|
461
|
Chris@87
|
462 Parameters
|
Chris@87
|
463 ----------
|
Chris@87
|
464 off, scl : scalars
|
Chris@87
|
465 The specified line is given by ``off + scl*x``.
|
Chris@87
|
466
|
Chris@87
|
467 Returns
|
Chris@87
|
468 -------
|
Chris@87
|
469 y : ndarray
|
Chris@87
|
470 This module's representation of the Chebyshev series for
|
Chris@87
|
471 ``off + scl*x``.
|
Chris@87
|
472
|
Chris@87
|
473 See Also
|
Chris@87
|
474 --------
|
Chris@87
|
475 polyline
|
Chris@87
|
476
|
Chris@87
|
477 Examples
|
Chris@87
|
478 --------
|
Chris@87
|
479 >>> import numpy.polynomial.chebyshev as C
|
Chris@87
|
480 >>> C.chebline(3,2)
|
Chris@87
|
481 array([3, 2])
|
Chris@87
|
482 >>> C.chebval(-3, C.chebline(3,2)) # should be -3
|
Chris@87
|
483 -3.0
|
Chris@87
|
484
|
Chris@87
|
485 """
|
Chris@87
|
486 if scl != 0:
|
Chris@87
|
487 return np.array([off, scl])
|
Chris@87
|
488 else:
|
Chris@87
|
489 return np.array([off])
|
Chris@87
|
490
|
Chris@87
|
491
|
Chris@87
|
492 def chebfromroots(roots):
|
Chris@87
|
493 """
|
Chris@87
|
494 Generate a Chebyshev series with given roots.
|
Chris@87
|
495
|
Chris@87
|
496 The function returns the coefficients of the polynomial
|
Chris@87
|
497
|
Chris@87
|
498 .. math:: p(x) = (x - r_0) * (x - r_1) * ... * (x - r_n),
|
Chris@87
|
499
|
Chris@87
|
500 in Chebyshev form, where the `r_n` are the roots specified in `roots`.
|
Chris@87
|
501 If a zero has multiplicity n, then it must appear in `roots` n times.
|
Chris@87
|
502 For instance, if 2 is a root of multiplicity three and 3 is a root of
|
Chris@87
|
503 multiplicity 2, then `roots` looks something like [2, 2, 2, 3, 3]. The
|
Chris@87
|
504 roots can appear in any order.
|
Chris@87
|
505
|
Chris@87
|
506 If the returned coefficients are `c`, then
|
Chris@87
|
507
|
Chris@87
|
508 .. math:: p(x) = c_0 + c_1 * T_1(x) + ... + c_n * T_n(x)
|
Chris@87
|
509
|
Chris@87
|
510 The coefficient of the last term is not generally 1 for monic
|
Chris@87
|
511 polynomials in Chebyshev form.
|
Chris@87
|
512
|
Chris@87
|
513 Parameters
|
Chris@87
|
514 ----------
|
Chris@87
|
515 roots : array_like
|
Chris@87
|
516 Sequence containing the roots.
|
Chris@87
|
517
|
Chris@87
|
518 Returns
|
Chris@87
|
519 -------
|
Chris@87
|
520 out : ndarray
|
Chris@87
|
521 1-D array of coefficients. If all roots are real then `out` is a
|
Chris@87
|
522 real array, if some of the roots are complex, then `out` is complex
|
Chris@87
|
523 even if all the coefficients in the result are real (see Examples
|
Chris@87
|
524 below).
|
Chris@87
|
525
|
Chris@87
|
526 See Also
|
Chris@87
|
527 --------
|
Chris@87
|
528 polyfromroots, legfromroots, lagfromroots, hermfromroots,
|
Chris@87
|
529 hermefromroots.
|
Chris@87
|
530
|
Chris@87
|
531 Examples
|
Chris@87
|
532 --------
|
Chris@87
|
533 >>> import numpy.polynomial.chebyshev as C
|
Chris@87
|
534 >>> C.chebfromroots((-1,0,1)) # x^3 - x relative to the standard basis
|
Chris@87
|
535 array([ 0. , -0.25, 0. , 0.25])
|
Chris@87
|
536 >>> j = complex(0,1)
|
Chris@87
|
537 >>> C.chebfromroots((-j,j)) # x^2 + 1 relative to the standard basis
|
Chris@87
|
538 array([ 1.5+0.j, 0.0+0.j, 0.5+0.j])
|
Chris@87
|
539
|
Chris@87
|
540 """
|
Chris@87
|
541 if len(roots) == 0:
|
Chris@87
|
542 return np.ones(1)
|
Chris@87
|
543 else:
|
Chris@87
|
544 [roots] = pu.as_series([roots], trim=False)
|
Chris@87
|
545 roots.sort()
|
Chris@87
|
546 p = [chebline(-r, 1) for r in roots]
|
Chris@87
|
547 n = len(p)
|
Chris@87
|
548 while n > 1:
|
Chris@87
|
549 m, r = divmod(n, 2)
|
Chris@87
|
550 tmp = [chebmul(p[i], p[i+m]) for i in range(m)]
|
Chris@87
|
551 if r:
|
Chris@87
|
552 tmp[0] = chebmul(tmp[0], p[-1])
|
Chris@87
|
553 p = tmp
|
Chris@87
|
554 n = m
|
Chris@87
|
555 return p[0]
|
Chris@87
|
556
|
Chris@87
|
557
|
Chris@87
|
558 def chebadd(c1, c2):
|
Chris@87
|
559 """
|
Chris@87
|
560 Add one Chebyshev series to another.
|
Chris@87
|
561
|
Chris@87
|
562 Returns the sum of two Chebyshev series `c1` + `c2`. The arguments
|
Chris@87
|
563 are sequences of coefficients ordered from lowest order term to
|
Chris@87
|
564 highest, i.e., [1,2,3] represents the series ``T_0 + 2*T_1 + 3*T_2``.
|
Chris@87
|
565
|
Chris@87
|
566 Parameters
|
Chris@87
|
567 ----------
|
Chris@87
|
568 c1, c2 : array_like
|
Chris@87
|
569 1-D arrays of Chebyshev series coefficients ordered from low to
|
Chris@87
|
570 high.
|
Chris@87
|
571
|
Chris@87
|
572 Returns
|
Chris@87
|
573 -------
|
Chris@87
|
574 out : ndarray
|
Chris@87
|
575 Array representing the Chebyshev series of their sum.
|
Chris@87
|
576
|
Chris@87
|
577 See Also
|
Chris@87
|
578 --------
|
Chris@87
|
579 chebsub, chebmul, chebdiv, chebpow
|
Chris@87
|
580
|
Chris@87
|
581 Notes
|
Chris@87
|
582 -----
|
Chris@87
|
583 Unlike multiplication, division, etc., the sum of two Chebyshev series
|
Chris@87
|
584 is a Chebyshev series (without having to "reproject" the result onto
|
Chris@87
|
585 the basis set) so addition, just like that of "standard" polynomials,
|
Chris@87
|
586 is simply "component-wise."
|
Chris@87
|
587
|
Chris@87
|
588 Examples
|
Chris@87
|
589 --------
|
Chris@87
|
590 >>> from numpy.polynomial import chebyshev as C
|
Chris@87
|
591 >>> c1 = (1,2,3)
|
Chris@87
|
592 >>> c2 = (3,2,1)
|
Chris@87
|
593 >>> C.chebadd(c1,c2)
|
Chris@87
|
594 array([ 4., 4., 4.])
|
Chris@87
|
595
|
Chris@87
|
596 """
|
Chris@87
|
597 # c1, c2 are trimmed copies
|
Chris@87
|
598 [c1, c2] = pu.as_series([c1, c2])
|
Chris@87
|
599 if len(c1) > len(c2):
|
Chris@87
|
600 c1[:c2.size] += c2
|
Chris@87
|
601 ret = c1
|
Chris@87
|
602 else:
|
Chris@87
|
603 c2[:c1.size] += c1
|
Chris@87
|
604 ret = c2
|
Chris@87
|
605 return pu.trimseq(ret)
|
Chris@87
|
606
|
Chris@87
|
607
|
Chris@87
|
608 def chebsub(c1, c2):
|
Chris@87
|
609 """
|
Chris@87
|
610 Subtract one Chebyshev series from another.
|
Chris@87
|
611
|
Chris@87
|
612 Returns the difference of two Chebyshev series `c1` - `c2`. The
|
Chris@87
|
613 sequences of coefficients are from lowest order term to highest, i.e.,
|
Chris@87
|
614 [1,2,3] represents the series ``T_0 + 2*T_1 + 3*T_2``.
|
Chris@87
|
615
|
Chris@87
|
616 Parameters
|
Chris@87
|
617 ----------
|
Chris@87
|
618 c1, c2 : array_like
|
Chris@87
|
619 1-D arrays of Chebyshev series coefficients ordered from low to
|
Chris@87
|
620 high.
|
Chris@87
|
621
|
Chris@87
|
622 Returns
|
Chris@87
|
623 -------
|
Chris@87
|
624 out : ndarray
|
Chris@87
|
625 Of Chebyshev series coefficients representing their difference.
|
Chris@87
|
626
|
Chris@87
|
627 See Also
|
Chris@87
|
628 --------
|
Chris@87
|
629 chebadd, chebmul, chebdiv, chebpow
|
Chris@87
|
630
|
Chris@87
|
631 Notes
|
Chris@87
|
632 -----
|
Chris@87
|
633 Unlike multiplication, division, etc., the difference of two Chebyshev
|
Chris@87
|
634 series is a Chebyshev series (without having to "reproject" the result
|
Chris@87
|
635 onto the basis set) so subtraction, just like that of "standard"
|
Chris@87
|
636 polynomials, is simply "component-wise."
|
Chris@87
|
637
|
Chris@87
|
638 Examples
|
Chris@87
|
639 --------
|
Chris@87
|
640 >>> from numpy.polynomial import chebyshev as C
|
Chris@87
|
641 >>> c1 = (1,2,3)
|
Chris@87
|
642 >>> c2 = (3,2,1)
|
Chris@87
|
643 >>> C.chebsub(c1,c2)
|
Chris@87
|
644 array([-2., 0., 2.])
|
Chris@87
|
645 >>> C.chebsub(c2,c1) # -C.chebsub(c1,c2)
|
Chris@87
|
646 array([ 2., 0., -2.])
|
Chris@87
|
647
|
Chris@87
|
648 """
|
Chris@87
|
649 # c1, c2 are trimmed copies
|
Chris@87
|
650 [c1, c2] = pu.as_series([c1, c2])
|
Chris@87
|
651 if len(c1) > len(c2):
|
Chris@87
|
652 c1[:c2.size] -= c2
|
Chris@87
|
653 ret = c1
|
Chris@87
|
654 else:
|
Chris@87
|
655 c2 = -c2
|
Chris@87
|
656 c2[:c1.size] += c1
|
Chris@87
|
657 ret = c2
|
Chris@87
|
658 return pu.trimseq(ret)
|
Chris@87
|
659
|
Chris@87
|
660
|
Chris@87
|
661 def chebmulx(c):
|
Chris@87
|
662 """Multiply a Chebyshev series by x.
|
Chris@87
|
663
|
Chris@87
|
664 Multiply the polynomial `c` by x, where x is the independent
|
Chris@87
|
665 variable.
|
Chris@87
|
666
|
Chris@87
|
667
|
Chris@87
|
668 Parameters
|
Chris@87
|
669 ----------
|
Chris@87
|
670 c : array_like
|
Chris@87
|
671 1-D array of Chebyshev series coefficients ordered from low to
|
Chris@87
|
672 high.
|
Chris@87
|
673
|
Chris@87
|
674 Returns
|
Chris@87
|
675 -------
|
Chris@87
|
676 out : ndarray
|
Chris@87
|
677 Array representing the result of the multiplication.
|
Chris@87
|
678
|
Chris@87
|
679 Notes
|
Chris@87
|
680 -----
|
Chris@87
|
681
|
Chris@87
|
682 .. versionadded:: 1.5.0
|
Chris@87
|
683
|
Chris@87
|
684 """
|
Chris@87
|
685 # c is a trimmed copy
|
Chris@87
|
686 [c] = pu.as_series([c])
|
Chris@87
|
687 # The zero series needs special treatment
|
Chris@87
|
688 if len(c) == 1 and c[0] == 0:
|
Chris@87
|
689 return c
|
Chris@87
|
690
|
Chris@87
|
691 prd = np.empty(len(c) + 1, dtype=c.dtype)
|
Chris@87
|
692 prd[0] = c[0]*0
|
Chris@87
|
693 prd[1] = c[0]
|
Chris@87
|
694 if len(c) > 1:
|
Chris@87
|
695 tmp = c[1:]/2
|
Chris@87
|
696 prd[2:] = tmp
|
Chris@87
|
697 prd[0:-2] += tmp
|
Chris@87
|
698 return prd
|
Chris@87
|
699
|
Chris@87
|
700
|
Chris@87
|
701 def chebmul(c1, c2):
|
Chris@87
|
702 """
|
Chris@87
|
703 Multiply one Chebyshev series by another.
|
Chris@87
|
704
|
Chris@87
|
705 Returns the product of two Chebyshev series `c1` * `c2`. The arguments
|
Chris@87
|
706 are sequences of coefficients, from lowest order "term" to highest,
|
Chris@87
|
707 e.g., [1,2,3] represents the series ``T_0 + 2*T_1 + 3*T_2``.
|
Chris@87
|
708
|
Chris@87
|
709 Parameters
|
Chris@87
|
710 ----------
|
Chris@87
|
711 c1, c2 : array_like
|
Chris@87
|
712 1-D arrays of Chebyshev series coefficients ordered from low to
|
Chris@87
|
713 high.
|
Chris@87
|
714
|
Chris@87
|
715 Returns
|
Chris@87
|
716 -------
|
Chris@87
|
717 out : ndarray
|
Chris@87
|
718 Of Chebyshev series coefficients representing their product.
|
Chris@87
|
719
|
Chris@87
|
720 See Also
|
Chris@87
|
721 --------
|
Chris@87
|
722 chebadd, chebsub, chebdiv, chebpow
|
Chris@87
|
723
|
Chris@87
|
724 Notes
|
Chris@87
|
725 -----
|
Chris@87
|
726 In general, the (polynomial) product of two C-series results in terms
|
Chris@87
|
727 that are not in the Chebyshev polynomial basis set. Thus, to express
|
Chris@87
|
728 the product as a C-series, it is typically necessary to "reproject"
|
Chris@87
|
729 the product onto said basis set, which typically produces
|
Chris@87
|
730 "unintuitive live" (but correct) results; see Examples section below.
|
Chris@87
|
731
|
Chris@87
|
732 Examples
|
Chris@87
|
733 --------
|
Chris@87
|
734 >>> from numpy.polynomial import chebyshev as C
|
Chris@87
|
735 >>> c1 = (1,2,3)
|
Chris@87
|
736 >>> c2 = (3,2,1)
|
Chris@87
|
737 >>> C.chebmul(c1,c2) # multiplication requires "reprojection"
|
Chris@87
|
738 array([ 6.5, 12. , 12. , 4. , 1.5])
|
Chris@87
|
739
|
Chris@87
|
740 """
|
Chris@87
|
741 # c1, c2 are trimmed copies
|
Chris@87
|
742 [c1, c2] = pu.as_series([c1, c2])
|
Chris@87
|
743 z1 = _cseries_to_zseries(c1)
|
Chris@87
|
744 z2 = _cseries_to_zseries(c2)
|
Chris@87
|
745 prd = _zseries_mul(z1, z2)
|
Chris@87
|
746 ret = _zseries_to_cseries(prd)
|
Chris@87
|
747 return pu.trimseq(ret)
|
Chris@87
|
748
|
Chris@87
|
749
|
Chris@87
|
750 def chebdiv(c1, c2):
|
Chris@87
|
751 """
|
Chris@87
|
752 Divide one Chebyshev series by another.
|
Chris@87
|
753
|
Chris@87
|
754 Returns the quotient-with-remainder of two Chebyshev series
|
Chris@87
|
755 `c1` / `c2`. The arguments are sequences of coefficients from lowest
|
Chris@87
|
756 order "term" to highest, e.g., [1,2,3] represents the series
|
Chris@87
|
757 ``T_0 + 2*T_1 + 3*T_2``.
|
Chris@87
|
758
|
Chris@87
|
759 Parameters
|
Chris@87
|
760 ----------
|
Chris@87
|
761 c1, c2 : array_like
|
Chris@87
|
762 1-D arrays of Chebyshev series coefficients ordered from low to
|
Chris@87
|
763 high.
|
Chris@87
|
764
|
Chris@87
|
765 Returns
|
Chris@87
|
766 -------
|
Chris@87
|
767 [quo, rem] : ndarrays
|
Chris@87
|
768 Of Chebyshev series coefficients representing the quotient and
|
Chris@87
|
769 remainder.
|
Chris@87
|
770
|
Chris@87
|
771 See Also
|
Chris@87
|
772 --------
|
Chris@87
|
773 chebadd, chebsub, chebmul, chebpow
|
Chris@87
|
774
|
Chris@87
|
775 Notes
|
Chris@87
|
776 -----
|
Chris@87
|
777 In general, the (polynomial) division of one C-series by another
|
Chris@87
|
778 results in quotient and remainder terms that are not in the Chebyshev
|
Chris@87
|
779 polynomial basis set. Thus, to express these results as C-series, it
|
Chris@87
|
780 is typically necessary to "reproject" the results onto said basis
|
Chris@87
|
781 set, which typically produces "unintuitive" (but correct) results;
|
Chris@87
|
782 see Examples section below.
|
Chris@87
|
783
|
Chris@87
|
784 Examples
|
Chris@87
|
785 --------
|
Chris@87
|
786 >>> from numpy.polynomial import chebyshev as C
|
Chris@87
|
787 >>> c1 = (1,2,3)
|
Chris@87
|
788 >>> c2 = (3,2,1)
|
Chris@87
|
789 >>> C.chebdiv(c1,c2) # quotient "intuitive," remainder not
|
Chris@87
|
790 (array([ 3.]), array([-8., -4.]))
|
Chris@87
|
791 >>> c2 = (0,1,2,3)
|
Chris@87
|
792 >>> C.chebdiv(c2,c1) # neither "intuitive"
|
Chris@87
|
793 (array([ 0., 2.]), array([-2., -4.]))
|
Chris@87
|
794
|
Chris@87
|
795 """
|
Chris@87
|
796 # c1, c2 are trimmed copies
|
Chris@87
|
797 [c1, c2] = pu.as_series([c1, c2])
|
Chris@87
|
798 if c2[-1] == 0:
|
Chris@87
|
799 raise ZeroDivisionError()
|
Chris@87
|
800
|
Chris@87
|
801 lc1 = len(c1)
|
Chris@87
|
802 lc2 = len(c2)
|
Chris@87
|
803 if lc1 < lc2:
|
Chris@87
|
804 return c1[:1]*0, c1
|
Chris@87
|
805 elif lc2 == 1:
|
Chris@87
|
806 return c1/c2[-1], c1[:1]*0
|
Chris@87
|
807 else:
|
Chris@87
|
808 z1 = _cseries_to_zseries(c1)
|
Chris@87
|
809 z2 = _cseries_to_zseries(c2)
|
Chris@87
|
810 quo, rem = _zseries_div(z1, z2)
|
Chris@87
|
811 quo = pu.trimseq(_zseries_to_cseries(quo))
|
Chris@87
|
812 rem = pu.trimseq(_zseries_to_cseries(rem))
|
Chris@87
|
813 return quo, rem
|
Chris@87
|
814
|
Chris@87
|
815
|
Chris@87
|
816 def chebpow(c, pow, maxpower=16):
|
Chris@87
|
817 """Raise a Chebyshev series to a power.
|
Chris@87
|
818
|
Chris@87
|
819 Returns the Chebyshev series `c` raised to the power `pow`. The
|
Chris@87
|
820 argument `c` is a sequence of coefficients ordered from low to high.
|
Chris@87
|
821 i.e., [1,2,3] is the series ``T_0 + 2*T_1 + 3*T_2.``
|
Chris@87
|
822
|
Chris@87
|
823 Parameters
|
Chris@87
|
824 ----------
|
Chris@87
|
825 c : array_like
|
Chris@87
|
826 1-D array of Chebyshev series coefficients ordered from low to
|
Chris@87
|
827 high.
|
Chris@87
|
828 pow : integer
|
Chris@87
|
829 Power to which the series will be raised
|
Chris@87
|
830 maxpower : integer, optional
|
Chris@87
|
831 Maximum power allowed. This is mainly to limit growth of the series
|
Chris@87
|
832 to unmanageable size. Default is 16
|
Chris@87
|
833
|
Chris@87
|
834 Returns
|
Chris@87
|
835 -------
|
Chris@87
|
836 coef : ndarray
|
Chris@87
|
837 Chebyshev series of power.
|
Chris@87
|
838
|
Chris@87
|
839 See Also
|
Chris@87
|
840 --------
|
Chris@87
|
841 chebadd, chebsub, chebmul, chebdiv
|
Chris@87
|
842
|
Chris@87
|
843 Examples
|
Chris@87
|
844 --------
|
Chris@87
|
845
|
Chris@87
|
846 """
|
Chris@87
|
847 # c is a trimmed copy
|
Chris@87
|
848 [c] = pu.as_series([c])
|
Chris@87
|
849 power = int(pow)
|
Chris@87
|
850 if power != pow or power < 0:
|
Chris@87
|
851 raise ValueError("Power must be a non-negative integer.")
|
Chris@87
|
852 elif maxpower is not None and power > maxpower:
|
Chris@87
|
853 raise ValueError("Power is too large")
|
Chris@87
|
854 elif power == 0:
|
Chris@87
|
855 return np.array([1], dtype=c.dtype)
|
Chris@87
|
856 elif power == 1:
|
Chris@87
|
857 return c
|
Chris@87
|
858 else:
|
Chris@87
|
859 # This can be made more efficient by using powers of two
|
Chris@87
|
860 # in the usual way.
|
Chris@87
|
861 zs = _cseries_to_zseries(c)
|
Chris@87
|
862 prd = zs
|
Chris@87
|
863 for i in range(2, power + 1):
|
Chris@87
|
864 prd = np.convolve(prd, zs)
|
Chris@87
|
865 return _zseries_to_cseries(prd)
|
Chris@87
|
866
|
Chris@87
|
867
|
Chris@87
|
868 def chebder(c, m=1, scl=1, axis=0):
|
Chris@87
|
869 """
|
Chris@87
|
870 Differentiate a Chebyshev series.
|
Chris@87
|
871
|
Chris@87
|
872 Returns the Chebyshev series coefficients `c` differentiated `m` times
|
Chris@87
|
873 along `axis`. At each iteration the result is multiplied by `scl` (the
|
Chris@87
|
874 scaling factor is for use in a linear change of variable). The argument
|
Chris@87
|
875 `c` is an array of coefficients from low to high degree along each
|
Chris@87
|
876 axis, e.g., [1,2,3] represents the series ``1*T_0 + 2*T_1 + 3*T_2``
|
Chris@87
|
877 while [[1,2],[1,2]] represents ``1*T_0(x)*T_0(y) + 1*T_1(x)*T_0(y) +
|
Chris@87
|
878 2*T_0(x)*T_1(y) + 2*T_1(x)*T_1(y)`` if axis=0 is ``x`` and axis=1 is
|
Chris@87
|
879 ``y``.
|
Chris@87
|
880
|
Chris@87
|
881 Parameters
|
Chris@87
|
882 ----------
|
Chris@87
|
883 c : array_like
|
Chris@87
|
884 Array of Chebyshev series coefficients. If c is multidimensional
|
Chris@87
|
885 the different axis correspond to different variables with the
|
Chris@87
|
886 degree in each axis given by the corresponding index.
|
Chris@87
|
887 m : int, optional
|
Chris@87
|
888 Number of derivatives taken, must be non-negative. (Default: 1)
|
Chris@87
|
889 scl : scalar, optional
|
Chris@87
|
890 Each differentiation is multiplied by `scl`. The end result is
|
Chris@87
|
891 multiplication by ``scl**m``. This is for use in a linear change of
|
Chris@87
|
892 variable. (Default: 1)
|
Chris@87
|
893 axis : int, optional
|
Chris@87
|
894 Axis over which the derivative is taken. (Default: 0).
|
Chris@87
|
895
|
Chris@87
|
896 .. versionadded:: 1.7.0
|
Chris@87
|
897
|
Chris@87
|
898 Returns
|
Chris@87
|
899 -------
|
Chris@87
|
900 der : ndarray
|
Chris@87
|
901 Chebyshev series of the derivative.
|
Chris@87
|
902
|
Chris@87
|
903 See Also
|
Chris@87
|
904 --------
|
Chris@87
|
905 chebint
|
Chris@87
|
906
|
Chris@87
|
907 Notes
|
Chris@87
|
908 -----
|
Chris@87
|
909 In general, the result of differentiating a C-series needs to be
|
Chris@87
|
910 "reprojected" onto the C-series basis set. Thus, typically, the
|
Chris@87
|
911 result of this function is "unintuitive," albeit correct; see Examples
|
Chris@87
|
912 section below.
|
Chris@87
|
913
|
Chris@87
|
914 Examples
|
Chris@87
|
915 --------
|
Chris@87
|
916 >>> from numpy.polynomial import chebyshev as C
|
Chris@87
|
917 >>> c = (1,2,3,4)
|
Chris@87
|
918 >>> C.chebder(c)
|
Chris@87
|
919 array([ 14., 12., 24.])
|
Chris@87
|
920 >>> C.chebder(c,3)
|
Chris@87
|
921 array([ 96.])
|
Chris@87
|
922 >>> C.chebder(c,scl=-1)
|
Chris@87
|
923 array([-14., -12., -24.])
|
Chris@87
|
924 >>> C.chebder(c,2,-1)
|
Chris@87
|
925 array([ 12., 96.])
|
Chris@87
|
926
|
Chris@87
|
927 """
|
Chris@87
|
928 c = np.array(c, ndmin=1, copy=1)
|
Chris@87
|
929 if c.dtype.char in '?bBhHiIlLqQpP':
|
Chris@87
|
930 c = c.astype(np.double)
|
Chris@87
|
931 cnt, iaxis = [int(t) for t in [m, axis]]
|
Chris@87
|
932
|
Chris@87
|
933 if cnt != m:
|
Chris@87
|
934 raise ValueError("The order of derivation must be integer")
|
Chris@87
|
935 if cnt < 0:
|
Chris@87
|
936 raise ValueError("The order of derivation must be non-negative")
|
Chris@87
|
937 if iaxis != axis:
|
Chris@87
|
938 raise ValueError("The axis must be integer")
|
Chris@87
|
939 if not -c.ndim <= iaxis < c.ndim:
|
Chris@87
|
940 raise ValueError("The axis is out of range")
|
Chris@87
|
941 if iaxis < 0:
|
Chris@87
|
942 iaxis += c.ndim
|
Chris@87
|
943
|
Chris@87
|
944 if cnt == 0:
|
Chris@87
|
945 return c
|
Chris@87
|
946
|
Chris@87
|
947 c = np.rollaxis(c, iaxis)
|
Chris@87
|
948 n = len(c)
|
Chris@87
|
949 if cnt >= n:
|
Chris@87
|
950 c = c[:1]*0
|
Chris@87
|
951 else:
|
Chris@87
|
952 for i in range(cnt):
|
Chris@87
|
953 n = n - 1
|
Chris@87
|
954 c *= scl
|
Chris@87
|
955 der = np.empty((n,) + c.shape[1:], dtype=c.dtype)
|
Chris@87
|
956 for j in range(n, 2, -1):
|
Chris@87
|
957 der[j - 1] = (2*j)*c[j]
|
Chris@87
|
958 c[j - 2] += (j*c[j])/(j - 2)
|
Chris@87
|
959 if n > 1:
|
Chris@87
|
960 der[1] = 4*c[2]
|
Chris@87
|
961 der[0] = c[1]
|
Chris@87
|
962 c = der
|
Chris@87
|
963 c = np.rollaxis(c, 0, iaxis + 1)
|
Chris@87
|
964 return c
|
Chris@87
|
965
|
Chris@87
|
966
|
Chris@87
|
967 def chebint(c, m=1, k=[], lbnd=0, scl=1, axis=0):
|
Chris@87
|
968 """
|
Chris@87
|
969 Integrate a Chebyshev series.
|
Chris@87
|
970
|
Chris@87
|
971 Returns the Chebyshev series coefficients `c` integrated `m` times from
|
Chris@87
|
972 `lbnd` along `axis`. At each iteration the resulting series is
|
Chris@87
|
973 **multiplied** by `scl` and an integration constant, `k`, is added.
|
Chris@87
|
974 The scaling factor is for use in a linear change of variable. ("Buyer
|
Chris@87
|
975 beware": note that, depending on what one is doing, one may want `scl`
|
Chris@87
|
976 to be the reciprocal of what one might expect; for more information,
|
Chris@87
|
977 see the Notes section below.) The argument `c` is an array of
|
Chris@87
|
978 coefficients from low to high degree along each axis, e.g., [1,2,3]
|
Chris@87
|
979 represents the series ``T_0 + 2*T_1 + 3*T_2`` while [[1,2],[1,2]]
|
Chris@87
|
980 represents ``1*T_0(x)*T_0(y) + 1*T_1(x)*T_0(y) + 2*T_0(x)*T_1(y) +
|
Chris@87
|
981 2*T_1(x)*T_1(y)`` if axis=0 is ``x`` and axis=1 is ``y``.
|
Chris@87
|
982
|
Chris@87
|
983 Parameters
|
Chris@87
|
984 ----------
|
Chris@87
|
985 c : array_like
|
Chris@87
|
986 Array of Chebyshev series coefficients. If c is multidimensional
|
Chris@87
|
987 the different axis correspond to different variables with the
|
Chris@87
|
988 degree in each axis given by the corresponding index.
|
Chris@87
|
989 m : int, optional
|
Chris@87
|
990 Order of integration, must be positive. (Default: 1)
|
Chris@87
|
991 k : {[], list, scalar}, optional
|
Chris@87
|
992 Integration constant(s). The value of the first integral at zero
|
Chris@87
|
993 is the first value in the list, the value of the second integral
|
Chris@87
|
994 at zero is the second value, etc. If ``k == []`` (the default),
|
Chris@87
|
995 all constants are set to zero. If ``m == 1``, a single scalar can
|
Chris@87
|
996 be given instead of a list.
|
Chris@87
|
997 lbnd : scalar, optional
|
Chris@87
|
998 The lower bound of the integral. (Default: 0)
|
Chris@87
|
999 scl : scalar, optional
|
Chris@87
|
1000 Following each integration the result is *multiplied* by `scl`
|
Chris@87
|
1001 before the integration constant is added. (Default: 1)
|
Chris@87
|
1002 axis : int, optional
|
Chris@87
|
1003 Axis over which the integral is taken. (Default: 0).
|
Chris@87
|
1004
|
Chris@87
|
1005 .. versionadded:: 1.7.0
|
Chris@87
|
1006
|
Chris@87
|
1007 Returns
|
Chris@87
|
1008 -------
|
Chris@87
|
1009 S : ndarray
|
Chris@87
|
1010 C-series coefficients of the integral.
|
Chris@87
|
1011
|
Chris@87
|
1012 Raises
|
Chris@87
|
1013 ------
|
Chris@87
|
1014 ValueError
|
Chris@87
|
1015 If ``m < 1``, ``len(k) > m``, ``np.isscalar(lbnd) == False``, or
|
Chris@87
|
1016 ``np.isscalar(scl) == False``.
|
Chris@87
|
1017
|
Chris@87
|
1018 See Also
|
Chris@87
|
1019 --------
|
Chris@87
|
1020 chebder
|
Chris@87
|
1021
|
Chris@87
|
1022 Notes
|
Chris@87
|
1023 -----
|
Chris@87
|
1024 Note that the result of each integration is *multiplied* by `scl`.
|
Chris@87
|
1025 Why is this important to note? Say one is making a linear change of
|
Chris@87
|
1026 variable :math:`u = ax + b` in an integral relative to `x`. Then
|
Chris@87
|
1027 .. math::`dx = du/a`, so one will need to set `scl` equal to
|
Chris@87
|
1028 :math:`1/a`- perhaps not what one would have first thought.
|
Chris@87
|
1029
|
Chris@87
|
1030 Also note that, in general, the result of integrating a C-series needs
|
Chris@87
|
1031 to be "reprojected" onto the C-series basis set. Thus, typically,
|
Chris@87
|
1032 the result of this function is "unintuitive," albeit correct; see
|
Chris@87
|
1033 Examples section below.
|
Chris@87
|
1034
|
Chris@87
|
1035 Examples
|
Chris@87
|
1036 --------
|
Chris@87
|
1037 >>> from numpy.polynomial import chebyshev as C
|
Chris@87
|
1038 >>> c = (1,2,3)
|
Chris@87
|
1039 >>> C.chebint(c)
|
Chris@87
|
1040 array([ 0.5, -0.5, 0.5, 0.5])
|
Chris@87
|
1041 >>> C.chebint(c,3)
|
Chris@87
|
1042 array([ 0.03125 , -0.1875 , 0.04166667, -0.05208333, 0.01041667,
|
Chris@87
|
1043 0.00625 ])
|
Chris@87
|
1044 >>> C.chebint(c, k=3)
|
Chris@87
|
1045 array([ 3.5, -0.5, 0.5, 0.5])
|
Chris@87
|
1046 >>> C.chebint(c,lbnd=-2)
|
Chris@87
|
1047 array([ 8.5, -0.5, 0.5, 0.5])
|
Chris@87
|
1048 >>> C.chebint(c,scl=-2)
|
Chris@87
|
1049 array([-1., 1., -1., -1.])
|
Chris@87
|
1050
|
Chris@87
|
1051 """
|
Chris@87
|
1052 c = np.array(c, ndmin=1, copy=1)
|
Chris@87
|
1053 if c.dtype.char in '?bBhHiIlLqQpP':
|
Chris@87
|
1054 c = c.astype(np.double)
|
Chris@87
|
1055 if not np.iterable(k):
|
Chris@87
|
1056 k = [k]
|
Chris@87
|
1057 cnt, iaxis = [int(t) for t in [m, axis]]
|
Chris@87
|
1058
|
Chris@87
|
1059 if cnt != m:
|
Chris@87
|
1060 raise ValueError("The order of integration must be integer")
|
Chris@87
|
1061 if cnt < 0:
|
Chris@87
|
1062 raise ValueError("The order of integration must be non-negative")
|
Chris@87
|
1063 if len(k) > cnt:
|
Chris@87
|
1064 raise ValueError("Too many integration constants")
|
Chris@87
|
1065 if iaxis != axis:
|
Chris@87
|
1066 raise ValueError("The axis must be integer")
|
Chris@87
|
1067 if not -c.ndim <= iaxis < c.ndim:
|
Chris@87
|
1068 raise ValueError("The axis is out of range")
|
Chris@87
|
1069 if iaxis < 0:
|
Chris@87
|
1070 iaxis += c.ndim
|
Chris@87
|
1071
|
Chris@87
|
1072 if cnt == 0:
|
Chris@87
|
1073 return c
|
Chris@87
|
1074
|
Chris@87
|
1075 c = np.rollaxis(c, iaxis)
|
Chris@87
|
1076 k = list(k) + [0]*(cnt - len(k))
|
Chris@87
|
1077 for i in range(cnt):
|
Chris@87
|
1078 n = len(c)
|
Chris@87
|
1079 c *= scl
|
Chris@87
|
1080 if n == 1 and np.all(c[0] == 0):
|
Chris@87
|
1081 c[0] += k[i]
|
Chris@87
|
1082 else:
|
Chris@87
|
1083 tmp = np.empty((n + 1,) + c.shape[1:], dtype=c.dtype)
|
Chris@87
|
1084 tmp[0] = c[0]*0
|
Chris@87
|
1085 tmp[1] = c[0]
|
Chris@87
|
1086 if n > 1:
|
Chris@87
|
1087 tmp[2] = c[1]/4
|
Chris@87
|
1088 for j in range(2, n):
|
Chris@87
|
1089 t = c[j]/(2*j + 1)
|
Chris@87
|
1090 tmp[j + 1] = c[j]/(2*(j + 1))
|
Chris@87
|
1091 tmp[j - 1] -= c[j]/(2*(j - 1))
|
Chris@87
|
1092 tmp[0] += k[i] - chebval(lbnd, tmp)
|
Chris@87
|
1093 c = tmp
|
Chris@87
|
1094 c = np.rollaxis(c, 0, iaxis + 1)
|
Chris@87
|
1095 return c
|
Chris@87
|
1096
|
Chris@87
|
1097
|
Chris@87
|
1098 def chebval(x, c, tensor=True):
|
Chris@87
|
1099 """
|
Chris@87
|
1100 Evaluate a Chebyshev series at points x.
|
Chris@87
|
1101
|
Chris@87
|
1102 If `c` is of length `n + 1`, this function returns the value:
|
Chris@87
|
1103
|
Chris@87
|
1104 .. math:: p(x) = c_0 * T_0(x) + c_1 * T_1(x) + ... + c_n * T_n(x)
|
Chris@87
|
1105
|
Chris@87
|
1106 The parameter `x` is converted to an array only if it is a tuple or a
|
Chris@87
|
1107 list, otherwise it is treated as a scalar. In either case, either `x`
|
Chris@87
|
1108 or its elements must support multiplication and addition both with
|
Chris@87
|
1109 themselves and with the elements of `c`.
|
Chris@87
|
1110
|
Chris@87
|
1111 If `c` is a 1-D array, then `p(x)` will have the same shape as `x`. If
|
Chris@87
|
1112 `c` is multidimensional, then the shape of the result depends on the
|
Chris@87
|
1113 value of `tensor`. If `tensor` is true the shape will be c.shape[1:] +
|
Chris@87
|
1114 x.shape. If `tensor` is false the shape will be c.shape[1:]. Note that
|
Chris@87
|
1115 scalars have shape (,).
|
Chris@87
|
1116
|
Chris@87
|
1117 Trailing zeros in the coefficients will be used in the evaluation, so
|
Chris@87
|
1118 they should be avoided if efficiency is a concern.
|
Chris@87
|
1119
|
Chris@87
|
1120 Parameters
|
Chris@87
|
1121 ----------
|
Chris@87
|
1122 x : array_like, compatible object
|
Chris@87
|
1123 If `x` is a list or tuple, it is converted to an ndarray, otherwise
|
Chris@87
|
1124 it is left unchanged and treated as a scalar. In either case, `x`
|
Chris@87
|
1125 or its elements must support addition and multiplication with
|
Chris@87
|
1126 with themselves and with the elements of `c`.
|
Chris@87
|
1127 c : array_like
|
Chris@87
|
1128 Array of coefficients ordered so that the coefficients for terms of
|
Chris@87
|
1129 degree n are contained in c[n]. If `c` is multidimensional the
|
Chris@87
|
1130 remaining indices enumerate multiple polynomials. In the two
|
Chris@87
|
1131 dimensional case the coefficients may be thought of as stored in
|
Chris@87
|
1132 the columns of `c`.
|
Chris@87
|
1133 tensor : boolean, optional
|
Chris@87
|
1134 If True, the shape of the coefficient array is extended with ones
|
Chris@87
|
1135 on the right, one for each dimension of `x`. Scalars have dimension 0
|
Chris@87
|
1136 for this action. The result is that every column of coefficients in
|
Chris@87
|
1137 `c` is evaluated for every element of `x`. If False, `x` is broadcast
|
Chris@87
|
1138 over the columns of `c` for the evaluation. This keyword is useful
|
Chris@87
|
1139 when `c` is multidimensional. The default value is True.
|
Chris@87
|
1140
|
Chris@87
|
1141 .. versionadded:: 1.7.0
|
Chris@87
|
1142
|
Chris@87
|
1143 Returns
|
Chris@87
|
1144 -------
|
Chris@87
|
1145 values : ndarray, algebra_like
|
Chris@87
|
1146 The shape of the return value is described above.
|
Chris@87
|
1147
|
Chris@87
|
1148 See Also
|
Chris@87
|
1149 --------
|
Chris@87
|
1150 chebval2d, chebgrid2d, chebval3d, chebgrid3d
|
Chris@87
|
1151
|
Chris@87
|
1152 Notes
|
Chris@87
|
1153 -----
|
Chris@87
|
1154 The evaluation uses Clenshaw recursion, aka synthetic division.
|
Chris@87
|
1155
|
Chris@87
|
1156 Examples
|
Chris@87
|
1157 --------
|
Chris@87
|
1158
|
Chris@87
|
1159 """
|
Chris@87
|
1160 c = np.array(c, ndmin=1, copy=1)
|
Chris@87
|
1161 if c.dtype.char in '?bBhHiIlLqQpP':
|
Chris@87
|
1162 c = c.astype(np.double)
|
Chris@87
|
1163 if isinstance(x, (tuple, list)):
|
Chris@87
|
1164 x = np.asarray(x)
|
Chris@87
|
1165 if isinstance(x, np.ndarray) and tensor:
|
Chris@87
|
1166 c = c.reshape(c.shape + (1,)*x.ndim)
|
Chris@87
|
1167
|
Chris@87
|
1168 if len(c) == 1:
|
Chris@87
|
1169 c0 = c[0]
|
Chris@87
|
1170 c1 = 0
|
Chris@87
|
1171 elif len(c) == 2:
|
Chris@87
|
1172 c0 = c[0]
|
Chris@87
|
1173 c1 = c[1]
|
Chris@87
|
1174 else:
|
Chris@87
|
1175 x2 = 2*x
|
Chris@87
|
1176 c0 = c[-2]
|
Chris@87
|
1177 c1 = c[-1]
|
Chris@87
|
1178 for i in range(3, len(c) + 1):
|
Chris@87
|
1179 tmp = c0
|
Chris@87
|
1180 c0 = c[-i] - c1
|
Chris@87
|
1181 c1 = tmp + c1*x2
|
Chris@87
|
1182 return c0 + c1*x
|
Chris@87
|
1183
|
Chris@87
|
1184
|
Chris@87
|
1185 def chebval2d(x, y, c):
|
Chris@87
|
1186 """
|
Chris@87
|
1187 Evaluate a 2-D Chebyshev series at points (x, y).
|
Chris@87
|
1188
|
Chris@87
|
1189 This function returns the values:
|
Chris@87
|
1190
|
Chris@87
|
1191 .. math:: p(x,y) = \\sum_{i,j} c_{i,j} * T_i(x) * T_j(y)
|
Chris@87
|
1192
|
Chris@87
|
1193 The parameters `x` and `y` are converted to arrays only if they are
|
Chris@87
|
1194 tuples or a lists, otherwise they are treated as a scalars and they
|
Chris@87
|
1195 must have the same shape after conversion. In either case, either `x`
|
Chris@87
|
1196 and `y` or their elements must support multiplication and addition both
|
Chris@87
|
1197 with themselves and with the elements of `c`.
|
Chris@87
|
1198
|
Chris@87
|
1199 If `c` is a 1-D array a one is implicitly appended to its shape to make
|
Chris@87
|
1200 it 2-D. The shape of the result will be c.shape[2:] + x.shape.
|
Chris@87
|
1201
|
Chris@87
|
1202 Parameters
|
Chris@87
|
1203 ----------
|
Chris@87
|
1204 x, y : array_like, compatible objects
|
Chris@87
|
1205 The two dimensional series is evaluated at the points `(x, y)`,
|
Chris@87
|
1206 where `x` and `y` must have the same shape. If `x` or `y` is a list
|
Chris@87
|
1207 or tuple, it is first converted to an ndarray, otherwise it is left
|
Chris@87
|
1208 unchanged and if it isn't an ndarray it is treated as a scalar.
|
Chris@87
|
1209 c : array_like
|
Chris@87
|
1210 Array of coefficients ordered so that the coefficient of the term
|
Chris@87
|
1211 of multi-degree i,j is contained in ``c[i,j]``. If `c` has
|
Chris@87
|
1212 dimension greater than 2 the remaining indices enumerate multiple
|
Chris@87
|
1213 sets of coefficients.
|
Chris@87
|
1214
|
Chris@87
|
1215 Returns
|
Chris@87
|
1216 -------
|
Chris@87
|
1217 values : ndarray, compatible object
|
Chris@87
|
1218 The values of the two dimensional Chebyshev series at points formed
|
Chris@87
|
1219 from pairs of corresponding values from `x` and `y`.
|
Chris@87
|
1220
|
Chris@87
|
1221 See Also
|
Chris@87
|
1222 --------
|
Chris@87
|
1223 chebval, chebgrid2d, chebval3d, chebgrid3d
|
Chris@87
|
1224
|
Chris@87
|
1225 Notes
|
Chris@87
|
1226 -----
|
Chris@87
|
1227
|
Chris@87
|
1228 .. versionadded::1.7.0
|
Chris@87
|
1229
|
Chris@87
|
1230 """
|
Chris@87
|
1231 try:
|
Chris@87
|
1232 x, y = np.array((x, y), copy=0)
|
Chris@87
|
1233 except:
|
Chris@87
|
1234 raise ValueError('x, y are incompatible')
|
Chris@87
|
1235
|
Chris@87
|
1236 c = chebval(x, c)
|
Chris@87
|
1237 c = chebval(y, c, tensor=False)
|
Chris@87
|
1238 return c
|
Chris@87
|
1239
|
Chris@87
|
1240
|
Chris@87
|
1241 def chebgrid2d(x, y, c):
|
Chris@87
|
1242 """
|
Chris@87
|
1243 Evaluate a 2-D Chebyshev series on the Cartesian product of x and y.
|
Chris@87
|
1244
|
Chris@87
|
1245 This function returns the values:
|
Chris@87
|
1246
|
Chris@87
|
1247 .. math:: p(a,b) = \sum_{i,j} c_{i,j} * T_i(a) * T_j(b),
|
Chris@87
|
1248
|
Chris@87
|
1249 where the points `(a, b)` consist of all pairs formed by taking
|
Chris@87
|
1250 `a` from `x` and `b` from `y`. The resulting points form a grid with
|
Chris@87
|
1251 `x` in the first dimension and `y` in the second.
|
Chris@87
|
1252
|
Chris@87
|
1253 The parameters `x` and `y` are converted to arrays only if they are
|
Chris@87
|
1254 tuples or a lists, otherwise they are treated as a scalars. In either
|
Chris@87
|
1255 case, either `x` and `y` or their elements must support multiplication
|
Chris@87
|
1256 and addition both with themselves and with the elements of `c`.
|
Chris@87
|
1257
|
Chris@87
|
1258 If `c` has fewer than two dimensions, ones are implicitly appended to
|
Chris@87
|
1259 its shape to make it 2-D. The shape of the result will be c.shape[2:] +
|
Chris@87
|
1260 x.shape + y.shape.
|
Chris@87
|
1261
|
Chris@87
|
1262 Parameters
|
Chris@87
|
1263 ----------
|
Chris@87
|
1264 x, y : array_like, compatible objects
|
Chris@87
|
1265 The two dimensional series is evaluated at the points in the
|
Chris@87
|
1266 Cartesian product of `x` and `y`. If `x` or `y` is a list or
|
Chris@87
|
1267 tuple, it is first converted to an ndarray, otherwise it is left
|
Chris@87
|
1268 unchanged and, if it isn't an ndarray, it is treated as a scalar.
|
Chris@87
|
1269 c : array_like
|
Chris@87
|
1270 Array of coefficients ordered so that the coefficient of the term of
|
Chris@87
|
1271 multi-degree i,j is contained in `c[i,j]`. If `c` has dimension
|
Chris@87
|
1272 greater than two the remaining indices enumerate multiple sets of
|
Chris@87
|
1273 coefficients.
|
Chris@87
|
1274
|
Chris@87
|
1275 Returns
|
Chris@87
|
1276 -------
|
Chris@87
|
1277 values : ndarray, compatible object
|
Chris@87
|
1278 The values of the two dimensional Chebyshev series at points in the
|
Chris@87
|
1279 Cartesian product of `x` and `y`.
|
Chris@87
|
1280
|
Chris@87
|
1281 See Also
|
Chris@87
|
1282 --------
|
Chris@87
|
1283 chebval, chebval2d, chebval3d, chebgrid3d
|
Chris@87
|
1284
|
Chris@87
|
1285 Notes
|
Chris@87
|
1286 -----
|
Chris@87
|
1287
|
Chris@87
|
1288 .. versionadded::1.7.0
|
Chris@87
|
1289
|
Chris@87
|
1290 """
|
Chris@87
|
1291 c = chebval(x, c)
|
Chris@87
|
1292 c = chebval(y, c)
|
Chris@87
|
1293 return c
|
Chris@87
|
1294
|
Chris@87
|
1295
|
Chris@87
|
1296 def chebval3d(x, y, z, c):
|
Chris@87
|
1297 """
|
Chris@87
|
1298 Evaluate a 3-D Chebyshev series at points (x, y, z).
|
Chris@87
|
1299
|
Chris@87
|
1300 This function returns the values:
|
Chris@87
|
1301
|
Chris@87
|
1302 .. math:: p(x,y,z) = \\sum_{i,j,k} c_{i,j,k} * T_i(x) * T_j(y) * T_k(z)
|
Chris@87
|
1303
|
Chris@87
|
1304 The parameters `x`, `y`, and `z` are converted to arrays only if
|
Chris@87
|
1305 they are tuples or a lists, otherwise they are treated as a scalars and
|
Chris@87
|
1306 they must have the same shape after conversion. In either case, either
|
Chris@87
|
1307 `x`, `y`, and `z` or their elements must support multiplication and
|
Chris@87
|
1308 addition both with themselves and with the elements of `c`.
|
Chris@87
|
1309
|
Chris@87
|
1310 If `c` has fewer than 3 dimensions, ones are implicitly appended to its
|
Chris@87
|
1311 shape to make it 3-D. The shape of the result will be c.shape[3:] +
|
Chris@87
|
1312 x.shape.
|
Chris@87
|
1313
|
Chris@87
|
1314 Parameters
|
Chris@87
|
1315 ----------
|
Chris@87
|
1316 x, y, z : array_like, compatible object
|
Chris@87
|
1317 The three dimensional series is evaluated at the points
|
Chris@87
|
1318 `(x, y, z)`, where `x`, `y`, and `z` must have the same shape. If
|
Chris@87
|
1319 any of `x`, `y`, or `z` is a list or tuple, it is first converted
|
Chris@87
|
1320 to an ndarray, otherwise it is left unchanged and if it isn't an
|
Chris@87
|
1321 ndarray it is treated as a scalar.
|
Chris@87
|
1322 c : array_like
|
Chris@87
|
1323 Array of coefficients ordered so that the coefficient of the term of
|
Chris@87
|
1324 multi-degree i,j,k is contained in ``c[i,j,k]``. If `c` has dimension
|
Chris@87
|
1325 greater than 3 the remaining indices enumerate multiple sets of
|
Chris@87
|
1326 coefficients.
|
Chris@87
|
1327
|
Chris@87
|
1328 Returns
|
Chris@87
|
1329 -------
|
Chris@87
|
1330 values : ndarray, compatible object
|
Chris@87
|
1331 The values of the multidimensional polynomial on points formed with
|
Chris@87
|
1332 triples of corresponding values from `x`, `y`, and `z`.
|
Chris@87
|
1333
|
Chris@87
|
1334 See Also
|
Chris@87
|
1335 --------
|
Chris@87
|
1336 chebval, chebval2d, chebgrid2d, chebgrid3d
|
Chris@87
|
1337
|
Chris@87
|
1338 Notes
|
Chris@87
|
1339 -----
|
Chris@87
|
1340
|
Chris@87
|
1341 .. versionadded::1.7.0
|
Chris@87
|
1342
|
Chris@87
|
1343 """
|
Chris@87
|
1344 try:
|
Chris@87
|
1345 x, y, z = np.array((x, y, z), copy=0)
|
Chris@87
|
1346 except:
|
Chris@87
|
1347 raise ValueError('x, y, z are incompatible')
|
Chris@87
|
1348
|
Chris@87
|
1349 c = chebval(x, c)
|
Chris@87
|
1350 c = chebval(y, c, tensor=False)
|
Chris@87
|
1351 c = chebval(z, c, tensor=False)
|
Chris@87
|
1352 return c
|
Chris@87
|
1353
|
Chris@87
|
1354
|
Chris@87
|
1355 def chebgrid3d(x, y, z, c):
|
Chris@87
|
1356 """
|
Chris@87
|
1357 Evaluate a 3-D Chebyshev series on the Cartesian product of x, y, and z.
|
Chris@87
|
1358
|
Chris@87
|
1359 This function returns the values:
|
Chris@87
|
1360
|
Chris@87
|
1361 .. math:: p(a,b,c) = \\sum_{i,j,k} c_{i,j,k} * T_i(a) * T_j(b) * T_k(c)
|
Chris@87
|
1362
|
Chris@87
|
1363 where the points `(a, b, c)` consist of all triples formed by taking
|
Chris@87
|
1364 `a` from `x`, `b` from `y`, and `c` from `z`. The resulting points form
|
Chris@87
|
1365 a grid with `x` in the first dimension, `y` in the second, and `z` in
|
Chris@87
|
1366 the third.
|
Chris@87
|
1367
|
Chris@87
|
1368 The parameters `x`, `y`, and `z` are converted to arrays only if they
|
Chris@87
|
1369 are tuples or a lists, otherwise they are treated as a scalars. In
|
Chris@87
|
1370 either case, either `x`, `y`, and `z` or their elements must support
|
Chris@87
|
1371 multiplication and addition both with themselves and with the elements
|
Chris@87
|
1372 of `c`.
|
Chris@87
|
1373
|
Chris@87
|
1374 If `c` has fewer than three dimensions, ones are implicitly appended to
|
Chris@87
|
1375 its shape to make it 3-D. The shape of the result will be c.shape[3:] +
|
Chris@87
|
1376 x.shape + y.shape + z.shape.
|
Chris@87
|
1377
|
Chris@87
|
1378 Parameters
|
Chris@87
|
1379 ----------
|
Chris@87
|
1380 x, y, z : array_like, compatible objects
|
Chris@87
|
1381 The three dimensional series is evaluated at the points in the
|
Chris@87
|
1382 Cartesian product of `x`, `y`, and `z`. If `x`,`y`, or `z` is a
|
Chris@87
|
1383 list or tuple, it is first converted to an ndarray, otherwise it is
|
Chris@87
|
1384 left unchanged and, if it isn't an ndarray, it is treated as a
|
Chris@87
|
1385 scalar.
|
Chris@87
|
1386 c : array_like
|
Chris@87
|
1387 Array of coefficients ordered so that the coefficients for terms of
|
Chris@87
|
1388 degree i,j are contained in ``c[i,j]``. If `c` has dimension
|
Chris@87
|
1389 greater than two the remaining indices enumerate multiple sets of
|
Chris@87
|
1390 coefficients.
|
Chris@87
|
1391
|
Chris@87
|
1392 Returns
|
Chris@87
|
1393 -------
|
Chris@87
|
1394 values : ndarray, compatible object
|
Chris@87
|
1395 The values of the two dimensional polynomial at points in the Cartesian
|
Chris@87
|
1396 product of `x` and `y`.
|
Chris@87
|
1397
|
Chris@87
|
1398 See Also
|
Chris@87
|
1399 --------
|
Chris@87
|
1400 chebval, chebval2d, chebgrid2d, chebval3d
|
Chris@87
|
1401
|
Chris@87
|
1402 Notes
|
Chris@87
|
1403 -----
|
Chris@87
|
1404
|
Chris@87
|
1405 .. versionadded::1.7.0
|
Chris@87
|
1406
|
Chris@87
|
1407 """
|
Chris@87
|
1408 c = chebval(x, c)
|
Chris@87
|
1409 c = chebval(y, c)
|
Chris@87
|
1410 c = chebval(z, c)
|
Chris@87
|
1411 return c
|
Chris@87
|
1412
|
Chris@87
|
1413
|
Chris@87
|
1414 def chebvander(x, deg):
|
Chris@87
|
1415 """Pseudo-Vandermonde matrix of given degree.
|
Chris@87
|
1416
|
Chris@87
|
1417 Returns the pseudo-Vandermonde matrix of degree `deg` and sample points
|
Chris@87
|
1418 `x`. The pseudo-Vandermonde matrix is defined by
|
Chris@87
|
1419
|
Chris@87
|
1420 .. math:: V[..., i] = T_i(x),
|
Chris@87
|
1421
|
Chris@87
|
1422 where `0 <= i <= deg`. The leading indices of `V` index the elements of
|
Chris@87
|
1423 `x` and the last index is the degree of the Chebyshev polynomial.
|
Chris@87
|
1424
|
Chris@87
|
1425 If `c` is a 1-D array of coefficients of length `n + 1` and `V` is the
|
Chris@87
|
1426 matrix ``V = chebvander(x, n)``, then ``np.dot(V, c)`` and
|
Chris@87
|
1427 ``chebval(x, c)`` are the same up to roundoff. This equivalence is
|
Chris@87
|
1428 useful both for least squares fitting and for the evaluation of a large
|
Chris@87
|
1429 number of Chebyshev series of the same degree and sample points.
|
Chris@87
|
1430
|
Chris@87
|
1431 Parameters
|
Chris@87
|
1432 ----------
|
Chris@87
|
1433 x : array_like
|
Chris@87
|
1434 Array of points. The dtype is converted to float64 or complex128
|
Chris@87
|
1435 depending on whether any of the elements are complex. If `x` is
|
Chris@87
|
1436 scalar it is converted to a 1-D array.
|
Chris@87
|
1437 deg : int
|
Chris@87
|
1438 Degree of the resulting matrix.
|
Chris@87
|
1439
|
Chris@87
|
1440 Returns
|
Chris@87
|
1441 -------
|
Chris@87
|
1442 vander : ndarray
|
Chris@87
|
1443 The pseudo Vandermonde matrix. The shape of the returned matrix is
|
Chris@87
|
1444 ``x.shape + (deg + 1,)``, where The last index is the degree of the
|
Chris@87
|
1445 corresponding Chebyshev polynomial. The dtype will be the same as
|
Chris@87
|
1446 the converted `x`.
|
Chris@87
|
1447
|
Chris@87
|
1448 """
|
Chris@87
|
1449 ideg = int(deg)
|
Chris@87
|
1450 if ideg != deg:
|
Chris@87
|
1451 raise ValueError("deg must be integer")
|
Chris@87
|
1452 if ideg < 0:
|
Chris@87
|
1453 raise ValueError("deg must be non-negative")
|
Chris@87
|
1454
|
Chris@87
|
1455 x = np.array(x, copy=0, ndmin=1) + 0.0
|
Chris@87
|
1456 dims = (ideg + 1,) + x.shape
|
Chris@87
|
1457 dtyp = x.dtype
|
Chris@87
|
1458 v = np.empty(dims, dtype=dtyp)
|
Chris@87
|
1459 # Use forward recursion to generate the entries.
|
Chris@87
|
1460 v[0] = x*0 + 1
|
Chris@87
|
1461 if ideg > 0:
|
Chris@87
|
1462 x2 = 2*x
|
Chris@87
|
1463 v[1] = x
|
Chris@87
|
1464 for i in range(2, ideg + 1):
|
Chris@87
|
1465 v[i] = v[i-1]*x2 - v[i-2]
|
Chris@87
|
1466 return np.rollaxis(v, 0, v.ndim)
|
Chris@87
|
1467
|
Chris@87
|
1468
|
Chris@87
|
1469 def chebvander2d(x, y, deg):
|
Chris@87
|
1470 """Pseudo-Vandermonde matrix of given degrees.
|
Chris@87
|
1471
|
Chris@87
|
1472 Returns the pseudo-Vandermonde matrix of degrees `deg` and sample
|
Chris@87
|
1473 points `(x, y)`. The pseudo-Vandermonde matrix is defined by
|
Chris@87
|
1474
|
Chris@87
|
1475 .. math:: V[..., deg[1]*i + j] = T_i(x) * T_j(y),
|
Chris@87
|
1476
|
Chris@87
|
1477 where `0 <= i <= deg[0]` and `0 <= j <= deg[1]`. The leading indices of
|
Chris@87
|
1478 `V` index the points `(x, y)` and the last index encodes the degrees of
|
Chris@87
|
1479 the Chebyshev polynomials.
|
Chris@87
|
1480
|
Chris@87
|
1481 If ``V = chebvander2d(x, y, [xdeg, ydeg])``, then the columns of `V`
|
Chris@87
|
1482 correspond to the elements of a 2-D coefficient array `c` of shape
|
Chris@87
|
1483 (xdeg + 1, ydeg + 1) in the order
|
Chris@87
|
1484
|
Chris@87
|
1485 .. math:: c_{00}, c_{01}, c_{02} ... , c_{10}, c_{11}, c_{12} ...
|
Chris@87
|
1486
|
Chris@87
|
1487 and ``np.dot(V, c.flat)`` and ``chebval2d(x, y, c)`` will be the same
|
Chris@87
|
1488 up to roundoff. This equivalence is useful both for least squares
|
Chris@87
|
1489 fitting and for the evaluation of a large number of 2-D Chebyshev
|
Chris@87
|
1490 series of the same degrees and sample points.
|
Chris@87
|
1491
|
Chris@87
|
1492 Parameters
|
Chris@87
|
1493 ----------
|
Chris@87
|
1494 x, y : array_like
|
Chris@87
|
1495 Arrays of point coordinates, all of the same shape. The dtypes
|
Chris@87
|
1496 will be converted to either float64 or complex128 depending on
|
Chris@87
|
1497 whether any of the elements are complex. Scalars are converted to
|
Chris@87
|
1498 1-D arrays.
|
Chris@87
|
1499 deg : list of ints
|
Chris@87
|
1500 List of maximum degrees of the form [x_deg, y_deg].
|
Chris@87
|
1501
|
Chris@87
|
1502 Returns
|
Chris@87
|
1503 -------
|
Chris@87
|
1504 vander2d : ndarray
|
Chris@87
|
1505 The shape of the returned matrix is ``x.shape + (order,)``, where
|
Chris@87
|
1506 :math:`order = (deg[0]+1)*(deg([1]+1)`. The dtype will be the same
|
Chris@87
|
1507 as the converted `x` and `y`.
|
Chris@87
|
1508
|
Chris@87
|
1509 See Also
|
Chris@87
|
1510 --------
|
Chris@87
|
1511 chebvander, chebvander3d. chebval2d, chebval3d
|
Chris@87
|
1512
|
Chris@87
|
1513 Notes
|
Chris@87
|
1514 -----
|
Chris@87
|
1515
|
Chris@87
|
1516 .. versionadded::1.7.0
|
Chris@87
|
1517
|
Chris@87
|
1518 """
|
Chris@87
|
1519 ideg = [int(d) for d in deg]
|
Chris@87
|
1520 is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)]
|
Chris@87
|
1521 if is_valid != [1, 1]:
|
Chris@87
|
1522 raise ValueError("degrees must be non-negative integers")
|
Chris@87
|
1523 degx, degy = ideg
|
Chris@87
|
1524 x, y = np.array((x, y), copy=0) + 0.0
|
Chris@87
|
1525
|
Chris@87
|
1526 vx = chebvander(x, degx)
|
Chris@87
|
1527 vy = chebvander(y, degy)
|
Chris@87
|
1528 v = vx[..., None]*vy[..., None,:]
|
Chris@87
|
1529 return v.reshape(v.shape[:-2] + (-1,))
|
Chris@87
|
1530
|
Chris@87
|
1531
|
Chris@87
|
1532 def chebvander3d(x, y, z, deg):
|
Chris@87
|
1533 """Pseudo-Vandermonde matrix of given degrees.
|
Chris@87
|
1534
|
Chris@87
|
1535 Returns the pseudo-Vandermonde matrix of degrees `deg` and sample
|
Chris@87
|
1536 points `(x, y, z)`. If `l, m, n` are the given degrees in `x, y, z`,
|
Chris@87
|
1537 then The pseudo-Vandermonde matrix is defined by
|
Chris@87
|
1538
|
Chris@87
|
1539 .. math:: V[..., (m+1)(n+1)i + (n+1)j + k] = T_i(x)*T_j(y)*T_k(z),
|
Chris@87
|
1540
|
Chris@87
|
1541 where `0 <= i <= l`, `0 <= j <= m`, and `0 <= j <= n`. The leading
|
Chris@87
|
1542 indices of `V` index the points `(x, y, z)` and the last index encodes
|
Chris@87
|
1543 the degrees of the Chebyshev polynomials.
|
Chris@87
|
1544
|
Chris@87
|
1545 If ``V = chebvander3d(x, y, z, [xdeg, ydeg, zdeg])``, then the columns
|
Chris@87
|
1546 of `V` correspond to the elements of a 3-D coefficient array `c` of
|
Chris@87
|
1547 shape (xdeg + 1, ydeg + 1, zdeg + 1) in the order
|
Chris@87
|
1548
|
Chris@87
|
1549 .. math:: c_{000}, c_{001}, c_{002},... , c_{010}, c_{011}, c_{012},...
|
Chris@87
|
1550
|
Chris@87
|
1551 and ``np.dot(V, c.flat)`` and ``chebval3d(x, y, z, c)`` will be the
|
Chris@87
|
1552 same up to roundoff. This equivalence is useful both for least squares
|
Chris@87
|
1553 fitting and for the evaluation of a large number of 3-D Chebyshev
|
Chris@87
|
1554 series of the same degrees and sample points.
|
Chris@87
|
1555
|
Chris@87
|
1556 Parameters
|
Chris@87
|
1557 ----------
|
Chris@87
|
1558 x, y, z : array_like
|
Chris@87
|
1559 Arrays of point coordinates, all of the same shape. The dtypes will
|
Chris@87
|
1560 be converted to either float64 or complex128 depending on whether
|
Chris@87
|
1561 any of the elements are complex. Scalars are converted to 1-D
|
Chris@87
|
1562 arrays.
|
Chris@87
|
1563 deg : list of ints
|
Chris@87
|
1564 List of maximum degrees of the form [x_deg, y_deg, z_deg].
|
Chris@87
|
1565
|
Chris@87
|
1566 Returns
|
Chris@87
|
1567 -------
|
Chris@87
|
1568 vander3d : ndarray
|
Chris@87
|
1569 The shape of the returned matrix is ``x.shape + (order,)``, where
|
Chris@87
|
1570 :math:`order = (deg[0]+1)*(deg([1]+1)*(deg[2]+1)`. The dtype will
|
Chris@87
|
1571 be the same as the converted `x`, `y`, and `z`.
|
Chris@87
|
1572
|
Chris@87
|
1573 See Also
|
Chris@87
|
1574 --------
|
Chris@87
|
1575 chebvander, chebvander3d. chebval2d, chebval3d
|
Chris@87
|
1576
|
Chris@87
|
1577 Notes
|
Chris@87
|
1578 -----
|
Chris@87
|
1579
|
Chris@87
|
1580 .. versionadded::1.7.0
|
Chris@87
|
1581
|
Chris@87
|
1582 """
|
Chris@87
|
1583 ideg = [int(d) for d in deg]
|
Chris@87
|
1584 is_valid = [id == d and id >= 0 for id, d in zip(ideg, deg)]
|
Chris@87
|
1585 if is_valid != [1, 1, 1]:
|
Chris@87
|
1586 raise ValueError("degrees must be non-negative integers")
|
Chris@87
|
1587 degx, degy, degz = ideg
|
Chris@87
|
1588 x, y, z = np.array((x, y, z), copy=0) + 0.0
|
Chris@87
|
1589
|
Chris@87
|
1590 vx = chebvander(x, degx)
|
Chris@87
|
1591 vy = chebvander(y, degy)
|
Chris@87
|
1592 vz = chebvander(z, degz)
|
Chris@87
|
1593 v = vx[..., None, None]*vy[..., None,:, None]*vz[..., None, None,:]
|
Chris@87
|
1594 return v.reshape(v.shape[:-3] + (-1,))
|
Chris@87
|
1595
|
Chris@87
|
1596
|
Chris@87
|
1597 def chebfit(x, y, deg, rcond=None, full=False, w=None):
|
Chris@87
|
1598 """
|
Chris@87
|
1599 Least squares fit of Chebyshev series to data.
|
Chris@87
|
1600
|
Chris@87
|
1601 Return the coefficients of a Legendre series of degree `deg` that is the
|
Chris@87
|
1602 least squares fit to the data values `y` given at points `x`. If `y` is
|
Chris@87
|
1603 1-D the returned coefficients will also be 1-D. If `y` is 2-D multiple
|
Chris@87
|
1604 fits are done, one for each column of `y`, and the resulting
|
Chris@87
|
1605 coefficients are stored in the corresponding columns of a 2-D return.
|
Chris@87
|
1606 The fitted polynomial(s) are in the form
|
Chris@87
|
1607
|
Chris@87
|
1608 .. math:: p(x) = c_0 + c_1 * T_1(x) + ... + c_n * T_n(x),
|
Chris@87
|
1609
|
Chris@87
|
1610 where `n` is `deg`.
|
Chris@87
|
1611
|
Chris@87
|
1612 Parameters
|
Chris@87
|
1613 ----------
|
Chris@87
|
1614 x : array_like, shape (M,)
|
Chris@87
|
1615 x-coordinates of the M sample points ``(x[i], y[i])``.
|
Chris@87
|
1616 y : array_like, shape (M,) or (M, K)
|
Chris@87
|
1617 y-coordinates of the sample points. Several data sets of sample
|
Chris@87
|
1618 points sharing the same x-coordinates can be fitted at once by
|
Chris@87
|
1619 passing in a 2D-array that contains one dataset per column.
|
Chris@87
|
1620 deg : int
|
Chris@87
|
1621 Degree of the fitting series
|
Chris@87
|
1622 rcond : float, optional
|
Chris@87
|
1623 Relative condition number of the fit. Singular values smaller than
|
Chris@87
|
1624 this relative to the largest singular value will be ignored. The
|
Chris@87
|
1625 default value is len(x)*eps, where eps is the relative precision of
|
Chris@87
|
1626 the float type, about 2e-16 in most cases.
|
Chris@87
|
1627 full : bool, optional
|
Chris@87
|
1628 Switch determining nature of return value. When it is False (the
|
Chris@87
|
1629 default) just the coefficients are returned, when True diagnostic
|
Chris@87
|
1630 information from the singular value decomposition is also returned.
|
Chris@87
|
1631 w : array_like, shape (`M`,), optional
|
Chris@87
|
1632 Weights. If not None, the contribution of each point
|
Chris@87
|
1633 ``(x[i],y[i])`` to the fit is weighted by `w[i]`. Ideally the
|
Chris@87
|
1634 weights are chosen so that the errors of the products ``w[i]*y[i]``
|
Chris@87
|
1635 all have the same variance. The default value is None.
|
Chris@87
|
1636
|
Chris@87
|
1637 .. versionadded:: 1.5.0
|
Chris@87
|
1638
|
Chris@87
|
1639 Returns
|
Chris@87
|
1640 -------
|
Chris@87
|
1641 coef : ndarray, shape (M,) or (M, K)
|
Chris@87
|
1642 Chebyshev coefficients ordered from low to high. If `y` was 2-D,
|
Chris@87
|
1643 the coefficients for the data in column k of `y` are in column
|
Chris@87
|
1644 `k`.
|
Chris@87
|
1645
|
Chris@87
|
1646 [residuals, rank, singular_values, rcond] : list
|
Chris@87
|
1647 These values are only returned if `full` = True
|
Chris@87
|
1648
|
Chris@87
|
1649 resid -- sum of squared residuals of the least squares fit
|
Chris@87
|
1650 rank -- the numerical rank of the scaled Vandermonde matrix
|
Chris@87
|
1651 sv -- singular values of the scaled Vandermonde matrix
|
Chris@87
|
1652 rcond -- value of `rcond`.
|
Chris@87
|
1653
|
Chris@87
|
1654 For more details, see `linalg.lstsq`.
|
Chris@87
|
1655
|
Chris@87
|
1656 Warns
|
Chris@87
|
1657 -----
|
Chris@87
|
1658 RankWarning
|
Chris@87
|
1659 The rank of the coefficient matrix in the least-squares fit is
|
Chris@87
|
1660 deficient. The warning is only raised if `full` = False. The
|
Chris@87
|
1661 warnings can be turned off by
|
Chris@87
|
1662
|
Chris@87
|
1663 >>> import warnings
|
Chris@87
|
1664 >>> warnings.simplefilter('ignore', RankWarning)
|
Chris@87
|
1665
|
Chris@87
|
1666 See Also
|
Chris@87
|
1667 --------
|
Chris@87
|
1668 polyfit, legfit, lagfit, hermfit, hermefit
|
Chris@87
|
1669 chebval : Evaluates a Chebyshev series.
|
Chris@87
|
1670 chebvander : Vandermonde matrix of Chebyshev series.
|
Chris@87
|
1671 chebweight : Chebyshev weight function.
|
Chris@87
|
1672 linalg.lstsq : Computes a least-squares fit from the matrix.
|
Chris@87
|
1673 scipy.interpolate.UnivariateSpline : Computes spline fits.
|
Chris@87
|
1674
|
Chris@87
|
1675 Notes
|
Chris@87
|
1676 -----
|
Chris@87
|
1677 The solution is the coefficients of the Chebyshev series `p` that
|
Chris@87
|
1678 minimizes the sum of the weighted squared errors
|
Chris@87
|
1679
|
Chris@87
|
1680 .. math:: E = \\sum_j w_j^2 * |y_j - p(x_j)|^2,
|
Chris@87
|
1681
|
Chris@87
|
1682 where :math:`w_j` are the weights. This problem is solved by setting up
|
Chris@87
|
1683 as the (typically) overdetermined matrix equation
|
Chris@87
|
1684
|
Chris@87
|
1685 .. math:: V(x) * c = w * y,
|
Chris@87
|
1686
|
Chris@87
|
1687 where `V` is the weighted pseudo Vandermonde matrix of `x`, `c` are the
|
Chris@87
|
1688 coefficients to be solved for, `w` are the weights, and `y` are the
|
Chris@87
|
1689 observed values. This equation is then solved using the singular value
|
Chris@87
|
1690 decomposition of `V`.
|
Chris@87
|
1691
|
Chris@87
|
1692 If some of the singular values of `V` are so small that they are
|
Chris@87
|
1693 neglected, then a `RankWarning` will be issued. This means that the
|
Chris@87
|
1694 coefficient values may be poorly determined. Using a lower order fit
|
Chris@87
|
1695 will usually get rid of the warning. The `rcond` parameter can also be
|
Chris@87
|
1696 set to a value smaller than its default, but the resulting fit may be
|
Chris@87
|
1697 spurious and have large contributions from roundoff error.
|
Chris@87
|
1698
|
Chris@87
|
1699 Fits using Chebyshev series are usually better conditioned than fits
|
Chris@87
|
1700 using power series, but much can depend on the distribution of the
|
Chris@87
|
1701 sample points and the smoothness of the data. If the quality of the fit
|
Chris@87
|
1702 is inadequate splines may be a good alternative.
|
Chris@87
|
1703
|
Chris@87
|
1704 References
|
Chris@87
|
1705 ----------
|
Chris@87
|
1706 .. [1] Wikipedia, "Curve fitting",
|
Chris@87
|
1707 http://en.wikipedia.org/wiki/Curve_fitting
|
Chris@87
|
1708
|
Chris@87
|
1709 Examples
|
Chris@87
|
1710 --------
|
Chris@87
|
1711
|
Chris@87
|
1712 """
|
Chris@87
|
1713 order = int(deg) + 1
|
Chris@87
|
1714 x = np.asarray(x) + 0.0
|
Chris@87
|
1715 y = np.asarray(y) + 0.0
|
Chris@87
|
1716
|
Chris@87
|
1717 # check arguments.
|
Chris@87
|
1718 if deg < 0:
|
Chris@87
|
1719 raise ValueError("expected deg >= 0")
|
Chris@87
|
1720 if x.ndim != 1:
|
Chris@87
|
1721 raise TypeError("expected 1D vector for x")
|
Chris@87
|
1722 if x.size == 0:
|
Chris@87
|
1723 raise TypeError("expected non-empty vector for x")
|
Chris@87
|
1724 if y.ndim < 1 or y.ndim > 2:
|
Chris@87
|
1725 raise TypeError("expected 1D or 2D array for y")
|
Chris@87
|
1726 if len(x) != len(y):
|
Chris@87
|
1727 raise TypeError("expected x and y to have same length")
|
Chris@87
|
1728
|
Chris@87
|
1729 # set up the least squares matrices in transposed form
|
Chris@87
|
1730 lhs = chebvander(x, deg).T
|
Chris@87
|
1731 rhs = y.T
|
Chris@87
|
1732 if w is not None:
|
Chris@87
|
1733 w = np.asarray(w) + 0.0
|
Chris@87
|
1734 if w.ndim != 1:
|
Chris@87
|
1735 raise TypeError("expected 1D vector for w")
|
Chris@87
|
1736 if len(x) != len(w):
|
Chris@87
|
1737 raise TypeError("expected x and w to have same length")
|
Chris@87
|
1738 # apply weights. Don't use inplace operations as they
|
Chris@87
|
1739 # can cause problems with NA.
|
Chris@87
|
1740 lhs = lhs * w
|
Chris@87
|
1741 rhs = rhs * w
|
Chris@87
|
1742
|
Chris@87
|
1743 # set rcond
|
Chris@87
|
1744 if rcond is None:
|
Chris@87
|
1745 rcond = len(x)*np.finfo(x.dtype).eps
|
Chris@87
|
1746
|
Chris@87
|
1747 # Determine the norms of the design matrix columns.
|
Chris@87
|
1748 if issubclass(lhs.dtype.type, np.complexfloating):
|
Chris@87
|
1749 scl = np.sqrt((np.square(lhs.real) + np.square(lhs.imag)).sum(1))
|
Chris@87
|
1750 else:
|
Chris@87
|
1751 scl = np.sqrt(np.square(lhs).sum(1))
|
Chris@87
|
1752 scl[scl == 0] = 1
|
Chris@87
|
1753
|
Chris@87
|
1754 # Solve the least squares problem.
|
Chris@87
|
1755 c, resids, rank, s = la.lstsq(lhs.T/scl, rhs.T, rcond)
|
Chris@87
|
1756 c = (c.T/scl).T
|
Chris@87
|
1757
|
Chris@87
|
1758 # warn on rank reduction
|
Chris@87
|
1759 if rank != order and not full:
|
Chris@87
|
1760 msg = "The fit may be poorly conditioned"
|
Chris@87
|
1761 warnings.warn(msg, pu.RankWarning)
|
Chris@87
|
1762
|
Chris@87
|
1763 if full:
|
Chris@87
|
1764 return c, [resids, rank, s, rcond]
|
Chris@87
|
1765 else:
|
Chris@87
|
1766 return c
|
Chris@87
|
1767
|
Chris@87
|
1768
|
Chris@87
|
1769 def chebcompanion(c):
|
Chris@87
|
1770 """Return the scaled companion matrix of c.
|
Chris@87
|
1771
|
Chris@87
|
1772 The basis polynomials are scaled so that the companion matrix is
|
Chris@87
|
1773 symmetric when `c` is aa Chebyshev basis polynomial. This provides
|
Chris@87
|
1774 better eigenvalue estimates than the unscaled case and for basis
|
Chris@87
|
1775 polynomials the eigenvalues are guaranteed to be real if
|
Chris@87
|
1776 `numpy.linalg.eigvalsh` is used to obtain them.
|
Chris@87
|
1777
|
Chris@87
|
1778 Parameters
|
Chris@87
|
1779 ----------
|
Chris@87
|
1780 c : array_like
|
Chris@87
|
1781 1-D array of Chebyshev series coefficients ordered from low to high
|
Chris@87
|
1782 degree.
|
Chris@87
|
1783
|
Chris@87
|
1784 Returns
|
Chris@87
|
1785 -------
|
Chris@87
|
1786 mat : ndarray
|
Chris@87
|
1787 Scaled companion matrix of dimensions (deg, deg).
|
Chris@87
|
1788
|
Chris@87
|
1789 Notes
|
Chris@87
|
1790 -----
|
Chris@87
|
1791
|
Chris@87
|
1792 .. versionadded::1.7.0
|
Chris@87
|
1793
|
Chris@87
|
1794 """
|
Chris@87
|
1795 # c is a trimmed copy
|
Chris@87
|
1796 [c] = pu.as_series([c])
|
Chris@87
|
1797 if len(c) < 2:
|
Chris@87
|
1798 raise ValueError('Series must have maximum degree of at least 1.')
|
Chris@87
|
1799 if len(c) == 2:
|
Chris@87
|
1800 return np.array([[-c[0]/c[1]]])
|
Chris@87
|
1801
|
Chris@87
|
1802 n = len(c) - 1
|
Chris@87
|
1803 mat = np.zeros((n, n), dtype=c.dtype)
|
Chris@87
|
1804 scl = np.array([1.] + [np.sqrt(.5)]*(n-1))
|
Chris@87
|
1805 top = mat.reshape(-1)[1::n+1]
|
Chris@87
|
1806 bot = mat.reshape(-1)[n::n+1]
|
Chris@87
|
1807 top[0] = np.sqrt(.5)
|
Chris@87
|
1808 top[1:] = 1/2
|
Chris@87
|
1809 bot[...] = top
|
Chris@87
|
1810 mat[:, -1] -= (c[:-1]/c[-1])*(scl/scl[-1])*.5
|
Chris@87
|
1811 return mat
|
Chris@87
|
1812
|
Chris@87
|
1813
|
Chris@87
|
1814 def chebroots(c):
|
Chris@87
|
1815 """
|
Chris@87
|
1816 Compute the roots of a Chebyshev series.
|
Chris@87
|
1817
|
Chris@87
|
1818 Return the roots (a.k.a. "zeros") of the polynomial
|
Chris@87
|
1819
|
Chris@87
|
1820 .. math:: p(x) = \\sum_i c[i] * T_i(x).
|
Chris@87
|
1821
|
Chris@87
|
1822 Parameters
|
Chris@87
|
1823 ----------
|
Chris@87
|
1824 c : 1-D array_like
|
Chris@87
|
1825 1-D array of coefficients.
|
Chris@87
|
1826
|
Chris@87
|
1827 Returns
|
Chris@87
|
1828 -------
|
Chris@87
|
1829 out : ndarray
|
Chris@87
|
1830 Array of the roots of the series. If all the roots are real,
|
Chris@87
|
1831 then `out` is also real, otherwise it is complex.
|
Chris@87
|
1832
|
Chris@87
|
1833 See Also
|
Chris@87
|
1834 --------
|
Chris@87
|
1835 polyroots, legroots, lagroots, hermroots, hermeroots
|
Chris@87
|
1836
|
Chris@87
|
1837 Notes
|
Chris@87
|
1838 -----
|
Chris@87
|
1839 The root estimates are obtained as the eigenvalues of the companion
|
Chris@87
|
1840 matrix, Roots far from the origin of the complex plane may have large
|
Chris@87
|
1841 errors due to the numerical instability of the series for such
|
Chris@87
|
1842 values. Roots with multiplicity greater than 1 will also show larger
|
Chris@87
|
1843 errors as the value of the series near such points is relatively
|
Chris@87
|
1844 insensitive to errors in the roots. Isolated roots near the origin can
|
Chris@87
|
1845 be improved by a few iterations of Newton's method.
|
Chris@87
|
1846
|
Chris@87
|
1847 The Chebyshev series basis polynomials aren't powers of `x` so the
|
Chris@87
|
1848 results of this function may seem unintuitive.
|
Chris@87
|
1849
|
Chris@87
|
1850 Examples
|
Chris@87
|
1851 --------
|
Chris@87
|
1852 >>> import numpy.polynomial.chebyshev as cheb
|
Chris@87
|
1853 >>> cheb.chebroots((-1, 1,-1, 1)) # T3 - T2 + T1 - T0 has real roots
|
Chris@87
|
1854 array([ -5.00000000e-01, 2.60860684e-17, 1.00000000e+00])
|
Chris@87
|
1855
|
Chris@87
|
1856 """
|
Chris@87
|
1857 # c is a trimmed copy
|
Chris@87
|
1858 [c] = pu.as_series([c])
|
Chris@87
|
1859 if len(c) < 2:
|
Chris@87
|
1860 return np.array([], dtype=c.dtype)
|
Chris@87
|
1861 if len(c) == 2:
|
Chris@87
|
1862 return np.array([-c[0]/c[1]])
|
Chris@87
|
1863
|
Chris@87
|
1864 m = chebcompanion(c)
|
Chris@87
|
1865 r = la.eigvals(m)
|
Chris@87
|
1866 r.sort()
|
Chris@87
|
1867 return r
|
Chris@87
|
1868
|
Chris@87
|
1869
|
Chris@87
|
1870 def chebgauss(deg):
|
Chris@87
|
1871 """
|
Chris@87
|
1872 Gauss-Chebyshev quadrature.
|
Chris@87
|
1873
|
Chris@87
|
1874 Computes the sample points and weights for Gauss-Chebyshev quadrature.
|
Chris@87
|
1875 These sample points and weights will correctly integrate polynomials of
|
Chris@87
|
1876 degree :math:`2*deg - 1` or less over the interval :math:`[-1, 1]` with
|
Chris@87
|
1877 the weight function :math:`f(x) = 1/\sqrt{1 - x^2}`.
|
Chris@87
|
1878
|
Chris@87
|
1879 Parameters
|
Chris@87
|
1880 ----------
|
Chris@87
|
1881 deg : int
|
Chris@87
|
1882 Number of sample points and weights. It must be >= 1.
|
Chris@87
|
1883
|
Chris@87
|
1884 Returns
|
Chris@87
|
1885 -------
|
Chris@87
|
1886 x : ndarray
|
Chris@87
|
1887 1-D ndarray containing the sample points.
|
Chris@87
|
1888 y : ndarray
|
Chris@87
|
1889 1-D ndarray containing the weights.
|
Chris@87
|
1890
|
Chris@87
|
1891 Notes
|
Chris@87
|
1892 -----
|
Chris@87
|
1893
|
Chris@87
|
1894 .. versionadded:: 1.7.0
|
Chris@87
|
1895
|
Chris@87
|
1896 The results have only been tested up to degree 100, higher degrees may
|
Chris@87
|
1897 be problematic. For Gauss-Chebyshev there are closed form solutions for
|
Chris@87
|
1898 the sample points and weights. If n = `deg`, then
|
Chris@87
|
1899
|
Chris@87
|
1900 .. math:: x_i = \cos(\pi (2 i - 1) / (2 n))
|
Chris@87
|
1901
|
Chris@87
|
1902 .. math:: w_i = \pi / n
|
Chris@87
|
1903
|
Chris@87
|
1904 """
|
Chris@87
|
1905 ideg = int(deg)
|
Chris@87
|
1906 if ideg != deg or ideg < 1:
|
Chris@87
|
1907 raise ValueError("deg must be a non-negative integer")
|
Chris@87
|
1908
|
Chris@87
|
1909 x = np.cos(np.pi * np.arange(1, 2*ideg, 2) / (2.0*ideg))
|
Chris@87
|
1910 w = np.ones(ideg)*(np.pi/ideg)
|
Chris@87
|
1911
|
Chris@87
|
1912 return x, w
|
Chris@87
|
1913
|
Chris@87
|
1914
|
Chris@87
|
1915 def chebweight(x):
|
Chris@87
|
1916 """
|
Chris@87
|
1917 The weight function of the Chebyshev polynomials.
|
Chris@87
|
1918
|
Chris@87
|
1919 The weight function is :math:`1/\sqrt{1 - x^2}` and the interval of
|
Chris@87
|
1920 integration is :math:`[-1, 1]`. The Chebyshev polynomials are
|
Chris@87
|
1921 orthogonal, but not normalized, with respect to this weight function.
|
Chris@87
|
1922
|
Chris@87
|
1923 Parameters
|
Chris@87
|
1924 ----------
|
Chris@87
|
1925 x : array_like
|
Chris@87
|
1926 Values at which the weight function will be computed.
|
Chris@87
|
1927
|
Chris@87
|
1928 Returns
|
Chris@87
|
1929 -------
|
Chris@87
|
1930 w : ndarray
|
Chris@87
|
1931 The weight function at `x`.
|
Chris@87
|
1932
|
Chris@87
|
1933 Notes
|
Chris@87
|
1934 -----
|
Chris@87
|
1935
|
Chris@87
|
1936 .. versionadded:: 1.7.0
|
Chris@87
|
1937
|
Chris@87
|
1938 """
|
Chris@87
|
1939 w = 1./(np.sqrt(1. + x) * np.sqrt(1. - x))
|
Chris@87
|
1940 return w
|
Chris@87
|
1941
|
Chris@87
|
1942
|
Chris@87
|
1943 def chebpts1(npts):
|
Chris@87
|
1944 """
|
Chris@87
|
1945 Chebyshev points of the first kind.
|
Chris@87
|
1946
|
Chris@87
|
1947 The Chebyshev points of the first kind are the points ``cos(x)``,
|
Chris@87
|
1948 where ``x = [pi*(k + .5)/npts for k in range(npts)]``.
|
Chris@87
|
1949
|
Chris@87
|
1950 Parameters
|
Chris@87
|
1951 ----------
|
Chris@87
|
1952 npts : int
|
Chris@87
|
1953 Number of sample points desired.
|
Chris@87
|
1954
|
Chris@87
|
1955 Returns
|
Chris@87
|
1956 -------
|
Chris@87
|
1957 pts : ndarray
|
Chris@87
|
1958 The Chebyshev points of the first kind.
|
Chris@87
|
1959
|
Chris@87
|
1960 See Also
|
Chris@87
|
1961 --------
|
Chris@87
|
1962 chebpts2
|
Chris@87
|
1963
|
Chris@87
|
1964 Notes
|
Chris@87
|
1965 -----
|
Chris@87
|
1966
|
Chris@87
|
1967 .. versionadded:: 1.5.0
|
Chris@87
|
1968
|
Chris@87
|
1969 """
|
Chris@87
|
1970 _npts = int(npts)
|
Chris@87
|
1971 if _npts != npts:
|
Chris@87
|
1972 raise ValueError("npts must be integer")
|
Chris@87
|
1973 if _npts < 1:
|
Chris@87
|
1974 raise ValueError("npts must be >= 1")
|
Chris@87
|
1975
|
Chris@87
|
1976 x = np.linspace(-np.pi, 0, _npts, endpoint=False) + np.pi/(2*_npts)
|
Chris@87
|
1977 return np.cos(x)
|
Chris@87
|
1978
|
Chris@87
|
1979
|
Chris@87
|
1980 def chebpts2(npts):
|
Chris@87
|
1981 """
|
Chris@87
|
1982 Chebyshev points of the second kind.
|
Chris@87
|
1983
|
Chris@87
|
1984 The Chebyshev points of the second kind are the points ``cos(x)``,
|
Chris@87
|
1985 where ``x = [pi*k/(npts - 1) for k in range(npts)]``.
|
Chris@87
|
1986
|
Chris@87
|
1987 Parameters
|
Chris@87
|
1988 ----------
|
Chris@87
|
1989 npts : int
|
Chris@87
|
1990 Number of sample points desired.
|
Chris@87
|
1991
|
Chris@87
|
1992 Returns
|
Chris@87
|
1993 -------
|
Chris@87
|
1994 pts : ndarray
|
Chris@87
|
1995 The Chebyshev points of the second kind.
|
Chris@87
|
1996
|
Chris@87
|
1997 Notes
|
Chris@87
|
1998 -----
|
Chris@87
|
1999
|
Chris@87
|
2000 .. versionadded:: 1.5.0
|
Chris@87
|
2001
|
Chris@87
|
2002 """
|
Chris@87
|
2003 _npts = int(npts)
|
Chris@87
|
2004 if _npts != npts:
|
Chris@87
|
2005 raise ValueError("npts must be integer")
|
Chris@87
|
2006 if _npts < 2:
|
Chris@87
|
2007 raise ValueError("npts must be >= 2")
|
Chris@87
|
2008
|
Chris@87
|
2009 x = np.linspace(-np.pi, 0, _npts)
|
Chris@87
|
2010 return np.cos(x)
|
Chris@87
|
2011
|
Chris@87
|
2012
|
Chris@87
|
2013 #
|
Chris@87
|
2014 # Chebyshev series class
|
Chris@87
|
2015 #
|
Chris@87
|
2016
|
Chris@87
|
2017 class Chebyshev(ABCPolyBase):
|
Chris@87
|
2018 """A Chebyshev series class.
|
Chris@87
|
2019
|
Chris@87
|
2020 The Chebyshev class provides the standard Python numerical methods
|
Chris@87
|
2021 '+', '-', '*', '//', '%', 'divmod', '**', and '()' as well as the
|
Chris@87
|
2022 methods listed below.
|
Chris@87
|
2023
|
Chris@87
|
2024 Parameters
|
Chris@87
|
2025 ----------
|
Chris@87
|
2026 coef : array_like
|
Chris@87
|
2027 Chebyshev coefficients in order of increasing degree, i.e.,
|
Chris@87
|
2028 ``(1, 2, 3)`` gives ``1*T_0(x) + 2*T_1(x) + 3*T_2(x)``.
|
Chris@87
|
2029 domain : (2,) array_like, optional
|
Chris@87
|
2030 Domain to use. The interval ``[domain[0], domain[1]]`` is mapped
|
Chris@87
|
2031 to the interval ``[window[0], window[1]]`` by shifting and scaling.
|
Chris@87
|
2032 The default value is [-1, 1].
|
Chris@87
|
2033 window : (2,) array_like, optional
|
Chris@87
|
2034 Window, see `domain` for its use. The default value is [-1, 1].
|
Chris@87
|
2035
|
Chris@87
|
2036 .. versionadded:: 1.6.0
|
Chris@87
|
2037
|
Chris@87
|
2038 """
|
Chris@87
|
2039 # Virtual Functions
|
Chris@87
|
2040 _add = staticmethod(chebadd)
|
Chris@87
|
2041 _sub = staticmethod(chebsub)
|
Chris@87
|
2042 _mul = staticmethod(chebmul)
|
Chris@87
|
2043 _div = staticmethod(chebdiv)
|
Chris@87
|
2044 _pow = staticmethod(chebpow)
|
Chris@87
|
2045 _val = staticmethod(chebval)
|
Chris@87
|
2046 _int = staticmethod(chebint)
|
Chris@87
|
2047 _der = staticmethod(chebder)
|
Chris@87
|
2048 _fit = staticmethod(chebfit)
|
Chris@87
|
2049 _line = staticmethod(chebline)
|
Chris@87
|
2050 _roots = staticmethod(chebroots)
|
Chris@87
|
2051 _fromroots = staticmethod(chebfromroots)
|
Chris@87
|
2052
|
Chris@87
|
2053 # Virtual properties
|
Chris@87
|
2054 nickname = 'cheb'
|
Chris@87
|
2055 domain = np.array(chebdomain)
|
Chris@87
|
2056 window = np.array(chebdomain)
|