Add EventGenerator class for recurring event patterns#3201
Merged
Conversation
Add EventGenerator class for recurring event patterns in the experimental discrete event simulation. EventGenerator creates recurring events at specified intervals, supporting both fixed and callable intervals for stochastic processes. It provides start(at, after) and stop(at, after, count) methods for flexible control over when patterns begin and end, with method chaining support. This is a first step toward a cleaner scheduling API that distinguishes between one-off events and recurring patterns.
|
Performance benchmarks:
|
quaquel
reviewed
Jan 24, 2026
| self, | ||
| model: Model, | ||
| function: Callable, | ||
| interval: float | int | Callable[[Model], float | int], |
Member
There was a problem hiding this comment.
I don't understand the typing of the callable here. Why would it need a number next to model?
Member
Author
There was a problem hiding this comment.
Sharp eye. This is intended, the Model is passed (and typed) so you can access the random number generator, like for:
# Poisson arrival
EventGenerator(model, fn, interval=lambda m: m.random.expovariate(2.0))
quaquel
reviewed
Jan 24, 2026
mesa/experimental/devs/eventlist.py
Outdated
Comment on lines
+152
to
+153
| function_args: list[Any] | None = None, | ||
| function_kwargs: dict[str, Any] | None = None, |
Member
There was a problem hiding this comment.
I understand doing this, although with functools.partial we can also ofload this to a python library.
Member
Author
There was a problem hiding this comment.
Thanks for the swift review.
Would the API them become like this?
EventGenerator(model, partial(fn, "a", k="v"), interval=1.0)
quaquel
approved these changes
Jan 24, 2026
Member
quaquel
left a comment
There was a problem hiding this comment.
Looks good to me. Clean API, well-documented and tested.
This was referenced Feb 8, 2026
EwoutH
added a commit
that referenced
this pull request
Feb 9, 2026
Replaces the hand-rolled step rescheduling loop in `Model` with an internal `EventGenerator`, simplifying the code while keeping all behavior and API identical. The current step scheduling uses a manual pattern: `_do_step()` calls `_schedule_step()`, which creates a `SimulationEvent` that calls `_do_step()` again. This is functionally an `EventGenerator` — but reimplemented ad-hoc. Now that `EventGenerator` exists (#3201), we should use it. This also removes `_schedule_step()` entirely and simplifies both `_do_step()` and `_wrapped_step()`, reducing the amount of scheduling logic in `Model`.
EwoutH
added a commit
that referenced
this pull request
Feb 11, 2026
### Summary Exposes event scheduling and time advancement methods directly on `Model`, giving users a public API for scheduling events and advancing simulation time without needing to interact with the `Simulator` class. Basically, these methods allow us: - to replace all existing behavior (in examples, tutorials, etc.) - start deprecation all current scheduling and stepping APIs - remove them in Mesa 4. ### Motive Currently, users advance their models by calling `model.step()`, which is tightly coupled to the concept of a single discrete step. With the event-based architecture taking shape (#3201, #3204), we need public methods that let users think in terms of *time* rather than *steps*. The key method here is `model.run_for(duration)`. For existing ABM users, `model.run_for(1)` is functionally equivalent to `model.step()`: it advances time by 1 unit, which triggers the scheduled step event. But unlike `step()`, it generalizes naturally: `run_for(0.5)` advances half a time unit, `run_for(100)` advances 100 units processing all scheduled events along the way. This makes `run_for` the foundation for both traditional ABMs and event-driven models. This is also a stepping stone toward deprecating `model.step()` as the primary way to advance a model. Instead of calling a method that *is* a step, users call a method that *advances time*, and steps happen as scheduled events within that time. The mental model shifts from "execute step N" to "advance time, and whatever is scheduled will run."
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I was doing too much stuff at the same time. Let's break it down in a few smaller PRs.
This is (in my opinion) a crucial and obvious building block: A
EventGeneratorclass that generates events in a certain pattern.Summary
Adds an
EventGeneratorclass to the discrete event simulation module, enabling recurring event patterns as a first-class concept separate from one-off events.Motive
The current event scheduling system only supports single events via
SimulationEvent. For recurring behaviors (like data collection every N time units, or Poisson arrivals), users must manually reschedule events after each execution. This PR introducesEventGeneratorto handle recurring patterns cleanly.Implementation
EventGeneratorclass toeventlist.pyinterval=5.0) or callable intervals (interval=lambda m: m.random.expovariate(2.0))start(at, after)controls when the pattern beginsstop(at, after, count)controls when the pattern endsSimulationEventselffor chaining:generator.start(at=0).stop(count=10)Usage Examples
Additional Notes
This is part of a larger effort to improve Mesa's scheduling API (#2921). Future work will integrate
EventGeneratorfurther, potentially into theModelclass viamodel.create_pattern()and/or auto-schedulemodel.step()as a pattern.