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()
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()
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
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