예제 #1
0
def diff(f, x, direction=0):
    """
    Compute f'(x) using a simple finite difference approximation.

    With direction = 0, use the central difference f(x-h), f(x+h)
    With direction = 1, use the forward difference f(x), f(x+h)
    With direction = -1, use the backward difference f(x-h), f(x)

        >>> print diff(cos, 1)
        -0.841470984807897
        >>> print diff(abs, 0, 0)
        0.0
        >>> print diff(abs, 0, 1)
        1.0
        >>> print diff(abs, 0, -1)
        -1.0

    The step size is taken similar to the epsilon of the precision.
    To eliminate cancellation errors, diff temporarily doubles the
    working precision while calculating the function values.
    """
    prec = mp.prec
    extra = 5
    h = ldexp(1, -prec-extra)
    try:
        mp.prec = 2*(prec+extra)
        if   direction == 0:  return (f(x+h) - f(x-h)) * ldexp(1, prec+extra-1)
        elif direction == 1:  return (f(x+h) - f(x)) * ldexp(1, prec+extra)
        elif direction == -1: return (f(x) - f(x-h)) * ldexp(1, prec+extra)
        else:
            raise ValueError("invalid difference direction: %r" % direction)
    finally:
        mp.prec = prec
예제 #2
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
예제 #3
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
예제 #4
0
 def __iter__(self):
     f = self.f
     a = self.a
     b = self.b
     l = b - a
     fb = f(b)
     while True:
         m = ldexp(a + b, -1)
         fm = f(m)
         if fm * fb < 0:
             a = m
         else:
             b = m
             fb = fm
         l /= 2
         yield (a + b) / 2, abs(l)
예제 #5
0
 def __iter__(self):
     f = self.f
     a = self.a
     b = self.b
     l = b - a
     fb = f(b)
     while True:
         m = ldexp(a + b, -1)
         fm = f(m)
         if fm * fb < 0:
             a = m
         else:
             b = m
             fb = fm
         l /= 2
         yield (a + b)/2, abs(l)
예제 #6
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
예제 #7
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
예제 #8
0
def ODE_step_rk4(x, y, h, derivs):
    """
    Advances the solution y(x) from x to x+h using the 4th-order Runge-Kutta
    method.

    derivs .... a python function f(x, (y1, y2, y3, ...)) returning
    a tuple (y1', y2', y3', ...) where y1' is the derivative of y1 at x.
    """
    h2 = ldexp(h, -1)
    third = mpf(1)/3
    k1 = smul(h, derivs(y, x))
    k2 = smul(h, derivs(vadd(y, smul(half, k1)), x+h2))
    k3 = smul(h, derivs(vadd(y, smul(half, k2)), x+h2))
    k4 = smul(h, derivs(vadd(y, k3), x+h))
    v = []
    for i in range(len(y)):
        v.append(y[i] + third*(k2[i]+k3[i] + half*(k1[i]+k4[i])))
    return v
예제 #9
0
파일: odes.py 프로젝트: fperez/sympy
def ode_taylor(derivs, x0, y0, tol_prec, n):
    h = tol = ldexp(1, -tol_prec)
    dim = len(y0)
    xs = [x0]
    ys = [y0]
    x = x0
    y = y0
    orig = mp.prec
    try:
        mp.prec = orig*(1+n)
        # Use n steps with Euler's method to get
        # evaluation points for derivatives
        for i in range(n):
            fxy = derivs(x, y)
            y = [y[i]+h*fxy[i] for i in xrange(len(y))]
            x += h
            xs.append(x)
            ys.append(y)
        # Compute derivatives
        ser = [[] for d in range(dim)]
        for j in range(n+1):
            s = [0]*dim
            b = (-1) ** (j & 1)
            k = 1
            for i in range(j+1):
                for d in range(dim):
                    s[d] += b * ys[i][d]
                b = (b * (j-k+1)) // (-k)
                k += 1
            scale = h**(-j) / fac(j)
            for d in range(dim):
                s[d] = s[d] * scale
                ser[d].append(s[d])
    finally:
        mp.prec = orig
    # Estimate radius for which we can get full accuracy.
    # XXX: do this right for zeros
    radius = mpf(1)
    for ts in ser:
        if ts[-1]:
            radius = min(radius, nthroot(tol/abs(ts[-1]), n))
    radius /= 2  # XXX
    return ser, x0+radius
