Я испытываю некоторое разочарование по поводу того, как Matlab обрабатывает числовую интеграцию против Scipy. Я наблюдаю следующие различия в моем тестовом коде ниже:
- Версия Matlab работает в среднем в 24 раза быстрее, чем мой эквивалент на Python!
- Версия Matlab способна вычислить интеграл без предупреждений, а Python возвращает
nan+nanj
Что я могу сделать, чтобы обеспечить одинаковую производительность в Python в отношении двух упомянутых пунктов? Согласно документации оба метода должны использовать «глобальную адаптивную квадратуру» для аппроксимации интеграла.
Ниже приведен код в двух версиях (довольно схожий, хотя 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
matlab
python
quadrature
numba
диполь
источник
источник
np.vectorize
). Попробуйте выполнить вычисления для всего массива одновременно. Если это невозможно, взгляните на numba или Cython, но я надеюсь, что последнее не нужно.integral
умолчанию абсолютные и относительные допуски равны1e-10
и1e-6
, соответственно.integrate.quad
указывает эти оба как1.49e-8
. Я не вижу, гдеintegrate.quad
описывается как «глобальный адаптивный» метод, и он, безусловно, отличается от (по-моему, адаптивного метода Гаусса-Кронрода)integral
. Я сам не уверен, что означает «глобальная» часть. Кроме того, никогда не стоит использоватьcputime
вместоtic
/toc
илиtime it
.Ответы:
Вопрос имеет два очень разных подвопроса. Я остановлюсь только на первом.
Второй субъективен. Я бы сказал, что информирование пользователя о том, что существует некоторая проблема с интегралом, - это хорошо, и такое поведение SciPy превосходит поведение Matlab, позволяя ему молчать и каким-то образом пытаться справиться с ним внутренне, как это известно только инженерам Matlab, которые решил, что это будет лучшим.
Я изменил интервал интеграции, чтобы быть от 0 до 30 (вместо 0 до np.inf). ), чтобы избежать обработки NaN, и добавил компиляцию JIT. Для оценки решения я повторил интеграцию 300 раз, результаты получены с моего ноутбука.
Без JIT-компиляции:
С компиляцией JIT:
Таким образом, добавление двух строк кода приводит к увеличению скорости кода Python примерно в 40 раз по сравнению с версией без JIT. У меня на ноутбуке нет Matlab для лучшего сравнения, однако, если он хорошо масштабируется для вашего ПК, чем 24/40 = 0,6, поэтому Python с JIT должен быть почти в два раза быстрее, чем Matlab для этого конкретного пользовательского алгоритма. Полный код:
Закомментируйте строку @jit, чтобы увидеть разницу для вашего ПК.
источник
Иногда функция для интеграции не может быть JITed. В этом случае использование другого метода интеграции будет решением.
Я бы порекомендовал
scipy.integrate.romberg
(ссылка) .romberg
может интегрировать сложные функции и может оценивать функцию с массивом.источник