|
import numpy as np |
|
from pyDOE2.doe_lhs import lhs |
|
import matplotlib.pyplot as plt |
|
import streamlit as st |
|
import mpmath |
|
|
|
|
|
mpmath.mp.dps = 50 |
|
|
|
|
|
pi = mpmath.pi |
|
|
|
st.title("Compute Pi with Monte Carlo") |
|
|
|
st.markdown( |
|
r""" |
|
We know that area of a circle is $\pi r^2$. If we enclose the circle in a square of side $2r$, then the area of the square is $4r^2$. The ratio of the area of the circle to the area of the square is $\frac{\pi r^2}{4r^2} = \frac{\pi}{4}$. If we randomly sample points in the square, then the ratio of the number of points in the circle to the total number of points is also $\frac{\pi}{4}$. |
|
""" |
|
) |
|
|
|
|
|
|
|
def draw_the_plot(): |
|
fig, ax = plt.subplots() |
|
ax.set_aspect("equal") |
|
|
|
square = plt.Rectangle( |
|
(-1, -1), 2, 2, facecolor="lightblue", edgecolor="r", linewidth=2 |
|
) |
|
ax.add_artist(square) |
|
|
|
upper_margin = 1.05 |
|
ax.arrow( |
|
-1, |
|
upper_margin, |
|
2, |
|
0, |
|
head_width=0.05, |
|
color="k", |
|
length_includes_head=True, |
|
clip_on=False, |
|
) |
|
ax.arrow( |
|
1, |
|
upper_margin, |
|
-2, |
|
0, |
|
head_width=0.05, |
|
color="k", |
|
length_includes_head=True, |
|
clip_on=False, |
|
) |
|
|
|
ax.annotate( |
|
"$2r$", |
|
xy=(0, upper_margin), |
|
xytext=(0, upper_margin), |
|
horizontalalignment="center", |
|
verticalalignment="bottom", |
|
) |
|
circle = plt.Circle((0, 0), 1, facecolor="lightgreen", edgecolor="b", linewidth=2) |
|
ax.add_artist(circle) |
|
|
|
ax.arrow(0, 0, 1, 0, head_width=0.05, color="k", length_includes_head=True) |
|
|
|
ax.annotate( |
|
"$r$", |
|
xy=(0.5, 0), |
|
xytext=(0.5, -0.1), |
|
horizontalalignment="center", |
|
verticalalignment="bottom", |
|
) |
|
|
|
ax.set_xlim(-1.1, 1.1) |
|
ax.set_ylim(-1.1, 1.1) |
|
ax.set_xticks([]) |
|
ax.set_yticks([]) |
|
|
|
ax.spines["left"].set_visible(False) |
|
ax.spines["right"].set_visible(False) |
|
ax.spines["top"].set_visible(False) |
|
ax.spines["bottom"].set_visible(False) |
|
|
|
return fig, ax |
|
|
|
|
|
fig, ax = draw_the_plot() |
|
st.pyplot(fig) |
|
|
|
st.subheader("Monte Carlo Simulation") |
|
|
|
seed = st.number_input("Random seed", min_value=0, max_value=10000000, value=0, step=1) |
|
N = st.number_input( |
|
"Number of points", min_value=1, max_value=10000000, value=10000, step=1 |
|
) |
|
type = st.selectbox("Type of sampling", ["Random", "Grid"]) |
|
|
|
if type == "Random": |
|
samples = lhs(2, samples=N, random_state=seed) * 2 - 1 |
|
else: |
|
samples = np.linspace(-1, 1, int(np.sqrt(N))) |
|
samples = np.array(np.meshgrid(samples, samples)).T.reshape(-1, 2) |
|
|
|
|
|
distances = np.linalg.norm(samples, axis=1) |
|
|
|
|
|
inside = samples[distances < 1, :] |
|
print(samples.shape, inside.shape) |
|
|
|
st.markdown( |
|
f""" |
|
Points inside the circle: = {len(inside)}\n |
|
Ratio: = {len(inside)} / {len(samples)} = {len(inside) / len(samples)}\n |
|
$\pi$ = {len(inside) / len(samples)} * 4\n |
|
| $\pi$ | value | |
|
| --- | --- | |
|
| estimated $\pi$ | {len(inside) / len(samples) * 4:.50f} | |
|
| real $\pi$ | {pi} | |
|
""" |
|
) |
|
|
|
fig, ax = draw_the_plot() |
|
ax.scatter(samples[:, 0], samples[:, 1], s=0.1, color="r") |
|
ax.scatter(inside[:, 0], inside[:, 1], s=0.1, color="b") |
|
st.pyplot(fig) |
|
|