예제 #1
0
def jacobi_der(n, alpha, beta, x):
    """First derivative of Pn with respect to x, at points x.

    Parameters
    ----------
    n : int
        polynomial order
    alpha : float
        first weight parameter
    beta : float
        second weight parameter
    x : numpy.ndarray
        x coordinates to evaluate at

    Returns
    -------
    numpy.ndarray
        jacobi polynomial evaluated at the given points

    """
    # see https://dlmf.nist.gov/18.9
    # dPn = (1/2) (n + a + b + 1)P_{n-1}^{a+1,b+1}
    # first two terms are specialized for speed
    if n == 0:
        return np.zeros_like(x)
    if n == 1:
        return np.ones_like(x) * (0.5 * (n + alpha + beta + 1))

    Pn = jacobi(n - 1, alpha + 1, beta + 1, x)
    coef = 0.5 * (n + alpha + beta + 1)
    return coef * Pn
예제 #2
0
def hermite_H_der_sequence(ns, x):
    """First derivative of He_[ns] with respect to x, at points x.

    Parameters
    ----------
    ns : iterable of int
        rising polynomial orders, assumed to be sorted
    x : numpy.ndarray
        point(s) to evaluate at.  Scalars and arrays both work.

    Returns
    -------
    generator of numpy.ndarray
        equivalent to array of shape (len(ns), len(x))

    """
    # this function includes all the optimizations in the hermite_He func,
    # but excludes the note comments.  Read that first if you're looking for
    # clarity

    # see also: prysm.polynomials.jacobi.jacobi_sequence for the meta machinery
    # in use here
    ns = list(ns)
    min_i = 0
    if ns[min_i] == 0:
        yield np.zeros_like(x)
        min_i += 1

    if min_i == len(ns):
        return

    if ns[min_i] == 1:
        yield 2 * np.ones_like(x)
        min_i += 1

    if min_i == len(ns):
        return

    x2 = 2 * x
    P1 = x2
    P2 = 4 * (x * x) - 2
    if ns[min_i] == 2:
        yield 4 * P1
        min_i += 1

    if min_i == len(ns):
        return

    Pnm2, Pnm1 = P1, P2
    max_n = ns[-1]
    for nn in range(3, max_n + 1):
        Pn = x2 * Pnm1 - (2 * (nn - 1)) * Pnm2
        if ns[min_i] == nn:
            yield 2 * nn * Pnm1
            min_i += 1

        Pnm2, Pnm1 = Pnm1, Pn
예제 #3
0
def hermite_He_sequence(ns, x):
    """Probabilist's Hermite polynomials He of orders ns at points x.

    Parameters
    ----------
    ns : iterable of int
        rising polynomial orders, assumed to be sorted
    x : numpy.ndarray
        point(s) to evaluate at.  Scalars and arrays both work.

    Returns
    -------
    generator of numpy.ndarray
        equivalent to array of shape (len(ns), len(x))

    """
    # this function includes all the optimizations in the hermite_He func,
    # but excludes the note comments.  Read that first if you're looking for
    # clarity

    # see also: prysm.polynomials.jacobi.jacobi_sequence for the meta machinery
    # in use here
    ns = list(ns)
    min_i = 0
    if ns[min_i] == 0:
        yield np.ones_like(x)
        min_i += 1

    if min_i == len(ns):
        return

    if ns[min_i] == 1:
        yield x
        min_i += 1

    if min_i == len(ns):
        return

    P1 = x
    P2 = x * x - 1
    if ns[min_i] == 2:
        yield P2
        min_i += 1

    if min_i == len(ns):
        return

    Pnm2, Pnm1 = P1, P2
    max_n = ns[-1]
    for nn in range(3, max_n + 1):
        Pn = x * Pnm1 - (nn - 1) * Pnm2
        Pnm2, Pnm1 = Pnm1, Pn
        if ns[min_i] == nn:
            yield Pn
            min_i += 1
예제 #4
0
def dickson1(n, alpha, x):
    """Dickson Polynomial of the first kind of order n.

    Parameters
    ----------
    n : int
        polynomial order
    alpha : float
        shape parameter
        if alpha = -1, the dickson polynomials are Fibonacci Polynomials
        if alpha = 0, the dickson polynomials are the monomials x^n
        if alpha = 1, the dickson polynomials and cheby1 polynomials are
        related by D_n(2x) = 2T_n(x)
    x : numpy.ndarray
        coordinates to evaluate the polynomial at

    Returns
    -------
    numpy.ndarray
        D_n(x)

    """
    if n == 0:
        return np.ones_like(x) * 2
    if n == 1:
        return x

    # general recursive polynomials:
    # P0, P1 are the n=0,1 seed terms
    # Pnm1 = P_{n-1}, Pnm2 = P_{n-2}
    P0 = np.ones_like(x) * 2
    P1 = x
    Pnm2 = P0
    Pnm1 = P1
    for _ in range(2, n + 1):
        Pn = x * Pnm1 - alpha * Pnm2
        Pnm1, Pnm2 = Pn, Pnm1

    return Pn
