2593 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			2593 lines
		
	
	
		
			72 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """
 | |
| A collection of functions to find the weights and abscissas for
 | |
| Gaussian Quadrature.
 | |
| 
 | |
| These calculations are done by finding the eigenvalues of a
 | |
| tridiagonal matrix whose entries are dependent on the coefficients
 | |
| in the recursion formula for the orthogonal polynomials with the
 | |
| corresponding weighting function over the interval.
 | |
| 
 | |
| Many recursion relations for orthogonal polynomials are given:
 | |
| 
 | |
| .. math::
 | |
| 
 | |
|     a1n f_{n+1} (x) = (a2n + a3n x ) f_n (x) - a4n f_{n-1} (x)
 | |
| 
 | |
| The recursion relation of interest is
 | |
| 
 | |
| .. math::
 | |
| 
 | |
|     P_{n+1} (x) = (x - A_n) P_n (x) - B_n P_{n-1} (x)
 | |
| 
 | |
| where :math:`P` has a different normalization than :math:`f`.
 | |
| 
 | |
| The coefficients can be found as:
 | |
| 
 | |
| .. math::
 | |
| 
 | |
|     A_n = -a2n / a3n
 | |
|     \\qquad
 | |
|     B_n = ( a4n / a3n \\sqrt{h_n-1 / h_n})^2
 | |
| 
 | |
| where
 | |
| 
 | |
| .. math::
 | |
| 
 | |
|     h_n = \\int_a^b w(x) f_n(x)^2
 | |
| 
 | |
| assume:
 | |
| 
 | |
| .. math::
 | |
| 
 | |
|     P_0 (x) = 1
 | |
|     \\qquad
 | |
|     P_{-1} (x) == 0
 | |
| 
 | |
| For the mathematical background, see [golub.welsch-1969-mathcomp]_ and
 | |
| [abramowitz.stegun-1965]_.
 | |
| 
 | |
| References
 | |
| ----------
 | |
| .. [golub.welsch-1969-mathcomp]
 | |
|    Golub, Gene H, and John H Welsch. 1969. Calculation of Gauss
 | |
|    Quadrature Rules. *Mathematics of Computation* 23, 221-230+s1--s10.
 | |
| 
 | |
| .. [abramowitz.stegun-1965]
 | |
|    Abramowitz, Milton, and Irene A Stegun. (1965) *Handbook of
 | |
|    Mathematical Functions: with Formulas, Graphs, and Mathematical
 | |
|    Tables*. Gaithersburg, MD: National Bureau of Standards.
 | |
|    http://www.math.sfu.ca/~cbm/aands/
 | |
| 
 | |
| .. [townsend.trogdon.olver-2014]
 | |
|    Townsend, A. and Trogdon, T. and Olver, S. (2014)
 | |
|    *Fast computation of Gauss quadrature nodes and
 | |
|    weights on the whole real line*. :arXiv:`1410.5286`.
 | |
| 
 | |
| .. [townsend.trogdon.olver-2015]
 | |
|    Townsend, A. and Trogdon, T. and Olver, S. (2015)
 | |
|    *Fast computation of Gauss quadrature nodes and
 | |
|    weights on the whole real line*.
 | |
|    IMA Journal of Numerical Analysis
 | |
|    :doi:`10.1093/imanum/drv002`.
 | |
| """
 | |
| #
 | |
| # Author:  Travis Oliphant 2000
 | |
| # Updated Sep. 2003 (fixed bugs --- tested to be accurate)
 | |
| 
 | |
| # SciPy imports.
 | |
| import numpy as np
 | |
| from numpy import (exp, inf, pi, sqrt, floor, sin, cos, around,
 | |
|                    hstack, arccos, arange)
 | |
| from scipy import linalg
 | |
| from scipy.special import airy
 | |
| 
 | |
| # Local imports.
 | |
| # There is no .pyi file for _specfun
 | |
| from . import _specfun  # type: ignore
 | |
| from . import _ufuncs
 | |
| _gam = _ufuncs.gamma
 | |
| 
 | |
| _polyfuns = ['legendre', 'chebyt', 'chebyu', 'chebyc', 'chebys',
 | |
|              'jacobi', 'laguerre', 'genlaguerre', 'hermite',
 | |
|              'hermitenorm', 'gegenbauer', 'sh_legendre', 'sh_chebyt',
 | |
|              'sh_chebyu', 'sh_jacobi']
 | |
| 
 | |
| # Correspondence between new and old names of root functions
 | |
| _rootfuns_map = {'roots_legendre': 'p_roots',
 | |
|                  'roots_chebyt': 't_roots',
 | |
|                  'roots_chebyu': 'u_roots',
 | |
|                  'roots_chebyc': 'c_roots',
 | |
|                  'roots_chebys': 's_roots',
 | |
|                  'roots_jacobi': 'j_roots',
 | |
|                  'roots_laguerre': 'l_roots',
 | |
|                  'roots_genlaguerre': 'la_roots',
 | |
|                  'roots_hermite': 'h_roots',
 | |
|                  'roots_hermitenorm': 'he_roots',
 | |
|                  'roots_gegenbauer': 'cg_roots',
 | |
|                  'roots_sh_legendre': 'ps_roots',
 | |
|                  'roots_sh_chebyt': 'ts_roots',
 | |
|                  'roots_sh_chebyu': 'us_roots',
 | |
|                  'roots_sh_jacobi': 'js_roots'}
 | |
| 
 | |
| __all__ = _polyfuns + list(_rootfuns_map.keys())
 | |
| 
 | |
| 
 | |
| class orthopoly1d(np.poly1d):
 | |
| 
 | |
|     def __init__(self, roots, weights=None, hn=1.0, kn=1.0, wfunc=None,
 | |
|                  limits=None, monic=False, eval_func=None):
 | |
|         equiv_weights = [weights[k] / wfunc(roots[k]) for
 | |
|                          k in range(len(roots))]
 | |
|         mu = sqrt(hn)
 | |
|         if monic:
 | |
|             evf = eval_func
 | |
|             if evf:
 | |
|                 knn = kn
 | |
|                 def eval_func(x):
 | |
|                     return evf(x) / knn
 | |
|             mu = mu / abs(kn)
 | |
|             kn = 1.0
 | |
| 
 | |
|         # compute coefficients from roots, then scale
 | |
|         poly = np.poly1d(roots, r=True)
 | |
|         np.poly1d.__init__(self, poly.coeffs * float(kn))
 | |
| 
 | |
|         self.weights = np.array(list(zip(roots, weights, equiv_weights)))
 | |
|         self.weight_func = wfunc
 | |
|         self.limits = limits
 | |
|         self.normcoef = mu
 | |
| 
 | |
|         # Note: eval_func will be discarded on arithmetic
 | |
|         self._eval_func = eval_func
 | |
| 
 | |
|     def __call__(self, v):
 | |
|         if self._eval_func and not isinstance(v, np.poly1d):
 | |
|             return self._eval_func(v)
 | |
|         else:
 | |
|             return np.poly1d.__call__(self, v)
 | |
| 
 | |
|     def _scale(self, p):
 | |
|         if p == 1.0:
 | |
|             return
 | |
|         self._coeffs *= p
 | |
| 
 | |
|         evf = self._eval_func
 | |
|         if evf:
 | |
|             self._eval_func = lambda x: evf(x) * p
 | |
|         self.normcoef *= p
 | |
| 
 | |
| 
 | |
| def _gen_roots_and_weights(n, mu0, an_func, bn_func, f, df, symmetrize, mu):
 | |
|     """[x,w] = gen_roots_and_weights(n,an_func,sqrt_bn_func,mu)
 | |
| 
 | |
|     Returns the roots (x) of an nth order orthogonal polynomial,
 | |
|     and weights (w) to use in appropriate Gaussian quadrature with that
 | |
|     orthogonal polynomial.
 | |
| 
 | |
|     The polynomials have the recurrence relation
 | |
|           P_n+1(x) = (x - A_n) P_n(x) - B_n P_n-1(x)
 | |
| 
 | |
|     an_func(n)          should return A_n
 | |
|     sqrt_bn_func(n)     should return sqrt(B_n)
 | |
|     mu ( = h_0 )        is the integral of the weight over the orthogonal
 | |
|                         interval
 | |
|     """
 | |
|     k = np.arange(n, dtype='d')
 | |
|     c = np.zeros((2, n))
 | |
|     c[0,1:] = bn_func(k[1:])
 | |
|     c[1,:] = an_func(k)
 | |
|     x = linalg.eigvals_banded(c, overwrite_a_band=True)
 | |
| 
 | |
|     # improve roots by one application of Newton's method
 | |
|     y = f(n, x)
 | |
|     dy = df(n, x)
 | |
|     x -= y/dy
 | |
| 
 | |
|     # fm and dy may contain very large/small values, so we
 | |
|     # log-normalize them to maintain precision in the product fm*dy
 | |
|     fm = f(n-1, x)
 | |
|     log_fm = np.log(np.abs(fm))
 | |
|     log_dy = np.log(np.abs(dy))
 | |
|     fm /= np.exp((log_fm.max() + log_fm.min()) / 2.)
 | |
|     dy /= np.exp((log_dy.max() + log_dy.min()) / 2.)
 | |
|     w = 1.0 / (fm * dy)
 | |
| 
 | |
|     if symmetrize:
 | |
|         w = (w + w[::-1]) / 2
 | |
|         x = (x - x[::-1]) / 2
 | |
| 
 | |
|     w *= mu0 / w.sum()
 | |
| 
 | |
|     if mu:
 | |
|         return x, w, mu0
 | |
|     else:
 | |
|         return x, w
 | |
| 
 | |
| # Jacobi Polynomials 1               P^(alpha,beta)_n(x)
 | |
| 
 | |
| 
 | |
| def roots_jacobi(n, alpha, beta, mu=False):
 | |
|     r"""Gauss-Jacobi quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Jacobi
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Jacobi polynomial, :math:`P^{\alpha, \beta}_n(x)`. These sample
 | |
|     points and weights correctly integrate polynomials of degree
 | |
|     :math:`2n - 1` or less over the interval :math:`[-1, 1]` with
 | |
|     weight function :math:`w(x) = (1 - x)^{\alpha} (1 +
 | |
|     x)^{\beta}`. See 22.2.1 in [AS]_ for details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     alpha : float
 | |
|         alpha must be > -1
 | |
|     beta : float
 | |
|         beta must be > -1
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     m = int(n)
 | |
|     if n < 1 or n != m:
 | |
|         raise ValueError("n must be a positive integer.")
 | |
|     if alpha <= -1 or beta <= -1:
 | |
|         raise ValueError("alpha and beta must be greater than -1.")
 | |
| 
 | |
|     if alpha == 0.0 and beta == 0.0:
 | |
|         return roots_legendre(m, mu)
 | |
|     if alpha == beta:
 | |
|         return roots_gegenbauer(m, alpha+0.5, mu)
 | |
| 
 | |
|     if (alpha + beta) <= 1000:
 | |
|         mu0 = 2.0**(alpha+beta+1) * _ufuncs.beta(alpha+1, beta+1)
 | |
|     else:
 | |
|         # Avoid overflows in pow and beta for very large parameters
 | |
|         mu0 = np.exp((alpha + beta + 1) * np.log(2.0)
 | |
|                      + _ufuncs.betaln(alpha+1, beta+1))
 | |
|     a = alpha
 | |
|     b = beta
 | |
|     if a + b == 0.0:
 | |
|         def an_func(k):
 | |
|             return np.where(k == 0, (b - a) / (2 + a + b), 0.0)
 | |
|     else:
 | |
|         def an_func(k):
 | |
|             return np.where(
 | |
|                 k == 0,
 | |
|                 (b - a) / (2 + a + b),
 | |
|                 (b * b - a * a) / ((2.0 * k + a + b) * (2.0 * k + a + b + 2))
 | |
|             )
 | |
| 
 | |
|     def bn_func(k):
 | |
|         return (
 | |
|             2.0 / (2.0 * k + a + b)
 | |
|             * np.sqrt((k + a) * (k + b) / (2 * k + a + b + 1))
 | |
|             * np.where(k == 1, 1.0, np.sqrt(k * (k + a + b) / (2.0 * k + a + b - 1)))
 | |
|         )
 | |
| 
 | |
|     def f(n, x):
 | |
|         return _ufuncs.eval_jacobi(n, a, b, x)
 | |
|     def df(n, x):
 | |
|         return 0.5 * (n + a + b + 1) * _ufuncs.eval_jacobi(n - 1, a + 1, b + 1, x)
 | |
|     return _gen_roots_and_weights(m, mu0, an_func, bn_func, f, df, False, mu)
 | |
| 
 | |
| 
 | |
| def jacobi(n, alpha, beta, monic=False):
 | |
|     r"""Jacobi polynomial.
 | |
