ทำไมส่วนประกอบสำคัญของ Matlab ถึงมีประสิทธิภาพสูงกว่ารวมอยู่ใน Scipy


13

ฉันกำลังรู้สึกหงุดหงิดกับวิธีที่ matlab จัดการกับการรวมเชิงตัวเลขกับ Scipy ฉันสังเกตเห็นความแตกต่างต่อไปนี้ในรหัสทดสอบด้านล่าง:

  1. รุ่นของ Matlab ทำงานเร็วกว่างูหลามของฉันโดยเฉลี่ย24 เท่า !
  2. เวอร์ชันของ Matlab สามารถคำนวณอินทิกรัลโดยไม่มีการเตือนขณะที่ python คืนค่า 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
คุณควรจะมีความสุขที่ Python ช้าลงเพียง 25x เท่านั้น (ไม่ใช่ 250x)
stali

4
เพราะคุณกำลังเรียกฟังก์ชันไพ ธ อนในลูปซ้ำแล้วซ้ำอีก (hid by np.vectorize) ลองทำการคำนวณกับอาร์เรย์ทั้งหมดในครั้งเดียว มันเป็นไปไม่ได้ลองดูที่ numba หรือ Cython แต่ฉันหวังว่ามันไม่จำเป็น
sebix

2
"การปรับพื้นที่สี่เหลี่ยมจัตุรัสส่วนกลาง" บ่งชี้ว่ามันปรับให้เหมาะสมจนกว่าจะถึงความแม่นยำ เพื่อให้แน่ใจว่าคุณกำลังเปรียบเทียบสิ่งเดียวกันให้มองหาพารามิเตอร์ (มีแน่นอน) ที่ตั้งค่าความแม่นยำและตั้งค่าสำหรับทั้งสอง
bgschaid

2
เกี่ยวกับความคิดเห็นของ @ bgschaid integralค่าเริ่มต้นของค่าเผื่อแน่นอนและค่าความสัมพันธ์สัมพัทธ์คือ1e-10และ1e-6ตามลำดับ ระบุเหล่านี้เป็นทั้งintegrate.quad 1.49e-8ฉันไม่เห็นที่integrate.quadจะอธิบายว่าเป็น "การปรับตัวระดับโลก" วิธีการและมันจะแตกต่างจากมากที่สุดอย่างแน่นอน (การปรับตัว Gauss-Kronrod ผมเชื่อว่า) integralวิธีการใช้โดย ฉันไม่แน่ใจว่าส่วน "โลก" หมายถึงตัวฉันเอง นอกจากนี้ก็ไม่เคยมีความคิดที่ดีที่จะใช้cputimeแทนtic/ หรือtoc time it
horchler

5
ก่อนอื่นฉันจะตรวจสอบว่าปัญหาคืออัลกอริทึมหรือภาษา: เพิ่มตัวแปรตัวนับทั่วโลกที่เพิ่มขึ้นภายในฟังก์ชั่น หลังจากการรวมสิ่งนี้ควรบอกคุณว่ามีการประเมินแต่ละฟังก์ชันบ่อยเพียงใด หากตัวนับเหล่านี้แตกต่างกันอย่างมีนัยสำคัญส่วนหนึ่งของปัญหาคือ MATLAB ใช้อัลกอริทึมที่ดีกว่า
bgschaid

คำตอบ:


15

คำถามนี้มีคำถามย่อยสองคำถามที่แตกต่างกันมาก ฉันจะอยู่คนแรกเท่านั้น

รุ่นของ Matlab ทำงานเร็วกว่างูหลามของฉันโดยเฉลี่ย24 เท่า !

คนที่สองเป็นเรื่องส่วนตัว ฉันจะบอกว่าการให้ผู้ใช้รู้ว่ามีปัญหาบางอย่างกับอินทิกรัลเป็นสิ่งที่ดีและพฤติกรรม SciPy นี้เหนือกว่า Matlab `s หนึ่งเพื่อให้มันเงียบและพยายามจัดการกับมันภายในวิธีที่วิศวกร Matlab รู้จักเท่านั้น ตัดสินใจให้ดีที่สุด

ฉันเปลี่ยนช่วงการรวมเป็นจาก0ถึง30 (แทนจาก0เป็นnp.inf ) เพื่อหลีกเลี่ยง NaN waring และเพิ่มการคอมไพล์ JIT เพื่อเปรียบเทียบวิธีแก้ปัญหาที่ฉันทำซ้ำการรวมกัน 300 ครั้งผลลัพธ์มาจากแล็ปท็อปของฉัน

ไม่มีการรวบรวม JIT:

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

ด้วยการรวบรวม JIT:

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

วิธีนี้การเพิ่มโค้ดสองบรรทัดนำไปสู่ปัจจัยการเร่งความเร็วของโค้ด Python ประมาณ 40 เท่าเมื่อเทียบกับเวอร์ชันที่ไม่ใช่ JIT ฉันไม่มี Matlab บนแล็ปท็อปของฉันที่จะให้การเปรียบเทียบที่ดีกว่า แต่ถ้ามันปรับขนาดได้ดีกับพีซีของคุณมากกว่า 24/40 = 0.6 ดังนั้นPython กับ JIT น่าจะเร็วกว่า 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 เพื่อดูความแตกต่างสำหรับพีซีของคุณ


1

บางครั้งฟังก์ชั่นเพื่อรวมไม่สามารถ JITed ในกรณีนั้นการใช้วิธีการรวมอื่นจะเป็นวิธีแก้ปัญหา

ฉันจะแนะนำ(อ้างอิง) scipy.integrate.romberg rombergสามารถรวมฟังก์ชั่นที่ซับซ้อนและสามารถประเมินฟังก์ชั่นที่มีอาร์เรย์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.