Mesa 3.0.3__py3-none-any.whl → 3.1.0.dev0__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 CHANGED
@@ -7,7 +7,6 @@ import datetime
7
7
 
8
8
  import mesa.experimental as experimental
9
9
  import mesa.space as space
10
- import mesa.time as time
11
10
  from mesa.agent import Agent
12
11
  from mesa.batchrunner import batch_run
13
12
  from mesa.datacollection import DataCollector
@@ -16,7 +15,6 @@ from mesa.model import Model
16
15
  __all__ = [
17
16
  "Model",
18
17
  "Agent",
19
- "time",
20
18
  "space",
21
19
  "DataCollector",
22
20
  "batch_run",
@@ -24,7 +22,7 @@ __all__ = [
24
22
  ]
25
23
 
26
24
  __title__ = "mesa"
27
- __version__ = "3.0.3"
25
+ __version__ = "3.1.0.dev"
28
26
  __license__ = "Apache 2.0"
29
- _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
27
+ _this_year = datetime.datetime.now(tz=datetime.UTC).date().year
30
28
  __copyright__ = f"Copyright {_this_year} Project Mesa Team"
mesa/agent.py CHANGED
@@ -155,7 +155,6 @@ class AgentSet(MutableSet, Sequence):
155
155
  at_most: int | float = float("inf"),
156
156
  inplace: bool = False,
157
157
  agent_type: type[Agent] | None = None,
158
- n: int | None = None,
159
158
  ) -> AgentSet:
160
159
  """Select a subset of agents from the AgentSet based on a filter function and/or quantity limit.
161
160
 
@@ -167,7 +166,6 @@ class AgentSet(MutableSet, Sequence):
167
166
  - If a float between 0 and 1, at most that fraction of original the agents are selected.
168
167
  inplace (bool, optional): If True, modifies the current AgentSet; otherwise, returns a new AgentSet. Defaults to False.
169
168
  agent_type (type[Agent], optional): The class type of the agents to select. Defaults to None, meaning no type filtering is applied.
170
- n (int): deprecated, use at_most instead
171
169
 
172
170
  Returns:
173
171
  AgentSet: A new AgentSet containing the selected agents, unless inplace is True, in which case the current AgentSet is updated.
@@ -176,14 +174,6 @@ class AgentSet(MutableSet, Sequence):
176
174
  - at_most just return the first n or fraction of agents. To take a random sample, shuffle() beforehand.
177
175
  - at_most is an upper limit. When specifying other criteria, the number of agents returned can be smaller.
178
176
  """
179
- if n is not None:
180
- warnings.warn(
181
- "The parameter 'n' is deprecated and will be removed in Mesa 3.1. Use 'at_most' instead.",
182
- DeprecationWarning,
183
- stacklevel=2,
184
- )
185
- at_most = n
186
-
187
177
  inf = float("inf")
188
178
  if filter_func is None and agent_type is None and at_most == inf:
189
179
  return self if inplace else copy.copy(self)
@@ -281,21 +271,6 @@ class AgentSet(MutableSet, Sequence):
281
271
  Returns:
282
272
  AgentSet | list[Any]: The results of the callable calls if return_results is True, otherwise the AgentSet itself.
