Mesa 3.0.0a2__py3-none-any.whl → 3.0.0a4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Mesa might be problematic. Click here for more details.

@@ -0,0 +1,264 @@
1
+ from collections.abc import Sequence
2
+ from itertools import combinations
3
+ from random import Random
4
+
5
+ import numpy as np
6
+
7
+ from mesa.experimental.cell_space.cell import Cell
8
+ from mesa.experimental.cell_space.discrete_space import DiscreteSpace
9
+
10
+
11
+ class Delaunay:
12
+ """
13
+ Class to compute a Delaunay triangulation in 2D
14
+ ref: http://github.com/jmespadero/pyDelaunay2D
15
+ """
16
+
17
+ def __init__(self, center: tuple = (0, 0), radius: int = 9999) -> None:
18
+ """
19
+ Init and create a new frame to contain the triangulation
20
+ center: Optional position for the center of the frame. Default (0,0)
21
+ radius: Optional distance from corners to the center.
22
+ """
23
+ center = np.asarray(center)
24
+ # Create coordinates for the corners of the frame
25
+ self.coords = [
26
+ center + radius * np.array((-1, -1)),
27
+ center + radius * np.array((+1, -1)),
28
+ center + radius * np.array((+1, +1)),
29
+ center + radius * np.array((-1, +1)),
30
+ ]
31
+
32
+ # Create two dicts to store triangle neighbours and circumcircles.
33
+ self.triangles = {}
34
+ self.circles = {}
35
+
36
+ # Create two CCW triangles for the frame
37
+ triangle1 = (0, 1, 3)
38
+ triangle2 = (2, 3, 1)
39
+ self.triangles[triangle1] = [triangle2, None, None]
40
+ self.triangles[triangle2] = [triangle1, None, None]
41
+
42
+ # Compute circumcenters and circumradius for each triangle
43
+ for t in self.triangles:
44
+ self.circles[t] = self._circumcenter(t)
45
+
46
+ def _circumcenter(self, triangle: list) -> tuple:
47
+ """
48
+ Compute circumcenter and circumradius of a triangle in 2D.
49
+ """
50
+ points = np.asarray([self.coords[v] for v in triangle])
51
+ points2 = np.dot(points, points.T)
52
+ a = np.bmat([[2 * points2, [[1], [1], [1]]], [[[1, 1, 1, 0]]]])
53
+
54
+ b = np.hstack((np.sum(points * points, axis=1), [1]))
55
+ x = np.linalg.solve(a, b)
56
+ bary_coords = x[:-1]
57
+ center = np.dot(bary_coords, points)
58
+
59
+ radius = np.sum(np.square(points[0] - center)) # squared distance
60
+ return (center, radius)
61
+
62
+ def _in_circle(self, triangle: list, point: list) -> bool:
63
+ """
64
+ Check if point p is inside of precomputed circumcircle of triangle.
65
+ """
66
+ center, radius = self.circles[triangle]
67
+ return np.sum(np.square(center - point)) <= radius
68
+
69
+ def add_point(self, point: Sequence) -> None:
70
+ """
71
+ Add a point to the current DT, and refine it using Bowyer-Watson.
72
+ """
73
+ point_index = len(self.coords)
74
+ self.coords.append(np.asarray(point))
75
+
76
+ bad_triangles = []
77
+ for triangle in self.triangles:
78
+ if self._in_circle(triangle, point):
79
+ bad_triangles.append(triangle)
80
+
81
+ boundary = []
82
+ triangle = bad_triangles[0]
83
+ edge = 0
84
+
85
+ while True:
86
+ opposite_triangle = self.triangles[triangle][edge]
87
+ if opposite_triangle not in bad_triangles:
88
+ boundary.append(
89
+ (
90
+ triangle[(edge + 1) % 3],
91
+ triangle[(edge - 1) % 3],
92
+ opposite_triangle,
93
+ )
94
+ )
95
+ edge = (edge + 1) % 3
96
+ if boundary[0][0] == boundary[-1][1]:
97
+ break
98
+ else:
99
+ edge = (self.triangles[opposite_triangle].index(triangle) + 1) % 3
100
+ triangle = opposite_triangle
101
+
102
+ for triangle in bad_triangles:
103
+ del self.triangles[triangle]
104
+ del self.circles[triangle]
105
+
106
+ new_triangles = []
107
+ for e0, e1, opposite_triangle in boundary:
108
+ triangle = (point_index, e0, e1)
109
+ self.circles[triangle] = self._circumcenter(triangle)
110
+ self.triangles[triangle] = [opposite_triangle, None, None]
111
+ if opposite_triangle:
112
+ for i, neighbor in enumerate(self.triangles[opposite_triangle]):
113
+ if neighbor and e1 in neighbor and e0 in neighbor:
114
+ self.triangles[opposite_triangle][i] = triangle
115
+
116
+ new_triangles.append(triangle)
117
+
118
+ n = len(new_triangles)
119
+ for i, triangle in enumerate(new_triangles):
120
+ self.triangles[triangle][1] = new_triangles[(i + 1) % n] # next
121
+ self.triangles[triangle][2] = new_triangles[(i - 1) % n] # previous
122
+
123
+ def export_triangles(self) -> list:
124
+ """
125
+ Export the current list of Delaunay triangles
126
+ """
127
+ triangles_list = [
128
+ (a - 4, b - 4, c - 4)
129
+ for (a, b, c) in self.triangles
130
+ if a > 3 and b > 3 and c > 3
131
+ ]
132
+ return triangles_list
133
+
134
+ def export_voronoi_regions(self):
135
+ """
136
+ Export coordinates and regions of Voronoi diagram as indexed data.
137
+ """
138
+ use_vertex = {i: [] for i in range(len(self.coords))}
139
+ vor_coors = []
140
+ index = {}
141
+ for triangle_index, (a, b, c) in enumerate(sorted(self.triangles)):
142
+ vor_coors.append(self.circles[(a, b, c)][0])
143
+ use_vertex[a] += [(b, c, a)]
144
+ use_vertex[b] += [(c, a, b)]
145
+ use_vertex[c] += [(a, b, c)]
146
+
147
+ index[(a, b, c)] = triangle_index
148
+ index[(c, a, b)] = triangle_index
149
+ index[(b, c, a)] = triangle_index
150
+
151
+ regions = {}
152
+ for i in range(4, len(self.coords)):
153
+ vertex = use_vertex[i][0][0]
154
+ region = []
155
+ for _ in range(len(use_vertex[i])):
156
+ triangle = next(
157
+ triangle for triangle in use_vertex[i] if triangle[0] == vertex
158
+ )
159
+ region.append(index[triangle])
160
+ vertex = triangle[1]
161
+ regions[i - 4] = region
162
+
163
+ return vor_coors, regions
164
+
165
+
166
+ def round_float(x: float) -> int:
167
+ return int(x * 500)
168
+
169
+
170
+ class VoronoiGrid(DiscreteSpace):
171
+ triangulation: Delaunay
172
+ voronoi_coordinates: list
173
+ regions: list
174
+
175
+ def __init__(
176
+ self,
177
+ centroids_coordinates: Sequence[Sequence[float]],
178
+ capacity: float | None = None,
179
+ random: Random | None = None,
180
+ cell_klass: type[Cell] = Cell,
181
+ capacity_function: callable = round_float,
182
+ cell_coloring_property: str | None = None,
183
+ ) -> None:
184
+ """
185
+ A Voronoi Tessellation Grid.
186
+
187
+ Given a set of points, this class creates a grid where a cell is centered in each point,
188
+ its neighbors are given by Voronoi Tessellation cells neighbors
189
+ and the capacity by the polygon area.
190
+
191
+ Args:
192
+ centroids_coordinates: coordinates of centroids to build the tessellation space
193
+ capacity (int) : capacity of the cells in the discrete space
194
+ random (Random): random number generator
195
+ CellKlass (type[Cell]): type of cell class
196
+ capacity_function (Callable): function to compute (int) capacity according to (float) area
197
+ cell_coloring_property (str): voronoi visualization polygon fill property
198
+ """
199
+ super().__init__(capacity=capacity, random=random, cell_klass=cell_klass)
200
+ self.centroids_coordinates = centroids_coordinates
201
+ self._validate_parameters()
202
+
203
+ self._cells = {
204
+ i: cell_klass(self.centroids_coordinates[i], capacity, random=self.random)
205
+ for i in range(len(self.centroids_coordinates))
206
+ }
207
+
208
+ self.regions = None
209
+ self.triangulation = None
210
+ self.voronoi_coordinates = None
211
+ self.capacity_function = capacity_function
212
+ self.cell_coloring_property = cell_coloring_property
213
+
214
+ self._connect_cells()
215
+ self._build_cell_polygons()
216
+
217
+ def _connect_cells(self) -> None:
218
+ """
219
+ Connect cells to neighbors based on given centroids and using Delaunay Triangulation
220
+ """
221
+ self.triangulation = Delaunay()
222
+ for centroid in self.centroids_coordinates:
223
+ self.triangulation.add_point(centroid)
224
+
225
+ for point in self.triangulation.export_triangles():
226
+ for i, j in combinations(point, 2):
227
+ self._cells[i].connect(self._cells[j])
228
+ self._cells[j].connect(self._cells[i])
229
+
230
+ def _validate_parameters(self) -> None:
231
+ if self.capacity is not None and not isinstance(self.capacity, float | int):
232
+ raise ValueError("Capacity must be a number or None.")
233
+ if not isinstance(self.centroids_coordinates, Sequence) or not isinstance(
234
+ self.centroids_coordinates[0], Sequence
235
+ ):
236
+ raise ValueError("Centroids should be a list of lists")
237
+ dimension_1 = len(self.centroids_coordinates[0])
238
+ for coordinate in self.centroids_coordinates:
239
+ if dimension_1 != len(coordinate):
240
+ raise ValueError("Centroid coordinates should be a homogeneous array")
241
+
242
+ def _get_voronoi_regions(self) -> tuple:
243
+ if self.voronoi_coordinates is None or self.regions is None:
244
+ self.voronoi_coordinates, self.regions = (
245
+ self.triangulation.export_voronoi_regions()
246
+ )
247
+ return self.voronoi_coordinates, self.regions
248
+
249
+ @staticmethod
250
+ def _compute_polygon_area(polygon: list) -> float:
251
+ polygon = np.array(polygon)
252
+ x = polygon[:, 0]
253
+ y = polygon[:, 1]
254
+ return 0.5 * np.abs(np.dot(x, np.roll(y, 1)) - np.dot(y, np.roll(x, 1)))
255
+
256
+ def _build_cell_polygons(self):
257
+ coordinates, regions = self._get_voronoi_regions()
258
+ for region in regions:
259
+ polygon = [coordinates[i] for i in regions[region]]
260
+ self._cells[region].properties["polygon"] = polygon
261
+ polygon_area = self._compute_polygon_area(polygon)
262
+ self._cells[region].properties["area"] = polygon_area
263
+ self._cells[region].capacity = self.capacity_function(polygon_area)
264
+ self._cells[region].properties[self.cell_coloring_property] = 0
@@ -0,0 +1,71 @@
1
+ import contextlib
2
+
3
+ import solara
4
+
5
+ with contextlib.suppress(ImportError):
6
+ import altair as alt
7
+
8
+
9
+ @solara.component
10
+ def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
11
+ space = getattr(model, "grid", None)
12
+ if space is None:
13
+ # Sometimes the space is defined as model.space instead of model.grid
14
+ space = model.space
15
+ chart = _draw_grid(space, agent_portrayal)
16
+ solara.FigureAltair(chart)
17
+
18
+
19
+ def _draw_grid(space, agent_portrayal):
20
+ def portray(g):
21
+ all_agent_data = []
22
+ for content, (x, y) in g.coord_iter():
23
+ if not content:
24
+ continue
25
+ if not hasattr(content, "__iter__"):
26
+ # Is a single grid
27
+ content = [content] # noqa: PLW2901
28
+ for agent in content:
29
+ # use all data from agent portrayal, and add x,y coordinates
30
+ agent_data = agent_portrayal(agent)
31
+ agent_data["x"] = x
32
+ agent_data["y"] = y
33
+ all_agent_data.append(agent_data)
34
+ return all_agent_data
35
+
36
+ all_agent_data = portray(space)
37
+ invalid_tooltips = ["color", "size", "x", "y"]
38
+
39
+ encoding_dict = {
40
+ # no x-axis label
41
+ "x": alt.X("x", axis=None, type="ordinal"),
42
+ # no y-axis label
43
+ "y": alt.Y("y", axis=None, type="ordinal"),
44
+ "tooltip": [
45
+ alt.Tooltip(key, type=alt.utils.infer_vegalite_type([value]))
46
+ for key, value in all_agent_data[0].items()
47
+ if key not in invalid_tooltips
48
+ ],
49
+ }
50
+ has_color = "color" in all_agent_data[0]
51
+ if has_color:
52
+ encoding_dict["color"] = alt.Color("color", type="nominal")
53
+ has_size = "size" in all_agent_data[0]
54
+ if has_size:
55
+ encoding_dict["size"] = alt.Size("size", type="quantitative")
56
+
57
+ chart = (
58
+ alt.Chart(
59
+ alt.Data(values=all_agent_data), encoding=alt.Encoding(**encoding_dict)
60
+ )
61
+ .mark_point(filled=True)
62
+ .properties(width=280, height=280)
63
+ # .configure_view(strokeOpacity=0) # hide grid/chart lines
64
+ )
65
+ # This is the default value for the marker size, which auto-scales
66
+ # according to the grid area.
67
+ if not has_size:
68
+ length = min(space.width, space.height)
69
+ chart = chart.mark_point(size=30000 / length**2, filled=True)
70
+
71
+ return chart
@@ -0,0 +1,224 @@
1
+ from collections import defaultdict
2
+
3
+ import networkx as nx
4
+ import solara
5
+ from matplotlib.figure import Figure
6
+ from matplotlib.ticker import MaxNLocator
7
+
8
+ import mesa
9
+ from mesa.experimental.cell_space import VoronoiGrid
10
+
11
+
12
+ @solara.component
13
+ def SpaceMatplotlib(model, agent_portrayal, dependencies: list[any] | None = None):
14
+ space_fig = Figure()
15
+ space_ax = space_fig.subplots()
16
+ space = getattr(model, "grid", None)
17
+ if space is None:
18
+ # Sometimes the space is defined as model.space instead of model.grid
19
+ space = model.space
20
+ if isinstance(space, mesa.space.NetworkGrid):
21
+ _draw_network_grid(space, space_ax, agent_portrayal)
22
+ elif isinstance(space, mesa.space.ContinuousSpace):
23
+ _draw_continuous_space(space, space_ax, agent_portrayal)
24
+ elif isinstance(space, VoronoiGrid):
25
+ _draw_voronoi(space, space_ax, agent_portrayal)
26
+ else:
27
+ _draw_grid(space, space_ax, agent_portrayal)
28
+ solara.FigureMatplotlib(space_fig, format="png", dependencies=dependencies)
29
+
30
+
31
+ # matplotlib scatter does not allow for multiple shapes in one call
32
+ def _split_and_scatter(portray_data, space_ax):
33
+ grouped_data = defaultdict(lambda: {"x": [], "y": [], "s": [], "c": []})
34
+
35
+ # Extract data from the dictionary
36
+ x = portray_data["x"]
37
+ y = portray_data["y"]
38
+ s = portray_data["s"]
39
+ c = portray_data["c"]
40
+ m = portray_data["m"]
41
+
42
+ if not (len(x) == len(y) == len(s) == len(c) == len(m)):
43
+ raise ValueError(
44
+ "Length mismatch in portrayal data lists: "
45
+ f"x: {len(x)}, y: {len(y)}, size: {len(s)}, "
46
+ f"color: {len(c)}, marker: {len(m)}"
47
+ )
48
+
49
+ # Group the data by marker
50
+ for i in range(len(x)):
51
+ marker = m[i]
52
+ grouped_data[marker]["x"].append(x[i])
53
+ grouped_data[marker]["y"].append(y[i])
54
+ grouped_data[marker]["s"].append(s[i])
55
+ grouped_data[marker]["c"].append(c[i])
56
+
57
+ # Plot each group with the same marker
58
+ for marker, data in grouped_data.items():
59
+ space_ax.scatter(data["x"], data["y"], s=data["s"], c=data["c"], marker=marker)
60
+
61
+
62
+ def _draw_grid(space, space_ax, agent_portrayal):
63
+ def portray(g):
64
+ x = []
65
+ y = []
66
+ s = [] # size
67
+ c = [] # color
68
+ m = [] # shape
69
+ for i in range(g.width):
70
+ for j in range(g.height):
71
+ content = g._grid[i][j]
72
+ if not content:
73
+ continue
74
+ if not hasattr(content, "__iter__"):
75
+ # Is a single grid
76
+ content = [content]
77
+ for agent in content:
78
+ data = agent_portrayal(agent)
79
+ x.append(i)
80
+ y.append(j)
81
+
82
+ # This is the default value for the marker size, which auto-scales
83
+ # according to the grid area.
84
+ default_size = (180 / max(g.width, g.height)) ** 2
85
+ # establishing a default prevents misalignment if some agents are not given size, color, etc.
86
+ size = data.get("size", default_size)
87
+ s.append(size)
88
+ color = data.get("color", "tab:blue")
89
+ c.append(color)
90
+ mark = data.get("shape", "o")
91
+ m.append(mark)
92
+ out = {"x": x, "y": y, "s": s, "c": c, "m": m}
93
+ return out
94
+
95
+ space_ax.set_xlim(-1, space.width)
96
+ space_ax.set_ylim(-1, space.height)
97
+ _split_and_scatter(portray(space), space_ax)
98
+
99
+
100
+ def _draw_network_grid(space, space_ax, agent_portrayal):
101
+ graph = space.G
102
+ pos = nx.spring_layout(graph, seed=0)
103
+ nx.draw(
104
+ graph,
105
+ ax=space_ax,
106
+ pos=pos,
107
+ **agent_portrayal(graph),
108
+ )
109
+
110
+
111
+ def _draw_continuous_space(space, space_ax, agent_portrayal):
112
+ def portray(space):
113
+ x = []
114
+ y = []
115
+ s = [] # size
116
+ c = [] # color
117
+ m = [] # shape
118
+ for agent in space._agent_to_index:
119
+ data = agent_portrayal(agent)
120
+ _x, _y = agent.pos
121
+ x.append(_x)
122
+ y.append(_y)
123
+
124
+ # This is matplotlib's default marker size
125
+ default_size = 20
126
+ # establishing a default prevents misalignment if some agents are not given size, color, etc.
127
+ size = data.get("size", default_size)
128
+ s.append(size)
129
+ color = data.get("color", "tab:blue")
130
+ c.append(color)
131
+ mark = data.get("shape", "o")
132
+ m.append(mark)
133
+ out = {"x": x, "y": y, "s": s, "c": c, "m": m}
134
+ return out
135
+
136
+ # Determine border style based on space.torus
137
+ border_style = "solid" if not space.torus else (0, (5, 10))
138
+
139
+ # Set the border of the plot
140
+ for spine in space_ax.spines.values():
141
+ spine.set_linewidth(1.5)
142
+ spine.set_color("black")
143
+ spine.set_linestyle(border_style)
144
+
145
+ width = space.x_max - space.x_min
146
+ x_padding = width / 20
147
+ height = space.y_max - space.y_min
148
+ y_padding = height / 20
149
+ space_ax.set_xlim(space.x_min - x_padding, space.x_max + x_padding)
150
+ space_ax.set_ylim(space.y_min - y_padding, space.y_max + y_padding)
151
+
152
+ # Portray and scatter the agents in the space
153
+ _split_and_scatter(portray(space), space_ax)
154
+
155
+
156
+ def _draw_voronoi(space, space_ax, agent_portrayal):
157
+ def portray(g):
158
+ x = []
159
+ y = []
160
+ s = [] # size
161
+ c = [] # color
162
+
163
+ for cell in g.all_cells:
164
+ for agent in cell.agents:
165
+ data = agent_portrayal(agent)
166
+ x.append(cell.coordinate[0])
167
+ y.append(cell.coordinate[1])
168
+ if "size" in data:
169
+ s.append(data["size"])
170
+ if "color" in data:
171
+ c.append(data["color"])
172
+ out = {"x": x, "y": y}
173
+ # This is the default value for the marker size, which auto-scales
174
+ # according to the grid area.
175
+ out["s"] = s
176
+ if len(c) > 0:
177
+ out["c"] = c
178
+
179
+ return out
180
+
181
+ x_list = [i[0] for i in space.centroids_coordinates]
182
+ y_list = [i[1] for i in space.centroids_coordinates]
183
+ x_max = max(x_list)
184
+ x_min = min(x_list)
185
+ y_max = max(y_list)
186
+ y_min = min(y_list)
187
+
188
+ width = x_max - x_min
189
+ x_padding = width / 20
190
+ height = y_max - y_min
191
+ y_padding = height / 20
192
+ space_ax.set_xlim(x_min - x_padding, x_max + x_padding)
193
+ space_ax.set_ylim(y_min - y_padding, y_max + y_padding)
194
+ space_ax.scatter(**portray(space))
195
+
196
+ for cell in space.all_cells:
197
+ polygon = cell.properties["polygon"]
198
+ space_ax.fill(
199
+ *zip(*polygon),
200
+ alpha=min(1, cell.properties[space.cell_coloring_property]),
201
+ c="red",
202
+ ) # Plot filled polygon
203
+ space_ax.plot(*zip(*polygon), color="black") # Plot polygon edges in red
204
+
205
+
206
+ @solara.component
207
+ def PlotMatplotlib(model, measure, dependencies: list[any] | None = None):
208
+ fig = Figure()
209
+ ax = fig.subplots()
210
+ df = model.datacollector.get_model_vars_dataframe()
211
+ if isinstance(measure, str):
212
+ ax.plot(df.loc[:, measure])
213
+ ax.set_ylabel(measure)
214
+ elif isinstance(measure, dict):
215
+ for m, color in measure.items():
216
+ ax.plot(df.loc[:, m], label=m, color=color)
217
+ fig.legend()
218
+ elif isinstance(measure, list | tuple):
219
+ for m in measure:
220
+ ax.plot(df.loc[:, m], label=m)
221
+ fig.legend()
222
+ # Set integer x axis
223
+ ax.xaxis.set_major_locator(MaxNLocator(integer=True))
224
+ solara.FigureMatplotlib(fig, dependencies=dependencies)
@@ -7,8 +7,8 @@ from mesa.space import SingleGrid
7
7
 
