Mesa 3.1.3__py3-none-any.whl → 3.1.4__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.
mesa/__init__.py CHANGED
@@ -22,7 +22,7 @@ __all__ = [
22
22
  ]
23
23
 
24
24
  __title__ = "mesa"
25
- __version__ = "3.1.3"
25
+ __version__ = "3.1.4"
26
26
  __license__ = "Apache 2.0"
27
27
  _this_year = datetime.datetime.now(tz=datetime.UTC).date().year
28
28
  __copyright__ = f"Copyright {_this_year} Project Mesa Team"
mesa/datacollection.py CHANGED
@@ -117,6 +117,9 @@ class DataCollector:
117
117
  self._agenttype_records = {}
118
118
  self.tables = {}
119
119
 
120
+ # add the signal of the validation of model reporter
121
+ self._validated = False
122
+
120
123
  if model_reporters is not None:
121
124
  for name, reporter in model_reporters.items():
122
125
  self._new_model_reporter(name, reporter)
@@ -134,13 +137,66 @@ class DataCollector:
134
137
  for name, columns in tables.items():
135
138
  self._new_table(name, columns)
136
139
 
140
+ def _validate_model_reporter(self, name, reporter, model):
141
+ """Validate model reporter and handle validation results appropriately.
142
+
143
+ Args:
144
+ name: Name of the reporter
145
+ reporter: Reporter definition (lambda/method/attribute/function list)
146
+ model: Model instance
147
+
148
+ Raises:
149
+ ValueError: If reporter is None or has invalid format
150
+ AttributeError: If model attribute doesn't exist
151
+ TypeError: If reporter type is not supported
152
+ RuntimeError: If reporter execution fails
153
+ """
154
+ self._validated = True # put the change of signal firstly avoid losing efficacy
155
+
156
+ # Type 1: Lambda function
157
+ if isinstance(reporter, types.LambdaType):
158
+ try:
159
+ reporter(model)
160
+ except Exception as e:
161
+ raise RuntimeError(
162
+ f"Lambda reporter '{name}' failed validation: {e!s}\n"
163
+ f"Example: lambda m: len(m.agents)"
164
+ ) from e
165
+
166
+ # Type 2: Method of class/instance
167
+ if not callable(reporter) and not isinstance(reporter, types.LambdaType):
168
+ pass
169
+
170
+ # Type 3: Model attribute (string)
171
+ if isinstance(reporter, str):
172
+ try:
173
+ if not hasattr(model, reporter):
174
+ raise AttributeError(
175
+ f"Model reporter '{name}' references non-existent attribute '{reporter}'\n"
176
+ )
177
+ getattr(model, reporter) # 验证属性是否可访问
178
+ except AttributeError as e:
179
+ raise AttributeError(
180
+ f"Model reporter '{name}' attribute validation failed: {e!s}\n"
181
+ ) from e
182
+
183
+ # Type 4: Function with parameters in list
184
+ if isinstance(reporter, list) and (not reporter or not callable(reporter[0])):
185
+ raise ValueError(
186
+ f"Invalid function list format for reporter '{name}'\n"
187
+ f"Expected: [function, [param1, param2]], got: {reporter}"
188
+ )
189
+
137
190
  def _new_model_reporter(self, name, reporter):
138
191
  """Add a new model-level reporter to collect.
139
192
 
140
193
  Args:
141
194
  name: Name of the model-level variable to collect.
142
- reporter: Attribute string, or function object that returns the
143
- variable when given a model instance.
195
+ reporter: Can be one of four types:
196
+ 1. Attribute name (str): "attribute_name"
197
+ 2. Lambda function: lambda m: len(m.agents)
198
+ 3. Method: model.get_count or Model.get_count
199
+ 4. List of [function, [parameters]]
144
200
  """
145
201
  self.model_reporters[name] = reporter
146
202
  self.model_vars[name] = []
@@ -262,6 +318,10 @@ class DataCollector:
262
318
  def collect(self, model):
263
319
  """Collect all the data for the given model object."""
264
320
  if self.model_reporters:
321
+ if not self._validated:
322
+ for name, reporter in self.model_reporters.items():
323
+ self._validate_model_reporter(name, reporter, model)
324
+
265
325
  for var, reporter in self.model_reporters.items():
266
326
  # Check if lambda or partial function
267
327
  if isinstance(reporter, types.LambdaType | partial):
@@ -1,46 +1,24 @@
1
- import numpy as np
2
- import solara
3
- from matplotlib.figure import Figure
4
-
5
1
  from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
6
2
  from mesa.visualization import Slider, SolaraViz, make_plot_component
3
+ from mesa.visualization.components.matplotlib_components import make_mpl_space_component
4
+
7
5
 
6
+ def agent_portrayal(agent):
7
+ return {"marker": "o", "color": "red", "size": 10}
8
8
 
