Divergent Integral in R is solvable in Wolfram

2019-03-04 13:42发布

问题:

I know that I asked the same question before, but as I am pretty new here the question was asked poorly and not reproducible. Therefore I try to do it better here. (If I only edit the old one probably nobody will read it)

I have this double integral that I would like to integrate:Here is a picture

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

integrate(Vectorize(function(t) integrate(function(g) 
                                          ff(g,t), -2.5,0)$value), -2, 2)

Running this in R gives me the error:

  the integral is probably divergent

When I try to run the sam function in Wolfram it gives me a proper value: (i had to switch g=x and t=y)

Link:

http://www.wolframalpha.com/input/?i=integration+[%2F%2Fmath%3Aexp%28%2816%29*x%29*exp%28-8*y-%28-y-0.01458757%29^2%2F%280.0001126501%29%29%2F%2F]+[%2F%2Fmath%3Adx+dy%2F%2F]+for+x+from+[%2F%2Fmath%3A-2.5%2F%2F]+to+[%2F%2Fmath%3A0%2F%2F]+for+y+from+[%2F%2Fmath%3A-2%2F%2F]+to+[%2F%2Fmath%3A2%2F%2F]

As you can see it gets a finite result, can somebody help me out here?

I plotted the function on the defined area and couldn't find a singularity issue. see:

library('Plot3D')
x <- seq(-2.5,0, by = 0.01) #to see the peak change to: seq(-0.2,0, by = 0.001)
y <- seq(-2,2, by = 0.01) #"": seq(-0.1,0.1, by = 0.001)
grid <- mesh(x,y) 
z <- with(grid,exp((16)*x)*
  exp(-8*y-(-0.013615734-y-0.001+0.5*0.007505^2*1)^2/(2*0.007505^2)))
persp3D(z = z, x = x, y = y)

Thanks for your help and I hope the question is better structured then the old one.

回答1:

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.



回答2:

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.



回答3:

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