Matlab의 통합이 Scipy에서 통합 성능을 능가하는 이유는 무엇입니까?


13

matlab이 숫자 통합과 Scipy를 처리하는 방식에 약간의 좌절이 있습니다. 아래 테스트 코드에서 다음과 같은 차이점이 있습니다.

  1. Matlab의 버전은 내 파이썬 버전 보다 평균 24 배 빠릅니다 !
  2. Matlab의 버전은 경고없이 정수를 계산할 수 있으며 파이썬은 반환합니다 nan+nanj

언급 된 두 가지 점과 관련하여 파이썬에서 동일한 성능을 얻으려면 어떻게해야합니까? 문서에 따르면 두 방법 모두 적분을 근사하기 위해 "전역 적응 구적법"을 사용해야합니다.

아래는 두 버전의 코드입니다 (파이썬은 복잡한 정수를 처리 할 수 ​​있도록 정수 함수를 작성해야하지만 상당히 유사합니다).

파이썬

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

4
파이썬이 25 배 더 느리고 250 배가 아니라는 점에 만족해야합니다.
stali

4
반복해서 파이썬 함수를 반복해서 호출하기 때문에 (숨겨 짐 np.vectorize). 전체 배열에서 한 번에 계산을 시도하십시오. 그것은 불가능하지만, numba 또는 Cython을 살펴보십시오. 그러나 후자가 필요하지 않기를 바랍니다.
sebix

2
"글로벌 적응 구적법"은 특정 정밀도에 도달 할 때까지 적응한다는 것을 나타냅니다. 같은 것을 비교하고 있는지 확인하려면 정밀도를 설정하고 두 매개 변수에 대해 설정하는 매개 변수 (하나가 있어야 함)를 찾으십시오.
bgschaid

2
@bgschaid의 의견과 관련하여 integral의 기본 절대 및 상대 공차는 각각 1e-101e-6입니다. integrate.quad둘 다로 지정합니다 1.49e-8. 나는 integrate.quad"전역 적응 적"방법으로 설명되는 곳을 보지 못하고에 의해 사용되는 (적응 가우스-크론로드, 나는 믿는) 방법과 가장 확실히 다릅니다 integral. "글로벌"부분이 무엇을 의미하는지 잘 모르겠습니다. 또한 / 또는 cputime대신에 사용하는 것은 좋은 생각이 아닙니다 . tictoctime it
horchler

5
문제가 알고리즘이나 언어인지 확인하기 전에 함수 내에서 증가하는 전역 카운터 변수를 추가하십시오. 통합 후에는 각 함수가 얼마나 자주 평가되는지 알려줍니다. 이러한 카운터는 크게 다음 다른 문제의 적어도 일부에서 MATLAB 더 나은 알고리즘을 사용하는 경우
bgschaid

답변:


15

이 질문에는 두 가지 매우 다른 하위 질문이 있습니다. 첫 번째 문제 만 다룰 것입니다.

Matlab의 버전은 내 파이썬 버전 보다 평균 24 배 빠릅니다 !

두 번째는 주관적입니다. 나는 사용자에게 적분에 문제가 있음을 알리는 것이 좋은 것이며이 SciPy 동작은 Matlab보다 성능이 뛰어나며 침묵을 유지하고 어떻게 든 Matlab 엔지니어들만 알고있는 방식으로 내부적으로 처리하려고 시도한다고 말합니다. 최고라고 결정했습니다.

NaN waring을 피하기 위해 통합 범위를 0 에서 30으로 ( 0 에서 np.inf 대신 ) 변경하고 JIT 컴파일을 추가했습니다. 솔루션을 벤치마킹하기 위해 통합을 300 회 반복했습니다. 결과는 랩톱에서 나왔습니다.

JIT 컴파일이없는 경우 :

$ ./test_integrate.py
34.20992112159729
(0.2618828053067563+0.24474506983644717j)

JIT 컴파일

$ ./test_integrate.py
0.8560323715209961
(0.261882805306756+0.24474506983644712j)

이 방법으로 두 줄의 코드를 추가 하면 비 JIT 버전에 비해 약 40 배 의 Python 코드 속도가 향상 됩니다. 더 나은 비교를 제공 할 랩톱에는 Matlab이 없지만 24/40 = 0.6보다 PC에 잘 맞으면 JIT가있는 Python은 Matlab보다 두 배 빠릅니다 .이 특정 사용자 알고리즘. 전체 코드 :

#!/usr/bin/env python3
import numpy as np
from scipy import integrate
from numba import complex128,float64,jit
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)


@jit(complex128(float64, float64), nopython=True, cache=True)
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
for i in range(300): 
    #result = integral(f_integrand, 0, np.inf, omega)
    result = integral(f_integrand, 0, 30, omega)
print (time.time()-t0)
print (result)

PC의 차이점을 보려면 @jit 줄을 주석으로 처리하십시오.


1

때때로 통합 기능을 JIT 할 수 없습니다. 이 경우 다른 통합 방법을 사용하는 것이 해결책입니다.

나는 추천한다 scipy.integrate.romberg (ref) . romberg복잡한 함수를 통합하고 함수를 배열로 평가할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.