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/time.py
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
"""
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
The time module and all its Schedulers are deprecated and will be removed in a future version.
|
|
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
|
|
1
|
+
"""
|
|
2
|
+
Mesa Time Module
|
|
3
|
+
================
|
|
7
4
|
|
|
8
5
|
Objects for handling the time component of a model. In particular, this module
|
|
9
6
|
contains Schedulers, which handle agent activation. A Scheduler is an object
|
|
@@ -42,16 +39,23 @@ TimeT = float | int
|
|
|
42
39
|
|
|
43
40
|
|
|
44
41
|
class BaseScheduler:
|
|
45
|
-
"""
|
|
42
|
+
"""
|
|
43
|
+
A simple scheduler that activates agents one at a time, in the order they were added.
|
|
46
44
|
|
|
47
45
|
This scheduler is designed to replicate the behavior of the scheduler in MASON, a multi-agent simulation toolkit.
|
|
48
46
|
It assumes that each agent added has a `step` method which takes no arguments and executes the agent's actions.
|
|
49
47
|
|
|
50
48
|
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
|
-
|
|
49
|
+
- model (Model): The model instance associated with the scheduler.
|
|
50
|
+
- steps (int): The number of steps the scheduler has taken.
|
|
51
|
+
- time (TimeT): The current time in the simulation. Can be an integer or a float.
|
|
52
|
+
|
|
53
|
+
Methods:
|
|
54
|
+
- add: Adds an agent to the scheduler.
|
|
55
|
+
- remove: Removes an agent from the scheduler.
|
|
56
|
+
- step: Executes a step, which involves activating each agent once.
|
|
57
|
+
- get_agent_count: Returns the number of agents in the scheduler.
|
|
58
|
+
- agents (property): Returns a list of all agent instances.
|
|
55
59
|
"""
|
|
56
60
|
|
|
57
61
|
def __init__(self, model: Model, agents: Iterable[Agent] | None = None) -> None:
|
|
@@ -62,22 +66,16 @@ class BaseScheduler:
|
|
|
62
66
|
agents (Iterable[Agent], None, optional): An iterable of agents who are controlled by the schedule
|
|
63
67
|
|
|
64
68
|
"""
|
|
65
|
-
warnings.warn(
|
|
66
|
-
"The time module and all its Schedulers are deprecated and will be removed in a future version. "
|
|
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
69
|
self.model = model
|
|
74
70
|
self.steps = 0
|
|
75
71
|
self.time: TimeT = 0
|
|
72
|
+
self._original_step = self.step
|
|
73
|
+
self.step = self._wrapped_step
|
|
76
74
|
|
|
77
75
|
if agents is None:
|
|
78
76
|
agents = []
|
|
79
77
|
|
|
80
|
-
self._agents: AgentSet = AgentSet(agents, model
|
|
78
|
+
self._agents: AgentSet = AgentSet(agents, model)
|
|
81
79
|
|
|
82
80
|
self._remove_warning_given = False
|
|
83
81
|
self._agents_key_warning_given = False
|
|
@@ -86,8 +84,10 @@ class BaseScheduler:
|
|
|
86
84
|
"""Add an Agent object to the schedule.
|
|
87
85
|
|
|
88
86
|
Args:
|
|
89
|
-
agent
|
|
87
|
+
agent: An Agent to be added to the schedule. NOTE: The agent must
|
|
88
|
+
have a step() method.
|
|
90
89
|
"""
|
|
90
|
+
|
|
91
91
|
if agent not in self._agents:
|
|
92
92
|
self._agents.add(agent)
|
|
93
93
|
else:
|
|
@@ -96,13 +96,12 @@ class BaseScheduler:
|
|
|
96
96
|
def remove(self, agent: Agent) -> None:
|
|
97
97
|
"""Remove all instances of a given agent from the schedule.
|
|
98
98
|
|
|
99
|
-
Args:
|
|
100
|
-
agent: An `Agent` instance.
|
|
101
|
-
|
|
102
99
|
Note:
|
|
103
100
|
It is only necessary to explicitly remove agents from the schedule if
|
|
104
101
|
the agent is not removed from the model.
|
|
105
102
|
|
|
103
|
+
Args:
|
|
104
|
+
agent: An agent object.
|
|
106
105
|
"""
|
|
107
106
|
self._agents.remove(agent)
|
|
108
107
|
|
|
@@ -114,18 +113,21 @@ class BaseScheduler:
|
|
|
114
113
|
self.steps += 1
|
|
115
114
|
self.time += 1
|
|
116
115
|
|
|
116
|
+
def _wrapped_step(self):
|
|
117
|
+
"""Wrapper for the step method to include time and step updating."""
|
|
118
|
+
self._original_step()
|
|
119
|
+
self.model._advance_time()
|
|
120
|
+
|
|
117
121
|
def get_agent_count(self) -> int:
|
|
118
122
|
"""Returns the current number of agents in the queue."""
|
|
119
123
|
return len(self._agents)
|
|
120
124
|
|
|
121
125
|
@property
|
|
122
126
|
def agents(self) -> AgentSet:
|
|
123
|
-
"""Return agents in the scheduler."""
|
|
124
127
|
# a bit dirty, but returns a copy of the internal agent set
|
|
125
128
|
return self._agents.select()
|
|
126
129
|
|
|
127
130
|
def get_agent_keys(self, shuffle: bool = False) -> list[int]:
|
|
128
|
-
"""Deprecated."""
|
|
129
131
|
# To be able to remove and/or add agents during stepping
|
|
130
132
|
# it's necessary to cast the keys view to a list.
|
|
131
133
|
|
|
@@ -143,21 +145,14 @@ class BaseScheduler:
|
|
|
143
145
|
return agent_keys
|
|
144
146
|
|
|
145
147
|
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
148
|
if shuffle:
|
|
155
149
|
self._agents.shuffle(inplace=True)
|
|
156
150
|
self._agents.do(method)
|
|
157
151
|
|
|
158
152
|
|
|
159
153
|
class RandomActivation(BaseScheduler):
|
|
160
|
-
"""
|
|
154
|
+
"""
|
|
155
|
+
A scheduler that activates each agent once per step, in a random order, with the order reshuffled each step.
|
|
161
156
|
|
|
162
157
|
This scheduler is equivalent to the NetLogo 'ask agents...' behavior and is a common default for ABMs.
|
|
163
158
|
It assumes that all agents have a `step` method.
|
|
@@ -167,17 +162,23 @@ class RandomActivation(BaseScheduler):
|
|
|
167
162
|
|
|
168
163
|
Inherits all attributes and methods from BaseScheduler.
|
|
169
164
|
|
|
165
|
+
Methods:
|
|
166
|
+
- step: Executes a step, activating each agent in a random order.
|
|
170
167
|
"""
|
|
171
168
|
|
|
172
169
|
def step(self) -> None:
|
|
173
|
-
"""Executes the step of all agents, one at a time, in
|
|
170
|
+
"""Executes the step of all agents, one at a time, in
|
|
171
|
+
random order.
|
|
172
|
+
|
|
173
|
+
"""
|
|
174
174
|
self.do_each("step", shuffle=True)
|
|
175
175
|
self.steps += 1
|
|
176
176
|
self.time += 1
|
|
177
177
|
|
|
178
178
|
|
|
179
179
|
class SimultaneousActivation(BaseScheduler):
|
|
180
|
-
"""
|
|
180
|
+
"""
|
|
181
|
+
A scheduler that simulates the simultaneous activation of all agents.
|
|
181
182
|
|
|
182
183
|
This scheduler is unique in that it requires agents to have both `step` and `advance` methods.
|
|
183
184
|
- The `step` method is for activating the agent and staging any changes without applying them immediately.
|
|
@@ -188,6 +189,8 @@ class SimultaneousActivation(BaseScheduler):
|
|
|
188
189
|
|
|
189
190
|
Inherits all attributes and methods from BaseScheduler.
|
|
190
191
|
|
|
192
|
+
Methods:
|
|
193
|
+
- step: Executes a step for all agents, first calling `step` then `advance` on each.
|
|
191
194
|
"""
|
|
192
195
|
|
|
193
196
|
def step(self) -> None:
|
|
@@ -202,9 +205,9 @@ class SimultaneousActivation(BaseScheduler):
|
|
|
202
205
|
|
|
203
206
|
|
|
204
207
|
class StagedActivation(BaseScheduler):
|
|
205
|
-
"""
|
|
206
|
-
|
|
207
|
-
|
|
208
|
+
"""
|
|
209
|
+
A scheduler allowing agent activation to be divided into several stages, with all agents executing one stage
|
|
210
|
+
before moving on to the next. This class is a generalization of SimultaneousActivation.
|
|
208
211
|
|
|
209
212
|
This scheduler is useful for complex models where actions need to be broken down into distinct phases
|
|
210
213
|
for each agent in each time step. Agents must implement methods for each defined stage.
|
|
@@ -219,6 +222,8 @@ class StagedActivation(BaseScheduler):
|
|
|
219
222
|
- shuffle (bool): Determines whether to shuffle the order of agents each step.
|
|
220
223
|
- shuffle_between_stages (bool): Determines whether to shuffle agents between each stage.
|
|
221
224
|
|
|
225
|
+
Methods:
|
|
226
|
+
- step: Executes all the stages for all agents in the defined order.
|
|
222
227
|
"""
|
|
223
228
|
|
|
224
229
|
def __init__(
|
|
@@ -263,7 +268,8 @@ class StagedActivation(BaseScheduler):
|
|
|
263
268
|
|
|
264
269
|
|
|
265
270
|
class RandomActivationByType(BaseScheduler):
|
|
266
|
-
"""
|
|
271
|
+
"""
|
|
272
|
+
A scheduler that activates each type of agent once per step, in random order, with the order reshuffled every step.
|
|
267
273
|
|
|
268
274
|
This scheduler is useful for models with multiple types of agents, ensuring that each type is treated
|
|
269
275
|
equitably in terms of activation order. The randomness in activation order helps in reducing biases
|
|
@@ -279,10 +285,14 @@ class RandomActivationByType(BaseScheduler):
|
|
|
279
285
|
Attributes:
|
|
280
286
|
- agents_by_type (defaultdict): A dictionary mapping agent types to dictionaries of agents.
|
|
281
287
|
|
|
288
|
+
Methods:
|
|
289
|
+
- step: Executes the step of each agent type in a random order.
|
|
290
|
+
- step_type: Activates all agents of a given type.
|
|
291
|
+
- get_type_count: Returns the count of agents of a specific type.
|
|
282
292
|
"""
|
|
283
293
|
|
|
284
294
|
@property
|
|
285
|
-
def agents_by_type(self):
|
|
295
|
+
def agents_by_type(self):
|
|
286
296
|
warnings.warn(
|
|
287
297
|
"Because of the shift to using AgentSet, in the future this attribute will return a dict with"
|
|
288
298
|
"type as key as AgentSet as value. Future behavior is available via RandomActivationByType._agents_by_type",
|
|
@@ -297,13 +307,14 @@ class RandomActivationByType(BaseScheduler):
|
|
|
297
307
|
return agentsbytype
|
|
298
308
|
|
|
299
309
|
def __init__(self, model: Model, agents: Iterable[Agent] | None = None) -> None:
|
|
300
|
-
|
|
310
|
+
super().__init__(model, agents)
|
|
311
|
+
"""
|
|
301
312
|
|
|
302
313
|
Args:
|
|
303
314
|
model (Model): The model to which the schedule belongs
|
|
304
315
|
agents (Iterable[Agent], None, optional): An iterable of agents who are controlled by the schedule
|
|
305
316
|
"""
|
|
306
|
-
|
|
317
|
+
|
|
307
318
|
# can't be a defaultdict because we need to pass model to AgentSet
|
|
308
319
|
self._agents_by_type: [type, AgentSet] = {}
|
|
309
320
|
|
|
@@ -312,12 +323,11 @@ class RandomActivationByType(BaseScheduler):
|
|
|
312
323
|
try:
|
|
313
324
|
self._agents_by_type[type(agent)].add(agent)
|
|
314
325
|
except KeyError:
|
|
315
|
-
self._agents_by_type[type(agent)] = AgentSet(
|
|
316
|
-
[agent], self.model.random
|
|
317
|
-
)
|
|
326
|
+
self._agents_by_type[type(agent)] = AgentSet([agent], self.model)
|
|
318
327
|
|
|
319
328
|
def add(self, agent: Agent) -> None:
|
|
320
|
-
"""
|
|
329
|
+
"""
|
|
330
|
+
Add an Agent object to the schedule
|
|
321
331
|
|
|
322
332
|
Args:
|
|
323
333
|
agent: An Agent to be added to the schedule.
|
|
@@ -327,24 +337,24 @@ class RandomActivationByType(BaseScheduler):
|
|
|
327
337
|
try:
|
|
328
338
|
self._agents_by_type[type(agent)].add(agent)
|
|
329
339
|
except KeyError:
|
|
330
|
-
self._agents_by_type[type(agent)] = AgentSet([agent], self.model
|
|
340
|
+
self._agents_by_type[type(agent)] = AgentSet([agent], self.model)
|
|
331
341
|
|
|
332
342
|
def remove(self, agent: Agent) -> None:
|
|
333
|
-
"""
|
|
334
|
-
|
|
335
|
-
Args:
|
|
336
|
-
agent: An Agent to be removed from the schedule.
|
|
337
|
-
|
|
343
|
+
"""
|
|
344
|
+
Remove all instances of a given agent from the schedule.
|
|
338
345
|
"""
|
|
339
346
|
super().remove(agent)
|
|
340
347
|
self._agents_by_type[type(agent)].remove(agent)
|
|
341
348
|
|
|
342
349
|
def step(self, shuffle_types: bool = True, shuffle_agents: bool = True) -> None:
|
|
343
|
-
"""
|
|
350
|
+
"""
|
|
351
|
+
Executes the step of each agent type, one at a time, in random order.
|
|
344
352
|
|
|
345
353
|
Args:
|
|
346
|
-
shuffle_types: If True, the order of execution of each types is
|
|
347
|
-
|
|
354
|
+
shuffle_types: If True, the order of execution of each types is
|
|
355
|
+
shuffled.
|
|
356
|
+
shuffle_agents: If True, the order of execution of each agents in a
|
|
357
|
+
type group is shuffled.
|
|
348
358
|
"""
|
|
349
359
|
# To be able to remove and/or add agents during stepping
|
|
350
360
|
# it's necessary to cast the keys view to a list.
|
|
@@ -357,11 +367,12 @@ class RandomActivationByType(BaseScheduler):
|
|
|
357
367
|
self.time += 1
|
|
358
368
|
|
|
359
369
|
def step_type(self, agenttype: type[Agent], shuffle_agents: bool = True) -> None:
|
|
360
|
-
"""
|
|
370
|
+
"""
|
|
371
|
+
Shuffle order and run all agents of a given type.
|
|
372
|
+
This method is equivalent to the NetLogo 'ask [breed]...'.
|
|
361
373
|
|
|
362
374
|
Args:
|
|
363
375
|
agenttype: Class object of the type to run.
|
|
364
|
-
shuffle_agents: If True, shuffle agents
|
|
365
376
|
"""
|
|
366
377
|
agents = self._agents_by_type[agenttype]
|
|
367
378
|
|
|
@@ -370,15 +381,19 @@ class RandomActivationByType(BaseScheduler):
|
|
|
370
381
|
agents.do("step")
|
|
371
382
|
|
|
372
383
|
def get_type_count(self, agenttype: type[Agent]) -> int:
|
|
373
|
-
"""
|
|
384
|
+
"""
|
|
385
|
+
Returns the current number of agents of certain type in the queue.
|
|
386
|
+
"""
|
|
374
387
|
return len(self._agents_by_type[agenttype])
|
|
375
388
|
|
|
376
389
|
|
|
377
390
|
class DiscreteEventScheduler(BaseScheduler):
|
|
378
|
-
"""
|
|
391
|
+
"""
|
|
392
|
+
This class has been deprecated and replaced by the functionality provided by experimental.devs
|
|
393
|
+
"""
|
|
379
394
|
|
|
380
395
|
def __init__(self, model: Model, time_step: TimeT = 1) -> None:
|
|
381
|
-
"""
|
|
396
|
+
"""
|
|
382
397
|
|
|
383
398
|
Args:
|
|
384
399
|
model (Model): The model to which the schedule belongs
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class UserParam: # noqa: D101
|
|
1
|
+
class UserParam:
|
|
5
2
|
_ERROR_MESSAGE = "Missing or malformed inputs for '{}' Option '{}'"
|
|
6
3
|
|
|
7
|
-
def maybe_raise_error(self, param_type, valid):
|
|
4
|
+
def maybe_raise_error(self, param_type, valid):
|
|
8
5
|
if valid:
|
|
9
6
|
return
|
|
10
7
|
msg = self._ERROR_MESSAGE.format(param_type, self.label)
|
|
@@ -12,9 +9,11 @@ class UserParam: # noqa: D101
|
|
|
12
9
|
|
|
13
10
|
|
|
14
11
|
class Slider(UserParam):
|
|
15
|
-
"""
|
|
12
|
+
"""
|
|
13
|
+
A number-based slider input with settable increment.
|
|
16
14
|
|
|
17
15
|
Example:
|
|
16
|
+
|
|
18
17
|
slider_option = Slider("My Slider", value=123, min=10, max=200, step=0.1)
|
|
19
18
|
|
|
20
19
|
Args:
|
|
@@ -35,16 +34,6 @@ class Slider(UserParam):
|
|
|
35
34
|
step=1,
|
|
36
35
|
dtype=None,
|
|
37
36
|
):
|
|
38
|
-
"""Slider class.
|
|
39
|
-
|
|
40
|
-
Args:
|
|
41
|
-
label: The displayed label in the UI
|
|
42
|
-
value: The initial value of the slider
|
|
43
|
-
min: The minimum possible value of the slider
|
|
44
|
-
max: The maximum possible value of the slider
|
|
45
|
-
step: The step between min and max for a range of possible values
|
|
46
|
-
dtype: either int or float
|
|
47
|
-
"""
|
|
48
37
|
self.label = label
|
|
49
38
|
self.value = value
|
|
50
39
|
self.min = min
|
|
@@ -63,5 +52,5 @@ class Slider(UserParam):
|
|
|
63
52
|
def _check_values_are_float(self, value, min, max, step):
|
|
64
53
|
return any(isinstance(n, float) for n in (value, min, max, step))
|
|
65
54
|
|
|
66
|
-
def get(self, attr):
|
|
55
|
+
def get(self, attr):
|
|
67
56
|
return getattr(self, attr)
|
mesa/visualization/__init__.py
CHANGED
|
@@ -1,26 +1,3 @@
|
|
|
1
|
-
|
|
1
|
+
from .jupyter_viz import JupyterViz, Slider, make_text
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
SolaraViz is experimental and still in active development for Mesa 3.0. While we attempt to minimize them, there might be API breaking changes between Mesa 3.0 and 3.1.
|
|
5
|
-
|
|
6
|
-
There won't be breaking changes between Mesa 3.0.x patch releases.
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
from mesa.visualization.mpl_space_drawing import (
|
|
10
|
-
draw_space,
|
|
11
|
-
)
|
|
12
|
-
|
|
13
|
-
from .components import make_plot_component, make_space_component
|
|
14
|
-
from .components.altair_components import make_space_altair
|
|
15
|
-
from .solara_viz import JupyterViz, SolaraViz
|
|
16
|
-
from .user_param import Slider
|
|
17
|
-
|
|
18
|
-
__all__ = [
|
|
19
|
-
"JupyterViz",
|
|
20
|
-
"SolaraViz",
|
|
21
|
-
"Slider",
|
|
22
|
-
"make_space_altair",
|
|
23
|
-
"draw_space",
|
|
24
|
-
"make_plot_component",
|
|
25
|
-
"make_space_component",
|
|
26
|
-
]
|
|
3
|
+
__all__ = ["JupyterViz", "make_text", "Slider"]
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
"""Altair components."""
|
|
2
|
-
|
|
3
1
|
import contextlib
|
|
4
2
|
|
|
5
3
|
import solara
|
|
@@ -10,14 +8,6 @@ with contextlib.suppress(ImportError):
|
|
|
10
8
|
|
|
11
9
|
@solara.component
|
|
12
10
|
def SpaceAltair(model, agent_portrayal, dependencies: list[any] | None = None):
|
|
13
|
-
"""A component that renders a Space using Altair.
|
|
14
|
-
|
|
15
|
-
Args:
|
|
16
|
-
model: a model instance
|
|
17
|
-
agent_portrayal: agent portray specification
|
|
18
|
-
dependencies: optional list of dependencies (currently not used)
|
|
19
|
-
|
|
20
|
-
"""
|
|
21
11
|
space = getattr(model, "grid", None)
|
|
22
12
|
if space is None:
|
|
23
13
|
# Sometimes the space is defined as model.space instead of model.grid
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import networkx as nx
|
|
2
|
+
import solara
|
|
3
|
+
from matplotlib.figure import Figure
|
|
4
|
+
from matplotlib.ticker import MaxNLocator
|
|
5
|
+
|
|
6
|
+
import mesa
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@solara.component
|
|
10
|
+
def SpaceMatplotlib(model, agent_portrayal, dependencies: list[any] | None = None):
|
|
11
|
+
space_fig = Figure()
|
|
12
|
+
space_ax = space_fig.subplots()
|
|
13
|
+
space = getattr(model, "grid", None)
|
|
14
|
+
if space is None:
|
|
15
|
+
# Sometimes the space is defined as model.space instead of model.grid
|
|
16
|
+
space = model.space
|
|
17
|
+
if isinstance(space, mesa.space.NetworkGrid):
|
|
18
|
+
_draw_network_grid(space, space_ax, agent_portrayal)
|
|
19
|
+
elif isinstance(space, mesa.space.ContinuousSpace):
|
|
20
|
+
_draw_continuous_space(space, space_ax, agent_portrayal)
|
|
21
|
+
else:
|
|
22
|
+
_draw_grid(space, space_ax, agent_portrayal)
|
|
23
|
+
solara.FigureMatplotlib(space_fig, format="png", dependencies=dependencies)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _draw_grid(space, space_ax, agent_portrayal):
|
|
27
|
+
def portray(g):
|
|
28
|
+
x = []
|
|
29
|
+
y = []
|
|
30
|
+
s = [] # size
|
|
31
|
+
c = [] # color
|
|
32
|
+
for i in range(g.width):
|
|
33
|
+
for j in range(g.height):
|
|
34
|
+
content = g._grid[i][j]
|
|
35
|
+
if not content:
|
|
36
|
+
continue
|
|
37
|
+
if not hasattr(content, "__iter__"):
|
|
38
|
+
# Is a single grid
|
|
39
|
+
content = [content]
|
|
40
|
+
for agent in content:
|
|
41
|
+
data = agent_portrayal(agent)
|
|
42
|
+
x.append(i)
|
|
43
|
+
y.append(j)
|
|
44
|
+
if "size" in data:
|
|
45
|
+
s.append(data["size"])
|
|
46
|
+
if "color" in data:
|
|
47
|
+
c.append(data["color"])
|
|
48
|
+
out = {"x": x, "y": y}
|
|
49
|
+
# This is the default value for the marker size, which auto-scales
|
|
50
|
+
# according to the grid area.
|
|
51
|
+
out["s"] = (180 / min(g.width, g.height)) ** 2
|
|
52
|
+
if len(s) > 0:
|
|
53
|
+
out["s"] = s
|
|
54
|
+
if len(c) > 0:
|
|
55
|
+
out["c"] = c
|
|
56
|
+
return out
|
|
57
|
+
|
|
58
|
+
space_ax.set_xlim(-1, space.width)
|
|
59
|
+
space_ax.set_ylim(-1, space.height)
|
|
60
|
+
space_ax.scatter(**portray(space))
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _draw_network_grid(space, space_ax, agent_portrayal):
|
|
64
|
+
graph = space.G
|
|
65
|
+
pos = nx.spring_layout(graph, seed=0)
|
|
66
|
+
nx.draw(
|
|
67
|
+
graph,
|
|
68
|
+
ax=space_ax,
|
|
69
|
+
pos=pos,
|
|
70
|
+
**agent_portrayal(graph),
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def _draw_continuous_space(space, space_ax, agent_portrayal):
|
|
75
|
+
def portray(space):
|
|
76
|
+
x = []
|
|
77
|
+
y = []
|
|
78
|
+
s = [] # size
|
|
79
|
+
c = [] # color
|
|
80
|
+
for agent in space._agent_to_index:
|
|
81
|
+
data = agent_portrayal(agent)
|
|
82
|
+
_x, _y = agent.pos
|
|
83
|
+
x.append(_x)
|
|
84
|
+
y.append(_y)
|
|
85
|
+
if "size" in data:
|
|
86
|
+
s.append(data["size"])
|
|
87
|
+
if "color" in data:
|
|
88
|
+
c.append(data["color"])
|
|
89
|
+
out = {"x": x, "y": y}
|
|
90
|
+
if len(s) > 0:
|
|
91
|
+
out["s"] = s
|
|
92
|
+
if len(c) > 0:
|
|
93
|
+
out["c"] = c
|
|
94
|
+
return out
|
|
95
|
+
|
|
96
|
+
# Determine border style based on space.torus
|
|
97
|
+
border_style = "solid" if not space.torus else (0, (5, 10))
|
|
98
|
+
|
|
99
|
+
# Set the border of the plot
|
|
100
|
+
for spine in space_ax.spines.values():
|
|
101
|
+
spine.set_linewidth(1.5)
|
|
102
|
+
spine.set_color("black")
|
|
103
|
+
spine.set_linestyle(border_style)
|
|
104
|
+
|
|
105
|
+
width = space.x_max - space.x_min
|
|
106
|
+
x_padding = width / 20
|
|
107
|
+
height = space.y_max - space.y_min
|
|
108
|
+
y_padding = height / 20
|
|
109
|
+
space_ax.set_xlim(space.x_min - x_padding, space.x_max + x_padding)
|
|
110
|
+
space_ax.set_ylim(space.y_min - y_padding, space.y_max + y_padding)
|
|
111
|
+
|
|
112
|
+
# Portray and scatter the agents in the space
|
|
113
|
+
space_ax.scatter(**portray(space))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
@solara.component
|
|
117
|
+
def PlotMatplotlib(model, measure, dependencies: list[any] | None = None):
|
|
118
|
+
fig = Figure()
|
|
119
|
+
ax = fig.subplots()
|
|
120
|
+
df = model.datacollector.get_model_vars_dataframe()
|
|
121
|
+
if isinstance(measure, str):
|
|
122
|
+
ax.plot(df.loc[:, measure])
|
|
123
|
+
ax.set_ylabel(measure)
|
|
124
|
+
elif isinstance(measure, dict):
|
|
125
|
+
for m, color in measure.items():
|
|
126
|
+
ax.plot(df.loc[:, m], label=m, color=color)
|
|
127
|
+
fig.legend()
|
|
128
|
+
elif isinstance(measure, list | tuple):
|
|
129
|
+
for m in measure:
|
|
130
|
+
ax.plot(df.loc[:, m], label=m)
|
|
131
|
+
fig.legend()
|
|
132
|
+
# Set integer x axis
|
|
133
|
+
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
|
|
134
|
+
solara.FigureMatplotlib(fig, dependencies=dependencies)
|