Matlab'ın integral performansı neden Scipy'de integral .quad?


13

Matlab'ın sayısal entegrasyonu Scipy ile karşılaştırması konusunda biraz hayal kırıklığı yaşıyorum. Aşağıdaki test kodumda aşağıdaki farklılıkları gözlemliyorum:

  1. Matlab'ın versiyonu python eşdeğümden ortalama 24 kat daha hızlı çalışıyor !
  2. Matlab'ın sürümü, integrali uyarmadan hesaplayabilirken, python döner nan+nanj

Bahsedilen iki noktaya göre python'da aynı performansı almamı sağlamak için ne yapabilirim? Dokümantasyona göre, her iki yöntem de integrali yaklaşık olarak belirlemek için bir "global uyarlanabilir kareleme" kullanmalıdır.

Aşağıda iki versiyondaki kod bulunmaktadır (python, karmaşık integralleri işleyebilmesi için bir integral fonksiyonunun oluşturulmasını gerektirmesine rağmen oldukça benzerdir.)

piton

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'un sadece 25x (250x değil) daha yavaş olduğundan mutlu olmalısınız.
stali

4
Çünkü bir döngü içinde tekrar tekrar bir python işlevi çağırıyorsunuz (tarafından saklandı np.vectorize). Tüm dizide aynı anda hesaplamalar yapmayı deneyin. Bu mümkün değil, numba veya Cython'a bir göz atın, ancak umarım ikincisi gerekli değildir.
sebix

2
"global uyarlanabilir kareleme" belirli bir hassasiyete ulaşıncaya kadar uyum sağladığını gösterir. Aynı şeyi karşılaştırdığınızdan emin olmak için, kesinliği ayarlayan ve her ikisi için ayarlayan parametreye bakın (kesinlikle bir tane var).
bgschaid

2
Bgschaid yorumuna @ ilgili olarak, integral'varsayılan mutlak ve bağıl toleransları vardır 1e-10ve 1e-6sırasıyla. integrate.quadbunların ikisini de belirtir 1.49e-8. Nerede integrate.quad"küresel uyarlanabilir" bir yöntem olarak tanımlandığını görmüyorum ve kesinlikle (uyarlanabilir Gauss-Kronrod, inanıyorum) tarafından kullanılan yöntemden farklı integral. "Küresel" kısmın ne anlama geldiğinden emin değilim, kendim. Ayrıca, / veya cputimeyerine kullanmak asla iyi bir fikir değildir . tictoctime it
14'te horchler

5
Herhangi bir şeyden önce problemin algoritma mı yoksa dil mi olduğunu kontrol ederdim: fonksiyonların içinde artan bir global sayaç değişkeni ekleyin. Entegrasyondan sonra bu, her bir fonksiyonun ne sıklıkla değerlendirildiğini size bildirmelidir. Bu sayaçlar önemli ölçüde farklıysa, sorunun en azından bir kısmı
MATLAB'ın

Yanıtlar:


15

Sorunun iki farklı alt sorusu var. Sadece ilkini ele alacağım.

Matlab'ın versiyonu python eşdeğümden ortalama 24 kat daha hızlı çalışıyor !

İkincisi özneldir. Kullanıcıya integral ile ilgili bir sorun olduğunu bilmenin iyi bir şey olduğunu ve bu SciPy davranışının Matlab'ın sessiz kalmasını ve bir şekilde sadece Matlab mühendisleri tarafından bilinen şekilde içsel olarak uğraşmayı denemesini sağladığını söyleyebilirim. en iyi olmaya karar verdi.

Değişmiş entegrasyon aralığı ile ilgili olduğu 0 ile 30 (yerine gelen 0 ile np.inf bir tam zamanında derleme) NaN waring önlemek ve ilave edildi. Çözümü karşılaştırmak için entegrasyonu 300 kez tekrarladım, sonuçlar dizüstü bilgisayarımdan geldi.

JIT derlemesi olmadan:

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

JIT derlemesi ile:

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

Bu şekilde iki kod satırı eklenmesi, JIT olmayan bir sürümle karşılaştırıldığında Python kodunun yaklaşık 40 katı hızlandırma faktörüne yol açar . Daha iyi bir karşılaştırma sağlamak için dizüstü bilgisayarımda Matlab'ım yok, ancak PC'nize 24/40 = 0.6'dan daha iyi ölçeklenirse, bu nedenle JIT'li Python, bu belirli kullanıcı algoritması için Matlab'ın neredeyse iki katı kadar hızlı olmalıdır . Tam kod:

#!/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'niz için farkı görmek için @jit satırına yorum yapın.


1

Bazen bütünleştirme işlevi JIT edilemez. Bu durumda, başka bir entegrasyon yöntemi kullanmak çözüm olacaktır.

scipy.integrate.romberg (Ref) tavsiye ederim . rombergkarmaşık fonksiyonları entegre edebilir ve fonksiyonu dizi ile değerlendirebilir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.