Mesa 3.0.0__py3-none-any.whl → 3.0.0a1__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 -3
- mesa/agent.py +114 -406
- mesa/batchrunner.py +27 -54
- mesa/cookiecutter-mesa/cookiecutter.json +8 -0
- mesa/cookiecutter-mesa/hooks/post_gen_project.py +11 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/README.md +4 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/app.pytemplate +27 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate +11 -0
- mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate +60 -0
- mesa/datacollection.py +29 -140
- mesa/experimental/__init__.py +1 -11
- mesa/experimental/cell_space/__init__.py +1 -16
- mesa/experimental/cell_space/cell.py +23 -93
- mesa/experimental/cell_space/cell_agent.py +21 -117
- mesa/experimental/cell_space/cell_collection.py +17 -54
- mesa/experimental/cell_space/discrete_space.py +8 -92
- mesa/experimental/cell_space/grid.py +8 -32
- mesa/experimental/cell_space/network.py +7 -12
- mesa/experimental/devs/__init__.py +0 -2
- mesa/experimental/devs/eventlist.py +14 -52
- mesa/experimental/devs/examples/epstein_civil_violence.py +39 -71
- mesa/experimental/devs/examples/wolf_sheep.py +45 -45
- mesa/experimental/devs/simulator.py +15 -55
- mesa/main.py +63 -0
- mesa/model.py +83 -211
- mesa/space.py +149 -215
- mesa/time.py +77 -62
- mesa/{experimental → visualization}/UserParam.py +6 -17
- mesa/visualization/__init__.py +2 -25
- mesa/{experimental → visualization}/components/altair.py +0 -10
- mesa/visualization/components/matplotlib.py +134 -0
- mesa/visualization/solara_viz.py +266 -267
- {mesa-3.0.0.dist-info → mesa-3.0.0a1.dist-info}/METADATA +13 -65
- mesa-3.0.0a1.dist-info/RECORD +38 -0
- mesa-3.0.0.dist-info/licenses/NOTICE → mesa-3.0.0a1.dist-info/licenses/LICENSE +2 -2
- mesa/examples/README.md +0 -37
- mesa/examples/__init__.py +0 -21
- mesa/examples/advanced/epstein_civil_violence/Epstein Civil Violence.ipynb +0 -116
- mesa/examples/advanced/epstein_civil_violence/Readme.md +0 -34
- mesa/examples/advanced/epstein_civil_violence/__init__.py +0 -0
- mesa/examples/advanced/epstein_civil_violence/agents.py +0 -164
- mesa/examples/advanced/epstein_civil_violence/app.py +0 -73
- mesa/examples/advanced/epstein_civil_violence/model.py +0 -114
- mesa/examples/advanced/pd_grid/Readme.md +0 -43
- mesa/examples/advanced/pd_grid/__init__.py +0 -0
- mesa/examples/advanced/pd_grid/agents.py +0 -50
- mesa/examples/advanced/pd_grid/analysis.ipynb +0 -228
- mesa/examples/advanced/pd_grid/app.py +0 -54
- mesa/examples/advanced/pd_grid/model.py +0 -71
- mesa/examples/advanced/sugarscape_g1mt/Readme.md +0 -64
- mesa/examples/advanced/sugarscape_g1mt/__init__.py +0 -0
- mesa/examples/advanced/sugarscape_g1mt/agents.py +0 -344
- mesa/examples/advanced/sugarscape_g1mt/app.py +0 -62
- mesa/examples/advanced/sugarscape_g1mt/model.py +0 -180
- mesa/examples/advanced/sugarscape_g1mt/sugar-map.txt +0 -50
- mesa/examples/advanced/sugarscape_g1mt/tests.py +0 -69
- mesa/examples/advanced/wolf_sheep/Readme.md +0 -57
- mesa/examples/advanced/wolf_sheep/__init__.py +0 -0
- mesa/examples/advanced/wolf_sheep/agents.py +0 -102
- mesa/examples/advanced/wolf_sheep/app.py +0 -84
- mesa/examples/advanced/wolf_sheep/model.py +0 -137
- mesa/examples/basic/__init__.py +0 -0
- mesa/examples/basic/boid_flockers/Readme.md +0 -22
- mesa/examples/basic/boid_flockers/__init__.py +0 -0
- mesa/examples/basic/boid_flockers/agents.py +0 -71
- mesa/examples/basic/boid_flockers/app.py +0 -58
- mesa/examples/basic/boid_flockers/model.py +0 -69
- mesa/examples/basic/boltzmann_wealth_model/Readme.md +0 -56
- mesa/examples/basic/boltzmann_wealth_model/__init__.py +0 -0
- mesa/examples/basic/boltzmann_wealth_model/agents.py +0 -31
- mesa/examples/basic/boltzmann_wealth_model/app.py +0 -74
- mesa/examples/basic/boltzmann_wealth_model/model.py +0 -43
- mesa/examples/basic/boltzmann_wealth_model/st_app.py +0 -115
- mesa/examples/basic/conways_game_of_life/Readme.md +0 -39
- mesa/examples/basic/conways_game_of_life/__init__.py +0 -0
- mesa/examples/basic/conways_game_of_life/agents.py +0 -47
- mesa/examples/basic/conways_game_of_life/app.py +0 -51
- mesa/examples/basic/conways_game_of_life/model.py +0 -31
- mesa/examples/basic/conways_game_of_life/st_app.py +0 -72
- mesa/examples/basic/schelling/Readme.md +0 -40
- mesa/examples/basic/schelling/__init__.py +0 -0
- mesa/examples/basic/schelling/agents.py +0 -26
- mesa/examples/basic/schelling/analysis.ipynb +0 -205
- mesa/examples/basic/schelling/app.py +0 -42
- mesa/examples/basic/schelling/model.py +0 -59
- mesa/examples/basic/virus_on_network/Readme.md +0 -61
- mesa/examples/basic/virus_on_network/__init__.py +0 -0
- mesa/examples/basic/virus_on_network/agents.py +0 -69
- mesa/examples/basic/virus_on_network/app.py +0 -114
- mesa/examples/basic/virus_on_network/model.py +0 -96
- mesa/experimental/cell_space/voronoi.py +0 -257
- mesa/experimental/components/matplotlib.py +0 -242
- mesa/experimental/solara_viz.py +0 -453
- mesa/visualization/components/__init__.py +0 -83
- mesa/visualization/components/altair_components.py +0 -188
- mesa/visualization/components/matplotlib_components.py +0 -175
- mesa/visualization/mpl_space_drawing.py +0 -593
- mesa/visualization/user_param.py +0 -69
- mesa/visualization/utils.py +0 -9
- mesa-3.0.0.dist-info/RECORD +0 -95
- mesa-3.0.0.dist-info/licenses/LICENSE +0 -202
- /mesa/{examples/advanced → cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}}/__init__.py +0 -0
- {mesa-3.0.0.dist-info → mesa-3.0.0a1.dist-info}/WHEEL +0 -0
- {mesa-3.0.0.dist-info → mesa-3.0.0a1.dist-info}/entry_points.txt +0 -0
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
# Boltzmann Wealth Model (Tutorial)
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
A simple model of agents exchanging wealth. All agents start with the same amount of money. Every step, each agent with one unit of money or more gives one unit of wealth to another random agent. This is the model described in the [Intro Tutorial](https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html), with the completed code.
|
|
6
|
-
|
|
7
|
-
If you want to go over the step-by-step tutorial, please go and run the [Jupyter Notebook](https://github.com/projectmesa/mesa/blob/main/docs/tutorials/intro_tutorial.ipynb). The code here runs the finalized code in the last cells directly.
|
|
8
|
-
|
|
9
|
-
As the model runs, the distribution of wealth among agents goes from being perfectly uniform (all agents have the same starting wealth), to highly skewed -- a small number have high wealth, more have none at all.
|
|
10
|
-
|
|
11
|
-
## How to Run
|
|
12
|
-
|
|
13
|
-
To follow the tutorial example, launch the Jupyter Notebook and run the code in ``Introduction to Mesa Tutorial Code.ipynb`` which you can find in the main mesa repo [here](https://github.com/projectmesa/mesa/blob/main/docs/tutorials/intro_tutorial.ipynb)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
To launch the interactive server, as described in the [last section of the tutorial](https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html#adding-visualization), run:
|
|
17
|
-
|
|
18
|
-
```
|
|
19
|
-
$ solara run app.py
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
If your browser doesn't open automatically, point it to [http://127.0.0.1:8765/](http://127.0.0.1:8765/). When the visualization loads, click on the Play button.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
## Files
|
|
26
|
-
|
|
27
|
-
* ``model.py``: Final version of the model.
|
|
28
|
-
* ``agents.py``: Final version of the agent.
|
|
29
|
-
* ``app.py``: Code for the interactive visualization.
|
|
30
|
-
|
|
31
|
-
## Optional
|
|
32
|
-
|
|
33
|
-
An optional visualization is also provided using Streamlit, which is another popular Python library for creating interactive web applications.
|
|
34
|
-
|
|
35
|
-
To run the Streamlit app, you will need to install the `streamlit` and `altair` libraries:
|
|
36
|
-
|
|
37
|
-
```
|
|
38
|
-
$ pip install streamlit altair
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Then, you can run the Streamlit app using the following command:
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
$ streamlit run st_app.py
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Further Reading
|
|
48
|
-
|
|
49
|
-
The full tutorial describing how the model is built can be found at:
|
|
50
|
-
https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html
|
|
51
|
-
|
|
52
|
-
This model is drawn from econophysics and presents a statistical mechanics approach to wealth distribution. Some examples of further reading on the topic can be found at:
|
|
53
|
-
|
|
54
|
-
[Milakovic, M. A Statistical Equilibrium Model of Wealth Distribution. February, 2001.](https://editorialexpress.com/cgi-bin/conference/download.cgi?db_name=SCE2001&paper_id=214)
|
|
55
|
-
|
|
56
|
-
[Dragulescu, A and Yakovenko, V. Statistical Mechanics of Money, Income, and Wealth: A Short Survey. November, 2002](http://arxiv.org/pdf/cond-mat/0211175v1.pdf)
|
|
File without changes
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
from mesa import Agent
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class MoneyAgent(Agent):
|
|
5
|
-
"""An agent with fixed initial wealth."""
|
|
6
|
-
|
|
7
|
-
def __init__(self, model):
|
|
8
|
-
super().__init__(model)
|
|
9
|
-
self.wealth = 1
|
|
10
|
-
|
|
11
|
-
def move(self):
|
|
12
|
-
possible_steps = self.model.grid.get_neighborhood(
|
|
13
|
-
self.pos, moore=True, include_center=False
|
|
14
|
-
)
|
|
15
|
-
new_position = self.random.choice(possible_steps)
|
|
16
|
-
self.model.grid.move_agent(self, new_position)
|
|
17
|
-
|
|
18
|
-
def give_money(self):
|
|
19
|
-
cellmates = self.model.grid.get_cell_list_contents([self.pos])
|
|
20
|
-
cellmates.pop(
|
|
21
|
-
cellmates.index(self)
|
|
22
|
-
) # Ensure agent is not giving money to itself
|
|
23
|
-
if len(cellmates) > 0:
|
|
24
|
-
other = self.random.choice(cellmates)
|
|
25
|
-
other.wealth += 1
|
|
26
|
-
self.wealth -= 1
|
|
27
|
-
|
|
28
|
-
def step(self):
|
|
29
|
-
self.move()
|
|
30
|
-
if self.wealth > 0:
|
|
31
|
-
self.give_money()
|
|
@@ -1,74 +0,0 @@
|
|
|
1
|
-
from mesa.examples.basic.boltzmann_wealth_model.model import BoltzmannWealthModel
|
|
2
|
-
from mesa.visualization import (
|
|
3
|
-
SolaraViz,
|
|
4
|
-
make_plot_component,
|
|
5
|
-
make_space_component,
|
|
6
|
-
)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def agent_portrayal(agent):
|
|
10
|
-
color = agent.wealth # we are using a colormap to translate wealth to color
|
|
11
|
-
return {"color": color}
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
model_params = {
|
|
15
|
-
"n": {
|
|
16
|
-
"type": "SliderInt",
|
|
17
|
-
"value": 50,
|
|
18
|
-
"label": "Number of agents:",
|
|
19
|
-
"min": 10,
|
|
20
|
-
"max": 100,
|
|
21
|
-
"step": 1,
|
|
22
|
-
},
|
|
23
|
-
"seed": {
|
|
24
|
-
"type": "InputText",
|
|
25
|
-
"value": 42,
|
|
26
|
-
"label": "Random Seed",
|
|
27
|
-
},
|
|
28
|
-
"width": 10,
|
|
29
|
-
"height": 10,
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def post_process(ax):
|
|
34
|
-
ax.get_figure().colorbar(ax.collections[0], label="wealth", ax=ax)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
# Create initial model instance
|
|
38
|
-
model = BoltzmannWealthModel(50, 10, 10)
|
|
39
|
-
|
|
40
|
-
# Create visualization elements. The visualization elements are solara components
|
|
41
|
-
# that receive the model instance as a "prop" and display it in a certain way.
|
|
42
|
-
# Under the hood these are just classes that receive the model instance.
|
|
43
|
-
# You can also author your own visualization elements, which can also be functions
|
|
44
|
-
# that receive the model instance and return a valid solara component.
|
|
45
|
-
|
|
46
|
-
SpaceGraph = make_space_component(
|
|
47
|
-
agent_portrayal, cmap="viridis", vmin=0, vmax=10, post_process=post_process
|
|
48
|
-
)
|
|
49
|
-
GiniPlot = make_plot_component("Gini")
|
|
50
|
-
|
|
51
|
-
# Create the SolaraViz page. This will automatically create a server and display the
|
|
52
|
-
# visualization elements in a web browser.
|
|
53
|
-
# Display it using the following command in the example directory:
|
|
54
|
-
# solara run app.py
|
|
55
|
-
# It will automatically update and display any changes made to this file
|
|
56
|
-
page = SolaraViz(
|
|
57
|
-
model,
|
|
58
|
-
components=[SpaceGraph, GiniPlot],
|
|
59
|
-
model_params=model_params,
|
|
60
|
-
name="Boltzmann Wealth Model",
|
|
61
|
-
)
|
|
62
|
-
page # noqa
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
# In a notebook environment, we can also display the visualization elements directly
|
|
66
|
-
# SpaceGraph(model1)
|
|
67
|
-
# GiniPlot(model1)
|
|
68
|
-
|
|
69
|
-
# The plots will be static. If you want to pick up model steps,
|
|
70
|
-
# you have to make the model reactive first
|
|
71
|
-
# reactive_model = solara.reactive(model1)
|
|
72
|
-
# SpaceGraph(reactive_model)
|
|
73
|
-
# In a different notebook block:
|
|
74
|
-
# reactive_model.value.step()
|
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
import mesa
|
|
2
|
-
from mesa.examples.basic.boltzmann_wealth_model.agents import MoneyAgent
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
class BoltzmannWealthModel(mesa.Model):
|
|
6
|
-
"""A simple model of an economy where agents exchange currency at random.
|
|
7
|
-
|
|
8
|
-
All the agents begin with one unit of currency, and each time step can give
|
|
9
|
-
a unit of currency to another agent. Note how, over time, this produces a
|
|
10
|
-
highly skewed distribution of wealth.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
def __init__(self, n=100, width=10, height=10, seed=None):
|
|
14
|
-
super().__init__(seed=seed)
|
|
15
|
-
self.num_agents = n
|
|
16
|
-
self.grid = mesa.space.MultiGrid(width, height, True)
|
|
17
|
-
|
|
18
|
-
self.datacollector = mesa.DataCollector(
|
|
19
|
-
model_reporters={"Gini": self.compute_gini},
|
|
20
|
-
agent_reporters={"Wealth": "wealth"},
|
|
21
|
-
)
|
|
22
|
-
# Create agents
|
|
23
|
-
for _ in range(self.num_agents):
|
|
24
|
-
a = MoneyAgent(self)
|
|
25
|
-
|
|
26
|
-
# Add the agent to a random grid cell
|
|
27
|
-
x = self.random.randrange(self.grid.width)
|
|
28
|
-
y = self.random.randrange(self.grid.height)
|
|
29
|
-
self.grid.place_agent(a, (x, y))
|
|
30
|
-
|
|
31
|
-
self.running = True
|
|
32
|
-
self.datacollector.collect(self)
|
|
33
|
-
|
|
34
|
-
def step(self):
|
|
35
|
-
self.agents.shuffle_do("step")
|
|
36
|
-
self.datacollector.collect(self)
|
|
37
|
-
|
|
38
|
-
def compute_gini(self):
|
|
39
|
-
agent_wealths = [agent.wealth for agent in self.agents]
|
|
40
|
-
x = sorted(agent_wealths)
|
|
41
|
-
n = self.num_agents
|
|
42
|
-
b = sum(xi * (n - i) for i, xi in enumerate(x)) / (n * sum(x))
|
|
43
|
-
return 1 + (1 / n) - 2 * b
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# Run with streamlit run st_app.py
|
|
2
|
-
|
|
3
|
-
import time
|
|
4
|
-
|
|
5
|
-
import altair as alt
|
|
6
|
-
import pandas as pd
|
|
7
|
-
import streamlit as st
|
|
8
|
-
from model import BoltzmannWealthModel
|
|
9
|
-
|
|
10
|
-
model = st.title("Boltzman Wealth Model")
|
|
11
|
-
num_agents = st.slider(
|
|
12
|
-
"Choose how many agents to include in the model",
|
|
13
|
-
min_value=1,
|
|
14
|
-
max_value=100,
|
|
15
|
-
value=50,
|
|
16
|
-
)
|
|
17
|
-
num_ticks = st.slider(
|
|
18
|
-
"Select number of Simulation Runs", min_value=1, max_value=100, value=50
|
|
19
|
-
)
|
|
20
|
-
height = st.slider("Select Grid Height", min_value=10, max_value=100, step=10, value=15)
|
|
21
|
-
width = st.slider("Select Grid Width", min_value=10, max_value=100, step=10, value=20)
|
|
22
|
-
model = BoltzmannWealthModel(num_agents, height, width)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
status_text = st.empty()
|
|
26
|
-
run = st.button("Run Simulation")
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
if run:
|
|
30
|
-
tick = time.time()
|
|
31
|
-
step = 0
|
|
32
|
-
# init grid
|
|
33
|
-
df_grid = pd.DataFrame()
|
|
34
|
-
df_gini = pd.DataFrame({"step": [0], "gini": [-1]})
|
|
35
|
-
for x in range(width):
|
|
36
|
-
for y in range(height):
|
|
37
|
-
df_grid = pd.concat(
|
|
38
|
-
[df_grid, pd.DataFrame({"x": [x], "y": [y], "agent_count": 0})],
|
|
39
|
-
ignore_index=True,
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
heatmap = (
|
|
43
|
-
alt.Chart(df_grid)
|
|
44
|
-
.mark_point(size=100)
|
|
45
|
-
.encode(x="x", y="y", color=alt.Color("agent_count"))
|
|
46
|
-
.interactive()
|
|
47
|
-
.properties(width=800, height=600)
|
|
48
|
-
)
|
|
49
|
-
|
|
50
|
-
line = (
|
|
51
|
-
alt.Chart(df_gini)
|
|
52
|
-
.mark_line(point=True)
|
|
53
|
-
.encode(x="step", y="gini")
|
|
54
|
-
.properties(width=800, height=600)
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
# init progress bar
|
|
58
|
-
my_bar = st.progress(0, text="Simulation Progress") # progress
|
|
59
|
-
placeholder = st.empty()
|
|
60
|
-
st.subheader("Agent Grid")
|
|
61
|
-
chart = st.altair_chart(heatmap)
|
|
62
|
-
st.subheader("Gini Values")
|
|
63
|
-
line_chart = st.altair_chart(line)
|
|
64
|
-
|
|
65
|
-
color_scale = alt.Scale(
|
|
66
|
-
domain=[0, 1, 2, 3, 4], range=["red", "cyan", "white", "white", "blue"]
|
|
67
|
-
)
|
|
68
|
-
for i in range(num_ticks):
|
|
69
|
-
model.step()
|
|
70
|
-
my_bar.progress((i / num_ticks), text="Simulation progress")
|
|
71
|
-
placeholder.text("Step = %d" % i)
|
|
72
|
-
for cell in model.grid.coord_iter():
|
|
73
|
-
cell_content, (x, y) = cell
|
|
74
|
-
agent_count = len(cell_content)
|
|
75
|
-
selected_row = df_grid[(df_grid["x"] == x) & (df_grid["y"] == y)]
|
|
76
|
-
df_grid.loc[selected_row.index, "agent_count"] = (
|
|
77
|
-
agent_count # random.choice([1,2])
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
df_gini = pd.concat(
|
|
81
|
-
[
|
|
82
|
-
df_gini,
|
|
83
|
-
pd.DataFrame(
|
|
84
|
-
{"step": [i], "gini": [model.datacollector.model_vars["Gini"][i]]}
|
|
85
|
-
),
|
|
86
|
-
]
|
|
87
|
-
)
|
|
88
|
-
# st.table(df_grid)
|
|
89
|
-
heatmap = (
|
|
90
|
-
alt.Chart(df_grid)
|
|
91
|
-
.mark_circle(size=100)
|
|
92
|
-
.encode(x="x", y="y", color=alt.Color("agent_count", scale=color_scale))
|
|
93
|
-
.interactive()
|
|
94
|
-
.properties(width=800, height=600)
|
|
95
|
-
)
|
|
96
|
-
chart.altair_chart(heatmap)
|
|
97
|
-
|
|
98
|
-
line = (
|
|
99
|
-
alt.Chart(df_gini)
|
|
100
|
-
.mark_line(point=True)
|
|
101
|
-
.encode(x="step", y="gini")
|
|
102
|
-
.properties(width=800, height=600)
|
|
103
|
-
)
|
|
104
|
-
line_chart.altair_chart(line)
|
|
105
|
-
|
|
106
|
-
time.sleep(0.01)
|
|
107
|
-
|
|
108
|
-
tock = time.time()
|
|
109
|
-
st.success(f"Simulation completed in {tock - tick:.2f} secs")
|
|
110
|
-
|
|
111
|
-
# st.subheader('Agent Grid')
|
|
112
|
-
# fig = px.imshow(agent_counts,labels={'color':'Agent Count'})
|
|
113
|
-
# st.plotly_chart(fig)
|
|
114
|
-
# st.subheader('Gini value over sim ticks (Plotly)')
|
|
115
|
-
# chart = st.line_chart(model.datacollector.model_vars['Gini'])
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# Conway's Game Of "Life"
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
|
|
5
|
-
[The Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life), also known simply as "Life", is a cellular automaton devised by the British mathematician John Horton Conway in 1970.
|
|
6
|
-
|
|
7
|
-
The "game" is a zero-player game, meaning that its evolution is determined by its initial state, requiring no further input by a human. One interacts with the Game of "Life" by creating an initial configuration and observing how it evolves, or, for advanced "players", by creating patterns with particular properties.
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
## How to Run
|
|
11
|
-
|
|
12
|
-
To run the model interactively you can use either the streamlit or solara version. For solara, you use
|
|
13
|
-
|
|
14
|
-
```
|
|
15
|
-
$ solara run app.py
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
For streamlit, you need
|
|
19
|
-
|
|
20
|
-
```
|
|
21
|
-
$ streamlit run st_app.py
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
This will open your browser and show you the controls. You can start the model by hitting the run button.
|
|
25
|
-
|
|
26
|
-
## Files
|
|
27
|
-
|
|
28
|
-
* ``agents.py``: Defines the behavior of an individual cell, which can be in two states: DEAD or ALIVE.
|
|
29
|
-
* ``model.py``: Defines the model itself, initialized with a random configuration of alive and dead cells.
|
|
30
|
-
* ``app.py``: Defines an interactive visualization using solara.
|
|
31
|
-
* ``st_app.py``: Defines an interactive visualization using Streamlit.
|
|
32
|
-
|
|
33
|
-
## Optional
|
|
34
|
-
|
|
35
|
-
* For the streamlit version, you need to have streamlit installed (can be done via pip install streamlit)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
## Further Reading
|
|
39
|
-
[Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life)
|
|
File without changes
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
from mesa import Agent
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
class Cell(Agent):
|
|
5
|
-
"""Represents a single ALIVE or DEAD cell in the simulation."""
|
|
6
|
-
|
|
7
|
-
DEAD = 0
|
|
8
|
-
ALIVE = 1
|
|
9
|
-
|
|
10
|
-
def __init__(self, pos, model, init_state=DEAD):
|
|
11
|
-
"""Create a cell, in the given state, at the given x, y position."""
|
|
12
|
-
super().__init__(model)
|
|
13
|
-
self.x, self.y = pos
|
|
14
|
-
self.state = init_state
|
|
15
|
-
self._next_state = None
|
|
16
|
-
|
|
17
|
-
@property
|
|
18
|
-
def is_alive(self):
|
|
19
|
-
return self.state == self.ALIVE
|
|
20
|
-
|
|
21
|
-
@property
|
|
22
|
-
def neighbors(self):
|
|
23
|
-
return self.model.grid.iter_neighbors((self.x, self.y), True)
|
|
24
|
-
|
|
25
|
-
def determine_state(self):
|
|
26
|
-
"""Compute if the cell will be dead or alive at the next tick. This is
|
|
27
|
-
based on the number of alive or dead neighbors. The state is not
|
|
28
|
-
changed here, but is just computed and stored in self._nextState,
|
|
29
|
-
because our current state may still be necessary for our neighbors
|
|
30
|
-
to calculate their next state.
|
|
31
|
-
"""
|
|
32
|
-
# Get the neighbors and apply the rules on whether to be alive or dead
|
|
33
|
-
# at the next tick.
|
|
34
|
-
live_neighbors = sum(neighbor.is_alive for neighbor in self.neighbors)
|
|
35
|
-
|
|
36
|
-
# Assume nextState is unchanged, unless changed below.
|
|
37
|
-
self._next_state = self.state
|
|
38
|
-
if self.is_alive:
|
|
39
|
-
if live_neighbors < 2 or live_neighbors > 3:
|
|
40
|
-
self._next_state = self.DEAD
|
|
41
|
-
else:
|
|
42
|
-
if live_neighbors == 3:
|
|
43
|
-
self._next_state = self.ALIVE
|
|
44
|
-
|
|
45
|
-
def assume_state(self):
|
|
46
|
-
"""Set the state to the new computed state -- computed in step()."""
|
|
47
|
-
self.state = self._next_state
|
|
@@ -1,51 +0,0 @@
|
|
|
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
|
|
@@ -1,31 +0,0 @@
|
|
|
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")
|
|
@@ -1,72 +0,0 @@
|
|
|
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")
|
|
@@ -1,40 +0,0 @@
|
|
|
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
|
|
@@ -1,26 +0,0 @@
|
|
|
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
|