#%config InlineBackend.figure_format = 'svg'
from pylab import *
The error in some numerical quadrature depends on the number of nodes used which the user has to select. It is desirable to have an automatic algorithm where the user specifies the error ϵ \epsilon ϵ n n n 
∣ I ( f ) − I n ( f ) ∣ ≤ ϵ |I(f) - I_n(f)| \le \epsilon ∣ I ( f ) − I n  ( f ) ∣ ≤ ϵ The error clearly depends on the smoothness of the integrand f f f f f f f ( x ) = x f(x) = \sqrt{x} f ( x ) = x  x = 0 x=0 x = 0 n n n h h h h h h 
9.14.1 Adaptive Simpson quadrature ¶ Let n n n n + 1 n+1 n + 1 
I n ( f ) = ∑ j = 1 n / 2 ( x 2 j − x 2 j − 2 6 ) ( f 2 j − 2 + 4 f 2 j − 1 + f 2 j ) , x 2 j − 1 = 1 2 ( x 2 j − 2 + x 2 j ) I_n(f) = \sum_{j=1}^{n/2} \left( \frac{x_{2j} - x_{2j-2}}{6} \right)( f_{2j-2} + 4 f_{2j-1} + f_{2j}), \qquad x_{2j-1} = \half(x_{2j-2} + x_{2j}) I n  ( f ) = j = 1 ∑ n /2  ( 6 x 2 j  − x 2 j − 2   ) ( f 2 j − 2  + 4 f 2 j − 1  + f 2 j  ) , x 2 j − 1  = 2 1  ( x 2 j − 2  + x 2 j  ) with error formula
I ( f ) − I n ( f ) = − 1 2880 ∑ j = 1 n / 2 ( x 2 j − x 2 j − 2 ) 5 f ( 4 ) ( ξ j ) , ξ j ∈ [ x 2 j − 2 , x 2 j ] I(f) - I_n(f) = -\frac{1}{2880} \sum_{j=1}^{n/2}(x_{2j} - x_{2j-2})^5 f^{(4)}(\xi_j), \qquad \xi_j \in [x_{2j-2}, x_{2j}] I ( f ) − I n  ( f ) = − 2880 1  j = 1 ∑ n /2  ( x 2 j  − x 2 j − 2  ) 5 f ( 4 ) ( ξ j  ) , ξ j  ∈ [ x 2 j − 2  , x 2 j  ] Note that we have a decomposition of the total error into error contributions from each of the sub-intervals. If f ( 4 ) f^{(4)} f ( 4 ) x 2 j − x 2 j − 2 x_{2j} - x_{2j-2} x 2 j  − x 2 j − 2  
The best strategy is to equi-distribute the error among all the sub-intervals.
Define
M j = max  x ∈ [ x 2 j − 2 , x 2 j ] ∣ f ( 4 ) ( x ) ∣ M_j = \max_{x \in [x_{2j-2},x_{2j}]} |f^{(4)}(x)| M j  = x ∈ [ x 2 j − 2  , x 2 j  ] max  ∣ f ( 4 ) ( x ) ∣ then
∣ I ( f ) − I n ( f ) ∣ ≤ 1 2880 ∑ j = 1 n / 2 ( x 2 j − x 2 j − 2 ) 5 M j |I(f) - I_n(f)| \le \frac{1}{2880} \sum_{j=1}^{n/2}(x_{2j} - x_{2j-2})^5 M_j ∣ I ( f ) − I n  ( f ) ∣ ≤ 2880 1  j = 1 ∑ n /2  ( x 2 j  − x 2 j − 2  ) 5 M j  We want
∣ I ( f ) − I n ( f ) ∣ ≤ ϵ |I(f) - I_n(f)| \le \epsilon ∣ I ( f ) − I n  ( f ) ∣ ≤ ϵ This can be achieved if
1 2880 ( x 2 j − x 2 j − 2 ) 5 M j ≤ ϵ n / 2 = 2 ϵ n , j = 1 , 2 , … , n / 2 \frac{1}{2880} (x_{2j} - x_{2j-2})^5 M_j \le \frac{\epsilon}{n/2} = \frac{2 \epsilon}{n}, \qquad j=1,2,\ldots,n/2 2880 1  ( x 2 j  − x 2 j − 2  ) 5 M j  ≤ n /2 ϵ  = n 2 ϵ  , j = 1 , 2 , … , n /2 If this condition is not satisfied, then we iteratively refine the sub-intervals until desired error level is reached.
9.14.1.1 Algorithm I ¶ We will assume that we can estimate M j M_j M j  
Start with three nodes, i.e., one Simpson rule, and compute I 2 ( f ) I_2(f) I 2  ( f ) ∣ I ( f ) − I 2 ( f ) ∣ ≤ ϵ |I(f) - I_2(f)| \le \epsilon ∣ I ( f ) − I 2  ( f ) ∣ ≤ ϵ I 2 ( f ) I_2(f) I 2  ( f ) 
Otherwise, insert a point mid-way between the three points and use
two Simpson rules. Estimate the error from each rule. If both errors
are ≤ ϵ / 2 \le \epsilon/2 ≤ ϵ /2 
If not, suppose first error > ϵ / 2 > \epsilon/2 > ϵ /2 ≤ ϵ / 2 \le \epsilon/2 ≤ ϵ /2 
Now the error from the first two rules must be ≤ ϵ / 4 \le \epsilon/4 ≤ ϵ /4 
Otherwise, continue with sub-division.
9.14.1.2 Algorithm II ¶ We may not be able to estimate the derivatives, and the only information available is the function values. We can approximate the derivatives by first constructing a local approximation and differentiating it. But we have seen other ways to estimate the error in integral, e.g., using Richardson extrapolation.
Set α = a \alpha=a α = a β = b \beta=b β = b 
I α , β = ∫ α β f ( x ) d x I_{\alpha,\beta} = \int_\alpha^\beta f(x) \ud x I α , β  = ∫ α β  f ( x ) d x The Simpson rule is
I α , β ( 1 ) = h 3 [ f ( α ) + f ( α + β 2 ) + f ( β ) ] , h = 1 2 ( β − α ) I_{\alpha,\beta}^{(1)} = \frac{h}{3}\left[f(\alpha) + f\left(\frac{\alpha+\beta}{2}\right) + f(\beta) \right], \qquad h = \half (\beta - \alpha) I α , β ( 1 )  = 3 h  [ f ( α ) + f ( 2 α + β  ) + f ( β ) ] , h = 2 1  ( β − α ) To estimate the error, we compute a refined estimate as
I α , β ( 2 ) = I α , γ ( 1 ) + I γ , β ( 1 ) , γ = 1 2 ( α + β ) I_{\alpha,\beta}^{(2)} = I_{\alpha,\gamma}^{(1)} + I_{\gamma,\beta}^{(1)}, \qquad \gamma = \half(\alpha + \beta) I α , β ( 2 )  = I α , γ ( 1 )  + I γ , β ( 1 )  , γ = 2 1  ( α + β ) and use error estimate based on Richarson extrapolation, see Section 9.13.2.1 
E α , β = 1 15 ∣ I α , β ( 2 ) − I α , β ( 1 ) ∣ ≈ ∣ I α , β − I α , β ( 2 ) ∣ E_{\alpha,\beta} = \frac{1}{15} |I_{\alpha,\beta}^{(2)} - I_{\alpha,\beta}^{(1)}| \approx |I_{\alpha,\beta} - I_{\alpha,\beta}^{(2)}| E α , β  = 15 1  ∣ I α , β ( 2 )  − I α , β ( 1 )  ∣ ≈ ∣ I α , β  − I α , β ( 2 )  ∣ as the error estimate.
If E α , β ≤ ϵ E_{\alpha,\beta} \le \epsilon E α , β  ≤ ϵ [ α , β ] [\alpha,\beta] [ α , β ] I α , β ( 2 ) I_{\alpha,\beta}^{(2)} I α , β ( 2 )  I α , β I_{\alpha,\beta} I α , β  
Otherwise, divide [ α , β ] [\alpha,\beta] [ α , β ] [ α , γ ] [\alpha,\gamma] [ α , γ ] [ γ , β ] [\gamma,\beta] [ γ , β ] 
E α , γ ≤ ϵ 2 , E γ , β ≤ ϵ 2 E_{\alpha,\gamma} \le \frac{\epsilon}{2}, \qquad E_{\gamma,\beta} \le \frac{\epsilon}{2} E α , γ  ≤ 2 ϵ  , E γ , β  ≤ 2 ϵ  If both conditions are satisfied, then the integral is
I α , γ ( 2 ) + I γ , β ( 2 ) I_{\alpha,\gamma}^{(2)} + I_{\gamma,\beta}^{(2)} I α , γ ( 2 )  + I γ , β ( 2 )  
Otherwise, suppose
E α , γ > ϵ 2 , E γ , β ≤ ϵ 2 E_{\alpha,\gamma} > \frac{\epsilon}{2}, \qquad E_{\gamma,\beta} \le \frac{\epsilon}{2} E α , γ  > 2 ϵ  , E γ , β  ≤ 2 ϵ  Then we keep I γ , β ( 2 ) I_{\gamma,\beta}^{(2)} I γ , β ( 2 )  [ α , γ ] [\alpha,\gamma] [ α , γ ] [ α , δ ] [\alpha,\delta] [ α , δ ] [ δ , γ ] [\delta,\gamma] [ δ , γ ] δ = 1 2 ( α + γ ) \delta=\half(\alpha + \gamma) δ = 2 1  ( α + γ ) 
E α , δ ≤ ϵ 4 , E δ , γ ≤ ϵ 4 E_{\alpha,\delta} \le \frac{\epsilon}{4}, \qquad E_{\delta,\gamma} \le \frac{\epsilon}{4} E α , δ  ≤ 4 ϵ  , E δ , γ  ≤ 4 ϵ  If both conditions are satisfied, then integral is
I α , δ ( 2 ) + I δ , γ ( 2 ) + I γ , β ( 2 ) I_{\alpha,\delta}^{(2)} + I_{\delta,\gamma}^{(2)} + I_{\gamma,\beta}^{(2)} I α , δ ( 2 )  + I δ , γ ( 2 )  + I γ , β ( 2 )  
Continue this process until the errors in all sub-intervals is less
than the tolerance.
This algorithm is implemented in the following code.
simpson1 performs Simpson quadrature in one interval [ a , b ] [a,b] [ a , b ] 
asimpson performs adaptive quadrature in a recursive manner.
from scipy.integrate import simpson
# Apply Simpson using points {a, (a+b)/2, c}
def simpson1(f,a,b):
    h = 0.5*(b-a)
    c = 0.5*(a+b)
    return (h/3.0)*(f(a) + 4*f(c) + f(b))
