Mesa 2.3.0.dev0__py3-none-any.whl → 2.3.0rc1__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 +2 -1
- mesa/agent.py +2 -16
- mesa/datacollection.py +1 -0
- mesa/experimental/__init__.py +4 -0
- mesa/experimental/cell_space/__init__.py +23 -0
- mesa/experimental/cell_space/cell.py +152 -0
- mesa/experimental/cell_space/cell_agent.py +37 -0
- mesa/experimental/cell_space/cell_collection.py +81 -0
- mesa/experimental/cell_space/discrete_space.py +64 -0
- mesa/experimental/cell_space/grid.py +204 -0
- mesa/experimental/cell_space/network.py +40 -0
- mesa/experimental/components/altair.py +13 -1
- mesa/experimental/components/matplotlib.py +3 -0
- mesa/experimental/devs/__init__.py +4 -0
- mesa/experimental/devs/eventlist.py +166 -0
- mesa/experimental/devs/examples/epstein_civil_violence.py +273 -0
- mesa/experimental/devs/examples/wolf_sheep.py +250 -0
- mesa/experimental/devs/simulator.py +293 -0
- mesa/experimental/jupyter_viz.py +51 -31
- mesa/model.py +2 -4
- mesa/space.py +26 -13
- mesa/time.py +3 -110
- {mesa-2.3.0.dev0.dist-info → mesa-2.3.0rc1.dist-info}/METADATA +4 -3
- mesa-2.3.0rc1.dist-info/RECORD +45 -0
- {mesa-2.3.0.dev0.dist-info → mesa-2.3.0rc1.dist-info}/WHEEL +1 -1
- mesa-2.3.0.dev0.dist-info/RECORD +0 -33
- {mesa-2.3.0.dev0.dist-info → mesa-2.3.0rc1.dist-info}/entry_points.txt +0 -0
- {mesa-2.3.0.dev0.dist-info → mesa-2.3.0rc1.dist-info}/licenses/LICENSE +0 -0
mesa/experimental/jupyter_viz.py
CHANGED
|
@@ -17,7 +17,7 @@ plt.switch_backend("agg")
|
|
|
17
17
|
# TODO: Turn this function into a Solara component once the current_step.value
|
|
18
18
|
# dependency is passed to measure()
|
|
19
19
|
def Card(
|
|
20
|
-
model, measures, agent_portrayal, space_drawer,
|
|
20
|
+
model, measures, agent_portrayal, space_drawer, dependencies, color, layout_type
|
|
21
21
|
):
|
|
22
22
|
with rv.Card(
|
|
23
23
|
style_=f"background-color: {color}; width: 100%; height: 100%"
|
|
@@ -27,11 +27,11 @@ def Card(
|
|
|
27
27
|
if space_drawer == "default":
|
|
28
28
|
# draw with the default implementation
|
|
29
29
|
components_matplotlib.SpaceMatplotlib(
|
|
30
|
-
model, agent_portrayal, dependencies=
|
|
30
|
+
model, agent_portrayal, dependencies=dependencies
|
|
31
31
|
)
|
|
32
32
|
elif space_drawer == "altair":
|
|
33
33
|
components_altair.SpaceAltair(
|
|
34
|
-
model, agent_portrayal, dependencies=
|
|
34
|
+
model, agent_portrayal, dependencies=dependencies
|
|
35
35
|
)
|
|
36
36
|
elif space_drawer:
|
|
37
37
|
# if specified, draw agent space with an alternate renderer
|
|
@@ -44,7 +44,7 @@ def Card(
|
|
|
44
44
|
measure(model)
|
|
45
45
|
else:
|
|
46
46
|
components_matplotlib.PlotMatplotlib(
|
|
47
|
-
model, measure, dependencies=
|
|
47
|
+
model, measure, dependencies=dependencies
|
|
48
48
|
)
|
|
49
49
|
return main
|
|
50
50
|
|
|
@@ -58,6 +58,7 @@ def JupyterViz(
|
|
|
58
58
|
agent_portrayal=None,
|
|
59
59
|
space_drawer="default",
|
|
60
60
|
play_interval=150,
|
|
61
|
+
seed=None,
|
|
61
62
|
):
|
|
62
63
|
"""Initialize a component to visualize a model.
|
|
63
64
|
Args:
|
|
@@ -71,6 +72,7 @@ def JupyterViz(
|
|
|
71
72
|
simulations with no space to visualize should
|
|
72
73
|
specify `space_drawer=False`
|
|
73
74
|
play_interval: play interval (default: 150)
|
|
75
|
+
seed: the random seed used to initialize the model
|
|
74
76
|
"""
|
|
75
77
|
if name is None:
|
|
76
78
|
name = model_class.__name__
|
|
@@ -78,6 +80,7 @@ def JupyterViz(
|
|
|
78
80
|
current_step = solara.use_reactive(0)
|
|
79
81
|
|
|
80
82
|
# 1. Set up model parameters
|
|
83
|
+
reactive_seed = solara.use_reactive(0)
|
|
81
84
|
user_params, fixed_params = split_model_params(model_params)
|
|
82
85
|
model_parameters, set_model_parameters = solara.use_state(
|
|
83
86
|
{**fixed_params, **{k: v.get("value") for k, v in user_params.items()}}
|
|
@@ -85,13 +88,21 @@ def JupyterViz(
|
|
|
85
88
|
|
|
86
89
|
# 2. Set up Model
|
|
87
90
|
def make_model():
|
|
88
|
-
model = model_class(
|
|
91
|
+
model = model_class.__new__(
|
|
92
|
+
model_class, **model_parameters, seed=reactive_seed.value
|
|
93
|
+
)
|
|
94
|
+
model.__init__(**model_parameters)
|
|
89
95
|
current_step.value = 0
|
|
90
96
|
return model
|
|
91
97
|
|
|
92
98
|
reset_counter = solara.use_reactive(0)
|
|
93
99
|
model = solara.use_memo(
|
|
94
|
-
make_model,
|
|
100
|
+
make_model,
|
|
101
|
+
dependencies=[
|
|
102
|
+
*list(model_parameters.values()),
|
|
103
|
+
reset_counter.value,
|
|
104
|
+
reactive_seed.value,
|
|
105
|
+
],
|
|
95
106
|
)
|
|
96
107
|
|
|
97
108
|
def handle_change_model_params(name: str, value: any):
|
|
@@ -103,8 +114,12 @@ def JupyterViz(
|
|
|
103
114
|
solara.AppBarTitle(name)
|
|
104
115
|
|
|
105
116
|
# render layout and plot
|
|
117
|
+
def do_reseed():
|
|
118
|
+
reactive_seed.value = model.random.random()
|
|
106
119
|
|
|
107
120
|
# jupyter
|
|
121
|
+
dependencies = [current_step.value, reactive_seed.value]
|
|
122
|
+
|
|
108
123
|
def render_in_jupyter():
|
|
109
124
|
with solara.GridFixed(columns=2):
|
|
110
125
|
UserInputs(user_params, on_change=handle_change_model_params)
|
|
@@ -116,11 +131,11 @@ def JupyterViz(
|
|
|
116
131
|
if space_drawer == "default":
|
|
117
132
|
# draw with the default implementation
|
|
118
133
|
components_matplotlib.SpaceMatplotlib(
|
|
119
|
-
model, agent_portrayal, dependencies=
|
|
134
|
+
model, agent_portrayal, dependencies=dependencies
|
|
120
135
|
)
|
|
121
136
|
elif space_drawer == "altair":
|
|
122
137
|
components_altair.SpaceAltair(
|
|
123
|
-
model, agent_portrayal, dependencies=
|
|
138
|
+
model, agent_portrayal, dependencies=dependencies
|
|
124
139
|
)
|
|
125
140
|
elif space_drawer:
|
|
126
141
|
# if specified, draw agent space with an alternate renderer
|
|
@@ -134,7 +149,7 @@ def JupyterViz(
|
|
|
134
149
|
measure(model)
|
|
135
150
|
else:
|
|
136
151
|
components_matplotlib.PlotMatplotlib(
|
|
137
|
-
model, measure, dependencies=
|
|
152
|
+
model, measure, dependencies=dependencies
|
|
138
153
|
)
|
|
139
154
|
|
|
140
155
|
def render_in_browser():
|
|
@@ -144,15 +159,21 @@ def JupyterViz(
|
|
|
144
159
|
if measures:
|
|
145
160
|
layout_types += [{"Measure": elem} for elem in range(len(measures))]
|
|
146
161
|
|
|
147
|
-
grid_layout_initial =
|
|
162
|
+
grid_layout_initial = make_initial_grid_layout(layout_types=layout_types)
|
|
148
163
|
grid_layout, set_grid_layout = solara.use_state(grid_layout_initial)
|
|
149
164
|
|
|
150
165
|
with solara.Sidebar():
|
|
151
166
|
with solara.Card("Controls", margin=1, elevation=2):
|
|
167
|
+
solara.InputText(
|
|
168
|
+
label="Seed",
|
|
169
|
+
value=reactive_seed,
|
|
170
|
+
continuous_update=True,
|
|
171
|
+
)
|
|
152
172
|
UserInputs(user_params, on_change=handle_change_model_params)
|
|
153
173
|
ModelController(model, play_interval, current_step, reset_counter)
|
|
154
|
-
|
|
155
|
-
|
|
174
|
+
solara.Button(label="Reseed", color="primary", on_click=do_reseed)
|
|
175
|
+
with solara.Card("Information", margin=1, elevation=2):
|
|
176
|
+
solara.Markdown(md_text=f"Step - {current_step}")
|
|
156
177
|
|
|
157
178
|
items = [
|
|
158
179
|
Card(
|
|
@@ -160,7 +181,7 @@ def JupyterViz(
|
|
|
160
181
|
measures,
|
|
161
182
|
agent_portrayal,
|
|
162
183
|
space_drawer,
|
|
163
|
-
|
|
184
|
+
dependencies,
|
|
164
185
|
color="white",
|
|
165
186
|
layout_type=layout_types[i],
|
|
166
187
|
)
|
|
@@ -241,7 +262,11 @@ def ModelController(model, play_interval, current_step, reset_counter):
|
|
|
241
262
|
solara.Style(
|
|
242
263
|
"""
|
|
243
264
|
.widget-play {
|
|
244
|
-
height:
|
|
265
|
+
height: 35px;
|
|
266
|
+
}
|
|
267
|
+
.widget-play button {
|
|
268
|
+
color: white;
|
|
269
|
+
background-color: #1976D2; // Solara blue color
|
|
245
270
|
}
|
|
246
271
|
"""
|
|
247
272
|
)
|
|
@@ -361,20 +386,15 @@ def make_text(renderer):
|
|
|
361
386
|
return function
|
|
362
387
|
|
|
363
388
|
|
|
364
|
-
def
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
else:
|
|
377
|
-
template_layout.update({"x": 6})
|
|
378
|
-
template_layout.update({"y": y_coord})
|
|
379
|
-
grid_lay.append(template_layout)
|
|
380
|
-
return grid_lay
|
|
389
|
+
def make_initial_grid_layout(layout_types):
|
|
390
|
+
return [
|
|
391
|
+
{
|
|
392
|
+
"i": i,
|
|
393
|
+
"w": 6,
|
|
394
|
+
"h": 10,
|
|
395
|
+
"moved": False,
|
|
396
|
+
"x": 6 * (i % 2),
|
|
397
|
+
"y": 16 * (i - i % 2),
|
|
398
|
+
}
|
|
399
|
+
for i in range(len(layout_types))
|
|
400
|
+
]
|
mesa/model.py
CHANGED
|
@@ -3,6 +3,7 @@ The model class for Mesa framework.
|
|
|
3
3
|
|
|
4
4
|
Core Objects: Model
|
|
5
5
|
"""
|
|
6
|
+
|
|
6
7
|
# Mypy; for the `|` operator purpose
|
|
7
8
|
# Remove this __future__ import once the oldest supported Python is 3.10
|
|
8
9
|
from __future__ import annotations
|
|
@@ -55,7 +56,7 @@ class Model:
|
|
|
55
56
|
if obj._seed is None:
|
|
56
57
|
# We explicitly specify the seed here so that we know its value in
|
|
57
58
|
# advance.
|
|
58
|
-
obj._seed = random.random()
|
|
59
|
+
obj._seed = random.random()
|
|
59
60
|
obj.random = random.Random(obj._seed)
|
|
60
61
|
# TODO: Remove these 2 lines just before Mesa 3.0
|
|
61
62
|
obj._steps = 0
|
|
@@ -76,9 +77,6 @@ class Model:
|
|
|
76
77
|
self._steps: int = 0
|
|
77
78
|
self._time: TimeT = 0 # the model's clock
|
|
78
79
|
|
|
79
|
-
# Warning flags for current experimental features. These make sure a warning is only printed once per model.
|
|
80
|
-
self.agentset_experimental_warning_given = False
|
|
81
|
-
|
|
82
80
|
@property
|
|
83
81
|
def agents(self) -> AgentSet:
|
|
84
82
|
"""Provides an AgentSet of all agents in the model, combining agents from all types."""
|
mesa/space.py
CHANGED
|
@@ -71,6 +71,21 @@ def is_integer(x: Real) -> bool:
|
|
|
71
71
|
return isinstance(x, _types_integer)
|
|
72
72
|
|
|
73
73
|
|
|
74
|
+
def warn_if_agent_has_position_already(placement_func):
|
|
75
|
+
def wrapper(self, agent, *args, **kwargs):
|
|
76
|
+
if agent.pos is not None:
|
|
77
|
+
warnings.warn(
|
|
78
|
+
f"""Agent {agent.unique_id} is being placed with
|
|
79
|
+
place_agent() despite already having the position {agent.pos}. In most
|
|
80
|
+
cases, you'd want to clear the current position with remove_agent()
|
|
81
|
+
before placing the agent again.""",
|
|
82
|
+
stacklevel=2,
|
|
83
|
+
)
|
|
84
|
+
placement_func(self, agent, *args, **kwargs)
|
|
85
|
+
|
|
86
|
+
return wrapper
|
|
87
|
+
|
|
88
|
+
|
|
74
89
|
class _Grid:
|
|
75
90
|
"""Base class for a rectangular grid.
|
|
76
91
|
|
|
@@ -135,14 +150,12 @@ class _Grid:
|
|
|
135
150
|
self._empties_built = True
|
|
136
151
|
|
|
137
152
|
@overload
|
|
138
|
-
def __getitem__(self, index: int | Sequence[Coordinate]) -> list[GridContent]:
|
|
139
|
-
...
|
|
153
|
+
def __getitem__(self, index: int | Sequence[Coordinate]) -> list[GridContent]: ...
|
|
140
154
|
|
|
141
155
|
@overload
|
|
142
156
|
def __getitem__(
|
|
143
157
|
self, index: tuple[int | slice, int | slice]
|
|
144
|
-
) -> GridContent | list[GridContent]:
|
|
145
|
-
...
|
|
158
|
+
) -> GridContent | list[GridContent]: ...
|
|
146
159
|
|
|
147
160
|
def __getitem__(self, index):
|
|
148
161
|
"""Access contents from the grid."""
|
|
@@ -405,11 +418,9 @@ class _Grid:
|
|
|
405
418
|
"""
|
|
406
419
|
return list(self.iter_cell_list_contents(cell_list))
|
|
407
420
|
|
|
408
|
-
def place_agent(self, agent: Agent, pos: Coordinate) -> None:
|
|
409
|
-
...
|
|
421
|
+
def place_agent(self, agent: Agent, pos: Coordinate) -> None: ...
|
|
410
422
|
|
|
411
|
-
def remove_agent(self, agent: Agent) -> None:
|
|
412
|
-
...
|
|
423
|
+
def remove_agent(self, agent: Agent) -> None: ...
|
|
413
424
|
|
|
414
425
|
def move_agent(self, agent: Agent, pos: Coordinate) -> None:
|
|
415
426
|
"""Move an agent from its current position to a new position.
|
|
@@ -763,16 +774,14 @@ class _PropertyGrid(_Grid):
|
|
|
763
774
|
|
|
764
775
|
Attributes:
|
|
765
776
|
properties (dict): A dictionary mapping property layer names to PropertyLayer instances.
|
|
766
|
-
empty_mask:
|
|
777
|
+
empty_mask (np.ndarray): A boolean array indicating empty cells on the grid.
|
|
767
778
|
|
|
768
779
|
Methods:
|
|
769
780
|
add_property_layer(property_layer): Adds a new property layer to the grid.
|
|
770
781
|
remove_property_layer(property_name): Removes a property layer from the grid by its name.
|
|
771
782
|
get_neighborhood_mask(pos, moore, include_center, radius): Generates a boolean mask of the neighborhood.
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
select_extreme_value_cells(property_name, mode, mask, return_list): Selects cells with extreme values of a property,
|
|
775
|
-
optionally with a mask, returning either a list of coordinates or a mask.
|
|
783
|
+
select_cells(conditions, extreme_values, masks, only_empty, return_list): Selects cells based on multiple conditions,
|
|
784
|
+
extreme values, masks, with an option to select only empty cells, returning either a list of coordinates or a mask.
|
|
776
785
|
|
|
777
786
|
Mask Usage:
|
|
778
787
|
Several methods in this class accept a mask as an input, which is a NumPy ndarray of boolean values. This mask
|
|
@@ -978,6 +987,7 @@ class SingleGrid(_PropertyGrid):
|
|
|
978
987
|
the grid for empty spaces.
|
|
979
988
|
"""
|
|
980
989
|
|
|
990
|
+
@warn_if_agent_has_position_already
|
|
981
991
|
def place_agent(self, agent: Agent, pos: Coordinate) -> None:
|
|
982
992
|
"""Place the agent at the specified location, and set its pos variable."""
|
|
983
993
|
if self.is_cell_empty(pos):
|
|
@@ -1026,6 +1036,7 @@ class MultiGrid(_PropertyGrid):
|
|
|
1026
1036
|
"""Default value for new cell elements."""
|
|
1027
1037
|
return []
|
|
1028
1038
|
|
|
1039
|
+
@warn_if_agent_has_position_already
|
|
1029
1040
|
def place_agent(self, agent: Agent, pos: Coordinate) -> None:
|
|
1030
1041
|
"""Place the agent at the specified location, and set its pos variable."""
|
|
1031
1042
|
x, y = pos
|
|
@@ -1357,6 +1368,7 @@ class ContinuousSpace:
|
|
|
1357
1368
|
self._agent_points = None
|
|
1358
1369
|
self._index_to_agent = {}
|
|
1359
1370
|
|
|
1371
|
+
@warn_if_agent_has_position_already
|
|
1360
1372
|
def place_agent(self, agent: Agent, pos: FloatCoordinate) -> None:
|
|
1361
1373
|
"""Place a new agent in the space.
|
|
1362
1374
|
|
|
@@ -1517,6 +1529,7 @@ class NetworkGrid:
|
|
|
1517
1529
|
"""Default value for a new node."""
|
|
1518
1530
|
return []
|
|
1519
1531
|
|
|
1532
|
+
@warn_if_agent_has_position_already
|
|
1520
1533
|
def place_agent(self, agent: Agent, node_id: int) -> None:
|
|
1521
1534
|
"""Place an agent in a node."""
|
|
1522
1535
|
self.G.nodes[node_id]["agent"].append(agent)
|
mesa/time.py
CHANGED
|
@@ -25,9 +25,7 @@ Key concepts:
|
|
|
25
25
|
# Remove this __future__ import once the oldest supported Python is 3.10
|
|
26
26
|
from __future__ import annotations
|
|
27
27
|
|
|
28
|
-
import heapq
|
|
29
28
|
import warnings
|
|
30
|
-
import weakref
|
|
31
29
|
from collections import defaultdict
|
|
32
30
|
from collections.abc import Iterable
|
|
33
31
|
|
|
@@ -393,46 +391,7 @@ class RandomActivationByType(BaseScheduler):
|
|
|
393
391
|
|
|
394
392
|
class DiscreteEventScheduler(BaseScheduler):
|
|
395
393
|
"""
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
This scheduler manages events where each event is associated with a
|
|
399
|
-
specific time and agent. The scheduler advances time not in fixed
|
|
400
|
-
increments, but to the moment the next event is scheduled to occur.
|
|
401
|
-
|
|
402
|
-
This implementation uses a priority queue (heapq) to manage events. Each
|
|
403
|
-
event is a tuple of the form (time, random_value, agent), where:
|
|
404
|
-
- time (float): The scheduled time for the event.
|
|
405
|
-
- random_value (float): A secondary sorting criterion to randomize
|
|
406
|
-
the order of events that are scheduled for the same time.
|
|
407
|
-
- agent (Agent): The agent associated with the event.
|
|
408
|
-
|
|
409
|
-
The random value for secondary sorting ensures that when two events are
|
|
410
|
-
scheduled for the same time, their execution order is randomized, thus
|
|
411
|
-
preventing direct comparison issues between different types of agents and
|
|
412
|
-
maintaining the integrity of the simulation's randomness.
|
|
413
|
-
|
|
414
|
-
Attributes:
|
|
415
|
-
model (Model): The model instance associated with the scheduler.
|
|
416
|
-
event_queue (list): A priority queue of scheduled events.
|
|
417
|
-
time_step (int or float): The fixed time period by which the model advances
|
|
418
|
-
on each step. Defaults to 1.
|
|
419
|
-
|
|
420
|
-
Methods:
|
|
421
|
-
schedule_event(time, agent): Schedule an event for a specific time.
|
|
422
|
-
schedule_in(delay, agent): Schedule an event after a specified delay.
|
|
423
|
-
step(): Execute all events within the next time_step period.
|
|
424
|
-
get_next_event_time(): Returns the time of the next scheduled event.
|
|
425
|
-
|
|
426
|
-
Usage:
|
|
427
|
-
1. Instantiate the DiscreteEventScheduler with a model instance and a time_step period.
|
|
428
|
-
2. Add agents to the scheduler using schedule.add(). With schedule_now=True (default),
|
|
429
|
-
the first event for the agent will be scheduled immediately.
|
|
430
|
-
3. In the Agent step() method, schedule the next event for the agent
|
|
431
|
-
(using schedule_in or schedule_event).
|
|
432
|
-
3. Add self.schedule.step() to the model's step() method, as usual.
|
|
433
|
-
|
|
434
|
-
Now, with each model step, the scheduler will execute all events within the
|
|
435
|
-
next time_step period, and advance time one time_step forward.
|
|
394
|
+
This class has been deprecated and replaced by the functionality provided by experimental.devs
|
|
436
395
|
"""
|
|
437
396
|
|
|
438
397
|
def __init__(self, model: Model, time_step: TimeT = 1) -> None:
|
|
@@ -444,72 +403,6 @@ class DiscreteEventScheduler(BaseScheduler):
|
|
|
444
403
|
|
|
445
404
|
"""
|
|
446
405
|
super().__init__(model)
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
warnings.warn(
|
|
451
|
-
"The DiscreteEventScheduler is experimental. It may be changed or removed in any and all future releases, including patch releases.\n"
|
|
452
|
-
"We would love to hear what you think about this new feature. If you have any thoughts, share them with us here: https://github.com/projectmesa/mesa/discussions/1923",
|
|
453
|
-
FutureWarning,
|
|
454
|
-
stacklevel=2,
|
|
406
|
+
raise Exception(
|
|
407
|
+
"DiscreteEventScheduler is deprecated in favor of the functionality provided by experimental.devs"
|
|
455
408
|
)
|
|
456
|
-
|
|
457
|
-
def schedule_event(self, time: TimeT, agent: Agent) -> None:
|
|
458
|
-
"""Schedule an event for an agent at a specific time."""
|
|
459
|
-
if time < self.time:
|
|
460
|
-
raise ValueError(
|
|
461
|
-
f"Scheduled time ({time}) must be >= the current time ({self.time})"
|
|
462
|
-
)
|
|
463
|
-
if agent not in self._agents:
|
|
464
|
-
raise ValueError(
|
|
465
|
-
"trying to schedule an event for agent which is not known to the scheduler"
|
|
466
|
-
)
|
|
467
|
-
|
|
468
|
-
# Create an event, sorted first on time, secondary on a random value
|
|
469
|
-
event = (time, self.model.random.random(), weakref.ref(agent))
|
|
470
|
-
heapq.heappush(self.event_queue, event)
|
|
471
|
-
|
|
472
|
-
def schedule_in(self, delay: TimeT, agent: Agent) -> None:
|
|
473
|
-
"""Schedule an event for an agent after a specified delay."""
|
|
474
|
-
if delay < 0:
|
|
475
|
-
raise ValueError("Delay must be non-negative")
|
|
476
|
-
event_time = self.time + delay
|
|
477
|
-
self.schedule_event(event_time, agent)
|
|
478
|
-
|
|
479
|
-
def step(self) -> None:
|
|
480
|
-
"""Execute the next event and advance the time."""
|
|
481
|
-
end_time = self.time + self.time_step
|
|
482
|
-
|
|
483
|
-
while self.event_queue and self.event_queue[0][0] <= end_time:
|
|
484
|
-
# Get the next event (ignore the random value during unpacking)
|
|
485
|
-
time, _, agent = heapq.heappop(self.event_queue)
|
|
486
|
-
agent = agent() # unpack weakref
|
|
487
|
-
|
|
488
|
-
if agent:
|
|
489
|
-
# Advance time to the event's time
|
|
490
|
-
self.time = time
|
|
491
|
-
# Execute the event
|
|
492
|
-
agent.step()
|
|
493
|
-
|
|
494
|
-
# After processing events, advance time by the time_step
|
|
495
|
-
self.time = end_time
|
|
496
|
-
self.steps += 1
|
|
497
|
-
|
|
498
|
-
def get_next_event_time(self) -> TimeT | None:
|
|
499
|
-
"""Returns the time of the next scheduled event."""
|
|
500
|
-
if not self.event_queue:
|
|
501
|
-
return None
|
|
502
|
-
return self.event_queue[0][0]
|
|
503
|
-
|
|
504
|
-
def add(self, agent: Agent, schedule_now: bool = True) -> None:
|
|
505
|
-
"""Add an Agent object to the schedule and optionally schedule its first event.
|
|
506
|
-
|
|
507
|
-
Args:
|
|
508
|
-
agent: An Agent to be added to the schedule. Must have a step() method.
|
|
509
|
-
schedule_now: If True, schedules the first event for the agent immediately.
|
|
510
|
-
"""
|
|
511
|
-
super().add(agent) # Call the add method from BaseScheduler
|
|
512
|
-
|
|
513
|
-
if schedule_now:
|
|
514
|
-
# Schedule the first event immediately
|
|
515
|
-
self.schedule_event(self.time, agent)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: Mesa
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.0rc1
|
|
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
|
|
@@ -34,6 +34,7 @@ Requires-Dist: tqdm
|
|
|
34
34
|
Provides-Extra: dev
|
|
35
35
|
Requires-Dist: coverage; extra == 'dev'
|
|
36
36
|
Requires-Dist: pytest-cov; extra == 'dev'
|
|
37
|
+
Requires-Dist: pytest-mock; extra == 'dev'
|
|
37
38
|
Requires-Dist: pytest>=4.6; extra == 'dev'
|
|
38
39
|
Requires-Dist: ruff~=0.1.1; extra == 'dev'
|
|
39
40
|
Requires-Dist: sphinx; extra == 'dev'
|
|
@@ -162,7 +163,7 @@ If you would like to add a feature, please reach out via [ticket](https://github
|
|
|
162
163
|
join a dev session (see [Mesa discussions](https://github.com/projectmesa/mesa/discussions)). A feature is most likely
|
|
163
164
|
to be added if you build it!
|
|
164
165
|
|
|
165
|
-
Don't forget to checkout the [Contributors guide](https://github.com/projectmesa/mesa/blob/main/CONTRIBUTING.
|
|
166
|
+
Don't forget to checkout the [Contributors guide](https://github.com/projectmesa/mesa/blob/main/CONTRIBUTING.md).
|
|
166
167
|
|
|
167
168
|
## Citing Mesa
|
|
168
169
|
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
mesa/__init__.py,sha256=zv1ix1DhUDQ7zQETpTWaa_9Dk74c4kWbJlkha7OM9W0,684
|
|
2
|
+
mesa/agent.py,sha256=L_kHMM3lTVLq8EM3UsX1xVLLo-P5IqFrJEqg83gT7xs,12902
|
|
3
|
+
mesa/batchrunner.py,sha256=2A1_FbFlSCkDm8xfv1ZamFiBE4VYce8sKP5SR_CledE,6087
|
|
4
|
+
mesa/datacollection.py,sha256=Iwz8Nuib1Fmo8sJ0ng-qstBhAkdKXn-CXomylCkCRGQ,11195
|
|
5
|
+
mesa/main.py,sha256=7MovfNz88VWNnfXP0kcERB6C3GfkVOh0hb0o32hM9LU,1602
|
|
6
|
+
mesa/model.py,sha256=RxTCJUBfEgRIu3dXiMK9oMlxS3owwNQaQIrVRs6HsZY,5823
|
|
7
|
+
mesa/space.py,sha256=A3QUZV7BLbo29dQhvorsgCAFxnJ2i21a5Vxd53wbIWo,62429
|
|
8
|
+
mesa/time.py,sha256=G83UKWeMFMnQV9-79Ps2gbD_Qz3hM07IFYLzf5Rvz1w,15243
|
|
9
|
+
mesa/cookiecutter-mesa/cookiecutter.json,sha256=tBSWli39fOWUXGfiDCTKd92M7uKaBIswXbkOdbUufYY,337
|
|
10
|
+
mesa/cookiecutter-mesa/hooks/post_gen_project.py,sha256=8JoXZKIioRYEWJURC0udj8WS3rg0c4So62sOZSGbrMY,294
|
|
11
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md,sha256=Yji4lGY-NtQSnW-oBj0_Jhs-XhCfZA8R1mBBM_IllGs,80
|
|
12
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate,sha256=7bYSRdq83--T1gnEuFls5aunb7lLNtUKFS2MUrovEg0,74
|
|
13
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate,sha256=UtRpLM_CkeUZRec-Ef_LiO_x7SKaWN11fOiH9T1UmTw,214
|
|
14
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate,sha256=gTxSZ9t8My_Qiwuusqaf8DmspncrLKptxbg9cvfbloc,1842
|
|
16
|
+
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate,sha256=nqi6cPjhiyrYw82_Y9hLSrfZtSVCGIhMLOXRB7kKTlQ,823
|
|
17
|
+
mesa/experimental/UserParam.py,sha256=kpFu5yH_RSTI4S2XQxYAGWJgGwQo6j-yCADYJHry4lE,1641
|
|
18
|
+
mesa/experimental/__init__.py,sha256=ncy-EG4IMoSzknYXLMt3Z61nkMwkrH96QbJpGUqOpxQ,168
|
|
19
|
+
mesa/experimental/jupyter_viz.py,sha256=q2Xn-QjH0nqJoxY1RV_SSHC_OgMFZyKWHJjrn5jHCRk,13444
|
|
20
|
+
mesa/experimental/cell_space/__init__.py,sha256=trFVKf2l5RbkCUyxP09Kox_J3ak2YdM4o3t40Tsjjm4,628
|
|
21
|
+
mesa/experimental/cell_space/cell.py,sha256=AUnvVnXWhdgzr0bLKDRDO9c93v22Zkw6W-tWxhEhGdQ,4578
|
|
22
|
+
mesa/experimental/cell_space/cell_agent.py,sha256=G4u9ht4gW9ns1y2L7pFumF3K4HiP6ROuxwrxHZ-mL1M,1107
|
|
23
|
+
mesa/experimental/cell_space/cell_collection.py,sha256=Rjho3zIyKaamO2X9XEZu16qlmEP41tIHyhMST2KHK4o,2293
|
|
24
|
+
mesa/experimental/cell_space/discrete_space.py,sha256=ta__YojsrrhWL4DgMzUqZpSgbeexKMrA6bxlYPJGfK0,1921
|
|
25
|
+
mesa/experimental/cell_space/grid.py,sha256=IltngMSlMwLsJSNYrs6B4J5ylUbL5Vk1vPX_OhWGzTs,6949
|
|
26
|
+
mesa/experimental/cell_space/network.py,sha256=NWEdROFyO18pHOTb6_t9zjjUyGhAztPJm8a9b21c8ZU,1195
|
|
27
|
+
mesa/experimental/components/altair.py,sha256=_atxt79OpRjykAEwockKm0K9dp1jDPmcLeKqffZ0Bfo,2397
|
|
28
|
+
mesa/experimental/components/matplotlib.py,sha256=DVf41wdN1J56IQJXaeonqL4rcwYlsUIUB7C6r5ozdfg,4325
|
|
29
|
+
mesa/experimental/devs/__init__.py,sha256=CWam15vCj-RD_biMyqv4sJfos1fsL823P7MDEGrbwW8,174
|
|
30
|
+
mesa/experimental/devs/eventlist.py,sha256=H9hufe9VmwvlXQr146wCa7PgbzVvivG4Bk9rlEERZ7A,4880
|
|
31
|
+
mesa/experimental/devs/simulator.py,sha256=NQ3rtBIzykBtMWNslG_Fg04NQn2lYT8cmH-7ndr8v0Y,9530
|
|
32
|
+
mesa/experimental/devs/examples/epstein_civil_violence.py,sha256=KqH9KI-A_BYt7oWi9kaOhTzjrf2pETqzSpAQG8ewud0,9667
|
|
33
|
+
mesa/experimental/devs/examples/wolf_sheep.py,sha256=h5z-eDqMpYeOjrq293N2BcQbs_LDVsgtg9vblXJM7XQ,7697
|
|
34
|
+
mesa/flat/__init__.py,sha256=hSqQDjkfIgDu7B3aYtjPeNEUXdlKPHQuNN8HEV0XR6s,218
|
|
35
|
+
mesa/flat/visualization.py,sha256=5aCm8xDCmZij3hoJZvOVmmpzU9ACXSSSvmQr51buLVg,290
|
|
36
|
+
mesa/visualization/ModularVisualization.py,sha256=gT-FYRnvTFFxtehu-N-8eBLShCF8t_Ut8Au4iVPZIlA,60
|
|
37
|
+
mesa/visualization/TextVisualization.py,sha256=BIP0XcmIdYhz0igqe8yRZXlXeOOqJZeu8q9XViJTuLc,57
|
|
38
|
+
mesa/visualization/UserParam.py,sha256=D3qxoX-Cpqhyn06IdIO_C5s0u8nlhv3988lVwkBlcGo,49
|
|
39
|
+
mesa/visualization/__init__.py,sha256=5fwVAzgVsmxAzgoLxdC26l2ZE-m2bWj963xPNSDaQEQ,287
|
|
40
|
+
mesa/visualization/modules.py,sha256=pf6K3KECX51VNNqpFCm2EE5KV0A22UYmfXzTVXPnF_o,47
|
|
41
|
+
mesa-2.3.0rc1.dist-info/METADATA,sha256=AhVQypSMMIg-Tji_x28Qid8g82bn2LSW-UTgcaZ5t4A,7868
|
|
42
|
+
mesa-2.3.0rc1.dist-info/WHEEL,sha256=osohxoshIHTFJFVPhsi1UkZuLRGMHRXZzwEBW2ezjrc,87
|
|
43
|
+
mesa-2.3.0rc1.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
|
|
44
|
+
mesa-2.3.0rc1.dist-info/licenses/LICENSE,sha256=OGUgret9fRrm8J3pdsPXETIjf0H8puK_Nmy970ZzT78,572
|
|
45
|
+
mesa-2.3.0rc1.dist-info/RECORD,,
|
mesa-2.3.0.dev0.dist-info/RECORD
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
mesa/__init__.py,sha256=OmOIqjTe0C6Mb0z3i5e4e-9To1n-zkoGkSTpbI2NPm8,683
|
|
2
|
-
mesa/agent.py,sha256=94Z_DAqBkoSGwI0S12afdJeC7LIwhPC9pFwtEMNUahw,13794
|
|
3
|
-
mesa/batchrunner.py,sha256=2A1_FbFlSCkDm8xfv1ZamFiBE4VYce8sKP5SR_CledE,6087
|
|
4
|
-
mesa/datacollection.py,sha256=kJc0nzp4anAl9v0X6c9GE-e8_ZMogctD3YxjPTRypd0,11194
|
|
5
|
-
mesa/main.py,sha256=7MovfNz88VWNnfXP0kcERB6C3GfkVOh0hb0o32hM9LU,1602
|
|
6
|
-
mesa/model.py,sha256=tmRM_ea7w4mt8XQFIZJQTa7JX4xu24ASwwLwmFm6shQ,6011
|
|
7
|
-
mesa/space.py,sha256=XDdLFAtQv0gbkzlXAafwrlrs4fUkrZdjJ02O-bJJFp8,61935
|
|
8
|
-
mesa/time.py,sha256=BAchKHNFh0ggQqaIMvojHhAF6I3zSQRlDXhQCyKq0NU,20208
|
|
9
|
-
mesa/cookiecutter-mesa/cookiecutter.json,sha256=tBSWli39fOWUXGfiDCTKd92M7uKaBIswXbkOdbUufYY,337
|
|
10
|
-
mesa/cookiecutter-mesa/hooks/post_gen_project.py,sha256=8JoXZKIioRYEWJURC0udj8WS3rg0c4So62sOZSGbrMY,294
|
|
11
|
-
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md,sha256=Yji4lGY-NtQSnW-oBj0_Jhs-XhCfZA8R1mBBM_IllGs,80
|
|
12
|
-
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate,sha256=7bYSRdq83--T1gnEuFls5aunb7lLNtUKFS2MUrovEg0,74
|
|
13
|
-
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate,sha256=UtRpLM_CkeUZRec-Ef_LiO_x7SKaWN11fOiH9T1UmTw,214
|
|
14
|
-
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
15
|
-
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate,sha256=gTxSZ9t8My_Qiwuusqaf8DmspncrLKptxbg9cvfbloc,1842
|
|
16
|
-
mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate,sha256=nqi6cPjhiyrYw82_Y9hLSrfZtSVCGIhMLOXRB7kKTlQ,823
|
|
17
|
-
mesa/experimental/UserParam.py,sha256=kpFu5yH_RSTI4S2XQxYAGWJgGwQo6j-yCADYJHry4lE,1641
|
|
18
|
-
mesa/experimental/__init__.py,sha256=mwy0XqXikmjXorHKPEKr7hoZLzV6IPVtRukfMQeczcc,63
|
|
19
|
-
mesa/experimental/jupyter_viz.py,sha256=AE8S6qbco1cKo08S5SLDn1HaaJiwwPSbRqosmLQ4SI0,13007
|
|
20
|
-
mesa/experimental/components/altair.py,sha256=L9BeFQBt7f3nrR1xpzByUgEtFGbm0ryP8Qk-G_tb2-0,1900
|
|
21
|
-
mesa/experimental/components/matplotlib.py,sha256=Ga3FEu1LxNJ7-d3QhRPW0GOcm6hLoXHHxwtrqAyQHGc,4157
|
|
22
|
-
mesa/flat/__init__.py,sha256=hSqQDjkfIgDu7B3aYtjPeNEUXdlKPHQuNN8HEV0XR6s,218
|
|
23
|
-
mesa/flat/visualization.py,sha256=5aCm8xDCmZij3hoJZvOVmmpzU9ACXSSSvmQr51buLVg,290
|
|
24
|
-
mesa/visualization/ModularVisualization.py,sha256=gT-FYRnvTFFxtehu-N-8eBLShCF8t_Ut8Au4iVPZIlA,60
|
|
25
|
-
mesa/visualization/TextVisualization.py,sha256=BIP0XcmIdYhz0igqe8yRZXlXeOOqJZeu8q9XViJTuLc,57
|
|
26
|
-
mesa/visualization/UserParam.py,sha256=D3qxoX-Cpqhyn06IdIO_C5s0u8nlhv3988lVwkBlcGo,49
|
|
27
|
-
mesa/visualization/__init__.py,sha256=5fwVAzgVsmxAzgoLxdC26l2ZE-m2bWj963xPNSDaQEQ,287
|
|
28
|
-
mesa/visualization/modules.py,sha256=pf6K3KECX51VNNqpFCm2EE5KV0A22UYmfXzTVXPnF_o,47
|
|
29
|
-
mesa-2.3.0.dev0.dist-info/METADATA,sha256=jwk3kleNbWc8dTSwEy-A89xN1DGFsOd_au7MBIA-GJk,7828
|
|
30
|
-
mesa-2.3.0.dev0.dist-info/WHEEL,sha256=TJPnKdtrSue7xZ_AVGkp9YXcvDrobsjBds1du3Nx6dc,87
|
|
31
|
-
mesa-2.3.0.dev0.dist-info/entry_points.txt,sha256=IOcQtetGF8l4wHpOs_hGb19Rz-FS__BMXOJR10IBPsA,39
|
|
32
|
-
mesa-2.3.0.dev0.dist-info/licenses/LICENSE,sha256=OGUgret9fRrm8J3pdsPXETIjf0H8puK_Nmy970ZzT78,572
|
|
33
|
-
mesa-2.3.0.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|