Beispiel #1
0
def _calc_spouge_coefficients(a, prec):
    """
    Calculate Spouge coefficients for approximation with parameter a.
    Return a list of big integers representing the coefficients in
    fixed-point form with a precision of prec bits.
    """

    # We'll store the coefficients as fixed-point numbers but calculate
    # them as Floats for convenience. The initial terms are huge, so we
    # need to allocate extra bits to ensure full accuracy. The integer
    # part of the largest term has size ~= exp(a) or 2**(1.4*a)
    floatprec = prec + int(a*1.4)
    Float.store()
    Float.setprec(floatprec)

    c = [0] * a
    b = exp(a-1)
    e = exp(1)
    c[0] = make_fixed(sqrt(2*pi_float()), prec)
    for k in range(1, a):
        # print "%02f" % (100.0 * k / a), "% done"
        c[k] = make_fixed(((-1)**(k-1) * (a-k)**k) * b / sqrt(a-k), prec)
        # Divide off e and k instead of computing exp and k! from scratch
        b = b / (e * k)

    Float.revert()
    return c
Beispiel #2
0
def zeta(s):
    """
    zeta(s) -- calculate the Riemann zeta function of a real or complex
    argument s.

    """
    Float.store()
    Float._prec += 8
    si = s
    s = ComplexFloat(s)
    if s.real < 0:
        # Reflection formula (XXX: gets bad around the zeros)
        pi = pi_float()
        y = power(2, s) * power(pi, s-1) * sin(pi*s/2) * gamma(1-s) * zeta(1-s)
    else:
        p = Float._prec
        n = int((p + 2.28*abs(float(s.imag)))/2.54) + 3
        d = _zeta_coefs(n)
        if isinstance(si, (int, long)):
            t = 0
            for k in range(n):
                t += (((-1)**k * (d[k] - d[n])) << p) // (k+1)**si
            y = (Float((t, -p)) / -d[n]) / (Float(1) - Float(2)**(1-si))
        else:
            t = Float(0)
            for k in range(n):
                t += (-1)**k * Float(d[k]-d[n]) * exp(-_logk(k+1)*s)
            y = (t / -d[n]) / (Float(1) - exp(log(2)*(1-s)))
    Float.revert()
    if isinstance(y, ComplexFloat) and s.imag == 0:
        return +y.real
    else:
        return +y
Beispiel #3
0
 def __call__(self, f, a, b, extraprec=3, verbose=False):
     Float.store()
     Float.extraprec(extraprec)
     f = self.transform(f, a, b)
     try:
         s, err = self._eval(f, verbose)
     except Exception, e:
         Float.revert()
         raise e
Beispiel #4
0
def erf(x):
    x = ComplexFloat(x)
    if x == 0: return Float(0)
    if x.real < 0: return -erf(-x)
    Float.store()
    Float._prec += 10
    y = lower_gamma(0.5, x**2) / sqrt(pi_float())
    if x.imag == 0:
        y = y.real
    Float.revert()
    return +y
Beispiel #5
0
def lower_gamma(a, z):
    Float.store()
    prec = Float._prec
    # XXX: may need more precision
    Float._prec += 15
    a = ComplexFloat(a)
    z = ComplexFloat(z)
    s = _lower_gamma_series(a.real, a.imag, z.real, z.imag, prec)
    y = exp(log(z)*a) * exp(-z) * s / a
    Float.revert()
    return +y
Beispiel #6
0
def gamma(x):
    """
    gamma(x) -- calculate the gamma function of a real or complex
    number x.
    
    x must not be a negative integer or 0
    """
    Float.store()
    Float._prec += 2

    if isinstance(x, complex):
        x = ComplexFloat(x)
    elif not isinstance(x, (Float, ComplexFloat, Rational, int, long)):
        x = Float(x)

    if isinstance(x, (ComplexFloat, complex)):
        re, im = x.real, x.imag
    else:
        re, im = x, 0

    # For negative x (or positive x close to the pole at x = 0),
    # we use the reflection formula
    if re < 0.25:
        if re == int(re) and im == 0:
            raise ZeroDivisionError, "gamma function pole"
        Float._prec += 3
        p = pi_float()
        g = p / (sin(p*x) * gamma(1-x))
    else:
        x -= 1
        prec, a, c = _get_spouge_coefficients(Float.getprec()+7)
        s = _spouge_sum(x, prec, a, c)
        if not isinstance(x, (Float, ComplexFloat)):
            x = Float(x)
        # TODO: higher precision may be needed here when the precision
        # and/or size of x are extremely large
        Float._prec += 10
        g = exp(log(x+a)*(x+Float(0.5))) * exp(-x-a) * s

    Float.revert()
    return +g
