Skip to content

Commit

Permalink
Replace the remaining schedulers with AgentSet functionality (#202)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
EwoutH authored Sep 21, 2024
1 parent 9a5396f commit 8d0c722
Show file tree
Hide file tree
Showing 12 changed files with 45 additions and 48 deletions.
2 changes: 1 addition & 1 deletion examples/pd_grid/analysis.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@
" grid[y][x] = 0\n",
" ax.pcolormesh(grid, cmap=bwr, vmin=0, vmax=1)\n",
" ax.axis(\"off\")\n",
" ax.set_title(f\"Steps: {model.schedule.steps}\")"
" ax.set_title(f\"Steps: {model.steps}\")"
]
},
{
Expand Down
4 changes: 2 additions & 2 deletions examples/pd_grid/pd_grid/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def step(self):
best_neighbor = max(neighbors, key=lambda a: a.score)
self.next_move = best_neighbor.move

if self.model.schedule_type != "Simultaneous":
if self.model.activation_order != "Simultaneous":
self.advance()

def advance(self):
Expand All @@ -42,7 +42,7 @@ def advance(self):

def increment_score(self):
neighbors = self.model.grid.get_neighbors(self.pos, True)
if self.model.schedule_type == "Simultaneous":
if self.model.activation_order == "Simultaneous":
moves = [neighbor.next_move for neighbor in neighbors]
else:
moves = [neighbor.move for neighbor in neighbors]
Expand Down
31 changes: 18 additions & 13 deletions examples/pd_grid/pd_grid/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,39 @@
class PdGrid(mesa.Model):
"""Model class for iterated, spatial prisoner's dilemma model."""

schedule_types = {
"Sequential": mesa.time.BaseScheduler,
"Random": mesa.time.RandomActivation,
"Simultaneous": mesa.time.SimultaneousActivation,
}
activation_regimes = ["Sequential", "Random", "Simultaneous"]

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

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

def __init__(
self, width=50, height=50, schedule_type="Random", payoffs=None, seed=None
self, width=50, height=50, activation_order="Random", payoffs=None, seed=None
):
"""
Create a new Spatial Prisoners' Dilemma Model.
Args:
width, height: Grid size. There will be one agent per grid cell.
schedule_type: Can be "Sequential", "Random", or "Simultaneous".
activation_order: Can be "Sequential", "Random", or "Simultaneous".
Determines the agent activation regime.
payoffs: (optional) Dictionary of (move, neighbor_move) payoffs.
"""
super().__init__()
self.activation_order = activation_order
self.grid = mesa.space.SingleGrid(width, height, torus=True)
self.schedule_type = schedule_type
self.schedule = self.schedule_types[self.schedule_type](self)

# Create agents
for x in range(width):
for y in range(height):
agent = PDAgent(self)
self.grid.place_agent(agent, (x, y))
self.schedule.add(agent)

self.datacollector = mesa.DataCollector(
{
"Cooperating_Agents": lambda m: len(
[a for a in m.schedule.agents if a.move == "C"]
[a for a in m.agents if a.move == "C"]
)
}
)
Expand All @@ -53,8 +47,19 @@ def __init__(
self.datacollector.collect(self)

def step(self):
self.schedule.step()
# collect data
# Activate all agents, based on the activation regime
match self.activation_order:
case "Sequential":
self.agents.do("step")
case "Random":
self.agents.shuffle_do("step")
case "Simultaneous":
self.agents.do("step")
self.agents.do("advance")
case _:
raise ValueError(f"Unknown activation order: {self.activation_order}")

# Collect data
self.datacollector.collect(self)

def run(self, n):
Expand Down
6 changes: 3 additions & 3 deletions examples/pd_grid/pd_grid/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
model_params = {
"height": 50,
"width": 50,
"schedule_type": mesa.visualization.Choice(
"Scheduler type",
"activation_order": mesa.visualization.Choice(
"Activation regime",
value="Random",
choices=list(PdGrid.schedule_types.keys()),
choices=PdGrid.activation_regimes,
),
}

Expand Down
2 changes: 1 addition & 1 deletion examples/pd_grid/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Launch the ``Demographic Prisoner's Dilemma Activation Schedule.ipynb`` notebook
## Files

* ``run.py`` is the entry point for the font-end simulations.
* ``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.
* ``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.
* ``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.

## Further Reading
Expand Down
8 changes: 4 additions & 4 deletions examples/schelling/analysis.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@
}
],
"source": [
"while model.running and model.schedule.steps < 100:\n",
"while model.running and model.steps < 100:\n",
" model.step()\n",
"print(model.schedule.steps) # Show how many steps have actually run"
"print(model.steps) # Show how many steps have actually run"
]
},
{
Expand Down Expand Up @@ -328,15 +328,15 @@
" Find the % of agents that only have neighbors of their same type.\n",
" \"\"\"\n",
" segregated_agents = 0\n",
" for agent in model.schedule.agents:\n",
" for agent in model.agents:\n",
" segregated = True\n",
" for neighbor in model.grid.iter_neighbors(agent.pos, True):\n",
" if neighbor.type != agent.type:\n",
" segregated = False\n",
" break\n",
" if segregated:\n",
" segregated_agents += 1\n",
" return segregated_agents / model.schedule.get_agent_count()"
" return segregated_agents / len(model.agents)"
]
},
{
Expand Down
8 changes: 4 additions & 4 deletions examples/schelling_experimental/analysis.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,9 @@
}
],
"source": [
"while model.running and model.schedule.steps < 100:\n",
"while model.running and model.steps < 100:\n",
" model.step()\n",
"print(model.schedule.steps) # Show how many steps have actually run"
"print(model.steps) # Show how many steps have actually run"
]
},
{
Expand Down Expand Up @@ -328,15 +328,15 @@
" Find the % of agents that only have neighbors of their same type.\n",
" \"\"\"\n",
" segregated_agents = 0\n",
" for agent in model.schedule.agents:\n",
" for agent in model.agents:\n",
" segregated = True\n",
" for neighbor in model.grid.iter_neighbors(agent.pos, True):\n",
" if neighbor.type != agent.type:\n",
" segregated = False\n",
" break\n",
" if segregated:\n",
" segregated_agents += 1\n",
" return segregated_agents / model.schedule.get_agent_count()"
" return segregated_agents / len(model.agents)"
]
},
{
Expand Down
3 changes: 1 addition & 2 deletions examples/sugarscape_cg/Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ The model is tests and demonstrates several Mesa concepts and features:
- MultiGrid
- Multiple agent types (ants, sugar patches)
- Overlay arbitrary text (wolf's energy) on agent's shapes while drawing on CanvasGrid
- Dynamically removing agents from the grid and schedule when they die
- Dynamically removing agents from the grid and model when they die

## Installation

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

* ``sugarscape/agents.py``: Defines the SsAgent, and Sugar agent classes.
* ``sugarscape/schedule.py``: This is exactly based on wolf_sheep/schedule.py.
* ``sugarscape/model.py``: Defines the Sugarscape Constant Growback model itself
* ``sugarscape/server.py``: Sets up the interactive visualization server
* ``run.py``: Launches a model visualization server.
Expand Down
2 changes: 1 addition & 1 deletion examples/sugarscape_cg/sugarscape_cg/agents.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ def step(self):
self.eat()
if self.sugar <= 0:
self.model.grid.remove_agent(self)
self.model.schedule.remove(self)
self.remove()


class Sugar(mesa.Agent):
Expand Down
18 changes: 7 additions & 11 deletions examples/sugarscape_cg/sugarscape_cg/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,9 @@ def __init__(self, width=50, height=50, initial_population=100):
self.height = height
self.initial_population = initial_population

self.schedule = mesa.time.RandomActivationByType(self)
self.grid = mesa.space.MultiGrid(self.width, self.height, torus=False)
self.datacollector = mesa.DataCollector(
{"SsAgent": lambda m: m.schedule.get_type_count(SsAgent)}
{"SsAgent": lambda m: len(m.agents_by_type[SsAgent])}
)

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

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

self.running = True
self.datacollector.collect(self)

def step(self):
self.schedule.step()
# Step suger and agents
self.agents_by_type[Sugar].do("step")
self.agents_by_type[SsAgent].shuffle_do("step")
# collect data
self.datacollector.collect(self)
if self.verbose:
print([self.schedule.time, self.schedule.get_type_count(SsAgent)])
print(f"Step: {self.steps}, SsAgents: {len(self.agents_by_type[SsAgent])}")

def run_model(self, step_count=200):
if self.verbose:
print(
"Initial number Sugarscape Agent: ",
self.schedule.get_type_count(SsAgent),
f"Initial number Sugarscape Agents: {len(self.agents_by_type[SsAgent])}"
)

for i in range(step_count):
self.step()

if self.verbose:
print("")
print(
"Final number Sugarscape Agent: ",
self.schedule.get_type_count(SsAgent),
f"\nFinal number Sugarscape Agents: {len(self.agents_by_type[SsAgent])}"
)
4 changes: 2 additions & 2 deletions examples/sugarscape_g1mt/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def test_decreasing_price_variance():
model.datacollector._new_model_reporter(
"price_variance",
lambda m: np.var(
flatten([a.prices for a in m.schedule.agents_by_type[Trader].values()])
flatten([a.prices for a in m.agents_by_type[Trader].values()])
),
)
model.run_model(step_count=50)
Expand All @@ -40,7 +40,7 @@ def calculate_carrying_capacities(enable_trade):
for vision_max in visions:
model = SugarscapeG1mt(vision_max=vision_max, enable_trade=enable_trade)
model.run_model(step_count=50)
carrying_capacities.append(len(model.schedule.agents_by_type[Trader]))
carrying_capacities.append(len(model.agents_by_type[Trader]))
return carrying_capacities

# Carrying capacity should increase over mean vision (figure IV-6).
Expand Down
5 changes: 1 addition & 4 deletions examples/wolf_sheep/wolf_sheep/test_random_walk.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

from mesa import Model
from mesa.space import MultiGrid
from mesa.time import RandomActivation
from mesa.visualization.TextVisualization import TextGrid, TextVisualization
from wolf_sheep.random_walk import RandomWalker

Expand Down Expand Up @@ -40,17 +39,15 @@ def __init__(self, width, height, agent_count):
self.grid = MultiGrid(self.width, self.height, torus=True)
self.agent_count = agent_count

self.schedule = RandomActivation(self)
# Create agents
for i in range(self.agent_count):
x = self.random.randrange(self.width)
y = self.random.randrange(self.height)
a = WalkerAgent(i, (x, y), self, True)
self.schedule.add(a)
self.grid.place_agent(a, (x, y))

def step(self):
self.schedule.step()
self.agents.shuffle_do("step")


class WalkerWorldViz(TextVisualization):
Expand Down

0 comments on commit 8d0c722

Please sign in to comment.