Scipy:加速2D复合积分的计算

时间:2013-04-03 16:43:25

标签: python numpy scipy integration complex-numbers

我想使用scipy.integrate中的dblquad重复计算二维复数积分。由于评估的数量非常高,我想提高我的代码的评估速度。

Dblquad似乎无法处理复杂的整数。因此,我将复杂的被积函数分为实部和虚部:

def integrand_real(x, y):
    R1=sqrt(x**2 + (y-y0)**2 + z**2)
    R2=sqrt(x**2 + y**2 + zxp**2)
    return real(exp(1j*k*(R1-R2)) * (-1j*z/lam/R2/R1**2) * (1+1j/k/R1))

def integrand_imag(x,y):
    R1=sqrt(x**2 + (y-y0)**2 + z**2)
    R2=sqrt(x**2 + y**2 + zxp**2)
    return imag(exp(1j*k*(R1-R2)) * (-1j*z/lam/R2/R1**2) * (1+1j/k/R1))

y0,z,zxp,k和lam是预先定义的变量。要评估半径为ra的圆的面积积分,请使用以下命令:

from __future__ import division
from scipy.integrate import dblquad
from pylab import *

def ymax(x):
    return sqrt(ra**2-x**2)

lam = 0.000532
zxp = 5.
z = 4.94
k = 2*pi/lam
ra = 1.0

res_real = dblquad(integrand_real, -ra, ra, lambda x: -ymax(x), lambda x: ymax(x))
res_imag = dblquad(integrand_imag, -ra, ra, lambda x: -ymax(x), lambda x: ymax(x))
res = res_real[0]+ 1j*res_imag[0]

根据剖析器,两个被积函数被评估约35000次。总计算大约需要一秒钟,这对我想到的应用来说太长了。

我是使用Python和Scipy进行科学计算的初学者,并且对于指出提高评估速度的方法的评论感到高兴。有没有办法在integrand_real和integrand_complex函数中重写这些可能导致速度提升的命令?

使用像Cython这样的工具编译这些函数是否有意义?如果是:哪种工具最适合此应用程序?

3 个答案:

答案 0 :(得分:13)

使用Cython可以获得大约10倍的速度,见下文:

In [87]: %timeit cythonmodule.doit(lam=lam, y0=y0, zxp=zxp, z=z, k=k, ra=ra)
1 loops, best of 3: 501 ms per loop
In [85]: %timeit doit()
1 loops, best of 3: 4.97 s per loop

这可能还不够,坏消息是这可能是 与C / Fortran速度的所有东西相当接近(最多可能是2倍) ---如果使用相同的算法进行自适应积分。 (scipy.integrate.quad 本身已经在Fortran。)

为了更进一步,你需要考虑不同的方法来做到这一点 积分。这需要一些思考---不能提供太多 我的头脑现在。

或者,您可以减小积分的容差 被评估。

# Do in Python
#
# >>> import pyximport; pyximport.install(reload_support=True)
# >>> import cythonmodule

cimport numpy as np
cimport cython

cdef extern from "complex.h":
    double complex csqrt(double complex z) nogil
    double complex cexp(double complex z) nogil
    double creal(double complex z) nogil
    double cimag(double complex z) nogil

from libc.math cimport sqrt

from scipy.integrate import dblquad

cdef class Params:
    cdef public double lam, y0, k, zxp, z, ra

    def __init__(self, lam, y0, k, zxp, z, ra):
        self.lam = lam
        self.y0 = y0
        self.k = k
        self.zxp = zxp
        self.z = z
        self.ra = ra

@cython.cdivision(True)
def integrand_real(double x, double y, Params p):
    R1 = sqrt(x**2 + (y-p.y0)**2 + p.z**2)
    R2 = sqrt(x**2 + y**2 + p.zxp**2)
    return creal(cexp(1j*p.k*(R1-R2)) * (-1j*p.z/p.lam/R2/R1**2) * (1+1j/p.k/R1))

@cython.cdivision(True)
def integrand_imag(double x, double y, Params p):
    R1 = sqrt(x**2 + (y-p.y0)**2 + p.z**2)
    R2 = sqrt(x**2 + y**2 + p.zxp**2)
    return cimag(cexp(1j*p.k*(R1-R2)) * (-1j*p.z/p.lam/R2/R1**2) * (1+1j/p.k/R1))

def ymax(double x, Params p):
    return sqrt(p.ra**2 + x**2)

def doit(lam, y0, k, zxp, z, ra):
    p = Params(lam=lam, y0=y0, k=k, zxp=zxp, z=z, ra=ra)
    rr, err = dblquad(integrand_real, -ra, ra, lambda x: -ymax(x, p), lambda x: ymax(x, p), args=(p,))
    ri, err = dblquad(integrand_imag, -ra, ra, lambda x: -ymax(x, p), lambda x: ymax(x, p), args=(p,))
    return rr + 1j*ri

答案 1 :(得分:4)

你考虑过多处理(多线程)吗?您似乎无需进行最终集成(在整个集合中),因此简单的并行处理可能就是答案。即使您必须进行集成,也可以等待运行线程完成计算,然后再进行最终集成。也就是说,您可以阻止主线程,直到所有工作人员完成。

http://docs.python.org/2/library/multiprocessing.html

答案 2 :(得分:0)

quadpy(属于我的项目)支持磁盘上功能的许多集成方案。它支持复数值函数,并且已完全向量化。例如,Peirce's scheme的订单数为83:

from numpy import sqrt, pi, exp
import quadpy

lam = 0.000532
zxp = 5.0
z = 4.94
k = 2 * pi / lam
ra = 1.0
y0 = 0.0


def f(X):
    x, y = X
    R1 = sqrt(x ** 2 + (y - y0) ** 2 + z ** 2)
    R2 = sqrt(x ** 2 + y ** 2 + zxp ** 2)
    return exp(1j * k * (R1 - R2)) * (-1j * z / lam / R2 / R1 ** 2) * (1 + 1j / k / R1)


scheme = quadpy.disk.peirce_1957(20)
val = scheme.integrate(f, [0.0, 0.0], ra)

print(val)
(18.57485726096671+9.619636385589759j)