| 
 | |
|     Defined to be the solution of
 | |
| 
 | |
|     .. math::
 | |
|         (1 - x^2)\frac{d^2}{dx^2}P_n^{(\alpha, \beta)}
 | |
|           + (\beta - \alpha - (\alpha + \beta + 2)x)
 | |
|             \frac{d}{dx}P_n^{(\alpha, \beta)}
 | |
|           + n(n + \alpha + \beta + 1)P_n^{(\alpha, \beta)} = 0
 | |
| 
 | |
|     for :math:`\alpha, \beta > -1`; :math:`P_n^{(\alpha, \beta)}` is a
 | |
|     polynomial of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     alpha : float
 | |
|         Parameter, must be greater than -1.
 | |
|     beta : float
 | |
|         Parameter, must be greater than -1.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     P : orthopoly1d
 | |
|         Jacobi polynomial.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     For fixed :math:`\alpha, \beta`, the polynomials
 | |
|     :math:`P_n^{(\alpha, \beta)}` are orthogonal over :math:`[-1, 1]`
 | |
|     with weight function :math:`(1 - x)^\alpha(1 + x)^\beta`.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     The Jacobi polynomials satisfy the recurrence relation:
 | |
| 
 | |
|     .. math::
 | |
|         P_n^{(\alpha, \beta-1)}(x) - P_n^{(\alpha-1, \beta)}(x)
 | |
|           = P_{n-1}^{(\alpha, \beta)}(x)
 | |
| 
 | |
|     This can be verified, for example, for :math:`\alpha = \beta = 2`
 | |
|     and :math:`n = 1` over the interval :math:`[-1, 1]`:
 | |
| 
 | |
|     >>> import numpy as np
 | |
|     >>> from scipy.special import jacobi
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> np.allclose(jacobi(0, 2, 2)(x),
 | |
|     ...             jacobi(1, 2, 1)(x) - jacobi(1, 1, 2)(x))
 | |
|     True
 | |
| 
 | |
|     Plot of the Jacobi polynomial :math:`P_5^{(\alpha, -0.5)}` for
 | |
|     different values of :math:`\alpha`:
 | |
| 
 | |
|     >>> import matplotlib.pyplot as plt
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> fig, ax = plt.subplots()
 | |
|     >>> ax.set_ylim(-2.0, 2.0)
 | |
|     >>> ax.set_title(r'Jacobi polynomials $P_5^{(\alpha, -0.5)}$')
 | |
|     >>> for alpha in np.arange(0, 4, 1):
 | |
|     ...     ax.plot(x, jacobi(5, alpha, -0.5)(x), label=rf'$\alpha={alpha}$')
 | |
|     >>> plt.legend(loc='best')
 | |
|     >>> plt.show()
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     def wfunc(x):
 | |
|         return (1 - x) ** alpha * (1 + x) ** beta
 | |
|     if n == 0:
 | |
|         return orthopoly1d([], [], 1.0, 1.0, wfunc, (-1, 1), monic,
 | |
|                            eval_func=np.ones_like)
 | |
|     x, w, mu = roots_jacobi(n, alpha, beta, mu=True)
 | |
|     ab1 = alpha + beta + 1.0
 | |
|     hn = 2**ab1 / (2 * n + ab1) * _gam(n + alpha + 1)
 | |
|     hn *= _gam(n + beta + 1.0) / _gam(n + 1) / _gam(n + ab1)
 | |
|     kn = _gam(2 * n + ab1) / 2.0**n / _gam(n + 1) / _gam(n + ab1)
 | |
|     # here kn = coefficient on x^n term
 | |
|     p = orthopoly1d(x, w, hn, kn, wfunc, (-1, 1), monic,
 | |
|                     lambda x: _ufuncs.eval_jacobi(n, alpha, beta, x))
 | |
|     return p
 | |
| 
 | |
| # Jacobi Polynomials shifted         G_n(p,q,x)
 | |
| 
 | |
| 
 | |
| def roots_sh_jacobi(n, p1, q1, mu=False):
 | |
|     """Gauss-Jacobi (shifted) quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Jacobi (shifted)
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     shifted Jacobi polynomial, :math:`G^{p,q}_n(x)`. These sample
 | |
|     points and weights correctly integrate polynomials of degree
 | |
|     :math:`2n - 1` or less over the interval :math:`[0, 1]` with
 | |
|     weight function :math:`w(x) = (1 - x)^{p-q} x^{q-1}`. See 22.2.2
 | |
|     in [AS]_ for details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     p1 : float
 | |
|         (p1 - q1) must be > -1
 | |
|     q1 : float
 | |
|         q1 must be > 0
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     if (p1-q1) <= -1 or q1 <= 0:
 | |
|         message = "(p - q) must be greater than -1, and q must be greater than 0."
 | |
|         raise ValueError(message)
 | |
|     x, w, m = roots_jacobi(n, p1-q1, q1-1, True)
 | |
|     x = (x + 1) / 2
 | |
|     scale = 2.0**p1
 | |
|     w /= scale
 | |
|     m /= scale
 | |
|     if mu:
 | |
|         return x, w, m
 | |
|     else:
 | |
|         return x, w
 | |
| 
 | |
| 
 | |
| def sh_jacobi(n, p, q, monic=False):
 | |
|     r"""Shifted Jacobi polynomial.
 | |
| 
 | |
|     Defined by
 | |
| 
 | |
|     .. math::
 | |
| 
 | |
|         G_n^{(p, q)}(x)
 | |
|           = \binom{2n + p - 1}{n}^{-1}P_n^{(p - q, q - 1)}(2x - 1),
 | |
| 
 | |
|     where :math:`P_n^{(\cdot, \cdot)}` is the nth Jacobi polynomial.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     p : float
 | |
|         Parameter, must have :math:`p > q - 1`.
 | |
|     q : float
 | |
|         Parameter, must be greater than 0.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     G : orthopoly1d
 | |
|         Shifted Jacobi polynomial.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     For fixed :math:`p, q`, the polynomials :math:`G_n^{(p, q)}` are
 | |
|     orthogonal over :math:`[0, 1]` with weight function :math:`(1 -
 | |
|     x)^{p - q}x^{q - 1}`.
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     def wfunc(x):
 | |
|         return (1.0 - x) ** (p - q) * x ** (q - 1.0)
 | |
|     if n == 0:
 | |
|         return orthopoly1d([], [], 1.0, 1.0, wfunc, (-1, 1), monic,
 | |
|                            eval_func=np.ones_like)
 | |
|     n1 = n
 | |
|     x, w = roots_sh_jacobi(n1, p, q)
 | |
|     hn = _gam(n + 1) * _gam(n + q) * _gam(n + p) * _gam(n + p - q + 1)
 | |
|     hn /= (2 * n + p) * (_gam(2 * n + p)**2)
 | |
|     # kn = 1.0 in standard form so monic is redundant. Kept for compatibility.
 | |
|     kn = 1.0
 | |
|     pp = orthopoly1d(x, w, hn, kn, wfunc=wfunc, limits=(0, 1), monic=monic,
 | |
|                      eval_func=lambda x: _ufuncs.eval_sh_jacobi(n, p, q, x))
 | |
|     return pp
 | |
| 
 | |
| # Generalized Laguerre               L^(alpha)_n(x)
 | |
| 
 | |
| 
 | |
| def roots_genlaguerre(n, alpha, mu=False):
 | |
|     r"""Gauss-generalized Laguerre quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-generalized
 | |
|     Laguerre quadrature. The sample points are the roots of the nth
 | |
|     degree generalized Laguerre polynomial, :math:`L^{\alpha}_n(x)`.
 | |
|     These sample points and weights correctly integrate polynomials of
 | |
|     degree :math:`2n - 1` or less over the interval :math:`[0,
 | |
|     \infty]` with weight function :math:`w(x) = x^{\alpha}
 | |
|     e^{-x}`. See 22.3.9 in [AS]_ for details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     alpha : float
 | |
|         alpha must be > -1
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     m = int(n)
 | |
|     if n < 1 or n != m:
 | |
|         raise ValueError("n must be a positive integer.")
 | |
|     if alpha < -1:
 | |
|         raise ValueError("alpha must be greater than -1.")
 | |
| 
 | |
|     mu0 = _ufuncs.gamma(alpha + 1)
 | |
| 
 | |
|     if m == 1:
 | |
|         x = np.array([alpha+1.0], 'd')
 | |
|         w = np.array([mu0], 'd')
 | |
|         if mu:
 | |
|             return x, w, mu0
 | |
|         else:
 | |
|             return x, w
 | |
| 
 | |
|     def an_func(k):
 | |
|         return 2 * k + alpha + 1
 | |
|     def bn_func(k):
 | |
|         return -np.sqrt(k * (k + alpha))
 | |
|     def f(n, x):
 | |
|         return _ufuncs.eval_genlaguerre(n, alpha, x)
 | |
|     def df(n, x):
 | |
|         return (n * _ufuncs.eval_genlaguerre(n, alpha, x)
 | |
|                 - (n + alpha) * _ufuncs.eval_genlaguerre(n - 1, alpha, x)) / x
 | |
|     return _gen_roots_and_weights(m, mu0, an_func, bn_func, f, df, False, mu)
 | |
| 
 | |
| 
 | |
| def genlaguerre(n, alpha, monic=False):
 | |
|     r"""Generalized (associated) Laguerre polynomial.
 | |
| 
 | |
|     Defined to be the solution of
 | |
| 
 | |
|     .. math::
 | |
|         x\frac{d^2}{dx^2}L_n^{(\alpha)}
 | |
|           + (\alpha + 1 - x)\frac{d}{dx}L_n^{(\alpha)}
 | |
|           + nL_n^{(\alpha)} = 0,
 | |
| 
 | |
|     where :math:`\alpha > -1`; :math:`L_n^{(\alpha)}` is a polynomial
 | |
|     of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     alpha : float
 | |
|         Parameter, must be greater than -1.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     L : orthopoly1d
 | |