예제 #5
0
def hermite_He(n, x):
    """Probabilist's Hermite polynomial He of order n at points x.

    Parameters
    ----------
    n : int
        polynomial order
    x : numpy.ndarray
        point(s) to evaluate at.  Scalars and arrays both work.

    Returns
    -------
    numpy.ndarray
        He_n(x)

    """
    # note: A, B, C = 1, 0, n
    # avoid a "recurrence_abc_He" function call on each loop to do these inline,
    # and avoiding adding zero to an array (waste work)
    if n == 0:
        return np.ones_like(x)
    if n == 1:
        return x

    # if n not in (0,1), then n >= 2
    # standard three-term recurrence relation
    # Pn+1 = (An x + Bn) Pn - Cn Pn-1
    # notation here does Pn = (An-1 ...)
    # Pnm2 = Pn-2
    # Pnm1 = Pn-1
    # i.e., starting from P2 = (A1 x + B1) P1 - C1 P0
    #
    # given that, we optimize P2 significantly by seeing that:
    # P0 = 1
    # A, B, C = 1, 0, n
    # P2 = x P1 - 1
    # -> P2 == x^2 - 1
    P2 = x * x - 1
    if n == 2:
        return P2

    Pnm2 = x
    Pnm1 = P2
    for nn in range(3, n + 1):
        # it would look like this without optimization
        # Pn = (A * x + B) * Pnm1 - C * Pnm2
        # A, B, C = 1, 0, n
        Pn = x * Pnm1 - (nn - 1) * Pnm2
        Pnm2, Pnm1 = Pnm1, Pn

    return Pn
예제 #6
0
def hermite_H(n, x):
    """Physicist's Hermite polynomial H of order n at points x.

    Parameters
    ----------
    n : int
        polynomial order
    x : numpy.ndarray
        point(s) to evaluate at.  Scalars and arrays both work.

    Returns
    -------
    numpy.ndarray
        H_n(x)

    """
    if n == 0:
        return np.ones_like(x)
    if n == 1:
        return 2 * x

    # if n not in (0,1), then n >= 2
    # standard three-term recurrence relation
    # Pn+1 = (An x + Bn) Pn - Cn Pn-1
    # notation here does Pn = (An-1 ...)
    # Pnm2 = Pn-2
    # Pnm1 = Pn-1
    # i.e., starting from P2 = (A1 x + B1) P1 - C1 P0
    #
    # given that, we optimize P2 significantly by seeing that:
    # P0 = 1
    # A, B, C = 2, 0, 2n
    # P2 = x P1 - 1
    # -> P2 == 4^2 - 2

    # another optimization: Ax == 2x == P1, so we compute that just once
    x2 = 2 * x
    P2 = 4 * (x * x) - 2
    if n == 2:
        return P2

    Pnm2 = x2
    Pnm1 = P2
    for nn in range(3, n + 1):
        # it would look like this without optimization
        # Pn = (A * x + B) * Pnm1 - C * Pnm2
        # A, B, C = 2, 0, 2n
        Pn = x2 * Pnm1 - (2 * (nn - 1)) * Pnm2
        Pnm2, Pnm1 = Pnm1, Pn

    return Pn
예제 #7
0
def dickson2(n, alpha, x):
    """Dickson Polynomial of the second kind of order n.

    Parameters
    ----------
    n : int
        polynomial order
    alpha : float
        shape parameter
        if alpha = -1, the dickson polynomials are Lucas Polynomials
    x : numpy.ndarray
        coordinates to evaluate the polynomial at

    Returns
    -------
    numpy.ndarray
        E_n(x)

    """
    if n == 0:
        return np.ones_like(x)
    if n == 1:
        return x

    # general recursive polynomials:
    # P0, P1 are the n=0,1 seed terms
    # Pnm1 = P_{n-1}, Pnm2 = P_{n-2}
    P0 = np.ones_like(x)
    P1 = x
    Pnm2 = P0
    Pnm1 = P1
    for _ in range(2, n + 1):
        Pn = x * Pnm1 - alpha * Pnm2
        Pnm1, Pnm2 = Pn, Pnm1

    return Pn
