Mesa 3.1.0__py3-none-any.whl → 3.1.0.dev0__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.

Files changed (49) hide show
  1. mesa/__init__.py +3 -3
  2. mesa/agent.py +0 -48
  3. mesa/batchrunner.py +1 -14
  4. mesa/datacollection.py +6 -1
  5. mesa/examples/__init__.py +2 -2
  6. mesa/examples/advanced/epstein_civil_violence/app.py +0 -5
  7. mesa/examples/advanced/pd_grid/app.py +0 -5
  8. mesa/examples/advanced/sugarscape_g1mt/app.py +2 -7
  9. mesa/examples/basic/boid_flockers/app.py +0 -5
  10. mesa/examples/basic/boltzmann_wealth_model/app.py +5 -8
  11. mesa/examples/basic/boltzmann_wealth_model/st_app.py +1 -1
  12. mesa/examples/basic/conways_game_of_life/app.py +0 -5
  13. mesa/examples/basic/conways_game_of_life/st_app.py +2 -2
  14. mesa/examples/basic/schelling/app.py +0 -5
  15. mesa/examples/basic/virus_on_network/app.py +0 -5
  16. mesa/experimental/UserParam.py +67 -0
  17. mesa/experimental/__init__.py +10 -17
  18. mesa/experimental/cell_space/__init__.py +7 -19
  19. mesa/experimental/cell_space/cell.py +37 -22
  20. mesa/experimental/cell_space/cell_agent.py +1 -12
  21. mesa/experimental/cell_space/cell_collection.py +3 -18
  22. mesa/experimental/cell_space/discrete_space.py +64 -15
  23. mesa/experimental/cell_space/grid.py +4 -74
  24. mesa/experimental/cell_space/network.py +1 -13
  25. mesa/experimental/cell_space/voronoi.py +1 -13
  26. mesa/experimental/components/altair.py +81 -0
  27. mesa/experimental/components/matplotlib.py +242 -0
  28. mesa/experimental/devs/__init__.py +2 -20
  29. mesa/experimental/devs/eventlist.py +1 -19
  30. mesa/experimental/devs/examples/epstein_civil_violence.py +305 -0
  31. mesa/experimental/devs/examples/wolf_sheep.py +250 -0
  32. mesa/experimental/devs/simulator.py +8 -24
  33. mesa/experimental/solara_viz.py +453 -0
  34. mesa/model.py +23 -17
  35. mesa/visualization/__init__.py +2 -2
  36. mesa/visualization/mpl_space_drawing.py +2 -2
  37. mesa/visualization/solara_viz.py +5 -23
  38. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/METADATA +1 -1
  39. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/RECORD +43 -43
  40. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/WHEEL +1 -1
  41. mesa/experimental/cell_space/property_layer.py +0 -444
  42. mesa/experimental/mesa_signals/__init__.py +0 -23
  43. mesa/experimental/mesa_signals/mesa_signal.py +0 -485
  44. mesa/experimental/mesa_signals/observable_collections.py +0 -133
  45. mesa/experimental/mesa_signals/signals_util.py +0 -52
  46. mesa/mesa_logging.py +0 -190
  47. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/entry_points.txt +0 -0
  48. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/licenses/LICENSE +0 -0
  49. {mesa-3.1.0.dist-info → mesa-3.1.0.dev0.dist-info}/licenses/NOTICE +0 -0
@@ -1,28 +1,17 @@
1
- """Base class for building cell-based spatial environments.
2
-
3
- DiscreteSpace provides the core functionality needed by all cell-based spaces:
4
- - Cell creation and tracking
5
- - Agent-cell relationship management
6
- - Property layer support
7
- - Random selection capabilities
8
- - Capacity management
9
-
10
- This serves as the foundation for specific space implementations like grids
11
- and networks, ensuring consistent behavior and shared functionality across
12
- different space types. All concrete cell space implementations (grids, networks, etc.)
13
- inherit from this class.
14
- """
1
+ """DiscreteSpace base class."""
15
2
 
16
3
  from __future__ import annotations
17
4
 
18
5
  import warnings
6
+ from collections.abc import Callable
19
7
  from functools import cached_property
20
8
  from random import Random
