Mesa 3.0.0a4__py3-none-any.whl → 3.0.0b0__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 (41) hide show
  1. mesa/__init__.py +2 -3
  2. mesa/agent.py +116 -85
  3. mesa/batchrunner.py +22 -23
  4. mesa/cookiecutter-mesa/hooks/post_gen_project.py +2 -0
  5. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +1 -0
  6. mesa/datacollection.py +138 -27
  7. mesa/experimental/UserParam.py +17 -6
  8. mesa/experimental/__init__.py +2 -0
  9. mesa/experimental/cell_space/__init__.py +14 -1
  10. mesa/experimental/cell_space/cell.py +84 -23
  11. mesa/experimental/cell_space/cell_agent.py +117 -21
  12. mesa/experimental/cell_space/cell_collection.py +54 -17
  13. mesa/experimental/cell_space/discrete_space.py +79 -7
  14. mesa/experimental/cell_space/grid.py +19 -8
  15. mesa/experimental/cell_space/network.py +9 -7
  16. mesa/experimental/cell_space/voronoi.py +26 -33
  17. mesa/experimental/components/altair.py +10 -0
  18. mesa/experimental/components/matplotlib.py +18 -0
  19. mesa/experimental/devs/__init__.py +2 -0
  20. mesa/experimental/devs/eventlist.py +36 -15
  21. mesa/experimental/devs/examples/epstein_civil_violence.py +65 -29
  22. mesa/experimental/devs/examples/wolf_sheep.py +40 -35
  23. mesa/experimental/devs/simulator.py +55 -15
  24. mesa/experimental/solara_viz.py +10 -19
  25. mesa/main.py +6 -4
  26. mesa/model.py +51 -54
  27. mesa/space.py +145 -120
  28. mesa/time.py +57 -67
  29. mesa/visualization/UserParam.py +19 -6
  30. mesa/visualization/__init__.py +3 -2
  31. mesa/visualization/components/altair.py +4 -2
  32. mesa/visualization/components/matplotlib.py +176 -85
  33. mesa/visualization/solara_viz.py +167 -84
  34. mesa/visualization/utils.py +3 -1
  35. {mesa-3.0.0a4.dist-info → mesa-3.0.0b0.dist-info}/METADATA +55 -13
  36. mesa-3.0.0b0.dist-info/RECORD +45 -0
  37. mesa-3.0.0b0.dist-info/licenses/LICENSE +202 -0
  38. mesa-3.0.0a4.dist-info/licenses/LICENSE → mesa-3.0.0b0.dist-info/licenses/NOTICE +2 -2
  39. mesa-3.0.0a4.dist-info/RECORD +0 -44
  40. {mesa-3.0.0a4.dist-info → mesa-3.0.0b0.dist-info}/WHEEL +0 -0
  41. {mesa-3.0.0a4.dist-info → mesa-3.0.0b0.dist-info}/entry_points.txt +0 -0
@@ -1,3 +1,10 @@
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
+
6
+ """
7
+
1
8
  from __future__ import annotations
2
9
 
3
10
  import numbers
@@ -27,6 +34,12 @@ class Simulator:
27
34
  # TODO: add experimentation support
28
35
 
29
36
  def __init__(self, time_unit: type, start_time: int | float):
37
+ """Initialize a Simulator instance.
38
+
39
+ Args:
40
+ time_unit: type of the smulaiton time
41
+ start_time: the starttime of the simulator
42
+ """
30
43
  # should model run in a separate thread,
31
44
  # and we can then interact with start, stop, run_until, and step?
32
45
  self.event_list = EventList()
@@ -36,10 +49,10 @@ class Simulator:
36
49
  self.time = self.start_time
37
50
  self.model = None
38
51
 
39
- def check_time_unit(self, time: int | float) -> bool: ...
52
+ def check_time_unit(self, time: int | float) -> bool: ... # noqa: D102
40
53
 
41
54
  def setup(self, model: Model) -> None:
42
- """Set up the simulator with the model to simulate
55
+ """Set up the simulator with the model to simulate.
43
56
 
44
57
  Args:
45
58
  model (Model): The model to simulate
@@ -49,12 +62,13 @@ class Simulator:
49
62
  self.model = model
50
63
 
51
64
  def reset(self):
52
- """Reset the simulator by clearing the event list and removing the model to simulate"""
65
+ """Reset the simulator by clearing the event list and removing the model to simulate."""
53
66
  self.event_list.clear()
54
67
  self.model = None
55
68
  self.time = self.start_time
56
69
 
57
70
  def run_until(self, end_time: int | float) -> None:
71
+ """Run the simulator until the end time."""
58
72
  while True:
59
73
  try:
60
74
  event = self.event_list.pop_event()
@@ -71,7 +85,7 @@ class Simulator:
71
85
  break
72
86
 
73
87
  def run_for(self, time_delta: int | float):
74
- """run the simulator for the specified time delta
88
+ """Run the simulator for the specified time delta.
75
89
 
76
90
  Args:
77
91
  time_delta (float| int): The time delta. The simulator is run from the current time to the current time
@@ -88,7 +102,7 @@ class Simulator:
88
102
  function_args: list[Any] | None = None,
89
103
  function_kwargs: dict[str, Any] | None = None,
90
104
  ) -> SimulationEvent:
91
- """Schedule event for the current time instant
105
+ """Schedule event for the current time instant.
92
106
 
93
107
  Args:
94
108
  function (Callable): The callable to execute for this event
@@ -116,7 +130,7 @@ class Simulator:
116
130
  function_args: list[Any] | None = None,
117
131
  function_kwargs: dict[str, Any] | None = None,
118
132
  ) -> SimulationEvent:
119
- """Schedule event for the specified time instant
133
+ """Schedule event for the specified time instant.
120
134
 
121
135
  Args:
122
136
  function (Callable): The callable to execute for this event
@@ -150,7 +164,7 @@ class Simulator:
150
164
  function_args: list[Any] | None = None,
151
165
  function_kwargs: dict[str, Any] | None = None,
152
166
  ) -> SimulationEvent:
153
- """Schedule event for the current time plus the time delta
167
+ """Schedule event for the current time plus the time delta.
154
168
 
155
169
  Args:
156
170
  function (Callable): The callable to execute for this event
@@ -174,13 +188,12 @@ class Simulator:
174
188
  return event
175
189
 
176
190
  def cancel_event(self, event: SimulationEvent) -> None:
177
- """remove the event from the event list
191
+ """Remove the event from the event list.
178
192
 
179
193
  Args:
180
194
  event (SimulationEvent): The simulation event to remove
181
195
 
182
196
  """
183
-
184
197
  self.event_list.remove(event)
185
198
 
186
199
  def _schedule_event(self, event: SimulationEvent):
@@ -204,13 +217,29 @@ class ABMSimulator(Simulator):
204
217
  """
205
218
 
206
219
  def __init__(self):
220
+ """Initialize a ABM simulator."""
207
221
  super().__init__(int, 0)
208
222
 
209
223
  def setup(self, model):
224
+ """Set up the simulator with the model to simulate.
225
+
226
+ Args:
227
+ model (Model): The model to simulate
228
+
229
+ """
210
230
  super().setup(model)
211
231
  self.schedule_event_now(self.model.step, priority=Priority.HIGH)
212
232
 
213
233
  def check_time_unit(self, time) -> bool:
234
+ """Check whether the time is of the correct unit.
235
+
236
+ Args:
237
+ time (int | float): the time
238
+
239
+ Returns:
240
+ bool: whether the time is of the correct unit
241
+
242
+ """
214
243
  if isinstance(time, int):
215
244
  return True
216
245
  if isinstance(time, float):
@@ -225,9 +254,9 @@ class ABMSimulator(Simulator):
225
254
  function_args: list[Any] | None = None,
226
255
  function_kwargs: dict[str, Any] | None = None,
227
256
  ) -> SimulationEvent:
228
- """Schedule a SimulationEvent for the next tick
257
+ """Schedule a SimulationEvent for the next tick.
229
258
 
230
- Args
259
+ Args:
231
260
  function (Callable): the callable to execute
232
261
  priority (Priority): the priority of the event
233
262
  function_args (List[Any]): List of arguments to pass to the callable
@@ -243,7 +272,7 @@ class ABMSimulator(Simulator):
243
272
  )
244
273
 
245
274
  def run_until(self, end_time: int) -> None:
246
- """run the simulator up to and included the specified end time
275
+ """Run the simulator up to and included the specified end time.
247
276
 
248
277
  Args:
249
278
  end_time (float| int): The end_time delta. The simulator is until the specified end time
@@ -270,7 +299,7 @@ class ABMSimulator(Simulator):
270
299
  break
271
300
 
272
301
  def run_for(self, time_delta: int):
273
- """run the simulator for the specified time delta
302
+ """Run the simulator for the specified time delta.
274
303
 
275
304
  Args:
276
305
  time_delta (float| int): The time delta. The simulator is run from the current time to the current time
@@ -282,13 +311,24 @@ class ABMSimulator(Simulator):
282
311
 
283
312
 
284
313
  class DEVSimulator(Simulator):
285
- """A simulator where the unit of time is a float. Can be used for full-blown discrete event simulating using
286
- event scheduling.
314
+ """A simulator where the unit of time is a float.
315
+
316
+ Can be used for full-blown discrete event simulating using event scheduling.
287
317
 
288
318
  """
