NIntegrate - 在给定的情况下,为什么Mathematica 8中的速度要慢得多?

时间:2011-12-10 13:26:35

标签: wolfram-mathematica mathematica-8

我有一个Mathematica代码,我必须在数字上评估数千个与此类似的积分

NIntegrate[
    (Pi*Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] + 
    Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), 
    {x, 0, 100}, {y, 0, 100}
] //AbsoluteTiming

被积函数是一个很好的绝对可积的函数,没有奇点,它在一个方向上呈指数衰减,在另一个方向上以1 / y ^ 3衰减。

NIntegrate命令在Mathematica 7中运行正常,但在最新版本8.0.4中,它减慢了两个数量级。我假设在新版本中它试图更好地控制错误,但代价是时间的巨大增加。是否有一些我可以使用的设置,以便计算以与Mathematica 7相同的速度进行?

3 个答案:

答案 0 :(得分:16)

ruebenko 的回答以及来自 user1091201 Leonid 的评论结合在一起,可以给出正确答案。

ruebenko 编辑1 回答是常规这类情况的正确答案,即添加选项{{1 }}:

Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False}

并且 user1091201 的评论建议expr = (Pi* Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] + Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)); NIntegrate[expr, {x, 0, 100}, {y, 0, 100}, Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False}] // AbsoluteTiming 接近此特定问题的最快答案。

我将在这个具体示例中描述NIntegrate中发生的事情,并且一路上给出一些处理明显类似情况的提示。

方法选择

在这个例子中,NIntegrate检查Method -> "GaussKronrodRule",得出结论多维" LevinRule"是这个被积函数的最佳方法,并应用它。然而,对于这个特殊的例子," LevinRule"慢于" MultidimensionalRule" (尽管" LevinRule"得到更令人满意的误差估计)。 " LevinRule"也比在两个维度上迭代的几个高斯型一维规则中的任何一个慢,例如" GaussKronrodRule" user1091201 找到了。

NIntegrate在对被积函数进行一些符号分析后做出决定。应用了几种类型的符号预处理;设置expr禁用一种符号预处理。

基本上,使用" OscillatorySelection"启用后,NIntegrate选择" LevinRule"。用" OscillatorySelection" NIntegrate选择" MultidimensionalRule",这对于这个积分更快,尽管我们可能会根据消息NIntegrate :: slwcon不信任结果,这表示观察到异常缓慢的收敛。

这是Mathematica 8与Mathematica 7不同的部分:Mathematica 8增加了" LevinRule"和相关的方法选择试探法" OscillatorySelection"。

除了使NIntegrate选择不同的方法之外,禁用" OscillatorySelection"还节省了进行实际符号处理所花费的时间,这在某些情况下可能很重要。

设置Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False}覆盖并跳过与方法选择关联的符号处理,而是使用二维笛卡尔积规则Method -> "GaussKronrodRule"。这恰好是这种积分的一种非常快速的方法。

其他符号处理

ruebenko Method -> {"CartesianRule", Method -> {"GaussKronrodRule", "GaussKronrodRule"}} user1091201 ' Method -> {"SymbolicPreprocessing", "OscillatorySelection" -> False}不会禁用其他形式的符号处理,这通常是件好事。有关可能应用的符号预处理类型的列表,请参阅this part of the NIntegrate advanced documentation。特别是" SymbolicPiecewiseSubdivision"由于存在分段函数,对于在几个点处非分析的积分非常有价值。

要禁用所有符号处理并仅使用默认方法选项获取默认方法,请使用Method -> "GaussKronrodRule"。对于一维积分,这当前等于Method -> {Automatic, "SymbolicProcessing" -> 0},具有这些方法的所有参数的某些默认设置(规则中的插值点的数量,全局自适应策略的奇点处理的类型等)。对于多维积分,它当前等于Method -> {"GlobalAdaptive", Method -> "GaussKronrodRule"},同样具有某些默认参数值。对于高维积分,将使用蒙特卡罗方法。

我不建议直接切换到Method -> {"GlobalAdaptive", Method -> "MultidimensionalRule"}作为NIntegrate的第一个优化步骤,但在某些情况下它可能很有用。

最快的方法

只有总是某种方式可以加速数值积分至少一点,有时候很多,因为有很多参数可以启发式选择,你可以从调整中受益。 (查看"LevinRule" method"GlobalAdaptive" strategy具有的不同选项和参数,包括所有子方法等。)

那就是说,这是我找到的最快的方法:

Method -> {Automatic, "SymbolicProcessing" -> 0}

(设置NIntegrate[(Pi* Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] + Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0, 100}, {y, 0, 100}, Method -> {"GlobalAdaptive", Method -> "GaussKronrodRule", "SingularityDepth" -> Infinity}] // AbsoluteTiming 禁用奇点处理转换。)

整合范围

顺便提一下,您所需的集成范围是"SingularityDepth" -> Infinity,还是{x, 0, 100}, {y, 0, 100}是您应用的真正所需集成范围?

如果你真的需要{x, 0, Infinity}, {y, 0, Infinity},我建议改用它。对于无限长度积分范围,NIntegrate将被积函数压缩到有限范围,有效地以几何间隔方式对其进行采样。这通常比用于有限积分范围的线性(均匀)间隔样本更有效。

答案 1 :(得分:8)

这是一种解决方法:

NIntegrate[(Pi*
      Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
         Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0, 
   100}, {y, 0, 100}, 
  Method -> "AdaptiveMonteCarlo"] // AbsoluteTiming

您也可以使用ParallelTry并行测试各种方法。

在实施新方法或修改启发式方法时,可能会发生特定参数的减速。这些可能有助于解决一类新问题,而其他一些问题则变得更慢。人们必须仔细调查这里发生了什么。

您可能想要更改问题的主题 - 它表示所有积分在V8中评估较慢,这是不正确的。

修改1 : 我认为它被卡在LevinRule中(V8中用于振荡积分的新东西)所以,我认为,这应该关闭它。

NIntegrate[(Pi*
      Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + y)*(-Sin[(2*Pi*x)/(1 + y)] +
         Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, 0, 
   100}, {y, 0, 100}, 
  Method -> {"SymbolicPreprocessing", 
    "OscillatorySelection" -> False}] // AbsoluteTiming

答案 2 :(得分:2)

对于这个特殊积分,主要罪魁祸首似乎是x上的积分,可能是由于存在快速衰减和高振荡项。此外,对于这种情况,可以通过分析x进行集成:

In[92]:= 
-Integrate[(Pi*Cos[(Pi*(-2*x+y))/(1+y)]+(1+y)*(-Sin[(2*Pi*x)/(1+y)]+Sin[(Pi*(-2*x+y))/(1+y)]))/
 (E^x*  (1+y)),x]/.x->0//FullSimplify

Out[92]= (-\[Pi] (1+y) (2+Cos[(\[Pi] y)/(1+y)])+(2 \[Pi]^2+(1+y)^2) Sin[(\[Pi] y)/(1+y)])/
(4 \[Pi]^2+(1+y)^2)

(我放弃了上限的值,因为它y一致非常小)。然后可以用y数字积分来获得正确的结果:

In[94]:= NIntegrate[%,{y,0,100}]
Out[94]= 1.17525

更一般的解决方法是将xy集成分开,如下所示:

ClearAll[f];
f[y_?NumericQ, xrange : {_?NumericQ, _?NumericQ}] :=
  NIntegrate[(Pi*
   Cos[(Pi*(-2*x + y))/(1 + y)] + (1 + 
     y)*(-Sin[(2*Pi*x)/(1 + y)] + 
     Sin[(Pi*(-2*x + y))/(1 + y)]))/(E^x*(1 + y)), {x, First@xrange, Last@xrange}];

然后我们有:

In[105]:= NIntegrate[f[y,{0,100}],{y,0,100}]//Timing
Out[105]= {2.578,1.17525}

这不是很快,但速度相当快。这个过程并不总是能很好地工作(因为这个过程产生的2D集成网格并不总是最优),但是当被积函数高于x和{{1}时的积分时,它应该运行得很好充分“解耦”。