Mesa 2.4.0__py3-none-any.whl → 3.0.0__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 (110) hide show
  1. mesa/__init__.py +3 -5
  2. mesa/agent.py +105 -92
  3. mesa/batchrunner.py +55 -31
  4. mesa/datacollection.py +10 -14
  5. mesa/examples/README.md +37 -0
  6. mesa/examples/__init__.py +21 -0
  7. mesa/examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +116 -0
  8. mesa/examples/advanced/epstein_civil_violence/Readme.md +34 -0
  9. mesa/examples/advanced/epstein_civil_violence/__init__.py +0 -0
  10. mesa/examples/advanced/epstein_civil_violence/agents.py +164 -0
  11. mesa/examples/advanced/epstein_civil_violence/app.py +73 -0
  12. mesa/examples/advanced/epstein_civil_violence/model.py +114 -0
  13. mesa/examples/advanced/pd_grid/Readme.md +43 -0
  14. mesa/examples/advanced/pd_grid/__init__.py +0 -0
  15. mesa/examples/advanced/pd_grid/agents.py +50 -0
  16. mesa/examples/advanced/pd_grid/analysis.ipynb +228 -0
  17. mesa/examples/advanced/pd_grid/app.py +54 -0
  18. mesa/examples/advanced/pd_grid/model.py +71 -0
  19. mesa/examples/advanced/sugarscape_g1mt/Readme.md +64 -0
  20. mesa/examples/advanced/sugarscape_g1mt/__init__.py +0 -0
  21. mesa/examples/advanced/sugarscape_g1mt/agents.py +344 -0
  22. mesa/examples/advanced/sugarscape_g1mt/app.py +62 -0
  23. mesa/examples/advanced/sugarscape_g1mt/model.py +180 -0
  24. mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt +50 -0
  25. mesa/examples/advanced/sugarscape_g1mt/tests.py +69 -0
  26. mesa/examples/advanced/wolf_sheep/Readme.md +57 -0
  27. mesa/examples/advanced/wolf_sheep/__init__.py +0 -0
  28. mesa/examples/advanced/wolf_sheep/agents.py +102 -0
  29. mesa/examples/advanced/wolf_sheep/app.py +84 -0
  30. mesa/examples/advanced/wolf_sheep/model.py +137 -0
  31. mesa/examples/basic/__init__.py +0 -0
  32. mesa/examples/basic/boid_flockers/Readme.md +22 -0
  33. mesa/examples/basic/boid_flockers/__init__.py +0 -0
  34. mesa/examples/basic/boid_flockers/agents.py +71 -0
  35. mesa/examples/basic/boid_flockers/app.py +58 -0
  36. mesa/examples/basic/boid_flockers/model.py +69 -0
  37. mesa/examples/basic/boltzmann_wealth_model/Readme.md +56 -0
  38. mesa/examples/basic/boltzmann_wealth_model/__init__.py +0 -0
  39. mesa/examples/basic/boltzmann_wealth_model/agents.py +31 -0
  40. mesa/examples/basic/boltzmann_wealth_model/app.py +74 -0
  41. mesa/examples/basic/boltzmann_wealth_model/model.py +43 -0
  42. mesa/examples/basic/boltzmann_wealth_model/st_app.py +115 -0
  43. mesa/examples/basic/conways_game_of_life/Readme.md +39 -0
  44. mesa/examples/basic/conways_game_of_life/__init__.py +0 -0
  45. mesa/examples/basic/conways_game_of_life/agents.py +47 -0
  46. mesa/examples/basic/conways_game_of_life/app.py +51 -0
  47. mesa/examples/basic/conways_game_of_life/model.py +31 -0
  48. mesa/examples/basic/conways_game_of_life/st_app.py +72 -0
  49. mesa/examples/basic/schelling/Readme.md +40 -0
  50. mesa/examples/basic/schelling/__init__.py +0 -0
  51. mesa/examples/basic/schelling/agents.py +26 -0
  52. mesa/examples/basic/schelling/analysis.ipynb +205 -0
  53. mesa/examples/basic/schelling/app.py +42 -0
  54. mesa/examples/basic/schelling/model.py +59 -0
  55. mesa/examples/basic/virus_on_network/Readme.md +61 -0
  56. mesa/examples/basic/virus_on_network/__init__.py +0 -0
  57. mesa/examples/basic/virus_on_network/agents.py +69 -0
  58. mesa/examples/basic/virus_on_network/app.py +114 -0
  59. mesa/examples/basic/virus_on_network/model.py +96 -0
  60. mesa/experimental/UserParam.py +18 -7
  61. mesa/experimental/__init__.py +10 -2
  62. mesa/experimental/cell_space/__init__.py +16 -1
  63. mesa/experimental/cell_space/cell.py +93 -23
  64. mesa/experimental/cell_space/cell_agent.py +117 -21
  65. mesa/experimental/cell_space/cell_collection.py +56 -19
  66. mesa/experimental/cell_space/discrete_space.py +92 -8
  67. mesa/experimental/cell_space/grid.py +33 -9
  68. mesa/experimental/cell_space/network.py +15 -10
  69. mesa/experimental/cell_space/voronoi.py +257 -0
  70. mesa/experimental/components/altair.py +11 -2
  71. mesa/experimental/components/matplotlib.py +132 -26
  72. mesa/experimental/devs/__init__.py +2 -0
  73. mesa/experimental/devs/eventlist.py +54 -15
  74. mesa/experimental/devs/examples/epstein_civil_violence.py +69 -38
  75. mesa/experimental/devs/examples/wolf_sheep.py +42 -43
  76. mesa/experimental/devs/simulator.py +57 -16
  77. mesa/experimental/{jupyter_viz.py → solara_viz.py} +151 -99
  78. mesa/model.py +136 -78
  79. mesa/space.py +208 -148
  80. mesa/time.py +63 -80
  81. mesa/visualization/__init__.py +25 -6
  82. mesa/visualization/components/__init__.py +83 -0
  83. mesa/visualization/components/altair_components.py +188 -0
  84. mesa/visualization/components/matplotlib_components.py +175 -0
  85. mesa/visualization/mpl_space_drawing.py +593 -0
  86. mesa/visualization/solara_viz.py +458 -0
  87. mesa/visualization/user_param.py +69 -0
  88. mesa/visualization/utils.py +9 -0
  89. {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/METADATA +62 -17
  90. mesa-3.0.0.dist-info/RECORD +95 -0
  91. mesa-3.0.0.dist-info/licenses/LICENSE +202 -0
  92. mesa-2.4.0.dist-info/licenses/LICENSE → mesa-3.0.0.dist-info/licenses/NOTICE +2 -2
  93. mesa/cookiecutter-mesa/cookiecutter.json +0 -8
  94. mesa/cookiecutter-mesa/hooks/post_gen_project.py +0 -11
  95. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +0 -4
  96. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate +0 -3
  97. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +0 -11
  98. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +0 -60
  99. mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate +0 -36
  100. mesa/flat/__init__.py +0 -6
  101. mesa/flat/visualization.py +0 -5
  102. mesa/main.py +0 -63
  103. mesa/visualization/ModularVisualization.py +0 -1
  104. mesa/visualization/TextVisualization.py +0 -1
  105. mesa/visualization/UserParam.py +0 -1
  106. mesa/visualization/modules.py +0 -1
  107. mesa-2.4.0.dist-info/RECORD +0 -45
  108. /mesa/{cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}} → examples/advanced}/__init__.py +0 -0
  109. {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/WHEEL +0 -0
  110. {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/entry_points.txt +0 -0
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
  """
@@ -9,15 +8,20 @@ Core Objects: Model
9
8
  from __future__ import annotations
10
9
 
11
10
  import random
11
+ import sys
12
12
  import warnings
13
+ from collections.abc import Sequence
13
14
 
14
15
  # mypy
15
- from typing import Any, Union
16
+ from typing import Any
17
+
18
+ import numpy as np
16
19
 
17
20
  from mesa.agent import Agent, AgentSet
18
21
  from mesa.datacollection import DataCollector
19
22
 
20
- TimeT = Union[float, int]
23
+ SeedLike = int | np.integer | Sequence[int] | np.random.SeedSequence
24
+ RNGLike = np.random.Generator | np.random.BitGenerator
21
25
 
22
26
 
23
27
  class Model:
@@ -30,23 +34,9 @@ class Model:
30
34
  Attributes:
31
35
  running: A boolean indicating if the model should continue running.
32
36
  schedule: An object to manage the order and execution of agent steps.
33
- current_id: A counter for assigning unique IDs to agents.
34
-
35
- Properties:
36
- agents: An AgentSet containing all agents in the model
37
- agent_types: A list of different agent types present in the model.
38
- agents_by_type: A dictionary where the keys are agent types and the values are the corresponding AgentSets.
39
-
40
- Methods:
41
- get_agents_of_type: Returns an AgentSet of agents of the specified type.
42
- Deprecated: Use agents_by_type[agenttype] instead.
43
- run_model: Runs the model's simulation until a defined end condition is reached.
44
- step: Executes a single step of the model's simulation process.
45
- next_id: Generates and returns the next unique identifier for an agent.
46
- reset_randomizer: Resets the model's random number generator with a new or existing seed.
47
- initialize_data_collector: Sets up the data collector for the model, requiring an initialized scheduler and agents.
48
- register_agent : register an agent with the model
49
- deregister_agent : remove an agent from the model
37
+ steps: the number of times `model.step()` has been called.
38
+ random: a seeded python.random number generator.
39
+ rng : a seeded numpy.random.Generator
50
40
 
51
41
  Notes:
52
42
  Model.agents returns the AgentSet containing all agents registered with the model. Changing
@@ -55,35 +45,80 @@ class Model:
55
45
 
56
46
  """
57
47
 
58
- def __new__(cls, *args: Any, **kwargs: Any) -> Any:
59
- """Create a new model object and instantiate its RNG automatically."""
60
- obj = object.__new__(cls)
61
- obj._seed = kwargs.get("seed")
62
- if obj._seed is None:
63
- # We explicitly specify the seed here so that we know its value in
64
- # advance.
65
- obj._seed = random.random()
66
- obj.random = random.Random(obj._seed)
67
-
68
- # TODO: Remove these 2 lines just before Mesa 3.0
69
- obj._steps = 0
70
- obj._time = 0
71
- return obj
72
-
73
- def __init__(self, *args: Any, **kwargs: Any) -> None:
74
- """Create a new model. Overload this method with the actual code to
75
- start the model. Always start with super().__init__() to initialize the
76
- model object properly.
77
- """
48
+ def __init__(
49
+ self,
50
+ *args: Any,
51
+ seed: float | None = None,
52
+ rng: RNGLike | SeedLike | None = None,
53
+ **kwargs: Any,
54
+ ) -> None:
55
+ """Create a new model.
78
56
 
79
- self.running = True
80
- self.schedule = None
81
- self.current_id = 0
57
+ Overload this method with the actual code to initialize the model. Always start with super().__init__()
58
+ to initialize the model object properly.
82
59
 
60
+ Args:
61
+ args: arguments to pass onto super
62
+ seed: the seed for the random number generator
63
+ rng : Pseudorandom number generator state. When `rng` is None, a new `numpy.random.Generator` is created
64
+ using entropy from the operating system. Types other than `numpy.random.Generator` are passed to
65
+ `numpy.random.default_rng` to instantiate a `Generator`.
66
+ kwargs: keyword arguments to pass onto super
67
+
68
+ Notes:
69
+ you have to pass either seed or rng, but not both.
70
+
71
+ """
72
+ super().__init__(*args, **kwargs)
73
+ self.running = True
74
+ self.steps: int = 0
75
+
76
+ if (seed is not None) and (rng is not None):
77
+ raise ValueError("you have to pass either rng or seed, not both")
78
+ elif seed is None:
79
+ self.rng: np.random.Generator = np.random.default_rng(rng)
80
+ self._rng = (
81
+ self.rng.bit_generator.state
82
+ ) # this allows for reproducing the rng
83
+
84
+ try:
85
+ self.random = random.Random(rng)
86
+ except TypeError:
87
+ seed = int(self.rng.integers(np.iinfo(np.int32).max))
88
+ self.random = random.Random(seed)
89
+ self._seed = seed # this allows for reproducing stdlib.random
90
+ elif rng is None:
91
+ self.random = random.Random(seed)
92
+ self._seed = seed # this allows for reproducing stdlib.random
93
+
94
+ try:
95
+ self.rng: np.random.Generator = np.random.default_rng(rng)
96
+ except TypeError:
97
+ rng = self.random.randint(0, sys.maxsize)
98
+ self.rng: np.random.Generator = np.random.default_rng(rng)
99
+ self._rng = self.rng.bit_generator.state
100
+
101
+ # Wrap the user-defined step method
102
+ self._user_step = self.step
103
+ self.step = self._wrapped_step
104
+
105
+ # setup agent registration data structures
83
106
  self._setup_agent_registration()
84
107
 
85
- self._steps: int = 0
86
- self._time: TimeT = 0 # the model's clock
108
+ def _wrapped_step(self, *args: Any, **kwargs: Any) -> None:
109
+ """Automatically increments time and steps after calling the user's step method."""
110
+ # Automatically increment time and step counters
111
+ self.steps += 1
112
+ # Call the original user-defined step method
113
+ self._user_step(*args, **kwargs)
114
+
115
+ def next_id(self) -> int: # noqa: D102
116
+ warnings.warn(
117
+ "using model.next_id() is deprecated. Agents track their unique ID automatically",
118
+ DeprecationWarning,
119
+ stacklevel=2,
120
+ )
121
+ return 0
87
122
 
88
123
  @property
89
124
  def agents(self) -> AgentSet:
@@ -92,12 +127,10 @@ class Model:
92
127
 
93
128
  @agents.setter
94
129
  def agents(self, agents: Any) -> None:
95
- warnings.warn(
130
+ raise AttributeError(
96
131
  "You are trying to set model.agents. In Mesa 3.0 and higher, this attribute is "
97
132
  "used by Mesa itself, so you cannot use it directly anymore."
98
- "Please adjust your code to use a different attribute name for custom agent storage.",
99
- UserWarning,
100
- stacklevel=2,
133
+ "Please adjust your code to use a different attribute name for custom agent storage."
101
134
  )
102
135
 
103
136
  @property
@@ -121,15 +154,17 @@ class Model:
121
154
  return self.agents_by_type[agenttype]
122
155
 
123
156
  def _setup_agent_registration(self):
124
- """helper method to initialize the agent registration datastructures"""
157
+ """Helper method to initialize the agent registration datastructures."""
125
158
  self._agents = {} # the hard references to all agents in the model
126
159
  self._agents_by_type: dict[
127
160
  type[Agent], AgentSet
128
161
  ] = {} # a dict with an agentset for each class of agents
129
- self._all_agents = AgentSet([], self) # an agenset with all agents
162
+ self._all_agents = AgentSet(
163
+ [], random=self.random
164
+ ) # an agenset with all agents
130
165
 
131
166
  def register_agent(self, agent):
132
- """Register the agent with the model
167
+ """Register the agent with the model.
133
168
 
134
169
  Args:
135
170
  agent: The agent to register.
@@ -160,16 +195,19 @@ class Model:
160
195
  [
161
196
  agent,
162
197
  ],
163
- self,
198
+ random=self.random,
164
199
  )
165
200
 
166
201
  self._all_agents.add(agent)
167
202
 
168
203
  def deregister_agent(self, agent):
169
- """Deregister the agent with the model
204
+ """Deregister the agent with the model.
205
+
206
+ Args:
207
+ agent: The agent to deregister.
170
208
 
171
- Notes::
172
- This method is called automatically by ``Agent.remove``
209
+ Notes:
210
+ This method is called automatically by ``Agent.remove``
173
211
 
174
212
  """
175
213
  del self._agents[agent]
@@ -177,8 +215,9 @@ class Model:
177
215
  self._all_agents.remove(agent)
178
216
 
179
217
  def run_model(self) -> None:
180
- """Run the model until the end condition is reached. Overload as
181
- needed.
218
+ """Run the model until the end condition is reached.
219
+
220
+ Overload as needed.
182
221
  """
183
222
  while self.running:
184
223
  self.step()
@@ -186,28 +225,26 @@ class Model:
186
225
  def step(self) -> None:
187
226
  """A single step. Fill in here."""
188
227
 
189
- def _advance_time(self, deltat: TimeT = 1):
190
- """Increment the model's steps counter and clock."""
191
- self._steps += 1
192
- self._time += deltat
193
-
194
- def next_id(self) -> int:
195
- """Return the next unique ID for agents, increment current_id"""
196
- self.current_id += 1
197
- return self.current_id
198
-
199
228
  def reset_randomizer(self, seed: int | None = None) -> None:
200
229
  """Reset the model random number generator.
201
230
 
202
231
  Args:
203
232
  seed: A new seed for the RNG; if None, reset using the current seed
204
233
  """
205
-
206
234
  if seed is None:
207
235
  seed = self._seed
208
236
  self.random.seed(seed)
209
237
  self._seed = seed
210
238
 
239
+ def reset_rng(self, rng: RNGLike | SeedLike | None = None) -> None:
240
+ """Reset the model random number generator.
241
+
242
+ Args:
243
+ rng: A new seed for the RNG; if None, reset using the current seed
244
+ """
245
+ self.rng = np.random.default_rng(rng)
246
+ self._rng = self.rng.bit_generator.state
247
+
211
248
  def initialize_data_collector(
212
249
  self,
213
250
  model_reporters=None,
@@ -215,14 +252,22 @@ class Model:
215
252
  agenttype_reporters=None,
216
253
  tables=None,
217
254
  ) -> None:
218
- if not hasattr(self, "schedule") or self.schedule is None:
219
- raise RuntimeError(
220
- "You must initialize the scheduler (self.schedule) before initializing the data collector."
221
- )
222
- if self.schedule.get_agent_count() == 0:
223
- raise RuntimeError(
224
- "You must add agents to the scheduler before initializing the data collector."
225
- )
255
+ """Initialize the data collector for the model.
256
+
257
+ Args:
258
+ model_reporters: model reporters to collect
259
+ agent_reporters: agent reporters to collect
260
+ agenttype_reporters: agent type reporters to collect
261
+ tables: tables to collect
262
+
263
+ """
264
+ warnings.warn(
265
+ "initialize_data_collector() is deprecated. Please use the DataCollector class directly. "
266
+ "by using `self.datacollector = DataCollector(...)`.",
267
+ DeprecationWarning,
268
+ stacklevel=2,
269
+ )
270
+
226
271
  self.datacollector = DataCollector(
227
272
  model_reporters=model_reporters,
228
273
  agent_reporters=agent_reporters,
@@ -231,3 +276,16 @@ class Model:
231
276
  )
232
277
  # Collect data for the first time during initialization.
233
278
  self.datacollector.collect(self)
279
+
280
+ def remove_all_agents(self):
281
+ """Remove all agents from the model.
282
+
283
+ Notes:
284
+ This method calls agent.remove for all agents in the model. If you need to remove agents from
285
+ e.g., a SingleGrid, you can either explicitly implement your own agent.remove method or clean this up
286
+ near where you are calling this method.
287
+
288
+ """
289
+ # we need to wrap keys in a list to avoid a RunTimeError: dictionary changed size during iteration
290
+ for agent in list(self._agents.keys()):
291
+ agent.remove()