Thanks to visit codestin.com
Credit goes to gist.github.com

Skip to content

Instantly share code, notes, and snippets.

@ewmoore
Last active August 29, 2015 14:06
Show Gist options
  • Select an option

  • Save ewmoore/0bf21509ac4c8195ba5e to your computer and use it in GitHub Desktop.

Select an option

Save ewmoore/0bf21509ac4c8195ba5e to your computer and use it in GitHub Desktop.

Revisions

  1. ewmoore revised this gist Sep 12, 2014. 1 changed file with 72 additions and 6 deletions.
    78 changes: 72 additions & 6 deletions hroots_test.py
    Original file line number Diff line number Diff line change
    @@ -90,6 +90,7 @@ def h_roots(n, mu=False):
    df = lambda n, x: 2.0 * n * cephes.eval_hermite(n-1, x)
    return _gen_roots_and_weights(m, mu0, an_func, bn_func, f, df, True, mu)

    # from scipy PR 3987, essentially.
    def h_roots_new(n):
    bn = np.sqrt(np.arange(1, n, dtype='d') / 2.0)
    c = np.zeros((2, n), 'd', 'F')
    @@ -106,6 +107,71 @@ def h_roots_new(n):

    return x, w

    def h_roots_pbdv(n):
    bn = np.sqrt(np.arange(1, n, dtype='d') / 2.0)
    c = np.zeros((2, n), 'd', 'F')
    c[0,1:] = bn

    x = linalg.eigvals_banded(c, overwrite_a_band=True)

    # improve roots by one application of Newton's method

    y, dy = cephes.pbdv(n, np.sqrt(2)*x)
    x -= y/dy

    fm = np.exp(x**2/2)*cephes.pbdv(n-1, np.sqrt(2)*x)[0]
    fm /= np.abs(fm).max()
    w = 1.0 / fm / fm

    w = (w + w[::-1]) / 2
    x = (x - x[::-1]) / 2

    mu0 = np.sqrt(np.pi)
    w *= mu0 / w.sum()

    return x, w

    def hfunc(n, x):
    y0 = np.exp(-x**2/2) / np.sqrt(np.sqrt(np.pi))
    y1 = 2*x * np.exp(-x**2/2) / np.sqrt(2*np.sqrt(np.pi))

    if n < 0:
    return 0.0
    elif n == 0:
    return y0
    elif n == 1:
    return y1
    else:
    for k in range(2,n+1):
    y2 = np.sqrt(2.0/k)*x*y1 - np.sqrt((k-1.0)/k)*y0
    y0 = y1
    y1 = y2
    return y2

    def h_roots_hf(n):
    bn = np.sqrt(np.arange(1, n, dtype='d') / 2.0)
    c = np.zeros((2, n), 'd', 'F')
    c[0,1:] = bn

    x = linalg.eigvals_banded(c, overwrite_a_band=True)

    # improve roots by one application of Newton's method

    y = hfunc(n, x)
    dy = -x*y + np.sqrt(2*n)*hfunc(n-1,x)
    x -= y/dy

    fm = np.exp(x**2/2) * hfunc(n-1, x)
    fm /= np.abs(fm).max()
    w = 1.0 / fm / fm

    w = (w + w[::-1]) / 2
    x = (x - x[::-1]) / 2

    mu0 = np.sqrt(np.pi)
    w *= mu0 / w.sum()

    return x, w

    def h_roots_mpmath(n, prec=False):
    # doing it all here is unnecessary and slow...
    @@ -193,11 +259,11 @@ def find_max_n():
    mp.dps = dps0
    return n-1

    mp.dps = 400
    xs, ws = h_roots(205)
    xn, wn = h_roots_new(205)
    xm, wm = h_roots_mpmath(205)
    xmd = xm.astype('d')
    wmd = wm.astype('d')
    #mp.dps = 400
    #xs, ws = h_roots(205)
    #xn, wn = h_roots_new(205)
    #xm, wm = h_roots_mpmath(205)
    #xmd = xm.astype('d')
    #wmd = wm.astype('d')


  2. ewmoore created this gist Sep 11, 2014.
    203 changes: 203 additions & 0 deletions hroots_test.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,203 @@
    import numpy as np
    import scipy.special as cephes
    import scipy.linalg as linalg
    from mpmath import mp

    # current scipy routine
    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 = f(n-1, x)
    fm /= np.abs(fm).max()
    dy /= np.abs(dy).max()
    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


    # current scipy routine
    def h_roots(n, mu=False):
    """Gauss-Hermite (physicst's) quadrature
    Computes the sample points and weights for Gauss-Hermite quadrature.
    The sample points are the roots of the `n`th degree Hermite polynomial,
    :math:`H_n(x)`. These sample points and weights correctly integrate
    polynomials of degree :math:`2*n - 1` or less over the interval
    :math:`[-inf, inf]` with weight function :math:`f(x) = e^{-x^2}`.
    Parameters
    ----------
    n : int
    quadrature order
    mu : boolean
    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
    --------
    integrate.quadrature
    integrate.fixed_quad
    numpy.polynomial.hermite.hermgauss
    """
    m = int(n)
    if n < 1 or n != m:
    raise ValueError("n must be a positive integer.")

    mu0 = np.sqrt(np.pi)
    an_func = lambda k: 0.0*k
    bn_func = lambda k: np.sqrt(k/2.0)
    f = cephes.eval_hermite
    df = lambda n, x: 2.0 * n * cephes.eval_hermite(n-1, x)
    return _gen_roots_and_weights(m, mu0, an_func, bn_func, f, df, True, mu)

    def h_roots_new(n):
    bn = np.sqrt(np.arange(1, n, dtype='d') / 2.0)
    c = np.zeros((2, n), 'd', 'F')
    c[0,1:] = bn

    x,v = linalg.eig_banded(c, overwrite_a_band=True)
    w = v[0,:]**2

    w = (w + w[::-1]) / 2
    x = (x - x[::-1]) / 2

    mu0 = np.sqrt(np.pi)
    w *= mu0 / w.sum()

    return x, w


    def h_roots_mpmath(n, prec=False):
    # doing it all here is unnecessary and slow...
    # bn = np.arange(1,n)
    # c = np.diag(bn, -1) + np.diag(bn, +1)
    # c = mp.matrix(c)
    #
    # # mpmath isn't vectorized..
    # for k in range(n-1):
    # c[k,k+1] = mp.sqrt(c[k,k+1]/2)
    # c[k+1,k] = mp.sqrt(c[k+1,k]/2)
    #
    # xm, vm = mp.eigh(c)
    #
    # mu = mp.sqrt(mp.pi())
    # x = []
    # w = []
    # for k in range(n):
    # print k
    # x.append(xm[k])
    # w.append(vm[0,k]**2 * mu)
    bn = np.sqrt(np.arange(1, n, dtype='d') / 2.0)
    c = np.zeros((2, n), 'd', 'F')
    c[0,1:] = bn

    x0 = linalg.eigvals_banded(c, overwrite_a_band=True)

    # now use mpmath from here on.
    x = np.empty(x0.shape, object)
    for k in range(n):
    x[k] = mp.mpf(x0[k])

    f = np.vectorize(mp.hermite, 'O')
    df = np.vectorize(lambda n, x: mp.hermite(n-1, x) * 2 * n, 'O')

    k = 0
    xm1old = f(n, x[-1])
    #print xm1old
    xm1 = 2*xm1old
    #print xm1old != xm1
    while float(xm1) != 0.0 and xm1old != xm1 and k < 25:
    y = f(n, x)
    dy = df(n, x)
    x -= y/dy
    k += 1
    xm1old = xm1
    xm1 = y[-1]

    fm = f(n-1, x)
    fm /= np.abs(fm).max()
    dy /= np.abs(dy).max()
    w = 1.0 / (fm * dy)

    w = (w + w[::-1]) / 2
    x = (x - x[::-1]) / 2

    mu0 = mp.sqrt(mp.pi())
    w *= mu0 / w.sum()

    if prec:
    return x, w, xm1
    else:
    return x, w

    def find_max_n():
    """
    Determine the maximum order of gauss-hermite quadrature for which all of
    the weight are nonzero when represented as doubles.
    """
    # the first and last weight are the smallest.
    n = 350
    dps0 = mp.dps

    mp.dps = 400
    x, w, p = h_roots_mpmath(n, True)
    while w[-1] > np.finfo('d').tiny:
    n += 1
    x, w, p = h_roots_mpmath(n, True)
    print n, mp.dps, float(p), float(w[-1])
    while abs(p) > 1e-20:
    mp.dps += 25
    x, w, p = h_roots_mpmath(n, True)
    print n, mp.dps, float(p), float(w[-1])

    mp.dps = dps0
    return n-1

    mp.dps = 400
    xs, ws = h_roots(205)
    xn, wn = h_roots_new(205)
    xm, wm = h_roots_mpmath(205)
    xmd = xm.astype('d')
    wmd = wm.astype('d')