M/M/1 Queue Example
A classic single-server queueing system with theoretical validation.
Overview
The M/M/1 queue is the simplest queueing model:
M: Markovian (Poisson) arrivals
M: Markovian (exponential) service times
1: Single server
This example demonstrates:
Basic event scheduling
Server resource usage
Statistics collection (Tally, TimeSeries)
Comparison with theoretical values
Queueing Theory Background
For an M/M/1 queue with arrival rate λ and service rate μ:
Utilization: ρ = λ/μ
Average queue length: Lq = ρ² / (1 - ρ)
Average wait time: Wq = ρ / (μ(1 - ρ))
Average system time: W = 1 / (μ(1 - ρ))
The system is stable when ρ < 1.
Code Walkthrough
Customer Entity
from dataclasses import dataclass
from simcraft.core.entity import TimedEntity
@dataclass
class Customer(TimedEntity):
"""A customer in the queueing system."""
priority: int = 0
The TimedEntity base class provides automatic timing:
record_entry(time): Mark arrival timerecord_service_start(time): Mark when service beginsrecord_exit(time): Mark departure timewaiting_time: Time from entry to service startflow_time: Total time in system
Simulation Class
class MM1Queue(Simulation):
def __init__(
self,
arrival_rate: float = 0.8,
service_rate: float = 1.0,
) -> None:
super().__init__(name="MM1Queue")
self.arrival_rate = arrival_rate
self.service_rate = service_rate
self.rho = arrival_rate / service_rate
# Create server with exponential service time
self.server = Server(
sim=self,
capacity=1,
service_time=lambda: self.rng.exponential(1.0 / self.service_rate),
)
# Statistics
self.queue_length = TimeSeries(self, name="QueueLength")
self.wait_times = Tally(name="WaitTime")
self.system_times = Tally(name="SystemTime")
# Set up callbacks
self.server.on_service_start(self._on_service_start)
self.server.on_departure(self._on_departure)
Event Handlers
def on_init(self) -> None:
"""Schedule first arrival."""
self.schedule(self._arrival, delay=0)
def _arrival(self) -> None:
"""Handle customer arrival."""
customer = Customer()
customer.record_entry(self.now)
# Track queue length
self.queue_length.observe_change(1)
# Enter server queue
self.server.enqueue(customer)
# Schedule next arrival (exponential interarrival)
interarrival = self.rng.exponential(1.0 / self.arrival_rate)
self.schedule(self._arrival, delay=interarrival)
def _on_service_start(self, customer: Customer) -> None:
"""Record wait time when service begins."""
customer.record_service_start(self.now)
self.wait_times.observe(customer.waiting_time)
def _on_departure(self, customer: Customer) -> None:
"""Record system time when customer leaves."""
customer.record_exit(self.now)
self.queue_length.observe_change(-1)
self.system_times.observe(customer.flow_time)
Running the Example
from simcraft.examples.mm1_queue import MM1Queue
# Create simulation with ρ = 0.8
sim = MM1Queue(arrival_rate=0.8, service_rate=1.0)
# Run for 10,000 time units
sim.run(until=10000)
# Get results
report = sim.report()
print(f"Simulation queue length: {report['simulation_results']['average_queue_length']:.4f}")
print(f"Theoretical queue length: {report['theoretical_values']['average_queue_length']:.4f}")
Sample Output
M/M/1 Queue Simulation
============================================================
Parameters:
arrival_rate: 0.8000
service_rate: 1.0000
utilization_rho: 0.8000
Simulation Results:
simulation_time: 10000.0000
customers_arrived: 7987
customers_served: 7986
average_queue_length: 3.1842
average_wait_time: 3.9712
average_system_time: 4.9893
server_utilization: 0.7992
Theoretical Values:
average_queue_length: 3.2000
average_wait_time: 4.0000
average_system_time: 5.0000
Comparison (Simulation vs Theoretical):
Queue Length: 3.1842 vs 3.2000
Wait Time: 3.9712 vs 4.0000
Extending the Example
M/M/c Queue (Multiple Servers)
# Simply increase server capacity
self.server = Server(
sim=self,
capacity=3, # Three servers
service_time=lambda: self.rng.exponential(1.0 / self.service_rate),
)
Priority Queue
from simcraft.resources.queue import PriorityQueue
# Use priority queue instead of FIFO
self.server = Server(
sim=self,
capacity=1,
queue=PriorityQueue(),
service_time=lambda: self.rng.exponential(1.0 / self.service_rate),
)
# Assign priority when creating customers
customer = Customer(priority=self.rng.randint(1, 5))
Warmup Period
sim = MM1Queue(arrival_rate=0.8, service_rate=1.0)
# Run warmup (statistics are reset afterward)
sim.warmup(duration=1000)
# Run main simulation
sim.run(until=10000)
Source Code
The complete source code is in simcraft/examples/mm1_queue.py.