Sto vivendo una certa frustrazione per il modo in cui matlab gestisce l'integrazione numerica rispetto a Scipy. Osservo le seguenti differenze nel mio codice di prova di seguito:
- La versione di Matlab funziona in media 24 volte più veloce del mio equivalente di Python!
- La versione di Matlab è in grado di calcolare l'integrale senza avvisi, mentre python ritorna
nan+nanj
Cosa posso fare per assicurarmi di ottenere le stesse prestazioni in Python rispetto ai due punti menzionati? Secondo la documentazione, entrambi i metodi dovrebbero usare una "quadratura adattativa globale" per approssimare l'integrale.
Di seguito è riportato il codice nelle due versioni (abbastanza simile sebbene Python richieda che venga creata una funzione integrale in modo che possa gestire integrandi complessi).
Pitone
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
). Prova a fare calcoli sull'intero array contemporaneamente. Se ciò non è possibile, dai un'occhiata a numba o anche a Cython, ma spero che quest'ultimo non sia necessario.
integral
le tolleranze assolute e relative predefinite sono 1e-10
e 1e-6
, rispettivamente. integrate.quad
specifica questi entrambi come 1.49e-8
. Non vedo dove integrate.quad
sia descritto come un metodo "adattativo globale" ed è sicuramente diverso dal metodo (adattivo Gauss-Kronrod, credo) usato da integral
. Non sono sicuro di cosa significhi la parte "globale", me stesso. Inoltre, non è mai una buona idea usare al cputime
posto di tic
/ toc
o time it
.