예제 #1
0
def fresnels(z):
    """Fresnel integral S, S(z)"""
    if z == inf:
        return mpf(0.5)
    if z == -inf:
        return mpf(-0.5)
    return pi*z**3/6*hypsum([[3,4]],[],[],[[3,2],[7,4]],[],[],-pi**2*z**4/16)
예제 #2
0
def calculate_nome(k):
    """
    Calculate the nome, q, from the value for k.

    Useful factoids:

    k**2 = m;   m is used in Abramowitz
    """
    k = convert_lossless(k)

    if k > mpf('1'):  # range error
        raise ValueError

    zero = mpf('0')
    one = mpf('1')

    if k == zero:
        return zero
    elif k == one:
        return one
    else:
        kprimesquared = one - k**2
        kprime = sqrt(kprimesquared)
        top = ellipk(kprimesquared)
        bottom = ellipk(k**2)

        argument = mpf('-1') * pi * top / bottom

        nome = exp(argument)
        return nome
예제 #3
0
def calculate_nome(k):
    """
    Calculate the nome, q, from the value for k.

    Useful factoids:

    k**2 = m;   m is used in Abramowitz
    """
    k = convert_lossless(k)

    if k > mpf('1'):             # range error
        raise ValueError

    zero = mpf('0')
    one = mpf('1')

    if k == zero:
        return zero
    elif k == one:
        return one
    else:
        kprimesquared = one - k**2
        kprime = sqrt(kprimesquared)
        top = ellipk(kprimesquared)
        bottom = ellipk(k**2)

        argument = mpf('-1')*pi*top/bottom

        nome = exp(argument)
        return nome
예제 #4
0
def jacobi_elliptic_cn(u, m, verbose=False):
    """
    Implements the jacobi elliptic cn function, using the expansion in
    terms of q, from Abramowitz 16.23.2.
    """
    u = convert_lossless(u)
    m = convert_lossless(m)

    if verbose:
        print >> sys.stderr, '\nelliptic.jacobi_elliptic_cn'
        print >> sys.stderr, '\tu: %1.12f' % u
        print >> sys.stderr, '\tm: %1.12f' % m

    zero = mpf('0')
    onehalf = mpf('0.5')
    one = mpf('1')
    two = mpf('2')

    if m == zero:                   # cn collapses to cos(u)
        if verbose:
            print >> sys.stderr, 'cn: special case, m == 0'
        return cos(u)
    elif m == one:                  # cn collapses to sech(u)
        if verbose:
            print >> sys.stderr, 'cn: special case, m == 1'
        return sech(u)
    else:
        k = sqrt(m)                        # convert m to k
        q = calculate_nome(k)
        kprimesquared = one - k**2
        kprime = sqrt(kprimesquared)
        v = (pi * u) / (two*ellipk(k**2))

    sum = zero
    term = zero                     # series starts at zero

    if verbose:
        print >> sys.stderr, 'elliptic.jacobi_elliptic_cn: calculating'
    while True:
        factor1 = (q**(term + onehalf)) / (one + q**(two*term + one))
        factor2 = cos((two*term + one)*v)

        term_n = factor1*factor2
        sum = sum + term_n

        if verbose:
            print >> sys.stderr, '\tTerm: %d' % term,
            print >> sys.stderr, '\tterm_n: %e' % term_n,
            print >> sys.stderr, '\tsum: %e' % sum

        if not factor2 == zero:
            #if log(term_n, '10') < -1*mpf.dps:
            if abs(term_n) < eps:
                break

        term = term + one

    answer = (two*pi) / (sqrt(m) * ellipk(k**2)) * sum

    return answer
예제 #5
0
def chebcoeff(f,a,b,j,N):
    s = mpf(0)
    h = mpf(0.5)
    for k in range(1, N+1):
        t = cos(pi*(k-h)/N)
        s += f(t*(b-a)*h + (b+a)*h) * cos(pi*j*(k-h)/N)
    return 2*s/N
예제 #6
0
def calculate_k(q, verbose=False):
    """
    Calculates the value of k for a particular nome, q.

    Uses special cases of the jacobi theta functions, with
    q as an argument, rather than m.

    k = (v2(0, q)/v3(0, q))**2
    """
    zero = mpf('0')
    one = mpf('1')

    q = convert_lossless(q)
    if q > one or q < zero:
        raise ValueError

    # calculate v2(0, q)
    sum = zero
    term = zero                     # series starts at zero
    while True:
        factor1 = q**(term*(term + 1))
        term_n = factor1            # suboptimal, kept for readability
        sum = sum + term_n

        if verbose:
            print >> sys.stderr, '\tTerm: %d' % term,
            print >> sys.stderr, '\tterm_n: %e' % term_n,
            print >> sys.stderr, '\tsum: %e' % sum

        if factor1 == zero:         # all further terms will be zero
            break
        #if log(term_n, '10') < -1*mpf.dps:
        if abs(term_n) < eps:
            break

        term = term + 1

    v2 = 2*q**(mpf('0.25'))*sum

    # calculate v3(0, q)
    sum = zero
    term = one                          # series starts at one
    while True:
        factor1 = q**(term*term)
        term_n = factor1                # suboptimal, kept for readability
        sum = sum + term_n

        if factor1 == mpf('0'):      # all further terms will be zero
            break
        #if log(term_n, '10') < -1*mpf.dps:
        if abs(term_n) < eps:
            break

        term = term + 1

    v3 = one + 2*sum

    k = v2**2/v3**2

    return k
예제 #7
0
def sumsh(f, interval, n=None, m=None):
    """
    Sum f(k) for k = a, a+1, ..., b where [a, b] = interval,
    using an n-term Shanks transformation. With m > 1, the Shanks
    transformation is applied recursively m times.

    Shanks summation often works well for slowly convergent and/or
    alternating Taylor series.
    """
    a, b = AS_POINTS(interval)
    assert b == inf
    if not n: n = 5 + int(mp.dps * 1.2)
    if not m: m = 2 + n//3
    orig = mp.prec
    try:
        mp.prec = 2*orig
        s = mpf(0)
        tbl = []
        for k in range(a, a+n+m+2):
            s += f(mpf(k))
            tbl.append(s)
        s = shanks_extrapolation(tbl, n, m)
    finally:
        mp.prec = orig
    return +s