# Performs adaptive Simpson to make error < eps
def asimpson(f,a,b,eps):
    c = 0.5*(a+b)
    i1 = simpson1(f,a,b)
    i2 = simpson1(f,a,c) + simpson1(f,c,b)
    err = (1.0/15.0) * abs(i2 - i1)
    if err < eps:
        print('[%12.6g %12.6g] %12.6g %12.6g %12.6g' % (a,b,err,eps,i2))
        return i2
    else:
        eps1 = 0.5*eps
        return asimpson(f,a,c,eps1) + asimpson(f,c,b,eps1)
Let us test it for
I = ∫ 0 1 x d x = 2 3 I = \int_0^1 \sqrt{x} \ud x = \frac{2}{3} I = ∫ 0 1  x  d x = 3 2  Here is the result of adaptive quadrature.
f = lambda x: sqrt(x)
a, b = 0.0, 1.0
eps = 0.0001
I  = asimpson(f,a,b,eps)
Ie = 2.0/3.0
print('\nIntegral,err,eps = %14.6e %14.6e %14.6e'%(I,abs(I-Ie),eps))[           0   0.00390625]  3.00376e-07  3.90625e-07  0.000160285
[  0.00390625    0.0078125]  1.29637e-09  3.90625e-07  0.000297594
[   0.0078125     0.015625]  3.66668e-09   7.8125e-07  0.000841723
[    0.015625      0.03125]  1.03709e-08   1.5625e-06   0.00238075
[     0.03125       0.0625]  2.93335e-08    3.125e-06   0.00673378
[      0.0625        0.125]  8.29676e-08     6.25e-06     0.019046
[       0.125         0.25]  2.34668e-07     1.25e-05    0.0538703
[        0.25          0.5]  6.63741e-07      2.5e-05     0.152368
[         0.5            1]  1.87734e-06        5e-05     0.430962
Integral,err,eps =   6.666608e-01   5.898359e-06   1.000000e-04
 The interval [ 0 , 0.5 ] [0, 0.5] [ 0 , 0.5 ] [ 0.5 , 1 ] [0.5, 1] [ 0.5 , 1 ] eps = 0.0001.
