From 84ec04465ffd1a06d482bb93d786d66f9814e085 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Mon, 31 Oct 2022 04:37:37 +0100
Subject: [PATCH 001/214] SingleGrid: Remove extraneous attribute declaration
(empties) (#1491)
---
mesa/space.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index 362099671a2..42e8b6777ef 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -488,8 +488,6 @@ def exists_empty_cells(self) -> bool:
class SingleGrid(Grid):
"""Grid where each cell contains exactly at most one object."""
- empties: set[Coordinate] = set()
-
def position_agent(
self, agent: Agent, x: int | str = "random", y: int | str = "random"
) -> None:
From 09b59f2038682e67963b01598cc62f2f1516de0d Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Mon, 31 Oct 2022 11:48:37 +0100
Subject: [PATCH 002/214] Refactor Grid.move_to_empty (#1482)
* Refactor Grid.move_to_empty
* Update space.py
---
mesa/space.py | 48 +++++++++++++++++-----------------------------
tests/test_grid.py | 2 --
2 files changed, 18 insertions(+), 32 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index 42e8b6777ef..ec7b4f468d0 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -56,11 +56,6 @@
F = TypeVar("F", bound=Callable[..., Any])
-def clamp(x: float, lowest: float, highest: float) -> float:
- # much faster than np.clip for a scalar x.
- return lowest if x <= lowest else (highest if x >= highest else x)
-
-
def accept_tuple_argument(wrapped_function: F) -> F:
"""Decorator to allow grid methods that take a list of (x, y) coord tuples
to also handle a single position, by automatically wrapping tuple in
@@ -103,6 +98,7 @@ def __init__(self, width: int, height: int, torus: bool) -> None:
self.height = height
self.width = width
self.torus = torus
+ self.num_cells = height * width
self.grid: list[list[GridContent]]
self.grid = [
@@ -422,32 +418,24 @@ def move_to_empty(
self, agent: Agent, cutoff: float = 0.998, num_agents: int | None = None
) -> None:
"""Moves agent to a random empty cell, vacating agent's old cell."""
- if len(self.empties) == 0:
+ if num_agents is not None:
+ warn(
+ (
+ "`num_agents` is being deprecated since it's no longer used "
+ "inside `move_to_empty`. It shouldn't be passed as a parameter."
+ ),
+ DeprecationWarning,
+ )
+ num_empty_cells = len(self.empties)
+ if num_empty_cells == 0:
raise Exception("ERROR: No empty cells")
- if num_agents is None:
- try:
- num_agents = agent.model.schedule.get_agent_count()
- except AttributeError:
- raise Exception(
- "Your agent is not attached to a model, and so Mesa is unable\n"
- "to figure out the total number of agents you have created.\n"
- "This number is required in order to calculate the threshold\n"
- "for using a much faster algorithm to find an empty cell.\n"
- "In this case, you must specify `num_agents`."
- )
- new_pos = (0, 0) # Initialize it with a starting value.
- # This method is based on Agents.jl's random_empty() implementation.
- # See https://github.com/JuliaDynamics/Agents.jl/pull/541.
- # For the discussion, see
- # https://github.com/projectmesa/mesa/issues/1052.
- # This switch assumes the worst case (for this algorithm) of one
- # agent per position, which is not true in general but is appropriate
- # here.
- if clamp(num_agents / (self.width * self.height), 0.0, 1.0) < cutoff:
- # The default cutoff value provided is the break-even comparison
- # with the time taken in the else branching point.
- # The number is measured to be 0.998 in Agents.jl, but since Mesa
- # run under different environment, the number is different here.
+
+ # This method is based on Agents.jl's random_empty() implementation. See
+ # https://github.com/JuliaDynamics/Agents.jl/pull/541. For the discussion, see
+ # https://github.com/projectmesa/mesa/issues/1052. The default cutoff value
+ # provided is the break-even comparison with the time taken in the else
+ # branching point.
+ if 1 - num_empty_cells / self.num_cells < cutoff:
while True:
new_pos = (
agent.random.randrange(self.width),
diff --git a/tests/test_grid.py b/tests/test_grid.py
index 3104ba2e68e..64e93a448f9 100644
--- a/tests/test_grid.py
+++ b/tests/test_grid.py
@@ -251,8 +251,6 @@ def test_enforcement(self, mock_model):
assert a.pos not in self.grid.empties
assert len(self.grid.empties) == 8
for i in range(10):
- # Since the agents and the grid are not associated with a model, we
- # must explicitly tell move_to_empty the number of agents.
self.grid.move_to_empty(a, num_agents=self.num_agents)
assert len(self.grid.empties) == 8
From 2efcb0dd9bc97e654fd4292d0ebc45e4047496ab Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Tue, 1 Nov 2022 00:56:19 +0100
Subject: [PATCH 003/214] Remove extraneous spaces from docstrings in modules
(#1493)
---
mesa/__init__.py | 1 -
mesa/agent.py | 1 -
mesa/batchrunner.py | 5 -----
mesa/datacollection.py | 9 ---------
mesa/model.py | 3 ---
mesa/space.py | 2 --
mesa/time.py | 8 --------
mesa/visualization/ModularVisualization.py | 4 ----
mesa/visualization/TextVisualization.py | 3 ---
mesa/visualization/__init__.py | 1 -
10 files changed, 37 deletions(-)
diff --git a/mesa/__init__.py b/mesa/__init__.py
index 1371a90032b..56bf8a310e7 100644
--- a/mesa/__init__.py
+++ b/mesa/__init__.py
@@ -2,7 +2,6 @@
Mesa Agent-Based Modeling Framework
Core Objects: Model, and Agent.
-
"""
import datetime
diff --git a/mesa/agent.py b/mesa/agent.py
index a3daf7cf612..51c2f1fe32c 100644
--- a/mesa/agent.py
+++ b/mesa/agent.py
@@ -2,7 +2,6 @@
The agent class for Mesa framework.
Core Objects: Agent
-
"""
# Mypy; for the `|` operator purpose
# Remove this __future__ import once the oldest supported Python is 3.10
diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py
index 0f9d31c68a9..260223c784e 100644
--- a/mesa/batchrunner.py
+++ b/mesa/batchrunner.py
@@ -3,7 +3,6 @@
===========
A single class to manage a batch run or parameter sweep of a given model.
-
"""
import copy
import itertools
@@ -284,7 +283,6 @@ def __init__(
collected at the level of each agent present in the model at
the end of the run.
display_progress: Display progress bar with time estimation?
-
"""
self.model_cls = model_cls
if parameters_list is None:
@@ -393,7 +391,6 @@ def run_model(self, model):
If your model runs in a non-standard way, this is the method to modify
in your subclass.
-
"""
while model.running and model.schedule.steps < self.max_steps:
model.step()
@@ -536,7 +533,6 @@ class BatchRunner(FixedBatchRunner):
Note that by default, the reporters only collect data at the *end* of the
run. To get step by step data, simply have a reporter store the model's
entire DataCollector object.
-
"""
def __init__(
@@ -579,7 +575,6 @@ def __init__(
collected at the level of each agent present in the model at
the end of the run.
display_progress: Display progress bar with time estimation?
-
"""
warn(
"BatchRunner class has been replaced by batch_run function. Please see documentation.",
diff --git a/mesa/datacollection.py b/mesa/datacollection.py
index 695742ad33b..ba25c80eec0 100644
--- a/mesa/datacollection.py
+++ b/mesa/datacollection.py
@@ -33,7 +33,6 @@
* The model has a schedule object called 'schedule'
* The schedule has an agent list called agents
* For collecting agent-level variables, agents must have a unique_id
-
"""
from functools import partial
import itertools
@@ -50,7 +49,6 @@ class DataCollector:
functions which actually collect them. When the collect(...) method is
called, it collects these attributes and executes these functions one by
one and stores the results.
-
"""
def __init__(self, model_reporters=None, agent_reporters=None, tables=None):
@@ -91,7 +89,6 @@ class attributes of model
{"model_attribute": "model_attribute"}
functions with parameters that have placed in a list
{"Model_Function":[function, [param_1, param_2]]}
-
"""
self.model_reporters = {}
self.agent_reporters = {}
@@ -132,7 +129,6 @@ def _new_agent_reporter(self, name, reporter):
name: Name of the agent-level variable to collect.
reporter: Attribute string, or function object that returns the
variable when given a model instance.
-
"""
if type(reporter) is str:
attribute_name = reporter
@@ -146,7 +142,6 @@ def _new_table(self, table_name, table_columns):
Args:
table_name: Name of the new table.
table_columns: List of columns to add to the table.
-
"""
new_table = {column: [] for column in table_columns}
self.tables[table_name] = new_table
@@ -200,7 +195,6 @@ def add_table_row(self, table_name, row, ignore_missing=False):
row: A dictionary of the form {column_name: value...}
ignore_missing: If True, fill any missing columns with Nones;
if False, throw an error if any columns are missing
-
"""
if table_name not in self.tables:
raise Exception("Table does not exist.")
@@ -223,7 +217,6 @@ def get_model_vars_dataframe(self):
The DataFrame has one column for each model variable, and the index is
(implicitly) the model tick.
-
"""
return pd.DataFrame(self.model_vars)
@@ -232,7 +225,6 @@ def get_agent_vars_dataframe(self):
The DataFrame has one column for each variable, with two additional
columns for tick and agent_id.
-
"""
all_records = itertools.chain.from_iterable(self._agent_records.values())
rep_names = list(self.agent_reporters)
@@ -249,7 +241,6 @@ def get_table_dataframe(self, table_name):
Args:
table_name: The name of the table to convert.
-
"""
if table_name not in self.tables:
raise Exception("No such table.")
diff --git a/mesa/model.py b/mesa/model.py
index aba1db9a741..0851a4ea1d6 100644
--- a/mesa/model.py
+++ b/mesa/model.py
@@ -2,7 +2,6 @@
The model class for Mesa framework.
Core Objects: Model
-
"""
# Mypy; for the `|` operator purpose
# Remove this __future__ import once the oldest supported Python is 3.10
@@ -33,7 +32,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
Attributes:
schedule: schedule object
running: a bool indicating if the model should continue running
-
"""
self.running = True
@@ -43,7 +41,6 @@ def __init__(self, *args: Any, **kwargs: Any) -> None:
def run_model(self) -> None:
"""Run the model until the end condition is reached. Overload as
needed.
-
"""
while self.running:
self.step()
diff --git a/mesa/space.py b/mesa/space.py
index ec7b4f468d0..95e123d8e50 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -7,7 +7,6 @@
Grid: base grid, a simple list-of-lists.
SingleGrid: grid which strictly enforces one object per cell.
MultiGrid: extension to Grid where each cell is a set of objects.
-
"""
# Instruction for PyLint to suppress variable name errors, since we have a
# good reason to use one-character variable names for x and y.
@@ -576,7 +575,6 @@ def iter_cell_list_contents(
Returns:
A iterator of the contents of the cells identified in cell_list
-
"""
return itertools.chain.from_iterable(
self[x][y] for x, y in cell_list if not self.is_cell_empty((x, y))
diff --git a/mesa/time.py b/mesa/time.py
index 34fc8705fad..f9c84acf62f 100644
--- a/mesa/time.py
+++ b/mesa/time.py
@@ -45,7 +45,6 @@ class BaseScheduler:
Assumes that each agent added has a *step* method which takes no arguments.
(This is explicitly meant to replicate the scheduler in MASON).
-
"""
def __init__(self, model: Model) -> None:
@@ -61,7 +60,6 @@ def add(self, agent: Agent) -> None:
Args:
agent: An Agent to be added to the schedule. NOTE: The agent must
have a step() method.
-
"""
if agent.unique_id in self._agents:
@@ -76,7 +74,6 @@ def remove(self, agent: Agent) -> None:
Args:
agent: An agent object.
-
"""
del self._agents[agent.unique_id]
@@ -98,7 +95,6 @@ def agents(self) -> list[Agent]:
def agent_buffer(self, shuffled: bool = False) -> Iterator[Agent]:
"""Simple generator that yields the agents while letting the user
remove and/or add agents during stepping.
-
"""
agent_keys = self._agents.keys()
if shuffled:
@@ -118,7 +114,6 @@ class RandomActivation(BaseScheduler):
default behavior for an ABM.
Assumes that all agents have a step(model) method.
-
"""
def step(self) -> None:
@@ -138,7 +133,6 @@ class SimultaneousActivation(BaseScheduler):
This scheduler requires that each agent have two methods: step and advance.
step() activates the agent and stages any necessary changes, but does not
apply them yet. advance() then applies the changes.
-
"""
def step(self) -> None:
@@ -163,7 +157,6 @@ class StagedActivation(BaseScheduler):
This schedule tracks steps and time separately. Time advances in fractional
increments of 1 / (# of stages), meaning that 1 step = 1 unit of time.
-
"""
def __init__(
@@ -183,7 +176,6 @@ def __init__(
shuffle_between_stages: If True, shuffle the agents after each
stage; otherwise, only shuffle at the start
of each step.
-
"""
super().__init__(model)
self.stage_list = ["step"] if not stage_list else stage_list
diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py
index 0b49b3f6931..6345db2489d 100644
--- a/mesa/visualization/ModularVisualization.py
+++ b/mesa/visualization/ModularVisualization.py
@@ -93,7 +93,6 @@
{
"type": "get_params"
}
-
"""
import asyncio
import os
@@ -145,7 +144,6 @@ class VisualizationElement:
Methods:
render: Takes a model object, and produces JSON data which can be sent
to the client.
-
"""
package_includes = []
@@ -165,7 +163,6 @@ def render(self, model):
Returns:
A JSON-ready object.
-
"""
return "VisualizationElement goes here."
@@ -389,7 +386,6 @@ def reset_model(self):
def render_model(self):
"""Turn the current state of the model into a dictionary of
visualizations
-
"""
visualization_state = []
for element in self.visualization_elements:
diff --git a/mesa/visualization/TextVisualization.py b/mesa/visualization/TextVisualization.py
index 548ceb6d51b..79e68b2b950 100644
--- a/mesa/visualization/TextVisualization.py
+++ b/mesa/visualization/TextVisualization.py
@@ -24,7 +24,6 @@
is used so as to allow the method to access Agent internals, as well as to
potentially render a cell based on several values (e.g. an Agent grid and a
Patch value grid).
-
"""
# Pylint instructions: allow single-character variable names.
# pylint: disable=invalid-name
@@ -38,7 +37,6 @@ class TextVisualization:
model: The underlying model object to be visualized.
elements: List of visualization elements, which will be rendered
in the order they are added.
-
"""
def __init__(self, model):
@@ -97,7 +95,6 @@ class TextGrid(ASCIIElement):
Properties:
grid: The underlying grid object.
-
"""
grid = None
diff --git a/mesa/visualization/__init__.py b/mesa/visualization/__init__.py
index ae62921653c..6f3a73fb8bc 100644
--- a/mesa/visualization/__init__.py
+++ b/mesa/visualization/__init__.py
@@ -6,5 +6,4 @@
TextServer: Class which takes a TextVisualization child class as an input, and
renders it in-browser, along with an interface.
-
"""
From c000dada255dd8f176f92fc2fe7d12a7d932c535 Mon Sep 17 00:00:00 2001
From: "pre-commit-ci[bot]"
<66853113+pre-commit-ci[bot]@users.noreply.github.com>
Date: Tue, 1 Nov 2022 00:26:17 +0000
Subject: [PATCH 004/214] [pre-commit.ci] pre-commit autoupdate
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
updates:
- [github.com/asottile/pyupgrade: v3.1.0 → v3.2.0](https://github.com/asottile/pyupgrade/compare/v3.1.0...v3.2.0)
---
.pre-commit-config.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 396dfc75d10..d3a37f26eb5 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -9,7 +9,7 @@ repos:
- id: black-jupyter
exclude: ^mesa/cookiecutter-mesa/
- repo: https://github.com/asottile/pyupgrade
- rev: v3.1.0
+ rev: v3.2.0
hooks:
- id: pyupgrade
args: [--py38-plus]
From be9c53873a2e331be67c735d0358739f1e298772 Mon Sep 17 00:00:00 2001
From: jackiekazil
Date: Tue, 1 Nov 2022 06:36:26 +0000
Subject: [PATCH 005/214] [Bot] Update Pipfile.lock dependencies
---
Pipfile.lock | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Pipfile.lock b/Pipfile.lock
index 31f0fa262ec..1b26342f271 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -241,10 +241,10 @@
},
"pytz": {
"hashes": [
- "sha256:335ab46900b1465e714b4fda4963d87363264eb662aab5e65da039c25f1f5b22",
- "sha256:c4d88f472f54d615e9cd582a5004d1e5f624854a6a27a6211591c251f22a6914"
+ "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427",
+ "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2"
],
- "version": "==2022.5"
+ "version": "==2022.6"
},
"pyyaml": {
"hashes": [
From 8d052a1536864f9facf0be7256e182be460e0035 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Tue, 1 Nov 2022 14:34:12 +0100
Subject: [PATCH 006/214] Remove remaining extraneous spaces from docstrings
(#1496)
---
examples/boid_flockers/boid_flockers/boid.py | 1 -
examples/color_patches/color_patches/server.py | 1 -
.../epstein_civil_violence/epstein_civil_violence/agent.py | 2 --
.../epstein_civil_violence/epstein_civil_violence/model.py | 1 -
examples/sugarscape_cg/sugarscape_cg/agents.py | 1 -
examples/wolf_sheep/wolf_sheep/random_walk.py | 1 -
mesa/visualization/modules/BarChartVisualization.py | 2 --
mesa/visualization/modules/CanvasGridVisualization.py | 3 ---
mesa/visualization/modules/ChartVisualization.py | 2 --
mesa/visualization/modules/HexGridVisualization.py | 3 ---
mesa/visualization/modules/NetworkVisualization.py | 1 -
mesa/visualization/modules/PieChartVisualization.py | 5 -----
12 files changed, 23 deletions(-)
diff --git a/examples/boid_flockers/boid_flockers/boid.py b/examples/boid_flockers/boid_flockers/boid.py
index d8f45222650..f427f9ddbbc 100644
--- a/examples/boid_flockers/boid_flockers/boid.py
+++ b/examples/boid_flockers/boid_flockers/boid.py
@@ -43,7 +43,6 @@ def __init__(
cohere: the relative importance of matching neighbors' positions
separate: the relative importance of avoiding close neighbors
match: the relative importance of matching neighbors' headings
-
"""
super().__init__(unique_id, model)
self.pos = np.array(pos)
diff --git a/examples/color_patches/color_patches/server.py b/examples/color_patches/color_patches/server.py
index 44c4624ebb3..aa52332eb3b 100644
--- a/examples/color_patches/color_patches/server.py
+++ b/examples/color_patches/color_patches/server.py
@@ -43,7 +43,6 @@ def color_patch_draw(cell):
:param cell: the cell in the simulation
:return: the portrayal dictionary.
-
"""
if cell is None:
raise AssertionError
diff --git a/examples/epstein_civil_violence/epstein_civil_violence/agent.py b/examples/epstein_civil_violence/epstein_civil_violence/agent.py
index c270a9700ed..358b4484d44 100644
--- a/examples/epstein_civil_violence/epstein_civil_violence/agent.py
+++ b/examples/epstein_civil_violence/epstein_civil_violence/agent.py
@@ -26,7 +26,6 @@ class Citizen(mesa.Agent):
how aggrieved is agent at the regime?
arrest_probability: agent's assessment of arrest probability, given
rebellion
-
"""
def __init__(
@@ -108,7 +107,6 @@ def update_estimated_arrest_probability(self):
"""
Based on the ratio of cops to actives in my neighborhood, estimate the
p(Arrest | I go active).
-
"""
cops_in_vision = len([c for c in self.neighbors if c.breed == "cop"])
actives_in_vision = 1.0 # citizen counts herself
diff --git a/examples/epstein_civil_violence/epstein_civil_violence/model.py b/examples/epstein_civil_violence/epstein_civil_violence/model.py
index 02d92c39318..760767c26d9 100644
--- a/examples/epstein_civil_violence/epstein_civil_violence/model.py
+++ b/examples/epstein_civil_violence/epstein_civil_violence/model.py
@@ -27,7 +27,6 @@ class EpsteinCivilViolence(mesa.Model):
movement: binary, whether agents try to move at step end
max_iters: model may not have a natural stopping point, so we set a
max.
-
"""
def __init__(
diff --git a/examples/sugarscape_cg/sugarscape_cg/agents.py b/examples/sugarscape_cg/sugarscape_cg/agents.py
index 28a99095120..6e245eaa19d 100644
--- a/examples/sugarscape_cg/sugarscape_cg/agents.py
+++ b/examples/sugarscape_cg/sugarscape_cg/agents.py
@@ -8,7 +8,6 @@ def get_distance(pos_1, pos_2):
Args:
pos_1, pos_2: Coordinate tuples for both points.
-
"""
x1, y1 = pos_1
x2, y2 = pos_2
diff --git a/examples/wolf_sheep/wolf_sheep/random_walk.py b/examples/wolf_sheep/wolf_sheep/random_walk.py
index 1125589b1e9..49219fa7fff 100644
--- a/examples/wolf_sheep/wolf_sheep/random_walk.py
+++ b/examples/wolf_sheep/wolf_sheep/random_walk.py
@@ -11,7 +11,6 @@ class RandomWalker(mesa.Agent):
Not intended to be used on its own, but to inherit its methods to multiple
other agents.
-
"""
grid = None
diff --git a/mesa/visualization/modules/BarChartVisualization.py b/mesa/visualization/modules/BarChartVisualization.py
index afe1558bca1..a0c3cedbe52 100644
--- a/mesa/visualization/modules/BarChartVisualization.py
+++ b/mesa/visualization/modules/BarChartVisualization.py
@@ -3,7 +3,6 @@
============
Module for drawing live-updating bar charts using d3.js
-
"""
import json
from mesa.visualization.ModularVisualization import VisualizationElement, D3_JS_FILE
@@ -24,7 +23,6 @@ class BarChartModule(VisualizationElement):
canvas_height, canvas_width: The width and height to draw the chart on the page, in pixels.
Default to 800 x 400
data_collector_name: Name of the DataCollector object in the model to retrieve data from.
-
"""
package_includes = [D3_JS_FILE, "BarChartModule.js"]
diff --git a/mesa/visualization/modules/CanvasGridVisualization.py b/mesa/visualization/modules/CanvasGridVisualization.py
index 49e85196135..281cd574de3 100644
--- a/mesa/visualization/modules/CanvasGridVisualization.py
+++ b/mesa/visualization/modules/CanvasGridVisualization.py
@@ -3,7 +3,6 @@
========================
Module for visualizing model objects in grid cells.
-
"""
from collections import defaultdict
from mesa.visualization.ModularVisualization import VisualizationElement
@@ -59,7 +58,6 @@ class CanvasGrid(VisualizationElement):
canvas_height, canvas_width: Size, in pixels, of the grid visualization
to draw on the client.
template: "canvas_module.html" stores the module's HTML template.
-
"""
package_includes = ["GridDraw.js", "CanvasModule.js", "InteractionHandler.js"]
@@ -80,7 +78,6 @@ def __init__(
grid_width, grid_height: Size of the grid, in cells.
canvas_height, canvas_width: Size of the canvas to draw in the
client, in pixels. (default: 500x500)
-
"""
self.portrayal_method = portrayal_method
self.grid_width = grid_width
diff --git a/mesa/visualization/modules/ChartVisualization.py b/mesa/visualization/modules/ChartVisualization.py
index 8fc51a3450e..fc6ee1fefa5 100644
--- a/mesa/visualization/modules/ChartVisualization.py
+++ b/mesa/visualization/modules/ChartVisualization.py
@@ -3,7 +3,6 @@
============
Module for drawing live-updating line charts using Charts.js
-
"""
import json
from mesa.visualization.ModularVisualization import VisualizationElement, CHART_JS_FILE
@@ -39,7 +38,6 @@ class ChartModule(VisualizationElement):
More Pythonic customization; in particular, have both series-level and
chart-level options settable in Python, and passed to the front-end
the same way that "Color" is currently.
-
"""
package_includes = [CHART_JS_FILE, "ChartModule.js"]
diff --git a/mesa/visualization/modules/HexGridVisualization.py b/mesa/visualization/modules/HexGridVisualization.py
index ebe2781d798..ddd26a97aa5 100644
--- a/mesa/visualization/modules/HexGridVisualization.py
+++ b/mesa/visualization/modules/HexGridVisualization.py
@@ -3,7 +3,6 @@
========================
Module for visualizing model objects in hexagonal grid cells.
-
"""
from collections import defaultdict
from mesa.visualization.ModularVisualization import VisualizationElement
@@ -36,7 +35,6 @@ class CanvasHexGrid(VisualizationElement):
canvas_height, canvas_width: Size, in pixels, of the grid visualization
to draw on the client.
template: "canvas_module.html" stores the module's HTML template.
-
"""
package_includes = ["HexDraw.js", "CanvasHexModule.js", "InteractionHandler.js"]
@@ -60,7 +58,6 @@ def __init__(
grid_width, grid_height: Size of the grid, in cells.
canvas_height, canvas_width: Size of the canvas to draw in the
client, in pixels. (default: 500x500)
-
"""
self.portrayal_method = portrayal_method
self.grid_width = grid_width
diff --git a/mesa/visualization/modules/NetworkVisualization.py b/mesa/visualization/modules/NetworkVisualization.py
index a50e92090f7..85a353062fd 100644
--- a/mesa/visualization/modules/NetworkVisualization.py
+++ b/mesa/visualization/modules/NetworkVisualization.py
@@ -3,7 +3,6 @@
============
Module for rendering the network, using [d3.js](https://d3js.org/) framework.
-
"""
from mesa.visualization.ModularVisualization import VisualizationElement, D3_JS_FILE
diff --git a/mesa/visualization/modules/PieChartVisualization.py b/mesa/visualization/modules/PieChartVisualization.py
index 470e38fc9f7..671d53cb688 100644
--- a/mesa/visualization/modules/PieChartVisualization.py
+++ b/mesa/visualization/modules/PieChartVisualization.py
@@ -3,7 +3,6 @@
============
Module for drawing live-updating pie charts using d3.js
-
"""
import json
from mesa.visualization.ModularVisualization import VisualizationElement, D3_JS_FILE
@@ -13,7 +12,6 @@ class PieChartModule(VisualizationElement):
"""Each chart can visualize one set of fields from a datacollector as a
pie chart.
-
Attributes:
fields: A list of dictionaries containing information on fields to
plot. Each dictionary must contain (at least) the "Label" and
@@ -24,9 +22,6 @@ class PieChartModule(VisualizationElement):
the page, in pixels. Default to 500 x 500
data_collector_name: Name of the DataCollector object in the model to
retrieve data from.
-
-
-
"""
package_includes = [D3_JS_FILE, "PieChartModule.js"]
From 79f4e9986472246ae4e5017dd0a7264a47b72601 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Tue, 1 Nov 2022 14:17:31 +0100
Subject: [PATCH 007/214] Add default_val function to NetworkGrid
---
mesa/space.py | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index 95e123d8e50..5efc013781b 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -955,7 +955,12 @@ class NetworkGrid:
def __init__(self, G: Any) -> None:
self.G = G
for node_id in self.G.nodes:
- G.nodes[node_id]["agent"] = list()
+ G.nodes[node_id]["agent"] = self.default_val()
+
+ @staticmethod
+ def default_val() -> list:
+ """Default value for a new node."""
+ return []
def place_agent(self, agent: Agent, node_id: int) -> None:
"""Place a agent in a node."""
@@ -986,7 +991,7 @@ def remove_agent(self, agent: Agent) -> None:
def is_cell_empty(self, node_id: int) -> bool:
"""Returns a bool of the contents of a cell."""
- return not self.G.nodes[node_id]["agent"]
+ return self.G.nodes[node_id]["agent"] == self.default_val()
def get_cell_list_contents(self, cell_list: list[int]) -> list[GridContent]:
"""Returns the contents of a list of cells ((x,y) tuples)
From 31f65ab8ccb2a12a5f50c18ba1abf19a8c160f48 Mon Sep 17 00:00:00 2001
From: Jangsea Park
Date: Wed, 2 Nov 2022 17:39:54 +0900
Subject: [PATCH 008/214] Update year for copyright
Update year for copyright, `2021` -> `2022`
---
LICENSE | 2 +-
docs/conf.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/LICENSE b/LICENSE
index 137491e71c5..939717b7778 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright 2021 Core Mesa Team and contributors
+Copyright 2022 Core Mesa Team and contributors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
diff --git a/docs/conf.py b/docs/conf.py
index d682ce1a8ba..a5fd442434a 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -57,7 +57,7 @@
# General information about the project.
project = "Mesa"
-copyright = "2015-2021, Project Mesa Team"
+copyright = "2015-2022, Project Mesa Team"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
From 8a45d6a0013c76237c0c0749c0d3cc556a8dbae0 Mon Sep 17 00:00:00 2001
From: Jangsea Park
Date: Thu, 3 Nov 2022 08:45:43 +0900
Subject: [PATCH 009/214] Auto update year for copyright in docs
---
docs/conf.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/docs/conf.py b/docs/conf.py
index a5fd442434a..102ce58fe79 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -14,6 +14,7 @@
import sys
import os
+from datetime import date
# If extensions (or modules to document with autodoc) are in another directory,
@@ -57,7 +58,7 @@
# General information about the project.
project = "Mesa"
-copyright = "2015-2022, Project Mesa Team"
+copyright = f"2015-{date.today().year}, Project Mesa Team"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
From 8759f7b61f197f71719fbd09bed739a8cafbea45 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Thu, 3 Nov 2022 23:50:19 +0100
Subject: [PATCH 010/214] Hexgrid: use get_neighborhood in iter_neighbors
---
mesa/space.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/mesa/space.py b/mesa/space.py
index 5efc013781b..7a31381d612 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -737,7 +737,7 @@ def iter_neighbors(
Returns:
An iterator of non-None objects in the given neighborhood
"""
- neighborhood = self.iter_neighborhood(pos, include_center, radius)
+ neighborhood = self.get_neighborhood(pos, include_center, radius)
return self.iter_cell_list_contents(neighborhood)
def get_neighbors(
From 3267522a0ee2146deffb4755189059987ed5b92c Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Fri, 4 Nov 2022 10:57:44 +0100
Subject: [PATCH 011/214] Refactor NetworkGrid docstrings and
iter/get_cell_list_contents (#1498)
* Refactor NetworkGrid docstrings and iter/get_cell_list_contents
* Update space.py
* Update space.py
---
mesa/space.py | 29 +++++++++++++----------------
1 file changed, 13 insertions(+), 16 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index 7a31381d612..c0988cc4ba2 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -963,14 +963,12 @@ def default_val() -> list:
return []
def place_agent(self, agent: Agent, node_id: int) -> None:
- """Place a agent in a node."""
-
+ """Place an agent in a node."""
self.G.nodes[node_id]["agent"].append(agent)
agent.pos = node_id
def get_neighbors(self, node_id: int, include_center: bool = False) -> list[int]:
"""Get all adjacent nodes"""
-
neighbors = list(self.G.neighbors(node_id))
if include_center:
neighbors.append(node_id)
@@ -979,7 +977,6 @@ def get_neighbors(self, node_id: int, include_center: bool = False) -> list[int]
def move_agent(self, agent: Agent, node_id: int) -> None:
"""Move an agent from its current node to a new node."""
-
self.remove_agent(agent)
self.place_agent(agent, node_id)
@@ -994,22 +991,22 @@ def is_cell_empty(self, node_id: int) -> bool:
return self.G.nodes[node_id]["agent"] == self.default_val()
def get_cell_list_contents(self, cell_list: list[int]) -> list[GridContent]:
- """Returns the contents of a list of cells ((x,y) tuples)
- Note: this method returns a list of `Agent`'s; `None` contents are excluded.
+ """Returns a list of the agents contained in the nodes identified
+ in `cell_list`; nodes with empty content are excluded.
"""
- return list(self.iter_cell_list_contents(cell_list))
-
- def get_all_cell_contents(self) -> list[GridContent]:
- """Returns a list of the contents of the cells
- identified in cell_list."""
- return list(self.iter_cell_list_contents(self.G))
-
- def iter_cell_list_contents(self, cell_list: list[int]) -> list[GridContent]:
- """Returns an iterator of the contents of the cells
- identified in cell_list."""
list_of_lists = [
self.G.nodes[node_id]["agent"]
for node_id in cell_list
if not self.is_cell_empty(node_id)
]
return [item for sublist in list_of_lists for item in sublist]
+
+ def get_all_cell_contents(self) -> list[GridContent]:
+ """Returns a list of all the agents in the network."""
+ return self.get_cell_list_contents(self.G)
+
+ def iter_cell_list_contents(self, cell_list: list[int]) -> Iterator[GridContent]:
+ """Returns an iterator of the agents contained in the nodes identified
+ in `cell_list`; nodes with empty content are excluded.
+ """
+ yield from self.get_cell_list_contents(cell_list)
From c7ec7b477b34e17b7fe41d610a9d95d0be65af38 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Fri, 4 Nov 2022 18:33:32 +0100
Subject: [PATCH 012/214] Fix return types of some NetworkGrid methods
---
mesa/space.py | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index c0988cc4ba2..e15d5a3b9cf 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -972,7 +972,6 @@ def get_neighbors(self, node_id: int, include_center: bool = False) -> list[int]
neighbors = list(self.G.neighbors(node_id))
if include_center:
neighbors.append(node_id)
-
return neighbors
def move_agent(self, agent: Agent, node_id: int) -> None:
@@ -990,7 +989,7 @@ def is_cell_empty(self, node_id: int) -> bool:
"""Returns a bool of the contents of a cell."""
return self.G.nodes[node_id]["agent"] == self.default_val()
- def get_cell_list_contents(self, cell_list: list[int]) -> list[GridContent]:
+ def get_cell_list_contents(self, cell_list: list[int]) -> list[Agent]:
"""Returns a list of the agents contained in the nodes identified
in `cell_list`; nodes with empty content are excluded.
"""
@@ -1001,11 +1000,11 @@ def get_cell_list_contents(self, cell_list: list[int]) -> list[GridContent]:
]
return [item for sublist in list_of_lists for item in sublist]
- def get_all_cell_contents(self) -> list[GridContent]:
+ def get_all_cell_contents(self) -> list[Agent]:
"""Returns a list of all the agents in the network."""
return self.get_cell_list_contents(self.G)
- def iter_cell_list_contents(self, cell_list: list[int]) -> Iterator[GridContent]:
+ def iter_cell_list_contents(self, cell_list: list[int]) -> Iterator[Agent]:
"""Returns an iterator of the agents contained in the nodes identified
in `cell_list`; nodes with empty content are excluded.
"""
From c201bcb2a61e29ed03c0c04ac0a64bddb201fa10 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Sun, 6 Nov 2022 00:15:39 +0100
Subject: [PATCH 013/214] Make swap_pos part of Grid instead of SingleGrid
(#1507)
* Make swap_pos part of Grid instead of SingleGrid
* Update test_grid.py
The second commit changes the agents sampling to
`agent_a, agent_b = list(filter(None, self.grid))[:2]`
because when the test is moved to `TestBaseGrid`, the `self.grid` in `TestBaseGrid` is not full.
---
mesa/space.py | 40 ++++++++++++++++----------------
tests/test_grid.py | 58 ++++++++++++++++++++++++----------------------
2 files changed, 50 insertions(+), 48 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index e15d5a3b9cf..c0081f749ce 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -408,6 +408,26 @@ def remove_agent(self, agent: Agent) -> None:
self.empties.add(pos)
agent.pos = None
+ def swap_pos(self, agent_a: Agent, agent_b: Agent) -> None:
+ """Swap agents positions"""
+ agents_no_pos = []
+ if (pos_a := agent_a.pos) is None:
+ agents_no_pos.append(agent_a)
+ if (pos_b := agent_b.pos) is None:
+ agents_no_pos.append(agent_b)
+ if agents_no_pos:
+ agents_no_pos = [f"" for a in agents_no_pos]
+ raise Exception(f"{', '.join(agents_no_pos)} - not on the grid")
+
+ if pos_a == pos_b:
+ return
+
+ self.remove_agent(agent_a)
+ self.remove_agent(agent_b)
+
+ self.place_agent(agent_a, pos_b)
+ self.place_agent(agent_b, pos_a)
+
def is_cell_empty(self, pos: Coordinate) -> bool:
"""Returns a bool of the contents of a cell."""
x, y = pos
@@ -492,26 +512,6 @@ def position_agent(
coords = (x, y)
self.place_agent(agent, coords)
- def swap_pos(self, agent_a: Agent, agent_b: Agent) -> None:
- """Swap agents positions"""
- agents_no_pos = []
- if (pos_a := agent_a.pos) is None:
- agents_no_pos.append(agent_a)
- if (pos_b := agent_b.pos) is None:
- agents_no_pos.append(agent_b)
- if agents_no_pos:
- agents_no_pos = [f"" for a in agents_no_pos]
- raise Exception(f"{', '.join(agents_no_pos)} - not on the grid")
-
- if pos_a == pos_b:
- return
-
- self.remove_agent(agent_a)
- self.remove_agent(agent_b)
-
- self.place_agent(agent_a, pos_b)
- self.place_agent(agent_b, pos_a)
-
def place_agent(self, agent: Agent, pos: Coordinate) -> None:
if self.is_cell_empty(pos):
super().place_agent(agent, pos)
diff --git a/tests/test_grid.py b/tests/test_grid.py
index 64e93a448f9..245a5c357eb 100644
--- a/tests/test_grid.py
+++ b/tests/test_grid.py
@@ -169,6 +169,36 @@ def test_agent_remove(self):
assert agent.pos is None
assert self.grid.grid[x][y] is None
+ def test_swap_pos(self):
+
+ # Swap agents positions
+ agent_a, agent_b = list(filter(None, self.grid))[:2]
+ pos_a = agent_a.pos
+ pos_b = agent_b.pos
+
+ self.grid.swap_pos(agent_a, agent_b)
+
+ assert agent_a.pos == pos_b
+ assert agent_b.pos == pos_a
+ assert self.grid[pos_a] == agent_b
+ assert self.grid[pos_b] == agent_a
+
+ # Swap the same agents
+ self.grid.swap_pos(agent_a, agent_a)
+
+ assert agent_a.pos == pos_b
+ assert self.grid[pos_b] == agent_a
+
+ # Raise for agents not on the grid
+ self.grid.remove_agent(agent_a)
+ self.grid.remove_agent(agent_b)
+
+ id_a = agent_a.unique_id
+ id_b = agent_b.unique_id
+ e_message = f", - not on the grid"
+ with self.assertRaisesRegex(Exception, e_message):
+ self.grid.swap_pos(agent_a, agent_b)
+
class TestBaseGridTorus(TestBaseGrid):
"""
@@ -268,34 +298,6 @@ def test_enforcement(self, mock_model):
with self.assertRaises(Exception):
self.move_to_empty(self.agents[0], num_agents=self.num_agents)
- # Swap agents positions
- agent_a, agent_b = random.sample(list(self.grid), k=2)
- pos_a = agent_a.pos
- pos_b = agent_b.pos
-
- self.grid.swap_pos(agent_a, agent_b)
-
- assert agent_a.pos == pos_b
- assert agent_b.pos == pos_a
- assert self.grid[pos_a] == agent_b
- assert self.grid[pos_b] == agent_a
-
- # Swap the same agents
- self.grid.swap_pos(agent_a, agent_a)
-
- assert agent_a.pos == pos_b
- assert self.grid[pos_b] == agent_a
-
- # Raise for agents not on the grid
- self.grid.remove_agent(agent_a)
- self.grid.remove_agent(agent_b)
-
- id_a = agent_a.unique_id
- id_b = agent_b.unique_id
- e_message = f", - not on the grid"
- with self.assertRaisesRegex(Exception, e_message):
- self.grid.swap_pos(agent_a, agent_b)
-
# Number of agents at each position for testing
# Initial agent positions for testing
From bfca50c9d06917196441ca1561e7ca5d316a0841 Mon Sep 17 00:00:00 2001
From: rht
Date: Sun, 6 Nov 2022 03:31:54 -0500
Subject: [PATCH 014/214] fix: position_agent: Enforce type of x and y
---
examples/schelling/model.py | 2 +-
mesa/space.py | 11 +++++++++++
tests/test_grid.py | 19 +++++++++++++++++++
3 files changed, 31 insertions(+), 1 deletion(-)
diff --git a/examples/schelling/model.py b/examples/schelling/model.py
index 52cfafa2c57..aadc9700799 100644
--- a/examples/schelling/model.py
+++ b/examples/schelling/model.py
@@ -70,7 +70,7 @@ def __init__(self, width=20, height=20, density=0.8, minority_pc=0.2, homophily=
agent_type = 0
agent = SchellingAgent((x, y), self, agent_type)
- self.grid.position_agent(agent, (x, y))
+ self.grid.position_agent(agent, x, y)
self.schedule.add(agent)
self.running = True
diff --git a/mesa/space.py b/mesa/space.py
index c0081f749ce..538423f1776 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -504,6 +504,17 @@ def position_agent(
If x or y are positive, they are used.
Use 'swap_pos()' to swap agents positions.
"""
+ if not (isinstance(x, int) or x == "random"):
+ raise Exception(
+ "x must be an integer or a string 'random'."
+ f" Actual type: {type(x)}. Actual value: {x}."
+ )
+ if not (isinstance(y, int) or y == "random"):
+ raise Exception(
+ "y must be an integer or a string 'random'."
+ f" Actual type: {type(y)}. Actual value: {y}."
+ )
+
if x == "random" or y == "random":
if len(self.empties) == 0:
raise Exception("ERROR: Grid full")
diff --git a/tests/test_grid.py b/tests/test_grid.py
index 245a5c357eb..7a70619823d 100644
--- a/tests/test_grid.py
+++ b/tests/test_grid.py
@@ -262,6 +262,25 @@ def setUp(self):
self.grid.place_agent(a, (x, y))
self.num_agents = len(self.agents)
+ @patch.object(MockAgent, "model", create=True)
+ def test_position_agent(self, mock_model):
+ a = MockAgent(100, None)
+ with self.assertRaises(Exception) as exc_info:
+ self.grid.position_agent(a, (1, 1))
+ expected = (
+ "x must be an integer or a string 'random'."
+ " Actual type: . Actual value: (1, 1)."
+ )
+ assert str(exc_info.exception) == expected
+ with self.assertRaises(Exception) as exc_info:
+ self.grid.position_agent(a, "(1, 1)")
+ expected = (
+ "x must be an integer or a string 'random'."
+ " Actual type: . Actual value: (1, 1)."
+ )
+ assert str(exc_info.exception) == expected
+ self.grid.position_agent(a, "random")
+
@patch.object(MockAgent, "model", create=True)
def test_enforcement(self, mock_model):
"""
From 45171f3fef118b9344ccf1def93a82ce2cb4991b Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Sun, 6 Nov 2022 14:36:59 +0100
Subject: [PATCH 015/214] Deprecate SingleGrid.position_agent (#1512)
---
examples/schelling/model.py | 2 +-
mesa/space.py | 10 ++++++++++
2 files changed, 11 insertions(+), 1 deletion(-)
diff --git a/examples/schelling/model.py b/examples/schelling/model.py
index aadc9700799..821b68af951 100644
--- a/examples/schelling/model.py
+++ b/examples/schelling/model.py
@@ -70,7 +70,7 @@ def __init__(self, width=20, height=20, density=0.8, minority_pc=0.2, homophily=
agent_type = 0
agent = SchellingAgent((x, y), self, agent_type)
- self.grid.position_agent(agent, x, y)
+ self.grid.place_agent(agent, (x, y))
self.schedule.add(agent)
self.running = True
diff --git a/mesa/space.py b/mesa/space.py
index 538423f1776..de335668920 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -504,6 +504,16 @@ def position_agent(
If x or y are positive, they are used.
Use 'swap_pos()' to swap agents positions.
"""
+ warn(
+ (
+ "`position_agent` is being deprecated; use instead "
+ "`place_agent` to place an agent at a specified "
+ "location or `move_to_empty` to place an agent "
+ "at a random empty cell."
+ ),
+ DeprecationWarning,
+ )
+
if not (isinstance(x, int) or x == "random"):
raise Exception(
"x must be an integer or a string 'random'."
From a783971f6b49456d93b3a057d18f273f535aa87e Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Mon, 7 Nov 2022 15:08:32 +0100
Subject: [PATCH 016/214] Update NetworkGrid.__init__ docstring
Specify that NetworkGrid requires a Networkx
graph instance.
---
mesa/space.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/mesa/space.py b/mesa/space.py
index de335668920..2d76c8eaaff 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -974,6 +974,11 @@ class NetworkGrid:
"""Network Grid where each node contains zero or more agents."""
def __init__(self, G: Any) -> None:
+ """Create a new network.
+
+ Args:
+ G: a NetworkX graph instance.
+ """
self.G = G
for node_id in self.G.nodes:
G.nodes[node_id]["agent"] = self.default_val()
From bfc0e86fc540fed6ec06207784b7a5a31208d46f Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Tue, 8 Nov 2022 01:43:11 +0100
Subject: [PATCH 017/214] Revert changes of #1478 and #1456 (#1516)
* Revert changes of #1478 and parts of #1456
These PRs are reverted because they
introduce the bug discussed in #1515
which prevents agents from being added/removed
while looping through the agent keys.
* Update time.py
* Add full stop to comment.
* Add more full stop to comment
Co-authored-by: rht
---
mesa/time.py | 39 ++++++++++++++++++++++-----------------
1 file changed, 22 insertions(+), 17 deletions(-)
diff --git a/mesa/time.py b/mesa/time.py
index f9c84acf62f..54f6e25acf1 100644
--- a/mesa/time.py
+++ b/mesa/time.py
@@ -28,7 +28,7 @@
from collections import defaultdict
# mypy
-from typing import Iterator, Union, Iterable
+from typing import Iterator, Union
from mesa.agent import Agent
from mesa.model import Model
@@ -96,9 +96,10 @@ def agent_buffer(self, shuffled: bool = False) -> Iterator[Agent]:
"""Simple generator that yields the agents while letting the user
remove and/or add agents during stepping.
"""
- agent_keys = self._agents.keys()
+ # To be able to remove and/or add agents during stepping
+ # it's necessary to cast the keys view to a list.
+ agent_keys = list(self._agents.keys())
if shuffled:
- agent_keys = list(agent_keys)
self.model.random.shuffle(agent_keys)
for key in agent_keys:
@@ -137,12 +138,16 @@ class SimultaneousActivation(BaseScheduler):
def step(self) -> None:
"""Step all agents, then advance them."""
- for agent in self._agents.values():
- agent.step()
- # the previous steps might remove some agents, but
- # this loop will go over the remaining existing agents
- for agent in self._agents.values():
- agent.advance()
+ # To be able to remove and/or add agents during stepping
+ # it's necessary to cast the keys view to a list.
+ agent_keys = list(self._agents.keys())
+ for agent_key in agent_keys:
+ self._agents[agent_key].step()
+ # We recompute the keys because some agents might have been removed in
+ # the previous loop.
+ agent_keys = list(self._agents.keys())
+ for agent_key in agent_keys:
+ self._agents[agent_key].advance()
self.steps += 1
self.time += 1
@@ -185,18 +190,18 @@ def __init__(
def step(self) -> None:
"""Executes all the stages for all agents."""
- agent_keys = self._agents.keys()
+ # To be able to remove and/or add agents during stepping
+ # it's necessary to cast the keys view to a list.
+ agent_keys = list(self._agents.keys())
if self.shuffle:
- agent_keys = list(agent_keys)
self.model.random.shuffle(agent_keys)
for stage in self.stage_list:
for agent_key in agent_keys:
getattr(self._agents[agent_key], stage)() # Run stage
# We recompute the keys because some agents might have been removed
# in the previous loop.
- agent_keys = self._agents.keys()
+ agent_keys = list(self._agents.keys())
if self.shuffle_between_stages:
- agent_keys = list(agent_keys)
self.model.random.shuffle(agent_keys)
self.time += self.stage_time
@@ -259,9 +264,10 @@ def step(self, shuffle_types: bool = True, shuffle_agents: bool = True) -> None:
shuffle_agents: If True, the order of execution of each agents in a
type group is shuffled.
"""
- type_keys: Iterable[type[Agent]] = self.agents_by_type.keys()
+ # To be able to remove and/or add agents during stepping
+ # it's necessary to cast the keys view to a list.
+ type_keys: list[type[Agent]] = list(self.agents_by_type.keys())
if shuffle_types:
- type_keys = list(type_keys)
self.model.random.shuffle(type_keys)
for agent_class in type_keys:
self.step_type(agent_class, shuffle_agents=shuffle_agents)
@@ -276,9 +282,8 @@ def step_type(self, type_class: type[Agent], shuffle_agents: bool = True) -> Non
Args:
type_class: Class object of the type to run.
"""
- agent_keys: Iterable[int] = self.agents_by_type[type_class].keys()
+ agent_keys: list[int] = list(self.agents_by_type[type_class].keys())
if shuffle_agents:
- agent_keys = list(agent_keys)
self.model.random.shuffle(agent_keys)
for agent_key in agent_keys:
self.agents_by_type[type_class][agent_key].step()
From b0bc381a64ea7fcf437f16a4f0a60d83e78862db Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Tue, 8 Nov 2022 10:24:14 +0100
Subject: [PATCH 018/214] Make Grid.get_neighborhood faster (#1476)
A more performant algorithm is now applied.
Co-authored-by: Corvince <13568919+Corvince@users.noreply.github.com>
Co-authored-by: rht
Co-authored-by: Corvince <13568919+Corvince@users.noreply.github.com>
Co-authored-by: rht
---
mesa/space.py | 38 +++++++++++++++++++++++---------------
1 file changed, 23 insertions(+), 15 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index 2d76c8eaaff..f567bd771ef 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -251,29 +251,37 @@ def get_neighborhood(
if neighborhood is not None:
return neighborhood
- coordinates: set[Coordinate] = set()
+ neighborhood = []
x, y = pos
- for dy in range(-radius, radius + 1):
- for dx in range(-radius, radius + 1):
- # Skip coordinates that are outside manhattan distance
- if not moore and abs(dx) + abs(dy) > radius:
- continue
+ if self.torus:
+ x_radius = min(radius, self.width // 2)
+ y_radius = min(radius, self.height // 2)
- coord = (x + dx, y + dy)
+ for dx in range(-x_radius, x_radius + 1):
+ for dy in range(-y_radius, y_radius + 1):
- if self.out_of_bounds(coord):
- # Skip if not a torus and new coords out of bounds.
- if not self.torus:
+ if not moore and abs(dx) + abs(dy) > radius:
continue
- coord = self.torus_adj(coord)
- coordinates.add(coord)
+ nx, ny = (x + dx) % self.width, (y + dy) % self.height
+ neighborhood.append((nx, ny))
- if not include_center:
- coordinates.discard(pos)
+ else:
+ x_range = range(max(0, x - radius), min(self.width, x + radius + 1))
+ y_range = range(max(0, y - radius), min(self.height, y + radius + 1))
+
+ for nx in x_range:
+ for ny in y_range:
+
+ if not moore and abs(nx - x) + abs(ny - y) > radius:
+ continue
+
+ neighborhood.append((nx, ny))
+
+ if not include_center and neighborhood:
+ neighborhood.remove(pos)
- neighborhood = sorted(coordinates)
self._neighborhood_cache[cache_key] = neighborhood
return neighborhood
From 77599fa67e74331ec958482ff4d053bb4da514b6 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Wed, 9 Nov 2022 00:44:59 +0100
Subject: [PATCH 019/214] Update space module-level docstring summary (#1518)
---
mesa/space.py | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index f567bd771ef..e895ba54da0 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -4,9 +4,13 @@
Objects used to add a spatial component to a model.
-Grid: base grid, a simple list-of-lists.
-SingleGrid: grid which strictly enforces one object per cell.
-MultiGrid: extension to Grid where each cell is a set of objects.
+Grid: base grid, which creates a rectangular grid.
+SingleGrid: extension to Grid which strictly enforces one agent per cell.
+MultiGrid: extension to Grid where each cell can contain a set of agents.
+HexGrid: extension to Grid to handle hexagonal neighbors.
+ContinuousSpace: a two-dimensional space where each agent has an arbitrary
+ position of `float`'s.
+NetworkGrid: a network where each node contains zero or more agents.
"""
# Instruction for PyLint to suppress variable name errors, since we have a
# good reason to use one-character variable names for x and y.
@@ -75,7 +79,7 @@ def is_integer(x: Real) -> bool:
class Grid:
- """Base class for a square grid.
+ """Base class for a rectangular grid.
Grid cells are indexed by [x][y], where [0][0] is assumed to be the
bottom-left and [width-1][height-1] is the top-right. If a grid is
From a4096758ed96a41ddd84f4957781388660d44d46 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Fri, 11 Nov 2022 10:42:05 +0100
Subject: [PATCH 020/214] Make MultiGrid.place_agent faster (#1508)
The condition tested now is a constant time operation when an agent has not been placed in a grid yet, while before it was a linear time operation in relation to the size of the list of agents.
---
mesa/space.py | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index e895ba54da0..48979763d60 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -582,10 +582,10 @@ def default_val() -> MultiGridContent:
def place_agent(self, agent: Agent, pos: Coordinate) -> None:
"""Place the agent at the specified location, and set its pos variable."""
x, y = pos
- if agent not in self.grid[x][y]:
+ if agent.pos is None or agent not in self.grid[x][y]:
self.grid[x][y].append(agent)
- self.empties.discard(pos)
- agent.pos = pos
+ agent.pos = pos
+ self.empties.discard(pos)
def remove_agent(self, agent: Agent) -> None:
"""Remove the agent from the given location and set its pos attribute to None."""
From 78a6f9a523362b8b51a0808824435b02ac764098 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Fri, 11 Nov 2022 15:10:13 +0100
Subject: [PATCH 021/214] Fix bug in Grid.get_neighborhood (#1517)
This fixes the edge case when the radius is as big as possible and the dimension is even, `nx` (or `ny`) has the value of 0, happens twice in the loop.
* Fix bug in Grid.get_neighborhood
* Deleted pypy mentioned
* typos fixed
* Update space.py
* Update space.py
* Grid.get_neighborhood: Tweak comments
Co-authored-by: rht
---
mesa/space.py | 27 +++++++++++++++++++++------
tests/test_grid.py | 25 +++++++++++++++++--------
2 files changed, 38 insertions(+), 14 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index 48979763d60..a0586a45614 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -255,22 +255,37 @@ def get_neighborhood(
if neighborhood is not None:
return neighborhood
+ # We use a list instead of a dict for the neighborhood because it would
+ # be easier to port the code to Cython or Numba (for performance
+ # purpose), with minimal changes. To better understand how the
+ # algorithm was conceived, look at
+ # https://github.com/projectmesa/mesa/pull/1476#issuecomment-1306220403
+ # and the discussion in that PR in general.
neighborhood = []
x, y = pos
if self.torus:
- x_radius = min(radius, self.width // 2)
- y_radius = min(radius, self.height // 2)
-
- for dx in range(-x_radius, x_radius + 1):
- for dy in range(-y_radius, y_radius + 1):
+ x_max_radius, y_max_radius = self.width // 2, self.height // 2
+ x_radius, y_radius = min(radius, x_max_radius), min(radius, y_max_radius)
+
+ # For each dimension, in the edge case where the radius is as big as
+ # possible and the dimension is even, we need to shrink by one the range
+ # of values, to avoid duplicates in neighborhood. For example, if
+ # the width is 4, while x, x_radius, and x_max_radius are 2, then
+ # (x + dx) has a value from 0 to 4 (inclusive), but this means that
+ # the 0 position is repeated since 0 % 4 and 4 % 4 are both 0.
+ xdim_even, ydim_even = (self.width + 1) % 2, (self.height + 1) % 2
+ kx = int(x_radius == x_max_radius and xdim_even)
+ ky = int(y_radius == y_max_radius and ydim_even)
+
+ for dx in range(-x_radius, x_radius + 1 - kx):
+ for dy in range(-y_radius, y_radius + 1 - ky):
if not moore and abs(dx) + abs(dy) > radius:
continue
nx, ny = (x + dx) % self.width, (y + dy) % self.height
neighborhood.append((nx, ny))
-
else:
x_range = range(max(0, x - radius), min(self.width, x + radius + 1))
y_range = range(max(0, y - radius), min(self.height, y + radius + 1))
diff --git a/tests/test_grid.py b/tests/test_grid.py
index 7a70619823d..007aa6c46f4 100644
--- a/tests/test_grid.py
+++ b/tests/test_grid.py
@@ -15,7 +15,7 @@
# 1 0 1
# 0 0 1
# -------------------
-TEST_GRID = [[0, 1, 0, 1, 0], [0, 0, 1, 1, 0], [1, 1, 0, 0, 0]]
+TEST_GRID = [[0, 1, 0, 1, 0, 0], [0, 0, 1, 1, 0, 1], [1, 1, 0, 0, 0, 1]]
class MockAgent:
@@ -40,8 +40,9 @@ def setUp(self):
"""
Create a test non-toroidal grid and populate it with Mock Agents
"""
+ # The height needs to be even to test the edge case described in PR #1517
+ height = 6 # height of grid
width = 3 # width of grid
- height = 5 # height of grid
self.grid = Grid(width, height, self.torus)
self.agents = []
counter = 0
@@ -109,10 +110,10 @@ def test_neighbors(self):
assert len(neighborhood) == 8
neighborhood = self.grid.get_neighborhood((1, 4), moore=False)
- assert len(neighborhood) == 3
+ assert len(neighborhood) == 4
neighborhood = self.grid.get_neighborhood((1, 4), moore=True)
- assert len(neighborhood) == 5
+ assert len(neighborhood) == 8
neighborhood = self.grid.get_neighborhood((0, 0), moore=False)
assert len(neighborhood) == 2
@@ -127,7 +128,7 @@ def test_neighbors(self):
assert len(neighbors) == 3
neighbors = self.grid.get_neighbors((1, 3), moore=False, radius=2)
- assert len(neighbors) == 2
+ assert len(neighbors) == 3
def test_coord_iter(self):
ci = self.grid.coord_iter()
@@ -221,17 +222,25 @@ def test_neighbors(self):
neighborhood = self.grid.get_neighborhood((0, 0), moore=False)
assert len(neighborhood) == 4
+ # here we test the edge case described in PR #1517 using a radius
+ # measuring half of the grid height
+ neighborhood = self.grid.get_neighborhood((0, 0), moore=True, radius=3)
+ assert len(neighborhood) == 17
+
+ neighborhood = self.grid.get_neighborhood((1, 1), moore=False, radius=3)
+ assert len(neighborhood) == 15
+
neighbors = self.grid.get_neighbors((1, 4), moore=False)
- assert len(neighbors) == 1
+ assert len(neighbors) == 2
neighbors = self.grid.get_neighbors((1, 4), moore=True)
- assert len(neighbors) == 3
+ assert len(neighbors) == 4
neighbors = self.grid.get_neighbors((1, 1), moore=False, include_center=True)
assert len(neighbors) == 3
neighbors = self.grid.get_neighbors((1, 3), moore=False, radius=2)
- assert len(neighbors) == 2
+ assert len(neighbors) == 3
class TestSingleGrid(unittest.TestCase):
From 9dbffeb54e13a55fb70118341a2a9ec75d51f673 Mon Sep 17 00:00:00 2001
From: Ewout ter Hoeven
Date: Wed, 19 Oct 2022 23:08:29 +0200
Subject: [PATCH 022/214] batchrunner: Replace two loops with dictionary
comprehension
---
mesa/batchrunner.py | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py
index 260223c784e..e856b097700 100644
--- a/mesa/batchrunner.py
+++ b/mesa/batchrunner.py
@@ -402,19 +402,19 @@ def run_model(self, model):
def collect_model_vars(self, model):
"""Run reporters and collect model-level variables."""
- model_vars = {}
- for var, reporter in self.model_reporters.items():
- model_vars[var] = reporter(model)
-
+ model_vars = {
+ var: reporter(model) for var, reporter in self.model_reporters.items()
+ }
return model_vars
def collect_agent_vars(self, model):
"""Run reporters and collect agent-level variables."""
agent_vars = {}
for agent in model.schedule._agents.values():
- agent_record = {}
- for var, reporter in self.agent_reporters.items():
- agent_record[var] = getattr(agent, reporter)
+ agent_record = {
+ var: getattr(agent, reporter)
+ for var, reporter in self.agent_reporters.items()
+ }
agent_vars[agent.unique_id] = agent_record
return agent_vars
From 06291403307360d0ec133312d5359eca134ede3e Mon Sep 17 00:00:00 2001
From: jackiekazil
Date: Mon, 14 Nov 2022 22:16:50 -0500
Subject: [PATCH 023/214] Update cookiecutter to flat import style.
---
examples/schelling/analysis.ipynb | 6 +++---
.../{{cookiecutter.snake}}/model.py | 15 ++++++---------
.../{{cookiecutter.snake}}/server.py | 11 ++++++-----
3 files changed, 15 insertions(+), 17 deletions(-)
diff --git a/examples/schelling/analysis.ipynb b/examples/schelling/analysis.ipynb
index 64a57e23f2f..50f382c66a0 100644
--- a/examples/schelling/analysis.ipynb
+++ b/examples/schelling/analysis.ipynb
@@ -431,9 +431,9 @@
],
"metadata": {
"kernelspec": {
- "display_name": "Python [conda env:mesa]",
+ "display_name": "Python 3 (ipykernel)",
"language": "python",
- "name": "conda-env-mesa-py"
+ "name": "python3"
},
"language_info": {
"codemirror_mode": {
@@ -445,7 +445,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
- "version": "3.6.6"
+ "version": "3.9.9"
},
"widgets": {
"state": {},
diff --git a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.py b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.py
index ef59ed81d24..eedca040807 100644
--- a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.py
+++ b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.py
@@ -1,10 +1,7 @@
-from mesa import Agent, Model
-from mesa.time import RandomActivation
-from mesa.space import MultiGrid
-from mesa.datacollection import DataCollector
+import mesa
-class {{cookiecutter.agent}}(Agent): # noqa
+class {{cookiecutter.agent}}(mesa.Agent): # noqa
"""
An agent
"""
@@ -24,7 +21,7 @@ def step(self):
pass
-class {{cookiecutter.model}}(Model):
+class {{cookiecutter.model}}(mesa.Model):
"""
The model class holds the model-level attributes, manages the agents, and generally handles
the global level of our model.
@@ -38,8 +35,8 @@ class {{cookiecutter.model}}(Model):
def __init__(self, num_agents, width, height):
super().__init__()
self.num_agents = num_agents
- self.schedule = RandomActivation(self)
- self.grid = MultiGrid(width=width, height=height, torus=True)
+ self.schedule = mesa.time.RandomActivation(self)
+ self.grid = mesa.space.MultiGrid(width=width, height=height, torus=True)
for i in range(self.num_agents):
agent = {{cookiecutter.agent}}(i, self)
@@ -50,7 +47,7 @@ def __init__(self, num_agents, width, height):
self.grid.place_agent(agent, (x, y))
# example data collector
- self.datacollector = DataCollector()
+ self.datacollector = mesa.datacollection.DataCollector()
self.running = True
self.datacollector.collect(self)
diff --git a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.py b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.py
index aa8e8a9fe83..832f2df7db4 100644
--- a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.py
+++ b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.py
@@ -4,8 +4,7 @@
from .model import {{ cookiecutter.model }}, {{ cookiecutter.agent }} # noqa
-from mesa.visualization.ModularVisualization import ModularServer
-from mesa.visualization.modules import CanvasGrid, ChartModule
+import mesa
def circle_portrayal_example(agent):
@@ -22,12 +21,14 @@ def circle_portrayal_example(agent):
return portrayal
-canvas_element = CanvasGrid(circle_portrayal_example, 20, 20, 500, 500)
-chart_element = ChartModule([{"Label": "{{ cookiecutter.camel }}", "Color": "Pink"}])
+canvas_element = mesa.visualization.CanvasGrid(
+ circle_portrayal_example, 20, 20, 500, 500
+)
+chart_element = mesa.visualization.ChartModule([{"Label": "{{ cookiecutter.camel }}", "Color": "Pink"}])
model_kwargs = {"num_agents": 10, "width": 10, "height": 10}
-server = ModularServer(
+server = mesa.visualization.ModularServer(
{{cookiecutter.model}},
[canvas_element, chart_element],
"{{ cookiecutter.camel }}",
From bd26c2f718e1d7fd18bc94b221b86c95c2007926 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Wed, 16 Nov 2022 02:44:35 +0100
Subject: [PATCH 024/214] perf: Refactor iter_cell_list_contents (#1527)
---
mesa/space.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index a0586a45614..e36acc48a30 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -388,10 +388,8 @@ def iter_cell_list_contents(
Returns:
An iterator of the contents of the cells identified in cell_list
"""
- # Note: filter(None, iterator) filters away an element of iterator that
- # is falsy. Hence, iter_cell_list_contents returns only non-empty
- # contents.
- return filter(None, (self.grid[x][y] for x, y in cell_list))
+ # iter_cell_list_contents returns only non-empty contents.
+ return (self.grid[x][y] for x, y in cell_list if self.grid[x][y])
@accept_tuple_argument
def get_cell_list_contents(self, cell_list: Iterable[Coordinate]) -> list[Agent]:
From 5a8d5ccb47cb7cebd71ba1bb497e5747393e651b Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Fri, 18 Nov 2022 02:15:40 +0100
Subject: [PATCH 025/214] Enhance schedulers to support intra-step removal of
agents (#1523)
* Enhance schedulers to support intra-step removal of agents
* Update test_time.py
---
mesa/time.py | 15 +++++++--------
tests/test_time.py | 43 ++++++++++++++++++++++++++++++++++++-------
2 files changed, 43 insertions(+), 15 deletions(-)
diff --git a/mesa/time.py b/mesa/time.py
index 54f6e25acf1..4c332e3e317 100644
--- a/mesa/time.py
+++ b/mesa/time.py
@@ -61,7 +61,6 @@ def add(self, agent: Agent) -> None:
agent: An Agent to be added to the schedule. NOTE: The agent must
have a step() method.
"""
-
if agent.unique_id in self._agents:
raise Exception(
f"Agent with unique id {repr(agent.unique_id)} already added to scheduler"
@@ -102,9 +101,9 @@ def agent_buffer(self, shuffled: bool = False) -> Iterator[Agent]:
if shuffled:
self.model.random.shuffle(agent_keys)
- for key in agent_keys:
- if key in self._agents:
- yield self._agents[key]
+ for agent_key in agent_keys:
+ if agent_key in self._agents:
+ yield self._agents[agent_key]
class RandomActivation(BaseScheduler):
@@ -197,7 +196,8 @@ def step(self) -> None:
self.model.random.shuffle(agent_keys)
for stage in self.stage_list:
for agent_key in agent_keys:
- getattr(self._agents[agent_key], stage)() # Run stage
+ if agent_key in self._agents:
+ getattr(self._agents[agent_key], stage)() # Run stage
# We recompute the keys because some agents might have been removed
# in the previous loop.
agent_keys = list(self._agents.keys())
@@ -239,7 +239,6 @@ def add(self, agent: Agent) -> None:
Args:
agent: An Agent to be added to the schedule.
"""
-
super().add(agent)
agent_class: type[Agent] = type(agent)
self.agents_by_type[agent_class][agent.unique_id] = agent
@@ -248,7 +247,6 @@ def remove(self, agent: Agent) -> None:
"""
Remove all instances of a given agent from the schedule.
"""
-
del self._agents[agent.unique_id]
agent_class: type[Agent] = type(agent)
@@ -286,7 +284,8 @@ def step_type(self, type_class: type[Agent], shuffle_agents: bool = True) -> Non
if shuffle_agents:
self.model.random.shuffle(agent_keys)
for agent_key in agent_keys:
- self.agents_by_type[type_class][agent_key].step()
+ if agent_key in self.agents_by_type[type_class]:
+ self.agents_by_type[type_class][agent_key].step()
def get_type_count(self, type_class: type[Agent]) -> int:
"""
diff --git a/tests/test_time.py b/tests/test_time.py
index d92ae04c37c..7bf198e935c 100644
--- a/tests/test_time.py
+++ b/tests/test_time.py
@@ -29,7 +29,15 @@ def __init__(self, unique_id, model):
self.steps = 0
self.advances = 0
+ def kill_other_agent(self):
+ for agent in self.model.schedule.agents:
+ if agent is not self:
+ self.model.schedule.remove(agent)
+ break
+
def stage_one(self):
+ if self.model.enable_kill_other_agent:
+ self.kill_other_agent()
self.model.log.append(self.unique_id + "_1")
def stage_two(self):
@@ -39,11 +47,14 @@ def advance(self):
self.advances += 1
def step(self):
+ if self.model.enable_kill_other_agent:
+ self.kill_other_agent()
self.steps += 1
+ self.model.log.append(self.unique_id)
class MockModel(Model):
- def __init__(self, shuffle=False, activation=STAGED):
+ def __init__(self, shuffle=False, activation=STAGED, enable_kill_other_agent=False):
"""
Creates a Model instance with a schedule
@@ -59,6 +70,7 @@ def __init__(self, shuffle=False, activation=STAGED):
The default scheduler is a BaseScheduler.
"""
self.log = []
+ self.enable_kill_other_agent = enable_kill_other_agent
# Make scheduler
if activation == STAGED:
@@ -91,7 +103,7 @@ class TestStagedActivation(TestCase):
def test_no_shuffle(self):
"""
- Testing staged activation without shuffling.
+ Testing the staged activation without shuffling.
"""
model = MockModel(shuffle=False)
model.step()
@@ -100,7 +112,7 @@ def test_no_shuffle(self):
def test_shuffle(self):
"""
- Test staged activation with shuffling
+ Test the staged activation with shuffling
"""
model = MockModel(shuffle=True)
model.step()
@@ -118,7 +130,7 @@ def test_shuffle_shuffles_agents(self):
def test_remove(self):
"""
- Test staged activation can remove an agent
+ Test the staged activation can remove an agent
"""
model = MockModel(shuffle=True)
agent_keys = list(model.schedule._agents.keys())
@@ -126,6 +138,15 @@ def test_remove(self):
model.schedule.remove(agent)
assert agent not in model.schedule.agents
+ def test_intrastep_remove(self):
+ """
+ Test the staged activation can remove an agent in a
+ step of another agent so that the one removed doesn't step.
+ """
+ model = MockModel(shuffle=True, enable_kill_other_agent=True)
+ model.step()
+ assert len(model.log) == 2
+
def test_add_existing_agent(self):
model = MockModel()
agent = model.schedule.agents[0]
@@ -162,13 +183,21 @@ def test_random_activation_step_steps_each_agent(self):
"""
Test the random activation step causes each agent to step
"""
-
model = MockModel(activation=RANDOM)
model.step()
agent_steps = [i.steps for i in model.schedule.agents]
# one step for each of 2 agents
assert all(map(lambda x: x == 1, agent_steps))
+ def test_intrastep_remove(self):
+ """
+ Test the random activation can remove an agent in a
+ step of another agent so that the one removed doesn't step.
+ """
+ model = MockModel(activation=RANDOM, enable_kill_other_agent=True)
+ model.step()
+ assert len(model.log) == 1
+
class TestSimultaneousActivation(TestCase):
"""
@@ -233,8 +262,8 @@ def test_add_non_unique_ids(self):
RandomActivationByType.
"""
model = MockModel(activation=RANDOM_BY_TYPE)
- a = MockAgent(0, None)
- b = MockAgent(0, None)
+ a = MockAgent(0, model)
+ b = MockAgent(0, model)
model.schedule.add(a)
with self.assertRaises(Exception):
model.schedule.add(b)
From 1db2468e923b073115ccb528eb482c63e5223d00 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Thu, 17 Nov 2022 18:03:49 +0100
Subject: [PATCH 026/214] Simplify accept_tuple_argument decorator in space.py
---
mesa/space.py | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index e36acc48a30..86e99a32cba 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -64,11 +64,11 @@ def accept_tuple_argument(wrapped_function: F) -> F:
to also handle a single position, by automatically wrapping tuple in
single-item list rather than forcing user to do it."""
- def wrapper(*args: Any) -> Any:
- if isinstance(args[1], tuple) and len(args[1]) == 2:
- return wrapped_function(args[0], [args[1]])
+ def wrapper(grid_instance, positions) -> Any:
+ if isinstance(positions, tuple) and len(positions) == 2:
+ return wrapped_function(grid_instance, [positions])
else:
- return wrapped_function(*args)
+ return wrapped_function(grid_instance, positions)
return cast(F, wrapper)
From 63be75fc57e18f6b8ab8d36a0313c31ebcd31590 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Mon, 21 Nov 2022 10:53:03 +0100
Subject: [PATCH 027/214] Improve docstrings of ContinuousSpace (#1535)
* Improve docstrings of ContinuousSpace
* get_heading: Tweak docstring
Co-authored-by: rht
---
mesa/space.py | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index 86e99a32cba..3e9ec516f34 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -807,16 +807,14 @@ def get_neighbors(
class ContinuousSpace:
"""Continuous space where each agent can have an arbitrary position.
- Assumes that all agents are point objects, and have a pos property storing
- their position as an (x, y) tuple.
+ Assumes that all agents have a pos property storing their position as
+ an (x, y) tuple.
- This class uses a numpy array internally to store agent objects, to speed
+ This class uses a numpy array internally to store agents in order to speed
up neighborhood lookups. This array is calculated on the first neighborhood
- lookup, and is reused (and updated) until agents are added or removed.
+ lookup, and is updated if agents are added or removed.
"""
- _grid = None
-
def __init__(
self,
x_max: float,
@@ -849,7 +847,7 @@ def __init__(
self._agent_to_index: dict[Agent, int | None] = {}
def _build_agent_cache(self):
- """Cache Agent positions to speed up neighbors calculations."""
+ """Cache agents positions to speed up neighbors calculations."""
self._index_to_agent = {}
agents = self._agent_to_index.keys()
for idx, agent in enumerate(agents):
@@ -860,7 +858,7 @@ def _build_agent_cache(self):
)
def _invalidate_agent_cache(self):
- """Clear cached data of Agents and positions in the space."""
+ """Clear cached data of agents and positions in the space."""
self._agent_points = None
self._index_to_agent = {}
@@ -894,7 +892,7 @@ def move_agent(self, agent: Agent, pos: FloatCoordinate) -> None:
self._agent_points[idx, 1] = pos[1]
def remove_agent(self, agent: Agent) -> None:
- """Remove an agent from the simulation.
+ """Remove an agent from the space.
Args:
agent: The agent object to remove
@@ -909,7 +907,7 @@ def remove_agent(self, agent: Agent) -> None:
def get_neighbors(
self, pos: FloatCoordinate, radius: float, include_center: bool = True
) -> list[Agent]:
- """Get all objects within a certain radius.
+ """Get all agents within a certain radius.
Args:
pos: (x,y) coordinate tuple to center the search at.
@@ -936,7 +934,9 @@ def get_neighbors(
def get_heading(
self, pos_1: FloatCoordinate, pos_2: FloatCoordinate
) -> FloatCoordinate:
- """Get the heading angle between two points, accounting for toroidal space.
+ """Get the heading vector between two points, accounting for toroidal space.
+ It is possible to calculate the heading angle by applying the atan2 function to the
+ result.
Args:
pos_1, pos_2: Coordinate tuples for both points.
From c9f10a64be30247923a6381fdb378a75285fac52 Mon Sep 17 00:00:00 2001
From: Tortar <68152031+Tortar@users.noreply.github.com>
Date: Thu, 24 Nov 2022 02:16:42 +0100
Subject: [PATCH 028/214] Simplify code in ContinuousSpace (#1536)
* Simplify code in ContinuousSpace
* Update space.py
---
mesa/space.py | 13 +++++--------
1 file changed, 5 insertions(+), 8 deletions(-)
diff --git a/mesa/space.py b/mesa/space.py
index 3e9ec516f34..42dd29e3572 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -849,13 +849,11 @@ def __init__(
def _build_agent_cache(self):
"""Cache agents positions to speed up neighbors calculations."""
self._index_to_agent = {}
- agents = self._agent_to_index.keys()
- for idx, agent in enumerate(agents):
+ for idx, agent in enumerate(self._agent_to_index):
self._agent_to_index[agent] = idx
self._index_to_agent[idx] = agent
- self._agent_points = np.array(
- [self._index_to_agent[idx].pos for idx in range(len(agents))]
- )
+ # Since dicts are ordered by insertion, we can iterate through agents keys
+ self._agent_points = np.array([agent.pos for agent in self._agent_to_index])
def _invalidate_agent_cache(self):
"""Clear cached data of agents and positions in the space."""
@@ -888,8 +886,7 @@ def move_agent(self, agent: Agent, pos: FloatCoordinate) -> None:
# instead of invalidating the full cache,
# apply the move to the cached values
idx = self._agent_to_index[agent]
- self._agent_points[idx, 0] = pos[0]
- self._agent_points[idx, 1] = pos[1]
+ self._agent_points[idx] = pos
def remove_agent(self, agent: Agent) -> None:
"""Remove an agent from the space.
@@ -899,7 +896,7 @@ def remove_agent(self, agent: Agent) -> None:
"""
if agent not in self._agent_to_index:
raise Exception("Agent does not exist in the space")
- self._agent_to_index.pop(agent)
+ del self._agent_to_index[agent]
self._invalidate_agent_cache()
agent.pos = None
From 238c2c0bd6286970418bd1439bf0c136df665782 Mon Sep 17 00:00:00 2001
From: Wang Boyu
Date: Fri, 25 Nov 2022 19:39:21 -0500
Subject: [PATCH 029/214] fix tutorial url in examples
---
examples/boltzmann_wealth_model/Readme.md | 6 +++---
examples/boltzmann_wealth_model_network/README.md | 4 ++--
examples/virus_on_network/README.md | 2 +-
3 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/examples/boltzmann_wealth_model/Readme.md b/examples/boltzmann_wealth_model/Readme.md
index d8f96dcabd4..785a0946a24 100644
--- a/examples/boltzmann_wealth_model/Readme.md
+++ b/examples/boltzmann_wealth_model/Readme.md
@@ -2,7 +2,7 @@
## 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](http://mesa.readthedocs.io/en/latest/intro-tutorial.html).
+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.
@@ -10,7 +10,7 @@ As the model runs, the distribution of wealth among agents goes from being perfe
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](http://mesa.readthedocs.io/en/latest/intro-tutorial.html#adding-visualization), run:
+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
@@ -28,7 +28,7 @@ If your browser doesn't open automatically, point it to [http://127.0.0.1:8521/]
## Further Reading
The full tutorial describing how the model is built can be found at:
-http://mesa.readthedocs.io/en/latest/intro-tutorial.html
+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:
diff --git a/examples/boltzmann_wealth_model_network/README.md b/examples/boltzmann_wealth_model_network/README.md
index 8a33d096e06..cd3bcd8df00 100644
--- a/examples/boltzmann_wealth_model_network/README.md
+++ b/examples/boltzmann_wealth_model_network/README.md
@@ -4,7 +4,7 @@
This is the same Boltzmann Wealth Model, but with a network grid implementation.
-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](http://mesa.readthedocs.io/en/latest/intro-tutorial.html).
+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).
In this network implementation, agents must be located on a node, with a limit of one agent per node. In order to give or receive the unit of money, the agent must be directly connected to the other agent (there must be a direct link between the nodes).
@@ -39,7 +39,7 @@ Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and p
## Further Reading
The full tutorial describing how the model is built can be found at:
-http://mesa.readthedocs.io/en/master/tutorials/intro_tutorial.html
+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:
diff --git a/examples/virus_on_network/README.md b/examples/virus_on_network/README.md
index b6e989217e0..b9fd1e94ecb 100644
--- a/examples/virus_on_network/README.md
+++ b/examples/virus_on_network/README.md
@@ -35,7 +35,7 @@ Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and p
## Further Reading
The full tutorial describing how the model is built can be found at:
-http://mesa.readthedocs.io/en/master/tutorials/intro_tutorial.html
+https://mesa.readthedocs.io/en/latest/tutorials/intro_tutorial.html
[Stonedahl, F. and Wilensky, U. (2008). NetLogo Virus on a Network model](http://ccl.northwestern.edu/netlogo/models/VirusonaNetwork).
From 7346715ae8d7af40c177ddcaed8eb470965af1f2 Mon Sep 17 00:00:00 2001
From: jackiekazil
Date: Sun, 4 Dec 2022 21:06:40 -0500
Subject: [PATCH 030/214] Move examples from mesa repo to mesa-examples repo.
---
examples/Readme.md | 57 --
examples/bank_reserves/Readme.md | 58 --
.../bank_reserves/bank_reserves/agents.py | 182 -----
examples/bank_reserves/bank_reserves/model.py | 163 -----
.../bank_reserves/random_walk.py | 47 --
.../bank_reserves/bank_reserves/server.py | 90 ---
examples/bank_reserves/batch_run.py | 221 -------
examples/bank_reserves/requirements.txt | 4 -
examples/bank_reserves/run.py | 3 -
examples/boid_flockers/Flocker Test.ipynb | 114 ----
examples/boid_flockers/Readme.md | 34 -
.../boid_flockers/SimpleContinuousModule.py | 32 -
examples/boid_flockers/boid_flockers/boid.py | 104 ---
examples/boid_flockers/boid_flockers/model.py | 76 ---
.../boid_flockers/boid_flockers/server.py | 23 -
.../boid_flockers/simple_continuous_canvas.js | 79 ---
examples/boid_flockers/requirements.txt | 3 -
examples/boid_flockers/run.py | 3 -
examples/boltzmann_wealth_model/Readme.md | 39 --
.../boltzmann_wealth_model/__init__.py | 0
.../boltzmann_wealth_model/model.py | 73 --
.../boltzmann_wealth_model/server.py | 40 --
.../boltzmann_wealth_model/requirements.txt | 4 -
examples/boltzmann_wealth_model/run.py | 3 -
.../boltzmann_wealth_model_network/README.md | 48 --
.../__init__.py | 0
.../boltzmann_wealth_model_network/model.py | 79 ---
.../boltzmann_wealth_model_network/server.py | 58 --
.../requirements.txt | 5 -
.../boltzmann_wealth_model_network/run.py | 3 -
examples/charts/Readme.md | 40 --
examples/charts/charts/agents.py | 182 -----
examples/charts/charts/model.py | 147 -----
examples/charts/charts/random_walk.py | 47 --
examples/charts/charts/server.py | 113 ----
examples/charts/requirements.txt | 4 -
examples/charts/run.py | 3 -
examples/color_patches/Readme.md | 38 --
.../color_patches/color_patches/__init__.py | 0
examples/color_patches/color_patches/model.py | 129 ----
.../color_patches/color_patches/server.py | 67 --
examples/color_patches/requirements.txt | 1 -
examples/color_patches/run.py | 3 -
examples/conways_game_of_life/Readme.md | 30 -
.../conways_game_of_life/cell.py | 53 --
.../conways_game_of_life/model.py | 43 --
.../conways_game_of_life/portrayal.py | 19 -
.../conways_game_of_life/server.py | 12 -
.../conways_game_of_life/requirements.txt | 1 -
examples/conways_game_of_life/run.py | 3 -
.../Epstein Civil Violence.ipynb | 119 ----
examples/epstein_civil_violence/Readme.md | 33 -
.../epstein_civil_violence/agent.py | 184 ------
.../epstein_civil_violence/model.py | 141 ----
.../epstein_civil_violence/portrayal.py | 33 -
.../epstein_civil_violence/server.py | 54 --
.../epstein_civil_violence/requirements.txt | 3 -
examples/epstein_civil_violence/run.py | 3 -
examples/forest_fire/Forest Fire Model.ipynb | 623 ------------------
examples/forest_fire/forest_fire/__init__.py | 0
examples/forest_fire/forest_fire/agent.py | 36 -
examples/forest_fire/forest_fire/model.py | 66 --
examples/forest_fire/forest_fire/server.py | 36 -
examples/forest_fire/readme.md | 41 --
examples/forest_fire/requirements.txt | 3 -
examples/forest_fire/run.py | 3 -
examples/hex_snowflake/Readme.md | 27 -
examples/hex_snowflake/hex_snowflake/cell.py | 58 --
examples/hex_snowflake/hex_snowflake/model.py | 46 --
.../hex_snowflake/hex_snowflake/portrayal.py | 18 -
.../hex_snowflake/hex_snowflake/server.py | 13 -
examples/hex_snowflake/requirements.txt | 1 -
examples/hex_snowflake/run.py | 3 -
examples/pd_grid/analysis.ipynb | 231 -------
examples/pd_grid/pd_grid/__init__.py | 0
examples/pd_grid/pd_grid/agent.py | 49 --
examples/pd_grid/pd_grid/model.py | 62 --
examples/pd_grid/pd_grid/portrayal.py | 19 -
examples/pd_grid/pd_grid/server.py | 22 -
examples/pd_grid/readme.md | 42 --
examples/pd_grid/requirements.txt | 3 -
examples/pd_grid/run.py | 3 -
examples/schelling/README.md | 49 --
examples/schelling/analysis.ipynb | 457 -------------
examples/schelling/model.py | 89 ---
examples/schelling/requirements.txt | 3 -
examples/schelling/run.py | 3 -
examples/schelling/run_ascii.py | 49 --
examples/schelling/server.py | 46 --
examples/shape_example/Readme.md | 41 --
examples/shape_example/requirements.txt | 1 -
examples/shape_example/run.py | 3 -
examples/shape_example/shape_example/model.py | 39 --
.../shape_example/shape_example/server.py | 44 --
examples/sugarscape_cg/Readme.md | 61 --
examples/sugarscape_cg/requirements.txt | 2 -
examples/sugarscape_cg/run.py | 3 -
.../sugarscape_cg/sugarscape_cg/__init__.py | 0
.../sugarscape_cg/sugarscape_cg/agents.py | 83 ---
examples/sugarscape_cg/sugarscape_cg/model.py | 93 ---
.../sugarscape_cg/resources/ant.png | Bin 66107 -> 0 bytes
.../sugarscape_cg/sugarscape_cg/server.py | 41 --
.../sugarscape_cg/sugarscape_cg/sugar-map.txt | 50 --
examples/virus_on_network/README.md | 46 --
examples/virus_on_network/requirements.txt | 1 -
examples/virus_on_network/run.py | 3 -
.../virus_on_network/__init__.py | 0
.../virus_on_network/model.py | 160 -----
.../virus_on_network/server.py | 133 ----
examples/wolf_sheep/Readme.md | 57 --
examples/wolf_sheep/requirements.txt | 1 -
examples/wolf_sheep/run.py | 3 -
examples/wolf_sheep/wolf_sheep/__init__.py | 0
examples/wolf_sheep/wolf_sheep/agents.py | 120 ----
examples/wolf_sheep/wolf_sheep/model.py | 166 -----
examples/wolf_sheep/wolf_sheep/random_walk.py | 41 --
.../wolf_sheep/wolf_sheep/resources/sheep.png | Bin 1322 -> 0 bytes
.../wolf_sheep/wolf_sheep/resources/wolf.png | Bin 1473 -> 0 bytes
examples/wolf_sheep/wolf_sheep/scheduler.py | 28 -
examples/wolf_sheep/wolf_sheep/server.py | 79 ---
.../wolf_sheep/wolf_sheep/test_random_walk.py | 82 ---
121 files changed, 6632 deletions(-)
delete mode 100644 examples/Readme.md
delete mode 100644 examples/bank_reserves/Readme.md
delete mode 100644 examples/bank_reserves/bank_reserves/agents.py
delete mode 100644 examples/bank_reserves/bank_reserves/model.py
delete mode 100644 examples/bank_reserves/bank_reserves/random_walk.py
delete mode 100644 examples/bank_reserves/bank_reserves/server.py
delete mode 100644 examples/bank_reserves/batch_run.py
delete mode 100644 examples/bank_reserves/requirements.txt
delete mode 100644 examples/bank_reserves/run.py
delete mode 100644 examples/boid_flockers/Flocker Test.ipynb
delete mode 100644 examples/boid_flockers/Readme.md
delete mode 100644 examples/boid_flockers/boid_flockers/SimpleContinuousModule.py
delete mode 100644 examples/boid_flockers/boid_flockers/boid.py
delete mode 100644 examples/boid_flockers/boid_flockers/model.py
delete mode 100644 examples/boid_flockers/boid_flockers/server.py
delete mode 100644 examples/boid_flockers/boid_flockers/simple_continuous_canvas.js
delete mode 100644 examples/boid_flockers/requirements.txt
delete mode 100644 examples/boid_flockers/run.py
delete mode 100644 examples/boltzmann_wealth_model/Readme.md
delete mode 100644 examples/boltzmann_wealth_model/boltzmann_wealth_model/__init__.py
delete mode 100644 examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py
delete mode 100644 examples/boltzmann_wealth_model/boltzmann_wealth_model/server.py
delete mode 100644 examples/boltzmann_wealth_model/requirements.txt
delete mode 100644 examples/boltzmann_wealth_model/run.py
delete mode 100644 examples/boltzmann_wealth_model_network/README.md
delete mode 100644 examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/__init__.py
delete mode 100644 examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py
delete mode 100644 examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/server.py
delete mode 100644 examples/boltzmann_wealth_model_network/requirements.txt
delete mode 100644 examples/boltzmann_wealth_model_network/run.py
delete mode 100644 examples/charts/Readme.md
delete mode 100644 examples/charts/charts/agents.py
delete mode 100644 examples/charts/charts/model.py
delete mode 100644 examples/charts/charts/random_walk.py
delete mode 100644 examples/charts/charts/server.py
delete mode 100644 examples/charts/requirements.txt
delete mode 100644 examples/charts/run.py
delete mode 100644 examples/color_patches/Readme.md
delete mode 100644 examples/color_patches/color_patches/__init__.py
delete mode 100644 examples/color_patches/color_patches/model.py
delete mode 100644 examples/color_patches/color_patches/server.py
delete mode 100644 examples/color_patches/requirements.txt
delete mode 100644 examples/color_patches/run.py
delete mode 100644 examples/conways_game_of_life/Readme.md
delete mode 100644 examples/conways_game_of_life/conways_game_of_life/cell.py
delete mode 100644 examples/conways_game_of_life/conways_game_of_life/model.py
delete mode 100644 examples/conways_game_of_life/conways_game_of_life/portrayal.py
delete mode 100644 examples/conways_game_of_life/conways_game_of_life/server.py
delete mode 100644 examples/conways_game_of_life/requirements.txt
delete mode 100644 examples/conways_game_of_life/run.py
delete mode 100644 examples/epstein_civil_violence/Epstein Civil Violence.ipynb
delete mode 100644 examples/epstein_civil_violence/Readme.md
delete mode 100644 examples/epstein_civil_violence/epstein_civil_violence/agent.py
delete mode 100644 examples/epstein_civil_violence/epstein_civil_violence/model.py
delete mode 100644 examples/epstein_civil_violence/epstein_civil_violence/portrayal.py
delete mode 100644 examples/epstein_civil_violence/epstein_civil_violence/server.py
delete mode 100644 examples/epstein_civil_violence/requirements.txt
delete mode 100644 examples/epstein_civil_violence/run.py
delete mode 100644 examples/forest_fire/Forest Fire Model.ipynb
delete mode 100644 examples/forest_fire/forest_fire/__init__.py
delete mode 100644 examples/forest_fire/forest_fire/agent.py
delete mode 100644 examples/forest_fire/forest_fire/model.py
delete mode 100644 examples/forest_fire/forest_fire/server.py
delete mode 100644 examples/forest_fire/readme.md
delete mode 100644 examples/forest_fire/requirements.txt
delete mode 100644 examples/forest_fire/run.py
delete mode 100644 examples/hex_snowflake/Readme.md
delete mode 100644 examples/hex_snowflake/hex_snowflake/cell.py
delete mode 100644 examples/hex_snowflake/hex_snowflake/model.py
delete mode 100644 examples/hex_snowflake/hex_snowflake/portrayal.py
delete mode 100644 examples/hex_snowflake/hex_snowflake/server.py
delete mode 100644 examples/hex_snowflake/requirements.txt
delete mode 100644 examples/hex_snowflake/run.py
delete mode 100644 examples/pd_grid/analysis.ipynb
delete mode 100644 examples/pd_grid/pd_grid/__init__.py
delete mode 100644 examples/pd_grid/pd_grid/agent.py
delete mode 100644 examples/pd_grid/pd_grid/model.py
delete mode 100644 examples/pd_grid/pd_grid/portrayal.py
delete mode 100644 examples/pd_grid/pd_grid/server.py
delete mode 100644 examples/pd_grid/readme.md
delete mode 100644 examples/pd_grid/requirements.txt
delete mode 100644 examples/pd_grid/run.py
delete mode 100644 examples/schelling/README.md
delete mode 100644 examples/schelling/analysis.ipynb
delete mode 100644 examples/schelling/model.py
delete mode 100644 examples/schelling/requirements.txt
delete mode 100644 examples/schelling/run.py
delete mode 100644 examples/schelling/run_ascii.py
delete mode 100644 examples/schelling/server.py
delete mode 100644 examples/shape_example/Readme.md
delete mode 100644 examples/shape_example/requirements.txt
delete mode 100644 examples/shape_example/run.py
delete mode 100644 examples/shape_example/shape_example/model.py
delete mode 100644 examples/shape_example/shape_example/server.py
delete mode 100644 examples/sugarscape_cg/Readme.md
delete mode 100644 examples/sugarscape_cg/requirements.txt
delete mode 100644 examples/sugarscape_cg/run.py
delete mode 100644 examples/sugarscape_cg/sugarscape_cg/__init__.py
delete mode 100644 examples/sugarscape_cg/sugarscape_cg/agents.py
delete mode 100644 examples/sugarscape_cg/sugarscape_cg/model.py
delete mode 100644 examples/sugarscape_cg/sugarscape_cg/resources/ant.png
delete mode 100644 examples/sugarscape_cg/sugarscape_cg/server.py
delete mode 100644 examples/sugarscape_cg/sugarscape_cg/sugar-map.txt
delete mode 100644 examples/virus_on_network/README.md
delete mode 100644 examples/virus_on_network/requirements.txt
delete mode 100644 examples/virus_on_network/run.py
delete mode 100644 examples/virus_on_network/virus_on_network/__init__.py
delete mode 100644 examples/virus_on_network/virus_on_network/model.py
delete mode 100644 examples/virus_on_network/virus_on_network/server.py
delete mode 100644 examples/wolf_sheep/Readme.md
delete mode 100644 examples/wolf_sheep/requirements.txt
delete mode 100644 examples/wolf_sheep/run.py
delete mode 100644 examples/wolf_sheep/wolf_sheep/__init__.py
delete mode 100644 examples/wolf_sheep/wolf_sheep/agents.py
delete mode 100644 examples/wolf_sheep/wolf_sheep/model.py
delete mode 100644 examples/wolf_sheep/wolf_sheep/random_walk.py
delete mode 100644 examples/wolf_sheep/wolf_sheep/resources/sheep.png
delete mode 100644 examples/wolf_sheep/wolf_sheep/resources/wolf.png
delete mode 100644 examples/wolf_sheep/wolf_sheep/scheduler.py
delete mode 100644 examples/wolf_sheep/wolf_sheep/server.py
delete mode 100644 examples/wolf_sheep/wolf_sheep/test_random_walk.py
diff --git a/examples/Readme.md b/examples/Readme.md
deleted file mode 100644
index dac81ed341e..00000000000
--- a/examples/Readme.md
+++ /dev/null
@@ -1,57 +0,0 @@
-# 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.
diff --git a/examples/bank_reserves/Readme.md b/examples/bank_reserves/Readme.md
deleted file mode 100644
index 27570d209b0..00000000000
--- a/examples/bank_reserves/Readme.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# Bank Reserves Model
-
-## Summary
-
-A highly abstracted, simplified model of an economy, with only one type of agent and a single bank representing all banks in an economy. People (represented by circles) move randomly within the grid. If two or more people are on the same grid location, there is a 50% chance that they will trade with each other. If they trade, there is an equal chance of giving the other agent $5 or $2. A positive trade balance will be deposited in the bank as savings. If trading results in a negative balance, the agent will try to withdraw from its savings to cover the balance. If it does not have enough savings to cover the negative balance, it will take out a loan from the bank to cover the difference. The bank is required to keep a certain percentage of deposits as reserves. If run.py is used to run the model, then the percent of deposits the bank is required to retain is a user settable parameter. The amount the bank is able to loan at any given time is a function of the amount of deposits, its reserves, and its current total outstanding loan amount.
-
-The model demonstrates the following Mesa features:
- - MultiGrid for creating shareable space for agents
- - DataCollector for collecting data on individual model runs
- - Slider for adjusting initial model parameters
- - ModularServer for visualization of agent interaction
- - Agent object inheritance
- - Using a BatchRunner to collect data on multiple combinations of model parameters
-
-## Installation
-
-To install the dependencies use pip and the requirements.txt in this directory. e.g.
-
-```
- $ pip install -r requirements.txt
-```
-
-## Interactive Model Run
-
-To run the model interactively, use `mesa runserver` in this directory:
-
-```
- $ mesa runserver
-```
-
-Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/), select the model parameters, press Reset, then Start.
-
-## Batch Run
-
-To run the model as a batch run to collect data on multiple combinations of model parameters, run "batch_run.py" in this directory.
-
-```
- $ python batch_run.py
-```
-A progress status bar will display.
-
-To update the parameters to test other parameter sweeps, edit the list of parameters in the dictionary named "br_params" in "batch_run.py".
-
-## Files
-
-* ``bank_reserves/random_walker.py``: This defines a class that inherits from the Mesa Agent class. The main purpose is to provide a method for agents to move randomly one cell at a time.
-* ``bank_reserves/agents.py``: Defines the People and Bank classes.
-* ``bank_reserves/model.py``: Defines the Bank Reserves model and the DataCollector functions.
-* ``bank_reserves/server.py``: Sets up the interactive visualization server.
-* ``run.py``: Launches a model visualization server.
-* ``batch_run.py``: Basically the same as model.py, but includes a Mesa BatchRunner. The result of the batch run will be a .csv file with the data from every step of every run.
-
-## Further Reading
-
-This model is a Mesa implementation of the Bank Reserves model from NetLogo:
-
-Wilensky, U. (1998). NetLogo Bank Reserves model. http://ccl.northwestern.edu/netlogo/models/BankReserves. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
-
diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/bank_reserves/agents.py
deleted file mode 100644
index d59390df084..00000000000
--- a/examples/bank_reserves/bank_reserves/agents.py
+++ /dev/null
@@ -1,182 +0,0 @@
-"""
-The following code was adapted from the Bank Reserves model included in Netlogo
-Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves
-Accessed on: November 2, 2017
-Author of NetLogo code:
- Wilensky, U. (1998). NetLogo Bank Reserves model.
- http://ccl.northwestern.edu/netlogo/models/BankReserves.
- Center for Connected Learning and Computer-Based Modeling,
- Northwestern University, Evanston, IL.
-"""
-
-import mesa
-
-from bank_reserves.random_walk import RandomWalker
-
-
-class Bank(mesa.Agent):
- def __init__(self, unique_id, model, reserve_percent=50):
- # initialize the parent class with required parameters
- super().__init__(unique_id, model)
- # for tracking total value of loans outstanding
- self.bank_loans = 0
- """percent of deposits the bank must keep in reserves - this is set via
- Slider in server.py"""
- self.reserve_percent = reserve_percent
- # for tracking total value of deposits
- self.deposits = 0
- # total amount of deposits in reserve
- self.reserves = (self.reserve_percent / 100) * self.deposits
- # amount the bank is currently able to loan
- self.bank_to_loan = 0
-
- """update the bank's reserves and amount it can loan;
- this is called every time a person balances their books
- see below for Person.balance_books()"""
-
- def bank_balance(self):
- self.reserves = (self.reserve_percent / 100) * self.deposits
- self.bank_to_loan = self.deposits - (self.reserves + self.bank_loans)
-
-
-# subclass of RandomWalker, which is subclass to Mesa Agent
-class Person(RandomWalker):
- def __init__(self, unique_id, pos, model, moore, bank, rich_threshold):
- # init parent class with required parameters
- super().__init__(unique_id, pos, model, moore=moore)
- # the amount each person has in savings
- self.savings = 0
- # total loan amount person has outstanding
- self.loans = 0
- """start everyone off with a random amount in their wallet from 1 to a
- user settable rich threshold amount"""
- self.wallet = self.random.randint(1, rich_threshold + 1)
- # savings minus loans, see balance_books() below
- self.wealth = 0
- # person to trade with, see do_business() below
- self.customer = 0
- # person's bank, set at __init__, all people have the same bank in this model
- self.bank = bank
-
- def do_business(self):
- """check if person has any savings, any money in wallet, or if the
- bank can loan them any money"""
- if self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0:
- # create list of people at my location (includes self)
- my_cell = self.model.grid.get_cell_list_contents([self.pos])
- # check if other people are at my location
- if len(my_cell) > 1:
- # set customer to self for while loop condition
- customer = self
- while customer == self:
- """select a random person from the people at my location
- to trade with"""
- customer = self.random.choice(my_cell)
- # 50% chance of trading with customer
- if self.random.randint(0, 1) == 0:
- # 50% chance of trading $5
- if self.random.randint(0, 1) == 0:
- # give customer $5 from my wallet (may result in negative wallet)
- customer.wallet += 5
- self.wallet -= 5
- # 50% chance of trading $2
- else:
- # give customer $2 from my wallet (may result in negative wallet)
- customer.wallet += 2
- self.wallet -= 2
-
- def balance_books(self):
- # check if wallet is negative from trading with customer
- if self.wallet < 0:
- # if negative money in wallet, check if my savings can cover the balance
- if self.savings >= (self.wallet * -1):
- """if my savings can cover the balance, withdraw enough
- money from my savings so that my wallet has a 0 balance"""
- self.withdraw_from_savings(self.wallet * -1)
- # if my savings cannot cover the negative balance of my wallet
- else:
- # check if i have any savings
- if self.savings > 0:
- """if i have savings, withdraw all of it to reduce my
- negative balance in my wallet"""
- self.withdraw_from_savings(self.savings)
- # record how much money the bank can loan out right now
- temp_loan = self.bank.bank_to_loan
- """check if the bank can loan enough money to cover the
- remaining negative balance in my wallet"""
- if temp_loan >= (self.wallet * -1):
- """if the bank can loan me enough money to cover
- the remaining negative balance in my wallet, take out a
- loan for the remaining negative balance"""
- self.take_out_loan(self.wallet * -1)
- else:
- """if the bank cannot loan enough money to cover the negative
- balance of my wallet, then take out a loan for the
- total amount the bank can loan right now"""
- self.take_out_loan(temp_loan)
- else:
- """if i have money in my wallet from trading with customer, deposit
- it to my savings in the bank"""
- self.deposit_to_savings(self.wallet)
- # check if i have any outstanding loans, and if i have savings
- if self.loans > 0 and self.savings > 0:
- # check if my savings can cover my outstanding loans
- if self.savings >= self.loans:
- # payoff my loans with my savings
- self.withdraw_from_savings(self.loans)
- self.repay_a_loan(self.loans)
- # if my savings won't cover my loans
- else:
- # pay off part of my loans with my savings
- self.withdraw_from_savings(self.savings)
- self.repay_a_loan(self.wallet)
- # calculate my wealth
- self.wealth = self.savings - self.loans
-
- # part of balance_books()
- def deposit_to_savings(self, amount):
- # take money from my wallet and put it in savings
- self.wallet -= amount
- self.savings += amount
- # increase bank deposits
- self.bank.deposits += amount
-
- # part of balance_books()
- def withdraw_from_savings(self, amount):
- # put money in my wallet from savings
- self.wallet += amount
- self.savings -= amount
- # decrease bank deposits
- self.bank.deposits -= amount
-
- # part of balance_books()
- def repay_a_loan(self, amount):
- # take money from my wallet to pay off all or part of a loan
- self.loans -= amount
- self.wallet -= amount
- # increase the amount the bank can loan right now
- self.bank.bank_to_loan += amount
- # decrease the bank's outstanding loans
- self.bank.bank_loans -= amount
-
- # part of balance_books()
- def take_out_loan(self, amount):
- """borrow from the bank to put money in my wallet, and increase my
- outstanding loans"""
- self.loans += amount
- self.wallet += amount
- # decresae the amount the bank can loan right now
- self.bank.bank_to_loan -= amount
- # increase the bank's outstanding loans
- self.bank.bank_loans += amount
-
- # step is called for each agent in model.BankReservesModel.schedule.step()
- def step(self):
- # move to a cell in my Moore neighborhood
- self.random_move()
- # trade
- self.do_business()
- # deposit money or take out a loan
- self.balance_books()
- # update the bank's reserves and the amount it can loan right now
- self.bank.bank_balance()
diff --git a/examples/bank_reserves/bank_reserves/model.py b/examples/bank_reserves/bank_reserves/model.py
deleted file mode 100644
index a27b1bdace6..00000000000
--- a/examples/bank_reserves/bank_reserves/model.py
+++ /dev/null
@@ -1,163 +0,0 @@
-"""
-The following code was adapted from the Bank Reserves model included in Netlogo
-Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves
-Accessed on: November 2, 2017
-Author of NetLogo code:
- Wilensky, U. (1998). NetLogo Bank Reserves model.
- http://ccl.northwestern.edu/netlogo/models/BankReserves.
- Center for Connected Learning and Computer-Based Modeling,
- Northwestern University, Evanston, IL.
-"""
-
-import mesa
-import numpy as np
-
-from bank_reserves.agents import Bank, Person
-
-"""
-If you want to perform a parameter sweep, call batch_run.py instead of run.py.
-For details see batch_run.py in the same directory as run.py.
-"""
-
-# Start of datacollector functions
-
-
-def get_num_rich_agents(model):
- """return number of rich agents"""
-
- rich_agents = [a for a in model.schedule.agents if a.savings > model.rich_threshold]
- return len(rich_agents)
-
-
-def get_num_poor_agents(model):
- """return number of poor agents"""
-
- poor_agents = [a for a in model.schedule.agents if a.loans > 10]
- return len(poor_agents)
-
-
-def get_num_mid_agents(model):
- """return number of middle class agents"""
-
- mid_agents = [
- a
- for a in model.schedule.agents
- if a.loans < 10 and a.savings < model.rich_threshold
- ]
- return len(mid_agents)
-
-
-def get_total_savings(model):
- """sum of all agents' savings"""
-
- agent_savings = [a.savings for a in model.schedule.agents]
- # return the sum of agents' savings
- return np.sum(agent_savings)
-
-
-def get_total_wallets(model):
- """sum of amounts of all agents' wallets"""
-
- agent_wallets = [a.wallet for a in model.schedule.agents]
- # return the sum of all agents' wallets
- return np.sum(agent_wallets)
-
-
-def get_total_money(model):
- # sum of all agents' wallets
- wallet_money = get_total_wallets(model)
- # sum of all agents' savings
- savings_money = get_total_savings(model)
- # return sum of agents' wallets and savings for total money
- return wallet_money + savings_money
-
-
-def get_total_loans(model):
- # list of amounts of all agents' loans
- agent_loans = [a.loans for a in model.schedule.agents]
- # return sum of all agents' loans
- return np.sum(agent_loans)
-
-
-class BankReserves(mesa.Model):
- """
- This model is a Mesa implementation of the Bank Reserves model from NetLogo.
- It is a highly abstracted, simplified model of an economy, with only one
- type of agent and a single bank representing all banks in an economy. People
- (represented by circles) move randomly within the grid. If two or more people
- are on the same grid location, there is a 50% chance that they will trade with
- each other. If they trade, there is an equal chance of giving the other agent
- $5 or $2. A positive trade balance will be deposited in the bank as savings.
- If trading results in a negative balance, the agent will try to withdraw from
- its savings to cover the balance. If it does not have enough savings to cover
- the negative balance, it will take out a loan from the bank to cover the
- difference. The bank is required to keep a certain percentage of deposits as
- reserves and the bank's ability to loan at any given time is a function of
- the amount of deposits, its reserves, and its current total outstanding loan
- amount.
- """
-
- # grid height
- grid_h = 20
- # grid width
- grid_w = 20
-
- """init parameters "init_people", "rich_threshold", and "reserve_percent"
- are all set via Slider"""
-
- def __init__(
- self,
- height=grid_h,
- width=grid_w,
- init_people=2,
- rich_threshold=10,
- reserve_percent=50,
- ):
- self.height = height
- self.width = width
- self.init_people = init_people
- self.schedule = mesa.time.RandomActivation(self)
- self.grid = mesa.space.MultiGrid(self.width, self.height, torus=True)
- # rich_threshold is the amount of savings a person needs to be considered "rich"
- self.rich_threshold = rich_threshold
- self.reserve_percent = reserve_percent
- # see datacollector functions above
- self.datacollector = mesa.DataCollector(
- model_reporters={
- "Rich": get_num_rich_agents,
- "Poor": get_num_poor_agents,
- "Middle Class": get_num_mid_agents,
- "Savings": get_total_savings,
- "Wallets": get_total_wallets,
- "Money": get_total_money,
- "Loans": get_total_loans,
- },
- agent_reporters={"Wealth": lambda x: x.wealth},
- )
-
- # create a single bank for the model
- self.bank = Bank(1, self, self.reserve_percent)
-
- # create people for the model according to number of people set by user
- for i in range(self.init_people):
- # set x, y coords randomly within the grid
- x = self.random.randrange(self.width)
- y = self.random.randrange(self.height)
- p = Person(i, (x, y), self, True, self.bank, self.rich_threshold)
- # place the Person object on the grid at coordinates (x, y)
- self.grid.place_agent(p, (x, y))
- # add the Person object to the model schedule
- self.schedule.add(p)
-
- self.running = True
- self.datacollector.collect(self)
-
- def step(self):
- # tell all the agents in the model to run their step function
- self.schedule.step()
- # collect data
- self.datacollector.collect(self)
-
- def run_model(self):
- for i in range(self.run_time):
- self.step()
diff --git a/examples/bank_reserves/bank_reserves/random_walk.py b/examples/bank_reserves/bank_reserves/random_walk.py
deleted file mode 100644
index 7e067881e4e..00000000000
--- a/examples/bank_reserves/bank_reserves/random_walk.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""
-Citation:
-The following code is a copy from random_walk.py at
-https://github.com/projectmesa/mesa/blob/main/examples/wolf_sheep/wolf_sheep/random_walk.py
-Accessed on: November 2, 2017
-Original Author: Jackie Kazil
-
-Generalized behavior for random walking, one grid cell at a time.
-"""
-
-import mesa
-
-
-class RandomWalker(mesa.Agent):
- """
- Class implementing random walker methods in a generalized manner.
- Not intended to be used on its own, but to inherit its methods to multiple
- other agents.
- """
-
- grid = None
- x = None
- y = None
- # use a Moore neighborhood
- moore = True
-
- def __init__(self, unique_id, pos, model, moore=True):
- """
- grid: The MultiGrid object in which the agent lives.
- x: The agent's current x coordinate
- y: The agent's current y coordinate
- moore: If True, may move in all 8 directions.
- Otherwise, only up, down, left, right.
- """
- super().__init__(unique_id, model)
- self.pos = pos
- self.moore = moore
-
- def random_move(self):
- """
- Step one cell in any allowable direction.
- """
- # Pick the next cell from the adjacent cells.
- next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True)
- next_move = self.random.choice(next_moves)
- # Now move:
- self.model.grid.move_agent(self, next_move)
diff --git a/examples/bank_reserves/bank_reserves/server.py b/examples/bank_reserves/bank_reserves/server.py
deleted file mode 100644
index c66cd920b9c..00000000000
--- a/examples/bank_reserves/bank_reserves/server.py
+++ /dev/null
@@ -1,90 +0,0 @@
-import mesa
-
-from bank_reserves.agents import Person
-from bank_reserves.model import BankReserves
-
-"""
-Citation:
-The following code was adapted from server.py at
-https://github.com/projectmesa/mesa/blob/main/examples/wolf_sheep/wolf_sheep/server.py
-Accessed on: November 2, 2017
-Author of original code: Taylor Mutch
-"""
-
-# The colors here are taken from Matplotlib's tab10 palette
-# Green
-RICH_COLOR = "#2ca02c"
-# Red
-POOR_COLOR = "#d62728"
-# Blue
-MID_COLOR = "#1f77b4"
-
-
-def person_portrayal(agent):
- if agent is None:
- return
-
- portrayal = {}
-
- # update portrayal characteristics for each Person object
- if isinstance(agent, Person):
- portrayal["Shape"] = "circle"
- portrayal["r"] = 0.5
- portrayal["Layer"] = 0
- portrayal["Filled"] = "true"
-
- color = MID_COLOR
-
- # set agent color based on savings and loans
- if agent.savings > agent.model.rich_threshold:
- color = RICH_COLOR
- if agent.savings < 10 and agent.loans < 10:
- color = MID_COLOR
- if agent.loans > 10:
- color = POOR_COLOR
-
- portrayal["Color"] = color
-
- return portrayal
-
-
-# dictionary of user settable parameters - these map to the model __init__ parameters
-model_params = {
- "init_people": mesa.visualization.Slider(
- "People", 25, 1, 200, description="Initial Number of People"
- ),
- "rich_threshold": mesa.visualization.Slider(
- "Rich Threshold",
- 10,
- 1,
- 20,
- description="Upper End of Random Initial Wallet Amount",
- ),
- "reserve_percent": mesa.visualization.Slider(
- "Reserves",
- 50,
- 1,
- 100,
- description="Percent of deposits the bank has to hold in reserve",
- ),
-}
-
-# set the portrayal function and size of the canvas for visualization
-canvas_element = mesa.visualization.CanvasGrid(person_portrayal, 20, 20, 500, 500)
-
-# map data to chart in the ChartModule
-chart_element = mesa.visualization.ChartModule(
- [
- {"Label": "Rich", "Color": RICH_COLOR},
- {"Label": "Poor", "Color": POOR_COLOR},
- {"Label": "Middle Class", "Color": MID_COLOR},
- ]
-)
-
-# create instance of Mesa ModularServer
-server = mesa.visualization.ModularServer(
- BankReserves,
- [canvas_element, chart_element],
- "Bank Reserves Model",
- model_params=model_params,
-)
diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py
deleted file mode 100644
index 768fe1876b8..00000000000
--- a/examples/bank_reserves/batch_run.py
+++ /dev/null
@@ -1,221 +0,0 @@
-"""
-The following code was adapted from the Bank Reserves model included in Netlogo
-Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves
-Accessed on: November 2, 2017
-Author of NetLogo code:
- Wilensky, U. (1998). NetLogo Bank Reserves model.
- http://ccl.northwestern.edu/netlogo/models/BankReserves.
- Center for Connected Learning and Computer-Based Modeling,
- Northwestern University, Evanston, IL.
-
-This version of the model has a BatchRunner at the bottom. This
-is for collecting data on parameter sweeps. It is not meant to
-be run with run.py, since run.py starts up a server for visualization, which
-isn't necessary for the BatchRunner. To run a parameter sweep, call
-batch_run.py in the command line.
-
-The BatchRunner is set up to collect step by step data of the model. It does
-this by collecting the DataCollector object in a model_reporter (i.e. the
-DataCollector is collecting itself every step).
-
-The end result of the batch run will be a CSV file created in the same
-directory from which Python was run. The CSV file will contain the data from
-every step of every run.
-"""
-
-import itertools
-
-import mesa
-import numpy as np
-import pandas as pd
-
-from bank_reserves.agents import Bank, Person
-
-# Start of datacollector functions
-
-
-def get_num_rich_agents(model):
- """list of rich agents"""
-
- rich_agents = [a for a in model.schedule.agents if a.savings > model.rich_threshold]
- # return number of rich agents
- return len(rich_agents)
-
-
-def get_num_poor_agents(model):
- """list of poor agents"""
-
- poor_agents = [a for a in model.schedule.agents if a.loans > 10]
- # return number of poor agents
- return len(poor_agents)
-
-
-def get_num_mid_agents(model):
- """list of middle class agents"""
-
- mid_agents = [
- a
- for a in model.schedule.agents
- if a.loans < 10 and a.savings < model.rich_threshold
- ]
- # return number of middle class agents
- return len(mid_agents)
-
-
-def get_total_savings(model):
- """list of amounts of all agents' savings"""
-
- agent_savings = [a.savings for a in model.schedule.agents]
- # return the sum of agents' savings
- return np.sum(agent_savings)
-
-
-def get_total_wallets(model):
- """list of amounts of all agents' wallets"""
-
- agent_wallets = [a.wallet for a in model.schedule.agents]
- # return the sum of all agents' wallets
- return np.sum(agent_wallets)
-
-
-def get_total_money(model):
- """sum of all agents' wallets"""
-
- wallet_money = get_total_wallets(model)
- # sum of all agents' savings
- savings_money = get_total_savings(model)
- # return sum of agents' wallets and savings for total money
- return wallet_money + savings_money
-
-
-def get_total_loans(model):
- """list of amounts of all agents' loans"""
-
- agent_loans = [a.loans for a in model.schedule.agents]
- # return sum of all agents' loans
- return np.sum(agent_loans)
-
-
-def track_params(model):
- return (model.init_people, model.rich_threshold, model.reserve_percent)
-
-
-def track_run(model):
- return model.uid
-
-
-class BankReservesModel(mesa.Model):
- # id generator to track run number in batch run data
- id_gen = itertools.count(1)
-
- # grid height
- grid_h = 20
- # grid width
- grid_w = 20
-
- """init parameters "init_people", "rich_threshold", and "reserve_percent"
- are all set via Slider"""
-
- def __init__(
- self,
- height=grid_h,
- width=grid_w,
- init_people=2,
- rich_threshold=10,
- reserve_percent=50,
- ):
- self.uid = next(self.id_gen)
- self.height = height
- self.width = width
- self.init_people = init_people
- self.schedule = mesa.time.RandomActivation(self)
- self.grid = mesa.space.MultiGrid(self.width, self.height, torus=True)
- # rich_threshold is the amount of savings a person needs to be considered "rich"
- self.rich_threshold = rich_threshold
- self.reserve_percent = reserve_percent
- # see datacollector functions above
- self.datacollector = mesa.DataCollector(
- model_reporters={
- "Rich": get_num_rich_agents,
- "Poor": get_num_poor_agents,
- "Middle Class": get_num_mid_agents,
- "Savings": get_total_savings,
- "Wallets": get_total_wallets,
- "Money": get_total_money,
- "Loans": get_total_loans,
- "Model Params": track_params,
- "Run": track_run,
- },
- agent_reporters={"Wealth": "wealth"},
- )
-
- # create a single bank for the model
- self.bank = Bank(1, self, self.reserve_percent)
-
- # create people for the model according to number of people set by user
- for i in range(self.init_people):
- # set x coordinate as a random number within the width of the grid
- x = self.random.randrange(self.width)
- # set y coordinate as a random number within the height of the grid
- y = self.random.randrange(self.height)
- p = Person(i, (x, y), self, True, self.bank, self.rich_threshold)
- # place the Person object on the grid at coordinates (x, y)
- self.grid.place_agent(p, (x, y))
- # add the Person object to the model schedule
- self.schedule.add(p)
-
- self.running = True
-
- def step(self):
- # collect data
- self.datacollector.collect(self)
- # tell all the agents in the model to run their step function
- self.schedule.step()
-
- def run_model(self):
- for i in range(self.run_time):
- self.step()
-
-
-# parameter lists for each parameter to be tested in batch run
-br_params = {
- "init_people": [25, 100],
- "rich_threshold": [5, 10],
- "reserve_percent": 5,
-}
-
-if __name__ == "__main__":
- data = mesa.batch_run(
- BankReservesModel,
- br_params,
- )
- br_df = pd.DataFrame(data)
- br_df.to_csv("BankReservesModel_Data.csv")
-
- # The commented out code below is the equivalent code as above, but done
- # via the legacy BatchRunner class. This is a good example to look at if
- # you want to migrate your code to use `batch_run()` from `BatchRunner`.
- # Things to note:
- # - You have to set "reserve_percent" in br_params to `[5]`, because the
- # legacy BatchRunner doesn't auto-detect that it is single-valued.
- # - The model reporters need to be explicitly specified in the legacy
- # BatchRunner
- """
- from mesa.batchrunner import BatchRunnerMP
- br = BatchRunnerMP(
- BankReservesModel,
- nr_processes=2,
- variable_parameters=br_params,
- iterations=2,
- max_steps=1000,
- model_reporters={"Data Collector": lambda m: m.datacollector},
- )
- br.run_all()
- br_df = br.get_model_vars_dataframe()
- br_step_data = pd.DataFrame()
- for i in range(len(br_df["Data Collector"])):
- if isinstance(br_df["Data Collector"][i], DataCollector):
- i_run_data = br_df["Data Collector"][i].get_model_vars_dataframe()
- br_step_data = br_step_data.append(i_run_data, ignore_index=True)
- br_step_data.to_csv("BankReservesModel_Step_Data.csv")
- """
diff --git a/examples/bank_reserves/requirements.txt b/examples/bank_reserves/requirements.txt
deleted file mode 100644
index 90169c50035..00000000000
--- a/examples/bank_reserves/requirements.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-itertools
-mesa
-numpy
-pandas
diff --git a/examples/bank_reserves/run.py b/examples/bank_reserves/run.py
deleted file mode 100644
index d6cf2ec0268..00000000000
--- a/examples/bank_reserves/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from bank_reserves.server import server
-
-server.launch()
diff --git a/examples/boid_flockers/Flocker Test.ipynb b/examples/boid_flockers/Flocker Test.ipynb
deleted file mode 100644
index 664019e51fc..00000000000
--- a/examples/boid_flockers/Flocker Test.ipynb
+++ /dev/null
@@ -1,114 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "from boid_flockers.model import BoidFlockers\n",
- "import numpy as np\n",
- "import matplotlib.pyplot as plt\n",
- "\n",
- "%matplotlib inline"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": [
- "def draw_boids(model):\n",
- " x_vals = []\n",
- " y_vals = []\n",
- " for boid in model.schedule.agents:\n",
- " x, y = boid.pos\n",
- " x_vals.append(x)\n",
- " y_vals.append(y)\n",
- " fig = plt.figure(figsize=(10, 10))\n",
- " ax = fig.add_subplot(111)\n",
- " ax.scatter(x_vals, y_vals)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "model = BoidFlockers(100, 100, 100, speed=5, vision=5, separation=1)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {
- "collapsed": false
- },
- "outputs": [],
- "source": [
- "for i in range(50):\n",
- " model.step()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {
- "collapsed": false
- },
- "outputs": [
- {
- "data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAAlwAAAJPCAYAAACpXgqFAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAIABJREFUeJzt3W+snNd9H/jvT1LMUnEVmQwg+Y9iB22MxEbqVt1N04Kt\nuGtLVI3WirCA0wAu1LTJInAXN1rSrSUnqPUi68ZuyPVqF4bRJnaJoPZWTaPYKdwV2TRMs9ggzsZx\n7Ur22img1rIhuiHtMHEU1TbPvpih7tXVveS9d+bcZ56ZzwcYaJ5n5rlz9PDOne+c8zvnqdZaAADo\n57qhGwAAsOwELgCAzgQuAIDOBC4AgM4ELgCAzgQuAIDOdhS4quoDVXW+qj69Yd8/qqrPVNW/r6pf\nrKpv2/DYg1X1+ar6bFXd1aPhAABjsdMerg8muXvTvjNJXttae12SzyV5MEmq6jVJfjDJa6bHvK+q\n9KQBACtrR0GotfbrSb6yad/Z1trl6eZvJnnF9P49ST7cWvt6a+3JJL+b5Pvm01wAgPGZV8/T307y\nsen9lyV5asNjTyV5+ZxeBwBgdGYOXFX1E0n+a2vtQ1d5musHAQAr64ZZDq6qv5XkjUlev2H3F5Pc\ntmH7FdN9m48VwgCA0Wit1V6P3XPgqqq7k/y9JHe01v54w0MfTfKhqjqVyVDidyX5+FY/Y5aGr7qq\neqi19tDQ7Rgr5282zt/eOXezcf5m4/zt3awdRTsKXFX14SR3JPn2qvpCkndmMivxRUnOVlWS/EZr\n7a2ttSeq6pEkTyT5RpK3ttb0ZgEAK2tHgau19kNb7P7AVZ7/riTv2mujAACWifWxxuvc0A0YuXND\nN2Dkzg3dgBE7N3QDRu7c0A0YuXNDN2BV1VCjfVXV1HABAGMwa27RwwUA0JnABQDQmcAFANCZwAUA\n0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZ\nwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAF\nANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQ\nmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnA\nBQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA\n0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANDZjgJXVX2gqs5X1ac37DtU\nVWer6nNVdaaqbt7w2INV9fmq+mxV3dWj4QAAY7HTHq4PJrl7074Hkpxtrb06ya9Mt1NVr0nyg0le\nMz3mfVWlJw0AWFk7CkKttV9P8pVNu9+U5PT0/ukkPzC9f0+SD7fWvt5aezLJ7yb5vtmbCgAwTrP0\nPN3SWjs/vX8+yS3T+y9L8tSG5z2V5OUzvA4AwKjNZaivtdaStKs9ZR6vAwAwRjfMcOz5qrq1tfZ0\nVb00yZen+7+Y5LYNz3vFdN8LVNVDGzbPtdbOzdAedqiqjiWHTky2Lp5srT02bIsAYLFU1dEkR+f2\n8yadUzt64Vcl+eXW2vdOt9+T5EJr7d1V9UCSm1trD0yL5j+USd3Wy5P8myR/um16oapqrbWa1/8I\nOzMJWzc9mjx8cLJn7XLyzU8mX3uH4AUAW5s1t+yoh6uqPpzkjiTfXlVfSPIPkvx0kkeq6u8keTLJ\nm5OktfZEVT2S5Ikk30jy1s1hiyEdOpGcOpjcd2XHdcn7b08+9ZGqlzyeXHdBrxcAzNeOAldr7Ye2\neegN2zz/XUnetddGsd+uT3LjgeRnbp9srx2pqnuFLgCYj1lquBiliyeTtSNJpkOKb0/y3Ul+Jht6\nvQ4mx08kEbgAYA4sSLpiJr1Wl+5N7v9Ecv/l5C1Jnh26WQCw1HZcND/3F1Y0P7j12YrPHk6uf23y\n8IHJI2vPJJcMKQLA1Ky5ReAiiaUiAObJ39TlI3CxLW94gP23xfI7Rg2WwL4sC8H4rL/hT115w5t5\nCLAvXrD8jolICFzLyxseABaFwAUAc7V5+Z21Z5JLJwdtEoNTw7WkJkOKN34k+TPTmYefejb5o3sM\nKQL0p4Z2+ajh4ipuSPJj0/trQzYEYKVMA5aQxXMEriXy/G9UNx9O3ntgQw3XATVcADAMgWtJvHBW\n4v2Xh20RAHCFwLU0Ns9K/PR1ydrlPHf5JkWbADAUgWtpfW+Sb34yOX5hsn1J0SYADMQsxSWx15WN\nJ8fd/K7kulcmz/6n5GvvmDxidg0AXOHSPjxnt9OQpyHtI+sXrX5bkj/4enLgsgtZA8A6y0LwnN1P\nQz50Ijm1cSZjkp/8luSnYoV6AJif64ZuAADAstPDtdIunkzW/kqSTUOKa5fX95ndCACzUsO1gjbV\nep1Lbv4fFM0DwPYUzbMre53NCACrbNbcooZrhKrqWNXhM5NbHdvd0YdOTMLWfZncHj643psFAPSg\nhmtkXngJn7UjVbWLHqrLh/u1DgDYisA1Opsv4bPzZRsmYe3G106K469Ye1ZRPAD0JXCtlCvrbt2a\n5B8n+VKSbz6ufgsA+hK4RufiyWTtSJKNRe+77KE6Nr2dzvq1FgGAXsxSHKHdXsLn+ceZoQgAu2VZ\nCHZlr2ENAFaZwAUA0Jl1uAAAFpzAtQJmWygVAJiVIcUlp1AeAGY3a26xLMTS2/tCqQDAfBhSBADo\nTA/X0pvHQqkAwCzUcK0Aa28BwGyswwUA0Jl1uAAAFpzABQDQmcAFANCZwAUA0JnABQDQmcAFANCZ\nwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAF\nANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0JnABQDQ\nmcAFANCZwAUA0JnABQDQmcAFANCZwAUA0NnMgauqHqyqx6vq01X1oao6UFWHqupsVX2uqs5U1c3z\naCwAzENVHas6fGZyq2NDt4flV621vR9c9aok/zbJ97TWnq2qf57kY0lem+T3Wmvvqaq3J3lJa+2B\nTce21lrt+cUBWGmToHToxGTr4snW2mM7P+6mR5OHD072rD2TXLp3p8ezmmbNLTfM+PqXknw9yY1V\n9c0kNyb5UpIHk9wxfc7pJOeSPLDVDwCA3VoPTaeuhKYjVbXD0HToxOS4+67sOJgcP5FE4KKbmYYU\nW2sXk5xM8p8zCVpfba2dTXJLa+389Gnnk9wyUysB4HkOnZj0UN2Xye3hg+u9XbB4Zurhqqo/leT+\nJK9K8vtJ/kVVvWXjc1prraq2HLesqoc2bJ5rrZ2bpT0AcG0XTyZrR5JsHFI8OWiTWDhVdTTJ0bn9\nvBlruH4wyZ2ttR+Zbv/NJN+f5L9P8t+11p6uqpcm+dXW2ndvOlYNFwB7Mmsd1l7rv1hds+aWWQPX\n65L8syT/bZI/TvJPk3w8ySuTXGitvbuqHkhys6J5AOZJaGI/DRq4pg34+5kMoF9O8okkP5LkTyZ5\n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\nMinr9tba9ya5PsnfiHN3NR/M5LNhoy3Pl8/cLW11/uaWWfb95FowdWbfl+R3W2tPtta+nuT/zOTc\nsYXW2tOttU9O7/9hks9kMpnjTUlOT592OskPDNPCxVdVr0jyxiQ/m+TKDB3nbwem34b/cmvtA0nS\nWvtGa+334/ztxKVMvjDdWFU3JLkxyZfi3G2rtfbrSb6yafd258tn7iZbnb95Zpah06wFU3fv5Um+\nsGHbedqh6TfmP5fJm+aW1tr56UPnk9wyULPG4H9N8veSXN6wz/nbme9M8l+q6oNV9Ymq+idV9a1x\n/q6ptXYxk5nw/zmToPXV1trZOHe7td358pm7ezNlli6Bazpe/Oktbn99w3MsmLo3zskeVNWLk/zL\nJD/eWvuDjY9NZ9E6r1uoqr+W5Muttd/Jeu/W8zh/V3VDktuTvK+1dnuSr2XTEJjzt7Wq+lNJ7k/y\nqkw+3F5cVW/Z+Bznbnd2cL6cy23MI7PMsizE9q/Y2p1Xe3y6YOobk7x+w+4vJrltw/Yrpvt4vs3n\n6bY8P2WzSVV9SyZh6+dba7803X2+qm5trT1dVS9N8uXhWrjQ/lKSN1XVG5P8iSQ3VdXPx/nbqaeS\nPNVa+63p9i9kUgPytPN3Tf9Nkv+ntXYhSarqF5P8xTh3u7Xde9Vn7g7NK7MMMUvx7kyGJ+65sjr9\n1EeT/I2qelFVfWeS70ry8f1u3wj8v0m+q6peVVUvyqRo76MDt2lhVVUl+bkkT7TW3rvhoY8muW96\n/74kv7T5WJLW2jtaa7e11r4zk4Llf9ta+5tx/naktfZ0ki9U1aunu96Q5PEkvxzn71o+m+T7q+rg\n9H38hkwmbjh3u7Pde9Vn7g7MM7Ps+zpcVfX5TBZMvTjd9RuttbdOH3tHJmOk38hk6OexfW3cSFTV\nX03y3kxm7fxca+0fDtykhVVVR5L8uySfynp374OZvDEeSfIdSZ5M8ubW2leHaONYVNUdSU601t5U\nVYfi/O1IVb0ukwkHL0ryH5P8cCbvXefvGqrq72cSEi4n+USSH0nyJ+PcbamqPpzkjiTfnkm91j9I\n8pFsc7585j7fFufvnZl8Xswls1j4FACgs6FnKQIALD2BCwCgM4ELAKAzgQsAoDOBCwCgM4ELAKAz\ngQsAoDOBCwCgs/8fICoqGcqtXKgAAAAASUVORK5CYII=\n",
- "text/plain": [
- ""
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "draw_boids(model)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "collapsed": true
- },
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.4.2"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/examples/boid_flockers/Readme.md b/examples/boid_flockers/Readme.md
deleted file mode 100644
index cb3292b4f68..00000000000
--- a/examples/boid_flockers/Readme.md
+++ /dev/null
@@ -1,34 +0,0 @@
-# Flockers
-
-An implementation of Craig Reynolds's Boids flocker model. Agents (simulated birds) try to fly towards the average position of their neighbors and in the same direction as them, while maintaining a minimum distance. This produces flocking behavior.
-
-This model tests Mesa's continuous space feature, and uses numpy arrays to represent vectors. It also demonstrates how to create custom visualization components.
-
-## How to Run
-
-Launch the model:
-```
- $ python Flocker_Server.py
-```
-
-Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
-
-## Files
-
-* [flockers/model.py](flockers/model.py): Core model file; contains the BoidModel class.
-* [flockers/boid.py](flockers/boid.py): The Boid agent class.
-* [flockers/SimpleContinuousModule.py](flockers/SimpleContinuousModule.py): Defines ``SimpleCanvas``, the Python side of a custom visualization module for drawing agents with continuous positions.
-* [flockers/simple_continuous_canvas.js](flockers/simple_continuous_canvas.js): JavaScript side of the ``SimpleCanvas`` visualization module; takes the output generated by the Python ``SimpleCanvas`` element and draws it in the browser window via HTML5 canvas.
-* [flockers/server.py](flockers/server.py): Sets up the visualization; uses the SimpleCanvas element defined above
-* [run.py](run.py) Launches the visualization.
-* [Flocker Test.ipynb](Flocker Test.ipynb): Tests the model in a Jupyter notebook.
-
-## Further Reading
-
-=======
-* Launch the visualization
-```
-$ mesa runserver
-```
-* Visit your browser: http://127.0.0.1:8521/
-* In your browser hit *run*
diff --git a/examples/boid_flockers/boid_flockers/SimpleContinuousModule.py b/examples/boid_flockers/boid_flockers/SimpleContinuousModule.py
deleted file mode 100644
index 3f3da5dd01e..00000000000
--- a/examples/boid_flockers/boid_flockers/SimpleContinuousModule.py
+++ /dev/null
@@ -1,32 +0,0 @@
-import mesa
-
-
-class SimpleCanvas(mesa.visualization.VisualizationElement):
- local_includes = ["boid_flockers/simple_continuous_canvas.js"]
- portrayal_method = None
- canvas_height = 500
- canvas_width = 500
-
- def __init__(self, portrayal_method, canvas_height=500, canvas_width=500):
- """
- Instantiate a new SimpleCanvas
- """
- self.portrayal_method = portrayal_method
- self.canvas_height = canvas_height
- self.canvas_width = canvas_width
- new_element = "new Simple_Continuous_Module({}, {})".format(
- self.canvas_width, self.canvas_height
- )
- self.js_code = "elements.push(" + new_element + ");"
-
- def render(self, model):
- space_state = []
- for obj in model.schedule.agents:
- portrayal = self.portrayal_method(obj)
- x, y = obj.pos
- x = (x - model.space.x_min) / (model.space.x_max - model.space.x_min)
- y = (y - model.space.y_min) / (model.space.y_max - model.space.y_min)
- portrayal["x"] = x
- portrayal["y"] = y
- space_state.append(portrayal)
- return space_state
diff --git a/examples/boid_flockers/boid_flockers/boid.py b/examples/boid_flockers/boid_flockers/boid.py
deleted file mode 100644
index f427f9ddbbc..00000000000
--- a/examples/boid_flockers/boid_flockers/boid.py
+++ /dev/null
@@ -1,104 +0,0 @@
-import mesa
-import numpy as np
-
-
-class Boid(mesa.Agent):
- """
- A Boid-style flocker agent.
-
- The agent follows three behaviors to flock:
- - Cohesion: steering towards neighboring agents.
- - Separation: avoiding getting too close to any other agent.
- - Alignment: try to fly in the same direction as the neighbors.
-
- Boids have a vision that defines the radius in which they look for their
- neighbors to flock with. Their speed (a scalar) and velocity (a vector)
- define their movement. Separation is their desired minimum distance from
- any other Boid.
- """
-
- def __init__(
- self,
- unique_id,
- model,
- pos,
- speed,
- velocity,
- vision,
- separation,
- cohere=0.025,
- separate=0.25,
- match=0.04,
- ):
- """
- Create a new Boid flocker agent.
-
- Args:
- unique_id: Unique agent identifyer.
- pos: Starting position
- speed: Distance to move per step.
- heading: numpy vector for the Boid's direction of movement.
- vision: Radius to look around for nearby Boids.
- separation: Minimum distance to maintain from other Boids.
- cohere: the relative importance of matching neighbors' positions
- separate: the relative importance of avoiding close neighbors
- match: the relative importance of matching neighbors' headings
- """
- super().__init__(unique_id, model)
- self.pos = np.array(pos)
- self.speed = speed
- self.velocity = velocity
- self.vision = vision
- self.separation = separation
- self.cohere_factor = cohere
- self.separate_factor = separate
- self.match_factor = match
-
- def cohere(self, neighbors):
- """
- Return the vector toward the center of mass of the local neighbors.
- """
- cohere = np.zeros(2)
- if neighbors:
- for neighbor in neighbors:
- cohere += self.model.space.get_heading(self.pos, neighbor.pos)
- cohere /= len(neighbors)
- return cohere
-
- def separate(self, neighbors):
- """
- Return a vector away from any neighbors closer than separation dist.
- """
- me = self.pos
- them = (n.pos for n in neighbors)
- separation_vector = np.zeros(2)
- for other in them:
- if self.model.space.get_distance(me, other) < self.separation:
- separation_vector -= self.model.space.get_heading(me, other)
- return separation_vector
-
- def match_heading(self, neighbors):
- """
- Return a vector of the neighbors' average heading.
- """
- match_vector = np.zeros(2)
- if neighbors:
- for neighbor in neighbors:
- match_vector += neighbor.velocity
- match_vector /= len(neighbors)
- return match_vector
-
- def step(self):
- """
- Get the Boid's neighbors, compute the new vector, and move accordingly.
- """
-
- neighbors = self.model.space.get_neighbors(self.pos, self.vision, False)
- self.velocity += (
- self.cohere(neighbors) * self.cohere_factor
- + self.separate(neighbors) * self.separate_factor
- + self.match_heading(neighbors) * self.match_factor
- ) / 2
- self.velocity /= np.linalg.norm(self.velocity)
- new_pos = self.pos + self.velocity * self.speed
- self.model.space.move_agent(self, new_pos)
diff --git a/examples/boid_flockers/boid_flockers/model.py b/examples/boid_flockers/boid_flockers/model.py
deleted file mode 100644
index 00a08d765d5..00000000000
--- a/examples/boid_flockers/boid_flockers/model.py
+++ /dev/null
@@ -1,76 +0,0 @@
-"""
-Flockers
-=============================================================
-A Mesa implementation of Craig Reynolds's Boids flocker model.
-Uses numpy arrays to represent vectors.
-"""
-
-import mesa
-import numpy as np
-
-from .boid import Boid
-
-
-class BoidFlockers(mesa.Model):
- """
- Flocker model class. Handles agent creation, placement and scheduling.
- """
-
- def __init__(
- self,
- population=100,
- width=100,
- height=100,
- speed=1,
- vision=10,
- separation=2,
- cohere=0.025,
- separate=0.25,
- match=0.04,
- ):
- """
- Create a new Flockers model.
-
- Args:
- population: Number of Boids
- width, height: Size of the space.
- speed: How fast should the Boids move.
- vision: How far around should each Boid look for its neighbors
- separation: What's the minimum distance each Boid will attempt to
- keep from any other
- cohere, separate, match: factors for the relative importance of
- the three drives."""
- self.population = population
- self.vision = vision
- self.speed = speed
- self.separation = separation
- self.schedule = mesa.time.RandomActivation(self)
- self.space = mesa.space.ContinuousSpace(width, height, True)
- self.factors = dict(cohere=cohere, separate=separate, match=match)
- self.make_agents()
- self.running = True
-
- def make_agents(self):
- """
- Create self.population agents, with random positions and starting headings.
- """
- for i in range(self.population):
- x = self.random.random() * self.space.x_max
- y = self.random.random() * self.space.y_max
- pos = np.array((x, y))
- velocity = np.random.random(2) * 2 - 1
- boid = Boid(
- i,
- self,
- pos,
- self.speed,
- velocity,
- self.vision,
- self.separation,
- **self.factors
- )
- self.space.place_agent(boid, pos)
- self.schedule.add(boid)
-
- def step(self):
- self.schedule.step()
diff --git a/examples/boid_flockers/boid_flockers/server.py b/examples/boid_flockers/boid_flockers/server.py
deleted file mode 100644
index 4906df699c7..00000000000
--- a/examples/boid_flockers/boid_flockers/server.py
+++ /dev/null
@@ -1,23 +0,0 @@
-import mesa
-
-from .model import BoidFlockers
-from .SimpleContinuousModule import SimpleCanvas
-
-
-def boid_draw(agent):
- return {"Shape": "circle", "r": 2, "Filled": "true", "Color": "Red"}
-
-
-boid_canvas = SimpleCanvas(boid_draw, 500, 500)
-model_params = {
- "population": 100,
- "width": 100,
- "height": 100,
- "speed": 5,
- "vision": 10,
- "separation": 2,
-}
-
-server = mesa.visualization.ModularServer(
- BoidFlockers, [boid_canvas], "Boids", model_params
-)
diff --git a/examples/boid_flockers/boid_flockers/simple_continuous_canvas.js b/examples/boid_flockers/boid_flockers/simple_continuous_canvas.js
deleted file mode 100644
index 20c0ded8732..00000000000
--- a/examples/boid_flockers/boid_flockers/simple_continuous_canvas.js
+++ /dev/null
@@ -1,79 +0,0 @@
-const ContinuousVisualization = function(width, height, context) {
- this.draw = function(objects) {
- for (const p of objects) {
- if (p.Shape == "rect")
- this.drawRectange(p.x, p.y, p.w, p.h, p.Color, p.Filled);
- if (p.Shape == "circle")
- this.drawCircle(p.x, p.y, p.r, p.Color, p.Filled);
- };
-
- };
-
- this.drawCircle = function(x, y, radius, color, fill) {
- const cx = x * width;
- const cy = y * height;
- const r = radius;
-
- context.beginPath();
- context.arc(cx, cy, r, 0, Math.PI * 2, false);
- context.closePath();
-
- context.strokeStyle = color;
- context.stroke();
-
- if (fill) {
- context.fillStyle = color;
- context.fill();
- }
-
- };
-
- this.drawRectange = function(x, y, w, h, color, fill) {
- context.beginPath();
- const dx = w * width;
- const dy = h * height;
-
- // Keep the drawing centered:
- const x0 = (x*width) - 0.5*dx;
- const y0 = (y*height) - 0.5*dy;
-
- context.strokeStyle = color;
- context.fillStyle = color;
- if (fill)
- context.fillRect(x0, y0, dx, dy);
- else
- context.strokeRect(x0, y0, dx, dy);
- };
-
- this.resetCanvas = function() {
- context.clearRect(0, 0, width, height);
- context.beginPath();
- };
-};
-
-const Simple_Continuous_Module = function(canvas_width, canvas_height) {
- // Create the element
- // ------------------
-
- const canvas = document.createElement("canvas");
- Object.assign(canvas, {
- width: canvas_width,
- height: canvas_height,
- style: 'border:1px dotted'
- });
- // Append it to body:
- document.getElementById("elements").appendChild(canvas);
-
- // Create the context and the drawing controller:
- const context = canvas.getContext("2d");
- const canvasDraw = new ContinuousVisualization(canvas_width, canvas_height, context);
-
- this.render = function(data) {
- canvasDraw.resetCanvas();
- canvasDraw.draw(data);
- };
-
- this.reset = function() {
- canvasDraw.resetCanvas();
- };
-};
diff --git a/examples/boid_flockers/requirements.txt b/examples/boid_flockers/requirements.txt
deleted file mode 100644
index bcbfbbe220b..00000000000
--- a/examples/boid_flockers/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-jupyter
-matplotlib
-mesa
diff --git a/examples/boid_flockers/run.py b/examples/boid_flockers/run.py
deleted file mode 100644
index be0c1c75c58..00000000000
--- a/examples/boid_flockers/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from boid_flockers.server import server
-
-server.launch()
diff --git a/examples/boltzmann_wealth_model/Readme.md b/examples/boltzmann_wealth_model/Readme.md
deleted file mode 100644
index 785a0946a24..00000000000
--- a/examples/boltzmann_wealth_model/Readme.md
+++ /dev/null
@@ -1,39 +0,0 @@
-# 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`
diff --git a/examples/boltzmann_wealth_model/boltzmann_wealth_model/__init__.py b/examples/boltzmann_wealth_model/boltzmann_wealth_model/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py b/examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py
deleted file mode 100644
index 76ebc516b36..00000000000
--- a/examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py
+++ /dev/null
@@ -1,73 +0,0 @@
-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()
diff --git a/examples/boltzmann_wealth_model/boltzmann_wealth_model/server.py b/examples/boltzmann_wealth_model/boltzmann_wealth_model/server.py
deleted file mode 100644
index a49546ce741..00000000000
--- a/examples/boltzmann_wealth_model/boltzmann_wealth_model/server.py
+++ /dev/null
@@ -1,40 +0,0 @@
-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
diff --git a/examples/boltzmann_wealth_model/requirements.txt b/examples/boltzmann_wealth_model/requirements.txt
deleted file mode 100644
index 23603b7348c..00000000000
--- a/examples/boltzmann_wealth_model/requirements.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-jupyter
-matplotlib
-mesa
-numpy
diff --git a/examples/boltzmann_wealth_model/run.py b/examples/boltzmann_wealth_model/run.py
deleted file mode 100644
index ea57809eb0a..00000000000
--- a/examples/boltzmann_wealth_model/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from boltzmann_wealth_model.server import server
-
-server.launch()
diff --git a/examples/boltzmann_wealth_model_network/README.md b/examples/boltzmann_wealth_model_network/README.md
deleted file mode 100644
index cd3bcd8df00..00000000000
--- a/examples/boltzmann_wealth_model_network/README.md
+++ /dev/null
@@ -1,48 +0,0 @@
-# Boltzmann Wealth Model with Network
-
-## Summary
-
-This is the same Boltzmann Wealth Model, but with a network grid implementation.
-
-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).
-
-In this network implementation, agents must be located on a node, with a limit of one agent per node. In order to give or receive the unit of money, the agent must be directly connected to the other agent (there must be a direct link between the nodes).
-
-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.
-
-JavaScript library used in this example to render the network: [sigma.js](http://sigmajs.org/).
-
-## 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.
-
-## Files
-
-* ``run.py``: Launches a model visualization server.
-* ``model.py``: Contains the agent class, and the overall model class.
-* ``server.py``: Defines classes for visualizing the model (network layout) in the browser via Mesa's modular server, and instantiates a visualization server.
-
-## 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)
diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/__init__.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py
deleted file mode 100644
index 181daec4e45..00000000000
--- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py
+++ /dev/null
@@ -1,79 +0,0 @@
-import mesa
-import networkx as nx
-
-
-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 BoltzmannWealthModelNetwork(mesa.Model):
- """A model with some number of agents."""
-
- def __init__(self, num_agents=7, num_nodes=10):
-
- self.num_agents = num_agents
- self.num_nodes = num_nodes if num_nodes >= self.num_agents else self.num_agents
- self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=0.5)
- self.grid = mesa.space.NetworkGrid(self.G)
- self.schedule = mesa.time.RandomActivation(self)
- self.datacollector = mesa.DataCollector(
- model_reporters={"Gini": compute_gini},
- agent_reporters={"Wealth": lambda _: _.wealth},
- )
-
- list_of_random_nodes = self.random.sample(list(self.G), self.num_agents)
-
- # Create agents
- for i in range(self.num_agents):
- a = MoneyAgent(i, self)
- self.schedule.add(a)
- # Add the agent to a random node
- self.grid.place_agent(a, list_of_random_nodes[i])
-
- 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 = [
- node
- for node in self.model.grid.get_neighbors(self.pos, include_center=False)
- if self.model.grid.is_cell_empty(node)
- ]
- if len(possible_steps) > 0:
- new_position = self.random.choice(possible_steps)
- self.model.grid.move_agent(self, new_position)
-
- def give_money(self):
-
- neighbors_nodes = self.model.grid.get_neighbors(self.pos, include_center=False)
- neighbors = self.model.grid.get_cell_list_contents(neighbors_nodes)
- if len(neighbors) > 0:
- other = self.random.choice(neighbors)
- other.wealth += 1
- self.wealth -= 1
-
- def step(self):
- self.move()
- if self.wealth > 0:
- self.give_money()
diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/server.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/server.py
deleted file mode 100644
index abc493a3e5b..00000000000
--- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/server.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import mesa
-
-from .model import BoltzmannWealthModelNetwork
-
-
-def network_portrayal(G):
- # The model ensures there is 0 or 1 agent per node
-
- portrayal = dict()
- portrayal["nodes"] = [
- {
- "id": node_id,
- "size": 3 if agents else 1,
- "color": "#CC0000" if not agents or agents[0].wealth == 0 else "#007959",
- "label": None
- if not agents
- else f"Agent:{agents[0].unique_id} Wealth:{agents[0].wealth}",
- }
- for (node_id, agents) in G.nodes.data("agent")
- ]
-
- portrayal["edges"] = [
- {"id": edge_id, "source": source, "target": target, "color": "#000000"}
- for edge_id, (source, target) in enumerate(G.edges)
- ]
-
- return portrayal
-
-
-grid = mesa.visualization.NetworkModule(network_portrayal, 500, 500)
-chart = mesa.visualization.ChartModule(
- [{"Label": "Gini", "Color": "Black"}], data_collector_name="datacollector"
-)
-
-model_params = {
- "num_agents": mesa.visualization.Slider(
- "Number of agents",
- 7,
- 2,
- 10,
- 1,
- description="Choose how many agents to include in the model",
- ),
- "num_nodes": mesa.visualization.Slider(
- "Number of nodes",
- 10,
- 3,
- 12,
- 1,
- description="Choose how many nodes to include in the model, with at "
- "least the same number of agents",
- ),
-}
-
-server = mesa.visualization.ModularServer(
- BoltzmannWealthModelNetwork, [grid, chart], "Money Model", model_params
-)
-server.port = 8521
diff --git a/examples/boltzmann_wealth_model_network/requirements.txt b/examples/boltzmann_wealth_model_network/requirements.txt
deleted file mode 100644
index f3aa7ff7a50..00000000000
--- a/examples/boltzmann_wealth_model_network/requirements.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-jupyter
-matplotlib
-mesa
-numpy
-networkx
diff --git a/examples/boltzmann_wealth_model_network/run.py b/examples/boltzmann_wealth_model_network/run.py
deleted file mode 100644
index 34a388a484c..00000000000
--- a/examples/boltzmann_wealth_model_network/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from boltzmann_wealth_model_network.server import server
-
-server.launch()
diff --git a/examples/charts/Readme.md b/examples/charts/Readme.md
deleted file mode 100644
index c3145d91ddf..00000000000
--- a/examples/charts/Readme.md
+++ /dev/null
@@ -1,40 +0,0 @@
-# Mesa Charts Example
-
-## Summary
-
-A modified version of the "bank_reserves" example made to provide examples of mesa's charting tools.
-
-The chart types included in this example are:
-- Line Charts for time-series data of multiple model parameters
-- Pie Charts for model parameters
-- Bar charts for both model and agent-level parameters
-
-## Installation
-
-To install the dependencies use pip and the requirements.txt in this directory. e.g.
-
-```
- $ pip install -r requirements.txt
-```
-
-## Interactive Model Run
-
-To run the model interactively, use `mesa runserver` in this directory:
-
-```
- $ mesa runserver
-```
-
-Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/), select the model parameters, press Reset, then Start.
-
-## Files
-
-* ``bank_reserves/random_walker.py``: This defines a class that inherits from the Mesa Agent class. The main purpose is to provide a method for agents to move randomly one cell at a time.
-* ``bank_reserves/agents.py``: Defines the People and Bank classes.
-* ``bank_reserves/model.py``: Defines the Bank Reserves model and the DataCollector functions.
-* ``bank_reserves/server.py``: Sets up the interactive visualization server.
-* ``run.py``: Launches a model visualization server.
-
-## Further Reading
-
-See the "bank_reserves" model for more information.
diff --git a/examples/charts/charts/agents.py b/examples/charts/charts/agents.py
deleted file mode 100644
index 0d18c453e5f..00000000000
--- a/examples/charts/charts/agents.py
+++ /dev/null
@@ -1,182 +0,0 @@
-"""
-The following code was adapted from the Bank Reserves model included in Netlogo
-Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves
-Accessed on: November 2, 2017
-Author of NetLogo code:
- Wilensky, U. (1998). NetLogo Bank Reserves model.
- http://ccl.northwestern.edu/netlogo/models/BankReserves.
- Center for Connected Learning and Computer-Based Modeling,
- Northwestern University, Evanston, IL.
-"""
-
-import mesa
-
-from charts.random_walk import RandomWalker
-
-
-class Bank(mesa.Agent):
- def __init__(self, unique_id, model, reserve_percent=50):
- # initialize the parent class with required parameters
- super().__init__(unique_id, model)
- # for tracking total value of loans outstanding
- self.bank_loans = 0
- """percent of deposits the bank must keep in reserves - this is set via
- Slider in server.py"""
- self.reserve_percent = reserve_percent
- # for tracking total value of deposits
- self.deposits = 0
- # total amount of deposits in reserve
- self.reserves = (self.reserve_percent / 100) * self.deposits
- # amount the bank is currently able to loan
- self.bank_to_loan = 0
-
- """update the bank's reserves and amount it can loan;
- this is called every time a person balances their books
- see below for Person.balance_books()"""
-
- def bank_balance(self):
- self.reserves = (self.reserve_percent / 100) * self.deposits
- self.bank_to_loan = self.deposits - (self.reserves + self.bank_loans)
-
-
-# subclass of RandomWalker, which is subclass to Mesa Agent
-class Person(RandomWalker):
- def __init__(self, unique_id, pos, model, moore, bank, rich_threshold):
- # init parent class with required parameters
- super().__init__(unique_id, pos, model, moore=moore)
- # the amount each person has in savings
- self.savings = 0
- # total loan amount person has outstanding
- self.loans = 0
- """start everyone off with a random amount in their wallet from 1 to a
- user settable rich threshold amount"""
- self.wallet = self.random.randint(1, rich_threshold + 1)
- # savings minus loans, see balance_books() below
- self.wealth = 0
- # person to trade with, see do_business() below
- self.customer = 0
- # person's bank, set at __init__, all people have the same bank in this model
- self.bank = bank
-
- def do_business(self):
- """check if person has any savings, any money in wallet, or if the
- bank can loan them any money"""
- if self.savings > 0 or self.wallet > 0 or self.bank.bank_to_loan > 0:
- # create list of people at my location (includes self)
- my_cell = self.model.grid.get_cell_list_contents([self.pos])
- # check if other people are at my location
- if len(my_cell) > 1:
- # set customer to self for while loop condition
- customer = self
- while customer == self:
- """select a random person from the people at my location
- to trade with"""
- customer = self.random.choice(my_cell)
- # 50% chance of trading with customer
- if self.random.randint(0, 1) == 0:
- # 50% chance of trading $5
- if self.random.randint(0, 1) == 0:
- # give customer $5 from my wallet (may result in negative wallet)
- customer.wallet += 5
- self.wallet -= 5
- # 50% chance of trading $2
- else:
- # give customer $2 from my wallet (may result in negative wallet)
- customer.wallet += 2
- self.wallet -= 2
-
- def balance_books(self):
- # check if wallet is negative from trading with customer
- if self.wallet < 0:
- # if negative money in wallet, check if my savings can cover the balance
- if self.savings >= (self.wallet * -1):
- """if my savings can cover the balance, withdraw enough
- money from my savings so that my wallet has a 0 balance"""
- self.withdraw_from_savings(self.wallet * -1)
- # if my savings cannot cover the negative balance of my wallet
- else:
- # check if i have any savings
- if self.savings > 0:
- """if i have savings, withdraw all of it to reduce my
- negative balance in my wallet"""
- self.withdraw_from_savings(self.savings)
- # record how much money the bank can loan out right now
- temp_loan = self.bank.bank_to_loan
- """check if the bank can loan enough money to cover the
- remaining negative balance in my wallet"""
- if temp_loan >= (self.wallet * -1):
- """if the bank can loan me enough money to cover
- the remaining negative balance in my wallet, take out a
- loan for the remaining negative balance"""
- self.take_out_loan(self.wallet * -1)
- else:
- """if the bank cannot loan enough money to cover the negative
- balance of my wallet, then take out a loan for the
- total amount the bank can loan right now"""
- self.take_out_loan(temp_loan)
- else:
- """if i have money in my wallet from trading with customer, deposit
- it to my savings in the bank"""
- self.deposit_to_savings(self.wallet)
- # check if i have any outstanding loans, and if i have savings
- if self.loans > 0 and self.savings > 0:
- # check if my savings can cover my outstanding loans
- if self.savings >= self.loans:
- # payoff my loans with my savings
- self.withdraw_from_savings(self.loans)
- self.repay_a_loan(self.loans)
- # if my savings won't cover my loans
- else:
- # pay off part of my loans with my savings
- self.withdraw_from_savings(self.savings)
- self.repay_a_loan(self.wallet)
- # calculate my wealth
- self.wealth = self.savings - self.loans
-
- # part of balance_books()
- def deposit_to_savings(self, amount):
- # take money from my wallet and put it in savings
- self.wallet -= amount
- self.savings += amount
- # increase bank deposits
- self.bank.deposits += amount
-
- # part of balance_books()
- def withdraw_from_savings(self, amount):
- # put money in my wallet from savings
- self.wallet += amount
- self.savings -= amount
- # decrease bank deposits
- self.bank.deposits -= amount
-
- # part of balance_books()
- def repay_a_loan(self, amount):
- # take money from my wallet to pay off all or part of a loan
- self.loans -= amount
- self.wallet -= amount
- # increase the amount the bank can loan right now
- self.bank.bank_to_loan += amount
- # decrease the bank's outstanding loans
- self.bank.bank_loans -= amount
-
- # part of balance_books()
- def take_out_loan(self, amount):
- """borrow from the bank to put money in my wallet, and increase my
- outstanding loans"""
- self.loans += amount
- self.wallet += amount
- # decresae the amount the bank can loan right now
- self.bank.bank_to_loan -= amount
- # increase the bank's outstanding loans
- self.bank.bank_loans += amount
-
- # step is called for each agent in model.BankReservesModel.schedule.step()
- def step(self):
- # move to a cell in my Moore neighborhood
- self.random_move()
- # trade
- self.do_business()
- # deposit money or take out a loan
- self.balance_books()
- # update the bank's reserves and the amount it can loan right now
- self.bank.bank_balance()
diff --git a/examples/charts/charts/model.py b/examples/charts/charts/model.py
deleted file mode 100644
index 295dfc27665..00000000000
--- a/examples/charts/charts/model.py
+++ /dev/null
@@ -1,147 +0,0 @@
-"""
-The following code was adapted from the Bank Reserves model included in Netlogo
-Model information can be found at: http://ccl.northwestern.edu/netlogo/models/BankReserves
-Accessed on: November 2, 2017
-Author of NetLogo code:
- Wilensky, U. (1998). NetLogo Bank Reserves model.
- http://ccl.northwestern.edu/netlogo/models/BankReserves.
- Center for Connected Learning and Computer-Based Modeling,
- Northwestern University, Evanston, IL.
-"""
-
-import mesa
-import numpy as np
-
-from charts.agents import Bank, Person
-
-"""
-If you want to perform a parameter sweep, call batch_run.py instead of run.py.
-For details see batch_run.py in the same directory as run.py.
-"""
-
-# Start of datacollector functions
-
-
-def get_num_rich_agents(model):
- """return number of rich agents"""
-
- rich_agents = [a for a in model.schedule.agents if a.savings > model.rich_threshold]
- return len(rich_agents)
-
-
-def get_num_poor_agents(model):
- """return number of poor agents"""
-
- poor_agents = [a for a in model.schedule.agents if a.loans > 10]
- return len(poor_agents)
-
-
-def get_num_mid_agents(model):
- """return number of middle class agents"""
-
- mid_agents = [
- a
- for a in model.schedule.agents
- if a.loans < 10 and a.savings < model.rich_threshold
- ]
- return len(mid_agents)
-
-
-def get_total_savings(model):
- """sum of all agents' savings"""
-
- agent_savings = [a.savings for a in model.schedule.agents]
- # return the sum of agents' savings
- return np.sum(agent_savings)
-
-
-def get_total_wallets(model):
- """sum of amounts of all agents' wallets"""
-
- agent_wallets = [a.wallet for a in model.schedule.agents]
- # return the sum of all agents' wallets
- return np.sum(agent_wallets)
-
-
-def get_total_money(model):
- # sum of all agents' wallets
- wallet_money = get_total_wallets(model)
- # sum of all agents' savings
- savings_money = get_total_savings(model)
- # return sum of agents' wallets and savings for total money
- return wallet_money + savings_money
-
-
-def get_total_loans(model):
- # list of amounts of all agents' loans
- agent_loans = [a.loans for a in model.schedule.agents]
- # return sum of all agents' loans
- return np.sum(agent_loans)
-
-
-class Charts(mesa.Model):
-
- # grid height
- grid_h = 20
- # grid width
- grid_w = 20
-
- """init parameters "init_people", "rich_threshold", and "reserve_percent"
- are all set via Slider"""
-
- def __init__(
- self,
- height=grid_h,
- width=grid_w,
- init_people=2,
- rich_threshold=10,
- reserve_percent=50,
- ):
- self.height = height
- self.width = width
- self.init_people = init_people
- self.schedule = mesa.time.RandomActivation(self)
- self.grid = mesa.space.MultiGrid(self.width, self.height, torus=True)
- # rich_threshold is the amount of savings a person needs to be considered "rich"
- self.rich_threshold = rich_threshold
- self.reserve_percent = reserve_percent
- # see datacollector functions above
- self.datacollector = mesa.DataCollector(
- model_reporters={
- "Rich": get_num_rich_agents,
- "Poor": get_num_poor_agents,
- "Middle Class": get_num_mid_agents,
- "Savings": get_total_savings,
- "Wallets": get_total_wallets,
- "Money": get_total_money,
- "Loans": get_total_loans,
- },
- agent_reporters={"Wealth": lambda x: x.wealth},
- )
-
- # create a single bank for the model
- self.bank = Bank(1, self, self.reserve_percent)
-
- # create people for the model according to number of people set by user
- for i in range(self.init_people):
- # set x, y coords randomly within the grid
- x = self.random.randrange(self.width)
- y = self.random.randrange(self.height)
- p = Person(i, (x, y), self, True, self.bank, self.rich_threshold)
- # place the Person object on the grid at coordinates (x, y)
- self.grid.place_agent(p, (x, y))
- # add the Person object to the model schedule
- self.schedule.add(p)
-
- self.running = True
- self.datacollector.collect(self)
-
- def step(self):
- # tell all the agents in the model to run their step function
- self.schedule.step()
- # collect data
- self.datacollector.collect(self)
-
- def run_model(self):
- for i in range(self.run_time):
- self.step()
diff --git a/examples/charts/charts/random_walk.py b/examples/charts/charts/random_walk.py
deleted file mode 100644
index 7e067881e4e..00000000000
--- a/examples/charts/charts/random_walk.py
+++ /dev/null
@@ -1,47 +0,0 @@
-"""
-Citation:
-The following code is a copy from random_walk.py at
-https://github.com/projectmesa/mesa/blob/main/examples/wolf_sheep/wolf_sheep/random_walk.py
-Accessed on: November 2, 2017
-Original Author: Jackie Kazil
-
-Generalized behavior for random walking, one grid cell at a time.
-"""
-
-import mesa
-
-
-class RandomWalker(mesa.Agent):
- """
- Class implementing random walker methods in a generalized manner.
- Not intended to be used on its own, but to inherit its methods to multiple
- other agents.
- """
-
- grid = None
- x = None
- y = None
- # use a Moore neighborhood
- moore = True
-
- def __init__(self, unique_id, pos, model, moore=True):
- """
- grid: The MultiGrid object in which the agent lives.
- x: The agent's current x coordinate
- y: The agent's current y coordinate
- moore: If True, may move in all 8 directions.
- Otherwise, only up, down, left, right.
- """
- super().__init__(unique_id, model)
- self.pos = pos
- self.moore = moore
-
- def random_move(self):
- """
- Step one cell in any allowable direction.
- """
- # Pick the next cell from the adjacent cells.
- next_moves = self.model.grid.get_neighborhood(self.pos, self.moore, True)
- next_move = self.random.choice(next_moves)
- # Now move:
- self.model.grid.move_agent(self, next_move)
diff --git a/examples/charts/charts/server.py b/examples/charts/charts/server.py
deleted file mode 100644
index ba7bfbdfd51..00000000000
--- a/examples/charts/charts/server.py
+++ /dev/null
@@ -1,113 +0,0 @@
-import mesa
-
-from charts.agents import Person
-from charts.model import Charts
-
-"""
-Citation:
-The following code was adapted from server.py at
-https://github.com/projectmesa/mesa/blob/main/examples/wolf_sheep/wolf_sheep/server.py
-Accessed on: November 2, 2017
-Author of original code: Taylor Mutch
-"""
-
-# The colors here are taken from Matplotlib's tab10 palette
-# Green
-RICH_COLOR = "#2ca02c"
-# Red
-POOR_COLOR = "#d62728"
-# Blue
-MID_COLOR = "#1f77b4"
-
-
-def person_portrayal(agent):
- if agent is None:
- return
-
- portrayal = {}
-
- # update portrayal characteristics for each Person object
- if isinstance(agent, Person):
- portrayal["Shape"] = "circle"
- portrayal["r"] = 0.5
- portrayal["Layer"] = 0
- portrayal["Filled"] = "true"
-
- color = MID_COLOR
-
- # set agent color based on savings and loans
- if agent.savings > agent.model.rich_threshold:
- color = RICH_COLOR
- if agent.savings < 10 and agent.loans < 10:
- color = MID_COLOR
- if agent.loans > 10:
- color = POOR_COLOR
-
- portrayal["Color"] = color
-
- return portrayal
-
-
-# dictionary of user settable parameters - these map to the model __init__ parameters
-model_params = {
- "init_people": mesa.visualization.Slider(
- "People", 25, 1, 200, description="Initial Number of People"
- ),
- "rich_threshold": mesa.visualization.Slider(
- "Rich Threshold",
- 10,
- 1,
- 20,
- description="Upper End of Random Initial Wallet Amount",
- ),
- "reserve_percent": mesa.visualization.Slider(
- "Reserves",
- 50,
- 1,
- 100,
- description="Percent of deposits the bank has to hold in reserve",
- ),
-}
-
-# set the portrayal function and size of the canvas for visualization
-canvas_element = mesa.visualization.CanvasGrid(person_portrayal, 20, 20, 500, 500)
-
-# map data to chart in the ChartModule
-line_chart = mesa.visualization.ChartModule(
- [
- {"Label": "Rich", "Color": RICH_COLOR},
- {"Label": "Poor", "Color": POOR_COLOR},
- {"Label": "Middle Class", "Color": MID_COLOR},
- ]
-)
-
-model_bar = mesa.visualization.BarChartModule(
- [
- {"Label": "Rich", "Color": RICH_COLOR},
- {"Label": "Poor", "Color": POOR_COLOR},
- {"Label": "Middle Class", "Color": MID_COLOR},
- ]
-)
-
-agent_bar = mesa.visualization.BarChartModule(
- [{"Label": "Wealth", "Color": MID_COLOR}],
- scope="agent",
- sorting="ascending",
- sort_by="Wealth",
-)
-
-pie_chart = mesa.visualization.PieChartModule(
- [
- {"Label": "Rich", "Color": RICH_COLOR},
- {"Label": "Middle Class", "Color": MID_COLOR},
- {"Label": "Poor", "Color": POOR_COLOR},
- ]
-)
-
-# create instance of Mesa ModularServer
-server = mesa.visualization.ModularServer(
- Charts,
- [canvas_element, line_chart, model_bar, agent_bar, pie_chart],
- "Mesa Charts",
- model_params=model_params,
-)
diff --git a/examples/charts/requirements.txt b/examples/charts/requirements.txt
deleted file mode 100644
index 90169c50035..00000000000
--- a/examples/charts/requirements.txt
+++ /dev/null
@@ -1,4 +0,0 @@
-itertools
-mesa
-numpy
-pandas
diff --git a/examples/charts/run.py b/examples/charts/run.py
deleted file mode 100644
index ec56c635b58..00000000000
--- a/examples/charts/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from charts.server import server
-
-server.launch()
diff --git a/examples/color_patches/Readme.md b/examples/color_patches/Readme.md
deleted file mode 100644
index 5b722bcea04..00000000000
--- a/examples/color_patches/Readme.md
+++ /dev/null
@@ -1,38 +0,0 @@
-# Color Patches
-
-
-This is a cellular automaton model where each agent lives in a cell on a 2D grid, and never moves.
-
-An agent's state represents its "opinion" and is shown by the color of the cell the agent lives in. Each color represents an opinion - there are 16 of them. At each time step, an agent's opinion is influenced by that of its neighbors, and changes to the most common one found; ties are randomly arbitrated. As an agent adapts its thinking to that of its neighbors, the cell color changes.
-
-### Parameters you can play with:
-(you must change the code to alter the parameters at this stage)
-* Vary the number of opinions.
-* Vary the size of the grid
-* Change the grid from fixed borders to a torus continuum
-
-### Observe
-* how groups of like minded agents form and evolve
-* how sometimes a single opinion prevails
-* how some minority or fragmented opinions rapidly disappear
-
-## 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.
-
-## Files
-
-* ``color_patches/model.py``: Defines the cell and model classes. The cell class governs each cell's behavior. The model class itself controls the lattice on which the cells live and interact.
-* ``color_patches/server.py``: Defines an interactive visualization.
-* ``run.py``: Launches an interactive visualization
-
-## Further Reading
-
-Inspired from [this model](http://www.cs.sjsu.edu/~pearce/modules/lectures/abs/as/ca.htm) from San Jose University
-Other similar models: [Schelling Segregation Model](https://github.com/projectmesa/mesa/tree/main/examples/schelling)
diff --git a/examples/color_patches/color_patches/__init__.py b/examples/color_patches/color_patches/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/examples/color_patches/color_patches/model.py b/examples/color_patches/color_patches/model.py
deleted file mode 100644
index fb15a221430..00000000000
--- a/examples/color_patches/color_patches/model.py
+++ /dev/null
@@ -1,129 +0,0 @@
-"""
-The model - a 2D lattice where agents live and have an opinion
-"""
-
-from collections import Counter
-
-import mesa
-
-
-class ColorCell(mesa.Agent):
- """
- Represents a cell's opinion (visualized by a color)
- """
-
- OPINIONS = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
-
- def __init__(self, pos, model, initial_state):
- """
- Create a cell, in the given state, at the given row, col position.
- """
- super().__init__(pos, model)
- self._row = pos[0]
- self._col = pos[1]
- self._state = initial_state
- self._next_state = None
-
- def get_col(self):
- """Return the col location of this cell."""
- return self._col
-
- def get_row(self):
- """Return the row location of this cell."""
- return self._row
-
- def get_state(self):
- """Return the current state (OPINION) of this cell."""
- return self._state
-
- def step(self):
- """
- Determines the agent opinion for the next step by polling its neighbors
- The opinion is determined by the majority of the 8 neighbors' opinion
- A choice is made at random in case of a tie
- The next state is stored until all cells have been polled
- """
- _neighbor_iter = self.model.grid.iter_neighbors((self._row, self._col), True)
- neighbors_opinion = Counter(n.get_state() for n in _neighbor_iter)
- # Following is a a tuple (attribute, occurrences)
- polled_opinions = neighbors_opinion.most_common()
- tied_opinions = []
- for neighbor in polled_opinions:
- if neighbor[1] == polled_opinions[0][1]:
- tied_opinions.append(neighbor)
-
- self._next_state = self.random.choice(tied_opinions)[0]
-
- def advance(self):
- """
- Set the state of the agent to the next state
- """
- self._state = self._next_state
-
-
-class ColorPatches(mesa.Model):
- """
- represents a 2D lattice where agents live
- """
-
- def __init__(self, width=20, height=20):
- """
- Create a 2D lattice with strict borders where agents live
- The agents next state is first determined before updating the grid
- """
-
- self._grid = mesa.space.Grid(width, height, torus=False)
- self._schedule = mesa.time.SimultaneousActivation(self)
-
- # self._grid.coord_iter()
- # --> should really not return content + col + row
- # -->but only col & row
- # for (contents, col, row) in self._grid.coord_iter():
- # replaced content with _ to appease linter
- for (_, row, col) in self._grid.coord_iter():
- cell = ColorCell(
- (row, col), self, ColorCell.OPINIONS[self.random.randrange(0, 16)]
- )
- self._grid.place_agent(cell, (row, col))
- self._schedule.add(cell)
-
- self.running = True
-
- def step(self):
- """
- Advance the model one step.
- """
- self._schedule.step()
-
- # the following is a temporary fix for the framework classes accessing
- # model attributes directly
- # I don't think it should
- # --> it imposes upon the model builder to use the attributes names that
- # the framework expects.
- #
- # Traceback included in docstrings
-
- @property
- def grid(self):
- """
- /mesa/visualization/modules/CanvasGridVisualization.py
- is directly accessing Model.grid
- 76 def render(self, model):
- 77 grid_state = defaultdict(list)
- ---> 78 for y in range(model.grid.height):
- 79 for x in range(model.grid.width):
- 80 cell_objects = model.grid.get_cell_list_contents([(x, y)])
-
- AttributeError: 'ColorPatches' object has no attribute 'grid'
- """
- return self._grid
-
- @property
- def schedule(self):
- """
- mesa_ABM/examples_ABM/color_patches/mesa/visualization/ModularVisualization.py",
- line 278, in run_model
- while self.model.schedule.steps < self.max_steps and self.model.running:
- AttributeError: 'NoneType' object has no attribute 'steps'
- """
- return self._schedule
diff --git a/examples/color_patches/color_patches/server.py b/examples/color_patches/color_patches/server.py
deleted file mode 100644
index aa52332eb3b..00000000000
--- a/examples/color_patches/color_patches/server.py
+++ /dev/null
@@ -1,67 +0,0 @@
-"""
-handles the definition of the canvas parameters and
-the drawing of the model representation on the canvas
-"""
-# import webbrowser
-
-import mesa
-
-from .model import ColorPatches
-
-_COLORS = [
- "Aqua",
- "Blue",
- "Fuchsia",
- "Gray",
- "Green",
- "Lime",
- "Maroon",
- "Navy",
- "Olive",
- "Orange",
- "Purple",
- "Red",
- "Silver",
- "Teal",
- "White",
- "Yellow",
-]
-
-
-grid_rows = 50
-grid_cols = 25
-cell_size = 10
-canvas_width = grid_rows * cell_size
-canvas_height = grid_cols * cell_size
-
-
-def color_patch_draw(cell):
- """
- This function is registered with the visualization server to be called
- each tick to indicate how to draw the cell in its current state.
-
- :param cell: the cell in the simulation
-
- :return: the portrayal dictionary.
- """
- if cell is None:
- raise AssertionError
- portrayal = {"Shape": "rect", "w": 1, "h": 1, "Filled": "true", "Layer": 0}
- portrayal["x"] = cell.get_row()
- portrayal["y"] = cell.get_col()
- portrayal["Color"] = _COLORS[cell.get_state()]
- return portrayal
-
-
-canvas_element = mesa.visualization.CanvasGrid(
- color_patch_draw, grid_rows, grid_cols, canvas_width, canvas_height
-)
-
-server = mesa.visualization.ModularServer(
- ColorPatches,
- [canvas_element],
- "Color Patches",
- {"width": grid_rows, "height": grid_cols},
-)
-
-# webbrowser.open('http://127.0.0.1:8521') # TODO: make this configurable
diff --git a/examples/color_patches/requirements.txt b/examples/color_patches/requirements.txt
deleted file mode 100644
index 1ad1bbec7ab..00000000000
--- a/examples/color_patches/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-mesa
\ No newline at end of file
diff --git a/examples/color_patches/run.py b/examples/color_patches/run.py
deleted file mode 100644
index afe422d45d9..00000000000
--- a/examples/color_patches/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from color_patches.server import server
-
-server.launch()
diff --git a/examples/conways_game_of_life/Readme.md b/examples/conways_game_of_life/Readme.md
deleted file mode 100644
index 686afb4065a..00000000000
--- a/examples/conways_game_of_life/Readme.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Conway's Game Of "Life"
-
-## Summary
-
-[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.
-
-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.
-
-
-## 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 ``run``.
-
-## Files
-
-* ``game_of_life/cell.py``: Defines the behavior of an individual cell, which can be in two states: DEAD or ALIVE.
-* ``game_of_life/model.py``: Defines the model itself, initialized with a random configuration of alive and dead cells.
-* ``game_of_life/portrayal.py``: Describes for the front end how to render a cell.
-* ``game_of_live/server.py``: Defines an interactive visualization.
-* ``run.py``: Launches the visualization
-
-## Further Reading
-[Conway's Game of Life](https://en.wikipedia.org/wiki/Conway%27s_Game_of_Life)
-
diff --git a/examples/conways_game_of_life/conways_game_of_life/cell.py b/examples/conways_game_of_life/conways_game_of_life/cell.py
deleted file mode 100644
index 8639288d4ca..00000000000
--- a/examples/conways_game_of_life/conways_game_of_life/cell.py
+++ /dev/null
@@ -1,53 +0,0 @@
-import mesa
-
-
-class Cell(mesa.Agent):
- """Represents a single ALIVE or DEAD cell in the simulation."""
-
- DEAD = 0
- ALIVE = 1
-
- def __init__(self, pos, model, init_state=DEAD):
- """
- Create a cell, in the given state, at the given x, y position.
- """
- super().__init__(pos, model)
- self.x, self.y = pos
- self.state = init_state
- self._nextState = None
-
- @property
- def isAlive(self):
- return self.state == self.ALIVE
-
- @property
- def neighbors(self):
- return self.model.grid.iter_neighbors((self.x, self.y), True)
-
- def step(self):
- """
- Compute if the cell will be dead or alive at the next tick. This is
- based on the number of alive or dead neighbors. The state is not
- changed here, but is just computed and stored in self._nextState,
- because our current state may still be necessary for our neighbors
- to calculate their next state.
- """
-
- # Get the neighbors and apply the rules on whether to be alive or dead
- # at the next tick.
- live_neighbors = sum(neighbor.isAlive for neighbor in self.neighbors)
-
- # Assume nextState is unchanged, unless changed below.
- self._nextState = self.state
- if self.isAlive:
- if live_neighbors < 2 or live_neighbors > 3:
- self._nextState = self.DEAD
- else:
- if live_neighbors == 3:
- self._nextState = self.ALIVE
-
- def advance(self):
- """
- Set the state to the new computed state -- computed in step().
- """
- self.state = self._nextState
diff --git a/examples/conways_game_of_life/conways_game_of_life/model.py b/examples/conways_game_of_life/conways_game_of_life/model.py
deleted file mode 100644
index 635ccaa959d..00000000000
--- a/examples/conways_game_of_life/conways_game_of_life/model.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import mesa
-
-from .cell import Cell
-
-
-class ConwaysGameOfLife(mesa.Model):
- """
- Represents the 2-dimensional array of cells in Conway's
- Game of Life.
- """
-
- def __init__(self, width=50, height=50):
- """
- Create a new playing area of (width, height) cells.
- """
-
- # Set up the grid and schedule.
-
- # Use SimultaneousActivation which simulates all the cells
- # computing their next state simultaneously. This needs to
- # be done because each cell's next state depends on the current
- # state of all its neighbors -- before they've changed.
- self.schedule = mesa.time.SimultaneousActivation(self)
-
- # Use a simple grid, where edges wrap around.
- self.grid = mesa.space.Grid(width, height, torus=True)
-
- # Place a cell at each location, with some initialized to
- # ALIVE and some to DEAD.
- for (contents, x, y) in self.grid.coord_iter():
- cell = Cell((x, y), self)
- if self.random.random() < 0.1:
- cell.state = cell.ALIVE
- self.grid.place_agent(cell, (x, y))
- self.schedule.add(cell)
-
- self.running = True
-
- def step(self):
- """
- Have the scheduler advance each cell by one step
- """
- self.schedule.step()
diff --git a/examples/conways_game_of_life/conways_game_of_life/portrayal.py b/examples/conways_game_of_life/conways_game_of_life/portrayal.py
deleted file mode 100644
index 4f68468d857..00000000000
--- a/examples/conways_game_of_life/conways_game_of_life/portrayal.py
+++ /dev/null
@@ -1,19 +0,0 @@
-def portrayCell(cell):
- """
- This function is registered with the visualization server to be called
- each tick to indicate how to draw the cell in its current state.
- :param cell: the cell in the simulation
- :return: the portrayal dictionary.
- """
- if cell is None:
- raise AssertionError
- return {
- "Shape": "rect",
- "w": 1,
- "h": 1,
- "Filled": "true",
- "Layer": 0,
- "x": cell.x,
- "y": cell.y,
- "Color": "black" if cell.isAlive else "white",
- }
diff --git a/examples/conways_game_of_life/conways_game_of_life/server.py b/examples/conways_game_of_life/conways_game_of_life/server.py
deleted file mode 100644
index 4167b3d01bd..00000000000
--- a/examples/conways_game_of_life/conways_game_of_life/server.py
+++ /dev/null
@@ -1,12 +0,0 @@
-import mesa
-
-from .portrayal import portrayCell
-from .model import ConwaysGameOfLife
-
-
-# Make a world that is 50x50, on a 250x250 display.
-canvas_element = mesa.visualization.CanvasGrid(portrayCell, 50, 50, 250, 250)
-
-server = mesa.visualization.ModularServer(
- ConwaysGameOfLife, [canvas_element], "Game of Life", {"height": 50, "width": 50}
-)
diff --git a/examples/conways_game_of_life/requirements.txt b/examples/conways_game_of_life/requirements.txt
deleted file mode 100644
index 1ad1bbec7ab..00000000000
--- a/examples/conways_game_of_life/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-mesa
\ No newline at end of file
diff --git a/examples/conways_game_of_life/run.py b/examples/conways_game_of_life/run.py
deleted file mode 100644
index 2854fdee59d..00000000000
--- a/examples/conways_game_of_life/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from conways_game_of_life.server import server
-
-server.launch()
diff --git a/examples/epstein_civil_violence/Epstein Civil Violence.ipynb b/examples/epstein_civil_violence/Epstein Civil Violence.ipynb
deleted file mode 100644
index 2fe5ed25879..00000000000
--- a/examples/epstein_civil_violence/Epstein Civil Violence.ipynb
+++ /dev/null
@@ -1,119 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This example implements the first model from \"Modeling civil violence: An agent-based computational approach,\" by Joshua Epstein. The paper (pdf) can be found [here](http://www.uvm.edu/~pdodds/files/papers/others/2002/epstein2002a.pdf).\n",
- "\n",
- "The model consists of two types of agents: \"Citizens\" (called \"Agents\" in the paper) and \"Cops.\" Agents decide whether or not to rebel by weighing their unhappiness ('grievance') against the risk of rebelling, which they estimate by comparing the local ratio of rebels to cops. \n",
- "\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "import matplotlib.pyplot as plt\n",
- "\n",
- "%matplotlib inline\n",
- "\n",
- "from epstein_civil_violence.agent import Citizen, Cop\n",
- "from epstein_civil_violence.model import EpsteinCivilViolence"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "model = EpsteinCivilViolence(\n",
- " height=40,\n",
- " width=40,\n",
- " citizen_density=0.7,\n",
- " cop_density=0.074,\n",
- " citizen_vision=7,\n",
- " cop_vision=7,\n",
- " legitimacy=0.8,\n",
- " max_jail_term=1000,\n",
- " max_iters=1000,\n",
- ") # cap the number of steps the model takes\n",
- "model.run_model()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The model's data collector counts the number of citizens who are Active (in rebellion), Jailed, or Quiescent after each step."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [],
- "source": [
- "model_out = model.datacollector.get_model_vars_dataframe()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- "
"
- ]
- },
- "metadata": {
- "needs_background": "light"
- },
- "output_type": "display_data"
- }
- ],
- "source": [
- "ax = model_out.plot()\n",
- "ax.set_title(\"Citizen Condition Over Time\")\n",
- "ax.set_xlabel(\"Step\")\n",
- "ax.set_ylabel(\"Number of Citizens\")\n",
- "_ = ax.legend(bbox_to_anchor=(1.35, 1.025))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.7.3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 1
-}
diff --git a/examples/epstein_civil_violence/Readme.md b/examples/epstein_civil_violence/Readme.md
deleted file mode 100644
index 2e715b33b99..00000000000
--- a/examples/epstein_civil_violence/Readme.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Epstein Civil Violence Model
-
-## Summary
-
-This model is based on Joshua Epstein's simulation of how civil unrest grows and is suppressed. Citizen agents wander the grid randomly, and are endowed with individual risk aversion and hardship levels; there is also a universal regime legitimacy value. There are also Cop agents, who work on behalf of the regime. Cops arrest Citizens who are actively rebelling; Citizens decide whether to rebel based on their hardship and the regime legitimacy, and their perceived probability of arrest.
-
-The model generates mass uprising as self-reinforcing processes: if enough agents are rebelling, the probability of any individual agent being arrested is reduced, making more agents more likely to join the uprising. However, the more rebelling Citizens the Cops arrest, the less likely additional agents become to join.
-
-## How to Run
-
-To run the model interactively, run ``EpsteinCivilViolenceServer.py`` in this directory. e.g.
-
-```
- $ python EpsteinCivilViolenceServer.py
-```
-
-Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
-
-## Files
-
-* ``EpsteinCivilViolence.py``: Core model and agent code.
-* ``EpsteinCivilViolenceServer.py``: Sets up the interactive visualization.
-* ``Epstein Civil Violence.ipynb``: Jupyter notebook conducting some preliminary analysis of the model.
-
-## Further Reading
-
-This model is based adapted from:
-
-[Epstein, J. “Modeling civil violence: An agent-based computational approach”, Proceedings of the National Academy of Sciences, Vol. 99, Suppl. 3, May 14, 2002](http://www.pnas.org/content/99/suppl.3/7243.short)
-
-A similar model is also included with NetLogo:
-
-Wilensky, U. (2004). NetLogo Rebellion model. http://ccl.northwestern.edu/netlogo/models/Rebellion. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
diff --git a/examples/epstein_civil_violence/epstein_civil_violence/agent.py b/examples/epstein_civil_violence/epstein_civil_violence/agent.py
deleted file mode 100644
index 358b4484d44..00000000000
--- a/examples/epstein_civil_violence/epstein_civil_violence/agent.py
+++ /dev/null
@@ -1,184 +0,0 @@
-import math
-
-import mesa
-
-
-class Citizen(mesa.Agent):
- """
- A member of the general population, may or may not be in active rebellion.
- Summary of rule: If grievance - risk > threshold, rebel.
-
- Attributes:
- unique_id: unique int
- x, y: Grid coordinates
- hardship: Agent's 'perceived hardship (i.e., physical or economic
- privation).' Exogenous, drawn from U(0,1).
- regime_legitimacy: Agent's perception of regime legitimacy, equal
- across agents. Exogenous.
- risk_aversion: Exogenous, drawn from U(0,1).
- threshold: if (grievance - (risk_aversion * arrest_probability)) >
- threshold, go/remain Active
- vision: number of cells in each direction (N, S, E and W) that agent
- can inspect
- condition: Can be "Quiescent" or "Active;" deterministic function of
- greivance, perceived risk, and
- grievance: deterministic function of hardship and regime_legitimacy;
- how aggrieved is agent at the regime?
- arrest_probability: agent's assessment of arrest probability, given
- rebellion
- """
-
- def __init__(
- self,
- unique_id,
- model,
- pos,
- hardship,
- regime_legitimacy,
- risk_aversion,
- threshold,
- vision,
- ):
- """
- Create a new Citizen.
- Args:
- unique_id: unique int
- x, y: Grid coordinates
- hardship: Agent's 'perceived hardship (i.e., physical or economic
- privation).' Exogenous, drawn from U(0,1).
- regime_legitimacy: Agent's perception of regime legitimacy, equal
- across agents. Exogenous.
- risk_aversion: Exogenous, drawn from U(0,1).
- threshold: if (grievance - (risk_aversion * arrest_probability)) >
- threshold, go/remain Active
- vision: number of cells in each direction (N, S, E and W) that
- agent can inspect. Exogenous.
- model: model instance
- """
- super().__init__(unique_id, model)
- self.breed = "citizen"
- self.pos = pos
- self.hardship = hardship
- self.regime_legitimacy = regime_legitimacy
- self.risk_aversion = risk_aversion
- self.threshold = threshold
- self.condition = "Quiescent"
- self.vision = vision
- self.jail_sentence = 0
- self.grievance = self.hardship * (1 - self.regime_legitimacy)
- self.arrest_probability = None
-
- def step(self):
- """
- Decide whether to activate, then move if applicable.
- """
- if self.jail_sentence:
- self.jail_sentence -= 1
- return # no other changes or movements if agent is in jail.
- self.update_neighbors()
- self.update_estimated_arrest_probability()
- net_risk = self.risk_aversion * self.arrest_probability
- if (
- self.condition == "Quiescent"
- and (self.grievance - net_risk) > self.threshold
- ):
- self.condition = "Active"
- elif (
- self.condition == "Active" and (self.grievance - net_risk) <= self.threshold
- ):
- self.condition = "Quiescent"
- if self.model.movement and self.empty_neighbors:
- new_pos = self.random.choice(self.empty_neighbors)
- self.model.grid.move_agent(self, new_pos)
-
- def update_neighbors(self):
- """
- Look around and see who my neighbors are
- """
- self.neighborhood = self.model.grid.get_neighborhood(
- self.pos, moore=False, radius=1
- )
- self.neighbors = self.model.grid.get_cell_list_contents(self.neighborhood)
- self.empty_neighbors = [
- c for c in self.neighborhood if self.model.grid.is_cell_empty(c)
- ]
-
- def update_estimated_arrest_probability(self):
- """
- Based on the ratio of cops to actives in my neighborhood, estimate the
- p(Arrest | I go active).
- """
- cops_in_vision = len([c for c in self.neighbors if c.breed == "cop"])
- actives_in_vision = 1.0 # citizen counts herself
- for c in self.neighbors:
- if (
- c.breed == "citizen"
- and c.condition == "Active"
- and c.jail_sentence == 0
- ):
- actives_in_vision += 1
- self.arrest_probability = 1 - math.exp(
- -1 * self.model.arrest_prob_constant * (cops_in_vision / actives_in_vision)
- )
-
-
-class Cop(mesa.Agent):
- """
- A cop for life. No defection.
- Summary of rule: Inspect local vision and arrest a random active agent.
-
- Attributes:
- unique_id: unique int
- x, y: Grid coordinates
- vision: number of cells in each direction (N, S, E and W) that cop is
- able to inspect
- """
-
- def __init__(self, unique_id, model, pos, vision):
- """
- Create a new Cop.
- Args:
- unique_id: unique int
- x, y: Grid coordinates
- vision: number of cells in each direction (N, S, E and W) that
- agent can inspect. Exogenous.
- model: model instance
- """
- super().__init__(unique_id, model)
- self.breed = "cop"
- self.pos = pos
- self.vision = vision
-
- def step(self):
- """
- Inspect local vision and arrest a random active agent. Move if
- applicable.
- """
- self.update_neighbors()
- active_neighbors = []
- for agent in self.neighbors:
- if (
- agent.breed == "citizen"
- and agent.condition == "Active"
- and agent.jail_sentence == 0
- ):
- active_neighbors.append(agent)
- if active_neighbors:
- arrestee = self.random.choice(active_neighbors)
- sentence = self.random.randint(0, self.model.max_jail_term)
- arrestee.jail_sentence = sentence
- if self.model.movement and self.empty_neighbors:
- new_pos = self.random.choice(self.empty_neighbors)
- self.model.grid.move_agent(self, new_pos)
-
- def update_neighbors(self):
- """
- Look around and see who my neighbors are.
- """
- self.neighborhood = self.model.grid.get_neighborhood(
- self.pos, moore=False, radius=1
- )
- self.neighbors = self.model.grid.get_cell_list_contents(self.neighborhood)
- self.empty_neighbors = [
- c for c in self.neighborhood if self.model.grid.is_cell_empty(c)
- ]
diff --git a/examples/epstein_civil_violence/epstein_civil_violence/model.py b/examples/epstein_civil_violence/epstein_civil_violence/model.py
deleted file mode 100644
index 760767c26d9..00000000000
--- a/examples/epstein_civil_violence/epstein_civil_violence/model.py
+++ /dev/null
@@ -1,141 +0,0 @@
-import mesa
-
-from .agent import Cop, Citizen
-
-
-class EpsteinCivilViolence(mesa.Model):
- """
- Model 1 from "Modeling civil violence: An agent-based computational
- approach," by Joshua Epstein.
- http://www.pnas.org/content/99/suppl_3/7243.full
- Attributes:
- height: grid height
- width: grid width
- citizen_density: approximate % of cells occupied by citizens.
- cop_density: approximate % of cells occupied by cops.
- citizen_vision: number of cells in each direction (N, S, E and W) that
- citizen can inspect
- cop_vision: number of cells in each direction (N, S, E and W) that cop
- can inspect
- legitimacy: (L) citizens' perception of regime legitimacy, equal
- across all citizens
- max_jail_term: (J_max)
- active_threshold: if (grievance - (risk_aversion * arrest_probability))
- > threshold, citizen rebels
- arrest_prob_constant: set to ensure agents make plausible arrest
- probability estimates
- movement: binary, whether agents try to move at step end
- max_iters: model may not have a natural stopping point, so we set a
- max.
- """
-
- def __init__(
- self,
- width=40,
- height=40,
- citizen_density=0.7,
- cop_density=0.074,
- citizen_vision=7,
- cop_vision=7,
- legitimacy=0.8,
- max_jail_term=1000,
- active_threshold=0.1,
- arrest_prob_constant=2.3,
- movement=True,
- max_iters=1000,
- ):
- super().__init__()
- self.width = width
- self.height = height
- self.citizen_density = citizen_density
- self.cop_density = cop_density
- self.citizen_vision = citizen_vision
- self.cop_vision = cop_vision
- self.legitimacy = legitimacy
- self.max_jail_term = max_jail_term
- self.active_threshold = active_threshold
- self.arrest_prob_constant = arrest_prob_constant
- self.movement = movement
- self.max_iters = max_iters
- self.iteration = 0
- self.schedule = mesa.time.RandomActivation(self)
- self.grid = mesa.space.Grid(width, height, torus=True)
- model_reporters = {
- "Quiescent": lambda m: self.count_type_citizens(m, "Quiescent"),
- "Active": lambda m: self.count_type_citizens(m, "Active"),
- "Jailed": self.count_jailed,
- }
- agent_reporters = {
- "x": lambda a: a.pos[0],
- "y": lambda a: a.pos[1],
- "breed": lambda a: a.breed,
- "jail_sentence": lambda a: getattr(a, "jail_sentence", None),
- "condition": lambda a: getattr(a, "condition", None),
- "arrest_probability": lambda a: getattr(a, "arrest_probability", None),
- }
- self.datacollector = mesa.DataCollector(
- model_reporters=model_reporters, agent_reporters=agent_reporters
- )
- unique_id = 0
- if self.cop_density + self.citizen_density > 1:
- raise ValueError("Cop density + citizen density must be less than 1")
- for (contents, x, y) in self.grid.coord_iter():
- if self.random.random() < self.cop_density:
- cop = Cop(unique_id, self, (x, y), vision=self.cop_vision)
- unique_id += 1
- self.grid[x][y] = cop
- self.schedule.add(cop)
- elif self.random.random() < (self.cop_density + self.citizen_density):
- citizen = Citizen(
- unique_id,
- self,
- (x, y),
- hardship=self.random.random(),
- regime_legitimacy=self.legitimacy,
- risk_aversion=self.random.random(),
- threshold=self.active_threshold,
- vision=self.citizen_vision,
- )
- unique_id += 1
- self.grid[x][y] = citizen
- self.schedule.add(citizen)
-
- self.running = True
- self.datacollector.collect(self)
-
- def step(self):
- """
- Advance the model by one step and collect data.
- """
- self.schedule.step()
- # collect data
- self.datacollector.collect(self)
- self.iteration += 1
- if self.iteration > self.max_iters:
- self.running = False
-
- @staticmethod
- def count_type_citizens(model, condition, exclude_jailed=True):
- """
- Helper method to count agents by Quiescent/Active.
- """
- count = 0
- for agent in model.schedule.agents:
- if agent.breed == "cop":
- continue
- if exclude_jailed and agent.jail_sentence:
- continue
- if agent.condition == condition:
- count += 1
- return count
-
- @staticmethod
- def count_jailed(model):
- """
- Helper method to count jailed agents.
- """
- count = 0
- for agent in model.schedule.agents:
- if agent.breed == "citizen" and agent.jail_sentence:
- count += 1
- return count
diff --git a/examples/epstein_civil_violence/epstein_civil_violence/portrayal.py b/examples/epstein_civil_violence/epstein_civil_violence/portrayal.py
deleted file mode 100644
index 80134adcc79..00000000000
--- a/examples/epstein_civil_violence/epstein_civil_violence/portrayal.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from .agent import Citizen, Cop
-
-COP_COLOR = "#000000"
-AGENT_QUIET_COLOR = "#0066CC"
-AGENT_REBEL_COLOR = "#CC0000"
-JAIL_COLOR = "#757575"
-
-
-def citizen_cop_portrayal(agent):
- if agent is None:
- return
-
- portrayal = {
- "Shape": "circle",
- "x": agent.pos[0],
- "y": agent.pos[1],
- "Filled": "true",
- }
-
- if isinstance(agent, Citizen):
- color = (
- AGENT_QUIET_COLOR if agent.condition == "Quiescent" else AGENT_REBEL_COLOR
- )
- color = JAIL_COLOR if agent.jail_sentence else color
- portrayal["Color"] = color
- portrayal["r"] = 0.8
- portrayal["Layer"] = 0
-
- elif isinstance(agent, Cop):
- portrayal["Color"] = COP_COLOR
- portrayal["r"] = 0.5
- portrayal["Layer"] = 1
- return portrayal
diff --git a/examples/epstein_civil_violence/epstein_civil_violence/server.py b/examples/epstein_civil_violence/epstein_civil_violence/server.py
deleted file mode 100644
index 6b835bd2b14..00000000000
--- a/examples/epstein_civil_violence/epstein_civil_violence/server.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import mesa
-
-from .model import EpsteinCivilViolence
-from .agent import Citizen, Cop
-
-
-COP_COLOR = "#000000"
-AGENT_QUIET_COLOR = "#0066CC"
-AGENT_REBEL_COLOR = "#CC0000"
-JAIL_COLOR = "#757575"
-
-
-def citizen_cop_portrayal(agent):
- if agent is None:
- return
-
- portrayal = {
- "Shape": "circle",
- "x": agent.pos[0],
- "y": agent.pos[1],
- "Filled": "true",
- }
-
- if type(agent) is Citizen:
- color = (
- AGENT_QUIET_COLOR if agent.condition == "Quiescent" else AGENT_REBEL_COLOR
- )
- color = JAIL_COLOR if agent.jail_sentence else color
- portrayal["Color"] = color
- portrayal["r"] = 0.8
- portrayal["Layer"] = 0
-
- elif type(agent) is Cop:
- portrayal["Color"] = COP_COLOR
- portrayal["r"] = 0.5
- portrayal["Layer"] = 1
- return portrayal
-
-
-model_params = dict(
- height=40,
- width=40,
- citizen_density=0.7,
- cop_density=0.074,
- citizen_vision=7,
- cop_vision=7,
- legitimacy=0.8,
- max_jail_term=1000,
-)
-
-canvas_element = mesa.visualization.CanvasGrid(citizen_cop_portrayal, 40, 40, 480, 480)
-server = mesa.visualization.ModularServer(
- EpsteinCivilViolence, [canvas_element], "Epstein Civil Violence", model_params
-)
diff --git a/examples/epstein_civil_violence/requirements.txt b/examples/epstein_civil_violence/requirements.txt
deleted file mode 100644
index bcbfbbe220b..00000000000
--- a/examples/epstein_civil_violence/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-jupyter
-matplotlib
-mesa
diff --git a/examples/epstein_civil_violence/run.py b/examples/epstein_civil_violence/run.py
deleted file mode 100644
index 5aa2644ac3d..00000000000
--- a/examples/epstein_civil_violence/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from epstein_civil_violence.server import server
-
-server.launch()
diff --git a/examples/forest_fire/Forest Fire Model.ipynb b/examples/forest_fire/Forest Fire Model.ipynb
deleted file mode 100644
index db9be7203e0..00000000000
--- a/examples/forest_fire/Forest Fire Model.ipynb
+++ /dev/null
@@ -1,623 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# The Forest Fire Model\n",
- "## A rapid introduction to Mesa\n",
- "\n",
- "The [Forest Fire Model](http://en.wikipedia.org/wiki/Forest-fire_model) is one of the simplest examples of a model that exhibits self-organized criticality.\n",
- "\n",
- "Mesa is a new, Pythonic agent-based modeling framework. A big advantage of using Python is that it a great language for interactive data analysis. Unlike some other ABM frameworks, with Mesa you can write a model, run it, and analyze it all in the same environment. (You don't have to, of course. But you can).\n",
- "\n",
- "In this notebook, we'll go over a rapid-fire (pun intended, sorry) introduction to building and analyzing a model with Mesa."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "First, some imports. We'll go over what all the Mesa ones mean just below."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "import numpy as np\n",
- "\n",
- "import matplotlib.pyplot as plt\n",
- "\n",
- "%matplotlib inline\n",
- "\n",
- "from mesa import Model, Agent\n",
- "from mesa.time import RandomActivation\n",
- "from mesa.space import Grid\n",
- "from mesa.datacollection import DataCollector\n",
- "from mesa.batchrunner import BatchRunner"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Building the model\n",
- "\n",
- "Most models consist of basically two things: agents, and an world for the agents to be in. The Forest Fire model has only one kind of agent: a tree. A tree can either be unburned, on fire, or already burned. The environment is a grid, where each cell can either be empty or contain a tree.\n",
- "\n",
- "First, let's define our tree agent. The agent needs to be assigned **x** and **y** coordinates on the grid, and that's about it. We could assign agents a condition to be in, but for now let's have them all start as being 'Fine'. Since the agent doesn't move, and there is only at most one tree per cell, we can use a tuple of its coordinates as a unique identifier.\n",
- "\n",
- "Next, we define the agent's **step** method. This gets called whenever the agent needs to act in the world and takes the *model* object to which it belongs as an input. The tree's behavior is simple: If it is currently on fire, it spreads the fire to any trees above, below, to the left and the right of it that are not themselves burned out or on fire; then it burns itself out. "
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "class TreeCell(Agent):\n",
- " \"\"\"\n",
- " A tree cell.\n",
- "\n",
- " Attributes:\n",
- " x, y: Grid coordinates\n",
- " condition: Can be \"Fine\", \"On Fire\", or \"Burned Out\"\n",
- " unique_id: (x,y) tuple.\n",
- "\n",
- " unique_id isn't strictly necessary here, but it's good practice to give one to each\n",
- " agent anyway.\n",
- " \"\"\"\n",
- "\n",
- " def __init__(self, model, pos):\n",
- " \"\"\"\n",
- " Create a new tree.\n",
- " Args:\n",
- " pos: The tree's coordinates on the grid. Used as the unique_id\n",
- " \"\"\"\n",
- " super().__init__(pos, model)\n",
- " self.pos = pos\n",
- " self.unique_id = pos\n",
- " self.condition = \"Fine\"\n",
- "\n",
- " def step(self):\n",
- " \"\"\"\n",
- " If the tree is on fire, spread it to fine trees nearby.\n",
- " \"\"\"\n",
- " if self.condition == \"On Fire\":\n",
- " neighbors = self.model.grid.get_neighbors(self.pos, moore=False)\n",
- " for neighbor in neighbors:\n",
- " if neighbor.condition == \"Fine\":\n",
- " neighbor.condition = \"On Fire\"\n",
- " self.condition = \"Burned Out\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now we need to define the model object itself. The main thing the model needs is the grid, which the trees are placed on. But since the model is dynamic, it also needs to include time -- it needs a schedule, to manage the trees activation as they spread the fire from one to the other.\n",
- "\n",
- "The model also needs a few parameters: how large the grid is and what the density of trees on it will be. Density will be the key parameter we'll explore below.\n",
- "\n",
- "Finally, we'll give the model a data collector. This is a Mesa object which collects and stores data on the model as it runs for later analysis.\n",
- "\n",
- "The constructor needs to do a few things. It instantiates all the model-level variables and objects; it randomly places trees on the grid, based on the density parameter; and it starts the fire by setting all the trees on one edge of the grid (x=0) as being On \"Fire\".\n",
- "\n",
- "Next, the model needs a **step** method. Like at the agent level, this method defines what happens every step of the model. We want to activate all the trees, one at a time; then we run the data collector, to count how many trees are currently on fire, burned out, or still fine. If there are no trees left on fire, we stop the model by setting its **running** property to False."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "class ForestFire(Model):\n",
- " \"\"\"\n",
- " Simple Forest Fire model.\n",
- " \"\"\"\n",
- "\n",
- " def __init__(self, width, height, density):\n",
- " \"\"\"\n",
- " Create a new forest fire model.\n",
- "\n",
- " Args:\n",
- " width, height: The size of the grid to model\n",
- " density: What fraction of grid cells have a tree in them.\n",
- " \"\"\"\n",
- " # Set up model objects\n",
- " self.schedule = RandomActivation(self)\n",
- " self.grid = Grid(width, height, torus=False)\n",
- " self.dc = DataCollector(\n",
- " {\n",
- " \"Fine\": lambda m: self.count_type(m, \"Fine\"),\n",
- " \"On Fire\": lambda m: self.count_type(m, \"On Fire\"),\n",
- " \"Burned Out\": lambda m: self.count_type(m, \"Burned Out\"),\n",
- " }\n",
- " )\n",
- "\n",
- " # Place a tree in each cell with Prob = density\n",
- " for x in range(self.width):\n",
- " for y in range(self.height):\n",
- " if self.random.random() < density:\n",
- " # Create a tree\n",
- " new_tree = TreeCell(self, (x, y))\n",
- " # Set all trees in the first column on fire.\n",
- " if x == 0:\n",
- " new_tree.condition = \"On Fire\"\n",
- " self.grid[x][y] = new_tree\n",
- " self.schedule.add(new_tree)\n",
- " self.running = True\n",
- "\n",
- " def step(self):\n",
- " \"\"\"\n",
- " Advance the model by one step.\n",
- " \"\"\"\n",
- " self.schedule.step()\n",
- " self.dc.collect(self)\n",
- " # Halt if no more fire\n",
- " if self.count_type(self, \"On Fire\") == 0:\n",
- " self.running = False\n",
- "\n",
- " @staticmethod\n",
- " def count_type(model, tree_condition):\n",
- " \"\"\"\n",
- " Helper method to count trees in a given condition in a given model.\n",
- " \"\"\"\n",
- " count = 0\n",
- " for tree in model.schedule.agents:\n",
- " if tree.condition == tree_condition:\n",
- " count += 1\n",
- " return count"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Running the model\n",
- "\n",
- "Let's create a model with a 100 x 100 grid, and a tree density of 0.6. Remember, ForestFire takes the arguments *height*, *width*, *density*."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "fire = ForestFire(100, 100, 0.6)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "To run the model until it's done (that is, until it sets its **running** property to False) just use the **run_model()** method. This is implemented in the Model parent object, so we didn't need to implement it above."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [],
- "source": [
- "fire.run_model()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "That's all there is to it!\n",
- "\n",
- "But... so what? This code doesn't include a visualization, after all. \n",
- "\n",
- "Remember the data collector? Now we can put the data it collected into a pandas DataFrame:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 6,
- "metadata": {},
- "outputs": [],
- "source": [
- "results = fire.dc.get_model_vars_dataframe()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "And chart it, to see the dynamics."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 7,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 7,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- "
"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "results.plot()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In this case, the fire burned itself out after about 90 steps, with many trees left unburned. \n",
- "\n",
- "You can try changing the density parameter and rerunning the code above, to see how different densities yield different dynamics. For example:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 8,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- ""
- ]
- },
- "execution_count": 8,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- "
"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "fire = ForestFire(100, 100, 0.8)\n",
- "fire.run_model()\n",
- "results = fire.dc.get_model_vars_dataframe()\n",
- "results.plot()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "... But to really understand how the final outcome varies with density, we can't just tweak the parameter by hand over and over again. We need to do a batch run. \n",
- "\n",
- "## Batch runs\n",
- "\n",
- "Batch runs, also called parameter sweeps, allow use to systemically vary the density parameter, run the model, and check the output. Mesa provides a BatchRunner object which takes a model class, a dictionary of parameters and the range of values they can take and runs the model at each combination of these values. We can also give it reporters, which collect some data on the model at the end of each run and store it, associated with the parameters that produced it.\n",
- "\n",
- "For ease of typing and reading, we'll first create the parameters to vary and the reporter, and then assign them to a new BatchRunner."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 9,
- "metadata": {},
- "outputs": [],
- "source": [
- "fixed_params = dict(height=50, width=50) # Height and width are constant\n",
- "# Vary density from 0.01 to 1, in 0.01 increments:\n",
- "variable_params = dict(density=np.linspace(0, 1, 101)[1:])"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 10,
- "metadata": {},
- "outputs": [],
- "source": [
- "# At the end of each model run, calculate the fraction of trees which are Burned Out\n",
- "model_reporter = {\n",
- " \"BurnedOut\": lambda m: (\n",
- " ForestFire.count_type(m, \"Burned Out\") / m.schedule.get_agent_count()\n",
- " )\n",
- "}"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 11,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Create the batch runner\n",
- "param_run = BatchRunner(\n",
- " ForestFire,\n",
- " variable_parameters=variable_params,\n",
- " fixed_parameters=fixed_params,\n",
- " model_reporters=model_reporter,\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Now the BatchRunner, which we've named param_run, is ready to go. To run the model at every combination of parameters (in this case, every density value), just use the **run_all()** method."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 12,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "100it [00:04, 11.23it/s]\n"
- ]
- }
- ],
- "source": [
- "param_run.run_all()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Like with the data collector, we can extract the data the batch runner collected into a dataframe:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 13,
- "metadata": {},
- "outputs": [],
- "source": [
- "df = param_run.get_model_vars_dataframe()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 14,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "
\n",
- "\n",
- "
\n",
- " \n",
- "
\n",
- "
\n",
- "
density
\n",
- "
Run
\n",
- "
BurnedOut
\n",
- "
height
\n",
- "
width
\n",
- "
\n",
- " \n",
- " \n",
- "
\n",
- "
0
\n",
- "
0.01
\n",
- "
0
\n",
- "
0.025000
\n",
- "
50
\n",
- "
50
\n",
- "
\n",
- "
\n",
- "
72
\n",
- "
0.73
\n",
- "
0
\n",
- "
0.989983
\n",
- "
50
\n",
- "
50
\n",
- "
\n",
- "
\n",
- "
71
\n",
- "
0.72
\n",
- "
0
\n",
- "
0.992896
\n",
- "
50
\n",
- "
50
\n",
- "
\n",
- "
\n",
- "
70
\n",
- "
0.71
\n",
- "
0
\n",
- "
0.981069
\n",
- "
50
\n",
- "
50
\n",
- "
\n",
- "
\n",
- "
69
\n",
- "
0.70
\n",
- "
0
\n",
- "
0.980057
\n",
- "
50
\n",
- "
50
\n",
- "
\n",
- " \n",
- "
\n",
- "
"
- ],
- "text/plain": [
- " density Run BurnedOut height width\n",
- "0 0.01 0 0.025000 50 50\n",
- "72 0.73 0 0.989983 50 50\n",
- "71 0.72 0 0.992896 50 50\n",
- "70 0.71 0 0.981069 50 50\n",
- "69 0.70 0 0.980057 50 50"
- ]
- },
- "execution_count": 14,
- "metadata": {},
- "output_type": "execute_result"
- }
- ],
- "source": [
- "df.head()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "As you can see, each row here is a run of the model, identified by its parameter values (and given a unique index by the Run column). To view how the BurnedOut fraction varies with density, we can easily just plot them:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 15,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/plain": [
- "(0, 1)"
- ]
- },
- "execution_count": 15,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- "
"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "plt.scatter(df.density, df.BurnedOut)\n",
- "plt.xlim(0, 1)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "And we see the very clear emergence of a critical value around 0.5, where the model quickly shifts from almost no trees being burned, to almost all of them.\n",
- "\n",
- "In this case we ran the model only once at each value. However, it's easy to have the BatchRunner execute multiple runs at each parameter combination, in order to generate more statistically reliable results. We do this using the *iteration* argument.\n",
- "\n",
- "Let's run the model 5 times at each parameter point, and export and plot the results as above."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 16,
- "metadata": {},
- "outputs": [
- {
- "name": "stderr",
- "output_type": "stream",
- "text": [
- "500it [00:22, 11.33it/s] \n"
- ]
- },
- {
- "data": {
- "text/plain": [
- "(0, 1)"
- ]
- },
- "execution_count": 16,
- "metadata": {},
- "output_type": "execute_result"
- },
- {
- "data": {
- "image/png": "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\n",
- "text/plain": [
- "
"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "param_run = BatchRunner(\n",
- " ForestFire,\n",
- " variable_params,\n",
- " fixed_params,\n",
- " iterations=5,\n",
- " model_reporters=model_reporter,\n",
- ")\n",
- "param_run.run_all()\n",
- "df = param_run.get_model_vars_dataframe()\n",
- "plt.scatter(df.density, df.BurnedOut)\n",
- "plt.xlim(0, 1)"
- ]
- }
- ],
- "metadata": {
- "anaconda-cloud": {},
- "kernelspec": {
- "display_name": "Python 3",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.8.6"
- },
- "widgets": {
- "state": {},
- "version": "1.1.2"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 4
-}
diff --git a/examples/forest_fire/forest_fire/__init__.py b/examples/forest_fire/forest_fire/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/examples/forest_fire/forest_fire/agent.py b/examples/forest_fire/forest_fire/agent.py
deleted file mode 100644
index 34ff2aa24ad..00000000000
--- a/examples/forest_fire/forest_fire/agent.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import mesa
-
-
-class TreeCell(mesa.Agent):
- """
- A tree cell.
-
- Attributes:
- x, y: Grid coordinates
- condition: Can be "Fine", "On Fire", or "Burned Out"
- unique_id: (x,y) tuple.
-
- unique_id isn't strictly necessary here, but it's good
- practice to give one to each agent anyway.
- """
-
- def __init__(self, pos, model):
- """
- Create a new tree.
- Args:
- pos: The tree's coordinates on the grid.
- model: standard model reference for agent.
- """
- super().__init__(pos, model)
- self.pos = pos
- self.condition = "Fine"
-
- def step(self):
- """
- If the tree is on fire, spread it to fine trees nearby.
- """
- if self.condition == "On Fire":
- for neighbor in self.model.grid.iter_neighbors(self.pos, True):
- if neighbor.condition == "Fine":
- neighbor.condition = "On Fire"
- self.condition = "Burned Out"
diff --git a/examples/forest_fire/forest_fire/model.py b/examples/forest_fire/forest_fire/model.py
deleted file mode 100644
index de74118f26e..00000000000
--- a/examples/forest_fire/forest_fire/model.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import mesa
-
-from .agent import TreeCell
-
-
-class ForestFire(mesa.Model):
- """
- Simple Forest Fire model.
- """
-
- def __init__(self, width=100, height=100, density=0.65):
- """
- Create a new forest fire model.
-
- Args:
- width, height: The size of the grid to model
- density: What fraction of grid cells have a tree in them.
- """
- # Set up model objects
- self.schedule = mesa.time.RandomActivation(self)
- self.grid = mesa.space.Grid(width, height, torus=False)
-
- self.datacollector = mesa.DataCollector(
- {
- "Fine": lambda m: self.count_type(m, "Fine"),
- "On Fire": lambda m: self.count_type(m, "On Fire"),
- "Burned Out": lambda m: self.count_type(m, "Burned Out"),
- }
- )
-
- # Place a tree in each cell with Prob = density
- for (contents, x, y) in self.grid.coord_iter():
- if self.random.random() < density:
- # Create a tree
- new_tree = TreeCell((x, y), self)
- # Set all trees in the first column on fire.
- if x == 0:
- new_tree.condition = "On Fire"
- self.grid.place_agent(new_tree, (x, y))
- self.schedule.add(new_tree)
-
- self.running = True
- self.datacollector.collect(self)
-
- def step(self):
- """
- Advance the model by one step.
- """
- self.schedule.step()
- # collect data
- self.datacollector.collect(self)
-
- # Halt if no more fire
- if self.count_type(self, "On Fire") == 0:
- self.running = False
-
- @staticmethod
- def count_type(model, tree_condition):
- """
- Helper method to count trees in a given condition in a given model.
- """
- count = 0
- for tree in model.schedule.agents:
- if tree.condition == tree_condition:
- count += 1
- return count
diff --git a/examples/forest_fire/forest_fire/server.py b/examples/forest_fire/forest_fire/server.py
deleted file mode 100644
index 6d8f9fd31cf..00000000000
--- a/examples/forest_fire/forest_fire/server.py
+++ /dev/null
@@ -1,36 +0,0 @@
-import mesa
-
-from .model import ForestFire
-
-COLORS = {"Fine": "#00AA00", "On Fire": "#880000", "Burned Out": "#000000"}
-
-
-def forest_fire_portrayal(tree):
- if tree is None:
- return
- portrayal = {"Shape": "rect", "w": 1, "h": 1, "Filled": "true", "Layer": 0}
- (x, y) = tree.pos
- portrayal["x"] = x
- portrayal["y"] = y
- portrayal["Color"] = COLORS[tree.condition]
- return portrayal
-
-
-canvas_element = mesa.visualization.CanvasGrid(
- forest_fire_portrayal, 100, 100, 500, 500
-)
-tree_chart = mesa.visualization.ChartModule(
- [{"Label": label, "Color": color} for (label, color) in COLORS.items()]
-)
-pie_chart = mesa.visualization.PieChartModule(
- [{"Label": label, "Color": color} for (label, color) in COLORS.items()]
-)
-
-model_params = {
- "height": 100,
- "width": 100,
- "density": mesa.visualization.Slider("Tree density", 0.65, 0.01, 1.0, 0.01),
-}
-server = mesa.visualization.ModularServer(
- ForestFire, [canvas_element, tree_chart, pie_chart], "Forest Fire", model_params
-)
diff --git a/examples/forest_fire/readme.md b/examples/forest_fire/readme.md
deleted file mode 100644
index 6a16f976d02..00000000000
--- a/examples/forest_fire/readme.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Forest Fire Model
-
-## Summary
-
-The [forest fire model](http://en.wikipedia.org/wiki/Forest-fire_model) is a simple, cellular automaton simulation of a fire spreading through a forest. The forest is a grid of cells, each of which can either be empty or contain a tree. Trees can be unburned, on fire, or burned. The fire spreads from every on-fire tree to unburned neighbors; the on-fire tree then becomes burned. This continues until the fire dies out.
-
-## 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 the model analyses, use the ``Forest Fire Model`` Notebook.
-
-## Files
-
-### ``forest_fire/model.py``
-
-This defines the model. There is one agent class, **TreeCell**. Each TreeCell object which has (x, y) coordinates on the grid, and its condition is *Fine* by default. Every step, if the tree's condition is *On Fire*, it spreads the fire to any *Fine* trees in its [Von Neumann neighborhood](http://en.wikipedia.org/wiki/Von_Neumann_neighborhood) before changing its own condition to *Burned Out*.
-
-The **ForestFire** class is the model container. It is instantiated with width and height parameters which define the grid size, and density, which is the probability of any given cell having a tree in it. When a new model is instantiated, cells are randomly filled with trees with probability equal to density. All the trees in the left-hand column (x=0) are set to *On Fire*.
-
-Each step of the model, trees are activated in random order, spreading the fire and burning out. This continues until there are no more trees on fire -- the fire has completely burned out.
-
-
-### ``forest_fire/server.py``
-
-This code defines and launches the in-browser visualization for the ForestFire model. It includes the **forest_fire_draw** method, which takes a TreeCell object as an argument and turns it into a portrayal to be drawn in the browser. Each tree is drawn as a rectangle filling the entire cell, with a color based on its condition. *Fine* trees are green, *On Fire* trees red, and *Burned Out* trees are black.
-
-## Further Reading
-
-Read about the Forest Fire model on Wikipedia: http://en.wikipedia.org/wiki/Forest-fire_model
-
-This is directly based on the comparable NetLogo model:
-
-Wilensky, U. (1997). NetLogo Fire model. http://ccl.northwestern.edu/netlogo/models/Fire. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
-
diff --git a/examples/forest_fire/requirements.txt b/examples/forest_fire/requirements.txt
deleted file mode 100644
index bcbfbbe220b..00000000000
--- a/examples/forest_fire/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-jupyter
-matplotlib
-mesa
diff --git a/examples/forest_fire/run.py b/examples/forest_fire/run.py
deleted file mode 100644
index 98c1adf25f2..00000000000
--- a/examples/forest_fire/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from forest_fire.server import server
-
-server.launch()
diff --git a/examples/hex_snowflake/Readme.md b/examples/hex_snowflake/Readme.md
deleted file mode 100644
index 990e1dea994..00000000000
--- a/examples/hex_snowflake/Readme.md
+++ /dev/null
@@ -1,27 +0,0 @@
-# Conway's Game Of "Life" on a hexagonal grid
-
-## Summary
-
-In this model, each dead cell will become alive if it has exactly one neighbor. Alive cells stay alive forever.
-
-
-## 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 ``run``.
-
-## Files
-
-* ``hex_snowflake/cell.py``: Defines the behavior of an individual cell, which can be in two states: DEAD or ALIVE.
-* ``hex_snowflake/model.py``: Defines the model itself, initialized with one alive cell at the center.
-* ``hex_snowflake/portrayal.py``: Describes for the front end how to render a cell.
-* ``hex_snowflake/server.py``: Defines an interactive visualization.
-* ``run.py``: Launches the visualization
-
-## Further Reading
-[Explanation of how hexagon neighbors are calculated. (The method is slightly different for Cartesian coordinates)](http://www.redblobgames.com/grids/hexagons/#neighbors-offset)
diff --git a/examples/hex_snowflake/hex_snowflake/cell.py b/examples/hex_snowflake/hex_snowflake/cell.py
deleted file mode 100644
index 32656c53027..00000000000
--- a/examples/hex_snowflake/hex_snowflake/cell.py
+++ /dev/null
@@ -1,58 +0,0 @@
-import mesa
-
-
-class Cell(mesa.Agent):
- """Represents a single ALIVE or DEAD cell in the simulation."""
-
- DEAD = 0
- ALIVE = 1
-
- def __init__(self, pos, model, init_state=DEAD):
- """
- Create a cell, in the given state, at the given x, y position.
- """
- super().__init__(pos, model)
- self.x, self.y = pos
- self.state = init_state
- self._nextState = None
- self.isConsidered = False
-
- @property
- def isAlive(self):
- return self.state == self.ALIVE
-
- @property
- def neighbors(self):
- return self.model.grid.iter_neighbors((self.x, self.y))
-
- @property
- def considered(self):
- return self.isConsidered is True
-
- def step(self):
- """
- Compute if the cell will be dead or alive at the next tick. A dead
- cell will become alive if it has only one neighbor. The state is not
- changed here, but is just computed and stored in self._nextState,
- because our current state may still be necessary for our neighbors
- to calculate their next state.
- When a cell is made alive, its neighbors are able to be considered in the next step. Only cells that are considered check their neighbors for performance reasons.
- """
- # assume no state change
- self._nextState = self.state
-
- if not self.isAlive and self.isConsidered:
- # Get the neighbors and apply the rules on whether to be alive or dead
- # at the next tick.
- live_neighbors = sum(neighbor.isAlive for neighbor in self.neighbors)
-
- if live_neighbors == 1:
- self._nextState = self.ALIVE
- for a in self.neighbors:
- a.isConsidered = True
-
- def advance(self):
- """
- Set the state to the new computed state -- computed in step().
- """
- self.state = self._nextState
diff --git a/examples/hex_snowflake/hex_snowflake/model.py b/examples/hex_snowflake/hex_snowflake/model.py
deleted file mode 100644
index 6a932907cd4..00000000000
--- a/examples/hex_snowflake/hex_snowflake/model.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import mesa
-
-from hex_snowflake.cell import Cell
-
-
-class HexSnowflake(mesa.Model):
- """
- Represents the hex grid of cells. The grid is represented by a 2-dimensional array of cells with adjacency rules specific to hexagons.
- """
-
- def __init__(self, width=50, height=50):
- """
- Create a new playing area of (width, height) cells.
- """
-
- # Set up the grid and schedule.
-
- # Use SimultaneousActivation which simulates all the cells
- # computing their next state simultaneously. This needs to
- # be done because each cell's next state depends on the current
- # state of all its neighbors -- before they've changed.
- self.schedule = mesa.time.SimultaneousActivation(self)
-
- # Use a hexagonal grid, where edges wrap around.
- self.grid = mesa.space.HexGrid(width, height, torus=True)
-
- # Place a dead cell at each location.
- for (contents, x, y) in self.grid.coord_iter():
- cell = Cell((x, y), self)
- self.grid.place_agent(cell, (x, y))
- self.schedule.add(cell)
-
- # activate the center(ish) cell.
- centerishCell = self.grid[width // 2][height // 2]
-
- centerishCell.state = 1
- for a in centerishCell.neighbors:
- a.isConsidered = True
-
- self.running = True
-
- def step(self):
- """
- Have the scheduler advance each cell by one step
- """
- self.schedule.step()
diff --git a/examples/hex_snowflake/hex_snowflake/portrayal.py b/examples/hex_snowflake/hex_snowflake/portrayal.py
deleted file mode 100644
index a0a4020e896..00000000000
--- a/examples/hex_snowflake/hex_snowflake/portrayal.py
+++ /dev/null
@@ -1,18 +0,0 @@
-def portrayCell(cell):
- """
- This function is registered with the visualization server to be called
- each tick to indicate how to draw the cell in its current state.
- :param cell: the cell in the simulation
- :return: the portrayal dictionary.
- """
- if cell is None:
- raise AssertionError
- return {
- "Shape": "hex",
- "r": 1,
- "Filled": "true",
- "Layer": 0,
- "x": cell.x,
- "y": cell.y,
- "Color": "black" if cell.isAlive else "white",
- }
diff --git a/examples/hex_snowflake/hex_snowflake/server.py b/examples/hex_snowflake/hex_snowflake/server.py
deleted file mode 100644
index 3095bd5c582..00000000000
--- a/examples/hex_snowflake/hex_snowflake/server.py
+++ /dev/null
@@ -1,13 +0,0 @@
-import mesa
-
-from hex_snowflake.portrayal import portrayCell
-from hex_snowflake.model import HexSnowflake
-
-width, height = 50, 50
-
-# Make a world that is 50x50, on a 500x500 display.
-canvas_element = mesa.visualization.CanvasHexGrid(portrayCell, width, height, 500, 500)
-
-server = mesa.visualization.ModularServer(
- HexSnowflake, [canvas_element], "Hex Snowflake", {"height": height, "width": width}
-)
diff --git a/examples/hex_snowflake/requirements.txt b/examples/hex_snowflake/requirements.txt
deleted file mode 100644
index 1ad1bbec7ab..00000000000
--- a/examples/hex_snowflake/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-mesa
\ No newline at end of file
diff --git a/examples/hex_snowflake/run.py b/examples/hex_snowflake/run.py
deleted file mode 100644
index 8bc7c1469df..00000000000
--- a/examples/hex_snowflake/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from hex_snowflake.server import server
-
-server.launch()
diff --git a/examples/pd_grid/analysis.ipynb b/examples/pd_grid/analysis.ipynb
deleted file mode 100644
index 53a63345884..00000000000
--- a/examples/pd_grid/analysis.ipynb
+++ /dev/null
@@ -1,231 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Demographic Prisoner's Dilemma\n",
- "\n",
- "The Demographic Prisoner's Dilemma is a family of variants on the classic two-player [Prisoner's Dilemma](https://en.wikipedia.org/wiki/Prisoner's_dilemma), first developed by [Joshua Epstein](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.8.8629&rep=rep1&type=pdf). The model consists of agents, each with a strategy of either Cooperate or Defect. Each agent's payoff is based on its strategy and the strategies of its spatial neighbors. After each step of the model, the agents adopt the strategy of their neighbor with the highest total score. \n",
- "\n",
- "The specific variant presented here is adapted from the [Evolutionary Prisoner's Dilemma](http://ccl.northwestern.edu/netlogo/models/PDBasicEvolutionary) model included with NetLogo. Its payoff table is a slight variant of the traditional PD payoff table:\n",
- "\n",
- "
\n",
- "
**Cooperate**
**Defect**
\n",
- "
**Cooperate**
1, 1
0, *D*
\n",
- "
**Defect**
*D*, 0
0, 0
\n",
- "
\n",
- "\n",
- "Where *D* is the defection bonus, generally set higher than 1. In these runs, the defection bonus is set to $D=1.6$.\n",
- "\n",
- "The Demographic Prisoner's Dilemma demonstrates how simple rules can lead to the emergence of widespread cooperation, despite the Defection strategy dominiating each individual interaction game. However, it is also interesting for another reason: it is known to be sensitive to the activation regime employed in it.\n",
- "\n",
- "Below, we demonstrate this by instantiating the same model (with the same random seed) three times, with three different activation regimes: \n",
- "\n",
- "* Sequential activation, where agents are activated in the order they were added to the model;\n",
- "* Random activation, where they are activated in random order every step;\n",
- "* Simultaneous activation, simulating them all being activated simultaneously.\n",
- "\n"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "from pd_grid.model import PdGrid\n",
- "\n",
- "import numpy as np\n",
- "\n",
- "import matplotlib.pyplot as plt\n",
- "import matplotlib.gridspec\n",
- "\n",
- "%matplotlib inline"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Helper functions"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "bwr = plt.get_cmap(\"bwr\")\n",
- "\n",
- "\n",
- "def draw_grid(model, ax=None):\n",
- " \"\"\"\n",
- " Draw the current state of the grid, with Defecting agents in red\n",
- " and Cooperating agents in blue.\n",
- " \"\"\"\n",
- " if not ax:\n",
- " fig, ax = plt.subplots(figsize=(6, 6))\n",
- " grid = np.zeros((model.grid.width, model.grid.height))\n",
- " for agent, x, y in model.grid.coord_iter():\n",
- " if agent.move == \"D\":\n",
- " grid[y][x] = 1\n",
- " else:\n",
- " grid[y][x] = 0\n",
- " ax.pcolormesh(grid, cmap=bwr, vmin=0, vmax=1)\n",
- " ax.axis(\"off\")\n",
- " ax.set_title(\"Steps: {}\".format(model.schedule.steps))"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "def run_model(model):\n",
- " \"\"\"\n",
- " Run an experiment with a given model, and plot the results.\n",
- " \"\"\"\n",
- " fig = plt.figure(figsize=(12, 8))\n",
- "\n",
- " ax1 = fig.add_subplot(231)\n",
- " ax2 = fig.add_subplot(232)\n",
- " ax3 = fig.add_subplot(233)\n",
- " ax4 = fig.add_subplot(212)\n",
- "\n",
- " draw_grid(model, ax1)\n",
- " model.run(10)\n",
- " draw_grid(model, ax2)\n",
- " model.run(10)\n",
- " draw_grid(model, ax3)\n",
- " model.datacollector.get_model_vars_dataframe().plot(ax=ax4)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "# Set the random seed\n",
- "seed = 21"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Sequential Activation"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "image/png": "iVBORw0KGgoAAAANSUhEUgAAAtIAAAHiCAYAAADF+CuaAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAIABJREFUeJzt3Xm4XXWd5/v3NzPkHCBk2AGCJOg5UUDBEBGKFCKIAqKIU4HeFoenkNKyy7baBlsLEK265XDVri7Lvig0eFtjFAFpGwdA0UJECYgKwQwgQ4QMhCkhZP7dP/Y68XByprXWns/79Tz7OXuv8bdX9jr5nLW/+7sjpYQkSZKkfMY1ewCSJElSOzJIS5IkSQUYpCVJkqQCDNKSJElSAQZpSZIkqQCDtCRJklSAQVqSJEkqwCDdISJiUUTcFhFPR8QTEfGLiHhFNu/dEXFrs8cIEBHviIiHIuLZiLguIvZv9pikRmiHczQiDoiI6yPi0YhIETF3wPzJEXFFRDwTEWsi4iPNGalUX21yvr4+Im6NiKey8/GrEdHdb77nawMYpDtAROwDfB/478D+wEHAJ4GtzRzXQBFxOPD/Av8BqACbgX9r6qCkBmiXcxTYBfwQeMsQ8y8BeoBDgFcD/yUiTm3M0KTGaKPzdV/g08CBwEuAOcDn+s2/BM/X+kspeWvzG7AQeGqIeS8BtgA7gU19ywGTgc8DDwNrgf8B7JXNOxFYDfxX4HHgQeCd/bZ5OrAM2Aj8CfjPoxznPwHf7Pf4hcA2oLvZx9Cbt3re2uUc7bf+BCABcwdM/xPw2n6PPwV8q9nH15u3Wt7a7Xztt503A7/v99jztQE3r0h3hhXAzoi4KiJOi4hpfTNSSvcB5wO/TCl1pZT2y2Z9BugFjgJeRPUv7ov6bXM2MCObfi5wWUTMz+ZdDrw/pdQNHAH8pG+l7C2mRUOM83Dgt/3Gdj/VIN1b7GlLbaNdztEhZWM+kH7ncHb/8Lzbklpcu56vJwD3Zut5vjaIQboDpJSeARZRvYL0VWB9VudYGWz5iAjgr4H/lFJ6IqW0kerV4rMHLPoPKaWtKaWfAf8HeHs2fTtwWETsk1J6MqV0V7+x7JdSGqp2rAt4esC0p4HuQZaVOkYbnaPD6cp+9j+HPX/VcdrxfI2IU6gG9L7w7vnaIAbpDpFSui+l9O6U0hyqf9EeCHxpiMVnAnsDd2Z/7T5FtS5yZr9lnkwpPdvv8UPZNqFaP3k68FBE/CwijhvlMDcB+wyYtg/Vt7OkjtYm5+hwNmU/+5/Dnr/qSO10vkbEscA3gbemlFZkkz1fG8Qg3YFSSn8ArqR68kP1r+r+HgeeAw7P/trdL6W0b0qpq98y0yJiar/HLwAezbZ/R0rpTGAWcB3w7VEO7V7gyL4HEXEo1bqyFUOuIXWgFj5Hhxvzk8Bj9DuHs/v3lt221Mpa+XyNiJcD1wPvTSnd3G/Mnq8NYpDuABHx4oj4+4iYkz0+GDgHuD1bZC0wJyImAaSUdlF9u+qLETErW+egiHjdgE1/MiImRcRfAmcA38kevzMi9k0pbQeeofqhi9H4BvCGiPjL7BfKpcA12dtgUsdqo3OUiJhC9Q9cgMnZ4z5fBz4REdMi4sVU386+crTbltpBu5yvEXEE1SvfH0op/e9BFvF8bQCDdGfYCLwS+FVEPEv1ZL8H+Pts/k+o/hW6JiIez6ZdAKwCbo+IZ4CbgPn9trkGeJLqX8zfAM7P/iqHavu6B7P1zgf+r76VImJT9ktiDymle7PlvwGso1qr9YESz1tqF21xjmae489vC/8he9znYuB+qm9L/wz4XErph6M6AlL7aJfz9e+plo9cni23KSL6X3H2fG2ASGngOxQa6yLiROB/ZbVhklqM56jUPjxfO5tXpCVJkqQCDNKSJElSAZZ2SJIkSQV4RVqSJEkqwCAtSZIkFTCh2QMYVsTz6k5ijx7o9ZOIXMuPNLaRtpf3uQ3c3sD1R5o/3PbKHue8+y67/YHKHos8+671v9tIy5NSvhdmow04Z6Uxr5XPWc9X6fkKnK9ekZYkSZIKMEhLkiRJBRikJUmSpAJaukZ6pFrXsttr1LqjWT9vzXPZ9YcbT9mx1HpsZWui846n6LK1MPKxkCRJrcIr0pIkSVIBBmlJkiSpAIO0JEmSVEBL10iX7b08cP0y26t1H+i865ftU11GrXtWl91fXiONd7j9la3nbua/myRJqi+vSEuSJEkFGKQlSZKkAgzSkiRJUgEtXSNda3lrZfNsq6yyvZLzjqdML+Vaq3dNdd75/R+X7f89UK17o0uSpObxirQkSZJUgEFakiRJKsAgLUmSJBXQ0jXS9e4fPNzyIy1b6x7XI6n18sMdm3rXf5f5dxnN8nnnD7f9Wo9NkiR1Dq9IS5IkSQUYpCVJkqQCDNKSJElSAS1dIz2SsnXKtawFrnfdb1l5eivXeiz1fq71PPZltz0Sa6olSWpfXpGWJEmSCjBIS5IkSQUYpCVJkqQC2rpGutb1q3l6Kde7j3TZ3sllanUb3Ud6pOXzrl/r8efZdq17ZkuSpNblFWlJkiSpAIO0JEmSVIBBWpIkSSqgrWukBypbvzrcsiPJW/ta7xrqvOMfro90nh7UtVDP5zbY+mX6XJc9FvlfB9ZQS5LUKrwiLUmSJBVgkJYkSZIKMEhLkiRJBbRVjXS9e/TmqW+tdw11XmXqfMuOpWyf6LL/jmV7bNezJrrWrxNJktQ6vCItSZIkFWCQliRJkgowSEuSJEkFtFWN9EjK1s7m2Va963pr2RN7NPPzbDtvn+eyy+dV62PXSCOPXZIktQqvSEuSJEkFGKQlSZKkAgzSkiRJUgEtXSNdpsYZ8te+9l++0fseSa1romtZH15W2R7YjaxxbnQ99Z7HwippSZJahVekJUmSpAIM0pIkSVIBBmlJkiSpgJaukS7bD7hMbW+j64TzyltHXEaZWvNazM/7XMv2wa7nv22Z2nVJktRavCItSZIkFWCQliRJkgowSEuSJEkFtHSNdFl5a2FrWZ+at+62bJ1u2drbWtYF17N2fTTqeaxr3c877/J2kZYkqXV4RVqSJEkqwCAtSZIkFWCQliRJkgpoqxrpRtbe5l231jXO9a6xHm5/ecfS6J7aZfeX51jmfa556+zzb88qaUmSWoVXpCVJkqQCDNKSJElSAQZpSZIkqYCWrpEu2/e5TF/oWtYcj2b5Wo8n7/aGU3asI22v3vXgeccz3PbqXwM9/PpWSEuS1Dq8Ii1JkiQVYJCWJEmSCjBIS5IkSQW0dI10reWpzc1bC1vrut1a72+gMjXWta7XzltfXuvtD7e/keqn89bp5621lyRJrcsr0pIkSVIBBmlJkiSpAIO0JEmSVEBb10jXsw653jXHtR57LXs3l61RrnXNc73rzfOMN29v89r3PreGWpKkVuEVaUmSJKkAg7QkSZJUgEFakiRJKqCla6Tz1p+W1X/7ZWtfmz1/oDzHLm9N8kjbLls/3sy+1Y3uA13vntqSJKl2vCItSZIkFWCQliRJkgowSEuSJEkFtHSNdPmeu8MvP1w9a61rU2tdO1vP/ZV97o2u6611rfxw6t2z2ppoSZLah1ekJUmSpAIM0pIkSVIBBmlJkiSpgJaukc5b+1q213KZfZcda975eXs95+nNXLaXca2PxUjL17N3c62Pc/l/V0mS1Cq8Ii1JkiQVYJCWJEmSCmjp0o6y8pYA1LL0I+9b+I38+vPR7D/PsmWfS9nnWrakJ08bxJG+/rzW7ev23J7FHZIktQqvSEuSJEkFGKQlSZKkAgzSkiRJUgFtXSNdy3Z3g61fZix5t93orxAfbv1af2112frvWn8td5nlyz6XRn6duSRJqi+vSEuSJEkFGKQlSZKkAgzSkiRJUgFtXSNd617MZb4aeqSxlVXr/sR5xlfPr+BuxP4HKvM6qXetuyRJah9ekZYkSZIKMEhLkiRJBRikJUmSpALaukY6bx/pWta35q2hLts7udH7q+W2B6p1H+m8NdhlXie1HstI6++5PUmS1Cq8Ii1JkiQVYJCWJEmSCjBIS5IkSQW0dI10rXsn13J7eets89bW5t1+LcdT777RZeu9692Hutavu+Hk71dulbQkSa3CK9KSJElSAQZpSZIkqQCDtCRJklRAS9dI562NrWWdctk62bw10WW3P1LN9Ejy9E6ud/123n7fZV8XjewbXb4+XJIktQqvSEuSJEkFGKQlSZKkAgzSkiRJUgEtXSNd6zrlkepZm9lbOe/+yvZGHm48ta5Zzru9WteXlzlWtR5r+eWtkpYkqVV4RVqSJEkqwCAtSZIkFWCQliRJkgpo6RrpvMr36B19bexI+85Tfz2a9QcqWz+eZ9u1PK6jmT9Q3uVHGl+ZdetZmz7YY0mS1Lq8Ii1JkiQVYJCWJEmSCjBIS5IkSQW0dI102d7NI8lT/5q3VrZsnXC964iH21/eOt28zyWvssdmoDK9oevd43rk5ypJklqFV6QlSZKkAgzSkiRJUgEGaUmSJKmAlq6RrnXd8EjzG9lvuGy/4Eb2Yi5b351X/euMR//c8/471rsntiRJah1ekZYkSZIKMEhLkiRJBRikJUmSpAJaukZ6JGXrkIebX+8e1o2u985TN1zrsQ1U5t9lsP2V7fGdZ/+1rgcfqN6vO0mSVDtekZYkSZIKMEhLkiRJBRikJUmSpALaqka6lv2BB9N/e2XrbmvdH7jetbl59lXvntm17iNddvlarTsaI2/PGmpJklqFV6QlSZKkAgzSkiRJUgEGaUmSJKmAlq6RrnXtbT3lrSsuW4dctmZ6uPVHGmuebY1mbGX7So+kbF/peo4lfz23JElqFV6RliRJkgowSEuSJEkFGKQlSZKkAlq6RnokeWtvy/QALtubuNY102UNd+zK7jvvsSrbE7uex6rWPbPLb88qaUmSWoVXpCVJkqQCDNKSJElSAQZpSZIkqYC2qpEuW3ubpw65bN3uQGVrputtuP3Vusd1Kz03qG3tfLOfqyRJahyvSEuSJEkFGKQlSZKkAgzSkiRJUgFtVSOdt245b31q/+3Xu290rbeXtz/xcPvPO9a8yvZibmRN9khjrfW/syRJah9ekZYkSZIKMEhLkiRJBRikJUmSpALaqka6bO1umdrcvLWtta7brff2htt23rHk2Vcj9l/L8VkDLUmS+nhFWpIkSSrAIC1JkiQVYJCWJEmSCmjpGul61+KW7b1cZl95lR1bnhrrWvakLqLZdcjD9dSuZ236SGOpri9JklqFV6QlSZKkAgzSkiRJUgEGaUmSJKmAlq6Rzitv/Wqe5cvWCZettR1p/bLbG25beeeXPe71rsnOc2zKjrXWz1WSJLUOr0hLkiRJBRikJUmSpAIM0pIkSVIBLV0jnbcWN2/d8HDbr3e/4Lx1xGX3l2d7te55XXZ7ZevLy7wuytaul7Xn/q2pliSpVXhFWpIkSSrAIC1JkiQVYJCWJEmSCmjpGumBmtmDt2y9dtka5rL14HnG18z67MG2V7ZXc5ljV/Y1VvZ1IUmSWpdXpCVJkqQCDNKSJElSAQZpSZIkqYC2qpEeKG/9aS37B+etaa71+rWuS87z3EfaV6PrxUdSZnt5x1rr+m5JktS6vCItSZIkFWCQliRJkgpo69KOWr+tXmbZsmPJK29JQJnx1Pvr0kdafqR2dWXVs7zC9neSJHUur0hLkiRJBRikJUmSpAIM0pIkSVIBLV0jXev60VrWq9b7K7/L7j/v9of7Wuxat5srq2xLujxq+dXrkiSps3hFWpIkSSrAIC1JkiQVYJCWJEmSCmjpGum89alltzdcbW+jeyeXVcvn2uyvQ2/09vqvX/a5lZ1vjbUkSa3LK9KSJElSAQZpSZIkqQCDtCRJklRAS9dI17pfcC3rTcvUIBdZvt7r13PdRvZ9LrL94Y5lrft1S5KkzuEVaUmSJKkAg7QkSZJUgEFakiRJKqCla6RHMlJ9atkewHnk3VYzx5p3+/Wux250z+089eu1rl23plqSpM7hFWlJkiSpAIO0JEmSVIBBWpIkSSqgrWukB6pnv+K8ta617oVc7zri4Zav93Ov9bHL28M7j7K17NZES5LUObwiLUmSJBVgkJYkSZIKMEhLkiRJBbR1jXTeWtg8PXzLrFtkbCOpd9/p4Xon5+0DXevey3nV8tjWuu6+3rX0kiSpcbwiLUmSJBVgkJYkSZIKMEhLkiRJBbR0jXTeOuO8dct5eiePpN69lUean3f/zexn3Owe23mee61r3cvXb0uSpFbhFWlJkiSpAIO0JEmSVIBBWpIkSSogUrLqUpIkScrLK9KSJElSAQZpSZIkqQCDtCRJklSAQVqSJEkqwCAtSZIkFWCQliRJkgowSEuSJEkFGKQlSZKkAgzSkiRJUgEGaUmSJKkAg7QkSZJUgEFakiRJKsAgLUmSJBVgkJYkSZIKMEhLkiRJBRikJUmSpAIM0pIkSVIBBmlJkiSpAIO0JEmSVIBBWpIkSSrAIC1JkiQVYJCWJEmSCjBIS5IkSQUYpCVJkqQCDNKSJElSAQZpSZIkqQCDtCRJklSAQVqSJEkqwCAtSZIkFWCQliRJkgowSEuSJEkFGKQlSZKkAgzSkiRJUgEGaUmSJKkAg7QkSZJUgEFakiRJKsAgLUmSJBVgkJYkSZIKmNDsAQxnxowZae7cuc0ehiRJkjrcnXfe+XhKaWaedVo6SM+dO5elS5c2exiSJEnqcBHxUN51LO2QJEmSCjBIS5IkSQUYpCVJkqQCWrpGWpIkKa/t27ezevVqtmzZ0uyhqAVNmTKFOXPmMHHixNLbMkhLkqSOsnr1arq7u5k7dy4R0ezhqIWklNiwYQOrV69m3rx5pbdnaYckSeooW7ZsYfr06YZo7SEimD59es3erTBID+LT31/G/7o9dwcUSZLUIgzRGkotXxsG6UHcdv8GbrpvbbOHIUmSpBZmkB5Eb6WLFWs2NnsYkiSpja1Zs4azzz6bF77whRx22GGcfvrprFixotnD2u26665j2bJlux9fdNFF3HTTTTXfz7XXXktE8Ic//KHm2wa4++67ueGGG+qy7ZEYpAfRU+nm0ae3sHHL9mYPRZIktaGUEmeddRYnnngi999/P8uWLeOf/umfWLu2se9479y5c8h5A4P0pZdeymte85qaj2Hx4sUsWrSIb33rWzXfNhikW878SjcAK9dtavJIJElSO/rpT3/KxIkTOf/883dPO+qoo1i0aBEf/ehHOeKII3jpS1/KkiVLgGrwHmz6LbfcwgknnMBZZ53FYYcdxvnnn8+uXbsA+PGPf8xxxx3HggULeNvb3samTdXcMnfuXC699FIWLVrEd77zHb761a/yile8giOPPJK3vOUtbN68mdtuu43rr7+ej370oxx11FHcf//9vPvd7+bqq6/evY2LL76YBQsW8NKXvnT31eT169dzyimnsGDBAt7//vdzyCGH8Pjjjw95HDZt2sQvfvELLr/88ucF6V27dvGBD3yAww8/nDPOOIPTTz99977vvPNOXvWqV3H00Ufzute9jsceewyAE088kQsuuIBjjjmG3t5e/v3f/51t27Zx0UUXsWTJEo466iiWLFnCz372M4466iiOOuooXv7yl7NxY/2qDEZsfxcRVwBnAOtSSkdk0z4HvAHYBtwPvCel9FQ272PA+4CdwH9MKf0om34q8N+A8cDXUkr/XPunUxu9WZBesWYjC14wrcmjkSRJRX3yf9/Lskefqek2DztwHy5+w+HDLnPPPfdw9NFH7zH9mmuu4e677+a3v/0tjz/+OK94xSs44YQTuO222wadDvDrX/+aZcuWccghh3DqqadyzTXXcOKJJ/LpT3+am266ialTp/KZz3yGL3zhC1x00UVAtVfyrbfeCsCGDRv467/+awA+8YlPcPnll/OhD32IN77xjZxxxhm89a1vHfQ5zJgxg7vuuot/+7d/4/Of/zxf+9rX+OQnP8lJJ53Exz72MX74wx9y2WWXDXscrrvuOk499VR6e3vZf//9ueuuu1iwYAHXXHMNDz74IL///e9Zt24dL3nJS3jve9/L9u3b+dCHPsT3vvc9Zs6cyZIlS/j4xz/OFVdcAcCOHTv49a9/zQ033MAnP/lJbrrpJi699FKWLl3Kv/7rvwLwhje8gS9/+cscf/zxbNq0iSlTpgw7xjJG00f6SuBfga/3m3Yj8LGU0o6I+AzwMeCCiDgMOBs4HDgQuCkierN1vgycAqwG7oiI61NKy2hBc6btxV4Tx7NirVekJUlS7dx6662cc845jB8/nkqlwqte9SruuOOOIafvs88+HHPMMRx66KEAnHPOOdx6661MmTKFZcuWcfzxxwOwbds2jjvuuN37+au/+qvd9++55x4+8YlP8NRTT7Fp0yZe97rXjWqsb37zmwE4+uijueaaa3aP/9prrwXg1FNPZdq04S84Ll68mA9/+MMAnH322SxevJgFCxZw66238ra3vY1x48Yxe/ZsXv3qVwOwfPly7rnnHk455RSgWppywAEHDDqmBx98cNB9Hn/88XzkIx/hne98J29+85uZM2fOqJ5vESMG6ZTSzyNi7oBpP+738Hag70+ZM4FvpZS2An+MiFXAMdm8VSmlBwAi4lvZsi0ZpMeNC3oqXaxc5wcOJUlqZyNdOa6Xww8/fHepQn8ppUGXH2o67NmuLSJIKXHKKaewePHiQdeZOnXq7vvvfve7ue666zjyyCO58sorueWWW0bxDGDy5MkAjB8/nh07dow4zoE2bNjAT37yE+655x4igp07dxIRfPaznx32OBx++OH88pe/HPWYBrrwwgt5/etfzw033MCxxx7LTTfdxItf/OJRjzuPWtRIvxf4QXb/IOCRfvNWZ9OGmt6yemZ1s9zOHZIkqYCTTjqJrVu38tWvfnX3tDvuuINp06axZMkSdu7cyfr16/n5z3/OMcccwwknnDDodKiWdvzxj39k165dLFmyhEWLFnHsscfyi1/8glWrVgGwefPmITuCbNy4kQMOOIDt27fzjW98Y/f07u7u3PXDixYt4tvf/jZQrdF+8sknh1z26quv5l3vehcPPfQQDz74II888gjz5s3j1ltvZdGiRXz3u99l165drF27dne4nz9/PuvXr98dpLdv386999477JgGPo/777+fl770pVxwwQUsXLiwbt1CoGSQjoiPAzuAvn+VwTpcp2GmD7bN8yJiaUQsXb9+fZnhldJb6WLdxq08vdnOHZIkKZ+I4Nprr+XGG2/khS98IYcffjiXXHIJ73jHO3jZy17GkUceyUknncRnP/tZZs+ezVlnnTXodIDjjjuOCy+8kCOOOIJ58+Zx1llnMXPmTK688krOOeccXvayl3HssccOGRg/9alP8cpXvpJTTjnleVdmzz77bD73uc/x8pe/nPvvv39Uz+viiy/mxz/+MQsWLOAHP/gBBxxwAN3d3YMuu3jxYs4666znTXvLW97CN7/5Td7ylrcwZ84cjjjiCN7//vfzyle+kn333ZdJkyZx9dVXc8EFF3DkkUdy1FFHcdtttw07ple/+tUsW7Zs94cNv/SlL3HEEUdw5JFHstde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\n",
- "text/plain": [
- "
"
- ]
- },
- "metadata": {},
- "output_type": "display_data"
- }
- ],
- "source": [
- "m = PdGrid(50, 50, \"Simultaneous\", seed=seed)\n",
- "run_model(m)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": []
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python [conda env:mesa]",
- "language": "python",
- "name": "conda-env-mesa-py"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.6.6"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 1
-}
diff --git a/examples/pd_grid/pd_grid/__init__.py b/examples/pd_grid/pd_grid/__init__.py
deleted file mode 100644
index e69de29bb2d..00000000000
diff --git a/examples/pd_grid/pd_grid/agent.py b/examples/pd_grid/pd_grid/agent.py
deleted file mode 100644
index 57e247240ea..00000000000
--- a/examples/pd_grid/pd_grid/agent.py
+++ /dev/null
@@ -1,49 +0,0 @@
-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)
diff --git a/examples/pd_grid/pd_grid/model.py b/examples/pd_grid/pd_grid/model.py
deleted file mode 100644
index d2445c88d61..00000000000
--- a/examples/pd_grid/pd_grid/model.py
+++ /dev/null
@@ -1,62 +0,0 @@
-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()
diff --git a/examples/pd_grid/pd_grid/portrayal.py b/examples/pd_grid/pd_grid/portrayal.py
deleted file mode 100644
index a7df44a439f..00000000000
--- a/examples/pd_grid/pd_grid/portrayal.py
+++ /dev/null
@@ -1,19 +0,0 @@
-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",
- }
diff --git a/examples/pd_grid/pd_grid/server.py b/examples/pd_grid/pd_grid/server.py
deleted file mode 100644
index 50095311ac5..00000000000
--- a/examples/pd_grid/pd_grid/server.py
+++ /dev/null
@@ -1,22 +0,0 @@
-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
-)
diff --git a/examples/pd_grid/readme.md b/examples/pd_grid/readme.md
deleted file mode 100644
index 8b4bc40c88f..00000000000
--- a/examples/pd_grid/readme.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Demographic Prisoner's Dilemma on a Grid
-
-## Summary
-
-The Demographic Prisoner's Dilemma is a family of variants on the classic two-player [Prisoner's Dilemma]. The model consists of agents, each with a strategy of either Cooperate or Defect. Each agent's payoff is based on its strategy and the strategies of its spatial neighbors. After each step of the model, the agents adopt the strategy of their neighbor with the highest total score.
-
-The model payoff table is:
-
-| | Cooperate | Defect|
-|:-------------:|:---------:|:-----:|
-| **Cooperate** | 1, 1 | 0, D |
-| **Defect** | D, 0 | 0, 0 |
-
-Where *D* is the defection bonus, generally set higher than 1. In these runs, the defection bonus is set to $D=1.6$.
-
-The Demographic Prisoner's Dilemma demonstrates how simple rules can lead to the emergence of widespread cooperation, despite the Defection strategy dominating each individual interaction game. However, it is also interesting for another reason: it is known to be sensitive to the activation regime employed in it.
-
-## How to Run
-
-##### Web based model simulation
-
-To run the model interactively, run ``mesa runserver`` in this directory.
-
-##### Jupyter Notebook
-
-Launch the ``Demographic Prisoner's Dilemma Activation Schedule.ipynb`` notebook and run the code.
-
-## Files
-
-* ``run.py`` is the entry point for the font-end simulations.
-* ``pd_grid/``: contains the model and agent classes; the model takes a ``schedule_type`` string as an argument, which determines what schedule type the model uses: Sequential, Random or Simultaneous.
-* ``Demographic Prisoner's Dilemma Activation Schedule.ipynb``: Jupyter Notebook for running the scheduling experiment. This runs the model three times, one for each activation type, and demonstrates how the activation regime drives the model to different outcomes.
-
-## Further Reading
-
-This model is adapted from:
-
-Wilensky, U. (2002). NetLogo PD Basic Evolutionary model. http://ccl.northwestern.edu/netlogo/models/PDBasicEvolutionary. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL.
-
-The Demographic Prisoner's Dilemma originates from:
-
-[Epstein, J. Zones of Cooperation in Demographic Prisoner's Dilemma. 1998.](http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.8.8629&rep=rep1&type=pdf)
diff --git a/examples/pd_grid/requirements.txt b/examples/pd_grid/requirements.txt
deleted file mode 100644
index bcbfbbe220b..00000000000
--- a/examples/pd_grid/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-jupyter
-matplotlib
-mesa
diff --git a/examples/pd_grid/run.py b/examples/pd_grid/run.py
deleted file mode 100644
index ec7d04bebfa..00000000000
--- a/examples/pd_grid/run.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from pd_grid.server import server
-
-server.launch()
diff --git a/examples/schelling/README.md b/examples/schelling/README.md
deleted file mode 100644
index 64cc9c83295..00000000000
--- a/examples/schelling/README.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# 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.
diff --git a/examples/schelling/analysis.ipynb b/examples/schelling/analysis.ipynb
deleted file mode 100644
index 50f382c66a0..00000000000
--- a/examples/schelling/analysis.ipynb
+++ /dev/null
@@ -1,457 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Schelling Segregation Model\n",
- "\n",
- "## Background\n",
- "\n",
- "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",
- "\n",
- "## Implementation\n",
- "\n",
- "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."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 1,
- "metadata": {},
- "outputs": [],
- "source": [
- "import matplotlib.pyplot as plt\n",
- "\n",
- "%matplotlib inline\n",
- "\n",
- "from model import Schelling"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "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."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 2,
- "metadata": {},
- "outputs": [],
- "source": [
- "model = Schelling(10, 10, 0.8, 0.2, 3)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "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:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [
- {
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "100\n"
- ]
- }
- ],
- "source": [
- "while model.running and model.schedule.steps < 100:\n",
- " model.step()\n",
- "print(model.schedule.steps) # Show how many steps have actually run"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "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:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "model_out = model.datacollector.get_model_vars_dataframe()"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 5,
- "metadata": {},
- "outputs": [
- {
- "data": {
- "text/html": [
- "