Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fixed consistent capitalization of files.
  • Loading branch information
ItsQuinnMoore committed Apr 24, 2023
commit 13b54860b3e54595a3229ae4d879c0a8009894fe
57 changes: 57 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Example Code
This directory contains example models meant to test and demonstrate Mesa's features, and provide demonstrations for how to build and analyze agent-based models. For more information on each model, see its own Readme and documentation.

## Models

Classic models, some of which can be found in NetLogo's/MASON's example models.

### bank_reserves
A highly abstracted, simplified model of an economy, with only one type of agent and a single bank representing all banks in an economy.

### color_patches
A cellular automaton model where agents opinions are influenced by that of their neighbors. As the model evolves, color patches representing the prevailing opinion in a given area expand, contract, and sometimes disappear.

### conways_game_of_life
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.

### epstein_civil_violence
Joshua Epstein's [model](http://www.uvm.edu/~pdodds/files/papers/others/2002/epstein2002a.pdf) of how a decentralized uprising can be suppressed or reach a critical mass of support.

### boid_flockers
[Boids](https://en.wikipedia.org/wiki/Boids)-style flocking model, demonstrating the use of agents moving through a continuous space following direction vectors.

### forest_fire
Simple cellular automata of a fire spreading through a forest of cells on a grid, based on the NetLogo [Fire model](http://ccl.northwestern.edu/netlogo/models/Fire).

### hex_snowflake
Conway's game of life on a hexagonal grid.

### pd_grid
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.

### schelling (GUI and Text)
Mesa implementation of the classic [Schelling segregation model](http://nifty.stanford.edu/2014/mccown-schelling-model-segregation/).

### boltzmann_wealth_model
Completed code to go along with the [tutorial]() on making a simple model of how a highly-skewed wealth distribution can emerge from simple rules.

### wolf_sheep
Implementation of an ecological model of predation and reproduction, based on the NetLogo [Wolf Sheep Predation model](http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation).

### sugarscape_cg
Implementation of Sugarscape 2 Constant Growback model, based on the Netlogo
[Sugarscape 2 Constant Growback](http://ccl.northwestern.edu/netlogo/models/Sugarscape2ConstantGrowback)

### virus_on_network
This model is based on the NetLogo model "Virus on Network".

## Feature examples

Example models specifically for demonstrating Mesa's features.

### charts

A modified version of the "bank_reserves" example made to provide examples of mesa's charting tools.

### Shape Example
Example of grid display and direction showing agents in the form of arrow-head shape.
39 changes: 39 additions & 0 deletions examples/boltzmann_wealth_model/Readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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).

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 examples, launch the Jupyter Notebook and run the code in ``Introduction to Mesa Tutorial Code.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 viz_money_model.py
```

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

* ``Introduction to Mesa Tutorial Code.ipynb``: Jupyter Notebook with all the steps as described in the tutorial.
* ``money_model.py``: Final version of the model.
* ``viz_money_model.py``: Creates and launches interactive visualization.

## 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)
____
You will need to open the file as a Jupyter (aka iPython) notebook with an iPython 3 kernel. Required dependencies are listed in the provided `requirements.txt` file which can be installed by running `pip install -r requirements.txt`
Empty file.
73 changes: 73 additions & 0 deletions examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
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])
if len(cellmates) > 1:
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 examples/boltzmann_wealth_model/boltzmann_wealth_model/server.py
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
4 changes: 4 additions & 0 deletions examples/boltzmann_wealth_model/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
jupyter
matplotlib
mesa~=1.1
numpy
3 changes: 3 additions & 0 deletions examples/boltzmann_wealth_model/run.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from boltzmann_wealth_model.server import server

server.launch()
231 changes: 231 additions & 0 deletions examples/pd_grid/analysis.ipynb

Large diffs are not rendered by default.

Empty file.
49 changes: 49 additions & 0 deletions examples/pd_grid/pd_grid/agent.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import mesa


class PDAgent(mesa.Agent):
"""Agent member of the iterated, spatial prisoner's dilemma model."""

def __init__(self, pos, model, starting_move=None):
"""
Create a new Prisoner's Dilemma agent.

Args:
pos: (x, y) tuple of the agent's position.
model: model instance
starting_move: If provided, determines the agent's initial state:
C(ooperating) or D(efecting). Otherwise, random.
"""
super().__init__(pos, model)
self.pos = pos
self.score = 0
if starting_move:
self.move = starting_move
else:
self.move = self.random.choice(["C", "D"])
self.next_move = None

@property
def isCooroperating(self):
return self.move == "C"

def step(self):
"""Get the best neighbor's move, and change own move accordingly if better than own score."""
neighbors = self.model.grid.get_neighbors(self.pos, True, include_center=True)
best_neighbor = max(neighbors, key=lambda a: a.score)
self.next_move = best_neighbor.move

if self.model.schedule_type != "Simultaneous":
self.advance()

def advance(self):
self.move = self.next_move
self.score += self.increment_score()

def increment_score(self):
neighbors = self.model.grid.get_neighbors(self.pos, True)
if self.model.schedule_type == "Simultaneous":
moves = [neighbor.next_move for neighbor in neighbors]
else:
moves = [neighbor.move for neighbor in neighbors]
return sum(self.model.payoff[(self.move, move)] for move in moves)
62 changes: 62 additions & 0 deletions examples/pd_grid/pd_grid/model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import mesa

from .agent import PDAgent


class PdGrid(mesa.Model):
"""Model class for iterated, spatial prisoner's dilemma model."""

schedule_types = {
"Sequential": mesa.time.BaseScheduler,
"Random": mesa.time.RandomActivation,
"Simultaneous": mesa.time.SimultaneousActivation,
}

# This dictionary holds the payoff for this agent,
# keyed on: (my_move, other_move)

payoff = {("C", "C"): 1, ("C", "D"): 0, ("D", "C"): 1.6, ("D", "D"): 0}

def __init__(
self, width=50, height=50, schedule_type="Random", payoffs=None, seed=None
):
"""
Create a new Spatial Prisoners' Dilemma Model.

Args:
width, height: Grid size. There will be one agent per grid cell.
schedule_type: Can be "Sequential", "Random", or "Simultaneous".
Determines the agent activation regime.
payoffs: (optional) Dictionary of (move, neighbor_move) payoffs.
"""
self.grid = mesa.space.SingleGrid(width, height, torus=True)
self.schedule_type = schedule_type
self.schedule = self.schedule_types[self.schedule_type](self)

# Create agents
for x in range(width):
for y in range(height):
agent = PDAgent((x, y), self)
self.grid.place_agent(agent, (x, y))
self.schedule.add(agent)

self.datacollector = mesa.DataCollector(
{
"Cooperating_Agents": lambda m: len(
[a for a in m.schedule.agents if a.move == "C"]
)
}
)

self.running = True
self.datacollector.collect(self)

def step(self):
self.schedule.step()
# collect data
self.datacollector.collect(self)

def run(self, n):
"""Run the model for n steps."""
for _ in range(n):
self.step()
19 changes: 19 additions & 0 deletions examples/pd_grid/pd_grid/portrayal.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
def portrayPDAgent(agent):
"""
This function is registered with the visualization server to be called
each tick to indicate how to draw the agent in its current state.
:param agent: the agent in the simulation
:return: the portrayal dictionary
"""
if agent is None:
raise AssertionError
return {
"Shape": "rect",
"w": 1,
"h": 1,
"Filled": "true",
"Layer": 0,
"x": agent.pos[0],
"y": agent.pos[1],
"Color": "blue" if agent.isCooroperating else "red",
}
22 changes: 22 additions & 0 deletions examples/pd_grid/pd_grid/server.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import mesa

from .portrayal import portrayPDAgent
from .model import PdGrid


# Make a world that is 50x50, on a 500x500 display.
canvas_element = mesa.visualization.CanvasGrid(portrayPDAgent, 50, 50, 500, 500)

model_params = {
"height": 50,
"width": 50,
"schedule_type": mesa.visualization.Choice(
"Scheduler type",
value="Random",
choices=list(PdGrid.schedule_types.keys()),
),
}

server = mesa.visualization.ModularServer(
PdGrid, [canvas_element], "Prisoner's Dilemma", model_params
)
Loading