예제 #8
0
def dickson1_sequence(ns, alpha, x):
    """Sequence of Dickson Polynomial of the first kind of orders ns.

    Parameters
    ----------
    ns : iterable of int
        rising polynomial orders, assumed to be sorted
    alpha : float
        shape parameter
        if alpha = -1, the dickson polynomials are Fibonacci Polynomials
        if alpha = 0, the dickson polynomials are the monomials x^n
        if alpha = 1, the dickson polynomials and cheby1 polynomials are
        related by D_n(2x) = 2T_n(x)
    x : numpy.ndarray
        coordinates to evaluate the polynomial at

    Returns
    -------
    generator of numpy.ndarray
        equivalent to array of shape (len(ns), len(x))

    """
    ns = list(ns)
    min_i = 0
    P0 = np.ones_like(x) * 2
    if ns[min_i] == 0:
        yield P0
        min_i += 1

    if min_i == len(ns):
        return

    P1 = x
    if ns[min_i] == 1:
        yield P1
        min_i += 1

    if min_i == len(ns):
        return

    Pnm2 = P0
    Pnm1 = P1
    for i in range(2, ns[-1] + 1):
        Pn = x * Pnm1 - alpha * Pnm2
        Pnm1, Pnm2 = Pn, Pnm1
        if ns[min_i] == i:
            yield Pn
            min_i += 1
예제 #9
0
def dickson2_sequence(ns, alpha, x):
    """Sequence of Dickson Polynomial of the second kind of orders ns.

    Parameters
    ----------
    ns : iterable of int
        rising polynomial orders, assumed to be sorted
    alpha : float
        shape parameter
        if alpha = -1, the dickson polynomials are Lucas Polynomials
    x : numpy.ndarray
        coordinates to evaluate the polynomial at

    Returns
    -------
    numpy.ndarray
        D_n(x)

    """
    ns = list(ns)
    min_i = 0
    P0 = np.ones_like(x)
    if ns[min_i] == 0:
        yield P0
        min_i += 1

    if min_i == len(ns):
        return

    P1 = x
    if ns[min_i] == 1:
        yield P1
        min_i += 1

    if min_i == len(ns):
        return

    Pnm2 = P0
    Pnm1 = P1
    for i in range(2, ns[-1] + 1):
        Pn = x * Pnm1 - alpha * Pnm2
        Pnm1, Pnm2 = Pn, Pnm1
        if ns[min_i] == i:
            yield Pn
            min_i += 1
예제 #10
0
def jacobi(n, alpha, beta, x):
    """Jacobi polynomial of order n with weight parameters alpha and beta.

    Parameters
    ----------
    n : int
        polynomial order
    alpha : float
        first weight parameter
    beta : float
        second weight parameter
    x : numpy.ndarray
        x coordinates to evaluate at

    Returns
    -------
    numpy.ndarray
        jacobi polynomial evaluated at the given points

    """
    if n == 0:
        return np.ones_like(x)
    elif n == 1:
        term1 = alpha + 1
        term2 = alpha + beta + 2
        term3 = (x - 1) / 2
        return term1 + term2 * term3

    Pnm1 = alpha + 1 + (alpha + beta + 2) * ((x - 1) / 2)
    A, B, C = recurrence_abc(1, alpha, beta)
    Pn = (A * x + B) * Pnm1 - C  # no C * Pnm2 =because Pnm2 = 1
    if n == 2:
        return Pn

    for i in range(3, n + 1):
        Pnm2, Pnm1 = Pnm1, Pn
        A, B, C = recurrence_abc(i - 1, alpha, beta)
        Pn = (A * x + B) * Pnm1 - C * Pnm2

    return Pn