21
- from typing import Generic, TypeVar
9
+ from typing import Any, Generic, TypeVar
22
10
 
23
11
  from mesa.agent import AgentSet
24
12
  from mesa.experimental.cell_space.cell import Cell
25
13
  from mesa.experimental.cell_space.cell_collection import CellCollection
14
+ from mesa.space import PropertyLayer
26
15
 
27
16
  T = TypeVar("T", bound=Cell)
28
17
 
@@ -72,6 +61,8 @@ class DiscreteSpace(Generic[T]):
72
61
  self.cell_klass = cell_klass
73
62
 
74
63
  self._empties: dict[tuple[int, ...], None] = {}
64
+ self._empties_initialized = False
65
+ self.property_layers: dict[str, PropertyLayer] = {}
75
66
 
76
67
  @property
77
68
  def cutoff_empties(self): # noqa
@@ -107,6 +98,64 @@ class DiscreteSpace(Generic[T]):
107
98
  """Select random empty cell."""
108
99
  return self.random.choice(list(self.empties))
109
100
 
101
+ # PropertyLayer methods
102
+ def add_property_layer(
103
+ self, property_layer: PropertyLayer, add_to_cells: bool = True
104
+ ):
105
+ """Add a property layer to the grid.
106
+
107
+ Args:
108
+ property_layer: the property layer to add
109
+ add_to_cells: whether to add the property layer to all cells (default: True)
110
+ """
111
+ if property_layer.name in self.property_layers:
112
+ raise ValueError(f"Property layer {property_layer.name} already exists.")
113
+ self.property_layers[property_layer.name] = property_layer
114
+ if add_to_cells:
115
+ for cell in self._cells.values():
116
+ cell._mesa_property_layers[property_layer.name] = property_layer
117
+
118
+ def remove_property_layer(self, property_name: str, remove_from_cells: bool = True):
119
+ """Remove a property layer from the grid.
120
+
121
+ Args:
122
+ property_name: the name of the property layer to remove
123
+ remove_from_cells: whether to remove the property layer from all cells (default: True)
124
+ """
125
+ del self.property_layers[property_name]
126
+ if remove_from_cells:
127
+ for cell in self._cells.values():
128
+ del cell._mesa_property_layers[property_name]
129
+
130
+ def set_property(
131
+ self, property_name: str, value, condition: Callable[[T], bool] | None = None
132
+ ):
133
+ """Set the value of a property for all cells in the grid.
134
+
135
+ Args:
136
+ property_name: the name of the property to set
137
+ value: the value to set
138
+ condition: a function that takes a cell and returns a boolean
139
+ """
140
+ self.property_layers[property_name].set_cells(value, condition)
141
+
142
+ def modify_properties(
143
+ self,
144
+ property_name: str,
145
+ operation: Callable,
146
+ value: Any = None,
147
+ condition: Callable[[T], bool] | None = None,
148
+ ):
149
+ """Modify the values of a specific property for all cells in the grid.
150
+
151
+ Args:
152
+ property_name: the name of the property to modify
153
+ operation: the operation to perform
154
+ value: the value to use in the operation
155
+ condition: a function that takes a cell and returns a boolean (used to filter cells)
156
+ """
157
+ self.property_layers[property_name].modify_cells(operation, value, condition)
158
+
110
159
  def __setstate__(self, state):
111
160
  """Set the state of the discrete space and rebuild the connections."""
112
161
  self.__dict__ = state
@@ -1,62 +1,18 @@
1
- """Grid-based cell space implementations with different connection patterns.
2
-
3
- Provides several grid types for organizing cells:
4
- - OrthogonalMooreGrid: 8 neighbors in 2D, (3^n)-1 in nD
5
- - OrthogonalVonNeumannGrid: 4 neighbors in 2D, 2n in nD
6
- - HexGrid: 6 neighbors in hexagonal pattern (2D only)
7
-
8
- Each grid type supports optional wrapping (torus) and cell capacity limits.
9
- Choose based on how movement and connectivity should work in your model -
10
- Moore for unrestricted movement, Von Neumann for orthogonal-only movement,
11
- or Hex for more uniform distances.
12
- """
1
+ """Various Grid Spaces."""
13
2
 
