Estou com uma certa frustração com a maneira como o matlab lida com a integração numérica versus o Scipy. Observo as seguintes diferenças no meu código de teste abaixo:
- A versão do Matlab é executada em média 24 vezes mais rápido que o meu equivalente em python!
- A versão do Matlab é capaz de calcular a integral sem avisos, enquanto o python retorna
nan+nanj
O que posso fazer para garantir o mesmo desempenho em python em relação aos dois pontos mencionados? De acordo com a documentação, ambos os métodos devem usar uma "quadratura adaptativa global" para aproximar a integral.
Abaixo está o código nas duas versões (bastante semelhante, embora o python exija que uma função integral seja criada para que ele possa lidar com integrandos complexos).
Pitão
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
matlab
python
quadrature
numba
Dipolo
fonte
fonte
np.vectorize
). Tente fazer cálculos em toda a matriz de uma só vez. Isso não é possível, dê uma olhada no numba ou também no Cython, mas espero que o último não seja necessário.integral
as tolerâncias absolutas e relativas padrão são1e-10
e1e-6
, respectivamente.integrate.quad
especifica esses dois como1.49e-8
. Não vejo ondeintegrate.quad
é descrito como um método "global adaptativo" e certamente é diferente do método (adaptável de Gauss-Kronrod, acredito) usado pelointegral
. Não sei ao certo o que significa a parte "global". Além disso, nunca é uma boa ideia usar emcputime
vez detic
/toc
outime it
.Respostas:
A questão tem duas subquestões muito diferentes. Vou abordar apenas o primeiro.
O segundo é subjetivo. Eu diria que informar o usuário de que existe algum problema com a integral é uma coisa boa e esse comportamento SciPy supera o do Matlab para mantê-lo em silêncio e de alguma forma tentar lidar com ele internamente da maneira conhecida apenas pelos engenheiros da Matlab que decidiu que era o melhor.
Alterei o período de integração para 0 a 30 (em vez de 0 para np.inf ) para evitar o aviso de NaN e adicionei uma compilação JIT. Para comparar a solução, repeti a integração 300 vezes, os resultados são do meu laptop.
Sem compilação JIT:
Com a compilação JIT:
Dessa forma, adicionar duas linhas de código leva ao fator de aceleração de cerca de 40 vezes o código Python em comparação com uma versão não JIT. Não tenho Matlab no meu laptop para fornecer uma comparação melhor; no entanto, se ele for escalável para o seu PC além de 24/40 = 0,6, o Python com JIT deve ser quase duas vezes mais rápido que o Matlab para esse algoritmo de usuário específico. Código completo:
Comente a linha @jit para ver a diferença no seu PC.
fonte
Às vezes, a função a integrar não pode ser JITed. Nesse caso, usar outro método de integração seria a solução.
Eu recomendaria
scipy.integrate.romberg
(ref) .romberg
pode integrar funções complexas e pode avaliar a função com matriz.fonte