Reputation: 123
Sometimes, I get wrong solution when I integrate with infinite boundaries in Python. Here is a simple example to illustrate my confusion:
from scipy import *
import numpy as np
from scipy.integrate import quad
def integrand1(x):
output = exp(-(x-1.0)**2.0)
return output
def integrand2(x):
output = exp(-(x-100.0)**2.0)
return output
solution1 = quad(integrand1,-np.inf,np.inf)
print solution1
solution1 = quad(integrand2,-np.inf,np.inf)
print solution2
while the output is:
(1.7724538509055159, 3.668332157626072e-11)
(0.0, 0.0)
I don't understand why the second integral is wrong while the first one avoid to be wrong. It will be great to tell me some tricks to handle infinite in Python.
Upvotes: 2
Views: 18928
Reputation: 1669
There is nothing inherently wrong with your code. The results you get are due to the quad
algorithm being an approximate method whose accuracy, from what I gathered doing some tests, greatly depends on where the midpoint of the integration interval is located, with respect to the x-axis interval where the integrand is significantly different from 0.
The midpoint of the integration interval in case of a (-inf,+inf)
interval is always 0 (see comment to relevant Fortran code here, starting at line 238), and (sadly) cannot be configured. Your integrand2
function is centered on x=100, which is too far from the quad
algorithm's midpoint for it to be accurate enough.
It would be nice to be able to specify the midpoint in case of integration between -inf
and +inf
, but the good news is that you can implement it yourself with function decorators. First you need a wrapper for your integrand function, in order to shift it arbitrarily over the x axis:
def shift_integrand(integrand, offset):
def dec(x):
return integrand(x - offset)
return dec
This generates a new function based on any integrand you like, just shifting it along the x axis according to the offset
parameter. So if you do something like this (using your integrand1
and integrand2
functions):
new_integrand1 = shift_integrand(integrand1, -1.0)
print new_integrand1(0.0)
new_integrand2 = shift_integrand(integrand2, -100.0)
print new_integrand2(0.0)
you get:
1.0
1.0
Now you need another wrapper for the quad
function, in order to be able to pass a new midpoint:
def my_quad(func, a, b, midpoint=0.0, **kwargs):
if midpoint != 0.0:
func = shift_integrand(func, -midpoint)
return quad(func, a, b, **kwargs)
Finally, knowing where your integrands are centered, you can call it thus:
solution2 = my_quad(integrand2, -np.inf, np.inf, midpoint=100.0)
print solution2
Which yields:
(1.772453850905516, 1.4202639944499085e-08)
Upvotes: 6