예제 #11
0
def Qbfs(n, x):
    """Qbfs polynomial of order n at point(s) x.

    Parameters
    ----------
    n : int
        polynomial order
    x : numpy.array
        point(s) at which to evaluate

    Returns
    -------
    numpy.ndarray
        Qbfs_n(x)

    """
    # to compute the Qbfs polynomials, compute the auxiliary polynomial P_n
    # recursively.  Simultaneously use the recurrence relation for Q_n
    # to compute the intermediary Q polynomials.
    # for input x, transform r = x ^ 2
    # then compute P(r) and consequently Q(r)
    # and scale outputs by Qbfs = r*(1-r) * Q
    # the auxiliary polynomials are the jacobi polynomials with
    # alpha,beta = (-1/2,+1/2),
    # also known as the chebyshev polynomials of the third kind, V(x)

    # the first two Qbfs polynomials are
    # Q_bfs0 = x^2 - x^4
    # Q_bfs1 = 1/19^.5 * (13 - 16 * x^2) * (x^2 - x^4)
    rho = x**2
    # c_Q is the leading term used to convert Qm to Qbfs
    c_Q = rho * (1 - rho)
    if n == 0:
        return c_Q  # == x^2 - x^4

    if n == 1:
        return 1 / np.sqrt(19) * (13 - 16 * rho) * c_Q

    # c is the leading term of the recurrence relation for P
    c = 2 - 4 * rho
    # P0, P1 are the first two terms of the recurrence relation for auxiliary
    # polynomial P_n
    P0 = np.ones_like(x) * 2
    P1 = 6 - 8 * rho
    Pnm2 = P0
    Pnm1 = P1

    # Q0, Q1 are the first two terms of the recurrence relation for Qm
    Q0 = np.ones_like(x)
    Q1 = 1 / np.sqrt(19) * (13 - 16 * rho)
    Qnm2 = Q0
    Qnm1 = Q1
    for nn in range(2, n + 1):
        Pn = c * Pnm1 - Pnm2
        Pnm2 = Pnm1
        Pnm1 = Pn
        g = g_qbfs(nn - 1)
        h = h_qbfs(nn - 2)
        f = f_qbfs(nn)
        Qn = (Pn - g * Qnm1 - h * Qnm2) * (
            1 / f)  # small optimization; mul by 1/f instead of div by f
        Qnm2 = Qnm1
        Qnm1 = Qn

    # Qn is certainly defined (flake8 can't tell the previous ifs bound the loop
    # to always happen once)
    return Qn * c_Q  # NOQA
예제 #12
0
def Qbfs_sequence(ns, x):
    """Qbfs polynomials of orders ns at point(s) x.

    Parameters
    ----------
    ns : Iterable of int
        polynomial orders
    x : numpy.array
        point(s) at which to evaluate

    Returns
    -------
    generator of numpy.ndarray
        yielding one order of ns at a time

    """
    # see the leading comment of Qbfs for some explanation of this code
    # and prysm:jacobi.py#jacobi_sequence the "_sequence" portion

    ns = list(ns)
    min_i = 0

    rho = x**2
    # c_Q is the leading term used to convert Qm to Qbfs
    c_Q = rho * (1 - rho)
    if ns[min_i] == 0:
        yield np.ones_like(x) * c_Q
        min_i += 1

    if min_i == len(ns):
        return

    if ns[min_i] == 1:
        yield 1 / np.sqrt(19) * (13 - 16 * rho) * c_Q
        min_i += 1

    if min_i == len(ns):
        return

    # c is the leading term of the recurrence relation for P
    c = 2 - 4 * rho
    # P0, P1 are the first two terms of the recurrence relation for auxiliary
    # polynomial P_n
    P0 = np.ones_like(x) * 2
    P1 = 6 - 8 * rho
    Pnm2 = P0
    Pnm1 = P1

    # Q0, Q1 are the first two terms of the recurrence relation for Qbfs_n
    Q0 = np.ones_like(x)
    Q1 = 1 / np.sqrt(19) * (13 - 16 * rho)
    Qnm2 = Q0
    Qnm1 = Q1
    for nn in range(2, ns[-1] + 1):
        Pn = c * Pnm1 - Pnm2
        Pnm2 = Pnm1
        Pnm1 = Pn
        g = g_qbfs(nn - 1)
        h = h_qbfs(nn - 2)
        f = f_qbfs(nn)
        Qn = (Pn - g * Qnm1 - h * Qnm2) * (
            1 / f)  # small optimization; mul by 1/f instead of div by f
        Qnm2 = Qnm1
        Qnm1 = Qn
        if ns[min_i] == nn:
            yield Qn * c_Q
            min_i += 1

        if min_i == len(ns):
            return
