Monte-Carlo numerical integration
by Eugeniy E. Mikhailov and
Greg Bentsen
Logistics and Agenda
- Monday: Monte-Carlo integration
- Tuesday: root finding and integration gotchas
- Friday: mock in-class exam
Midterm 1:
Do it at home part
- Midterm write up and code is due Sunday October 4th 11:59pm
- there is no late submission option
- You may not use AI to write the report.
- Report should be done by you and only one per group is needed
In class part
- on Friday October 2nd: mock exam (no grades and you do not even have
to do it)
- check if your computers are battle worthy for off-line use
- see a typical problem set
- on Monday October 5th, with computers but no AI and no Internet
connection
- we will give you modified assignment which would be similar
- it will require modification or reuse of prepared code
Monte Carlo Integration
Toy example - area of the pond
Pond and random hits illustration
where
- Points must be uniformly and randomly
distributed across the area.
- The smaller the enclosing box the better it is.
Naive Monte Carlo integration
Naive Monte-Carlo setup
where
- Points must be uniformly and randomly
distributed across the area.
- The smaller the enclosing box the better it is. So
Monte Carlo integration derived
Improved Monte-Carlo setup
Notice that if we choose a small stripe around the bin value
,
then subset of points in that stripe gives an estimate for
.
Thus why bother spreading points around area?
Let’s chose a uniform random distribution of points
inside
Numerical Integration methods error estimates
Error estimate for Monte-Carlo method
Monte Carlo method algorithmic error
where
is the estimate of the function standard deviation given by
On the first glance it is horendous method in comaparison even to the
box method where
Monte-Carlo 1D example
import numpy as np
def monte_carlo(func, a, b, N=1_000_000):
"""Monte Carlo Integration"""
xi = np.random.uniform(a, b, N)
f_xi = func(xi)
return np.mean(f_xi) * (b - a)
if __name__ == "__main__":
import matplotlib.pyplot as plt
def f(x):
return x * x
a = 0
b = 1
Np = 100 # hom many points to plot
N = np.logspace(1, 6, Np, dtype=np.int64) # used in Monte-Carlo
integral_for_N = np.zeros(Np)
for i, n in enumerate(N):
integral_for_N[i] = monte_carlo(f, a, b, n)
plt.loglog(N, np.abs(integral_for_N - 1 / 3))
plt.grid(True)
plt.xlabel("N")
plt.ylabel("Deviation from true value")
plt.savefig("monte_carlo_deviation.png")
Scaling of Monte-Carlo algorithmic error
Multidimensional integration
Multidimensional integration with interval splitting
Note that last integral is the function of only
Thus we replaced multidimensional integral as consequent series of
single dimension integrals, which we already know how to do.
3D case would look like this
Multidimensional integration with Monte Carlo
Note that if we would like to split integration region by
points in every of
dimensions, then evaluation time grows
,
which renders Rectangle, Trapezoidal, Simpson, and alike methods
useless.
Monte Carlo method is a notable exception, it looks very simple even
for multidimensional case and maintains the same
evaluation time.
3D case would look like this
A general case
where V is the multidimensional volume ,
randomly and uniformly distributed points in the volume V.
Python library Monte-Carlo method
Quasi-Monte Carlo quadrature qmc_quad from
scipy.integrate package
qmc_quad(func, a, b, *, n_estimates=8, n_points=1024, qrng=None, log=False)
it has many more parameters then our simple function
from scipy.integrate import qmc_quad
def f(x): return x*x
res = qmc_quad(f, 0, 1, n_points=10000)
In [28]: res
Out[28]: QMCQuadResult(integral=np.float64(0.3333562442002344), standard_error=np.float64(2.1699395303402513e-05))
In [29]: res.integral
Out[29]: np.float64(0.3333562442002344
Here is example of 2D integral, which finds the area of the disk with
radius 1, i.e. the final result should be
In [104]: from scipy.integrate import qmc_quad
In [105]: def disk(rv): return np.int64(np.sum(rv*rv, axis=0) <=1)
In [106]: res=qmc_quad(disk, [-1, -1], [1,1], n_points=10000)
In [107]: res
Out[107]: QMCQuadResult(integral=np.float64(3.141000000000001), standard_error=np.float64(0.000994269293788858))
In [108]: res.integral
Out[108]: np.float64(3.141000000000001)