Search code examples
pythondecimalapproximation

Find roots of a system of equations to an arbitrary decimal precision


Given an initial guess for an array of values x, I am trying to find the root of a system that is closest to x. If you are familiar with finding roots of a system, you will understand that finding a root for a system of equations f satisfies:

0 = f_1(x)
0 = f_2(x)
....
0 = f_n(x)

Where f_i is one particular function within f

There is a package within scipy that will do this exactly: scipy.optimize.newton_krylov. For example:

import scipy.optimize as sp

def f(x):
    f0 = (x[0]**2) + (3*(x[1]**3)) - 2
    f1 = x[0] * (x[1]**2)
    return [f0, f1]
# Nearest root is [sqrt(2), 0]
print sp.newton_krylov(f, [2, .01], iter=100, f_tol=Dc('1e-15')) 

>>> [  1.41421356e+00   3.49544535e-10] # Close enough!

However, I am using the decimal package within python because I am doing extremely precise work. decimal offers more than normal decimal precision. scipy.optimize.newton_krylov returns float-precision values. Is there a way to get my answer at an arbitrarily precise decimal precision?


Solution

  • I have found the mpmath module, which contains mpmath.findroot. mpmath uses arbitrary decimal-point precision for all of its numbers. mpmath.findroot will find the nearest root within tolerance. Here is an example of using mpmath for the same problem, to a higher precision:

    import scipy.optimize as sp
    import mpmath
    from mpmath import mpf
    mpmath.mp.dps = 15
    
    def mp_f(x1, x2):
        f1 = (x1**2) + (3*(x2**3)) - 2
        f2 = x1 * (x2**2)
        return f1, f2
    
    def f(x):
        f0 = (x[0]**2) + (3*(x[1]**3)) - 2
        f1 = x[0] * (x[1]**2)
        return [f0, f1]
    
    tmp_solution = sp.newton_krylov(f, [2, .01], f_tol=Dc('1e-10'))
    print tmp_solution
    
    >>> [  1.41421356e+00   4.87315249e-06]
    
    for _ in range(8):
        tmp_solution = mpmath.findroot(mp_f, (tmp_solution[0], tmp_solution[1]))
        print tmp_solution
        mpmath.mp.dps += 10 # Increase precision
    
    >>> [    1.4142135623731]
    [4.76620313173184e-9]
    >>> [    1.414213562373095048801689]
    [4.654573673348783724565804e-12]
    >>> [    1.4142135623730950488016887242096981]
    [4.5454827012374811707063801808968925e-15]
    >>> [    1.41421356237309504880168872420969807856967188]
    [4.43894795688326535096068850443292395286770757e-18]
    >>> [    1.414213562373095048801688724209698078569671875376948073]
    [4.334910114213471839327827177504976152074382061299675453e-21]
    >>> [     1.414213562373095048801688724209698078569671875376948073176679738]
    [4.2333106584123451747941381835420647823192649980317402073699554127e-24]
    >>> [    1.41421356237309504880168872420969807856967187537694807317667973799073247846]
    [4.1340924398558139440207202654766836515453497962889870471467483995909717197e-27]
    >>> [     1.41421356237309504880168872420969807856967187537694807317667973799073247846210703885]
    [4.037199648296693366576484784520203892002447351324378380584214947262318103197216393589e-30]
    

    The precision can be raised arbitrarily.