Mesa 3.0.0__py3-none-any.whl → 3.0.0a0__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.
- mesa/__init__.py +3 -3
- mesa/agent.py +114 -406
- mesa/batchrunner.py +27 -54
- mesa/cookiecutter-mesa/cookiecutter.json +8 -0
- mesa/cookiecutter-mesa/hooks/post_gen_project.py +11 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +4 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/app.pytemplate +27 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +11 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +60 -0
- mesa/datacollection.py +29 -140
- mesa/experimental/__init__.py +1 -11
- mesa/experimental/cell_space/__init__.py +1 -16
- mesa/experimental/cell_space/cell.py +23 -93
- mesa/experimental/cell_space/cell_agent.py +21 -117
- mesa/experimental/cell_space/cell_collection.py +17 -54
- mesa/experimental/cell_space/discrete_space.py +8 -92
- mesa/experimental/cell_space/grid.py +8 -32
- mesa/experimental/cell_space/network.py +7 -12
- mesa/experimental/devs/__init__.py +0 -2
- mesa/experimental/devs/eventlist.py +14 -52
- mesa/experimental/devs/examples/epstein_civil_violence.py +39 -71
- mesa/experimental/devs/examples/wolf_sheep.py +45 -45
- mesa/experimental/devs/simulator.py +15 -55
- mesa/main.py +63 -0
- mesa/model.py +83 -211
- mesa/space.py +149 -215
- mesa/time.py +77 -62
- mesa/{experimental → visualization}/UserParam.py +6 -17
- mesa/visualization/__init__.py +2 -25
- mesa/{experimental → visualization}/components/altair.py +0 -10
- mesa/visualization/components/matplotlib.py +134 -0
- mesa/{experimental/solara_viz.py → visualization/jupyter_viz.py} +110 -65
- {mesa-3.0.0.dist-info → mesa-3.0.0a0.dist-info}/METADATA +13 -65
- mesa-3.0.0a0.dist-info/RECORD +38 -0
- mesa-3.0.0.dist-info/licenses/NOTICE → mesa-3.0.0a0.dist-info/licenses/LICENSE +2 -2
- mesa/examples/README.md +0 -37
- mesa/examples/__init__.py +0 -21
- mesa/examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +0 -116
- mesa/examples/advanced/epstein_civil_violence/Readme.md +0 -34
- mesa/examples/advanced/epstein_civil_violence/__init__.py +0 -0
- mesa/examples/advanced/epstein_civil_violence/agents.py +0 -164
- mesa/examples/advanced/epstein_civil_violence/app.py +0 -73
- mesa/examples/advanced/epstein_civil_violence/model.py +0 -114
- mesa/examples/advanced/pd_grid/Readme.md +0 -43
- mesa/examples/advanced/pd_grid/__init__.py +0 -0
- mesa/examples/advanced/pd_grid/agents.py +0 -50
- mesa/examples/advanced/pd_grid/analysis.ipynb +0 -228
- mesa/examples/advanced/pd_grid/app.py +0 -54
- mesa/examples/advanced/pd_grid/model.py +0 -71
- mesa/examples/advanced/sugarscape_g1mt/Readme.md +0 -64
- mesa/examples/advanced/sugarscape_g1mt/__init__.py +0 -0
- mesa/examples/advanced/sugarscape_g1mt/agents.py +0 -344
- mesa/examples/advanced/sugarscape_g1mt/app.py +0 -62
- mesa/examples/advanced/sugarscape_g1mt/model.py +0 -180
- mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt +0 -50
- mesa/examples/advanced/sugarscape_g1mt/tests.py +0 -69
- mesa/examples/advanced/wolf_sheep/Readme.md +0 -57
- mesa/examples/advanced/wolf_sheep/__init__.py +0 -0
- mesa/examples/advanced/wolf_sheep/agents.py +0 -102
- mesa/examples/advanced/wolf_sheep/app.py +0 -84
- mesa/examples/advanced/wolf_sheep/model.py +0 -137
- mesa/examples/basic/__init__.py +0 -0
- mesa/examples/basic/boid_flockers/Readme.md +0 -22
- mesa/examples/basic/boid_flockers/__init__.py +0 -0
- mesa/examples/basic/boid_flockers/agents.py +0 -71
- mesa/examples/basic/boid_flockers/app.py +0 -58
- mesa/examples/basic/boid_flockers/model.py +0 -69
- mesa/examples/basic/boltzmann_wealth_model/Readme.md +0 -56
- mesa/examples/basic/boltzmann_wealth_model/__init__.py +0 -0
- mesa/examples/basic/boltzmann_wealth_model/agents.py +0 -31
- mesa/examples/basic/boltzmann_wealth_model/app.py +0 -74
- mesa/examples/basic/boltzmann_wealth_model/model.py +0 -43
- mesa/examples/basic/boltzmann_wealth_model/st_app.py +0 -115
- mesa/examples/basic/conways_game_of_life/Readme.md +0 -39
- mesa/examples/basic/conways_game_of_life/__init__.py +0 -0
- mesa/examples/basic/conways_game_of_life/agents.py +0 -47
- mesa/examples/basic/conways_game_of_life/app.py +0 -51
- mesa/examples/basic/conways_game_of_life/model.py +0 -31
- mesa/examples/basic/conways_game_of_life/st_app.py +0 -72
- mesa/examples/basic/schelling/Readme.md +0 -40
- mesa/examples/basic/schelling/__init__.py +0 -0
- mesa/examples/basic/schelling/agents.py +0 -26
- mesa/examples/basic/schelling/analysis.ipynb +0 -205
- mesa/examples/basic/schelling/app.py +0 -42
- mesa/examples/basic/schelling/model.py +0 -59
- mesa/examples/basic/virus_on_network/Readme.md +0 -61
- mesa/examples/basic/virus_on_network/__init__.py +0 -0
- mesa/examples/basic/virus_on_network/agents.py +0 -69
- mesa/examples/basic/virus_on_network/app.py +0 -114
- mesa/examples/basic/virus_on_network/model.py +0 -96
- mesa/experimental/cell_space/voronoi.py +0 -257
- mesa/experimental/components/matplotlib.py +0 -242
- mesa/visualization/components/__init__.py +0 -83
- mesa/visualization/components/altair_components.py +0 -188
- mesa/visualization/components/matplotlib_components.py +0 -175
- mesa/visualization/mpl_space_drawing.py +0 -593
- mesa/visualization/solara_viz.py +0 -458
- mesa/visualization/user_param.py +0 -69
- mesa/visualization/utils.py +0 -9
- mesa-3.0.0.dist-info/RECORD +0 -95
- mesa-3.0.0.dist-info/licenses/LICENSE +0 -202
- /mesa/{examples/advanced → cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}}/__init__.py +0 -0
- {mesa-3.0.0.dist-info → mesa-3.0.0a0.dist-info}/WHEEL +0 -0
- {mesa-3.0.0.dist-info → mesa-3.0.0a0.dist-info}/entry_points.txt +0 -0
mesa/model.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
The model class for Mesa framework.
|
|
2
3
|
|
|
3
4
|
Core Objects: Model
|
|
4
5
|
"""
|
|
@@ -7,21 +8,18 @@ Core Objects: Model
|
|
|
7
8
|
# Remove this __future__ import once the oldest supported Python is 3.10
|
|
8
9
|
from __future__ import annotations
|
|
9
10
|
|
|
11
|
+
import itertools
|
|
10
12
|
import random
|
|
11
|
-
import sys
|
|
12
13
|
import warnings
|
|
13
|
-
from collections
|
|
14
|
+
from collections import defaultdict
|
|
14
15
|
|
|
15
16
|
# mypy
|
|
16
17
|
from typing import Any
|
|
17
18
|
|
|
18
|
-
import numpy as np
|
|
19
|
-
|
|
20
19
|
from mesa.agent import Agent, AgentSet
|
|
21
20
|
from mesa.datacollection import DataCollector
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
RNGLike = np.random.Generator | np.random.BitGenerator
|
|
22
|
+
TimeT = float | int
|
|
25
23
|
|
|
26
24
|
|
|
27
25
|
class Model:
|
|
@@ -34,190 +32,85 @@ class Model:
|
|
|
34
32
|
Attributes:
|
|
35
33
|
running: A boolean indicating if the model should continue running.
|
|
36
34
|
schedule: An object to manage the order and execution of agent steps.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
35
|
+
current_id: A counter for assigning unique IDs to agents.
|
|
36
|
+
agents_: A defaultdict mapping each agent type to a dict of its instances.
|
|
37
|
+
This private attribute is used internally to manage agents.
|
|
38
|
+
|
|
39
|
+
Properties:
|
|
40
|
+
agents: An AgentSet containing all agents in the model, generated from the _agents attribute.
|
|
41
|
+
agent_types: A list of different agent types present in the model.
|
|
42
|
+
|
|
43
|
+
Methods:
|
|
44
|
+
get_agents_of_type: Returns an AgentSet of agents of the specified type.
|
|
45
|
+
run_model: Runs the model's simulation until a defined end condition is reached.
|
|
46
|
+
step: Executes a single step of the model's simulation process.
|
|
47
|
+
next_id: Generates and returns the next unique identifier for an agent.
|
|
48
|
+
reset_randomizer: Resets the model's random number generator with a new or existing seed.
|
|
49
|
+
initialize_data_collector: Sets up the data collector for the model, requiring an initialized scheduler and agents.
|
|
46
50
|
"""
|
|
47
51
|
|
|
48
|
-
def
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
kwargs: keyword arguments to pass onto super
|
|
67
|
-
|
|
68
|
-
Notes:
|
|
69
|
-
you have to pass either seed or rng, but not both.
|
|
70
|
-
|
|
52
|
+
def __new__(cls, *args: Any, **kwargs: Any) -> Any:
|
|
53
|
+
"""Create a new model object and instantiate its RNG automatically."""
|
|
54
|
+
obj = object.__new__(cls)
|
|
55
|
+
obj._seed = kwargs.get("seed")
|
|
56
|
+
if obj._seed is None:
|
|
57
|
+
# We explicitly specify the seed here so that we know its value in
|
|
58
|
+
# advance.
|
|
59
|
+
obj._seed = random.random()
|
|
60
|
+
obj.random = random.Random(obj._seed)
|
|
61
|
+
# TODO: Remove these 2 lines just before Mesa 3.0
|
|
62
|
+
obj._steps = 0
|
|
63
|
+
obj._time = 0
|
|
64
|
+
return obj
|
|
65
|
+
|
|
66
|
+
def __init__(self, *args: Any, **kwargs: Any) -> None:
|
|
67
|
+
"""Create a new model. Overload this method with the actual code to
|
|
68
|
+
start the model. Always start with super().__init__() to initialize the
|
|
69
|
+
model object properly.
|
|
71
70
|
"""
|
|
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
71
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
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
|
|
106
|
-
self._setup_agent_registration()
|
|
107
|
-
|
|
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)
|
|
72
|
+
self.running = True
|
|
73
|
+
self.schedule = None
|
|
74
|
+
self.current_id = 0
|
|
75
|
+
self.agents_: defaultdict[type, dict] = defaultdict(dict)
|
|
114
76
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
"using model.next_id() is deprecated. Agents track their unique ID automatically",
|
|
118
|
-
DeprecationWarning,
|
|
119
|
-
stacklevel=2,
|
|
120
|
-
)
|
|
121
|
-
return 0
|
|
77
|
+
self._steps: int = 0
|
|
78
|
+
self._time: TimeT = 0 # the model's clock
|
|
122
79
|
|
|
123
80
|
@property
|
|
124
81
|
def agents(self) -> AgentSet:
|
|
125
82
|
"""Provides an AgentSet of all agents in the model, combining agents from all types."""
|
|
126
|
-
|
|
83
|
+
|
|
84
|
+
if hasattr(self, "_agents"):
|
|
85
|
+
return self._agents
|
|
86
|
+
else:
|
|
87
|
+
all_agents = itertools.chain.from_iterable(self.agents_.values())
|
|
88
|
+
return AgentSet(all_agents, self)
|
|
127
89
|
|
|
128
90
|
@agents.setter
|
|
129
91
|
def agents(self, agents: Any) -> None:
|
|
130
|
-
raise AttributeError(
|
|
131
|
-
"You are trying to set model.agents. In Mesa 3.0 and higher, this attribute is "
|
|
132
|
-
"used by Mesa itself, so you cannot use it directly anymore."
|
|
133
|
-
"Please adjust your code to use a different attribute name for custom agent storage."
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
@property
|
|
137
|
-
def agent_types(self) -> list[type]:
|
|
138
|
-
"""Return a list of all unique agent types registered with the model."""
|
|
139
|
-
return list(self._agents_by_type.keys())
|
|
140
|
-
|
|
141
|
-
@property
|
|
142
|
-
def agents_by_type(self) -> dict[type[Agent], AgentSet]:
|
|
143
|
-
"""A dictionary where the keys are agent types and the values are the corresponding AgentSets."""
|
|
144
|
-
return self._agents_by_type
|
|
145
|
-
|
|
146
|
-
def get_agents_of_type(self, agenttype: type[Agent]) -> AgentSet:
|
|
147
|
-
"""Deprecated: Retrieves an AgentSet containing all agents of the specified type."""
|
|
148
92
|
warnings.warn(
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
93
|
+
"You are trying to set model.agents. In a next release, this attribute is used "
|
|
94
|
+
"by MESA itself so you cannot use it directly anymore."
|
|
95
|
+
"Please adjust your code to use a different attribute name for custom agent storage",
|
|
96
|
+
UserWarning,
|
|
152
97
|
stacklevel=2,
|
|
153
98
|
)
|
|
154
|
-
return self.agents_by_type[agenttype]
|
|
155
|
-
|
|
156
|
-
def _setup_agent_registration(self):
|
|
157
|
-
"""Helper method to initialize the agent registration datastructures."""
|
|
158
|
-
self._agents = {} # the hard references to all agents in the model
|
|
159
|
-
self._agents_by_type: dict[
|
|
160
|
-
type[Agent], AgentSet
|
|
161
|
-
] = {} # a dict with an agentset for each class of agents
|
|
162
|
-
self._all_agents = AgentSet(
|
|
163
|
-
[], random=self.random
|
|
164
|
-
) # an agenset with all agents
|
|
165
|
-
|
|
166
|
-
def register_agent(self, agent):
|
|
167
|
-
"""Register the agent with the model.
|
|
168
|
-
|
|
169
|
-
Args:
|
|
170
|
-
agent: The agent to register.
|
|
171
|
-
|
|
172
|
-
Notes:
|
|
173
|
-
This method is called automatically by ``Agent.__init__``, so there is no need to use this
|
|
174
|
-
if you are subclassing Agent and calling its super in the ``__init__`` method.
|
|
175
|
-
|
|
176
|
-
"""
|
|
177
|
-
if not hasattr(self, "_agents"):
|
|
178
|
-
self._setup_agent_registration()
|
|
179
|
-
|
|
180
|
-
warnings.warn(
|
|
181
|
-
"The Mesa Model class was not initialized. In the future, you need to explicitly initialize "
|
|
182
|
-
"the Model by calling super().__init__() on initialization.",
|
|
183
|
-
FutureWarning,
|
|
184
|
-
stacklevel=2,
|
|
185
|
-
)
|
|
186
99
|
|
|
187
|
-
self._agents
|
|
188
|
-
|
|
189
|
-
# because AgentSet requires model, we cannot use defaultdict
|
|
190
|
-
# tricks with a function won't work because model then cannot be pickled
|
|
191
|
-
try:
|
|
192
|
-
self._agents_by_type[type(agent)].add(agent)
|
|
193
|
-
except KeyError:
|
|
194
|
-
self._agents_by_type[type(agent)] = AgentSet(
|
|
195
|
-
[
|
|
196
|
-
agent,
|
|
197
|
-
],
|
|
198
|
-
random=self.random,
|
|
199
|
-
)
|
|
200
|
-
|
|
201
|
-
self._all_agents.add(agent)
|
|
202
|
-
|
|
203
|
-
def deregister_agent(self, agent):
|
|
204
|
-
"""Deregister the agent with the model.
|
|
205
|
-
|
|
206
|
-
Args:
|
|
207
|
-
agent: The agent to deregister.
|
|
100
|
+
self._agents = agents
|
|
208
101
|
|
|
209
|
-
|
|
210
|
-
|
|
102
|
+
@property
|
|
103
|
+
def agent_types(self) -> list[type]:
|
|
104
|
+
"""Return a list of different agent types."""
|
|
105
|
+
return list(self.agents_.keys())
|
|
211
106
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
self.
|
|
215
|
-
self._all_agents.remove(agent)
|
|
107
|
+
def get_agents_of_type(self, agenttype: type[Agent]) -> AgentSet:
|
|
108
|
+
"""Retrieves an AgentSet containing all agents of the specified type."""
|
|
109
|
+
return AgentSet(self.agents_[agenttype].keys(), self)
|
|
216
110
|
|
|
217
111
|
def run_model(self) -> None:
|
|
218
|
-
"""Run the model until the end condition is reached.
|
|
219
|
-
|
|
220
|
-
Overload as needed.
|
|
112
|
+
"""Run the model until the end condition is reached. Overload as
|
|
113
|
+
needed.
|
|
221
114
|
"""
|
|
222
115
|
while self.running:
|
|
223
116
|
self.step()
|
|
@@ -225,67 +118,46 @@ class Model:
|
|
|
225
118
|
def step(self) -> None:
|
|
226
119
|
"""A single step. Fill in here."""
|
|
227
120
|
|
|
121
|
+
def _advance_time(self, deltat: TimeT = 1):
|
|
122
|
+
"""Increment the model's steps counter and clock."""
|
|
123
|
+
self._steps += 1
|
|
124
|
+
self._time += deltat
|
|
125
|
+
|
|
126
|
+
def next_id(self) -> int:
|
|
127
|
+
"""Return the next unique ID for agents, increment current_id"""
|
|
128
|
+
self.current_id += 1
|
|
129
|
+
return self.current_id
|
|
130
|
+
|
|
228
131
|
def reset_randomizer(self, seed: int | None = None) -> None:
|
|
229
132
|
"""Reset the model random number generator.
|
|
230
133
|
|
|
231
134
|
Args:
|
|
232
135
|
seed: A new seed for the RNG; if None, reset using the current seed
|
|
233
136
|
"""
|
|
137
|
+
|
|
234
138
|
if seed is None:
|
|
235
139
|
seed = self._seed
|
|
236
140
|
self.random.seed(seed)
|
|
237
141
|
self._seed = seed
|
|
238
142
|
|
|
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
|
-
|
|
248
143
|
def initialize_data_collector(
|
|
249
144
|
self,
|
|
250
145
|
model_reporters=None,
|
|
251
146
|
agent_reporters=None,
|
|
252
|
-
agenttype_reporters=None,
|
|
253
147
|
tables=None,
|
|
254
148
|
) -> None:
|
|
255
|
-
""
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
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
|
-
|
|
149
|
+
if not hasattr(self, "schedule") or self.schedule is None:
|
|
150
|
+
raise RuntimeError(
|
|
151
|
+
"You must initialize the scheduler (self.schedule) before initializing the data collector."
|
|
152
|
+
)
|
|
153
|
+
if self.schedule.get_agent_count() == 0:
|
|
154
|
+
raise RuntimeError(
|
|
155
|
+
"You must add agents to the scheduler before initializing the data collector."
|
|
156
|
+
)
|
|
271
157
|
self.datacollector = DataCollector(
|
|
272
158
|
model_reporters=model_reporters,
|
|
273
159
|
agent_reporters=agent_reporters,
|
|
274
|
-
agenttype_reporters=agenttype_reporters,
|
|
275
160
|
tables=tables,
|
|
276
161
|
)
|
|
277
162
|
# Collect data for the first time during initialization.
|
|
278
163
|
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()
|