Mesa 3.0.0a4__py3-none-any.whl → 3.0.0a5__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 -3
- mesa/agent.py +106 -61
- mesa/batchrunner.py +15 -23
- mesa/cookiecutter-mesa/hooks/post_gen_project.py +2 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +1 -0
- mesa/datacollection.py +138 -27
- mesa/experimental/UserParam.py +17 -6
- mesa/experimental/__init__.py +2 -0
- mesa/experimental/cell_space/__init__.py +7 -0
- mesa/experimental/cell_space/cell.py +61 -20
- mesa/experimental/cell_space/cell_agent.py +10 -5
- mesa/experimental/cell_space/cell_collection.py +54 -17
- mesa/experimental/cell_space/discrete_space.py +16 -5
- mesa/experimental/cell_space/grid.py +19 -8
- mesa/experimental/cell_space/network.py +9 -7
- mesa/experimental/cell_space/voronoi.py +26 -33
- mesa/experimental/components/altair.py +10 -0
- mesa/experimental/components/matplotlib.py +18 -0
- mesa/experimental/devs/__init__.py +2 -0
- mesa/experimental/devs/eventlist.py +36 -15
- mesa/experimental/devs/examples/epstein_civil_violence.py +65 -29
- mesa/experimental/devs/examples/wolf_sheep.py +38 -34
- mesa/experimental/devs/simulator.py +55 -15
- mesa/experimental/solara_viz.py +10 -19
- mesa/main.py +6 -4
- mesa/model.py +43 -45
- mesa/space.py +145 -120
- mesa/time.py +57 -67
- mesa/visualization/UserParam.py +19 -6
- mesa/visualization/__init__.py +3 -2
- mesa/visualization/components/altair.py +4 -2
- mesa/visualization/components/matplotlib.py +6 -4
- mesa/visualization/solara_viz.py +157 -83
- mesa/visualization/utils.py +3 -1
- {mesa-3.0.0a4.dist-info → mesa-3.0.0a5.dist-info}/METADATA +1 -1
- mesa-3.0.0a5.dist-info/RECORD +44 -0
- mesa-3.0.0a4.dist-info/RECORD +0 -44
- {mesa-3.0.0a4.dist-info → mesa-3.0.0a5.dist-info}/WHEEL +0 -0
- {mesa-3.0.0a4.dist-info → mesa-3.0.0a5.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.0a4.dist-info → mesa-3.0.0a5.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Epstein civil violence example using ABMSimulator."""
|
|
2
|
+
|
|
1
3
|
import enum
|
|
2
4
|
import math
|
|
3
5
|
|
|
@@ -7,21 +9,32 @@ from mesa.space import SingleGrid
|
|
|
7
9
|
|
|
8
10
|
|
|
9
11
|
class EpsteinAgent(Agent):
|
|
12
|
+
"""Epstein Agent."""
|
|
13
|
+
|
|
10
14
|
def __init__(self, model, vision, movement):
|
|
15
|
+
"""Initialize the agent.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
model: a model instance
|
|
19
|
+
vision: size of neighborhood
|
|
20
|
+
movement: boolean whether agent can move or not
|
|
21
|
+
"""
|
|
11
22
|
super().__init__(model)
|
|
12
23
|
self.vision = vision
|
|
13
24
|
self.movement = movement
|
|
14
25
|
|
|
15
26
|
|
|
16
27
|
class AgentState(enum.IntEnum):
|
|
28
|
+
"""Agent states."""
|
|
29
|
+
|
|
17
30
|
QUIESCENT = enum.auto()
|
|
18
31
|
ARRESTED = enum.auto()
|
|
19
32
|
ACTIVE = enum.auto()
|
|
20
33
|
|
|
21
34
|
|
|
22
35
|
class Citizen(EpsteinAgent):
|
|
23
|
-
"""
|
|
24
|
-
|
|
36
|
+
"""A member of the general population, may or may not be in active rebellion.
|
|
37
|
+
|
|
25
38
|
Summary of rule: If grievance - risk > threshold, rebel.
|
|
26
39
|
|
|
27
40
|
Attributes:
|
|
@@ -55,10 +68,13 @@ class Citizen(EpsteinAgent):
|
|
|
55
68
|
threshold,
|
|
56
69
|
arrest_prob_constant,
|
|
57
70
|
):
|
|
58
|
-
"""
|
|
59
|
-
|
|
71
|
+
"""Create a new Citizen.
|
|
72
|
+
|
|
60
73
|
Args:
|
|
61
74
|
model : model instance
|
|
75
|
+
vision: number of cells in each direction (N, S, E and W) that
|
|
76
|
+
agent can inspect. Exogenous.
|
|
77
|
+
movement: whether agent can move or not
|
|
62
78
|
hardship: Agent's 'perceived hardship (i.e., physical or economic
|
|
63
79
|
privation).' Exogenous, drawn from U(0,1).
|
|
64
80
|
regime_legitimacy: Agent's perception of regime legitimacy, equal
|
|
@@ -66,8 +82,8 @@ class Citizen(EpsteinAgent):
|
|
|
66
82
|
risk_aversion: Exogenous, drawn from U(0,1).
|
|
67
83
|
threshold: if (grievance - (risk_aversion * arrest_probability)) >
|
|
68
84
|
threshold, go/remain Active
|
|
69
|
-
|
|
70
|
-
|
|
85
|
+
arrest_prob_constant : agent's assessment of arrest probability
|
|
86
|
+
|
|
71
87
|
"""
|
|
72
88
|
super().__init__(model, vision, movement)
|
|
73
89
|
self.hardship = hardship
|
|
@@ -80,9 +96,7 @@ class Citizen(EpsteinAgent):
|
|
|
80
96
|
self.arrest_prob_constant = arrest_prob_constant
|
|
81
97
|
|
|
82
98
|
def step(self):
|
|
83
|
-
"""
|
|
84
|
-
Decide whether to activate, then move if applicable.
|
|
85
|
-
"""
|
|
99
|
+
"""Decide whether to activate, then move if applicable."""
|
|
86
100
|
self.update_neighbors()
|
|
87
101
|
self.update_estimated_arrest_probability()
|
|
88
102
|
net_risk = self.risk_aversion * self.arrest_probability
|
|
@@ -95,9 +109,7 @@ class Citizen(EpsteinAgent):
|
|
|
95
109
|
self.model.grid.move_agent(self, new_pos)
|
|
96
110
|
|
|
97
111
|
def update_neighbors(self):
|
|
98
|
-
"""
|
|
99
|
-
Look around and see who my neighbors are
|
|
100
|
-
"""
|
|
112
|
+
"""Look around and see who my neighbors are."""
|
|
101
113
|
self.neighborhood = self.model.grid.get_neighborhood(
|
|
102
114
|
self.pos, moore=True, radius=self.vision
|
|
103
115
|
)
|
|
@@ -107,10 +119,7 @@ class Citizen(EpsteinAgent):
|
|
|
107
119
|
]
|
|
108
120
|
|
|
109
121
|
def update_estimated_arrest_probability(self):
|
|
110
|
-
"""
|
|
111
|
-
Based on the ratio of cops to actives in my neighborhood, estimate the
|
|
112
|
-
p(Arrest | I go active).
|
|
113
|
-
"""
|
|
122
|
+
"""Based on the ratio of cops to actives in my neighborhood, estimate the p(Arrest | I go active)."""
|
|
114
123
|
cops_in_vision = len([c for c in self.neighbors if isinstance(c, Cop)])
|
|
115
124
|
actives_in_vision = 1.0 # citizen counts herself
|
|
116
125
|
for c in self.neighbors:
|
|
@@ -121,18 +130,25 @@ class Citizen(EpsteinAgent):
|
|
|
121
130
|
)
|
|
122
131
|
|
|
123
132
|
def sent_to_jail(self, value):
|
|
133
|
+
"""Sent agent to jail.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
value: duration of jail sentence
|
|
137
|
+
|
|
138
|
+
"""
|
|
124
139
|
self.model.active_agents.remove(self)
|
|
125
140
|
self.condition = AgentState.ARRESTED
|
|
126
141
|
self.model.simulator.schedule_event_relative(self.release_from_jail, value)
|
|
127
142
|
|
|
128
143
|
def release_from_jail(self):
|
|
144
|
+
"""Release agent from jail."""
|
|
129
145
|
self.model.active_agents.add(self)
|
|
130
146
|
self.condition = AgentState.QUIESCENT
|
|
131
147
|
|
|
132
148
|
|
|
133
149
|
class Cop(EpsteinAgent):
|
|
134
|
-
"""
|
|
135
|
-
|
|
150
|
+
"""A cop for life. No defection.
|
|
151
|
+
|
|
136
152
|
Summary of rule: Inspect local vision and arrest a random active agent.
|
|
137
153
|
|
|
138
154
|
Attributes:
|
|
@@ -143,14 +159,19 @@ class Cop(EpsteinAgent):
|
|
|
143
159
|
"""
|
|
144
160
|
|
|
145
161
|
def __init__(self, model, vision, movement, max_jail_term):
|
|
162
|
+
"""Initialize a Cop agent.
|
|
163
|
+
|
|
164
|
+
Args:
|
|
165
|
+
model: a model instance
|
|
166
|
+
vision: size of neighborhood
|
|
167
|
+
movement: whether agent can move or not
|
|
168
|
+
max_jail_term: maximum jail sentence
|
|
169
|
+
"""
|
|
146
170
|
super().__init__(model, vision, movement)
|
|
147
171
|
self.max_jail_term = max_jail_term
|
|
148
172
|
|
|
149
173
|
def step(self):
|
|
150
|
-
"""
|
|
151
|
-
Inspect local vision and arrest a random active agent. Move if
|
|
152
|
-
applicable.
|
|
153
|
-
"""
|
|
174
|
+
"""Inspect local vision and arrest a random active agent. Move if applicable."""
|
|
154
175
|
self.update_neighbors()
|
|
155
176
|
active_neighbors = []
|
|
156
177
|
for agent in self.neighbors:
|
|
@@ -164,9 +185,7 @@ class Cop(EpsteinAgent):
|
|
|
164
185
|
self.model.grid.move_agent(self, new_pos)
|
|
165
186
|
|
|
166
187
|
def update_neighbors(self):
|
|
167
|
-
"""
|
|
168
|
-
Look around and see who my neighbors are.
|
|
169
|
-
"""
|
|
188
|
+
"""Look around and see who my neighbors are."""
|
|
170
189
|
self.neighborhood = self.model.grid.get_neighborhood(
|
|
171
190
|
self.pos, moore=True, radius=self.vision
|
|
172
191
|
)
|
|
@@ -177,9 +196,8 @@ class Cop(EpsteinAgent):
|
|
|
177
196
|
|
|
178
197
|
|
|
179
198
|
class EpsteinCivilViolence(Model):
|
|
180
|
-
"""
|
|
181
|
-
|
|
182
|
-
approach," by Joshua Epstein.
|
|
199
|
+
"""Model 1 from "Modeling civil violence: An agent-based computational approach," by Joshua Epstein.
|
|
200
|
+
|
|
183
201
|
http://www.pnas.org/content/99/suppl_3/7243.full
|
|
184
202
|
Attributes:
|
|
185
203
|
height: grid height
|
|
@@ -218,6 +236,23 @@ class EpsteinCivilViolence(Model):
|
|
|
218
236
|
max_iters=1000,
|
|
219
237
|
seed=None,
|
|
220
238
|
):
|
|
239
|
+
"""Initialize the Eppstein civil violence model.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
width: the width of the grid
|
|
243
|
+
height: the height of the grid
|
|
244
|
+
citizen_density: density of citizens
|
|
245
|
+
cop_density: density of cops
|
|
246
|
+
citizen_vision: size of citizen vision
|
|
247
|
+
cop_vision: size of cop vision
|
|
248
|
+
legitimacy: perceived legitimacy
|
|
249
|
+
max_jail_term: maximum jail term
|
|
250
|
+
active_threshold: threshold for citizen to become active
|
|
251
|
+
arrest_prob_constant: arrest probability
|
|
252
|
+
movement: allow agent movement or not
|
|
253
|
+
max_iters: number of iterations
|
|
254
|
+
seed: seed for random number generator
|
|
255
|
+
"""
|
|
221
256
|
super().__init__(seed)
|
|
222
257
|
if cop_density + citizen_density > 1:
|
|
223
258
|
raise ValueError("Cop density + citizen density must be less than 1")
|
|
@@ -257,7 +292,8 @@ class EpsteinCivilViolence(Model):
|
|
|
257
292
|
self.active_agents = self.agents
|
|
258
293
|
|
|
259
294
|
def step(self):
|
|
260
|
-
|
|
295
|
+
"""Run one step of the model."""
|
|
296
|
+
self.active_agents.shuffle_do("step")
|
|
261
297
|
|
|
262
298
|
|
|
263
299
|
if __name__ == "__main__":
|
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Wolf-Sheep Predation Model
|
|
3
|
-
================================
|
|
4
|
-
|
|
5
|
-
Replication of the model found in NetLogo:
|
|
6
|
-
Wilensky, U. (1997). NetLogo Wolf Sheep Predation model.
|
|
7
|
-
http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation.
|
|
8
|
-
Center for Connected Learning and Computer-Based Modeling,
|
|
9
|
-
Northwestern University, Evanston, IL.
|
|
10
|
-
"""
|
|
1
|
+
"""Example of using ABM simulator for Wolf-Sheep Predation Model."""
|
|
11
2
|
|
|
12
3
|
import mesa
|
|
13
4
|
from mesa.experimental.devs.simulator import ABMSimulator
|
|
14
5
|
|
|
15
6
|
|
|
16
7
|
class Animal(mesa.Agent):
|
|
8
|
+
"""Base Animal class."""
|
|
9
|
+
|
|
17
10
|
def __init__(self, model, moore, energy, p_reproduce, energy_from_food):
|
|
11
|
+
"""Initialize Animal instance.
|
|
12
|
+
|
|
13
|
+
Args:
|
|
14
|
+
model: a model instance
|
|
15
|
+
moore: using moore grid or not
|
|
16
|
+
energy: initial energy
|
|
17
|
+
p_reproduce: probability of reproduction
|
|
18
|
+
energy_from_food: energy gained from 1 unit of food
|
|
19
|
+
"""
|
|
18
20
|
super().__init__(model)
|
|
19
21
|
self.energy = energy
|
|
20
22
|
self.p_reproduce = p_reproduce
|
|
@@ -22,12 +24,14 @@ class Animal(mesa.Agent):
|
|
|
22
24
|
self.moore = moore
|
|
23
25
|
|
|
24
26
|
def random_move(self):
|
|
27
|
+
"""Move to random neighboring cell."""
|
|
25
28
|
next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True)
|
|
26
29
|
next_move = self.random.choice(next_moves)
|
|
27
30
|
# Now move:
|
|
28
31
|
self.model.grid.move_agent(self, next_move)
|
|
29
32
|
|
|
30
33
|
def spawn_offspring(self):
|
|
34
|
+
"""Create offspring."""
|
|
31
35
|
self.energy /= 2
|
|
32
36
|
offspring = self.__class__(
|
|
33
37
|
self.model,
|
|
@@ -38,13 +42,15 @@ class Animal(mesa.Agent):
|
|
|
38
42
|
)
|
|
39
43
|
self.model.grid.place_agent(offspring, self.pos)
|
|
40
44
|
|
|
41
|
-
def feed(self): ...
|
|
45
|
+
def feed(self): ... # noqa: D102
|
|
42
46
|
|
|
43
47
|
def die(self):
|
|
48
|
+
"""Die."""
|
|
44
49
|
self.model.grid.remove_agent(self)
|
|
45
50
|
self.remove()
|
|
46
51
|
|
|
47
52
|
def step(self):
|
|
53
|
+
"""Execute one step of the agent."""
|
|
48
54
|
self.random_move()
|
|
49
55
|
self.energy -= 1
|
|
50
56
|
|
|
@@ -57,13 +63,10 @@ class Animal(mesa.Agent):
|
|
|
57
63
|
|
|
58
64
|
|
|
59
65
|
class Sheep(Animal):
|
|
60
|
-
"""
|
|
61
|
-
A sheep that walks around, reproduces (asexually) and gets eaten.
|
|
62
|
-
|
|
63
|
-
The init is the same as the RandomWalker.
|
|
64
|
-
"""
|
|
66
|
+
"""A sheep that walks around, reproduces (asexually) and gets eaten."""
|
|
65
67
|
|
|
66
68
|
def feed(self):
|
|
69
|
+
"""Eat grass and gain energy."""
|
|
67
70
|
# If there is grass available, eat it
|
|
68
71
|
agents = self.model.grid.get_cell_list_contents(self.pos)
|
|
69
72
|
grass_patch = next(obj for obj in agents if isinstance(obj, GrassPatch))
|
|
@@ -73,11 +76,10 @@ class Sheep(Animal):
|
|
|
73
76
|
|
|
74
77
|
|
|
75
78
|
class Wolf(Animal):
|
|
76
|
-
"""
|
|
77
|
-
A wolf that walks around, reproduces (asexually) and eats sheep.
|
|
78
|
-
"""
|
|
79
|
+
"""A wolf that walks around, reproduces (asexually) and eats sheep."""
|
|
79
80
|
|
|
80
81
|
def feed(self):
|
|
82
|
+
"""Eat wolf and gain energy."""
|
|
81
83
|
agents = self.model.grid.get_cell_list_contents(self.pos)
|
|
82
84
|
sheep = [obj for obj in agents if isinstance(obj, Sheep)]
|
|
83
85
|
if len(sheep) > 0:
|
|
@@ -89,12 +91,10 @@ class Wolf(Animal):
|
|
|
89
91
|
|
|
90
92
|
|
|
91
93
|
class GrassPatch(mesa.Agent):
|
|
92
|
-
"""
|
|
93
|
-
A patch of grass that grows at a fixed rate and it is eaten by sheep
|
|
94
|
-
"""
|
|
94
|
+
"""A patch of grass that grows at a fixed rate and it is eaten by sheep."""
|
|
95
95
|
|
|
96
96
|
@property
|
|
97
|
-
def fully_grown(self) -> bool:
|
|
97
|
+
def fully_grown(self) -> bool: # noqa: D102
|
|
98
98
|
return self._fully_grown
|
|
99
99
|
|
|
100
100
|
@fully_grown.setter
|
|
@@ -109,12 +109,13 @@ class GrassPatch(mesa.Agent):
|
|
|
109
109
|
)
|
|
110
110
|
|
|
111
111
|
def __init__(self, model, fully_grown, countdown, grass_regrowth_time):
|
|
112
|
-
"""
|
|
113
|
-
Creates a new patch of grass
|
|
112
|
+
"""Creates a new patch of grass.
|
|
114
113
|
|
|
115
114
|
Args:
|
|
116
|
-
|
|
115
|
+
model: a model instance
|
|
116
|
+
fully_grown: (boolean) Whether the patch of grass is fully grown or not
|
|
117
117
|
countdown: Time for the patch of grass to be fully grown again
|
|
118
|
+
grass_regrowth_time: regrowth time for the grass
|
|
118
119
|
"""
|
|
119
120
|
super().__init__(model)
|
|
120
121
|
self._fully_grown = fully_grown
|
|
@@ -125,13 +126,12 @@ class GrassPatch(mesa.Agent):
|
|
|
125
126
|
setattr, countdown, function_args=[self, "fully_grown", True]
|
|
126
127
|
)
|
|
127
128
|
|
|
128
|
-
def set_fully_grown(self):
|
|
129
|
+
def set_fully_grown(self): # noqa
|
|
129
130
|
self.fully_grown = True
|
|
130
131
|
|
|
131
132
|
|
|
132
133
|
class WolfSheep(mesa.Model):
|
|
133
|
-
"""
|
|
134
|
-
Wolf-Sheep Predation Model
|
|
134
|
+
"""Wolf-Sheep Predation Model.
|
|
135
135
|
|
|
136
136
|
A model for simulating wolf and sheep (predator-prey) ecosystem modelling.
|
|
137
137
|
"""
|
|
@@ -151,10 +151,11 @@ class WolfSheep(mesa.Model):
|
|
|
151
151
|
simulator=None,
|
|
152
152
|
seed=None,
|
|
153
153
|
):
|
|
154
|
-
"""
|
|
155
|
-
Create a new Wolf-Sheep model with the given parameters.
|
|
154
|
+
"""Create a new Wolf-Sheep model with the given parameters.
|
|
156
155
|
|
|
157
156
|
Args:
|
|
157
|
+
height: height of the grid
|
|
158
|
+
width: width of the grid
|
|
158
159
|
initial_sheep: Number of sheep to start with
|
|
159
160
|
initial_wolves: Number of wolves to start with
|
|
160
161
|
sheep_reproduce: Probability of each sheep reproducing each step
|
|
@@ -164,7 +165,9 @@ class WolfSheep(mesa.Model):
|
|
|
164
165
|
grass_regrowth_time: How long it takes for a grass patch to regrow
|
|
165
166
|
once it is eaten
|
|
166
167
|
sheep_gain_from_food: Energy sheep gain from grass, if enabled.
|
|
167
|
-
moore:
|
|
168
|
+
moore: whether to use moore or von Neumann grid
|
|
169
|
+
simulator: Simulator to use for simulating wolf and sheep
|
|
170
|
+
seed: Random seed
|
|
168
171
|
"""
|
|
169
172
|
super().__init__(seed=seed)
|
|
170
173
|
# Set parameters
|
|
@@ -226,8 +229,9 @@ class WolfSheep(mesa.Model):
|
|
|
226
229
|
self.grid.place_agent(patch, pos)
|
|
227
230
|
|
|
228
231
|
def step(self):
|
|
229
|
-
|
|
230
|
-
self.agents_by_type[
|
|
232
|
+
"""Perform one step of the model."""
|
|
233
|
+
self.agents_by_type[Sheep].shuffle_do("step")
|
|
234
|
+
self.agents_by_type[Wolf].shuffle_do("step")
|
|
231
235
|
|
|
232
236
|
|
|
233
237
|
if __name__ == "__main__":
|
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
"""Provides several simulator classes.
|
|
2
|
+
|
|
3
|
+
A Simulator is responsible for executing a simulation model. It controls time advancement and enables event scheduling.
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
"""
|
|
7
|
+
|
|
1
8
|
from __future__ import annotations
|
|
2
9
|
|
|
3
10
|
import numbers
|
|
@@ -27,6 +34,12 @@ class Simulator:
|
|
|
27
34
|
# TODO: add experimentation support
|
|
28
35
|
|
|
29
36
|
def __init__(self, time_unit: type, start_time: int | float):
|
|
37
|
+
"""Initialize a Simulator instance.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
time_unit: type of the smulaiton time
|
|
41
|
+
start_time: the starttime of the simulator
|
|
42
|
+
"""
|
|
30
43
|
# should model run in a separate thread,
|
|
31
44
|
# and we can then interact with start, stop, run_until, and step?
|
|
32
45
|
self.event_list = EventList()
|
|
@@ -36,10 +49,10 @@ class Simulator:
|
|
|
36
49
|
self.time = self.start_time
|
|
37
50
|
self.model = None
|
|
38
51
|
|
|
39
|
-
def check_time_unit(self, time: int | float) -> bool: ...
|
|
52
|
+
def check_time_unit(self, time: int | float) -> bool: ... # noqa: D102
|
|
40
53
|
|
|
41
54
|
def setup(self, model: Model) -> None:
|
|
42
|
-
"""Set up the simulator with the model to simulate
|
|
55
|
+
"""Set up the simulator with the model to simulate.
|
|
43
56
|
|
|
44
57
|
Args:
|
|
45
58
|
model (Model): The model to simulate
|
|
@@ -49,12 +62,13 @@ class Simulator:
|
|
|
49
62
|
self.model = model
|
|
50
63
|
|
|
51
64
|
def reset(self):
|
|
52
|
-
"""Reset the simulator by clearing the event list and removing the model to simulate"""
|
|
65
|
+
"""Reset the simulator by clearing the event list and removing the model to simulate."""
|
|
53
66
|
self.event_list.clear()
|
|
54
67
|
self.model = None
|
|
55
68
|
self.time = self.start_time
|
|
56
69
|
|
|
57
70
|
def run_until(self, end_time: int | float) -> None:
|
|
71
|
+
"""Run the simulator until the end time."""
|
|
58
72
|
while True:
|
|
59
73
|
try:
|
|
60
74
|
event = self.event_list.pop_event()
|
|
@@ -71,7 +85,7 @@ class Simulator:
|
|
|
71
85
|
break
|
|
72
86
|
|
|
73
87
|
def run_for(self, time_delta: int | float):
|
|
74
|
-
"""
|
|
88
|
+
"""Run the simulator for the specified time delta.
|
|
75
89
|
|
|
76
90
|
Args:
|
|
77
91
|
time_delta (float| int): The time delta. The simulator is run from the current time to the current time
|
|
@@ -88,7 +102,7 @@ class Simulator:
|
|
|
88
102
|
function_args: list[Any] | None = None,
|
|
89
103
|
function_kwargs: dict[str, Any] | None = None,
|
|
90
104
|
) -> SimulationEvent:
|
|
91
|
-
"""Schedule event for the current time instant
|
|
105
|
+
"""Schedule event for the current time instant.
|
|
92
106
|
|
|
93
107
|
Args:
|
|
94
108
|
function (Callable): The callable to execute for this event
|
|
@@ -116,7 +130,7 @@ class Simulator:
|
|
|
116
130
|
function_args: list[Any] | None = None,
|
|
117
131
|
function_kwargs: dict[str, Any] | None = None,
|
|
118
132
|
) -> SimulationEvent:
|
|
119
|
-
"""Schedule event for the specified time instant
|
|
133
|
+
"""Schedule event for the specified time instant.
|
|
120
134
|
|
|
121
135
|
Args:
|
|
122
136
|
function (Callable): The callable to execute for this event
|
|
@@ -150,7 +164,7 @@ class Simulator:
|
|
|
150
164
|
function_args: list[Any] | None = None,
|
|
151
165
|
function_kwargs: dict[str, Any] | None = None,
|
|
152
166
|
) -> SimulationEvent:
|
|
153
|
-
"""Schedule event for the current time plus the time delta
|
|
167
|
+
"""Schedule event for the current time plus the time delta.
|
|
154
168
|
|
|
155
169
|
Args:
|
|
156
170
|
function (Callable): The callable to execute for this event
|
|
@@ -174,13 +188,12 @@ class Simulator:
|
|
|
174
188
|
return event
|
|
175
189
|
|
|
176
190
|
def cancel_event(self, event: SimulationEvent) -> None:
|
|
177
|
-
"""
|
|
191
|
+
"""Remove the event from the event list.
|
|
178
192
|
|
|
179
193
|
Args:
|
|
180
194
|
event (SimulationEvent): The simulation event to remove
|
|
181
195
|
|
|
182
196
|
"""
|
|
183
|
-
|
|
184
197
|
self.event_list.remove(event)
|
|
185
198
|
|
|
186
199
|
def _schedule_event(self, event: SimulationEvent):
|
|
@@ -204,13 +217,29 @@ class ABMSimulator(Simulator):
|
|
|
204
217
|
"""
|
|
205
218
|
|
|
206
219
|
def __init__(self):
|
|
220
|
+
"""Initialize a ABM simulator."""
|
|
207
221
|
super().__init__(int, 0)
|
|
208
222
|
|
|
209
223
|
def setup(self, model):
|
|
224
|
+
"""Set up the simulator with the model to simulate.
|
|
225
|
+
|
|
226
|
+
Args:
|
|
227
|
+
model (Model): The model to simulate
|
|
228
|
+
|
|
229
|
+
"""
|
|
210
230
|
super().setup(model)
|
|
211
231
|
self.schedule_event_now(self.model.step, priority=Priority.HIGH)
|
|
212
232
|
|
|
213
233
|
def check_time_unit(self, time) -> bool:
|
|
234
|
+
"""Check whether the time is of the correct unit.
|
|
235
|
+
|
|
236
|
+
Args:
|
|
237
|
+
time (int | float): the time
|
|
238
|
+
|
|
239
|
+
Returns:
|
|
240
|
+
bool: whether the time is of the correct unit
|
|
241
|
+
|
|
242
|
+
"""
|
|
214
243
|
if isinstance(time, int):
|
|
215
244
|
return True
|
|
216
245
|
if isinstance(time, float):
|
|
@@ -225,9 +254,9 @@ class ABMSimulator(Simulator):
|
|
|
225
254
|
function_args: list[Any] | None = None,
|
|
226
255
|
function_kwargs: dict[str, Any] | None = None,
|
|
227
256
|
) -> SimulationEvent:
|
|
228
|
-
"""Schedule a SimulationEvent for the next tick
|
|
257
|
+
"""Schedule a SimulationEvent for the next tick.
|
|
229
258
|
|
|
230
|
-
Args
|
|
259
|
+
Args:
|
|
231
260
|
function (Callable): the callable to execute
|
|
232
261
|
priority (Priority): the priority of the event
|
|
233
262
|
function_args (List[Any]): List of arguments to pass to the callable
|
|
@@ -243,7 +272,7 @@ class ABMSimulator(Simulator):
|
|
|
243
272
|
)
|
|
244
273
|
|
|
245
274
|
def run_until(self, end_time: int) -> None:
|
|
246
|
-
"""
|
|
275
|
+
"""Run the simulator up to and included the specified end time.
|
|
247
276
|
|
|
248
277
|
Args:
|
|
249
278
|
end_time (float| int): The end_time delta. The simulator is until the specified end time
|
|
@@ -270,7 +299,7 @@ class ABMSimulator(Simulator):
|
|
|
270
299
|
break
|
|
271
300
|
|
|
272
301
|
def run_for(self, time_delta: int):
|
|
273
|
-
"""
|
|
302
|
+
"""Run the simulator for the specified time delta.
|
|
274
303
|
|
|
275
304
|
Args:
|
|
276
305
|
time_delta (float| int): The time delta. The simulator is run from the current time to the current time
|
|
@@ -282,13 +311,24 @@ class ABMSimulator(Simulator):
|
|
|
282
311
|
|
|
283
312
|
|
|
284
313
|
class DEVSimulator(Simulator):
|
|
285
|
-
"""A simulator where the unit of time is a float.
|
|
286
|
-
|
|
314
|
+
"""A simulator where the unit of time is a float.
|
|
315
|
+
|
|
316
|
+
Can be used for full-blown discrete event simulating using event scheduling.
|
|
287
317
|
|
|
288
318
|
"""
|
|
289
319
|
|
|
290
320
|
def __init__(self):
|
|
321
|
+
"""Initialize a DEVS simulator."""
|
|
291
322
|
super().__init__(float, 0.0)
|
|
292
323
|
|
|
293
324
|
def check_time_unit(self, time) -> bool:
|
|
325
|
+
"""Check whether the time is of the correct unit.
|
|
326
|
+
|
|
327
|
+
Args:
|
|
328
|
+
time (float): the time
|
|
329
|
+
|
|
330
|
+
Returns:
|
|
331
|
+
bool: whether the time is of the correct unit
|
|
332
|
+
|
|
333
|
+
"""
|
|
294
334
|
return isinstance(time, numbers.Number)
|
mesa/experimental/solara_viz.py
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Mesa visualization module for creating interactive model visualizations.
|
|
1
|
+
"""Mesa visualization module for creating interactive model visualizations.
|
|
3
2
|
|
|
4
3
|
This module provides components to create browser- and Jupyter notebook-based visualizations of
|
|
5
4
|
Mesa models, allowing users to watch models run step-by-step and interact with model parameters.
|
|
@@ -39,8 +38,7 @@ from mesa.experimental.UserParam import Slider
|
|
|
39
38
|
def Card(
|
|
40
39
|
model, measures, agent_portrayal, space_drawer, dependencies, color, layout_type
|
|
41
40
|
):
|
|
42
|
-
"""
|
|
43
|
-
Create a card component for visualizing model space or measures.
|
|
41
|
+
"""Create a card component for visualizing model space or measures.
|
|
44
42
|
|
|
45
43
|
Args:
|
|
46
44
|
model: The Mesa model instance
|
|
@@ -95,8 +93,7 @@ def SolaraViz(
|
|
|
95
93
|
play_interval=150,
|
|
96
94
|
seed=None,
|
|
97
95
|
):
|
|
98
|
-
"""
|
|
99
|
-
Initialize a component to visualize a model.
|
|
96
|
+
"""Initialize a component to visualize a model.
|
|
100
97
|
|
|
101
98
|
Args:
|
|
102
99
|
model_class: Class of the model to instantiate
|
|
@@ -212,8 +209,7 @@ JupyterViz = SolaraViz
|
|
|
212
209
|
|
|
213
210
|
@solara.component
|
|
214
211
|
def ModelController(model, play_interval, current_step, reset_counter):
|
|
215
|
-
"""
|
|
216
|
-
Create controls for model execution (step, play, pause, reset).
|
|
212
|
+
"""Create controls for model execution (step, play, pause, reset).
|
|
217
213
|
|
|
218
214
|
Args:
|
|
219
215
|
model: The model being visualized
|
|
@@ -315,8 +311,7 @@ def ModelController(model, play_interval, current_step, reset_counter):
|
|
|
315
311
|
|
|
316
312
|
|
|
317
313
|
def split_model_params(model_params):
|
|
318
|
-
"""
|
|
319
|
-
Split model parameters into user-adjustable and fixed parameters.
|
|
314
|
+
"""Split model parameters into user-adjustable and fixed parameters.
|
|
320
315
|
|
|
321
316
|
Args:
|
|
322
317
|
model_params: Dictionary of all model parameters
|
|
@@ -335,8 +330,7 @@ def split_model_params(model_params):
|
|
|
335
330
|
|
|
336
331
|
|
|
337
332
|
def check_param_is_fixed(param):
|
|
338
|
-
"""
|
|
339
|
-
Check if a parameter is fixed (not user-adjustable).
|
|
333
|
+
"""Check if a parameter is fixed (not user-adjustable).
|
|
340
334
|
|
|
341
335
|
Args:
|
|
342
336
|
param: Parameter to check
|
|
@@ -354,8 +348,8 @@ def check_param_is_fixed(param):
|
|
|
354
348
|
|
|
355
349
|
@solara.component
|
|
356
350
|
def UserInputs(user_params, on_change=None):
|
|
357
|
-
"""
|
|
358
|
-
|
|
351
|
+
"""Initialize user inputs for configurable model parameters.
|
|
352
|
+
|
|
359
353
|
Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`,
|
|
360
354
|
:class:`solara.Select`, and :class:`solara.Checkbox`.
|
|
361
355
|
|
|
@@ -364,7 +358,6 @@ def UserInputs(user_params, on_change=None):
|
|
|
364
358
|
min and max values, and other fields specific to the input type.
|
|
365
359
|
on_change: Function to be called with (name, value) when the value of an input changes.
|
|
366
360
|
"""
|
|
367
|
-
|
|
368
361
|
for name, options in user_params.items():
|
|
369
362
|
|
|
370
363
|
def change_handler(value, name=name):
|
|
@@ -423,8 +416,7 @@ def UserInputs(user_params, on_change=None):
|
|
|
423
416
|
|
|
424
417
|
|
|
425
418
|
def make_text(renderer):
|
|
426
|
-
"""
|
|
427
|
-
Create a function that renders text using Markdown.
|
|
419
|
+
"""Create a function that renders text using Markdown.
|
|
428
420
|
|
|
429
421
|
Args:
|
|
430
422
|
renderer: Function that takes a model and returns a string
|
|
@@ -440,8 +432,7 @@ def make_text(renderer):
|
|
|
440
432
|
|
|
441
433
|
|
|
442
434
|
def make_initial_grid_layout(layout_types):
|
|
443
|
-
"""
|
|
444
|
-
Create an initial grid layout for visualization components.
|
|
435
|
+
"""Create an initial grid layout for visualization components.
|
|
445
436
|
|
|
446
437
|
Args:
|
|
447
438
|
layout_types: List of layout types (Space or Measure)
|