8
8
 
9
9
  class EpsteinAgent(Agent):
10
- def __init__(self, unique_id, model, vision, movement):
11
- super().__init__(unique_id, model)
10
+ def __init__(self, model, vision, movement):
11
+ super().__init__(model)
12
12
  self.vision = vision
13
13
  self.movement = movement
14
14
 
@@ -46,7 +46,6 @@ class Citizen(EpsteinAgent):
46
46
 
47
47
  def __init__(
48
48
  self,
49
- unique_id,
50
49
  model,
51
50
  vision,
52
51
  movement,
@@ -59,7 +58,6 @@ class Citizen(EpsteinAgent):
59
58
  """
60
59
  Create a new Citizen.
61
60
  Args:
62
- unique_id: unique int
63
61
  model : model instance
64
62
  hardship: Agent's 'perceived hardship (i.e., physical or economic
65
63
  privation).' Exogenous, drawn from U(0,1).
@@ -71,7 +69,7 @@ class Citizen(EpsteinAgent):
71
69
  vision: number of cells in each direction (N, S, E and W) that
72
70
  agent can inspect. Exogenous.
73
71
  """
74
- super().__init__(unique_id, model, vision, movement)
72
+ super().__init__(model, vision, movement)
75
73
  self.hardship = hardship
76
74
  self.regime_legitimacy = regime_legitimacy
77
75
  self.risk_aversion = risk_aversion
@@ -144,8 +142,8 @@ class Cop(EpsteinAgent):
144
142
  able to inspect
145
143
  """
146
144
 
147
- def __init__(self, unique_id, model, vision, movement, max_jail_term):
148
- super().__init__(unique_id, model, vision, movement)
145
+ def __init__(self, model, vision, movement, max_jail_term):
146
+ super().__init__(model, vision, movement)
149
147
  self.max_jail_term = max_jail_term
150
148
 
151
149
  def step(self):
@@ -236,7 +234,6 @@ class EpsteinCivilViolence(Model):
236
234
  for _, pos in self.grid.coord_iter():
237
235
  if self.random.random() < self.cop_density:
238
236
  agent = Cop(
239
- self.next_id(),
240
237
  self,
241
238
  cop_vision,
242
239
  movement,
@@ -244,7 +241,6 @@ class EpsteinCivilViolence(Model):
244
241
  )
245
242
  elif self.random.random() < (self.cop_density + self.citizen_density):
246
243
  agent = Citizen(
247
- self.next_id(),
248
244
  self,
249
245
  citizen_vision,
250
246
  movement,
@@ -270,4 +266,4 @@ if __name__ == "__main__":
270
266
 
271
267
  simulator.setup(model)
272
268
 
273
- simulator.run(time_delta=100)
269
+ simulator.run_for(time_delta=100)
@@ -14,8 +14,8 @@ from mesa.experimental.devs.simulator import ABMSimulator
14
14
 
15
15
 
16
16
  class Animal(mesa.Agent):
17
- def __init__(self, unique_id, model, moore, energy, p_reproduce, energy_from_food):
18
- super().__init__(unique_id, model)
17
+ def __init__(self, model, moore, energy, p_reproduce, energy_from_food):
18
+ super().__init__(model)
19
19
  self.energy = energy
20
20
  self.p_reproduce = p_reproduce
21
21
  self.energy_from_food = energy_from_food
@@ -30,7 +30,6 @@ class Animal(mesa.Agent):
30
30
  def spawn_offspring(self):
31
31
  self.energy /= 2
32
32
  offspring = self.__class__(
33
- self.model.next_id(),
34
33
  self.model,
35
34
  self.moore,
36
35
  self.energy,
@@ -109,7 +108,7 @@ class GrassPatch(mesa.Agent):
109
108
  function_args=[self, "fully_grown", True],
110
109
  )
111
110
 
112
- def __init__(self, unique_id, model, fully_grown, countdown, grass_regrowth_time):
111
+ def __init__(self, model, fully_grown, countdown, grass_regrowth_time):
113
112
  """
114
113
  Creates a new patch of grass
115
114
 
@@ -117,7 +116,7 @@ class GrassPatch(mesa.Agent):
117
116
  grown: (boolean) Whether the patch of grass is fully grown or not
118
117
  countdown: Time for the patch of grass to be fully grown again
119
118
  """
120
- super().__init__(unique_id, model)
119
+ super().__init__(model)
121
120
  self._fully_grown = fully_grown
122
121
  self.grass_regrowth_time = grass_regrowth_time
123
122
 
@@ -191,7 +190,6 @@ class WolfSheep(mesa.Model):
191
190
  )
192
191
  energy = self.random.randrange(2 * sheep_gain_from_food)
193
192
  sheep = Sheep(
194
- self.next_id(),
195
193
  self,
196
194
  moore,
197
195
  energy,
@@ -208,7 +206,6 @@ class WolfSheep(mesa.Model):
208
206
  )
209
207
  energy = self.random.randrange(2 * wolf_gain_from_food)
210
208
  wolf = Wolf(
211
- self.next_id(),
212
209
  self,
213
210
  moore,
214
211
  energy,
@@ -225,14 +222,12 @@ class WolfSheep(mesa.Model):
225
222
  countdown = grass_regrowth_time
226
223
  else:
227
224
  countdown = self.random.randrange(grass_regrowth_time)
228
- patch = GrassPatch(
229
- self.next_id(), self, fully_grown, countdown, grass_regrowth_time
230
- )
225
+ patch = GrassPatch(self, fully_grown, countdown, grass_regrowth_time)
231
226
  self.grid.place_agent(patch, pos)
232
227
 
233
228
  def step(self):
234
- self.get_agents_of_type(Sheep).shuffle(inplace=True).do("step")
235
- self.get_agents_of_type(Wolf).shuffle(inplace=True).do("step")
229
+ self.agents_by_type[Sheep].shuffle(inplace=True).do("step")
230
+ self.agents_by_type[Wolf].shuffle(inplace=True).do("step")
236
231
 
237
232
 
238
233
  if __name__ == "__main__":