Skip to content

Add M/M/c queue example as canonical DES model#340

Open
EwoutH wants to merge 2 commits intomainfrom
mmc
Open

Add M/M/c queue example as canonical DES model#340
EwoutH wants to merge 2 commits intomainfrom
mmc

Conversation

@EwoutH
Copy link
Member

@EwoutH EwoutH commented Feb 27, 2026

Summary

Adds a classic M/M/c queuing model as the first pure discrete event simulation example, demonstrating Mesa's new event scheduling API without using step().

Motive

Mesa 3.5 introduced public event scheduling and time advancement (mesa/mesa#3266), but we lack an example showing pure DES usage. The M/M/c queue is the "hello world" of discrete event simulation: simple, well-understood, and with analytical solutions for validation. It fills the gap between our ABM examples and the new DES capabilities.

Implementation

  • agents.py: Customer (created on arrival, removed after service) and Server (pulls from queue when idle — server-centric design).
  • model.py: MMcQueue model using schedule_recurring with a callable interval for Poisson arrivals and schedule_event(after=...) for service completions. Disables the default step schedule via _default_schedule.stop().
  • analytical_mmc.py: Erlang C steady-state solutions for validation.

Usage Examples

from model import MMcQueue, analytical_mmc

model = MMcQueue(arrival_rate=2.0, service_rate=1.0, n_servers=3, rng=42)
model.run_until(10_000)

print(model.server_utilization)  # ≈ 0.667
print(model.avg_wait_time)       # ≈ 0.375

# Compare against analytical steady-state values
analytical = analytical_mmc(2.0, 1.0, 3)

Running python model.py prints a comparison table:

M/M/3 Queue (λ=2.0, μ=1.0, T=10000.0)
Customers served: 19992

Metric                     Simulated   Analytical
---------------------------------------------------
Server utilization              0.6672       0.6667
Avg wait time                   0.3716       0.3750
Avg system time                 1.3716       1.3750

Additional Notes

  • The model disables the default step schedule with _default_schedule.stop(). This works but may warrant a more explicit opt-out mechanism for pure DES models (e.g. a Model parameter).
  • No visualization app is included yet, an implementation would be appreciated!

Adds a classic M/M/c queuing model as the first pure discrete event simulation example, demonstrating Mesa's new event scheduling API without using step().
This way python model.py works directly, and importlib.import_module("examples.mmc_queue.model") in the test harness also works. It's a common pattern for examples that need to function both standalone and as part of a package.
rng: Random number generator seed.
"""

def __init__(self, arrival_rate=1.0, service_rate=0.5, n_servers=2, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

consider using a scenario class here.

),
)

def _customer_arrival(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is a question beyond this example, but should this not become some seperate mesa base class we can use?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could very well be!

I mainly created this example to see if we supported everything to do it. The answer is yes, and for future potential Mesa improvements we could check if it simplifies this example or not.

self.servers = [Server(self, service_rate) for _ in range(n_servers)]

# Disable default step schedule — pure DES
self._default_schedule.stop()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this also reveals something we have to fix more elegantly in mesa 4.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed.

interval=lambda m: m.rng.exponential(1.0 / m.arrival_rate),
start=0.0,
),
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about adding the new data collection to this example. Your use of properties should make this quite easy.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds cool, but let's make that a separate PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants