Mesa 3.0.3__py3-none-any.whl → 3.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of Mesa might be problematic. Click here for more details.

Files changed (50) hide show
  1. mesa/__init__.py +4 -6
  2. mesa/agent.py +48 -25
  3. mesa/batchrunner.py +14 -1
  4. mesa/datacollection.py +1 -6
  5. mesa/examples/__init__.py +2 -2
  6. mesa/examples/advanced/epstein_civil_violence/app.py +5 -0
  7. mesa/examples/advanced/pd_grid/app.py +5 -0
  8. mesa/examples/advanced/sugarscape_g1mt/app.py +7 -2
  9. mesa/examples/basic/boid_flockers/app.py +5 -0
  10. mesa/examples/basic/boltzmann_wealth_model/app.py +8 -5
  11. mesa/examples/basic/boltzmann_wealth_model/st_app.py +1 -1
  12. mesa/examples/basic/conways_game_of_life/app.py +5 -0
  13. mesa/examples/basic/conways_game_of_life/st_app.py +2 -2
  14. mesa/examples/basic/schelling/app.py +5 -0
  15. mesa/examples/basic/virus_on_network/app.py +5 -0
  16. mesa/experimental/__init__.py +17 -10
  17. mesa/experimental/cell_space/__init__.py +19 -7
  18. mesa/experimental/cell_space/cell.py +22 -37
  19. mesa/experimental/cell_space/cell_agent.py +12 -1
  20. mesa/experimental/cell_space/cell_collection.py +14 -1
  21. mesa/experimental/cell_space/discrete_space.py +15 -64
  22. mesa/experimental/cell_space/grid.py +74 -4
  23. mesa/experimental/cell_space/network.py +13 -1
  24. mesa/experimental/cell_space/property_layer.py +444 -0
  25. mesa/experimental/cell_space/voronoi.py +13 -1
  26. mesa/experimental/devs/__init__.py +20 -2
  27. mesa/experimental/devs/eventlist.py +19 -1
  28. mesa/experimental/devs/simulator.py +24 -8
  29. mesa/experimental/mesa_signals/__init__.py +23 -0
  30. mesa/experimental/mesa_signals/mesa_signal.py +485 -0
  31. mesa/experimental/mesa_signals/observable_collections.py +133 -0
  32. mesa/experimental/mesa_signals/signals_util.py +52 -0
  33. mesa/mesa_logging.py +190 -0
  34. mesa/model.py +17 -74
  35. mesa/visualization/__init__.py +2 -2
  36. mesa/visualization/mpl_space_drawing.py +2 -2
  37. mesa/visualization/solara_viz.py +12 -0
  38. {mesa-3.0.3.dist-info → mesa-3.1.0.dist-info}/METADATA +3 -4
  39. {mesa-3.0.3.dist-info → mesa-3.1.0.dist-info}/RECORD +43 -44
  40. mesa/experimental/UserParam.py +0 -67
  41. mesa/experimental/components/altair.py +0 -81
  42. mesa/experimental/components/matplotlib.py +0 -242
  43. mesa/experimental/devs/examples/epstein_civil_violence.py +0 -305
  44. mesa/experimental/devs/examples/wolf_sheep.py +0 -250
  45. mesa/experimental/solara_viz.py +0 -453
  46. mesa/time.py +0 -391
  47. {mesa-3.0.3.dist-info → mesa-3.1.0.dist-info}/WHEEL +0 -0
  48. {mesa-3.0.3.dist-info → mesa-3.1.0.dist-info}/entry_points.txt +0 -0
  49. {mesa-3.0.3.dist-info → mesa-3.1.0.dist-info}/licenses/LICENSE +0 -0
  50. {mesa-3.0.3.dist-info → mesa-3.1.0.dist-info}/licenses/NOTICE +0 -0