예제 #10
0
def ode_taylor(derivs, x0, y0, tol_prec, n):
    h = tol = ldexp(1, -tol_prec)
    dim = len(y0)
    xs = [x0]
    ys = [y0]
    x = x0
    y = y0
    orig = mp.prec
    try:
        mp.prec = orig*(1+n)
        # Use n steps with Euler's method to get
        # evaluation points for derivatives
        for i in range(n):
            fxy = derivs(x, y)
            y = [y[i]+h*fxy[i] for i in xrange(len(y))]
            x += h
            xs.append(x)
            ys.append(y)
        # Compute derivatives
        ser = [[] for d in range(dim)]
        for j in range(n+1):
            s = [0]*dim
            b = (-1) ** (j & 1)
            k = 1
            for i in range(j+1):
                for d in range(dim):
                    s[d] += b * ys[i][d]
                b = (b * (j-k+1)) // (-k)
                k += 1
            scale = h**(-j) / fac(j)
            for d in range(dim):
                s[d] = s[d] * scale
                ser[d].append(s[d])
    finally:
        mp.prec = orig
    # Estimate radius for which we can get full accuracy.
    # XXX: do this right for zeros
    radius = mpf(1)
    for ts in ser:
        if ts[-1]:
            radius = min(radius, nthroot(tol/abs(ts[-1]), n))
    radius /= 2  # XXX
    return ser, x0+radius
예제 #11
0
    def calc_nodes(cls, prec, level, verbose=False):
        """
        The abscissas and weights for tanh-sinh quadrature are given by

            x[k] = tanh(pi/2 * sinh(t))
            w[k] = pi/2 * cosh(t) / cosh(pi/2 sinh(t))**2

        Here t varies uniformly with k: t0, t0+h, t0+2*h, ...

        The list of nodes is actually infinite, but the weights
        die off so rapidly that only a few are needed.
        """
        nodes = []

        extra = 20
        mp.prec += extra
        eps = ldexp(1, -prec-10)
        pi4 = pi/4

        # For simplicity, we work in steps h = 1/2^n, with the first point
        # offset so that we can reuse the sum from the previous level

        # We define level 1 to include the "level 0" steps, including
        # the point x = 0. (It doesn't work well otherwise; not sure why.)
        t0 = ldexp(1, -level)
        if level == 1:
            nodes.append((mpf(0), pi4))
            h = t0
        else:
            h = t0*2

        # Since h is fixed, we can compute the next exponential
        # by simply multiplying by exp(h)
        expt0 = exp(t0)
        a = pi4 * expt0
        b = pi4 / expt0
        udelta = exp(h)
        urdelta = 1/udelta

        for k in xrange(0, 20*2**level+1):
            # Reference implementation:
            # t = t0 + k*h
            # x = tanh(pi/2 * sinh(t))
            # w = pi/2 * cosh(t) / cosh(pi/2 * sinh(t))**2

            # Fast implementation. Note that c = exp(pi/2 * sinh(t))
            c = exp(a-b)
            d = 1/c
            co = (c+d)/2
            si = (c-d)/2
            x = si / co
            w = (a+b) / co**2
            diff = abs(x-1)
            if diff <= eps:
                break

            nodes.append((x, w))
            a *= udelta
            b *= urdelta

            if verbose and k % 300 == 150:
                # Note: the number displayed is rather arbitrary. Should
                # figure out how to print something that looks more like a
                # percentage
                print "Calculating nodes:", nstr(-log(diff, 10) / prec)

        mp.prec -= extra
        return nodes
