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
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
def atan2(y, x): """atan2(y,x) has the same magnitude as atan(y/x) but accounts for the signs of y and x""" if y < 0: return -atan2(-y, x) if not x and not y: return Float(0) if y > 0 and x == 0: Float._prec += 2 t = pi_float() / 2 Float._prec -= 2 return t Float._prec += 2 if x > 0: a = atan(y / x) else: a = pi_float() - atan(-y / x) Float._prec -= 2 return +a
def atan(x): """Compute atan(x) for a real number x""" if not isinstance(x, Float): x = Float(x) if x < -0.6: return _with_extraprec(2, lambda: -atan(-x)) if x < 0.6: return _atan_series_1(x) if x < 1.5: return _atan_series_2(x) # For large x, use atan(x) = pi/2 - atan(1/x) Float._prec += 4 prec = Float._prec if x.exp > 10 * prec: t = pi_float() / 2 # XXX else: t = pi_float() / 2 - atan(Float(1) / x) Float._prec -= 4 return +t
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
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
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