14
3
  from __future__ import annotations
15
4
 
16
- import copyreg
17
5
  from collections.abc import Sequence
18
6
  from itertools import product
19
7
  from random import Random
20
- from typing import Any, Generic, TypeVar
8
+ from typing import Generic, TypeVar
21
9
 
22
10
  from mesa.experimental.cell_space import Cell, DiscreteSpace
23
- from mesa.experimental.cell_space.property_layer import (
24
- HasPropertyLayers,
25
- PropertyDescriptor,
26
- )
27
11
 
28
12
  T = TypeVar("T", bound=Cell)
29
13
 
30
14
 
31
- def pickle_gridcell(obj):
32
- """Helper function for pickling GridCell instances."""
33
- # we have the base class and the state via __getstate__
34
- args = obj.__class__.__bases__[0], obj.__getstate__()
35
- return unpickle_gridcell, args
36
-
37
-
38
- def unpickle_gridcell(parent, fields):
39
- """Helper function for unpickling GridCell instances."""
40
- # since the class is dynamically created, we recreate it here
41
- cell_klass = type(
42
- "GridCell",
43
- (parent,),
44
- {"_mesa_properties": set()},
45
- )
46
- instance = cell_klass(
47
- (0, 0)
48
- ) # we use a default coordinate and overwrite it with the correct value next
49
-
50
- # __gestate__ returns a tuple with dict and slots, but slots contains the dict so we can just use the
51
- # second item only
52
- for k, v in fields[1].items():
53
- if k != "__dict__":
54
- setattr(instance, k, v)
55
-
56
- return instance
57
-
58
-
59
- class Grid(DiscreteSpace[T], Generic[T], HasPropertyLayers):
15
+ class Grid(DiscreteSpace[T], Generic[T]):
60
16
  """Base class for all grid classes.
61
17
 
62
18
  Attributes:
@@ -104,23 +60,14 @@ class Grid(DiscreteSpace[T], Generic[T], HasPropertyLayers):
104
60
  self._try_random = True
105
61
  self._ndims = len(dimensions)
106
62
  self._validate_parameters()
107
- self.cell_klass = type(
108
- "GridCell",
109
- (self.cell_klass,),
110
- {"_mesa_properties": set()},
111
- )
112
-
113
- # we register the pickle_gridcell helper function
114
- copyreg.pickle(self.cell_klass, pickle_gridcell)
115
63
 
116
64
  coordinates = product(*(range(dim) for dim in self.dimensions))
117
65
 
118
66
  self._cells = {
119
- coord: self.cell_klass(coord, capacity, random=self.random)
67
+ coord: cell_klass(coord, capacity, random=self.random)
120
68
  for coord in coordinates
121
69
  }
122
70
  self._connect_cells()
123
- self.create_property_layer("empty", default_value=True, dtype=bool)
124
71
 
125
72
  def _connect_cells(self) -> None:
126
73
  if self._ndims == 2:
@@ -179,23 +126,6 @@ class Grid(DiscreteSpace[T], Generic[T], HasPropertyLayers):
179
126
  if 0 <= ni < height and 0 <= nj < width:
180
127
  cell.connect(self._cells[ni, nj], (di, dj))
181
128
 
182
- def __getstate__(self) -> dict[str, Any]:
183
- """Custom __getstate__ for handling dynamic GridCell class and PropertyDescriptors."""
184
- state = super().__getstate__()
185
- state = {k: v for k, v in state.items() if k != "cell_klass"}
186
- return state
187
-
188
- def __setstate__(self, state: dict[str, Any]) -> None:
189
- """Custom __setstate__ for handling dynamic GridCell class and PropertyDescriptors."""
190
- self.__dict__ = state
191
- self._connect_cells() # using super fails for this for some reason, so we repeat ourselves
192
-
193
- self.cell_klass = type(
194
- self._cells[(0, 0)]
195
- ) # the __reduce__ function handles this for us nicely
196
- for layer in self._mesa_property_layers.values():
197
- setattr(self.cell_klass, layer.name, PropertyDescriptor(layer))
198
-
199
129
 
200
130
  class OrthogonalMooreGrid(Grid[T]):
201
131
  """Grid where cells are connected to their 8 neighbors.