289
319
 
290
320
  def __init__(self):
321
+ """Initialize a DEVS simulator."""
291
322
  super().__init__(float, 0.0)
292
323
 
293
324
  def check_time_unit(self, time) -> bool:
325
+ """Check whether the time is of the correct unit.
326
+
327
+ Args:
328
+ time (float): the time
329
+
330
+ Returns:
331
+ bool: whether the time is of the correct unit
332
+
333
+ """
294
334
  return isinstance(time, numbers.Number)
@@ -1,5 +1,4 @@
1
- """
2
- Mesa visualization module for creating interactive model visualizations.
1
+ """Mesa visualization module for creating interactive model visualizations.
3
2
 
4
3
  This module provides components to create browser- and Jupyter notebook-based visualizations of
5
4
  Mesa models, allowing users to watch models run step-by-step and interact with model parameters.
@@ -39,8 +38,7 @@ from mesa.experimental.UserParam import Slider
39
38
  def Card(
40
39
  model, measures, agent_portrayal, space_drawer, dependencies, color, layout_type
41
40
  ):
42
- """
43
- Create a card component for visualizing model space or measures.
41
+ """Create a card component for visualizing model space or measures.
44
42
 
45
43
  Args:
46
44
  model: The Mesa model instance
@@ -95,8 +93,7 @@ def SolaraViz(
95
93
  play_interval=150,
96
94
  seed=None,
97
95
  ):
98
- """
99
- Initialize a component to visualize a model.
96
+ """Initialize a component to visualize a model.
100
97
 
101
98
  Args:
102
99
  model_class: Class of the model to instantiate
@@ -212,8 +209,7 @@ JupyterViz = SolaraViz
212
209
 
213
210
  @solara.component
214
211
  def ModelController(model, play_interval, current_step, reset_counter):
215
- """
216
- Create controls for model execution (step, play, pause, reset).
212
+ """Create controls for model execution (step, play, pause, reset).
217
213
 
218
214
  Args:
219
215
  model: The model being visualized
@@ -315,8 +311,7 @@ def ModelController(model, play_interval, current_step, reset_counter):
315
311
 
316
312
 
317
313
  def split_model_params(model_params):
318
- """
319
- Split model parameters into user-adjustable and fixed parameters.
314
+ """Split model parameters into user-adjustable and fixed parameters.
320
315
 
321
316
  Args:
322
317
  model_params: Dictionary of all model parameters
@@ -335,8 +330,7 @@ def split_model_params(model_params):
335
330
 
336
331
 
337
332
  def check_param_is_fixed(param):
338
- """
339
- Check if a parameter is fixed (not user-adjustable).
333
+ """Check if a parameter is fixed (not user-adjustable).
340
334
 
341
335
  Args:
342
336
  param: Parameter to check
@@ -354,8 +348,8 @@ def check_param_is_fixed(param):
354
348
 
355
349
  @solara.component
356
350
  def UserInputs(user_params, on_change=None):
357
- """
358
- Initialize user inputs for configurable model parameters.
351
+ """Initialize user inputs for configurable model parameters.
352
+
359
353
  Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,
360
354
  :class:`solara.Select`, and :class:`solara.Checkbox`.
361
355
 
@@ -364,7 +358,6 @@ def UserInputs(user_params, on_change=None):
364
358
  min and max values, and other fields specific to the input type.
365
359
  on_change: Function to be called with (name, value) when the value of an input changes.
