Skip to content

Commit c296a83

Browse files
Remove remaining mesa.space usage (#333)
1 parent 2a74f29 commit c296a83

14 files changed

Lines changed: 115 additions & 84 deletions

File tree

examples/conways_game_of_life_fast/model.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22
from mesa import Model
33
from mesa.datacollection import DataCollector
4-
from mesa.space import PropertyLayer
4+
from mesa.discrete_space import PropertyLayer
55
from scipy.signal import convolve2d
66

77

@@ -10,7 +10,9 @@ class GameOfLifeModel(Model):
1010
def __init__(self, width=10, height=10, alive_fraction=0.2):
1111
super().__init__()
1212
# Initialize the property layer for cell states
13-
self.cell_layer = PropertyLayer("cells", width, height, False, dtype=bool)
13+
self.cell_layer = PropertyLayer(
14+
"cells", (width, height), default_value=False, dtype=bool
15+
)
1416
# Randomly set cells to alive
1517
self.cell_layer.data = np.random.choice([True, False], size=(width, height), p=[alive_fraction, 1 - alive_fraction])
1618

examples/dining_philosophers/app.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ def DiningTable(model):
4444
)
4545
)
4646

47-
for agent in model.grid.get_all_cell_contents():
48-
node_id = agent.pos
47+
for agent in model.grid.agents:
48+
node_id = agent.cell.coordinate
4949
x, y = pos[node_id]
5050

5151
# Map (-1, 1) to (50, 350)
@@ -151,10 +151,9 @@ def SpaghettiBarChart(model):
151151
# We want them sorted by ID (position)
152152

153153
philosophers = []
154-
# Access internal agent storage for efficiency or just filter grid
155-
# model.philosophers is an AgentSet which is iterable
156-
157-
philosophers = sorted(model.philosophers, key=lambda p: p.position)
154+
philosophers = sorted(
155+
model.agents_by_type[PhilosopherAgent], key=lambda p: p.position
156+
)
158157

159158
labels = [f"P{p.position // 2}" for p in philosophers]
160159
values = [p.total_eaten for p in philosophers]

examples/dining_philosophers/dining_philosophers/agent.py

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from enum import Enum, auto
22

3-
import mesa
3+
from mesa.discrete_space import FixedAgent
44

55

66
class State(Enum):
@@ -9,28 +9,34 @@ class State(Enum):
99
EATING = auto()
1010

1111

12-
class ForkAgent(mesa.Agent):
13-
def __init__(self, model, position):
12+
class ForkAgent(FixedAgent):
13+
def __init__(self, model):
1414
super().__init__(model)
15-
self.position = position
1615
self.is_used = False
1716
self.owner = None
1817

18+
@property
19+
def position(self):
20+
return self.cell.coordinate
21+
1922
def __repr__(self):
2023
owner = None if self.owner is None else getattr(self.owner, "position", None)
2124
return f"Fork-{self.position}(used={self.is_used}, owner={owner})"
2225

2326

24-
class PhilosopherAgent(mesa.Agent):
25-
def __init__(self, model, position):
27+
class PhilosopherAgent(FixedAgent):
28+
def __init__(self, model):
2629
super().__init__(model)
27-
self.position = position
2830
self.state = State.THINKING
2931
self.total_eaten = 0
3032
self.ticks_since_state_change = 0
3133
self.total_wait_time = 0
3234
self.eating_count = 0
3335

36+
@property
37+
def position(self):
38+
return self.cell.coordinate
39+
3440
def _start_eating(self):
3541
self.total_wait_time += self.ticks_since_state_change
3642
self.eating_count += 1
@@ -59,8 +65,7 @@ def step(self):
5965
self.ticks_since_state_change = 0
6066

6167
def put_down_forks(self):
62-
neighbors = self.model.grid.get_neighbors(self.pos, include_center=False)
63-
my_forks = [a for a in neighbors if isinstance(a, ForkAgent)]
68+
my_forks = self._get_neighbor_forks()
6469