|         Generalized Laguerre polynomial.
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     laguerre : Laguerre polynomial.
 | |
|     hyp1f1 : confluent hypergeometric function
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     For fixed :math:`\alpha`, the polynomials :math:`L_n^{(\alpha)}`
 | |
|     are orthogonal over :math:`[0, \infty)` with weight function
 | |
|     :math:`e^{-x}x^\alpha`.
 | |
| 
 | |
|     The Laguerre polynomials are the special case where :math:`\alpha
 | |
|     = 0`.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     The generalized Laguerre polynomials are closely related to the confluent
 | |
|     hypergeometric function :math:`{}_1F_1`:
 | |
| 
 | |
|         .. math::
 | |
|             L_n^{(\alpha)} = \binom{n + \alpha}{n} {}_1F_1(-n, \alpha +1, x)
 | |
| 
 | |
|     This can be verified, for example,  for :math:`n = \alpha = 3` over the
 | |
|     interval :math:`[-1, 1]`:
 | |
| 
 | |
|     >>> import numpy as np
 | |
|     >>> from scipy.special import binom
 | |
|     >>> from scipy.special import genlaguerre
 | |
|     >>> from scipy.special import hyp1f1
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> np.allclose(genlaguerre(3, 3)(x), binom(6, 3) * hyp1f1(-3, 4, x))
 | |
|     True
 | |
| 
 | |
|     This is the plot of the generalized Laguerre polynomials
 | |
|     :math:`L_3^{(\alpha)}` for some values of :math:`\alpha`:
 | |
| 
 | |
|     >>> import matplotlib.pyplot as plt
 | |
|     >>> x = np.arange(-4.0, 12.0, 0.01)
 | |
|     >>> fig, ax = plt.subplots()
 | |
|     >>> ax.set_ylim(-5.0, 10.0)
 | |
|     >>> ax.set_title(r'Generalized Laguerre polynomials $L_3^{\alpha}$')
 | |
|     >>> for alpha in np.arange(0, 5):
 | |
|     ...     ax.plot(x, genlaguerre(3, alpha)(x), label=rf'$L_3^{(alpha)}$')
 | |
|     >>> plt.legend(loc='best')
 | |
|     >>> plt.show()
 | |
| 
 | |
|     """
 | |
|     if alpha <= -1:
 | |
|         raise ValueError("alpha must be > -1")
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     if n == 0:
 | |
|         n1 = n + 1
 | |
|     else:
 | |
|         n1 = n
 | |
|     x, w = roots_genlaguerre(n1, alpha)
 | |
|     def wfunc(x):
 | |
|         return exp(-x) * x ** alpha
 | |
|     if n == 0:
 | |
|         x, w = [], []
 | |
|     hn = _gam(n + alpha + 1) / _gam(n + 1)
 | |
|     kn = (-1)**n / _gam(n + 1)
 | |
|     p = orthopoly1d(x, w, hn, kn, wfunc, (0, inf), monic,
 | |
|                     lambda x: _ufuncs.eval_genlaguerre(n, alpha, x))
 | |
|     return p
 | |
| 
 | |
| # Laguerre                      L_n(x)
 | |
| 
 | |
| 
 | |
| def roots_laguerre(n, mu=False):
 | |
|     r"""Gauss-Laguerre quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Laguerre
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Laguerre polynomial, :math:`L_n(x)`. These sample points and
 | |
|     weights correctly integrate polynomials of degree :math:`2n - 1`
 | |
|     or less over the interval :math:`[0, \infty]` with weight function
 | |
|     :math:`w(x) = e^{-x}`. See 22.2.13 in [AS]_ for details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
|     numpy.polynomial.laguerre.laggauss
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     return roots_genlaguerre(n, 0.0, mu=mu)
 | |
| 
 | |
| 
 | |
| def laguerre(n, monic=False):
 | |
