Mesa 3.1.0.dev0__py3-none-any.whl → 3.1.1__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 (57) hide show
  1. mesa/__init__.py +3 -3
  2. mesa/agent.py +48 -0
  3. mesa/batchrunner.py +14 -1
  4. mesa/datacollection.py +1 -6
  5. mesa/examples/__init__.py +2 -2
  6. mesa/examples/advanced/epstein_civil_violence/app.py +5 -0
  7. mesa/examples/advanced/pd_grid/agents.py +2 -1
  8. mesa/examples/advanced/pd_grid/app.py +5 -0
  9. mesa/examples/advanced/pd_grid/model.py +3 -5
  10. mesa/examples/advanced/sugarscape_g1mt/agents.py +12 -65
  11. mesa/examples/advanced/sugarscape_g1mt/app.py +24 -19
  12. mesa/examples/advanced/sugarscape_g1mt/model.py +45 -52
  13. mesa/examples/advanced/wolf_sheep/agents.py +3 -1
  14. mesa/examples/advanced/wolf_sheep/model.py +17 -16
  15. mesa/examples/basic/boid_flockers/app.py +5 -0
  16. mesa/examples/basic/boltzmann_wealth_model/app.py +8 -5
  17. mesa/examples/basic/boltzmann_wealth_model/st_app.py +1 -1
  18. mesa/examples/basic/conways_game_of_life/app.py +5 -0
  19. mesa/examples/basic/conways_game_of_life/st_app.py +2 -2
  20. mesa/examples/basic/schelling/agents.py +11 -5
  21. mesa/examples/basic/schelling/app.py +6 -1
  22. mesa/examples/basic/virus_on_network/app.py +5 -0
  23. mesa/experimental/__init__.py +17 -10
  24. mesa/experimental/cell_space/__init__.py +19 -7
  25. mesa/experimental/cell_space/cell.py +22 -37
  26. mesa/experimental/cell_space/cell_agent.py +12 -1
  27. mesa/experimental/cell_space/cell_collection.py +18 -3
  28. mesa/experimental/cell_space/discrete_space.py +15 -64
  29. mesa/experimental/cell_space/grid.py +74 -4
  30. mesa/experimental/cell_space/network.py +13 -1
  31. mesa/experimental/cell_space/property_layer.py +444 -0
  32. mesa/experimental/cell_space/voronoi.py +13 -1
  33. mesa/experimental/devs/__init__.py +20 -2
  34. mesa/experimental/devs/eventlist.py +19 -1
  35. mesa/experimental/devs/simulator.py +24 -8
  36. mesa/experimental/mesa_signals/__init__.py +23 -0
  37. mesa/experimental/mesa_signals/mesa_signal.py +485 -0
  38. mesa/experimental/mesa_signals/observable_collections.py +133 -0
  39. mesa/experimental/mesa_signals/signals_util.py +52 -0
  40. mesa/mesa_logging.py +190 -0
  41. mesa/model.py +17 -23
  42. mesa/visualization/__init__.py +2 -2
  43. mesa/visualization/mpl_space_drawing.py +8 -5
  44. mesa/visualization/solara_viz.py +49 -11
  45. {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/METADATA +1 -1
  46. mesa-3.1.1.dist-info/RECORD +94 -0
  47. {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/WHEEL +1 -1
  48. mesa/experimental/UserParam.py +0 -67
  49. mesa/experimental/components/altair.py +0 -81
  50. mesa/experimental/components/matplotlib.py +0 -242
  51. mesa/experimental/devs/examples/epstein_civil_violence.py +0 -305
  52. mesa/experimental/devs/examples/wolf_sheep.py +0 -250
  53. mesa/experimental/solara_viz.py +0 -453
  54. mesa-3.1.0.dev0.dist-info/RECORD +0 -94
  55. {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/entry_points.txt +0 -0
  56. {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/licenses/LICENSE +0 -0
  57. {mesa-3.1.0.dev0.dist-info → mesa-3.1.1.dist-info}/licenses/NOTICE +0 -0
@@ -0,0 +1,444 @@
1
+ """Efficient storage and manipulation of cell properties across spaces.
2
+
3
+ PropertyLayers provide a way to associate properties with cells in a space efficiently.
4
+ The module includes:
5
+ - PropertyLayer class for managing grid-wide properties
6
+ - Property access descriptors for cells
7
+ - Batch operations for property modification
8
+ - Property-based cell selection
9
+ - Integration with numpy for efficient operations
10
+
11
+ This system separates property storage from cells themselves, enabling
12
+ fast bulk operations and sophisticated property-based behaviors while
13
+ maintaining an intuitive interface through cell attributes. Properties
14
+ can represent environmental factors, cell states, or any other grid-wide
15
+ attributes.
16
+ """
17
+
18
+ import warnings
19
+ from collections.abc import Callable, Sequence
20
+ from typing import Any, TypeVar
21
+
22
+ import numpy as np
23
+
24
+ from mesa.experimental.cell_space import Cell
25
+
26
+ Coordinate = Sequence[int]
27
+ T = TypeVar("T", bound=Cell)
28
+
29
+
30
+ class PropertyLayer:
31
+ """A class representing a layer of properties in a two-dimensional grid.
32
+
33
+ Each cell in the grid can store a value of a specified data type.
34
+
35
+ Attributes:
36
+ name: The name of the property layer.
37
+ dimensions: The width of the grid (number of columns).
38
+ data: A NumPy array representing the grid data.
39
+
40
+ """
41
+
42
+ # Fixme
43
+ # can't we simplify this a lot
44
+ # in essence, this is just a numpy array with a name and fixed dimensions
45
+ # all other functionality seems redundant to me?
46
+
47
+ @property
48
+ def data(self): # noqa: D102
49
+ return self._mesa_data
50
+
51
+ @data.setter
52
+ def data(self, value):
53
+ self.set_cells(value)
54
+
55
+ propertylayer_experimental_warning_given = False
56
+
57
+ def __init__(
58
+ self, name: str, dimensions: Sequence[int], default_value=0.0, dtype=float
59
+ ):
60
+ """Initializes a new PropertyLayer instance.
61
+
62
+ Args:
63
+ name: The name of the property layer.
64
+ dimensions: the dimensions of the property layer.
65
+ default_value: The default value to initialize each cell in the grid. Should ideally
66
+ be of the same type as specified by the dtype parameter.
67
+ dtype (data-type, optional): The desired data-type for the grid's elements. Default is float.
68
+
69
+ Notes:
70
+ A UserWarning is raised if the default_value is not of a type compatible with dtype.
71
+ The dtype parameter can accept both Python data types (like bool, int or float) and NumPy data types
72
+ (like np.int64 or np.float64).
73
+ """
74
+ self.name = name
75
+ self.dimensions = dimensions
76
+
77
+ # Check if the dtype is suitable for the data
78
+ if not isinstance(default_value, dtype):
79
+ warnings.warn(
80
+ f"Default value {default_value} ({type(default_value).__name__}) might not be best suitable with dtype={dtype.__name__}.",
81
+ UserWarning,
82
+ stacklevel=2,
83
+ )
84
+
85
+ # fixme why not initialize with empty?
86
+ self._mesa_data = np.full(self.dimensions, default_value, dtype=dtype)
87
+
88
+ if not self.__class__.propertylayer_experimental_warning_given:
89
+ warnings.warn(
90
+ "The property layer functionality and associated classes are experimental. It may be changed or removed in any and all future releases, including patch releases.\n"
91
+ "We would love to hear what you think about this new feature. If you have any thoughts, share them with us here: https://github.com/projectmesa/mesa/discussions/1932",
92
+ FutureWarning,
93
+ stacklevel=2,
94
+ )
95
+ self.__class__.propertylayer_experimental_warning_given = True
96
+
97
+ @classmethod
98
+ def from_data(cls, name: str, data: np.ndarray):
99
+ """Create a property layer from a NumPy array.
100
+
101
+ Args:
102
+ name: The name of the property layer.
103
+ data: A NumPy array representing the grid data.
104
+
105
+ """
106
+ layer = cls(
107
+ name,
108
+ data.shape,
109
+ default_value=data[*[0 for _ in range(len(data.shape))]],
110
+ dtype=data.dtype.type,
111
+ )
112
+ layer.set_cells(data)
113
+ return layer
114
+
115
+ def set_cells(self, value, condition: Callable | None = None):
116
+ """Perform a batch update either on the entire grid or conditionally, in-place.
117
+
118
+ Args:
119
+ value: The value to be used for the update.
120
+ condition: (Optional) A callable that returns a boolean array when applied to the data.
121
+ """
122
+ if condition is None:
123
+ np.copyto(self._mesa_data, value) # In-place update
124
+ else:
125
+ vectorized_condition = np.vectorize(condition)
126
+ condition_result = vectorized_condition(self._mesa_data)
127
+ np.copyto(self._mesa_data, value, where=condition_result)
128
+
129
+ def modify_cells(
130
+ self,
131
+ operation: Callable,
132
+ value=None,
133
+ condition: Callable | None = None,
134
+ ):
135
+ """Modify cells using an operation, which can be a lambda function or a NumPy ufunc.
136
+
137
+ If a NumPy ufunc is used, an additional value should be provided.
138
+
139
+ Args:
140
+ operation: A function to apply. Can be a lambda function or a NumPy ufunc.
141
+ value: The value to be used if the operation is a NumPy ufunc. Ignored for lambda functions.
142
+ condition: (Optional) A callable that returns a boolean array when applied to the data.
143
+ """
144
+ condition_array = np.ones_like(
145
+ self._mesa_data, dtype=bool
146
+ ) # Default condition (all cells)
147
+ if condition is not None:
148
+ vectorized_condition = np.vectorize(condition)
149
+ condition_array = vectorized_condition(self._mesa_data)
150
+
151
+ # Check if the operation is a lambda function or a NumPy ufunc
152
+ if isinstance(operation, np.ufunc):
153
+ if ufunc_requires_additional_input(operation):
154
+ if value is None:
155
+ raise ValueError("This ufunc requires an additional input value.")
156
+ modified_data = operation(self._mesa_data, value)
157
+ else:
158
+ modified_data = operation(self._mesa_data)
159
+ else:
160
+ # Vectorize non-ufunc operations
161
+ vectorized_operation = np.vectorize(operation)
162
+ modified_data = vectorized_operation(self._mesa_data)
163
+
164
+ self._mesa_data = np.where(condition_array, modified_data, self._mesa_data)
165
+
166
+ def select_cells(self, condition: Callable, return_list=True):
167
+ """Find cells that meet a specified condition using NumPy's boolean indexing, in-place.
168
+
169
+ Args:
170
+ condition: A callable that returns a boolean array when applied to the data.
171
+ return_list: (Optional) If True, return a list of (x, y) tuples. Otherwise, return a boolean array.
172
+
173
+ Returns:
174
+ A list of (x, y) tuples or a boolean array.
175
+ """
176
+ # fixme: consider splitting into two separate functions
177
+ # select_cells_boolean
178
+ # select_cells_index
179
+
180
+ condition_array = condition(self._mesa_data)
181
+ if return_list:
182
+ return list(zip(*np.where(condition_array)))
183
+ else:
184
+ return condition_array
185
+
186
+ def aggregate(self, operation: Callable):
187
+ """Perform an aggregate operation (e.g., sum, mean) on a property across all cells.
188
+
189
+ Args:
190
+ operation: A function to apply. Can be a lambda function or a NumPy ufunc.
191
+ """
192
+ return operation(self._mesa_data)
193
+
194
+
195
+ class HasPropertyLayers:
196
+ """Mixin-like class to add property layer functionality to Grids.
197
+
198
+ Property layers can be added to a grid using create_property_layer or add_property_layer. Once created, property
199
+ layers can be accessed as attributes if the name used for the layer is a valid python identifier.
200
+
201
+ """
202
+
203
+ # fixme is there a way to indicate that a mixin only works with specific classes?
204
+ def __init__(self, *args, **kwargs):
205
+ """Initialize a HasPropertyLayers instance."""
206
+ super().__init__(*args, **kwargs)
207
+ self._mesa_property_layers = {}
208
+
209
+ def create_property_layer(
210
+ self,
211
+ name: str,
212
+ default_value=0.0,
213
+ dtype=float,
214
+ ):
215
+ """Add a property layer to the grid.
216
+
217
+ Args:
218
+ name: The name of the property layer.
219
+ default_value: The default value of the property layer.
220
+ dtype: The data type of the property layer.
221
+
222
+ Returns:
223
+ Property layer instance.
224
+
225
+ """
226
+ layer = PropertyLayer(
227
+ name, self.dimensions, default_value=default_value, dtype=dtype
228
+ )
229
+ self.add_property_layer(layer)
230
+ return layer
231
+
232
+ def add_property_layer(self, layer: PropertyLayer):
233
+ """Add a predefined property layer to the grid.
234
+
235
+ Args:
236
+ layer: The property layer to add.
237
+
238
+ Raises:
239
+ ValueError: If the dimensions of the layer and the grid are not the same.
240
+
241
+ """
242
+ if layer.dimensions != self.dimensions:
243
+ raise ValueError(
244
+ "Dimensions of property layer do not match the dimensions of the grid"
245
+ )
246
+ if layer.name in self._mesa_property_layers:
247
+ raise ValueError(f"Property layer {layer.name} already exists.")
248
+ if (
249
+ layer.name in self.cell_klass.__slots__
250
+ or layer.name in self.cell_klass.__dict__
251
+ ):
252
+ raise ValueError(
253
+ f"Property layer {layer.name} clashes with existing attribute in {self.cell_klass.__name__}"
254
+ )
255
+
256
+ self._mesa_property_layers[layer.name] = layer
257
+ setattr(self.cell_klass, layer.name, PropertyDescriptor(layer))
258
+ self.cell_klass._mesa_properties.add(layer.name)
259
+
260
+ def remove_property_layer(self, property_name: str):
261
+ """Remove a property layer from the grid.
262
+
263
+ Args:
264
+ property_name: the name of the property layer to remove
265
+ remove_from_cells: whether to remove the property layer from all cells (default: True)
266
+ """
267
+ del self._mesa_property_layers[property_name]
268
+ delattr(self.cell_klass, property_name)
269
+ self.cell_klass._mesa_properties.remove(property_name)
270
+
271
+ def set_property(
272
+ self, property_name: str, value, condition: Callable[[T], bool] | None = None
273
+ ):
274
+ """Set the value of a property for all cells in the grid.
275
+
276
+ Args:
277
+ property_name: the name of the property to set
278
+ value: the value to set
279
+ condition: a function that takes a cell and returns a boolean
280
+ """
281
+ self._mesa_property_layers[property_name].set_cells(value, condition)
282
+
283
+ def modify_properties(
284
+ self,
285
+ property_name: str,
286
+ operation: Callable,
287
+ value: Any = None,
288
+ condition: Callable[[T], bool] | None = None,
289
+ ):
290
+ """Modify the values of a specific property for all cells in the grid.
291
+
292
+ Args:
293
+ property_name: the name of the property to modify
294
+ operation: the operation to perform
295
+ value: the value to use in the operation
296
+ condition: a function that takes a cell and returns a boolean (used to filter cells)
297
+ """
298
+ self._mesa_property_layers[property_name].modify_cells(
299
+ operation, value, condition
300
+ )
301
+
302
+ def get_neighborhood_mask(
303
+ self, coordinate: Coordinate, include_center: bool = True, radius: int = 1
304
+ ) -> np.ndarray:
305
+ """Generate a boolean mask representing the neighborhood.
306
+
307
+ Args:
308
+ coordinate: Center of the neighborhood.
309
+ include_center: Include the central cell in the neighborhood.
310
+ radius: The radius of the neighborhood.
311
+
312
+ Returns:
313
+ np.ndarray: A boolean mask representing the neighborhood.
314
+ """
315
+ cell = self._cells[coordinate]
316
+ neighborhood = cell.get_neighborhood(
317
+ include_center=include_center, radius=radius
318
+ )
319
+ mask = np.zeros(self.dimensions, dtype=bool)
320
+
321
+ # Convert the neighborhood list to a NumPy array and use advanced indexing
322
+ coords = np.array([c.coordinate for c in neighborhood])
323
+ indices = [coords[:, i] for i in range(coords.shape[1])]
324
+ mask[*indices] = True
325
+ return mask
326
+
327
+ def select_cells(
328
+ self,
329
+ conditions: dict | None = None,
330
+ extreme_values: dict | None = None,
331
+ masks: np.ndarray | list[np.ndarray] = None,
332
+ only_empty: bool = False,
333
+ return_list: bool = True,
334
+ ) -> list[Coordinate] | np.ndarray:
335
+ """Select cells based on property conditions, extreme values, and/or masks, with an option to only select empty cells.
336
+
337
+ Args:
338
+ conditions (dict): A dictionary where keys are property names and values are callables that return a boolean when applied.
339
+ extreme_values (dict): A dictionary where keys are property names and values are either 'highest' or 'lowest'.
340
+ masks (np.ndarray | list[np.ndarray], optional): A mask or list of masks to restrict the selection.
341
+ only_empty (bool, optional): If True, only select cells that are empty. Default is False.
342
+ return_list (bool, optional): If True, return a list of coordinates, otherwise return a mask.
343
+
344
+ Returns:
345
+ Union[list[Coordinate], np.ndarray]: Coordinates where conditions are satisfied or the combined mask.
346
+ """
347
+ # fixme: consider splitting into two separate functions
348
+ # select_cells_boolean
349
+ # select_cells_index
350
+ # also we might want to change the naming to avoid classes with PropertyLayer
351
+
352
+ # Initialize the combined mask
353
+ combined_mask = np.ones(self.dimensions, dtype=bool)
354
+
355
+ # Apply the masks
356
+ if masks is not None:
357
+ if isinstance(masks, list):
358
+ for mask in masks:
359
+ combined_mask = np.logical_and(combined_mask, mask)
360
+ else:
361
+ combined_mask = np.logical_and(combined_mask, masks)
362
+
363
+ # Apply the empty mask if only_empty is True
364
+ if only_empty:
365
+ combined_mask = np.logical_and(
366
+ combined_mask, self._mesa_property_layers["empty"]
367
+ )
368
+
369
+ # Apply conditions
370
+ if conditions:
371
+ for prop_name, condition in conditions.items():
372
+ prop_layer = self._mesa_property_layers[prop_name].data
373
+ prop_mask = condition(prop_layer)
374
+ combined_mask = np.logical_and(combined_mask, prop_mask)
375
+
376
+ # Apply extreme values
377
+ if extreme_values:
378
+ for property_name, mode in extreme_values.items():
379
+ prop_values = self._mesa_property_layers[property_name].data
380
+
381
+ # Create a masked array using the combined_mask
382
+ masked_values = np.ma.masked_array(prop_values, mask=~combined_mask)
383
+
384
+ if mode == "highest":
385
+ target_value = masked_values.max()
386
+ elif mode == "lowest":
387
+ target_value = masked_values.min()
388
+ else:
389
+ raise ValueError(
390
+ f"Invalid mode {mode}. Choose from 'highest' or 'lowest'."
391
+ )
392
+
393
+ extreme_value_mask = prop_values == target_value
394
+ combined_mask = np.logical_and(combined_mask, extreme_value_mask)
395
+
396
+ # Generate output
397
+ if return_list:
398
+ selected_cells = list(zip(*np.where(combined_mask)))
399
+ return selected_cells
400
+ else:
401
+ return combined_mask
402
+
403
+ def __getattr__(self, name: str) -> Any: # noqa: D105
404
+ try:
405
+ return self._mesa_property_layers[name]
406
+ except KeyError as e:
407
+ raise AttributeError(
408
+ f"'{type(self).__name__}' object has no property layer called '{name}'"
409
+ ) from e
410
+
411
+ def __setattr__(self, key, value): # noqa: D105
412
+ # fixme
413
+ # this might be done more elegantly, the main problem is that _mesa_property_layers must already be defined to avoid infinite recursion errors from happening
414
+ # also, this protection only works if the attribute is added after the layer, not the other way around
415
+ try:
416
+ layers = self.__dict__["_mesa_property_layers"]
417
+ except KeyError:
418
+ super().__setattr__(key, value)
419
+ else:
420
+ if key in layers:
421
+ raise AttributeError(
422
+ f"'{type(self).__name__}' object already has a property layer with name '{key}'"
423
+ )
424
+ else:
425
+ super().__setattr__(key, value)
426
+
427
+
428
+ class PropertyDescriptor:
429
+ """Descriptor for giving cells attribute like access to values defined in property layers."""
430
+
431
+ def __init__(self, property_layer: PropertyLayer): # noqa: D107
432
+ self.layer: PropertyLayer = property_layer
433
+
434
+ def __get__(self, instance: Cell, owner): # noqa: D105
435
+ return self.layer.data[instance.coordinate]
436
+
437
+ def __set__(self, instance: Cell, value): # noqa: D105
438
+ self.layer.data[instance.coordinate] = value
439
+
440
+
441
+ def ufunc_requires_additional_input(ufunc): # noqa: D103
442
+ # NumPy ufuncs have a 'nargs' attribute indicating the number of input arguments
443
+ # For binary ufuncs (like np.add), nargs is 2
444
+ return ufunc.nargs > 1
@@ -1,4 +1,16 @@
1
- """Support for Voronoi meshed grids."""
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
+ """
2
14
 
3
15
  from collections.abc import Sequence
4
16
  from itertools import combinations
@@ -1,6 +1,24 @@
1
- """Support for event scheduling."""
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
+ """
2
20
 
3
21
  from .eventlist import Priority, SimulationEvent
4
22
  from .simulator import ABMSimulator, DEVSimulator
5
23
 
6
- __all__ = ["ABMSimulator", "DEVSimulator", "SimulationEvent", "Priority"]
24
+ __all__ = ["ABMSimulator", "DEVSimulator", "Priority", "SimulationEvent"]
@@ -1,4 +1,22 @@
1
- """Eventlist which is at the core of event scheduling."""
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
+ """
2
20
 
3
21
  from __future__ import annotations
4
22
 
@@ -1,20 +1,36 @@
1
- """Provides several simulator classes.
2
-
3
- A Simulator is responsible for executing a simulation model. It controls time advancement and enables event scheduling.
4
-
5
-
1
+ """Simulator implementations for different time advancement approaches in Mesa.
2
+
3
+ This module provides simulator classes that control how simulation time advances and how
4
+ events are executed. It supports both discrete-time and continuous-time simulations through
5
+ three main classes:
6
+
7
+ - Simulator: Base class defining the core simulation control interface
8
+ - ABMSimulator: A simulator for agent-based models that combines fixed time steps with
9
+ event scheduling. Uses integer time units and automatically schedules model.step()
10
+ - DEVSimulator: A pure discrete event simulator using floating-point time units for
11
+ continuous time simulation
12
+
13
+ Key features:
14
+ - Flexible time units (integer or float)
15
+ - Event scheduling using absolute or relative times
16
+ - Priority-based event execution
17
+ - Support for running simulations for specific durations or until specific end times
18
+
19
+ The simulators enable Mesa models to use traditional time-step based approaches, pure
20
+ event-driven approaches, or hybrid combinations of both.
6
21
  """
7
22
 
8
23
  from __future__ import annotations
9
24
 
10
25
  import numbers
11
26
  from collections.abc import Callable
12
- from typing import Any
13
-
14
- from mesa import Model
27
+ from typing import TYPE_CHECKING, Any
15
28
 
16
29
  from .eventlist import EventList, Priority, SimulationEvent
17
30
 
31
+ if TYPE_CHECKING:
32
+ from mesa import Model
33
+
18
34
 
19
35
  class Simulator:
20
36
  """The Simulator controls the time advancement of the model.
@@ -0,0 +1,23 @@
1
+ """Mesa Signals (Observables) package that provides reactive programming capabilities.
2
+
3
+ This package enables tracking changes to properties and state in Mesa models through a
4
+ reactive programming paradigm. It enables building models where components can observe
5
+ and react to changes in other components' state.
6
+
7
+ The package provides the core Observable classes and utilities needed to implement
8
+ reactive patterns in agent-based models. This includes capabilities for watching changes
9
+ to attributes, computing derived values, and managing collections that emit signals
10
+ when modified.
11
+ """
12
+
13
+ from .mesa_signal import All, Computable, Computed, HasObservables, Observable
14
+ from .observable_collections import ObservableList
15
+
16
+ __all__ = [
17
+ "All",
18
+ "Computable",
19
+ "Computed",
20
+ "HasObservables",
21
+ "Observable",
22
+ "ObservableList",
23
+ ]