366
360
  """
367
-
368
361
  for name, options in user_params.items():
369
362
 
370
363
  def change_handler(value, name=name):
@@ -423,8 +416,7 @@ def UserInputs(user_params, on_change=None):
423
416
 
424
417
 
425
418
  def make_text(renderer):
426
- """
427
- Create a function that renders text using Markdown.
419
+ """Create a function that renders text using Markdown.
428
420
 
429
421
  Args:
430
422
  renderer: Function that takes a model and returns a string
@@ -440,8 +432,7 @@ def make_text(renderer):
440
432
 
441
433
 
442
434
  def make_initial_grid_layout(layout_types):
443
- """
444
- Create an initial grid layout for visualization components.
435
+ """Create an initial grid layout for visualization components.
445
436
 
446
437
  Args:
447
438
  layout_types: List of layout types (Space or Measure)
mesa/main.py CHANGED
@@ -1,3 +1,5 @@
1
+ """main module for running mesa models with a server."""
2
+
1
3
  import os
2
4
  import sys
3
5
  from pathlib import Path
@@ -19,13 +21,13 @@ CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]}
19
21
 
20
22
  @click.group(context_settings=CONTEXT_SETTINGS)
21
23
  def cli():
22
- "Manage Mesa projects"
24
+ """Manage Mesa projects."""
23
25
 
24
26
 
25
27
  @cli.command()
26
28
  @click.argument("project", type=PROJECT_PATH, default=".")
27
29
  def runserver(project):
28
- """Run mesa project PROJECT
30
+ """Run mesa project PROJECT.
29
31
 
30
32
  PROJECT is the path to the directory containing `run.py`, or the current
31
33
  directory if not specified.
@@ -45,7 +47,7 @@ def runserver(project):
45
47
  "--no-input", is_flag=True, help="Do not prompt user for custom mesa model input."
46
48
  )
47
49
  def startproject(no_input):
48
- """Create a new mesa project"""
50
+ """Create a new mesa project."""
49
51
  args = ["cookiecutter", COOKIECUTTER_PATH]
50
52
  if no_input:
51
53
  args.append("--no-input")
@@ -54,7 +56,7 @@ def startproject(no_input):
54
56
 
55
57
  @click.command()
56
58
  def version():
57
- """Show the version of mesa"""
59
+ """Show the version of mesa."""
58
60
  print(f"mesa {__version__}")
59
61
 
60
62
 
mesa/model.py CHANGED
@@ -1,5 +1,4 @@
1
- """
2
- The model class for Mesa framework.
1
+ """The model class for Mesa framework.
3
2
 
4
3
  Core Objects: Model
5
4
  """
@@ -28,24 +27,8 @@ class Model:
28
27
  Attributes:
29
28
  running: A boolean indicating if the model should continue running.
30
29
  schedule: An object to manage the order and execution of agent steps.
31
-
32
- Properties:
33
- agents: An AgentSet containing all agents in the model
34
- agent_types: A list of different agent types present in the model.
35
- agents_by_type: A dictionary where the keys are agent types and the values are the corresponding AgentSets.
36
- steps: An integer representing the number of steps the model has taken.
37
- It increases automatically at the start of each step() call.
38
-
39
- Methods:
40
- get_agents_of_type: Returns an AgentSet of agents of the specified type.
41
- Deprecated: Use agents_by_type[agenttype] instead.
42
- run_model: Runs the model's simulation until a defined end condition is reached.
43
- step: Executes a single step of the model's simulation process.
44
- next_id: Generates and returns the next unique identifier for an agent.
45
- reset_randomizer: Resets the model's random number generator with a new or existing seed.
46
- initialize_data_collector: Sets up the data collector for the model, requiring an initialized scheduler and agents.
47
- register_agent : register an agent with the model
48
- deregister_agent : remove an agent from the model
30
+ steps: the number of times `model.step()` has been called.
31
+ random: a seeded random number generator.
49
32
 
50
33
  Notes:
51
34
  Model.agents returns the AgentSet containing all agents registered with the model. Changing
@@ -54,29 +37,30 @@ class Model:
54
37
 
55
38
  """
56
39
 
57
- def __new__(cls, *args: Any, **kwargs: Any) -> Any:
58
- """Create a new model object and instantiate its RNG automatically."""
59
- obj = object.__new__(cls)
60
- obj._seed = kwargs.get("seed")
61
- if obj._seed is None:
62
- # We explicitly specify the seed here so that we know its value in
63
- # advance.
64
- obj._seed = random.random()
65
- obj.random = random.Random(obj._seed)
66
- return obj
67
-
68
- def __init__(self, *args: Any, **kwargs: Any) -> None:
69
- """Create a new model. Overload this method with the actual code to
70
- start the model. Always start with super().__init__() to initialize the
71
- model object properly.
72
- """
40
+ def __init__(self, *args: Any, seed: float | None = None, **kwargs: Any) -> None:
41
+ """Create a new model.
42
+
43
+ Overload this method with the actual code to initialize the model. Always start with super().__init__()
44
+ to initialize the model object properly.
73
45
 
