Mesa 3.0.0b2__py3-none-any.whl → 3.0.1__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 +1 -1
- mesa/agent.py +15 -3
- mesa/batchrunner.py +26 -1
- mesa/examples/README.md +11 -11
- mesa/examples/__init__.py +2 -2
- mesa/examples/advanced/epstein_civil_violence/agents.py +44 -38
- mesa/examples/advanced/epstein_civil_violence/app.py +29 -28
- mesa/examples/advanced/epstein_civil_violence/model.py +33 -65
- mesa/examples/advanced/pd_grid/app.py +9 -5
- mesa/examples/advanced/pd_grid/model.py +1 -1
- mesa/examples/advanced/sugarscape_g1mt/app.py +5 -13
- mesa/examples/advanced/sugarscape_g1mt/model.py +3 -1
- mesa/examples/advanced/wolf_sheep/agents.py +53 -39
- mesa/examples/advanced/wolf_sheep/app.py +37 -19
- mesa/examples/advanced/wolf_sheep/model.py +68 -74
- mesa/examples/basic/boid_flockers/agents.py +49 -18
- mesa/examples/basic/boid_flockers/app.py +2 -2
- mesa/examples/basic/boid_flockers/model.py +55 -19
- mesa/examples/basic/boltzmann_wealth_model/agents.py +23 -5
- mesa/examples/basic/boltzmann_wealth_model/app.py +22 -13
- mesa/examples/basic/boltzmann_wealth_model/model.py +48 -13
- mesa/examples/basic/boltzmann_wealth_model/st_app.py +2 -2
- mesa/examples/basic/conways_game_of_life/app.py +15 -3
- mesa/examples/basic/schelling/agents.py +9 -5
- mesa/examples/basic/schelling/app.py +5 -5
- mesa/examples/basic/schelling/model.py +48 -26
- mesa/examples/basic/virus_on_network/app.py +25 -47
- mesa/experimental/cell_space/cell_collection.py +14 -2
- mesa/experimental/cell_space/discrete_space.py +16 -2
- mesa/experimental/devs/simulator.py +59 -14
- mesa/model.py +4 -4
- mesa/space.py +0 -30
- mesa/time.py +4 -4
- mesa/visualization/__init__.py +17 -6
- mesa/visualization/components/__init__.py +83 -0
- mesa/visualization/components/{altair.py → altair_components.py} +34 -2
- mesa/visualization/components/matplotlib_components.py +175 -0
- mesa/visualization/mpl_space_drawing.py +593 -0
- mesa/visualization/solara_viz.py +156 -67
- {mesa-3.0.0b2.dist-info → mesa-3.0.1.dist-info}/METADATA +6 -8
- {mesa-3.0.0b2.dist-info → mesa-3.0.1.dist-info}/RECORD +46 -44
- {mesa-3.0.0b2.dist-info → mesa-3.0.1.dist-info}/WHEEL +1 -1
- mesa/visualization/components/matplotlib.py +0 -386
- /mesa/visualization/{UserParam.py → user_param.py} +0 -0
- {mesa-3.0.0b2.dist-info → mesa-3.0.1.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.0b2.dist-info → mesa-3.0.1.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.0.0b2.dist-info → mesa-3.0.1.dist-info}/licenses/NOTICE +0 -0
mesa/__init__.py
CHANGED
|
@@ -24,7 +24,7 @@ __all__ = [
|
|
|
24
24
|
]
|
|
25
25
|
|
|
26
26
|
__title__ = "mesa"
|
|
27
|
-
__version__ = "3.0.
|
|
27
|
+
__version__ = "3.0.1"
|
|
28
28
|
__license__ = "Apache 2.0"
|
|
29
29
|
_this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
|
|
30
30
|
__copyright__ = f"Copyright {_this_year} Project Mesa Team"
|
mesa/agent.py
CHANGED
|
@@ -110,6 +110,12 @@ class AgentSet(MutableSet, Sequence):
|
|
|
110
110
|
without preventing garbage collection. It is associated with a specific model instance, enabling
|
|
111
111
|
interactions with the model's environment and other agents.The implementation uses a WeakKeyDictionary to store agents,
|
|
112
112
|
which means that agents not referenced elsewhere in the program may be automatically removed from the AgentSet.
|
|
113
|
+
|
|
114
|
+
Notes:
|
|
115
|
+
A `UserWarning` is issued if `random=None`. You can resolve this warning by explicitly
|
|
116
|
+
passing a random number generator. In most cases, this will be the seeded random number
|
|
117
|
+
generator in the model. So, you would do `random=self.random` in a `Model` or `Agent` instance.
|
|
118
|
+
|
|
113
119
|
"""
|
|
114
120
|
|
|
115
121
|
def __init__(self, agents: Iterable[Agent], random: Random | None = None):
|
|
@@ -120,6 +126,11 @@ class AgentSet(MutableSet, Sequence):
|
|
|
120
126
|
random (Random): the random number generator
|
|
121
127
|
"""
|
|
122
128
|
if random is None:
|
|
129
|
+
warnings.warn(
|
|
130
|
+
"Random number generator not specified, this can make models non-reproducible. Please pass a random number generator explicitly",
|
|
131
|
+
UserWarning,
|
|
132
|
+
stacklevel=2,
|
|
133
|
+
)
|
|
123
134
|
random = (
|
|
124
135
|
Random()
|
|
125
136
|
) # FIXME see issue 1981, how to get the central rng from model
|
|
@@ -167,7 +178,7 @@ class AgentSet(MutableSet, Sequence):
|
|
|
167
178
|
"""
|
|
168
179
|
if n is not None:
|
|
169
180
|
warnings.warn(
|
|
170
|
-
"The parameter 'n' is deprecated. Use 'at_most' instead.",
|
|
181
|
+
"The parameter 'n' is deprecated and will be removed in Mesa 3.1. Use 'at_most' instead.",
|
|
171
182
|
DeprecationWarning,
|
|
172
183
|
stacklevel=2,
|
|
173
184
|
)
|
|
@@ -276,8 +287,9 @@ class AgentSet(MutableSet, Sequence):
|
|
|
276
287
|
return_results = False
|
|
277
288
|
else:
|
|
278
289
|
warnings.warn(
|
|
279
|
-
"Using return_results is deprecated
|
|
280
|
-
"AgentSet.map in case of return_results=True",
|
|
290
|
+
"Using return_results is deprecated and will be removed in Mesa 3.1."
|
|
291
|
+
"Use AgenSet.do in case of return_results=False, and AgentSet.map in case of return_results=True",
|
|
292
|
+
DeprecationWarning,
|
|
281
293
|
stacklevel=2,
|
|
282
294
|
)
|
|
283
295
|
|
mesa/batchrunner.py
CHANGED
|
@@ -1,4 +1,29 @@
|
|
|
1
|
-
"""batchrunner for running a factorial experiment design over a model.
|
|
1
|
+
"""batchrunner for running a factorial experiment design over a model.
|
|
2
|
+
|
|
3
|
+
To take advantage of parallel execution of experiments, `batch_run` uses
|
|
4
|
+
multiprocessing if ``number_processes`` is larger than 1. It is strongly advised
|
|
5
|
+
to only run in parallel using a normal python file (so don't try to do it in a
|
|
6
|
+
jupyter notebook). Moreover, best practice when using multiprocessing is to
|
|
7
|
+
put the code inside an ``if __name__ == '__main__':`` code black as shown below::
|
|
8
|
+
|
|
9
|
+
from mesa.batchrunner import batch_run
|
|
10
|
+
|
|
11
|
+
params = {"width": 10, "height": 10, "N": range(10, 500, 10)}
|
|
12
|
+
|
|
13
|
+
if __name__ == '__main__':
|
|
14
|
+
results = batch_run(
|
|
15
|
+
MoneyModel,
|
|
16
|
+
parameters=params,
|
|
17
|
+
iterations=5,
|
|
18
|
+
max_steps=100,
|
|
19
|
+
number_processes=None,
|
|
20
|
+
data_collection_period=1,
|
|
21
|
+
display_progress=True,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
"""
|
|
2
27
|
|
|
3
28
|
import itertools
|
|
4
29
|
import multiprocessing
|
mesa/examples/README.md
CHANGED
|
@@ -1,37 +1,37 @@
|
|
|
1
1
|
# Mesa core examples
|
|
2
|
-
|
|
2
|
+
These examples are a collection of classic agent based models built using Mesa. These core examples are maintained by the Mesa team and are intended to demonstrate the capabilities of Mesa.
|
|
3
3
|
|
|
4
4
|
More user examples and showcases can be found in the [mesa-examples](https://github.com/projectmesa/mesa-examples) repository.
|
|
5
5
|
|
|
6
6
|
## Basic Examples
|
|
7
7
|
The basic examples are relatively simple and only use stable Mesa features. They are good starting points for learning how to use Mesa.
|
|
8
8
|
|
|
9
|
-
### [Boltzmann Wealth Model](basic/boltzmann_wealth_model)
|
|
9
|
+
### [Boltzmann Wealth Model](examples/basic/boltzmann_wealth_model)
|
|
10
10
|
Completed code to go along with the [tutorial](https://mesa.readthedocs.io/latest/tutorials/intro_tutorial.html) on making a simple model of how a highly-skewed wealth distribution can emerge from simple rules.
|
|
11
11
|
|
|
12
|
-
### [Boids Flockers Model](basic/boid_flockers)
|
|
12
|
+
### [Boids Flockers Model](examples/basic/boid_flockers)
|
|
13
13
|
[Boids](https://en.wikipedia.org/wiki/Boids)-style flocking model, demonstrating the use of agents moving through a continuous space following direction vectors.
|
|
14
14
|
|
|
15
|
-
### [Conway's Game of Life](basic/conways_game_of_life)
|
|
15
|
+
### [Conway's Game of Life](examples/basic/conways_game_of_life)
|
|
16
16
|
Implementation of [Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life), a cellular automata where simple rules can give rise to complex patterns.
|
|
17
17
|
|
|
18
|
-
### [Schelling Segregation Model](basic/schelling)
|
|
18
|
+
### [Schelling Segregation Model](examples/basic/schelling)
|
|
19
19
|
Mesa implementation of the classic [Schelling segregation](http://nifty.stanford.edu/2014/mccown-schelling-model-segregation/) model.
|
|
20
20
|
|
|
21
|
-
### [Virus on a Network Model](basic/virus_on_network)
|
|
21
|
+
### [Virus on a Network Model](examples/basic/virus_on_network)
|
|
22
22
|
This model is based on the NetLogo [Virus on a Network](https://ccl.northwestern.edu/netlogo/models/VirusonaNetwork) model.
|
|
23
23
|
|
|
24
24
|
## Advanced Examples
|
|
25
25
|
The advanced examples are more complex and may use experimental Mesa features. They are good starting points for learning how to build more complex models.
|
|
26
26
|
|
|
27
|
-
### [Epstein Civil Violence Model](advanced/epstein_civil_violence)
|
|
28
|
-
Joshua Epstein's [model](
|
|
27
|
+
### [Epstein Civil Violence Model](examples/advanced/epstein_civil_violence)
|
|
28
|
+
Joshua Epstein's [model](https://www.pnas.org/doi/10.1073/pnas.092080199) of how a decentralized uprising can be suppressed or reach a critical mass of support.
|
|
29
29
|
|
|
30
|
-
### [Demographic Prisoner's Dilemma on a Grid](advanced/pd_grid)
|
|
30
|
+
### [Demographic Prisoner's Dilemma on a Grid](examples/advanced/pd_grid)
|
|
31
31
|
Grid-based demographic prisoner's dilemma model, demonstrating how simple rules can lead to the emergence of widespread cooperation -- and how a model activation regime can change its outcome.
|
|
32
32
|
|
|
33
|
-
### [Sugarscape Model with Traders](advanced/sugarscape_g1mt)
|
|
33
|
+
### [Sugarscape Model with Traders](examples/advanced/sugarscape_g1mt)
|
|
34
34
|
This is Epstein & Axtell's Sugarscape model with Traders, a detailed description is in Chapter four of *Growing Artificial Societies: Social Science from the Bottom Up (1996)*. The model shows how emergent price equilibrium can happen via decentralized dynamics.
|
|
35
35
|
|
|
36
|
-
### [Wolf-Sheep Predation Model](advanced/wolf_sheep)
|
|
36
|
+
### [Wolf-Sheep Predation Model](examples/advanced/wolf_sheep)
|
|
37
37
|
Implementation of an ecological model of predation and reproduction, based on the NetLogo [Wolf Sheep Predation](http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation) model.
|
mesa/examples/__init__.py
CHANGED
|
@@ -3,14 +3,14 @@ from mesa.examples.advanced.pd_grid.model import PdGrid
|
|
|
3
3
|
from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
|
|
4
4
|
from mesa.examples.advanced.wolf_sheep.model import WolfSheep
|
|
5
5
|
from mesa.examples.basic.boid_flockers.model import BoidFlockers
|
|
6
|
-
from mesa.examples.basic.boltzmann_wealth_model.model import
|
|
6
|
+
from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealth
|
|
7
7
|
from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
|
|
8
8
|
from mesa.examples.basic.schelling.model import Schelling
|
|
9
9
|
from mesa.examples.basic.virus_on_network.model import VirusOnNetwork
|
|
10
10
|
|
|
11
11
|
__all__ = [
|
|
12
12
|
"BoidFlockers",
|
|
13
|
-
"
|
|
13
|
+
"BoltzmannWealth",
|
|
14
14
|
"ConwaysGameOfLife",
|
|
15
15
|
"Schelling",
|
|
16
16
|
"VirusOnNetwork",
|
|
@@ -1,18 +1,29 @@
|
|
|
1
1
|
import math
|
|
2
|
+
from enum import Enum
|
|
2
3
|
|
|
3
4
|
import mesa
|
|
4
5
|
|
|
5
6
|
|
|
7
|
+
class CitizenState(Enum):
|
|
8
|
+
ACTIVE = 1
|
|
9
|
+
QUIET = 2
|
|
10
|
+
ARRESTED = 3
|
|
11
|
+
|
|
12
|
+
|
|
6
13
|
class EpsteinAgent(mesa.experimental.cell_space.CellAgent):
|
|
7
14
|
def update_neighbors(self):
|
|
8
15
|
"""
|
|
9
16
|
Look around and see who my neighbors are
|
|
10
17
|
"""
|
|
11
18
|
self.neighborhood = self.cell.get_neighborhood(radius=self.vision)
|
|
12
|
-
|
|
13
19
|
self.neighbors = self.neighborhood.agents
|
|
14
20
|
self.empty_neighbors = [c for c in self.neighborhood if c.is_empty]
|
|
15
21
|
|
|
22
|
+
def move(self):
|
|
23
|
+
if self.model.movement and self.empty_neighbors:
|
|
24
|
+
new_pos = self.random.choice(self.empty_neighbors)
|
|
25
|
+
self.move_to(new_pos)
|
|
26
|
+
|
|
16
27
|
|
|
17
28
|
class Citizen(EpsteinAgent):
|
|
18
29
|
"""
|
|
@@ -38,13 +49,7 @@ class Citizen(EpsteinAgent):
|
|
|
38
49
|
"""
|
|
39
50
|
|
|
40
51
|
def __init__(
|
|
41
|
-
self,
|
|
42
|
-
model,
|
|
43
|
-
hardship,
|
|
44
|
-
regime_legitimacy,
|
|
45
|
-
risk_aversion,
|
|
46
|
-
threshold,
|
|
47
|
-
vision,
|
|
52
|
+
self, model, regime_legitimacy, threshold, vision, arrest_prob_constant
|
|
48
53
|
):
|
|
49
54
|
"""
|
|
50
55
|
Create a new Citizen.
|
|
@@ -62,16 +67,21 @@ class Citizen(EpsteinAgent):
|
|
|
62
67
|
model: model instance
|
|
63
68
|
"""
|
|
64
69
|
super().__init__(model)
|
|
65
|
-
self.hardship =
|
|
70
|
+
self.hardship = self.random.random()
|
|
71
|
+
self.risk_aversion = self.random.random()
|
|
66
72
|
self.regime_legitimacy = regime_legitimacy
|
|
67
|
-
self.risk_aversion = risk_aversion
|
|
68
73
|
self.threshold = threshold
|
|
69
|
-
self.
|
|
74
|
+
self.state = CitizenState.QUIET
|
|
70
75
|
self.vision = vision
|
|
71
76
|
self.jail_sentence = 0
|
|
72
77
|
self.grievance = self.hardship * (1 - self.regime_legitimacy)
|
|
78
|
+
self.arrest_prob_constant = arrest_prob_constant
|
|
73
79
|
self.arrest_probability = None
|
|
74
80
|
|
|
81
|
+
self.neighborhood = []
|
|
82
|
+
self.neighbors = []
|
|
83
|
+
self.empty_neighbors = []
|
|
84
|
+
|
|
75
85
|
def step(self):
|
|
76
86
|
"""
|
|
77
87
|
Decide whether to activate, then move if applicable.
|
|
@@ -81,32 +91,33 @@ class Citizen(EpsteinAgent):
|
|
|
81
91
|
return # no other changes or movements if agent is in jail.
|
|
82
92
|
self.update_neighbors()
|
|
83
93
|
self.update_estimated_arrest_probability()
|
|
94
|
+
|
|
84
95
|
net_risk = self.risk_aversion * self.arrest_probability
|
|
85
|
-
if self.grievance - net_risk > self.threshold:
|
|
86
|
-
self.
|
|
96
|
+
if (self.grievance - net_risk) > self.threshold:
|
|
97
|
+
self.state = CitizenState.ACTIVE
|
|
87
98
|
else:
|
|
88
|
-
self.
|
|
99
|
+
self.state = CitizenState.QUIET
|
|
89
100
|
|
|
90
|
-
|
|
91
|
-
new_cell = self.random.choice(self.empty_neighbors)
|
|
92
|
-
self.move_to(new_cell)
|
|
101
|
+
self.move()
|
|
93
102
|
|
|
94
103
|
def update_estimated_arrest_probability(self):
|
|
95
104
|
"""
|
|
96
105
|
Based on the ratio of cops to actives in my neighborhood, estimate the
|
|
97
106
|
p(Arrest | I go active).
|
|
98
107
|
"""
|
|
99
|
-
cops_in_vision =
|
|
100
|
-
actives_in_vision = 1
|
|
101
|
-
for
|
|
102
|
-
if (
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
and c.jail_sentence == 0
|
|
106
|
-
):
|
|
108
|
+
cops_in_vision = 0
|
|
109
|
+
actives_in_vision = 1 # citizen counts herself
|
|
110
|
+
for neighbor in self.neighbors:
|
|
111
|
+
if isinstance(neighbor, Cop):
|
|
112
|
+
cops_in_vision += 1
|
|
113
|
+
elif neighbor.state == CitizenState.ACTIVE:
|
|
107
114
|
actives_in_vision += 1
|
|
115
|
+
|
|
116
|
+
# there is a body of literature on this equation
|
|
117
|
+
# the round is not in the pnas paper but without it, its impossible to replicate
|
|
118
|
+
# the dynamics shown there.
|
|
108
119
|
self.arrest_probability = 1 - math.exp(
|
|
109
|
-
-1 * self.
|
|
120
|
+
-1 * self.arrest_prob_constant * round(cops_in_vision / actives_in_vision)
|
|
110
121
|
)
|
|
111
122
|
|
|
112
123
|
|
|
@@ -122,7 +133,7 @@ class Cop(EpsteinAgent):
|
|
|
122
133
|
able to inspect
|
|
123
134
|
"""
|
|
124
135
|
|
|
125
|
-
def __init__(self, model, vision):
|
|
136
|
+
def __init__(self, model, vision, max_jail_term):
|
|
126
137
|
"""
|
|
127
138
|
Create a new Cop.
|
|
128
139
|
Args:
|
|
@@ -133,6 +144,7 @@ class Cop(EpsteinAgent):
|
|
|
133
144
|
"""
|
|
134
145
|
super().__init__(model)
|
|
135
146
|
self.vision = vision
|
|
147
|
+
self.max_jail_term = max_jail_term
|
|
136
148
|
|
|
137
149
|
def step(self):
|
|
138
150
|
"""
|
|
@@ -142,17 +154,11 @@ class Cop(EpsteinAgent):
|
|
|
142
154
|
self.update_neighbors()
|
|
143
155
|
active_neighbors = []
|
|
144
156
|
for agent in self.neighbors:
|
|
145
|
-
if (
|
|
146
|
-
isinstance(agent, Citizen)
|
|
147
|
-
and agent.condition == "Active"
|
|
148
|
-
and agent.jail_sentence == 0
|
|
149
|
-
):
|
|
157
|
+
if isinstance(agent, Citizen) and agent.state == CitizenState.ACTIVE:
|
|
150
158
|
active_neighbors.append(agent)
|
|
151
159
|
if active_neighbors:
|
|
152
160
|
arrestee = self.random.choice(active_neighbors)
|
|
153
|
-
|
|
154
|
-
arrestee.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
new_pos = self.random.choice(self.empty_neighbors)
|
|
158
|
-
self.move_to(new_pos)
|
|
161
|
+
arrestee.jail_sentence = self.random.randint(0, self.max_jail_term)
|
|
162
|
+
arrestee.state = CitizenState.ARRESTED
|
|
163
|
+
|
|
164
|
+
self.move()
|
|
@@ -1,17 +1,23 @@
|
|
|
1
|
-
from mesa.examples.advanced.epstein_civil_violence.agents import
|
|
1
|
+
from mesa.examples.advanced.epstein_civil_violence.agents import (
|
|
2
|
+
Citizen,
|
|
3
|
+
CitizenState,
|
|
4
|
+
Cop,
|
|
5
|
+
)
|
|
2
6
|
from mesa.examples.advanced.epstein_civil_violence.model import EpsteinCivilViolence
|
|
3
7
|
from mesa.visualization import (
|
|
4
8
|
Slider,
|
|
5
9
|
SolaraViz,
|
|
6
|
-
|
|
7
|
-
|
|
10
|
+
make_plot_component,
|
|
11
|
+
make_space_component,
|
|
8
12
|
)
|
|
9
13
|
|
|
10
14
|
COP_COLOR = "#000000"
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
+
|
|
16
|
+
agent_colors = {
|
|
17
|
+
CitizenState.ACTIVE: "#FE6100",
|
|
18
|
+
CitizenState.QUIET: "#648FFF",
|
|
19
|
+
CitizenState.ARRESTED: "#808080",
|
|
20
|
+
}
|
|
15
21
|
|
|
16
22
|
|
|
17
23
|
def citizen_cop_portrayal(agent):
|
|
@@ -19,34 +25,24 @@ def citizen_cop_portrayal(agent):
|
|
|
19
25
|
return
|
|
20
26
|
|
|
21
27
|
portrayal = {
|
|
22
|
-
"size":
|
|
23
|
-
"shape": "s", # square marker
|
|
28
|
+
"size": 50,
|
|
24
29
|
}
|
|
25
30
|
|
|
26
31
|
if isinstance(agent, Citizen):
|
|
27
|
-
color =
|
|
28
|
-
AGENT_QUIET_COLOR if agent.condition == "Quiescent" else AGENT_REBEL_COLOR
|
|
29
|
-
)
|
|
30
|
-
color = JAIL_COLOR if agent.jail_sentence else color
|
|
31
|
-
shape = JAIL_SHAPE if agent.jail_sentence else "circle"
|
|
32
|
-
portrayal["color"] = color
|
|
33
|
-
portrayal["shape"] = shape
|
|
34
|
-
if shape == "s":
|
|
35
|
-
portrayal["w"] = 0.9
|
|
36
|
-
portrayal["h"] = 0.9
|
|
37
|
-
else:
|
|
38
|
-
portrayal["r"] = 0.5
|
|
39
|
-
portrayal["filled"] = False
|
|
40
|
-
portrayal["layer"] = 0
|
|
41
|
-
|
|
32
|
+
portrayal["color"] = agent_colors[agent.state]
|
|
42
33
|
elif isinstance(agent, Cop):
|
|
43
34
|
portrayal["color"] = COP_COLOR
|
|
44
|
-
portrayal["r"] = 0.9
|
|
45
|
-
portrayal["layer"] = 1
|
|
46
35
|
|
|
47
36
|
return portrayal
|
|
48
37
|
|
|
49
38
|
|
|
39
|
+
def post_process(ax):
|
|
40
|
+
ax.set_aspect("equal")
|
|
41
|
+
ax.set_xticks([])
|
|
42
|
+
ax.set_yticks([])
|
|
43
|
+
ax.get_figure().set_size_inches(10, 10)
|
|
44
|
+
|
|
45
|
+
|
|
50
46
|
model_params = {
|
|
51
47
|
"height": 40,
|
|
52
48
|
"width": 40,
|
|
@@ -58,8 +54,13 @@ model_params = {
|
|
|
58
54
|
"max_jail_term": Slider("Max Jail Term", 30, 0, 50, 1),
|
|
59
55
|
}
|
|
60
56
|
|
|
61
|
-
space_component =
|
|
62
|
-
|
|
57
|
+
space_component = make_space_component(
|
|
58
|
+
citizen_cop_portrayal, post_process=post_process, draw_grid=False
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
chart_component = make_plot_component(
|
|
62
|
+
{state.name.lower(): agent_colors[state] for state in CitizenState}
|
|
63
|
+
)
|
|
63
64
|
|
|
64
65
|
epstein_model = EpsteinCivilViolence()
|
|
65
66
|
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import mesa
|
|
2
|
-
from mesa.examples.advanced.epstein_civil_violence.agents import
|
|
2
|
+
from mesa.examples.advanced.epstein_civil_violence.agents import (
|
|
3
|
+
Citizen,
|
|
4
|
+
CitizenState,
|
|
5
|
+
Cop,
|
|
6
|
+
)
|
|
3
7
|
|
|
4
8
|
|
|
5
9
|
class EpsteinCivilViolence(mesa.Model):
|
|
@@ -7,7 +11,8 @@ class EpsteinCivilViolence(mesa.Model):
|
|
|
7
11
|
Model 1 from "Modeling civil violence: An agent-based computational
|
|
8
12
|
approach," by Joshua Epstein.
|
|
9
13
|
http://www.pnas.org/content/99/suppl_3/7243.full
|
|
10
|
-
|
|
14
|
+
|
|
15
|
+
Args:
|
|
11
16
|
height: grid height
|
|
12
17
|
width: grid width
|
|
13
18
|
citizen_density: approximate % of cells occupied by citizens.
|
|
@@ -45,61 +50,49 @@ class EpsteinCivilViolence(mesa.Model):
|
|
|
45
50
|
seed=None,
|
|
46
51
|
):
|
|
47
52
|
super().__init__(seed=seed)
|
|
48
|
-
self.width = width
|
|
49
|
-
self.height = height
|
|
50
|
-
self.citizen_density = citizen_density
|
|
51
|
-
self.cop_density = cop_density
|
|
52
|
-
self.citizen_vision = citizen_vision
|
|
53
|
-
self.cop_vision = cop_vision
|
|
54
|
-
self.legitimacy = legitimacy
|
|
55
|
-
self.max_jail_term = max_jail_term
|
|
56
|
-
self.active_threshold = active_threshold
|
|
57
|
-
self.arrest_prob_constant = arrest_prob_constant
|
|
58
53
|
self.movement = movement
|
|
59
54
|
self.max_iters = max_iters
|
|
60
|
-
self.iteration = 0
|
|
61
55
|
|
|
62
|
-
self.grid = mesa.experimental.cell_space.
|
|
56
|
+
self.grid = mesa.experimental.cell_space.OrthogonalVonNeumannGrid(
|
|
63
57
|
(width, height), capacity=1, torus=True, random=self.random
|
|
64
58
|
)
|
|
65
59
|
|
|
66
60
|
model_reporters = {
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"Cops": self.count_cops,
|
|
61
|
+
"active": CitizenState.ACTIVE.name,
|
|
62
|
+
"quiet": CitizenState.QUIET.name,
|
|
63
|
+
"arrested": CitizenState.ARRESTED.name,
|
|
71
64
|
}
|
|
72
65
|
agent_reporters = {
|
|
73
|
-
"x": lambda a: a.cell.coordinate[0],
|
|
74
|
-
"y": lambda a: a.cell.coordinate[1],
|
|
75
|
-
"breed": lambda a: type(a).__name__,
|
|
76
66
|
"jail_sentence": lambda a: getattr(a, "jail_sentence", None),
|
|
77
|
-
"condition": lambda a: getattr(a, "condition", None),
|
|
78
67
|
"arrest_probability": lambda a: getattr(a, "arrest_probability", None),
|
|
79
68
|
}
|
|
80
69
|
self.datacollector = mesa.DataCollector(
|
|
81
70
|
model_reporters=model_reporters, agent_reporters=agent_reporters
|
|
82
71
|
)
|
|
83
|
-
if
|
|
72
|
+
if cop_density + citizen_density > 1:
|
|
84
73
|
raise ValueError("Cop density + citizen density must be less than 1")
|
|
85
74
|
|
|
86
75
|
for cell in self.grid.all_cells:
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
klass = self.random.choices(
|
|
77
|
+
[Citizen, Cop, None],
|
|
78
|
+
cum_weights=[citizen_density, citizen_density + cop_density, 1],
|
|
79
|
+
)[0]
|
|
90
80
|
|
|
91
|
-
|
|
81
|
+
if klass == Cop:
|
|
82
|
+
cop = Cop(self, vision=cop_vision, max_jail_term=max_jail_term)
|
|
83
|
+
cop.move_to(cell)
|
|
84
|
+
elif klass == Citizen:
|
|
92
85
|
citizen = Citizen(
|
|
93
86
|
self,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
vision=self.citizen_vision,
|
|
87
|
+
regime_legitimacy=legitimacy,
|
|
88
|
+
threshold=active_threshold,
|
|
89
|
+
vision=citizen_vision,
|
|
90
|
+
arrest_prob_constant=arrest_prob_constant,
|
|
99
91
|
)
|
|
100
92
|
citizen.move_to(cell)
|
|
101
93
|
|
|
102
94
|
self.running = True
|
|
95
|
+
self._update_counts()
|
|
103
96
|
self.datacollector.collect(self)
|
|
104
97
|
|
|
105
98
|
def step(self):
|
|
@@ -107,40 +100,15 @@ class EpsteinCivilViolence(mesa.Model):
|
|
|
107
100
|
Advance the model by one step and collect data.
|
|
108
101
|
"""
|
|
109
102
|
self.agents.shuffle_do("step")
|
|
110
|
-
|
|
103
|
+
self._update_counts()
|
|
111
104
|
self.datacollector.collect(self)
|
|
112
|
-
self.iteration += 1
|
|
113
|
-
if self.iteration > self.max_iters:
|
|
114
|
-
self.running = False
|
|
115
|
-
|
|
116
|
-
@staticmethod
|
|
117
|
-
def count_type_citizens(model, condition, exclude_jailed=True):
|
|
118
|
-
"""
|
|
119
|
-
Helper method to count agents by Quiescent/Active.
|
|
120
|
-
"""
|
|
121
|
-
citizens = model.agents_by_type[Citizen]
|
|
122
105
|
|
|
123
|
-
if
|
|
124
|
-
|
|
125
|
-
[
|
|
126
|
-
c
|
|
127
|
-
for c in citizens
|
|
128
|
-
if (c.condition == condition) and (c.jail_sentence == 0)
|
|
129
|
-
]
|
|
130
|
-
)
|
|
131
|
-
else:
|
|
132
|
-
return len([c for c in citizens if c.condition == condition])
|
|
106
|
+
if self.steps > self.max_iters:
|
|
107
|
+
self.running = False
|
|
133
108
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
""
|
|
137
|
-
Helper method to count jailed agents.
|
|
138
|
-
"""
|
|
139
|
-
return len([a for a in model.agents_by_type[Citizen] if a.jail_sentence > 0])
|
|
109
|
+
def _update_counts(self):
|
|
110
|
+
"""Helper function for counting nr. of citizens in given state."""
|
|
111
|
+
counts = self.agents_by_type[Citizen].groupby("state").count()
|
|
140
112
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
"""
|
|
144
|
-
Helper method to count jailed agents.
|
|
145
|
-
"""
|
|
146
|
-
return len(model.agents_by_type[Cop])
|
|
113
|
+
for state in CitizenState:
|
|
114
|
+
setattr(self, state.name, counts.get(state, 0))
|
|
@@ -3,8 +3,12 @@ Solara-based visualization for the Spatial Prisoner's Dilemma Model.
|
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
5
|
from mesa.examples.advanced.pd_grid.model import PdGrid
|
|
6
|
-
from mesa.visualization import
|
|
7
|
-
|
|
6
|
+
from mesa.visualization import (
|
|
7
|
+
Slider,
|
|
8
|
+
SolaraViz,
|
|
9
|
+
make_plot_component,
|
|
10
|
+
make_space_component,
|
|
11
|
+
)
|
|
8
12
|
|
|
9
13
|
|
|
10
14
|
def pd_agent_portrayal(agent):
|
|
@@ -13,7 +17,7 @@ def pd_agent_portrayal(agent):
|
|
|
13
17
|
"""
|
|
14
18
|
return {
|
|
15
19
|
"color": "blue" if agent.move == "C" else "red",
|
|
16
|
-
"
|
|
20
|
+
"marker": "s", # square marker
|
|
17
21
|
"size": 25,
|
|
18
22
|
}
|
|
19
23
|
|
|
@@ -32,10 +36,10 @@ model_params = {
|
|
|
32
36
|
|
|
33
37
|
|
|
34
38
|
# Create grid visualization component using Altair
|
|
35
|
-
grid_viz =
|
|
39
|
+
grid_viz = make_space_component(agent_portrayal=pd_agent_portrayal)
|
|
36
40
|
|
|
37
41
|
# Create plot for tracking cooperating agents over time
|
|
38
|
-
plot_component =
|
|
42
|
+
plot_component = make_plot_component("Cooperating_Agents")
|
|
39
43
|
|
|
40
44
|
# Initialize model
|
|
41
45
|
initial_model = PdGrid()
|
|
@@ -27,7 +27,7 @@ class PdGrid(mesa.Model):
|
|
|
27
27
|
"""
|
|
28
28
|
super().__init__(seed=seed)
|
|
29
29
|
self.activation_order = activation_order
|
|
30
|
-
self.grid = OrthogonalMooreGrid((width, height), torus=True)
|
|
30
|
+
self.grid = OrthogonalMooreGrid((width, height), torus=True, random=self.random)
|
|
31
31
|
|
|
32
32
|
if payoffs is not None:
|
|
33
33
|
self.payoff = payoffs
|
|
@@ -1,18 +1,10 @@
|
|
|
1
|
-
import os.path
|
|
2
|
-
import sys
|
|
3
|
-
|
|
4
|
-
sys.path.insert(
|
|
5
|
-
0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../../"))
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
|
|
9
1
|
import numpy as np
|
|
10
2
|
import solara
|
|
11
3
|
from matplotlib.figure import Figure
|
|
12
|
-
from sugarscape_g1mt.model import SugarscapeG1mt
|
|
13
|
-
from sugarscape_g1mt.trader_agents import Trader
|
|
14
4
|
|
|
15
|
-
from mesa.
|
|
5
|
+
from mesa.examples.advanced.sugarscape_g1mt.agents import Trader
|
|
6
|
+
from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
|
|
7
|
+
from mesa.visualization import SolaraViz, make_plot_component
|
|
16
8
|
|
|
17
9
|
|
|
18
10
|
def SpaceDrawer(model):
|
|
@@ -63,8 +55,8 @@ model1 = SugarscapeG1mt(50, 50)
|
|
|
63
55
|
|
|
64
56
|
page = SolaraViz(
|
|
65
57
|
model1,
|
|
66
|
-
components=[SpaceDrawer,
|
|
58
|
+
components=[SpaceDrawer, make_plot_component(["Trader", "Price"])],
|
|
67
59
|
name="Sugarscape {G1, M, T}",
|
|
68
|
-
play_interval=
|
|
60
|
+
play_interval=150,
|
|
69
61
|
)
|
|
70
62
|
page # noqa
|
|
@@ -70,7 +70,9 @@ class SugarscapeG1mt(mesa.Model):
|
|
|
70
70
|
self.running = True
|
|
71
71
|
|
|
72
72
|
# initiate mesa grid class
|
|
73
|
-
self.grid = OrthogonalVonNeumannGrid(
|
|
73
|
+
self.grid = OrthogonalVonNeumannGrid(
|
|
74
|
+
(self.width, self.height), torus=False, random=self.random
|
|
75
|
+
)
|
|
74
76
|
# initiate datacollector
|
|
75
77
|
self.datacollector = mesa.DataCollector(
|
|
76
78
|
model_reporters={
|