예제 #8
0
def chebyfit(f, interval, N, error=False):
    """
    Chebyshev approximation: returns coefficients of a degree N-1
    polynomial that approximates f on the interval [a, b]. With error=True,
    also returns an estimate of the maximum error.
    """
    a, b = AS_POINTS(interval)
    orig = mp.prec
    try:
        mp.prec = orig + int(N**0.5) + 20
        c = [chebcoeff(f,a,b,k,N) for k in range(N)]
        d = [mpf(0)] * N
        d[0] = -c[0]/2
        h = mpf(0.5)
        T = chebT(mpf(2)/(b-a), mpf(-1)*(b+a)/(b-a))
        for k in range(N):
            Tk = T.next()
            for i in range(len(Tk)):
                d[i] += c[k]*Tk[i]
        d = d[::-1]
        # Estimate maximum error
        err = mpf(0)
        for k in range(N):
            x = cos(pi*k/N) * (b-a)*h + (b+a)*h
            err = max(err, abs(f(x) - polyval(d, x)))
    finally:
        mp.prec = orig
        if error:
            return d, +err
        else:
            return d
예제 #9
0
def fresnelc(z):
    """Fresnel integral C, C(z)"""
    if z == inf:
        return mpf(0.5)
    if z == -inf:
        return mpf(-0.5)
    return z*hypsum([[1,4]],[],[],[[1,2],[5,4]],[],[],-pi**2*z**4/16)
예제 #10
0
def jacobi_elliptic_cn(u, m, verbose=False):
    """
    Implements the jacobi elliptic cn function, using the expansion in
    terms of q, from Abramowitz 16.23.2.
    """
    u = convert_lossless(u)
    m = convert_lossless(m)

    if verbose:
        print >> sys.stderr, '\nelliptic.jacobi_elliptic_cn'
        print >> sys.stderr, '\tu: %1.12f' % u
        print >> sys.stderr, '\tm: %1.12f' % m

    zero = mpf('0')
    onehalf = mpf('0.5')
    one = mpf('1')
    two = mpf('2')

    if m == zero:  # cn collapses to cos(u)
        if verbose:
            print >> sys.stderr, 'cn: special case, m == 0'
        return cos(u)
    elif m == one:  # cn collapses to sech(u)
        if verbose:
            print >> sys.stderr, 'cn: special case, m == 1'
        return sech(u)
    else:
        k = sqrt(m)  # convert m to k
        q = calculate_nome(k)
        kprimesquared = one - k**2
        kprime = sqrt(kprimesquared)
        v = (pi * u) / (two * ellipk(k**2))

    sum = zero
    term = zero  # series starts at zero

    if verbose:
        print >> sys.stderr, 'elliptic.jacobi_elliptic_cn: calculating'
    while True:
        factor1 = (q**(term + onehalf)) / (one + q**(two * term + one))
        factor2 = cos((two * term + one) * v)

        term_n = factor1 * factor2
        sum = sum + term_n

        if verbose:
            print >> sys.stderr, '\tTerm: %d' % term,
            print >> sys.stderr, '\tterm_n: %e' % term_n,
            print >> sys.stderr, '\tsum: %e' % sum

        if not factor2 == zero:
            #if log(term_n, '10') < -1*mpf.dps:
            if abs(term_n) < eps:
                break

        term = term + one

    answer = (two * pi) / (sqrt(m) * ellipk(k**2)) * sum

    return answer
예제 #11
0
    def estimate_error(self, results, prec, epsilon):
        r"""
        Given results from integrations `[I_1, I_2, \ldots, I_k]` done
        with a quadrature of rule of degree `1, 2, \ldots, k`, estimate
        the error of `I_k`.

        For `k = 2`, we estimate  `|I_{\infty}-I_2|` as `|I_2-I_1|`.

        For `k > 2`, we extrapolate `|I_{\infty}-I_k| \approx |I_{k+1}-I_k|`
        from `|I_k-I_{k-1}|` and `|I_k-I_{k-2}|` under the assumption
        that each degree increment roughly doubles the accuracy of
        the quadrature rule (this is true for both :class:`TanhSinh`
        and :class:`GaussLegendre`). The extrapolation formula is given
        by Borwein, Bailey & Girgensohn. Although not very conservative,
        this method seems to be very robust in practice.
        """
        if len(results) == 2:
            return abs(results[0]-results[1])
        try:
            if results[-1] == results[-2] == results[-3]:
                return mpf(0)
            D1 = log(abs(results[-1]-results[-2]), 10)
            D2 = log(abs(results[-1]-results[-3]), 10)
        except ValueError:
            return epsilon
        D3 = -prec
        D4 = min(0, max(D1**2/D2, 2*D1, D3))
        return mpf(10) ** int(D4)
예제 #12
0
    def estimate_error(self, results, prec, epsilon):
        r"""
        Given results from integrations `[I_1, I_2, \ldots, I_k]` done
        with a quadrature of rule of degree `1, 2, \ldots, k`, estimate
        the error of `I_k`.

        For `k = 2`, we estimate  `|I_{\infty}-I_2|` as `|I_2-I_1|`.

        For `k > 2`, we extrapolate `|I_{\infty}-I_k| \approx |I_{k+1}-I_k|`
        from `|I_k-I_{k-1}|` and `|I_k-I_{k-2}|` under the assumption
        that each degree increment roughly doubles the accuracy of
        the quadrature rule (this is true for both :class:`TanhSinh`
        and :class:`GaussLegendre`). The extrapolation formula is given
        by Borwein, Bailey & Girgensohn. Although not very conservative,
        this method seems to be very robust in practice.
        """
        if len(results) == 2:
            return abs(results[0]-results[1])
        try:
            if results[-1] == results[-2] == results[-3]:
                return mpf(0)
            D1 = log(abs(results[-1]-results[-2]), 10)
            D2 = log(abs(results[-1]-results[-3]), 10)
        except ValueError:
            return epsilon
        D3 = -prec
        D4 = min(0, max(D1**2/D2, 2*D1, D3))
        return mpf(10) ** int(D4)