46
+ Args:
47
+ args: arguments to pass onto super
48
+ seed: the seed for the random number generator
49
+ kwargs: keyword arguments to pass onto super
50
+ """
51
+ super().__init__(*args, **kwargs)
74
52
  self.running = True
75
- self.schedule = None
76
53
  self.steps: int = 0
77
54
 
78
55
  self._setup_agent_registration()
79
56
 
57
+ self._seed = seed
58
+ if self._seed is None:
59
+ # We explicitly specify the seed here so that we know its value in
60
+ # advance.
61
+ self._seed = random.random()
62
+ self.random = random.Random(self._seed)
63
+
80
64
  # Wrap the user-defined step method
81
65
  self._user_step = self.step
82
66
  self.step = self._wrapped_step
@@ -88,7 +72,7 @@ class Model:
88
72
  # Call the original user-defined step method
89
73
  self._user_step(*args, **kwargs)
90
74
 
91
- def next_id(self) -> int:
75
+ def next_id(self) -> int: # noqa: D102
92
76
  warnings.warn(
93
77
  "using model.next_id() is deprecated. Agents track their unique ID automatically",
94
78
  DeprecationWarning,
@@ -130,7 +114,7 @@ class Model:
130
114
  return self.agents_by_type[agenttype]
131
115
 
132
116
  def _setup_agent_registration(self):
133
- """helper method to initialize the agent registration datastructures"""
117
+ """Helper method to initialize the agent registration datastructures."""
134
118
  self._agents = {} # the hard references to all agents in the model
135
119
  self._agents_by_type: dict[
136
120
  type[Agent], AgentSet
@@ -138,7 +122,7 @@ class Model:
138
122
  self._all_agents = AgentSet([], self) # an agenset with all agents
139
123
 
140
124
  def register_agent(self, agent):
141
- """Register the agent with the model
125
+ """Register the agent with the model.
142
126
 
143
127
  Args:
144
128
  agent: The agent to register.
@@ -175,10 +159,13 @@ class Model:
175
159
  self._all_agents.add(agent)
176
160
 
177
161
  def deregister_agent(self, agent):
178
- """Deregister the agent with the model
162
+ """Deregister the agent with the model.
163
+
164
+ Args:
165
+ agent: The agent to deregister.
179
166
 
180
- Notes::
181
- This method is called automatically by ``Agent.remove``
167
+ Notes:
168
+ This method is called automatically by ``Agent.remove``
182
169
 
183
170
  """
184
171
  del self._agents[agent]
@@ -186,8 +173,9 @@ class Model:
186
173
  self._all_agents.remove(agent)
187
174
 
188
175
  def run_model(self) -> None:
189
- """Run the model until the end condition is reached. Overload as
190
- needed.
176
+ """Run the model until the end condition is reached.
177
+
178
+ Overload as needed.
191
179
  """
192
180
  while self.running:
193
181
  self.step()
@@ -201,7 +189,6 @@ class Model:
201
189
  Args:
202
190
  seed: A new seed for the RNG; if None, reset using the current seed
203
191
  """
204
-
205
192
  if seed is None:
206
193
  seed = self._seed
207
194
  self.random.seed(seed)
@@ -211,19 +198,29 @@ class Model:
211
198
  self,
212
199
  model_reporters=None,
213
200
  agent_reporters=None,
201
+ agenttype_reporters=None,
214
202
  tables=None,
215
203
  ) -> None:
216
- if not hasattr(self, "schedule") or self.schedule is None:
217
- raise RuntimeError(
218
- "You must initialize the scheduler (self.schedule) before initializing the data collector."
219
- )
220
- if self.schedule.get_agent_count() == 0:
221
- raise RuntimeError(
222
- "You must add agents to the scheduler before initializing the data collector."
223
- )
204
+ """Initialize the data collector for the model.
205
+
206
+ Args:
207
+ model_reporters: model reporters to collect
208
+ agent_reporters: agent reporters to collect
209
+ agenttype_reporters: agent type reporters to collect
210
+ tables: tables to collect
211
+
212
+ """
213
+ warnings.warn(
214
+ "initialize_data_collector() is deprecated. Please use the DataCollector class directly. "
215
+ "by using `self.datacollector = DataCollector(...)`.",
216
+ DeprecationWarning,
217
+ stacklevel=2,
218
+ )
219
+
224
220
  self.datacollector = DataCollector(
225
221
  model_reporters=model_reporters,
226
222
  agent_reporters=agent_reporters,
223
+ agenttype_reporters=agenttype_reporters,
227
224
  tables=tables,
228
225
  )
229
226
  # Collect data for the first time during initialization.