Skip to content

add pause and resume to EventGenerator#3431

Merged
quaquel merged 4 commits intomesa:mainfrom
souro26:eventgenerator-pause-and-resume
Mar 9, 2026
Merged

add pause and resume to EventGenerator#3431
quaquel merged 4 commits intomesa:mainfrom
souro26:eventgenerator-pause-and-resume

Conversation

@souro26
Copy link
Copy Markdown
Contributor

@souro26 souro26 commented Mar 4, 2026

Summary

This PR adds pause and resume methods to EventGenerator to allow temporarily suspending recurring event execution without terminating the generator.

After the addition of next_scheduled_time in #3423, this continues the improvements proposed in #3420 to make the lifecycle and scheduling state of EventGenerator clearer and easier to control.

Motive

Currently EventGenerator only has start and stop for lifecycle control. stop() fully terminates the generator and removes it from the model, which means temporarily suspending event generation requires restarting the generator again.

In some simulations it is useful to pause recurring behavior temporarily and later continue it without resetting the generator lifecycle. Providing explicit pause and resume methods allows this type of control without requiring users to manipulate internal state or restart generators.

Implementation

  1. Added an internal _paused state flag.
  2. Implemented pause which cancels the currently scheduled event while keeping the generator active.
  3. Implemented resume which schedules the next execution using the generator’s normal interval logic.
  4. Added a guard in _execute_and_reschedule so events are not rescheduled while the generator is paused.
  5. Ensured stop resets the paused state to keep lifecycle transitions consistent.

The design intentionally keeps the API minimal and builds on the existing scheduling logic rather than introducing additional scheduling parameters.

Usage Examples

gen = EventGenerator(model, fn, Schedule(interval=2.0))
gen.start()

# Temporarily suspend recurring execution
gen.pause()

# Resume event generation
gen.resume()

Additional Notes

  1. Pausing cancels the currently scheduled event but keeps the generator active.
  2. When a paused generator is resumed, the next execution is scheduled using the generator’s normal interval logic starting from the current simulation time. Missed executions during the paused period are not replayed.
  3. Existing behavior of start and stop remains unchanged.
  4. Additional tests were added to verify pause/resume lifecycle behavior and edge cases.

@github-actions
Copy link
Copy Markdown

github-actions bot commented Mar 4, 2026

Performance benchmarks:

Model Size Init time [95% CI] Run time [95% CI]
BoltzmannWealth small 🔵 -0.1% [-0.3%, +0.1%] 🔵 -0.4% [-0.6%, -0.3%]
BoltzmannWealth large 🔵 -0.0% [-0.6%, +0.7%] 🔵 -1.3% [-2.4%, -0.4%]
Schelling small 🔵 +0.9% [+0.7%, +1.1%] 🔵 +0.1% [-0.0%, +0.2%]
Schelling large 🔵 +0.1% [-0.6%, +0.9%] 🔵 +0.9% [+0.1%, +1.5%]
WolfSheep small 🔵 +1.0% [+0.8%, +1.2%] 🔵 -0.0% [-0.1%, +0.1%]
WolfSheep large 🔵 +2.7% [+1.7%, +3.7%] 🔵 +1.6% [+0.6%, +2.7%]
SugarscapeG1mt small 🔵 +0.6% [+0.2%, +1.0%] 🔵 -0.7% [-0.9%, -0.6%]
SugarscapeG1mt large 🔵 +0.5% [-0.2%, +1.1%] 🔵 -0.1% [-0.4%, +0.2%]
BoidFlockers small 🔵 -1.1% [-1.5%, -0.8%] 🔵 -1.8% [-2.0%, -1.7%]
BoidFlockers large 🔵 -0.9% [-1.3%, -0.5%] 🔵 -1.0% [-1.3%, -0.6%]

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Mar 5, 2026

I am open to considering this PR, but I fail to see a clear use case for it. So can you give some examples of when this might be useful?

@souro26
Copy link
Copy Markdown
Contributor Author

souro26 commented Mar 5, 2026

The main situation I had in mind is when a recurring process exists in the model but becomes temporarily inactive because of state changes.

For example, an agent might have some recurring behavior that runs every few ticks. If the agent becomes inactive for a while the user might want to suspend that behavior and later resume it when the agent becomes active again.

In that case stop() feels too final because it removes the generator entirely and the user would need to recreate it later. pause() keeps the generator part of the model lifecycle but cancels the currently scheduled event so nothing fires while it’s paused. When resume() is called, it just schedules the next execution again using the normal interval logic.

So the idea is mainly to model processes that temporarily stop due to state changes but are expected to continue later, rather than being permanently terminated.

@quaquel quaquel added the enhancement Release notes label label Mar 7, 2026
@souro26
Copy link
Copy Markdown
Contributor Author

souro26 commented Mar 8, 2026

I've been thinking about a few example use cases and here's what i came up with. One example comes from the Boltzmann Wealth model. Agents repeatedly move and exchange wealth. If expressed as a recurring process instead of the step() loop, an agent could temporarily stop participating in exchanges when it has no wealth and resume once it receives wealth again.

self.trade_process = EventGenerator(
    model,
    self.trade,
    Schedule(interval=1.0),
).start()

def trade(self):
    self.move()

    if self.wealth <= 0:
        self.trade_process.pause()
        return

    self.give_money()

def receive_money(self):
    self.wealth += 1
    if self.trade_process.is_paused:
        self.trade_process.resume()

Another example is the Wolf–Sheep model. Sheep and wolves lose energy every step and eventually die if their energy becomes negative. If this were expressed as a recurring process instead of a step() loop, an agent might pause its recurring behavior when it dies and resume if it later becomes active again.

self.behavior = EventGenerator(
    model,
    self.step_behavior,
    Schedule(interval=1.0),
).start()

def step_behavior(self):
    self.move()
    self.energy -= 1

    if self.energy < 0:
        self.behavior.pause()
        return

    self.feed()

In both situations the process is temporarily inactive due to state changes but expected to continue later, so pause() and resume() avoids having to destroy and recreate the generator with stop() and start().

@quaquel
Copy link
Copy Markdown
Member

quaquel commented Mar 8, 2026

Thanks for this. I have been thinking about this myself a bit as well. I actually think that a better use case is some random arrival process. Say we have a Poisson arrival process for some supermarket. This arrival process is only active when the store is open, but during off-hours, we might want to pause it (and schedule its reactivation for the next day).

@quaquel quaquel merged commit 151756b into mesa:main Mar 9, 2026
14 checks passed
@EwoutH
Copy link
Copy Markdown
Member

EwoutH commented Mar 13, 2026

@quaquel can you tag me as a reviewer on PRs related to time and events?

@EwoutH EwoutH mentioned this pull request Mar 13, 2026
42 tasks
EwoutH pushed a commit that referenced this pull request Mar 15, 2026
EwoutH pushed a commit that referenced this pull request Mar 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement Release notes label

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants