I watched a YouTube video over a year ago that showed an implementation of a specific discrete event simulation. Here is the video:
I typed out the implementation in that video, and added some docstrings and type annotations.
import numpy as np
class Simulation:
def __init__(self):
"""
Initialize the simulation with default parameters.
"""
self.num_in_system: int = 0
self.clock: float = 0.0
self.t_arrival: float = self.generate_interarrival()
self.t_depart: float = float("inf")
self.num_arrivals: int = 0
self.num_departs: int = 0
self.total_wait: float = 0.0
def advance_time(self) -> None:
"""
Advance the simulation time to the next event (either arrival or departure).
"""
t_event: float = min(self.t_arrival, self.t_depart)
self.total_wait += self.num_in_system * (t_event - self.clock)
self.clock = t_event
if self.t_arrival <= self.t_depart:
self.handle_arrival_event()
else:
self.handle_departure_event()
def handle_arrival_event(self) -> None:
"""
Handle an arrival event in the simulation.
"""
self.num_in_system += 1
self.num_arrivals += 1
if self.num_in_system <= 1:
self.t_depart = self.clock + self.generate_service()
self.t_arrival = self.clock + self.generate_interarrival()
def handle_departure_event(self) -> None:
"""
Handle a departure event in the simulation.
"""
self.num_in_system -= 1
self.num_departs += 1
if self.num_in_system > 0:
self.t_depart = self.clock + self.generate_service()
else:
self.t_depart = float("inf")
def generate_interarrival(self) -> float:
"""
Generate the time until the next arrival event using an exponential distribution.
"""
return np.random.exponential(1 / 3)
def generate_service(self) -> float:
"""
Generate the service time for an entity using an exponential distribution.
"""
return np.random.exponential(1 / 4)We can then initialize the simulation and run it for discrete units of time like this:
from .simulation import Simulation
def main() -> None:
S = Simulation()
for _ in range(2018):
S.advance_time()
if __name__ == "__main__":
main()Grogan’s implementation is a pedagogical example that can be quickly coded and explained. What can we use for prototyping and using something more fleshed out?
The Ciw package is a ready-to-use discrete event simulation package for queueing networks. It covers a variety of cases, including a similar simulation:
import ciw
# Define the parameters for the arrival and service processes
arrival_rate = 3 # Interarrival rate (lambda)
service_rate = 4 # Service rate (mu)
# Define the parameters for the Ciw network
network = ciw.create_network(
arrival_distributions=[ciw.dists.Exponential(arrival_rate)],
service_distributions=[ciw.dists.Exponential(service_rate)],
number_of_servers=[1],
)
# Create and run the simulation
ciw.seed(2018)
sim = ciw.Simulation(network)
sim.simulate_until_max_time(2018)