Beispiel #7
0
    def __init__(self, eps, m, verbose=False):
        # Compute abscissas and weights

        prec = Float.getdps()

        self.prec = prec
        self.eps = eps
        self.m = m
        self.h = h = Float(1) / 2**m
        self.x = []
        self.w = []

        if (eps, m) in _tscache:
            self.x, self.w = _tscache[(eps, m)]
            return

        if verbose:
            print ("calculating nodes for tanh-sinh quadrature with "
                "epsilon %s and degree %i" % (eps, m))

        Float.store()
        Float.setdps(prec + 10)

        for k in xrange(20 * 2**m + 1):
            x, w = _tsnode(k, h)
            self.x.append(x)
            self.w.append(w)
            diff = abs(self.x[-1] - Float(1))
            if diff <= eps:
                break
            if verbose and m > 6 and k % 300 == 299:
                Float.store(); Float.setdps(5)
                print "  progress", -log(diff, 10) / prec
                Float.revert()

        _tscache[(eps, m)] = self.x, self.w

        Float.revert()
Beispiel #8
0
    def __init__(self, n=None, verbose=False):
        self.n = n
        prec = dps = Float.getdps()
        self.prec = prec

        # Reuse old nodes
        if n in _gausscache:
            cacheddps, xs, ws = _gausscache[n]
            if cacheddps >= dps:
                self.x = [x for x in xs]
                self.w = [w for w in ws]
                return

        if verbose:
            print ("calculating nodes for degree-%i Gauss-Legendre "
                "quadrature..." % n)

        Float.store()
        Float.setdps(2*prec + 5)

        self.x = [None] * n
        self.w = [None] * n

        pf = polyfunc(legendre(n, 'x'), True)

        for k in xrange(n//2 + 1):
            if verbose and k % 4 == 0:
                print "  node", k, "of", n//2
            x, w = _gaussnode(pf, k, n)
            self.x[k] = x
            self.x[n-k-1] = -x
            self.w[k] = self.w[n-k-1] = w

        _gausscache[n] = (dps, self.x, self.w)

        Float.revert()
Beispiel #9
0
def nintegrate(f, a, b, method=0, maxsteps=5000, verbose=False):
    """
    Basic usage
    ===========

    nintegrate(f, a, b) numerically evaluates the integral

           - b
          |    f(x) dx.
         -   a


    The integrand f should be a callable that accepts a Float as input
    and outputs a Float or ComplexFloat; a and b should be Floats,
    numbers that can be converted to Floats, or +/- infinity.

    A simple example:

        >>> Float.setdps(15)
        >>> print nintegrate(lambda x: 2*x**2 - 4*x, 2, 3)
        2.66666666666667

    Calculating the area of a unit circle, with 30 digits:

        >>> Float.setdps(30)
        >>> print nintegrate(lambda x: 4*sqrt(1-x**2), 0, 1)
        3.14159265358979323846264338328

    The integration interval can be infinite or semi-infinite:
    
        >>> Float.setdps(15)
        >>> print nintegrate(lambda x: exp(-x)*sin(x), 0, oo)
        0.5

    Integration methods and accuracy
    ================================

    Nintegrate attempts to obtain a value that is fully accurate within
    the current working precision (i.e., correct to 15 decimal places
    at the default precision level). If nintegrate fails to reach full
    accuracy after a certain number of steps, it prints a warning
    message.

    This message signifies either that the integral is either divergent
    or, if convergent, ill-behaved. It may still be possible to
    evaluate an ill-behaved integral by increasing the 'maxsteps'
    setting, changing the integration method, and/or manually
    transforming the integrand.

    Nintegrate currently supports the following integration methods:

        method = 0  :  Gaussian quadrature (default)
        method = 1  :  tanh-sinh quadrature

    Gaussian quadrature is generally very efficient if the integration
    interval is finite and the integrand is smooth on the entire range
    (including the endpoints). It may fail if the integrand has many
    discontinuities, is highly oscillatory, or possesses integrable
    singularities.

    The tanh-sinh algorithm is often better if the integration interval
    is infinite or if singularities are present at the endpoints;
    especially at very high precision levels. It does not perform well
    if there are singularities between the endpoints or the integrand
    is bumpy or oscillatory.

    It may help to manually transform the integrand, e.g. changing
    variables to remove singularities or breaking up the integration
    interval so that singularities appear only at the endpoints.

    The 'verbose' flag can be set to track the computation's progress.
    """
    dps = Float.getdps()
    Float.store()
    Float.setdps(dps + 3)
    prec = Float.getprec()

    # Transform infinite or semi-infinite interval to a finite interval
    if a == -oo or b == oo:
        g = f
        if a == -oo and b == oo:
            def f(x):
                # make adaptive quadrature work from the left
                x = 1 - x
                return g(x) + g(Float(1)/x)/x**2 + g(-x) + g(Float(-1)/x)/(-x)**2
        elif b == oo:
            aa = Float(a)
            def f(x):
                x = 1 - x
                return g(x + aa) + g(Float(1)/x + aa)/x**2
        elif a == -oo:
            bb = Float(b)
            def f(x):
                x = 1 - x
                return g(-x + bb) + g(Float(-1)/x + bb)/(-x)**2
        a, b = Float(0), Float(1)
    else:
        a, b = Float(a), Float(b)

    eps = Float((1, -prec+4))

    if method == 0:
        degree = int(5 + dps**0.8)
        rule = CompositeGaussLegendre(degree, degree//2, verbose)
    elif method == 1:
        rule = AdaptiveTanhSinh(initial = int(3 + max(0, math.log(prec/30.0, 2))))

    else:
        Float.revert()
        raise ValueError("unknown method")

    s, err, steps = rule.adaptive(f, Float(a), Float(b), eps, steps=0, maxsteps=maxsteps, verbose=verbose)

    Float.revert()

    if not err.ae(0):
        Float.store()
        Float.setdps(1)
        print "Warning: failed to reach full accuracy.", \
            "Estimated magnitude of error: ", str(err)
        Float.revert()

    return +s
Beispiel #10
0
def evalf(expr):
    """
    evalf(expr) attempts to evaluate a SymPy expression to a Float or
    ComplexFloat with an error smaller than 10**(-Float.getdps())
    """

    if isinstance(expr, (Float, ComplexFloat)):
        return expr
    elif isinstance(expr, (int, float)):
        return Float(expr)
    elif isinstance(expr, complex):
        return ComplexFloat(expr)

    expr = Basic.sympify(expr)

    if isinstance(expr, (Rational)):
        y = Float(expr)
    elif isinstance(expr, Real):
        y = Float(str(expr))

    elif expr is I:
        y = ComplexFloat(0,1)

    elif expr is pi:
        y = constants.pi_float()

    elif expr is E:
        y = functions.exp(1)

    elif isinstance(expr, Mul):
        factors = expr[:]
        workprec = Float.getprec() + 1 + len(factors)
        Float.store()
        Float.setprec(workprec)
        y = Float(1)
        for f in factors:
            y *= evalf(f)
        Float.revert()

    elif isinstance(expr, Pow):
        base, expt = expr[:]
        workprec = Float.getprec() + 8 # may need more
        Float.store()
        Float.setprec(workprec)
        base = evalf(base)
        expt = evalf(expt)
        if expt == 0.5:
            y = functions.sqrt(base)
        else:
            y = functions.exp(functions.log(base) * expt)
        Float.revert()

    elif isinstance(expr, Basic.exp):
        Float.store()
        Float.setprec(Float.getprec() + 3)
        #XXX: how is it possible, that this works:
        x = evalf(expr[0])
        #and this too:
        #x = evalf(expr[1])
        #?? (Try to uncomment it and you'll see)
        y = functions.exp(x)
        Float.revert()

    elif isinstance(expr, Add):
        # TODO: this doesn't yet work as it should.
        # We need some way to handle sums whose results are
        # very close to 0, and when necessary, repeat the
        # summation with higher precision
        reqprec = Float.getprec()
        Float.store()
        Float.setprec(10)
        terms = expr[:]
        approxterms = [abs(evalf(x)) for x in terms]
        min_mag = min(x.exp for x in approxterms)
        max_mag = max(x.exp+bitcount(x.man) for x in approxterms)
        Float.setprec(reqprec - 10 + max_mag - min_mag + 1 + len(terms))
        workprec = Float.getdps()
        y = 0
        for t in terms:
            y += evalf(t)
        Float.revert()

    else:
        # print expr, expr.__class__
        raise NotImplementedError

    # print expr, y

    return +y