Python 在scipy中积分多维积分

Python 在scipy中积分多维积分,python,math,numpy,scipy,scientific-computing,Python,Math,Numpy,Scipy,Scientific Computing,动机:我有一个多维积分,为了完整起见,我在下面复制了它。当存在明显的各向异性时,它来自第二维里系数的计算: 这里W是所有变量的函数。这是一个已知的函数,我可以为其定义python函数 编程问题:如何让scipy集成此表达式?我曾考虑将两个三元组()链接在一起,但我担心性能和准确性。scipy中是否有高维积分器,可以处理任意数量的嵌套积分?如果不是,最好的方法是什么?首先我要说,我的数学不是很好,所以请友善一点。不管怎样,这是我的尝试: 请注意,您的问题中有6变量,但有7积分 在Python中使

动机:我有一个多维积分,为了完整起见,我在下面复制了它。当存在明显的各向异性时,它来自第二维里系数的计算:

这里W是所有变量的函数。这是一个已知的函数,我可以为其定义python函数


编程问题:如何让
scipy
集成此表达式?我曾考虑将两个三元组()链接在一起,但我担心性能和准确性。
scipy
中是否有高维积分器,可以处理任意数量的嵌套积分?如果不是,最好的方法是什么?

首先我要说,我的数学不是很好,所以请友善一点。不管怎样,这是我的尝试:
请注意,您的问题中有6变量,但有7积分
Python
中使用
Sympy

>>> r,theta,phi,alpha,beta,gamma,W,k,T = symbols('r theta phi alpha beta gamma W k T')
>>> W = r+theta+phi+alpha+beta+gamma
>>> Integral((exp(-W/(k*T))-1)*r**2*sin(beta)*sin(theta),(r,(0,2*pi)),(theta,(0,pi)),(phi,(0,2*pi)),(alpha,(0,2*pi)),(beta,(0,pi)),(gamma,(0,pi)))  

>>> integrate((exp(-W)-1)*r**2*sin(beta)*sin(theta),(r,(0,2*pi)),(theta,(0,pi)),(phi,(0,2*pi)),(alpha,(0,2*pi)),(beta,(0,pi)),(gamma,(0,pi)))  
结果如下:[乳胶代码]

\begin{equation*}- \frac{128}{3} \pi^{6} - \frac{\pi^{2}}{e^{2 \pi}} - \frac{\pi}{e^{2 \pi}} - \frac{2}{e^{2 \pi}} - \frac{\pi^{2}}{e^{3 \pi}} - \frac{\pi}{e^{3 \pi}} - \frac{2}{e^{3 \pi}} - 3 \frac{\pi^{2}}{e^{6 \pi}} - 3 \frac{\pi}{e^{6 \pi}} - \frac{2}{e^{6 \pi}} - 3 \frac{\pi^{2}}{e^{7 \pi}} - 3 \frac{\pi}{e^{7 \pi}} - \frac{2}{e^{7 \pi}} + \frac{1}{2 e^{9 \pi}} + \frac{\pi}{e^{9 \pi}} + \frac{\pi^{2}}{e^{9 \pi}} + \frac{1}{2 e^{8 \pi}} + \frac{\pi}{e^{8 \pi}} + \frac{\pi^{2}}{e^{8 \pi}} + \frac{3}{e^{5 \pi}} + 3 \frac{\pi}{e^{5 \pi}} + 3 \frac{\pi^{2}}{e^{5 \pi}} + \frac{3}{e^{4 \pi}} + 3 \frac{\pi}{e^{4 \pi}} + 3 \frac{\pi^{2}}{e^{4 \pi}} + \frac{1}{2 e^{\pi}} + \frac{1}{2}\end{equation*}

你可以为你的问题多弹一点;)

对于像这样的高维积分,蒙特卡罗方法通常是一种有用的技术-它们以函数求值数的平方根反比的形式收敛于答案,这对于高维积分来说更好,而通常情况下,即使是相当复杂的自适应方法(除非你对你的被积函数非常了解——对称性是可以利用的,等等)

该包执行蒙特卡罗积分:使用一个非平凡的
W
运行,但它仍然是可积的,因此我们知道我们得到的答案(注意,我已将r从[0,1]截断;您必须进行某种对数变换或其他操作,以将该半无界域转换为大多数数值积分器可处理的域):

跑步给予

Using n =  1000
Result =  1654.19633236 estimated error =  399.360391622
Known result =  1632.10498552  error =  22.0913468345  =  1.35354937522 %

Using n =  10000
Result =  1634.88583778 estimated error =  128.824988953
Known result =  1632.10498552  error =  2.78085225405  =  0.170384397984 %

Using n =  100000
Result =  1646.72936 estimated error =  41.3384733174
Known result =  1632.10498552  error =  14.6243744747  =  0.8960437352 %

Using n =  1000000
Result =  1640.67189792 estimated error =  13.0282663003
Known result =  1632.10498552  error =  8.56691239895  =  0.524899591322 %

Using n =  10000000
Result =  1635.52135088 estimated error =  4.12131562436
Known result =  1632.10498552  error =  3.41636536248  =  0.209322647304 %

Using n =  100000000
Result =  1631.5982799 estimated error =  1.30214644297
Known result =  1632.10498552  error =  0.506705620147  =  0.0310461413109 %
通过对随机数生成进行矢量化,可以大大加快速度

当然,你可以像你建议的那样把三重积分链起来:

import numpy
import scipy.integrate
import math

def w(r, theta, phi, alpha, beta, gamma):
    return(-math.log(theta * beta))

def integrand(phi, alpha, gamma, r, theta, beta):
    ww = w(r, theta, phi, alpha, beta, gamma)
    k = 1.
    T = 1.
    return (math.exp(-ww/(k*T)) - 1.)*r*r*math.sin(beta)*math.sin(theta)

# limits of integration

def zero(x, y=0):
    return 0.

def one(x, y=0):
    return 1.

def pi(x, y=0):
    return math.pi

def twopi(x, y=0):
    return 2.*math.pi

# integrate over phi [0, Pi), alpha [0, 2 Pi), gamma [0, 2 Pi)
def secondIntegrals(r, theta, beta):
    res, err = scipy.integrate.tplquad(integrand, 0., 2.*math.pi, zero, twopi, zero, pi, args=(r, theta, beta))
    return res

# integrate over r [0, 1), beta [0, 2 Pi), theta [0, 2 Pi)
def integral():
    return scipy.integrate.tplquad(secondIntegrals, 0., 2.*math.pi, zero, twopi, zero, one)

expected = 16*math.pow(math.pi,5)/3.
result, err = integral()
diff = abs(result - expected)

print "Result = ", result, " estimated error = ", err
print "Known result = ", expected, " error = ", diff, " = ", 100.*diff/expected, "%"
这是缓慢的,但对于这个简单的例子给出了很好的结果。更好的方法是归结到你的
W
有多复杂,以及你的精度要求是什么。简单(快速评估)和高精度将促使你采用这种方法;复杂(评估缓慢)具有中等精度要求的W将推动您采用MC技术

Result =  1632.10498552  estimated error =  3.59054059995e-11
Known result =  1632.10498552  error =  4.54747350886e-13  =  2.7862628625e-14 %

我只想就如何准确地进行这类积分做一些一般性的评论,但是这个建议并不是针对scipy的(评论太长了,尽管它不是一个答案)

我不知道您的使用案例,即您是否对一个“好”答案感到满意,该答案的精度只有几位数,如Jonathan Dursi的答案中所述,可以使用蒙特卡罗直接获得,或者您是否真的希望尽可能提高数值精度

我自己进行了维里系数的分析、蒙特卡罗和求积计算。如果你想准确地进行积分,那么你应该做以下几件事:

  • 尝试尽可能多地精确地执行积分;很可能在某些坐标中的积分非常简单

  • 考虑转换积分变量,使被积函数尽可能平滑。(这对蒙特卡罗和求积都有帮助)

  • 对于蒙特卡罗,使用重要性抽样以获得最佳收敛性

  • 对于求积,使用7个积分,使用tanh-sinh求积可以获得非常快的收敛速度。如果你可以得到5个积分,那么你应该能够得到10位精度的积分。为此,我强烈推荐mathtool/ARPREC,可从David Bailey的主页获得:

  • 他回答得很好。我只想补充一下他的回答

    现在,
    scipy.integrate
    有一个名为
    nqad
    的函数,可以轻松执行多维积分。有关更多信息,请参阅。下面我们以Jonathan为例,使用
    nqad
    计算积分:

    from scipy import integrate
    import math
    import numpy as np
    
    def w(r, theta, phi, alpha, beta, gamma):
        return(-math.log(theta * beta))
    
    def integrand(r, theta, phi, alpha, beta, gamma):
        ww = w(r, theta, phi, alpha, beta, gamma)
        k = 1.
        T = 1.
        return (math.exp(-ww/(k*T)) - 1.)*r*r*math.sin(beta)*math.sin(theta)
    
    result, error = integrate.nquad(integrand, [[0, 1],             # r
                                                [0, 2 * math.pi],   # theta
                                                [0, math.pi],       # phi
                                                [0, 2 * math.pi],   # alpha
                                                [0, 2 * math.pi],   # beta
                                                [0, 2 * math.pi]])  # gamma
    expected = 16*math.pow(math.pi,5)/3
    diff = abs(result - expected)
    
    结果比链接的
    tplquad
    更精确:

    >>> print(diff)
    0.0
    

    您最好尝试一下。这看起来仍然像是在进行符号计算,也就是说,您的W是输入变量的线性函数,因此是精确的结果。对于我来说,W是非线性的,不能表示为数学函数,而是另一个计算的结果(因此定义为python函数)。你是对的,我应该只有6个积分,我一定是对它进行了特化处理。谢谢!我来看看
    mcint
    ,看看它是否比我现在使用的特别MC方法性能更好。@JohnathanDursi在Python中可以得到多维高斯求积吗?这种求积集用于实例ce用于求解热传导方程。在这种情况下,根据一些求积规则和方位角(方向)分布极角都是均匀分布的。谢谢你的输入。你介意详细说明一下#2吗?先验地,我怎么知道什么是好的转换?因为你以前做过这些类型的计算,任何额外的输入都将不胜感激。
    >>> print(diff)
    0.0