@@ -1,16 +1,4 @@
1
- """Network-based cell space using arbitrary connection patterns.
2
-
3
- Creates spaces where cells connect based on network relationships rather than
4
- spatial proximity. Built on NetworkX graphs, this enables:
5
- - Arbitrary connectivity patterns between cells
6
- - Graph-based neighborhood definitions
7
- - Logical rather than physical distances
8
- - Dynamic connectivity changes
9
- - Integration with NetworkX's graph algorithms
10
-
11
- Useful for modeling systems like social networks, transportation systems,
12
- or any environment where connectivity matters more than physical location.
13
- """
1
+ """A Network grid."""
14
2
 
15
3
  from random import Random
16
4
  from typing import Any
@@ -1,16 +1,4 @@
1
- """Cell spaces based on Voronoi tessellation around seed points.
2
-
3
- Creates irregular spatial divisions by building cells around seed points,
4
- where each cell contains the area closer to its seed than any other.
5
- Features:
6
- - Organic-looking spaces from point sets
7
- - Automatic neighbor determination
8
- - Area-based cell capacities
9
- - Natural regional divisions
10
-
11
- Useful for models requiring irregular but mathematically meaningful spatial
12
- divisions, like territories, service areas, or natural regions.
13
- """
1
+ """Support for Voronoi meshed grids."""
14
2
 
15
3
  from collections.abc import Sequence
16
4
  from itertools import combinations