6570
for fork in my_forks:
6671
if fork.owner == self:
@@ -78,8 +83,7 @@ def try_to_eat(self):
7883
self.eat_strategy_cooperative()
7984

8085
def eat_strategy_naive(self):
81-
neighbors = self.model.grid.get_neighbors(self.pos, include_center=False)
82-
my_forks = [a for a in neighbors if isinstance(a, ForkAgent)]
86+
my_forks = self._get_neighbor_forks()
8387

8488
left_pos = (self.position - 1) % self.model.num_nodes
8589
right_pos = (self.position + 1) % self.model.num_nodes
@@ -100,8 +104,7 @@ def eat_strategy_naive(self):
100104
pass
101105

102106
def eat_strategy_atomic(self):
103-
neighbors = self.model.grid.get_neighbors(self.pos, include_center=False)
104-
my_forks = [a for a in neighbors if isinstance(a, ForkAgent)]
107+
my_forks = self._get_neighbor_forks()
105108

106109
if all(not fork.is_used for fork in my_forks):
107110
for fork in my_forks:
@@ -110,8 +113,7 @@ def eat_strategy_atomic(self):
110113
self._start_eating()
111114

112115
def eat_strategy_cooperative(self):
113-
neighbors = self.model.grid.get_neighbors(self.pos, include_center=False)
114-
my_forks = [a for a in neighbors if isinstance(a, ForkAgent)]
116+
my_forks = self._get_neighbor_forks()
115117

116118
# If any fork is used, we can't eat anyway
117119
if any(fork.is_used for fork in my_forks):
@@ -123,7 +125,7 @@ def eat_strategy_cooperative(self):
123125

124126
neighbors_p = [
125127
p
126-
for p in self.model.philosophers
128+
for p in self.model.agents_by_type[PhilosopherAgent]
127129
if p.position in (left_p_pos, right_p_pos)
128130
]
129131

@@ -151,3 +153,10 @@ def eat_strategy_cooperative(self):
151153

152154
def __repr__(self):
153155
return f"Phil-{self.position}({self.state.name})"
156+
157+
def _get_neighbor_forks(self):
158+
return [
159+
agent
160+
for agent in self.cell.neighborhood.agents
161+
if isinstance(agent, ForkAgent)
162+
]
Lines changed: 38 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import mesa
22
import networkx as nx
3+
from mesa.discrete_space import Network
34

45
from .agent import ForkAgent, PhilosopherAgent, State
56

