-
Notifications
You must be signed in to change notification settings - Fork 169
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add back original boltzmann and schelling
- Loading branch information
Showing
16 changed files
with
976 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# Boltzmann Wealth Model (Tutorial) | ||
|
||
## Summary | ||
|
||
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. | ||
|
||
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. | ||
|
||
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. | ||
|
||
## How to Run | ||
|
||
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) | ||
|
||
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: | ||
|
||
``` | ||
$ python server.py | ||
``` | ||
|
||
Make sure to install the requirements first: | ||
|
||
``` | ||
pip install -r requirements.txt | ||
``` | ||
|
||
If your browser doesn't open automatically, point it to [http://127.0.0.1:8521/](http://127.0.0.1:8521/). When the visualization loads, press Reset, then Run. | ||
|
||
|
||
## Files | ||
|
||
* ``boltzmann_wealth_model/model.py``: Final version of the model. | ||
* ``boltzmann_wealth_model/server.py``: Code for the interactive visualization. | ||
* ``run.py``: Launches the server. | ||
|
||
## Optional | ||
|
||
* ``boltzmann_wealth_model/app.py``: can be used to run the simulation via the streamlit interface. | ||
* For this some additional packages like ``streamlit`` and ``altair`` needs to be installed. | ||
* Once installed, the app can be opened in the browser using : ``streamlit run app.py`` | ||
|
||
## Further Reading | ||
|
||
The full tutorial describing how the model is built can be found at: | ||
https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html | ||
|
||
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: | ||
|
||
[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) | ||
|
||
[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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
import time | ||
|
||
import altair as alt | ||
import pandas as pd | ||
import streamlit as st | ||
from boltzmann_wealth_model.model import BoltzmannWealthModel | ||
|
||
model = st.title("Boltzman Wealth Model") | ||
num_agents = st.slider( | ||
"Choose how many agents to include in the model", | ||
min_value=1, | ||
max_value=100, | ||
value=50, | ||
) | ||
num_ticks = st.slider( | ||
"Select number of Simulation Runs", min_value=1, max_value=100, value=50 | ||
) | ||
height = st.slider("Select Grid Height", min_value=10, max_value=100, step=10, value=15) | ||
width = st.slider("Select Grid Width", min_value=10, max_value=100, step=10, value=20) | ||
model = BoltzmannWealthModel(num_agents, height, width) | ||
|
||
|
||
status_text = st.empty() | ||
run = st.button("Run Simulation") | ||
|
||
|
||
if run: | ||
tick = time.time() | ||
step = 0 | ||
# init grid | ||
df_grid = pd.DataFrame() | ||
df_gini = pd.DataFrame({"step": [0], "gini": [-1]}) | ||
for x in range(width): | ||
for y in range(height): | ||
df_grid = pd.concat( | ||
[df_grid, pd.DataFrame({"x": [x], "y": [y], "agent_count": 0})], | ||
ignore_index=True, | ||
) | ||
|
||
heatmap = ( | ||
alt.Chart(df_grid) | ||
.mark_point(size=100) | ||
.encode(x="x", y="y", color=alt.Color("agent_count")) | ||
.interactive() | ||
.properties(width=800, height=600) | ||
) | ||
|
||
line = ( | ||
alt.Chart(df_gini) | ||
.mark_line(point=True) | ||
.encode(x="step", y="gini") | ||
.properties(width=800, height=600) | ||
) | ||
|
||
# init progress bar | ||
my_bar = st.progress(0, text="Simulation Progress") # progress | ||
placeholder = st.empty() | ||
st.subheader("Agent Grid") | ||
chart = st.altair_chart(heatmap) | ||
st.subheader("Gini Values") | ||
line_chart = st.altair_chart(line) | ||
|
||
color_scale = alt.Scale( | ||
domain=[0, 1, 2, 3, 4], range=["red", "cyan", "white", "white", "blue"] | ||
) | ||
for i in range(num_ticks): | ||
model.step() | ||
my_bar.progress((i / num_ticks), text="Simulation progress") | ||
placeholder.text("Step = %d" % i) | ||
for cell in model.grid.coord_iter(): | ||
cell_content, x, y = cell | ||
agent_count = len(cell_content) | ||
selected_row = df_grid[(df_grid["x"] == x) & (df_grid["y"] == y)] | ||
df_grid.loc[ | ||
selected_row.index, "agent_count" | ||
] = agent_count # random.choice([1,2]) | ||
|
||
df_gini = pd.concat( | ||
[ | ||
df_gini, | ||
pd.DataFrame( | ||
{"step": [i], "gini": [model.datacollector.model_vars["Gini"][i]]} | ||
), | ||
] | ||
) | ||
# st.table(df_grid) | ||
heatmap = ( | ||
alt.Chart(df_grid) | ||
.mark_circle(size=100) | ||
.encode(x="x", y="y", color=alt.Color("agent_count", scale=color_scale)) | ||
.interactive() | ||
.properties(width=800, height=600) | ||
) | ||
chart.altair_chart(heatmap) | ||
|
||
line = ( | ||
alt.Chart(df_gini) | ||
.mark_line(point=True) | ||
.encode(x="step", y="gini") | ||
.properties(width=800, height=600) | ||
) | ||
line_chart.altair_chart(line) | ||
|
||
time.sleep(0.01) | ||
|
||
tock = time.time() | ||
st.success(f"Simulation completed in {tock - tick:.2f} secs") | ||
|
||
# st.subheader('Agent Grid') | ||
# fig = px.imshow(agent_counts,labels={'color':'Agent Count'}) | ||
# st.plotly_chart(fig) | ||
# st.subheader('Gini value over sim ticks (Plotly)') | ||
# chart = st.line_chart(model.datacollector.model_vars['Gini']) |
Empty file.
76 changes: 76 additions & 0 deletions
76
examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
import mesa | ||
|
||
|
||
def compute_gini(model): | ||
agent_wealths = [agent.wealth for agent in model.schedule.agents] | ||
x = sorted(agent_wealths) | ||
N = model.num_agents | ||
B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x)) | ||
return 1 + (1 / N) - 2 * B | ||
|
||
|
||
class BoltzmannWealthModel(mesa.Model): | ||
"""A simple model of an economy where agents exchange currency at random. | ||
All the agents begin with one unit of currency, and each time step can give | ||
a unit of currency to another agent. Note how, over time, this produces a | ||
highly skewed distribution of wealth. | ||
""" | ||
|
||
def __init__(self, N=100, width=10, height=10): | ||
self.num_agents = N | ||
self.grid = mesa.space.MultiGrid(width, height, True) | ||
self.schedule = mesa.time.RandomActivation(self) | ||
self.datacollector = mesa.DataCollector( | ||
model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} | ||
) | ||
# Create agents | ||
for i in range(self.num_agents): | ||
a = MoneyAgent(i, self) | ||
self.schedule.add(a) | ||
# Add the agent to a random grid cell | ||
x = self.random.randrange(self.grid.width) | ||
y = self.random.randrange(self.grid.height) | ||
self.grid.place_agent(a, (x, y)) | ||
|
||
self.running = True | ||
self.datacollector.collect(self) | ||
|
||
def step(self): | ||
self.schedule.step() | ||
# collect data | ||
self.datacollector.collect(self) | ||
|
||
def run_model(self, n): | ||
for i in range(n): | ||
self.step() | ||
|
||
|
||
class MoneyAgent(mesa.Agent): | ||
"""An agent with fixed initial wealth.""" | ||
|
||
def __init__(self, unique_id, model): | ||
super().__init__(unique_id, model) | ||
self.wealth = 1 | ||
|
||
def move(self): | ||
possible_steps = self.model.grid.get_neighborhood( | ||
self.pos, moore=True, include_center=False | ||
) | ||
new_position = self.random.choice(possible_steps) | ||
self.model.grid.move_agent(self, new_position) | ||
|
||
def give_money(self): | ||
cellmates = self.model.grid.get_cell_list_contents([self.pos]) | ||
cellmates.pop( | ||
cellmates.index(self) | ||
) # Ensure agent is not giving money to itself | ||
if len(cellmates) > 0: | ||
other = self.random.choice(cellmates) | ||
other.wealth += 1 | ||
self.wealth -= 1 | ||
|
||
def step(self): | ||
self.move() | ||
if self.wealth > 0: | ||
self.give_money() |
40 changes: 40 additions & 0 deletions
40
examples/boltzmann_wealth_model/boltzmann_wealth_model/server.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
import mesa | ||
|
||
from .model import BoltzmannWealthModel | ||
|
||
|
||
def agent_portrayal(agent): | ||
portrayal = {"Shape": "circle", "Filled": "true", "r": 0.5} | ||
|
||
if agent.wealth > 0: | ||
portrayal["Color"] = "red" | ||
portrayal["Layer"] = 0 | ||
else: | ||
portrayal["Color"] = "grey" | ||
portrayal["Layer"] = 1 | ||
portrayal["r"] = 0.2 | ||
return portrayal | ||
|
||
|
||
grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500) | ||
chart = mesa.visualization.ChartModule( | ||
[{"Label": "Gini", "Color": "#0000FF"}], data_collector_name="datacollector" | ||
) | ||
|
||
model_params = { | ||
"N": mesa.visualization.Slider( | ||
"Number of agents", | ||
100, | ||
2, | ||
200, | ||
1, | ||
description="Choose how many agents to include in the model", | ||
), | ||
"width": 10, | ||
"height": 10, | ||
} | ||
|
||
server = mesa.visualization.ModularServer( | ||
BoltzmannWealthModel, [grid, chart], "Money Model", model_params | ||
) | ||
server.port = 8521 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
mesa~=1.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from boltzmann_wealth_model.server import server | ||
|
||
server.launch(open_browser=True) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
# Schelling Segregation Model | ||
|
||
## Summary | ||
|
||
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. | ||
|
||
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. | ||
|
||
## Installation | ||
|
||
To install the dependencies use pip and the requirements.txt in this directory. e.g. | ||
|
||
``` | ||
$ pip install -r requirements.txt | ||
``` | ||
|
||
## How to Run | ||
|
||
To run the model interactively, run ``mesa runserver`` in this directory. e.g. | ||
|
||
``` | ||
$ mesa runserver | ||
``` | ||
|
||
Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run. | ||
|
||
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/). | ||
|
||
## How to Run without the GUI | ||
|
||
To run the model with the grid displayed as an ASCII text, run `python run_ascii.py` in this directory. | ||
|
||
## Files | ||
|
||
* ``run.py``: Launches a model visualization server. | ||
* ``run_ascii.py``: Run the model in text mode. | ||
* ``schelling.py``: Contains the agent class, and the overall model class. | ||
* ``server.py``: Defines classes for visualizing the model in the browser via Mesa's modular server, and instantiates a visualization server. | ||
* ``analysis.ipynb``: Notebook demonstrating how to run experiments and parameter sweeps on the model. | ||
|
||
## Further Reading | ||
|
||
Schelling's original paper describing the model: | ||
|
||
[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) | ||
|
||
An interactive, browser-based explanation and implementation: | ||
|
||
[Parable of the Polygons](http://ncase.me/polygons/), by Vi Hart and Nicky Case. |
Empty file.
Oops, something went wrong.