@@ -1,305 +0,0 @@
1
- """Epstein civil violence example using ABMSimulator."""
2
-
3
- import enum
4
- import math
5
-
6
- from mesa import Agent, Model
7
- from mesa.experimental.devs.simulator import ABMSimulator
8
- from mesa.space import SingleGrid
9
-
10
-
11
- class EpsteinAgent(Agent):
12
- """Epstein Agent."""
13
-
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
- """
22
- super().__init__(model)
23
- self.vision = vision
24
- self.movement = movement
25
-
26
-
27
- class AgentState(enum.IntEnum):
28
- """Agent states."""
29
-
30
- QUIESCENT = enum.auto()
31
- ARRESTED = enum.auto()
32
- ACTIVE = enum.auto()
33
-
34
-
35
- class Citizen(EpsteinAgent):
36
- """A member of the general population, may or may not be in active rebellion.
37
-
38
- Summary of rule: If grievance - risk > threshold, rebel.
39
-
40
- Attributes:
41
- unique_id: unique int
42
- model :
43
- hardship: Agent's 'perceived hardship (i.e., physical or economic
44
- privation).' Exogenous, drawn from U(0,1).
45
- regime_legitimacy: Agent's perception of regime legitimacy, equal
46
- across agents. Exogenous.
47
- risk_aversion: Exogenous, drawn from U(0,1).
48
- threshold: if (grievance - (risk_aversion * arrest_probability)) >
49
- threshold, go/remain Active
50
- vision: number of cells in each direction (N, S, E and W) that agent
51
- can inspect
52
- condition: Can be "Quiescent" or "Active;" deterministic function of
53
- greivance, perceived risk, and
54
- grievance: deterministic function of hardship and regime_legitimacy;
55
- how aggrieved is agent at the regime?
56
- arrest_probability: agent's assessment of arrest probability, given
57
- rebellion
58
- """
59
-
60
- def __init__(
61
- self,
62
- model,
63
- vision,
64
- movement,
65
- hardship,
66
- regime_legitimacy,
67
- risk_aversion,
68
- threshold,
69
- arrest_prob_constant,
70
- ):
71
- """Create a new Citizen.
72
-
73
- Args:
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
78
- hardship: Agent's 'perceived hardship (i.e., physical or economic
79
- privation).' Exogenous, drawn from U(0,1).
80
- regime_legitimacy: Agent's perception of regime legitimacy, equal
81
- across agents. Exogenous.
82
- risk_aversion: Exogenous, drawn from U(0,1).
83
- threshold: if (grievance - (risk_aversion * arrest_probability)) >
84
- threshold, go/remain Active
85
- arrest_prob_constant : agent's assessment of arrest probability
86
-
87
- """
88
- super().__init__(model, vision, movement)
89
- self.hardship = hardship
90
- self.regime_legitimacy = regime_legitimacy
91
- self.risk_aversion = risk_aversion
92
- self.threshold = threshold
93
- self.condition = AgentState.QUIESCENT
94
- self.grievance = self.hardship * (1 - self.regime_legitimacy)
95
- self.arrest_probability = None
96
- self.arrest_prob_constant = arrest_prob_constant
97
-
98
- def step(self):
99
- """Decide whether to activate, then move if applicable."""
100
- self.update_neighbors()
101
- self.update_estimated_arrest_probability()
102
- net_risk = self.risk_aversion * self.arrest_probability
103
- if self.grievance - net_risk > self.threshold:
104
- self.condition = AgentState.ACTIVE
105
- else:
106
- self.condition = AgentState.QUIESCENT
107
- if self.movement and self.empty_neighbors:
108
- new_pos = self.random.choice(self.empty_neighbors)
109
- self.model.grid.move_agent(self, new_pos)
110
-
111
- def update_neighbors(self):
112
- """Look around and see who my neighbors are."""
113
- self.neighborhood = self.model.grid.get_neighborhood(
114
- self.pos, moore=True, radius=self.vision
115
- )
116
- self.neighbors = self.model.grid.get_cell_list_contents(self.neighborhood)
117
- self.empty_neighbors = [
118
- c for c in self.neighborhood if self.model.grid.is_cell_empty(c)
119
- ]
120
-
121
- def update_estimated_arrest_probability(self):
122
- """Based on the ratio of cops to actives in my neighborhood, estimate the p(Arrest | I go active)."""
123
- cops_in_vision = len([c for c in self.neighbors if isinstance(c, Cop)])
124
- actives_in_vision = 1.0 # citizen counts herself
125
- for c in self.neighbors:
126
- if isinstance(c, Citizen) and c.condition == AgentState.ACTIVE:
127
- actives_in_vision += 1
128
- self.arrest_probability = 1 - math.exp(
129
- -1 * self.arrest_prob_constant * (cops_in_vision / actives_in_vision)
130
- )
131
-
132
- def sent_to_jail(self, value):
133
- """Sent agent to jail.
134
-
135
- Args:
136
- value: duration of jail sentence
137
-
138
- """
139
- self.model.active_agents.remove(self)
140
- self.condition = AgentState.ARRESTED
141
- self.model.simulator.schedule_event_relative(self.release_from_jail, value)
142
-
143
- def release_from_jail(self):
144
- """Release agent from jail."""
145
- self.model.active_agents.add(self)
146
- self.condition = AgentState.QUIESCENT
147
-
148
-
149
- class Cop(EpsteinAgent):
150
- """A cop for life. No defection.
151
-
152
- Summary of rule: Inspect local vision and arrest a random active agent.
153
-
154
- Attributes:
155
- unique_id: unique int
156
- x, y: Grid coordinates
157
- vision: number of cells in each direction (N, S, E and W) that cop is
158
- able to inspect
159
- """
160
-
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
- """
170
- super().__init__(model, vision, movement)
171
- self.max_jail_term = max_jail_term
172
-
173
- def step(self):
174
- """Inspect local vision and arrest a random active agent. Move if applicable."""
175
- self.update_neighbors()
176
- active_neighbors = []
177
- for agent in self.neighbors:
178
- if isinstance(agent, Citizen) and agent.condition == "Active":
179
- active_neighbors.append(agent)
180
- if active_neighbors:
181
- arrestee = self.random.choice(active_neighbors)
182
- arrestee.sent_to_jail(self.random.randint(0, self.max_jail_term))
183
- if self.movement and self.empty_neighbors:
184
- new_pos = self.random.choice(self.empty_neighbors)
185
- self.model.grid.move_agent(self, new_pos)
186
-
187
- def update_neighbors(self):
188
- """Look around and see who my neighbors are."""
189
- self.neighborhood = self.model.grid.get_neighborhood(
190
- self.pos, moore=True, radius=self.vision
191
- )
192
- self.neighbors = self.model.grid.get_cell_list_contents(self.neighborhood)
193
- self.empty_neighbors = [
194
- c for c in self.neighborhood if self.model.grid.is_cell_empty(c)
195
- ]
196
-
197
-
198
- class EpsteinCivilViolence(Model):
199
- """Model 1 from "Modeling civil violence: An agent-based computational approach," by Joshua Epstein.
200
-
201
- http://www.pnas.org/content/99/suppl_3/7243.full
202
- Attributes:
203
- height: grid height
204
- width: grid width
205
- citizen_density: approximate % of cells occupied by citizens.
206
- cop_density: approximate % of cells occupied by cops.
207
- citizen_vision: number of cells in each direction (N, S, E and W) that
208
- citizen can inspect
209
- cop_vision: number of cells in each direction (N, S, E and W) that cop
210
- can inspect
211
- legitimacy: (L) citizens' perception of regime legitimacy, equal
212
- across all citizens
213
- max_jail_term: (J_max)
214
- active_threshold: if (grievance - (risk_aversion * arrest_probability))
215
- > threshold, citizen rebels
216
- arrest_prob_constant: set to ensure agents make plausible arrest
217
- probability estimates
218
- movement: binary, whether agents try to move at step end
219
- max_iters: model may not have a natural stopping point, so we set a
220
- max.
221
- """
222
-
223
- def __init__(
224
- self,
225
- width=40,
226
- height=40,
227
- citizen_density=0.7,
228
- cop_density=0.074,
229
- citizen_vision=7,
230
- cop_vision=7,
231
- legitimacy=0.8,
232
- max_jail_term=1000,
233
- active_threshold=0.1,
234
- arrest_prob_constant=2.3,
235
- movement=True,
236
- max_iters=1000,
237
- seed=None,
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
- """
256
- super().__init__(seed)
257
- if cop_density + citizen_density > 1:
258
- raise ValueError("Cop density + citizen density must be less than 1")
259
-
260
- self.width = width
261
- self.height = height
262
- self.citizen_density = citizen_density
263
- self.cop_density = cop_density
264
-
265
- self.max_iters = max_iters
266
-
267
- self.grid = SingleGrid(self.width, self.height, torus=True)
268
-
269
- for _, pos in self.grid.coord_iter():
270
- if self.random.random() < self.cop_density:
271
- agent = Cop(
272
- self,
273
- cop_vision,
274
- movement,
275
- max_jail_term,
276
- )
277
- elif self.random.random() < (self.cop_density + self.citizen_density):
278
- agent = Citizen(
279
- self,
280
- citizen_vision,
281
- movement,
282
- hardship=self.random.random(),
283
- regime_legitimacy=legitimacy,
284
- risk_aversion=self.random.random(),
285
- threshold=active_threshold,
286
- arrest_prob_constant=arrest_prob_constant,
287
- )
288
- else:
289
- continue
290
- self.grid.place_agent(agent, pos)
291
-
292
- self.active_agents = self.agents
293
-
294
- def step(self):
295
- """Run one step of the model."""
296
- self.active_agents.shuffle_do("step")
297
-
298
-
299
- if __name__ == "__main__":
300
- model = EpsteinCivilViolence(seed=15)
301
- simulator = ABMSimulator()
302
-
303
- simulator.setup(model)
304
-
305
- simulator.run_for(time_delta=100)
@@ -1,250 +0,0 @@
1
- """Example of using ABM simulator for Wolf-Sheep Predation Model."""
2
-
3
- import mesa
4
- from mesa.experimental.cell_space import FixedAgent
5
- from mesa.experimental.devs.simulator import ABMSimulator
6
-
7
-
8
- class Animal(mesa.Agent):
9
- """Base Animal class."""
10
-
11
- def __init__(self, model, moore, energy, p_reproduce, energy_from_food):
12
- """Initialize Animal instance.
13
-
14
- Args:
15
- model: a model instance
16
- moore: using moore grid or not
17
- energy: initial energy
18
- p_reproduce: probability of reproduction
19
- energy_from_food: energy gained from 1 unit of food
20
- """
21
- super().__init__(model)
22
- self.energy = energy
23
- self.p_reproduce = p_reproduce
24
- self.energy_from_food = energy_from_food
25
- self.moore = moore
26
-
27
- def random_move(self):
28
- """Move to random neighboring cell."""
29
- next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True)
30
- next_move = self.random.choice(next_moves)
31
- # Now move:
32
- self.model.grid.move_agent(self, next_move)
33
-
34
- def spawn_offspring(self):
35
- """Create offspring."""
36
- self.energy /= 2
37
- offspring = self.__class__(
38
- self.model,
39
- self.moore,
40
- self.energy,
41
- self.p_reproduce,
42
- self.energy_from_food,
43
- )
44
- self.model.grid.place_agent(offspring, self.pos)
45
-
46
- def feed(self): ... # noqa: D102
47
-
48
- def die(self):
49
- """Die."""
50
- self.model.grid.remove_agent(self)
51
- self.remove()
52
-
53
- def step(self):
54
- """Execute one step of the agent."""
55
- self.random_move()
56
- self.energy -= 1
57
-
58
- self.feed()
59
-
60
- if self.energy < 0:
61
- self.die()
62
- elif self.random.random() < self.p_reproduce:
63
- self.spawn_offspring()
64
-
65
-
66
- class Sheep(Animal):
67
- """A sheep that walks around, reproduces (asexually) and gets eaten."""
68
-
69
- def feed(self):
70
- """Eat grass and gain energy."""
71
- # If there is grass available, eat it
72
- agents = self.model.grid.get_cell_list_contents(self.pos)
73
- grass_patch = next(obj for obj in agents if isinstance(obj, GrassPatch))
74
- if grass_patch.fully_grown:
75
- self.energy += self.energy_from_food
76
- grass_patch.fully_grown = False
77
-
78
-
79
- class Wolf(Animal):
80
- """A wolf that walks around, reproduces (asexually) and eats sheep."""
81
-
82
- def feed(self):
83
- """Eat wolf and gain energy."""
84
- agents = self.model.grid.get_cell_list_contents(self.pos)
85
- sheep = [obj for obj in agents if isinstance(obj, Sheep)]
86
- if len(sheep) > 0:
87
- sheep_to_eat = self.random.choice(sheep)
88
- self.energy += self.energy
89
-
90
- # Kill the sheep
91
- sheep_to_eat.die()
92
-
93
-
94
- class GrassPatch(FixedAgent):
95
- """A patch of grass that grows at a fixed rate and it is eaten by sheep."""
96
-
97
- @property
98
- def fully_grown(self) -> bool: # noqa: D102
99
- return self._fully_grown
100
-
101
- @fully_grown.setter
102
- def fully_grown(self, value: bool):
103
- self._fully_grown = value
104
-
105
- if not value:
106
- self.model.simulator.schedule_event_relative(
107
- setattr,
108
- self.grass_regrowth_time,
109
- function_args=[self, "fully_grown", True],
110
- )
111
-
112
- def __init__(self, model, fully_grown, countdown, grass_regrowth_time):
113
- """Creates a new patch of grass.
114
-
115
- Args:
116
- model: a model instance
117
- fully_grown: (boolean) Whether the patch of grass is fully grown or not
118
- countdown: Time for the patch of grass to be fully grown again
119
- grass_regrowth_time: regrowth time for the grass
120
- """
121
- super().__init__(model)
122
- self._fully_grown = fully_grown
123
- self.grass_regrowth_time = grass_regrowth_time
124
-
125
- if not self.fully_grown:
126
- self.model.simulator.schedule_event_relative(
127
- setattr, countdown, function_args=[self, "fully_grown", True]
128
- )
129
-
130
- def set_fully_grown(self): # noqa
131
- self.fully_grown = True
132
-
133
-
134
- class WolfSheep(mesa.Model):
135
- """Wolf-Sheep Predation Model.
136
-
137
- A model for simulating wolf and sheep (predator-prey) ecosystem modelling.
138
- """
139
-
140
- def __init__(
141
- self,
142
- height,
143
- width,
144
- initial_sheep,
145
- initial_wolves,
146
- sheep_reproduce,
147
- wolf_reproduce,
148
- grass_regrowth_time,
149
- wolf_gain_from_food=13,
150
- sheep_gain_from_food=5,
151
- moore=False,
152
- simulator=None,
153
- seed=None,
154
- ):
155
- """Create a new Wolf-Sheep model with the given parameters.
156
-
157
- Args:
158
- height: height of the grid
159
- width: width of the grid
160
- initial_sheep: Number of sheep to start with
161
- initial_wolves: Number of wolves to start with
162
- sheep_reproduce: Probability of each sheep reproducing each step
163
- wolf_reproduce: Probability of each wolf reproducing each step
164
- wolf_gain_from_food: Energy a wolf gains from eating a sheep
165
- grass: Whether to have the sheep eat grass for energy
166
- grass_regrowth_time: How long it takes for a grass patch to regrow
167
- once it is eaten
168
- sheep_gain_from_food: Energy sheep gain from grass, if enabled.
169
- moore: whether to use moore or von Neumann grid
170
- simulator: Simulator to use for simulating wolf and sheep
171
- seed: Random seed
172
- """
173
- super().__init__(seed=seed)
174
- # Set parameters
175
- self.height = height
176
- self.width = width
177
- self.initial_sheep = initial_sheep
178
- self.initial_wolves = initial_wolves
179
- self.simulator = simulator
180
-
181
- # self.sheep_reproduce = sheep_reproduce
182
- # self.wolf_reproduce = wolf_reproduce
183
- # self.grass_regrowth_time = grass_regrowth_time
184
- # self.wolf_gain_from_food = wolf_gain_from_food
185
- # self.sheep_gain_from_food = sheep_gain_from_food
186
- # self.moore = moore
187
-
188
- self.grid = mesa.space.MultiGrid(self.height, self.width, torus=False)
189
-
190
- for _ in range(self.initial_sheep):
191
- pos = (
192
- self.random.randrange(self.width),
193
- self.random.randrange(self.height),
194
- )
195
- energy = self.random.randrange(2 * sheep_gain_from_food)
196
- sheep = Sheep(
197
- self,
198
- moore,
199
- energy,
200
- sheep_reproduce,
201
- sheep_gain_from_food,
202
- )
203
- self.grid.place_agent(sheep, pos)
204
-
205
- # Create wolves
206
- for _ in range(self.initial_wolves):
207
- pos = (
208
- self.random.randrange(self.width),
209
- self.random.randrange(self.height),
210
- )
211
- energy = self.random.randrange(2 * wolf_gain_from_food)
212
- wolf = Wolf(
213
- self,
214
- moore,
215
- energy,
216
- wolf_reproduce,
217
- wolf_gain_from_food,
218
- )
219
- self.grid.place_agent(wolf, pos)
220
-
221
- # Create grass patches
222
- possibly_fully_grown = [True, False]
223
- for _agent, pos in self.grid.coord_iter():
224
- fully_grown = self.random.choice(possibly_fully_grown)
225
- if fully_grown:
226
- countdown = grass_regrowth_time
227
- else:
228
- countdown = self.random.randrange(grass_regrowth_time)
229
- patch = GrassPatch(self, fully_grown, countdown, grass_regrowth_time)
230
- self.grid.place_agent(patch, pos)
231
-
232
- def step(self):
233
- """Perform one step of the model."""
234
- self.agents_by_type[Sheep].shuffle_do("step")
235
- self.agents_by_type[Wolf].shuffle_do("step")
236
-
237
-
238
- if __name__ == "__main__":
239
- import time
240
-
241
- simulator = ABMSimulator()
242
-
243
- model = WolfSheep(25, 25, 60, 40, 0.2, 0.1, 20, simulator=simulator, seed=15)
244
-
245
- simulator.setup(model)
246
-
247
- start_time = time.perf_counter()
248
- simulator.run(100)
249
- print(simulator.time)
250
- print("Time:", time.perf_counter() - start_time)