9
- def SpaceDrawer(model):
10
- def portray(g):
11
- layers = {
12
- "trader": {"x": [], "y": [], "c": "tab:red", "marker": "o", "s": 10},
13
- }
14
9
 
15
- for agent in g.all_cells.agents:
16
- i, j = agent.cell.coordinate
17
- layers["trader"]["x"].append(i)
18
- layers["trader"]["y"].append(j)
19
- return layers
10
+ propertylayer_portrayal = {
11
+ "sugar": {"color": "blue", "alpha": 0.8, "colorbar": True, "vmin": 0, "vmax": 10},
12
+ "spice": {"color": "red", "alpha": 0.8, "colorbar": True, "vmin": 0, "vmax": 10},
13
+ }
20
14
 
21
- fig = Figure()
22
- ax = fig.subplots()
23
- out = portray(model.grid)
24
- # Sugar
25
- # Important note: imshow by default draws from upper left. You have to
26
- # always explicitly specify origin="lower".
27
- im = ax.imshow(
28
- np.ma.masked_where(model.grid.sugar.data <= 1, model.grid.sugar.data),
29
- cmap="spring",
30
- origin="lower",
31
- )
32
- fig.colorbar(im, orientation="vertical")
33
- # Spice
34
- ax.imshow(
35
- np.ma.masked_where(model.grid.spice.data <= 1, model.grid.spice.data),
36
- cmap="winter",
37
- origin="lower",
38
- )
39
- # Trader
40
- ax.scatter(**out["trader"])
41
- ax.set_axis_off()
42
- return solara.FigureMatplotlib(fig)
43
15
 
16
+ sugarscape_space = make_mpl_space_component(
17
+ agent_portrayal=agent_portrayal,
18
+ propertylayer_portrayal=propertylayer_portrayal,
19
+ post_process=None,
20
+ draw_grid=False,
21
+ )
44
22
 
