Mesa 3.0.0b1__py3-none-any.whl → 3.0.0rc0__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 -3
- mesa/agent.py +23 -8
- mesa/batchrunner.py +26 -1
- {examples → mesa/examples}/README.md +11 -11
- mesa/examples/__init__.py +21 -0
- {examples → mesa/examples}/advanced/epstein_civil_violence/Readme.md +3 -2
- examples/advanced/epstein_civil_violence/epstein_civil_violence/agent.py → mesa/examples/advanced/epstein_civil_violence/agents.py +44 -38
- mesa/examples/advanced/epstein_civil_violence/app.py +73 -0
- mesa/examples/advanced/epstein_civil_violence/model.py +114 -0
- examples/advanced/pd_grid/readme.md → mesa/examples/advanced/pd_grid/Readme.md +4 -3
- mesa/examples/advanced/pd_grid/app.py +54 -0
- {examples/advanced/pd_grid → mesa/examples/advanced}/pd_grid/model.py +1 -2
- {examples → mesa/examples}/advanced/sugarscape_g1mt/Readme.md +6 -29
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/trader_agents.py → mesa/examples/advanced/sugarscape_g1mt/agents.py +26 -3
- {examples → mesa/examples}/advanced/sugarscape_g1mt/app.py +19 -18
- {examples/advanced/sugarscape_g1mt → mesa/examples/advanced}/sugarscape_g1mt/model.py +6 -6
- {examples → mesa/examples}/advanced/sugarscape_g1mt/tests.py +3 -6
- mesa/examples/advanced/wolf_sheep/app.py +84 -0
- {examples/advanced/wolf_sheep → mesa/examples/advanced}/wolf_sheep/model.py +9 -8
- mesa/examples/basic/boid_flockers/Readme.md +22 -0
- {examples → mesa/examples}/basic/boid_flockers/app.py +3 -4
- {examples → mesa/examples}/basic/boid_flockers/model.py +1 -2
- {examples → mesa/examples}/basic/boltzmann_wealth_model/Readme.md +1 -5
- mesa/examples/basic/boltzmann_wealth_model/__init__.py +0 -0
- {examples → mesa/examples}/basic/boltzmann_wealth_model/app.py +15 -12
- {examples → mesa/examples}/basic/boltzmann_wealth_model/model.py +3 -4
- {examples → mesa/examples}/basic/conways_game_of_life/Readme.md +11 -7
- mesa/examples/basic/conways_game_of_life/__init__.py +0 -0
- {examples → mesa/examples}/basic/conways_game_of_life/agents.py +8 -8
- mesa/examples/basic/conways_game_of_life/app.py +51 -0
- {examples → mesa/examples}/basic/conways_game_of_life/model.py +3 -4
- {examples → mesa/examples}/basic/conways_game_of_life/st_app.py +2 -1
- examples/basic/schelling/README.md → mesa/examples/basic/schelling/Readme.md +2 -9
- mesa/examples/basic/schelling/__init__.py +0 -0
- {examples → mesa/examples}/basic/schelling/app.py +6 -7
- {examples → mesa/examples}/basic/schelling/model.py +1 -2
- mesa/examples/basic/virus_on_network/__init__.py +0 -0
- mesa/examples/basic/virus_on_network/app.py +114 -0
- {examples → mesa/examples}/basic/virus_on_network/model.py +4 -7
- mesa/experimental/cell_space/discrete_space.py +6 -0
- mesa/experimental/devs/eventlist.py +6 -0
- mesa/model.py +13 -0
- mesa/space.py +70 -35
- mesa/visualization/__init__.py +16 -5
- mesa/visualization/components/__init__.py +83 -0
- mesa/visualization/components/altair_components.py +188 -0
- mesa/visualization/components/matplotlib_components.py +176 -0
- mesa/visualization/mpl_space_drawing.py +558 -0
- mesa/visualization/solara_viz.py +30 -20
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/METADATA +1 -3
- mesa-3.0.0rc0.dist-info/RECORD +95 -0
- examples/advanced/epstein_civil_violence/epstein_civil_violence/model.py +0 -146
- examples/advanced/epstein_civil_violence/epstein_civil_violence/portrayal.py +0 -33
- examples/advanced/epstein_civil_violence/epstein_civil_violence/server.py +0 -81
- examples/advanced/epstein_civil_violence/requirements.txt +0 -3
- examples/advanced/epstein_civil_violence/run.py +0 -3
- examples/advanced/pd_grid/pd_grid/portrayal.py +0 -19
- examples/advanced/pd_grid/pd_grid/server.py +0 -21
- examples/advanced/pd_grid/requirements.txt +0 -3
- examples/advanced/pd_grid/run.py +0 -3
- examples/advanced/sugarscape_g1mt/requirements.txt +0 -6
- examples/advanced/sugarscape_g1mt/run.py +0 -105
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/resource_agents.py +0 -26
- examples/advanced/sugarscape_g1mt/sugarscape_g1mt/server.py +0 -61
- examples/advanced/wolf_sheep/requirements.txt +0 -1
- examples/advanced/wolf_sheep/run.py +0 -3
- examples/advanced/wolf_sheep/wolf_sheep/resources/sheep.png +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/resources/wolf.png +0 -0
- examples/advanced/wolf_sheep/wolf_sheep/server.py +0 -78
- examples/basic/__init__.py +0 -13
- examples/basic/boid_flockers/Readme.md +0 -43
- examples/basic/conways_game_of_life/portrayal.py +0 -18
- examples/basic/conways_game_of_life/requirements.txt +0 -1
- examples/basic/conways_game_of_life/server.py +0 -11
- examples/basic/virus_on_network/app.py +0 -133
- mesa/cookiecutter-mesa/cookiecutter.json +0 -8
- mesa/cookiecutter-mesa/hooks/post_gen_project.py +0 -13
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +0 -4
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/app.pytemplate +0 -27
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +0 -11
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/__init__.py +0 -1
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +0 -60
- mesa/examples.py +0 -3
- mesa/main.py +0 -65
- mesa/visualization/components/altair.py +0 -88
- mesa/visualization/components/matplotlib.py +0 -342
- mesa-3.0.0b1.dist-info/RECORD +0 -114
- {examples → mesa/examples/advanced}/__init__.py +0 -0
- {examples → mesa/examples}/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +0 -0
- {examples/advanced → mesa/examples/advanced/epstein_civil_violence}/__init__.py +0 -0
- {examples/advanced/epstein_civil_violence/epstein_civil_violence → mesa/examples/advanced/pd_grid}/__init__.py +0 -0
- /examples/advanced/pd_grid/pd_grid/agent.py → /mesa/examples/advanced/pd_grid/agents.py +0 -0
- {examples → mesa/examples}/advanced/pd_grid/analysis.ipynb +0 -0
- {examples/advanced/pd_grid/pd_grid → mesa/examples/advanced/sugarscape_g1mt}/__init__.py +0 -0
- {examples/advanced/sugarscape_g1mt → mesa/examples/advanced}/sugarscape_g1mt/sugar-map.txt +0 -0
- {examples → mesa/examples}/advanced/wolf_sheep/Readme.md +0 -0
- {examples/advanced/sugarscape_g1mt/sugarscape_g1mt → mesa/examples/advanced/wolf_sheep}/__init__.py +0 -0
- {examples/advanced/wolf_sheep → mesa/examples/advanced}/wolf_sheep/agents.py +0 -0
- {examples/advanced/wolf_sheep → mesa/examples/basic}/__init__.py +0 -0
- {examples/advanced/wolf_sheep/wolf_sheep → mesa/examples/basic/boid_flockers}/__init__.py +0 -0
- {examples → mesa/examples}/basic/boid_flockers/agents.py +0 -0
- {examples → mesa/examples}/basic/boltzmann_wealth_model/agents.py +0 -0
- {examples → mesa/examples}/basic/boltzmann_wealth_model/st_app.py +0 -0
- {examples → mesa/examples}/basic/schelling/agents.py +0 -0
- {examples → mesa/examples}/basic/schelling/analysis.ipynb +0 -0
- /examples/basic/virus_on_network/README.md → /mesa/examples/basic/virus_on_network/Readme.md +0 -0
- {examples → mesa/examples}/basic/virus_on_network/agents.py +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/WHEEL +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/entry_points.txt +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/licenses/LICENSE +0 -0
- {mesa-3.0.0b1.dist-info → mesa-3.0.0rc0.dist-info}/licenses/NOTICE +0 -0
mesa/__init__.py
CHANGED
|
@@ -5,7 +5,6 @@ Core Objects: Model, and Agent.
|
|
|
5
5
|
|
|
6
6
|
import datetime
|
|
7
7
|
|
|
8
|
-
import mesa.examples as examples
|
|
9
8
|
import mesa.experimental as experimental
|
|
10
9
|
import mesa.space as space
|
|
11
10
|
import mesa.time as time
|
|
@@ -22,11 +21,10 @@ __all__ = [
|
|
|
22
21
|
"DataCollector",
|
|
23
22
|
"batch_run",
|
|
24
23
|
"experimental",
|
|
25
|
-
"examples",
|
|
26
24
|
]
|
|
27
25
|
|
|
28
26
|
__title__ = "mesa"
|
|
29
|
-
__version__ = "3.0.
|
|
27
|
+
__version__ = "3.0.0rc0"
|
|
30
28
|
__license__ = "Apache 2.0"
|
|
31
29
|
_this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year
|
|
32
30
|
__copyright__ = f"Copyright {_this_year} Project Mesa Team"
|
mesa/agent.py
CHANGED
|
@@ -21,6 +21,8 @@ from random import Random
|
|
|
21
21
|
# mypy
|
|
22
22
|
from typing import TYPE_CHECKING, Any, Literal, overload
|
|
23
23
|
|
|
24
|
+
import numpy as np
|
|
25
|
+
|
|
24
26
|
if TYPE_CHECKING:
|
|
25
27
|
# We ensure that these are not imported during runtime to prevent cyclic
|
|
26
28
|
# dependency.
|
|
@@ -67,7 +69,13 @@ class Agent:
|
|
|
67
69
|
self.model.register_agent(self)
|
|
68
70
|
|
|
69
71
|
def remove(self) -> None:
|
|
70
|
-
"""Remove and delete the agent from the model.
|
|
72
|
+
"""Remove and delete the agent from the model.
|
|
73
|
+
|
|
74
|
+
Notes:
|
|
75
|
+
If you need to do additional cleanup when removing an agent by for example removing
|
|
76
|
+
it from a space, consider extending this method in your own agent class.
|
|
77
|
+
|
|
78
|
+
"""
|
|
71
79
|
with contextlib.suppress(KeyError):
|
|
72
80
|
self.model.deregister_agent(self)
|
|
73
81
|
|
|
@@ -79,9 +87,14 @@ class Agent:
|
|
|
79
87
|
|
|
80
88
|
@property
|
|
81
89
|
def random(self) -> Random:
|
|
82
|
-
"""Return a seeded rng."""
|
|
90
|
+
"""Return a seeded stdlib rng."""
|
|
83
91
|
return self.model.random
|
|
84
92
|
|
|
93
|
+
@property
|
|
94
|
+
def rng(self) -> np.random.Generator:
|
|
95
|
+
"""Return a seeded np.random rng."""
|
|
96
|
+
return self.model.rng
|
|
97
|
+
|
|
85
98
|
|
|
86
99
|
class AgentSet(MutableSet, Sequence):
|
|
87
100
|
"""A collection class that represents an ordered set of agents within an agent-based model (ABM).
|
|
@@ -288,15 +301,17 @@ class AgentSet(MutableSet, Sequence):
|
|
|
288
301
|
|
|
289
302
|
It's a fast, optimized version of calling shuffle() followed by do().
|
|
290
303
|
"""
|
|
291
|
-
|
|
292
|
-
self.random.shuffle(
|
|
304
|
+
weakrefs = list(self._agents.keyrefs())
|
|
305
|
+
self.random.shuffle(weakrefs)
|
|
293
306
|
|
|
294
307
|
if isinstance(method, str):
|
|
295
|
-
for
|
|
296
|
-
|
|
308
|
+
for ref in weakrefs:
|
|
309
|
+
if (agent := ref()) is not None:
|
|
310
|
+
getattr(agent, method)(*args, **kwargs)
|
|
297
311
|
else:
|
|
298
|
-
for
|
|
299
|
-
|
|
312
|
+
for ref in weakrefs:
|
|
313
|
+
if (agent := ref()) is not None:
|
|
314
|
+
method(agent, *args, **kwargs)
|
|
300
315
|
|
|
301
316
|
return self
|
|
302
317
|
|
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
|
|
@@ -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.
|
|
@@ -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
|
+
]
|
|
@@ -18,8 +18,9 @@ Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and p
|
|
|
18
18
|
|
|
19
19
|
## Files
|
|
20
20
|
|
|
21
|
-
* ``
|
|
22
|
-
* ``
|
|
21
|
+
* ``model.py``: Core model code.
|
|
22
|
+
* ``agent.py``: Agent classes.
|
|
23
|
+
* ``app.py``: Sets up the interactive visualization.
|
|
23
24
|
* ``Epstein Civil Violence.ipynb``: Jupyter notebook conducting some preliminary analysis of the model.
|
|
24
25
|
|
|
25
26
|
## Further Reading
|
|
@@ -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()
|
|
@@ -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
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import mesa
|
|
2
|
+
from mesa.examples.advanced.epstein_civil_violence.agents import (
|
|
3
|
+
Citizen,
|
|
4
|
+
CitizenState,
|
|
5
|
+
Cop,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class EpsteinCivilViolence(mesa.Model):
|
|
10
|
+
"""
|
|
11
|
+
Model 1 from "Modeling civil violence: An agent-based computational
|
|
12
|
+
approach," by Joshua Epstein.
|
|
13
|
+
http://www.pnas.org/content/99/suppl_3/7243.full
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
height: grid height
|
|
17
|
+
width: grid width
|
|
18
|
+
citizen_density: approximate % of cells occupied by citizens.
|
|
19
|
+
cop_density: approximate % of cells occupied by cops.
|
|
20
|
+
citizen_vision: number of cells in each direction (N, S, E and W) that
|
|
21
|
+
citizen can inspect
|
|
22
|
+
cop_vision: number of cells in each direction (N, S, E and W) that cop
|
|
23
|
+
can inspect
|
|
24
|
+
legitimacy: (L) citizens' perception of regime legitimacy, equal
|
|
25
|
+
across all citizens
|
|
26
|
+
max_jail_term: (J_max)
|
|
27
|
+
active_threshold: if (grievance - (risk_aversion * arrest_probability))
|
|
28
|
+
> threshold, citizen rebels
|
|
29
|
+
arrest_prob_constant: set to ensure agents make plausible arrest
|
|
30
|
+
probability estimates
|
|
31
|
+
movement: binary, whether agents try to move at step end
|
|
32
|
+
max_iters: model may not have a natural stopping point, so we set a
|
|
33
|
+
max.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
def __init__(
|
|
37
|
+
self,
|
|
38
|
+
width=40,
|
|
39
|
+
height=40,
|
|
40
|
+
citizen_density=0.7,
|
|
41
|
+
cop_density=0.074,
|
|
42
|
+
citizen_vision=7,
|
|
43
|
+
cop_vision=7,
|
|
44
|
+
legitimacy=0.8,
|
|
45
|
+
max_jail_term=1000,
|
|
46
|
+
active_threshold=0.1,
|
|
47
|
+
arrest_prob_constant=2.3,
|
|
48
|
+
movement=True,
|
|
49
|
+
max_iters=1000,
|
|
50
|
+
seed=None,
|
|
51
|
+
):
|
|
52
|
+
super().__init__(seed=seed)
|
|
53
|
+
self.movement = movement
|
|
54
|
+
self.max_iters = max_iters
|
|
55
|
+
|
|
56
|
+
self.grid = mesa.experimental.cell_space.OrthogonalVonNeumannGrid(
|
|
57
|
+
(width, height), capacity=1, torus=True, random=self.random
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
model_reporters = {
|
|
61
|
+
"active": CitizenState.ACTIVE.name,
|
|
62
|
+
"quiet": CitizenState.QUIET.name,
|
|
63
|
+
"arrested": CitizenState.ARRESTED.name,
|
|
64
|
+
}
|
|
65
|
+
agent_reporters = {
|
|
66
|
+
"jail_sentence": lambda a: getattr(a, "jail_sentence", None),
|
|
67
|
+
"arrest_probability": lambda a: getattr(a, "arrest_probability", None),
|
|
68
|
+
}
|
|
69
|
+
self.datacollector = mesa.DataCollector(
|
|
70
|
+
model_reporters=model_reporters, agent_reporters=agent_reporters
|
|
71
|
+
)
|
|
72
|
+
if cop_density + citizen_density > 1:
|
|
73
|
+
raise ValueError("Cop density + citizen density must be less than 1")
|
|
74
|
+
|
|
75
|
+
for cell in self.grid.all_cells:
|
|
76
|
+
klass = self.random.choices(
|
|
77
|
+
[Citizen, Cop, None],
|
|
78
|
+
cum_weights=[citizen_density, citizen_density + cop_density, 1],
|
|
79
|
+
)[0]
|
|
80
|
+
|
|
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:
|
|
85
|
+
citizen = Citizen(
|
|
86
|
+
self,
|
|
87
|
+
regime_legitimacy=legitimacy,
|
|
88
|
+
threshold=active_threshold,
|
|
89
|
+
vision=citizen_vision,
|
|
90
|
+
arrest_prob_constant=arrest_prob_constant,
|
|
91
|
+
)
|
|
92
|
+
citizen.move_to(cell)
|
|
93
|
+
|
|
94
|
+
self.running = True
|
|
95
|
+
self._update_counts()
|
|
96
|
+
self.datacollector.collect(self)
|
|
97
|
+
|
|
98
|
+
def step(self):
|
|
99
|
+
"""
|
|
100
|
+
Advance the model by one step and collect data.
|
|
101
|
+
"""
|
|
102
|
+
self.agents.shuffle_do("step")
|
|
103
|
+
self._update_counts()
|
|
104
|
+
self.datacollector.collect(self)
|
|
105
|
+
|
|
106
|
+
if self.steps > self.max_iters:
|
|
107
|
+
self.running = False
|
|
108
|
+
|
|
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()
|
|
112
|
+
|
|
113
|
+
for state in CitizenState:
|
|
114
|
+
setattr(self, state.name, counts.get(state, 0))
|
|
@@ -19,7 +19,7 @@ The Demographic Prisoner's Dilemma demonstrates how simple rules can lead to the
|
|
|
19
19
|
|
|
20
20
|
##### Web based model simulation
|
|
21
21
|
|
|
22
|
-
To run the model interactively, run ``
|
|
22
|
+
To run the model interactively, run ``solara run app.py`` in this directory.
|
|
23
23
|
|
|
24
24
|
##### Jupyter Notebook
|
|
25
25
|
|
|
@@ -27,8 +27,9 @@ Launch the ``Demographic Prisoner's Dilemma Activation Schedule.ipynb`` notebook
|
|
|
27
27
|
|
|
28
28
|
## Files
|
|
29
29
|
|
|
30
|
-
* ``
|
|
31
|
-
* ``
|
|
30
|
+
* ``agents.py``: contains the agent class.
|
|
31
|
+
* ``model.py``: contains the model class; the model takes a ``activation_order`` string as an argument, which determines in which order agents are activated: Sequential, Random or Simultaneous.
|
|
32
|
+
* ``app.py``: contains the interactive visualization server.
|
|
32
33
|
* ``Demographic Prisoner's Dilemma Activation Schedule.ipynb``: Jupyter Notebook for running the scheduling experiment. This runs the model three times, one for each activation type, and demonstrates how the activation regime drives the model to different outcomes.
|
|
33
34
|
|
|
34
35
|
## Further Reading
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Solara-based visualization for the Spatial Prisoner's Dilemma Model.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from mesa.examples.advanced.pd_grid.model import PdGrid
|
|
6
|
+
from mesa.visualization import (
|
|
7
|
+
SolaraViz,
|
|
8
|
+
make_plot_component,
|
|
9
|
+
make_space_component,
|
|
10
|
+
)
|
|
11
|
+
from mesa.visualization.UserParam import Slider
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def pd_agent_portrayal(agent):
|
|
15
|
+
"""
|
|
16
|
+
Portrayal function for rendering PD agents in the visualization.
|
|
17
|
+
"""
|
|
18
|
+
return {
|
|
19
|
+
"color": "blue" if agent.move == "C" else "red",
|
|
20
|
+
"marker": "s", # square marker
|
|
21
|
+
"size": 25,
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Model parameters
|
|
26
|
+
model_params = {
|
|
27
|
+
"width": Slider("Grid Width", value=50, min=10, max=100, step=1),
|
|
28
|
+
"height": Slider("Grid Height", value=50, min=10, max=100, step=1),
|
|
29
|
+
"activation_order": {
|
|
30
|
+
"type": "Select",
|
|
31
|
+
"value": "Random",
|
|
32
|
+
"values": PdGrid.activation_regimes,
|
|
33
|
+
"label": "Activation Regime",
|
|
34
|
+
},
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# Create grid visualization component using Altair
|
|
39
|
+
grid_viz = make_space_component(agent_portrayal=pd_agent_portrayal)
|
|
40
|
+
|
|
41
|
+
# Create plot for tracking cooperating agents over time
|
|
42
|
+
plot_component = make_plot_component("Cooperating_Agents")
|
|
43
|
+
|
|
44
|
+
# Initialize model
|
|
45
|
+
initial_model = PdGrid()
|
|
46
|
+
|
|
47
|
+
# Create visualization with all components
|
|
48
|
+
page = SolaraViz(
|
|
49
|
+
model=initial_model,
|
|
50
|
+
components=[grid_viz, plot_component],
|
|
51
|
+
model_params=model_params,
|
|
52
|
+
name="Spatial Prisoner's Dilemma",
|
|
53
|
+
)
|
|
54
|
+
page # noqa B018
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import mesa
|
|
2
|
+
from mesa.examples.advanced.pd_grid.agents import PDAgent
|
|
2
3
|
from mesa.experimental.cell_space import OrthogonalMooreGrid
|
|
3
4
|
|
|
4
|
-
from .agent import PDAgent
|
|
5
|
-
|
|
6
5
|
|
|
7
6
|
class PdGrid(mesa.Model):
|
|
8
7
|
"""Model class for iterated, spatial prisoner's dilemma model."""
|