예제 #12
0
    def calc_nodes(self, degree, prec, verbose=False):
        r"""
        The abscissas and weights for tanh-sinh quadrature of degree
        `m` are given by

        .. math::

            x_k = \tanh(\pi/2 \sinh(t_k))

            w_k = \pi/2 \cosh(t_k) / \cosh(\pi/2 \sinh(t_k))^2

        where `t_k = t_0 + hk` for a step length `h \sim 2^{-m}`. The
        list of nodes is actually infinite, but the weights die off so
        rapidly that only a few are needed.
        """
        nodes = []

        extra = 20
        mp.prec += extra
        eps = ldexp(1, -prec-10)
        pi4 = pi/4

        # For simplicity, we work in steps h = 1/2^n, with the first point
        # offset so that we can reuse the sum from the previous degree

        # We define degree 1 to include the "degree 0" steps, including
        # the point x = 0. (It doesn't work well otherwise; not sure why.)
        t0 = ldexp(1, -degree)
        if degree == 1:
            #nodes.append((mpf(0), pi4))
            #nodes.append((-mpf(0), pi4))
            nodes.append((mpf(0), pi/2))
            h = t0
        else:
            h = t0*2

        # Since h is fixed, we can compute the next exponential
        # by simply multiplying by exp(h)
        expt0 = exp(t0)
        a = pi4 * expt0
        b = pi4 / expt0
        udelta = exp(h)
        urdelta = 1/udelta

        for k in xrange(0, 20*2**degree+1):
            # Reference implementation:
            # t = t0 + k*h
            # x = tanh(pi/2 * sinh(t))
            # w = pi/2 * cosh(t) / cosh(pi/2 * sinh(t))**2

            # Fast implementation. Note that c = exp(pi/2 * sinh(t))
            c = exp(a-b)
            d = 1/c
            co = (c+d)/2
            si = (c-d)/2
            x = si / co
            w = (a+b) / co**2
            diff = abs(x-1)
            if diff <= eps:
                break

            nodes.append((x, w))
            nodes.append((NEG(x), w))

            a *= udelta
            b *= urdelta

            if verbose and k % 300 == 150:
                # Note: the number displayed is rather arbitrary. Should
                # figure out how to print something that looks more like a
                # percentage
                print "Calculating nodes:", nstr(-log(diff, 10) / prec)

        mp.prec -= extra
        return nodes
예제 #13
0
def polyroots(coeffs, maxsteps=50, cleanup=True, extraprec=10, error=False):
    """
    Numerically locate all (complex) roots of a polynomial using the
    Durand-Kerner method.

    With error=True, this function returns a tuple (roots, err) where roots
    is a list of complex numbers sorted by absolute value, and err is an
    estimate of the maximum error. The polynomial should be given as a list
    of coefficients, in the same format as accepted by polyval(). The
    leading coefficient must be nonzero.

    These are the roots of x^3 - x^2 - 14*x + 24 and 4x^2 + 3x + 2:

        >>> nprint(polyroots([1,-1,-14,24]), 4)
        [-4.0, 2.0, 3.0]
        >>> nprint(polyroots([4,3,2], error=True))
        ([(-0.375 - 0.599479j), (-0.375 + 0.599479j)], 2.22045e-16)

    """
    if len(coeffs) <= 1:
        if not coeffs or not coeffs[0]:
            raise ValueError("Input to polyroots must not be the zero polynomial")
        # Constant polynomial with no roots
        return []

    orig = mp.prec
    weps = +eps
    try:
        mp.prec += 10
        deg = len(coeffs) - 1
        # Must be monic
        lead = convert_lossless(coeffs[0])
        if lead == 1:
            coeffs = map(convert_lossless, coeffs)
        else:
            coeffs = [c/lead for c in coeffs]
        f = lambda x: polyval(coeffs, x)
        roots = [mpc((0.4+0.9j)**n) for n in xrange(deg)]
        err = [mpf(1) for n in xrange(deg)]
        for step in xrange(maxsteps):
            if max(err).ae(0):
                break
            for i in xrange(deg):
                if not err[i].ae(0):
                    p = roots[i]
                    x = f(p)
                    for j in range(deg):
                        if i != j:
                            try:
                                x /= (p-roots[j])
                            except ZeroDivisionError:
                                continue
                    roots[i] = p - x
                    err[i] = abs(x)
        if cleanup:
            for i in xrange(deg):
                if abs(roots[i].imag) < weps:
                    roots[i] = roots[i].real
                elif abs(roots[i].real) < weps:
                    roots[i] = roots[i].imag * 1j
        roots.sort(key=lambda x: (abs(x.imag), x.real))
    finally:
        mp.prec = orig
    if error:
        err = max(err)
        err = max(err, ldexp(1, -orig+1))
        return [+r for r in roots], +err
    else:
        return [+r for r in roots]