45
23
  model_params = {
46
24
  "seed": {
@@ -72,7 +50,7 @@ model = SugarscapeG1mt()
72
50
  page = SolaraViz(
73
51
  model,
74
52
  components=[
75
- SpaceDrawer,
53
+ sugarscape_space,
76
54
  make_plot_component("#Traders"),
77
55
  make_plot_component("Price"),
78
56
  ],
@@ -273,20 +273,20 @@ class HexGrid(Grid[T]):
273
273
  def _connect_cells_2d(self) -> None:
274
274
  # fmt: off
275
275
  even_offsets = [
276
- (-1, -1), (-1, 0),
277
- ( 0, -1), ( 0, 1),
278
- ( 1, -1), ( 1, 0),
276
+ (-1, -1), (0, -1),
277
+ ( -1, 0), ( 1, 0),
278
+ ( -1, 1), (0, 1),
279
279
  ]
280
280
  odd_offsets = [
281
- (-1, 0), (-1, 1),
282
- ( 0, -1), ( 0, 1),
283
- ( 1, 0), ( 1, 1),
281
+ (0, -1), (1, -1),
282
+ ( -1, 0), ( 1, 0),
283
+ ( 0, 1), ( 1, 1),
284
284
  ]
285
285
  # fmt: on
286
286
 
287
287
  for cell in self.all_cells:
288
- i = cell.coordinate[0]
289
- offsets = even_offsets if i % 2 == 0 else odd_offsets
288
+ i = cell.coordinate[1]
289
+ offsets = even_offsets if i % 2 else odd_offsets
290
290
  self._connect_single_cell_2d(cell, offsets=offsets)
291
291
 
292
292
  def _connect_cells_nd(self) -> None:
@@ -1,9 +1,8 @@
1
1
  """Solara based visualization for Mesa models.
2
2
 
3
3
  .. note::
4
- SolaraViz is experimental and still in active development for Mesa 3.0. While we attempt to minimize them, there might be API breaking changes between Mesa 3.0 and 3.1.
4
+ SolaraViz is experimental and still in active development in Mesa 3.x. While we attempt to minimize them, there might be API breaking changes in minor releases.
5
5
 
6
- There won't be breaking changes between Mesa 3.0.x patch releases.
7
6
  """
8
7
 
9
8
  from mesa.visualization.mpl_space_drawing import (
@@ -1,13 +1,10 @@
1
1
  """Altair based solara components for visualization mesa spaces."""
2
2
 
3
- import contextlib
4
3
  import warnings
5
4
 
5
+ import altair as alt
6
6
  import solara
7
7
 
8
- with contextlib.suppress(ImportError):
9
- import altair as alt
10
-
11
8
  from mesa.experimental.cell_space import DiscreteSpace, Grid
12
9
  from mesa.space import ContinuousSpace, _Grid
13
10
  from mesa.visualization.utils import update_counter
@@ -30,7 +27,7 @@ def make_altair_space(
30
27
  Args:
31
28
  agent_portrayal: Function to portray agents.
32
29
  propertylayer_portrayal: not yet implemented
33
- post_process :not yet implemented
30
+ post_process :A user specified callable that will be called with the Chart instance from Altair. Allows for fine tuning plots (e.g., control ticks)
34
31
  space_drawing_kwargs : not yet implemented
35
32
 
36
33
  ``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color",
@@ -46,13 +43,15 @@ def make_altair_space(
46
43
  return {"id": a.unique_id}
47
44
 
48
45
  def MakeSpaceAltair(model):
49
- return SpaceAltair(model, agent_portrayal)
46
+ return SpaceAltair(model, agent_portrayal, post_process=post_process)
50
47
 
51
48
  return MakeSpaceAltair
52
49
 
53
50
 
54
51
  @solara.component
55
- def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
52
+ def SpaceAltair(
53
+ model, agent_portrayal, dependencies: list[any] | None = None, post_process=None
54
+ ):
56
55
  """Create an Altair-based space visualization component.
57
56
 
58
57
  Returns:
@@ -65,6 +64,9 @@ def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
65
64
  space = model.space
66
65
 
67
66
  chart = _draw_grid(space, agent_portrayal)
67
+ # Apply post-processing if provided
68
+ if post_process is not None:
69
+ chart = post_process(chart)
68
70
  solara.FigureAltair(chart)
69
71
 
70
72
 
@@ -159,7 +161,7 @@ def _draw_grid(space, agent_portrayal):
159
161
  # no y-axis label
160
162
  "y": alt.Y("y", axis=None, type=x_y_type),
161
163
  "tooltip": [
162
- alt.Tooltip(key, type=alt.utils.infer_vegalite_type([value]))
164
+ alt.Tooltip(key, type=alt.utils.infer_vegalite_type_for_pandas([value]))
163
165
  for key, value in all_agent_data[0].items()
164
166
  if key not in invalid_tooltips
165
167
  ],
@@ -8,9 +8,10 @@ for a paper.
8
8
 
9
9
  import contextlib
10
10
  import itertools
11
- import math
12
11
  import warnings
13
12
  from collections.abc import Callable
13
+ from functools import lru_cache
14
+ from itertools import pairwise
14
15
  from typing import Any
15
16
 
16
17
  import networkx as nx
@@ -18,9 +19,9 @@ import numpy as np
18
19
  from matplotlib import pyplot as plt
19
20
  from matplotlib.axes import Axes
20
21
  from matplotlib.cm import ScalarMappable
21
- from matplotlib.collections import PatchCollection
22
+ from matplotlib.collections import LineCollection, PatchCollection, PolyCollection
22
23
  from matplotlib.colors import LinearSegmentedColormap, Normalize, to_rgba
23
- from matplotlib.patches import Polygon, RegularPolygon
24
+ from matplotlib.patches import Polygon
24
25
 
25
26
  import mesa
26
27
  from mesa.experimental.cell_space import (
@@ -159,6 +160,40 @@ def draw_space(
159
160
  return ax
160
161
 
161
162
 
163
+ @lru_cache(maxsize=1024, typed=True)
164
+ def _get_hexmesh(
165
+ width: int, height: int, size: float = 1.0
166
+ ) -> list[tuple[float, float]]:
167
+ """Generate hexagon vertices for the mesh. Yields list of vertex coordinates for each hexagon."""
168
+
169
+ # Helper function for getting the vertices of a hexagon given the center and size
170
+ def _get_hex_vertices(
171
+ center_x: float, center_y: float, size: float = 1.0
172
+ ) -> list[tuple[float, float]]:
173
+ """Get vertices for a hexagon centered at (center_x, center_y)."""
174
+ vertices = [
175
+ (center_x, center_y + size), # top
176
+ (center_x + size * np.sqrt(3) / 2, center_y + size / 2), # top right
177
+ (center_x + size * np.sqrt(3) / 2, center_y - size / 2), # bottom right
178
+ (center_x, center_y - size), # bottom
179
+ (center_x - size * np.sqrt(3) / 2, center_y - size / 2), # bottom left
180
+ (center_x - size * np.sqrt(3) / 2, center_y + size / 2), # top left
181
+ ]
182
+ return vertices
183
+
184
+ x_spacing = np.sqrt(3) * size
185
+ y_spacing = 1.5 * size
186
+ hexagons = []
187
+
188
+ for row, col in itertools.product(range(height), range(width)):
189
+ # Calculate center position with offset for even rows
190
+ x = col * x_spacing + (row % 2 == 0) * (x_spacing / 2)
191
+ y = row * y_spacing
192
+ hexagons.append(_get_hex_vertices(x, y, size))
193
+
194
+ return hexagons
195
+
196
+
162
197
  def draw_property_layers(
163
198
  space, propertylayer_portrayal: dict[str, dict[str, Any]], ax: Axes
164
199
  ):
@@ -191,11 +226,10 @@ def draw_property_layers(
191
226
  continue
192
227
 
193
228
  data = layer.data.astype(float) if layer.data.dtype == bool else layer.data
194
- width, height = data.shape # if space is None else (space.width, space.height)
195
229
 
196
- if space and data.shape != (width, height):
230
+ if (space.width, space.height) != data.shape:
197
231
  warnings.warn(
198
- f"Layer {layer_name} dimensions ({data.shape}) do not match space dimensions ({width}, {height}).",
232
+ f"Layer {layer_name} dimensions ({data.shape}) do not match space dimensions ({space.width}, {space.height}).",
199
233
  UserWarning,
200
234
  stacklevel=2,
201
235
  )
@@ -206,45 +240,75 @@ def draw_property_layers(
206
240
  vmax = portrayal.get("vmax", np.max(data))
207
241
  colorbar = portrayal.get("colorbar", True)
208
242
 
209
- # Draw the layer
243
+ # Prepare colormap
210
244
  if "color" in portrayal:
211
245
  rgba_color = to_rgba(portrayal["color"])
212
- normalized_data = (data - vmin) / (vmax - vmin)
213
- rgba_data = np.full((*data.shape, 4), rgba_color)
214
- rgba_data[..., 3] *= normalized_data * alpha
215
- rgba_data = np.clip(rgba_data, 0, 1)
216
246
  cmap = LinearSegmentedColormap.from_list(
217
247
  layer_name, [(0, 0, 0, 0), (*rgba_color[:3], alpha)]
218
248
  )
219
- im = ax.imshow(
220
- rgba_data,
221
- origin="lower",
222
- )
223
- if colorbar:
224
- norm = Normalize(vmin=vmin, vmax=vmax)
225
- sm = ScalarMappable(norm=norm, cmap=cmap)
226
- sm.set_array([])
227
- ax.figure.colorbar(sm, ax=ax, orientation="vertical")
228
-
229
249
  elif "colormap" in portrayal:
230
250
  cmap = portrayal.get("colormap", "viridis")
231
251
  if isinstance(cmap, list):
232
252
  cmap = LinearSegmentedColormap.from_list(layer_name, cmap)
233
- im = ax.imshow(
234
- data,
235
- cmap=cmap,
236
- alpha=alpha,
237
- vmin=vmin,
238
- vmax=vmax,
239
- origin="lower",
240
- )
241
- if colorbar:
242
- plt.colorbar(im, ax=ax, label=layer_name)
253
+ elif isinstance(cmap, str):
254
+ cmap = plt.get_cmap(cmap)
243
255
  else:
244
256
  raise ValueError(
245
257
  f"PropertyLayer {layer_name} portrayal must include 'color' or 'colormap'."
246
258
  )
247
259
 
260
+ if isinstance(space, OrthogonalGrid):
261
+ if "color" in portrayal:
262
+ data = data.T
263
+ normalized_data = (data - vmin) / (vmax - vmin)
264
+ rgba_data = np.full((*data.shape, 4), rgba_color)
265
+ rgba_data[..., 3] *= normalized_data * alpha
266
+ rgba_data = np.clip(rgba_data, 0, 1)
267
+ ax.imshow(rgba_data, origin="lower")
268
+ else:
269
+ ax.imshow(
270
+ data.T,
271
+ cmap=cmap,
272
+ alpha=alpha,
273
+ vmin=vmin,
274
+ vmax=vmax,
275
+ origin="lower",
276
+ )
277
+
278
+ elif isinstance(space, HexGrid):
279
+ width, height = data.shape
280
+
281
+ # Generate hexagon mesh
282
+ hexagons = _get_hexmesh(width, height)
283
+
284
+ # Normalize colors
285
+ norm = Normalize(vmin=vmin, vmax=vmax)
286
+ colors = data.ravel() # flatten data to 1D array
287
+
288
+ if "color" in portrayal:
289
+ normalized_colors = np.clip(norm(colors), 0, 1)
290
+ rgba_colors = np.full((len(colors), 4), rgba_color)
291
+ rgba_colors[:, 3] = normalized_colors * alpha
292
+ else:
293
+ rgba_colors = cmap(norm(colors))
294
+ rgba_colors[..., 3] *= alpha
295
+
296
+ # Draw hexagons
297
+ collection = PolyCollection(hexagons, facecolors=rgba_colors, zorder=-1)
298
+ ax.add_collection(collection)
299
+
300
+ else:
301
+ raise NotImplementedError(
302
+ f"PropertyLayer visualization not implemented for {type(space)}."
303
+ )
304
+
305
+ # Add colorbar if requested
306
+ if colorbar:
307
+ norm = Normalize(vmin=vmin, vmax=vmax)
308
+ sm = ScalarMappable(norm=norm, cmap=cmap)
309
+ sm.set_array([])
310
+ plt.colorbar(sm, ax=ax, label=layer_name)
311
+
248
312
 
249
313
  def draw_orthogonal_grid(
250
314
  space: OrthogonalGrid,
@@ -308,13 +372,6 @@ def draw_hex_grid(
308
372
  ax: a Matplotlib Axes instance. If none is provided a new figure and ax will be created using plt.subplots
309
373
  draw_grid: whether to draw the grid
310
374
  kwargs: additional keyword arguments passed to ax.scatter
311
-
312
- Returns:
313
- Returns the Axes object with the plot drawn onto it.
314
-
315
- ``agent_portrayal`` is called with an agent and should return a dict. Valid fields in this dict are "color",
316
- "size", "marker", and "zorder". Other field are ignored and will result in a user warning.
317
-
318
375
  """
319
376
  if ax is None:
320
377
  fig, ax = plt.subplots()
@@ -323,62 +380,54 @@ def draw_hex_grid(
323
380
  s_default = (180 / max(space.width, space.height)) ** 2
324
381
  arguments = collect_agent_data(space, agent_portrayal, size=s_default)
325
382
 
326
- # for hexgrids we have to go from logical coordinates to visual coordinates
327
- # this is a bit messy.
328
-
329
- # give all even rows an offset in the x direction
330
- # give all rows an offset in the y direction
331
-
332
- # numbers here are based on a distance of 1 between centers of hexes
333
- offset = math.sqrt(0.75)
383
+ # Parameters for hexagon grid
384
+ size = 1.0
385
+ x_spacing = np.sqrt(3) * size
386
+ y_spacing = 1.5 * size
334
387
 
335
388
  loc = arguments["loc"].astype(float)
336
-
337
- logical = np.mod(loc[:, 1], 2) == 0
338
- loc[:, 0][logical] += 0.5
339
- loc[:, 1] *= offset
340
- arguments["loc"] = loc
341
-
342
- # plot the agents
343
- _scatter(ax, arguments, **kwargs)
344
-
345
- # further styling and adding of grid
346
- ax.set_xlim(-1, space.width + 0.5)
347
- ax.set_ylim(-offset, space.height * offset)
348
-
349
- def setup_hexmesh(
350
- width,
351
- height,
352
- ):
353
- """Helper function for creating the hexmaesh."""
354
- # fixme: this should be done once, rather than in each update
355
- # fixme check coordinate system in hexgrid (see https://www.redblobgames.com/grids/hexagons/#coordinates-offset)
356
-
357
- patches = []
358
- for x, y in itertools.product(range(width), range(height)):
359
- if y % 2 == 0:
360
- x += 0.5 # noqa: PLW2901
361
- y *= offset # noqa: PLW2901
362
- hex = RegularPolygon(
363
- (x, y),
364
- numVertices=6,
365
- radius=math.sqrt(1 / 3),
366
- orientation=np.radians(120),
367
- )
368
- patches.append(hex)
369
- mesh = PatchCollection(
370
- patches, edgecolor="k", facecolor=(1, 1, 1, 0), linestyle="dotted", lw=1
371
- )
372
- return mesh
389
+ # Calculate hexagon centers for agents if agents are present and plot them.
390
+ if loc.size > 0:
391
+ loc[:, 0] = loc[:, 0] * x_spacing + ((loc[:, 1] - 1) % 2) * (x_spacing / 2)
392
+ loc[:, 1] = loc[:, 1] * y_spacing
393
+ arguments["loc"] = loc
394
+
395
+ # plot the agents
396
+ _scatter(ax, arguments, **kwargs)
397
+
398
+ # Calculate proper bounds that account for the full hexagon width and height
399
+ x_max = space.width * x_spacing + (space.height % 2) * (x_spacing / 2)
400
+ y_max = space.height * y_spacing
401
+
402
+ # Add padding that accounts for the hexagon points
403
+ x_padding = (
404
+ size * np.sqrt(3) / 2
405
+ ) # Distance from center to rightmost point of hexagon
406
+ y_padding = size # Distance from center to topmost point of hexagon
407
+
408
+ # Plot limits to perfectly contain the hexagonal grid
409
+ # Determined through physical testing.
410
+ ax.set_xlim(-2 * x_padding, x_max + x_padding)
411
+ ax.set_ylim(-2 * y_padding, y_max + y_padding)
412
+
413
+ def setup_hexmesh(width, height):
414
+ """Helper function for creating the hexmesh with unique edges."""
415
+ edges = set()
416
+
417
+ # Generate edges for each hexagon
418
+ hexagons = _get_hexmesh(width, height)
419
+ for vertices in hexagons:
420
+ # Edge logic, connecting each vertex to the next
421
+ for v1, v2 in pairwise([*vertices, vertices[0]]):
422
+ # Sort vertices to ensure consistent edge representation and avoid duplicates.
423
+ edge = tuple(sorted([tuple(np.round(v1, 6)), tuple(np.round(v2, 6))]))
424
+ edges.add(edge)
425
+
426
+ return LineCollection(edges, linestyle=":", color="black", linewidth=1, alpha=1)
373
427
 
374
428
  if draw_grid:
375
- # add grid
376
- ax.add_collection(
377
- setup_hexmesh(
378
- space.width,
379
- space.height,
380
- )
381
- )
429
+ ax.add_collection(setup_hexmesh(space.width, space.height))
430
+
382
431
  return ax
383
432
 
384
433
 
@@ -97,7 +97,11 @@ def SolaraViz(
97
97
  reduce update frequency,resulting in faster execution.
98
98
  """
99
99
  if components == "default":
100
- components = [components_altair.make_altair_space()]
100
+ components = [
101
+ components_altair.make_altair_space(
102
+ agent_portrayal=None, propertylayer_portrayal=None, post_process=None
103
+ )
104
+ ]
101
105
  if model_params is None:
102
106
  model_params = {}
103
107
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Mesa
3
- Version: 3.1.3
3
+ Version: 3.1.4
4
4
  Summary: Agent-based modeling (ABM) in Python
5
5
  Project-URL: homepage, https://github.com/projectmesa/mesa
6
6
  Project-URL: repository, https://github.com/projectmesa/mesa
@@ -27,6 +27,7 @@ Requires-Dist: pandas
27
27
  Requires-Dist: scipy
28
28
  Requires-Dist: tqdm
29
29
  Provides-Extra: all
30
+ Requires-Dist: altair; extra == 'all'
30
31
  Requires-Dist: ipython; extra == 'all'
31
32
  Requires-Dist: matplotlib; extra == 'all'
32
33
  Requires-Dist: myst-nb; extra == 'all'
@@ -41,7 +42,9 @@ Requires-Dist: scipy; extra == 'all'
41
42
  Requires-Dist: seaborn; extra == 'all'
42
43
  Requires-Dist: solara; extra == 'all'
43
44
  Requires-Dist: sphinx; extra == 'all'
45
+ Requires-Dist: sphinx-copybutton; extra == 'all'
44
46
  Provides-Extra: dev
47
+ Requires-Dist: altair; extra == 'dev'
45
48
  Requires-Dist: matplotlib; extra == 'dev'
46
49
  Requires-Dist: networkx; extra == 'dev'
47
50
  Requires-Dist: pytest; extra == 'dev'
@@ -51,6 +54,7 @@ Requires-Dist: ruff; extra == 'dev'
51
54
  Requires-Dist: solara; extra == 'dev'
52
55
  Requires-Dist: sphinx; extra == 'dev'
53
56
  Provides-Extra: docs
57
+ Requires-Dist: altair; extra == 'docs'
54
58
  Requires-Dist: ipython; extra == 'docs'
55
59
  Requires-Dist: matplotlib; extra == 'docs'
56
60
  Requires-Dist: myst-nb; extra == 'docs'
@@ -60,7 +64,9 @@ Requires-Dist: pydata-sphinx-theme; extra == 'docs'
60
64
  Requires-Dist: seaborn; extra == 'docs'
61
65
  Requires-Dist: solara; extra == 'docs'
62
66
  Requires-Dist: sphinx; extra == 'docs'
67
+ Requires-Dist: sphinx-copybutton; extra == 'docs'
63
68
  Provides-Extra: examples
69
+ Requires-Dist: altair; extra == 'examples'
64
70
  Requires-Dist: matplotlib; extra == 'examples'
65
71
  Requires-Dist: networkx; extra == 'examples'
66
72
  Requires-Dist: pytest; extra == 'examples'
@@ -69,10 +75,12 @@ Requires-Dist: solara; extra == 'examples'
69
75
  Provides-Extra: network
70
76
  Requires-Dist: networkx; extra == 'network'
71
77
  Provides-Extra: rec
78
+ Requires-Dist: altair; extra == 'rec'
72
79
  Requires-Dist: matplotlib; extra == 'rec'
73
80
  Requires-Dist: networkx; extra == 'rec'
74
81
  Requires-Dist: solara; extra == 'rec'
75
82
  Provides-Extra: viz
83
+ Requires-Dist: altair; extra == 'viz'
76
84
  Requires-Dist: matplotlib; extra == 'viz'
77
85
  Requires-Dist: solara; extra == 'viz'
78
86
  Description-Content-Type: text/markdown
@@ -107,27 +115,22 @@ can be displayed in browser windows or Jupyter.*
107
115
 
108
116
  ## Using Mesa
109
117
 
110
- To install our latest stable release (3.0.x), run:
118
+ To install our latest stable release, run:
111
119
 
112
120
  ``` bash
113
121
  pip install -U mesa
114
122
  ```
115
123
 
116
- To install our latest pre-release, run:
117
-
118
- ``` bash
119
- pip install -U --pre mesa
120
- ```
121
124
  Starting with Mesa 3.0, we don't install all our dependencies anymore by default.
122
125
  ```bash
123
126
  # You can customize the additional dependencies you need, if you want. Available are:
124
- pip install -U --pre mesa[network,viz]
127
+ pip install -U mesa[network,viz]
125
128
 
126
129
  # This is equivalent to our recommended dependencies:
127
- pip install -U --pre mesa[rec]
130
+ pip install -U mesa[rec]
128
131
 
129
132
  # To install all, including developer, dependencies:
130
- pip install -U --pre mesa[all]
133
+ pip install -U mesa[all]
131
134
  ```
132
135
 
133
136
  You can also use `pip` to install the latest GitHub version:
@@ -1,7 +1,7 @@
1
- mesa/__init__.py,sha256=cQpR402xBpOx0vJjUbQB8d6D9d2JK_ZpWkmWSlm4cWk,611
1
+ mesa/__init__.py,sha256=wLwKIsmbzCJwpwM_L7SH--8rPFOpXmSEK2F7LlkTyCM,611
2
2
  mesa/agent.py,sha256=4CXMOFA9KhvTypaV_OHZGqxOR4GVwyX4x8DOtQENUQA,26130
3
3
  mesa/batchrunner.py,sha256=w8StV82F_7DAAVQc5V7_Ggp0EL1NYn__UcBE-Nwrgv4,7771
4
- mesa/datacollection.py,sha256=8loT4pQsXcHArxHSsbRc7HTc2GP5gsEIeKFKr3xya4I,15991
4
+ mesa/datacollection.py,sha256=vxW0KEmx6BHMM6SnKrRJJr7Eb_siGLyofwuyN8pSWXg,18483
5
5
  mesa/mesa_logging.py,sha256=PEDqUaQ2Y4bkYBkrHVkGT0sF86gUdbSH1T3vCg3qQeE,4949
6
6
  mesa/model.py,sha256=VkdBea_mkWcBMxMq-pwuU23UlI1gbG4TOIyqkBfeiFo,8354
7
7
  mesa/space.py,sha256=MNCblKf862pdkoIAa-VpjaurmI8GJtb02W3q3QWFjTE,64458
@@ -23,7 +23,7 @@ mesa/examples/advanced/pd_grid/model.py,sha256=-ytTSW0a_TQGTQW02k7XKAMiMak_b2XkJ
23
23
  mesa/examples/advanced/sugarscape_g1mt/Readme.md,sha256=x3kKw1Rre2FPkNhGDLtdzeThmH089mxsGYUPZUeu26k,3595
24
24
  mesa/examples/advanced/sugarscape_g1mt/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  mesa/examples/advanced/sugarscape_g1mt/agents.py,sha256=ugTNvWjQNm_jMInv9d0sOIFaChIDs-TWazN78YrDP9g,8466
26
- mesa/examples/advanced/sugarscape_g1mt/app.py,sha256=7BnjOEkzltfUSx_Hz8h7QXqWARrnAIULodReXd4joRo,2479
26
+ mesa/examples/advanced/sugarscape_g1mt/app.py,sha256=fg8ZXBoc4tPI9AfOLF0fgMk3Ey4H8xZu6tUaTwDp3Nk,1952
27
27
  mesa/examples/advanced/sugarscape_g1mt/model.py,sha256=s4n9SRaaMNY9CXFtry83BELZU69UJOCa4DMrw_eehr4,5763
28
28
  mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt,sha256=zZtGYciBPT4miZVnbVuoQ5TugTmGrbDWV9yb5KH6tnU,5000
29
29
  mesa/examples/advanced/sugarscape_g1mt/tests.py,sha256=UNahmZTgLquSqmoi_9GcE3JP0qBHjkrHFZ15NMm0ce8,2517
@@ -67,7 +67,7 @@ mesa/experimental/cell_space/cell.py,sha256=Dl0ek7W_vgAZOp6zXBvkwYTG7E36OQ-4zZ5b
67
67
  mesa/experimental/cell_space/cell_agent.py,sha256=LOTLKR2To9hU-igKsauA5sikS7k8wgwlt-Pi0C7lGU0,4262
68
68
  mesa/experimental/cell_space/cell_collection.py,sha256=VnD3I4bqnwgFxKSzTElYYJIBFJGdaEaNTggrbFKgPrc,4715
69
69
  mesa/experimental/cell_space/discrete_space.py,sha256=qNGez9SV4E83Outs_12suuS0ZtTHcwyv6ZB1xzTXUWk,3846
70
- mesa/experimental/cell_space/grid.py,sha256=d-1S2iXijGkoJ9yc271pB8iXlzsX13usJjcjevCs_rU,10432
70
+ mesa/experimental/cell_space/grid.py,sha256=VTPjqzryAX6I9EunuqSvZvYD2zHznlt6Ct7HXQ_7STQ,10426
71
71
  mesa/experimental/cell_space/network.py,sha256=ujN2dV1i9hcXh6H0s7gwTuPT6gh7BCaziOUYPCybQKk,1862
72
72
  mesa/experimental/cell_space/property_layer.py,sha256=HFpBWOjI7PFU_K8VDb_pl9h62MftCBWL7PUKQNT3Ke8,17379
73
73
  mesa/experimental/cell_space/voronoi.py,sha256=FXJD8ci81Jil3FaL7ZFNfMPGvXvg3uym5Ooo1ZqKSKs,10199
@@ -81,17 +81,16 @@ mesa/experimental/mesa_signals/__init__.py,sha256=QjG4FSKQl5ZSzV9ctiaB7UqYDR3FAR
81
81
  mesa/experimental/mesa_signals/mesa_signal.py,sha256=Vxo4gIV6a959MANL3RMANsGh0R9lkZTNO19XIYzvKSM,16860
82
82
  mesa/experimental/mesa_signals/observable_collections.py,sha256=rHEj6BYxLHFFGzSdoDKAdtzJ6y-IHHfcP3qEDJJsY6Y,3917
83
83
  mesa/experimental/mesa_signals/signals_util.py,sha256=fmq_FsIxsIvGjtmc4A9TGdBUtdliMHhEOpjRXivRDjA,1618
84
- mesa/visualization/__init__.py,sha256=YW-oHEOTjbtDKD_TylAMtVnt8mrsz1Fw7ifdc4WeHxA,743
85
- mesa/visualization/mpl_space_drawing.py,sha256=iqm1PYUUsmhUIraK8L9OTcTaDPDYQtlQCKtepREBA5c,20326
86
- mesa/visualization/solara_viz.py,sha256=ItExWMLjg7rHb5RGlZx99YsuPhmC4i0ZCaY1MYzgqZ4,20931
84
+ mesa/visualization/__init__.py,sha256=wnafHqOc7q-niu8HDOLwcmwuYu5BuvdjLTHmfpyXAoA,664
85
+ mesa/visualization/mpl_space_drawing.py,sha256=9byWxbKHVVT5GLmu-6U0qVFddThEag612JBGYmZiWps,22686
86
+ mesa/visualization/solara_viz.py,sha256=AoMVDdjMc99qCClAvsFXg-jBNPaDF5kfIhXa5Hd57MA,21052
87
87
  mesa/visualization/user_param.py,sha256=Dl2WOwLYLf0pfLpabCZtIdFRyKZrK6Qtc3utZx5GPYg,2139
88
88
  mesa/visualization/utils.py,sha256=lJHgRKF5BHLf72Tw3YpwyiWuRoIimaTKQ7xBCw_Rx3A,146
89
89
  mesa/visualization/components/__init__.py,sha256=Bq3nrPikcaIo9BSs0O3zptWVLlUmAkLo3s0mEmpH1RE,3022
90
- mesa/visualization/components/altair_components.py,sha256=wotpFFQgMY-ZR3lNVm_fRos-iDg0Wjnj6Tk67_7f1SQ,5847
90
+ mesa/visualization/components/altair_components.py,sha256=7OdVdZOvAx4p3j6iux6FqUYrWT0OADBIhFGf6EeGcb4,6071
91
91
  mesa/visualization/components/matplotlib_components.py,sha256=xQETaFyHIfmL_9JwrLIgubuIQ7-pp7TMoXT1WMmozus,5441
92
- mesa-3.1.3.dist-info/METADATA,sha256=4-LYctEQ5WYoTPl1jXxSOrIn6vfKV4lLTR_l7byd7E8,9970
93
- mesa-3.1.3.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
- mesa-3.1.3.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
95
- mesa-3.1.3.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
96
- mesa-3.1.3.dist-info/licenses/NOTICE,sha256=GbsWoK0QWv1JyZ_xer2s-jNilv0RtWl-0UrtlJANHPg,578
97
- mesa-3.1.3.dist-info/RECORD,,
92
+ mesa-3.1.4.dist-info/METADATA,sha256=NFoxMdQKJ_fLTMOskESPV6JZ1nfKJHzD2Vu4qxChx4o,10197
93
+ mesa-3.1.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
94
+ mesa-3.1.4.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
95
+ mesa-3.1.4.dist-info/licenses/NOTICE,sha256=GbsWoK0QWv1JyZ_xer2s-jNilv0RtWl-0UrtlJANHPg,578
96
+ mesa-3.1.4.dist-info/RECORD,,
@@ -1,2 +0,0 @@
1
- [console_scripts]
2
- mesa = mesa.main:cli
File without changes