Update Epstein Civil Violence model to use the new experimental Scenario class#3167
Update Epstein Civil Violence model to use the new experimental Scenario class#3167
Scenario class#3167Conversation
This comment was marked as off-topic.
This comment was marked as off-topic.
|
One issue (and why the tests are failing) is that when using a module-level The problem: # model.py (at module level)
default_scenario = Scenario(...) # Created once
# Later:
model1 = EpsteinCivilViolence(seed=42) # scenario.model = model1, model1.running = True
model2 = EpsteinCivilViolence(seed=43) # Tries to set scenario.model = model2
# ERROR: "Cannot change scenario parameters during model run"
# because scenario.model still points to model1 where running=TrueCurrent workaround: Create a fresh scenario instance inside def __init__(self, ..., scenario=None):
if scenario is None:
scenario = Scenario(rng=seed, citizen_density=0.7, ...)
super().__init__(seed=seed, scenario=scenario)Subclassing might also help with this. Then we could have: class EpsteinScenario(Scenario):
citizen_density: float = 0.7
cop_vision: int = 7
# ... etcAnd then either:
This gives us type hints, default values, and avoids the shared-instance problem. (but maybe I'm using all this wrong) |
This is useful for stress-testing the design. In part, this interacts with #3166.
|
|
Thanks. I'm curious what you think about #3168. |
|
I assume this can be updated with #3168 merged. While thinking about it some more, I am inclined to make scenarios frozen. That is, they simply cannot be changed once created. We can then remove the |
|
I have made a bunch of updates. The bigger issue is that the UI side of things is not yet updated to use |
|
Ah, I also had some changes ready on my branch (part to develop and test #3168), I will check how they compare. |
8c54c93 to
404db2a
Compare
|
I stored your work on |
|
No, feel free to continue what you were doing. I was mainly curious to see how much #3168 helped. |
|
In that case I think this PR is ready. Viz indeed might need to be updated. One question: I put the |
I don't know yet. It basically means that people are forced to use |
| app.page # noqa: B018 | ||
|
|
||
| model = EpsteinCivilViolence(rng=42) | ||
| model = EpsteinCivilViolence() |
There was a problem hiding this comment.
No, rng = 42 is defined in EpsteinScenario, and if no Scenario is inputted, a default one is created:
if scenario is None:
scenario = EpsteinScenario()That should pass the rng=42 to the right place, right?
| citizen.move_to(cell) | ||
| if klass is not None: | ||
| agent = klass(self) # Either Citizen or Cop | ||
| agent.move_to(cell) |
There was a problem hiding this comment.
this change higlights how Scenario can really clean up stuff.
There was a problem hiding this comment.
Yes, I was very happy when I realized this could be simplified!
quaquel
left a comment
There was a problem hiding this comment.
A few minor suggestions and clarification questions, but once addressed feel free to merge.
|
It might be good to test this against #3178 to see if it works with solara. If I have time today, I'll try that and report back here. |
|
Awesome, thanks! |
… `Scenario` class Replaces individual model and agent parameters with a Scenario object, centralizing configuration for agent vision, movement, legitimacy, jail terms, and other settings. Updates agent and model initialization to reference scenario attributes.
Replaces the default_scenario instance with a typed EpsteinScenario subclass containing default parameters. The model now creates a default EpsteinScenario if none is provided, improving type safety and clarity of scenario configuration.
Co-authored-by: Jan Kwakkel <[email protected]>
ac74c67 to
ded7a68
Compare
| @@ -1,10 +1,30 @@ | |||
| import mesa | |||
| from mesa.discrete_space import OrthogonalMooreGrid, OrthogonalVonNeumannGrid # | |||
| from mesa.discrete_space import OrthogonalMooreGrid, OrthogonalVonNeumannGrid | |||
There was a problem hiding this comment.
| from mesa.discrete_space import OrthogonalMooreGrid, OrthogonalVonNeumannGrid | |
| from mesa.discrete_space import OrthogonalMooreGrid, OrthogonalVonNeumannGrid | |
| from typing import Literal |
|
|
||
|
|
||
| # Define a typed scenario subclass with defaults | ||
| class EpsteinScenario(Scenario): |
There was a problem hiding this comment.
this should be used correctly in app.py
|
I'll try to take a look and see what's going on with #3178 or, otherwise, open a new PR myself to fix it. |
|
I can also confirm that this example now runs locally. |
This PR updates the Epstein Civil Violence model to use the new experimental
Scenarioclass for parameter management.The goal for me personally was to get some feel how our new
Scenarioclass works in practice. My first impression is quite positive, it removes so much of the boilerplate of just passing variables.It also led me to a few open questions:
We now have default parameters. How do we want to handle that? I now chose a
default_scenario, does that make sense?Is using a
default_scenario, where whould it be defined? At module level, as a class attribute, a separatescenario.py, or somewhere else?Documentation patters: Is
# Parameters accessed via model.scenario: x, y, zsufficient in docstrings, or should we be more explicit about each parameter's purpose?With the current "just create an instance", type hinting doesn't work:

Should we support/recommend subclassing for this? That might also make a more elegant way to define default values.
Or could we make
Scenariobe a@dataclassto enable type-hinted subclasses without boilerplate?