Divergent Integral in R is solvable in Wolfram

江枫思渺然 提交于 2019-12-02 08:44:22

It's also worth noting that in the integrate.c source file, the description for the error message is

error messages
...
ier = 5 the integral is probably divergent, or
    slowly convergent. it must be noted that
    divergence can occur with any other value of ier.

so despite the fact the message says "probably-divergent" it seems with your code it is more likely to be slowly convergent.

Also, you can continue to run when you get this message and extract the error if you set stop.on.error=FALSE

r <- integrate(Vectorize(function(t) 
    integrate(function(g) ff(g,t), -2.5,0)$value
), -2, 2, stop.on.error=FALSE); 
r$value

R doesn't claim to be a fancy mathematical solver like the Wolfram products such as Mathematica. It doesn't do any symbolic simplifications of integrals and that's the kind of stuff that Wolfram's been perfecting over the years. if you're just looking to numerically solve a bunch of double integrals, programs like Mathematica or Maple are probably better choices. That just doesn't seem to be where R spends as much of its development resources.

Your integrand is significantly nonzero only for a small range around y=0. From ?integrate

When integrating over infinite intervals do so explicitly, rather than just using a large number as the endpoint. This increases the chance of a correct answer – any function whose integral over an infinite interval is finite must be near zero for most of that interval.

While you're not strictly integrating over an infinite interval, the same numerical problem applies. And indeed:

ff <- function(x, y)
exp(16*x - 8*y - (-y - 0.01458757)^2/0.0001126501)

f <- function(y)
integrate(ff, lower=-2.5, upper=0, y=y)$value

integrate(Vectorize(f), lower=-Inf, upper=Inf)
0.001323689 with absolute error < 4.4e-08

It's interesting that the answer is different to that obtained from Wolfram Alpha. I'm not sure who to trust here; on the one hand I've used R's integrate many times and haven't had problems (that I can tell); however as @MrFlick says R isn't a dedicated mathematical solver like Wolfram Alpha.

You can also set the rel.tol convergence parameter to a more stringent value, say, 1e-7 or 1e-8. This is more important in the inner integral than the outer one, since errors in the former will propagate to the latter. In this case, it didn't make a difference to the final result.

For double integrals, it is better to use the cubature package.

library(cubature)
f <- function(x){
  exp(16*x[1] - 8*x[2] - (x[2] + 0.01458757)^2/0.0001126501)
}

The hcubature function gives a result which is not stable when one decreases the tolerance:

> hcubature(f, c(-2.5, -2), c(0,2))$integral
[1] 0.001285129
> hcubature(f, c(-2.5, -2), c(0,2), tol=1e-10)$integral
[1] 0.001293842

Contrary to the result of pcubature, which is stable:

> pcubature(f, c(-2.5, -2), c(0,2))$integral
[1] 0.001323689
> pcubature(f, c(-2.5, -2), c(0,2), tol=1e-10)$integral
[1] 0.001323689

The p-adaptive version, pcubature, repeatedly doubles the degree of the quadrature rules until convergence is achieved, and is based on a tensor product of Clenshaw-Curtis quadrature rules. This algorithm is often superior to h-adaptive integration for smooth integrands in a few (<=3) dimensions, but is a poor choice in higher dimensions or for non-smooth integrands.

Next, RcppNumerical provides a powerful multiple integration.

// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::depends(RcppNumerical)]]
#include <RcppNumerical.h>
#include <cmath>
using namespace Numer;

class ValegardIntegrand: public MFunc
{
public:    
  double operator()(Constvec& x)
  {
    return exp(16*x[0] - 8*x[1] - pow(-x[1] - 0.01458757,2)/0.0001126501);
  }
};    

// [[Rcpp::export]]
Rcpp::List Valegard()
{
  ValegardIntegrand f;  
  Eigen::VectorXd lower(2);
  lower << -2.5, -2;
  Eigen::VectorXd upper(2);
  upper << 0.0, 2.0;
  double err_est;
  int err_code;
  double res = integrate(f, lower, upper, err_est, err_code, 
                         10000000, 1e-14, 1e-14);
  return Rcpp::List::create(
    Rcpp::Named("approximate") = res,
    Rcpp::Named("error_estimate") = err_est,
    Rcpp::Named("error_code") = err_code
  );
}

It gives the same result as pcubature:

> Valegard()
$approximate
[1] 0.001323689

$error_estimate
[1] 9.893371e-14

$error_code
[1] 0

By the way, an exact integration with Mathematica 11 also provides this result:

Integrate[E^(16 x - 8877.04 (0.0145876 + y)^2 - 8 y), {y, -2, 2}, {x, -2.5, 0}]
0.00132369
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!