M/M/1 Queue

This model has an arrival process, a service process, and one Queue representing the line. Arrivals put work into the queue; the server takes work out one item at a time and holds for a random service time. The collect function records the time-average queue length.

"""Tutorial 1.1: a first M/M/1 queue model."""

import cimba
import cimba.random as random
import cimba.sim as sim


class MM1(sim.Model):
    utilization: sim.Param
    avg_queue_length: sim.Output
    queue: sim.Queue


model = MM1("MM1")


@model.process
def arrival(env: MM1):
    while True:
        t_ia = random.exponential(1.0 / env.utilization)
        sim.hold(t_ia)
        env.queue.put(1)


@model.process
def service(env: MM1):
    while True:
        env.queue.get(1)
        t_srv = random.exponential(1.0)
        sim.hold(t_srv)


@model.collect
def collect_stats(env: MM1):
    env.avg_queue_length = env.queue.mean_level()


def main() -> None:
    cimba.logger_flags_on(cimba.LOGGER_INFO)
    exp = model.experiment(
        utilization=[0.75],
        replications=1,
        duration=10.0,
        warmup=0.0,
        seed=42,
    )
    failures = exp.run()
    if failures:
        raise RuntimeError(f"{failures} trial(s) failed")
    avg = float(exp["avg_queue_length"][0])
    print(f"Average queue length over the first 10 time units: {avg:.6f}")


if __name__ == "__main__":
    main()

The examples in tutorial/tut_1_1.py through tutorial/tut_1_7.py build this model up in smaller steps, ending with a parallel utilization sweep whose results are compared against the analytical rho^2 / (1 - rho) queue length.