예제 #13
0
def calculate_k(q, verbose=False):
    """
    Calculates the value of k for a particular nome, q.

    Uses special cases of the jacobi theta functions, with
    q as an argument, rather than m.

    k = (v2(0, q)/v3(0, q))**2
    """
    zero = mpf('0')
    one = mpf('1')

    q = convert_lossless(q)
    if q > one or q < zero:
        raise ValueError

    # calculate v2(0, q)
    sum = zero
    term = zero  # series starts at zero
    while True:
        factor1 = q**(term * (term + 1))
        term_n = factor1  # suboptimal, kept for readability
        sum = sum + term_n

        if verbose:
            print >> sys.stderr, '\tTerm: %d' % term,
            print >> sys.stderr, '\tterm_n: %e' % term_n,
            print >> sys.stderr, '\tsum: %e' % sum

        if factor1 == zero:  # all further terms will be zero
            break
        #if log(term_n, '10') < -1*mpf.dps:
        if abs(term_n) < eps:
            break

        term = term + 1

    v2 = 2 * q**(mpf('0.25')) * sum

    # calculate v3(0, q)
    sum = zero
    term = one  # series starts at one
    while True:
        factor1 = q**(term * term)
        term_n = factor1  # suboptimal, kept for readability
        sum = sum + term_n

        if factor1 == mpf('0'):  # all further terms will be zero
            break
        #if log(term_n, '10') < -1*mpf.dps:
        if abs(term_n) < eps:
            break

        term = term + 1

    v3 = one + 2 * sum

    k = v2**2 / v3**2

    return k
예제 #14
0
def fresnelc(z):
    """Fresnel integral C, C(z)"""
    if z == inf:
        return mpf(0.5)
    if z == -inf:
        return mpf(-0.5)
    return z * hypsum([[1, 4]], [], [], [[1, 2], [5, 4]], [], [],
                      -pi**2 * z**4 / 16)
예제 #15
0
def fresnels(z):
    """Fresnel integral S, S(z)"""
    if z == inf:
        return mpf(0.5)
    if z == -inf:
        return mpf(-0.5)
    return pi * z**3 / 6 * hypsum([[3, 4]], [], [], [[3, 2], [7, 4]], [], [],
                                  -pi**2 * z**4 / 16)
예제 #16
0
def airyai(z):
    """Airy function, Ai(z)"""
    if z == inf:
        return 1/z
    if z == -inf:
        return mpf(0)
    z3 = z**3 / 9
    a = sum_hyp0f1_rat((2,3), z3) / (cbrt(9) * gamma(mpf(2)/3))
    b = z * sum_hyp0f1_rat((4,3), z3) / (cbrt(3) * gamma(mpf(1)/3))
    return a - b
예제 #17
0
def airyai(z):
    """Airy function, Ai(z)"""
    if z == inf:
        return 1 / z
    if z == -inf:
        return mpf(0)
    z3 = z**3 / 9
    a = sum_hyp0f1_rat((2, 3), z3) / (cbrt(9) * gamma(mpf(2) / 3))
    b = z * sum_hyp0f1_rat((4, 3), z3) / (cbrt(3) * gamma(mpf(1) / 3))
    return a - b
예제 #18
0
 def sum_next(cls, prec, level, previous, f, verbose=False):
     h = mpf(2)**(-level)
     # Abscissas overlap, so reusing saves half of the time
     if previous:
         S = previous[-1] / (h * 2)
     else:
         S = mpf(0)
     for x, w in cls.get_nodes(prec, level, verbose=False):
         S += w * (f(NEG(x)) + f(x))
     return h * S
예제 #19
0
 def sum_next(cls, prec, level, previous, f, verbose=False):
     h = mpf(2)**(-level)
     # Abscissas overlap, so reusing saves half of the time
     if previous:
         S = previous[-1]/(h*2)
     else:
         S = mpf(0)
     for x, w in cls.get_nodes(prec, level, verbose=False):
         S += w*(f(NEG(x)) + f(x))
     return h*S
예제 #20
0
def jacobi_elliptic_dn(u, m, verbose=False):
    """
    Implements the jacobi elliptic cn function, using the expansion in
    terms of q, from Abramowitz 16.23.3.
    """
    u = convert_lossless(u)
    m = convert_lossless(m)

    if verbose:
        print >> sys.stderr, '\nelliptic.jacobi_elliptic_dn'
        print >> sys.stderr, '\tu: %1.12f' % u
        print >> sys.stderr, '\tm: %1.12f' % m

    zero = mpf('0')
    onehalf = mpf('0.5')
    one = mpf('1')
    two = mpf('2')

    if m == zero:           # dn collapes to 1
        return one
    elif m == one:          # dn collapses to sech(u)
        return sech(u)
    else:
        k = sqrt(m)                        # convert m to k
        q = calculate_nome(k)
        v = (pi * u) / (two*ellipk(k**2))

    sum = zero
    term = one                  # series starts at one

    if verbose:
        print >> sys.stderr, 'elliptic.jacobi_elliptic_dn: calculating'
    while True:
        factor1 = (q**term) / (one + q**(two*term))
        factor2 = cos(two*term*v)

        term_n = factor1*factor2
        sum = sum + term_n

        if verbose:
            print >> sys.stderr, '\tTerm: %d' % term,
            print >> sys.stderr, '\tterm_n: %e' % term_n,
            print >> sys.stderr, '\tsum: %e' % sum

        if not factor2 == zero:
            #if log(term_n, '10') < -1*mpf.dps:
            if abs(term_n) < eps:
                break

        term = term + one

    K = ellipk(k**2)
    answer = (pi / (two*K)) + (two*pi*sum)/(ellipk(k**2))

    return answer
