1

There is a MATLAB function quadgk that can compute complex integrals, or at least functions with poles and singularities. In Python, there is a general-purpose scipy.integrate.quad which is handy for integration along the real axis. Is there a Python equivalent to MATLAB's quadgk? All I could find was dr jimbob's code in another SO question, which doesn't seem to work on Python 3.4 any more.

Community
  • 1
  • 1
Medulla Oblongata
  • 3,771
  • 8
  • 36
  • 75
  • I suggest that you emphasize that you need a complex *contour*. For a real contour you could still use `scipy.integrate.quad` to compute the real and imaginary parts separately. If all else fails, you could do it by hand still: define a front-end function that defines the linear path `a1+i*a2` and `b1+i*b2` for two complex limits, with a real parameter between them, and use `quad` on the resulting real and imaginary parts... I admit it's far from pretty, and I hope there's a simpler, ready-made solution:) – Andras Deak -- Слава Україні Dec 08 '15 at 13:58

1 Answers1

1

I don't think SciPy does provide an equivalent of MATLAB's quadgk, but for what it's worth the code you link to in this question can be made to work in Python 3 with only minor changes:

import scipy
from scipy import array

def quad_routine(func, a, b, x_list, w_list):
    c_1 = (b-a)/2.0
    c_2 = (b+a)/2.0
    eval_points = map(lambda x: c_1*x+c_2, x_list)
    func_evals = list(map(func, eval_points))    # Python 3: make a list here
    return c_1 * sum(array(func_evals) * array(w_list))

def quad_gauss_7(func, a, b):
    x_gauss = [-0.949107912342759, -0.741531185599394, -0.405845151377397, 0, 0.405845151377397, 0.741531185599394, 0.949107912342759]
    w_gauss = array([0.129484966168870, 0.279705391489277, 0.381830050505119, 0.417959183673469, 0.381830050505119, 0.279705391489277,0.129484966168870])
    return quad_routine(func,a,b,x_gauss, w_gauss)

def quad_kronrod_15(func, a, b):
    x_kr = [-0.991455371120813,-0.949107912342759, -0.864864423359769, -0.741531185599394, -0.586087235467691,-0.405845151377397, -0.207784955007898, 0.0, 0.207784955007898,0.405845151377397, 0.586087235467691, 0.741531185599394, 0.864864423359769, 0.949107912342759, 0.991455371120813]
    w_kr = [0.022935322010529, 0.063092092629979, 0.104790010322250, 0.140653259715525, 0.169004726639267, 0.190350578064785, 0.204432940075298, 0.209482141084728, 0.204432940075298, 0.190350578064785, 0.169004726639267, 0.140653259715525,  0.104790010322250, 0.063092092629979, 0.022935322010529]
    return quad_routine(func,a,b,x_kr, w_kr)

class Memoize:                     # Python 3: no need to inherit from object
    def __init__(self, func):
        self.func = func
        self.eval_points = {}
    def __call__(self, *args):
        if args not in self.eval_points:
            self.eval_points[args] = self.func(*args)
        return self.eval_points[args]

def quad(func,a,b):
    ''' Output is the 15 point estimate; and the estimated error '''
    func = Memoize(func) #  Memoize function to skip repeated function calls.
    g7 = quad_gauss_7(func,a,b)
    k15 = quad_kronrod_15(func,a,b)
    # I don't have much faith in this error estimate taken from wikipedia
    # without incorporating how it should scale with changing limits
    return [k15, (200*scipy.absolute(g7-k15))**1.5]

For example,

print(quad(lambda x: scipy.exp(1j*x), 0,scipy.pi/2.0))
[(0.99999999999999711+0.99999999999999689j), 9.6120083407040365e-19]
Community
  • 1
  • 1
xnx
  • 24,509
  • 11
  • 70
  • 109