@@ -20,41 +21,52 @@ def __init__(
2021
self.num_nodes = num_philosophers * 2
2122

2223
self.G = nx.circulant_graph(self.num_nodes, [1])
23-
self.grid = mesa.space.NetworkGrid(self.G)
24-
25-
philosophers_list = []
24+
self.grid = Network(self.G, random=self.random)
2625

2726
for node_id in range(self.num_nodes):
27+
node_cell = self.grid[node_id]
2828
if node_id % 2 == 0:
29-
p = PhilosopherAgent(self, node_id)
30-
self.grid.place_agent(p, node_id)
31-
philosophers_list.append(p)
29+
p = PhilosopherAgent(self)
30+
p.cell = node_cell
3231
else:
33-
f = ForkAgent(self, node_id)
34-
self.grid.place_agent(f, node_id)
35-
36-
self.philosophers = mesa.agent.AgentSet(philosophers_list, random=self.random)
32+
f = ForkAgent(self)
33+
f.cell = node_cell
3734

3835
model_reporters = {
3936
"Eating": lambda m: len(
40-
[p for p in m.philosophers if p.state == State.EATING]
37+
[
38+
p
39+
for p in m.agents_by_type[PhilosopherAgent]
40+
if p.state == State.EATING
41+
]
4142
),
4243
"Hungry": lambda m: len(
43-
[p for p in m.philosophers if p.state == State.HUNGRY]
44+
[
45+
p
46+
for p in m.agents_by_type[PhilosopherAgent]
47+
if p.state == State.HUNGRY
48+
]
4449
),
4550
"Thinking": lambda m: len(
46-
[p for p in m.philosophers if p.state == State.THINKING]
51+
[
52+
p
53+
for p in m.agents_by_type[PhilosopherAgent]
54+
if p.state == State.THINKING
55+
]
4756
),
4857
"Avg Wait Time": lambda m: (
4958
(
50-
sum(p.total_wait_time for p in m.philosophers)
51-
/ sum(p.eating_count for p in m.philosophers)
59+
sum(p.total_wait_time for p in m.agents_by_type[PhilosopherAgent])
60+
/ sum(p.eating_count for p in m.agents_by_type[PhilosopherAgent])
5261
)
53-
if sum(p.eating_count for p in m.philosophers) > 0
62+
if sum(p.eating_count for p in m.agents_by_type[PhilosopherAgent]) > 0
5463
else 0
5564
),
5665
"Throughput": lambda m: (
57-
(sum(p.total_eaten for p in m.philosophers) / m.time)
66+
(
67+
sum(p.total_eaten for p in m.agents_by_type[PhilosopherAgent])
68+
/ m.time
69+
)
5870
if m.time > 0
5971
else 0
6072
),
@@ -68,11 +80,18 @@ def __init__(
6880

6981
# Use default argument in lambda to capture the current value of p_id
7082
model_reporters[f"P{i}"] = lambda m, pid=p_id: (
71-
next((p.total_eaten for p in m.philosophers if p.position == pid), 0)
83+
next(
84+
(
85+
p.total_eaten
86+
for p in m.agents_by_type[PhilosopherAgent]
87+
if p.position == pid
88+
),
89+
0,
90+
)
7291
)
7392

7493
self.datacollector = mesa.DataCollector(model_reporters=model_reporters)
7594

7695
def step(self):
77-
self.philosophers.shuffle_do("step")
96+
self.agents_by_type[PhilosopherAgent].shuffle_do("step")
7897
self.datacollector.collect(self)

examples/hex_ant/model.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import mesa
22
from mesa.discrete_space import HexGrid
33

4+
from .agent import Ant
5+
46

57
class AntForaging(mesa.Model):
68
"""
@@ -80,8 +82,6 @@ def _init_agents(self, num_ants):
8082
center = (self.grid.width // 2, self.grid.height // 2)
8183
center_cell = self.grid[center]
8284

83-
from .agent import Ant
84-
8585
for _ in range(num_ants):
8686
ant = Ant(self)
8787
# Add agent to the cell (spatial placement)

examples/warehouse/warehouse/agents.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,14 +132,17 @@ def continue_task(self):
132132

133133
if status == "movement complete" and self.meta_agent.status == "inventory":
134134
# Pick up item and bring to loading dock
135+
source_coordinate = self.meta_agent.cell.coordinate
136+
target_level = self.item.cell.coordinate[2]
135137
self.meta_agent.cell = self.model.warehouse[
136-
*self.meta_agent.cell.coordinate[:2], self.item.cell.coordinate[2]
138+
(source_coordinate[0], source_coordinate[1], target_level)
137139
]
138140
self.meta_agent.status = "loading"
139141
self.carrying = self.item.item
140142
self.item.quantity -= 1
143+
loading_coordinate = self.meta_agent.cell.coordinate
141144
self.meta_agent.cell = self.model.warehouse[
142-
*self.meta_agent.cell.coordinate[:2], 0
145+
(loading_coordinate[0], loading_coordinate[1], 0)
143146
]
144147
self.path = self.find_path(self.cell, self.loading_dock)
145148

gis/agents_and_networks/src/agent/building.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,18 @@
88
import pyproj
99
from shapely.geometry import Polygon
1010

11+
FloatCoordinate = tuple[float, float]
12+
1113

1214
class Building(mg.GeoAgent):
1315
unique_id: int # an ID that represents the building
1416
model: mesa.Model
1517
geometry: Polygon
1618
crs: pyproj.CRS
17-
centroid: mesa.space.FloatCoordinate
19+
centroid: FloatCoordinate
1820
name: str
1921
function: float # 1.0 for work, 2.0 for home, 0.0 for neither
20-
entrance_pos: mesa.space.FloatCoordinate # nearest vertex on road
22+
entrance_pos: FloatCoordinate # nearest vertex on road
2123

2224
def __init__(self, model, geometry, crs) -> None:
2325
super().__init__(model=model, geometry=geometry, crs=crs)

gis/agents_and_networks/src/agent/commuter.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from ..space.utils import UnitTransformer, redistribute_vertices
1212
from .building import Building
1313

14+
FloatCoordinate = tuple[float, float]
15+
1416

1517
class Commuter(mg.GeoAgent):
1618
unique_id: int # commuter_id, used to link commuters and nodes
@@ -20,7 +22,7 @@ class Commuter(mg.GeoAgent):
2022
origin: Building # where he begins his trip
2123
destination: Building # the destination he wants to arrive at
2224
my_path: list[
23-
mesa.space.FloatCoordinate
25+
FloatCoordinate
2426
] # a set containing nodes to visit in the shortest path
2527
step_in_path: int # the number of step taking in the walk
2628
my_home: Building

gis/agents_and_networks/src/logger.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import logging
2+
from functools import wraps
23

34

45
def logger(func):
5-
from functools import wraps
6-
76
@wraps(func)
87
def wrapper(*args, **kwargs):
98
logger = logging.getLogger(func.__name__)

gis/agents_and_networks/src/space/campus.py

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,22 @@
22
from collections import defaultdict
33
from typing import DefaultDict
44

5-
import mesa
65
import mesa_geo as mg
76
from shapely.geometry import Point
87

98
from ..agent.building import Building
109
from ..agent.commuter import Commuter
1110

11+
FloatCoordinate = tuple[float, float]
12+
1213

1314
class Campus(mg.GeoSpace):
1415
homes: tuple[Building]
1516
works: tuple[Building]
1617
other_buildings: tuple[Building]
17-
home_counter: DefaultDict[mesa.space.FloatCoordinate, int]
18+
home_counter: DefaultDict[FloatCoordinate, int]
1819
_buildings: dict[int, Building]
19-
_commuters_pos_map: DefaultDict[mesa.space.FloatCoordinate, set[Commuter]]
20+
_commuters_pos_map: DefaultDict[FloatCoordinate, set[Commuter]]
2021
_commuter_id_map: dict[int, Commuter]
2122

2223
def __init__(self, crs: str) -> None:
@@ -54,9 +55,7 @@ def add_buildings(self, agents) -> None:
5455
self.works = self.works + tuple(works)
5556
self.homes = self.homes + tuple(homes)
5657

57-
def get_commuters_by_pos(
58-
self, float_pos: mesa.space.FloatCoordinate
59-
) -> set[Commuter]:
58+
def get_commuters_by_pos(self, float_pos: FloatCoordinate) -> set[Commuter]:
6059
return self._commuters_pos_map[float_pos]
6160

6261
def get_commuter_by_id(self, commuter_id: int) -> Commuter:
@@ -69,16 +68,14 @@ def add_commuter(self, agent: Commuter) -> None:
6968

7069
def update_home_counter(
7170
self,
72-
old_home_pos: mesa.space.FloatCoordinate | None,
73-
new_home_pos: mesa.space.FloatCoordinate,
71+
old_home_pos: FloatCoordinate | None,
72+
new_home_pos: FloatCoordinate,
7473
) -> None:
7574
if old_home_pos is not None:
7675
self.home_counter[old_home_pos] -= 1
7776
self.home_counter[new_home_pos] += 1
7877

79-
def move_commuter(
80-
self, commuter: Commuter, pos: mesa.space.FloatCoordinate
81-
) -> None:
78+
def move_commuter(self, commuter: Commuter, pos: FloatCoordinate) -> None:
8279
self.__remove_commuter(commuter)
8380
commuter.geometry = Point(pos)
8481
self.add_commuter(commuter)

0 commit comments

Comments
 (0)