283
273
  """
284
- try:
285
- return_results = kwargs.pop("return_results")
286
- except KeyError:
287
- return_results = False
288
- else:
289
- warnings.warn(
290
- "Using return_results is deprecated and will be removed in Mesa 3.1."
291
- "Use AgenSet.do in case of return_results=False, and AgentSet.map in case of return_results=True",
292
- DeprecationWarning,
293
- stacklevel=2,
294
- )
295
-
296
- if return_results:
297
- return self.map(method, *args, **kwargs)
298
-
299
274
  # we iterate over the actual weakref keys and check if weakref is alive before calling the method
300
275
  if isinstance(method, str):
301
276
  for agentref in self._agents.keyrefs():
@@ -48,10 +48,8 @@ class CellCollection(Generic[T]):
48
48
  else:
49
49
  self._cells = {cell: cell.agents for cell in cells}
50
50
 
51
- # Get capacity from first cell if collection is not empty
52
- self._capacity: int | None = (
53
- next(iter(self._cells.keys())).capacity if self._cells else None
54
- )
51
+ #
52
+ self._capacity: int = next(iter(self._cells.keys())).capacity
55
53
 
56
54
  if random is None:
57
55
  warnings.warn(
mesa/model.py CHANGED
@@ -18,7 +18,6 @@ from typing import Any
18
18
  import numpy as np
19
19
 
20
20
  from mesa.agent import Agent, AgentSet
21
- from mesa.datacollection import DataCollector
22
21
 
23
22
  SeedLike = int | np.integer | Sequence[int] | np.random.SeedSequence
24
23
  RNGLike = np.random.Generator | np.random.BitGenerator
@@ -112,14 +111,6 @@ class Model:
112
111
  # Call the original user-defined step method
113
112
  self._user_step(*args, **kwargs)
114
113
 
115
- def next_id(self) -> int: # noqa: D102
116
- warnings.warn(
117
- "using model.next_id() is deprecated and will be removed in Mesa 3.1. Agents track their unique ID automatically",
118
- DeprecationWarning,
119
- stacklevel=2,
120
- )
121
- return 0
122
-
123
114
  @property
124
115
  def agents(self) -> AgentSet:
125
116
  """Provides an AgentSet of all agents in the model, combining agents from all types."""
@@ -143,16 +134,6 @@ class Model:
143
134
  """A dictionary where the keys are agent types and the values are the corresponding AgentSets."""
144
135
  return self._agents_by_type
145
136
 
146
- def get_agents_of_type(self, agenttype: type[Agent]) -> AgentSet:
147
- """Deprecated: Retrieves an AgentSet containing all agents of the specified type."""
148
- warnings.warn(
149
- f"Model.get_agents_of_type() is deprecated and will be removed in Mesa 3.1."
150
- f"Please replace get_agents_of_type({agenttype}) with the property agents_by_type[{agenttype}].",
151
- DeprecationWarning,
152
- stacklevel=2,
153
- )
154
- return self.agents_by_type[agenttype]
155
-
156
137
  def _setup_agent_registration(self):
157
138
  """Helper method to initialize the agent registration datastructures."""
158
139
  self._agents = {} # the hard references to all agents in the model
@@ -245,38 +226,6 @@ class Model:
245
226
  self.rng = np.random.default_rng(rng)
246
227
  self._rng = self.rng.bit_generator.state
247
228
 
248
- def initialize_data_collector(
249
- self,
250
- model_reporters=None,
251
- agent_reporters=None,
252
- agenttype_reporters=None,
253
- tables=None,
254
- ) -> None:
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 and will be removed in Mesa 3.1. Please use the DataCollector class directly. "
266
- "by using `self.datacollector = DataCollector(...)`.",
267
- DeprecationWarning,
268
- stacklevel=2,
269
- )
270
-
271
- self.datacollector = DataCollector(
272
- model_reporters=model_reporters,
273
- agent_reporters=agent_reporters,
274
- agenttype_reporters=agenttype_reporters,
275
- tables=tables,
276
- )
277
- # Collect data for the first time during initialization.
278
- self.datacollector.collect(self)
279
-
280
229
  def remove_all_agents(self):
281
230
  """Remove all agents from the model.
282
231
 
@@ -382,17 +382,11 @@ def ModelCreator(
382
382
  )
383
383
  user_params, fixed_params = split_model_params(user_params)
384
384
 
385
- # Use solara.use_effect to run the initialization code only once
386
- solara.use_effect(
387
- # set model_parameters to the default values for all parameters
388
- lambda: model_parameters.set(
389
- {
390
- **fixed_params,
391
- **{k: v.get("value") for k, v in user_params.items()},
392
- }
393
- ),
394
- [],
395
- )
385
+ # set model_parameters to the default values for all parameters
386
+ model_parameters.value = {
387
+ **fixed_params,
388
+ **{k: v.get("value") for k, v in user_params.items()},
389
+ }
396
390
 
397
391
  def on_change(name, value):
398
392
  model_parameters.value = {**model_parameters.value, name: value}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: Mesa
3
- Version: 3.0.3
3
+ Version: 3.1.0.dev0
4
4
  Summary: Agent-based modeling (ABM) in Python
5
5
  Project-URL: homepage, https://github.com/projectmesa/mesa
6
6
  Project-URL: repository, https://github.com/projectmesa/mesa
@@ -13,14 +13,13 @@ Classifier: License :: OSI Approved :: Apache Software License
13
13
  Classifier: Natural Language :: English
14
14
  Classifier: Operating System :: OS Independent
15
15
  Classifier: Programming Language :: Python :: 3 :: Only
16
- Classifier: Programming Language :: Python :: 3.10
17
16
  Classifier: Programming Language :: Python :: 3.11
18
17
  Classifier: Programming Language :: Python :: 3.12
19
18
  Classifier: Programming Language :: Python :: 3.13
