Reputation: 2889
I am using sqipy.integrate.quad
to calculate a double integral. Basically I'm trying to calculate the integral over exp[-mu_wx_par] where mu_wx_par is also a integral.
My code mostly works. However, for some values it fails, i.e. it returns incorrect values.
import numpy as np
from scipy import integrate
def mu_wx_par(x, year, par):
""" First function to be integrated """
m = max(par['alfa'], 0) + par['beta'] * 10 ** (par['gamma'] * x)
w = np.minimum(par['frem_a'] + par['frem_b'] * x + par['frem_c'] * x**2, 0)
return m * (1 + w/100)**(year - par['innf_aar'])
def tpx_wx(x, t, par, year):
""" Second function to be integrated (which contains an integral itself)"""
result, _ = integrate.quad(lambda s: mu_wx_par(x + s, year + s, par), 0, t)
return np.exp(-result)
def est_lifetime(x, year, par):
""" Integral of second function. """
result, _ = integrate.quad(lambda s: tpx_wx(x, s, par, year), 0, 125 - x)
return result
# Test variables
par = {'alfa': 0.00019244401470947973,
'beta': 2.420260552210541e-06,
'gamma': 0.0525500987420195,
'frem_a': 0.3244611019518985,
'frem_b': -0.12382978382606026,
'frem_c': 0.0011901237463116591,
'innf_aar': 2018
}
year = 2018
estimate_42 = est_lifetime(42, year, par)
estimate_43 = est_lifetime(43, year, par)
rough_estimate_42 = sum([tpx_wx(42, t, par, year) for t in range(0, 100)])
print(estimate_42)
print(estimate_43)
print(rough_estimate_42)
3.1184634065887544
46.25925442287578
47.86323490659588
The value of estimate_42
is not correct. It should be about the same value as rough_estimate_42
. Note however that estimate_43
looks fine. What is going on here?
I am using scipy v1.1.0 and numpy v1.15.1 and Windows.
It has been suggested that the function is close to zero almost everywhere, as in this post scipy integrate.quad return an incorrect value. That is not the case as a simple plot of tpx_wx
for x=42
from a=0
to b=125-42
clearly shows
from matplotlib import pyplot as plt
plt.plot(range(125-42), [tpx_wx(42, t, par, year) for t in range(0, 125-42)])
plt.show()
Upvotes: 2
Views: 1019
Reputation:
This appears to be a known issue with the way that some Fortran code behind quad
is compiled for Windows: calling it recursively can lead to failure in some cases. See also Big integration error with integrate.nquad.
Barring recompiling SciPy with better flags, it seems that one should avoid nested integration with quad
while on Windows. One workaround is to use romberg
method for one of the integration steps. Replacing quad
in est_lifetime
with
integrate.romberg(lambda s: tpx_wx(x, s, par, year), 0, 125 - x, divmax=20)
results in 47.3631754795
for estimate_42
, consistent with what quad
returns on Linux.
One way to visualize the process of integration is to declare a global list eval_points
and insert eval_points.append(t)
into tpx_wx
. With the same version of SciPy (0.19.1 in this test), the results of plt.plot(eval_points, '.')
look different.
On Linux:
On Windows:
Iterated bisection of the neighborhood of a tricky point around 60 is terminated prematurely on Windows, and it seems that the result thrown up is some partial integral over a subinterval.
Upvotes: 1