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