예제 #14
0
    def calc_nodes(self, degree, prec, verbose=False):
        r"""
        The abscissas and weights for tanh-sinh quadrature of degree
        `m` are given by

        .. math::

            x_k = \tanh(\pi/2 \sinh(t_k))

            w_k = \pi/2 \cosh(t_k) / \cosh(\pi/2 \sinh(t_k))^2

        where `t_k = t_0 + hk` for a step length `h \sim 2^{-m}`. The
        list of nodes is actually infinite, but the weights die off so
        rapidly that only a few are needed.
        """
        nodes = []

        extra = 20
        mp.prec += extra
        eps = ldexp(1, -prec-10)
        pi4 = pi/4

        # For simplicity, we work in steps h = 1/2^n, with the first point
        # offset so that we can reuse the sum from the previous degree

        # We define degree 1 to include the "degree 0" steps, including
        # the point x = 0. (It doesn't work well otherwise; not sure why.)
        t0 = ldexp(1, -degree)
        if degree == 1:
            #nodes.append((mpf(0), pi4))
            #nodes.append((-mpf(0), pi4))
            nodes.append((mpf(0), pi/2))
            h = t0
        else:
            h = t0*2

        # Since h is fixed, we can compute the next exponential
        # by simply multiplying by exp(h)
        expt0 = exp(t0)
        a = pi4 * expt0
        b = pi4 / expt0
        udelta = exp(h)
        urdelta = 1/udelta

        for k in xrange(0, 20*2**degree+1):
            # Reference implementation:
            # t = t0 + k*h
            # x = tanh(pi/2 * sinh(t))
            # w = pi/2 * cosh(t) / cosh(pi/2 * sinh(t))**2

            # Fast implementation. Note that c = exp(pi/2 * sinh(t))
            c = exp(a-b)
            d = 1/c
            co = (c+d)/2
            si = (c-d)/2
            x = si / co
            w = (a+b) / co**2
            diff = abs(x-1)
            if diff <= eps:
                break

            nodes.append((x, w))
            nodes.append((NEG(x), w))

            a *= udelta
            b *= urdelta

            if verbose and k % 300 == 150:
                # Note: the number displayed is rather arbitrary. Should
                # figure out how to print something that looks more like a
                # percentage
                print "Calculating nodes:", nstr(-log(diff, 10) / prec)

        mp.prec -= extra
        return nodes
예제 #15
0
    def calc_nodes(cls, prec, level, verbose=False):
        """
        The abscissas and weights for tanh-sinh quadrature are given by

            x[k] = tanh(pi/2 * sinh(t))
            w[k] = pi/2 * cosh(t) / cosh(pi/2 sinh(t))**2

        Here t varies uniformly with k: t0, t0+h, t0+2*h, ...

        The list of nodes is actually infinite, but the weights
        die off so rapidly that only a few are needed.
        """
        nodes = []

        extra = 20
        mp.prec += extra
        eps = ldexp(1, -prec - 10)
        pi4 = pi / 4

        # For simplicity, we work in steps h = 1/2^n, with the first point
        # offset so that we can reuse the sum from the previous level

        # We define level 1 to include the "level 0" steps, including
        # the point x = 0. (It doesn't work well otherwise; not sure why.)
        t0 = ldexp(1, -level)
        if level == 1:
            nodes.append((mpf(0), pi4))
            h = t0
        else:
            h = t0 * 2

        # Since h is fixed, we can compute the next exponential
        # by simply multiplying by exp(h)
        expt0 = exp(t0)
        a = pi4 * expt0
        b = pi4 / expt0
        udelta = exp(h)
        urdelta = 1 / udelta

        for k in xrange(0, 20 * 2**level + 1):
            # Reference implementation:
            # t = t0 + k*h
            # x = tanh(pi/2 * sinh(t))
            # w = pi/2 * cosh(t) / cosh(pi/2 * sinh(t))**2

            # Fast implementation. Note that c = exp(pi/2 * sinh(t))
            c = exp(a - b)
            d = 1 / c
            co = (c + d) / 2
            si = (c - d) / 2
            x = si / co
            w = (a + b) / co**2
            diff = abs(x - 1)
            if diff <= eps:
                break

            nodes.append((x, w))
            a *= udelta
            b *= urdelta

            if verbose and k % 300 == 150:
                # Note: the number displayed is rather arbitrary. Should
                # figure out how to print something that looks more like a
                # percentage
                print "Calculating nodes:", nstr(-log(diff, 10) / prec)

        mp.prec -= extra
        return nodes