20
19
  Classifier: Topic :: Scientific/Engineering
21
20
  Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
22
21
  Classifier: Topic :: Scientific/Engineering :: Artificial Life
23
- Requires-Python: >=3.10
22
+ Requires-Python: >=3.11
24
23
  Requires-Dist: numpy
25
24
  Requires-Dist: pandas
26
25
  Requires-Dist: tqdm
@@ -81,7 +80,7 @@ Description-Content-Type: text/markdown
81
80
  | --- | --- |
82
81
  | CI/CD | [![GitHub Actions build status](https://github.com/projectmesa/mesa/workflows/build/badge.svg)](https://github.com/projectmesa/mesa/actions) [![Coverage status](https://codecov.io/gh/projectmesa/mesa/branch/main/graph/badge.svg)](https://codecov.io/gh/projectmesa/mesa) |
83
82
  | Package | [![PyPI - Version](https://img.shields.io/pypi/v/mesa.svg?logo=pypi&label=PyPI&logoColor=gold)](https://pypi.org/project/Mesa/) [![PyPI - Downloads](https://img.shields.io/pypi/dm/mesa.svg?color=blue&label=Downloads&logo=pypi&logoColor=gold)](https://pypi.org/project/Mesa/) [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/mesa.svg?logo=python&label=Python&logoColor=gold)](https://pypi.org/project/Mesa/) |
84
- | Meta | [![linting - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) |
83
+ | Meta | [![linting - Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff) [![code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black) [![Hatch project](https://img.shields.io/badge/%F0%9F%A5%9A-Hatch-4051b5.svg)](https://github.com/pypa/hatch) [![SPEC 0 — Minimum Supported Dependencies](https://img.shields.io/badge/SPEC-0-green?labelColor=%23004811&color=%235CA038)](https://scientific-python.org/specs/spec-0000/) |
85
84
  | Chat | [![chat](https://img.shields.io/matrix/project-mesa:matrix.org?label=chat&logo=Matrix)](https://matrix.to/#/#project-mesa:matrix.org) |
86
85
 
87
86
  Mesa allows users to quickly create agent-based models using built-in
@@ -1,10 +1,9 @@
1
- mesa/__init__.py,sha256=B-mERjSf7OMn3SyxAszE2eRL-UaEmQyhOwntfnXlack,657
2
- mesa/agent.py,sha256=k85bw5iVX0MmaVlJg3mG_liobN_sIVsrZf76YZ_hMyA,25289
1
+ mesa/__init__.py,sha256=yfsF1GQ64lI5t02W_YGXJWnGEiiyGvxJeIKP8Gkc43I,615
2
+ mesa/agent.py,sha256=_ZI_cR9ZR_mzEBrOXorFe3UlmeJM7e1-ODV8MoW6tRk,24406
3
3
  mesa/batchrunner.py,sha256=sMFLTxj5avP_-HGO0leLVuxXK2dH0xdPopvhAmawwRQ,7213
4
4
  mesa/datacollection.py,sha256=xyb07aBpd-HSDh5bk-XcVqGiDu5bfaLlxj5eDlGIwqY,16138
5
- mesa/model.py,sha256=Eb7wksREf882HGY-t7IwHVRIYDLzNaYOSmOLIdbaykA,10502
5
+ mesa/model.py,sha256=EzDYnwkt9Bm5fEah8JztzjghFBzUmnIr4WknbaR7zzk,8573
6
6
  mesa/space.py,sha256=cfzlRfy9chegp8d89k2aqI29jo9cb18USlz2G2iOZU4,64082
7
- mesa/time.py,sha256=KXwDqZHHvYbuqzB8zRbHglN23qVMfj8RXD-T_kJ3DfY,15022
8
7
  mesa/examples/README.md,sha256=dNn8kv0BNQem3NNhO5mbOANQoK8UUYOo7rnkCFV9tnE,2882
9
8
  mesa/examples/__init__.py,sha256=fP1vcexySnKowS2CKhfeyYLt058VpIkK26EOo3btyO0,825
10
9
  mesa/examples/advanced/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -67,7 +66,7 @@ mesa/experimental/solara_viz.py,sha256=uWrNQAX3oEWSftmyjNorN839dBCUp86hnhpL704dy
67
66
  mesa/experimental/cell_space/__init__.py,sha256=-NtSCT7mA-aszLSAdqbICGqeFe2vBdb-GrcW562legY,999
68
67
  mesa/experimental/cell_space/cell.py,sha256=lDm7NQhPDFf3-SZu5W594lDNGtzcdSPUSSsELFRg0bs,7166
69
68
  mesa/experimental/cell_space/cell_agent.py,sha256=jvYOV9OIaBaqAsAG0YLV64X_f3BJe_wP7qfos_RXi0Y,3759
70
- mesa/experimental/cell_space/cell_collection.py,sha256=MQNHj7n3b7wWLAZtTVQ9GeWjWcptwmROhQDckQr5TpM,4119
69
+ mesa/experimental/cell_space/cell_collection.py,sha256=ADK_42ykyWQpVGVSFE8GG_djqTgL8wfWwu2YTwXUPcs,4007
71
70
  mesa/experimental/cell_space/discrete_space.py,sha256=A54lHbTo4udcU4zkJEnsWuWKOBrELg-1eU8-dPoLMPk,5826
72
71
  mesa/experimental/cell_space/grid.py,sha256=oWTy6kaaHXLPneA-w5XdqzwA0YItMWYgCq_UjNH9iA8,7711
73
72
  mesa/experimental/cell_space/network.py,sha256=_x0zKlI-odNCSRb_Zqh4nBPjqnW5iVj8sVheKPCLzmU,1321
@@ -81,15 +80,15 @@ mesa/experimental/devs/examples/epstein_civil_violence.py,sha256=E8YSV3O5ihKsntG
81
80
  mesa/experimental/devs/examples/wolf_sheep.py,sha256=1eb1CfYNQoprqSJat-LPYPvwWH1ENQdj39viEqwSk0s,8103
82
81
  mesa/visualization/__init__.py,sha256=o30F2VApnDJ6Zq0_U5_wBnj-6-Bu7FGPsOq3A6p2EqA,743
83
82
  mesa/visualization/mpl_space_drawing.py,sha256=yf3Sc7pm5EJLxfWQdtR-9PcUtZveEEjar1WUr7qwI4o,20057
84
- mesa/visualization/solara_viz.py,sha256=M58P1U_x1IfV3Ha4E2ZVo3mocux3wWnEJqPKwNPBgZE,18117
83
+ mesa/visualization/solara_viz.py,sha256=pGtfjFfFMLwF9vmy5btiHRZww6NGoiKu0tzNdhZaYj4,17947
85
84
  mesa/visualization/user_param.py,sha256=Dl2WOwLYLf0pfLpabCZtIdFRyKZrK6Qtc3utZx5GPYg,2139
86
85
  mesa/visualization/utils.py,sha256=lJHgRKF5BHLf72Tw3YpwyiWuRoIimaTKQ7xBCw_Rx3A,146
87
86
  mesa/visualization/components/__init__.py,sha256=Bq3nrPikcaIo9BSs0O3zptWVLlUmAkLo3s0mEmpH1RE,3022
88
87
  mesa/visualization/components/altair_components.py,sha256=wotpFFQgMY-ZR3lNVm_fRos-iDg0Wjnj6Tk67_7f1SQ,5847
89
88
  mesa/visualization/components/matplotlib_components.py,sha256=xQETaFyHIfmL_9JwrLIgubuIQ7-pp7TMoXT1WMmozus,5441
90
- mesa-3.0.3.dist-info/METADATA,sha256=B9ZoDWevYunj_DI8ztkK_TYrhWPzfJ7LVVhfgEdpY5s,9782
91
- mesa-3.0.3.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
92
- mesa-3.0.3.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
93
- mesa-3.0.3.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
94
- mesa-3.0.3.dist-info/licenses/NOTICE,sha256=GbsWoK0QWv1JyZ_xer2s-jNilv0RtWl-0UrtlJANHPg,578
95
- mesa-3.0.3.dist-info/RECORD,,
89
+ mesa-3.1.0.dev0.dist-info/METADATA,sha256=iqUH3bKVcZW0XOY9KzYXKUBg3q3J2TO-EalHU9JIe-Q,9911
90
+ mesa-3.1.0.dev0.dist-info/WHEEL,sha256=3U_NnUcV_1B1kPkYaPzN-irRckL5VW_lytn0ytO_kRY,87
91
+ mesa-3.1.0.dev0.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
92
+ mesa-3.1.0.dev0.dist-info/licenses/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
93
+ mesa-3.1.0.dev0.dist-info/licenses/NOTICE,sha256=GbsWoK0QWv1JyZ_xer2s-jNilv0RtWl-0UrtlJANHPg,578
94
+ mesa-3.1.0.dev0.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: hatchling 1.26.3
2
+ Generator: hatchling 1.26.1
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
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
- )