Matlabの積分がScipyでintegrated.quadより優れているのはなぜですか?


13

matlabが数値積分とScipyを処理する方法に不満を感じています。以下のテストコードで次の違いを観察します。

  1. Matlabのバージョンは、私のPythonの同等バージョンよりも平均で24倍高速に動作します!
  2. Matlabのバージョンは警告なしで積分を計算することができますが、Pythonは nan+nanj

上記の2つの点に関して、Pythonで同じパフォーマンスが得られるようにするにはどうすればよいですか?文書によると、両方の方法は、積分を近似するために「グローバル適応求積法」を使用する必要があります。

以下は、2つのバージョンのコードです(かなり似ていますが、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

4
Pythonは250倍ではなく、25倍しか遅くないことに満足すべきです。
stali 14年

4
あなたはpython関数を繰り返しループで呼び出しているからです(隠されていますnp.vectorize)。配列全体で一度に計算を試してください。それは不可能です、numbaまたはCythonを見てください、しかし後者は必要ないことを望みます。
sebix 14年

2
「グローバル適応求積法」は、一定の精度に達するまで適応することを示します。同じものを比較していることを確認するには、精度を設定し、両方に設定するパラメーター(必ず1つ)を探します。
bgschaid 14年

2
bgschaidさんのコメント@については、integralのデフォルト絶対的および相対的公差がある1e-101e-6、それぞれ。integrate.quadこれらの両方をとして指定します1.49e-8。どこintegrate.quadが「グローバルな適応」方法として説明されているかはわかりませんが、それはによって使用される(適応ガウス・クロンロッド、私は信じる)方法とは間違いなく異なりintegralます。私は、「グローバル」な部分が何を意味するのか分かりません。また、/ またはのcputime代わりに使用することはお勧めできません。tictoctime it
horchler

5
何よりも前に、問題がアルゴリズムなのか言語なのかを確認します。関数内でインクリメントされるグローバルカウンター変数を追加します。統合後、これにより各関数が評価される頻度がわかります。これらのカウンタは、少なくとも、その後大幅に異なる場合は、問題の一部は、MATLABは、より良いアルゴリズムを使用していることである
bgschaid

回答:


15

この質問には、大きく異なる2つのサブ質問があります。最初のもののみを取り上げます。

Matlabのバージョンは、私のPythonの同等バージョンよりも平均で24倍高速に動作します!

2番目は主観的です。積分に何らかの問題があることをユーザーに知らせることは良いことであり、このSciPyの動作はMatlabの動作よりも優れているため、Matlabエンジニアのみが知っている方法で、Matlabのサイレント状態を維持します。最高だと決めた。

NaN警告を回避するために、統合スパンを0から300からnp.infではなく)に変更し、JITコンパイルを追加しました。ソリューションをベンチマークするために、統合を300回繰り返しましたが、結果は私のラップトップからのものです。

JITコンパイルなし:

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

JITコンパイルの場合:

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

この方法で2行のコードを追加すると、非JITバージョンと比較してPythonコードの約40倍の速度向上要因になります。ラップトップにはMatlabがありませんが、より良い比較を提供しますが、24/40 = 0.6よりもPCにうまく拡張できる場合、JITを使用したPythonは、この特定のユーザーアルゴリズムのMatlabのほぼ2倍の速度になります。完全なコード:

#!/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)

@jit行をコメントアウトして、PCの違いを確認します。


1

統合する機能をJITできない場合があります。その場合、別の統合方法を使用することが解決策になります。

scipy.integrate.romberg (ref)をお勧めします。 romberg複雑な関数を統合でき、配列を使用して関数を評価できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.