为什么Matlab的整体性能优于Scipy?


13

对于Matlab处理数值积分与Scipy的方式,我感到有些沮丧。我在下面的测试代码中观察到以下差异:

  1. Matlab的版本平均运行速度比我的python 快24倍
  2. 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

4
您应该很高兴Python仅慢25倍(而不是250倍)。
stali 2014年

4
因为您是在循环中一次又一次地调用python函数(由隐藏np.vectorize)。尝试一次对整个数组进行计算。这是不可能的,看看numba或Cython,但我希望后者没有必要。
sebix 2014年

2
“全局自适应正交”表示它会自适应直到达到一定精度为止。为了确保您比较的是同一事物,请寻找设置精度并同时为两个精度设置的参数(肯定是一个)。
bgschaid 2014年

2
关于@bgschaid的评论,integral默认的绝对公差和相对公差分别为1e-101e-6integrate.quad将两者都指定为1.49e-8。我看不到哪里integrate.quad将其描述为“全局自适应”方法,并且它肯定与所使用的(自适应高斯-克朗罗德)方法不同integral。我自己不确定“全局”部分的含义。此外,它是不是一个好主意来使用cputime,而不是tic/ toctime it
horchler 2014年

5
在开始之前,我先检查问题是算法还是语言:添加一个在函数内部递增的全局计数器变量。集成后,这应该告诉您每个函数的评估频率。如果这些计数器相差很大,那么至少部分问题是MATLAB使用了更好的算法
bgschaid 2014年

Answers:


15

这个问题有两个非常不同的子问题。我只会讲第一个。

Matlab的版本平均运行速度比我的python 快24倍

第二个是主观的。我要说的是,让用户知道积分存在问题是一件好事,这种SciPy行为优于Matlab的行为,使其保持沉默,并以某种方式尝试以内部处理方式,只有Matlab工程师才知道决定是最好的。

为了避免NaN 警告,我将集成范围从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版本相比,以这种方式添加两行代码可以使Python代码的加速因子提高40倍左右。我的笔记本电脑上没有Matlab可以提供更好的比较,但是,如果它可以很好地扩展到您的PC上,而不是24/40 = 0.6,那么对于这种特定的用户算法,使用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)

注释掉@jit行,以查看适用于您PC的区别。


1

有时无法对要集成的功能进行JIT操作。在那种情况下,使用另一种集成方法将是解决方案。

我会推荐scipy.integrate.romberg (ref)romberg可以集成复杂的函数,并可以使用数组评估函数。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.