Mesa 3.0.2__py3-none-any.whl → 3.1.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.
- mesa/__init__.py +4 -6
- mesa/agent.py +48 -25
- mesa/batchrunner.py +14 -1
- mesa/datacollection.py +1 -6
- mesa/examples/__init__.py +2 -2
- mesa/examples/advanced/epstein_civil_violence/app.py +5 -0
- mesa/examples/advanced/pd_grid/app.py +5 -0
- mesa/examples/advanced/sugarscape_g1mt/app.py +7 -2
- mesa/examples/basic/boid_flockers/app.py +5 -0
- mesa/examples/basic/boltzmann_wealth_model/app.py +8 -5
- mesa/examples/basic/boltzmann_wealth_model/st_app.py +1 -1
- mesa/examples/basic/conways_game_of_life/app.py +5 -0
- mesa/examples/basic/conways_game_of_life/st_app.py +2 -2
- mesa/examples/basic/schelling/app.py +5 -0
- mesa/examples/basic/virus_on_network/app.py +5 -0
- mesa/experimental/__init__.py +17 -10
- mesa/experimental/cell_space/__init__.py +19 -7
- mesa/experimental/cell_space/cell.py +22 -37
- mesa/experimental/cell_space/cell_agent.py +12 -1
- mesa/experimental/cell_space/cell_collection.py +18 -3
- mesa/experimental/cell_space/discrete_space.py +15 -64
- mesa/experimental/cell_space/grid.py +74 -4
- mesa/experimental/cell_space/network.py +13 -1
- mesa/experimental/cell_space/property_layer.py +444 -0
- mesa/experimental/cell_space/voronoi.py +13 -1
- mesa/experimental/devs/__init__.py +20 -2
- mesa/experimental/devs/eventlist.py +19 -1
- mesa/experimental/devs/simulator.py +24 -8
- mesa/experimental/mesa_signals/__init__.py +23 -0
- mesa/experimental/mesa_signals/mesa_signal.py +485 -0
- mesa/experimental/mesa_signals/observable_collections.py +133 -0
- mesa/experimental/mesa_signals/signals_util.py +52 -0
- mesa/mesa_logging.py +190 -0
- mesa/model.py +17 -74
- mesa/visualization/__init__.py +2 -2
- mesa/visualization/mpl_space_drawing.py +2 -2
- mesa/visualization/solara_viz.py +23 -5
- {mesa-3.0.2.dist-info → mesa-3.1.0.dist-info}/METADATA +3 -4
- {mesa-3.0.2.dist-info → mesa-3.1.0.dist-info}/RECORD +43 -44
- {mesa-3.0.2.dist-info → mesa-3.1.0.dist-info}/WHEEL +1 -1
- mesa/experimental/UserParam.py +0 -67
- mesa/experimental/components/altair.py +0 -81
- mesa/experimental/components/matplotlib.py +0 -242
- mesa/experimental/devs/examples/epstein_civil_violence.py +0 -305
- mesa/experimental/devs/examples/wolf_sheep.py +0 -250
- mesa/experimental/solara_viz.py +0 -453
- mesa/time.py +0 -391
- {mesa-3.0.2.dist-info → mesa-3.1.0.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.2.dist-info → mesa-3.1.0.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.0.2.dist-info → mesa-3.1.0.dist-info}/licenses/NOTICE +0 -0
mesa/time.py
DELETED
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
"""Mesa Time Module.
|
|
2
|
-
|
|
3
|
-
.. warning::
|
|
4
|
-
The time module and all its Schedulers are deprecated and will be removed in Mesa 3.1.
|
|
5
|
-
They can be replaced with AgentSet functionality. See the migration guide for details:
|
|
6
|
-
https://mesa.readthedocs.io/latest/migration_guide.html#time-and-schedulers
|
|
7
|
-
|
|
8
|
-
Objects for handling the time component of a model. In particular, this module
|
|
9
|
-
contains Schedulers, which handle agent activation. A Scheduler is an object
|
|
10
|
-
which controls when agents are called upon to act, and when.
|
|
11
|
-
|
|
12
|
-
The activation order can have a serious impact on model behavior, so it's
|
|
13
|
-
important to specify it explicitly. Example simple activation regimes include
|
|
14
|
-
activating all agents in the same order every step, shuffling the activation
|
|
15
|
-
order every time, activating each agent *on average* once per step, and more.
|
|
16
|
-
|
|
17
|
-
Key concepts:
|
|
18
|
-
Step: Many models advance in 'steps'. A step may involve the activation of
|
|
19
|
-
all agents, or a random (or selected) subset of them. Each agent in turn
|
|
20
|
-
may have their own step() method.
|
|
21
|
-
|
|
22
|
-
Time: Some models may simulate a continuous 'clock' instead of discrete
|
|
23
|
-
steps. However, by default, the Time is equal to the number of steps the
|
|
24
|
-
model has taken.
|
|
25
|
-
"""
|
|
26
|
-
|
|
27
|
-
# Mypy; for the `|` operator purpose
|
|
28
|
-
# Remove this __future__ import once the oldest supported Python is 3.10
|
|
29
|
-
from __future__ import annotations
|
|
30
|
-
|
|
31
|
-
import warnings
|
|
32
|
-
from collections import defaultdict
|
|
33
|
-
from collections.abc import Iterable
|
|
34
|
-
|
|
35
|
-
# mypy
|
|
36
|
-
from mesa.agent import Agent, AgentSet
|
|
37
|
-
from mesa.model import Model
|
|
38
|
-
|
|
39
|
-
# BaseScheduler has a self.time of int, while
|
|
40
|
-
# StagedActivation has a self.time of float
|
|
41
|
-
TimeT = float | int
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
class BaseScheduler:
|
|
45
|
-
"""A simple scheduler that activates agents one at a time, in the order they were added.
|
|
46
|
-
|
|
47
|
-
This scheduler is designed to replicate the behavior of the scheduler in MASON, a multi-agent simulation toolkit.
|
|
48
|
-
It assumes that each agent added has a `step` method which takes no arguments and executes the agent's actions.
|
|
49
|
-
|
|
50
|
-
Attributes:
|
|
51
|
-
model (Model): The model instance associated with the scheduler.
|
|
52
|
-
steps (int): The number of steps the scheduler has taken.
|
|
53
|
-
time (TimeT): The current time in the simulation. Can be an integer or a float.
|
|
54
|
-
|
|
55
|
-
"""
|
|
56
|
-
|
|
57
|
-
def __init__(self, model: Model, agents: Iterable[Agent] | None = None) -> None:
|
|
58
|
-
"""Create a new BaseScheduler.
|
|
59
|
-
|
|
60
|
-
Args:
|
|
61
|
-
model (Model): The model to which the schedule belongs
|
|
62
|
-
agents (Iterable[Agent], None, optional): An iterable of agents who are controlled by the schedule
|
|
63
|
-
|
|
64
|
-
"""
|
|
65
|
-
warnings.warn(
|
|
66
|
-
"The time module and all its Schedulers are deprecated and will be removed in Mesa 3.1. "
|
|
67
|
-
"They can be replaced with AgentSet functionality. See the migration guide for details. "
|
|
68
|
-
"https://mesa.readthedocs.io/latest/migration_guide.html#time-and-schedulers",
|
|
69
|
-
DeprecationWarning,
|
|
70
|
-
stacklevel=2,
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
self.model = model
|
|
74
|
-
self.steps = 0
|
|
75
|
-
self.time: TimeT = 0
|
|
76
|
-
|
|
77
|
-
if agents is None:
|
|
78
|
-
agents = []
|
|
79
|
-
|
|
80
|
-
self._agents: AgentSet = AgentSet(agents, model.random)
|
|
81
|
-
|
|
82
|
-
self._remove_warning_given = False
|
|
83
|
-
self._agents_key_warning_given = False
|
|
84
|
-
|
|
85
|
-
def add(self, agent: Agent) -> None:
|
|
86
|
-
"""Add an Agent object to the schedule.
|
|
87
|
-
|
|
88
|
-
Args:
|
|
89
|
-
agent (Agent): An Agent to be added to the schedule.
|
|
90
|
-
"""
|
|
91
|
-
if agent not in self._agents:
|
|
92
|
-
self._agents.add(agent)
|
|
93
|
-
else:
|
|
94
|
-
raise ValueError("agent already added to scheduler")
|
|
95
|
-
|
|
96
|
-
def remove(self, agent: Agent) -> None:
|
|
97
|
-
"""Remove all instances of a given agent from the schedule.
|
|
98
|
-
|
|
99
|
-
Args:
|
|
100
|
-
agent: An `Agent` instance.
|
|
101
|
-
|
|
102
|
-
Note:
|
|
103
|
-
It is only necessary to explicitly remove agents from the schedule if
|
|
104
|
-
the agent is not removed from the model.
|
|
105
|
-
|
|
106
|
-
"""
|
|
107
|
-
self._agents.remove(agent)
|
|
108
|
-
|
|
109
|
-
def step(self) -> None:
|
|
110
|
-
"""Execute the step of all the agents, one at a time."""
|
|
111
|
-
# To be able to remove and/or add agents during stepping
|
|
112
|
-
# it's necessary for the keys view to be a list.
|
|
113
|
-
self.do_each("step")
|
|
114
|
-
self.steps += 1
|
|
115
|
-
self.time += 1
|
|
116
|
-
|
|
117
|
-
def get_agent_count(self) -> int:
|
|
118
|
-
"""Returns the current number of agents in the queue."""
|
|
119
|
-
return len(self._agents)
|
|
120
|
-
|
|
121
|
-
@property
|
|
122
|
-
def agents(self) -> AgentSet:
|
|
123
|
-
"""Return agents in the scheduler."""
|
|
124
|
-
# a bit dirty, but returns a copy of the internal agent set
|
|
125
|
-
return self._agents.select()
|
|
126
|
-
|
|
127
|
-
def get_agent_keys(self, shuffle: bool = False) -> list[int]:
|
|
128
|
-
"""Deprecated."""
|
|
129
|
-
# To be able to remove and/or add agents during stepping
|
|
130
|
-
# it's necessary to cast the keys view to a list.
|
|
131
|
-
|
|
132
|
-
if not self._agents_key_warning_given:
|
|
133
|
-
self._agents_key_warning_given = True
|
|
134
|
-
warnings.warn(
|
|
135
|
-
"Because of the shift to using weakrefs, this method will be removed in a future version",
|
|
136
|
-
DeprecationWarning,
|
|
137
|
-
stacklevel=2,
|
|
138
|
-
)
|
|
139
|
-
|
|
140
|
-
agent_keys = [agent.unique_id for agent in self._agents]
|
|
141
|
-
if shuffle:
|
|
142
|
-
self.model.random.shuffle(agent_keys)
|
|
143
|
-
return agent_keys
|
|
144
|
-
|
|
145
|
-
def do_each(self, method, shuffle=False):
|
|
146
|
-
"""Perform `method` on each agent.
|
|
147
|
-
|
|
148
|
-
Args:
|
|
149
|
-
method: method to call
|
|
150
|
-
shuffle: shuffle the agents or not prior to calling method
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
"""
|
|
154
|
-
if shuffle:
|
|
155
|
-
self._agents.shuffle(inplace=True)
|
|
156
|
-
self._agents.do(method)
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
class RandomActivation(BaseScheduler):
|
|
160
|
-
"""A scheduler that activates each agent once per step, in a random order, with the order reshuffled each step.
|
|
161
|
-
|
|
162
|
-
This scheduler is equivalent to the NetLogo 'ask agents...' behavior and is a common default for ABMs.
|
|
163
|
-
It assumes that all agents have a `step` method.
|
|
164
|
-
|
|
165
|
-
The random activation ensures that no single agent or sequence of agents consistently influences the model due
|
|
166
|
-
to ordering effects, which is crucial for certain types of simulations.
|
|
167
|
-
|
|
168
|
-
Inherits all attributes and methods from BaseScheduler.
|
|
169
|
-
|
|
170
|
-
"""
|
|
171
|
-
|
|
172
|
-
def step(self) -> None:
|
|
173
|
-
"""Executes the step of all agents, one at a time, in random order."""
|
|
174
|
-
self.do_each("step", shuffle=True)
|
|
175
|
-
self.steps += 1
|
|
176
|
-
self.time += 1
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
class SimultaneousActivation(BaseScheduler):
|
|
180
|
-
"""A scheduler that simulates the simultaneous activation of all agents.
|
|
181
|
-
|
|
182
|
-
This scheduler is unique in that it requires agents to have both `step` and `advance` methods.
|
|
183
|
-
- The `step` method is for activating the agent and staging any changes without applying them immediately.
|
|
184
|
-
- The `advance` method then applies these changes, simulating simultaneous action.
|
|
185
|
-
|
|
186
|
-
This scheduler is useful in scenarios where the interactions between agents are sensitive to the order
|
|
187
|
-
of execution, and a quasi-simultaneous execution is more realistic.
|
|
188
|
-
|
|
189
|
-
Inherits all attributes and methods from BaseScheduler.
|
|
190
|
-
|
|
191
|
-
"""
|
|
192
|
-
|
|
193
|
-
def step(self) -> None:
|
|
194
|
-
"""Step all agents, then advance them."""
|
|
195
|
-
self.do_each("step")
|
|
196
|
-
# do_each recomputes the agent_keys from scratch whenever it is called.
|
|
197
|
-
# It can handle the case when some agents might have been removed in
|
|
198
|
-
# the previous loop.
|
|
199
|
-
self.do_each("advance")
|
|
200
|
-
self.steps += 1
|
|
201
|
-
self.time += 1
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
class StagedActivation(BaseScheduler):
|
|
205
|
-
"""A scheduler allowing agent activation to be divided into several stages.
|
|
206
|
-
|
|
207
|
-
All agents executing one stage before moving on to the next. This class is a generalization of SimultaneousActivation.
|
|
208
|
-
|
|
209
|
-
This scheduler is useful for complex models where actions need to be broken down into distinct phases
|
|
210
|
-
for each agent in each time step. Agents must implement methods for each defined stage.
|
|
211
|
-
|
|
212
|
-
The scheduler also tracks steps and time separately, allowing fractional time increments based on the number
|
|
213
|
-
of stages. Time advances in fractional increments of 1 / (# of stages), meaning that 1 step = 1 unit of time.
|
|
214
|
-
|
|
215
|
-
Inherits all attributes and methods from BaseScheduler.
|
|
216
|
-
|
|
217
|
-
Attributes:
|
|
218
|
-
- stage_list (list[str]): A list of stage names that define the order of execution.
|
|
219
|
-
- shuffle (bool): Determines whether to shuffle the order of agents each step.
|
|
220
|
-
- shuffle_between_stages (bool): Determines whether to shuffle agents between each stage.
|
|
221
|
-
|
|
222
|
-
"""
|
|
223
|
-
|
|
224
|
-
def __init__(
|
|
225
|
-
self,
|
|
226
|
-
model: Model,
|
|
227
|
-
agents: Iterable[Agent] | None = None,
|
|
228
|
-
stage_list: list[str] | None = None,
|
|
229
|
-
shuffle: bool = False,
|
|
230
|
-
shuffle_between_stages: bool = False,
|
|
231
|
-
) -> None:
|
|
232
|
-
"""Create an empty Staged Activation schedule.
|
|
233
|
-
|
|
234
|
-
Args:
|
|
235
|
-
model (Model): The model to which the schedule belongs
|
|
236
|
-
agents (Iterable[Agent], None, optional): An iterable of agents who are controlled by the schedule
|
|
237
|
-
stage_list (:obj:`list` of :obj:`str`): List of strings of names of stages to run, in the
|
|
238
|
-
order to run them in.
|
|
239
|
-
shuffle (bool, optional): If True, shuffle the order of agents each step.
|
|
240
|
-
shuffle_between_stages (bool, optional): If True, shuffle the agents after each
|
|
241
|
-
stage; otherwise, only shuffle at the start
|
|
242
|
-
of each step.
|
|
243
|
-
"""
|
|
244
|
-
super().__init__(model, agents)
|
|
245
|
-
self.stage_list = stage_list if stage_list else ["step"]
|
|
246
|
-
self.shuffle = shuffle
|
|
247
|
-
self.shuffle_between_stages = shuffle_between_stages
|
|
248
|
-
self.stage_time = 1 / len(self.stage_list)
|
|
249
|
-
|
|
250
|
-
def step(self) -> None:
|
|
251
|
-
"""Executes all the stages for all agents."""
|
|
252
|
-
shuffle = self.shuffle
|
|
253
|
-
for stage in self.stage_list:
|
|
254
|
-
if stage.startswith("model."):
|
|
255
|
-
getattr(self.model, stage[6:])()
|
|
256
|
-
else:
|
|
257
|
-
self.do_each(stage, shuffle=shuffle)
|
|
258
|
-
|
|
259
|
-
shuffle = self.shuffle_between_stages
|
|
260
|
-
self.time += self.stage_time
|
|
261
|
-
|
|
262
|
-
self.steps += 1
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
class RandomActivationByType(BaseScheduler):
|
|
266
|
-
"""A scheduler that activates each type of agent once per step, in random order, with the order reshuffled every step.
|
|
267
|
-
|
|
268
|
-
This scheduler is useful for models with multiple types of agents, ensuring that each type is treated
|
|
269
|
-
equitably in terms of activation order. The randomness in activation order helps in reducing biases
|
|
270
|
-
due to ordering effects.
|
|
271
|
-
|
|
272
|
-
Inherits all attributes and methods from BaseScheduler.
|
|
273
|
-
|
|
274
|
-
If you want to do some computations / data collections specific to an agent
|
|
275
|
-
type, you can either:
|
|
276
|
-
- loop through all agents, and filter by their type
|
|
277
|
-
- access via `your_model.scheduler.agents_by_type[your_type_class]`
|
|
278
|
-
|
|
279
|
-
Attributes:
|
|
280
|
-
- agents_by_type (defaultdict): A dictionary mapping agent types to dictionaries of agents.
|
|
281
|
-
|
|
282
|
-
"""
|
|
283
|
-
|
|
284
|
-
@property
|
|
285
|
-
def agents_by_type(self): # noqa: D102
|
|
286
|
-
warnings.warn(
|
|
287
|
-
"Because of the shift to using AgentSet, in the future this attribute will return a dict with"
|
|
288
|
-
"type as key as AgentSet as value. Future behavior is available via RandomActivationByType._agents_by_type",
|
|
289
|
-
DeprecationWarning,
|
|
290
|
-
stacklevel=2,
|
|
291
|
-
)
|
|
292
|
-
|
|
293
|
-
agentsbytype = defaultdict(dict)
|
|
294
|
-
for k, v in self._agents_by_type.items():
|
|
295
|
-
agentsbytype[k] = {agent.unique_id: agent for agent in v}
|
|
296
|
-
|
|
297
|
-
return agentsbytype
|
|
298
|
-
|
|
299
|
-
def __init__(self, model: Model, agents: Iterable[Agent] | None = None) -> None:
|
|
300
|
-
"""Initialize RandomActivationByType instance.
|
|
301
|
-
|
|
302
|
-
Args:
|
|
303
|
-
model (Model): The model to which the schedule belongs
|
|
304
|
-
agents (Iterable[Agent], None, optional): An iterable of agents who are controlled by the schedule
|
|
305
|
-
"""
|
|
306
|
-
super().__init__(model, agents)
|
|
307
|
-
# can't be a defaultdict because we need to pass model to AgentSet
|
|
308
|
-
self._agents_by_type: [type, AgentSet] = {}
|
|
309
|
-
|
|
310
|
-
if agents is not None:
|
|
311
|
-
for agent in agents:
|
|
312
|
-
try:
|
|
313
|
-
self._agents_by_type[type(agent)].add(agent)
|
|
314
|
-
except KeyError:
|
|
315
|
-
self._agents_by_type[type(agent)] = AgentSet(
|
|
316
|
-
[agent], self.model.random
|
|
317
|
-
)
|
|
318
|
-
|
|
319
|
-
def add(self, agent: Agent) -> None:
|
|
320
|
-
"""Add an Agent object to the schedule.
|
|
321
|
-
|
|
322
|
-
Args:
|
|
323
|
-
agent: An Agent to be added to the schedule.
|
|
324
|
-
"""
|
|
325
|
-
super().add(agent)
|
|
326
|
-
|
|
327
|
-
try:
|
|
328
|
-
self._agents_by_type[type(agent)].add(agent)
|
|
329
|
-
except KeyError:
|
|
330
|
-
self._agents_by_type[type(agent)] = AgentSet([agent], self.model.random)
|
|
331
|
-
|
|
332
|
-
def remove(self, agent: Agent) -> None:
|
|
333
|
-
"""Remove all instances of a given agent from the schedule.
|
|
334
|
-
|
|
335
|
-
Args:
|
|
336
|
-
agent: An Agent to be removed from the schedule.
|
|
337
|
-
|
|
338
|
-
"""
|
|
339
|
-
super().remove(agent)
|
|
340
|
-
self._agents_by_type[type(agent)].remove(agent)
|
|
341
|
-
|
|
342
|
-
def step(self, shuffle_types: bool = True, shuffle_agents: bool = True) -> None:
|
|
343
|
-
"""Executes the step of each agent type, one at a time, in random order.
|
|
344
|
-
|
|
345
|
-
Args:
|
|
346
|
-
shuffle_types: If True, the order of execution of each types is shuffled.
|
|
347
|
-
shuffle_agents: If True, the order of execution of each agents in a type group is shuffled.
|
|
348
|
-
"""
|
|
349
|
-
# To be able to remove and/or add agents during stepping
|
|
350
|
-
# it's necessary to cast the keys view to a list.
|
|
351
|
-
type_keys: list[type[Agent]] = list(self._agents_by_type.keys())
|
|
352
|
-
if shuffle_types:
|
|
353
|
-
self.model.random.shuffle(type_keys)
|
|
354
|
-
for agent_class in type_keys:
|
|
355
|
-
self.step_type(agent_class, shuffle_agents=shuffle_agents)
|
|
356
|
-
self.steps += 1
|
|
357
|
-
self.time += 1
|
|
358
|
-
|
|
359
|
-
def step_type(self, agenttype: type[Agent], shuffle_agents: bool = True) -> None:
|
|
360
|
-
"""Shuffle order and run all agents of a given type.
|
|
361
|
-
|
|
362
|
-
Args:
|
|
363
|
-
agenttype: Class object of the type to run.
|
|
364
|
-
shuffle_agents: If True, shuffle agents
|
|
365
|
-
"""
|
|
366
|
-
agents = self._agents_by_type[agenttype]
|
|
367
|
-
|
|
368
|
-
if shuffle_agents:
|
|
369
|
-
agents.shuffle(inplace=True)
|
|
370
|
-
agents.do("step")
|
|
371
|
-
|
|
372
|
-
def get_type_count(self, agenttype: type[Agent]) -> int:
|
|
373
|
-
"""Returns the current number of agents of certain type in the queue."""
|
|
374
|
-
return len(self._agents_by_type[agenttype])
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
class DiscreteEventScheduler(BaseScheduler):
|
|
378
|
-
"""This class has been removed and replaced by the functionality provided by experimental.devs."""
|
|
379
|
-
|
|
380
|
-
def __init__(self, model: Model, time_step: TimeT = 1) -> None:
|
|
381
|
-
"""Initialize DiscreteEventScheduler.
|
|
382
|
-
|
|
383
|
-
Args:
|
|
384
|
-
model (Model): The model to which the schedule belongs
|
|
385
|
-
time_step (TimeT): The fixed time step between steps
|
|
386
|
-
|
|
387
|
-
"""
|
|
388
|
-
super().__init__(model)
|
|
389
|
-
raise Exception(
|
|
390
|
-
"DiscreteEventScheduler is removed in favor of the functionality provided by experimental.devs"
|
|
391
|
-
)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|