@@ -0,0 +1,81 @@
1
+ """Altair components."""
2
+
3
+ import contextlib
4
+
5
+ import solara
6
+
7
+ with contextlib.suppress(ImportError):
8
+ import altair as alt
9
+
10
+
11
+ @solara.component
12
+ def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
13
+ """A component that renders a Space using Altair.
14
+
15
+ Args:
16
+ model: a model instance
17
+ agent_portrayal: agent portray specification
18
+ dependencies: optional list of dependencies (currently not used)
19
+
20
+ """
21
+ space = getattr(model, "grid", None)
22
+ if space is None:
23
+ # Sometimes the space is defined as model.space instead of model.grid
24
+ space = model.space
25
+ chart = _draw_grid(space, agent_portrayal)
26
+ solara.FigureAltair(chart)
27
+
28
+
29
+ def _draw_grid(space, agent_portrayal):
30
+ def portray(g):
31
+ all_agent_data = []
32
+ for content, (x, y) in g.coord_iter():
33
+ if not content:
34
+ continue
35
+ if not hasattr(content, "__iter__"):
36
+ # Is a single grid
37
+ content = [content] # noqa: PLW2901
38
+ for agent in content:
39
+ # use all data from agent portrayal, and add x,y coordinates
40
+ agent_data = agent_portrayal(agent)
41
+ agent_data["x"] = x
42
+ agent_data["y"] = y
43
+ all_agent_data.append(agent_data)
44
+ return all_agent_data
45
+
46
+ all_agent_data = portray(space)
47
+ invalid_tooltips = ["color", "size", "x", "y"]
48
+
49
+ encoding_dict = {
50
+ # no x-axis label
51
+ "x": alt.X("x", axis=None, type="ordinal"),
52
+ # no y-axis label
53
+ "y": alt.Y("y", axis=None, type="ordinal"),
54
+ "tooltip": [
55
+ alt.Tooltip(key, type=alt.utils.infer_vegalite_type([value]))
56
+ for key, value in all_agent_data[0].items()
57
+ if key not in invalid_tooltips
58
+ ],
59
+ }
60
+ has_color = "color" in all_agent_data[0]
61
+ if has_color:
62
+ encoding_dict["color"] = alt.Color("color", type="nominal")
63
+ has_size = "size" in all_agent_data[0]
64
+ if has_size:
65
+ encoding_dict["size"] = alt.Size("size", type="quantitative")
66
+
67
+ chart = (
68
+ alt.Chart(
69
+ alt.Data(values=all_agent_data), encoding=alt.Encoding(**encoding_dict)
70
+ )
71
+ .mark_point(filled=True)
72
+ .properties(width=280, height=280)
73
+ # .configure_view(strokeOpacity=0) # hide grid/chart lines
74
+ )
75
+ # This is the default value for the marker size, which auto-scales
76
+ # according to the grid area.
77
+ if not has_size:
78
+ length = min(space.width, space.height)
79
+ chart = chart.mark_point(size=30000 / length**2, filled=True)
80
+
81
+ return chart
@@ -0,0 +1,242 @@
1
+ """Support for using matplotlib to draw spaces."""
2
+
3
+ from collections import defaultdict
4
+
5
+ import networkx as nx
6
+ import solara
7
+ from matplotlib.figure import Figure
8
+ from matplotlib.ticker import MaxNLocator
9
+
10
+ import mesa
11
+ from mesa.experimental.cell_space import VoronoiGrid
12
+
13
+
14
+ @solara.component
15
+ def SpaceMatplotlib(model, agent_portrayal, dependencies: list[any] | None = None):
16
+ """A component for rendering a space using Matplotlib.
17
+
18
+ Args:
19
+ model: a model instance
20
+ agent_portrayal: a specification of how to portray an agent.
21
+ dependencies: list of dependencies.
22
+
23
+ """
24
+ space_fig = Figure()
25
+ space_ax = space_fig.subplots()
26
+ space = getattr(model, "grid", None)
27
+ if space is None:
28
+ # Sometimes the space is defined as model.space instead of model.grid
29
+ space = model.space
30
+ if isinstance(space, mesa.space.NetworkGrid):
31
+ _draw_network_grid(space, space_ax, agent_portrayal)
32
+ elif isinstance(space, mesa.space.ContinuousSpace):
33
+ _draw_continuous_space(space, space_ax, agent_portrayal)
34
+ elif isinstance(space, VoronoiGrid):
35
+ _draw_voronoi(space, space_ax, agent_portrayal)
36
+ else:
37
+ _draw_grid(space, space_ax, agent_portrayal)
38
+ solara.FigureMatplotlib(space_fig, format="png", dependencies=dependencies)
39
+
40
+
41
+ # matplotlib scatter does not allow for multiple shapes in one call
42
+ def _split_and_scatter(portray_data, space_ax):
43
+ grouped_data = defaultdict(lambda: {"x": [], "y": [], "s": [], "c": []})
44
+
45
+ # Extract data from the dictionary
46
+ x = portray_data["x"]
47
+ y = portray_data["y"]
48
+ s = portray_data["s"]
49
+ c = portray_data["c"]
50
+ m = portray_data["m"]
51
+
52
+ if not (len(x) == len(y) == len(s) == len(c) == len(m)):
53
+ raise ValueError(
54
+ "Length mismatch in portrayal data lists: "
55
+ f"x: {len(x)}, y: {len(y)}, size: {len(s)}, "
56
+ f"color: {len(c)}, marker: {len(m)}"
57
+ )
58
+
59
+ # Group the data by marker
60
+ for i in range(len(x)):
61
+ marker = m[i]
62
+ grouped_data[marker]["x"].append(x[i])
63
+ grouped_data[marker]["y"].append(y[i])
64
+ grouped_data[marker]["s"].append(s[i])
65
+ grouped_data[marker]["c"].append(c[i])
66
+
67
+ # Plot each group with the same marker
68
+ for marker, data in grouped_data.items():
69
+ space_ax.scatter(data["x"], data["y"], s=data["s"], c=data["c"], marker=marker)
70
+
71
+
72
+ def _draw_grid(space, space_ax, agent_portrayal):
73
+ def portray(g):
74
+ x = []
75
+ y = []
76
+ s = [] # size
77
+ c = [] # color
78
+ m = [] # shape
79
+ for i in range(g.width):
80
+ for j in range(g.height):
81
+ content = g._grid[i][j]
82
+ if not content:
83
+ continue
84
+ if not hasattr(content, "__iter__"):
85
+ # Is a single grid
86
+ content = [content]
87
+ for agent in content:
88
+ data = agent_portrayal(agent)
89
+ x.append(i)
90
+ y.append(j)
91
+
92
+ # This is the default value for the marker size, which auto-scales
93
+ # according to the grid area.
94
+ default_size = (180 / max(g.width, g.height)) ** 2
95
+ # establishing a default prevents misalignment if some agents are not given size, color, etc.
96
+ size = data.get("size", default_size)
97
+ s.append(size)
98
+ color = data.get("color", "tab:blue")
99
+ c.append(color)
100
+ mark = data.get("shape", "o")
101
+ m.append(mark)
102
+ out = {"x": x, "y": y, "s": s, "c": c, "m": m}
103
+ return out
104
+
105
+ space_ax.set_xlim(-1, space.width)
106
+ space_ax.set_ylim(-1, space.height)
107
+ _split_and_scatter(portray(space), space_ax)
108
+
109
+
110
+ def _draw_network_grid(space, space_ax, agent_portrayal):
111
+ graph = space.G
112
+ pos = nx.spring_layout(graph, seed=0)
113
+ nx.draw(
114
+ graph,
115
+ ax=space_ax,
116
+ pos=pos,
117
+ **agent_portrayal(graph),
118
+ )
119
+
120
+
121
+ def _draw_continuous_space(space, space_ax, agent_portrayal):
122
+ def portray(space):
123
+ x = []
124
+ y = []
125
+ s = [] # size
126
+ c = [] # color
127
+ m = [] # shape
128
+ for agent in space._agent_to_index:
129
+ data = agent_portrayal(agent)
130
+ _x, _y = agent.pos
131
+ x.append(_x)
132
+ y.append(_y)
133
+
134
+ # This is matplotlib's default marker size
135
+ default_size = 20
136
+ # establishing a default prevents misalignment if some agents are not given size, color, etc.
137
+ size = data.get("size", default_size)
138
+ s.append(size)
139
+ color = data.get("color", "tab:blue")
140
+ c.append(color)
141
+ mark = data.get("shape", "o")
142
+ m.append(mark)
143
+ out = {"x": x, "y": y, "s": s, "c": c, "m": m}
144
+ return out
145
+
146
+ # Determine border style based on space.torus
147
+ border_style = "solid" if not space.torus else (0, (5, 10))
148
+
149
+ # Set the border of the plot
150
+ for spine in space_ax.spines.values():
151
+ spine.set_linewidth(1.5)
152
+ spine.set_color("black")
153
+ spine.set_linestyle(border_style)
154
+
155
+ width = space.x_max - space.x_min
156
+ x_padding = width / 20
157
+ height = space.y_max - space.y_min
158
+ y_padding = height / 20
159
+ space_ax.set_xlim(space.x_min - x_padding, space.x_max + x_padding)
160
+ space_ax.set_ylim(space.y_min - y_padding, space.y_max + y_padding)
161
+
162
+ # Portray and scatter the agents in the space
163
+ _split_and_scatter(portray(space), space_ax)
164
+
165
+
166
+ def _draw_voronoi(space, space_ax, agent_portrayal):
167
+ def portray(g):
168
+ x = []
169
+ y = []
170
+ s = [] # size
171
+ c = [] # color
172
+
173
+ for cell in g.all_cells:
174
+ for agent in cell.agents:
175
+ data = agent_portrayal(agent)
176
+ x.append(cell.coordinate[0])
177
+ y.append(cell.coordinate[1])
178
+ if "size" in data:
179
+ s.append(data["size"])
180
+ if "color" in data:
181
+ c.append(data["color"])
182
+ out = {"x": x, "y": y}
183
+ # This is the default value for the marker size, which auto-scales
184
+ # according to the grid area.
185
+ out["s"] = s
186
+ if len(c) > 0:
187
+ out["c"] = c
188
+
189
+ return out
190
+
191
+ x_list = [i[0] for i in space.centroids_coordinates]
192
+ y_list = [i[1] for i in space.centroids_coordinates]
193
+ x_max = max(x_list)
194
+ x_min = min(x_list)
195
+ y_max = max(y_list)
196
+ y_min = min(y_list)
197
+
198
+ width = x_max - x_min
199
+ x_padding = width / 20
200
+ height = y_max - y_min
201
+ y_padding = height / 20
202
+ space_ax.set_xlim(x_min - x_padding, x_max + x_padding)
203
+ space_ax.set_ylim(y_min - y_padding, y_max + y_padding)
204
+ space_ax.scatter(**portray(space))
205
+
206
+ for cell in space.all_cells:
207
+ polygon = cell.properties["polygon"]
208
+ space_ax.fill(
209
+ *zip(*polygon),
210
+ alpha=min(1, cell.properties[space.cell_coloring_property]),
211
+ c="red",
212
+ ) # Plot filled polygon
213
+ space_ax.plot(*zip(*polygon), color="black") # Plot polygon edges in red
214
+
215
+
216
+ @solara.component
217
+ def PlotMatplotlib(model, measure, dependencies: list[any] | None = None):
218
+ """A solara component for creating a matplotlib figure.
219
+
220
+ Args:
221
+ model: Model instance
222
+ measure: measure to plot
223
+ dependencies: list of additional dependencies
224
+
225
+ """
226
+ fig = Figure()
227
+ ax = fig.subplots()
228
+ df = model.datacollector.get_model_vars_dataframe()
229
+ if isinstance(measure, str):
230
+ ax.plot(df.loc[:, measure])
231
+ ax.set_ylabel(measure)
232
+ elif isinstance(measure, dict):
233
+ for m, color in measure.items():
234
+ ax.plot(df.loc[:, m], label=m, color=color)
235
+ fig.legend()
236
+ elif isinstance(measure, list | tuple):
237
+ for m in measure:
238
+ ax.plot(df.loc[:, m], label=m)
239
+ fig.legend()
240
+ # Set integer x axis
241
+ ax.xaxis.set_major_locator(MaxNLocator(integer=True))
242
+ solara.FigureMatplotlib(fig, dependencies=dependencies)
@@ -1,24 +1,6 @@
1
- """Core event management functionality for Mesa's discrete event simulation system.
2
-
3
- This module provides the foundational data structures and classes needed for event-based
4
- simulation in Mesa. The EventList class is a priority queue implementation that maintains
5
- simulation events in chronological order while respecting event priorities. Key features:
6
-
7
- - Priority-based event ordering
8
- - Weak references to prevent memory leaks from canceled events
9
- - Efficient event insertion and removal using a heap queue
10
- - Support for event cancellation without breaking the heap structure
11
-
12
- The module contains three main components:
13
- - Priority: An enumeration defining event priority levels (HIGH, DEFAULT, LOW)
14
- - SimulationEvent: A class representing individual events with timing and execution details
15
- - EventList: A heap-based priority queue managing the chronological ordering of events
16
-
17
- The implementation supports both pure discrete event simulation and hybrid approaches
18
- combining agent-based modeling with event scheduling.
19
- """
1
+ """Support for event scheduling."""
20
2
 
