Skip to content

Commit 8d0c722

Browse files
authored
Replace the remaining schedulers with AgentSet functionality (#202)
This PR completes the migration from schedulers to AgentSet functionality across the mesa-examples repository for all regular (non-`gis`/-`rl`) examples. Key changes include: - Replaced `RandomActivation`, `SimultaneousActivation`, and `RandomActivationByType` schedulers with appropriate AgentSet methods - Updated `Model.step()` implementations to use AgentSet activation - Removed references to `schedule.steps`, `schedule.agents`, and `schedule.agents_by_type` - Updated agent addition/removal logic to work with AgentSets - Adjusted data collection and visualization code to use `Model.steps` and `Model.agents` For more details on migrating from schedulers to AgentSets, see the migration guide: https://mesa.readthedocs.io/en/latest/migration_guide.html#time-and-schedulers
1 parent 9a5396f commit 8d0c722

File tree

12 files changed

+45
-48
lines changed

12 files changed

+45
-48
lines changed

examples/pd_grid/analysis.ipynb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
" grid[y][x] = 0\n",
7373
" ax.pcolormesh(grid, cmap=bwr, vmin=0, vmax=1)\n",
7474
" ax.axis(\"off\")\n",
75-
" ax.set_title(f\"Steps: {model.schedule.steps}\")"
75+
" ax.set_title(f\"Steps: {model.steps}\")"
7676
]
7777
},
7878
{

examples/pd_grid/pd_grid/agent.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def step(self):
3333
best_neighbor = max(neighbors, key=lambda a: a.score)
3434
self.next_move = best_neighbor.move
3535

36-
if self.model.schedule_type != "Simultaneous":
36+
if self.model.activation_order != "Simultaneous":
3737
self.advance()
3838

3939
def advance(self):
@@ -42,7 +42,7 @@ def advance(self):
4242

4343
def increment_score(self):
4444
neighbors = self.model.grid.get_neighbors(self.pos, True)
45-
if self.model.schedule_type == "Simultaneous":
45+
if self.model.activation_order == "Simultaneous":
4646
moves = [neighbor.next_move for neighbor in neighbors]
4747
else:
4848
moves = [neighbor.move for neighbor in neighbors]

examples/pd_grid/pd_grid/model.py

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,39 @@
66
class PdGrid(mesa.Model):
77
"""Model class for iterated, spatial prisoner's dilemma model."""
88

9-
schedule_types = {
10-
"Sequential": mesa.time.BaseScheduler,
11-
"Random": mesa.time.RandomActivation,
12-
"Simultaneous": mesa.time.SimultaneousActivation,
13-
}
9+
activation_regimes = ["Sequential", "Random", "Simultaneous"]
1410

1511
# This dictionary holds the payoff for this agent,
1612
# keyed on: (my_move, other_move)
1713

1814
payoff = {("C", "C"): 1, ("C", "D"): 0, ("D", "C"): 1.6, ("D", "D"): 0}
1915

2016
def __init__(
21-
self, width=50, height=50, schedule_type="Random", payoffs=None, seed=None
17+
self, width=50, height=50, activation_order="Random", payoffs=None, seed=None
2218
):
2319
"""
2420
Create a new Spatial Prisoners' Dilemma Model.
2521
2622
Args:
2723
width, height: Grid size. There will be one agent per grid cell.
28-
schedule_type: Can be "Sequential", "Random", or "Simultaneous".
24+
activation_order: Can be "Sequential", "Random", or "Simultaneous".
2925
Determines the agent activation regime.
3026
payoffs: (optional) Dictionary of (move, neighbor_move) payoffs.
3127
"""
3228
super().__init__()
29+
self.activation_order = activation_order
3330
self.grid = mesa.space.SingleGrid(width, height, torus=True)
34-
self.schedule_type = schedule_type
35-
self.schedule = self.schedule_types[self.schedule_type](self)
3631

3732
# Create agents
3833
for x in range(width):
3934
for y in range(height):
4035
agent = PDAgent(self)
4136
self.grid.place_agent(agent, (x, y))
42-
self.schedule.add(agent)
4337

4438
self.datacollector = mesa.DataCollector(
4539
{
4640
"Cooperating_Agents": lambda m: len(
47-
[a for a in m.schedule.agents if a.move == "C"]
41+
[a for a in m.agents if a.move == "C"]
4842
)
4943
}
5044
)
@@ -53,8 +47,19 @@ def __init__(
5347
self.datacollector.collect(self)
5448

5549
def step(self):
56-
self.schedule.step()
57-
# collect data
50+
# Activate all agents, based on the activation regime
51+
match self.activation_order:
52+
case "Sequential":
53+
self.agents.do("step")
54+
case "Random":
55+
self.agents.shuffle_do("step")
56+
case "Simultaneous":
57+
self.agents.do("step")
58+
self.agents.do("advance")
59+
case _:
60+
raise ValueError(f"Unknown activation order: {self.activation_order}")
61+
62+
# Collect data
5863
self.datacollector.collect(self)
5964

6065
def run(self, n):

examples/pd_grid/pd_grid/server.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,10 @@
99
model_params = {
1010
"height": 50,
1111
"width": 50,
12-
"schedule_type": mesa.visualization.Choice(
13-
"Scheduler type",
12+
"activation_order": mesa.visualization.Choice(
13+
"Activation regime",
1414
value="Random",
15-
choices=list(PdGrid.schedule_types.keys()),
15+
choices=PdGrid.activation_regimes,
1616
),
1717
}
1818

examples/pd_grid/readme.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ Launch the ``Demographic Prisoner's Dilemma Activation Schedule.ipynb`` notebook
2828
## Files
2929

3030
* ``run.py`` is the entry point for the font-end simulations.
31-
* ``pd_grid/``: contains the model and agent classes; the model takes a ``schedule_type`` string as an argument, which determines what schedule type the model uses: Sequential, Random or Simultaneous.
31+
* ``pd_grid/``: contains the model and agent classes; the model takes a ``activation_order`` string as an argument, which determines in which order agents are activated: Sequential, Random or Simultaneous.
3232
* ``Demographic Prisoner's Dilemma Activation Schedule.ipynb``: Jupyter Notebook for running the scheduling experiment. This runs the model three times, one for each activation type, and demonstrates how the activation regime drives the model to different outcomes.
3333

3434
## Further Reading

examples/schelling/analysis.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@
6565
}
6666
],
6767
"source": [
68-
"while model.running and model.schedule.steps < 100:\n",
68+
"while model.running and model.steps < 100:\n",
6969
" model.step()\n",
70-
"print(model.schedule.steps) # Show how many steps have actually run"
70+
"print(model.steps) # Show how many steps have actually run"
7171
]
7272
},
7373
{
@@ -328,15 +328,15 @@
328328
" Find the % of agents that only have neighbors of their same type.\n",
329329
" \"\"\"\n",
330330
" segregated_agents = 0\n",
331-
" for agent in model.schedule.agents:\n",
331+
" for agent in model.agents:\n",
332332
" segregated = True\n",
333333
" for neighbor in model.grid.iter_neighbors(agent.pos, True):\n",
334334
" if neighbor.type != agent.type:\n",
335335
" segregated = False\n",
336336
" break\n",
337337
" if segregated:\n",
338338
" segregated_agents += 1\n",
339-
" return segregated_agents / model.schedule.get_agent_count()"
339+
" return segregated_agents / len(model.agents)"
340340
]
341341
},
342342
{

examples/schelling_experimental/analysis.ipynb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,9 +65,9 @@
6565
}
6666
],
6767
"source": [
68-
"while model.running and model.schedule.steps < 100:\n",
68+
"while model.running and model.steps < 100:\n",
6969
" model.step()\n",
70-
"print(model.schedule.steps) # Show how many steps have actually run"
70+
"print(model.steps) # Show how many steps have actually run"
7171
]
7272
},
7373
{
@@ -328,15 +328,15 @@
328328
" Find the % of agents that only have neighbors of their same type.\n",
329329
" \"\"\"\n",
330330
" segregated_agents = 0\n",
331-
" for agent in model.schedule.agents:\n",
331+
" for agent in model.agents:\n",
332332
" segregated = True\n",
333333
" for neighbor in model.grid.iter_neighbors(agent.pos, True):\n",
334334
" if neighbor.type != agent.type:\n",
335335
" segregated = False\n",
336336
" break\n",
337337
" if segregated:\n",
338338
" segregated_agents += 1\n",
339-
" return segregated_agents / model.schedule.get_agent_count()"
339+
" return segregated_agents / len(model.agents)"
340340
]
341341
},
342342
{

examples/sugarscape_cg/Readme.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ The model is tests and demonstrates several Mesa concepts and features:
2121
- MultiGrid
2222
- Multiple agent types (ants, sugar patches)
2323
- Overlay arbitrary text (wolf's energy) on agent's shapes while drawing on CanvasGrid
24-
- Dynamically removing agents from the grid and schedule when they die
24+
- Dynamically removing agents from the grid and model when they die
2525

2626
## Installation
2727

@@ -44,7 +44,6 @@ Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and p
4444
## Files
4545

4646
* ``sugarscape/agents.py``: Defines the SsAgent, and Sugar agent classes.
47-
* ``sugarscape/schedule.py``: This is exactly based on wolf_sheep/schedule.py.
4847
* ``sugarscape/model.py``: Defines the Sugarscape Constant Growback model itself
4948
* ``sugarscape/server.py``: Sets up the interactive visualization server
5049
* ``run.py``: Launches a model visualization server.

examples/sugarscape_cg/sugarscape_cg/agents.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ def step(self):
6767
self.eat()
6868
if self.sugar <= 0:
6969
self.model.grid.remove_agent(self)
70-
self.model.schedule.remove(self)
70+
self.remove()
7171

7272

7373
class Sugar(mesa.Agent):

examples/sugarscape_cg/sugarscape_cg/model.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,9 @@ def __init__(self, width=50, height=50, initial_population=100):
3737
self.height = height
3838
self.initial_population = initial_population
3939

40-
self.schedule = mesa.time.RandomActivationByType(self)
4140
self.grid = mesa.space.MultiGrid(self.width, self.height, torus=False)
4241
self.datacollector = mesa.DataCollector(
43-
{"SsAgent": lambda m: m.schedule.get_type_count(SsAgent)}
42+
{"SsAgent": lambda m: len(m.agents_by_type[SsAgent])}
4443
)
4544

4645
# Create sugar
@@ -51,7 +50,6 @@ def __init__(self, width=50, height=50, initial_population=100):
5150
max_sugar = sugar_distribution[x, y]
5251
sugar = Sugar(self, max_sugar)
5352
self.grid.place_agent(sugar, (x, y))
54-
self.schedule.add(sugar)
5553

5654
# Create agent:
5755
for i in range(self.initial_population):
@@ -62,31 +60,29 @@ def __init__(self, width=50, height=50, initial_population=100):
6260
vision = self.random.randrange(1, 6)
6361
ssa = SsAgent(self, False, sugar, metabolism, vision)
6462
self.grid.place_agent(ssa, (x, y))
65-
self.schedule.add(ssa)
6663

6764
self.running = True
6865
self.datacollector.collect(self)
6966

7067
def step(self):
71-
self.schedule.step()
68+
# Step suger and agents
69+
self.agents_by_type[Sugar].do("step")
70+
self.agents_by_type[SsAgent].shuffle_do("step")
7271
# collect data
7372
self.datacollector.collect(self)
7473
if self.verbose:
75-
print([self.schedule.time, self.schedule.get_type_count(SsAgent)])
74+
print(f"Step: {self.steps}, SsAgents: {len(self.agents_by_type[SsAgent])}")
7675

7776
def run_model(self, step_count=200):
7877
if self.verbose:
7978
print(
80-
"Initial number Sugarscape Agent: ",
81-
self.schedule.get_type_count(SsAgent),
79+
f"Initial number Sugarscape Agents: {len(self.agents_by_type[SsAgent])}"
8280
)
8381

8482
for i in range(step_count):
8583
self.step()
8684

8785
if self.verbose:
88-
print("")
8986
print(
90-
"Final number Sugarscape Agent: ",
91-
self.schedule.get_type_count(SsAgent),
87+
f"\nFinal number Sugarscape Agents: {len(self.agents_by_type[SsAgent])}"
9288
)

0 commit comments

Comments
 (0)