Mam frustrację związaną ze sposobem, w jaki Matlab obsługuje integrację numeryczną vs. Scipy. Poniżej obserwuję następujące różnice w moim kodzie testowym:
- Wersja Matlaba działa średnio 24 razy szybciej niż mój odpowiednik w Pythonie!
- Wersja Matlaba jest w stanie obliczyć całkę bez ostrzeżeń, podczas gdy Python powraca
nan+nanj
Co mogę zrobić, aby uzyskać taką samą wydajność w Pythonie w odniesieniu do dwóch wymienionych punktów? Zgodnie z dokumentacją obie metody powinny wykorzystywać „globalną kwadraturę adaptacyjną” w celu przybliżenia całki.
Poniżej znajduje się kod w dwóch wersjach (dość podobny, chociaż python wymaga utworzenia funkcji całkowej, aby mogła obsługiwać złożone całki).
Pyton
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
). Spróbuj wykonać obliczenia dla całej tablicy jednocześnie. Nie jest to możliwe, spójrz na Numba lub Cython, ale mam nadzieję, że to drugie nie jest konieczne.
integral
„s domyślne bezwzględne i względne tolerancje 1e-10
i 1e-6
odpowiednio. integrate.quad
określa oba jako 1.49e-8
. Nie widzę, gdzie integrate.quad
jest opisana jako metoda „globalnej adaptacji” i na pewno różni się ona od metody (adaptacyjnej metody Gaussa-Kronroda, jak sądzę) integral
. Sam nie jestem pewien, co oznacza ta „globalna” część. Ponadto nigdy nie warto używać cputime
zamiast tic
/toc
lub time it
.