Monte Carlo Simulation: MPI4Py

Afzal Badshah, PhD
4 min readMay 23, 2024

Monte Carlo simulations are a statistical technique that allows for solving problems through random sampling. They are widely used in various fields such as physics, finance, and engineering to understand the impact of risk and uncertainty in prediction and forecasting models. The core idea is to use randomness to solve problems that might be deterministic in nature. You can visit the detailed tutorial here.

For example, to estimate the value of Pi ((\pi)), we can use the Monte Carlo method. We generate random points in a unit square and count how many fall within a quarter-circle inscribed within the square. The ratio of the points inside the quarter-circle to the total number of points approximates (\pi/4).

Detailed Program

Below is a Python program used mpi4py for parallel processing in a Monte Carlo simulation to estimate the value of Pi.

from mpi4py import MPI
import numpy as np
import random
def monte_carlo_pi(num_samples):
count = 0
for _ in range(num_samples):
x, y = random.random(), random.random()
if x**2 + y**2 <= 1:
count += 1
return count
def main():
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = comm.Get_size()
num_samples_per_proc = 1000000 # Adjust as needed
total_samples = num_samples_per_proc * size
# Each process runs its own Monte Carlo simulation
local_count = monte_carlo_pi(num_samples_per_proc)
# Gather all local counts to the root process
total_count = comm.reduce(local_count, op=MPI.SUM, root=0)
if rank == 0:
# Root process calculates the final estimate of Pi
pi_estimate = (4.0 * total_count) / total_samples
print(f"Estimated Pi: {pi_estimate}")
if __name__ == "__main__":
main()

Line-by-Line Explanation

from mpi4py import MPI

This line imports the MPI module from the mpi4py package, which provides support for parallel processing.

import numpy as np
import random

These lines import the numpy and random modules. numpy is a powerful library for numerical computations, although it's not explicitly used in this example, and random provides functions for generating random numbers.

def monte_carlo_pi(num_samples):

This line defines a function named monte_carlo_pi that takes one parameter num_samples, representing the number of random points to generate.

count = 0

Initializes a counter count to zero. This will be used to count the number of points that fall inside the quarter-circle.

for _ in range(num_samples):

Starts a loop that iterates num_samples times to generate random points.

x, y = random.random(), random.random()

Generates two random floating-point numbers x and y between 0 and 1, representing the coordinates of a point.

if x**2 + y**2 <= 1:

Checks if the point (x, y) lies inside the quarter-circle by verifying if the sum of the squares of x and y is less than or equal to 1.

count += 1

If the point lies inside the quarter-circle, increments the counter count by 1.

return count

Returns the total count of points that lie inside the quarter-circle.

def main():

Defines the main function which will be executed when the script is run.

comm = MPI.COMM_WORLD

Initializes the MPI communication world, allowing communication between different processes.

rank = comm.Get_rank()

Gets the rank (ID) of the current process. The rank is used to identify each process uniquely.

size = comm.Get_size()

Gets the total number of processes running in the MPI world.

num_samples_per_proc = 1000000  # Adjust as needed

Defines the number of samples each process will generate. This can be adjusted based on the desired accuracy and computational resources.

total_samples = num_samples_per_proc * size

Calculates the total number of samples by multiplying the number of samples per process by the total number of processes.

local_count = monte_carlo_pi(num_samples_per_proc)

Each process runs the monte_carlo_pi function independently to simulate its portion of the samples and stores the result in local_count.

total_count = comm.reduce(local_count, op=MPI.SUM, root=0)

Gathers all local counts to the root process (rank 0) and sums them up using the reduce operation.

if rank == 0:

Checks if the current process is the root process.

pi_estimate = (4.0 * total_count) / total_samples

The root process calculates the final estimate of Pi using the total count of points inside the quarter-circle and the total number of samples.

print(f"Estimated Pi: {pi_estimate}")

Prints the estimated value of Pi.

if __name__ == "__main__":
main()

Checks if the script is being run directly and, if so, calls the main function to start the program.

Running the Program

To run this program with MPI, use the following command, specifying the number of processes you want to use (e.g., 4):

mpiexec -n 4 python your_script_name.py

This command will start the script with 4 parallel processes, each performing part of the Monte Carlo simulation and collaborating to estimate the value of Pi.

--

--

Afzal Badshah, PhD

Dr Afzal Badshah focuses on academic skills, pedagogy (teaching skills) and life skills.