SciPy - integration

https://docs.scipy.org/doc/scipy/tutorial/integrate.html

https://en.wikipedia.org/wiki/Numerical_integration

https://en.wikipedia.org/wiki/Trapezoidal_rule

https://en.wikipedia.org/wiki/Simpson%27s_rule

INTRODUCTION

The 'scipy.integrate' sub-package provides several integration techniques including an ordinary differential equation integrator.


import numpy as np
import scipy.integrate as integrate   # help(integrate)

# The function quad() is provided to integrate a function of one variable 
# between two points.

# \integ_x1^x2 dx f(x) - a definite integral
# integrate.quad(f, x1, x2)
# The first argument to quad is a callable Python object.
# x1 and x2 are the limits of integration.
# Infinite inputs are also allowed in quad by using 'np.inf'.

f = lambda x: np.exp(-x)
result = integrate.quad(f, 0, 1)
# (0.6321205588285578, 7.017947987503856e-15)
# 0.632_120_558_828_5578(70)

# exact = (np.e - 1) / np.e = 0.632_120_558_828_5577(1)

# np.e = 2.718_281_828_459_045   # 16 digits

from scipy.special import jv
# Bessel function of the first kind

f = lambda x: jv(0, x)
result = integrate.quad(f, 0, 5)
# (0.7153119177847678, 2.47260738289741e-14)
# 0.715_311_917_784_768(25)

# Double integration with the dblquad() function.

# \integ_y1^y2 dy \integ_x1(y)^x2(y) dx f(x,y)
# integrate.dblquad(f, y1, y2, x1, x2)
# Note that x1=g(y) an x2=h(y) can be functions of y.

# y2 (0,1)+---+(1,1)
#         |   |
# y1 (0,0)+---+(1,0)
#         x1  x2

f = lambda x, y: np.exp(-x**2 -y**2)
y1, y2 = 0, 1
x1 = lambda y: 0
x2 = lambda y: 1
area = integrate.dblquad(f, y1, y2, x1, x2)
# (0.5577462853510337, 8.291374381535408e-15)
# 0.557_746_285_351_0337(83)

# f(x,y) = x*y, the area is a triangle with boundaries:
# x = 0, y = 0, x + 2*y = 1,
# vertices are (0, 0), (1, 0), (0, 0.5).
# (0,0.5)+
#        | \
#        |  \
#   (0,0)+---+(1,0)

f = lambda x, y: x*y
y1, y2 = 0, 0.5
x1 = lambda y: 0
x2 = lambda y: 1-2*y   # x = 1 - 2*y
area = integrate.dblquad(f, y1, y2, x1, x2)
# (0.010416666666666668, 4.101620128472366e-16)
# 0.010_416_666_666_666_67(41)

# exact = 1. / 96. = 0.010_416_666_666_666_666(1)

# Triple integration with the tplquad() function.

# For n-fold integration, scipy provides the function nquad.
# integrate.nquad(f, iterable_with_bounds)

def bounds_y():
    return [0, 0.5]

def bounds_x(y):
    return [0, 1-2*y]

area3 = integrate.nquad(lambda x, y: x*y, [bounds_x, bounds_y])

NUMERICAL INTEGRATION

The purpose of the numerical integration is to calculate the numerical value of a definite integral to a given degree of accuracy. The integrand is evaluated at a finite set of points called 'integration points' and a weighted sum of these values is used to approximate the integral.


I = \integ_x1^x2 dx f(x)

The midpoint rule or rectangle rule (constant function approximation)

I = (x2-x1) f((x1+x2) / 2)

The trapezoidal rule (linear function approximation)

I = (x2-x1) [f(x1) + f(x2)] / 2

Simpson's 1/3 rule (quadratic function approximation)

I = [(x2-x1) / 6] [f(x1) + 4 f((x1+x2) / 2) + f(x2)]

# Integrating functions using fixed samples.

# trapz - Newton-Coates formulas (straight lines between adjacent points)
# simps - Simpson’s rule (parabolas between three adjacent points)
# romb - Romberg integration (2**k+1 points are required)

# \integ_x1^x2 dx f(x)

# \integ_0^2 dx sqrt(x)

f = lambda x: np.sqrt(x)
x1, x2 = 0, 2
x = np.linspace(x1, x2, 2**5+1)
y = f(x)
res_trapz = integrate.trapz(y, x)
res_simps = integrate.simps(y, x)   # recommended
res_romb = integrate.romb(y, dx=x[1]-x[0])   # 2**k+1 points are required
res_exact = (2.0/3.0) * (x2-x1)**(3.0/2.0)

# for 2**5+1=33 points
# res_trapz 1.882_484_948_488_58
# res_simps 1.884_349_579_078_4088
# res_romb  1.884_546_200_832_0417
# res_exact 1.885_618_083_164_1267

# for 2**6+1=65 points
# res_trapz 1.884_498_435_329_6215
# res_simps 1.885_169_597_609_9685
# res_romb  1.885_239_284_741_2136
# res_exact 1.885_618_083_164_1267