Unify event list between Model and Simulator#3204
Conversation
mesa/experimental/devs/simulator.py
Outdated
| def _schedule_step(self, time: int) -> None: | ||
| """Schedule the model step at the given time.""" | ||
| event = SimulationEvent(time, self._do_step, priority=Priority.HIGH) | ||
| self.event_list.add_event(event) | ||
|
|
||
| def _do_step(self) -> None: | ||
| """Execute one step: call user step, increment steps, schedule next.""" | ||
| self.model.steps += 1 | ||
| self.model._user_step() | ||
| self._schedule_step(int(self.model.time) + 1) |
There was a problem hiding this comment.
what is the idea behind this and should this not use the EventGenerator?
There was a problem hiding this comment.
The idea is that no matter if you progress time with model.step() or simulator.run_for(1) you get the exact same behavior. I hope this will make it easier to deprecate old behavior and add public APIs for new ones.
It might have used EventGenerator. For now this was the minimal-change version to get this working.
| self._advance_time(self.time + 1) | ||
| self._user_step(*args, **kwargs) | ||
|
|
||
| def _advance_time(self, until: float) -> None: |
There was a problem hiding this comment.
not sure I would put this on the model directly. See the earlier conversation on encapsulating the different parts instead of having a blob model class.
There was a problem hiding this comment.
We should have a bigger discussion where we put the public API. This is a private method that bridges the gap between the step() time progression in Mesa 3 and whatever we will have in Mesa 4. It will be removed once processing time with step() is deactivated.
There was a problem hiding this comment.
We should have a bigger discussion where we put the public API.
Yes, I agree on this. I see a few options. One of which is what I think you did in an earlier PR: have a bunch of "passthrough" methods on the model which internally call the appropriate method on the underlying class. Another option would be to use multiple inheritance, although that can become tricky quickly.
There was a problem hiding this comment.
I’m indeed exploring a Scheduling Mixin. It is quite clean, full IDE and typing support, and can be added to both the Model and Agent.
This is still some essential foundation to be able to properly deprecate old behavior.
|
Performance benchmarks:
|
4724ce6 to
fafd5c7
Compare
|
Performance benchmarks:
|
This comment was marked as outdated.
This comment was marked as outdated.
bb843e5 to
71fa340
Compare
|
Performance benchmarks:
|
|
I accidentally touched the internal event scheduling loop, which of course was highly optimized. Reverted to the old loop, performance restored. Coverage failure is unrelated. |
|
Can you check why coverage is only 80%? |
|
I didn’t add test for the setstate and get state. I already split it of in #3205, I will address it there. |
|
While convenient, I don't like the "Update branch from main" applied on my development branches. The merge commits makes undoing a commit, cherry picking and rebasing more difficult. I can rebase on request :) |
One event list, one unified way to advance time. Whether you call model.step() or use a simulator, it should work the same way underneath. One event list, two ways to drive it: 1. model.step() → advances 1 time unit, runs user step 2. simulator.run_until() → advances to end_time, processing scheduled events (including steps for ABMSimulator)
645167b to
2a45e3f
Compare
Now all green, including coverage |
|
|
||
| from mesa.agent import Agent, AgentSet | ||
| from mesa.experimental.devs import Simulator | ||
| from mesa.experimental.devs.eventlist import EventList, Priority, SimulationEvent |
There was a problem hiding this comment.
since you are now integrating everything into model, when do you plan to move devs out of experimental?
There was a problem hiding this comment.
Good question. I think as soon as we have a stable public API might make sense.
### 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."
Summary
Refactors the internal architecture so that
Modelowns a single event list (_event_list) that simulators use via a property. This lays the groundwork for a unified event-based API while maintaining full backward compatibility.Motive
We're moving toward an event-based API where both
model.step()and simulator-driven execution use the same underlying mechanism. Previously, theSimulatorowned its ownEventList, creating two separate systems. This PR unifies them so there's one event list with two ways to drive it:model.step()→ advances 1 time unit via_advance_time()simulator.run_until()/run_for()→ processes events from the same listImplementation
_event_listattribute and_advance_time(until)method that processes events up to a given timeevent_listfrom instance variable to property that returnsmodel._event_list_schedule_step()and_do_step()methods, removed duplicaterun_until()override__getstate__/__setstate__for pickling support with weak referencesUsage Examples
No public API changes. Existing code works exactly as before:
Additional Notes