예제 #21
0
def jacobi_elliptic_dn(u, m, verbose=False):
    """
    Implements the jacobi elliptic cn function, using the expansion in
    terms of q, from Abramowitz 16.23.3.
    """
    u = convert_lossless(u)
    m = convert_lossless(m)

    if verbose:
        print >> sys.stderr, '\nelliptic.jacobi_elliptic_dn'
        print >> sys.stderr, '\tu: %1.12f' % u
        print >> sys.stderr, '\tm: %1.12f' % m

    zero = mpf('0')
    onehalf = mpf('0.5')
    one = mpf('1')
    two = mpf('2')

    if m == zero:  # dn collapes to 1
        return one
    elif m == one:  # dn collapses to sech(u)
        return sech(u)
    else:
        k = sqrt(m)  # convert m to k
        q = calculate_nome(k)
        v = (pi * u) / (two * ellipk(k**2))

    sum = zero
    term = one  # series starts at one

    if verbose:
        print >> sys.stderr, 'elliptic.jacobi_elliptic_dn: calculating'
    while True:
        factor1 = (q**term) / (one + q**(two * term))
        factor2 = cos(two * term * v)

        term_n = factor1 * factor2
        sum = sum + term_n

        if verbose:
            print >> sys.stderr, '\tTerm: %d' % term,
            print >> sys.stderr, '\tterm_n: %e' % term_n,
            print >> sys.stderr, '\tsum: %e' % sum

        if not factor2 == zero:
            #if log(term_n, '10') < -1*mpf.dps:
            if abs(term_n) < eps:
                break

        term = term + one

    K = ellipk(k**2)
    answer = (pi / (two * K)) + (two * pi * sum) / (ellipk(k**2))

    return answer
예제 #22
0
def airybi(z):
    """Airy function, Bi(z)"""
    if z == inf:
        return z
    if z == -inf:
        return mpf(0)
    z3 = z**3 / 9
    rt = nthroot(3, 6)
    a = sum_hyp0f1_rat((2,3), z3) / (rt * gamma(mpf(2)/3))
    b = z * rt * sum_hyp0f1_rat((4,3), z3) / gamma(mpf(1)/3)
    return a + b
예제 #23
0
def airybi(z):
    """Airy function, Bi(z)"""
    if z == inf:
        return z
    if z == -inf:
        return mpf(0)
    z3 = z**3 / 9
    rt = nthroot(3, 6)
    a = sum_hyp0f1_rat((2, 3), z3) / (rt * gamma(mpf(2) / 3))
    b = z * rt * sum_hyp0f1_rat((4, 3), z3) / gamma(mpf(1) / 3)
    return a + b
예제 #24
0
    def transform_nodes(self, nodes, a, b, verbose=False):
        r"""
        Rescale standardized nodes (for `[-1, 1]`) to a general
        interval `[a, b]`. For a finite interval, a simple linear
        change of variables is used. Otherwise, the following
        transformations are used:

        .. math ::

            [a, \infty] : t = \frac{1}{x} + (a-1)

            [-\infty, b] : t = (b+1) - \frac{1}{x}

            [-\infty, \infty] : t = \frac{x}{\sqrt{1-x^2}}

        """
        a = mpmathify(a)
        b = mpmathify(b)
        one = mpf(1)
        if (a, b) == (-one, one):
            return nodes
        half = mpf(0.5)
        new_nodes = []
        if (a, b) == (-inf, inf):
            p05 = mpf(-0.5)
            for x, w in nodes:
                x2 = x*x
                px1 = one-x2
                spx1 = px1**p05
                x = x*spx1
                w *= spx1/px1
                new_nodes.append((x, w))
        elif a == -inf:
            b1 = b+1
            for x, w in nodes:
                u = 2/(x+one)
                x = b1-u
                w *= half*u**2
                new_nodes.append((x, w))
        elif b == inf:
            a1 = a-1
            for x, w in nodes:
                u = 2/(x+one)
                x = a1+u
                w *= half*u**2
                new_nodes.append((x, w))
        else:
            # Simple linear change of variables
            C = (b-a)/2
            D = (b+a)/2
            for x, w in nodes:
                new_nodes.append((D+C*x, C*w))
        return new_nodes
예제 #25
0
    def transform_nodes(self, nodes, a, b, verbose=False):
        r"""
        Rescale standardized nodes (for `[-1, 1]`) to a general
        interval `[a, b]`. For a finite interval, a simple linear
        change of variables is used. Otherwise, the following
        transformations are used:

        .. math ::

            [a, \infty] : t = \frac{1}{x} + (a-1)

            [-\infty, b] : t = (b+1) - \frac{1}{x}

            [-\infty, \infty] : t = \frac{x}{\sqrt{1-x^2}}

        """
        a = mpmathify(a)
        b = mpmathify(b)
        one = mpf(1)
        if (a, b) == (-one, one):
            return nodes
        half = mpf(0.5)
        new_nodes = []
        if (a, b) == (-inf, inf):
            p05 = mpf(-0.5)
            for x, w in nodes:
                x2 = x*x
                px1 = one-x2
                spx1 = px1**p05
                x = x*spx1
                w *= spx1/px1
                new_nodes.append((x, w))
        elif a == -inf:
            b1 = b+1
            for x, w in nodes:
                u = 2/(x+one)
                x = b1-u
                w *= half*u**2
                new_nodes.append((x, w))
        elif b == inf:
            a1 = a-1
            for x, w in nodes:
                u = 2/(x+one)
                x = a1+u
                w *= half*u**2
                new_nodes.append((x, w))
        else:
            # Simple linear change of variables
            C = (b-a)/2
            D = (b+a)/2
            for x, w in nodes:
                new_nodes.append((D+C*x, C*w))
        return new_nodes
예제 #26
0
def transform(f, a, b):
    """
    Given an integrand f defined over the interval [a, b], return an
    equivalent integrand g defined on the standard interval [-1, 1].

    If a and b are finite, this is achived by means of a linear change
    of variables. If at least one point is infinite, the substitution
    t = 1/x is used.
    """
    a = convert_lossless(a)
    b = convert_lossless(b)
    if (a, b) == (-1, 1):
        return f
    one = mpf(1)
    half = mpf(0.5)
    # The transformation 1/x sends [1, inf] to [0, 1], which in turn
    # can be transformed to [-1, 1] the usual way. For a double
    # infinite interval, we simply evaluate the function symmetrically
    if (a, b) == (-inf, inf):
        # return transform(lambda x: (f(-1/x+1)+f(1/x-1))/x**2, 0, 1)
        def y(x):
            u = 2 / (x + one)
            w = one - u
            return half * (f(w) + f(-w)) * u**2

        return y
    if a == -inf:
        # return transform(lambda x: f(-1/x+b+1)/x**2, 0, 1)
        b1 = b + 1

        def y(x):
            u = 2 / (x + one)
            return half * f(b1 - u) * u**2

        return y
    if b == inf:
        # return transform(lambda x: f(1/x+a-1)/x**2, 0, 1)
        a1 = a - 1

        def y(x):
            u = 2 / (x + one)
            return half * f(a1 + u) * u**2

        return y
    # Simple linear change of variables
    C = (b - a) / 2
    D = (b + a) / 2

    def g(x):
        return C * f(D + C * x)

    return g
예제 #27
0
def richardson_extrapolation(f, n, N):
    if not callable(f):
        g = f; f = lambda k: g.__getitem__(int(k))
    orig = mp.prec
    try:
        mp.prec = 2*orig
        s = mpf(0)
        for j in range(0, N+1):
            c = (n+j)**N * (-1)**(j+N) / (factorial(j) * factorial(N-j))
            s += c * f(mpf(n+j))
    finally:
        mp.prec = orig
    return +s
예제 #28
0
 def sum_next(self, f, nodes, degree, prec, previous, verbose=False):
     """
     Step sum for tanh-sinh quadrature of degree `m`. We exploit the
     fact that half of the abscissas at degree `m` are precisely the
     abscissas from degree `m-1`. Thus reusing the result from
     the previous level allows a 2x speedup.
     """
     h = mpf(2)**(-degree)
     # Abscissas overlap, so reusing saves half of the time
     if previous:
         S = previous[-1]/(h*2)
     else:
         S = mpf(0)
     S += fdot((w,f(x)) for (x,w) in nodes)
     return h*S
예제 #29
0
 def sum_next(self, f, nodes, degree, prec, previous, verbose=False):
     """
     Step sum for tanh-sinh quadrature of degree `m`. We exploit the
     fact that half of the abscissas at degree `m` are precisely the
     abscissas from degree `m-1`. Thus reusing the result from
     the previous level allows a 2x speedup.
     """
     h = mpf(2)**(-degree)
     # Abscissas overlap, so reusing saves half of the time
     if previous:
         S = previous[-1]/(h*2)
     else:
         S = mpf(0)
     S += fdot((w,f(x)) for (x,w) in nodes)
     return h*S
예제 #30
0
 def estimate_error(cls, results, prec, epsilon):
     """
     Estimate error of the calculation at the present level by
     comparing it to the results from two previous levels. The
     algorithm is given by Borwein, Bailey & Girgensohn.
     """
     try:
         if results[-1] == results[-2] == results[-3]:
             return mpf(0)
         D1 = log(abs(results[-1] - results[-2]), 10)
         D2 = log(abs(results[-1] - results[-3]), 10)
     except ValueError:
         return epsilon
     D3 = -prec
     D4 = min(0, max(D1**2 / D2, 2 * D1, D3))
     return mpf(10)**int(D4)
예제 #31
0
 def summation(cls, f, points, prec, epsilon, max_level, verbose=False):
     """
     Main summation function
     """
     I = err = mpf(0)
     for i in xrange(len(points)-1):
         a, b = points[i], points[i+1]
         if a == b:
             continue
         g = transform(f, a, b)
         results = []
         for level in xrange(1, max_level+1):
             if verbose:
                 print "Integrating from %s to %s (level %s of %s)" % \
                     (nstr(a), nstr(b), level, max_level)
             results.append(cls.sum_next(prec, level, results, g, verbose))
             if level > 2:
                 err = cls.estimate_error(results, prec, epsilon)
                 if err <= epsilon:
                     break
                 if verbose:
                     print "Estimated error:", nstr(err)
         I += results[-1]
     if err > epsilon:
         if verbose:
             print "Failed to reach full accuracy. Estimated error:", nstr(err)
     return I, err
예제 #32
0
 def summation(cls, f, points, prec, epsilon, max_level, verbose=False):
     """
     Main summation function
     """
     I = err = mpf(0)
     for i in xrange(len(points) - 1):
         a, b = points[i], points[i + 1]
         if a == b:
             continue
         g = transform(f, a, b)
         results = []
         for level in xrange(1, max_level + 1):
             if verbose:
                 print "Integrating from %s to %s (level %s of %s)" % \
                     (nstr(a), nstr(b), level, max_level)
             results.append(cls.sum_next(prec, level, results, g, verbose))
             if level > 2:
                 err = cls.estimate_error(results, prec, epsilon)
                 if err <= epsilon:
                     break
                 if verbose:
                     print "Estimated error:", nstr(err)
         I += results[-1]
     if err > epsilon:
         if verbose:
             print "Failed to reach full accuracy. Estimated error:", nstr(
                 err)
     return I, err
예제 #33
0
def diffc(f, x, n=1, radius=mpf(0.5)):
    """
    Compute an approximation of the nth derivative of f at the point x
    using the Cauchy integral formula. This only works for analytic
    functions. A circular path with the given radius is used.

    diffc increases the working precision slightly to avoid simple
    rounding errors. Note that, especially for large n, differentiation
    is extremely ill-conditioned, so this precaution does not
    guarantee a correct result. (Provided there are no singularities
    in the way, increasing the radius may help.)

    The returned value will be a complex number; a large imaginary part
    for a derivative that should be real may indicate a large numerical
    error.
    """
    prec = mp.prec
    try:
        mp.prec += 10
        def g(t):
            rei = radius*exp(j*t)
            z = x + rei
            return f(z) / rei**n
        d = quadts(g, [0, 2*pi])
        return d * factorial(n) / (2*pi)
    finally:
        mp.prec = prec
예제 #34
0
def stieltjes(n):
    """Computes the nth Stieltjes constant."""
    n = int(n)
    if n == 0:
        return +euler
    if n < 0:
        raise ValueError("Stieltjes constants defined for n >= 0")
    if n in stieltjes_cache:
        prec, s = stieltjes_cache[n]
        if prec >= mp.prec:
            return +s
    from quadrature import quadgl
    def f(x):
        r = exp(pi*j*x)
        return (zeta(r+1) / r**n).real
    orig = mp.prec
    try:
        p = int(log(factorial(n), 2) + 35)
        mp.prec += p
        u = quadgl(f, [-1, 1])
        v = mpf(-1)**n * factorial(n) * u / 2
    finally:
        mp.prec = orig
    stieltjes_cache[n] = (mp.prec, v)
    return +v
예제 #35
0
def stieltjes(n):
    """Computes the nth Stieltjes constant."""
    n = int(n)
    if n == 0:
        return +euler
    if n < 0:
        raise ValueError("Stieltjes constants defined for n >= 0")
    if n in stieltjes_cache:
        prec, s = stieltjes_cache[n]
        if prec >= mp.prec:
            return +s
    from quadrature import quadgl

    def f(x):
        r = exp(pi * j * x)
        return (zeta(r + 1) / r**n).real

    orig = mp.prec
    try:
        p = int(log(factorial(n), 2) + 35)
        mp.prec += p
        u = quadgl(f, [-1, 1])
        v = mpf(-1)**n * factorial(n) * u / 2
    finally:
        mp.prec = orig
    stieltjes_cache[n] = (mp.prec, v)
    return +v
예제 #36
0
 def calc_nodes(cls, prec, level, verbose=False):
     # It is important that the epsilon is set lower than the
     # "real" epsilon
     epsilon = ldexp(1, -prec - 8)
     # Fairly high precision might be required for accurate
     # evaluation of the roots
     orig = mp.prec
     mp.prec = int(prec * 1.5)
     nodes = []
     n = 3 * 2**(level - 1)
     upto = n // 2 + 1
     for j in xrange(1, upto):
         # Asymptotic formula for the roots
         r = mpf(math.cos(math.pi * (j - 0.25) / (n + 0.5)))
         # Newton iteration
         while 1:
             t1, t2 = 1, 0
             # Evaluates the Legendre polynomial using its defining
             # recurrence relation
             for j1 in xrange(1, n + 1):
                 t3, t2, t1 = t2, t1, ((2 * j1 - 1) * r * t1 -
                                       (j1 - 1) * t2) / j1
             t4 = n * (r * t1 - t2) / (r**2 - 1)
             t5 = r
             a = t1 / t4
             r = r - a
             if abs(a) < epsilon:
                 break
         x = r
         w = 2 / ((1 - r**2) * t4**2)
         if verbose and j % 30 == 15:
             print "Computing nodes (%i of %i)" % (j, upto)
         nodes.append((x, w))
     mp.prec = orig
     return nodes
예제 #37
0
 def estimate_error(cls, results, prec, epsilon):
     """
     Estimate error of the calculation at the present level by
     comparing it to the results from two previous levels. The
     algorithm is given by Borwein, Bailey & Girgensohn.
     """
     try:
         if results[-1] == results[-2] == results[-3]:
             return mpf(0)
         D1 = log(abs(results[-1]-results[-2]), 10)
         D2 = log(abs(results[-1]-results[-3]), 10)
     except ValueError:
         return epsilon
     D3 = -prec
     D4 = min(0, max(D1**2/D2, 2*D1, D3))
     return mpf(10) ** int(D4)
예제 #38
0
 def calc_nodes(cls, prec, level, verbose=False):
     # It is important that the epsilon is set lower than the
     # "real" epsilon
     epsilon = ldexp(1, -prec-8)
     # Fairly high precision might be required for accurate
     # evaluation of the roots
     orig = mp.prec
     mp.prec = int(prec*1.5)
     nodes = []
     n = 3*2**(level-1)
     upto = n//2 + 1
     for j in xrange(1, upto):
         # Asymptotic formula for the roots
         r = mpf(math.cos(math.pi*(j-0.25)/(n+0.5)))
         # Newton iteration
         while 1:
             t1, t2 = 1, 0
             # Evaluates the Legendre polynomial using its defining
             # recurrence relation
             for j1 in xrange(1,n+1):
                 t3, t2, t1 = t2, t1, ((2*j1-1)*r*t1 - (j1-1)*t2)/j1
             t4 = n*(r*t1- t2)/(r**2-1)
             t5 = r
             a = t1/t4
             r = r - a
             if abs(a) < epsilon:
                 break
         x = r
         w = 2/((1-r**2)*t4**2)
         if verbose  and j % 30 == 15:
             print "Computing nodes (%i of %i)" % (j, upto)
         nodes.append((x, w))
     mp.prec = orig
     return nodes
