Mesa 2.4.0__py3-none-any.whl → 3.0.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.
- mesa/__init__.py +3 -5
- mesa/agent.py +105 -92
- mesa/batchrunner.py +55 -31
- mesa/datacollection.py +10 -14
- mesa/examples/README.md +37 -0
- mesa/examples/__init__.py +21 -0
- mesa/examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +116 -0
- mesa/examples/advanced/epstein_civil_violence/Readme.md +34 -0
- mesa/examples/advanced/epstein_civil_violence/__init__.py +0 -0
- mesa/examples/advanced/epstein_civil_violence/agents.py +164 -0
- mesa/examples/advanced/epstein_civil_violence/app.py +73 -0
- mesa/examples/advanced/epstein_civil_violence/model.py +114 -0
- mesa/examples/advanced/pd_grid/Readme.md +43 -0
- mesa/examples/advanced/pd_grid/__init__.py +0 -0
- mesa/examples/advanced/pd_grid/agents.py +50 -0
- mesa/examples/advanced/pd_grid/analysis.ipynb +228 -0
- mesa/examples/advanced/pd_grid/app.py +54 -0
- mesa/examples/advanced/pd_grid/model.py +71 -0
- mesa/examples/advanced/sugarscape_g1mt/Readme.md +64 -0
- mesa/examples/advanced/sugarscape_g1mt/__init__.py +0 -0
- mesa/examples/advanced/sugarscape_g1mt/agents.py +344 -0
- mesa/examples/advanced/sugarscape_g1mt/app.py +62 -0
- mesa/examples/advanced/sugarscape_g1mt/model.py +180 -0
- mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt +50 -0
- mesa/examples/advanced/sugarscape_g1mt/tests.py +69 -0
- mesa/examples/advanced/wolf_sheep/Readme.md +57 -0
- mesa/examples/advanced/wolf_sheep/__init__.py +0 -0
- mesa/examples/advanced/wolf_sheep/agents.py +102 -0
- mesa/examples/advanced/wolf_sheep/app.py +84 -0
- mesa/examples/advanced/wolf_sheep/model.py +137 -0
- mesa/examples/basic/__init__.py +0 -0
- mesa/examples/basic/boid_flockers/Readme.md +22 -0
- mesa/examples/basic/boid_flockers/__init__.py +0 -0
- mesa/examples/basic/boid_flockers/agents.py +71 -0
- mesa/examples/basic/boid_flockers/app.py +58 -0
- mesa/examples/basic/boid_flockers/model.py +69 -0
- mesa/examples/basic/boltzmann_wealth_model/Readme.md +56 -0
- mesa/examples/basic/boltzmann_wealth_model/__init__.py +0 -0
- mesa/examples/basic/boltzmann_wealth_model/agents.py +31 -0
- mesa/examples/basic/boltzmann_wealth_model/app.py +74 -0
- mesa/examples/basic/boltzmann_wealth_model/model.py +43 -0
- mesa/examples/basic/boltzmann_wealth_model/st_app.py +115 -0
- mesa/examples/basic/conways_game_of_life/Readme.md +39 -0
- mesa/examples/basic/conways_game_of_life/__init__.py +0 -0
- mesa/examples/basic/conways_game_of_life/agents.py +47 -0
- mesa/examples/basic/conways_game_of_life/app.py +51 -0
- mesa/examples/basic/conways_game_of_life/model.py +31 -0
- mesa/examples/basic/conways_game_of_life/st_app.py +72 -0
- mesa/examples/basic/schelling/Readme.md +40 -0
- mesa/examples/basic/schelling/__init__.py +0 -0
- mesa/examples/basic/schelling/agents.py +26 -0
- mesa/examples/basic/schelling/analysis.ipynb +205 -0
- mesa/examples/basic/schelling/app.py +42 -0
- mesa/examples/basic/schelling/model.py +59 -0
- mesa/examples/basic/virus_on_network/Readme.md +61 -0
- mesa/examples/basic/virus_on_network/__init__.py +0 -0
- mesa/examples/basic/virus_on_network/agents.py +69 -0
- mesa/examples/basic/virus_on_network/app.py +114 -0
- mesa/examples/basic/virus_on_network/model.py +96 -0
- mesa/experimental/UserParam.py +18 -7
- mesa/experimental/__init__.py +10 -2
- mesa/experimental/cell_space/__init__.py +16 -1
- mesa/experimental/cell_space/cell.py +93 -23
- mesa/experimental/cell_space/cell_agent.py +117 -21
- mesa/experimental/cell_space/cell_collection.py +56 -19
- mesa/experimental/cell_space/discrete_space.py +92 -8
- mesa/experimental/cell_space/grid.py +33 -9
- mesa/experimental/cell_space/network.py +15 -10
- mesa/experimental/cell_space/voronoi.py +257 -0
- mesa/experimental/components/altair.py +11 -2
- mesa/experimental/components/matplotlib.py +132 -26
- mesa/experimental/devs/__init__.py +2 -0
- mesa/experimental/devs/eventlist.py +54 -15
- mesa/experimental/devs/examples/epstein_civil_violence.py +69 -38
- mesa/experimental/devs/examples/wolf_sheep.py +42 -43
- mesa/experimental/devs/simulator.py +57 -16
- mesa/experimental/{jupyter_viz.py → solara_viz.py} +151 -99
- mesa/model.py +136 -78
- mesa/space.py +208 -148
- mesa/time.py +63 -80
- mesa/visualization/__init__.py +25 -6
- mesa/visualization/components/__init__.py +83 -0
- mesa/visualization/components/altair_components.py +188 -0
- mesa/visualization/components/matplotlib_components.py +175 -0
- mesa/visualization/mpl_space_drawing.py +593 -0
- mesa/visualization/solara_viz.py +458 -0
- mesa/visualization/user_param.py +69 -0
- mesa/visualization/utils.py +9 -0
- {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/METADATA +62 -17
- mesa-3.0.0.dist-info/RECORD +95 -0
- mesa-3.0.0.dist-info/licenses/LICENSE +202 -0
- mesa-2.4.0.dist-info/licenses/LICENSE → mesa-3.0.0.dist-info/licenses/NOTICE +2 -2
- mesa/cookiecutter-mesa/cookiecutter.json +0 -8
- mesa/cookiecutter-mesa/hooks/post_gen_project.py +0 -11
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +0 -4
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate +0 -3
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +0 -11
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +0 -60
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate +0 -36
- mesa/flat/__init__.py +0 -6
- mesa/flat/visualization.py +0 -5
- mesa/main.py +0 -63
- mesa/visualization/ModularVisualization.py +0 -1
- mesa/visualization/TextVisualization.py +0 -1
- mesa/visualization/UserParam.py +0 -1
- mesa/visualization/modules.py +0 -1
- mesa-2.4.0.dist-info/RECORD +0 -45
- /mesa/{cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}} → examples/advanced}/__init__.py +0 -0
- {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/WHEEL +0 -0
- {mesa-2.4.0.dist-info → mesa-3.0.0.dist-info}/entry_points.txt +0 -0
mesa/datacollection.py
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Mesa Data Collection Module
|
|
3
|
-
===========================
|
|
1
|
+
"""Mesa Data Collection Module.
|
|
4
2
|
|
|
5
3
|
DataCollector is meant to provide a simple, standard way to collect data
|
|
6
4
|
generated by a Mesa model. It collects four types of data: model-level data,
|
|
@@ -20,6 +18,7 @@ agent-type-level functions are called on each agent of the specified type.
|
|
|
20
18
|
Additionally, other objects can write directly to tables by passing in an
|
|
21
19
|
appropriate dictionary object for a table row.
|
|
22
20
|
|
|
21
|
+
The DataCollector then stores the data it collects in dictionaries:
|
|
23
22
|
* model_vars maps each reporter to a list of its values
|
|
24
23
|
* tables maps each table to a dictionary, with each column as a key with a
|
|
25
24
|
list as its value.
|
|
@@ -29,10 +28,6 @@ appropriate dictionary object for a table row.
|
|
|
29
28
|
each containing a list of each agent's id and its values.
|
|
30
29
|
|
|
31
30
|
Finally, DataCollector can create a pandas DataFrame from each collection.
|
|
32
|
-
|
|
33
|
-
The default DataCollector here makes several assumptions:
|
|
34
|
-
* The model has an agent list called agents
|
|
35
|
-
* For collecting agent-level variables, agents must have a unique_id
|
|
36
31
|
"""
|
|
37
32
|
|
|
38
33
|
import contextlib
|
|
@@ -229,7 +224,7 @@ class DataCollector:
|
|
|
229
224
|
rep_funcs = self.agent_reporters.values()
|
|
230
225
|
|
|
231
226
|
def get_reports(agent):
|
|
232
|
-
_prefix = (agent.model.
|
|
227
|
+
_prefix = (agent.model.steps, agent.unique_id)
|
|
233
228
|
reports = tuple(rep(agent) for rep in rep_funcs)
|
|
234
229
|
return _prefix + reports
|
|
235
230
|
|
|
@@ -246,7 +241,7 @@ class DataCollector:
|
|
|
246
241
|
rep_funcs = self.agenttype_reporters[agent_type].values()
|
|
247
242
|
|
|
248
243
|
def get_reports(agent):
|
|
249
|
-
_prefix = (agent.model.
|
|
244
|
+
_prefix = (agent.model.steps, agent.unique_id)
|
|
250
245
|
reports = tuple(rep(agent) for rep in rep_funcs)
|
|
251
246
|
return _prefix + reports
|
|
252
247
|
|
|
@@ -274,7 +269,7 @@ class DataCollector:
|
|
|
274
269
|
if self.model_reporters:
|
|
275
270
|
for var, reporter in self.model_reporters.items():
|
|
276
271
|
# Check if lambda or partial function
|
|
277
|
-
if isinstance(reporter,
|
|
272
|
+
if isinstance(reporter, types.LambdaType | partial):
|
|
278
273
|
# Use deepcopy to store a copy of the data,
|
|
279
274
|
# preventing references from being updated across steps.
|
|
280
275
|
self.model_vars[var].append(deepcopy(reporter(model)))
|
|
@@ -287,19 +282,18 @@ class DataCollector:
|
|
|
287
282
|
elif isinstance(reporter, list):
|
|
288
283
|
self.model_vars[var].append(deepcopy(reporter[0](*reporter[1])))
|
|
289
284
|
# Assume it's a callable otherwise (e.g., method)
|
|
290
|
-
# TODO: Check if method of a class explicitly
|
|
291
285
|
else:
|
|
292
286
|
self.model_vars[var].append(deepcopy(reporter()))
|
|
293
287
|
|
|
294
288
|
if self.agent_reporters:
|
|
295
289
|
agent_records = self._record_agents(model)
|
|
296
|
-
self._agent_records[model.
|
|
290
|
+
self._agent_records[model.steps] = list(agent_records)
|
|
297
291
|
|
|
298
292
|
if self.agenttype_reporters:
|
|
299
|
-
self._agenttype_records[model.
|
|
293
|
+
self._agenttype_records[model.steps] = {}
|
|
300
294
|
for agent_type in self.agenttype_reporters:
|
|
301
295
|
agenttype_records = self._record_agenttype(model, agent_type)
|
|
302
|
-
self._agenttype_records[model.
|
|
296
|
+
self._agenttype_records[model.steps][agent_type] = list(
|
|
303
297
|
agenttype_records
|
|
304
298
|
)
|
|
305
299
|
|
|
@@ -361,8 +355,10 @@ class DataCollector:
|
|
|
361
355
|
|
|
362
356
|
def get_agenttype_vars_dataframe(self, agent_type):
|
|
363
357
|
"""Create a pandas DataFrame from the agent-type variables for a specific agent type.
|
|
358
|
+
|
|
364
359
|
The DataFrame has one column for each variable, with two additional
|
|
365
360
|
columns for tick and agent_id.
|
|
361
|
+
|
|
366
362
|
Args:
|
|
367
363
|
agent_type: The type of agent to get the data for.
|
|
368
364
|
"""
|
mesa/examples/README.md
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Mesa core examples
|
|
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
|
+
|
|
4
|
+
More user examples and showcases can be found in the [mesa-examples](https://github.com/projectmesa/mesa-examples) repository.
|
|
5
|
+
|
|
6
|
+
## Basic Examples
|
|
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
|
+
|
|
9
|
+
### [Boltzmann Wealth Model](examples/basic/boltzmann_wealth_model)
|
|
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
|
+
|
|
12
|
+
### [Boids Flockers Model](examples/basic/boid_flockers)
|
|
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
|
+
|
|
15
|
+
### [Conway's Game of Life](examples/basic/conways_game_of_life)
|
|
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
|
+
|
|
18
|
+
### [Schelling Segregation Model](examples/basic/schelling)
|
|
19
|
+
Mesa implementation of the classic [Schelling segregation](http://nifty.stanford.edu/2014/mccown-schelling-model-segregation/) model.
|
|
20
|
+
|
|
21
|
+
### [Virus on a Network Model](examples/basic/virus_on_network)
|
|
22
|
+
This model is based on the NetLogo [Virus on a Network](https://ccl.northwestern.edu/netlogo/models/VirusonaNetwork) model.
|
|
23
|
+
|
|
24
|
+
## Advanced Examples
|
|
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
|
+
|
|
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
|
+
|
|
30
|
+
### [Demographic Prisoner's Dilemma on a Grid](examples/advanced/pd_grid)
|
|
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
|
+
|
|
33
|
+
### [Sugarscape Model with Traders](examples/advanced/sugarscape_g1mt)
|
|
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
|
+
|
|
36
|
+
### [Wolf-Sheep Predation Model](examples/advanced/wolf_sheep)
|
|
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.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from mesa.examples.advanced.epstein_civil_violence.model import EpsteinCivilViolence
|
|
2
|
+
from mesa.examples.advanced.pd_grid.model import PdGrid
|
|
3
|
+
from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
|
|
4
|
+
from mesa.examples.advanced.wolf_sheep.model import WolfSheep
|
|
5
|
+
from mesa.examples.basic.boid_flockers.model import BoidFlockers
|
|
6
|
+
from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel
|
|
7
|
+
from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
|
|
8
|
+
from mesa.examples.basic.schelling.model import Schelling
|
|
9
|
+
from mesa.examples.basic.virus_on_network.model import VirusOnNetwork
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"BoidFlockers",
|
|
13
|
+
"BoltzmannWealthModel",
|
|
14
|
+
"ConwaysGameOfLife",
|
|
15
|
+
"Schelling",
|
|
16
|
+
"VirusOnNetwork",
|
|
17
|
+
"EpsteinCivilViolence",
|
|
18
|
+
"PdGrid",
|
|
19
|
+
"SugarscapeG1mt",
|
|
20
|
+
"WolfSheep",
|
|
21
|
+
]
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"This example implements the first model from \"Modeling civil violence: An agent-based computational approach,\" by Joshua Epstein. The paper (pdf) can be found [here](http://www.uvm.edu/~pdodds/files/papers/others/2002/epstein2002a.pdf).\n",
|
|
8
|
+
"\n",
|
|
9
|
+
"The model consists of two types of agents: \"Citizens\" (called \"Agents\" in the paper) and \"Cops.\" Agents decide whether or not to rebel by weighing their unhappiness ('grievance') against the risk of rebelling, which they estimate by comparing the local ratio of rebels to cops. \n",
|
|
10
|
+
"\n",
|
|
11
|
+
"\n"
|
|
12
|
+
]
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"cell_type": "code",
|
|
16
|
+
"execution_count": 5,
|
|
17
|
+
"metadata": {},
|
|
18
|
+
"outputs": [],
|
|
19
|
+
"source": [
|
|
20
|
+
"%matplotlib inline\n",
|
|
21
|
+
"\n",
|
|
22
|
+
"from epstein_civil_violence.model import EpsteinCivilViolence"
|
|
23
|
+
]
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
"cell_type": "code",
|
|
27
|
+
"execution_count": 6,
|
|
28
|
+
"metadata": {},
|
|
29
|
+
"outputs": [],
|
|
30
|
+
"source": [
|
|
31
|
+
"model = EpsteinCivilViolence(\n",
|
|
32
|
+
" height=40,\n",
|
|
33
|
+
" width=40,\n",
|
|
34
|
+
" citizen_density=0.7,\n",
|
|
35
|
+
" cop_density=0.074,\n",
|
|
36
|
+
" citizen_vision=7,\n",
|
|
37
|
+
" cop_vision=7,\n",
|
|
38
|
+
" legitimacy=0.8,\n",
|
|
39
|
+
" max_jail_term=1000,\n",
|
|
40
|
+
" max_iters=1000,\n",
|
|
41
|
+
") # cap the number of steps the model takes\n",
|
|
42
|
+
"model.run_model()"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
"cell_type": "markdown",
|
|
47
|
+
"metadata": {},
|
|
48
|
+
"source": [
|
|
49
|
+
"The model's data collector counts the number of citizens who are Active (in rebellion), Jailed, or Quiescent after each step."
|
|
50
|
+
]
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"cell_type": "code",
|
|
54
|
+
"execution_count": 7,
|
|
55
|
+
"metadata": {},
|
|
56
|
+
"outputs": [],
|
|
57
|
+
"source": [
|
|
58
|
+
"model_out = model.datacollector.get_model_vars_dataframe()"
|
|
59
|
+
]
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"cell_type": "code",
|
|
63
|
+
"execution_count": 8,
|
|
64
|
+
"metadata": {},
|
|
65
|
+
"outputs": [
|
|
66
|
+
{
|
|
67
|
+
"data": {
|
|
68
|
+
"image/png": "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\n",
|
|
69
|
+
"text/plain": [
|
|
70
|
+
"<Figure size 432x288 with 1 Axes>"
|
|
71
|
+
]
|
|
72
|
+
},
|
|
73
|
+
"metadata": {
|
|
74
|
+
"needs_background": "light"
|
|
75
|
+
},
|
|
76
|
+
"output_type": "display_data"
|
|
77
|
+
}
|
|
78
|
+
],
|
|
79
|
+
"source": [
|
|
80
|
+
"ax = model_out.plot()\n",
|
|
81
|
+
"ax.set_title(\"Citizen Condition Over Time\")\n",
|
|
82
|
+
"ax.set_xlabel(\"Step\")\n",
|
|
83
|
+
"ax.set_ylabel(\"Number of Citizens\")\n",
|
|
84
|
+
"_ = ax.legend(bbox_to_anchor=(1.35, 1.025))"
|
|
85
|
+
]
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
"cell_type": "code",
|
|
89
|
+
"execution_count": null,
|
|
90
|
+
"metadata": {},
|
|
91
|
+
"outputs": [],
|
|
92
|
+
"source": []
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"metadata": {
|
|
96
|
+
"kernelspec": {
|
|
97
|
+
"display_name": "Python 3",
|
|
98
|
+
"language": "python",
|
|
99
|
+
"name": "python3"
|
|
100
|
+
},
|
|
101
|
+
"language_info": {
|
|
102
|
+
"codemirror_mode": {
|
|
103
|
+
"name": "ipython",
|
|
104
|
+
"version": 3
|
|
105
|
+
},
|
|
106
|
+
"file_extension": ".py",
|
|
107
|
+
"mimetype": "text/x-python",
|
|
108
|
+
"name": "python",
|
|
109
|
+
"nbconvert_exporter": "python",
|
|
110
|
+
"pygments_lexer": "ipython3",
|
|
111
|
+
"version": "3.7.3"
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
"nbformat": 4,
|
|
115
|
+
"nbformat_minor": 1
|
|
116
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Epstein Civil Violence Model
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
This model is based on Joshua Epstein's simulation of how civil unrest grows and is suppressed. Citizen agents wander the grid randomly, and are endowed with individual risk aversion and hardship levels; there is also a universal regime legitimacy value. There are also Cop agents, who work on behalf of the regime. Cops arrest Citizens who are actively rebelling; Citizens decide whether to rebel based on their hardship and the regime legitimacy, and their perceived probability of arrest.
|
|
6
|
+
|
|
7
|
+
The model generates mass uprising as self-reinforcing processes: if enough agents are rebelling, the probability of any individual agent being arrested is reduced, making more agents more likely to join the uprising. However, the more rebelling Citizens the Cops arrest, the less likely additional agents become to join.
|
|
8
|
+
|
|
9
|
+
## How to Run
|
|
10
|
+
|
|
11
|
+
To run the model interactively, run ``EpsteinCivilViolenceServer.py`` in this directory. e.g.
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
$ python EpsteinCivilViolenceServer.py
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
|
|
18
|
+
|
|
19
|
+
## Files
|
|
20
|
+
|
|
21
|
+
* ``model.py``: Core model code.
|
|
22
|
+
* ``agent.py``: Agent classes.
|
|
23
|
+
* ``app.py``: Sets up the interactive visualization.
|
|
24
|
+
* ``Epstein Civil Violence.ipynb``: Jupyter notebook conducting some preliminary analysis of the model.
|
|
25
|
+
|
|
26
|
+
## Further Reading
|
|
27
|
+
|
|
28
|
+
This model is based adapted from:
|
|
29
|
+
|
|
30
|
+
[Epstein, J. “Modeling civil violence: An agent-based computational approach”, Proceedings of the National Academy of Sciences, Vol. 99, Suppl. 3, May 14, 2002](http://www.pnas.org/content/99/suppl.3/7243.short)
|
|
31
|
+
|
|
32
|
+
A similar model is also included with NetLogo:
|
|
33
|
+
|
|
34
|
+
Wilensky, U. (2004). NetLogo Rebellion model. http://ccl.northwestern.edu/netlogo/models/Rebellion. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
|
|
File without changes
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import math
|
|
2
|
+
from enum import Enum
|
|
3
|
+
|
|
4
|
+
import mesa
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class CitizenState(Enum):
|
|
8
|
+
ACTIVE = 1
|
|
9
|
+
QUIET = 2
|
|
10
|
+
ARRESTED = 3
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class EpsteinAgent(mesa.experimental.cell_space.CellAgent):
|
|
14
|
+
def update_neighbors(self):
|
|
15
|
+
"""
|
|
16
|
+
Look around and see who my neighbors are
|
|
17
|
+
"""
|
|
18
|
+
self.neighborhood = self.cell.get_neighborhood(radius=self.vision)
|
|
19
|
+
self.neighbors = self.neighborhood.agents
|
|
20
|
+
self.empty_neighbors = [c for c in self.neighborhood if c.is_empty]
|
|
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
|
+
|
|
27
|
+
|
|
28
|
+
class Citizen(EpsteinAgent):
|
|
29
|
+
"""
|
|
30
|
+
A member of the general population, may or may not be in active rebellion.
|
|
31
|
+
Summary of rule: If grievance - risk > threshold, rebel.
|
|
32
|
+
|
|
33
|
+
Attributes:
|
|
34
|
+
hardship: Agent's 'perceived hardship (i.e., physical or economic
|
|
35
|
+
privation).' Exogenous, drawn from U(0,1).
|
|
36
|
+
regime_legitimacy: Agent's perception of regime legitimacy, equal
|
|
37
|
+
across agents. Exogenous.
|
|
38
|
+
risk_aversion: Exogenous, drawn from U(0,1).
|
|
39
|
+
threshold: if (grievance - (risk_aversion * arrest_probability)) >
|
|
40
|
+
threshold, go/remain Active
|
|
41
|
+
vision: number of cells in each direction (N, S, E and W) that agent
|
|
42
|
+
can inspect
|
|
43
|
+
condition: Can be "Quiescent" or "Active;" deterministic function of
|
|
44
|
+
greivance, perceived risk, and
|
|
45
|
+
grievance: deterministic function of hardship and regime_legitimacy;
|
|
46
|
+
how aggrieved is agent at the regime?
|
|
47
|
+
arrest_probability: agent's assessment of arrest probability, given
|
|
48
|
+
rebellion
|
|
49
|
+
"""
|
|
50
|
+
|
|
51
|
+
def __init__(
|
|
52
|
+
self, model, regime_legitimacy, threshold, vision, arrest_prob_constant
|
|
53
|
+
):
|
|
54
|
+
"""
|
|
55
|
+
Create a new Citizen.
|
|
56
|
+
Args:
|
|
57
|
+
model: the model to which the agent belongs
|
|
58
|
+
hardship: Agent's 'perceived hardship (i.e., physical or economic
|
|
59
|
+
privation).' Exogenous, drawn from U(0,1).
|
|
60
|
+
regime_legitimacy: Agent's perception of regime legitimacy, equal
|
|
61
|
+
across agents. Exogenous.
|
|
62
|
+
risk_aversion: Exogenous, drawn from U(0,1).
|
|
63
|
+
threshold: if (grievance - (risk_aversion * arrest_probability)) >
|
|
64
|
+
threshold, go/remain Active
|
|
65
|
+
vision: number of cells in each direction (N, S, E and W) that
|
|
66
|
+
agent can inspect. Exogenous.
|
|
67
|
+
model: model instance
|
|
68
|
+
"""
|
|
69
|
+
super().__init__(model)
|
|
70
|
+
self.hardship = self.random.random()
|
|
71
|
+
self.risk_aversion = self.random.random()
|
|
72
|
+
self.regime_legitimacy = regime_legitimacy
|
|
73
|
+
self.threshold = threshold
|
|
74
|
+
self.state = CitizenState.QUIET
|
|
75
|
+
self.vision = vision
|
|
76
|
+
self.jail_sentence = 0
|
|
77
|
+
self.grievance = self.hardship * (1 - self.regime_legitimacy)
|
|
78
|
+
self.arrest_prob_constant = arrest_prob_constant
|
|
79
|
+
self.arrest_probability = None
|
|
80
|
+
|
|
81
|
+
self.neighborhood = []
|
|
82
|
+
self.neighbors = []
|
|
83
|
+
self.empty_neighbors = []
|
|
84
|
+
|
|
85
|
+
def step(self):
|
|
86
|
+
"""
|
|
87
|
+
Decide whether to activate, then move if applicable.
|
|
88
|
+
"""
|
|
89
|
+
if self.jail_sentence:
|
|
90
|
+
self.jail_sentence -= 1
|
|
91
|
+
return # no other changes or movements if agent is in jail.
|
|
92
|
+
self.update_neighbors()
|
|
93
|
+
self.update_estimated_arrest_probability()
|
|
94
|
+
|
|
95
|
+
net_risk = self.risk_aversion * self.arrest_probability
|
|
96
|
+
if (self.grievance - net_risk) > self.threshold:
|
|
97
|
+
self.state = CitizenState.ACTIVE
|
|
98
|
+
else:
|
|
99
|
+
self.state = CitizenState.QUIET
|
|
100
|
+
|
|
101
|
+
self.move()
|
|
102
|
+
|
|
103
|
+
def update_estimated_arrest_probability(self):
|
|
104
|
+
"""
|
|
105
|
+
Based on the ratio of cops to actives in my neighborhood, estimate the
|
|
106
|
+
p(Arrest | I go active).
|
|
107
|
+
"""
|
|
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:
|
|
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.
|
|
119
|
+
self.arrest_probability = 1 - math.exp(
|
|
120
|
+
-1 * self.arrest_prob_constant * round(cops_in_vision / actives_in_vision)
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class Cop(EpsteinAgent):
|
|
125
|
+
"""
|
|
126
|
+
A cop for life. No defection.
|
|
127
|
+
Summary of rule: Inspect local vision and arrest a random active agent.
|
|
128
|
+
|
|
129
|
+
Attributes:
|
|
130
|
+
unique_id: unique int
|
|
131
|
+
x, y: Grid coordinates
|
|
132
|
+
vision: number of cells in each direction (N, S, E and W) that cop is
|
|
133
|
+
able to inspect
|
|
134
|
+
"""
|
|
135
|
+
|
|
136
|
+
def __init__(self, model, vision, max_jail_term):
|
|
137
|
+
"""
|
|
138
|
+
Create a new Cop.
|
|
139
|
+
Args:
|
|
140
|
+
x, y: Grid coordinates
|
|
141
|
+
vision: number of cells in each direction (N, S, E and W) that
|
|
142
|
+
agent can inspect. Exogenous.
|
|
143
|
+
model: model instance
|
|
144
|
+
"""
|
|
145
|
+
super().__init__(model)
|
|
146
|
+
self.vision = vision
|
|
147
|
+
self.max_jail_term = max_jail_term
|
|
148
|
+
|
|
149
|
+
def step(self):
|
|
150
|
+
"""
|
|
151
|
+
Inspect local vision and arrest a random active agent. Move if
|
|
152
|
+
applicable.
|
|
153
|
+
"""
|
|
154
|
+
self.update_neighbors()
|
|
155
|
+
active_neighbors = []
|
|
156
|
+
for agent in self.neighbors:
|
|
157
|
+
if isinstance(agent, Citizen) and agent.state == CitizenState.ACTIVE:
|
|
158
|
+
active_neighbors.append(agent)
|
|
159
|
+
if active_neighbors:
|
|
160
|
+
arrestee = self.random.choice(active_neighbors)
|
|
161
|
+
arrestee.jail_sentence = self.random.randint(0, self.max_jail_term)
|
|
162
|
+
arrestee.state = CitizenState.ARRESTED
|
|
163
|
+
|
|
164
|
+
self.move()
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from mesa.examples.advanced.epstein_civil_violence.agents import (
|
|
2
|
+
Citizen,
|
|
3
|
+
CitizenState,
|
|
4
|
+
Cop,
|
|
5
|
+
)
|
|
6
|
+
from mesa.examples.advanced.epstein_civil_violence.model import EpsteinCivilViolence
|
|
7
|
+
from mesa.visualization import (
|
|
8
|
+
Slider,
|
|
9
|
+
SolaraViz,
|
|
10
|
+
make_plot_component,
|
|
11
|
+
make_space_component,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
COP_COLOR = "#000000"
|
|
15
|
+
|
|
16
|
+
agent_colors = {
|
|
17
|
+
CitizenState.ACTIVE: "#FE6100",
|
|
18
|
+
CitizenState.QUIET: "#648FFF",
|
|
19
|
+
CitizenState.ARRESTED: "#808080",
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def citizen_cop_portrayal(agent):
|
|
24
|
+
if agent is None:
|
|
25
|
+
return
|
|
26
|
+
|
|
27
|
+
portrayal = {
|
|
28
|
+
"size": 50,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if isinstance(agent, Citizen):
|
|
32
|
+
portrayal["color"] = agent_colors[agent.state]
|
|
33
|
+
elif isinstance(agent, Cop):
|
|
34
|
+
portrayal["color"] = COP_COLOR
|
|
35
|
+
|
|
36
|
+
return portrayal
|
|
37
|
+
|
|
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
|
+
|
|
46
|
+
model_params = {
|
|
47
|
+
"height": 40,
|
|
48
|
+
"width": 40,
|
|
49
|
+
"citizen_density": Slider("Initial Agent Density", 0.7, 0.0, 0.9, 0.1),
|
|
50
|
+
"cop_density": Slider("Initial Cop Density", 0.04, 0.0, 0.1, 0.01),
|
|
51
|
+
"citizen_vision": Slider("Citizen Vision", 7, 1, 10, 1),
|
|
52
|
+
"cop_vision": Slider("Cop Vision", 7, 1, 10, 1),
|
|
53
|
+
"legitimacy": Slider("Government Legitimacy", 0.82, 0.0, 1, 0.01),
|
|
54
|
+
"max_jail_term": Slider("Max Jail Term", 30, 0, 50, 1),
|
|
55
|
+
}
|
|
56
|
+
|
|
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
|
+
)
|
|
64
|
+
|
|
65
|
+
epstein_model = EpsteinCivilViolence()
|
|
66
|
+
|
|
67
|
+
page = SolaraViz(
|
|
68
|
+
epstein_model,
|
|
69
|
+
components=[space_component, chart_component],
|
|
70
|
+
model_params=model_params,
|
|
71
|
+
name="Epstein Civil Violence",
|
|
72
|
+
)
|
|
73
|
+
page # noqa
|