21
3
  from .eventlist import Priority, SimulationEvent
22
4
  from .simulator import ABMSimulator, DEVSimulator
23
5
 
24
- __all__ = ["ABMSimulator", "DEVSimulator", "Priority", "SimulationEvent"]
6
+ __all__ = ["ABMSimulator", "DEVSimulator", "SimulationEvent", "Priority"]
@@ -1,22 +1,4 @@
1
- """Core event management functionality for Mesa's discrete event simulation system.
2
-
3
- This module provides the foundational data structures and classes needed for event-based
4
- simulation in Mesa. The EventList class is a priority queue implementation that maintains
5
- simulation events in chronological order while respecting event priorities. Key features:
6
-
7
- - Priority-based event ordering
8
- - Weak references to prevent memory leaks from canceled events
9
- - Efficient event insertion and removal using a heap queue
10
- - Support for event cancellation without breaking the heap structure
11
-
12
- The module contains three main components:
13
- - Priority: An enumeration defining event priority levels (HIGH, DEFAULT, LOW)
14
- - SimulationEvent: A class representing individual events with timing and execution details
15
- - EventList: A heap-based priority queue managing the chronological ordering of events
16
-
17
- The implementation supports both pure discrete event simulation and hybrid approaches
18
- combining agent-based modeling with event scheduling.
19
- """
1
+ """Eventlist which is at the core of event scheduling."""
20
2
 
21
3
  from __future__ import annotations
22
4