예제 #39
0
def transform(f, a, b):
    """
    Given an integrand f defined over the interval [a, b], return an
    equivalent integrand g defined on the standard interval [-1, 1].

    If a and b are finite, this is achived by means of a linear change
    of variables. If at least one point is infinite, the substitution
    t = 1/x is used.
    """
    a = convert_lossless(a)
    b = convert_lossless(b)
    if (a, b) == (-1, 1):
        return f
    one = mpf(1)
    half = mpf(0.5)
    # The transformation 1/x sends [1, inf] to [0, 1], which in turn
    # can be transformed to [-1, 1] the usual way. For a double
    # infinite interval, we simply evaluate the function symmetrically
    if (a, b) == (-inf, inf):
        # return transform(lambda x: (f(-1/x+1)+f(1/x-1))/x**2, 0, 1)
        def y(x):
            u = 2/(x+one)
            w = one - u
            return half * (f(w)+f(-w)) * u**2
        return y
    if a == -inf:
        # return transform(lambda x: f(-1/x+b+1)/x**2, 0, 1)
        b1 = b+1
        def y(x):
            u = 2/(x+one)
            return half * f(b1-u) * u**2
        return y
    if b == inf:
        # return transform(lambda x: f(1/x+a-1)/x**2, 0, 1)
        a1 = a-1
        def y(x):
            u = 2/(x+one)
            return half * f(a1+u) * u**2
        return y
    # Simple linear change of variables
    C = (b-a)/2
    D = (b+a)/2
    def g(x):
        return C * f(D + C*x)
    return g
예제 #40
0
 def calc_nodes(self, degree, prec, verbose=False):
     """
     Calculates the abscissas and weights for Gauss-Legendre
     quadrature of degree of given degree (actually `3 \cdot 2^m`).
     """
     # It is important that the epsilon is set lower than the
     # "real" epsilon
     epsilon = ldexp(1, -prec-8)
     # Fairly high precision might be required for accurate
     # evaluation of the roots
     orig = mp.prec
     mp.prec = int(prec*1.5)
     if degree == 1:
         x = mpf(3)/5
         w = mpf(5)/9
         nodes = [(-x,w),(mpf(0),mpf(8)/9),(x,w)]
         mp.prec = orig
         return nodes
     nodes = []
     n = 3*2**(degree-1)
     upto = n//2 + 1
     for j in xrange(1, upto):
         # Asymptotic formula for the roots
         r = mpf(math.cos(math.pi*(j-0.25)/(n+0.5)))
         # Newton iteration
         while 1:
             t1, t2 = 1, 0
             # Evaluates the Legendre polynomial using its defining
             # recurrence relation
             for j1 in xrange(1,n+1):
                 t3, t2, t1 = t2, t1, ((2*j1-1)*r*t1 - (j1-1)*t2)/j1
             t4 = n*(r*t1- t2)/(r**2-1)
             t5 = r
             a = t1/t4
             r = r - a
             if abs(a) < epsilon:
                 break
         x = r
         w = 2/((1-r**2)*t4**2)
         if verbose  and j % 30 == 15:
             print "Computing nodes (%i of %i)" % (j, upto)
         nodes.append((x, w))
         nodes.append((NEG(x), w))
     mp.prec = orig
     return nodes
예제 #41
0
 def calc_nodes(self, degree, prec, verbose=False):
     """
     Calculates the abscissas and weights for Gauss-Legendre
     quadrature of degree of given degree (actually `3 \cdot 2^m`).
     """
     # It is important that the epsilon is set lower than the
     # "real" epsilon
     epsilon = ldexp(1, -prec-8)
     # Fairly high precision might be required for accurate
     # evaluation of the roots
     orig = mp.prec
     mp.prec = int(prec*1.5)
     if degree == 1:
         x = mpf(3)/5
         w = mpf(5)/9
         nodes = [(-x,w),(mpf(0),mpf(8)/9),(x,w)]
         mp.prec = orig
         return nodes
     nodes = []
     n = 3*2**(degree-1)
     upto = n//2 + 1
     for j in xrange(1, upto):
         # Asymptotic formula for the roots
         r = mpf(math.cos(math.pi*(j-0.25)/(n+0.5)))
         # Newton iteration
         while 1:
             t1, t2 = 1, 0
             # Evaluates the Legendre polynomial using its defining
             # recurrence relation
             for j1 in xrange(1,n+1):
                 t3, t2, t1 = t2, t1, ((2*j1-1)*r*t1 - (j1-1)*t2)/j1
             t4 = n*(r*t1- t2)/(r**2-1)
             t5 = r
             a = t1/t4
             r = r - a
             if abs(a) < epsilon:
                 break
         x = r
         w = 2/((1-r**2)*t4**2)
         if verbose  and j % 30 == 15:
             print "Computing nodes (%i of %i)" % (j, upto)
         nodes.append((x, w))
         nodes.append((NEG(x), w))
     mp.prec = orig
     return nodes
예제 #42
0
def secant(f, x0, x1=None, maxsteps=20, verbose=False):
    """Solve the equation f(x) = 0 using the secant method, starting
    at the given initial point x0 and performing up to `maxsteps`
    steps or quitting when the difference between successive x values
    is smaller than the epsilon of the current working precision.

    The secant method requires a second starting point x1 with both
    x0 and x1 located close to the root. If only x0 is provided, x1
    is automatically generated as x0 + 1/4."""
    weps = 2*eps
    x = x0 * mpf(1)
    if x1 is None:
        xprev = x0 + mpf(0.25)
    else:
        xprev = x1 * mpf(1)
    deriv_prev = None
    fxprev = f(xprev)
    for i in xrange(maxsteps):
        if verbose:
            print "Step", i
            print "x =", x
        fx = f(x)
        ydiff = fx - fxprev
        xdiff = x - xprev
        if verbose:
            print "f(x) =", fx
            print "xdiff = ", xdiff
            print "ydiff = ", ydiff
        try:
            deriv = xdiff / ydiff
            deriv_prev = deriv
        except ZeroDivisionError:
            if deriv_prev is None:
                raise ZeroDivisionError(msg1)
            if verbose and abs(xdiff) > weps:
                print msg2
            deriv = deriv_prev
        x, xprev = x - fx*deriv, x
        fxprev = fx
        if verbose:
            print
        if abs(xdiff) <= weps:
            break
    return x