|     r"""Laguerre polynomial.
 | |
| 
 | |
|     Defined to be the solution of
 | |
| 
 | |
|     .. math::
 | |
|         x\frac{d^2}{dx^2}L_n + (1 - x)\frac{d}{dx}L_n + nL_n = 0;
 | |
| 
 | |
|     :math:`L_n` is a polynomial of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     L : orthopoly1d
 | |
|         Laguerre Polynomial.
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     genlaguerre : Generalized (associated) Laguerre polynomial.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`L_n` are orthogonal over :math:`[0,
 | |
|     \infty)` with weight function :math:`e^{-x}`.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     The Laguerre polynomials :math:`L_n` are the special case
 | |
|     :math:`\alpha = 0` of the generalized Laguerre polynomials
 | |
|     :math:`L_n^{(\alpha)}`.
 | |
|     Let's verify it on the interval :math:`[-1, 1]`:
 | |
| 
 | |
|     >>> import numpy as np
 | |
|     >>> from scipy.special import genlaguerre
 | |
|     >>> from scipy.special import laguerre
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> np.allclose(genlaguerre(3, 0)(x), laguerre(3)(x))
 | |
|     True
 | |
| 
 | |
|     The polynomials :math:`L_n` also satisfy the recurrence relation:
 | |
| 
 | |
|     .. math::
 | |
|         (n + 1)L_{n+1}(x) = (2n +1 -x)L_n(x) - nL_{n-1}(x)
 | |
| 
 | |
|     This can be easily checked on :math:`[0, 1]` for :math:`n = 3`:
 | |
| 
 | |
|     >>> x = np.arange(0.0, 1.0, 0.01)
 | |
|     >>> np.allclose(4 * laguerre(4)(x),
 | |
|     ...             (7 - x) * laguerre(3)(x) - 3 * laguerre(2)(x))
 | |
|     True
 | |
| 
 | |
|     This is the plot of the first few Laguerre polynomials :math:`L_n`:
 | |
| 
 | |
|     >>> import matplotlib.pyplot as plt
 | |
|     >>> x = np.arange(-1.0, 5.0, 0.01)
 | |
|     >>> fig, ax = plt.subplots()
 | |
|     >>> ax.set_ylim(-5.0, 5.0)
 | |
|     >>> ax.set_title(r'Laguerre polynomials $L_n$')
 | |
|     >>> for n in np.arange(0, 5):
 | |
|     ...     ax.plot(x, laguerre(n)(x), label=rf'$L_{n}$')
 | |
|     >>> plt.legend(loc='best')
 | |
|     >>> plt.show()
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     if n == 0:
 | |
|         n1 = n + 1
 | |
|     else:
 | |
|         n1 = n
 | |
|     x, w = roots_laguerre(n1)
 | |
|     if n == 0:
 | |
|         x, w = [], []
 | |
|     hn = 1.0
 | |
|     kn = (-1)**n / _gam(n + 1)
 | |
|     p = orthopoly1d(x, w, hn, kn, lambda x: exp(-x), (0, inf), monic,
 | |
|                     lambda x: _ufuncs.eval_laguerre(n, x))
 | |
|     return p
 | |
| 
 | |
| # Hermite  1                         H_n(x)
 | |
| 
 | |
| 
 | |
| def roots_hermite(n, mu=False):
 | |
|     r"""Gauss-Hermite (physicist's) quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Hermite
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Hermite polynomial, :math:`H_n(x)`. These sample points and
 | |
|     weights correctly integrate polynomials of degree :math:`2n - 1`
 | |
|     or less over the interval :math:`[-\infty, \infty]` with weight
 | |
|     function :math:`w(x) = e^{-x^2}`. See 22.2.14 in [AS]_ for
 | |
|     details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
|     numpy.polynomial.hermite.hermgauss
 | |
|     roots_hermitenorm
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     For small n up to 150 a modified version of the Golub-Welsch
 | |
|     algorithm is used. Nodes are computed from the eigenvalue
 | |
|     problem and improved by one step of a Newton iteration.
 | |
|     The weights are computed from the well-known analytical formula.
 | |
| 
 | |
|     For n larger than 150 an optimal asymptotic algorithm is applied
 | |
|     which computes nodes and weights in a numerically stable manner.
 | |
|     The algorithm has linear runtime making computation for very
 | |
|     large n (several thousand or more) feasible.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [townsend.trogdon.olver-2014]
 | |
|         Townsend, A. and Trogdon, T. and Olver, S. (2014)
 | |
|         *Fast computation of Gauss quadrature nodes and
 | |
|         weights on the whole real line*. :arXiv:`1410.5286`.
 | |
|     .. [townsend.trogdon.olver-2015]
 | |
|         Townsend, A. and Trogdon, T. and Olver, S. (2015)
 | |
|         *Fast computation of Gauss quadrature nodes and
 | |
|         weights on the whole real line*.
 | |
|         IMA Journal of Numerical Analysis
 | |
|         :doi:`10.1093/imanum/drv002`.
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     m = int(n)
 | |
|     if n < 1 or n != m:
 | |
|         raise ValueError("n must be a positive integer.")
 | |
| 
 | |
|     mu0 = np.sqrt(np.pi)
 | |
|     if n <= 150:
 | |
|         def an_func(k):
 | |
|             return 0.0 * k
 | |
|         def bn_func(k):
 | |
|             return np.sqrt(k / 2.0)
 | |
|         f = _ufuncs.eval_hermite
 | |
|         def df(n, x):
 | |
|             return 2.0 * n * _ufuncs.eval_hermite(n - 1, x)
 | |
|         return _gen_roots_and_weights(m, mu0, an_func, bn_func, f, df, True, mu)
 | |
|     else:
 | |
|         nodes, weights = _roots_hermite_asy(m)
 | |
|         if mu:
 | |
|             return nodes, weights, mu0
 | |
|         else:
 | |
|             return nodes, weights
 | |
| 
 | |
| 
 | |
| def _compute_tauk(n, k, maxit=5):
 | |
|     """Helper function for Tricomi initial guesses
 | |
| 
 | |
|     For details, see formula 3.1 in lemma 3.1 in the
 | |
|     original paper.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Quadrature order
 | |
|     k : ndarray of type int
 | |
|         Index of roots :math:`\tau_k` to compute
 | |
|     maxit : int
 | |
|         Number of Newton maxit performed, the default
 | |
|         value of 5 is sufficient.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     tauk : ndarray
 | |
|         Roots of equation 3.1
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     initial_nodes_a
 | |
|     roots_hermite_asy
 | |
|     """
 | |
|     a = n % 2 - 0.5
 | |
|     c = (4.0*floor(n/2.0) - 4.0*k + 3.0)*pi / (4.0*floor(n/2.0) + 2.0*a + 2.0)
 | |
|     def f(x):
 | |
|         return x - sin(x) - c
 | |
|     def df(x):
 | |
|         return 1.0 - cos(x)
 | |
|     xi = 0.5*pi
 | |
|     for i in range(maxit):
 | |
|         xi = xi - f(xi)/df(xi)
 | |
|     return xi
 | |
| 
 | |
| 
 | |
| def _initial_nodes_a(n, k):
 | |
|     r"""Tricomi initial guesses
 | |
| 
 | |
|     Computes an initial approximation to the square of the `k`-th
 | |
|     (positive) root :math:`x_k` of the Hermite polynomial :math:`H_n`
 | |
|     of order :math:`n`. The formula is the one from lemma 3.1 in the
 | |
|     original paper. The guesses are accurate except in the region
 | |
|     near :math:`\sqrt{2n + 1}`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Quadrature order
 | |
|     k : ndarray of type int
 | |
|         Index of roots to compute
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     xksq : ndarray
 | |
|         Square of the approximate roots
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     initial_nodes
 | |
|     roots_hermite_asy
 | |
|     """
 | |
|     tauk = _compute_tauk(n, k)
 | |
|     sigk = cos(0.5*tauk)**2
 | |
|     a = n % 2 - 0.5
 | |
|     nu = 4.0*floor(n/2.0) + 2.0*a + 2.0
 | |
|     # Initial approximation of Hermite roots (square)
 | |
|     xksq = nu*sigk - 1.0/(3.0*nu) * (5.0/(4.0*(1.0-sigk)**2) - 1.0/(1.0-sigk) - 0.25)
 | |
|     return xksq
 | |
| 
 | |
| 
 | |
| def _initial_nodes_b(n, k):
 | |
|     r"""Gatteschi initial guesses
 | |
| 
 | |
|     Computes an initial approximation to the square of the kth
 | |
|     (positive) root :math:`x_k` of the Hermite polynomial :math:`H_n`
 | |
|     of order :math:`n`. The formula is the one from lemma 3.2 in the
 | |
|     original paper. The guesses are accurate in the region just
 | |
|     below :math:`\sqrt{2n + 1}`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Quadrature order
 | |
|     k : ndarray of type int
 | |
|         Index of roots to compute
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     xksq : ndarray
 | |
|         Square of the approximate root
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     initial_nodes
 | |
|     roots_hermite_asy
 | |
|     """
 | |
|     a = n % 2 - 0.5
 | |
|     nu = 4.0*floor(n/2.0) + 2.0*a + 2.0
 | |
|     # Airy roots by approximation
 | |
|     ak = _specfun.airyzo(k.max(), 1)[0][::-1]
 | |
|     # Initial approximation of Hermite roots (square)
 | |
|     xksq = (nu
 | |
|             + 2.0**(2.0/3.0) * ak * nu**(1.0/3.0)
 | |
|             + 1.0/5.0 * 2.0**(4.0/3.0) * ak**2 * nu**(-1.0/3.0)
 | |
|             + (9.0/140.0 - 12.0/175.0 * ak**3) * nu**(-1.0)
 | |
|             + (16.0/1575.0 * ak + 92.0/7875.0 * ak**4) * 2.0**(2.0/3.0) * nu**(-5.0/3.0)
 | |
|             - (15152.0/3031875.0 * ak**5 + 1088.0/121275.0 * ak**2)
 | |
|               * 2.0**(1.0/3.0) * nu**(-7.0/3.0))
 | |
|     return xksq
 | |
| 
 | |
| 
 | |
| def _initial_nodes(n):
 | |
|     """Initial guesses for the Hermite roots
 | |
| 
 | |
|     Computes an initial approximation to the non-negative
 | |
|     roots :math:`x_k` of the Hermite polynomial :math:`H_n`
 | |
|     of order :math:`n`. The Tricomi and Gatteschi initial
 | |
|     guesses are used in the region where they are accurate.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Quadrature order
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     xk : ndarray
 | |
|         Approximate roots
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     roots_hermite_asy
 | |
|     """
 | |
|     # Turnover point
 | |
|     # linear polynomial fit to error of 10, 25, 40, ..., 1000 point rules
 | |
|     fit = 0.49082003*n - 4.37859653
 | |
|     turnover = around(fit).astype(int)
 | |
|     # Compute all approximations
 | |
|     ia = arange(1, int(floor(n*0.5)+1))
 | |
|     ib = ia[::-1]
 | |
|     xasq = _initial_nodes_a(n, ia[:turnover+1])
 | |
|     xbsq = _initial_nodes_b(n, ib[turnover+1:])
 | |
|     # Combine
 | |
|     iv = sqrt(hstack([xasq, xbsq]))
 | |
|     # Central node is always zero
 | |
|     if n % 2 == 1:
 | |
|         iv = hstack([0.0, iv])
 | |
|     return iv
 | |
| 
 | |
| 
 | |
| def _pbcf(n, theta):
 | |
|     r"""Asymptotic series expansion of parabolic cylinder function
 | |
| 
 | |
|     The implementation is based on sections 3.2 and 3.3 from the
 | |
|     original paper. Compared to the published version this code
 | |
|     adds one more term to the asymptotic series. The detailed
 | |
|     formulas can be found at [parabolic-asymptotics]_. The evaluation
 | |
|     is done in a transformed variable :math:`\theta := \arccos(t)`
 | |
|     where :math:`t := x / \mu` and :math:`\mu := \sqrt{2n + 1}`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Quadrature order
 | |
|     theta : ndarray
 | |
|         Transformed position variable
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     U : ndarray
 | |
|         Value of the parabolic cylinder function :math:`U(a, \theta)`.
 | |
|     Ud : ndarray
 | |
|         Value of the derivative :math:`U^{\prime}(a, \theta)` of
 | |
|         the parabolic cylinder function.
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     roots_hermite_asy
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [parabolic-asymptotics]
 | |
|        https://dlmf.nist.gov/12.10#vii
 | |
|     """
 | |
|     st = sin(theta)
 | |
|     ct = cos(theta)
 | |
|     # https://dlmf.nist.gov/12.10#vii
 | |
|     mu = 2.0*n + 1.0
 | |
|     # https://dlmf.nist.gov/12.10#E23
 | |
|     eta = 0.5*theta - 0.5*st*ct
 | |
|     # https://dlmf.nist.gov/12.10#E39
 | |
|     zeta = -(3.0*eta/2.0) ** (2.0/3.0)
 | |
|     # https://dlmf.nist.gov/12.10#E40
 | |
|     phi = (-zeta / st**2) ** (0.25)
 | |
|     # Coefficients
 | |
|     # https://dlmf.nist.gov/12.10#E43
 | |
|     a0 = 1.0
 | |
|     a1 = 0.10416666666666666667
 | |
|     a2 = 0.08355034722222222222
 | |
|     a3 = 0.12822657455632716049
 | |
|     a4 = 0.29184902646414046425
 | |
|     a5 = 0.88162726744375765242
 | |
|     b0 = 1.0
 | |
|     b1 = -0.14583333333333333333
 | |
|     b2 = -0.09874131944444444444
 | |
|     b3 = -0.14331205391589506173
 | |
|     b4 = -0.31722720267841354810
 | |
|     b5 = -0.94242914795712024914
 | |
|     # Polynomials
 | |
|     # https://dlmf.nist.gov/12.10#E9
 | |
|     # https://dlmf.nist.gov/12.10#E10
 | |
|     ctp = ct ** arange(16).reshape((-1,1))
 | |
|     u0 = 1.0
 | |
|     u1 = (1.0*ctp[3,:] - 6.0*ct) / 24.0
 | |
|     u2 = (-9.0*ctp[4,:] + 249.0*ctp[2,:] + 145.0) / 1152.0
 | |
|     u3 = (-4042.0*ctp[9,:] + 18189.0*ctp[7,:] - 28287.0*ctp[5,:]
 | |
|           - 151995.0*ctp[3,:] - 259290.0*ct) / 414720.0
 | |
|     u4 = (72756.0*ctp[10,:] - 321339.0*ctp[8,:] - 154982.0*ctp[6,:]
 | |
|           + 50938215.0*ctp[4,:] + 122602962.0*ctp[2,:] + 12773113.0) / 39813120.0
 | |
|     u5 = (82393456.0*ctp[15,:] - 617950920.0*ctp[13,:] + 1994971575.0*ctp[11,:]
 | |
|           - 3630137104.0*ctp[9,:] + 4433574213.0*ctp[7,:] - 37370295816.0*ctp[5,:]
 | |
|           - 119582875013.0*ctp[3,:] - 34009066266.0*ct) / 6688604160.0
 | |
|     v0 = 1.0
 | |
|     v1 = (1.0*ctp[3,:] + 6.0*ct) / 24.0
 | |
|     v2 = (15.0*ctp[4,:] - 327.0*ctp[2,:] - 143.0) / 1152.0
 | |
|     v3 = (-4042.0*ctp[9,:] + 18189.0*ctp[7,:] - 36387.0*ctp[5,:] 
 | |
|           + 238425.0*ctp[3,:] + 259290.0*ct) / 414720.0
 | |
|     v4 = (-121260.0*ctp[10,:] + 551733.0*ctp[8,:] - 151958.0*ctp[6,:]
 | |
|           - 57484425.0*ctp[4,:] - 132752238.0*ctp[2,:] - 12118727) / 39813120.0
 | |
|     v5 = (82393456.0*ctp[15,:] - 617950920.0*ctp[13,:] + 2025529095.0*ctp[11,:]
 | |
|           - 3750839308.0*ctp[9,:] + 3832454253.0*ctp[7,:] + 35213253348.0*ctp[5,:]
 | |
|           + 130919230435.0*ctp[3,:] + 34009066266*ct) / 6688604160.0
 | |
|     # Airy Evaluation (Bi and Bip unused)
 | |
|     Ai, Aip, Bi, Bip = airy(mu**(4.0/6.0) * zeta)
 | |
|     # Prefactor for U
 | |
|     P = 2.0*sqrt(pi) * mu**(1.0/6.0) * phi
 | |
|     # Terms for U
 | |
|     # https://dlmf.nist.gov/12.10#E42
 | |
|     phip = phi ** arange(6, 31, 6).reshape((-1,1))
 | |
|     A0 = b0*u0
 | |
|     A1 = (b2*u0 + phip[0,:]*b1*u1 + phip[1,:]*b0*u2) / zeta**3
 | |
|     A2 = (b4*u0 + phip[0,:]*b3*u1 + phip[1,:]*b2*u2 + phip[2,:]*b1*u3
 | |
|           + phip[3,:]*b0*u4) / zeta**6
 | |
|     B0 = -(a1*u0 + phip[0,:]*a0*u1) / zeta**2
 | |
|     B1 = -(a3*u0 + phip[0,:]*a2*u1 + phip[1,:]*a1*u2 + phip[2,:]*a0*u3) / zeta**5
 | |
|     B2 = -(a5*u0 + phip[0,:]*a4*u1 + phip[1,:]*a3*u2 + phip[2,:]*a2*u3
 | |
|            + phip[3,:]*a1*u4 + phip[4,:]*a0*u5) / zeta**8
 | |
|     # U
 | |
|     # https://dlmf.nist.gov/12.10#E35
 | |
|     U = P * (Ai * (A0 + A1/mu**2.0 + A2/mu**4.0) +
 | |
|              Aip * (B0 + B1/mu**2.0 + B2/mu**4.0) / mu**(8.0/6.0))
 | |
|     # Prefactor for derivative of U
 | |
|     Pd = sqrt(2.0*pi) * mu**(2.0/6.0) / phi
 | |
|     # Terms for derivative of U
 | |
|     # https://dlmf.nist.gov/12.10#E46
 | |
|     C0 = -(b1*v0 + phip[0,:]*b0*v1) / zeta
 | |
|     C1 = -(b3*v0 + phip[0,:]*b2*v1 + phip[1,:]*b1*v2 + phip[2,:]*b0*v3) / zeta**4
 | |
|     C2 = -(b5*v0 + phip[0,:]*b4*v1 + phip[1,:]*b3*v2 + phip[2,:]*b2*v3
 | |
|            + phip[3,:]*b1*v4 + phip[4,:]*b0*v5) / zeta**7
 | |
|     D0 = a0*v0
 | |
|     D1 = (a2*v0 + phip[0,:]*a1*v1 + phip[1,:]*a0*v2) / zeta**3
 | |
|     D2 = (a4*v0 + phip[0,:]*a3*v1 + phip[1,:]*a2*v2 + phip[2,:]*a1*v3
 | |
|           + phip[3,:]*a0*v4) / zeta**6
 | |
|     # Derivative of U
 | |
|     # https://dlmf.nist.gov/12.10#E36
 | |
|     Ud = Pd * (Ai * (C0 + C1/mu**2.0 + C2/mu**4.0) / mu**(4.0/6.0) +
 | |
|                Aip * (D0 + D1/mu**2.0 + D2/mu**4.0))
 | |
|     return U, Ud
 | |
| 
 | |
| 
 | |
| def _newton(n, x_initial, maxit=5):
 | |
|     """Newton iteration for polishing the asymptotic approximation
 | |
|     to the zeros of the Hermite polynomials.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Quadrature order
 | |
|     x_initial : ndarray
 | |
|         Initial guesses for the roots
 | |
|     maxit : int
 | |
|         Maximal number of Newton iterations.
 | |
|         The default 5 is sufficient, usually
 | |
|         only one or two steps are needed.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     nodes : ndarray
 | |
|         Quadrature nodes
 | |
|     weights : ndarray
 | |
|         Quadrature weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     roots_hermite_asy
 | |
|     """
 | |
|     # Variable transformation
 | |
|     mu = sqrt(2.0*n + 1.0)
 | |
|     t = x_initial / mu
 | |
|     theta = arccos(t)
 | |
|     # Newton iteration
 | |
|     for i in range(maxit):
 | |
|         u, ud = _pbcf(n, theta)
 | |
|         dtheta = u / (sqrt(2.0) * mu * sin(theta) * ud)
 | |
|         theta = theta + dtheta
 | |
|         if max(abs(dtheta)) < 1e-14:
 | |
|             break
 | |
|     # Undo variable transformation
 | |
|     x = mu * cos(theta)
 | |
|     # Central node is always zero
 | |
|     if n % 2 == 1:
 | |
|         x[0] = 0.0
 | |
|     # Compute weights
 | |
|     w = exp(-x**2) / (2.0*ud**2)
 | |
|     return x, w
 | |
| 
 | |
| 
 | |
| def _roots_hermite_asy(n):
 | |
|     r"""Gauss-Hermite (physicist's) quadrature for large n.
 | |
| 
 | |
|     Computes the sample points and weights for Gauss-Hermite quadrature.
 | |
|     The sample points are the roots of the nth degree Hermite polynomial,
 | |
|     :math:`H_n(x)`. These sample points and weights correctly integrate
 | |
|     polynomials of degree :math:`2n - 1` or less over the interval
 | |
|     :math:`[-\infty, \infty]` with weight function :math:`f(x) = e^{-x^2}`.
 | |
| 
 | |
|     This method relies on asymptotic expansions which work best for n > 150.
 | |
|     The algorithm has linear runtime making computation for very large n
 | |
|     feasible.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     nodes : ndarray
 | |
|         Quadrature nodes
 | |
|     weights : ndarray
 | |
|         Quadrature weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     roots_hermite
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [townsend.trogdon.olver-2014]
 | |
|        Townsend, A. and Trogdon, T. and Olver, S. (2014)
 | |
|        *Fast computation of Gauss quadrature nodes and
 | |
|        weights on the whole real line*. :arXiv:`1410.5286`.
 | |
| 
 | |
|     .. [townsend.trogdon.olver-2015]
 | |
|        Townsend, A. and Trogdon, T. and Olver, S. (2015)
 | |
|        *Fast computation of Gauss quadrature nodes and
 | |
|        weights on the whole real line*.
 | |
|        IMA Journal of Numerical Analysis
 | |
|        :doi:`10.1093/imanum/drv002`.
 | |
|     """
 | |
|     iv = _initial_nodes(n)
 | |
|     nodes, weights = _newton(n, iv)
 | |
|     # Combine with negative parts
 | |
|     if n % 2 == 0:
 | |
|         nodes = hstack([-nodes[::-1], nodes])
 | |
|         weights = hstack([weights[::-1], weights])
 | |
|     else:
 | |
|         nodes = hstack([-nodes[-1:0:-1], nodes])
 | |
|         weights = hstack([weights[-1:0:-1], weights])
 | |
|     # Scale weights
 | |
|     weights *= sqrt(pi) / sum(weights)
 | |
|     return nodes, weights
 | |
| 
 | |
| 
 | |
| def hermite(n, monic=False):
 | |
|     r"""Physicist's Hermite polynomial.
 | |
| 
 | |
|     Defined by
 | |
| 
 | |
|     .. math::
 | |
| 
 | |
|         H_n(x) = (-1)^ne^{x^2}\frac{d^n}{dx^n}e^{-x^2};
 | |
| 
 | |
|     :math:`H_n` is a polynomial of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     H : orthopoly1d
 | |
|         Hermite polynomial.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`H_n` are orthogonal over :math:`(-\infty,
 | |
|     \infty)` with weight function :math:`e^{-x^2}`.
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     >>> from scipy import special
 | |
|     >>> import matplotlib.pyplot as plt
 | |
|     >>> import numpy as np
 | |
| 
 | |
|     >>> p_monic = special.hermite(3, monic=True)
 | |
|     >>> p_monic
 | |
|     poly1d([ 1. ,  0. , -1.5,  0. ])
 | |
|     >>> p_monic(1)
 | |
|     -0.49999999999999983
 | |
|     >>> x = np.linspace(-3, 3, 400)
 | |
|     >>> y = p_monic(x)
 | |
|     >>> plt.plot(x, y)
 | |
|     >>> plt.title("Monic Hermite polynomial of degree 3")
 | |
|     >>> plt.xlabel("x")
 | |
|     >>> plt.ylabel("H_3(x)")
 | |
|     >>> plt.show()
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     if n == 0:
 | |
|         n1 = n + 1
 | |
|     else:
 | |
|         n1 = n
 | |
|     x, w = roots_hermite(n1)
 | |
|     def wfunc(x):
 | |
|         return exp(-x * x)
 | |
|     if n == 0:
 | |
|         x, w = [], []
 | |
|     hn = 2**n * _gam(n + 1) * sqrt(pi)
 | |
|     kn = 2**n
 | |
|     p = orthopoly1d(x, w, hn, kn, wfunc, (-inf, inf), monic,
 | |
|                     lambda x: _ufuncs.eval_hermite(n, x))
 | |
|     return p
 | |
| 
 | |
| # Hermite  2                         He_n(x)
 | |
| 
 | |
| 
 | |
| def roots_hermitenorm(n, mu=False):
 | |
|     r"""Gauss-Hermite (statistician's) quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Hermite
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Hermite polynomial, :math:`He_n(x)`. These sample points and
 | |
|     weights correctly integrate polynomials of degree :math:`2n - 1`
 | |
|     or less over the interval :math:`[-\infty, \infty]` with weight
 | |
|     function :math:`w(x) = e^{-x^2/2}`. See 22.2.15 in [AS]_ for more
 | |
|     details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
|     numpy.polynomial.hermite_e.hermegauss
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     For small n up to 150 a modified version of the Golub-Welsch
 | |
|     algorithm is used. Nodes are computed from the eigenvalue
 | |
|     problem and improved by one step of a Newton iteration.
 | |
|     The weights are computed from the well-known analytical formula.
 | |
| 
 | |
|     For n larger than 150 an optimal asymptotic algorithm is used
 | |
|     which computes nodes and weights in a numerical stable manner.
 | |
|     The algorithm has linear runtime making computation for very
 | |
|     large n (several thousand or more) feasible.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     m = int(n)
 | |
|     if n < 1 or n != m:
 | |
|         raise ValueError("n must be a positive integer.")
 | |
| 
 | |
|     mu0 = np.sqrt(2.0*np.pi)
 | |
|     if n <= 150:
 | |
|         def an_func(k):
 | |
|             return 0.0 * k
 | |
|         def bn_func(k):
 | |
|             return np.sqrt(k)
 | |
|         f = _ufuncs.eval_hermitenorm
 | |
|         def df(n, x):
 | |
|             return n * _ufuncs.eval_hermitenorm(n - 1, x)
 | |
|         return _gen_roots_and_weights(m, mu0, an_func, bn_func, f, df, True, mu)
 | |
|     else:
 | |
|         nodes, weights = _roots_hermite_asy(m)
 | |
|         # Transform
 | |
|         nodes *= sqrt(2)
 | |
|         weights *= sqrt(2)
 | |
|         if mu:
 | |
|             return nodes, weights, mu0
 | |
|         else:
 | |
|             return nodes, weights
 | |
| 
 | |
| 
 | |
| def hermitenorm(n, monic=False):
 | |
|     r"""Normalized (probabilist's) Hermite polynomial.
 | |
| 
 | |
|     Defined by
 | |
| 
 | |
|     .. math::
 | |
| 
 | |
|         He_n(x) = (-1)^ne^{x^2/2}\frac{d^n}{dx^n}e^{-x^2/2};
 | |
| 
 | |
|     :math:`He_n` is a polynomial of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     He : orthopoly1d
 | |
|         Hermite polynomial.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
| 
 | |
|     The polynomials :math:`He_n` are orthogonal over :math:`(-\infty,
 | |
|     \infty)` with weight function :math:`e^{-x^2/2}`.
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     if n == 0:
 | |
|         n1 = n + 1
 | |
|     else:
 | |
|         n1 = n
 | |
|     x, w = roots_hermitenorm(n1)
 | |
|     def wfunc(x):
 | |
|         return exp(-x * x / 2.0)
 | |
|     if n == 0:
 | |
|         x, w = [], []
 | |
|     hn = sqrt(2 * pi) * _gam(n + 1)
 | |
|     kn = 1.0
 | |
|     p = orthopoly1d(x, w, hn, kn, wfunc=wfunc, limits=(-inf, inf), monic=monic,
 | |
|                     eval_func=lambda x: _ufuncs.eval_hermitenorm(n, x))
 | |
|     return p
 | |
| 
 | |
| # The remainder of the polynomials can be derived from the ones above.
 | |
| 
 | |
| # Ultraspherical (Gegenbauer)        C^(alpha)_n(x)
 | |
| 
 | |
| 
 | |
| def roots_gegenbauer(n, alpha, mu=False):
 | |
|     r"""Gauss-Gegenbauer quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Gegenbauer
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Gegenbauer polynomial, :math:`C^{\alpha}_n(x)`. These sample
 | |
|     points and weights correctly integrate polynomials of degree
 | |
|     :math:`2n - 1` or less over the interval :math:`[-1, 1]` with
 | |
|     weight function :math:`w(x) = (1 - x^2)^{\alpha - 1/2}`. See
 | |
|     22.2.3 in [AS]_ for more details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     alpha : float
 | |
|         alpha must be > -0.5
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     m = int(n)
 | |
|     if n < 1 or n != m:
 | |
|         raise ValueError("n must be a positive integer.")
 | |
|     if alpha < -0.5:
 | |
|         raise ValueError("alpha must be greater than -0.5.")
 | |
|     elif alpha == 0.0:
 | |
|         # C(n,0,x) == 0 uniformly, however, as alpha->0, C(n,alpha,x)->T(n,x)
 | |
|         # strictly, we should just error out here, since the roots are not
 | |
|         # really defined, but we used to return something useful, so let's
 | |
|         # keep doing so.
 | |
|         return roots_chebyt(n, mu)
 | |
| 
 | |
|     if alpha <= 170:
 | |
|         mu0 = (np.sqrt(np.pi) * _ufuncs.gamma(alpha + 0.5)) \
 | |
|               / _ufuncs.gamma(alpha + 1)
 | |
|     else:
 | |
|         # For large alpha we use a Taylor series expansion around inf,
 | |
|         # expressed as a 6th order polynomial of a^-1 and using Horner's
 | |
|         # method to minimize computation and maximize precision
 | |
|         inv_alpha = 1. / alpha
 | |
|         coeffs = np.array([0.000207186, -0.00152206, -0.000640869,
 | |
|                            0.00488281, 0.0078125, -0.125, 1.])
 | |
|         mu0 = coeffs[0]
 | |
|         for term in range(1, len(coeffs)):
 | |
|             mu0 = mu0 * inv_alpha + coeffs[term]
 | |
|         mu0 = mu0 * np.sqrt(np.pi / alpha)
 | |
|     def an_func(k):
 | |
|         return 0.0 * k
 | |
|     def bn_func(k):
 | |
|         return np.sqrt(k * (k + 2 * alpha - 1) / (4 * (k + alpha) * (k + alpha - 1)))
 | |
|     def f(n, x):
 | |
|         return _ufuncs.eval_gegenbauer(n, alpha, x)
 | |
|     def df(n, x):
 | |
|         return (
 | |
|             -n * x * _ufuncs.eval_gegenbauer(n, alpha, x)
 | |
|             + (n + 2 * alpha - 1) * _ufuncs.eval_gegenbauer(n - 1, alpha, x)
 | |
|         ) / (1 - x ** 2)
 | |
|     return _gen_roots_and_weights(m, mu0, an_func, bn_func, f, df, True, mu)
 | |
| 
 | |
| 
 | |
| def gegenbauer(n, alpha, monic=False):
 | |
|     r"""Gegenbauer (ultraspherical) polynomial.
 | |
| 
 | |
|     Defined to be the solution of
 | |
| 
 | |
|     .. math::
 | |
|         (1 - x^2)\frac{d^2}{dx^2}C_n^{(\alpha)}
 | |
|           - (2\alpha + 1)x\frac{d}{dx}C_n^{(\alpha)}
 | |
|           + n(n + 2\alpha)C_n^{(\alpha)} = 0
 | |
| 
 | |
|     for :math:`\alpha > -1/2`; :math:`C_n^{(\alpha)}` is a polynomial
 | |
|     of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     alpha : float
 | |
|         Parameter, must be greater than -0.5.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     C : orthopoly1d
 | |
|         Gegenbauer polynomial.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`C_n^{(\alpha)}` are orthogonal over
 | |
|     :math:`[-1,1]` with weight function :math:`(1 - x^2)^{(\alpha -
 | |
|     1/2)}`.
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     >>> import numpy as np
 | |
|     >>> from scipy import special
 | |
|     >>> import matplotlib.pyplot as plt
 | |
| 
 | |
|     We can initialize a variable ``p`` as a Gegenbauer polynomial using the
 | |
|     `gegenbauer` function and evaluate at a point ``x = 1``.
 | |
| 
 | |
|     >>> p = special.gegenbauer(3, 0.5, monic=False)
 | |
|     >>> p
 | |
|     poly1d([ 2.5,  0. , -1.5,  0. ])
 | |
|     >>> p(1)
 | |
|     1.0
 | |
| 
 | |
|     To evaluate ``p`` at various points ``x`` in the interval ``(-3, 3)``,
 | |
|     simply pass an array ``x`` to ``p`` as follows:
 | |
| 
 | |
|     >>> x = np.linspace(-3, 3, 400)
 | |
|     >>> y = p(x)
 | |
| 
 | |
|     We can then visualize ``x, y`` using `matplotlib.pyplot`.
 | |
| 
 | |
|     >>> fig, ax = plt.subplots()
 | |
|     >>> ax.plot(x, y)
 | |
|     >>> ax.set_title("Gegenbauer (ultraspherical) polynomial of degree 3")
 | |
|     >>> ax.set_xlabel("x")
 | |
|     >>> ax.set_ylabel("G_3(x)")
 | |
|     >>> plt.show()
 | |
| 
 | |
|     """
 | |
|     if not np.isfinite(alpha) or alpha <= -0.5 :
 | |
|         raise ValueError("`alpha` must be a finite number greater than -1/2")
 | |
|     base = jacobi(n, alpha - 0.5, alpha - 0.5, monic=monic)
 | |
|     if monic or n == 0:
 | |
|         return base
 | |
|     #  Abrahmowitz and Stegan 22.5.20
 | |
|     factor = (_gam(2*alpha + n) * _gam(alpha + 0.5) /
 | |
|               _gam(2*alpha) / _gam(alpha + 0.5 + n))
 | |
|     base._scale(factor)
 | |
|     base.__dict__['_eval_func'] = lambda x: _ufuncs.eval_gegenbauer(float(n),
 | |
|                                                                     alpha, x)
 | |
|     return base
 | |
| 
 | |
| # Chebyshev of the first kind: T_n(x) =
 | |
| #     n! sqrt(pi) / _gam(n+1./2)* P^(-1/2,-1/2)_n(x)
 | |
| # Computed anew.
 | |
| 
 | |
| 
 | |
| def roots_chebyt(n, mu=False):
 | |
|     r"""Gauss-Chebyshev (first kind) quadrature.
 | |
| 
 | |
|     Computes the sample points and weights for Gauss-Chebyshev
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Chebyshev polynomial of the first kind, :math:`T_n(x)`. These
 | |
|     sample points and weights correctly integrate polynomials of
 | |
|     degree :math:`2n - 1` or less over the interval :math:`[-1, 1]`
 | |
|     with weight function :math:`w(x) = 1/\sqrt{1 - x^2}`. See 22.2.4
 | |
|     in [AS]_ for more details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
|     numpy.polynomial.chebyshev.chebgauss
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     m = int(n)
 | |
|     if n < 1 or n != m:
 | |
|         raise ValueError('n must be a positive integer.')
 | |
|     x = _ufuncs._sinpi(np.arange(-m + 1, m, 2) / (2*m))
 | |
|     w = np.full_like(x, pi/m)
 | |
|     if mu:
 | |
|         return x, w, pi
 | |
|     else:
 | |
|         return x, w
 | |
| 
 | |
| 
 | |
| def chebyt(n, monic=False):
 | |
|     r"""Chebyshev polynomial of the first kind.
 | |
| 
 | |
|     Defined to be the solution of
 | |
| 
 | |
|     .. math::
 | |
|         (1 - x^2)\frac{d^2}{dx^2}T_n - x\frac{d}{dx}T_n + n^2T_n = 0;
 | |
| 
 | |
|     :math:`T_n` is a polynomial of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     T : orthopoly1d
 | |
|         Chebyshev polynomial of the first kind.
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     chebyu : Chebyshev polynomial of the second kind.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`T_n` are orthogonal over :math:`[-1, 1]`
 | |
|     with weight function :math:`(1 - x^2)^{-1/2}`.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     Chebyshev polynomials of the first kind of order :math:`n` can
 | |
|     be obtained as the determinant of specific :math:`n \times n`
 | |
|     matrices. As an example we can check how the points obtained from
 | |
|     the determinant of the following :math:`3 \times 3` matrix
 | |
|     lay exactly on :math:`T_3`:
 | |
| 
 | |
|     >>> import numpy as np
 | |
|     >>> import matplotlib.pyplot as plt
 | |
|     >>> from scipy.linalg import det
 | |
|     >>> from scipy.special import chebyt
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> fig, ax = plt.subplots()
 | |
|     >>> ax.set_ylim(-2.0, 2.0)
 | |
|     >>> ax.set_title(r'Chebyshev polynomial $T_3$')
 | |
|     >>> ax.plot(x, chebyt(3)(x), label=rf'$T_3$')
 | |
|     >>> for p in np.arange(-1.0, 1.0, 0.1):
 | |
|     ...     ax.plot(p,
 | |
|     ...             det(np.array([[p, 1, 0], [1, 2*p, 1], [0, 1, 2*p]])),
 | |
|     ...             'rx')
 | |
|     >>> plt.legend(loc='best')
 | |
|     >>> plt.show()
 | |
| 
 | |
|     They are also related to the Jacobi Polynomials
 | |
|     :math:`P_n^{(-0.5, -0.5)}` through the relation:
 | |
| 
 | |
|     .. math::
 | |
|         P_n^{(-0.5, -0.5)}(x) = \frac{1}{4^n} \binom{2n}{n} T_n(x)
 | |
| 
 | |
|     Let's verify it for :math:`n = 3`:
 | |
| 
 | |
|     >>> from scipy.special import binom
 | |
|     >>> from scipy.special import jacobi
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> np.allclose(jacobi(3, -0.5, -0.5)(x),
 | |
|     ...             1/64 * binom(6, 3) * chebyt(3)(x))
 | |
|     True
 | |
| 
 | |
|     We can plot the Chebyshev polynomials :math:`T_n` for some values
 | |
|     of :math:`n`:
 | |
| 
 | |
|     >>> x = np.arange(-1.5, 1.5, 0.01)
 | |
|     >>> fig, ax = plt.subplots()
 | |
|     >>> ax.set_ylim(-4.0, 4.0)
 | |
|     >>> ax.set_title(r'Chebyshev polynomials $T_n$')
 | |
|     >>> for n in np.arange(2,5):
 | |
|     ...     ax.plot(x, chebyt(n)(x), label=rf'$T_n={n}$')
 | |
|     >>> plt.legend(loc='best')
 | |
|     >>> plt.show()
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     def wfunc(x):
 | |
|         return 1.0 / sqrt(1 - x * x)
 | |
|     if n == 0:
 | |
|         return orthopoly1d([], [], pi, 1.0, wfunc, (-1, 1), monic,
 | |
|                            lambda x: _ufuncs.eval_chebyt(n, x))
 | |
|     n1 = n
 | |
|     x, w, mu = roots_chebyt(n1, mu=True)
 | |
|     hn = pi / 2
 | |
|     kn = 2**(n - 1)
 | |
|     p = orthopoly1d(x, w, hn, kn, wfunc, (-1, 1), monic,
 | |
|                     lambda x: _ufuncs.eval_chebyt(n, x))
 | |
|     return p
 | |
| 
 | |
| # Chebyshev of the second kind
 | |
| #    U_n(x) = (n+1)! sqrt(pi) / (2*_gam(n+3./2)) * P^(1/2,1/2)_n(x)
 | |
| 
 | |
| 
 | |
| def roots_chebyu(n, mu=False):
 | |
|     r"""Gauss-Chebyshev (second kind) quadrature.
 | |
| 
 | |
|     Computes the sample points and weights for Gauss-Chebyshev
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Chebyshev polynomial of the second kind, :math:`U_n(x)`. These
 | |
|     sample points and weights correctly integrate polynomials of
 | |
|     degree :math:`2n - 1` or less over the interval :math:`[-1, 1]`
 | |
|     with weight function :math:`w(x) = \sqrt{1 - x^2}`. See 22.2.5 in
 | |
|     [AS]_ for details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     m = int(n)
 | |
|     if n < 1 or n != m:
 | |
|         raise ValueError('n must be a positive integer.')
 | |
|     t = np.arange(m, 0, -1) * pi / (m + 1)
 | |
|     x = np.cos(t)
 | |
|     w = pi * np.sin(t)**2 / (m + 1)
 | |
|     if mu:
 | |
|         return x, w, pi / 2
 | |
|     else:
 | |
|         return x, w
 | |
| 
 | |
| 
 | |
| def chebyu(n, monic=False):
 | |
|     r"""Chebyshev polynomial of the second kind.
 | |
| 
 | |
|     Defined to be the solution of
 | |
| 
 | |
|     .. math::
 | |
|         (1 - x^2)\frac{d^2}{dx^2}U_n - 3x\frac{d}{dx}U_n
 | |
|           + n(n + 2)U_n = 0;
 | |
| 
 | |
|     :math:`U_n` is a polynomial of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     U : orthopoly1d
 | |
|         Chebyshev polynomial of the second kind.
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     chebyt : Chebyshev polynomial of the first kind.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`U_n` are orthogonal over :math:`[-1, 1]`
 | |
|     with weight function :math:`(1 - x^2)^{1/2}`.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     Chebyshev polynomials of the second kind of order :math:`n` can
 | |
|     be obtained as the determinant of specific :math:`n \times n`
 | |
|     matrices. As an example we can check how the points obtained from
 | |
|     the determinant of the following :math:`3 \times 3` matrix
 | |
|     lay exactly on :math:`U_3`:
 | |
| 
 | |
|     >>> import numpy as np
 | |
|     >>> import matplotlib.pyplot as plt
 | |
|     >>> from scipy.linalg import det
 | |
|     >>> from scipy.special import chebyu
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> fig, ax = plt.subplots()
 | |
|     >>> ax.set_ylim(-2.0, 2.0)
 | |
|     >>> ax.set_title(r'Chebyshev polynomial $U_3$')
 | |
|     >>> ax.plot(x, chebyu(3)(x), label=rf'$U_3$')
 | |
|     >>> for p in np.arange(-1.0, 1.0, 0.1):
 | |
|     ...     ax.plot(p,
 | |
|     ...             det(np.array([[2*p, 1, 0], [1, 2*p, 1], [0, 1, 2*p]])),
 | |
|     ...             'rx')
 | |
|     >>> plt.legend(loc='best')
 | |
|     >>> plt.show()
 | |
| 
 | |
|     They satisfy the recurrence relation:
 | |
| 
 | |
|     .. math::
 | |
|         U_{2n-1}(x) = 2 T_n(x)U_{n-1}(x)
 | |
| 
 | |
|     where the :math:`T_n` are the Chebyshev polynomial of the first kind.
 | |
|     Let's verify it for :math:`n = 2`:
 | |
| 
 | |
|     >>> from scipy.special import chebyt
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> np.allclose(chebyu(3)(x), 2 * chebyt(2)(x) * chebyu(1)(x))
 | |
|     True
 | |
| 
 | |
|     We can plot the Chebyshev polynomials :math:`U_n` for some values
 | |
|     of :math:`n`:
 | |
| 
 | |
|     >>> x = np.arange(-1.0, 1.0, 0.01)
 | |
|     >>> fig, ax = plt.subplots()
 | |
|     >>> ax.set_ylim(-1.5, 1.5)
 | |
|     >>> ax.set_title(r'Chebyshev polynomials $U_n$')
 | |
|     >>> for n in np.arange(1,5):
 | |
|     ...     ax.plot(x, chebyu(n)(x), label=rf'$U_n={n}$')
 | |
|     >>> plt.legend(loc='best')
 | |
|     >>> plt.show()
 | |
| 
 | |
|     """
 | |
|     base = jacobi(n, 0.5, 0.5, monic=monic)
 | |
|     if monic:
 | |
|         return base
 | |
|     factor = sqrt(pi) / 2.0 * _gam(n + 2) / _gam(n + 1.5)
 | |
|     base._scale(factor)
 | |
|     return base
 | |
| 
 | |
| # Chebyshev of the first kind        C_n(x)
 | |
| 
 | |
| 
 | |
| def roots_chebyc(n, mu=False):
 | |
|     r"""Gauss-Chebyshev (first kind) quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Chebyshev
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Chebyshev polynomial of the first kind, :math:`C_n(x)`. These
 | |
|     sample points and weights correctly integrate polynomials of
 | |
|     degree :math:`2n - 1` or less over the interval :math:`[-2, 2]`
 | |
|     with weight function :math:`w(x) = 1 / \sqrt{1 - (x/2)^2}`. See
 | |
|     22.2.6 in [AS]_ for more details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     x, w, m = roots_chebyt(n, True)
 | |
|     x *= 2
 | |
|     w *= 2
 | |
|     m *= 2
 | |
|     if mu:
 | |
|         return x, w, m
 | |
|     else:
 | |
|         return x, w
 | |
| 
 | |
| 
 | |
| def chebyc(n, monic=False):
 | |
|     r"""Chebyshev polynomial of the first kind on :math:`[-2, 2]`.
 | |
| 
 | |
|     Defined as :math:`C_n(x) = 2T_n(x/2)`, where :math:`T_n` is the
 | |
|     nth Chebychev polynomial of the first kind.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     C : orthopoly1d
 | |
|         Chebyshev polynomial of the first kind on :math:`[-2, 2]`.
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     chebyt : Chebyshev polynomial of the first kind.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`C_n(x)` are orthogonal over :math:`[-2, 2]`
 | |
|     with weight function :math:`1/\sqrt{1 - (x/2)^2}`.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [1] Abramowitz and Stegun, "Handbook of Mathematical Functions"
 | |
|            Section 22. National Bureau of Standards, 1972.
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     if n == 0:
 | |
|         n1 = n + 1
 | |
|     else:
 | |
|         n1 = n
 | |
|     x, w = roots_chebyc(n1)
 | |
|     if n == 0:
 | |
|         x, w = [], []
 | |
|     hn = 4 * pi * ((n == 0) + 1)
 | |
|     kn = 1.0
 | |
|     p = orthopoly1d(x, w, hn, kn,
 | |
|                     wfunc=lambda x: 1.0 / sqrt(1 - x * x / 4.0),
 | |
|                     limits=(-2, 2), monic=monic)
 | |
|     if not monic:
 | |
|         p._scale(2.0 / p(2))
 | |
|         p.__dict__['_eval_func'] = lambda x: _ufuncs.eval_chebyc(n, x)
 | |
|     return p
 | |
| 
 | |
| # Chebyshev of the second kind       S_n(x)
 | |
| 
 | |
| 
 | |
| def roots_chebys(n, mu=False):
 | |
|     r"""Gauss-Chebyshev (second kind) quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Chebyshev
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     Chebyshev polynomial of the second kind, :math:`S_n(x)`. These
 | |
|     sample points and weights correctly integrate polynomials of
 | |
|     degree :math:`2n - 1` or less over the interval :math:`[-2, 2]`
 | |
|     with weight function :math:`w(x) = \sqrt{1 - (x/2)^2}`. See 22.2.7
 | |
|     in [AS]_ for more details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     x, w, m = roots_chebyu(n, True)
 | |
|     x *= 2
 | |
|     w *= 2
 | |
|     m *= 2
 | |
|     if mu:
 | |
|         return x, w, m
 | |
|     else:
 | |
|         return x, w
 | |
| 
 | |
| 
 | |
| def chebys(n, monic=False):
 | |
|     r"""Chebyshev polynomial of the second kind on :math:`[-2, 2]`.
 | |
| 
 | |
|     Defined as :math:`S_n(x) = U_n(x/2)` where :math:`U_n` is the
 | |
|     nth Chebychev polynomial of the second kind.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     S : orthopoly1d
 | |
|         Chebyshev polynomial of the second kind on :math:`[-2, 2]`.
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     chebyu : Chebyshev polynomial of the second kind
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`S_n(x)` are orthogonal over :math:`[-2, 2]`
 | |
|     with weight function :math:`\sqrt{1 - (x/2)}^2`.
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [1] Abramowitz and Stegun, "Handbook of Mathematical Functions"
 | |
|            Section 22. National Bureau of Standards, 1972.
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     if n == 0:
 | |
|         n1 = n + 1
 | |
|     else:
 | |
|         n1 = n
 | |
|     x, w = roots_chebys(n1)
 | |
|     if n == 0:
 | |
|         x, w = [], []
 | |
|     hn = pi
 | |
|     kn = 1.0
 | |
|     p = orthopoly1d(x, w, hn, kn,
 | |
|                     wfunc=lambda x: sqrt(1 - x * x / 4.0),
 | |
|                     limits=(-2, 2), monic=monic)
 | |
|     if not monic:
 | |
|         factor = (n + 1.0) / p(2)
 | |
|         p._scale(factor)
 | |
|         p.__dict__['_eval_func'] = lambda x: _ufuncs.eval_chebys(n, x)
 | |
|     return p
 | |
| 
 | |
| # Shifted Chebyshev of the first kind     T^*_n(x)
 | |
| 
 | |
| 
 | |
| def roots_sh_chebyt(n, mu=False):
 | |
|     r"""Gauss-Chebyshev (first kind, shifted) quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Chebyshev
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     shifted Chebyshev polynomial of the first kind, :math:`T_n(x)`.
 | |
|     These sample points and weights correctly integrate polynomials of
 | |
|     degree :math:`2n - 1` or less over the interval :math:`[0, 1]`
 | |
|     with weight function :math:`w(x) = 1/\sqrt{x - x^2}`. See 22.2.8
 | |
|     in [AS]_ for more details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     xw = roots_chebyt(n, mu)
 | |
|     return ((xw[0] + 1) / 2,) + xw[1:]
 | |
| 
 | |
| 
 | |
| def sh_chebyt(n, monic=False):
 | |
|     r"""Shifted Chebyshev polynomial of the first kind.
 | |
| 
 | |
|     Defined as :math:`T^*_n(x) = T_n(2x - 1)` for :math:`T_n` the nth
 | |
|     Chebyshev polynomial of the first kind.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     T : orthopoly1d
 | |
|         Shifted Chebyshev polynomial of the first kind.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`T^*_n` are orthogonal over :math:`[0, 1]`
 | |
|     with weight function :math:`(x - x^2)^{-1/2}`.
 | |
| 
 | |
|     """
 | |
|     base = sh_jacobi(n, 0.0, 0.5, monic=monic)
 | |
|     if monic:
 | |
|         return base
 | |
|     if n > 0:
 | |
|         factor = 4**n / 2.0
 | |
|     else:
 | |
|         factor = 1.0
 | |
|     base._scale(factor)
 | |
|     return base
 | |
| 
 | |
| 
 | |
| # Shifted Chebyshev of the second kind    U^*_n(x)
 | |
| def roots_sh_chebyu(n, mu=False):
 | |
|     r"""Gauss-Chebyshev (second kind, shifted) quadrature.
 | |
| 
 | |
|     Computes the sample points and weights for Gauss-Chebyshev
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     shifted Chebyshev polynomial of the second kind, :math:`U_n(x)`.
 | |
|     These sample points and weights correctly integrate polynomials of
 | |
|     degree :math:`2n - 1` or less over the interval :math:`[0, 1]`
 | |
|     with weight function :math:`w(x) = \sqrt{x - x^2}`. See 22.2.9 in
 | |
|     [AS]_ for more details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     x, w, m = roots_chebyu(n, True)
 | |
|     x = (x + 1) / 2
 | |
|     m_us = _ufuncs.beta(1.5, 1.5)
 | |
|     w *= m_us / m
 | |
|     if mu:
 | |
|         return x, w, m_us
 | |
|     else:
 | |
|         return x, w
 | |
| 
 | |
| 
 | |
| def sh_chebyu(n, monic=False):
 | |
|     r"""Shifted Chebyshev polynomial of the second kind.
 | |
| 
 | |
|     Defined as :math:`U^*_n(x) = U_n(2x - 1)` for :math:`U_n` the nth
 | |
|     Chebyshev polynomial of the second kind.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     U : orthopoly1d
 | |
|         Shifted Chebyshev polynomial of the second kind.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`U^*_n` are orthogonal over :math:`[0, 1]`
 | |
|     with weight function :math:`(x - x^2)^{1/2}`.
 | |
| 
 | |
|     """
 | |
|     base = sh_jacobi(n, 2.0, 1.5, monic=monic)
 | |
|     if monic:
 | |
|         return base
 | |
|     factor = 4**n
 | |
|     base._scale(factor)
 | |
|     return base
 | |
| 
 | |
| # Legendre
 | |
| 
 | |
| 
 | |
| def roots_legendre(n, mu=False):
 | |
|     r"""Gauss-Legendre quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Legendre
 | |
|     quadrature [GL]_. The sample points are the roots of the nth degree
 | |
|     Legendre polynomial :math:`P_n(x)`. These sample points and
 | |
|     weights correctly integrate polynomials of degree :math:`2n - 1`
 | |
|     or less over the interval :math:`[-1, 1]` with weight function
 | |
|     :math:`w(x) = 1`. See 2.2.10 in [AS]_ for more details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
|     numpy.polynomial.legendre.leggauss
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
|     .. [GL] Gauss-Legendre quadrature, Wikipedia,
 | |
|         https://en.wikipedia.org/wiki/Gauss%E2%80%93Legendre_quadrature
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     >>> import numpy as np
 | |
|     >>> from scipy.special import roots_legendre, eval_legendre
 | |
|     >>> roots, weights = roots_legendre(9)
 | |
| 
 | |
|     ``roots`` holds the roots, and ``weights`` holds the weights for
 | |
|     Gauss-Legendre quadrature.
 | |
| 
 | |
|     >>> roots
 | |
|     array([-0.96816024, -0.83603111, -0.61337143, -0.32425342,  0.        ,
 | |
|             0.32425342,  0.61337143,  0.83603111,  0.96816024])
 | |
|     >>> weights
 | |
|     array([0.08127439, 0.18064816, 0.2606107 , 0.31234708, 0.33023936,
 | |
|            0.31234708, 0.2606107 , 0.18064816, 0.08127439])
 | |
| 
 | |
|     Verify that we have the roots by evaluating the degree 9 Legendre
 | |
|     polynomial at ``roots``.  All the values are approximately zero:
 | |
| 
 | |
|     >>> eval_legendre(9, roots)
 | |
|     array([-8.88178420e-16, -2.22044605e-16,  1.11022302e-16,  1.11022302e-16,
 | |
|             0.00000000e+00, -5.55111512e-17, -1.94289029e-16,  1.38777878e-16,
 | |
|            -8.32667268e-17])
 | |
| 
 | |
|     Here we'll show how the above values can be used to estimate the
 | |
|     integral from 1 to 2 of f(t) = t + 1/t with Gauss-Legendre
 | |
|     quadrature [GL]_.  First define the function and the integration
 | |
|     limits.
 | |
| 
 | |
|     >>> def f(t):
 | |
|     ...    return t + 1/t
 | |
|     ...
 | |
|     >>> a = 1
 | |
|     >>> b = 2
 | |
| 
 | |
|     We'll use ``integral(f(t), t=a, t=b)`` to denote the definite integral
 | |
|     of f from t=a to t=b.  The sample points in ``roots`` are from the
 | |
|     interval [-1, 1], so we'll rewrite the integral with the simple change
 | |
|     of variable::
 | |
| 
 | |
|         x = 2/(b - a) * t - (a + b)/(b - a)
 | |
| 
 | |
|     with inverse::
 | |
| 
 | |
|         t = (b - a)/2 * x + (a + b)/2
 | |
| 
 | |
|     Then::
 | |
| 
 | |
|         integral(f(t), a, b) =
 | |
|             (b - a)/2 * integral(f((b-a)/2*x + (a+b)/2), x=-1, x=1)
 | |
| 
 | |
|     We can approximate the latter integral with the values returned
 | |
|     by `roots_legendre`.
 | |
| 
 | |
|     Map the roots computed above from [-1, 1] to [a, b].
 | |
| 
 | |
|     >>> t = (b - a)/2 * roots + (a + b)/2
 | |
| 
 | |
|     Approximate the integral as the weighted sum of the function values.
 | |
| 
 | |
|     >>> (b - a)/2 * f(t).dot(weights)
 | |
|     2.1931471805599276
 | |
| 
 | |
|     Compare that to the exact result, which is 3/2 + log(2):
 | |
| 
 | |
|     >>> 1.5 + np.log(2)
 | |
|     2.1931471805599454
 | |
| 
 | |
|     """
 | |
|     m = int(n)
 | |
|     if n < 1 or n != m:
 | |
|         raise ValueError("n must be a positive integer.")
 | |
| 
 | |
|     mu0 = 2.0
 | |
|     def an_func(k):
 | |
|         return 0.0 * k
 | |
|     def bn_func(k):
 | |
|         return k * np.sqrt(1.0 / (4 * k * k - 1))
 | |
|     f = _ufuncs.eval_legendre
 | |
|     def df(n, x):
 | |
|         return (-n * x * _ufuncs.eval_legendre(n, x)
 | |
|                 + n * _ufuncs.eval_legendre(n - 1, x)) / (1 - x ** 2)
 | |
|     return _gen_roots_and_weights(m, mu0, an_func, bn_func, f, df, True, mu)
 | |
| 
 | |
| 
 | |
| def legendre(n, monic=False):
 | |
|     r"""Legendre polynomial.
 | |
| 
 | |
|     Defined to be the solution of
 | |
| 
 | |
|     .. math::
 | |
|         \frac{d}{dx}\left[(1 - x^2)\frac{d}{dx}P_n(x)\right]
 | |
|           + n(n + 1)P_n(x) = 0;
 | |
| 
 | |
|     :math:`P_n(x)` is a polynomial of degree :math:`n`.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     P : orthopoly1d
 | |
|         Legendre polynomial.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`P_n` are orthogonal over :math:`[-1, 1]`
 | |
|     with weight function 1.
 | |
| 
 | |
|     Examples
 | |
|     --------
 | |
|     Generate the 3rd-order Legendre polynomial 1/2*(5x^3 + 0x^2 - 3x + 0):
 | |
| 
 | |
|     >>> from scipy.special import legendre
 | |
|     >>> legendre(3)
 | |
|     poly1d([ 2.5,  0. , -1.5,  0. ])
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     if n == 0:
 | |
|         n1 = n + 1
 | |
|     else:
 | |
|         n1 = n
 | |
|     x, w = roots_legendre(n1)
 | |
|     if n == 0:
 | |
|         x, w = [], []
 | |
|     hn = 2.0 / (2 * n + 1)
 | |
|     kn = _gam(2 * n + 1) / _gam(n + 1)**2 / 2.0**n
 | |
|     p = orthopoly1d(x, w, hn, kn, wfunc=lambda x: 1.0, limits=(-1, 1),
 | |
|                     monic=monic,
 | |
|                     eval_func=lambda x: _ufuncs.eval_legendre(n, x))
 | |
|     return p
 | |
| 
 | |
| # Shifted Legendre              P^*_n(x)
 | |
| 
 | |
| 
 | |
| def roots_sh_legendre(n, mu=False):
 | |
|     r"""Gauss-Legendre (shifted) quadrature.
 | |
| 
 | |
|     Compute the sample points and weights for Gauss-Legendre
 | |
|     quadrature. The sample points are the roots of the nth degree
 | |
|     shifted Legendre polynomial :math:`P^*_n(x)`. These sample points
 | |
|     and weights correctly integrate polynomials of degree :math:`2n -
 | |
|     1` or less over the interval :math:`[0, 1]` with weight function
 | |
|     :math:`w(x) = 1.0`. See 2.2.11 in [AS]_ for details.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         quadrature order
 | |
|     mu : bool, optional
 | |
|         If True, return the sum of the weights, optional.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     x : ndarray
 | |
|         Sample points
 | |
|     w : ndarray
 | |
|         Weights
 | |
|     mu : float
 | |
|         Sum of the weights
 | |
| 
 | |
|     See Also
 | |
|     --------
 | |
|     scipy.integrate.fixed_quad
 | |
| 
 | |
|     References
 | |
|     ----------
 | |
|     .. [AS] Milton Abramowitz and Irene A. Stegun, eds.
 | |
|         Handbook of Mathematical Functions with Formulas,
 | |
|         Graphs, and Mathematical Tables. New York: Dover, 1972.
 | |
| 
 | |
|     """
 | |
|     x, w = roots_legendre(n)
 | |
|     x = (x + 1) / 2
 | |
|     w /= 2
 | |
|     if mu:
 | |
|         return x, w, 1.0
 | |
|     else:
 | |
|         return x, w
 | |
| 
 | |
| 
 | |
| def sh_legendre(n, monic=False):
 | |
|     r"""Shifted Legendre polynomial.
 | |
| 
 | |
|     Defined as :math:`P^*_n(x) = P_n(2x - 1)` for :math:`P_n` the nth
 | |
|     Legendre polynomial.
 | |
| 
 | |
|     Parameters
 | |
|     ----------
 | |
|     n : int
 | |
|         Degree of the polynomial.
 | |
|     monic : bool, optional
 | |
|         If `True`, scale the leading coefficient to be 1. Default is
 | |
|         `False`.
 | |
| 
 | |
|     Returns
 | |
|     -------
 | |
|     P : orthopoly1d
 | |
|         Shifted Legendre polynomial.
 | |
| 
 | |
|     Notes
 | |
|     -----
 | |
|     The polynomials :math:`P^*_n` are orthogonal over :math:`[0, 1]`
 | |
|     with weight function 1.
 | |
| 
 | |
|     """
 | |
|     if n < 0:
 | |
|         raise ValueError("n must be nonnegative.")
 | |
| 
 | |
|     def wfunc(x):
 | |
|         return 0.0 * x + 1.0
 | |
|     if n == 0:
 | |
|         return orthopoly1d([], [], 1.0, 1.0, wfunc, (0, 1), monic,
 | |
|                            lambda x: _ufuncs.eval_sh_legendre(n, x))
 | |
|     x, w = roots_sh_legendre(n)
 | |
|     hn = 1.0 / (2 * n + 1.0)
 | |
|     kn = _gam(2 * n + 1) / _gam(n + 1)**2
 | |
|     p = orthopoly1d(x, w, hn, kn, wfunc, limits=(0, 1), monic=monic,
 | |
|                     eval_func=lambda x: _ufuncs.eval_sh_legendre(n, x))
 | |
|     return p
 | |
| 
 | |
| 
 | |
| # Make the old root function names an alias for the new ones
 | |
| _modattrs = globals()
 | |
| for newfun, oldfun in _rootfuns_map.items():
 | |
|     _modattrs[oldfun] = _modattrs[newfun]
 | |
|     __all__.append(oldfun)
 |