I am experiencing some frustration over the way matlab handles numerical integration vs. Scipy. I observe the following differences in my test code below:
- Matlab's version runs on average 24 times faster than my python equivalent!
- Matlab's version is able to calculate the integral without warnings, while python returns
nan+nanj
What can I do to ensure I get the same performance in python with respect to the two points mentioned? According to documentation both methods should be using a "global adaptive quadrature" to approximate the integral.
Below is the code in the two versions (fairly similar although python requires that an integral function is created so that it can handle complex integrands.)
Python
import numpy as np
from scipy import integrate
import time
def integral(integrand, a, b, arg):
def real_func(x,arg):
return np.real(integrand(x,arg))
def imag_func(x,arg):
return np.imag(integrand(x,arg))
real_integral = integrate.quad(real_func, a, b, args=(arg))
imag_integral = integrate.quad(imag_func, a, b, args=(arg))
return real_integral[0] + 1j*imag_integral[0]
vintegral = np.vectorize(integral)
def f_integrand(s, omega):
sigma = np.pi/(np.pi+2)
xs = np.exp(-np.pi*s/(2*sigma))
x1 = -2*sigma/np.pi*(np.log(xs/(1+np.sqrt(1-xs**2)))+np.sqrt(1-xs**2))
x2 = 1-2*sigma/np.pi*(1-xs)
zeta = x2+x1*1j
Vc = 1/(2*sigma)
theta = -1*np.arcsin(np.exp(-np.pi/(2.0*sigma)*s))
t1 = 1/np.sqrt(1+np.tan(theta)**2)
t2 = -1/np.sqrt(1+1/np.tan(theta)**2)
return np.real((t1-1j*t2)/np.sqrt(zeta**2-1))*np.exp(1j*omega*s/Vc);
t0 = time.time()
omega = 10
result = integral(f_integrand, 0, np.inf, omega)
print time.time()-t0
print result
Matlab
function [ out ] = f_integrand( s, omega )
sigma = pi/(pi+2);
xs = exp(-pi.*s./(2*sigma));
x1 = -2*sigma./pi.*(log(xs./(1+sqrt(1-xs.^2)))+sqrt(1-xs.^2));
x2 = 1-2*sigma./pi.*(1-xs);
zeta = x2+x1*1j;
Vc = 1/(2*sigma);
theta = -1*asin(exp(-pi./(2.0.*sigma).*s));
t1 = 1./sqrt(1+tan(theta).^2);
t2 = -1./sqrt(1+1./tan(theta).^2);
out = real((t1-1j.*t2)./sqrt(zeta.^2-1)).*exp(1j.*omega.*s./Vc);
end
t=cputime;
omega = 10;
result = integral(@(s) f_integrand(s,omega),0,Inf)
time_taken = cputime-t
np.vectorize
). Try doing calculations on the entire array at once. It that is not possible, have a look at numba or also Cython, but I hope the latter is not necessary. $\endgroup$integral
's default absolute and relative tolerances are1e-10
and1e-6
, respectively.integrate.quad
specifies these both as1.49e-8
. I don't see whereintegrate.quad
is described as a "global adaptive" method and it is most certainly different from the (adaptive Gauss-Kronrod, I believe) method used byintegral
. I'm not sure what the "global" part means, myself. Also, it's never a good idea to be usingcputime
instead oftic
/toc
ortime it
. $\endgroup$