예제 #43
0
def polyval(coeffs, x, derivative=False):
    """
    Given coefficients [cn, ..., c2, c1, c0], evaluate
    P(x) = cn*x**n + ... + c2*x**2 + c1*x + c0.

    If derivative=True is set, a tuple (P(x), P'(x)) is returned.
    """
    if not coeffs:
        return mpf(0)
    p = mpnumeric(coeffs[0])
    q = mpf(0)
    for c in coeffs[1:]:
        if derivative:
            q = p + x*q
        p = c + x*p
    if derivative:
        return p, q
    else:
        return p
예제 #44
0
def agm(a, b=1):
    """Arithmetic-geometric mean of a and b. Can be called with
    a single argument, computing agm(a,1) = agm(1,a)."""
    if not a or not b:
        return a * b
    weps = eps * 16
    half = mpf(0.5)
    while abs(a - b) > weps:
        a, b = (a + b) * half, (a * b)**half
    return a
예제 #45
0
def legendre(n, x):
    """Legendre polynomial P_n(x)."""
    if isint(n):
        n = int(n)
    if x == -1:
        # TODO: hyp2f1 should handle this
        if x == int(x):
            return (-1)**(n + (n >= 0)) * mpf(-1)
        return inf
    return hyp2f1(-n, n + 1, 1, (1 - x) / 2)
예제 #46
0
def legendre(n, x):
    """Legendre polynomial P_n(x)."""
    if isint(n):
        n = int(n)
    if x == -1:
        # TODO: hyp2f1 should handle this
        if x == int(x):
            return (-1)**(n + (n>=0)) * mpf(-1)
        return inf
    return hyp2f1(-n,n+1,1,(1-x)/2)
예제 #47
0
def agm(a, b=1):
    """Arithmetic-geometric mean of a and b. Can be called with
    a single argument, computing agm(a,1) = agm(1,a)."""
    if not a or not b:
        return a*b
    weps = eps * 16
    half = mpf(0.5)
    while abs(a-b) > weps:
        a, b = (a+b)*half, (a*b)**half
    return a
예제 #48
0
def exp_pade(a):
    """Exponential of a matrix using Pade approximants.

       See G. H. Golub, C. F. van Loan 'Matrix Computations',
         third Ed., page 572

       TODO:
         - find a good estimate for q
         - reduce the number of matrix multiplications to improve
           performance
    """
    def eps_pade(p):
        return mpf(2)**(3-2*p) * factorial(p)**2/(factorial(2*p)**2 * (2*p + 1))
    q = 4
    extraq = 8
    while 1:
        if eps_pade(q) < eps:
            break
        q += 1
    q += extraq
    j = max(1, int(log(mnorm(a,'inf'),2)))
    extra = q
    mp.dps += extra
    try:
        a = a/2**j
        na = a.rows
        den = eye(na)
        num = eye(na)
        x = eye(na)
        c = mpf(1)
        for k in range(1, q+1):
            c *= mpf(q - k + 1)/((2*q - k + 1) * k)
            x = a*x
            cx = c*x
            num += cx
            den += (-1)**k * cx
        f = lu_solve_mat(den, num)
        for k in range(j):
            f = f*f
    finally:
        mp.dps -= extra
    return f
예제 #49
0
def exp_pade(a):
    """Exponential of a matrix using Pade approximants.

       See G. H. Golub, C. F. van Loan 'Matrix Computations',
         third Ed., page 572

       TODO:
         - find a good estimate for q
         - reduce the number of matrix multiplications to improve
           performance
    """
    def eps_pade(p):
        return mpf(2)**(3-2*p) * factorial(p)**2/(factorial(2*p)**2 * (2*p + 1))
    q = 4
    extraq = 8
    while 1:
        if eps_pade(q) < eps:
            break
        q += 1
    q += extraq
    j = max(1, int(log(mnorm(a,'inf'),2)))
    extra = q
    mp.dps += extra
    try:
        a = a/2**j
        na = a.rows
        den = eye(na)
        num = eye(na)
        x = eye(na)
        c = mpf(1)
        for k in range(1, q+1):
            c *= mpf(q - k + 1)/((2*q - k + 1) * k)
            x = a*x
            cx = c*x
            num += cx
            den += (-1)**k * cx
        f = lu_solve_mat(den, num)
        for k in range(j):
            f = f*f
    finally:
        mp.dps -= extra
    return f
예제 #50
0
def findpoly(x, n=1):
    """Find an integer polynomial P of degree at most n such that
    x is an approximate root of P."""
    if x == 0:
        return [1, 0]
    xs = [mpf(1)]
    for i in range(1, n + 1):
        xs.append(x**i)
        a = pslq(xs)
        if a is not None:
            return a[::-1]
예제 #51
0
def ei(z):
    """Exponential integral, Ei(z)"""
    if z == inf:
        return z
    if z == -inf:
        return -mpf(0)
    v = z*hypsum([[1,1],[1,1]],[],[],[[2,1],[2,1]],[],[],z) + \
        (log(z)-log(1/z))/2 + euler
    if isinstance(z, mpf) and z < 0:
        return v.real
    return v
예제 #52
0
def limit(f, x, direction=-1, n=None, N=None):
    """Compute lim of f(t) as t -> x using Richardson extrapolation.
    For infinite x, the function values [f(n), ... f(n+N)] are used.
    For finite x, [f(x-direction/n)), ... f(x-direction/(n+N))] are
    used. If x is inf, f can be also be a precomputed sequence
    with a __getitem__ method."""
    if callable(f):
        if not n: n = 3 + int(mp.dps * 0.5)
        if not N: N = 2*n
    else:
        # If a sequence, take as many terms are are available
        g = f; f = lambda k: g.__getitem__(int(k))
        if not N: N = len(g)-1
        if not n: n = 0
    if   x == inf:  return richardson_extrapolation(lambda k: f(mpf(k)), n, N)
    elif x == -inf: return richardson_extrapolation(lambda k: f(mpf(-k)), n, N)
    direction *= mpf(1)
    def g(k):
        return f(x - direction/(1+k))
    return richardson_extrapolation(g, n, N)