예제 #13
0
def jacobi_sequence(ns, alpha, beta, x):
    """Jacobi polynomials of orders ns with weight parameters alpha and beta.

    Parameters
    ----------
    ns : iterable
        sorted polynomial orders to return, e.g. [1, 3, 5, 7, ...]
    alpha : float
        first weight parameter
    beta : float
        second weight parameter
    x : numpy.ndarray
        x coordinates to evaluate at

    Returns
    -------
    generator
        equivalent to array of shape (len(ns), len(x))

    """
    # three key flavors: return list, return array, or return generator
    # return generator has most pleasant interface, benchmarked at 68 ns
    # per yield (315 clocks).  With 32 clocks per element of x, 1% of the
    # time is spent on yield when x has 1000 elements, or 32x32
    # => use generator
    # benchmarked at 4.6 ns/element (256x256), 4.6GHz CPU = 21 clocks
    # ~4x faster than previous impl (118 ms => 29.8)
    ns = list(ns)
    min_i = 0
    Pn = np.ones_like(x)
    if ns[min_i] == 0:
        yield Pn
        min_i += 1

    if min_i == len(ns):
        return

    Pn = alpha + 1 + (alpha + beta + 2) * ((x - 1) / 2)
    if ns[min_i] == 1:
        yield Pn
        min_i += 1

    if min_i == len(ns):
        return

    Pnm1 = Pn
    A, B, C = recurrence_abc(1, alpha, beta)
    Pn = (A * x + B) * Pnm1 - C  # no C * Pnm2 =because Pnm2 = 1
    if ns[min_i] == 2:
        yield Pn
        min_i += 1

    if min_i == len(ns):
        return

    max_n = ns[-1]
    for i in range(3, max_n + 1):
        Pnm2, Pnm1 = Pnm1, Pn
        A, B, C = recurrence_abc(i - 1, alpha, beta)
        Pn = (A * x + B) * Pnm1 - C * Pnm2
        if ns[min_i] == i:
            yield Pn
            min_i += 1
예제 #14
0
def jacobi_der_sequence(ns, alpha, beta, x):
    """First partial derivative of Pn w.r.t. x for order ns, i.e. P_n'.

    Parameters
    ----------
    ns : iterable
        sorted orders to return, e.g. [1, 2, 3, 10] returns P1', P2', P3', P10'
    alpha : float
        first weight parameter
    beta : float
        second weight parameter
    x : numpy.ndarray
        x coordinates to evaluate at

    Returns
    -------
    generator
        equivalent to array of shape (len(ns), len(x))

    """
    # the body of this function is very similar to that of jacobi_sequence,
    # except note that der is related to jacobi n-1,
    # and the actual jacobi polynomial has a different alpha and beta

    # special note: P0 is invariant of alpha, beta
    # and within this function alphap1 and betap1 are "a+1" and "b+1"
    alphap1 = alpha + 1
    betap1 = beta + 1
    # except when it comes time to yield terms, we yield the modification
    # per A&S / the NIST link
    # and we modify the arguments to
    ns = list(ns)
    min_i = 0
    if ns[min_i] == 0:
        # n=0 is piston, der==0
        yield np.zeros_like(x)
        min_i += 1

    if min_i == len(ns):
        return

    if ns[min_i] == 1:
        yield np.ones_like(x) * (0.5 * (1 + alpha + beta + 1))
        min_i += 1

    if min_i == len(ns):
        return

    # min_n is at least two, which means min n-1 is 1
    # from here below, Pn is P of order i to keep the reader sane, but Pnm1
    # is all that is needed;
    # therefor, Pn is computed only after testing if we are done and can return
    # to avoid a waste computation at the end of the loop
    # note that we can hardcode / unroll the loop up to n=3, one further than
    # in jacobi, because we use Pnm1
    P1 = alphap1 + 1 + (alphap1 + betap1 + 2) * ((x - 1) / 2)
    if ns[min_i] == 2:
        yield P1 * (0.5 * (2 + alpha + beta + 1))
        min_i += 1

    if min_i == len(ns):
        return

    A, B, C = recurrence_abc(1, alphap1, betap1)
    P2 = (A * x + B) * P1 - C  # no C * Pnm2 =because Pnm2 = 1
    if ns[min_i] == 3:
        yield P2 * (0.5 * (3 + alpha + beta + 1))
        min_i += 1

    if min_i == len(ns):
        return

    # weird look just above P2, need to prepare for lower loop
    # by setting Pnm2 = P1, Pnm1 = P2
    Pnm2 = P1
    Pnm1 = P1
    Pn = P2
    # A, B, C = recurrence_abc(2, alpha, beta)
    # P3 = (A * x + B) * P2 - C * P1
    # Pn = P3

    max_n = ns[-1]
    for i in range(3, max_n + 1):
        Pnm2, Pnm1 = Pnm1, Pn
        if ns[min_i] == i:
            coef = 0.5 * (i + alpha + beta + 1)
            yield Pnm1 * coef
            min_i += 1

        if min_i == len(ns):
            return

        A, B, C = recurrence_abc(i - 1, alphap1, betap1)
        Pn = (A * x + B) * Pnm1 - C * Pnm2