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
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
|
|
2
|
+
from mesa.visualization import (
|
|
3
|
+
SolaraViz,
|
|
4
|
+
make_space_component,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def agent_portrayal(agent):
|
|
9
|
+
return {
|
|
10
|
+
"color": "white" if agent.state == 0 else "black",
|
|
11
|
+
"marker": "s",
|
|
12
|
+
"size": 25,
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def post_process(ax):
|
|
17
|
+
ax.set_aspect("equal")
|
|
18
|
+
ax.set_xticks([])
|
|
19
|
+
ax.set_yticks([])
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
model_params = {
|
|
23
|
+
"width": 50,
|
|
24
|
+
"height": 50,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
# Create initial model instance
|
|
28
|
+
model1 = ConwaysGameOfLife(50, 50)
|
|
29
|
+
|
|
30
|
+
# Create visualization elements. The visualization elements are solara components
|
|
31
|
+
# that receive the model instance as a "prop" and display it in a certain way.
|
|
32
|
+
# Under the hood these are just classes that receive the model instance.
|
|
33
|
+
# You can also author your own visualization elements, which can also be functions
|
|
34
|
+
# that receive the model instance and return a valid solara component.
|
|
35
|
+
SpaceGraph = make_space_component(
|
|
36
|
+
agent_portrayal, post_process=post_process, draw_grid=False
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Create the SolaraViz page. This will automatically create a server and display the
|
|
41
|
+
# visualization elements in a web browser.
|
|
42
|
+
# Display it using the following command in the example directory:
|
|
43
|
+
# solara run app.py
|
|
44
|
+
# It will automatically update and display any changes made to this file
|
|
45
|
+
page = SolaraViz(
|
|
46
|
+
model1,
|
|
47
|
+
components=[SpaceGraph],
|
|
48
|
+
model_params=model_params,
|
|
49
|
+
name="Game of Life",
|
|
50
|
+
)
|
|
51
|
+
page # noqa
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from mesa import Model
|
|
2
|
+
from mesa.examples.basic.conways_game_of_life.agents import Cell
|
|
3
|
+
from mesa.space import SingleGrid
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ConwaysGameOfLife(Model):
|
|
7
|
+
"""Represents the 2-dimensional array of cells in Conway's Game of Life."""
|
|
8
|
+
|
|
9
|
+
def __init__(self, width=50, height=50, seed=None):
|
|
10
|
+
"""Create a new playing area of (width, height) cells."""
|
|
11
|
+
super().__init__(seed=seed)
|
|
12
|
+
# Use a simple grid, where edges wrap around.
|
|
13
|
+
self.grid = SingleGrid(width, height, torus=True)
|
|
14
|
+
|
|
15
|
+
# Place a cell at each location, with some initialized to
|
|
16
|
+
# ALIVE and some to DEAD.
|
|
17
|
+
for _contents, (x, y) in self.grid.coord_iter():
|
|
18
|
+
cell = Cell((x, y), self)
|
|
19
|
+
if self.random.random() < 0.1:
|
|
20
|
+
cell.state = cell.ALIVE
|
|
21
|
+
self.grid.place_agent(cell, (x, y))
|
|
22
|
+
|
|
23
|
+
self.running = True
|
|
24
|
+
|
|
25
|
+
def step(self):
|
|
26
|
+
"""Perform the model step in two stages:
|
|
27
|
+
- First, all cells assume their next state (whether they will be dead or alive)
|
|
28
|
+
- Then, all cells change state to their next state.
|
|
29
|
+
"""
|
|
30
|
+
self.agents.do("determine_state")
|
|
31
|
+
self.agents.do("assume_state")
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import time
|
|
2
|
+
|
|
3
|
+
import altair as alt
|
|
4
|
+
import numpy as np
|
|
5
|
+
import pandas as pd
|
|
6
|
+
import streamlit as st
|
|
7
|
+
|
|
8
|
+
from mesa.examples.basic.conways_game_of_life.model import ConwaysGameOfLife
|
|
9
|
+
|
|
10
|
+
model = st.title("Conway's Game of Life")
|
|
11
|
+
num_ticks = st.slider("Select number of Steps", min_value=1, max_value=100, value=50)
|
|
12
|
+
height = st.slider("Select Grid Height", min_value=10, max_value=100, step=10, value=15)
|
|
13
|
+
width = st.slider("Select Grid Width", min_value=10, max_value=100, step=10, value=20)
|
|
14
|
+
model = ConwaysGameOfLife(height, width)
|
|
15
|
+
|
|
16
|
+
col1, col2, col3 = st.columns(3)
|
|
17
|
+
status_text = st.empty()
|
|
18
|
+
# step_mode = st.checkbox('Run Step-by-Step')
|
|
19
|
+
run = st.button("Run Simulation")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
if run:
|
|
23
|
+
tick = time.time()
|
|
24
|
+
step = 0
|
|
25
|
+
# init grid
|
|
26
|
+
df_grid = pd.DataFrame()
|
|
27
|
+
agent_counts = np.zeros((model.grid.width, model.grid.height))
|
|
28
|
+
for x in range(width):
|
|
29
|
+
for y in range(height):
|
|
30
|
+
df_grid = pd.concat(
|
|
31
|
+
[df_grid, pd.DataFrame({"x": [x], "y": [y], "state": [0]})],
|
|
32
|
+
ignore_index=True,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
heatmap = (
|
|
36
|
+
alt.Chart(df_grid)
|
|
37
|
+
.mark_point(size=100)
|
|
38
|
+
.encode(x="x", y="y", color=alt.Color("state"))
|
|
39
|
+
.interactive()
|
|
40
|
+
.properties(width=800, height=600)
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
# init progress bar
|
|
44
|
+
my_bar = st.progress(0, text="Simulation Progress") # progress
|
|
45
|
+
placeholder = st.empty()
|
|
46
|
+
st.subheader("Agent Grid")
|
|
47
|
+
chart = st.altair_chart(heatmap, use_container_width=True)
|
|
48
|
+
color_scale = alt.Scale(domain=[0, 1], range=["red", "yellow"])
|
|
49
|
+
for i in range(num_ticks):
|
|
50
|
+
model.step()
|
|
51
|
+
my_bar.progress((i / num_ticks), text="Simulation progress")
|
|
52
|
+
placeholder.text("Step = %d" % i)
|
|
53
|
+
for contents, (x, y) in model.grid.coord_iter():
|
|
54
|
+
# print('x:',x,'y:',y, 'state:',contents)
|
|
55
|
+
selected_row = df_grid[(df_grid["x"] == x) & (df_grid["y"] == y)]
|
|
56
|
+
df_grid.loc[selected_row.index, "state"] = (
|
|
57
|
+
contents.state
|
|
58
|
+
) # random.choice([1,2])
|
|
59
|
+
|
|
60
|
+
heatmap = (
|
|
61
|
+
alt.Chart(df_grid)
|
|
62
|
+
.mark_circle(size=100)
|
|
63
|
+
.encode(x="x", y="y", color=alt.Color("state", scale=color_scale))
|
|
64
|
+
.interactive()
|
|
65
|
+
.properties(width=800, height=600)
|
|
66
|
+
)
|
|
67
|
+
chart.altair_chart(heatmap)
|
|
68
|
+
|
|
69
|
+
time.sleep(0.1)
|
|
70
|
+
|
|
71
|
+
tock = time.time()
|
|
72
|
+
st.success(f"Simulation completed in {tock - tick:.2f} secs")
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Schelling Segregation Model
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
The Schelling segregation model is a classic agent-based model, demonstrating how even a mild preference for similar neighbors can lead to a much higher degree of segregation than we would intuitively expect. The model consists of agents on a square grid, where each grid cell can contain at most one agent. Agents come in two colors: red and blue. They are happy if a certain number of their eight possible neighbors are of the same color, and unhappy otherwise. Unhappy agents will pick a random empty cell to move to each step, until they are happy. The model keeps running until there are no unhappy agents.
|
|
6
|
+
|
|
7
|
+
By default, the number of similar neighbors the agents need to be happy is set to 3. That means the agents would be perfectly happy with a majority of their neighbors being of a different color (e.g. a Blue agent would be happy with five Red neighbors and three Blue ones). Despite this, the model consistently leads to a high degree of segregation, with most agents ending up with no neighbors of a different color.
|
|
8
|
+
|
|
9
|
+
## How to Run
|
|
10
|
+
|
|
11
|
+
To run the model interactively, in this directory, run the following command
|
|
12
|
+
|
|
13
|
+
```
|
|
14
|
+
$ solara run app.py
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Then open your browser to [http://127.0.0.1:8765/](http://127.0.0.1:8765/) and click the Play button.
|
|
18
|
+
|
|
19
|
+
To view and run some example model analyses, launch the IPython Notebook and open ``analysis.ipynb``. Visualizing the analysis also requires [matplotlib](http://matplotlib.org/).
|
|
20
|
+
|
|
21
|
+
## How to Run without the GUI
|
|
22
|
+
|
|
23
|
+
To run the model with the grid displayed as an ASCII text, run `python run_ascii.py` in this directory.
|
|
24
|
+
|
|
25
|
+
## Files
|
|
26
|
+
|
|
27
|
+
* ``model.py``: Contains the Schelling model class
|
|
28
|
+
* ``agents.py``: Contains the Schelling agent class
|
|
29
|
+
* ``app.py``: Code for the interactive visualization.
|
|
30
|
+
* ``analysis.ipynb``: Notebook demonstrating how to run experiments and parameter sweeps on the model.
|
|
31
|
+
|
|
32
|
+
## Further Reading
|
|
33
|
+
|
|
34
|
+
Schelling's original paper describing the model:
|
|
35
|
+
|
|
36
|
+
[Schelling, Thomas C. Dynamic Models of Segregation. Journal of Mathematical Sociology. 1971, Vol. 1, pp 143-186.](https://www.stat.berkeley.edu/~aldous/157/Papers/Schelling_Seg_Models.pdf)
|
|
37
|
+
|
|
38
|
+
An interactive, browser-based explanation and implementation:
|
|
39
|
+
|
|
40
|
+
[Parable of the Polygons](http://ncase.me/polygons/), by Vi Hart and Nicky Case.
|
|
File without changes
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from mesa import Agent, Model
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SchellingAgent(Agent):
|
|
5
|
+
"""Schelling segregation agent."""
|
|
6
|
+
|
|
7
|
+
def __init__(self, model: Model, agent_type: int) -> None:
|
|
8
|
+
"""Create a new Schelling agent.
|
|
9
|
+
|
|
10
|
+
Args:
|
|
11
|
+
agent_type: Indicator for the agent's type (minority=1, majority=0)
|
|
12
|
+
"""
|
|
13
|
+
super().__init__(model)
|
|
14
|
+
self.type = agent_type
|
|
15
|
+
|
|
16
|
+
def step(self) -> None:
|
|
17
|
+
neighbors = self.model.grid.iter_neighbors(
|
|
18
|
+
self.pos, moore=True, radius=self.model.radius
|
|
19
|
+
)
|
|
20
|
+
similar = sum(1 for neighbor in neighbors if neighbor.type == self.type)
|
|
21
|
+
|
|
22
|
+
# If unhappy, move:
|
|
23
|
+
if similar < self.model.homophily:
|
|
24
|
+
self.model.grid.move_to_empty(self)
|
|
25
|
+
else:
|
|
26
|
+
self.model.happy += 1
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
{
|
|
2
|
+
"cells": [
|
|
3
|
+
{
|
|
4
|
+
"cell_type": "markdown",
|
|
5
|
+
"metadata": {},
|
|
6
|
+
"source": [
|
|
7
|
+
"# Schelling Segregation Model\n",
|
|
8
|
+
"\n",
|
|
9
|
+
"## Background\n",
|
|
10
|
+
"\n",
|
|
11
|
+
"The Schelling (1971) segregation model is a classic of agent-based modeling, demonstrating how agents following simple rules lead to the emergence of qualitatively different macro-level outcomes. Agents are randomly placed on a grid. There are two types of agents, one constituting the majority and the other the minority. All agents want a certain number (generally, 3) of their 8 surrounding neighbors to be of the same type in order for them to be happy. Unhappy agents will move to a random available grid space. While individual agents do not have a preference for a segregated outcome (e.g. they would be happy with 3 similar neighbors and 5 different ones), the aggregate outcome is nevertheless heavily segregated.\n",
|
|
12
|
+
"\n",
|
|
13
|
+
"## Implementation\n",
|
|
14
|
+
"\n",
|
|
15
|
+
"This is a demonstration of running a Mesa model in an IPython Notebook. The actual model and agent code are implemented in Schelling.py, in the same directory as this notebook. Below, we will import the model class, instantiate it, run it, and plot the time series of the number of happy agents."
|
|
16
|
+
]
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"cell_type": "code",
|
|
20
|
+
"metadata": {},
|
|
21
|
+
"source": [
|
|
22
|
+
"import matplotlib.pyplot as plt\n",
|
|
23
|
+
"import pandas as pd\n",
|
|
24
|
+
"\n",
|
|
25
|
+
"%matplotlib inline\n",
|
|
26
|
+
"\n",
|
|
27
|
+
"from model import Schelling"
|
|
28
|
+
],
|
|
29
|
+
"outputs": [],
|
|
30
|
+
"execution_count": null
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"cell_type": "markdown",
|
|
34
|
+
"metadata": {},
|
|
35
|
+
"source": [
|
|
36
|
+
"Now we instantiate a model instance: a 10x10 grid, with an 80% change of an agent being placed in each cell, approximately 20% of agents set as minorities, and agents wanting at least 3 similar neighbors."
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
"cell_type": "code",
|
|
41
|
+
"metadata": {},
|
|
42
|
+
"source": "model = Schelling(height=10, width=10, homophily=3, density=0.8, minority_pc=0.2)",
|
|
43
|
+
"outputs": [],
|
|
44
|
+
"execution_count": null
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"cell_type": "markdown",
|
|
48
|
+
"metadata": {},
|
|
49
|
+
"source": [
|
|
50
|
+
"We want to run the model until all the agents are happy with where they are. However, there's no guarantee that a given model instantiation will *ever* settle down. So let's run it for either 100 steps or until it stops on its own, whichever comes first:"
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"cell_type": "code",
|
|
55
|
+
"metadata": {},
|
|
56
|
+
"source": [
|
|
57
|
+
"while model.running and model.steps < 100:\n",
|
|
58
|
+
" model.step()\n",
|
|
59
|
+
"print(model.steps) # Show how many steps have actually run"
|
|
60
|
+
],
|
|
61
|
+
"outputs": [],
|
|
62
|
+
"execution_count": null
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"cell_type": "markdown",
|
|
66
|
+
"metadata": {},
|
|
67
|
+
"source": [
|
|
68
|
+
"The model has a DataCollector object, which checks and stores how many agents are happy at the end of each step. It can also generate a pandas DataFrame of the data it has collected:"
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"cell_type": "code",
|
|
73
|
+
"metadata": {},
|
|
74
|
+
"source": [
|
|
75
|
+
"model_out = model.datacollector.get_model_vars_dataframe()"
|
|
76
|
+
],
|
|
77
|
+
"outputs": [],
|
|
78
|
+
"execution_count": null
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"cell_type": "code",
|
|
82
|
+
"metadata": {},
|
|
83
|
+
"source": [
|
|
84
|
+
"model_out.head()"
|
|
85
|
+
],
|
|
86
|
+
"outputs": [],
|
|
87
|
+
"execution_count": null
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"cell_type": "markdown",
|
|
91
|
+
"metadata": {},
|
|
92
|
+
"source": [
|
|
93
|
+
"Finally, we can plot the 'happy' series:"
|
|
94
|
+
]
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"cell_type": "code",
|
|
98
|
+
"metadata": {},
|
|
99
|
+
"source": [
|
|
100
|
+
"model_out.happy.plot()"
|
|
101
|
+
],
|
|
102
|
+
"outputs": [],
|
|
103
|
+
"execution_count": null
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
"cell_type": "markdown",
|
|
107
|
+
"metadata": {},
|
|
108
|
+
"source": [
|
|
109
|
+
"# Effect of Homophily on happiness\n",
|
|
110
|
+
"\n",
|
|
111
|
+
"Now, we can do a parameter sweep to see how happiness changes with homophily.\n",
|
|
112
|
+
"\n",
|
|
113
|
+
"First, we create a function which takes a model instance and returns what fraction of agents are segregated -- that is, have no neighbors of the opposite type."
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"cell_type": "code",
|
|
118
|
+
"metadata": {},
|
|
119
|
+
"source": "from mesa.batchrunner import batch_run",
|
|
120
|
+
"outputs": [],
|
|
121
|
+
"execution_count": null
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
"cell_type": "markdown",
|
|
125
|
+
"metadata": {},
|
|
126
|
+
"source": [
|
|
127
|
+
"Now, we set up the batch run, with a dictionary of fixed and changing parameters. Let's hold everything fixed except for Homophily."
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"cell_type": "code",
|
|
132
|
+
"metadata": {},
|
|
133
|
+
"source": [
|
|
134
|
+
"fixed_params = {\"height\": 10, \"width\": 10, \"density\": 0.8, \"minority_pc\": 0.2}\n",
|
|
135
|
+
"variable_parms = {\"homophily\": range(1, 9)}\n",
|
|
136
|
+
"all_params = fixed_params | variable_parms"
|
|
137
|
+
],
|
|
138
|
+
"outputs": [],
|
|
139
|
+
"execution_count": null
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"cell_type": "code",
|
|
143
|
+
"metadata": {},
|
|
144
|
+
"source": [
|
|
145
|
+
"results = batch_run(\n",
|
|
146
|
+
" Schelling,\n",
|
|
147
|
+
" parameters=all_params,\n",
|
|
148
|
+
" iterations=10,\n",
|
|
149
|
+
" max_steps=200,\n",
|
|
150
|
+
")"
|
|
151
|
+
],
|
|
152
|
+
"outputs": [],
|
|
153
|
+
"execution_count": null
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"metadata": {},
|
|
157
|
+
"cell_type": "code",
|
|
158
|
+
"source": [
|
|
159
|
+
"df = pd.DataFrame(results)\n",
|
|
160
|
+
"df"
|
|
161
|
+
],
|
|
162
|
+
"outputs": [],
|
|
163
|
+
"execution_count": null
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
"cell_type": "code",
|
|
167
|
+
"metadata": {},
|
|
168
|
+
"source": [
|
|
169
|
+
"plt.scatter(df.homophily, df.happy)\n",
|
|
170
|
+
"plt.xlabel(\"Homophily\")\n",
|
|
171
|
+
"plt.ylabel(\"Happy Agents\")\n",
|
|
172
|
+
"plt.grid()\n",
|
|
173
|
+
"plt.title(\"Effect of Homophily on segregation\")\n",
|
|
174
|
+
"plt.show()"
|
|
175
|
+
],
|
|
176
|
+
"outputs": [],
|
|
177
|
+
"execution_count": null
|
|
178
|
+
}
|
|
179
|
+
],
|
|
180
|
+
"metadata": {
|
|
181
|
+
"kernelspec": {
|
|
182
|
+
"display_name": "Python 3 (ipykernel)",
|
|
183
|
+
"language": "python",
|
|
184
|
+
"name": "python3"
|
|
185
|
+
},
|
|
186
|
+
"language_info": {
|
|
187
|
+
"codemirror_mode": {
|
|
188
|
+
"name": "ipython",
|
|
189
|
+
"version": 3
|
|
190
|
+
},
|
|
191
|
+
"file_extension": ".py",
|
|
192
|
+
"mimetype": "text/x-python",
|
|
193
|
+
"name": "python",
|
|
194
|
+
"nbconvert_exporter": "python",
|
|
195
|
+
"pygments_lexer": "ipython3",
|
|
196
|
+
"version": "3.9.9"
|
|
197
|
+
},
|
|
198
|
+
"widgets": {
|
|
199
|
+
"state": {},
|
|
200
|
+
"version": "1.1.2"
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
"nbformat": 4,
|
|
204
|
+
"nbformat_minor": 1
|
|
205
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import solara
|
|
2
|
+
|
|
3
|
+
from mesa.examples.basic.schelling.model import Schelling
|
|
4
|
+
from mesa.visualization import (
|
|
5
|
+
Slider,
|
|
6
|
+
SolaraViz,
|
|
7
|
+
make_plot_component,
|
|
8
|
+
make_space_component,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def get_happy_agents(model):
|
|
13
|
+
"""Display a text count of how many happy agents there are."""
|
|
14
|
+
return solara.Markdown(f"**Happy agents: {model.happy}**")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def agent_portrayal(agent):
|
|
18
|
+
return {"color": "tab:orange" if agent.type == 0 else "tab:blue"}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
model_params = {
|
|
22
|
+
"density": Slider("Agent density", 0.8, 0.1, 1.0, 0.1),
|
|
23
|
+
"minority_pc": Slider("Fraction minority", 0.2, 0.0, 1.0, 0.05),
|
|
24
|
+
"homophily": Slider("Homophily", 3, 0, 8, 1),
|
|
25
|
+
"width": 20,
|
|
26
|
+
"height": 20,
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
model1 = Schelling(20, 20, 0.8, 0.2, 3)
|
|
30
|
+
|
|
31
|
+
HappyPlot = make_plot_component({"happy": "tab:green"})
|
|
32
|
+
|
|
33
|
+
page = SolaraViz(
|
|
34
|
+
model1,
|
|
35
|
+
components=[
|
|
36
|
+
make_space_component(agent_portrayal),
|
|
37
|
+
HappyPlot,
|
|
38
|
+
get_happy_agents,
|
|
39
|
+
],
|
|
40
|
+
model_params=model_params,
|
|
41
|
+
)
|
|
42
|
+
page # noqa
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import mesa
|
|
2
|
+
from mesa import Model
|
|
3
|
+
from mesa.examples.basic.schelling.agents import SchellingAgent
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Schelling(Model):
|
|
7
|
+
"""Model class for the Schelling segregation model."""
|
|
8
|
+
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
height=20,
|
|
12
|
+
width=20,
|
|
13
|
+
homophily=3,
|
|
14
|
+
radius=1,
|
|
15
|
+
density=0.8,
|
|
16
|
+
minority_pc=0.2,
|
|
17
|
+
seed=None,
|
|
18
|
+
):
|
|
19
|
+
"""Create a new Schelling model.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
width, height: Size of the space.
|
|
23
|
+
density: Initial Chance for a cell to populated
|
|
24
|
+
minority_pc: Chances for an agent to be in minority class
|
|
25
|
+
homophily: Minimum number of agents of same class needed to be happy
|
|
26
|
+
radius: Search radius for checking similarity
|
|
27
|
+
seed: Seed for Reproducibility
|
|
28
|
+
"""
|
|
29
|
+
super().__init__(seed=seed)
|
|
30
|
+
self.homophily = homophily
|
|
31
|
+
self.radius = radius
|
|
32
|
+
|
|
33
|
+
self.grid = mesa.space.SingleGrid(width, height, torus=True)
|
|
34
|
+
|
|
35
|
+
self.happy = 0
|
|
36
|
+
self.datacollector = mesa.DataCollector(
|
|
37
|
+
model_reporters={"happy": "happy"}, # Model-level count of happy agents
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Set up agents
|
|
41
|
+
# We use a grid iterator that returns
|
|
42
|
+
# the coordinates of a cell as well as
|
|
43
|
+
# its contents. (coord_iter)
|
|
44
|
+
for _, pos in self.grid.coord_iter():
|
|
45
|
+
if self.random.random() < density:
|
|
46
|
+
agent_type = 1 if self.random.random() < minority_pc else 0
|
|
47
|
+
agent = SchellingAgent(self, agent_type)
|
|
48
|
+
self.grid.place_agent(agent, pos)
|
|
49
|
+
|
|
50
|
+
self.datacollector.collect(self)
|
|
51
|
+
|
|
52
|
+
def step(self):
|
|
53
|
+
"""Run one step of the model."""
|
|
54
|
+
self.happy = 0 # Reset counter of happy agents
|
|
55
|
+
self.agents.shuffle_do("step")
|
|
56
|
+
|
|
57
|
+
self.datacollector.collect(self)
|
|
58
|
+
|
|
59
|
+
self.running = self.happy != len(self.agents)
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# Virus on a Network
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
This model is based on the NetLogo model "Virus on Network". It demonstrates the spread of a virus through a network and follows the SIR model, commonly seen in epidemiology.
|
|
6
|
+
|
|
7
|
+
The SIR model is one of the simplest compartmental models, and many models are derivatives of this basic form. The model consists of three compartments:
|
|
8
|
+
|
|
9
|
+
S: The number of susceptible individuals. When a susceptible and an infectious individual come into "infectious contact", the susceptible individual contracts the disease and transitions to the infectious compartment.
|
|
10
|
+
I: The number of infectious individuals. These are individuals who have been infected and are capable of infecting susceptible individuals.
|
|
11
|
+
R for the number of removed (and immune) or deceased individuals. These are individuals who have been infected and have either recovered from the disease and entered the removed compartment, or died. It is assumed that the number of deaths is negligible with respect to the total population. This compartment may also be called "recovered" or "resistant".
|
|
12
|
+
|
|
13
|
+
For more information about this model, read the NetLogo's web page: http://ccl.northwestern.edu/netlogo/models/VirusonaNetwork.
|
|
14
|
+
|
|
15
|
+
JavaScript library used in this example to render the network: [d3.js](https://d3js.org/).
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
To install the dependencies use pip and the requirements.txt in this directory. e.g.
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
$ pip install -r requirements.txt
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## How to Run
|
|
26
|
+
|
|
27
|
+
To run the model interactively, run ``mesa runserver`` in this directory. e.g.
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
$ mesa runserver
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
|
|
34
|
+
|
|
35
|
+
or
|
|
36
|
+
|
|
37
|
+
Directly run the file ``run.py`` in the terminal. e.g.
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
$ python run.py
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
## Files
|
|
45
|
+
|
|
46
|
+
* ``model.py``: Contains the agent class, and the overall model class.
|
|
47
|
+
* ``agents.py``: Contains the agent class.
|
|
48
|
+
* ``app.py``: Contains the code for the interactive Solara visualization.
|
|
49
|
+
|
|
50
|
+
## Further Reading
|
|
51
|
+
|
|
52
|
+
The full tutorial describing how the model is built can be found at:
|
|
53
|
+
https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
[Stonedahl, F. and Wilensky, U. (2008). NetLogo Virus on a Network model](http://ccl.northwestern.edu/netlogo/models/VirusonaNetwork).
|
|
57
|
+
Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
[Wilensky, U. (1999). NetLogo](http://ccl.northwestern.edu/netlogo/)
|
|
61
|
+
Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
|
|
File without changes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
from mesa import Agent
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class State(Enum):
|
|
7
|
+
SUSCEPTIBLE = 0
|
|
8
|
+
INFECTED = 1
|
|
9
|
+
RESISTANT = 2
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class VirusAgent(Agent):
|
|
13
|
+
"""Individual Agent definition and its properties/interaction methods."""
|
|
14
|
+
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
model,
|
|
18
|
+
initial_state,
|
|
19
|
+
virus_spread_chance,
|
|
20
|
+
virus_check_frequency,
|
|
21
|
+
recovery_chance,
|
|
22
|
+
gain_resistance_chance,
|
|
23
|
+
):
|
|
24
|
+
super().__init__(model)
|
|
25
|
+
|
|
26
|
+
self.state = initial_state
|
|
27
|
+
|
|
28
|
+
self.virus_spread_chance = virus_spread_chance
|
|
29
|
+
self.virus_check_frequency = virus_check_frequency
|
|
30
|
+
self.recovery_chance = recovery_chance
|
|
31
|
+
self.gain_resistance_chance = gain_resistance_chance
|
|
32
|
+
|
|
33
|
+
def try_to_infect_neighbors(self):
|
|
34
|
+
neighbors_nodes = self.model.grid.get_neighborhood(
|
|
35
|
+
self.pos, include_center=False
|
|
36
|
+
)
|
|
37
|
+
susceptible_neighbors = [
|
|
38
|
+
agent
|
|
39
|
+
for agent in self.model.grid.get_cell_list_contents(neighbors_nodes)
|
|
40
|
+
if agent.state is State.SUSCEPTIBLE
|
|
41
|
+
]
|
|
42
|
+
for a in susceptible_neighbors:
|
|
43
|
+
if self.random.random() < self.virus_spread_chance:
|
|
44
|
+
a.state = State.INFECTED
|
|
45
|
+
|
|
46
|
+
def try_gain_resistance(self):
|
|
47
|
+
if self.random.random() < self.gain_resistance_chance:
|
|
48
|
+
self.state = State.RESISTANT
|
|
49
|
+
|
|
50
|
+
def try_remove_infection(self):
|
|
51
|
+
# Try to remove
|
|
52
|
+
if self.random.random() < self.recovery_chance:
|
|
53
|
+
# Success
|
|
54
|
+
self.state = State.SUSCEPTIBLE
|
|
55
|
+
self.try_gain_resistance()
|
|
56
|
+
else:
|
|
57
|
+
# Failed
|
|
58
|
+
self.state = State.INFECTED
|
|
59
|
+
|
|
60
|
+
def try_check_situation(self):
|
|
61
|
+
if (self.random.random() < self.virus_check_frequency) and (
|
|
62
|
+
self.state is State.INFECTED
|
|
63
|
+
):
|
|
64
|
+
self.try_remove_infection()
|
|
65
|
+
|
|
66
|
+
def step(self):
|
|
67
|
+
if self.state is State.INFECTED:
|
|
68
|
+
self.try_to_infect_neighbors()
|
|
69
|
+
self.try_check_situation()
|