If we use uniformly spaced points, then
# Simpson rule using uniform sample points
# Choose n to be even only
n, N = 2, 30
for i in range(N):
    x = linspace(a, b, n+1)
    y = f(x)
    I = simpson(y, x=x)
    print('%5d %16.6e %16.6e %16.6e' % (int(n/2),(b-a)/n,I,abs(I-Ie)))
    if abs(I-Ie) < eps:
        break
    n = n + 4    1     5.000000e-01     6.380712e-01     2.859548e-02
    3     1.666667e-01     6.611443e-01     5.522350e-03
    5     1.000000e-01     6.640996e-01     2.567077e-03
    7     7.142857e-02     6.651169e-01     1.549768e-03
    9     5.555556e-02     6.656036e-01     1.063058e-03
   11     4.545455e-02     6.658799e-01     7.867465e-04
   13     3.846154e-02     6.660543e-01     6.123652e-04
   15     3.333333e-02     6.661726e-01     4.940713e-04
   17     2.941176e-02     6.662572e-01     4.094998e-04
   19     2.631579e-02     6.663201e-01     3.465749e-04
   21     2.380952e-02     6.663684e-01     2.982626e-04
   23     2.173913e-02     6.664064e-01     2.602173e-04
   25     2.000000e-02     6.664370e-01     2.296245e-04
   27     1.851852e-02     6.664621e-01     2.045892e-04
   29     1.724138e-02     6.664829e-01     1.837940e-04
   31     1.612903e-02     6.665004e-01     1.662976e-04
   33     1.515152e-02     6.665153e-01     1.514110e-04
   35     1.428571e-02     6.665280e-01     1.386202e-04
   37     1.351351e-02     6.665391e-01     1.275340e-04
   39     1.282051e-02     6.665488e-01     1.178506e-04
   41     1.219512e-02     6.665573e-01     1.093334e-04
   43     1.162791e-02     6.665649e-01     1.017949e-04
   45     1.111111e-02     6.665716e-01     9.508453e-05
 This requires more points (46) to reach the error tolerance, and the final error is still not as small as with adaptive quadrature which uses fewer points.
Error estimates E α , β E_{\alpha,\beta} E α , β  β − α \beta-\alpha β − α