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": "iVBORw0KGgoAAAANSUhEUgAAAfsAAAEWCAYAAABhUT6OAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nOzdd3gc1bn48e+7Vb3L6rLce8E2xmC68Q2mh5oQWoAQbiAhJL8ASUglySUJKeTSk5Bg4NJLKAYChBIMJsg2uMuWZVm9d620q909vz9mJcu2mm0Ve3k/z7OPd8+cmXlnLemdc+bMHDHGoJRSSqnwZRvrAJRSSik1sjTZK6WUUmFOk71SSikV5jTZK6WUUmFOk71SSikV5jTZK6WUUmFOk706ZCLyAxH5ywDLvyIi/xzNmEaTiFwlIh/0+twmIhMHqL9ZRE4eleAOIyLygIj8aKzjUOrzSJO9GhIRuVRE8kOJrFJEXhOR4wGMMb8yxlwbqpcnIkZEHN3rGmMeN8b81+EU80gyxsQYY4pCMfxdRH6xz/JZxph3R2LfInKWiPxHRNpFpF5EHheR7JHY1z77zQ19z90vE4qh+/MJxpjrjTF3jHQsSqn9abJXgxKR7wB/BH4FpAG5wH3AuWMZ10COxJgPlYhcCPwfcDeQAswCvMAHIpI4zPty9P5sjCkJneTEGGNiQsXzepX9ezj3r5Q6QMYYfemr3xcQD7QBFw1Q56fAY6H3JYAJrdMGHAtcBXwQWn5Lr2VtQBfw9177+itQCZQDvwDsoWVXAR8AdwGNwC5gxSHE7MY6GagIvf4IuEPLTgbKgO8CNaF4vtpr3WTgJaAF+A9wR/fxhZYbYDJwXej4fKF4Xg4tLwZOO9Q49jkeAXYDt+xTbgM2AT8P7asJmN1reSrQAYwLfT4L+DRU70Ngbq+6xcCtwAaskwjHAN+vASbvU/Z34Bf7HNstvY7tPOAMYDvQAPxgn+O4DdgJ1ANPA0lj/fuhL30dKS9t2avBHAtEAC8Msf6JoX8TjNWi+6j3QmPMb8ye1t8MoBbrDzfAI4AfK1EeBfwXcG2v1Y8BCrBarb8B/ioicpAx/xBYAswH5gGLgdt7LU/HOmnIAq4B7u3VOr4X6AQygKtDr/0YYx4CHge6j/nsYY6jt2lYvRfP7BNDEHgOWG6M8QLPA1/uVeVi4D1jTI2ILAAeBr6OdULzIPCSiLh71f8ycCbW/6+/r+M+AOlY/09ZwI+BPwOXAQuBE4Af9xr78C2sk4GTgEysE757D3H/Sn1uaLJXg0kG6obhD/teRCQSeBG42xizSkTSgBXAt40x7caYGuAPwJd6rbbbGPNnY0wA68QgA6uL/mBi/grwc2NMjTGmFvgZcHmv5V2h5V3GmFVYLfNpImIHLgB+HIpzUyiWg3VQcfSxnZTQv5V9LKvstfz/2DvZXxoqA/ga8KAx5mNjTMAY8whWC35Jr/p/MsaUGmM6hn6I/eoCfmmM6QKeDMV4tzGm1RizGdgMzA3V/TrwQ2NMWeik5afAhfteTlBK9U1/UdRg6oEUEXEMc8L/K1BgjPl16PN4wAlU9mqs24DSXutUdb8xxnhC9WLY31BizsTq9u62O1TWs4191vWE9pWK9XtTus+6B+tg49hXXejfDKxLHL1l9Fr+LyBSRI7B+j7ns6cHZDxwpYh8s9e6rn3i6X3ch6o+dOIG1qUEgOpeyzvYc6zjgRdEJNhreQDrZK98GGNSKixpy14N5iOsLuvzhlh/0GkUReQ2rNbpNb2KS7FakSnGmITQK84YM+tAA2ZoMVdgJZBuuaGywdRiXWrI2Wfd/gz2fRxsHPsqwLoGflHvQhGxYfVEvA093fpPY7XuLwVeMca0hqqXYrW0E3q9oowxTxzA8YyUUqwxGr1jizDGaKJXagg02asBGWOasa6n3isi54lIlIg4RWSFiPymj1VqgSDQ533mIrKC0PXX3l3BxphK4J/A70QkTkRsIjJJRE4aoZifAG4XkVQRSQnVf2wI2w5gXff+aWi7M4ErB1ilmn6+i0OJo4+4DPD/Qtu6VEQiRSQd+AsQh3VJpNv/AZdgXUL4v17lfwauF5FjxBItImeKSOyBxjMCHgB+KSLjAULfV9jeWaHUcNNkrwZljPk98B2sgWO1WK2sG7Guue9b1wP8ElgtIk0ismSfKpdgdYVv7XUP9gOhZVdgdRtvwRqA9SxWF/RIxPwLIB9rZPlGYF2obChuxOpersIaYf63Aer+FZgZ+i72+74OMY69GGOewrrefzNWt/0WIBJYaoyp71XvY6Adq3v+tV7l+VjX7e/B+v4Lse6COBzcjXUHxD9FpBVYgzVgUyk1BGI1CJRSSikVrrRlr5RSSoU5TfZKKaVUmNNkr5RSSoU5TfZKKaVUmAvLh+qkpKSYvLy8sQ5DKaWOKGvXrq0zxqSOwn7GORyOvwCz0UbncAgCm/x+/7ULFy6s6atCWCb7vLw88vPzxzoMpZQ6oojIoTwNcsgcDsdf0tPTZ6SmpjbabDa9JewQBYNBqa2tnVlVVfUX4Jy+6ozYGZWIPCwiNSKyqVfZb0Vkm4hsEJEXRCSh17Lvi0ihiBSIyBd6lZ8eKisMPXlNKaXUkW12ampqiyb64WGz2UxqamozVk9J33VGcP9/B07fp+xNrOk152JNY/l9gNBTyL6ENf/26cB9ImIPTTpyL9YEKTOBL4fqKqWUOnLZNNEPr9D32W9OH7Fkb4x5H2tO6t5l/+w1qccaIDv0/lzgSWOM1xizC+vJXYtDr0JjTJExxoc1M5Y+IlMppZQ6AGM5MOJq9jyqM4u9Z9MqC5X1V74fEblORPJFJL+2tnYEwlVKKRVOdu7c6Vy2bNmk8ePHz87Ozp5zxRVX5HZ0dMhA65x00kmT6+rq7KMV477+9Kc/JRcXFzsPdL0xSfYi8kOsmcMe7y7qo5oZoHz/QmMeMsYsMsYsSk0d8cGkSimljmDBYJDzzjtv8jnnnNO0e/fuTcXFxRs7OzvlG9/4RvZA67333nuFKSkpgYHqjKTHHnsspaSk5ICT/aiPxheRK4GzgGVmz4P5y9h7ytBs9kzz2V+5UkqpI9z3nv0sZ3tVa9RwbnNqeqzntxfOKx2ozssvvxzrdruDN910Uz2Aw+HggQceKM3Ly5s7ZcqUzm3btkWuXLmyBOCUU06Z/N3vfrf6rLPOas3KypqTn5+/NSMjw3/fffcl3X///WldXV2yYMGC9pUrV+4GuOSSS/I2bNgQLSLmK1/5St1PfvKTmk2bNrmvu+668fX19Q673W6eeeaZolmzZnl/9KMfpb3wwgtJPp9PzjzzzKY//OEPFQUFBa4VK1ZMWbx4cVt+fn5MWlqa74033ih85plnEjZt2hR1xRVXTIyIiAjm5+dvjYmJGdLYh1Ft2YvI6cCtwDmh2dG6vQR8SUTcIjIBmAL8B/gEmCIiE0TEhTWI76XRjFkppVT42bhxY+S8efN65yGSkpKCWVlZPr/fP2BXPsC6desinn322aT8/Pxt27Zt22Kz2cwDDzyQ/NFHH0VVVlY6d+zYsXn79u1bbrjhhnqASy+9dML1119fU1BQsCU/P39bbm5u1/PPPx9XWFgYsWHDhq1bt27d8umnn0a99tprMQAlJSUR3/rWt2oKCws3x8fHB1auXJn41a9+tXH27NmelStXFm3btm3LUBM9jGDLXkSeAE4GUkSkDPgJ1uh7N/CmiACsMcZcb4zZLCJPY03J6QduCM0bjojcCLwB2IGHjTGbB9t3dUsn1S2dpMVFjMCRKaWUGi6DtcBHijEGEdkvWQ51JtjXX389dtOmTVHz5s2bAdDZ2WkbN26c/5JLLmkqLS11X3nllTlnn3128xe/+MWWxsZGW3V1teuKK65oAoiKijKAef311+Pef//9uJkzZ84E8Hg8tm3btkVMnDjRl5WV5T3uuOM6AI466ihPcXGx+1COd8SSvTHmy30U/3WA+r/Emgd93/JVwKoD2XdNq5eaFq8me6WUUn2aM2dOxz/+8Y/E3mUNDQ22+vp6R3Jysn/79u095V6vd79ecGOMXHTRRfX33ntv+b7LNm3atOWFF16Iu++++8Y99dRTSQ8++GBJXzEYY/j2t79d+b3vfa+ud3lBQYHL5XL1nHXY7XbT0dFxSD3xYfuYQtP3OD6llFKKc845p7Wzs9N2zz33JAP4/X6+8Y1v5Fx99dU1kydP9m3evDkqEAhQWFjo3LBhQ/S+659++uktr7zySmJ5ebkDoLq62r59+3ZXZWWlIxAIcNVVVzX94he/KN+4cWNUUlJSMD093ffoo48mAHR0dEhra6ttxYoVLY8++mhKc3OzDWDXrl3O7u31JyYmJtDc3HzAdwOEbbIPaq5XSinVD5vNxosvvlj4/PPPJ44fP352YmLifJvNxq9//euq5cuXt+Xk5HinTZs266abbsqZOXOmZ9/1Fy5c2Hn77beXL1u2bOrUqVNnnnrqqVNLS0udxcXFzuOPP37a9OnTZ1599dUTfv7zn5cBPPbYY7vuvffecVOnTp25aNGi6aWlpY7zzz+/5aKLLmo4+uijp0+dOnXmF7/4xUlNTU0DJvIrrrii7pvf/Ob46dOnz2xraxt0bEE3Ger1iSOJO2OKWfPxfzgqN3HwykoppQAQkbXGmEUjvZ/PPvuseN68eXWD1xw9b775ZvSVV1458amnntp5wgkn7JfcjwSfffZZyrx58/L6WhaWE+FAPzfjK6WUUn1Yvnx5e0VFxcaxjmOkhG03fhh2WCillFIHJWyTvbbtlVJKKUvYJnsdoKeUUkpZwjbZaze+UkopZQnjZK/ZXimllIJwTvZjHYBSSqnD2sqVKxNEZOH69esHfNzqvtPKXnLJJePXrl17RD2iNWyTfVBb9koppQbw5JNPJi1YsKDt0UcfTRqo3r7Tyj711FO7Fy5c2DnyEQ6fsL3PXpv2Sil1BHjxhhxqtgzrFLeMm+nhvHsHnGCnubnZlp+fH/PWW28VnHvuuZN///vfVwDcfvvtaU8//XSyiLBs2bLmo48+2rPvtLKnnnrq1Lvuuqv0o48+it61a5f7gQceKAOrB2Dt2rVRjzzySGlf0986HGOXcsM22WuuV0op1Z/HH3884eSTT26eO3euNyEhIfDBBx9EVVRUOF599dXEtWvXbouNjQ1WV1fb09LSAvfff/+4u+66q/TEE0/c68l6l19+eeOSJUumA2UAzz77bNIPf/jDyt7T37rdbnPZZZflPvDAA8k33nhj/ZgcLOGc7DXbK6XU4W+QFvhIefrpp5NuuummGoALLrig4dFHH00KBoNcdtlldbGxsUGAtLS0wEDbyMzM9Ofk5Hjffvvt6FmzZnUWFRVFLF++vO3OO+9M7Wv625E/qv6Fb7LXtr1SSqk+VFVV2desWRO3ffv2yBtvvJFAICAiYs4444wmkSHPLQPAhRde2PjEE08kTp8+vXPFihWNNpttwOlvx0oYD9Ab6wiUUkodjh599NHE888/v76iomJjeXn5xqqqqg3Z2dm+pKQk/6OPPprS2tpqA2vaWhh4WtnLLrus8fXXX0985plnki699NIG6H/629E6vr6EbbLX++yVUkr15Zlnnkk+//zzG3uXnXvuuY0VFRXOFStWNM2fP3/G9OnTZ95xxx3pMPC0sqmpqYEpU6Z0lJeXu0855RQP9D/97egd4f7Cdorb199dzSnTxo11KEopdcT4PE9xGw4GmuI2bFv2esleKaWUsoRtsteH6iillFKWsE32muuVUkopS/gm+7EOQCmllDpMhG+y16a9UkopBYRzsh/rAJRSSqnDRPgme23ZK6WUGkBUVNRRAy0/6qijpgMUFBS4pkyZMutAtn3BBRfk/e1vf0s8lPiG04glexF5WERqRGRTr7IkEXlTRHaE/k0MlYuI/ElECkVkg4gs6LXOlaH6O0TkyqHuX3O9UkqpQ7F+/fptYx3DcBnJZ+P/HbgHWNmr7DbgbWPMnSJyW+jzrcAKYErodQxwP3CMiCQBPwEWYfXMrxWRl4wxez35qC+a65VS6vD3o9U/yilsLBzWKW4nJ0723LH0jiFNsNPc3Gw7/fTTJzc3N9v9fr/8+Mc/rrjsssuawGr5ezye9b3r+/1+brjhhuzVq1fH+nw++drXvlbzve99ry4YDHLVVVflrl69OjYnJ8d7uPUuj1iyN8a8LyJ5+xSfC5wcev8I8C5Wsj8XWGmsb2eNiCSISEao7pvGmAYAEXkTOB14YvD9H/IhKKWUCnNRUVHBV199tTApKSlYWVnpOOaYY6ZfeumlTTZb3x3ff/zjH1Pi4+MDmzZt2trR0SFHH3309LPPPrvl448/jiosLHQXFBRsLisrc86ZM2fWVVddNWZT2u5rtGe9SzPGVAIYYypFpPt5tllA77OwslBZf+X7EZHrgOsAXOmT9aE6Sil1BBhqC3ykBINB+fa3v529Zs2aGJvNRk1NjausrMyRm5vb55S0b731Vty2bduiXnrppUSA1tZW+5YtWyLee++92IsvvrjB4XCQl5fXdeyxx7aO7pEM7HCZ4ravOQXNAOX7FxrzEPAQWM/G11SvlFJqMA8++GBSfX29Y+PGjVvdbrfJysqa09HR0e94NmOM/O53vyu54IILWnqXv/LKK/EHOj3uaBrt0fjVoe55Qv/WhMrLgJxe9bKBigHKB3W4XS9RSil1+GlubranpKR0ud1u8/LLL8dWVFQMOBXt8uXLm++///5Ur9crABs2bHC3tLTYTjrppNZnnnkmye/3s3v3bueaNWtiR+cIhma0W/YvAVcCd4b+/Uev8htF5EmsAXrNoW7+N4BfdY/aB/4L+P4ox6yUUirMdHV14XK5zLXXXtuwYsWKybNnz54xa9Ysz4QJEzoHWu/mm2+uKy4uds+ZM2eGMUaSkpK6Vq1atfPyyy9vevvtt+OmTZs2a8KECZ2LFy/+fHTji8gTWAPsUkSkDGtU/Z3A0yJyDVACXBSqvgo4AygEPMBXAYwxDSJyB/BJqN7PuwfrDUYb9koppfqTn58fmZOT483IyPB/+umnfd5i1z0Sf9q0ab4dO3ZsBrDb7dxzzz3lQPm+9VeuXFkyokEfgpEcjf/lfhYt66OuAW7oZzsPAw8f6P51gJ5SSqm+/OY3v0l98MEHx/32t78d08GBo+lwGaA37DTXK6WU6sstt9xSe8stt9SOdRyjKXwflzvWASillOpPMBgMHr5D149Aoe8z2N/y8E322rRXSqnD1aba2tp4TfjDIxgMSm1tbTywqb862o2vlFJqVPn9/murqqr+UlVVNZswbnSOoiCwye/3X9tfhfBN9tqRr5RSh6WFCxfWAOeMdRyfJ2F7RqUte6WUUsoSvsl+rANQSimlDhPhm+w12yullFJAGCd7faiOUkopZQnbZK+pXimllLKEbbLXfnyllFLKErbJXlO9UkopZQnbZB8MarpXSimlIIyTvaZ6pZRSyhK+yV6zvVJKKQWEc7If6wCUUkqpw0T4Jntt2iullFJAWCf7sY5AKaWUOjyEb7LXjnyllFIKGEKyF5GLRCQ29P52EXleRBaMfGiHRlv2SimllGUoLfsfGWNaReR44AvAI8D9IxvWodNcr5RSSlmGkuwDoX/PBO43xvwDcI1cSMNDJ8JRSimlLENJ9uUi8iBwMbBKRNxDXG9Maa5XSimlLENJ2hcDbwCnG2OagCTgeyMalVJKKaWGzaDJ3hjjAf4BtItILuAEth3KTkXkZhHZLCKbROQJEYkQkQki8rGI7BCRp0TEFarrDn0uDC3PG8o+9D57pZRSyjKU0fjfBKqBN4FXQ69XDnaHIpIFfAtYZIyZDdiBLwG/Bv5gjJkCNALXhFa5Bmg0xkwG/hCqNyjN9UoppZRlKN34NwHTjDGzjDFzQq+5h7hfBxApIg4gCqgETgWeDS1/BDgv9P7c0GdCy5eJiAy2A530TimllLIMJdmXAs3DtUNjTDlwF1CCleSbgbVAkzHGH6pWBmSF3meFYiC0vBlIHnQ/evOdUkopBVgt7MEUAe+KyKuAt7vQGPP7g9mhiCRitdYnAE3AM8CKPqp2Z+u+WvH7ZXIRuQ64DsCVPlm78ZVSSqmQobTsS7Cu17uA2F6vg3UasMsYU2uM6QKeB44DEkLd+gDZQEXofRmQAxBaHg807LtRY8xDxphFxphFoA/VUUoppboN2rI3xvwMQESijTHtw7DPEmCJiEQBHcAyIB94B7gQeBK4EusOAICXQp8/Ci3/lxnCUHsdja+UUkpZhjIa/1gR2QJsDX2eJyL3HewOjTEfYw20WwdsDMXwEHAr8B0RKcS6Jv/X0Cp/BZJD5d8Bbhvafg42QqWUUiq8DOWa/R+xnon/EoAx5jMROfFQdmqM+Qnwk32Ki4DFfdTtBC464H1oR75SSikFDPGxt8aY0n2KAn1WPEwI2rJXSimlug2lZV8qIscBJvRUu28R6tI/nGmuV0oppSxDadlfD9yAdb97GTA/9Pmw1uUPjnUISiml1GFhKC37SGPMV3oXiEj6CMUzLCKcdj4p3u/uPKWUUupzaSgt+12hyWoie5WtGqmAhkOUy05R7XDcJaiUUkod+YaS7DcC/wY+EJFJobJBn00/lpx2G61eP+1e/+CVlVJKqTA3lGRvjDH3YQ3Me1lEzuYwH//mtFvnIlUtnWMciVJKKTX2hnLNXgCMMatFZBnwFDB9RKM6RA6bDT/Q2O6D1LGORimllBpbQ0n2Z3S/McZUisipWM+yP2zZbFbLvlW78ZVSSqn+k72IXGaMeQz4cj/Tx78/YlEdInt3su/UZK+UUkoN1LKPDv3b1wx3h/U1e1vo5KRNk71SSinVf7I3xjwYevuWMWZ172UisnREozpE9tCww9bOrrENRCmllDoMDGU0/v8OseywYRNBBFo02SullFIDXrM/FmsgXqqIfKfXojjAPtKBHaop42L4z64GjDH0M+ZAKaWU+lwYqGXvAmKwTghie71agAtHPrRDs3RyCp8UN/KbNwrGOhSllFJqTA10zf494D0R+bsxZvcoxjQsJqRY4wvvf3cnt55+WD8WQCmllBpRA3Xj/9EY823gHhHZb/S9MeacEY3sEF26OJdfv7aNmIihPEpAKaWUCl8DZcJHQ//eNRqBDDeH3ca3T5vKL1dtpaHdR1K0a6xDUkoppcbEQMm+RERmhrrze4jILKBmZMMaHjMz4wDYWtnC0skpYxyNUkopNTYGGqD3v/T9ZPls4O6RCWd4zciwkv1bW6vHOBKllFJq7AyU7Ofs26oHMMa8AcwduZCGT1K0i0XjE3n84xI8Pn2anlJKqc+ngZK98yCXHVa+fdpUfP4gD7xXNNahKKWUUmNioGS/Q0TO2LdQRFYAR0zmPH5KCjMy4nhhfRk7qlspa/SMdUhKKaXUqBpogN7NwCsicjGwNlS2CDgWOGukAxtOly8Zzw9e2MjyP1gT9d14ymSaOnxMTo3h8mPzembJU0oppcLRQA/V2S4ic4BLgdmh4veArxtjOkcjuOFy6TG5BIJB/ue1bXh8Ae55p7Bn2U9f3kJqrJuvHJOLzx9kRkYca3c3YrcJHV0BlkxM5ozZ6dhtMuTH7rZ7/WytbCHCaWdGRhwN7T5+8tImLjk6l3nZ8bR5/UQ67by9tabnOQAf7axnYmo0Xzo6lz/9awcCnL8gi5217ZwwJYUolwNjDP6gwRma6acrEGTt7kbWlzQxZVwMeSlRfLSznppWL8nRLuKjnJw3P+uAHhfsDwRx2IcyZYKlpbOLDl+AhCgnNpGe2A4X/kDwgP7v1Ojp/lnrCgSxh+azCATNgD9/gaDhs7ImSuo9TM+IZXp63ID7+LionormDpbPTMcYQ9DA6sI6Jo+LYWpaXxN6Hrw2r5/3CmqZmBrNhJRo/EFDjHvPn1h/IEhRXTuRTjs5SVEA+PxBXI7h+Z0xxlBY00aE005Du4/EKBcfFNYxPyeBuEgHaXERff5+dvgCtHn9/M+qrcMShzo8iTGjP1utiCQAf8E6iTDA1UAB8BSQBxQDFxtjGsX6K303cAbgAa4yxqwbaPuLFi0y+fn5fS5bX9LIO9tqWL2znnGxbsoaO9hY3jxozC67jSWTkllf0khchDVkISnaxcWLsimsaeO97bVMHhfDW1tH5q7EaWmxuBw2NpY3E+2yk50YRUF166DrnTQ1lfk5CWQlRnL3Wzu4cGE2OUlRrN3dyIrZ6Wwoa8Jpt3HspGR++tJm1pU0MSszDpsIXn+Aby2bQn2bj43lzczOjGNOdjzlTZ20e/3c9UYBLZ1ddAX2/AzdfNpUzpiTzri4CF5cX05ClJMTp6TyyoYKNle0kJcSzZ/e3oHHF+DPVyzi+XVlJEW7+NFZMwkaQ5TLQWtnF43tXWQkRFDd0kmk005yjJtA0GC3CV2BII98WMyrGytZX9LEF4/K4toTJjAzI46yxg7e31GLxxvA4wvw8oYKshIiiY908m5BDfNyEiiqbSfKZWdqeiydvgBzsuOZkBLNzIw4cpKiiHBaUz90JyNjDG9vrWHp5BQiXf1PC2GMwesPUtPiJTc5qqd8W1ULqwvrWTIxiaRoF26HnfhIJ1sqWshNjmLd7kZOnpbac0ISDBp8gWBPHN38gSBtXj/tvgBpsW42lDeztriRorp2vnR0DvNyEthU3szfVhdz5tx0cpOisIkQG+Fkc0UzNS1ePiiso6q5k8yECL68OJekaBedXUH+U9zA+KQoTp0+jvd31FLa2MFlx+QiIgSChu6/EyKCTeiJdVN5M6UNHtxOG1PGxdLa6ScrMRJjDMZAc0cXBdWtvFtQw5qiBnbVtXP10gmsKapnW1ULs7PiKWnw0OTZM2nV/JwEOnwBEqOdZMZHEhfpZGN5MxvLmvEFgnt9JwtyE5g8LoYfnDGDCKed0gYP7++oo7MrwEc76/mgsK7f/687zpvNCZNT2FHTRkZ8BCUNHuZkxfPRznrm5SQwISUau02w24QtFS1srWwhLyWKo3ISaff5ebeglh3VrVS1dPJZaXOfv4/zsuNZNiONLx2dw81Pf8rqwvr96lx/0iRuW2E95bOyuYM/vLmdpGg3mQkR/HNzNfNy4pmeHkd1SydtXhfMhG8AACAASURBVH/Pz0VRbRt2m42i2jbKGjto9Pjw+AL9Hm+36emxjIuLoN3rxxjDupKmnmW7f33WWmPMokE3oo44Y5XsHwH+bYz5i4i4gCjgB0CDMeZOEbkNSDTG3BoaN/BNrGR/DHC3MeaYgbY/ULLvS35xAxFOO3ab8HR+KaUNHSydnMxJU1P587+LeOI/pQA47VYL0ecPMjMjjppWL3Vt3v22lxztYvnMNP65pZqGdh8AE1OiqW31khjtoqTBGjdw+5kz+KS4gYz4SC5bkst97+zktU1V3Lx8Cksnp3DuPavxB/f8/0Q4bXR2Bffb376+v2I6bV4/nxQ3sKaoYcjfQ28Om+y17/6Mi3UzeVwMAB/u3P8P2YGyCThstv3+qGfGR9Do6cLlsNHccfCzGYrAYD/y3XVE4KicBGwi5O9uJDnaxe1nzWBbVSuvbazC4wtQ1+Yl1u0gNdZNUV17zzbGxbqZmBpNZnwkL2+o2OuEyGkXMuIje34OwEoKsRFO1u5uJGgMInDKtHGUNnqwidDm9VNU277XNnpvE6yTz+6ft4PV+2csJcaFMVDfxzaH+vMxVKmxbmLcDnaFvsPcpCh8/iBVLXt3Is7PSeDT0iayEiIpb+oYcJsi8MX5WXxhdjo/fGFTz+9q9zaGU0Z8BJ1dARo9XRwzIYnSBg8Vzft3gNoE+vraYt2O0EydB3fXUFZCJFPTYjhmYjIby5rZWtXCjHTr5HxNUT3vFtQC1vfsstto7ezq2ZfTLhw7KYWcxEh+df5cTfZhatSTvYjEAZ8BE02vnYtIAXCyMaZSRDKAd40x00TkwdD7J/at198+DjTZD0V3S8VmEzq7AkQ47XT4AqwvbSQjPpLkGBcNbT68/iBT02IQkZ4u5Davn2iXA1tobEBhTSuTUmP67Fru3Y1e09qJ02YjLtLZM67AHwhS3epld317KNHG9rlut21VLazaUElucjR5yVHsrvdgtwlNHh+ZCZF4/UF217fz8meVLBifwLeWTcEY64/C21tr2FHdyoyMONp9fpKiXazaWEmHL8CyGWkcPzmFhCjnXi3SD3fWs7G8mc9Kmyiub2dKWiw1LZ0smzGOtLgIvF1BTpuZxv/+aweVTVa5xxfgl6u2ctbcDDAQG+HAbrPx+qZKAsZw6vRxbChrZnNFCwlRTpKiXHztxIlMTYslEDQ8t7aMmtZOot0OjIEF4xOxC6TFRXDytHF0BYMYA/GRTkobPKwurOP02ekEgoZ3Cmp5fVMVC8cn0trZxYvry6lv9xEb4aQrENzrxMLlsOHzB3tOBhKjnDR6upibHU+Tp4uSBg9RLjvnzMvkyU9Ke9ZLjnZx5XF5fLSzntgIBxXNHXi7grR7/WQnRuENBKlr9RLpsjM+KYrEaBefFDfQ5OkiIz6CCKcdfzDIpvIWjp2YzNF5iawtaeQLs9KJj3QSF+Hkbx8W09BuJbOZGXFkJUThdtqobOqgvKmD3KRoVsxJJ9rloKiujefXlfd0IVc0dXDMhCQinHY+3FlPU4eP02els66kiY3lzfj8e068upN8Sowbt8NGeVMHd5w3m896JeCESCepsW4aPD4a231MSo3htJlpZCVE9lweK2voIDHauvQTH+nsaa2WNnjISojs+V3x+YNUNHVQXN/O9upWvnbCxJ5kWd3SyQeFdXj9QTaWNVFY08Zxk1JIinaxZGIyCVFOMhMiAXp6J7p/P97fXsvO2jbW7m6krs1LZ1eQzIQIfP4gCVEunl1bttfv0emz0pmYGk1lcycvrC8nPtLJZUtyWTIxmeMmpezV29H793FHTRvPryvD4wswPjmKa46fiE2goLoVh81GjNvB3W9vp6XDT1cgSGyEk+zESJbPTKO21cuivET+ta2G97fXkRLj4rIl46lv9+Hx+kmPjyA3KQp/0OzXCzQU3X/HehMRTfZhqt9kLyJvG2OWicivjTG3DtsOReYDDwFbgHlYg/9uAsqNMQm96jUaYxJF5BXgTmPMB91xAbcaY/L32e51wHUAubm5C3fvPuLm7lGHGWMMmytamDwuhginnd317Wwqb2Hp5GQSogZ+/HJrp5X8jYGpoUsw4agrEDzsxmkMJ38guF8y9QeCBA1h+X+qyT58DTQaP0NETgLOEZEngb1OWwe7bj7IPhcA3zTGfCwidwO3DVC/r5FVfU3M8xDWSQSLFi0a/WsTKuyICLOz4ns+j0+OZnxy9JDWjY1wMiszfvCKR7hwTvRgzbHhsO9fptSRZqBk/2OsJJwN/H6fZQY49SD3WQaUGWM+Dn1+NrSfahHJ6NWNX9Orfk6v9bOBioPct1JKKfW50+8pqjHmWWPMCuA3xphT9nkdbKLHGFMFlIrItFDRMqwu/ZeAK0NlVwL/CL1/CbhCLEuA5oGu1yullFJqb4NO9m6MuUNEzgFODBW9a4x55RD3+03g8dBI/CLgq1gnHk+LyDVACXBRqO4qrJH4hVi33n31EPetlFJKfa4MmuxF5H+AxcDjoaKbRGSpMeb7B7tTY8ynWE/j29eyPuoa4IaD3ZdSSin1eTdosgfOBOYbY4LQc4/8euCgk71SSimlRs9Qh5Um9Hof/kOMlVJKqTAylJb9/wDrReQdrNvgTkRb9UoppdQRYygD9J4QkXeBo7GS/a2hEfVKKaWUOgIMpWVP6Fa3l0Y4FqWUUkqNAH0UlFJKKRXmNNkrpZRSYW7AZC8iNhHZNFrBKKWUUmr4DZjsQ/fWfyYiuaMUj1JKKaWG2VAG6GUAm0XkP0B7d6Ex5pwRi0oppZRSw2Yoyf5nIx6FUkoppUbMUO6zf09ExgNTjDFviUgUYB9sPaWUUkodHgYdjS8iX8Oac/7BUFEW8OJIBqWUUkqp4TOUW+9uAJYCLQDGmB3AuJEMSimllFLDZyjJ3muM8XV/EBEHYEYuJKWUUkoNp6Ek+/dE5AdApIgsB54BXh7ZsJRSSik1XIaS7G8DaoGNwNeBVcDtIxmUUkoppYbPUEbjB0XkEeBjrO77AmOMduMrpZRSR4hBk72InAk8AOzEmuJ2goh83Rjz2kgHp5RSSqlDN5SH6vwOOMUYUwggIpOAVwFN9koppdQRYCjX7Gu6E31IEVAzQvEopZRSapj127IXkfNDbzeLyCrgaaxr9hcBn4xCbEoppZQaBgN145/d6301cFLofS2QOGIRKaWUUmpY9ZvsjTFfHc1AlFJKKTUyhjIafwLwTSCvd32d4lYppZQ6MgxlNP6LwF+xnpoXHK4di4gdyAfKjTFnhU4qngSSgHXA5cYYn4i4gZXAQqAeuMQYUzxccSillFLhbiij8TuNMX8yxrxjjHmv+zUM+74J2Nrr86+BPxhjpgCNwDWh8muARmPMZOAPoXpKKaWUGqKhJPu7ReQnInKsiCzofh3KTkUkGzgT+EvoswCnYk2lC/AIcF7o/bmhz4SWLwvVV0oppdQQDKUbfw5wOVYy7u7GN6HPB+uPwC1AbOhzMtBkjPGHPpcBWaH3WUApgDHGLyLNofp1vTcoItcB1wHk5uYeQmhKKaVUeBlKsv8iMLH3NLeHQkTOwnpQz1oRObm7uI+qZgjL9hQY8xDwEMCiRYv02f1HKF/AR1lrmfVBYHzseOw2O+Vt5cS6YolzxY1tgEopdQQaSrL/DEhg+J6atxQ4R0TOACKAOKyWfoKIOEKt+2ygIlS/DMgBykTEAcQDDQPuoWk3bFsF088YppBHXiAYYGPdRj6p+oSACeAL+PAGvFS2V1LWWkaHvwOn3UmMM4b5qfNx2BzkxOZwau6prKteR1tXG21dbTR7m7GLndy4XOJccWyo3cDult2cNfEsytrKiHBEsDh9MQnuBGo9tWTFZmGToVzN2Z+ny0N9Zz2J7kRiXDEHvH5lWyW+oI9aTy0b6zZS3FLMOyXv0Oht7KkT54rDG/DiDXixiY2J8RNZmrmUsrYydjbtpKq9iuMyj8Mb9LKueh1zU+eSFpVGlCOKC6deyKSESVS1V9HQ2UBtRy3p0ekUNRXR4mvBJjYS3Yn8u/zfxLniWJy+GLvNTpuvDV/QR1ZMFvNT59MV7KK2oxaA1MhU6jvqCRIkxhlDcmTyQX13Sik1mmSwCexE5F1gLtZT87zd5cNx612oZf//QqPxnwGeM8Y8KSIPABuMMfeJyA3AHGPM9SLyJeB8Y8zFA213Uabd5F8XAz9tPtQQ+/RB+QckRSTxTuk7bKvfRnJkMjaxsbZ6LVGOKJZkLqGxs5H6jnqqPdUAOGwOHDYHdrEzO2U2J+ecTFJEEqWtpdR31PP3zX+nqLlor/0IQlZMFgZDSmQKyRHJlLWVsb1x+wHFaxc7ARPoc1l6dDqJ7kQiHZHYxMbc1LnUd9TjsrtYkLaAnNgcHDYHmdGZPLb1MSraKohwRLC+ej07m3futZ0EdwLbGrYxOWEyE+In0BXoorilGLvYCRIkMzqTyQmTmZo0lVW7VrG6fPVesThsDk7MOpGTc04m0hFJVXsVn9V+Rrw7nuzYbLbUb+HN3W/27G9SwiSAnu1MSZxCs7cZT5eHtq62nu/Q7N8RNCxsYmNSwiQcYp0zp0WlEeOKwRvwEu+Oxxfw9fRGGAwT4iaQFJmEL+AjaIJUt1cTMAGyY7OZnjSdcVHjRiTOoarvqCfeHU/QBLGLHbvNPqbxqNEnImuNMYvGOg41/IaS7E/qq3w4RuTvk+wnsufWu/XAZcYYr4hEAI8CR2G16L9kjCnqb5swvMneH/TzUcVHvFv6LoVNhXQGOtlSv6VneWpkKnUddRgMbrubeFc8NR01PclzZvJMoh3RNHmb2Nm8k1Zfa5/7mZwwma/M+ArLxy/HbXfj8XuId8X3+Qc3EAzQ6G1kTeUadjXvYnH6YuLd8djFTnZsNjWeGtp8bZS1lTE9aToJ7gQ+rPiQWFcsMc4Y/l3+bzxdHjKiM1hXsw5fwEd5WznegJfytnLiXHH4Aj46A5377bv7eGenzGZ2ymzy4vKo76ynqKkIX9DHjsYdpEalsr1hO13BLqYmTiUlMoVmbzMtvhZ2t+zGYHDYHFw39zpyYnNw293MS51HckTyoAnGGGMlo171giaIMQa7zU73z3N9Zz3PbX+OzkAn8a54EiMSGR83ns31m5mWOI3EiETsYqcz0EmCOwGA9TXrsYud+s56JsRP4O3db+OwOfAH/cxJnUPQBClpKbF6DpxR7Gjcwa7mXQB0mS7KWsvwB/00e5vp8Hf0e4LVF0E4LvM4qj3VTEmYQltXGzWeGqYlTeP6udeTFJmE0+bEZXftt+7Opp1sa9iGXeysqVyDp8tDnDuOmckzOTbjWNKj0+k9ptUf9PNe2Xu8U/KO9Z1i2NW8i411G3GIA7/xMy5qHBdMuYAoRxQJEQl4ujykRaXRZbr4V8m/2Fq/lar2Kpx2J2lRaTR7m0mOTMYu9p6fo+SIZM6ceCbtXe1sqd9CZ6ATp81JnCuOCEcEy3KXcXLOydR31FPaWkpyZDIlLSU4bU4WpC0g2hmNIHrSMYo02YevQZP9kehgk33QBNneuJ1Paz7FJjby4vL4bf5v2dawjUhHJBnRGVS2V3JS9knkxOawIG0Bx2cdj6fLQ2egk0R3IiKCL+Drs2XU/V2XtZaxpWELnf5OMmMyiXPFMSVxykF3pw8XYwy1HbUkuhNB4J2SdyhqLiItKo31Nes5bfxpnJh9Iv6gH4dt4CtAHf4OPF2e/bq5W3wtbKjdwNTEqWPekh1J/qAfX8BHfnU+s1NmEzRBChoK6PR34vF7yInN6TlBq2yv5IPyD3ih8AWavc247W4cNgcR9giavE17nTSkR6czJWEKn9Z+ijGGWFcsle2V++2/O2mDdSnEYXOQHJlMRVsFvoCPrmAXEfYIEiMSey6RLM1cSlJEEp2BTtZVr6OgsaDf45ubOpejUo+i0dtIaWsphU2FZMdkkxKZAkBmTCZrKtewu2U3ANMSpyEi7GjcgV3s+IKDDwGyi50oZxRdgS6yY7Op66hjUsIkTsk5hRpPDU3eJhLdidhsNpakLyHCEYGI8O+yf1PRXkGcK44rZ11JVkzWoPtSFk324WsoLftW9gyIcwFOoN0Yc9iOlDqQZN/ia+GpbU/x8KaH+2yNxbpiufXoWzlt/GlEO6NHKmSlCJogNZ4a0qPTMcZgMOxo3MEnVZ/wdsnb5FfnkxKZgjGGeHc8ubG5NPuaiXXFct7k80iOSKatq43js44naII8t/05iluKWVu9lsyYTDr9nWTHZuO2u5mSOIUv5H2BSEdkn7EYY9jeuJ3UqFRqPDU4xEGjt5EaTw2n5JxClDNq0OMJBAO0+lrpCnaRGpXac4w2sbGtYRuFTYW8X/Y+s5JnMTF+InUddaRFpeE3fj6r/YwWbwulraWsq1lHTmwOca44ytvKqWyv7OlxGYzL5uKE7BPY2bSTifETsdvsZEZn4rA5iHZG09rVSou3paeXp8PfQYwzhoAJUNleSYQ9glNyTyE9yrpM1X2SNtDdv3Uddfyn8j8cn338kAaUegNeWn2t+IN+0qPTB60/kjTZh68DbtmLyHnAYmPMD0YmpEM3lGTvC/i499N7eXjTw4DVjXpKzikszVrKvNR51HhqeKP4Df57/n9ry0Cpw4Qxhh1NO0iNTCUxIpEOfwfN3mY+KP+AaGc0QRMkwZ3A0qylbK7fzONbHmdN5RpqO2qxix2X3UWHv6PPbXef+HT4O4iwR5AXn0dDZwM1nr3HJp+UfRLJkcmUt5VT1lpGbmwu4+PGU9tRS42nhi31W3oaDRnRGUyMn0hihDUuZlriNGw2Gy6bi6r2KlbtWtUzVkcQ5qTO4ei0o3E73CS5kxARUiNTcdvdxLpiiXZFkxyRTJwrbsATjoOlyT58HVQ3voisMcYsGYF4hsVgyd4X8HHzuzfzftn7RDmi+NXxv+LU3FNH5JdHKXV42VC7AX/Qj9PmJC8+j/U168mIzmBK4pT96gaCAQoaCyhrLcMX9LG6fDWvFL3SszzaGU1SRBItvhbcNjcAx2Qcw6yUWdR31LOzaSeb6zdT31Hfc1llX+dMOoeM6IyeS1yb6zcPegwOcTA5cTIzkmaQEZ3Bl6d/mXh3PKWtpWTHZh/0JUFN9uFrKN345/f6aAMWAScZY44dycAOxUDJ3hfwce0/r2V9zXpuOfoWLp52MW67ewyiVEodaYImyAs7XsBld7Esdxkuu2vQ8Svdl2S8AS/tXe1UtlVS2FRITmwOdpudo8YdtVf9Zq/1d6sr2EWnv5Oi5iJ2Ne8iOTK553bXlwpfwml39gwQ3dfUxKmckHUCtR217GjcwfFZx7MwbSHHZR43YKNGk334Gkqy/1uvj36gGPizMWa47rsfdgMl+0c2P8Jd+Xfxs+N+xvlTzu9jbaWUOjJUtVfxatGrtPpaqWivoL6jnsKmQho69zyKJNIR2XPpIi8uj/TodOaPm09GdAbHZR7XM07AGIPNZtNkH6YGfajOET2vfTAItj3dWS2+Fv688c8szVyqiV4pdcRLj07nmjnX7FUWNEHautoQhE5/J4kRiTR5m3i16FVe2/UaO5t2sqZyDQCxzlimJ0/vubtBha9+k72I/HiA9Ywx5o4RiGd4eeohJrXn432f3keLt4WbFtw0hkEppdTIsYmt5y6AWJc1/UhKZApXzrqSK2ddCUB5WzkFDQU8t+M5ChoKSI5MZk7KHFazut/tqiPbQC379j7KorGmnE0GDv9kv+k5WHI9ANsatvHEtie4eNrFzEieMcaBKaXU2MmKySIrJotTc/eez+xO7hyjiNRI6zfZG2N+1/1eRGKx5p//KtZT7n7X33qHldptPW8f3vQwsa5YvnnUN8cwIKWUUmr0DXjNXkSSgO8AX8GaU36BMaZxoHUOKx3WIBVjDJ9UfcLSzKXEu+PHOCillFJqdA10zf63wPlY08bOMca0jVpUw8VjJfui5iLqOupYnL54jANSSimlRt9AT174LpAJ3A5UiEhL6NUqIi2jE94h6rBGl3aPPF2Sedg+B0gppZQaMQNdsx/bWVmGQ/0O8DSwqmgVeXF5+thbpZRSn0tHfkLvz6Rl4O+ktTyfDXUbOHvS2WMdkVJKKTUmwjfZx2YA8FntpwBMT5o+ltEopZRSYyZ8k320Na/2S9Ufk+BOYGHawjEOSCmllBobYZ/sCzxVHDXuKJ2LXiml1OdW+CZ7m5OutFns7mpmclT6WEejlFJKjZnwTfZio+KU2wiIML6+ZKyjUUoppcZMGCd7oSQmEYDcDc9B/c4xDkgppZQaG+Gb7IGSVqtFn9Plh/9dMMbRKKWUUmMjfJO92ChtLSXKEUVyMGiVNRaPaUhKKaXUWAjjZC+UtJSQG5eLXP1Pq2zjM2Mbk1JKKTUGBpz17sgmlLSWMDVxKuQeAxNOhHWPwvHfBVv4nuMopdSAjIGyfNj6ElR+an1OmQLTzxrryNQICttkHwDK28pZlrvMKlh4FTx7NXx0Dyz91liGppRSI8fvhY5GiO11y3EwCO/+CvL/Bp66/dcp/jfkPzx6MapRN+rJXkRygJVAOhAEHjLG3C0iScBTQB5QDFxsjGkUEQHuBs4APMBVxph1g+2nNuDBH/TvmfxmxrmQNAne/BHEZcKcC4f/4JRS4e/930J7Pay4c/T26WmAyEQQgYAfit6F1GlQvQmqNkFrpfWI8OlnwCvfgdI1kDTRSvp+H3S179lWwniYvMyaP2TcDEjIhZot8OI3gA9H75jUqBqLlr0f+K4xZp2IxAJrReRN4CrgbWPMnSJyG3AbcCuwApgSeh0D3B/6d0AVPmsW3syYTKvA7oAvPwH3LoaXvw3TzgBX1HAfm1LqSBMMWklUZP9lLRVWa3jBFRCfDZueg3/9wlrWVg15x8OMs8EdB86I/ddvLoP374LWKshaCMfdaCXnVf8P7C6YdZ7VAjcGujqg5CNo3A3ps0FsVsPkrZ9BsMvaXt4J1kDj5tK+j+WdX+x531C097LcY+HCv0Fcxv7rZcyD/14N3+jjO1BhQYwxYxuAyD+Ae0Kvk40xlSKSAbxrjJkmIg+G3j8Rql/QXa+/bS7KtJufPnYr3y99mX+c9w8mxk/cs7DoPVh5jvX+6jcgV+e4V+qI11AEnc2QMb/vpO3zWMnT4baWf/oEbHkRGnZBXYFVxx0PE06w/kb4Wg9s/84o6PJY77/8pNVafuVmKP340I6rL0kTwea0Givjl1onGzFp0LATCl6DycutkxBXNJSvg4APMueDM3LQTYvIWmPMouEPWo21Mb1mLyJ5wFHAx0BadwIPJfxxoWpZQO/T2LJQ2V7JXkSuA64DWJhho6KrGYCM6H3OYieeBCf/wLp+9fAXICIBpiyHxV+HnKOhswUi4ob3QJU6UD4PmCB4W6BkDez+EHa8AYl51h/7xt2w4HKr6zb3WPC1gbcVmsutn2Owkt/uD63WYtYiKwG4Y63yjkZrUNZAmsuthFJXaHUZN+22klrypBE//L2011vJ3B0L46ZD8WorcXnqYd0jVsKu3rSnftocqwWdMN5K8K2V8PK3rOMGK1F2t5R78zbDtlf2L4/Lso6/+AOYeS781y+tk4a6HZD/1z3fZ8lHVv0nvrT3+ifeAktvsq6Jv/kjcETCefdZJwIfPwBTvgCLrrb+DyeeDOVrrZ6AiSdZJx7Tz+yZ62NASRNg8ml7l2XrBGDKMmYtexGJAd4DfmmMeV5EmowxCb2WNxpjEkXkVeB/jDEfhMrfBm4xxqztb9uLMu3mrAcv5R1PCe9d8l7fld78Caz+48BBTjgJco6xfgHTZll/OD5vJwLeVnDFQEs5xGYO/U6GYBAC3iG1Jo4YxkBnk3XtdDAlH0P1RqtldfS1kD4HKj+DzAVQ9gkE/Va3rs1uJYp/3g4Vn1qtzOQpUL/DSkoiVsvsQESnwriZsKufn/3eco+zuponngiLroGCVeBts/a75r7+10ufa+2juRSW32ElsvgssDmspJy9GDY9C1tfhuTJsOI3VvdxU6nV4mwogg1PWycQOcdYJ9yx6bDmASuht9VYCW/3h3ta3gCTToWd/+o7nqQJ0FIJZf8Z/LhTpsKX/s86IajaEDqB2gUJedBUDIkTrHoOtxXvUHS2QMV62P4GxKSC3Q2Lr7MuIfano3FoP0+jRFv24WtMkr2IOIFXgDeMMb8PlfV0zw9HN/4J953PLrp48bwX+w8kGOD/t3f/QVaV9x3H3193l91l2WX5oQgsAupKRKyRIOAPRI0/EDMh6diJNNNYY8Z2aqaa/khM40xsO52ME0cTq0lqTPzRdMSEOA1JLYYoThJjFKQWsPxGRBQDCCggAst++8f3ud67uAus7nL2nvt5zdy5nHPPvTznuc+e73m+z3PuYfV82LMtek9rftn5TNX3Cn4c1A+Gtndh+ldiCGD3ljgw1tRHT2z42dAwJLY/8G4czKtqip/Rtg/mfzXeO+Hq7C4D3L8nxgx3b4EtK+Cpf44ynXhmHPiGTYA5fxqzdAtOPBMGnBi9zXNvhNM/2TFl2t4Or/wW3lgGLzwI21bDBV+CnRvjvU0j4+C+eWnU4fk3RY+zujbqpbMxz67KfrQH4O5a+V+wZ2sEsp0b4afXQ00DzPhGBJkV8+J7GzQmDtSLvg/jPwVbV8YDYp9eW9zxc2sHRs/xcMZM61jfLedEevacL0TPsqpf1Nv+d6Ku/ndO9FpX/DzKfM4XYNH9HT9z7IUw8dq41MqOg6WPHr6Nlxo6LrIEyx+LFPUffSYyYRt/F99xT6iqjZPCrtQNjIlkaxZ0nlq/7J+iPUz/SvytAbz8mzhRMIvgO3IijL4ggqq3H307q0AK9vl1zIN9ml3/ELDd3W8uWf9N4M2SCXqD3f3LZnYV8EViNv4U4G53n3y4/2PSiCo/+56r2FvXxI9m/qh7BWxvjwNudW2M/725BpY8DCsfh+ZRxVTdkQyb0DG1OGpKpO1Kx/bqmqOnCHDdfBh91UZ+8QAAC+lJREFUbvyG/6CxcRKwe2ucKFTXFQ9Q+3bDsh/D1tWRhq2qic8cNzPSvCedB43DYtu9O6Lc656K3s7wsyIlu3VFvF7bFIH7SJpGRs++MzPviMC3bG705Nrbjq5+Co6rLr5n7HQ449ORihwwDJbOiV7nkNY4cXj+34qp2EtviyDmHpOddm6MHm3DkEhtL34ggmP9IBhwQmRkzvjj+GGlDb+FqX8FbXvjxOPA3ggMrzxz5PKeNiNmQre923F9Vb+OPfDm0bEfo8+Dn91Y3P4jn4iTqdpGWPgvsa7heJh1L5x2RfH97e3dy6IUJpjt+gOs/VV81ydO6Po9bfvh7U2RUm5vi+/uzXUR0J+9J06+Pju3617pO9vjsWJeBNkDe6ON798T392ODXGCcuKZsHVVzGBf/UQE7Cl/Gd/Lwf1w8a2RTXjhgXQivRfO/rP4+6lvjvIV6sE9HmbxN9o8GgaNPro6kqOiYJ9fWQT7C4DfAMuIS+8A/oEYt/8xcBKwEfgTd9+eTg7uAWYQl95d5+6L3/fBJSaNqPLWb1/KwOaxfO+y7/XsDrhHOvbt1+K5aWQctF7+daQm92yD3W8c+XMmfi6yCdtWv/+1kR+Ds2ZHECtomRwH0i0vHfmz6wfH+F+H9K8B6bseMy1ORFrSOVPdQJh0HfzqtjghOfWyCEatl0U5zGK/d70RvcKD+2OC06Lvd/J/D4re1riZcZDftCjGHp/9DuzdDjNujwzKkofjRGdXSYKmq7HUY6WpJSZWDT01hi9GTYGzromTprmfj0zEGZ+OwLbuqej5DjklMkSFXmV7W/Sym0YUP3f7+qi7lnM6ZnkKCgFMJGMK9vmV+Wz83jBpRJUPv3Ma44ZP4o7pd2RTiNdfjNR+w/Fxic7Pb4Jpfxe99wN7o7cJ0Svbvi5m7m5f33UPuqklguWBd2K8ccpfxInF09/ougz1g2PM8ITTY2JRYQz35Is+/P65x2VJq+fHmOcplxSHALpj58ZIkzcMSb/stSh6pm+9FunXhqGRzTiuGkZNjiBqVbDkQXjuvhjPvfDvY4LT1pUxO/mVZyID0Hp51Gldc9Tr8sfiJGvb6jjZGXxKBPS6pvjMwvCLSIVSsM+v3Ab7xm9OZvrYK7jtvNuyLk73uEcAHdgSQX390zHuWlMfcwDe2RavFbQfjKDbryF6h5teiJTxaTMOPzFIROQQCvb5ldtosPvgPgbUDMi6GN1nBuOuLC6XjuPW1HUM9BDp4+ZRxeXWQy69ERGRipfLYO/APj/AgH5lGOxFRER6WC5v/9aeJjs19mvMuCQiIiLZy2WwP5ieG2p66VpsERGRMpLLYF+4nq+xRj17ERGRXAb7gymNrzF7ERGRnAb7Qs++LGfji4iI9LBcB/v6PN2ERURE5APKZ7BPafz+1f0zLomIiEj28hns03N9tXr2IiIiuQ726tmLiIjkNdgbVFsVNZ3dYUxERKTC5DPYY9Qf1y/rYoiIiPQJuQz2DvSvqs26GCIiIn1CLoN9O1BfpZ69iIgI5DXYm6lnLyIikuQz2AP1CvYiIiJAjoN9/6q6rIshIiLSJ+Qz2JvG7EVERAryGewx9exFRESSnAZ7jdmLiIgU5DPYm8bsRURECnIZ7B1orNHv4ouIiEBOgz1AY3VD1kUQERHpE8om2JvZDDNbZWZrzeyWI23fqDveiYiIAGUS7M2sCrgXuBIYD8w2s/GHe88ApfFFRESAMgn2wGRgrbuvd/f9wBxg1uHe0FijNL6IiAiUT7AfCbxasrwprXuPmd1gZovNbPGAduP4ppOOaQFFRET6qnIJ9tbJOu+w4H6fu09y90mjjx/PyJapx6hoIiIifVu5BPtNwKiS5Rbg9YzKIiIiUlbKJdgvAlrNbKyZ9QOuAeZlXCYREZGyUJ11AY6Gu7eZ2ReBJ4Aq4Ifu/lLGxRIRESkLZRHsAdz9ceDxrMshIiJSbsoljS8iIiIfkIK9iIhIzinYi4iI5JyCvYiISM6Zux95qzJjZruAVVmXo48YCmzLuhB9hOqiSHVRpLooGufujVkXQnpe2czG76ZV7j4p60L0BWa2WHURVBdFqosi1UWRmS3OugzSO5TGFxERyTkFexERkZzLa7C/L+sC9CGqiyLVRZHqokh1UaS6yKlcTtATERGRorz27EVERCRRsBcREcm53AV7M5thZqvMbK2Z3ZJ1eXqbmY0ys4VmtsLMXjKzm9L6wWa2wMzWpOdBab2Z2d2pfpaa2cRs96BnmVmVmf2Pmf0iLY81s+dSPTyabpGMmdWm5bXp9TFZlrunmVmzmc01s5WpbZxbwW3iS+lvY7mZPWJmdZXSLszsh2a2xcyWl6zrdjsws2vT9mvM7Nos9kU+nFwFezOrAu4FrgTGA7PNbHy2pep1bcDfuvvpwFTgxrTPtwBPunsr8GRahqib1vS4AfjusS9yr7oJWFGyfDtwV6qHHcD1af31wA53PxW4K22XJ98G5rv7R4CziDqpuDZhZiOBvwYmufsE4hbZ11A57eJBYMYh67rVDsxsMPB1YAowGfh64QRBykeugj3RENe6+3p33w/MAWZlXKZe5e6b3X1J+vcu4qA+ktjvh9JmDwGfSv+eBTzs4fdAs5kNP8bF7hVm1gJcBdyflg24BJibNjm0Hgr1Mxf4eNq+7JlZE3Ah8AMAd9/v7jupwDaRVAP1ZlYN9Ac2UyHtwt1/DWw/ZHV328EVwAJ33+7uO4AFvP8EQvq4vAX7kcCrJcub0rqKkFKOZwPPAcPcfTPECQFwQtosz3X0LeDLQHtaHgLsdPe2tFy6r+/VQ3r9rbR9HpwMbAUeSEMa95tZAxXYJtz9NeAOYCMR5N8CXqAy20VBd9tBbttHJclbsO/sDLwiri00swHAT4Gb3f3tw23aybqyryMz+wSwxd1fKF3dyaZ+FK+Vu2pgIvBddz8b2EMxVduZ3NZFSjfPAsYCI4AGIl19qEpoF0fS1b5Xcp3kRt6C/SZgVMlyC/B6RmU5Zsyshgj0/+Huj6XVfyikYtPzlrQ+r3V0PvBJM9tADN9cQvT0m1P6Fjru63v1kF4fyPvTneVqE7DJ3Z9Ly3OJ4F9pbQLgUuBld9/q7geAx4DzqMx2UdDddpDn9lEx8hbsFwGtaaZtP2IizryMy9Sr0njiD4AV7n5nyUvzgMKs2WuBn5Ws/1yaeTsVeKuQ0itn7v5Vd29x9zHE9/6Uu38WWAhcnTY7tB4K9XN12j4XvRV3fwN41czGpVUfB/6PCmsTyUZgqpn1T38rhbqouHZRorvt4AngcjMblDIll6d1Uk7cPVcPYCawGlgHfC3r8hyD/b2ASKktBV5Mj5nEOOOTwJr0PDhtb8QVC+uAZcQs5cz3o4fr5CLgF+nfJwPPA2uBnwC1aX1dWl6bXj8563L3cB18FFic2sV/AoMqtU0A/wisBJYD/w7UVkq7AB4h5iocIHro13+QdgB8PtXJWuC6rPdLj+4/9HO5IiIiOZe3NL6IiIgcQsFeREQk5xTsRUREck7BXkREJOcU7EVERHJOwV6kh5jZ19Ld1Zaa2YtmNsXMbjaz/lmXTUQqmy69E+kBZnYucCdwkbvvM7OhQD/gd8T1ytsyLaCIVDT17EV6xnBgm7vvA0jB/Wri99gXmtlCADO73MyeNbMlZvaTdE8DzGyDmd1uZs+nx6lZ7YiI5I+CvUjP+CUwysxWm9l3zGy6u99N/Ib4xe5+cert3wpc6u4TiV+4+5uSz3jb3ScD9xC/6y8i0iOqj7yJiByJu+82s48B04CLgUfN7NA7zU0FxgPPpFuk9wOeLXn9kZLnu3q3xCJSSRTsRXqIux8EngaeNrNlFG82UmDAAnef3dVHdPFvEZEPRWl8kR5gZuPMrLVk1UeBV4BdQGNa93vg/MJ4fLoT22kl7/lMyXNpj19E5ENRz16kZwwA/tXMmoE24u5gNwCzgf82s81p3P7PgUfMrDa971biLo0AtWb2HHES3lXvX0Sk23TpnUgfYGYb0CV6ItJLlMYXERHJOfXsRUREck49exERkZxTsBcREck5BXsREZGcU7AXERHJOQV7ERGRnPt/5PtiRD7CCSQAAAAASUVORK5CYII=\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", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
densityRunBurnedOutheightwidth
00.0100.0250005050
720.7300.9899835050
710.7200.9928965050
700.7100.9810695050
690.7000.9800575050
\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": "iVBORw0KGgoAAAANSUhEUgAAAXwAAAD8CAYAAAB0IB+mAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMi4zLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvIxREBQAAGLhJREFUeJzt3X2MXNd53/Hvw+VKWlmyVg0Z2FqKJgvTTASpLe2F4oBA47eYsgpIhOLGUiDEKYQISaoUcVwWFBLYhoJCjInWaVC1MZ0IjlPEkuIKDGErYIFQRgrBMrQCZclSQ5SVXWmXLsS4WqGxVtaSevrHzJDD4bzcmZ3ZebnfDyB4Z+6dO2evub8585xzz43MRJI0+TYMuwGSpPVh4EtSSRj4klQSBr4klYSBL0klYeBLUkkY+JJUEga+JJWEgS9JJbFxWG+8adOm3LZt27DeXpLG0tNPP/13mbm5l9cOLfC3bdvGwsLCsN5eksZSRPzvXl9rSUeSSsLAl6SSMPAlqSQMfEkqCQNfkkrCwJekkjDwJakkOgZ+RDwYEa9ExHdbbI+I+MOIOBkRz0bEe/vfTEnSWhW58OrLwH8EvtJi+8eAHdX/fgb4z9X/laSODh9f4uDRE5xaXuGa2Rn27dnJ3l1zF22bvXyaTHhtZZVrZmf44E9t5vG/PX3RtknZr3HbVTPTRMAl73j3+3o911HkJuYRsQ34emZe32TbF4FvZuZXq49PAB/IzB+0O+b8/Hx6pa00ftoFdNHX1YJteWWVAOpTaHoqeNslG5tuE/zgT3+LH//gf0Yvr+3H0gpzwMt1jxerz7UNfEnj5/DxJe599DlWVs8CsLS8wqcefobfevgZ5tr0zq+ameZHb55h9Wwlvl99ffXcMRsDffVssryy2nSb1qYfgd/sk6bp/08RcTdwN8DWrVv78NaS1tPBoyfOhX1N7Y99aXmFex997tzz9R8MtQDXcPUj8BeBa+sebwFONdsxMw8Bh6BS0unDe0sagMayTa2WvLS80vZ1K6tn+fQj3+FsgVKx1l8/pmUeAX65Olvn/cBrner3kkZXrWyztLxCUum5/5cnX+oY9jWG/egqMi3zq8C3gJ0RsRgRd0XEr0XEr1V3eQx4ETgJfAn4jYG1VtLANSvbDFqtLnz15dNMb4iW22ZnpglgbnaGO9+/lbnZGaJh26Ts17htdmaaqy+fXtN57ljSycw7OmxP4F+uqRWSRsapgj15oKtZNNMbgisu28jy6xdPTWw12NvNLKCyiM+efLrX1w7tBiiSRkstaIsGeG1WTi2cN0S0LOc0zuBpZ++uOQN+QAx8aQJ120tunG7Zycz01Llj1vfMG48xMz3F/bfdYICPCANfmjDN5srXpks2Bm/tg6HdgOxcwxWfrT5Aao8tx4wuA1+aMM0GXVdWz3Lw6Ilz22tXu/79G2dYfat1ESeAJ/Z/qPB7W44ZbQa+NGFaDbrWroqtxXv91a6tXDM708eWadhcHlmaMO1CupsZ8rU6vSaHgS9NmH17djIzPbWmY8zNzjjYOoEs6UgTpn7wtOjVsTXOqpls9vClCXD4+BK7Dxxj+/5vsPvAMaAy2DrXoQY/PRUXXNlp2E82e/jSiFnrHPr6aZj79uy8aG587erYbi6G0mQw8KUR0s0c+pp20zBrUyqdGy8w8KWR0i68W4V0q2mYteedG68aa/jSCOkU3s20mobpHHo1MvClEdJLeDebhukcejVj4EsjpJfw3rtrjvtvu+HcuunOtlEr1vClEdLrAmTW6VWEgS+NGMNbg2JJR5JKwsCXpJIw8CWpJAx8SSoJA1+SSsLAl6SSMPAlqSQMfEkqCQNfkkrCwJekkjDwJakkDHxJKgkDX5JKolDgR8RNEXEiIk5GxP4m27dGxOMRcTwino2Im/vfVEnSWnQM/IiYAh4APgZcB9wREdc17Pa7wCOZuQu4HfhP/W6oJGltivTwbwROZuaLmfkm8BBwa8M+Cby9+vNVwKn+NVGS1A9FAn8OeLnu8WL1uXqfA+6MiEXgMeA3mx0oIu6OiIWIWDh9+nQPzZUk9apI4EeT57Lh8R3AlzNzC3Az8GcRcdGxM/NQZs5n5vzmzZu7b60kqWdFAn8RuLbu8RYuLtncBTwCkJnfAi4DNvWjgZKk/ihyT9ungB0RsR1YojIo+0sN+7wEfBj4ckT8NJXAt2Yj9dnh40td3+BcqonMxupMk50q0yz/AJgCHszMfxsR9wELmXmkOmvnS8AVVMo9/yYz/1u7Y87Pz+fCwsKafwGpLA4fX+LeR59jZfXsueeCyh/cnOFfGhHxdGbO9/LaIj18MvMxKoOx9c99pu7nF4DdvTRAUjEHj564IOzh/GDa0vIK9z76HIChr5a80lYaE6eWV9puX1k9y8GjJ9apNRpHBr40Jq6Znem4T6cPBZWbgS+NiX17djIzPdV2nyIfCiqvQjV8ScNXq80fPHqCpeWVcwO2NTPTU+zbs3MobdN4MPClMbJ319y54HeKprpl4Etjqj78pSKs4UtSSRj4klQSBr4klYSBL0klYeBLUkkY+JJUEga+JJWEgS9JJWHgS1JJeKWtNAJcJkHrwcCXhqzxTlbezESDYklHGrJmd7LyZiYaBANfGrJWNy3xZibqNwNfGrJWNy3xZibqN2v4Uh/1Mvi6b8/OC2r4cP5mJg7mqp8MfKlPeh18rb+TVX2wAw7mqq8iMzvvNQDz8/O5sLAwlPeWBmH3gWMsNam7z83O8MT+Dw39eJoMEfF0Zs738lpr+FKf9Hvw1cFc9ZuBL/VJvwdfHcxVvxn4Up/s27OTmempC56rDb6OwvEkB22lPmk1+NrrAGu/jyc5aCtJY8RBW0lSR4UCPyJuiogTEXEyIva32OcXI+KFiHg+Iv68v82Uxt/h40vsPnCM7fu/we4Dxzh8fGnYTVLJdKzhR8QU8ADw88Ai8FREHMnMF+r22QHcC+zOzFcj4icH1WBpHLkipkZBkR7+jcDJzHwxM98EHgJubdjnV4EHMvNVgMx8pb/NlMabK2JqFBQJ/Dng5brHi9Xn6r0HeE9EPBERT0bETf1qoDQJvIhKo6BI4EeT5xqn9mwEdgAfAO4A/jgiZi86UMTdEbEQEQunT5/utq3S2PIiKo2CIoG/CFxb93gLcKrJPn+ZmauZ+T3gBJUPgAtk5qHMnM/M+c2bN/faZmnseBGVRkGRwH8K2BER2yPiEuB24EjDPoeBDwJExCYqJZ4X+9lQaZzt3TXH/bfdwNzsDEFlAbT7b7vBAVutq46zdDLzTETcAxwFpoAHM/P5iLgPWMjMI9VtH42IF4CzwL7M/OEgGy6Nm7275gx4DZVX2krSGPFKW0lSRwa+JJWEgS9JJWHgS1JJGPiSVBIGviSVhIEvSSVh4EtSSRj4klQSBr4klYSBL0klYeBLUkkY+JJUEga+JJWEgS9JJWHgS1JJGPiSVBIGviSVhIEvSSVh4EtSSRj4klQSBr4klYSBL0klYeBLUkkY+JJUEga+JJWEgS9JJWHgS1JJGPiSVBIGviSVRKHAj4ibIuJERJyMiP1t9vt4RGREzPeviZKkfugY+BExBTwAfAy4DrgjIq5rst+VwL8Cvt3vRkqS1q5ID/9G4GRmvpiZbwIPAbc22e/3gM8Db/SxfZKkPikS+HPAy3WPF6vPnRMRu4BrM/Pr7Q4UEXdHxEJELJw+fbrrxkqSelck8KPJc3luY8QG4AvApzsdKDMPZeZ8Zs5v3ry5eCslSWu2scA+i8C1dY+3AKfqHl8JXA98MyIA3gEciYhbMnOhXw2VJsnh40scPHqCU8srXDM7w749O9m7a67zC6U1KBL4TwE7ImI7sATcDvxSbWNmvgZsqj2OiG8C/9qwl5o7fHyJex99jpXVswAsLa9w76PPARj6GqiOJZ3MPAPcAxwF/gfwSGY+HxH3RcQtg26gNGkOHj1xLuxrVlbPcvDoiSG1SGVRpIdPZj4GPNbw3Gda7PuBtTdLmlynlle6el7ql0KBL6m1buvx18zOsNQk3K+ZnRlkMyWXVpDWolaPX1peITlfjz98fKnla/bt2cnM9NQFz81MT7Fvz84Bt1ZlZ+BLa9BLPX7vrjnuv+0G5mZnCGBudob7b7vBAVsNnCUdaQ16rcfv3TVnwGvdGfhSAa3q9NbjNU4s6UgdtKvTW4/XODHwpQ7a1emtx2ucWNKROuhUp7cer3FhD1/qoFU93jq9xo2BL3VgnV6TwpKO1EGtXOPqlhp3Br5UgHV6TQJLOpJUEga+JJWEJR2pB96xSuPIwJe65B2rNK4s6Uhd8o5VGlcGvtQl71ilcWXgS13yyluNKwNf6pJX3mpcOWgrdambK2+dzaNRYuBLVd2Ec5Erb53No1FjSUeit5uRd+JsHo0aA19iMOHsbB6NGgNfYjDh7GwejRoDX2Iw4exsHo0aA19iMOHs/W41apylIzG4m5y4jr5GiYEvVRnOmnSFSjoRcVNEnIiIkxGxv8n2346IFyLi2Yj464h4V/+bKklai46BHxFTwAPAx4DrgDsi4rqG3Y4D85n5j4CvAZ/vd0MlSWtTpId/I3AyM1/MzDeBh4Bb63fIzMcz8/XqwyeBLf1tpiRprYoE/hzwct3jxepzrdwF/NVaGiVJ6r8ig7bR5LlsumPEncA88HMttt8N3A2wdevWgk2UJPVDkR7+InBt3eMtwKnGnSLiI8DvALdk5o+bHSgzD2XmfGbOb968uZf2SpJ6VCTwnwJ2RMT2iLgEuB04Ur9DROwCvkgl7F/pfzMlSWvVsaSTmWci4h7gKDAFPJiZz0fEfcBCZh4BDgJXAH8REQAvZeYtA2y3VFi7ZY9dr15lUujCq8x8DHis4bnP1P38kT63S+qLdmvSA65Xr1LxSltNpFrPfanJapf1yx63WhLZwNckMvA1cRp79c20W/a4ts1yjyaNga+J0+xmJo1qyx43+wZwzeyMtyfURHJ5ZE2cTjctqS173G5JZG9PqElkD18jZ62llGtmZ5r23KGyJn3j8Zq916cefqbp6709ocaZga+R0o9Syr49Oy+q4c9MTzW9+UirJZFbfWh4e0KNM0s6Gin9KKX0405T3p5Qk8gevkZKp5uJr9dFVIO6A5Y0TAa+BqrbEG5XSlnvi6i8A5YmjSUdDUwtoJeWV0jOh/Dh40stX9PrzBln1UidGfgamF5CuF39vV25p1MpqN7h40vsPnCM7fu/we4Dx9p+AEmTxJKOelKkVNNNCNfrdeZMkVk1XlClMrOHr64VLdW0msLY69TGZuWeqL7/62+eYXrDhffqaTarxtKPyszAV9eKhma/pzbWl3ugEva1W6+9+voqBMzOTLeditnrtw5pEljSUWHtVqCEi0NzEFMba+We3QeOXdSO1bPJ2y7dyDOf/WjL13tBlcrMwFchRVagbBaag5ra2GtPvdVVuF5QpTIYqcB3OdrR1WkFyvUOzV576l5QpTIbmcB39sToqf8Azjb7NVuQbNDW0lP3giqV1cgEfruBQP8411+REg5Uwv6J/R9ap1adZ09d6t7IBP44zJ7od8lpVEpYzdpR5CYitR51r7/HWn9/e+pSd0Ym8Ic5e6JI8HQqOXUbXoMuYRVdZGz28mn+/o0zrL6V59rxqYefaVvCCTh3TCi+hk2n97WEJw1WZLb70x6c+fn5XFhYOPe4WQmh1Rrm/VT0fZtNA4Tz9etu297ueGstkbT7nYBCpZpWGttX9PcoWiKqvXZQq2BK4y4ins7M+Z5eOyqBD4P9w2517KKBtX3/N5r2emu93aLh3Wkue+11rX73Ij33VseeiuDsGv7/bvYh1uq8wIXfBDr9zq3eCy7+gFqPjoA0qiYm8AelXY+3VfkigO8d+GfnHrf7YGg1iyWAL3zin7QsY7TTLNSa/R7TG4IrLtvIq6+vXnDlab+1+hBqdV7qzUxP9fSNonZF7aC+CUnjaC2BX4qlFVrNAPr0I99pGZAJF6yk2G6ZgFbjDLOXT1+w5syrr68WCvta+xqXKmj2e6y+lZVlBRhs2D+x/0NNe9TNzkujldWzTEW03aeZblfBlNTeUAdtuxlYzITXVlZ7KvW0CodO5Y2l5RX2fe07fO7I87y2ssrs5dNcunFD03Y09rwDzgVxrxrv8tRNSaQb01PB2y7ZyPLKxd8SOs1tb5we2eqMns28qKdf/77NdLMKpqTOhlbSefd1/zinf+H3expYbFfDbTXFsF9hWd/G+vf54E9t5vG/Pc3S8krXpZVWtfXa82st1bSr3fd7gLTT4HazY3c7yGwNX2U2ljX8K7bszE13/vuLnm9Xt23c74n9H2o71Q8q4fAL75vjvz691PPMlEZXXz7NG6tvNQ2hXgYn+92+xjbB+oVmr7Ot1utetdK4G8vAv/SdO/Kdn/yDi56vVXo7tao2IFp0qt/VdWWhDR161L1qN4BbUytjNJaF6kOtVfsazc5M86M3z7B69vy+tW8Dw5zaaEBLgzOWgd+PHn6R/ep16vGutafdbopmrc1Fwq/dVMf6YzV+wzFcpcm3lsAvNGgbETcB/wGYAv44Mw80bL8U+ArwPuCHwCcy8/vtjvmOt1/GdMMgXv0AYbue+/RU8KMfn2k52NdKbeZLbTpfs6Ccf9c/6FgiunTjhqbvXTvOWssn7T40asernSeXF5BUVMfAj4gp4AHg54FF4KmIOJKZL9Ttdhfwama+OyJuB34f+ES7485ePs3vVmverXqnzWbp1EK427Cvqc18aRWUjc8360FD828I9e1fS6+72YdGq1KNJBXVsaQTET8LfC4z91Qf3wuQmffX7XO0us+3ImIj8H+Azdnm4L1eeNXpQp9OU/36dcHOoEsplmokNTPoks4c8HLd40XgZ1rtk5lnIuI14CeAv2to6N3A3QBbt27tpb1tL7iZKzDVr1836Rh0KcVSjaR+KxL4zS6RbOy5F9mHzDwEHIJKD7/Ae1+k6Lo1rpcuSRcqEviLwLV1j7cAp1rss1gt6VwF/N++tLBBN3c6spcsSecVWUvnKWBHRGyPiEuA24EjDfscAT5Z/fnjwLF29fu12Ltrjvtvu4G52RmCSs/eqy4lqbOOPfxqTf4e4CiVaZkPZubzEXEfsJCZR4A/Af4sIk5S6dnfPshG23OXpO4VmoefmY8BjzU895m6n98A/nl/myZJ6qdSLI8sSTLwJak0DHxJKgkDX5JKwsCXpJIw8CWpJAx8SSqJod0AJSL+H3BiKG8+ejbRsNBciXkuzvNcnOe5OG9nZl7ZywsLXXg1ICd6XeJz0kTEgueiwnNxnufiPM/FeRHR/bryVZZ0JKkkDHxJKolhBv6hIb73qPFcnOe5OM9zcZ7n4ryez8XQBm0lSevLko4klcTAAz8iboqIExFxMiL2N9l+aUQ8XN3+7YjYNug2DUuBc/HbEfFCRDwbEX8dEe8aRjvXQ6dzUbffxyMiI2JiZ2gUORcR8YvVfxvPR8Sfr3cb10uBv5GtEfF4RByv/p3cPIx2DlpEPBgRr0TEd1tsj4j4w+p5ejYi3lvowJk5sP+o3DDlfwH/ELgE+A5wXcM+vwH8UfXn24GHB9mmYf1X8Fx8ELi8+vOvl/lcVPe7Evgb4ElgftjtHuK/ix3AceDq6uOfHHa7h3guDgG/Xv35OuD7w273gM7FPwXeC3y3xfabgb+icj/x9wPfLnLcQffwbwROZuaLmfkm8BBwa8M+twJ/Wv35a8CHI6LZTdHHXcdzkZmPZ+br1YdPUrl/8CQq8u8C4PeAzwNvrGfj1lmRc/GrwAOZ+SpAZr6yzm1cL0XORQJvr/58FRffX3siZObf0P6+4LcCX8mKJ4HZiHhnp+MOOvDngJfrHi9Wn2u6T2aeAV4DfmLA7RqGIuei3l1UPsEnUcdzERG7gGsz8+vr2bAhKPLv4j3AeyLiiYh4MiJuWrfWra8i5+JzwJ0RsUjlLny/uT5NGznd5gkw+Cttm/XUG6cFFdlnEhT+PSPiTmAe+LmBtmh42p6LiNgAfAH4lfVq0BAV+XexkUpZ5wNUvvX994i4PjOXB9y29VbkXNwBfDkz/11E/CyVe2lfn5lvDb55I6Wn3Bx0D38RuLbu8RYu/gp2bp+I2Ejla1q7rzLjqsi5ICI+AvwOcEtm/nid2rbeOp2LK4HrgW9GxPep1CiPTOjAbdG/kb/MzNXM/B6VNah2rFP71lORc3EX8AhAZn4LuIzKOjtlUyhPGg068J8CdkTE9oi4hMqg7JGGfY4An6z+/HHgWFZHJSZMx3NRLWN8kUrYT2qdFjqci8x8LTM3Zea2zNxGZTzjlszseQ2REVbkb+QwlQF9ImITlRLPi+vayvVR5Fy8BHwYICJ+mkrgn17XVo6GI8AvV2frvB94LTN/0OlFAy3pZOaZiLgHOEplBP7BzHw+Iu4DFjLzCPAnVL6WnaTSs799kG0aloLn4iBwBfAX1XHrlzLzlqE1ekAKnotSKHgujgIfjYgXgLPAvsz84fBaPRgFz8WngS9FxKeolDB+ZRI7iBHxVSolvE3V8YrPAtMAmflHVMYvbgZOAq8D/6LQcSfwXEmSmvBKW0kqCQNfkkrCwJekkjDwJakkDHxJKgkDX5JKwsCXpJIw8CWpJP4/pWMYa78tFSsAAAAASUVORK5CYII=\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", - " \n", - " \n", - " \n", - "
**Cooperate****Defect**
**Cooperate**1, 10, *D*
**Defect***D*, 00, 0
\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": "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W878SjcAK9dtavJIJElSO/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, \"Sequential\", seed=seed)\n", - "run_model(m)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Random Activation" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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/oQQJsUYy9PZpuhnpC9/eBrT56/M6j5H7dSdS48fvc3rp06dyr777vuZ5ffffz+TJ0/m7bff5pNPPmG//fZjzJgxvPzyyw0uB3j99deZPn06u+yyC+PHj+f+++9n7Nix/OIXv+Dpp5+mS5cuXH311VxzzTVccsklQF2v5AkTJgBQWVnJt771LQAuvvhibrrpJs477zxOOOGEzYVzQ3r27Mmbb77J9ddfz29+8xv+8pe/cPnll3P44Ydz0UUX8fjjj3PjjTc2+jg9+OCDjB8/nmHDhrHjjjvy5ptvss8++3D//fczd+5cpkyZwuLFixk5ciRnn302Gzdu5LzzzuMf//gHvXr14q677uJnP/sZN998MwDV1dW8/vrrPProo1x++eU8/fTTXHHFFUycOJE//vGPABx//PFcd911HHLIIaxevZpOnTo1OkZJUm7U1kYq12yoK47rFcVb/7xy/Wdnkbt2bEef7h3p26MTB+1WtrlQ3lQ09+vRibKuHSktsThWYSv6QrqQTJgwgTPOOIPS0lL69OnDF77wBd54441tLu/evTv7778/u+66KwBnnHEGEyZMoFOnTkyfPp1DDjkEgA0bNnDQQQdtvp3TTjtt889Tp07l4osvZvny5axevZqjjz66WWM9+eSTAdh33325//77N4//gQceAGD8+PHssMMOje7jjjvu4IILLgDg9NNP54477mCfffZhwoQJfPnLX6akpIS+ffty2GGHAfDuu+8ydepUxo0bB9RFU/r169fgmObOndvgbR5yyCH84Ac/4Gtf+xonn3wyAwYMaNb9lSQls2LtRmYuXMm7i1YxY8EqZi9exfzl61m8aj0ba7acRS4J0LNrXYE8qKwLB+5atsWMcp/U964dLT/UOhT9K7mxmeNcGT16NPfee+9nlm8rJtNYfGbrt6JCCMQYGTduHHfccUeD23Tp0mXzz9/4xjd48MEH2XPPPbnlllt4/vnnm3EPoGPHjgCUlpZSXV3d5Di3VllZybPPPsvUqVMJIVBTU0MIgf/+7/9u9HEYPXo0r7zySrPHtLULL7yQY489lkcffZQDDzyQp59+OicfcpSktmZDdS3vf7KamQtWMXPhqrrieeEqFqz4tDd/j+3aM7xPN/YfvGOqQO64RYHcq2tH2pU26xQVUqvgqz2Bww8/nKqqKv785z9vXvbGG2+www47cNdd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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "m = PdGrid(50, 50, \"Random\", seed=seed)\n", - "run_model(m)" - ] - }, - { - "cell_type": "markdown", - "metadata": { - "collapsed": true - }, - "source": [ - "## Simultaneous Activation" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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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": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
happy
00
173
267
372
472
\n", - "
" - ], - "text/plain": [ - " happy\n", - "0 0\n", - "1 73\n", - "2 72\n", - "3 73\n", - "4 72" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "model_out.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, we can plot the 'happy' series:" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "model_out.happy.plot()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "For testing purposes, here is a table giving each agent's x and y values at each step." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [], - "source": [ - "x_positions = model.datacollector.get_agent_vars_dataframe()" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
xy
StepAgentID
0(0, 0)01
(0, 1)89
(0, 2)52
(0, 3)00
(0, 4)17
\n", - "
" - ], - "text/plain": [ - " x y\n", - "Step AgentID \n", - "0 (0, 0) 0 1\n", - " (0, 1) 8 9\n", - " (0, 2) 5 2\n", - " (0, 3) 0 0\n", - " (0, 4) 1 7" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "x_positions.head()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Effect of Homophily on segregation\n", - "\n", - "Now, we can do a parameter sweep to see how segregation changes with homophily.\n", - "\n", - "First, we create a function which takes a model instance and returns what fraction of agents are segregated -- that is, have no neighbors of the opposite type." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [], - "source": [ - "from mesa.batchrunner import BatchRunner" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "def get_segregation(model):\n", - " \"\"\"\n", - " Find the % of agents that only have neighbors of their same type.\n", - " \"\"\"\n", - " segregated_agents = 0\n", - " for agent in model.schedule.agents:\n", - " segregated = True\n", - " for neighbor in model.grid.iter_neighbors(agent.pos, True):\n", - " if neighbor.type != agent.type:\n", - " segregated = False\n", - " break\n", - " if segregated:\n", - " segregated_agents += 1\n", - " return segregated_agents / model.schedule.get_agent_count()" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Now, we set up the batch run, with a dictionary of fixed and changing parameters. Let's hold everything fixed except for Homophily." - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "metadata": {}, - "outputs": [], - "source": [ - "fixed_params = {\"height\": 10, \"width\": 10, \"density\": 0.8, \"minority_pc\": 0.2}\n", - "variable_parms = {\"homophily\": range(1, 9)}" - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "metadata": {}, - "outputs": [], - "source": [ - "model_reporters = {\"Segregated_Agents\": get_segregation}" - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [], - "source": [ - "param_sweep = BatchRunner(\n", - " Schelling,\n", - " variable_parameters=variable_parms,\n", - " fixed_parameters=fixed_params,\n", - " iterations=10,\n", - " max_steps=200,\n", - " model_reporters=model_reporters,\n", - ")" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "80it [00:15, 3.13it/s]\n" - ] - } - ], - "source": [ - "param_sweep.run_all()" - ] - }, - { - "cell_type": "code", - "execution_count": 15, - "metadata": {}, - "outputs": [], - "source": [ - "df = param_sweep.get_model_vars_dataframe()" - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "metadata": {}, - "outputs": [ - { - "data": { - "image/png": "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\n", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "plt.scatter(df.homophily, df.Segregated_Agents)\n", - "plt.grid(True)" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.9.9" - }, - "widgets": { - "state": {}, - "version": "1.1.2" - } - }, - "nbformat": 4, - "nbformat_minor": 1 -} diff --git a/examples/schelling/model.py b/examples/schelling/model.py deleted file mode 100644 index 821b68af951..00000000000 --- a/examples/schelling/model.py +++ /dev/null @@ -1,89 +0,0 @@ -import mesa - - -class SchellingAgent(mesa.Agent): - """ - Schelling segregation agent - """ - - def __init__(self, pos, model, agent_type): - """ - Create a new Schelling agent. - - Args: - unique_id: Unique identifier for the agent. - x, y: Agent initial location. - agent_type: Indicator for the agent's type (minority=1, majority=0) - """ - super().__init__(pos, model) - self.pos = pos - self.type = agent_type - - def step(self): - similar = 0 - for neighbor in self.model.grid.iter_neighbors(self.pos, True): - if neighbor.type == self.type: - similar += 1 - - # If unhappy, move: - if similar < self.model.homophily: - self.model.grid.move_to_empty(self) - else: - self.model.happy += 1 - - -class Schelling(mesa.Model): - """ - Model class for the Schelling segregation model. - """ - - def __init__(self, width=20, height=20, density=0.8, minority_pc=0.2, homophily=3): - """ """ - - self.width = width - self.height = height - self.density = density - self.minority_pc = minority_pc - self.homophily = homophily - - self.schedule = mesa.time.RandomActivation(self) - self.grid = mesa.space.SingleGrid(width, height, torus=True) - - self.happy = 0 - self.datacollector = mesa.DataCollector( - {"happy": "happy"}, # Model-level count of happy agents - # For testing purposes, agent's individual x and y - {"x": lambda a: a.pos[0], "y": lambda a: a.pos[1]}, - ) - - # Set up agents - # We use a grid iterator that returns - # the coordinates of a cell as well as - # its contents. (coord_iter) - for cell in self.grid.coord_iter(): - x = cell[1] - y = cell[2] - if self.random.random() < self.density: - if self.random.random() < self.minority_pc: - agent_type = 1 - else: - agent_type = 0 - - agent = SchellingAgent((x, y), self, agent_type) - self.grid.place_agent(agent, (x, y)) - self.schedule.add(agent) - - self.running = True - self.datacollector.collect(self) - - def step(self): - """ - Run one step of the model. If All agents are happy, halt the model. - """ - self.happy = 0 # Reset counter of happy agents - self.schedule.step() - # collect data - self.datacollector.collect(self) - - if self.happy == self.schedule.get_agent_count(): - self.running = False diff --git a/examples/schelling/requirements.txt b/examples/schelling/requirements.txt deleted file mode 100644 index bcbfbbe220b..00000000000 --- a/examples/schelling/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -jupyter -matplotlib -mesa diff --git a/examples/schelling/run.py b/examples/schelling/run.py deleted file mode 100644 index a25f3b1294e..00000000000 --- a/examples/schelling/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from server import server - -server.launch() diff --git a/examples/schelling/run_ascii.py b/examples/schelling/run_ascii.py deleted file mode 100644 index 8d70c39756e..00000000000 --- a/examples/schelling/run_ascii.py +++ /dev/null @@ -1,49 +0,0 @@ -import mesa - -from model import Schelling - - -class SchellingTextVisualization(mesa.visualization.TextVisualization): - """ - ASCII visualization for schelling model - """ - - def __init__(self, model): - """ - Create new Schelling ASCII visualization. - """ - self.model = model - - grid_viz = mesa.visualization.TextGrid(self.model.grid, self.print_ascii_agent) - happy_viz = mesa.visualization.TextData(self.model, "happy") - self.elements = [grid_viz, happy_viz] - - @staticmethod - def print_ascii_agent(a): - """ - Minority agents are X, Majority are O. - """ - if a.type == 0: - return "O" - if a.type == 1: - return "X" - - -if __name__ == "__main__": - model_params = { - "height": 20, - "width": 20, - # Agent density, from 0.8 to 1.0 - "density": 0.8, - # Fraction minority, from 0.2 to 1.0 - "minority_pc": 0.2, - # Homophily, from 3 to 8 - "homophily": 3, - } - - model = Schelling(**model_params) - viz = SchellingTextVisualization(model) - for i in range(10): - print("Step:", i) - viz.step() - print("---") diff --git a/examples/schelling/server.py b/examples/schelling/server.py deleted file mode 100644 index fd643096db3..00000000000 --- a/examples/schelling/server.py +++ /dev/null @@ -1,46 +0,0 @@ -import mesa - -from model import Schelling - - -def get_happy_agents(model): - """ - Display a text count of how many happy agents there are. - """ - return f"Happy agents: {model.happy}" - - -def schelling_draw(agent): - """ - Portrayal Method for canvas - """ - if agent is None: - return - portrayal = {"Shape": "circle", "r": 0.5, "Filled": "true", "Layer": 0} - - if agent.type == 0: - portrayal["Color"] = ["#FF0000", "#FF9999"] - portrayal["stroke_color"] = "#00FF00" - else: - portrayal["Color"] = ["#0000FF", "#9999FF"] - portrayal["stroke_color"] = "#000000" - return portrayal - - -canvas_element = mesa.visualization.CanvasGrid(schelling_draw, 20, 20, 500, 500) -happy_chart = mesa.visualization.ChartModule([{"Label": "happy", "Color": "Black"}]) - -model_params = { - "height": 20, - "width": 20, - "density": mesa.visualization.Slider("Agent density", 0.8, 0.1, 1.0, 0.1), - "minority_pc": mesa.visualization.Slider("Fraction minority", 0.2, 0.00, 1.0, 0.05), - "homophily": mesa.visualization.Slider("Homophily", 3, 0, 8, 1), -} - -server = mesa.visualization.ModularServer( - Schelling, - [canvas_element, get_happy_agents, happy_chart], - "Schelling", - model_params, -) diff --git a/examples/shape_example/Readme.md b/examples/shape_example/Readme.md deleted file mode 100644 index fb0bae3f3f1..00000000000 --- a/examples/shape_example/Readme.md +++ /dev/null @@ -1,41 +0,0 @@ -# Shape Model -- Basic Grid with two agents - -## Summary - -A very basic example model to showcase the visualization on web browser. - -A simple grid is displayed on browser with two agents. The example does not -have any agent motion involved. This example does not have any movement of -agents so as to keep it to the simplest of level possible. - -This model showcases following features: - -* A rectangular grid -* Text Overlay on the agent's shape on CanvasGrid -* ArrowHead shaped agent for displaying heading of the agent on CanvasGrid - -## 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 - -* ``shape_model/model.py``: Defines the basic shape model and agents. -* ``shape_model/server.py``: Sets up the interactive visualization server. -* ``run.py``: Launches a model visualization server. diff --git a/examples/shape_example/requirements.txt b/examples/shape_example/requirements.txt deleted file mode 100644 index da0b5b956fd..00000000000 --- a/examples/shape_example/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -mesa diff --git a/examples/shape_example/run.py b/examples/shape_example/run.py deleted file mode 100644 index c9f32698864..00000000000 --- a/examples/shape_example/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from shape_example.server import server - -server.launch() diff --git a/examples/shape_example/shape_example/model.py b/examples/shape_example/shape_example/model.py deleted file mode 100644 index 5ffe114af68..00000000000 --- a/examples/shape_example/shape_example/model.py +++ /dev/null @@ -1,39 +0,0 @@ -import mesa - - -class Walker(mesa.Agent): - def __init__(self, unique_id, model, pos, heading=(1, 0)): - super().__init__(unique_id, model) - self.pos = pos - self.heading = heading - self.headings = {(1, 0), (0, 1), (-1, 0), (0, -1)} - - -class ShapeExample(mesa.Model): - def __init__(self, N=2, width=20, height=10): - self.N = N # num of agents - self.headings = ((1, 0), (0, 1), (-1, 0), (0, -1)) # tuples are fast - self.grid = mesa.space.SingleGrid(width, height, torus=False) - self.schedule = mesa.time.RandomActivation(self) - self.make_walker_agents() - self.running = True - - def make_walker_agents(self): - unique_id = 0 - while True: - if unique_id == self.N: - break - x = self.random.randrange(self.grid.width) - y = self.random.randrange(self.grid.height) - pos = (x, y) - heading = self.random.choice(self.headings) - # heading = (1, 0) - if self.grid.is_cell_empty(pos): - print(f"Creating agent {unique_id} at ({x}, {y})") - a = Walker(unique_id, self, pos, heading) - self.schedule.add(a) - self.grid.place_agent(a, pos) - unique_id += 1 - - def step(self): - self.schedule.step() diff --git a/examples/shape_example/shape_example/server.py b/examples/shape_example/shape_example/server.py deleted file mode 100644 index bec3a68d5f1..00000000000 --- a/examples/shape_example/shape_example/server.py +++ /dev/null @@ -1,44 +0,0 @@ -import mesa - -from .model import Walker, ShapeExample - - -def agent_draw(agent): - portrayal = None - if agent is None: - # Actually this if part is unnecessary, but still keeping it for - # aesthetics - pass - elif isinstance(agent, Walker): - print(f"Uid: {agent.unique_id}, Heading: {agent.heading}") - portrayal = { - "Shape": "arrowHead", - "Filled": "true", - "Layer": 2, - "Color": ["#00FF00", "#99FF99"], - "stroke_color": "#666666", - "Filled": "true", - "heading_x": agent.heading[0], - "heading_y": agent.heading[1], - "text": agent.unique_id, - "text_color": "white", - "scale": 0.8, - } - return portrayal - - -width = 15 -height = 10 -num_agents = 2 -pixel_ratio = 50 -grid = mesa.visualization.CanvasGrid( - agent_draw, width, height, width * pixel_ratio, height * pixel_ratio -) -server = mesa.visualization.ModularServer( - ShapeExample, - [grid], - "Shape Model Example", - {"N": num_agents, "width": width, "height": height}, -) -server.max_steps = 0 -server.port = 8521 diff --git a/examples/sugarscape_cg/Readme.md b/examples/sugarscape_cg/Readme.md deleted file mode 100644 index 948272b68a8..00000000000 --- a/examples/sugarscape_cg/Readme.md +++ /dev/null @@ -1,61 +0,0 @@ -# Sugarscape Constant Growback model - -## Summary - -This is Epstein & Axtell's Sugarscape Constant Growback model, with a detailed -description in the chapter 2 of Growing Artificial Societies: Social Science from the Bottom Up - -A simple ecological model, consisting of two agent types: ants, and sugar -patches. - -The ants wander around according to Epstein's rule M: -- Look out as far as vision pennies in the four principal lattice directions and identify the unoccupied site(s) having the most sugar. The order in which each agent search es the four directions is random. -- If the greatest sugar value appears on multiple sites then select the nearest one. That is, if the largest sugar within an agent s vision is four, but the value occurs twice, once at a lattice position two units away and again at a site three units away, the former is chosen. If it appears at multiple sites the same distance away, the first site encountered is selected (the site search order being random). -- Move to this site. Notice that there is no distinction between how far an agent can move and how far it can see. So, if vision equals 5, the agent can move up to 5 lattice positions north , south, east, or west. -- Collect all the sugar at this new position. - -The sugar patches grow at a constant rate of 1 until it reaches maximum capacity. If ant metabolizes to the point it has zero or negative sugar, it dies. - - -The model is tests and demonstrates several Mesa concepts and features: - - MultiGrid - - Multiple agent types (ants, sugar patches) - - Overlay arbitrary text (wolf's energy) on agent's shapes while drawing on CanvasGrid - - Dynamically removing agents from the grid and schedule when they die - -## 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 - -* ``sugarscape/agents.py``: Defines the SsAgent, and Sugar agent classes. -* ``sugarscape/schedule.py``: This is exactly based on wolf_sheep/schedule.py. -* ``sugarscape/model.py``: Defines the Sugarscape Constant Growback model itself -* ``sugarscape/server.py``: Sets up the interactive visualization server -* ``run.py``: Launches a model visualization server. - -## Further Reading - -This model is based on the Netlogo Sugarscape 2 Constant Growback: - -Li, J. and Wilensky, U. (2009). NetLogo Sugarscape 2 Constant Growback model. -http://ccl.northwestern.edu/netlogo/models/Sugarscape2ConstantGrowback. -Center for Connected Learning and Computer-Based Modeling, -Northwestern University, Evanston, IL. - -The ant sprite is taken from https://openclipart.org/detail/229519/ant-silhouette, with CC0 1.0 license. diff --git a/examples/sugarscape_cg/requirements.txt b/examples/sugarscape_cg/requirements.txt deleted file mode 100644 index 4baebfe444d..00000000000 --- a/examples/sugarscape_cg/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -jupyter -mesa diff --git a/examples/sugarscape_cg/run.py b/examples/sugarscape_cg/run.py deleted file mode 100644 index 6098cec7be4..00000000000 --- a/examples/sugarscape_cg/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from sugarscape_cg.server import server - -server.launch() diff --git a/examples/sugarscape_cg/sugarscape_cg/__init__.py b/examples/sugarscape_cg/sugarscape_cg/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/examples/sugarscape_cg/sugarscape_cg/agents.py b/examples/sugarscape_cg/sugarscape_cg/agents.py deleted file mode 100644 index 6e245eaa19d..00000000000 --- a/examples/sugarscape_cg/sugarscape_cg/agents.py +++ /dev/null @@ -1,83 +0,0 @@ -import math - -import mesa - - -def get_distance(pos_1, pos_2): - """Get the distance between two point - - Args: - pos_1, pos_2: Coordinate tuples for both points. - """ - x1, y1 = pos_1 - x2, y2 = pos_2 - dx = x1 - x2 - dy = y1 - y2 - return math.sqrt(dx**2 + dy**2) - - -class SsAgent(mesa.Agent): - def __init__( - self, unique_id, pos, model, moore=False, sugar=0, metabolism=0, vision=0 - ): - super().__init__(unique_id, model) - self.pos = pos - self.moore = moore - self.sugar = sugar - self.metabolism = metabolism - self.vision = vision - - def get_sugar(self, pos): - this_cell = self.model.grid.get_cell_list_contents([pos]) - for agent in this_cell: - if type(agent) is Sugar: - return agent - - def is_occupied(self, pos): - this_cell = self.model.grid.get_cell_list_contents([pos]) - return any(isinstance(agent, SsAgent) for agent in this_cell) - - def move(self): - # Get neighborhood within vision - neighbors = [ - i - for i in self.model.grid.get_neighborhood( - self.pos, self.moore, False, radius=self.vision - ) - if not self.is_occupied(i) - ] - neighbors.append(self.pos) - # Look for location with the most sugar - max_sugar = max(self.get_sugar(pos).amount for pos in neighbors) - candidates = [ - pos for pos in neighbors if self.get_sugar(pos).amount == max_sugar - ] - # Narrow down to the nearest ones - min_dist = min(get_distance(self.pos, pos) for pos in candidates) - final_candidates = [ - pos for pos in candidates if get_distance(self.pos, pos) == min_dist - ] - self.random.shuffle(final_candidates) - self.model.grid.move_agent(self, final_candidates[0]) - - def eat(self): - sugar_patch = self.get_sugar(self.pos) - self.sugar = self.sugar - self.metabolism + sugar_patch.amount - sugar_patch.amount = 0 - - def step(self): - self.move() - self.eat() - if self.sugar <= 0: - self.model.grid.remove_agent(self) - self.model.schedule.remove(self) - - -class Sugar(mesa.Agent): - def __init__(self, unique_id, pos, model, max_sugar): - super().__init__(unique_id, model) - self.amount = max_sugar - self.max_sugar = max_sugar - - def step(self): - self.amount = min([self.max_sugar, self.amount + 1]) diff --git a/examples/sugarscape_cg/sugarscape_cg/model.py b/examples/sugarscape_cg/sugarscape_cg/model.py deleted file mode 100644 index b27566e96f2..00000000000 --- a/examples/sugarscape_cg/sugarscape_cg/model.py +++ /dev/null @@ -1,93 +0,0 @@ -""" -Sugarscape Constant Growback Model -================================ - -Replication of the model found in Netlogo: -Li, J. and Wilensky, U. (2009). NetLogo Sugarscape 2 Constant Growback model. -http://ccl.northwestern.edu/netlogo/models/Sugarscape2ConstantGrowback. -Center for Connected Learning and Computer-Based Modeling, -Northwestern University, Evanston, IL. -""" - -import mesa - -from .agents import SsAgent, Sugar - - -class SugarscapeCg(mesa.Model): - """ - Sugarscape 2 Constant Growback - """ - - verbose = True # Print-monitoring - - def __init__(self, width=50, height=50, initial_population=100): - """ - Create a new Constant Growback model with the given parameters. - - Args: - initial_population: Number of population to start with - """ - - # Set parameters - self.width = width - self.height = height - self.initial_population = initial_population - - self.schedule = mesa.time.RandomActivationByType(self) - self.grid = mesa.space.MultiGrid(self.width, self.height, torus=False) - self.datacollector = mesa.DataCollector( - {"SsAgent": lambda m: m.schedule.get_type_count(SsAgent)} - ) - - # Create sugar - import numpy as np - - sugar_distribution = np.genfromtxt("sugarscape_cg/sugar-map.txt") - agent_id = 0 - for _, x, y in self.grid.coord_iter(): - max_sugar = sugar_distribution[x, y] - sugar = Sugar(agent_id, (x, y), self, max_sugar) - agent_id += 1 - self.grid.place_agent(sugar, (x, y)) - self.schedule.add(sugar) - - # Create agent: - for i in range(self.initial_population): - x = self.random.randrange(self.width) - y = self.random.randrange(self.height) - sugar = self.random.randrange(6, 25) - metabolism = self.random.randrange(2, 4) - vision = self.random.randrange(1, 6) - ssa = SsAgent(agent_id, (x, y), self, False, sugar, metabolism, vision) - agent_id += 1 - self.grid.place_agent(ssa, (x, y)) - self.schedule.add(ssa) - - self.running = True - self.datacollector.collect(self) - - def step(self): - self.schedule.step() - # collect data - self.datacollector.collect(self) - if self.verbose: - print([self.schedule.time, self.schedule.get_type_count(SsAgent)]) - - def run_model(self, step_count=200): - - if self.verbose: - print( - "Initial number Sugarscape Agent: ", - self.schedule.get_type_count(SsAgent), - ) - - for i in range(step_count): - self.step() - - if self.verbose: - print("") - print( - "Final number Sugarscape Agent: ", - self.schedule.get_type_count(SsAgent), - ) diff --git a/examples/sugarscape_cg/sugarscape_cg/resources/ant.png b/examples/sugarscape_cg/sugarscape_cg/resources/ant.png deleted file mode 100644 index f2c858251f80bb92125295dd227b8b3a576251ce..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 66107 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 $o3t>fogF201yCQ?IMz00 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 diff --git a/examples/sugarscape_cg/sugarscape_cg/server.py b/examples/sugarscape_cg/sugarscape_cg/server.py deleted file mode 100644 index 54a3e857750..00000000000 --- a/examples/sugarscape_cg/sugarscape_cg/server.py +++ /dev/null @@ -1,41 +0,0 @@ -import mesa - -from .agents import SsAgent, Sugar -from .model import SugarscapeCg - -color_dic = {4: "#005C00", 3: "#008300", 2: "#00AA00", 1: "#00F800"} - - -def SsAgent_portrayal(agent): - if agent is None: - return - - if type(agent) is SsAgent: - return {"Shape": "sugarscape_cg/resources/ant.png", "scale": 0.9, "Layer": 1} - - elif type(agent) is Sugar: - if agent.amount != 0: - color = color_dic[agent.amount] - else: - color = "#D6F5D6" - return { - "Color": color, - "Shape": "rect", - "Filled": "true", - "Layer": 0, - "w": 1, - "h": 1, - } - - return {} - - -canvas_element = mesa.visualization.CanvasGrid(SsAgent_portrayal, 50, 50, 500, 500) -chart_element = mesa.visualization.ChartModule( - [{"Label": "SsAgent", "Color": "#AA0000"}] -) - -server = mesa.visualization.ModularServer( - SugarscapeCg, [canvas_element, chart_element], "Sugarscape 2 Constant Growback" -) -# server.launch() diff --git a/examples/sugarscape_cg/sugarscape_cg/sugar-map.txt b/examples/sugarscape_cg/sugarscape_cg/sugar-map.txt deleted file mode 100644 index 1357a6676b4..00000000000 --- a/examples/sugarscape_cg/sugarscape_cg/sugar-map.txt +++ /dev/null @@ -1,50 +0,0 @@ -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 2 2 2 2 2 2 2 2 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 -0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 -0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 -0 0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 -0 0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 -0 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 -0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 -0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 -0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 -1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 -1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 -1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 -1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 -1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 -1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 -1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 -1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 -1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 -2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 -2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 -2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 -2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0 -2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 -2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 -2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 -2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 -2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 -2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 -2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 -2 2 2 2 2 3 3 3 3 3 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 -2 2 2 2 2 3 3 3 3 3 3 4 4 4 4 4 4 4 4 3 3 3 3 3 3 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 -2 2 2 2 2 2 3 3 3 3 3 3 3 4 4 4 4 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 3 3 3 3 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 -1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/examples/virus_on_network/README.md b/examples/virus_on_network/README.md deleted file mode 100644 index b9fd1e94ecb..00000000000 --- a/examples/virus_on_network/README.md +++ /dev/null @@ -1,46 +0,0 @@ -# Virus on a Network - -## Summary - -This model is based on the NetLogo model "Virus on Network". - -For more information about this model, read the NetLogo's web page: http://ccl.northwestern.edu/netlogo/models/VirusonaNetwork. - -JavaScript library used in this example to render the network: [d3.js](https://d3js.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 - - -[Stonedahl, F. and Wilensky, U. (2008). NetLogo Virus on a Network model](http://ccl.northwestern.edu/netlogo/models/VirusonaNetwork). -Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. - - -[Wilensky, U. (1999). NetLogo](http://ccl.northwestern.edu/netlogo/) -Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. diff --git a/examples/virus_on_network/requirements.txt b/examples/virus_on_network/requirements.txt deleted file mode 100644 index f8a0e4475ca..00000000000 --- a/examples/virus_on_network/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -networkx>=2.0 diff --git a/examples/virus_on_network/run.py b/examples/virus_on_network/run.py deleted file mode 100644 index 9f1ef7292ae..00000000000 --- a/examples/virus_on_network/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from virus_on_network.server import server - -server.launch() diff --git a/examples/virus_on_network/virus_on_network/__init__.py b/examples/virus_on_network/virus_on_network/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/examples/virus_on_network/virus_on_network/model.py b/examples/virus_on_network/virus_on_network/model.py deleted file mode 100644 index 7d1e68f7f15..00000000000 --- a/examples/virus_on_network/virus_on_network/model.py +++ /dev/null @@ -1,160 +0,0 @@ -import math -from enum import Enum -import networkx as nx - -import mesa - - -class State(Enum): - SUSCEPTIBLE = 0 - INFECTED = 1 - RESISTANT = 2 - - -def number_state(model, state): - return sum(1 for a in model.grid.get_all_cell_contents() if a.state is state) - - -def number_infected(model): - return number_state(model, State.INFECTED) - - -def number_susceptible(model): - return number_state(model, State.SUSCEPTIBLE) - - -def number_resistant(model): - return number_state(model, State.RESISTANT) - - -class VirusOnNetwork(mesa.Model): - """A virus model with some number of agents""" - - def __init__( - self, - num_nodes=10, - avg_node_degree=3, - initial_outbreak_size=1, - virus_spread_chance=0.4, - virus_check_frequency=0.4, - recovery_chance=0.3, - gain_resistance_chance=0.5, - ): - - self.num_nodes = num_nodes - prob = avg_node_degree / self.num_nodes - self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=prob) - self.grid = mesa.space.NetworkGrid(self.G) - self.schedule = mesa.time.RandomActivation(self) - self.initial_outbreak_size = ( - initial_outbreak_size if initial_outbreak_size <= num_nodes else num_nodes - ) - self.virus_spread_chance = virus_spread_chance - self.virus_check_frequency = virus_check_frequency - self.recovery_chance = recovery_chance - self.gain_resistance_chance = gain_resistance_chance - - self.datacollector = mesa.DataCollector( - { - "Infected": number_infected, - "Susceptible": number_susceptible, - "Resistant": number_resistant, - } - ) - - # Create agents - for i, node in enumerate(self.G.nodes()): - a = VirusAgent( - i, - self, - State.SUSCEPTIBLE, - self.virus_spread_chance, - self.virus_check_frequency, - self.recovery_chance, - self.gain_resistance_chance, - ) - self.schedule.add(a) - # Add the agent to the node - self.grid.place_agent(a, node) - - # Infect some nodes - infected_nodes = self.random.sample(list(self.G), self.initial_outbreak_size) - for a in self.grid.get_cell_list_contents(infected_nodes): - a.state = State.INFECTED - - self.running = True - self.datacollector.collect(self) - - def resistant_susceptible_ratio(self): - try: - return number_state(self, State.RESISTANT) / number_state( - self, State.SUSCEPTIBLE - ) - except ZeroDivisionError: - return math.inf - - 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 VirusAgent(mesa.Agent): - def __init__( - self, - unique_id, - model, - initial_state, - virus_spread_chance, - virus_check_frequency, - recovery_chance, - gain_resistance_chance, - ): - super().__init__(unique_id, model) - - self.state = initial_state - - self.virus_spread_chance = virus_spread_chance - self.virus_check_frequency = virus_check_frequency - self.recovery_chance = recovery_chance - self.gain_resistance_chance = gain_resistance_chance - - def try_to_infect_neighbors(self): - neighbors_nodes = self.model.grid.get_neighbors(self.pos, include_center=False) - susceptible_neighbors = [ - agent - for agent in self.model.grid.get_cell_list_contents(neighbors_nodes) - if agent.state is State.SUSCEPTIBLE - ] - for a in susceptible_neighbors: - if self.random.random() < self.virus_spread_chance: - a.state = State.INFECTED - - def try_gain_resistance(self): - if self.random.random() < self.gain_resistance_chance: - self.state = State.RESISTANT - - def try_remove_infection(self): - # Try to remove - if self.random.random() < self.recovery_chance: - # Success - self.state = State.SUSCEPTIBLE - self.try_gain_resistance() - else: - # Failed - self.state = State.INFECTED - - def try_check_situation(self): - if self.random.random() < self.virus_check_frequency: - # Checking... - if self.state is State.INFECTED: - self.try_remove_infection() - - def step(self): - if self.state is State.INFECTED: - self.try_to_infect_neighbors() - self.try_check_situation() diff --git a/examples/virus_on_network/virus_on_network/server.py b/examples/virus_on_network/virus_on_network/server.py deleted file mode 100644 index a8f47c61e6b..00000000000 --- a/examples/virus_on_network/virus_on_network/server.py +++ /dev/null @@ -1,133 +0,0 @@ -import math - -import mesa - -from .model import VirusOnNetwork, State, number_infected - - -def network_portrayal(G): - # The model ensures there is always 1 agent per node - - def node_color(agent): - return {State.INFECTED: "#FF0000", State.SUSCEPTIBLE: "#008000"}.get( - agent.state, "#808080" - ) - - def edge_color(agent1, agent2): - if State.RESISTANT in (agent1.state, agent2.state): - return "#000000" - return "#e8e8e8" - - def edge_width(agent1, agent2): - if State.RESISTANT in (agent1.state, agent2.state): - return 3 - return 2 - - def get_agents(source, target): - return G.nodes[source]["agent"][0], G.nodes[target]["agent"][0] - - portrayal = dict() - portrayal["nodes"] = [ - { - "size": 6, - "color": node_color(agents[0]), - "tooltip": f"id: {agents[0].unique_id}
state: {agents[0].state.name}", - } - for (_, agents) in G.nodes.data("agent") - ] - - portrayal["edges"] = [ - { - "source": source, - "target": target, - "color": edge_color(*get_agents(source, target)), - "width": edge_width(*get_agents(source, target)), - } - for (source, target) in G.edges - ] - - return portrayal - - -network = mesa.visualization.NetworkModule(network_portrayal, 500, 500) -chart = mesa.visualization.ChartModule( - [ - {"Label": "Infected", "Color": "#FF0000"}, - {"Label": "Susceptible", "Color": "#008000"}, - {"Label": "Resistant", "Color": "#808080"}, - ] -) - - -def get_resistant_susceptible_ratio(model): - ratio = model.resistant_susceptible_ratio() - ratio_text = "∞" if ratio is math.inf else f"{ratio:.2f}" - infected_text = str(number_infected(model)) - - return "Resistant/Susceptible Ratio: {}
Infected Remaining: {}".format( - ratio_text, infected_text - ) - - -model_params = { - "num_nodes": mesa.visualization.Slider( - "Number of agents", - 10, - 10, - 100, - 1, - description="Choose how many agents to include in the model", - ), - "avg_node_degree": mesa.visualization.Slider( - "Avg Node Degree", 3, 3, 8, 1, description="Avg Node Degree" - ), - "initial_outbreak_size": mesa.visualization.Slider( - "Initial Outbreak Size", - 1, - 1, - 10, - 1, - description="Initial Outbreak Size", - ), - "virus_spread_chance": mesa.visualization.Slider( - "Virus Spread Chance", - 0.4, - 0.0, - 1.0, - 0.1, - description="Probability that susceptible neighbor will be infected", - ), - "virus_check_frequency": mesa.visualization.Slider( - "Virus Check Frequency", - 0.4, - 0.0, - 1.0, - 0.1, - description="Frequency the nodes check whether they are infected by " "a virus", - ), - "recovery_chance": mesa.visualization.Slider( - "Recovery Chance", - 0.3, - 0.0, - 1.0, - 0.1, - description="Probability that the virus will be removed", - ), - "gain_resistance_chance": mesa.visualization.Slider( - "Gain Resistance Chance", - 0.5, - 0.0, - 1.0, - 0.1, - description="Probability that a recovered agent will become " - "resistant to this virus in the future", - ), -} - -server = mesa.visualization.ModularServer( - VirusOnNetwork, - [network, get_resistant_susceptible_ratio, chart], - "Virus Model", - model_params, -) -server.port = 8521 diff --git a/examples/wolf_sheep/Readme.md b/examples/wolf_sheep/Readme.md deleted file mode 100644 index 30794a6ee67..00000000000 --- a/examples/wolf_sheep/Readme.md +++ /dev/null @@ -1,57 +0,0 @@ -# Wolf-Sheep Predation Model - -## Summary - -A simple ecological model, consisting of three agent types: wolves, sheep, and grass. The wolves and the sheep wander around the grid at random. Wolves and sheep both expend energy moving around, and replenish it by eating. Sheep eat grass, and wolves eat sheep if they end up on the same grid cell. - -If wolves and sheep have enough energy, they reproduce, creating a new wolf or sheep (in this simplified model, only one parent is needed for reproduction). The grass on each cell regrows at a constant rate. If any wolves and sheep run out of energy, they die. - -The model is tests and demonstrates several Mesa concepts and features: - - MultiGrid - - Multiple agent types (wolves, sheep, grass) - - Overlay arbitrary text (wolf's energy) on agent's shapes while drawing on CanvasGrid - - Agents inheriting a behavior (random movement) from an abstract parent - - Writing a model composed of multiple files. - - Dynamically adding and removing agents from the schedule - -## Installation - -To install the dependencies use pip and the requirements.txt in this directory. e.g. - -``` - # First, we clone the Mesa repo - $ git clone https://github.com/projectmesa/mesa.git - $ cd mesa - # Then we cd to the example directory - $ cd examples/wolf_sheep - $ 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 - -* ``wolf_sheep/random_walk.py``: This defines the ``RandomWalker`` agent, which implements the behavior of moving randomly across a grid, one cell at a time. Both the Wolf and Sheep agents will inherit from it. -* ``wolf_sheep/test_random_walk.py``: Defines a simple model and a text-only visualization intended to make sure the RandomWalk class was working as expected. This doesn't actually model anything, but serves as an ad-hoc unit test. To run it, ``cd`` into the ``wolf_sheep`` directory and run ``python test_random_walk.py``. You'll see a series of ASCII grids, one per model step, with each cell showing a count of the number of agents in it. -* ``wolf_sheep/agents.py``: Defines the Wolf, Sheep, and GrassPatch agent classes. -* ``wolf_sheep/scheduler.py``: Defines a custom variant on the RandomActivationByType scheduler, where we can define filters for the `get_type_count` function. -* ``wolf_sheep/model.py``: Defines the Wolf-Sheep Predation model itself -* ``wolf_sheep/server.py``: Sets up the interactive visualization server -* ``run.py``: Launches a model visualization server. - -## Further Reading - -This model is closely based on the NetLogo Wolf-Sheep Predation Model: - -Wilensky, U. (1997). NetLogo Wolf Sheep Predation model. http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation. Center for Connected Learning and Computer-Based Modeling, Northwestern University, Evanston, IL. - -See also the [Lotka–Volterra equations -](https://en.wikipedia.org/wiki/Lotka%E2%80%93Volterra_equations) for an example of a classic differential-equation model with similar dynamics. diff --git a/examples/wolf_sheep/requirements.txt b/examples/wolf_sheep/requirements.txt deleted file mode 100644 index da0b5b956fd..00000000000 --- a/examples/wolf_sheep/requirements.txt +++ /dev/null @@ -1 +0,0 @@ -mesa diff --git a/examples/wolf_sheep/run.py b/examples/wolf_sheep/run.py deleted file mode 100644 index dc5d367e89d..00000000000 --- a/examples/wolf_sheep/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from wolf_sheep.server import server - -server.launch() diff --git a/examples/wolf_sheep/wolf_sheep/__init__.py b/examples/wolf_sheep/wolf_sheep/__init__.py deleted file mode 100644 index e69de29bb2d..00000000000 diff --git a/examples/wolf_sheep/wolf_sheep/agents.py b/examples/wolf_sheep/wolf_sheep/agents.py deleted file mode 100644 index fe62192bf60..00000000000 --- a/examples/wolf_sheep/wolf_sheep/agents.py +++ /dev/null @@ -1,120 +0,0 @@ -import mesa -from wolf_sheep.random_walk import RandomWalker - - -class Sheep(RandomWalker): - """ - A sheep that walks around, reproduces (asexually) and gets eaten. - - The init is the same as the RandomWalker. - """ - - energy = None - - def __init__(self, unique_id, pos, model, moore, energy=None): - super().__init__(unique_id, pos, model, moore=moore) - self.energy = energy - - def step(self): - """ - A model step. Move, then eat grass and reproduce. - """ - self.random_move() - living = True - - if self.model.grass: - # Reduce energy - self.energy -= 1 - - # If there is grass available, eat it - this_cell = self.model.grid.get_cell_list_contents([self.pos]) - grass_patch = [obj for obj in this_cell if isinstance(obj, GrassPatch)][0] - if grass_patch.fully_grown: - self.energy += self.model.sheep_gain_from_food - grass_patch.fully_grown = False - - # Death - if self.energy < 0: - self.model.grid.remove_agent(self) - self.model.schedule.remove(self) - living = False - - if living and self.random.random() < self.model.sheep_reproduce: - # Create a new sheep: - if self.model.grass: - self.energy /= 2 - lamb = Sheep( - self.model.next_id(), self.pos, self.model, self.moore, self.energy - ) - self.model.grid.place_agent(lamb, self.pos) - self.model.schedule.add(lamb) - - -class Wolf(RandomWalker): - """ - A wolf that walks around, reproduces (asexually) and eats sheep. - """ - - energy = None - - def __init__(self, unique_id, pos, model, moore, energy=None): - super().__init__(unique_id, pos, model, moore=moore) - self.energy = energy - - def step(self): - self.random_move() - self.energy -= 1 - - # If there are sheep present, eat one - x, y = self.pos - this_cell = self.model.grid.get_cell_list_contents([self.pos]) - sheep = [obj for obj in this_cell if isinstance(obj, Sheep)] - if len(sheep) > 0: - sheep_to_eat = self.random.choice(sheep) - self.energy += self.model.wolf_gain_from_food - - # Kill the sheep - self.model.grid.remove_agent(sheep_to_eat) - self.model.schedule.remove(sheep_to_eat) - - # Death or reproduction - if self.energy < 0: - self.model.grid.remove_agent(self) - self.model.schedule.remove(self) - else: - if self.random.random() < self.model.wolf_reproduce: - # Create a new wolf cub - self.energy /= 2 - cub = Wolf( - self.model.next_id(), self.pos, self.model, self.moore, self.energy - ) - self.model.grid.place_agent(cub, cub.pos) - self.model.schedule.add(cub) - - -class GrassPatch(mesa.Agent): - """ - A patch of grass that grows at a fixed rate and it is eaten by sheep - """ - - def __init__(self, unique_id, pos, model, fully_grown, countdown): - """ - Creates a new patch of grass - - Args: - grown: (boolean) Whether the patch of grass is fully grown or not - countdown: Time for the patch of grass to be fully grown again - """ - super().__init__(unique_id, model) - self.fully_grown = fully_grown - self.countdown = countdown - self.pos = pos - - def step(self): - if not self.fully_grown: - if self.countdown <= 0: - # Set as fully grown - self.fully_grown = True - self.countdown = self.model.grass_regrowth_time - else: - self.countdown -= 1 diff --git a/examples/wolf_sheep/wolf_sheep/model.py b/examples/wolf_sheep/wolf_sheep/model.py deleted file mode 100644 index 2b8fdbdeed1..00000000000 --- a/examples/wolf_sheep/wolf_sheep/model.py +++ /dev/null @@ -1,166 +0,0 @@ -""" -Wolf-Sheep Predation Model -================================ - -Replication of the model found in NetLogo: - Wilensky, U. (1997). NetLogo Wolf Sheep Predation model. - http://ccl.northwestern.edu/netlogo/models/WolfSheepPredation. - Center for Connected Learning and Computer-Based Modeling, - Northwestern University, Evanston, IL. -""" - -import mesa - -from wolf_sheep.scheduler import RandomActivationByTypeFiltered -from wolf_sheep.agents import Sheep, Wolf, GrassPatch - - -class WolfSheep(mesa.Model): - """ - Wolf-Sheep Predation Model - """ - - height = 20 - width = 20 - - initial_sheep = 100 - initial_wolves = 50 - - sheep_reproduce = 0.04 - wolf_reproduce = 0.05 - - wolf_gain_from_food = 20 - - grass = False - grass_regrowth_time = 30 - sheep_gain_from_food = 4 - - verbose = False # Print-monitoring - - description = ( - "A model for simulating wolf and sheep (predator-prey) ecosystem modelling." - ) - - def __init__( - self, - width=20, - height=20, - initial_sheep=100, - initial_wolves=50, - sheep_reproduce=0.04, - wolf_reproduce=0.05, - wolf_gain_from_food=20, - grass=False, - grass_regrowth_time=30, - sheep_gain_from_food=4, - ): - """ - Create a new Wolf-Sheep model with the given parameters. - - Args: - initial_sheep: Number of sheep to start with - initial_wolves: Number of wolves to start with - sheep_reproduce: Probability of each sheep reproducing each step - wolf_reproduce: Probability of each wolf reproducing each step - wolf_gain_from_food: Energy a wolf gains from eating a sheep - grass: Whether to have the sheep eat grass for energy - grass_regrowth_time: How long it takes for a grass patch to regrow - once it is eaten - sheep_gain_from_food: Energy sheep gain from grass, if enabled. - """ - super().__init__() - # Set parameters - self.width = width - self.height = height - self.initial_sheep = initial_sheep - self.initial_wolves = initial_wolves - self.sheep_reproduce = sheep_reproduce - self.wolf_reproduce = wolf_reproduce - self.wolf_gain_from_food = wolf_gain_from_food - self.grass = grass - self.grass_regrowth_time = grass_regrowth_time - self.sheep_gain_from_food = sheep_gain_from_food - - self.schedule = RandomActivationByTypeFiltered(self) - self.grid = mesa.space.MultiGrid(self.width, self.height, torus=True) - self.datacollector = mesa.DataCollector( - { - "Wolves": lambda m: m.schedule.get_type_count(Wolf), - "Sheep": lambda m: m.schedule.get_type_count(Sheep), - "Grass": lambda m: m.schedule.get_type_count( - GrassPatch, lambda x: x.fully_grown - ), - } - ) - - # Create sheep: - for i in range(self.initial_sheep): - x = self.random.randrange(self.width) - y = self.random.randrange(self.height) - energy = self.random.randrange(2 * self.sheep_gain_from_food) - sheep = Sheep(self.next_id(), (x, y), self, True, energy) - self.grid.place_agent(sheep, (x, y)) - self.schedule.add(sheep) - - # Create wolves - for i in range(self.initial_wolves): - x = self.random.randrange(self.width) - y = self.random.randrange(self.height) - energy = self.random.randrange(2 * self.wolf_gain_from_food) - wolf = Wolf(self.next_id(), (x, y), self, True, energy) - self.grid.place_agent(wolf, (x, y)) - self.schedule.add(wolf) - - # Create grass patches - if self.grass: - for agent, x, y in self.grid.coord_iter(): - - fully_grown = self.random.choice([True, False]) - - if fully_grown: - countdown = self.grass_regrowth_time - else: - countdown = self.random.randrange(self.grass_regrowth_time) - - patch = GrassPatch(self.next_id(), (x, y), self, fully_grown, countdown) - self.grid.place_agent(patch, (x, y)) - self.schedule.add(patch) - - self.running = True - self.datacollector.collect(self) - - def step(self): - self.schedule.step() - # collect data - self.datacollector.collect(self) - if self.verbose: - print( - [ - self.schedule.time, - self.schedule.get_type_count(Wolf), - self.schedule.get_type_count(Sheep), - self.schedule.get_type_count(GrassPatch, lambda x: x.fully_grown), - ] - ) - - def run_model(self, step_count=200): - - if self.verbose: - print("Initial number wolves: ", self.schedule.get_type_count(Wolf)) - print("Initial number sheep: ", self.schedule.get_type_count(Sheep)) - print( - "Initial number grass: ", - self.schedule.get_type_count(GrassPatch, lambda x: x.fully_grown), - ) - - for i in range(step_count): - self.step() - - if self.verbose: - print("") - print("Final number wolves: ", self.schedule.get_type_count(Wolf)) - print("Final number sheep: ", self.schedule.get_type_count(Sheep)) - print( - "Final number grass: ", - self.schedule.get_type_count(GrassPatch, lambda x: x.fully_grown), - ) diff --git a/examples/wolf_sheep/wolf_sheep/random_walk.py b/examples/wolf_sheep/wolf_sheep/random_walk.py deleted file mode 100644 index 49219fa7fff..00000000000 --- a/examples/wolf_sheep/wolf_sheep/random_walk.py +++ /dev/null @@ -1,41 +0,0 @@ -""" -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 - 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/wolf_sheep/wolf_sheep/resources/sheep.png b/examples/wolf_sheep/wolf_sheep/resources/sheep.png deleted file mode 100644 index dfb81b0e5d73bb9f41e4b788934b212b1d544818..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1322 zcmV+_1=aeAP)Nkl`0Z;)@0i*waLm3IY{CDgY_~DgY_~eJ8)u#Pa%QZ#O|v zEJc^Qwa4@3=gru=rz`@0O1dlQholdZ-lqSV@<`G{Nw?Gd z-)YXt(jMK#BcCKaC@6%ouO!`1KuS7)mh@N>a~BzNfxMA~H-1Wbk*vI*{^$FRq}?A$ z$CAFL?|6>yJjZ*0S!DD8AuMjC@Ij$(lD zuTu_`HX+W*kJ4~XkT<1)S6)iOv&=ELv$^3m1q8w502XC@OqL@jh<(O0N$+Qx5jn^& z4Zx9OL-{iW#1#P{4TfV7dWR$Or-1xQ?moys%yhtH z_~oGo7=YbLV924aF$LtWB;vJ1uU&$7m=wvNJA6w(NK{w}_jX%k2gOTEA(3|@#xxL= zhedK0S~Ii&z`oQYlM2Rzy2%}r0r|=xA0eG069NQRl6VNEz=+;{;)liholDE;q zog>x;HHUQUk&_6-yuM+9wY4tDC&5Yh?*1wjR2*;^fml#4Yb=d5o>4F=ml&-Bz6+ziS(l;k@Gj;#~V2z|T=sykLZozBanCwz#8NU zbO;TD9;@^es-Deja~xvq2a{rb{(LjTTApEWJhIU#Kaa$gA>VAt^9c!xfl9T?O)NP= zu5^vs3DE*T@A^`!y1uZ81*@b)JhB`@ET#n9B?PW zC%=3BkT*)9Weet1Y#=X$hJpJI6cOw){`k9=6aHp*v~ie@tEW!AE^AbW-|gIG`Y+2 zKt5NrH5P3a#oB8HD_hzqDn_V`0P%b)N8aF}*6DDTM_3e<%@f+Jgc5$$Q1gi2aB=d) zBfe;09|PIxuDRwBZ|;qv>mwNuhAodc!6R@KXI=c9eQ90;;$tKQ&F?KdKQb59yamLR zjnY(pDA{sX2NDw=04QJ}RB|V%6{&?*l;yi#=Et{ItA&gS^fkh`i^ahmbPn`}9b&iC@#=Ei!4*k_RMFTF0xMI;734eT27zp^XG?bg zSJY04k5Yij1FPBBv=%l3LhpL}c28v=?0qr{K6!l`y#J-rfUWqmscGCG?eF*6AJ&~f z`$iZbWWK z1U^5Sn%VQ0v1h?#L%s-*X)~%dLCeM49^f$s6<}QR!?izKHiSTV{xjq9aU+j3P89(< z)yF|1;3?F6EIt0lnEA@^ttIeO1ekW?fD)(|XVm>ZI9EZYjceHbMF0an&l5?+REIoZ z5%}gr)_#@Nd_<`+y`-5od#yHpQ;6Y2x+W1IYy0G^wL+^i?^I8j*;L;19|2n~lRLqx z2{=HHPMG-921_UvXu2uHtDpy?67*SR7|G81ReB2X*Z(cliwz}E zDPX#PDElGUmtwzwc`OXJ1fEE$WTMk6fqX$yA0S|qi*$@~*aXy-#=`^QA)eq@ah>#< zLNBN6${{e(xn(m8CZ^4FaM6}|JD&@?x4X?^&Tla=E1y0aS-ybZ0tg*$JeB~zT+_ql zKXkBQa-RxmBVHCHJuiMS)(?1Q-PhH=M+zMfBqZ} z-GGyLz#^b&E9?Y7ZV#-E8Kt{6RSF0dI6z^*dTd3>xivD%_P=ffZhd!HA7Y(}a!lH; z16EgU>KH=ES)LHa>> scheduler = RandomActivationByTypeFiltered(model) - >>> scheduler.get_type_count(AgentA, lambda agent: agent.some_attribute > 10) - """ - - def get_type_count( - self, - type_class: Type[mesa.Agent], - filter_func: Callable[[mesa.Agent], bool] = None, - ) -> int: - """ - Returns the current number of agents of certain type in the queue that satisfy the filter function. - """ - count = 0 - for agent in self.agents_by_type[type_class].values(): - if filter_func is None or filter_func(agent): - count += 1 - return count diff --git a/examples/wolf_sheep/wolf_sheep/server.py b/examples/wolf_sheep/wolf_sheep/server.py deleted file mode 100644 index bccf4ec849d..00000000000 --- a/examples/wolf_sheep/wolf_sheep/server.py +++ /dev/null @@ -1,79 +0,0 @@ -import mesa - -from wolf_sheep.agents import Wolf, Sheep, GrassPatch -from wolf_sheep.model import WolfSheep - - -def wolf_sheep_portrayal(agent): - if agent is None: - return - - portrayal = {} - - if type(agent) is Sheep: - portrayal["Shape"] = "wolf_sheep/resources/sheep.png" - # https://icons8.com/web-app/433/sheep - portrayal["scale"] = 0.9 - portrayal["Layer"] = 1 - - elif type(agent) is Wolf: - portrayal["Shape"] = "wolf_sheep/resources/wolf.png" - # https://icons8.com/web-app/36821/German-Shepherd - portrayal["scale"] = 0.9 - portrayal["Layer"] = 2 - portrayal["text"] = round(agent.energy, 1) - portrayal["text_color"] = "White" - - elif type(agent) is GrassPatch: - if agent.fully_grown: - portrayal["Color"] = ["#00FF00", "#00CC00", "#009900"] - else: - portrayal["Color"] = ["#84e184", "#adebad", "#d6f5d6"] - portrayal["Shape"] = "rect" - portrayal["Filled"] = "true" - portrayal["Layer"] = 0 - portrayal["w"] = 1 - portrayal["h"] = 1 - - return portrayal - - -canvas_element = mesa.visualization.CanvasGrid(wolf_sheep_portrayal, 20, 20, 500, 500) -chart_element = mesa.visualization.ChartModule( - [ - {"Label": "Wolves", "Color": "#AA0000"}, - {"Label": "Sheep", "Color": "#666666"}, - {"Label": "Grass", "Color": "#00AA00"}, - ] -) - -model_params = { - # The following line is an example to showcase StaticText. - "title": mesa.visualization.StaticText("Parameters:"), - "grass": mesa.visualization.Checkbox("Grass Enabled", True), - "grass_regrowth_time": mesa.visualization.Slider("Grass Regrowth Time", 20, 1, 50), - "initial_sheep": mesa.visualization.Slider( - "Initial Sheep Population", 100, 10, 300 - ), - "sheep_reproduce": mesa.visualization.Slider( - "Sheep Reproduction Rate", 0.04, 0.01, 1.0, 0.01 - ), - "initial_wolves": mesa.visualization.Slider("Initial Wolf Population", 50, 10, 300), - "wolf_reproduce": mesa.visualization.Slider( - "Wolf Reproduction Rate", - 0.05, - 0.01, - 1.0, - 0.01, - description="The rate at which wolf agents reproduce.", - ), - "wolf_gain_from_food": mesa.visualization.Slider( - "Wolf Gain From Food Rate", 20, 1, 50 - ), - "sheep_gain_from_food": mesa.visualization.Slider("Sheep Gain From Food", 4, 1, 10), -} - -server = mesa.visualization.ModularServer( - WolfSheep, [canvas_element, chart_element], "Wolf Sheep Predation", model_params -) -server.port = 8521 diff --git a/examples/wolf_sheep/wolf_sheep/test_random_walk.py b/examples/wolf_sheep/wolf_sheep/test_random_walk.py deleted file mode 100644 index ab3b044ab1e..00000000000 --- a/examples/wolf_sheep/wolf_sheep/test_random_walk.py +++ /dev/null @@ -1,82 +0,0 @@ -""" -Testing the RandomWalker by having an ABM composed only of random walker -agents. -""" - -from mesa import Model -from mesa.space import MultiGrid -from mesa.time import RandomActivation -from mesa.visualization.TextVisualization import TextVisualization, TextGrid - -from wolf_sheep.random_walk import RandomWalker - - -class WalkerAgent(RandomWalker): - """ - Agent which only walks around. - """ - - def step(self): - self.random_move() - - -class WalkerWorld(Model): - """ - Random walker world. - """ - - height = 10 - width = 10 - - def __init__(self, width, height, agent_count): - """ - Create a new WalkerWorld. - - Args: - width, height: World size. - agent_count: How many agents to create. - """ - self.height = height - self.width = width - self.grid = MultiGrid(self.width, self.height, torus=True) - self.agent_count = agent_count - - self.schedule = RandomActivation(self) - # Create agents - for i in range(self.agent_count): - x = self.random.randrange(self.width) - y = self.random.randrange(self.height) - a = WalkerAgent(i, (x, y), self, True) - self.schedule.add(a) - self.grid.place_agent(a, (x, y)) - - def step(self): - self.schedule.step() - - -class WalkerWorldViz(TextVisualization): - """ - ASCII Visualization for a WalkerWorld agent. - Each cell is displayed as the number of agents currently in that cell. - """ - - def __init__(self, model): - """ - Create a new visualization for a WalkerWorld instance. - - args: - model: An instance of a WalkerWorld model. - """ - self.model = model - grid_viz = TextGrid(self.model.grid, None) - grid_viz.converter = lambda x: str(len(x)) - self.elements = [grid_viz] - - -if __name__ == "__main__": - print("Testing 10x10 world, with 50 random walkers, for 10 steps.") - model = WalkerWorld(10, 10, 50) - viz = WalkerWorldViz(model) - for i in range(10): - print("Step:", str(i)) - viz.step() From c661f56191261dda608d933bea269f6b9d308248 Mon Sep 17 00:00:00 2001 From: jackiekazil Date: Sun, 4 Dec 2022 22:00:19 -0500 Subject: [PATCH 031/214] Update contributing.rst to reflect examples move. --- CONTRIBUTING.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 64a29f1dca3..44f5b5e8848 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -33,6 +33,7 @@ discuss via `Matrix`_ OR via `an issue`_. - Git add the new files and files with changes: ``git add FILE_NAME`` - Git commit your changes with a meaningful message: ``git commit -m "Fix issue X"`` - If implementing a new feature, include some documentation in docs folder. +- Make sure that your submission works with a few of the examples in the examples repository. If adding a new feauture to mesa, please illustrate usage by implementing it in an example. - Make sure that your submission passes the `GH Actions build`_. See "Testing and Standards below" to be able to run these locally. - Make sure that your code is formatted according to `the black`_ standard (you can do it via `pre-commit`_). - Push your changes to your fork on Github: ``git push origin NAME_OF_BRANCH``. From 6d96c4d98fc7e12863468fc0106f99061852893f Mon Sep 17 00:00:00 2001 From: jackiekazil Date: Sun, 4 Dec 2022 22:31:16 -0500 Subject: [PATCH 032/214] Add skip to test for examples b/c of move. Needs more work. --- tests/test_examples.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_examples.py b/tests/test_examples.py index 9baa31c8b55..026c807a82f 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -9,6 +9,9 @@ def classcase(name): return "".join(x.capitalize() for x in name.replace("-", "_").split("_")) +@unittest.skip( + "Skipping TextExamples, because TextExamples was moved. More discussion needed." +) class TestExamples(unittest.TestCase): """ Test examples' models. This creates a model object and iterates it through From c11af9ea2589b852452d763587da79fc4d8e3910 Mon Sep 17 00:00:00 2001 From: jackiekazil Date: Sun, 4 Dec 2022 22:43:18 -0500 Subject: [PATCH 033/214] Fix spelling error in contributing.rst. --- CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 44f5b5e8848..20affdb0061 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -33,7 +33,7 @@ discuss via `Matrix`_ OR via `an issue`_. - Git add the new files and files with changes: ``git add FILE_NAME`` - Git commit your changes with a meaningful message: ``git commit -m "Fix issue X"`` - If implementing a new feature, include some documentation in docs folder. -- Make sure that your submission works with a few of the examples in the examples repository. If adding a new feauture to mesa, please illustrate usage by implementing it in an example. +- Make sure that your submission works with a few of the examples in the examples repository. If adding a new feature to mesa, please illustrate usage by implementing it in an example. - Make sure that your submission passes the `GH Actions build`_. See "Testing and Standards below" to be able to run these locally. - Make sure that your code is formatted according to `the black`_ standard (you can do it via `pre-commit`_). - Push your changes to your fork on Github: ``git push origin NAME_OF_BRANCH``. From 9bc7b1a3a998c68dec2b85d259baedbf3e6bce02 Mon Sep 17 00:00:00 2001 From: jackiekazil Date: Sun, 4 Dec 2022 22:49:22 -0500 Subject: [PATCH 034/214] Skip tests impacted by examples folder move. --- tests/test_examples.py | 2 +- tests/test_main.py | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/test_examples.py b/tests/test_examples.py index 026c807a82f..73285b5ce35 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -10,7 +10,7 @@ def classcase(name): @unittest.skip( - "Skipping TextExamples, because TextExamples was moved. More discussion needed." + "Skipping TextExamples, because examples folder was moved. More discussion needed." ) class TestExamples(unittest.TestCase): """ diff --git a/tests/test_main.py b/tests/test_main.py index 2d6f0cac5ac..6aa385028c7 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -19,6 +19,9 @@ def setUp(self): def tearDown(self): sys.path[:] = self.old_sys_path + @unittest.skip( + "Skipping test_run, because examples folder was moved. More discussion needed." + ) def test_run(self): with patch("mesa.visualization.ModularServer") as ModularServer: example_dir = os.path.abspath( From b1671b612dc8a362b27d215a935f887b3916d359 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 00:55:51 +0000 Subject: [PATCH 035/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.2.0 → v3.3.0](https://github.com/asottile/pyupgrade/compare/v3.2.0...v3.3.0) - [github.com/pre-commit/pre-commit-hooks: v4.3.0 → v4.4.0](https://github.com/pre-commit/pre-commit-hooks/compare/v4.3.0...v4.4.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d3a37f26eb5..2285b3af89a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,12 +9,12 @@ repos: - id: black-jupyter exclude: ^mesa/cookiecutter-mesa/ - repo: https://github.com/asottile/pyupgrade - rev: v3.2.0 + rev: v3.3.0 hooks: - id: pyupgrade args: [--py38-plus] - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.3.0 # Use the ref you want to point at + rev: v4.4.0 # Use the ref you want to point at hooks: - id: trailing-whitespace - id: check-toml From e92e8848f2e51b7a682dd6107a929746dd00e3ff Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Dec 2022 00:56:00 +0000 Subject: [PATCH 036/214] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 20affdb0061..5ed0feef7e0 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -33,7 +33,7 @@ discuss via `Matrix`_ OR via `an issue`_. - Git add the new files and files with changes: ``git add FILE_NAME`` - Git commit your changes with a meaningful message: ``git commit -m "Fix issue X"`` - If implementing a new feature, include some documentation in docs folder. -- Make sure that your submission works with a few of the examples in the examples repository. If adding a new feature to mesa, please illustrate usage by implementing it in an example. +- Make sure that your submission works with a few of the examples in the examples repository. If adding a new feature to mesa, please illustrate usage by implementing it in an example. - Make sure that your submission passes the `GH Actions build`_. See "Testing and Standards below" to be able to run these locally. - Make sure that your code is formatted according to `the black`_ standard (you can do it via `pre-commit`_). - Push your changes to your fork on Github: ``git push origin NAME_OF_BRANCH``. From 336ae595dbfe032c5a09183d7461d56561bbe66e Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Tue, 6 Dec 2022 22:34:32 +0100 Subject: [PATCH 037/214] Add some missing const declarations --- mesa/visualization/templates/js/GridDraw.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/visualization/templates/js/GridDraw.js b/mesa/visualization/templates/js/GridDraw.js index b24e529d0cc..5e1603deb27 100644 --- a/mesa/visualization/templates/js/GridDraw.js +++ b/mesa/visualization/templates/js/GridDraw.js @@ -393,8 +393,8 @@ const GridVisualization = function ( this.drawGridLines = function () { context.beginPath(); context.strokeStyle = "#eee"; - maxX = cellWidth * gridWidth; - maxY = cellHeight * gridHeight; + const maxX = cellWidth * gridWidth; + const maxY = cellHeight * gridHeight; // Draw horizontal grid lines: for (let y = 0; y <= maxY; y += cellHeight) { From b09b608eeef0ed773628e1e11f5450490b1af298 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Fri, 9 Dec 2022 12:57:56 +0100 Subject: [PATCH 038/214] cleanup: Fix JS code warning suggested by lgtm.com (#1554) * Format js code * Update InteractionHandler.js --- mesa/visualization/templates/js/GridDraw.js | 10 +++------- mesa/visualization/templates/js/HexDraw.js | 14 +++++--------- .../templates/js/InteractionHandler.js | 2 -- 3 files changed, 8 insertions(+), 18 deletions(-) diff --git a/mesa/visualization/templates/js/GridDraw.js b/mesa/visualization/templates/js/GridDraw.js index 5e1603deb27..614ed6eb815 100644 --- a/mesa/visualization/templates/js/GridDraw.js +++ b/mesa/visualization/templates/js/GridDraw.js @@ -57,7 +57,7 @@ const GridVisualization = function ( // Calls the appropriate shape(agent) this.drawLayer = function (portrayalLayer) { // Re-initialize the lookup table - interactionHandler ? interactionHandler.mouseoverLookupTable.init() : null; + if (interactionHandler) interactionHandler.mouseoverLookupTable.init(); for (const i in portrayalLayer) { const p = portrayalLayer[i]; @@ -72,9 +72,7 @@ const GridVisualization = function ( p.y = gridHeight - p.y - 1; // if a handler exists, add coordinates for the portrayalLayer index - interactionHandler - ? interactionHandler.mouseoverLookupTable.set(p.x, p.y, i) - : null; + if (interactionHandler) interactionHandler.mouseoverLookupTable.set(p.x, p.y, i); // If the stroke color is not defined, then the first color in the colors array is the stroke color. if (!p.stroke_color) p.stroke_color = p.Color[0]; @@ -127,9 +125,7 @@ const GridVisualization = function ( this.drawCustomImage(p.Shape, p.x, p.y, p.scale, p.text, p.text_color); } // if a handler exists, update its mouse listeners with the new data - interactionHandler - ? interactionHandler.updateMouseListeners(portrayalLayer) - : null; + if (interactionHandler) interactionHandler.updateMouseListeners(portrayalLayer); }; // DRAWING METHODS diff --git a/mesa/visualization/templates/js/HexDraw.js b/mesa/visualization/templates/js/HexDraw.js index a09bbbff9bc..ad496389f9a 100644 --- a/mesa/visualization/templates/js/HexDraw.js +++ b/mesa/visualization/templates/js/HexDraw.js @@ -55,12 +55,12 @@ const HexVisualization = function ( const maxR = Math.min(cellHeight, cellWidth) / 2 - 1; // Configure the interaction handler to use a hex coordinate mapper - interactionHandler ? interactionHandler.setCoordinateMapper("hex") : null; + if (interactionHandler) interactionHandler.setCoordinateMapper("hex"); // Calls the appropriate shape(agent) this.drawLayer = function (portrayalLayer) { // Re-initialize the lookup table - interactionHandler ? interactionHandler.mouseoverLookupTable.init() : null; + if (interactionHandler) interactionHandler.mouseoverLookupTable.init(); for (const i in portrayalLayer) { const p = portrayalLayer[i]; // Does the inversion of y positioning because of html5 @@ -69,9 +69,7 @@ const HexVisualization = function ( p.y = gridHeight - p.y - 1; // if a handler exists, add coordinates for the portrayalLayer index - interactionHandler - ? interactionHandler.mouseoverLookupTable.set(p.x, p.y, i) - : null; + if (interactionHandler) interactionHandler.mouseoverLookupTable.set(p.x, p.y, i); if (p.Shape == "hex") this.drawHex(p.x, p.y, p.r, p.Color, p.Filled, p.text, p.text_color); @@ -93,9 +91,7 @@ const HexVisualization = function ( this.drawCustomImage(p.Shape, p.x, p.y, p.scale, p.text, p.text_color); } // if a handler exists, update its mouse listeners with the new data - interactionHandler - ? interactionHandler.updateMouseListeners(portrayalLayer) - : null; + if (interactionHandler) interactionHandler.updateMouseListeners(portrayalLayer); }; // DRAWING METHODS @@ -158,7 +154,7 @@ const HexVisualization = function ( } else { cy = (y + 0.5) * cellHeight + cellHeight / 2; } - maxHexRadius = cellHeight / Math.sqrt(3); + const maxHexRadius = cellHeight / Math.sqrt(3); const r = radius * maxHexRadius; function hex_corner(x, y, size, i) { diff --git a/mesa/visualization/templates/js/InteractionHandler.js b/mesa/visualization/templates/js/InteractionHandler.js index 93f1a6b6371..f98906d1a59 100644 --- a/mesa/visualization/templates/js/InteractionHandler.js +++ b/mesa/visualization/templates/js/InteractionHandler.js @@ -125,8 +125,6 @@ const InteractionHandler = function (width, height, gridWidth, gridHeight, ctx) // map the event to x,y coordinates const position = coordinateMapper(event); - const yPosition = Math.floor(event.offsetY / cellHeight); - const xPosition = Math.floor(event.offsetX / cellWidth); // look up the portrayal items the coordinates refer to and draw a tooltip mouseoverLookupTable From d3b62bbbb11e3f8a39bcaa3214df2804a42bc9d0 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 9 Nov 2022 19:19:40 -0500 Subject: [PATCH 039/214] ci: Add testing on Python 3.11 --- .github/workflows/build_lint.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 33d311b937b..362a917da00 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -32,6 +32,8 @@ jobs: os: [windows, ubuntu, macos] python-version: ["3.10"] include: + - os: ubuntu + python-version: "3.11.1" - os: ubuntu python-version: "3.9" - os: ubuntu From bf55f5366fa4b0aac6c0ccfba9b46e040f6431e2 Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 9 Dec 2022 05:53:17 -0500 Subject: [PATCH 040/214] ci: Make Python 3.11 version less specific --- .github/workflows/build_lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 362a917da00..399a9b35164 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -33,7 +33,7 @@ jobs: python-version: ["3.10"] include: - os: ubuntu - python-version: "3.11.1" + python-version: "3.11" - os: ubuntu python-version: "3.9" - os: ubuntu From ab0dbf40e187bf3572dc44d1addc076a0d7c58a2 Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 9 Dec 2022 09:33:21 -0500 Subject: [PATCH 041/214] ci: Use Python 3.11 as the default version --- .github/workflows/build_lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 399a9b35164..5d87a4b6880 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -30,10 +30,10 @@ jobs: fail-fast: False matrix: os: [windows, ubuntu, macos] - python-version: ["3.10"] + python-version: ["3.11"] include: - os: ubuntu - python-version: "3.11" + python-version: "3.10" - os: ubuntu python-version: "3.9" - os: ubuntu From fc013ab9838b79771c112d237596bdcb1909c4e7 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Wed, 14 Dec 2022 04:04:18 +0100 Subject: [PATCH 042/214] perf: Evaluate empties set more lazily (#1546) --- mesa/space.py | 36 +++++++++++++++++++++++++++--------- 1 file changed, 27 insertions(+), 9 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 42dd29e3572..f3e28b97e14 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -108,8 +108,10 @@ def __init__(self, width: int, height: int, torus: bool) -> None: [self.default_val() for _ in range(self.height)] for _ in range(self.width) ] - # Add all cells to the empties list. - self.empties = set(itertools.product(range(self.width), range(self.height))) + # Flag to check if the empties set has been created. Better than initializing + # _empties as set() because in this case it would become impossible to discern + # if the set hasn't still being built or if it has become empty after creation. + self.empties_built = False # Neighborhood Cache self._neighborhood_cache: dict[Any, list[Coordinate]] = dict() @@ -119,6 +121,21 @@ def default_val() -> None: """Default value for new cell elements.""" return None + @property + def empties(self) -> set: + if not self.empties_built: + self.build_empties() + return self._empties + + def build_empties(self) -> None: + self._empties = set( + filter( + self.is_cell_empty, + itertools.product(range(self.width), range(self.height)), + ) + ) + self.empties_built = True + @overload def __getitem__(self, index: int) -> list[GridContent]: ... @@ -421,7 +438,8 @@ def place_agent(self, agent: Agent, pos: Coordinate) -> None: """Place the agent at the specified location, and set its pos variable.""" x, y = pos self.grid[x][y] = agent - self.empties.discard(pos) + if self.empties_built: + self._empties.discard(pos) agent.pos = pos def remove_agent(self, agent: Agent) -> None: @@ -430,7 +448,8 @@ def remove_agent(self, agent: Agent) -> None: return x, y = pos self.grid[x][y] = self.default_val() - self.empties.add(pos) + if self.empties_built: + self._empties.add(pos) agent.pos = None def swap_pos(self, agent_a: Agent, agent_b: Agent) -> None: @@ -551,8 +570,6 @@ def position_agent( ) if x == "random" or y == "random": - if len(self.empties) == 0: - raise Exception("ERROR: Grid full") self.move_to_empty(agent) else: coords = (x, y) @@ -598,15 +615,16 @@ def place_agent(self, agent: Agent, pos: Coordinate) -> None: if agent.pos is None or agent not in self.grid[x][y]: self.grid[x][y].append(agent) agent.pos = pos - self.empties.discard(pos) + if self.empties_built: + 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.""" pos = agent.pos x, y = pos self.grid[x][y].remove(agent) - if self.is_cell_empty(pos): - self.empties.add(pos) + if self.empties_built and self.is_cell_empty(pos): + self._empties.add(pos) agent.pos = None @accept_tuple_argument From b36fce7845f97c0296dd188d63bb61adba6827c7 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Sat, 24 Dec 2022 04:39:57 +0100 Subject: [PATCH 043/214] feat: Implement radius for NetworkGrid.get_neighbors (#1564) --- mesa/space.py | 21 ++++++++++++++++----- tests/test_space.py | 14 +++++--------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index f3e28b97e14..279e876ab11 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -26,6 +26,7 @@ from warnings import warn import numpy as np +import networkx as nx from typing import ( Any, @@ -1033,11 +1034,21 @@ def place_agent(self, agent: Agent, node_id: int) -> None: 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) + def get_neighbors( + self, node_id: int, include_center: bool = False, radius: int = 1 + ) -> list[int]: + """Get all adjacent nodes within a certain radius""" + if radius == 1: + neighbors = list(self.G.neighbors(node_id)) + if include_center: + neighbors.append(node_id) + else: + neighbors_with_distance = nx.single_source_shortest_path_length( + self.G, node_id, radius + ) + if not include_center: + del neighbors_with_distance[node_id] + neighbors = list(neighbors_with_distance.keys()) return neighbors def move_agent(self, agent: Agent, node_id: int) -> None: diff --git a/tests/test_space.py b/tests/test_space.py index 769214cf0c5..37a6f691444 100644 --- a/tests/test_space.py +++ b/tests/test_space.py @@ -340,7 +340,7 @@ def setUp(self): """ Create a test network grid and populate with Mock Agents. """ - G = nx.complete_graph(TestSingleNetworkGrid.GRAPH_SIZE) + G = nx.cycle_graph(TestSingleNetworkGrid.GRAPH_SIZE) self.space = NetworkGrid(G) self.agents = [] for i, pos in enumerate(TEST_AGENTS_NETWORK_SINGLE): @@ -357,14 +357,10 @@ def test_agent_positions(self): assert a.pos == pos def test_get_neighbors(self): - assert ( - len(self.space.get_neighbors(0, include_center=True)) - == TestSingleNetworkGrid.GRAPH_SIZE - ) - assert ( - len(self.space.get_neighbors(0, include_center=False)) - == TestSingleNetworkGrid.GRAPH_SIZE - 1 - ) + assert len(self.space.get_neighbors(0, include_center=True)) == 3 + assert len(self.space.get_neighbors(0, include_center=False)) == 2 + assert len(self.space.get_neighbors(2, include_center=True, radius=3)) == 7 + assert len(self.space.get_neighbors(2, include_center=False, radius=3)) == 6 def test_move_agent(self): initial_pos = 1 From d607653635a759bcd0c19cef0eacdf6e13826f9d Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Sun, 25 Dec 2022 16:36:43 +0100 Subject: [PATCH 044/214] Establish reproducibility for NetworkGrid.get_neighbors when radius > 1 --- mesa/space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/space.py b/mesa/space.py index 279e876ab11..20ad651b998 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -1048,7 +1048,7 @@ def get_neighbors( ) if not include_center: del neighbors_with_distance[node_id] - neighbors = list(neighbors_with_distance.keys()) + neighbors = sorted(neighbors_with_distance.keys()) return neighbors def move_agent(self, agent: Agent, node_id: int) -> None: From 486cecaba5d17e3e7b2bde0f6604bb9d493cd039 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Wed, 28 Dec 2022 10:05:35 +0100 Subject: [PATCH 045/214] Make the internal grid and empties_built in Grid class private (#1568) * Make grid and empties_built in Grid class private * Update space.py --- mesa/space.py | 53 +++++++++++++++++++++------------------------ tests/test_grid.py | 4 ++-- tests/test_space.py | 12 +++++----- 3 files changed, 33 insertions(+), 36 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 20ad651b998..aea5ff6235d 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -89,7 +89,6 @@ class Grid: Properties: width, height: The grid's width and height. torus: Boolean which determines whether to treat the grid as a torus. - grid: Internal list-of-lists which holds the grid cells themselves. """ def __init__(self, width: int, height: int, torus: bool) -> None: @@ -104,15 +103,16 @@ def __init__(self, width: int, height: int, torus: bool) -> None: self.torus = torus self.num_cells = height * width - self.grid: list[list[GridContent]] - self.grid = [ + # Internal list-of-lists which holds the grid cells themselves + self._grid: list[list[GridContent]] + self._grid = [ [self.default_val() for _ in range(self.height)] for _ in range(self.width) ] # Flag to check if the empties set has been created. Better than initializing # _empties as set() because in this case it would become impossible to discern # if the set hasn't still being built or if it has become empty after creation. - self.empties_built = False + self._empties_built = False # Neighborhood Cache self._neighborhood_cache: dict[Any, list[Coordinate]] = dict() @@ -124,7 +124,7 @@ def default_val() -> None: @property def empties(self) -> set: - if not self.empties_built: + if not self._empties_built: self.build_empties() return self._empties @@ -135,7 +135,7 @@ def build_empties(self) -> None: itertools.product(range(self.width), range(self.height)), ) ) - self.empties_built = True + self._empties_built = True @overload def __getitem__(self, index: int) -> list[GridContent]: @@ -159,11 +159,11 @@ def __getitem__( if isinstance(index, int): # grid[x] - return self.grid[index] + return self._grid[index] elif isinstance(index[0], tuple): # grid[(x1, y1), (x2, y2), ...] index = cast(Sequence[Coordinate], index) - return [self.grid[x][y] for x, y in map(self.torus_adj, index)] + return [self._grid[x][y] for x, y in map(self.torus_adj, index)] x, y = index x_int, y_int = is_integer(x), is_integer(y) @@ -172,32 +172,32 @@ def __getitem__( # grid[x, y] index = cast(Coordinate, index) x, y = self.torus_adj(index) - return self.grid[x][y] + return self._grid[x][y] elif x_int: # grid[x, :] x, _ = self.torus_adj((x, 0)) y = cast(slice, y) - return self.grid[x][y] + return self._grid[x][y] elif y_int: # grid[:, y] _, y = self.torus_adj((0, y)) x = cast(slice, x) - return [rows[y] for rows in self.grid[x]] + return [rows[y] for rows in self._grid[x]] else: # grid[:, :] x, y = (cast(slice, x), cast(slice, y)) - return [cell for rows in self.grid[x] for cell in rows[y]] + return [cell for rows in self._grid[x] for cell in rows[y]] def __iter__(self) -> Iterator[GridContent]: """Create an iterator that chains the rows of the grid together as if it is one list:""" - return itertools.chain(*self.grid) + return itertools.chain(*self._grid) def coord_iter(self) -> Iterator[tuple[GridContent, int, int]]: """An iterator that returns coordinates as well as cell contents.""" for row in range(self.width): for col in range(self.height): - yield self.grid[row][col], row, col # agent, x, y + yield self._grid[row][col], row, col # agent, x, y def neighbor_iter(self, pos: Coordinate, moore: bool = True) -> Iterator[Agent]: """Iterate over position neighbors. @@ -407,7 +407,7 @@ def iter_cell_list_contents( An iterator of the contents of the cells identified 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]) + 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]: @@ -438,8 +438,8 @@ def move_agent(self, agent: Agent, pos: Coordinate) -> None: def place_agent(self, agent: Agent, pos: Coordinate) -> None: """Place the agent at the specified location, and set its pos variable.""" x, y = pos - self.grid[x][y] = agent - if self.empties_built: + self._grid[x][y] = agent + if self._empties_built: self._empties.discard(pos) agent.pos = pos @@ -448,8 +448,8 @@ def remove_agent(self, agent: Agent) -> None: if (pos := agent.pos) is None: return x, y = pos - self.grid[x][y] = self.default_val() - if self.empties_built: + self._grid[x][y] = self.default_val() + if self._empties_built: self._empties.add(pos) agent.pos = None @@ -476,7 +476,7 @@ def swap_pos(self, agent_a: Agent, agent_b: Agent) -> None: def is_cell_empty(self, pos: Coordinate) -> bool: """Returns a bool of the contents of a cell.""" x, y = pos - return self.grid[x][y] == self.default_val() + return self._grid[x][y] == self.default_val() def move_to_empty( self, agent: Agent, cutoff: float = 0.998, num_agents: int | None = None @@ -594,11 +594,8 @@ class MultiGrid(Grid): Properties: width, height: The grid's width and height. - torus: Boolean which determines whether to treat the grid as a torus. - grid: Internal list-of-lists which holds the grid cells themselves. - Methods: get_neighbors: Returns the objects surrounding a given cell. """ @@ -613,18 +610,18 @@ 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.pos is None or agent not in self.grid[x][y]: - self.grid[x][y].append(agent) + if agent.pos is None or agent not in self._grid[x][y]: + self._grid[x][y].append(agent) agent.pos = pos - if self.empties_built: + if self._empties_built: 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.""" pos = agent.pos x, y = pos - self.grid[x][y].remove(agent) - if self.empties_built and self.is_cell_empty(pos): + self._grid[x][y].remove(agent) + if self._empties_built and self.is_cell_empty(pos): self._empties.add(pos) agent.pos = None diff --git a/tests/test_grid.py b/tests/test_grid.py index 007aa6c46f4..d5734a85ae9 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -168,7 +168,7 @@ def test_agent_remove(self): x, y = agent.pos self.grid.remove_agent(agent) assert agent.pos is None - assert self.grid.grid[x][y] is None + assert self.grid[x][y] is None def test_swap_pos(self): @@ -510,7 +510,7 @@ class TestIndexing: # Create a grid where the content of each coordinate is a tuple of its coordinates grid = Grid(3, 5, True) for _, x, y in grid.coord_iter(): - grid.grid[x][y] = (x, y) + grid._grid[x][y] = (x, y) def test_int(self): assert self.grid[0][0] == (0, 0) diff --git a/tests/test_space.py b/tests/test_space.py index 37a6f691444..ba04e4125a7 100644 --- a/tests/test_space.py +++ b/tests/test_space.py @@ -302,10 +302,10 @@ def test_remove_agent(self): for i, pos in enumerate(TEST_AGENTS_GRID): a = self.agents[i] assert a.pos == pos - assert self.space.grid[pos[0]][pos[1]] == a + assert self.space[pos[0]][pos[1]] == a self.space.remove_agent(a) assert a.pos is None - assert self.space.grid[pos[0]][pos[1]] is None + assert self.space[pos[0]][pos[1]] is None def test_empty_cells(self): if self.space.exists_empty_cells(): @@ -325,12 +325,12 @@ def move_agent(self): _agent = self.agents[agent_number] assert _agent.pos == initial_pos - assert self.space.grid[initial_pos[0]][initial_pos[1]] == _agent - assert self.space.grid[final_pos[0]][final_pos[1]] is None + assert self.space[initial_pos[0]][initial_pos[1]] == _agent + assert self.space[final_pos[0]][final_pos[1]] is None self.space.move_agent(_agent, final_pos) assert _agent.pos == final_pos - assert self.space.grid[initial_pos[0]][initial_pos[1]] is None - assert self.space.grid[final_pos[0]][final_pos[1]] == _agent + assert self.space[initial_pos[0]][initial_pos[1]] is None + assert self.space[final_pos[0]][final_pos[1]] == _agent class TestSingleNetworkGrid(unittest.TestCase): From c50bb1b003e065ade9fb3386cda17c788da79579 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Jan 2023 02:17:28 +0000 Subject: [PATCH 046/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/psf/black: 22.10.0 → 22.12.0](https://github.com/psf/black/compare/22.10.0...22.12.0) - [github.com/asottile/pyupgrade: v3.3.0 → v3.3.1](https://github.com/asottile/pyupgrade/compare/v3.3.0...v3.3.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2285b3af89a..0b70964c153 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,12 +4,12 @@ ci: repos: - repo: https://github.com/psf/black - rev: 22.10.0 + rev: 22.12.0 hooks: - id: black-jupyter exclude: ^mesa/cookiecutter-mesa/ - repo: https://github.com/asottile/pyupgrade - rev: v3.3.0 + rev: v3.3.1 hooks: - id: pyupgrade args: [--py38-plus] From a384a0dfd44ae869017d127ef3049c2cffc91cbb Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Wed, 4 Jan 2023 11:04:18 +0100 Subject: [PATCH 047/214] Make Grid class private (#1575) --- mesa/space.py | 77 ++++++++++++++++++++----------------- tests/test_grid.py | 39 +++++++++---------- tests/test_visualization.py | 4 +- 3 files changed, 62 insertions(+), 58 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index aea5ff6235d..7de66b44a0c 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -79,11 +79,11 @@ def is_integer(x: Real) -> bool: return isinstance(x, (int, np.integer)) -class Grid: +class _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 + 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 toroidal, the top and bottom, and left and right, edges wrap to each other Properties: @@ -423,6 +423,12 @@ def get_cell_list_contents(self, cell_list: Iterable[Coordinate]) -> list[Agent] """ return list(self.iter_cell_list_contents(cell_list)) + def place_agent(self, agent: Agent, pos: Coordinate) -> None: + ... + + def remove_agent(self, agent: Agent) -> None: + ... + def move_agent(self, agent: Agent, pos: Coordinate) -> None: """Move an agent from its current position to a new position. @@ -435,24 +441,6 @@ def move_agent(self, agent: Agent, pos: Coordinate) -> None: self.remove_agent(agent) self.place_agent(agent, pos) - def place_agent(self, agent: Agent, pos: Coordinate) -> None: - """Place the agent at the specified location, and set its pos variable.""" - x, y = pos - self._grid[x][y] = agent - if self._empties_built: - self._empties.discard(pos) - agent.pos = pos - - def remove_agent(self, agent: Agent) -> None: - """Remove the agent from the grid and set its pos attribute to None.""" - if (pos := agent.pos) is None: - return - x, y = pos - self._grid[x][y] = self.default_val() - if self._empties_built: - self._empties.add(pos) - agent.pos = None - def swap_pos(self, agent_a: Agent, agent_b: Agent) -> None: """Swap agents positions""" agents_no_pos = [] @@ -537,8 +525,17 @@ def exists_empty_cells(self) -> bool: return len(self.empties) > 0 -class SingleGrid(Grid): - """Grid where each cell contains exactly at most one object.""" +class SingleGrid(_Grid): + """Rectangular grid where each cell contains exactly at most one agent. + + 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 + toroidal, the top and bottom, and left and right, edges wrap to each other. + + Properties: + width, height: The grid's width and height. + torus: Boolean which determines whether to treat the grid as a torus. + """ def position_agent( self, agent: Agent, x: int | str = "random", y: int | str = "random" @@ -577,27 +574,37 @@ def position_agent( self.place_agent(agent, coords) def place_agent(self, agent: Agent, pos: Coordinate) -> None: + """Place the agent at the specified location, and set its pos variable.""" if self.is_cell_empty(pos): - super().place_agent(agent, pos) + x, y = pos + self._grid[x][y] = agent + if self._empties_built: + self._empties.discard(pos) + agent.pos = pos else: raise Exception("Cell not empty") + def remove_agent(self, agent: Agent) -> None: + """Remove the agent from the grid and set its pos attribute to None.""" + if (pos := agent.pos) is None: + return + x, y = pos + self._grid[x][y] = self.default_val() + if self._empties_built: + self._empties.add(pos) + agent.pos = None -class MultiGrid(Grid): - """Grid where each cell can contain more than one object. - Grid cells are indexed by [x][y], where [0][0] is assumed to be at - bottom-left and [width-1][height-1] is the top-right. If a grid is - toroidal, the top and bottom, and left and right, edges wrap to each other. +class MultiGrid(_Grid): + """Rectangular grid where each cell can contain more than one agent. - Each grid cell holds a set object. + Grid cells are indexed by [x, y], where [0, 0] is assumed to be at + bottom-left and [width-1, height-1] is the top-right. If a grid is + toroidal, the top and bottom, and left and right, edges wrap to each other. Properties: width, height: The grid's width and height. torus: Boolean which determines whether to treat the grid as a torus. - - Methods: - get_neighbors: Returns the objects surrounding a given cell. """ grid: list[list[MultiGridContent]] @@ -643,8 +650,8 @@ def iter_cell_list_contents( ) -class HexGrid(Grid): - """Hexagonal Grid: Extends Grid to handle hexagonal neighbors. +class HexGrid(SingleGrid): + """Hexagonal Grid: Extends SingleGrid to handle hexagonal neighbors. Functions according to odd-q rules. See http://www.redblobgames.com/grids/hexagons/#coordinates for more. diff --git a/tests/test_grid.py b/tests/test_grid.py index d5734a85ae9..c9d27e81d9b 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -4,7 +4,7 @@ import random import unittest from unittest.mock import patch, Mock -from mesa.space import Grid, SingleGrid, MultiGrid, HexGrid +from mesa.space import SingleGrid, MultiGrid, HexGrid # Initial agent positions for testing # @@ -29,9 +29,9 @@ def __init__(self, unique_id, pos): self.pos = pos -class TestBaseGrid(unittest.TestCase): +class TestSingleGrid(unittest.TestCase): """ - Testing a non-toroidal grid. + Testing a non-toroidal singlegrid. """ torus = False @@ -43,7 +43,7 @@ def setUp(self): # 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 - self.grid = Grid(width, height, self.torus) + self.grid = SingleGrid(width, height, self.torus) self.agents = [] counter = 0 for x in range(width): @@ -149,8 +149,8 @@ def test_coord_iter(self): def test_agent_move(self): # get the agent at [0, 1] agent = self.agents[0] - self.grid.move_agent(agent, (1, 1)) - assert agent.pos == (1, 1) + self.grid.move_agent(agent, (1, 0)) + assert agent.pos == (1, 0) # move it off the torus and check for the exception if not self.torus: with self.assertRaises(Exception): @@ -158,10 +158,10 @@ def test_agent_move(self): with self.assertRaises(Exception): self.grid.move_agent(agent, [1, self.grid.height + 1]) else: - self.grid.move_agent(agent, [-1, 1]) - assert agent.pos == (self.grid.width - 1, 1) - self.grid.move_agent(agent, [1, self.grid.height + 1]) - assert agent.pos == (1, 1) + self.grid.move_agent(agent, [0, -1]) + assert agent.pos == (0, self.grid.height - 1) + self.grid.move_agent(agent, [1, self.grid.height]) + assert agent.pos == (1, 0) def test_agent_remove(self): agent = self.agents[0] @@ -201,9 +201,9 @@ def test_swap_pos(self): self.grid.swap_pos(agent_a, agent_b) -class TestBaseGridTorus(TestBaseGrid): +class TestSingleGridTorus(TestSingleGrid): """ - Testing the toroidal base grid. + Testing the toroidal singlegrid. """ torus = True @@ -243,12 +243,9 @@ def test_neighbors(self): assert len(neighbors) == 3 -class TestSingleGrid(unittest.TestCase): +class TestSingleGridEnforcement(unittest.TestCase): """ - Test the SingleGrid object. - - Since it inherits from Grid, all the functionality tested above should - work here too. Instead, this tests the enforcement. + Test the enforcement in SingleGrid. """ def setUp(self): @@ -402,7 +399,7 @@ def test_neighbors(self): class TestHexGrid(unittest.TestCase): """ - Testing a hexagonal grid. + Testing a hexagonal singlegrid. """ def setUp(self): @@ -455,9 +452,9 @@ def test_neighbors(self): assert sum(x + y for x, y in neighborhood) == 39 -class TestHexGridTorus(TestBaseGrid): +class TestHexGridTorus(TestSingleGrid): """ - Testing a hexagonal toroidal grid. + Testing a hexagonal toroidal singlegrid. """ torus = True @@ -508,7 +505,7 @@ def test_neighbors(self): class TestIndexing: # Create a grid where the content of each coordinate is a tuple of its coordinates - grid = Grid(3, 5, True) + grid = SingleGrid(3, 5, True) for _, x, y in grid.coord_iter(): grid._grid[x][y] = (x, y) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 3f33bee92fe..baeceaab069 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -2,7 +2,7 @@ from collections import defaultdict from mesa.model import Model -from mesa.space import Grid +from mesa.space import MultiGrid from mesa.time import SimultaneousActivation from mesa.visualization.ModularVisualization import ModularServer from mesa.visualization.modules import CanvasGrid, TextElement @@ -21,7 +21,7 @@ def __init__(self, width, height, key1=103, key2=104): self.key1 = (key1,) self.key2 = key2 self.schedule = SimultaneousActivation(self) - self.grid = Grid(width, height, torus=True) + self.grid = MultiGrid(width, height, torus=True) for (c, x, y) in self.grid.coord_iter(): a = MockAgent(x + y * 100, self, x * y * 3) From c7b2ccf818047b99da690493f807b21a0080df9a Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Thu, 5 Jan 2023 11:46:07 +0100 Subject: [PATCH 048/214] perf: Use filterfalse for iter_cell_list_contents (#1570) Improvements for: - `MultiGrid`: ~1.8x - `NetworkGrid`: ~1.3x For _Grid, it is a 2x drop in performance, because we have to use `is_cell_empty`, to make it consistent with the other classes. --- mesa/space.py | 41 +++++++++++++++++++++-------------------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 7de66b44a0c..a63426d2d77 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -397,29 +397,31 @@ def out_of_bounds(self, pos: Coordinate) -> bool: def iter_cell_list_contents( self, cell_list: Iterable[Coordinate] ) -> Iterator[Agent]: - """Returns an iterator of the contents of the cells - identified in cell_list. + """Returns an iterator of the agents contained in the cells identified + in `cell_list`; cells with empty content are excluded. Args: cell_list: Array-like of (x, y) tuples, or single tuple. Returns: - An iterator of the contents of the cells identified in cell_list + An iterator of the agents contained in the cells identified 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]) + return ( + self._grid[x][y] + for x, y in itertools.filterfalse(self.is_cell_empty, cell_list) + ) @accept_tuple_argument def get_cell_list_contents(self, cell_list: Iterable[Coordinate]) -> list[Agent]: - """Returns a list of the contents of the cells - identified in cell_list. - Note: this method returns a list of `Agent`'s; `None` contents are excluded. + """Returns an iterator of the agents contained in the cells identified + in `cell_list`; cells with empty content are excluded. Args: cell_list: Array-like of (x, y) tuples, or single tuple. Returns: - A list of the contents of the cells identified in cell_list + A list of the agents contained in the cells identified in `cell_list`. """ return list(self.iter_cell_list_contents(cell_list)) @@ -635,18 +637,19 @@ def remove_agent(self, agent: Agent) -> None: @accept_tuple_argument def iter_cell_list_contents( self, cell_list: Iterable[Coordinate] - ) -> Iterator[MultiGridContent]: - """Returns an iterator of the contents of the - cells identified in cell_list. + ) -> Iterator[Agent]: + """Returns an iterator of the agents contained in the cells identified + in `cell_list`; cells with empty content are excluded. Args: cell_list: Array-like of (x, y) tuples, or single tuple. Returns: - A iterator of the contents of the cells identified in cell_list + An iterator of the agents contained in 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)) + self._grid[x][y] + for x, y in itertools.filterfalse(self.is_cell_empty, cell_list) ) @@ -1074,12 +1077,7 @@ 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. """ - 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] + return list(self.iter_cell_list_contents(cell_list)) def get_all_cell_contents(self) -> list[Agent]: """Returns a list of all the agents in the network.""" @@ -1089,4 +1087,7 @@ 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. """ - yield from self.get_cell_list_contents(cell_list) + return itertools.chain.from_iterable( + self.G.nodes[node_id]["agent"] + for node_id in itertools.filterfalse(self.is_cell_empty, cell_list) + ) From e3cc4d4877ace362b204aadc06d6a44b03d59d11 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Sat, 21 Jan 2023 05:44:50 +0100 Subject: [PATCH 049/214] perf: Change index at DF creation in DataCollector.get_agent_vars_dataframe (#1586) --- mesa/datacollection.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index ba25c80eec0..78a2b8e7f9d 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -232,8 +232,8 @@ def get_agent_vars_dataframe(self): df = pd.DataFrame.from_records( data=all_records, columns=["Step", "AgentID"] + rep_names, + index=["Step", "AgentID"], ) - df = df.set_index(["Step", "AgentID"]) return df def get_table_dataframe(self, table_name): From 5cd08ce9a0bab4dde31da3e6adb994dcd55380b0 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Tue, 24 Jan 2023 01:50:18 +0100 Subject: [PATCH 050/214] Remove _reporter_decorator --- mesa/datacollection.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index 78a2b8e7f9d..eb59874e815 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -163,9 +163,6 @@ def get_reports(agent): agent_records = map(get_reports, model.schedule.agents) return agent_records - def _reporter_decorator(self, reporter): - return reporter() - def collect(self, model): """Collect all the data for the given model object.""" if self.model_reporters: @@ -181,7 +178,7 @@ def collect(self, model): elif isinstance(reporter, list): self.model_vars[var].append(reporter[0](*reporter[1])) else: - self.model_vars[var].append(self._reporter_decorator(reporter)) + self.model_vars[var].append(reporter()) if self.agent_reporters: agent_records = self._record_agents(model) From 64ce41627dfba200451c8b43f13611568b10660c Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Tue, 24 Jan 2023 00:54:51 +0100 Subject: [PATCH 051/214] perf: Use getattr for attribute strings in model data collection --- mesa/datacollection.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index eb59874e815..6295921d84c 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -117,8 +117,6 @@ def _new_model_reporter(self, name, reporter): reporter: Attribute string, or function object that returns the variable when given a model instance. """ - if type(reporter) is str: - reporter = partial(self._getattr, reporter) self.model_reporters[name] = reporter self.model_vars[name] = [] @@ -172,8 +170,8 @@ def collect(self, model): if isinstance(reporter, types.LambdaType): self.model_vars[var].append(reporter(model)) # Check if model attribute - elif isinstance(reporter, partial): - self.model_vars[var].append(reporter(model)) + elif isinstance(reporter, str): + self.model_vars[var].append(getattr(model, reporter, None)) # Check if function with arguments elif isinstance(reporter, list): self.model_vars[var].append(reporter[0](*reporter[1])) From 8e9617e8b5e85e31a3774a0ea0bcfb9050444dd1 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Fri, 27 Jan 2023 03:14:40 +0100 Subject: [PATCH 052/214] doc: Clarify docstring of DataCollector (#1592) * Fix bug in reporting @property decorated functions in collect * Update datacollection.py * Update datacollection.py * Update datacollection.py * Update datacollection.py * Update datacollection.py * Update datacollection.py --- mesa/datacollection.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index 6295921d84c..868cd32be39 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -83,9 +83,10 @@ def __init__(self, model_reporters=None, agent_reporters=None, tables=None): Model reporters can take four types of arguments: lambda like above: {"agent_count": lambda m: m.schedule.get_agent_count() } - method with @property decorators - {"agent_count": schedule.get_agent_count() - class attributes of model + method of a class/instance: + {"agent_count": self.get_agent_count} # self here is a class instance + {"agent_count": Model.get_agent_count} # Model here is a class + class attributes of a model {"model_attribute": "model_attribute"} functions with parameters that have placed in a list {"Model_Function":[function, [param_1, param_2]]} @@ -175,6 +176,8 @@ def collect(self, model): # Check if function with arguments elif isinstance(reporter, list): self.model_vars[var].append(reporter[0](*reporter[1])) + # TODO: Check if method of a class, as of now it is assumed + # implicitly if the other checks fail. else: self.model_vars[var].append(reporter()) From 912dfbf73088fbe0796a68e629f425e100a202bc Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 21 Jan 2023 00:19:02 -0500 Subject: [PATCH 053/214] ci: Replace flake8 with Ruff --- .flake8 | 8 -------- .github/workflows/build_lint.yml | 11 ++++++----- CONTRIBUTING.rst | 6 +++--- mesa/visualization/UserParam.py | 2 +- pyproject.toml | 13 +++++++++++++ setup.py | 2 +- 6 files changed, 24 insertions(+), 18 deletions(-) delete mode 100644 .flake8 create mode 100644 pyproject.toml diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 8e554855083..00000000000 --- a/.flake8 +++ /dev/null @@ -1,8 +0,0 @@ -[flake8] -# Ignore list taken from https://github.com/psf/black/blob/master/.flake8 -# E203 Whitespace before ':' -# E266 Too many leading '#' for block comment -# E501 Line too long (82 > 79 characters) -# W503 Line break occurred before a binary operator -ignore = E203, E266, E501, W503 -exclude = docs, build diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 5d87a4b6880..b8eb7dd0fbb 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -60,7 +60,7 @@ jobs: name: Codecov uses: codecov/codecov-action@v3 - lint-flake: + lint-ruff: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 @@ -68,10 +68,11 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.10" - - run: pip install flake8 - - name: Lint with flake8 - # Use settings from mesas .flake8 file - run: flake8 . --count --show-source --statistics + - run: pip install ruff + - name: Lint with ruff + # Include `--format=github` to enable automatic inline annotations. + # Use settings from pyproject.toml. + run: ruff . --format=github --extend-exclude mesa/cookiecutter-mesa/* lint-black: runs-on: ubuntu-latest diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index 5ed0feef7e0..f65b7baa1c0 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -68,11 +68,11 @@ If you're changing previous Mesa features, please make sure of the following: - Additional features or rewrites of current features are accompanied by tests. - New features are demonstrated in a model, so folks can understand more easily. -To ensure that your submission will not break the build, you will need to install Flake8 and pytest. +To ensure that your submission will not break the build, you will need to install Ruff and pytest. .. code-block:: bash - pip install flake8 pytest pytest-cov + pip install ruff pytest pytest-cov We test by implementing simple models and through traditional unit tests in the tests/ folder. The following only covers unit tests coverage. Ensure that your test coverage has not gone down. If it has and you need help, we will offer advice on how to structure tests for the contribution. @@ -91,7 +91,7 @@ You should no longer have to worry about code formatting. If still in doubt you .. code-block:: bash - flake8 . --ignore=F403,E501,E123,E128,W504,W503 --exclude=docs,build + ruff . .. _`PEP8` : https://www.python.org/dev/peps/pep-0008 diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py index 0a1c6ac7560..b7180006585 100644 --- a/mesa/visualization/UserParam.py +++ b/mesa/visualization/UserParam.py @@ -92,7 +92,7 @@ def __init__( valid = True if self.param_type == self.NUMBER: - valid = not (self.value is None) + valid = self.value is not None elif self.param_type == self.SLIDER: valid = not ( diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000000..dcc4868cd50 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,13 @@ +[tool.ruff] +# Ignore list taken from https://github.com/psf/black/blob/master/.flake8 +# E203 Whitespace before ':' +# E266 Too many leading '#' for block comment +# E501 Line too long (82 > 79 characters) +# W503 Line break occurred before a binary operator +# But we don't specify them because ruff's Black already +# checks for it. +# See https://github.com/charliermarsh/ruff/issues/1842#issuecomment-1381210185 +extend-ignore = ["E501"] +extend-exclude = ["docs", "build"] +# Hardcode to Python 3.10. +target-version = "py310" diff --git a/setup.py b/setup.py index 8a59a0f83f2..df49210c0a0 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,7 @@ requires = ["click", "cookiecutter", "networkx", "numpy", "pandas", "tornado", "tqdm"] extras_require = { - "dev": ["black", "coverage", "flake8", "pytest >= 4.6", "pytest-cov", "sphinx"], + "dev": ["black", "ruff", "coverage", "pytest >= 4.6", "pytest-cov", "sphinx"], "docs": ["sphinx", "ipython"], } From 4c113697dabdab23f1d05d8513c6c51c6ce61d6a Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 21 Jan 2023 00:43:58 -0500 Subject: [PATCH 054/214] ruff: Add isort --- mesa/__init__.py | 11 +++++------ mesa/agent.py | 3 ++- mesa/batchrunner.py | 2 +- .../{{cookiecutter.snake}}/setup.py | 2 +- mesa/datacollection.py | 5 +++-- mesa/main.py | 5 +++-- mesa/model.py | 4 ++-- mesa/space.py | 17 ++++++++--------- mesa/time.py | 2 +- mesa/visualization/ModularVisualization.py | 9 +++++---- mesa/visualization/UserParam.py | 3 +-- .../modules/BarChartVisualization.py | 3 ++- .../modules/CanvasGridVisualization.py | 1 + .../visualization/modules/ChartVisualization.py | 3 ++- .../modules/HexGridVisualization.py | 1 + .../modules/NetworkVisualization.py | 2 +- .../modules/PieChartVisualization.py | 3 ++- pyproject.toml | 1 + setup.py | 8 ++++---- tests/test_batchrunner.py | 7 +++---- tests/test_batchrunnerMP.py | 8 ++++---- tests/test_datacollector.py | 2 +- tests/test_examples.py | 6 +++--- tests/test_grid.py | 5 +++-- tests/test_import_namespace.py | 1 - tests/test_lifespan.py | 7 ++++--- tests/test_main.py | 1 + tests/test_scaffold.py | 3 ++- tests/test_space.py | 5 +---- tests/test_time.py | 7 ++++--- tests/test_tornado.py | 6 ++++-- tests/test_usersettableparam.py | 7 ++++--- tests/test_visualization.py | 3 +-- 33 files changed, 81 insertions(+), 72 deletions(-) diff --git a/mesa/__init__.py b/mesa/__init__.py index 56bf8a310e7..596e28f84d8 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -5,14 +5,13 @@ """ import datetime -from mesa.model import Model -from mesa.agent import Agent - -import mesa.time as time -import mesa.space as space import mesa.flat.visualization as visualization -from mesa.datacollection import DataCollector +import mesa.space as space +import mesa.time as time +from mesa.agent import Agent from mesa.batchrunner import batch_run # noqa +from mesa.datacollection import DataCollector +from mesa.model import Model __all__ = [ "Model", diff --git a/mesa/agent.py b/mesa/agent.py index 51c2f1fe32c..e4347c42f82 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -7,9 +7,10 @@ # Remove this __future__ import once the oldest supported Python is 3.10 from __future__ import annotations +from random import Random + # mypy from typing import TYPE_CHECKING -from random import Random if TYPE_CHECKING: # We ensure that these are not imported during runtime to prevent cyclic diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index e856b097700..d3939f88a3c 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -10,7 +10,6 @@ from functools import partial from itertools import count, product from multiprocessing import Pool, cpu_count -from warnings import warn from typing import ( Any, Dict, @@ -22,6 +21,7 @@ Type, Union, ) +from warnings import warn import pandas as pd from tqdm import tqdm diff --git a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py index 64023f8cb18..a72014b21de 100644 --- a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py +++ b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -from setuptools import setup, find_packages +from setuptools import find_packages, setup requires = ["mesa"] diff --git a/mesa/datacollection.py b/mesa/datacollection.py index 868cd32be39..f2ada29644d 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -34,11 +34,12 @@ * 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 +import types +from functools import partial from operator import attrgetter + import pandas as pd -import types class DataCollector: diff --git a/mesa/main.py b/mesa/main.py index 51f0639ac16..1537e885302 100644 --- a/mesa/main.py +++ b/mesa/main.py @@ -1,8 +1,9 @@ -import sys import os -import click +import sys from subprocess import call +import click + PROJECT_PATH = click.Path( exists=True, file_okay=False, dir_okay=True, resolve_path=True ) diff --git a/mesa/model.py b/mesa/model.py index 0851a4ea1d6..5225f0668fd 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -9,11 +9,11 @@ import random -from mesa.datacollection import DataCollector - # mypy from typing import Any +from mesa.datacollection import DataCollector + class Model: """Base class for models.""" diff --git a/mesa/space.py b/mesa/space.py index a63426d2d77..3bbd45df3ee 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -20,20 +20,16 @@ # Remove this __future__ import once the oldest supported Python is 3.10 from __future__ import annotations -import itertools import collections +import itertools import math -from warnings import warn - -import numpy as np -import networkx as nx - +from numbers import Real from typing import ( Any, Callable, - List, Iterable, Iterator, + List, Sequence, Tuple, TypeVar, @@ -41,11 +37,14 @@ cast, overload, ) +from warnings import warn + +import networkx as nx +import numpy as np +import numpy.typing as npt # For Mypy from .agent import Agent -from numbers import Real -import numpy.typing as npt Coordinate = Tuple[int, int] # used in ContinuousSpace diff --git a/mesa/time.py b/mesa/time.py index 4c332e3e317..7982eeecacb 100644 --- a/mesa/time.py +++ b/mesa/time.py @@ -29,10 +29,10 @@ # mypy from typing import Iterator, Union + from mesa.agent import Agent from mesa.model import Model - # BaseScheduler has a self.time of int, while # StagedActivation has a self.time of float TimeT = Union[float, int] diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py index 6345db2489d..d723a50b1e6 100644 --- a/mesa/visualization/ModularVisualization.py +++ b/mesa/visualization/ModularVisualization.py @@ -97,15 +97,16 @@ import asyncio import os import platform +import webbrowser + import tornado.autoreload +import tornado.escape +import tornado.gen import tornado.ioloop import tornado.web import tornado.websocket -import tornado.escape -import tornado.gen -import webbrowser -from mesa.visualization.UserParam import UserSettableParameter, UserParam +from mesa.visualization.UserParam import UserParam, UserSettableParameter # Suppress several pylint warnings for this file. # Attributes being defined outside of init is a Tornado feature. diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py index b7180006585..f08fd8e9440 100644 --- a/mesa/visualization/UserParam.py +++ b/mesa/visualization/UserParam.py @@ -1,6 +1,5 @@ -from warnings import warn import numbers - +from warnings import warn NUMBER = "number" CHECKBOX = "checkbox" diff --git a/mesa/visualization/modules/BarChartVisualization.py b/mesa/visualization/modules/BarChartVisualization.py index a0c3cedbe52..bec182bdd66 100644 --- a/mesa/visualization/modules/BarChartVisualization.py +++ b/mesa/visualization/modules/BarChartVisualization.py @@ -5,7 +5,8 @@ Module for drawing live-updating bar charts using d3.js """ import json -from mesa.visualization.ModularVisualization import VisualizationElement, D3_JS_FILE + +from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement class BarChartModule(VisualizationElement): diff --git a/mesa/visualization/modules/CanvasGridVisualization.py b/mesa/visualization/modules/CanvasGridVisualization.py index 281cd574de3..b8b4b462c3b 100644 --- a/mesa/visualization/modules/CanvasGridVisualization.py +++ b/mesa/visualization/modules/CanvasGridVisualization.py @@ -5,6 +5,7 @@ Module for visualizing model objects in grid cells. """ from collections import defaultdict + from mesa.visualization.ModularVisualization import VisualizationElement diff --git a/mesa/visualization/modules/ChartVisualization.py b/mesa/visualization/modules/ChartVisualization.py index fc6ee1fefa5..8f356fa4826 100644 --- a/mesa/visualization/modules/ChartVisualization.py +++ b/mesa/visualization/modules/ChartVisualization.py @@ -5,7 +5,8 @@ Module for drawing live-updating line charts using Charts.js """ import json -from mesa.visualization.ModularVisualization import VisualizationElement, CHART_JS_FILE + +from mesa.visualization.ModularVisualization import CHART_JS_FILE, VisualizationElement class ChartModule(VisualizationElement): diff --git a/mesa/visualization/modules/HexGridVisualization.py b/mesa/visualization/modules/HexGridVisualization.py index ddd26a97aa5..8e318bafbb0 100644 --- a/mesa/visualization/modules/HexGridVisualization.py +++ b/mesa/visualization/modules/HexGridVisualization.py @@ -5,6 +5,7 @@ Module for visualizing model objects in hexagonal grid cells. """ from collections import defaultdict + from mesa.visualization.ModularVisualization import VisualizationElement diff --git a/mesa/visualization/modules/NetworkVisualization.py b/mesa/visualization/modules/NetworkVisualization.py index 85a353062fd..fa0ea61db39 100644 --- a/mesa/visualization/modules/NetworkVisualization.py +++ b/mesa/visualization/modules/NetworkVisualization.py @@ -4,7 +4,7 @@ Module for rendering the network, using [d3.js](https://d3js.org/) framework. """ -from mesa.visualization.ModularVisualization import VisualizationElement, D3_JS_FILE +from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement class NetworkModule(VisualizationElement): diff --git a/mesa/visualization/modules/PieChartVisualization.py b/mesa/visualization/modules/PieChartVisualization.py index 671d53cb688..b976292203b 100644 --- a/mesa/visualization/modules/PieChartVisualization.py +++ b/mesa/visualization/modules/PieChartVisualization.py @@ -5,7 +5,8 @@ Module for drawing live-updating pie charts using d3.js """ import json -from mesa.visualization.ModularVisualization import VisualizationElement, D3_JS_FILE + +from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement class PieChartModule(VisualizationElement): diff --git a/pyproject.toml b/pyproject.toml index dcc4868cd50..6a5c4e33050 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,4 +1,5 @@ [tool.ruff] +select = ["E", "F", "I"] # Ignore list taken from https://github.com/psf/black/blob/master/.flake8 # E203 Whitespace before ':' # E266 Too many leading '#' for block comment diff --git a/setup.py b/setup.py index df49210c0a0..6e2292dd824 100644 --- a/setup.py +++ b/setup.py @@ -1,13 +1,13 @@ #!/usr/bin/env python -import re import os +import re +import shutil import urllib.request import zipfile -import shutil - -from setuptools import setup, find_packages from codecs import open +from setuptools import find_packages, setup + requires = ["click", "cookiecutter", "networkx", "numpy", "pandas", "tornado", "tqdm"] extras_require = { diff --git a/tests/test_batchrunner.py b/tests/test_batchrunner.py index 2bc7dc6f9d6..761598b477c 100644 --- a/tests/test_batchrunner.py +++ b/tests/test_batchrunner.py @@ -1,20 +1,19 @@ """ Test the BatchRunner """ +import unittest from functools import reduce from operator import mul -import unittest from mesa import Agent, Model -from mesa.time import BaseScheduler -from mesa.datacollection import DataCollector from mesa.batchrunner import ( BatchRunner, FixedBatchRunner, ParameterProduct, ParameterSampler, ) - +from mesa.datacollection import DataCollector +from mesa.time import BaseScheduler NUM_AGENTS = 7 diff --git a/tests/test_batchrunnerMP.py b/tests/test_batchrunnerMP.py index d6ff3001eae..935e61ed505 100644 --- a/tests/test_batchrunnerMP.py +++ b/tests/test_batchrunnerMP.py @@ -1,15 +1,15 @@ """ Test the BatchRunner """ +import unittest from functools import reduce +from multiprocessing import cpu_count, freeze_support from operator import mul -import unittest from mesa import Agent, Model -from mesa.time import BaseScheduler -from mesa.datacollection import DataCollector from mesa.batchrunner import BatchRunnerMP, ParameterProduct, ParameterSampler -from multiprocessing import freeze_support, cpu_count +from mesa.datacollection import DataCollector +from mesa.time import BaseScheduler NUM_AGENTS = 7 diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py index 0f2bd4c0833..565f9e95a19 100644 --- a/tests/test_datacollector.py +++ b/tests/test_datacollector.py @@ -3,7 +3,7 @@ """ import unittest -from mesa import Model, Agent +from mesa import Agent, Model from mesa.time import BaseScheduler diff --git a/tests/test_examples.py b/tests/test_examples.py index 73285b5ce35..bb2c31c30f1 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -1,8 +1,8 @@ -import sys -import os.path -import unittest import contextlib import importlib +import os.path +import sys +import unittest def classcase(name): diff --git a/tests/test_grid.py b/tests/test_grid.py index c9d27e81d9b..e5148246fae 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -3,8 +3,9 @@ """ import random import unittest -from unittest.mock import patch, Mock -from mesa.space import SingleGrid, MultiGrid, HexGrid +from unittest.mock import Mock, patch + +from mesa.space import HexGrid, MultiGrid, SingleGrid # Initial agent positions for testing # diff --git a/tests/test_import_namespace.py b/tests/test_import_namespace.py index e405dc8f710..09c8162d26a 100644 --- a/tests/test_import_namespace.py +++ b/tests/test_import_namespace.py @@ -3,7 +3,6 @@ def test_import(): # https://github.com/projectmesa/mesa/pull/1294. import mesa import mesa.flat as mf - from mesa.time import RandomActivation mesa.time.RandomActivation diff --git a/tests/test_lifespan.py b/tests/test_lifespan.py index 280b8beeb83..cfd60cdeb74 100644 --- a/tests/test_lifespan.py +++ b/tests/test_lifespan.py @@ -1,10 +1,11 @@ import unittest -from mesa.time import RandomActivation -from mesa.datacollection import DataCollector -from mesa import Model, Agent import numpy as np +from mesa import Agent, Model +from mesa.datacollection import DataCollector +from mesa.time import RandomActivation + class LifeTimeModel(Model): """Simple model for running models with a finite life""" diff --git a/tests/test_main.py b/tests/test_main.py index 6aa385028c7..730a2189619 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -2,6 +2,7 @@ import sys import unittest from unittest.mock import patch + from click.testing import CliRunner from mesa.main import cli diff --git a/tests/test_scaffold.py b/tests/test_scaffold.py index b2ed1130552..627552eb0eb 100644 --- a/tests/test_scaffold.py +++ b/tests/test_scaffold.py @@ -1,5 +1,6 @@ -import unittest import os +import unittest + from click.testing import CliRunner from mesa.main import cli diff --git a/tests/test_space.py b/tests/test_space.py index ba04e4125a7..765c3662a4d 100644 --- a/tests/test_space.py +++ b/tests/test_space.py @@ -4,12 +4,9 @@ import numpy as np import pytest -from mesa.space import ContinuousSpace -from mesa.space import SingleGrid -from mesa.space import NetworkGrid +from mesa.space import ContinuousSpace, NetworkGrid, SingleGrid from tests.test_grid import MockAgent - TEST_AGENTS = [(-20, -20), (-20, -20.05), (65, 18)] TEST_AGENTS_GRID = [(1, 1), (10, 0), (10, 10)] TEST_AGENTS_NETWORK_SINGLE = [0, 1, 5] diff --git a/tests/test_time.py b/tests/test_time.py index 7bf198e935c..903d3f2d207 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -4,13 +4,14 @@ import unittest from unittest import TestCase, mock -from mesa import Model, Agent + +from mesa import Agent, Model from mesa.time import ( BaseScheduler, - StagedActivation, RandomActivation, - SimultaneousActivation, RandomActivationByType, + SimultaneousActivation, + StagedActivation, ) RANDOM = "random" diff --git a/tests/test_tornado.py b/tests/test_tornado.py index e0f50cf52f3..24025a586be 100644 --- a/tests/test_tornado.py +++ b/tests/test_tornado.py @@ -1,8 +1,10 @@ -from tornado.testing import AsyncHTTPTestCase +import json + import tornado +from tornado.testing import AsyncHTTPTestCase + from mesa import Model from mesa.visualization.ModularVisualization import ModularServer -import json class TestServer(AsyncHTTPTestCase): diff --git a/tests/test_usersettableparam.py b/tests/test_usersettableparam.py index 6dc52774438..9106d050763 100644 --- a/tests/test_usersettableparam.py +++ b/tests/test_usersettableparam.py @@ -1,11 +1,12 @@ from unittest import TestCase + from mesa.visualization.UserParam import ( - UserSettableParameter, - Slider, Checkbox, Choice, - StaticText, NumberInput, + Slider, + StaticText, + UserSettableParameter, ) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index baeceaab069..76022f013e4 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -1,5 +1,5 @@ -from unittest import TestCase from collections import defaultdict +from unittest import TestCase from mesa.model import Model from mesa.space import MultiGrid @@ -7,7 +7,6 @@ from mesa.visualization.ModularVisualization import ModularServer from mesa.visualization.modules import CanvasGrid, TextElement from mesa.visualization.UserParam import UserSettableParameter - from tests.test_batchrunner import MockAgent From 13079579eb850e55cd64bf5420d7b95058b3412f Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 9 Dec 2022 05:40:34 -0500 Subject: [PATCH 055/214] Remove auto-update GH Actions for Pipfile.lock --- .github/workflows/update-pipfile.yml | 31 ---------------------------- 1 file changed, 31 deletions(-) delete mode 100644 .github/workflows/update-pipfile.yml diff --git a/.github/workflows/update-pipfile.yml b/.github/workflows/update-pipfile.yml deleted file mode 100644 index 59a90d522e3..00000000000 --- a/.github/workflows/update-pipfile.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: "Update Pipfile.lock" -on: - schedule: - - cron: '0 6 1 * *' # 1st day of each month at 06:00 UTC - push: - paths: - - 'Pipfile' - - '.github/workflows/update-pipfile.yml' - -jobs: - piplock: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-python@v4 - with: - python-version: '3.9' - - run: pip install -U pip - - run: pip install -U wheel - - run: pip install -U pipenv - - run: pipenv lock - - uses: actions/upload-artifact@v3 - with: - name: "Pipfile lock" - path: Pipfile.lock - - uses: peter-evans/create-pull-request@v4 - with: - title: "Update Pipfile.lock (dependencies)" - branch: update-pipfile - base: main - commit-message: "[Bot] Update Pipfile.lock dependencies" From cd7b7323e17cd8dc0fac5622e1c2cc13bc916ec8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 05:13:37 +0000 Subject: [PATCH 056/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/psf/black: 22.12.0 → 23.1.0](https://github.com/psf/black/compare/22.12.0...23.1.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 0b70964c153..4714cd84e22 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: - repo: https://github.com/psf/black - rev: 22.12.0 + rev: 23.1.0 hooks: - id: black-jupyter exclude: ^mesa/cookiecutter-mesa/ From a91567b80f260c877231538c5585b12f96222a77 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 7 Feb 2023 05:13:45 +0000 Subject: [PATCH 057/214] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- mesa/batchrunner.py | 1 - mesa/datacollection.py | 1 - mesa/space.py | 4 ---- mesa/visualization/UserParam.py | 1 - tests/test_grid.py | 1 - tests/test_visualization.py | 5 +---- 6 files changed, 1 insertion(+), 12 deletions(-) diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index d3939f88a3c..1f1d31118fe 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -470,7 +470,6 @@ def _prepare_report_table(self, vars_dict, extra_cols=None): ordered.sort_values(by="Run", inplace=True) if self._include_fixed: for param, val in self.fixed_parameters.items(): - # avoid error when val is an iterable vallist = [val for i in range(ordered.shape[0])] ordered[param] = vallist diff --git a/mesa/datacollection.py b/mesa/datacollection.py index f2ada29644d..ae0dfa9b707 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -166,7 +166,6 @@ def get_reports(agent): def collect(self, model): """Collect all the data for the given model object.""" if self.model_reporters: - for var, reporter in self.model_reporters.items(): # Check if Lambda operator if isinstance(reporter, types.LambdaType): diff --git a/mesa/space.py b/mesa/space.py index 3bbd45df3ee..75c54ffd0ec 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -297,7 +297,6 @@ def get_neighborhood( 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 @@ -309,7 +308,6 @@ def get_neighborhood( for nx in x_range: for ny in y_range: - if not moore and abs(nx - x) + abs(ny - y) > radius: continue @@ -708,12 +706,10 @@ def get_neighborhood( coordinates = set() while radius > 0: - level_size = len(queue) radius -= 1 for i in range(level_size): - x, y = queue.pop() if x % 2 == 0: diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py index f08fd8e9440..aec4ebbb56f 100644 --- a/mesa/visualization/UserParam.py +++ b/mesa/visualization/UserParam.py @@ -66,7 +66,6 @@ def __init__( choices=None, description=None, ): - warn( "UserSettableParameter is deprecated in favor of UserParam objects " "such as Slider, Checkbox, Choice, StaticText, NumberInput. " diff --git a/tests/test_grid.py b/tests/test_grid.py index e5148246fae..04ad6750ee0 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -172,7 +172,6 @@ def test_agent_remove(self): assert self.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 diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 76022f013e4..df38f8d456f 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -14,7 +14,6 @@ class MockModel(Model): """Test model for testing""" def __init__(self, width, height, key1=103, key2=104): - self.width = width self.height = height self.key1 = (key1,) @@ -22,7 +21,7 @@ def __init__(self, width, height, key1=103, key2=104): self.schedule = SimultaneousActivation(self) self.grid = MultiGrid(width, height, torus=True) - for (c, x, y) in self.grid.coord_iter(): + for c, x, y in self.grid.coord_iter(): a = MockAgent(x + y * 100, self, x * y * 3) self.grid.place_agent(a, (x, y)) self.schedule.add(a) @@ -47,7 +46,6 @@ def portrayal(self, cell): } def setUp(self): - self.user_params = { "width": 1, "height": 1, @@ -67,7 +65,6 @@ def setUp(self): ) def test_canvas_render_model_state(self): - test_portrayal = self.portrayal(None) test_grid_state = defaultdict(list) test_grid_state[test_portrayal["Layer"]].append(test_portrayal) From 5fea83753ddea75683f703c2ff4c23078c290076 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Thu, 9 Feb 2023 12:40:25 +0100 Subject: [PATCH 058/214] perf: Create tuple for is_integer function before execution (#1597) Co-authored-by: rht --- mesa/space.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesa/space.py b/mesa/space.py index 75c54ffd0ec..d64cabcaf7c 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -46,6 +46,9 @@ # For Mypy from .agent import Agent +# for better performance, we calculate the tuple to use in the is_integer function +_types_integer = (int, np.integer) + Coordinate = Tuple[int, int] # used in ContinuousSpace FloatCoordinate = Union[Tuple[float, float], npt.NDArray[float]] @@ -75,7 +78,7 @@ def wrapper(grid_instance, positions) -> Any: def is_integer(x: Real) -> bool: # Check if x is either a CPython integer or Numpy integer. - return isinstance(x, (int, np.integer)) + return isinstance(x, _types_integer) class _Grid: From 71b4bd45044bc914d8d529d5c08e8fd360de9e79 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 5 Feb 2023 09:46:21 -0500 Subject: [PATCH 059/214] ruff: Set Python 3.8 as earliest target --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 6a5c4e33050..02f1d832a8e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,5 +10,5 @@ select = ["E", "F", "I"] # See https://github.com/charliermarsh/ruff/issues/1842#issuecomment-1381210185 extend-ignore = ["E501"] extend-exclude = ["docs", "build"] -# Hardcode to Python 3.10. -target-version = "py310" +# Hardcode to Python 3.8. +target-version = "py38" From b05a04f4678d8b39742a171babcd99df419ef375 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 30 Jan 2023 02:10:43 -0500 Subject: [PATCH 060/214] ruff: Add more rules based on Zulip's config --- pyproject.toml | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 02f1d832a8e..483423524b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,30 @@ [tool.ruff] -select = ["E", "F", "I"] +# See https://github.com/charliermarsh/ruff#rules for error code definitions. +select = [ + # "ANN", # annotations TODO + "B", # bugbear + "C4", # comprehensions + "DTZ", # naive datetime + "E", # style errors + "F", # flakes + "I", # import sorting + "ISC", # string concatenation + "N", # naming + "PGH", # pygrep-hooks + "PIE", # miscellaneous + "PLC", # pylint convention + "PLE", # pylint error + # "PLR", # pylint refactor TODO + "PLW", # pylint warning + "Q", # quotes + "RUF", # Ruff + "S", # security + "SIM", # simplify + "T10", # debugger + "UP", # upgrade + "W", # style warnings + "YTT", # sys.version +] # Ignore list taken from https://github.com/psf/black/blob/master/.flake8 # E203 Whitespace before ':' # E266 Too many leading '#' for block comment @@ -8,7 +33,12 @@ select = ["E", "F", "I"] # But we don't specify them because ruff's Black already # checks for it. # See https://github.com/charliermarsh/ruff/issues/1842#issuecomment-1381210185 -extend-ignore = ["E501"] +extend-ignore = [ + "E501", + "S101", # Use of `assert` detected + "B017", # `assertRaises(Exception)` should be considered evil TODO + "PGH004", # Use specific rule codes when using `noqa` TODO +] extend-exclude = ["docs", "build"] # Hardcode to Python 3.8. target-version = "py38" From 09cf299825bca2783edfc2882c9ef20c95035a36 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 7 Feb 2023 05:11:27 -0500 Subject: [PATCH 061/214] ruff: Apply autofix --- mesa/__init__.py | 2 +- mesa/agent.py | 1 - mesa/batchrunner.py | 8 ++++---- mesa/datacollection.py | 2 +- mesa/main.py | 1 - mesa/model.py | 1 - mesa/space.py | 4 ++-- mesa/time.py | 2 +- mesa/visualization/UserParam.py | 2 +- tests/test_datacollector.py | 2 +- tests/test_grid.py | 4 ++-- tests/test_visualization.py | 2 +- 12 files changed, 14 insertions(+), 17 deletions(-) diff --git a/mesa/__init__.py b/mesa/__init__.py index 596e28f84d8..aea6e4710fd 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -9,7 +9,7 @@ import mesa.space as space import mesa.time as time from mesa.agent import Agent -from mesa.batchrunner import batch_run # noqa +from mesa.batchrunner import batch_run from mesa.datacollection import DataCollector from mesa.model import Model diff --git a/mesa/agent.py b/mesa/agent.py index e4347c42f82..5a8966cfd3b 100644 --- a/mesa/agent.py +++ b/mesa/agent.py @@ -35,7 +35,6 @@ def __init__(self, unique_id: int, model: Model) -> None: def step(self) -> None: """A single step of the agent.""" - pass def advance(self) -> None: pass diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 1f1d31118fe..338f182dee6 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -357,7 +357,7 @@ def run_iteration(self, kwargs, param_values, run_count): model = self.model_cls(**kwargs) results = self.run_model(model) if param_values is not None: - model_key = tuple(param_values) + (run_count,) + model_key = (*tuple(param_values), run_count) else: model_key = (run_count,) @@ -366,7 +366,7 @@ def run_iteration(self, kwargs, param_values, run_count): if self.agent_reporters: agent_vars = self.collect_agent_vars(model) for agent_id, reports in agent_vars.items(): - agent_key = model_key + (agent_id,) + agent_key = (*model_key, agent_id) self.agent_vars[agent_key] = reports # Collects data from datacollector object in model if results is not None: @@ -466,7 +466,7 @@ def _prepare_report_table(self, vars_dict, extra_cols=None): df = pd.DataFrame(records) rest_cols = set(df.columns) - set(index_cols) - ordered = df[index_cols + list(sorted(rest_cols))] + ordered = df[index_cols + sorted(rest_cols)] ordered.sort_values(by="Run", inplace=True) if self._include_fixed: for param, val in self.fixed_parameters.items(): @@ -713,7 +713,7 @@ def _result_prep_mp(self, results): if self.agent_reporters: agent_vars = self.collect_agent_vars(model) for agent_id, reports in agent_vars.items(): - agent_key = model_key + (agent_id,) + agent_key = (*model_key, agent_id) self.agent_vars[agent_key] = reports if hasattr(model, "datacollector"): if model.datacollector.model_reporters is not None: diff --git a/mesa/datacollection.py b/mesa/datacollection.py index ae0dfa9b707..dd9eb375cf5 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -229,7 +229,7 @@ def get_agent_vars_dataframe(self): df = pd.DataFrame.from_records( data=all_records, - columns=["Step", "AgentID"] + rep_names, + columns=["Step", "AgentID", *rep_names], index=["Step", "AgentID"], ) return df diff --git a/mesa/main.py b/mesa/main.py index 1537e885302..b59b449b46f 100644 --- a/mesa/main.py +++ b/mesa/main.py @@ -15,7 +15,6 @@ @click.group() def cli(): "Manage Mesa projects" - pass @cli.command() diff --git a/mesa/model.py b/mesa/model.py index 5225f0668fd..be6072663bf 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -47,7 +47,6 @@ def run_model(self) -> None: def step(self) -> None: """A single step. Fill in here.""" - pass def next_id(self) -> int: """Return the next unique ID for agents, increment current_id""" diff --git a/mesa/space.py b/mesa/space.py index d64cabcaf7c..2ad406242dc 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -117,7 +117,7 @@ def __init__(self, width: int, height: int, torus: bool) -> None: self._empties_built = False # Neighborhood Cache - self._neighborhood_cache: dict[Any, list[Coordinate]] = dict() + self._neighborhood_cache: dict[Any, list[Coordinate]] = {} @staticmethod def default_val() -> None: @@ -712,7 +712,7 @@ def get_neighborhood( level_size = len(queue) radius -= 1 - for i in range(level_size): + for _i in range(level_size): x, y = queue.pop() if x % 2 == 0: diff --git a/mesa/time.py b/mesa/time.py index 7982eeecacb..b079e1439f0 100644 --- a/mesa/time.py +++ b/mesa/time.py @@ -182,7 +182,7 @@ def __init__( of each step. """ super().__init__(model) - self.stage_list = ["step"] if not stage_list else stage_list + self.stage_list = stage_list if stage_list else ["step"] self.shuffle = shuffle self.shuffle_between_stages = shuffle_between_stages self.stage_time = 1 / len(self.stage_list) diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py index aec4ebbb56f..2fe3d80840c 100644 --- a/mesa/visualization/UserParam.py +++ b/mesa/visualization/UserParam.py @@ -73,7 +73,7 @@ def __init__( "UserSettableParameter will be removed in the next major release." ) if choices is None: - choices = list() + choices = [] if param_type not in self.TYPES: raise ValueError(f"{param_type} is not a valid Option type") self.param_type = param_type diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py index 565f9e95a19..7ba72c73df5 100644 --- a/tests/test_datacollector.py +++ b/tests/test_datacollector.py @@ -148,7 +148,7 @@ def test_table_rows(self): assert len(data_collector.tables["Final_Values"]) == 2 assert "agent_id" in data_collector.tables["Final_Values"] assert "final_value" in data_collector.tables["Final_Values"] - for key, data in data_collector.tables["Final_Values"].items(): + for _key, data in data_collector.tables["Final_Values"].items(): assert len(data) == 9 with self.assertRaises(Exception): diff --git a/tests/test_grid.py b/tests/test_grid.py index 04ad6750ee0..f9eb6dbe6a1 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -305,7 +305,7 @@ def test_enforcement(self, mock_model): # Test whether after placing, the empty cells are reduced by 1 assert a.pos not in self.grid.empties assert len(self.grid.empties) == 8 - for i in range(10): + for _i in range(10): self.grid.move_to_empty(a, num_agents=self.num_agents) assert len(self.grid.empties) == 8 @@ -355,7 +355,7 @@ def setUp(self): counter = 0 for x in range(width): for y in range(height): - for i in range(TEST_MULTIGRID[x][y]): + for _i in range(TEST_MULTIGRID[x][y]): counter += 1 # Create and place the mock agent a = MockAgent(counter, None) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index df38f8d456f..5799131d6de 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -21,7 +21,7 @@ def __init__(self, width, height, key1=103, key2=104): self.schedule = SimultaneousActivation(self) self.grid = MultiGrid(width, height, torus=True) - for c, x, y in self.grid.coord_iter(): + for _c, x, y in self.grid.coord_iter(): a = MockAgent(x + y * 100, self, x * y * 3) self.grid.place_agent(a, (x, y)) self.schedule.add(a) From cea722d81fa7e0bb6b57395e7748055123136e6a Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 30 Jan 2023 02:16:36 -0500 Subject: [PATCH 062/214] Fix Ruff-reported errors manually --- mesa/__init__.py | 3 ++- mesa/main.py | 2 +- mesa/space.py | 6 +++--- mesa/visualization/UserParam.py | 11 +++++------ mesa/visualization/modules/BarChartVisualization.py | 10 +++++----- pyproject.toml | 2 ++ setup.py | 12 ++++++------ tests/test_examples.py | 4 ++-- tests/test_main.py | 2 +- tests/test_space.py | 4 ++-- tests/test_time.py | 8 ++++---- 11 files changed, 33 insertions(+), 31 deletions(-) diff --git a/mesa/__init__.py b/mesa/__init__.py index aea6e4710fd..25054b50ff1 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -26,4 +26,5 @@ __title__ = "mesa" __version__ = "1.1.1" __license__ = "Apache 2.0" -__copyright__ = f"Copyright {datetime.date.today().year} Project Mesa Team" +_this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year +__copyright__ = f"Copyright {_this_year} Project Mesa Team" diff --git a/mesa/main.py b/mesa/main.py index b59b449b46f..9d800cbb5b3 100644 --- a/mesa/main.py +++ b/mesa/main.py @@ -30,7 +30,7 @@ def runserver(project): with open("run.py") as f: code = compile(f.read(), "run.py", "exec") - exec(code, {}, {}) + exec(code, {}, {}) # noqa: S102 @click.command() diff --git a/mesa/space.py b/mesa/space.py index 2ad406242dc..4b03b1e0033 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -1019,15 +1019,15 @@ def out_of_bounds(self, pos: FloatCoordinate) -> bool: class NetworkGrid: """Network Grid where each node contains zero or more agents.""" - def __init__(self, G: Any) -> None: + def __init__(self, g: Any) -> None: """Create a new network. Args: G: a NetworkX graph instance. """ - self.G = G + self.G = g for node_id in self.G.nodes: - G.nodes[node_id]["agent"] = self.default_val() + g.nodes[node_id]["agent"] = self.default_val() @staticmethod def default_val() -> list: diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py index 2fe3d80840c..7fd2103b993 100644 --- a/mesa/visualization/UserParam.py +++ b/mesa/visualization/UserParam.py @@ -121,12 +121,11 @@ def value(self, value): self._value = self.min_value elif self._value > self.max_value: self._value = self.max_value - elif self.param_type == self.CHOICE: - if self._value not in self.choices: - print( - "Selected choice value not in available choices, selected first choice from 'choices' list" - ) - self._value = self.choices[0] + elif (self.param_type == self.CHOICE) and self._value not in self.choices: + print( + "Selected choice value not in available choices, selected first choice from 'choices' list" + ) + self._value = self.choices[0] @property def json(self): diff --git a/mesa/visualization/modules/BarChartVisualization.py b/mesa/visualization/modules/BarChartVisualization.py index bec182bdd66..20fd5e7039c 100644 --- a/mesa/visualization/modules/BarChartVisualization.py +++ b/mesa/visualization/modules/BarChartVisualization.py @@ -75,20 +75,20 @@ def render(self, model): if self.scope == "agent": df = data_collector.get_agent_vars_dataframe().astype("float") latest_step = df.index.levels[0][-1] - labelStrings = [f["Label"] for f in self.fields] - dict = df.loc[latest_step].T.loc[labelStrings].to_dict() + label_strings = [f["Label"] for f in self.fields] + dict = df.loc[latest_step].T.loc[label_strings].to_dict() current_values = list(dict.values()) elif self.scope == "model": - outDict = {} + out_dict = {} for s in self.fields: name = s["Label"] try: val = data_collector.model_vars[name][-1] except (IndexError, KeyError): val = 0 - outDict[name] = val - current_values.append(outDict) + out_dict[name] = val + current_values.append(out_dict) else: raise ValueError("scope must be 'agent' or 'model'") return current_values diff --git a/pyproject.toml b/pyproject.toml index 483423524b1..6583f2196cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -38,6 +38,8 @@ extend-ignore = [ "S101", # Use of `assert` detected "B017", # `assertRaises(Exception)` should be considered evil TODO "PGH004", # Use specific rule codes when using `noqa` TODO + "B905", # `zip()` without an explicit `strict=` parameter + "N802", # Function name should be lowercase ] extend-exclude = ["docs", "build"] # Hardcode to Python 3.8. diff --git a/setup.py b/setup.py index 6e2292dd824..e829de3389b 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ os.makedirs(external_dir_single, exist_ok=True) -def ensure_JS_dep(dirname, url): +def ensure_js_dep(dirname, url): dst_path = os.path.join(external_dir, dirname) if os.path.isdir(dst_path): # Do nothing if already downloaded @@ -50,7 +50,7 @@ def ensure_JS_dep(dirname, url): print("Done") -def ensure_JS_dep_single(url, out_name=None): +def ensure_js_dep_single(url, out_name=None): # Used for downloading e.g. D3.js single file if out_name is None: out_name = url.split("/")[-1] @@ -67,14 +67,14 @@ def ensure_JS_dep_single(url, out_name=None): # Ensure Bootstrap bootstrap_version = "5.1.3" -ensure_JS_dep( +ensure_js_dep( f"bootstrap-{bootstrap_version}-dist", f"https://github.com/twbs/bootstrap/releases/download/v{bootstrap_version}/bootstrap-{bootstrap_version}-dist.zip", ) # Ensure Bootstrap Slider bootstrap_slider_version = "11.0.2" -ensure_JS_dep( +ensure_js_dep( f"bootstrap-slider-{bootstrap_slider_version}", f"https://github.com/seiyria/bootstrap-slider/archive/refs/tags/v{bootstrap_slider_version}.zip", ) @@ -82,14 +82,14 @@ def ensure_JS_dep_single(url, out_name=None): # Important: when updating the D3 version, make sure to update the constant # D3_JS_FILE in mesa/visualization/ModularVisualization.py. d3_version = "7.4.3" -ensure_JS_dep_single( +ensure_js_dep_single( f"https://cdnjs.cloudflare.com/ajax/libs/d3/{d3_version}/d3.min.js", out_name=f"d3-{d3_version}.min.js", ) # Important: Make sure to update CHART_JS_FILE in # mesa/visualization/ModularVisualization.py. chartjs_version = "3.6.1" -ensure_JS_dep_single( +ensure_js_dep_single( f"https://cdn.jsdelivr.net/npm/chart.js@{chartjs_version}/dist/chart.min.js", out_name=f"chart-{chartjs_version}.min.js", ) diff --git a/tests/test_examples.py b/tests/test_examples.py index bb2c31c30f1..1c149da4b75 100644 --- a/tests/test_examples.py +++ b/tests/test_examples.py @@ -62,7 +62,7 @@ def test_examples(self): f"{example.replace('-', '_')}.server" ) server.server.render_model() - Model = getattr(mod, classcase(example)) - model = Model() + model_class = getattr(mod, classcase(example)) + model = model_class() for _ in range(10): model.step() diff --git a/tests/test_main.py b/tests/test_main.py index 730a2189619..9f7f9834626 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -24,7 +24,7 @@ def tearDown(self): "Skipping test_run, because examples folder was moved. More discussion needed." ) def test_run(self): - with patch("mesa.visualization.ModularServer") as ModularServer: + with patch("mesa.visualization.ModularServer") as ModularServer: # noqa: N806 example_dir = os.path.abspath( os.path.join(os.path.dirname(__file__), "../examples/wolf_sheep") ) diff --git a/tests/test_space.py b/tests/test_space.py index 765c3662a4d..a7aba2529f2 100644 --- a/tests/test_space.py +++ b/tests/test_space.py @@ -337,7 +337,7 @@ def setUp(self): """ Create a test network grid and populate with Mock Agents. """ - G = nx.cycle_graph(TestSingleNetworkGrid.GRAPH_SIZE) + G = nx.cycle_graph(TestSingleNetworkGrid.GRAPH_SIZE) # noqa: N806 self.space = NetworkGrid(G) self.agents = [] for i, pos in enumerate(TEST_AGENTS_NETWORK_SINGLE): @@ -408,7 +408,7 @@ def setUp(self): """ Create a test network grid and populate with Mock Agents. """ - G = nx.complete_graph(TestMultipleNetworkGrid.GRAPH_SIZE) + G = nx.complete_graph(TestMultipleNetworkGrid.GRAPH_SIZE) # noqa: N806 self.space = NetworkGrid(G) self.agents = [] for i, pos in enumerate(TEST_AGENTS_NETWORK_MULTIPLE): diff --git a/tests/test_time.py b/tests/test_time.py index 903d3f2d207..7bf8b816b8a 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -188,7 +188,7 @@ def test_random_activation_step_steps_each_agent(self): 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)) + assert all(x == 1 for x in agent_steps) def test_intrastep_remove(self): """ @@ -214,8 +214,8 @@ def test_simultaneous_activation_step_steps_and_advances_each_agent(self): # one step for each of 2 agents agent_steps = [i.steps for i in model.schedule.agents] agent_advances = [i.advances for i in model.schedule.agents] - assert all(map(lambda x: x == 1, agent_steps)) - assert all(map(lambda x: x == 1, agent_advances)) + assert all(x == 1 for x in agent_steps) + assert all(x == 1 for x in agent_advances) class TestRandomActivationByType(TestCase): @@ -254,7 +254,7 @@ def test_random_activation_step_steps_each_agent(self): 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)) + assert all(x == 1 for x in agent_steps) def test_add_non_unique_ids(self): """ From d429a7f0ad7672b86faff5823f1084fd2ca9a386 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 5 Feb 2023 10:22:09 -0500 Subject: [PATCH 063/214] ruff: Explicitly quote --extend-exclude argument --- .github/workflows/build_lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index b8eb7dd0fbb..9f33e5c62be 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -72,7 +72,7 @@ jobs: - name: Lint with ruff # Include `--format=github` to enable automatic inline annotations. # Use settings from pyproject.toml. - run: ruff . --format=github --extend-exclude mesa/cookiecutter-mesa/* + run: ruff . --format=github --extend-exclude 'mesa/cookiecutter-mesa/*' lint-black: runs-on: ubuntu-latest From a2e6d244b6f32db0a07e797d50074408e26aa6e5 Mon Sep 17 00:00:00 2001 From: tpike3 Date: Sat, 4 Mar 2023 07:58:26 -0500 Subject: [PATCH 064/214] Update resources in README --- README.rst | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index aec8be7c259..421c8321439 100644 --- a/README.rst +++ b/README.rst @@ -57,19 +57,21 @@ Or any other (development) branch on this repo or your own fork: $ pip install -U -e git+https://github.com/YOUR_FORK/mesa@YOUR_BRANCH#egg=mesa -Take a look at the `examples `_ folder for sample models demonstrating Mesa features. +For resources or help on using Mesa, check out the following: -For more help on using Mesa, check out the following resources: - -* `Intro to Mesa Tutorial`_ -* `Docs`_ -* `Email list for users`_ -* `PyPI`_ +* `Intro to Mesa Tutorial`_ (An introductory model, the Boltzmann Wealth Model, for beginners or those new to Mesa.) +* `Complexity Explorer Tutorial`_ (An advanced-beginner model, SugarScape with Traders, with instructional videos) +* `Mesa Examples`_ (A repository of seminal ABMs using Mesa and examples of employing specific Mesa Features) +* `Docs`_ (Mesa's documentation, API and useful snippets) +* `Discussions`_ (GitHub threaded discussions about Mesa) +* `Matrix Chat`_ (Chat Forum via Matrix to talk about Mesa) .. _`Intro to Mesa Tutorial` : http://mesa.readthedocs.org/en/main/tutorials/intro_tutorial.html +.. _`Complexity Explorer Tutorial` : https://www.complexityexplorer.org/courses/172-agent-based-models-with-python-an-introduction-to-mesa +.. _`Mesa Examples` : https://github.com/projectmesa/mesa-examples/tree/main/examples .. _`Docs` : http://mesa.readthedocs.org/en/main/ -.. _`Email list for users` : https://groups.google.com/d/forum/projectmesa -.. _`PyPI` : https://pypi.python.org/pypi/Mesa/ +.. _`Discussions` : https://github.com/projectmesa/mesa/discussions +.. _`Matrix Chat` : https://matrix.to/#/#project-mesa:matrix.org Running Mesa in Docker ------------------------ From 279ab2736e2263cf95ff913741248b05f279f1ef Mon Sep 17 00:00:00 2001 From: jackiekazil Date: Mon, 6 Mar 2023 09:28:49 -0500 Subject: [PATCH 065/214] Update history & version number for Taylor release. --- HISTORY.rst | 81 ++++++++++++++++++++++++++++++++++++++++++++++++ mesa/__init__.py | 2 +- 2 files changed, 82 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index f62fb695070..761915a92cd 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,87 @@ Release History --------------- + +1.2.0 (2023-03-09) Taylor +++++++++++++++++++++++++++ + +**Special notes** + +New features: + +* Implement radius for NetworkGrid.get_neighbors #1564 + +Some highlights for the perf improvements: + +* Use getattr for attribute strings in model data collection #1590 this is a 2x speedup over the relevant line +* Faster is_integer function for common cases #1597 is for 1.3x speedup for grid access (grid[x, y]) +* Refactor iter/get_cell_list_contents methods #1570 at least 1.3x speedup for iter/get_cell_list_contents +* Evaluate empties set more lazily #1546 (comment) ~1.3x speedup for place_agent, remove_agent, and move_agent + +**Improvements** + +* ci: Add testing on Python 3.11 #1519 +* Remove auto-update GH Actions for Pipfile.lock #1558 +* ruff + * ruff: Add isort #1594 + * ci: Replace flake8 with Ruff #1587 + * ruff: Add more rules based on Zulip's config #1596 +* perf: faster is_integer function for common cases #1597 +* Remove _reporter_decorator #1591 +* Change index at DataFrame creation in get_agent_vars_dataframe #1586 +* Make Grid class private #1575 +* Make the internal grid and empties_built in Grid class private #1568 +* Simplify code in ContinuousSpace #1536 +* Improve docstrings of ContinuousSpace #1535 +* Simplify accept_tuple_argument decorator in space.py #1531 +* Enhance schedulers to support intra-step removal of agents #1523 +* perf: Refactor iter_cell_list_contents Performance #1527 +* Replace two loops with dictionary comprehension, list- with generator comprehension #1458 +* Make MultiGrid.place_agent faster #1508 +* Update space module-level docstring summary #1518 +* Update NetworkGrid.__init__ docstring #1514 +* Deprecate SingleGrid.position_agent #1512 +* Make swap_pos part of Grid instead of SingleGrid #1507 +* Refactor NetworkGrid docstrings and iter/get_cell_list_contents #1498 +* Hexgrid: use get_neighborhood in iter_neighbors #1504 +* Auto update year for copyright in docs #1503 +* Refactor Grid.move_to_empty #1482 +* Put "Mesa" instead of "it" in README #1490 +* Batchrunner: Remove unnecessary dict transformation, .keys() in len() #1460 +* Add Dependabot configuration for GitHub Actions update check #1480 +* Use list transformation only when shuffled is True #1478 +* Implement swap_pos #1474 +* Clean up DataCollector #1475 + + +**Fixes** + +* Update resources in README #1605 +* Fix accident from https://github.com/projectmesa/mesa/pull/1488 #1489 +* pre-commit autoupdate #1598, #1576, #1548, #1494 +* Fix docstring of DataCollector #1592 +* Update Pipfile.lock (dependencies) #1495 #1487 +* build(deps): + * build(deps): bump codecov/codecov-action from 2 to 3 dependencies Pull requests that update a dependency file #1486 + * build(deps): bump actions/upload-artifact from 2 to 3 dependencies Pull requests that update a dependency file #1485 + * build(deps): bump peter-evans/create-pull-request from 3 to 4 dependencies Pull requests that update a dependency file #1484 + * build(deps): bump actions/setup-python from 3 to 4 dependencies Pull requests that update a dependency file #1483 +* Establish reproducibility for NetworkGrid.get_neighbors when radius > 1 #1569 +* Format js code #1554 +* Add some missing const declarations #1549 +* fix tutorial url in examples #1538 +* Update cookiecutter to flat import style. #1525 +* Fix bug in Grid.get_neighborhood #1517 +* Revert changes of #1478 and #1456 #1516 +* Fix return types of some NetworkGrid methods #1505 +* Update year for copyright #1501 +* Add default_value function to NetworkGrid #1497 +* Remove extraneous spaces from docstrings in modules 2 #1496 +* Remove extraneous spaces from docstrings in modules #1493 +* SingleGrid: Remove extraneous attribute declaration (empties) #1491 + + + 1.1.1 (2022-10-21) ++++++++++++++++++ diff --git a/mesa/__init__.py b/mesa/__init__.py index 25054b50ff1..e2e7365b139 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -24,7 +24,7 @@ ] __title__ = "mesa" -__version__ = "1.1.1" +__version__ = "1.2.0" __license__ = "Apache 2.0" _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year __copyright__ = f"Copyright {_this_year} Project Mesa Team" From 5069f3098c971b89b1531b10813ba066593035b8 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 13 Mar 2023 08:22:39 -0400 Subject: [PATCH 066/214] fix: Include cookiecutter folders in install content --- MANIFEST.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MANIFEST.in b/MANIFEST.in index 58ade830f0e..a0cea1f60a5 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -3,10 +3,10 @@ include LICENSE include HISTORY.rst include README.rst include setup.py -include mesa/cookiecutter-mesa/* include mesa/visualization/templates/*.html include mesa/visualization/templates/css/* include mesa/visualization/templates/fonts/* +graft mesa/cookiecutter-mesa graft mesa/visualization/templates/js graft mesa/visualization/templates/external global-exclude *.py[co] From 303a0561acbe413e844af2f1fda4d418c9a7b326 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 8 Mar 2023 20:45:15 -0500 Subject: [PATCH 067/214] Fix Ruff errors --- pyproject.toml | 1 + tests/test_time.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 6583f2196cc..db2f6b43e68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ extend-ignore = [ "PGH004", # Use specific rule codes when using `noqa` TODO "B905", # `zip()` without an explicit `strict=` parameter "N802", # Function name should be lowercase + "N999", # Invalid module name. We should revisit this in the future, TODO ] extend-exclude = ["docs", "build"] # Hardcode to Python 3.8. diff --git a/tests/test_time.py b/tests/test_time.py index 7bf8b816b8a..3014a290fbb 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -109,7 +109,7 @@ def test_no_shuffle(self): model = MockModel(shuffle=False) model.step() model.step() - assert all([i == j for i, j in zip(model.log[:4], model.log[4:])]) + assert all(i == j for i, j in zip(model.log[:4], model.log[4:])) def test_shuffle(self): """ From ffd846eae6d900a55b3fc61193ff4ceb7448be86 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 8 Mar 2023 20:45:41 -0500 Subject: [PATCH 068/214] Pin Ruff version --- .github/workflows/build_lint.yml | 2 +- setup.py | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 9f33e5c62be..382c2e0fea1 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -68,7 +68,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.10" - - run: pip install ruff + - run: pip install ruff==0.0.254 - name: Lint with ruff # Include `--format=github` to enable automatic inline annotations. # Use settings from pyproject.toml. diff --git a/setup.py b/setup.py index e829de3389b..6760e56ef9b 100644 --- a/setup.py +++ b/setup.py @@ -11,7 +11,14 @@ requires = ["click", "cookiecutter", "networkx", "numpy", "pandas", "tornado", "tqdm"] extras_require = { - "dev": ["black", "ruff", "coverage", "pytest >= 4.6", "pytest-cov", "sphinx"], + "dev": [ + "black", + "ruff==0.0.254", + "coverage", + "pytest >= 4.6", + "pytest-cov", + "sphinx", + ], "docs": ["sphinx", "ipython"], } From 95863286c1200498502fd3df7feddfb062faa08b Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 14 Mar 2023 07:27:41 -0400 Subject: [PATCH 069/214] Remove trailing whitespaces --- HISTORY.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 761915a92cd..8fa159c51ab 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -23,7 +23,7 @@ Some highlights for the perf improvements: **Improvements** * ci: Add testing on Python 3.11 #1519 -* Remove auto-update GH Actions for Pipfile.lock #1558 +* Remove auto-update GH Actions for Pipfile.lock #1558 * ruff * ruff: Add isort #1594 * ci: Replace flake8 with Ruff #1587 @@ -63,7 +63,7 @@ Some highlights for the perf improvements: * pre-commit autoupdate #1598, #1576, #1548, #1494 * Fix docstring of DataCollector #1592 * Update Pipfile.lock (dependencies) #1495 #1487 -* build(deps): +* build(deps): * build(deps): bump codecov/codecov-action from 2 to 3 dependencies Pull requests that update a dependency file #1486 * build(deps): bump actions/upload-artifact from 2 to 3 dependencies Pull requests that update a dependency file #1485 * build(deps): bump peter-evans/create-pull-request from 3 to 4 dependencies Pull requests that update a dependency file #1484 @@ -75,7 +75,7 @@ Some highlights for the perf improvements: * Update cookiecutter to flat import style. #1525 * Fix bug in Grid.get_neighborhood #1517 * Revert changes of #1478 and #1456 #1516 -* Fix return types of some NetworkGrid methods #1505 +* Fix return types of some NetworkGrid methods #1505 * Update year for copyright #1501 * Add default_value function to NetworkGrid #1497 * Remove extraneous spaces from docstrings in modules 2 #1496 From 2e856679f41043b19a3376794e4c48c17c5c17a1 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Tue, 14 Mar 2023 16:35:54 +0100 Subject: [PATCH 070/214] datacollector: Add warning when returning empty dataframe with no reporters defined With this commit the get_model_vars_dataframe() and get_agent_vars_dataframe() raise warnings when no model_reporters or agent_reporteres are defined, and it thus returns an empty dataframe. This warning makes it clearer why it's returning an empty dataframe. Two of my students were stuck on this quite a while, since they were requesting the get_model_vars_dataframe() while having defined only agent reporters. --- mesa/datacollection.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index dd9eb375cf5..fcaedc7c8c8 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -216,6 +216,12 @@ def get_model_vars_dataframe(self): The DataFrame has one column for each model variable, and the index is (implicitly) the model tick. """ + # Check if self.model_reporters dictionary is empty, if so raise warning + if not self.model_reporters: + raise UserWarning( + "No model reporters have been defined in the DataCollector, returning empty DataFrame." + ) + return pd.DataFrame(self.model_vars) def get_agent_vars_dataframe(self): @@ -224,6 +230,12 @@ def get_agent_vars_dataframe(self): The DataFrame has one column for each variable, with two additional columns for tick and agent_id. """ + # Check if self.agent_reporters dictionary is empty, if so raise warning + if not self.agent_reporters: + raise UserWarning( + "No agent reporters have been defined in the DataCollector, returning empty DataFrame." + ) + all_records = itertools.chain.from_iterable(self._agent_records.values()) rep_names = list(self.agent_reporters) From 9a07a48526f11b78e462a9bab390366c95443814 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 15 Mar 2023 08:36:58 -0400 Subject: [PATCH 071/214] Bump version to 1.2.1 --- HISTORY.rst | 9 +++++++++ mesa/__init__.py | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 8fa159c51ab..f0af3e35bc3 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,15 @@ Release History --------------- +1.2.1 (2023-03-16) +++++++++++++++++++ + +This release fixes https://github.com/projectmesa/mesa/issues/1606, where `mesa startproject` doesn't work. + +Changes: +- fix: Include cookiecutter folders in install content #1611 +- Fix Ruff errors and pin Ruff version #1609 +- datacollector: Add warning when returning empty dataframe with no reporters defined #1614 1.2.0 (2023-03-09) Taylor ++++++++++++++++++++++++++ diff --git a/mesa/__init__.py b/mesa/__init__.py index e2e7365b139..e10693a47a6 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -24,7 +24,7 @@ ] __title__ = "mesa" -__version__ = "1.2.0" +__version__ = "1.2.1" __license__ = "Apache 2.0" _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year __copyright__ = f"Copyright {_this_year} Project Mesa Team" From cebdb87141200d7df60c5ceee4118d7cd9ecee85 Mon Sep 17 00:00:00 2001 From: jackiekazil Date: Sat, 18 Mar 2023 12:29:09 -0400 Subject: [PATCH 072/214] Fix history formatting. --- HISTORY.rst | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index f0af3e35bc3..88228d4250a 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,15 +3,16 @@ Release History --------------- -1.2.1 (2023-03-16) +1.2.1 (2023-03-18) ++++++++++++++++++ This release fixes https://github.com/projectmesa/mesa/issues/1606, where `mesa startproject` doesn't work. Changes: -- fix: Include cookiecutter folders in install content #1611 -- Fix Ruff errors and pin Ruff version #1609 -- datacollector: Add warning when returning empty dataframe with no reporters defined #1614 + +* fix: Include cookiecutter folders in install content #1611 +* Fix Ruff errors and pin Ruff version #1609 +* datacollector: Add warning when returning empty dataframe with no reporters defined #1614 1.2.0 (2023-03-09) Taylor ++++++++++++++++++++++++++ From 78ff484097a7f68329f5db51ec2d87df5511b0e7 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Apr 2023 07:06:36 +0000 Subject: [PATCH 073/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/psf/black: 23.1.0 → 23.3.0](https://github.com/psf/black/compare/23.1.0...23.3.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 4714cd84e22..8083bcb5841 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,7 +4,7 @@ ci: repos: - repo: https://github.com/psf/black - rev: 23.1.0 + rev: 23.3.0 hooks: - id: black-jupyter exclude: ^mesa/cookiecutter-mesa/ From 60733198906ccaf2a5f63cff5c1e1c8fde009b5b Mon Sep 17 00:00:00 2001 From: Stephen Mubita Date: Mon, 24 Apr 2023 10:15:34 -0500 Subject: [PATCH 074/214] Change the location of the Boltzmann Wealth Model to account for examples moving into their own repo --- docs/tutorials/intro_tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/intro_tutorial.rst b/docs/tutorials/intro_tutorial.rst index 1b13daee0be..186c7d69438 100644 --- a/docs/tutorials/intro_tutorial.rst +++ b/docs/tutorials/intro_tutorial.rst @@ -65,7 +65,7 @@ installed directly form the github repository by running: .. code:: bash - $ pip install -r https://raw.githubusercontent.com/projectmesa/mesa/main/examples/boltzmann_wealth_model/requirements.txt + $ pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt | This will install the dependencies listed in the requirements.txt file which are: From 10e779884b90b27d9bf94ae2cc425e6af48e61fa Mon Sep 17 00:00:00 2001 From: Stephen Mubita Date: Mon, 24 Apr 2023 10:36:56 -0500 Subject: [PATCH 075/214] change other example locations --- docs/best-practices.rst | 2 +- docs/tutorials/intro_tutorial.ipynb | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/best-practices.rst b/docs/best-practices.rst index fa47bf4e774..4519d6881ff 100644 --- a/docs/best-practices.rst +++ b/docs/best-practices.rst @@ -27,7 +27,7 @@ organize them. For example, if the visualization uses image files, put those in an ``images`` directory. The `Schelling -`_ model is +`_ model is a good example of a small well-packaged model. It's easy to create a cookiecutter mesa model by running ``mesa startproject`` diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 2fe16594e62..35303396867 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -40,6 +40,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -56,7 +57,7 @@ "When you do that, it will install Mesa itself, as well as any dependencies that aren't in your setup yet. Additional dependencies required by this tutorial can be found in the **examples/boltzmann_wealth_model/requirements.txt** file, which can be installed directly form the github repository by running:\n", "\n", "```bash\n", - " $ pip install -r https://raw.githubusercontent.com/projectmesa/mesa/main/examples/boltzmann_wealth_model/requirements.txt\n", + " $ pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt\n", "```\n", "\n", "This will install the dependencies listed in the requirements.txt file which are: \n", From 38ba70a4d168bc6d05c50114c81d35e7f57b5262 Mon Sep 17 00:00:00 2001 From: Stephen Mubita Date: Mon, 24 Apr 2023 10:49:12 -0500 Subject: [PATCH 076/214] remove $ from code blocks, so copied commands will run --- README.rst | 6 +++--- docs/index.rst | 4 ++-- docs/tutorials/intro_tutorial.ipynb | 4 ++-- docs/tutorials/intro_tutorial.rst | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index 421c8321439..61366e1123c 100644 --- a/README.rst +++ b/README.rst @@ -43,19 +43,19 @@ Getting started quickly: .. code-block:: bash - $ pip install mesa + pip install mesa You can also use `pip` to install the github version: .. code-block:: bash - $ pip install -U -e git+https://github.com/projectmesa/mesa@main#egg=mesa + pip install -U -e git+https://github.com/projectmesa/mesa@main#egg=mesa Or any other (development) branch on this repo or your own fork: .. code-block:: bash - $ pip install -U -e git+https://github.com/YOUR_FORK/mesa@YOUR_BRANCH#egg=mesa + pip install -U -e git+https://github.com/YOUR_FORK/mesa@YOUR_BRANCH#egg=mesa For resources or help on using Mesa, check out the following: diff --git a/docs/index.rst b/docs/index.rst index 94fd22f89d0..bbabb4d5d01 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -44,13 +44,13 @@ Getting started quickly: .. code-block:: bash - $ pip install mesa + pip install mesa To launch an example model, clone the `repository `_ folder and invoke ``mesa runserver`` for one of the ``examples/`` subdirectories: .. code-block:: bash - $ mesa runserver examples/wolf_sheep + mesa runserver examples/wolf_sheep For more help on using Mesa, check out the following resources: diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 35303396867..3364fb5d09a 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -51,13 +51,13 @@ "To install Mesa, simply:\n", "\n", "```bash\n", - " $ pip install mesa\n", + " pip install mesa\n", "```\n", "\n", "When you do that, it will install Mesa itself, as well as any dependencies that aren't in your setup yet. Additional dependencies required by this tutorial can be found in the **examples/boltzmann_wealth_model/requirements.txt** file, which can be installed directly form the github repository by running:\n", "\n", "```bash\n", - " $ pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt\n", + " pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt\n", "```\n", "\n", "This will install the dependencies listed in the requirements.txt file which are: \n", diff --git a/docs/tutorials/intro_tutorial.rst b/docs/tutorials/intro_tutorial.rst index 186c7d69438..0f68cf5f449 100644 --- a/docs/tutorials/intro_tutorial.rst +++ b/docs/tutorials/intro_tutorial.rst @@ -55,7 +55,7 @@ To install Mesa, simply: .. code:: bash - $ pip install mesa + pip install mesa When you do that, it will install Mesa itself, as well as any dependencies that aren’t in your setup yet. Additional dependencies @@ -65,7 +65,7 @@ installed directly form the github repository by running: .. code:: bash - $ pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt + pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt | This will install the dependencies listed in the requirements.txt file which are: From e834647e4ef46c31fd21d07842c514fba5e12c7a Mon Sep 17 00:00:00 2001 From: Stephen Mubita Date: Mon, 24 Apr 2023 11:39:56 -0500 Subject: [PATCH 077/214] Modify virtual environment link in introductory tutorial --- docs/tutorials/intro_tutorial.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/intro_tutorial.rst b/docs/tutorials/intro_tutorial.rst index 0f68cf5f449..e26595d8e0e 100644 --- a/docs/tutorials/intro_tutorial.rst +++ b/docs/tutorials/intro_tutorial.rst @@ -47,7 +47,7 @@ Installation ~~~~~~~~~~~~ To start, install Mesa. We recommend doing this in a `virtual -environment `__, +environment `__, but make sure your environment is set up with Python 3. Mesa requires Python3 and does not work in Python 2 environments. From 2cf8ca3a4ec0c7a120291b9e65b48c03be270f99 Mon Sep 17 00:00:00 2001 From: Jeremy Silver Date: Mon, 24 Apr 2023 10:16:58 -0600 Subject: [PATCH 078/214] cli: make quality of life improvements --- mesa/main.py | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/mesa/main.py b/mesa/main.py index 9d800cbb5b3..197a36b9274 100644 --- a/mesa/main.py +++ b/mesa/main.py @@ -1,9 +1,12 @@ import os import sys +from pathlib import Path from subprocess import call import click +from mesa import __version__ + PROJECT_PATH = click.Path( exists=True, file_okay=False, dir_okay=True, resolve_path=True ) @@ -11,8 +14,10 @@ SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__)) COOKIECUTTER_PATH = os.path.join(os.path.dirname(SCRIPTS_DIR), COOKIECUTTER_DIR) +CONTEXT_SETTINGS = {"help_option_names": ["-h", "--help"]} + -@click.group() +@click.group(context_settings=CONTEXT_SETTINGS) def cli(): "Manage Mesa projects" @@ -25,12 +30,11 @@ def runserver(project): PROJECT is the path to the directory containing `run.py`, or the current directory if not specified. """ - sys.path.insert(0, project) - os.chdir(project) - - with open("run.py") as f: - code = compile(f.read(), "run.py", "exec") - exec(code, {}, {}) # noqa: S102 + run_path = Path(project) / "run.py" + if not run_path.exists(): + sys.exit(f"ERROR: file {run_path} does not exist") + args = [sys.executable, str(run_path)] + call(args) @click.command() @@ -38,11 +42,19 @@ def runserver(project): "--no-input", is_flag=True, help="Do not prompt user for custom mesa model input." ) def startproject(no_input): + """Create a new mesa project""" args = ["cookiecutter", COOKIECUTTER_PATH] if no_input: args.append("--no-input") call(args) +@click.command() +def version(): + """Show the version of mesa""" + print(f"mesa {__version__}") + + cli.add_command(runserver) cli.add_command(startproject) +cli.add_command(version) From eaaed540e23f8df4ecd1fcb7b69a4938f9398c78 Mon Sep 17 00:00:00 2001 From: Glenn Lehman Date: Mon, 24 Apr 2023 12:14:35 -0600 Subject: [PATCH 079/214] Modify tutorial intro --- docs/tutorials/intro_tutorial.rst | 139 +++++++++++++++--------------- 1 file changed, 68 insertions(+), 71 deletions(-) diff --git a/docs/tutorials/intro_tutorial.rst b/docs/tutorials/intro_tutorial.rst index e26595d8e0e..912305f42f2 100644 --- a/docs/tutorials/intro_tutorial.rst +++ b/docs/tutorials/intro_tutorial.rst @@ -6,26 +6,23 @@ Tutorial Description `Mesa `__ is a Python framework for `agent-based -modeling `__. Getting -started with Mesa is easy. In this tutorial, we will walk through -creating a simple model and progressively add functionality which will -illustrate Mesa’s core features. - -**Note:** This tutorial is a work-in-progress. If you find any errors or -bugs, or just find something unclear or confusing, `let us +modeling `__. This tutorial will assist you in getting started. +Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked +through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone +find any errors, bugs, have a suggest or just are looking for clarification, `please let us know `__! -The base for this tutorial is a very simple model of agents exchanging -money. Next, we add *space* to allow agents to move. Then, we’ll cover -two of Mesa’s analytic tools: the *data collector* and *batch runner*. -After that, we’ll add an *interactive visualization* which lets us watch -the model as it runs. Finally, we go over how to write your own -visualization module, for users who are comfortable with JavaScript. +The premise of this tutorial is to create a a starter-level model representing agents exchanging +money. This exchange of money affects wealth. Next, *space* is added to allow agents to move +based on the change in wealth as time progresses. + +Two of Mesa's analytic tools: the *data collector* and *batch runner* will be used to examine this movement. +After that an *interactive visualization* is added which allows model viewing as it runs. + +Finally, the creation of a custom visualization module in JavaScript is explored. -You can also find all the code this tutorial describes in the -**examples/boltzmann_wealth_model** directory of the Mesa repository. -Sample Model Description +Model Description ------------------------ The tutorial model is a very simple simulated agent-based economy, drawn @@ -39,9 +36,9 @@ wealth distribution [Dragulescu2002]. The rules of our tutorial model: Despite its simplicity, this model yields results that are often unexpected to those not familiar with it. For our purposes, it also -easily demonstrates Mesa’s core features. +easily demonstrates Mesa's core features. -Let’s get started. +Let's get started. Installation ~~~~~~~~~~~~ @@ -58,7 +55,7 @@ To install Mesa, simply: pip install mesa When you do that, it will install Mesa itself, as well as any -dependencies that aren’t in your setup yet. Additional dependencies +dependencies that aren't in your setup yet. Additional dependencies required by this tutorial can be found in the **examples/boltzmann_wealth_model/requirements.txt** file, which can be installed directly form the github repository by running: @@ -70,9 +67,9 @@ installed directly form the github repository by running: | This will install the dependencies listed in the requirements.txt file which are: | - jupyter (Ipython interactive notebook) -| - matplotlib (Python’s visualization library) +| - matplotlib (Python's visualization library) | - mesa (this ABM library – if not installed) -| - numpy (Python’s numerical python library) +| - numpy (Python's numerical python library) Building a sample model ----------------------- @@ -84,7 +81,7 @@ models in two different ways: 2. Write the model interactively in `Jupyter Notebook `__ cells. -Either way, it’s good practice to put your model in its own folder – +Either way, it's good practice to put your model in its own folder – especially if the project will end up consisting of multiple files (for example, Python files for the model and the visualization, a Notebook for analysis, and a Readme with some documentation and discussion). @@ -101,7 +98,7 @@ model-level attributes, manages the agents, and generally handles the global level of our model. Each instantiation of the model class will be a specific model run. Each model will contain multiple agents, all of which are instantiations of the agent class. Both the model and agent -classes are child classes of Mesa’s generic ``Model`` and ``Agent`` +classes are child classes of Mesa's generic ``Model`` and ``Agent`` classes. This is seen in the code with ``class MoneyModel(mesa.Model)`` or ``class MoneyAgent(mesa.Agent)``. If you want you can specifically the class being imported by looking at the @@ -161,13 +158,13 @@ model uses, and see whether it changes the model behavior. This may not seem important, but scheduling patterns can have an impact on your results [Comer2014]. -For now, let’s use one of the simplest ones: ``RandomActivation``\ \*, +For now, let's use one of the simplest ones: ``RandomActivation``\ \*, which activates all the agents once per step, in random order. Every agent is expected to have a ``step`` method. The step method is the action the agent takes when it is activated by the model schedule. We add an agent to the schedule using the ``add`` method; when we call the -schedule’s ``step`` method, the model shuffles the order of the agents, -then activates and executes each agent’s ``step`` method. +schedule's ``step`` method, the model shuffles the order of the agents, +then activates and executes each agent's ``step`` method. \*Unlike ``mesa.model`` or ``mesa.agent``, ``mesa.time`` has multiple classes (e.g. ``RandomActivation``, ``StagedActivation`` etc). To ensure @@ -212,8 +209,8 @@ this: """Advance the model by one step.""" self.schedule.step() -At this point, we have a model which runs – it just doesn’t do anything. -You can see for yourself with a few easy lines. If you’ve been working +At this point, we have a model which runs – it just doesn't do anything. +You can see for yourself with a few easy lines. If you've been working in an interactive session, you can create a model object directly. Otherwise, you need to open an interactive session in the same directory as your source code file, and import the classes. For example, if your @@ -259,12 +256,12 @@ Now we just need to have the agents do what we intend for them to do: check their wealth, and if they have the money, give one unit of it away to another random agent. To allow the agent to choose another agent at random, we use the ``model.random`` random-number generator. This works -just like Python’s ``random`` module, but with a fixed seed set when the +just like Python's ``random`` module, but with a fixed seed set when the model is instantiated, that can be used to replicate a specific model run later. To pick an agent at random, we need a list of all agents. Notice that -there isn’t such a list explicitly in the model. The scheduler, however, +there isn't such a list explicitly in the model. The scheduler, however, does have an internal list of all the agents it is scheduled to activate. @@ -289,21 +286,21 @@ With that in mind, we rewrite the agent ``step`` method, like this: Running your first model ~~~~~~~~~~~~~~~~~~~~~~~~ -With that last piece in hand, it’s time for the first rudimentary run of +With that last piece in hand, it's time for the first rudimentary run of the model. -If you’ve written the code in its own file (``money_model.py`` or a +If you've written the code in its own file (``money_model.py`` or a different name), launch an interpreter in the same directory as the file (either the plain Python command-line interpreter, or the IPython interpreter), or launch a Jupyter Notebook there. Then import the classes you created. (If you wrote the code in a Notebook, obviously -this step isn’t necessary). +this step isn't necessary). .. code:: python from money_model import * -Now let’s create a model with 10 agents, and run it for 10 steps. +Now let's create a model with 10 agents, and run it for 10 steps. .. code:: ipython3 @@ -312,11 +309,11 @@ Now let’s create a model with 10 agents, and run it for 10 steps. model.step() Next, we need to get some data out of the model. Specifically, we want -to see the distribution of the agent’s wealth. We can get the wealth +to see the distribution of the agent's wealth. We can get the wealth values with list comprehension, and then use matplotlib (or another graphics library) to visualize the data in a histogram. -If you are running from a text editor or IDE, you’ll also need to add +If you are running from a text editor or IDE, you'll also need to add this line, to make the graph appear. .. code:: python @@ -349,7 +346,7 @@ this line, to make the graph appear. .. image:: intro_tutorial_files/output_19_1.png -You’ll should see something like the distribution above. Yours will +You'll should see something like the distribution above. Yours will almost certainly look at least slightly different, since each run of the model is random, after all. @@ -392,7 +389,7 @@ This runs 100 instantiations of the model, and runs each for 10 steps. (Notice that we set the histogram bins to be integers, since agents can only have whole numbers of wealth). This distribution looks a lot smoother. By running the model 100 times, we smooth out some of the -‘noise’ of randomness, and get to the model’s overall expected behavior. +‘noise'of randomness, and get to the model's overall expected behavior. This outcome might be surprising. Despite the fact that all agents, on average, give and receive one unit of money every step, the model @@ -414,9 +411,9 @@ those on the left edge, and the top to the bottom. This prevents some cells having fewer neighbors than others, or agents being able to go off the edge of the environment. -Let’s add a simple spatial element to our model by putting our agents on +Let's add a simple spatial element to our model by putting our agents on a grid and make them walk around at random. Instead of giving their unit -of money to any random agent, they’ll give it to an agent on the same +of money to any random agent, they'll give it to an agent on the same cell. Mesa has two main types of grids: ``SingleGrid`` and ``MultiGrid``\ \*. @@ -431,9 +428,9 @@ Similar to ``mesa.time`` context is retained with `mesa.space `__ We instantiate a grid with width and height parameters, and a boolean as -to whether the grid is toroidal. Let’s make width and height model +to whether the grid is toroidal. Let's make width and height model parameters, in addition to the number of agents, and have the grid -always be toroidal. We can place agents on a grid with the grid’s +always be toroidal. We can place agents on a grid with the grid's ``place_agent`` method, which takes an agent and an (x, y) tuple of the coordinates to place the agent. @@ -457,16 +454,16 @@ coordinates to place the agent. y = self.random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) -Under the hood, each agent’s position is stored in two ways: the agent +Under the hood, each agent's position is stored in two ways: the agent is contained in the grid in the cell it is currently in, and the agent has a ``pos`` variable with an (x, y) coordinate tuple. The ``place_agent`` method adds the coordinate to the agent automatically. -Now we need to add to the agents’ behaviors, letting them move around +Now we need to add to the agents'behaviors, letting them move around and only give money to other agents in the same cell. -First let’s handle movement, and have the agents move to a neighboring -cell. The grid object provides a ``move_agent`` method, which like you’d +First let's handle movement, and have the agents move to a neighboring +cell. The grid object provides a ``move_agent`` method, which like you'd imagine, moves an agent to a given cell. That still leaves us to get the possible neighboring cells to move to. There are a couple ways to do this. One is to use the current coordinates, and loop over all @@ -480,7 +477,7 @@ coordinates +/- 1 away from it. For example: for dy in [-1, 0, 1]: neighbors.append((x+dx, y+dy)) -But there’s an even simpler way, using the grid’s built-in +But there's an even simpler way, using the grid's built-in ``get_neighborhood`` method, which returns all the neighbors of a given cell. This method can get two types of cell neighborhoods: `Moore `__ (includes @@ -489,7 +486,7 @@ Neumann `__\ (only up/down/left/right). It also needs an argument as to whether to include the center cell itself as one of the neighbors. -With that in mind, the agent’s ``move`` method looks like this: +With that in mind, the agent's ``move`` method looks like this: .. code:: python @@ -505,7 +502,7 @@ With that in mind, the agent’s ``move`` method looks like this: Next, we need to get all the other agents present in a cell, and give one of them some money. We can get the contents of one or more cells -using the grid’s ``get_cell_list_contents`` method, or by accessing a +using the grid's ``get_cell_list_contents`` method, or by accessing a cell directly. The method accepts a list of cell coordinate tuples, or a single tuple if we only care about one cell. @@ -520,7 +517,7 @@ single tuple if we only care about one cell. other.wealth += 1 self.wealth -= 1 -And with those two methods, the agent’s ``step`` method becomes: +And with those two methods, the agent's ``step`` method becomes: .. code:: python @@ -581,7 +578,7 @@ Now, putting that all together should look like this: def step(self): self.schedule.step() -Let’s create a model with 50 agents on a 10x10 grid, and run it for 20 +Let's create a model with 50 agents on a 10x10 grid, and run it for 20 steps. .. code:: ipython3 @@ -590,11 +587,11 @@ steps. for i in range(20): model.step() -Now let’s use matplotlib and numpy to visualize the number of agents +Now let's use matplotlib and numpy to visualize the number of agents residing in each cell. To do that, we create a numpy array of the same -size as the grid, filled with zeros. Then we use the grid object’s +size as the grid, filled with zeros. Then we use the grid object's ``coord_iter()`` feature, which lets us loop over every cell in the -grid, giving us each cell’s coordinates and contents in turn. +grid, giving us each cell's coordinates and contents in turn. .. code:: ipython3 @@ -627,10 +624,10 @@ grid, giving us each cell’s coordinates and contents in turn. Collecting Data ~~~~~~~~~~~~~~~ -So far, at the end of every model run, we’ve had to go and write our own -code to get the data out of the model. This has two problems: it isn’t +So far, at the end of every model run, we've had to go and write our own +code to get the data out of the model. This has two problems: it isn't very efficient, and it only gives us end results. If we wanted to know -the wealth of each agent at each step, we’d have to add that to the loop +the wealth of each agent at each step, we'd have to add that to the loop of executing steps, and figure out some way to store the data. Since one of the main goals of agent-based modeling is generating data @@ -644,19 +641,19 @@ collector along with a function for collecting them. Model-level collection functions take a model object as an input, while agent-level collection functions take an agent object as an input. Both then return a value computed from the model or each agent at their current state. -When the data collector’s ``collect`` method is called, with a model +When the data collector's ``collect`` method is called, with a model object as its argument, it applies each model-level collection function to the model, and stores the results in a dictionary, associating the current value with the current step of the model. Similarly, the method applies each agent-level collection function to each agent currently in the schedule, associating the resulting value with the step of the -model, and the agent’s ``unique_id``. +model, and the agent's ``unique_id``. -Let’s add a DataCollector to the model with +Let's add a DataCollector to the model with `mesa.DataCollector `__, and collect two variables. At the agent level, we want to collect every -agent’s wealth at every step. At the model level, let’s measure the -model’s `Gini +agent's wealth at every step. At the model level, let's measure the +model's `Gini Coefficient `__, a measure of wealth inequality. @@ -723,7 +720,7 @@ measure of wealth inequality. self.schedule.step() At every step of the model, the datacollector will collect and store the -model-level current Gini coefficient, as well as each agent’s wealth, +model-level current Gini coefficient, as well as each agent's wealth, associating each with the current step. We run the model just as we did above. Now is when an interactive @@ -831,9 +828,9 @@ Similarly, we can get the agent-wealth data: -You’ll see that the DataFrame’s index is pairings of model step and +You'll see that the DataFrame's index is pairings of model step and agent ID. You can analyze it the way you would any other DataFrame. For -example, to get a histogram of agent wealth at the model’s end: +example, to get a histogram of agent wealth at the model's end: .. code:: ipython3 @@ -892,10 +889,10 @@ directory. After you run the code below you will see two files appear Batch Run ~~~~~~~~~ -Like we mentioned above, you usually won’t run a model only once, but +Like we mentioned above, you usually won't run a model only once, but multiple times, with fixed parameters to find the overall distributions the model generates, and with varying parameters to analyze how they -drive the model’s outputs and behaviors. Instead of needing to write +drive the model's outputs and behaviors. Instead of needing to write nested for-loops for each model, Mesa provides a `batch_run `__ function which automates it for you. @@ -980,7 +977,7 @@ of the model with each number of agents, and to run each for 100 steps. We want to keep track of 1. the Gini coefficient value and -2. the individual agent’s wealth development. +2. the individual agent's wealth development. Since for the latter changes at each time step might be interesting, we set ``data_collection_period = 1``. @@ -994,9 +991,9 @@ iteration). **Note for Windows OS users:** If you are running this tutorial in Jupyter, make sure that you set ``number_processes = 1`` (single process). If ``number_processes`` is greater than 1, it is less -straightforward to set up. You can read `Mesa’s collection of useful +straightforward to set up. You can read `Mesa's collection of useful snippets `__, -in ‘Using multi-process ``batch_run`` on Windows’ section for how to do +in ‘Using multi-process ``batch_run`` on Windows'section for how to do it. .. code:: ipython3 @@ -1066,7 +1063,7 @@ calling the batch run. .. image:: intro_tutorial_files/output_57_1.png -Second, we want to display the agent’s wealth at each time step of one +Second, we want to display the agent's wealth at each time step of one specific episode. To do this, we again filter our large data frame, this time with a fixed number of agents and only for a specific iteration of that population. To print the results, we convert the filtered data From 44bc0b77b08ba565eb42c4a547e13d4e0cb3f615 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Thu, 26 Jan 2023 00:59:12 +0100 Subject: [PATCH 080/214] Not describe again the types for overload decorated __getitem__ --- mesa/space.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 4b03b1e0033..165d5ed70f7 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -140,7 +140,7 @@ def build_empties(self) -> None: self._empties_built = True @overload - def __getitem__(self, index: int) -> list[GridContent]: + def __getitem__(self, index: int | Sequence[Coordinate]) -> list[GridContent]: ... @overload @@ -149,14 +149,7 @@ def __getitem__( ) -> GridContent | list[GridContent]: ... - @overload - def __getitem__(self, index: Sequence[Coordinate]) -> list[GridContent]: - ... - - def __getitem__( - self, - index: int | Sequence[Coordinate] | tuple[int | slice, int | slice], - ) -> GridContent | list[GridContent]: + def __getitem__(self, index): """Access contents from the grid.""" if isinstance(index, int): From f55228306ecfe5e4dcd8847c35c66f557794ae2d Mon Sep 17 00:00:00 2001 From: Jatin Khilnani Date: Mon, 24 Apr 2023 16:48:52 -0400 Subject: [PATCH 081/214] Update pip statement for zsh --- CONTRIBUTING.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.rst b/CONTRIBUTING.rst index f65b7baa1c0..c901713c1c7 100644 --- a/CONTRIBUTING.rst +++ b/CONTRIBUTING.rst @@ -28,7 +28,7 @@ discuss via `Matrix`_ OR via `an issue`_. - `Clone your repository`_ from Github to your machine. - Create a new branch in your fork: ``git checkout -b BRANCH_NAME`` - Run ``git config pull.rebase true``. This prevents messy merge commits when updating your branch on top of Mesa main branch. -- Install an editable version with developer requirements locally: ``pip install -e .[dev]`` +- Install an editable version with developer requirements locally: ``pip install -e ".[dev]"`` - Edit the code. Save. - Git add the new files and files with changes: ``git add FILE_NAME`` - Git commit your changes with a meaningful message: ``git commit -m "Fix issue X"`` From eb9fbe0dcf5cd589640e924c98fc1a3b78899f48 Mon Sep 17 00:00:00 2001 From: houssam7737 Date: Mon, 24 Apr 2023 19:25:47 -0400 Subject: [PATCH 082/214] Fix bug in tutorial agent giving money to itself (#1647) --- docs/tutorials/intro_tutorial.ipynb | 468 ++++++++++++++++++++++++---- 1 file changed, 415 insertions(+), 53 deletions(-) diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 3364fb5d09a..4c1e8c367a5 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -40,7 +40,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -101,7 +100,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 36, "metadata": {}, "outputs": [], "source": [ @@ -145,7 +144,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 37, "metadata": {}, "outputs": [], "source": [ @@ -195,9 +194,26 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 38, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Hi, I am agent 5.\n", + "Hi, I am agent 0.\n", + "Hi, I am agent 1.\n", + "Hi, I am agent 7.\n", + "Hi, I am agent 8.\n", + "Hi, I am agent 2.\n", + "Hi, I am agent 4.\n", + "Hi, I am agent 9.\n", + "Hi, I am agent 3.\n", + "Hi, I am agent 6.\n" + ] + } + ], "source": [ "empty_model = MoneyModel(10)\n", "empty_model.step()" @@ -232,7 +248,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 39, "metadata": {}, "outputs": [], "source": [ @@ -270,7 +286,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 40, "metadata": {}, "outputs": [], "source": [ @@ -299,9 +315,32 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 41, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([6., 0., 1., 0., 0., 1., 0., 1., 0., 1.]),\n", + " array([0. , 0.4, 0.8, 1.2, 1.6, 2. , 2.4, 2.8, 3.2, 3.6, 4. ]),\n", + " )" + ] + }, + "execution_count": 41, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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QI0YMSJGjBjRcfnCCy+M1157Le6999742c9+1ouT7V9jY2O8+OKL8Yc//KG3R6mKA91f0R6HI0aMiObm5mhra4tf//rXMXXq1FixYkWXP3iLKM8ei3b+WltbY9q0abFs2bJD+k2mhQiL7nxc+wknnJBrfW/qzv52d8QRR8Q555wTmzZtqsaIPa6r81dXVxdHHnlkL01VfePGjTvkf1jfdttt8eSTT8bKlStj2LBh+1xbpMfhLnn2t7tD/XFYW1sbp556akREnHfeebFmzZq4//77Y/78+XusLeK5i8i3x90d6udv3bp1sXXr1jj33HM7rtu5c2esXLkyfvSjH0WlUom+fft2uk1vnMdCvBTSnY9rHz9+fKf1ERHLli07JD/ePcXH0e/cuTNaWlpiyJAh1RqzRxXp/KXU3Nx8yJ7DLMvitttui0WLFsVzzz0XJ5988n5vU6Tz2J397a5oj8P29vaoVCp7/bcinbt92dced3eon79LL700Wlpaorm5ueMYO3ZsTJkyJZqbm/eIioheOo9Ve1toYgsXLsxKpVK2YMGC7OWXX86+8pWvZMccc0y2ZcuWLMuy7MYbb8xmzpzZsf7555/P+vXrl/3gBz/IXnnllWz27NnZEUcckbW0tPTWFvYp7/7uvPPObOnSpdlrr72WrVu3Lvvc5z6X9e/fP3vppZd6awv7tG3btmzDhg3Zhg0bsojI7rnnnmzDhg3Zm2++mWVZls2cOTO78cYbO9a//vrr2VFHHZXdcccd2SuvvJLNnTs369u3b7ZkyZLe2sJ+5d3jvffemy1evDh79dVXs5aWlmzatGlZnz59smeeeaa3trBPt956a1ZfX58tX748e++99zqOf/3rXx1rivw47M7+ivQ4nDlzZrZixYps8+bN2Z///Ods5syZWU1NTfa73/0uy7Jin7td8u6xSOevK7v/VsihcB4LExZZlmU//OEPsxNPPDGrra3Nxo0bl61evbrj3y666KJs6tSpndb/6le/yk477bSstrY2GzVqVPbUU0/18MT55Nnf9OnTO9Yef/zx2ac//els/fr1vTD1gdn1q5W7H7v2NHXq1Oyiiy7a4zZnn312Vltbm51yyinZww8/3ONz55F3j3fffXf2sY99LOvfv382cODAbOLEidlzzz3XO8MfgL3tLSI6nZciPw67s78iPQ6/+MUvZsOHD89qa2uzQYMGZZdeemnHD9wsK/a52yXvHot0/rqye1gcCufRx6YDAMkU4j0WAEAxCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBk/h9X3YomdlYsdQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "# For a jupyter notebook add the following line:\n", "%matplotlib inline\n", @@ -329,9 +368,32 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 42, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "(array([431., 303., 153., 76., 26., 9., 1., 1.]),\n", + " array([0., 1., 2., 3., 4., 5., 6., 7., 8.]),\n", + " )" + ] + }, + "execution_count": 42, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "all_wealth = []\n", "# This runs the model 100 times, each model executing 10 steps.\n", @@ -381,7 +443,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 43, "metadata": {}, "outputs": [], "source": [ @@ -467,7 +529,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 44, "metadata": {}, "outputs": [], "source": [ @@ -527,7 +589,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 45, "metadata": {}, "outputs": [], "source": [ @@ -545,9 +607,30 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 46, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 46, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "import numpy as np\n", "\n", @@ -580,7 +663,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 47, "metadata": {}, "outputs": [], "source": [ @@ -608,10 +691,15 @@ "\n", " def give_money(self):\n", " cellmates = self.model.grid.get_cell_list_contents([self.pos])\n", + " cellmates.pop(\n", + " cellmates.index(self)\n", + " ) # Ensure agent is not giving money to itself\n", " if len(cellmates) > 1:\n", " other = self.random.choice(cellmates)\n", " other.wealth += 1\n", " self.wealth -= 1\n", + " if other == self:\n", + " print(\"I JUST GAVE MONEY TO MYSELF HEHEHE!\")\n", "\n", " def step(self):\n", " self.move()\n", @@ -658,7 +746,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 48, "metadata": {}, "outputs": [], "source": [ @@ -676,9 +764,30 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 49, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 49, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "gini = model.datacollector.get_model_vars_dataframe()\n", "gini.plot()" @@ -693,9 +802,80 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 50, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Wealth
StepAgentID
001
11
21
31
41
\n", + "
" + ], + "text/plain": [ + " Wealth\n", + "Step AgentID \n", + "0 0 1\n", + " 1 1\n", + " 2 1\n", + " 3 1\n", + " 4 1" + ] + }, + "execution_count": 50, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "agent_wealth = model.datacollector.get_agent_vars_dataframe()\n", "agent_wealth.head()" @@ -710,9 +890,30 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 51, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 51, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGdCAYAAACyzRGfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAbO0lEQVR4nO3df4zXhX348dfn4Pjg1TscIBwXDkvbVdsaMBVlpM6g8qNsoWXjj06bFI3p0uU0tZemG0tVrprQuKRjSyntkk3WdDe7LQNTE9GTRogZtIIh1iU1Ql2qFVBsuIMjfvyE+3z/8HtHr4fKwefz+ngfHo/kkn7e9+b9efV1J/fM5/PhPoVKpVIJAIAkTfUeAAC4uIgPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACDV5HoP8PuGhobitddei9bW1igUCvUeBwA4B5VKJU6cOBEdHR3R1PTej2184OLjtddei87OznqPAQCch1deeSXmzp37nud84OKjtbU1It4Zvq2trarXLpfL8eSTT8aKFSuiubm5qtfmDHvOYc857DmPXeeo1Z4HBgais7Nz5Of4e/nAxcfwUy1tbW01iY+WlpZoa2vzjV1D9pzDnnPYcx67zlHrPZ/LSya84BQASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU44qPjRs3xnXXXRetra0xa9asWLNmTbz44oujzlm6dGkUCoVRH1/5yleqOjQAMHGNKz527doVXV1dsXfv3ujr64tyuRwrVqyIwcHBUed9+ctfjsOHD498PPTQQ1UdGgCYuMb1xnI7duwYdXvr1q0xa9as2L9/f9x4440jx1taWqK9vb06EwIADeWC3tW2v78/IiKmT58+6vi//du/xY9+9KNob2+P1atXx7333hstLS1nvUapVIpSqTRye2BgICLeede9crl8IeONMXy9al+X0ew5hz3nsOc8dp2jVnsez/UKlUqlcj53MjQ0FJ/73Ofi+PHj8cwzz4wc/6d/+qe44ooroqOjI55//vn467/+67j++uvjv//7v896nQ0bNkRPT8+Y4729ve8aLADAB8upU6fitttui/7+/mhra3vPc887Pv7qr/4qHn/88XjmmWdi7ty573reT3/607jlllvi4MGD8dGPfnTM58/2yEdnZ2ccO3bsfYcfr3K5HH19fXHvvqYoDRWqem3OKDZV4oFFQ7F8+fJobm6u9zgNa/j72Z5ry57z2HWOWu15YGAgZs6ceU7xcV5Pu9x1113x2GOPxe7du98zPCIiFi9eHBHxrvFRLBajWCyOOd7c3Fyzb77SUCFKp8VHrdXya8gZ9pzDnvPYdY5q73k81xpXfFQqlbj77rtj27Zt8fTTT8f8+fPf988cOHAgIiLmzJkznrsCABrUuOKjq6srent749FHH43W1tY4cuRIRERMmzYtLrnkkjh06FD09vbGn/zJn8SMGTPi+eefj6997Wtx4403xoIFC2ryfwAAmFjGFR9btmyJiHd+kdjvevjhh+P222+PKVOmxFNPPRWbNm2KwcHB6OzsjLVr18Y3v/nNqg0MAExs437a5b10dnbGrl27LmggAKCxeW8XACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACDVuOJj48aNcd1110Vra2vMmjUr1qxZEy+++OKoc956663o6uqKGTNmxKWXXhpr166No0ePVnVoAGDiGld87Nq1K7q6umLv3r3R19cX5XI5VqxYEYODgyPnfO1rX4uf/OQn8Z//+Z+xa9eueO211+LP//zPqz44ADAxTR7PyTt27Bh1e+vWrTFr1qzYv39/3HjjjdHf3x///M//HL29vXHzzTdHRMTDDz8cn/jEJ2Lv3r3xR3/0R9WbHACYkC7oNR/9/f0RETF9+vSIiNi/f3+Uy+VYtmzZyDlXXXVVzJs3L/bs2XMhdwUANIhxPfLxu4aGhuKee+6Jz3zmM3H11VdHRMSRI0diypQpcdlll406d/bs2XHkyJGzXqdUKkWpVBq5PTAwEBER5XI5yuXy+Y53VsPXKzZVqnpdRhveb7W/fow2vF97ri17zmPXOWq15/Fc77zjo6urK1544YV45plnzvcSEfHOi1h7enrGHH/yySejpaXlgq79bh5YNFST6zJaX19fvUe4KNhzDnvOY9c5qr3nU6dOnfO55xUfd911Vzz22GOxe/fumDt37sjx9vb2ePvtt+P48eOjHv04evRotLe3n/Va69evj+7u7pHbAwMD0dnZGStWrIi2trbzGe9dlcvl6Ovri3v3NUVpqFDVa3NGsakSDywaiuXLl0dzc3O9x2lYw9/P9lxb9pzHrnPUas/Dz1yci3HFR6VSibvvvju2bdsWTz/9dMyfP3/U56+99tpobm6OnTt3xtq1ayMi4sUXX4xf//rXsWTJkrNes1gsRrFYHHO8ubm5Zt98paFClE6Lj1qr5deQM+w5hz3nsesc1d7zeK41rvjo6uqK3t7eePTRR6O1tXXkdRzTpk2LSy65JKZNmxZ33nlndHd3x/Tp06OtrS3uvvvuWLJkiX/pAgBExDjjY8uWLRERsXTp0lHHH3744bj99tsjIuLv//7vo6mpKdauXRulUilWrlwZ3/ve96oyLAAw8Y37aZf3M3Xq1Ni8eXNs3rz5vIcCABqX93YBAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAg1bjjY/fu3bF69ero6OiIQqEQ27dvH/X522+/PQqFwqiPz372s9WaFwCY4MYdH4ODg7Fw4cLYvHnzu57z2c9+Ng4fPjzy8e///u8XNCQA0Dgmj/cPrFq1KlatWvWe5xSLxWhvbz/voQCAxjXu+DgXTz/9dMyaNSv+4A/+IG6++eZ48MEHY8aMGWc9t1QqRalUGrk9MDAQERHlcjnK5XJV5xq+XrGpUtXrMtrwfqv99WO04f3ac23Zcx67zlGrPY/neoVKpXLeP4kLhUJs27Yt1qxZM3LskUceiZaWlpg/f34cOnQo/vZv/zYuvfTS2LNnT0yaNGnMNTZs2BA9PT1jjvf29kZLS8v5jgYAJDp16lTcdttt0d/fH21tbe95btXj4/f96le/io9+9KPx1FNPxS233DLm82d75KOzszOOHTv2vsOPV7lcjr6+vrh3X1OUhgpVvTZnFJsq8cCioVi+fHk0NzfXe5yGNfz9bM+1Zc957DpHrfY8MDAQM2fOPKf4qMnTLr/rIx/5SMycOTMOHjx41vgoFotRLBbHHG9ubq7ZN19pqBCl0+Kj1mr5NeQMe85hz3nsOke19zyea9X893y8+uqr8eabb8acOXNqfVcAwAQw7kc+Tp48GQcPHhy5/fLLL8eBAwdi+vTpMX369Ojp6Ym1a9dGe3t7HDp0KL7xjW/Exz72sVi5cmVVBwcAJqZxx8e+ffvipptuGrnd3d0dERHr1q2LLVu2xPPPPx//+q//GsePH4+Ojo5YsWJFPPDAA2d9agUAuPiMOz6WLl0a7/Ua1SeeeOKCBgIAGpv3dgEAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUo07Pnbv3h2rV6+Ojo6OKBQKsX379lGfr1Qqcd9998WcOXPikksuiWXLlsVLL71UrXkBgAlu3PExODgYCxcujM2bN5/18w899FD84z/+Y3z/+9+Pn/3sZ/GhD30oVq5cGW+99dYFDwsATHyTx/sHVq1aFatWrTrr5yqVSmzatCm++c1vxuc///mIiPjhD38Ys2fPju3bt8df/MVfXNi0AMCEN+74eC8vv/xyHDlyJJYtWzZybNq0abF48eLYs2fPWeOjVCpFqVQauT0wMBAR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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "end_wealth = agent_wealth.xs(99, level=\"Step\")[\"Wealth\"]\n", "end_wealth.hist(bins=range(agent_wealth.Wealth.max() + 1))" @@ -727,9 +928,30 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 52, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 52, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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6OzZs5o+fbrmzp2rG2+8UZMnT5bU2sOxfft2FRUVWc+ZP3++rrnmGpWUlFjHnn76ad1555364IMPrOfdfvvt2rBhg3XO7NmzNX36dP3whz9MzDcHIG7oGQEQMzfccIM++OAD/eIXv9DChQtVWVmp6dOna/PmzRGf88Ybb+j++++3elays7O1YsUK1dTU6MyZM9Z5hYWFYc8rLCykZwQYIJjACiCmMjMzde211+raa6/V3Xffrdtuu0333nuvbr755i7Pb2xs1H333acvfvGLXX4tAAMfPSMA4mrixIny+/2SpPT0dAWDwbDHp0+frqNHj2rcuHHn3dzu9reoffv2hT1v3759uuyyy+L/DQCIO3pGAMTExx9/rKVLl+qrX/2qJk+eLJ/Pp6qqKn3ve9/TkiVLJLWuqKmoqNDVV18tr9eroUOH6p577tEXvvAFjR49Wl/60pfkdrv1xhtv6MiRI/r3f/936+tv27ZNM2fO1Gc/+1k988wz2r9/v5544gm7vl0AMcQEVgAxEQgE9G//9m/61a9+pT/96U9qbm5WQUGBli5dqrvuukuDBg3SL3/5S61du1Z/+ctfNHLkSGtp786dO3X//ffr0KFDSk9P14QJE3TbbbdpxYoVklonsD722GMqKyvTrl27NHz4cH33u9/VP/zDP9j4HQOIFcIIAMfrahUOgIGDOSMAAMBWhBEAAGArJrACcDxGk4GBjZ4RAABgK8IIAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBW/x98HZPml6qnJAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "one_agent_wealth = agent_wealth.xs(14, level=\"AgentID\")\n", "one_agent_wealth.Wealth.plot()" @@ -746,7 +968,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 53, "metadata": {}, "outputs": [], "source": [ @@ -775,7 +997,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 54, "metadata": {}, "outputs": [], "source": [ @@ -880,9 +1102,17 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 55, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 245/245 [00:48<00:00, 5.10it/s]\n" + ] + } + ], "source": [ "params = {\"width\": 10, \"height\": 10, \"N\": range(10, 500, 10)}\n", "\n", @@ -906,9 +1136,19 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 56, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Index(['RunId', 'iteration', 'Step', 'width', 'height', 'N', 'Gini', 'AgentID',\n", + " 'Wealth'],\n", + " dtype='object')\n" + ] + } + ], "source": [ "import pandas as pd\n", "\n", @@ -925,9 +1165,30 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 57, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "" + ] + }, + "execution_count": 57, + "metadata": {}, + "output_type": "execute_result" + }, + { + "data": { + "image/png": "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Z4SIiMh/GIx4wTkjRERE/sNgxIv0xEjEWi3wNW3EgM5KrOmJkf5sFhERUVBiMOJFiNGAgjlJANAtIHH9v2BOEteoISIi0gCDER9mTYrFuoenwWb1HIqxWS1Y9/A0rk1DRESkES6UJ2PWpFjMTLJx1V4iIqI+xGBEQYjRgMxxI/RuBhERUdDiMA0RERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemqR8HI2rVrkZCQAIvFgoyMDJSVlQk9b8uWLTAYDLjvvvt68rZEREQUhFQHI1u3bkVeXh4KCgpw4MABJCcnIzs7G/X19bLPO3nyJP7t3/4Nt956a48bS0RERMFHdTCyevVqLFiwALm5uUhKSsL69esRFhaGTZs2+XxOR0cHHnroIaxYsQJjx47tVYOJiIgouKgKRtrb21FeXo6srKyrL2A0IisrC6WlpT6f99xzzyE6OhqPPvqo0Pu0tbWhqanJ40ZERETBSVUw0tDQgI6ODsTExHjcHxMTA7vd7vU5e/fuxcaNG7Fhwwbh9yksLITVanXf4uPj1TSTiIiIAkifZtM0NzfjkUcewYYNGxAVFSX8vPz8fDgcDvetpqamD1tJREREehqkZuOoqCiEhISgrq7O4/66ujrYbLZu2584cQInT57EnDlz3Pc5nc7ONx40CMeOHcO4ceO6Pc9sNsNsNqtpGhEREQUoVT0jJpMJqampKCkpcd/ndDpRUlKCzMzMbttPnDgRhw8fxqFDh9y3e+65B//0T/+EQ4cOcfiFiIiI1PWMAEBeXh7mz5+PtLQ0pKeno6ioCC0tLcjNzQUAzJs3D3FxcSgsLITFYsGkSZM8nh8REQEA3e4nIiKigUl1MJKTk4MzZ85g+fLlsNvtSElJQXFxsXtSa3V1NYxGFnYlIiIiMQZJkiS9G6GkqakJVqsVDocD4eHhejeHiIiIBIiev9mFQURERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJE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0FpPtiMiIqLeG1DBSHpiJGKtFvhK4DWgM6smPdH3ujlERESkrQEVjIQYDSiYkwQA3QIS1/8L5iSx3ggREZEfDahgBABmTYrFuoenwWb1HIqxWS1M6yUiItKB6qJnwWDWpFjMTLKxAisREVE/MCCDEaBzyCZz3Ai9m0FERDTgDbhhGiIiIupfGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka56FIysXbsWCQkJsFgsyMjIQFlZmc9tv/jiC9x///1ISEiAwWBAUVFRT9tKREREQUh1MLJ161bk5eWhoKAABw4cQHJyMrKzs1FfX+91+4sXL2Ls2LFYtWoVbDZbrxtMREREwUV1MLJ69WosWLAAubm5SEpKwvr16xEWFoZNmzZ53X769Ol44YUX8MADD8BsNve6wURERBRcVAUj7e3tKC8vR1ZW1tUXMBqRlZWF0tJSzRrV1taGpqYmjxsREREFJ1XBSENDAzo6OhATE+Nxf0xMDOx2u2aNKiwshNVqdd/i4+M1e20iIiLqX/plNk1+fj4cDof7VlNTo3eTiIiIqI8MUrNxVFQUQkJCUFdX53F/XV2dppNTzWYz55cQERENEKp6RkwmE1JTU1FSUuK+z+l0oqSkBJmZmZo3joiIiIKfqp4RAMjLy8P8+fORlpaG9PR0FBUVoaWlBbm5uQCAefPmIS4uDoWFhQA6J71WVla6/3369GkcOnQIQ4cOxfjx4zX8KERERBSIVAcjOTk5OHPmDJYvXw673Y6UlBQUFxe7J7VWV1fDaLza4fLtt99i6tSp7v+/+OKLePHFF3H77bdjz549vf8EREREFNAMkiRJejdCSVNTE6xWKxwOB8LDw/VuDhEREQkQPX/3y2waIiIiGjgYjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuBundAL10OCWUVTWivrkV0cMsSE+MRIjRoHeziIiIBpwe9YysXbsWCQkJsFgsyMjIQFlZmez2f/7znzFx4kRYLBZMnjwZO3fu7FFjtVJcUYtbnt+NuRv2YcmWQ5i7YR9ueX43iitqdW0XERHRQKQ6GNm6dSvy8vJQUFCAAwcOIDk5GdnZ2aivr/e6/ccff4y5c+fi0UcfxcGDB3HffffhvvvuQ0VFRa8b3xPFFbVYuPkAah2tHvfbHa1YuPkAAxIiIiI/M0iSJKl5QkZGBqZPn441a9YAAJxOJ+Lj4/HEE09g6dKl3bbPyclBS0sL3nnnHfd9N910E1JSUrB+/Xqh92xqaoLVaoXD4UB4eLia5nrocEq45fnd3QIRFwMAm9WCvU/dwSEbIiKiXhI9f6vqGWlvb0d5eTmysrKuvoDRiKysLJSWlnp9Tmlpqcf2AJCdne1zewBoa2tDU1OTx00LZVWNPgMRAJAA1DpaUVbVqMn7ERERkTJVwUhDQwM6OjoQExPjcX9MTAzsdrvX59jtdlXbA0BhYSGsVqv7Fh8fr6aZPtU3+w5EerIdERER9V6/TO3Nz8+Hw+Fw32pqajR53ehhFk23IyIiot5TldobFRWFkJAQ1NXVedxfV1cHm83m9Tk2m03V9gBgNpthNpvVNE1IemIkYq0W2B2t8DZRxjVnJD0xUvP3JiIiIu9U9YyYTCakpqaipKTEfZ/T6URJSQkyMzO9PiczM9NjewDYtWuXz+37UojRgII5SQA6A4+uXP8vmJPEyatERER+pHqYJi8vDxs2bMCf/vQnHDlyBAsXLkRLSwtyc3MBAPPmzUN+fr57+yVLlqC4uBj/+Z//iaNHj+LZZ5/Fp59+isWLF2v3KVSYNSkW6x6eBpvVcyjGZrVg3cPTMGtSrC7tIiIiGqhUV2DNycnBmTNnsHz5ctjtdqSkpKC4uNg9SbW6uhpG49UYZ8aMGXjttdfwzDPP4Omnn8aECRPw5ptvYtKkSdp9CpVmTYrFzCQbK7ASERH1A6rrjOhBqzojRERE5D99UmeEiIiISGsMRoiIiEhXDEaIiIhIVwxGiIiISFcMRoiIiEhXDEaIiIhIVwxGiIiISFcMRoiIiEhXDEaIiIhIV6rLwevBVSS2qalJ55YQERGRKNd5W6nYe0AEI83NzQCA+Ph4nVtCREREajU3N8Nqtfp8PCDWpnE6nfj2228xbNgwGAxii9k1NTUhPj4eNTU1XM/Gj7jf9cH9rg/ud31wv+ujJ/tdkiQ0Nzdj1KhRHovoXisgekaMRiOuu+66Hj03PDycX1YdcL/rg/tdH9zv+uB+14fa/S7XI+LCCaxERESkKwYjREREpKugDUbMZjMKCgpgNpv1bsqAwv2uD+53fXC/64P7XR99ud8DYgIrERERBa+g7RkhIiKiwMBghIiIiHTFYISIiIh0xWCEiIiIdBWUwcjatWuRkJAAi8WCjIwMlJWV6d2kgPbBBx9gzpw5GDVqFAwGA958802PxyVJwvLlyxEbG4vBgwcjKysLX331lcc2jY2NeOihhxAeHo6IiAg8+uijuHDhgh8/ReApLCzE9OnTMWzYMERHR+O+++7DsWPHPLZpbW3FokWLMGLECAwdOhT3338/6urqPLaprq7G7NmzERYWhujoaPz7v/87rly54s+PElDWrVuHKVOmuAs7ZWZm4m9/+5v7ce7zvrdq1SoYDAb8/Oc/d9/H/d43nn32WRgMBo/bxIkT3Y/7bb9LQWbLli2SyWSSNm3aJH3xxRfSggULpIiICKmurk7vpgWsnTt3Sr/85S+lbdu2SQCkN954w+PxVatWSVarVXrzzTelzz77TLrnnnukxMRE6dKlS+5tZs2aJSUnJ0v79u2TPvzwQ2n8+PHS3Llz/fxJAkt2drb08ssvSxUVFdKhQ4eku+++Wxo9erR04cIF9zaPP/64FB8fL5WUlEiffvqpdNNNN0kzZsxwP37lyhVp0qRJUlZWlnTw4EFp586dUlRUlJSfn6/HRwoIb731lrRjxw7pyy+/lI4dOyY9/fTTUmhoqFRRUSFJEvd5XysrK5MSEhKkKVOmSEuWLHHfz/3eNwoKCqTvfe97Um1trft25swZ9+P+2u9BF4ykp6dLixYtcv+/o6NDGjVqlFRYWKhjq4LHtcGI0+mUbDab9MILL7jvO3/+vGQ2m6XXX39dkiRJqqyslABI+/fvd2/zt7/9TTIYDNLp06f91vZAV19fLwGQ3n//fUmSOvdzaGio9Oc//9m9zZEjRyQAUmlpqSRJnYGk0WiU7Ha7e5t169ZJ4eHhUltbm38/QAAbPny49Mc//pH7vI81NzdLEyZMkHbt2iXdfvvt7mCE+73vFBQUSMnJyV4f8+d+D6phmvb2dpSXlyMrK8t9n9FoRFZWFkpLS3VsWfCqqqqC3W732OdWqxUZGRnufV5aWoqIiAikpaW5t8nKyoLRaMQnn3zi9zYHKofDAQCIjIwEAJSXl+Py5cse+37ixIkYPXq0x76fPHkyYmJi3NtkZ2ejqakJX3zxhR9bH5g6OjqwZcsWtLS0IDMzk/u8jy1atAizZ8/22L8Av+t97auvvsKoUaMwduxYPPTQQ6iurgbg3/0eEAvliWpoaEBHR4fHTgGAmJgYHD16VKdWBTe73Q4AXve56zG73Y7o6GiPxwcNGoTIyEj3NiTP6XTi5z//OW6++WZMmjQJQOd+NZlMiIiI8Nj22n3v7W/jeoy8O3z4MDIzM9Ha2oqhQ4fijTfeQFJSEg4dOsR93ke2bNmCAwcOYP/+/d0e43e972RkZOCVV17BDTfcgNraWqxYsQK33norKioq/LrfgyoYIQpWixYtQkVFBfbu3at3UwaEG264AYcOHYLD4cBf/vIXzJ8/H++//77ezQpaNTU1WLJkCXbt2gWLxaJ3cwaUH/zgB+5/T5kyBRkZGRgzZgz+7//+D4MHD/ZbO4JqmCYqKgohISHdZvrW1dXBZrPp1Krg5tqvcvvcZrOhvr7e4/ErV66gsbGRfxcBixcvxjvvvIP33nsP1113nft+m82G9vZ2nD9/3mP7a/e9t7+N6zHyzmQyYfz48UhNTUVhYSGSk5Pxu9/9jvu8j5SXl6O+vh7Tpk3DoEGDMGjQILz//vv47//+bwwaNAgxMTHc734SERGB66+/HsePH/fr9z2oghGTyYTU1FSUlJS473M6nSgpKUFmZqaOLQteiYmJsNlsHvu8qakJn3zyiXufZ2Zm4vz58ygvL3dvs3v3bjidTmRkZPi9zYFCkiQsXrwYb7zxBnbv3o3ExESPx1NTUxEaGuqx748dO4bq6mqPfX/48GGPYHDXrl0IDw9HUlKSfz5IEHA6nWhra+M+7yN33nknDh8+jEOHDrlvaWlpeOihh9z/5n73jwsXLuDEiROIjY317/e9R9Nv+7EtW7ZIZrNZeuWVV6TKykrpsccekyIiIjxm+pI6zc3N0sGDB6WDBw9KAKTVq1dLBw8elE6dOiVJUmdqb0REhLR9+3bp888/l+69916vqb1Tp06VPvnkE2nv3r3ShAkTmNqrYOHChZLVapX27NnjkXZ38eJF9zaPP/64NHr0aGn37t3Sp59+KmVmZkqZmZnux11pd3fddZd06NAhqbi4WBo5ciTTHWUsXbpUev/996Wqqirp888/l5YuXSoZDAbpH//4hyRJ3Of+0jWbRpK43/vKk08+Ke3Zs0eqqqqSPvroIykrK0uKioqS6uvrJUny334PumBEkiTp97//vTR69GjJZDJJ6enp0r59+/RuUkB77733JADdbvPnz5ckqTO9d9myZVJMTIxkNpulO++8Uzp27JjHa5w9e1aaO3euNHToUCk8PFzKzc2Vmpubdfg0gcPbPgcgvfzyy+5tLl26JP3sZz+Thg8fLoWFhUk//OEPpdraWo/XOXnypPSDH/xAGjx4sBQVFSU9+eST0uXLl/38aQLHT37yE2nMmDGSyWSSRo4cKd15553uQESSuM/95dpghPu9b+Tk5EixsbGSyWSS4uLipJycHOn48ePux/213w2SJEm96tMhIiIi6oWgmjNCREREgYfBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHp6v8Duou0AvfZ0jgAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ "results_filtered = results_df[(results_df.AgentID == 0) & (results_df.Step == 100)]\n", "N_values = results_filtered.N.values\n", @@ -947,9 +1208,42 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Step AgentID Wealth\n", + " 0 0 1\n", + " 0 1 1\n", + " 0 2 1\n", + " 0 3 1\n", + " 0 4 1\n", + " 0 5 1\n", + " 0 6 1\n", + " 0 7 1\n", + " 0 8 1\n", + " 0 9 1\n", + " 1 0 1\n", + " 1 1 1\n", + " ... ... ...\n", + " 99 8 1\n", + " 99 9 0\n", + " 100 0 0\n", + " 100 1 1\n", + " 100 2 1\n", + " 100 3 1\n", + " 100 4 1\n", + " 100 5 2\n", + " 100 6 1\n", + " 100 7 2\n", + " 100 8 1\n", + " 100 9 0\n" + ] + } + ], "source": [ "# First, we filter the results\n", "one_episode_wealth = results_df[(results_df.N == 10) & (results_df.iteration == 2)]\n", @@ -973,9 +1267,42 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], + "execution_count": 59, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + " Step Gini\n", + " 0 0.00\n", + " 1 0.00\n", + " 2 0.00\n", + " 3 0.00\n", + " 4 0.00\n", + " 5 0.00\n", + " 6 0.00\n", + " 7 0.00\n", + " 8 0.00\n", + " 9 0.00\n", + " 10 0.00\n", + " 11 0.00\n", + " ... ...\n", + " 89 0.18\n", + " 90 0.18\n", + " 91 0.18\n", + " 92 0.18\n", + " 93 0.18\n", + " 94 0.18\n", + " 95 0.18\n", + " 96 0.18\n", + " 97 0.18\n", + " 98 0.18\n", + " 99 0.18\n", + " 100 0.18\n" + ] + } + ], "source": [ "results_one_episode = results_df[\n", " (results_df.N == 10) & (results_df.iteration == 1) & (results_df.AgentID == 0)\n", @@ -1002,12 +1329,47 @@ "\n", "[Dragulescu2002] Drăgulescu, Adrian A., and Victor M. Yakovenko. “Statistical Mechanics of Money, Income, and Wealth: A Short Survey.” arXiv Preprint Cond-mat/0211175, 2002. http://arxiv.org/abs/cond-mat/0211175." ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "anaconda-cloud": {}, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1021,7 +1383,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.6" + "version": "3.8.16" }, "widgets": { "state": {}, From 34eb008aa849c70f80e0fa69198d82ba2bf82193 Mon Sep 17 00:00:00 2001 From: Jatin Khilnani <43829573+jatinkhilnani@users.noreply.github.com> Date: Mon, 24 Apr 2023 19:45:37 -0400 Subject: [PATCH 083/214] Fix: Handle stop server (#1646) --- mesa/visualization/ModularVisualization.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py index d723a50b1e6..6e594f79f51 100644 --- a/mesa/visualization/ModularVisualization.py +++ b/mesa/visualization/ModularVisualization.py @@ -404,7 +404,10 @@ def launch(self, port=None, open_browser=True): if open_browser: webbrowser.open(url) tornado.autoreload.start() - tornado.ioloop.IOLoop.current().start() + try: + tornado.ioloop.IOLoop.current().start() + except KeyboardInterrupt: + tornado.ioloop.IOLoop.current().stop() @staticmethod def _is_stylesheet(filename): From 3b5749ae6078aa32745628ff3004eb3b4b59ad50 Mon Sep 17 00:00:00 2001 From: Glenn Lehman <55802043+glnnlhmn@users.noreply.github.com> Date: Tue, 25 Apr 2023 09:00:21 -0600 Subject: [PATCH 084/214] Rewrite intro tutorial text and generate rst for advanced tutorial (#1650) Tutorial Description - reworked the text, removed the term 'easy' and replaced with 'starter-level', removed note indicating this is a work in progress Model Description - Defined some of the terms associated with agent-based economy. Installation - Renamed to Prerequisites Setup, removed dependency on requirements.txt, marked Jupyter install as optional, added matplotlib as a requirement. Changed virtual environments link. This allowed them to be removed from the example folder project. Building the Sample Model - Completly reworked to more clearly define working in Juypter notebook and script files. --- docs/tutorials/adv_tutorial.rst | 158 ++++----- docs/tutorials/intro_tutorial.ipynb | 533 ++++++++++++++-------------- docs/tutorials/intro_tutorial.rst | 366 ++++++++++--------- 3 files changed, 557 insertions(+), 500 deletions(-) diff --git a/docs/tutorials/adv_tutorial.rst b/docs/tutorials/adv_tutorial.rst index 5f23a0e864b..031c2ee70ba 100644 --- a/docs/tutorials/adv_tutorial.rst +++ b/docs/tutorials/adv_tutorial.rst @@ -17,9 +17,10 @@ create a new visualization element. **Note for Jupyter users: Due to conflicts with the tornado server Mesa uses and Jupyter, the interactive browser of your model will load but -likely not work. This will require you to run the code from .py +likely not work. This will require you to use run the code from .py files. The Mesa development team is working to develop a** `Jupyter -compatible interface `_. +compatible +interface `__.*\* First, a quick explanation of how Mesa’s interactive visualization works. Visualization is done in a browser window, using JavaScript to @@ -35,7 +36,6 @@ server and turns a model state into JSON data; and a JavaScript side, which takes that JSON data and draws it in the browser window. Mesa comes with a few modules built in, and let you add your own as well. - Grid Visualization ^^^^^^^^^^^^^^^^^^ @@ -328,9 +328,9 @@ the class itself: .. code:: javascript - const HistogramModule = function(bins, canvas_width, canvas_height) { - // The actual code will go here. - }; + const HistogramModule = function(bins, canvas_width, canvas_height) { + // The actual code will go here. + }; Note that our object is instantiated with three arguments: the number of integer bins, and the width and height (in pixels) the chart will take @@ -339,27 +339,26 @@ up in the visualization window. When the visualization object is instantiated, the first thing it needs to do is prepare to draw on the current page. To do so, it adds a `canvas `__ -tag to the page. It also gets the canvas' context, which is required for doing -anything with it. +tag to the page. It also gets the canvas’ context, which is required for +doing anything with it. .. code:: javascript - const HistogramModule = function(bins, canvas_width, canvas_height) { - // Create the canvas object: - const canvas = document.createElement("canvas"); - Object.assign(canvas, { - width: canvas_width, - height: canvas_height, - style: "border:1px dotted", - }); - // Append it to #elements: - const elements = document.getElementById("elements"); - elements.appendChild(canvas); - - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - }; - + const HistogramModule = function(bins, canvas_width, canvas_height) { + // Create the canvas object: + const canvas = document.createElement("canvas"); + Object.assign(canvas, { + width: canvas_width, + height: canvas_height, + style: "border:1px dotted", + }); + // Append it to #elements: + const elements = document.getElementById("elements"); + elements.appendChild(canvas); + + // Create the context and the drawing controller: + const context = canvas.getContext("2d"); + }; Look at the Charts.js `bar chart documentation `__. @@ -373,49 +372,49 @@ created, we can create the chart object. .. code:: javascript - const HistogramModule = function(bins, canvas_width, canvas_height) { - // Create the canvas object: - const canvas = document.createElement("canvas"); - Object.assign(canvas, { - width: canvas_width, - height: canvas_height, - style: "border:1px dotted", - }); - // Append it to #elements: - const elements = document.getElementById("elements"); - elements.appendChild(canvas); - - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - - // Prep the chart properties and series: - const datasets = [{ - label: "Data", - fillColor: "rgba(151,187,205,0.5)", - strokeColor: "rgba(151,187,205,0.8)", - highlightFill: "rgba(151,187,205,0.75)", - highlightStroke: "rgba(151,187,205,1)", - data: [] - }]; - - // Add a zero value for each bin - for (var i in bins) - datasets[0].data.push(0); - - const data = { - labels: bins, - datasets: datasets - }; - - const options = { - scaleBeginsAtZero: true - }; - - // Create the chart object - const chart = new Chart(context, {type: 'bar', data: data, options: options}); - - // Now what? - }; + const HistogramModule = function(bins, canvas_width, canvas_height) { + // Create the canvas object: + const canvas = document.createElement("canvas"); + Object.assign(canvas, { + width: canvas_width, + height: canvas_height, + style: "border:1px dotted", + }); + // Append it to #elements: + const elements = document.getElementById("elements"); + elements.appendChild(canvas); + + // Create the context and the drawing controller: + const context = canvas.getContext("2d"); + + // Prep the chart properties and series: + const datasets = [{ + label: "Data", + fillColor: "rgba(151,187,205,0.5)", + strokeColor: "rgba(151,187,205,0.8)", + highlightFill: "rgba(151,187,205,0.75)", + highlightStroke: "rgba(151,187,205,1)", + data: [] + }]; + + // Add a zero value for each bin + for (var i in bins) + datasets[0].data.push(0); + + const data = { + labels: bins, + datasets: datasets + }; + + const options = { + scaleBeginsAtZero: true + }; + + // Create the chart object + const chart = new Chart(context, {type: 'bar', data: data, options: options}); + + // Now what? + }; There are two methods every client-side visualization class must implement to be able to work: ``render(data)`` to render the incoming @@ -433,18 +432,19 @@ With that in mind, we can add these two methods to the class: .. code:: javascript - const HistogramModule = function(bins, canvas_width, canvas_height) { - // ...Everything from above... - this.render = function(data) { - datasets[0].data = data; - chart.update(); - }; - - this.reset = function() { - chart.destroy(); - chart = new Chart(context, {type: 'bar', data: data, options: options}); - }; - }; + const HistogramModule = function(bins, canvas_width, canvas_height) { + // ...Everything from above... + this.render = function(data) { + datasets[0].data = data; + chart.update(); + }; + + this.reset = function() { + chart.destroy(); + chart = new Chart(context, {type: 'bar', data: data, options: options}); + }; + }; + Note the ``this``. before the method names. This makes them public and ensures that they are accessible outside of the object itself. All the other variables inside the class are only accessible inside the object diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 4c1e8c367a5..d0ce846f550 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -13,74 +13,98 @@ "source": [ "## Tutorial Description\n", "\n", - "[Mesa](https://github.com/projectmesa/mesa) is a Python framework for [agent-based modeling](https://en.wikipedia.org/wiki/Agent-based_model). Getting started with Mesa is easy. In this tutorial, we will walk through creating a simple model and progressively add functionality which will illustrate Mesa's core features.\n", + "[Mesa](https://github.com/projectmesa/mesa) is a Python framework for [agent-based modeling](https://en.wikipedia.org/wiki/Agent-based_model). This tutorial will assist you in getting started. Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone find any errors, bugs, have a suggestion, or just are looking for clarification, [let us know](https://github.com/projectmesa/mesa/issues)!\n", "\n", - "**Note:** This tutorial is a work-in-progress. If you find any errors or bugs, or just find something unclear or confusing, [let us know](https://github.com/projectmesa/mesa/issues)!\n", + "The premise of this tutorial is to create a starter-level model representing agents exchanging money. This exchange of money affects wealth. Next, *space* is added to allow agents to move based on the change in wealth as time progresses.\n", "\n", - "The base for this tutorial is a very simple model of agents exchanging money. Next, we add *space* to allow agents to move. Then, we'll cover two of Mesa's analytic tools: the *data collector* and *batch runner*. After that, we'll add an *interactive visualization* which lets us watch the model as it runs. Finally, we go over how to write your own visualization module, for users who are comfortable with JavaScript.\n", + "Two of Mesa's analytic tools: the *data collector* and *batch runner* will be used to examine this movement. After that an *interactive visualization* is added which allows model viewing as it runs.\n", "\n", - "You can also find all the code this tutorial describes in the **examples/boltzmann_wealth_model** directory of the Mesa repository." + "Finally, the creation of a custom visualization module in JavaScript is explored." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Sample Model Description\n", + "'## Model Description\n", "\n", - "The tutorial model is a very simple simulated agent-based economy, drawn from econophysics and presenting a statistical mechanics approach to wealth distribution [Dragulescu2002]. The rules of our tutorial model:\n", + "This is a starter-level simulated agent-based economy. In an agent-based economy, the behavior of an\n", + "individual economic agent, such as a consumer or producer, is studied in a market environment.\n", + "This model is drawn from the field econophysics, specifically a paper prepared by Drăgulescu et al.\n", + "for additional information on the modeling assumptions used in this model.\n", + "[Drăgulescu, 2002].\n", + "\n", + "The assumption that govern this model are:\n", "\n", "1. There are some number of agents.\n", "2. All agents begin with 1 unit of money.\n", - "3. At every step of the model, an agent gives 1 unit of money (if they have it) to some other agent.\n", - "\n", - "Despite its simplicity, this model yields results that are often unexpected to those not familiar with it. For our purposes, it also easily demonstrates Mesa's core features.\n", + "3. At every step of the model, an agent with money gives 1 unit of money.\n", "\n", - "Let's get started." + "Even as a starter-level model the yielded results are both interesting and unexpected to individuals unfamiliar\n", + "with it the specific topic. As such, this model is a good starting point to examine Mesa's core features." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Installation\n", + "### Prerequisites Setup\n", "\n", - "To start, install Mesa. We recommend doing this in a [virtual environment](https://virtualenvwrapper.readthedocs.org/en/stable/), but make sure your environment is set up with Python 3. Mesa requires Python3 and does not work in Python 2 environments.\n", + "Create and activate a [virtual environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/). *Python version 3.8 or higher is required*.\n", + "\n", + "Install Mesa:\n", "\n", - "To install Mesa, simply:\n", "\n", "```bash\n", - " pip install mesa\n", + "pip install mesa\n", "```\n", "\n", - "When you do that, it will install Mesa itself, as well as any dependencies that aren't in your setup yet. Additional dependencies required by this tutorial can be found in the **examples/boltzmann_wealth_model/requirements.txt** file, which can be installed directly form the github repository by running:\n", + "Install Jupyter Notebook (optional):\n", "\n", "```bash\n", - " pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt\n", + "pip install jupyter\n", "```\n", "\n", - "This will install the dependencies listed in the requirements.txt file which are: \n", - "- jupyter (Ipython interactive notebook) \n", - "- matplotlib (Python's visualization library) \n", - "- mesa (this ABM library -- if not installed) \n", - "- numpy (Python's numerical python library) " + "Install matplotlib:\n", + "\n", + "```bash\n", + "pip install matplotlib\n", + "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Building a sample model\n", + "## Building the Sample Model\n", + "\n", + "After Mesa is installed a model can be built. A jupyter notebook is recommended for this tutorial, this allows for small segments of codes to be examined one at a time. As an option this can be created using python script files.\n", + "\n", + "**Good Practice:** Place a model in its own folder/directory. This is not specifically required for the starter_model, but as other models become more complicated and expand multiple python scripts, documentation, discussions and notebooks may be added.\n", + "\n", + "### Create New Folder/Directory\n", + "\n", + "- Using operating system commands create a new folder/directory named 'starter_model'.\n", + "\n", + "- Change to the new folder/directory.\n", + "\n", + "\n", + "### Creating Model With Jupyter Notebook\n", + "\n", + "Write the model interactively in [Jupyter Notebook](http://jupyter.org/) cells.\n", + "\n", + "- Start Jupyter Notebook:\n", + "```bash\n", + "jupyter notebook\n", + "```\n", "\n", - "Once Mesa is installed, you can start building our model. You can write models in two different ways:\n", + "- Create a new Notebook named `money_model.ipynb` (or whatever you want to call it).\n", "\n", - "1. Write the code in its own file with your favorite text editor, or\n", - "2. Write the model interactively in [Jupyter Notebook](http://jupyter.org/) cells.\n", + "### Creating Model With Script File (IDE, Text Editor, Colab, etc.)\n", "\n", - "Either way, it's good practice to put your model in its own folder -- especially if the project will end up consisting of multiple files (for example, Python files for the model and the visualization, a Notebook for analysis, and a Readme with some documentation and discussion).\n", + "- Create a new file called `money_model.py` (or whatever you want to call it).\n", "\n", - "Begin by creating a folder, and either launch a Notebook or create a new Python source file. We will use the name `money_model.py` here.\n", - "\n" + "*Code will be added as the tutorial progresses.*" ] }, { @@ -100,8 +124,13 @@ }, { "cell_type": "code", - "execution_count": 36, - "metadata": {}, + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.193952Z", + "end_time": "2023-04-24T21:12:14.397412Z" + } + }, "outputs": [], "source": [ "import mesa\n", @@ -144,8 +173,13 @@ }, { "cell_type": "code", - "execution_count": 37, - "metadata": {}, + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.198980Z", + "end_time": "2023-04-24T21:12:14.398410Z" + } + }, "outputs": [], "source": [ "import mesa\n", @@ -194,23 +228,28 @@ }, { "cell_type": "code", - "execution_count": 38, - "metadata": {}, + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.203792Z", + "end_time": "2023-04-24T21:12:14.398410Z" + } + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Hi, I am agent 5.\n", - "Hi, I am agent 0.\n", - "Hi, I am agent 1.\n", - "Hi, I am agent 7.\n", + "Hi, I am agent 3.\n", "Hi, I am agent 8.\n", "Hi, I am agent 2.\n", "Hi, I am agent 4.\n", "Hi, I am agent 9.\n", - "Hi, I am agent 3.\n", - "Hi, I am agent 6.\n" + "Hi, I am agent 6.\n", + "Hi, I am agent 1.\n", + "Hi, I am agent 7.\n", + "Hi, I am agent 5.\n", + "Hi, I am agent 0.\n" ] } ], @@ -248,8 +287,13 @@ }, { "cell_type": "code", - "execution_count": 39, - "metadata": {}, + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.222303Z", + "end_time": "2023-04-24T21:12:14.500557Z" + } + }, "outputs": [], "source": [ "class MoneyAgent(mesa.Agent):\n", @@ -286,8 +330,13 @@ }, { "cell_type": "code", - "execution_count": 40, - "metadata": {}, + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.227319Z", + "end_time": "2023-04-24T21:12:14.501575Z" + } + }, "outputs": [], "source": [ "model = MoneyModel(10)\n", @@ -315,27 +364,26 @@ }, { "cell_type": "code", - "execution_count": 41, - "metadata": {}, + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.238809Z", + "end_time": "2023-04-24T21:12:14.613244Z" + } + }, "outputs": [ { "data": { - "text/plain": [ - "(array([6., 0., 1., 0., 0., 1., 0., 1., 0., 1.]),\n", - " array([0. , 0.4, 0.8, 1.2, 1.6, 2. , 2.4, 2.8, 3.2, 3.6, 4. ]),\n", - " )" - ] + "text/plain": "(array([5., 0., 0., 1., 0., 0., 3., 0., 0., 1.]),\n array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7, 3. ]),\n )" }, - "execution_count": 41, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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QI0YMSJGjBjRcfnCCy+M1157Le6999742c9+1ouT7V9jY2O8+OKL8Yc//KG3R6mKA91f0R6HI0aMiObm5mhra4tf//rXMXXq1FixYkWXP3iLKM8ei3b+WltbY9q0abFs2bJD+k2mhQiL7nxc+wknnJBrfW/qzv52d8QRR8Q555wTmzZtqsaIPa6r81dXVxdHHnlkL01VfePGjTvkf1jfdttt8eSTT8bKlStj2LBh+1xbpMfhLnn2t7tD/XFYW1sbp556akREnHfeebFmzZq4//77Y/78+XusLeK5i8i3x90d6udv3bp1sXXr1jj33HM7rtu5c2esXLkyfvSjH0WlUom+fft2uk1vnMdCvBTSnY9rHz9+fKf1ERHLli07JD/ePcXH0e/cuTNaWlpiyJAh1RqzRxXp/KXU3Nx8yJ7DLMvitttui0WLFsVzzz0XJ5988n5vU6Tz2J397a5oj8P29vaoVCp7/bcinbt92dced3eon79LL700Wlpaorm5ueMYO3ZsTJkyJZqbm/eIioheOo9Ve1toYgsXLsxKpVK2YMGC7OWXX86+8pWvZMccc0y2ZcuWLMuy7MYbb8xmzpzZsf7555/P+vXrl/3gBz/IXnnllWz27NnZEUcckbW0tPTWFvYp7/7uvPPObOnSpdlrr72WrVu3Lvvc5z6X9e/fP3vppZd6awv7tG3btmzDhg3Zhg0bsojI7rnnnmzDhg3Zm2++mWVZls2cOTO78cYbO9a//vrr2VFHHZXdcccd2SuvvJLNnTs369u3b7ZkyZLe2sJ+5d3jvffemy1evDh79dVXs5aWlmzatGlZnz59smeeeaa3trBPt956a1ZfX58tX748e++99zqOf/3rXx1rivw47M7+ivQ4nDlzZrZixYps8+bN2Z///Ods5syZWU1NTfa73/0uy7Jin7td8u6xSOevK7v/VsihcB4LExZZlmU//OEPsxNPPDGrra3Nxo0bl61evbrj3y666KJs6tSpndb/6le/yk477bSstrY2GzVqVPbUU0/18MT55Nnf9OnTO9Yef/zx2ac//els/fr1vTD1gdn1q5W7H7v2NHXq1Oyiiy7a4zZnn312Vltbm51yyinZww8/3ONz55F3j3fffXf2sY99LOvfv382cODAbOLEidlzzz3XO8MfgL3tLSI6nZciPw67s78iPQ6/+MUvZsOHD89qa2uzQYMGZZdeemnHD9wsK/a52yXvHot0/rqye1gcCufRx6YDAMkU4j0WAEAxCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBk/h9X3YomdlYsdQAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] + "text/plain": "
", + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -368,27 +416,26 @@ }, { "cell_type": "code", - "execution_count": 42, - "metadata": {}, + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.602152Z", + "end_time": "2023-04-24T21:12:14.700218Z" + } + }, "outputs": [ { "data": { - "text/plain": [ - "(array([431., 303., 153., 76., 26., 9., 1., 1.]),\n", - " array([0., 1., 2., 3., 4., 5., 6., 7., 8.]),\n", - " )" - ] + "text/plain": "(array([437., 292., 155., 80., 26., 10.]),\n array([0., 1., 2., 3., 4., 5., 6.]),\n )" }, - "execution_count": 42, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAd4ElEQVR4nO3de3BU9fnH8U8uJNyyGxNNQkoCeKkQuaiJwIq9SUqaRkaHaLGT2iiMTpnFAplSSYtQtRqkreIFQa0FO5pBbQsWHMQ01jAOAUJoOgEFtVWTadwEx7IL6bCB5Pz+6I+dRqga2HCebN+vmTPjnnN29/kOat6c7CXOcRxHAAAAhsS7PQAAAMCnESgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwJ9HtAc5ET0+P2tralJKSori4OLfHAQAAX4DjODpy5Iiys7MVH//Z10gGZKC0tbUpJyfH7TEAAMAZaG1t1ciRIz/znAEZKCkpKZL+vUCPx+PyNAAA4IsIhULKycmJ/Bz/LAMyUE7+Wsfj8RAoAAAMMF/k5Rm8SBYAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwJ9HtASwaveQVt0foVx+sKHF7BAAAPhNXUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAnLMKlBUrViguLk4LFy6M7Dt27Jj8fr/S09M1fPhwlZaWqr29vdf9WlpaVFJSoqFDhyojI0OLFy/WiRMnzmYUAAAQQ844UBoaGvTkk09q4sSJvfYvWrRImzdv1ksvvaS6ujq1tbVp1qxZkePd3d0qKSlRV1eXduzYoWeffVbr16/XsmXLznwVAAAgppxRoBw9elRlZWV6+umndd5550X2B4NBPfPMM3rooYd07bXXKj8/X+vWrdOOHTu0c+dOSdJrr72mt956S88995wuv/xyFRcX67777tPq1avV1dUVnVUBAIAB7YwCxe/3q6SkRIWFhb32NzY26vjx4732jx07Vrm5uaqvr5ck1dfXa8KECcrMzIycU1RUpFAopP3795/JOAAAIMYk9vUOGzZs0N69e9XQ0HDKsUAgoKSkJKWmpvban5mZqUAgEDnnP+Pk5PGTx04nHA4rHA5HbodCob6ODQAABpA+XUFpbW3VggUL9Pzzz2vw4MH9NdMpqqqq5PV6I1tOTs45e24AAHDu9SlQGhsb1dHRoSuvvFKJiYlKTExUXV2dHn30USUmJiozM1NdXV06fPhwr/u1t7crKytLkpSVlXXKu3pO3j55zqdVVlYqGAxGttbW1r6MDQAABpg+Bcr06dPV3NyspqamyFZQUKCysrLIPw8aNEi1tbWR+xw8eFAtLS3y+XySJJ/Pp+bmZnV0dETOqampkcfjUV5e3mmfNzk5WR6Pp9cGAABiV59eg5KSkqLx48f32jds2DClp6dH9s+dO1cVFRVKS0uTx+PRnXfeKZ/Pp6lTp0qSZsyYoby8PN1yyy1auXKlAoGAli5dKr/fr+Tk5CgtCwAADGR9fpHs53n44YcVHx+v0tJShcNhFRUV6YknnogcT0hI0JYtWzRv3jz5fD4NGzZM5eXluvfee6M9CgAAGKDiHMdx3B6ir0KhkLxer4LBYL/8umf0klei/piWfLCixO0RAAD/g/ry85vv4gEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5fQqUNWvWaOLEifJ4PPJ4PPL5fNq6dWvk+LFjx+T3+5Wenq7hw4ertLRU7e3tvR6jpaVFJSUlGjp0qDIyMrR48WKdOHEiOqsBAAAxoU+BMnLkSK1YsUKNjY3as2ePrr32Wl1//fXav3+/JGnRokXavHmzXnrpJdXV1amtrU2zZs2K3L+7u1slJSXq6urSjh079Oyzz2r9+vVatmxZdFcFAAAGtDjHcZyzeYC0tDT94he/0I033qgLLrhA1dXVuvHGGyVJBw4c0Lhx41RfX6+pU6dq69atuu6669TW1qbMzExJ0tq1a3XXXXfp0KFDSkpK+kLPGQqF5PV6FQwG5fF4zmb80xq95JWoPybOnQ9WlLg9AgDgNPry8/uMX4PS3d2tDRs2qLOzUz6fT42NjTp+/LgKCwsj54wdO1a5ubmqr6+XJNXX12vChAmROJGkoqIihUKhyFWY0wmHwwqFQr02AAAQu/ocKM3NzRo+fLiSk5P1gx/8QBs3blReXp4CgYCSkpKUmpra6/zMzEwFAgFJUiAQ6BUnJ4+fPPbfVFVVyev1RracnJy+jg0AAAaQPgfKpZdeqqamJu3atUvz5s1TeXm53nrrrf6YLaKyslLBYDCytba29uvzAQAAdyX29Q5JSUm6+OKLJUn5+flqaGjQI488otmzZ6urq0uHDx/udRWlvb1dWVlZkqSsrCzt3r271+OdfJfPyXNOJzk5WcnJyX0dFQAADFBn/TkoPT09CofDys/P16BBg1RbWxs5dvDgQbW0tMjn80mSfD6fmpub1dHRETmnpqZGHo9HeXl5ZzsKAACIEX26glJZWani4mLl5ubqyJEjqq6u1htvvKFt27bJ6/Vq7ty5qqioUFpamjwej+688075fD5NnTpVkjRjxgzl5eXplltu0cqVKxUIBLR06VL5/X6ukAAAgIg+BUpHR4e+//3v66OPPpLX69XEiRO1bds2ffOb35QkPfzww4qPj1dpaanC4bCKior0xBNPRO6fkJCgLVu2aN68efL5fBo2bJjKy8t17733RndVAABgQDvrz0FxA5+Dgs/C56AAgE3n5HNQAAAA+guBAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJjTp0CpqqrSVVddpZSUFGVkZOiGG27QwYMHe51z7Ngx+f1+paena/jw4SotLVV7e3uvc1paWlRSUqKhQ4cqIyNDixcv1okTJ85+NQAAICb0KVDq6urk9/u1c+dO1dTU6Pjx45oxY4Y6Ozsj5yxatEibN2/WSy+9pLq6OrW1tWnWrFmR493d3SopKVFXV5d27NihZ599VuvXr9eyZcuityoAADCgxTmO45zpnQ8dOqSMjAzV1dXpq1/9qoLBoC644AJVV1frxhtvlCQdOHBA48aNU319vaZOnaqtW7fquuuuU1tbmzIzMyVJa9eu1V133aVDhw4pKSnpc583FArJ6/UqGAzK4/Gc6fj/1eglr0T9MXHufLCixO0RAACn0Zef32f1GpRgMChJSktLkyQ1Njbq+PHjKiwsjJwzduxY5ebmqr6+XpJUX1+vCRMmROJEkoqKihQKhbR///7TPk84HFYoFOq1AQCA2HXGgdLT06OFCxdq2rRpGj9+vCQpEAgoKSlJqampvc7NzMxUIBCInPOfcXLy+Mljp1NVVSWv1xvZcnJyznRsAAAwAJxxoPj9fu3bt08bNmyI5jynVVlZqWAwGNlaW1v7/TkBAIB7Es/kTvPnz9eWLVu0fft2jRw5MrI/KytLXV1dOnz4cK+rKO3t7crKyoqcs3v37l6Pd/JdPifP+bTk5GQlJyefyagAAGAA6tMVFMdxNH/+fG3cuFGvv/66xowZ0+t4fn6+Bg0apNra2si+gwcPqqWlRT6fT5Lk8/nU3Nysjo6OyDk1NTXyeDzKy8s7m7UAAIAY0acrKH6/X9XV1Xr55ZeVkpISec2I1+vVkCFD5PV6NXfuXFVUVCgtLU0ej0d33nmnfD6fpk6dKkmaMWOG8vLydMstt2jlypUKBAJaunSp/H4/V0kAAICkPgbKmjVrJElf//rXe+1ft26dbr31VknSww8/rPj4eJWWliocDquoqEhPPPFE5NyEhARt2bJF8+bNk8/n07Bhw1ReXq5777337FYCAABixll9Dopb+BwUfBY+BwUAbDpnn4MCAADQHwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwJxEtwcAom30klfcHqHffbCixO0RAKBfcQUFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMKfPgbJ9+3bNnDlT2dnZiouL06ZNm3oddxxHy5Yt04gRIzRkyBAVFhbq3Xff7XXOJ598orKyMnk8HqWmpmru3Lk6evToWS0EAADEjj4HSmdnpyZNmqTVq1ef9vjKlSv16KOPau3atdq1a5eGDRumoqIiHTt2LHJOWVmZ9u/fr5qaGm3ZskXbt2/XHXfccearAAAAMSWxr3coLi5WcXHxaY85jqNVq1Zp6dKluv766yVJv/3tb5WZmalNmzbp5ptv1ttvv61XX31VDQ0NKigokCQ99thj+va3v61f/vKXys7OPovlAACAWBDV16C8//77CgQCKiwsjOzzer2aMmWK6uvrJUn19fVKTU2NxIkkFRYWKj4+Xrt27YrmOAAAYIDq8xWUzxIIBCRJmZmZvfZnZmZGjgUCAWVkZPQeIjFRaWlpkXM+LRwOKxwOR26HQqFojg0AAIwZEO/iqaqqktfrjWw5OTlujwQAAPpRVAMlKytLktTe3t5rf3t7e+RYVlaWOjo6eh0/ceKEPvnkk8g5n1ZZWalgMBjZWltbozk2AAAwJqqBMmbMGGVlZam2tjayLxQKadeuXfL5fJIkn8+nw4cPq7GxMXLO66+/rp6eHk2ZMuW0j5ucnCyPx9NrAwAAsavPr0E5evSo3nvvvcjt999/X01NTUpLS1Nubq4WLlyon//857rkkks0ZswY3X333crOztYNN9wgSRo3bpy+9a1v6fbbb9fatWt1/PhxzZ8/XzfffDPv4AEAAJLOIFD27Nmjb3zjG5HbFRUVkqTy8nKtX79eP/7xj9XZ2ak77rhDhw8f1jXXXKNXX31VgwcPjtzn+eef1/z58zV9+nTFx8ertLRUjz76aBSWAwAAYkGc4ziO20P0VSgUktfrVTAY7Jdf94xe8krUHxOIpg9WlLg9AgD0WV9+fg+Id/EAAID/LQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJiT6PYAAPou1r9xm29rBsAVFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMS3R4AAD5t9JJX3B6hX32wosTtEQDzuIICAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBy+zRgAzrFY/7ZmiW9sxtnjCgoAADCHQAEAAOa4GiirV6/W6NGjNXjwYE2ZMkW7d+92cxwAAGCEa4HywgsvqKKiQsuXL9fevXs1adIkFRUVqaOjw62RAACAEa4FykMPPaTbb79dt912m/Ly8rR27VoNHTpUv/nNb9waCQAAGOHKu3i6urrU2NioysrKyL74+HgVFhaqvr7+lPPD4bDC4XDkdjAYlCSFQqF+ma8n/K9+eVwA+F+Ru+glt0fAWdp3T1HUH/Pkz23HcT73XFcC5eOPP1Z3d7cyMzN77c/MzNSBAwdOOb+qqkr33HPPKftzcnL6bUYAAP6XeVf132MfOXJEXq/3M88ZEJ+DUllZqYqKisjtnp4effLJJ0pPT1dcXFxUnysUCiknJ0etra3yeDxRfWwLWN/AF+trZH0DX6yvMdbXJ/XfGh3H0ZEjR5Sdnf2557oSKOeff74SEhLU3t7ea397e7uysrJOOT85OVnJycm99qWmpvbniPJ4PDH7L57E+mJBrK+R9Q18sb7GWF+f1D9r/LwrJye58iLZpKQk5efnq7a2NrKvp6dHtbW18vl8bowEAAAMce1XPBUVFSovL1dBQYEmT56sVatWqbOzU7fddptbIwEAACNcC5TZs2fr0KFDWrZsmQKBgC6//HK9+uqrp7xw9lxLTk7W8uXLT/mVUqxgfQNfrK+R9Q18sb7GWF+fZGONcc4Xea8PAADAOcR38QAAAHMIFAAAYA6BAgAAzCFQAACAOQTKf1i9erVGjx6twYMHa8qUKdq9e7fbI0XN9u3bNXPmTGVnZysuLk6bNm1ye6Soqqqq0lVXXaWUlBRlZGTohhtu0MGDB90eK2rWrFmjiRMnRj40yefzaevWrW6P1W9WrFihuLg4LVy40O1RouZnP/uZ4uLiem1jx451e6yo+sc//qHvfe97Sk9P15AhQzRhwgTt2bPH7bGiZvTo0af8GcbFxcnv97s9WlR0d3fr7rvv1pgxYzRkyBBddNFFuu+++77Q9+b0BwLl/73wwguqqKjQ8uXLtXfvXk2aNElFRUXq6Ohwe7So6Ozs1KRJk7R69Wq3R+kXdXV18vv92rlzp2pqanT8+HHNmDFDnZ2dbo8WFSNHjtSKFSvU2NioPXv26Nprr9X111+v/fv3uz1a1DU0NOjJJ5/UxIkT3R4l6i677DJ99NFHke3NN990e6So+ec//6lp06Zp0KBB2rp1q9566y396le/0nnnnef2aFHT0NDQ68+vpqZGknTTTTe5PFl0PPjgg1qzZo0ef/xxvf3223rwwQe1cuVKPfbYY+4M5MBxHMeZPHmy4/f7I7e7u7ud7Oxsp6qqysWp+ockZ+PGjW6P0a86OjocSU5dXZ3bo/Sb8847z/n1r3/t9hhRdeTIEeeSSy5xampqnK997WvOggUL3B4papYvX+5MmjTJ7TH6zV133eVcc801bo9xTi1YsMC56KKLnJ6eHrdHiYqSkhJnzpw5vfbNmjXLKSsrc2UerqBI6urqUmNjowoLCyP74uPjVVhYqPr6ehcnw5kKBoOSpLS0NJcnib7u7m5t2LBBnZ2dMffVEH6/XyUlJb3+W4wl7777rrKzs3XhhReqrKxMLS0tbo8UNX/84x9VUFCgm266SRkZGbriiiv09NNPuz1Wv+nq6tJzzz2nOXPmRP1La91y9dVXq7a2Vu+8844k6a9//avefPNNFRcXuzLPgPg24/728ccfq7u7+5RPsc3MzNSBAwdcmgpnqqenRwsXLtS0adM0fvx4t8eJmubmZvl8Ph07dkzDhw/Xxo0blZeX5/ZYUbNhwwbt3btXDQ0Nbo/SL6ZMmaL169fr0ksv1UcffaR77rlHX/nKV7Rv3z6lpKS4Pd5Z+/vf/641a9aooqJCP/nJT9TQ0KAf/vCHSkpKUnl5udvjRd2mTZt0+PBh3XrrrW6PEjVLlixRKBTS2LFjlZCQoO7ubt1///0qKytzZR4CBTHH7/dr3759MfX7fUm69NJL1dTUpGAwqN/97ncqLy9XXV1dTERKa2urFixYoJqaGg0ePNjtcfrFf/4tdOLEiZoyZYpGjRqlF198UXPnznVxsujo6elRQUGBHnjgAUnSFVdcoX379mnt2rUxGSjPPPOMiouLlZ2d7fYoUfPiiy/q+eefV3V1tS677DI1NTVp4cKFys7OduXPkECRdP755yshIUHt7e299re3tysrK8ulqXAm5s+fry1btmj79u0aOXKk2+NEVVJSki6++GJJUn5+vhoaGvTII4/oySefdHmys9fY2KiOjg5deeWVkX3d3d3avn27Hn/8cYXDYSUkJLg4YfSlpqbqy1/+st577z23R4mKESNGnBLL48aN0+9//3uXJuo/H374of70pz/pD3/4g9ujRNXixYu1ZMkS3XzzzZKkCRMm6MMPP1RVVZUrgcJrUPTv//Hn5+ertrY2sq+np0e1tbUx9zv+WOU4jubPn6+NGzfq9ddf15gxY9weqd/19PQoHA67PUZUTJ8+Xc3NzWpqaopsBQUFKisrU1NTU8zFiSQdPXpUf/vb3zRixAi3R4mKadOmnfLW/nfeeUejRo1yaaL+s27dOmVkZKikpMTtUaLqX//6l+Lje2dBQkKCenp6XJmHKyj/r6KiQuXl5SooKNDkyZO1atUqdXZ26rbbbnN7tKg4evRor7+pvf/++2pqalJaWppyc3NdnCw6/H6/qqur9fLLLyslJUWBQECS5PV6NWTIEJenO3uVlZUqLi5Wbm6ujhw5ourqar3xxhvatm2b26NFRUpKyimvFxo2bJjS09Nj5nVEP/rRjzRz5kyNGjVKbW1tWr58uRISEvTd737X7dGiYtGiRbr66qv1wAMP6Dvf+Y52796tp556Sk899ZTbo0VVT0+P1q1bp/LyciUmxtaP0JkzZ+r+++9Xbm6uLrvsMv3lL3/RQw89pDlz5rgzkCvvHTLqsccec3Jzc52kpCRn8uTJzs6dO90eKWr+/Oc/O5JO2crLy90eLSpOtzZJzrp169weLSrmzJnjjBo1yklKSnIuuOACZ/r06c5rr73m9lj9KtbeZjx79mxnxIgRTlJSkvOlL33JmT17tvPee++5PVZUbd682Rk/fryTnJzsjB071nnqqafcHinqtm3b5khyDh486PYoURcKhZwFCxY4ubm5zuDBg50LL7zQ+elPf+qEw2FX5olzHJc+Ig4AAOC/4DUoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGDO/wGRqdsvlAUptwAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] + "text/plain": "
", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAb9klEQVR4nO3df6yW9X3/8Rc/PAcFzkGonCMD1KWuyBTaQoUzu1/KYOzUaMTNLswxR9qMHJxI5iyJw9UtPcRt1dog1G4Tl5XQuQQ7adASbDGLx1/HkCBOVjcbyOg52DjOAb7hgJz7+8fGnZ7iWg5C78+hj0dyJd7Xdd33/b6uGM/T61z3fYZVKpVKAAAKMrzWAwAA/CiBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHFG1nqAM9Hf35/9+/dn7NixGTZsWK3HAQBOQ6VSyaFDhzJp0qQMH/7jr5EMyUDZv39/pkyZUusxAIAzsG/fvkyePPnH7jMkA2Xs2LFJ/ucAGxoaajwNAHA6ent7M2XKlOrP8R9nSAbKyV/rNDQ0CBQAGGJO5/YMN8kCAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcUbWeoASXf65b9Z6hCHje2taaz0CAOchV1AAgOIIFACgOAIFACiOQAEAiiNQAIDiCBQAoDgCBQAojkABAIojUACA4ggUAKA4AgUAKI5AAQCKI1AAgOIIFACgOAIFACiOQAEAiiNQAIDiCBQAoDgCBQAojkABAIojUACA4ggUAKA4AgUAKI5AAQCKI1AAgOJ8oEBZs2ZNhg0blhUrVlTXHT16NG1tbZkwYULGjBmTRYsWpbu7e8Dz9u7dm9bW1lx00UWZOHFi7rnnnrz33nsfZBQA4DxyxoHyyiuv5Ctf+UpmzJgxYP3dd9+dp59+Ok8++WR27NiR/fv355ZbbqluP3HiRFpbW3Ps2LG88MILeeKJJ7Jhw4asXr36zI8CADivnFGgHD58OIsXL85Xv/rVXHzxxdX1PT09+bu/+7t88YtfzPXXX59Zs2bl8ccfzwsvvJAXX3wxSfKtb30rb7zxRv7xH/8xH/3oR7Nw4cL8xV/8RdauXZtjx46dnaMCAIa0MwqUtra2tLa2Zt68eQPWd3Z25vjx4wPWT5s2LVOnTk1HR0eSpKOjI9dcc02ampqq+yxYsCC9vb3ZvXv3+75fX19fent7BywAwPlr5GCfsGnTprz22mt55ZVXTtnW1dWVurq6jBs3bsD6pqamdHV1Vff54Tg5uf3ktvfT3t6ez3/+84MdFQAYogZ1BWXfvn2566678rWvfS2jRo06VzOdYtWqVenp6aku+/bt+6m9NwDw0zeoQOns7MyBAwfy8Y9/PCNHjszIkSOzY8eOPPLIIxk5cmSamppy7NixHDx4cMDzuru709zcnCRpbm4+5VM9Jx+f3OdH1dfXp6GhYcACAJy/BhUoN9xwQ3bt2pWdO3dWl9mzZ2fx4sXVf77ggguyffv26nP27NmTvXv3pqWlJUnS0tKSXbt25cCBA9V9tm3bloaGhkyfPv0sHRYAMJQN6h6UsWPH5uqrrx6wbvTo0ZkwYUJ1/dKlS7Ny5cqMHz8+DQ0NufPOO9PS0pK5c+cmSebPn5/p06fn9ttvz4MPPpiurq7cd999aWtrS319/Vk6LABgKBv0TbI/yUMPPZThw4dn0aJF6evry4IFC/Loo49Wt48YMSJbtmzJsmXL0tLSktGjR2fJkiV54IEHzvYoAMAQNaxSqVRqPcRg9fb2prGxMT09PefkfpTLP/fNs/6a56vvrWmt9QgADBGD+fntb/EAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxRlUoKxbty4zZsxIQ0NDGhoa0tLSkq1bt1a3Hz16NG1tbZkwYULGjBmTRYsWpbu7e8Br7N27N62trbnooosyceLE3HPPPXnvvffOztEAAOeFQQXK5MmTs2bNmnR2dubVV1/N9ddfn5tuuim7d+9Oktx99915+umn8+STT2bHjh3Zv39/brnllurzT5w4kdbW1hw7diwvvPBCnnjiiWzYsCGrV68+u0cFAAxpwyqVSuWDvMD48ePzV3/1V7n11ltzySWXZOPGjbn11luTJG+++WauuuqqdHR0ZO7cudm6dWs+9alPZf/+/WlqakqSrF+/Pvfee2/eeeed1NXVndZ79vb2prGxMT09PWloaPgg47+vyz/3zbP+muer761prfUIAAwRg/n5fcb3oJw4cSKbNm3KkSNH0tLSks7Ozhw/fjzz5s2r7jNt2rRMnTo1HR0dSZKOjo5cc8011ThJkgULFqS3t7d6FQYAYORgn7Br1660tLTk6NGjGTNmTDZv3pzp06dn586dqaury7hx4wbs39TUlK6uriRJV1fXgDg5uf3ktv9LX19f+vr6qo97e3sHOzYAMIQM+grKRz7ykezcuTMvvfRSli1bliVLluSNN944F7NVtbe3p7GxsbpMmTLlnL4fAFBbgw6Uurq6fPjDH86sWbPS3t6emTNn5ktf+lKam5tz7NixHDx4cMD+3d3daW5uTpI0Nzef8qmek49P7vN+Vq1alZ6enuqyb9++wY4NAAwhH/h7UPr7+9PX15dZs2blggsuyPbt26vb9uzZk71796alpSVJ0tLSkl27duXAgQPVfbZt25aGhoZMnz79/3yP+vr66kebTy4AwPlrUPegrFq1KgsXLszUqVNz6NChbNy4Md/5znfy7LPPprGxMUuXLs3KlSszfvz4NDQ05M4770xLS0vmzp2bJJk/f36mT5+e22+/PQ8++GC6urpy3333pa2tLfX19efkAAGAoWdQgXLgwIH8/u//fr7//e+nsbExM2bMyLPPPpvf+I3fSJI89NBDGT58eBYtWpS+vr4sWLAgjz76aPX5I0aMyJYtW7Js2bK0tLRk9OjRWbJkSR544IGze1QAwJD2gb8HpRZ8D0o5fA8KAKdrMD+/B/0xY/hhYu70iTmA0+ePBQIAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFGVSgtLe35xOf+ETGjh2biRMn5uabb86ePXsG7HP06NG0tbVlwoQJGTNmTBYtWpTu7u4B++zduzetra256KKLMnHixNxzzz157733PvjRAADnhUEFyo4dO9LW1pYXX3wx27Zty/HjxzN//vwcOXKkus/dd9+dp59+Ok8++WR27NiR/fv355ZbbqluP3HiRFpbW3Ps2LG88MILeeKJJ7Jhw4asXr367B0VADCkDatUKpUzffI777yTiRMnZseOHfmVX/mV9PT05JJLLsnGjRtz6623JknefPPNXHXVVeno6MjcuXOzdevWfOpTn8r+/fvT1NSUJFm/fn3uvffevPPOO6mrq/uJ79vb25vGxsb09PSkoaHhTMf/P13+uW+e9deE761prfUIADU1mJ/fH+gelJ6eniTJ+PHjkySdnZ05fvx45s2bV91n2rRpmTp1ajo6OpIkHR0dueaaa6pxkiQLFixIb29vdu/e/b7v09fXl97e3gELAHD+OuNA6e/vz4oVK3Ldddfl6quvTpJ0dXWlrq4u48aNG7BvU1NTurq6qvv8cJyc3H5y2/tpb29PY2NjdZkyZcqZjg0ADAFnHChtbW15/fXXs2nTprM5z/tatWpVenp6qsu+ffvO+XsCALUz8kyetHz58mzZsiXPP/98Jk+eXF3f3NycY8eO5eDBgwOuonR3d6e5ubm6z8svvzzg9U5+yufkPj+qvr4+9fX1ZzIqADAEDeoKSqVSyfLly7N58+Y899xzueKKKwZsnzVrVi644IJs3769um7Pnj3Zu3dvWlpakiQtLS3ZtWtXDhw4UN1n27ZtaWhoyPTp0z/IsQAA54lBXUFpa2vLxo0b841vfCNjx46t3jPS2NiYCy+8MI2NjVm6dGlWrlyZ8ePHp6GhIXfeeWdaWloyd+7cJMn8+fMzffr03H777XnwwQfT1dWV++67L21tba6SAABJBhko69atS5L82q/92oD1jz/+eP7gD/4gSfLQQw9l+PDhWbRoUfr6+rJgwYI8+uij1X1HjBiRLVu2ZNmyZWlpacno0aOzZMmSPPDAAx/sSACA88YH+h6UWvE9KAxFvgcF+Fn3U/seFACAc0GgAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRnZK0HgJ8Vl3/um7UeYcj43prWWo8A1JgrKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFGXSgPP/887nxxhszadKkDBs2LE899dSA7ZVKJatXr86ll16aCy+8MPPmzct3v/vdAfu8++67Wbx4cRoaGjJu3LgsXbo0hw8f/kAHAgCcPwYdKEeOHMnMmTOzdu3a993+4IMP5pFHHsn69evz0ksvZfTo0VmwYEGOHj1a3Wfx4sXZvXt3tm3bli1btuT555/PZz/72TM/CgDgvDJysE9YuHBhFi5c+L7bKpVKHn744dx333256aabkiT/8A//kKampjz11FP59Kc/nX/7t3/LM888k1deeSWzZ89Oknz5y1/Ob/3Wb+Wv//qvM2nSpA9wOADA+eCs3oPy9ttvp6urK/Pmzauua2xszJw5c9LR0ZEk6ejoyLhx46pxkiTz5s3L8OHD89JLL73v6/b19aW3t3fAAgCcv85qoHR1dSVJmpqaBqxvamqqbuvq6srEiRMHbB85cmTGjx9f3edHtbe3p7GxsbpMmTLlbI4NABRmSHyKZ9WqVenp6aku+/btq/VIAMA5dFYDpbm5OUnS3d09YH13d3d1W3Nzcw4cODBg+3vvvZd33323us+Pqq+vT0NDw4AFADh/ndVAueKKK9Lc3Jzt27dX1/X29uall15KS0tLkqSlpSUHDx5MZ2dndZ/nnnsu/f39mTNnztkcBwAYogb9KZ7Dhw/nrbfeqj5+++23s3PnzowfPz5Tp07NihUr8pd/+Ze58sorc8UVV+TP/uzPMmnSpNx8881Jkquuuiq/+Zu/mc985jNZv359jh8/nuXLl+fTn/60T/AAAEnOIFBeffXV/Pqv/3r18cqVK5MkS5YsyYYNG/Knf/qnOXLkSD772c/m4MGD+eQnP5lnnnkmo0aNqj7na1/7WpYvX54bbrghw4cPz6JFi/LII4+chcMBAM4HwyqVSqXWQwxWb29vGhsb09PTc07uR7n8c988668JnL7vrWmt9QjAOTCYn99D4lM8AMDPFoECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFCcQX/VPcC55tucT59v3eV85QoKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRnZK0HAODMXf65b9Z6hCHje2taaz0Cg+AKCgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHH8sUAAfib4w4qDU+s/rugKCgBQHIECABRHoAAAxalpoKxduzaXX355Ro0alTlz5uTll1+u5TgAQCFqFihf//rXs3Llytx///157bXXMnPmzCxYsCAHDhyo1UgAQCFqFihf/OIX85nPfCZ33HFHpk+fnvXr1+eiiy7K3//939dqJACgEDX5mPGxY8fS2dmZVatWVdcNHz488+bNS0dHxyn79/X1pa+vr/q4p6cnSdLb23tO5uvv+3/n5HUBYKg4Fz9jT75mpVL5ifvWJFB+8IMf5MSJE2lqahqwvqmpKW+++eYp+7e3t+fzn//8KeunTJlyzmYEgJ9ljQ+fu9c+dOhQGhsbf+w+Q+KL2latWpWVK1dWH/f39+fdd9/NhAkTMmzYsLP6Xr29vZkyZUr27duXhoaGs/ra5xvn6vQ5V6fPuTp9ztXpc64G51ydr0qlkkOHDmXSpEk/cd+aBMqHPvShjBgxIt3d3QPWd3d3p7m5+ZT96+vrU19fP2DduHHjzuWIaWho8C/xaXKuTp9zdfqcq9PnXJ0+52pwzsX5+klXTk6qyU2ydXV1mTVrVrZv315d19/fn+3bt6elpaUWIwEABanZr3hWrlyZJUuWZPbs2bn22mvz8MMP58iRI7njjjtqNRIAUIiaBcptt92Wd955J6tXr05XV1c++tGP5plnnjnlxtmftvr6+tx///2n/EqJUzlXp8+5On3O1elzrk6fczU4JZyvYZXT+awPAMBPkb/FAwAUR6AAAMURKABAcQQKAFAcgfJD1q5dm8svvzyjRo3KnDlz8vLLL9d6pCI9//zzufHGGzNp0qQMGzYsTz31VK1HKlZ7e3s+8YlPZOzYsZk4cWJuvvnm7Nmzp9ZjFWndunWZMWNG9YuhWlpasnXr1lqPNSSsWbMmw4YNy4oVK2o9SnH+/M//PMOGDRuwTJs2rdZjFeu//uu/8nu/93uZMGFCLrzwwlxzzTV59dVXazKLQPlfX//617Ny5crcf//9ee211zJz5swsWLAgBw4cqPVoxTly5EhmzpyZtWvX1nqU4u3YsSNtbW158cUXs23bthw/fjzz58/PkSNHaj1acSZPnpw1a9aks7Mzr776aq6//vrcdNNN2b17d61HK9orr7ySr3zlK5kxY0atRynWL/7iL+b73/9+dfnXf/3XWo9UpP/+7//OddddlwsuuCBbt27NG2+8kb/5m7/JxRdfXJuBKlQqlUrl2muvrbS1tVUfnzhxojJp0qRKe3t7DacqX5LK5s2baz3GkHHgwIFKksqOHTtqPcqQcPHFF1f+9m//ttZjFOvQoUOVK6+8srJt27bKr/7qr1buuuuuWo9UnPvvv78yc+bMWo8xJNx7772VT37yk7Ueo8oVlCTHjh1LZ2dn5s2bV103fPjwzJs3Lx0dHTWcjPNNT09PkmT8+PE1nqRsJ06cyKZNm3LkyBF//uLHaGtrS2tr64D/dnGq7373u5k0aVJ+/ud/PosXL87evXtrPVKR/uVf/iWzZ8/Ob//2b2fixIn52Mc+lq9+9as1m0egJPnBD36QEydOnPIttk1NTenq6qrRVJxv+vv7s2LFilx33XW5+uqraz1OkXbt2pUxY8akvr4+f/RHf5TNmzdn+vTptR6rSJs2bcprr72W9vb2Wo9StDlz5mTDhg155plnsm7durz99tv55V/+5Rw6dKjWoxXnP//zP7Nu3bpceeWVefbZZ7Ns2bL88R//cZ544omazFOzr7qHnzVtbW15/fXX/f77x/jIRz6SnTt3pqenJ//8z/+cJUuWZMeOHSLlR+zbty933XVXtm3bllGjRtV6nKItXLiw+s8zZszInDlzctlll+Wf/umfsnTp0hpOVp7+/v7Mnj07X/jCF5IkH/vYx/L6669n/fr1WbJkyU99HldQknzoQx/KiBEj0t3dPWB9d3d3mpubazQV55Ply5dny5Yt+fa3v53JkyfXepxi1dXV5cMf/nBmzZqV9vb2zJw5M1/60pdqPVZxOjs7c+DAgXz84x/PyJEjM3LkyOzYsSOPPPJIRo4cmRMnTtR6xGKNGzcuv/ALv5C33nqr1qMU59JLLz3lfwauuuqqmv1KTKDkf/6jOGvWrGzfvr26rr+/P9u3b/f7bz6QSqWS5cuXZ/PmzXnuuedyxRVX1HqkIaW/vz99fX21HqM4N9xwQ3bt2pWdO3dWl9mzZ2fx4sXZuXNnRowYUesRi3X48OH8x3/8Ry699NJaj1Kc66677pSvQfj3f//3XHbZZTWZx694/tfKlSuzZMmSzJ49O9dee20efvjhHDlyJHfccUetRyvO4cOHB/zfx9tvv52dO3dm/PjxmTp1ag0nK09bW1s2btyYb3zjGxk7dmz1nqbGxsZceOGFNZ6uLKtWrcrChQszderUHDp0KBs3bsx3vvOdPPvss7UerThjx4495T6m0aNHZ8KECe5v+hF/8id/khtvvDGXXXZZ9u/fn/vvvz8jRozI7/7u79Z6tOLcfffd+aVf+qV84QtfyO/8zu/k5ZdfzmOPPZbHHnusNgPV+mNEJfnyl79cmTp1aqWurq5y7bXXVl588cVaj1Skb3/725UkpyxLliyp9WjFeb/zlKTy+OOP13q04vzhH/5h5bLLLqvU1dVVLrnkksoNN9xQ+da3vlXrsYYMHzN+f7fddlvl0ksvrdTV1VV+7ud+rnLbbbdV3nrrrVqPVaynn366cvXVV1fq6+sr06ZNqzz22GM1m2VYpVKp1CaNAADen3tQAIDiCBQAoDgCBQAojkABAIojUACA4ggUAKA4AgUAKI5AAQCKI1AAgOIIFACgOAIFACiOQAEAivP/ARdAbrnVxMw2AAAAAElFTkSuQmCC" }, "metadata": {}, "output_type": "display_data" @@ -443,8 +490,13 @@ }, { "cell_type": "code", - "execution_count": 43, - "metadata": {}, + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.702142Z", + "end_time": "2023-04-24T21:12:14.705415Z" + } + }, "outputs": [], "source": [ "class MoneyModel(mesa.Model):\n", @@ -529,8 +581,13 @@ }, { "cell_type": "code", - "execution_count": 44, - "metadata": {}, + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.705415Z", + "end_time": "2023-04-24T21:12:14.711263Z" + } + }, "outputs": [], "source": [ "class MoneyAgent(mesa.Agent):\n", @@ -589,8 +646,13 @@ }, { "cell_type": "code", - "execution_count": 45, - "metadata": {}, + "execution_count": 16, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.715462Z", + "end_time": "2023-04-24T21:12:14.716977Z" + } + }, "outputs": [], "source": [ "model = MoneyModel(50, 10, 10)\n", @@ -607,25 +669,26 @@ }, { "cell_type": "code", - "execution_count": 46, - "metadata": {}, + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.719887Z", + "end_time": "2023-04-24T21:12:14.900840Z" + } + }, "outputs": [ { "data": { - "text/plain": [ - "" - ] + "text/plain": "" }, - "execution_count": 46, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", - "text/plain": [ - "
" - ] + "text/plain": "
", + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -663,8 +726,13 @@ }, { "cell_type": "code", - "execution_count": 47, - "metadata": {}, + "execution_count": 18, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.904858Z", + "end_time": "2023-04-24T21:12:14.909247Z" + } + }, "outputs": [], "source": [ "def compute_gini(model):\n", @@ -746,8 +814,13 @@ }, { "cell_type": "code", - "execution_count": 48, - "metadata": {}, + "execution_count": 19, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.910833Z", + "end_time": "2023-04-24T21:12:14.926306Z" + } + }, "outputs": [], "source": [ "model = MoneyModel(50, 10, 10)\n", @@ -764,25 +837,26 @@ }, { "cell_type": "code", - "execution_count": 49, - "metadata": {}, + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:14.926306Z", + "end_time": "2023-04-24T21:12:15.033738Z" + } + }, "outputs": [ { "data": { - "text/plain": [ - "" - ] + "text/plain": "" }, - "execution_count": 49, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGdCAYAAADAAnMpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABVfElEQVR4nO3deXhU1f0/8PcsmZnsK1nJxhrCFkggBBSxBtG64Va0CIhK64JFU6si30LVn4RWa2mVilhxt6AtuFDFJYCKBAKBsCcQQjbCZE8m60wyc39/TGaSkG0mmT3v1/PM85A7986cXCDzzjmfc45IEAQBRERERHYitncDiIiIaHhjGCEiIiK7YhghIiIiu2IYISIiIrtiGCEiIiK7YhghIiIiu2IYISIiIrtiGCEiIiK7ktq7AabQ6XQoKyuDt7c3RCKRvZtDREREJhAEAQ0NDQgPD4dY3Hf/h1OEkbKyMkRGRtq7GURERDQIJSUlGDlyZJ/PO0UY8fb2BqD/Znx8fOzcGiIiIjKFSqVCZGSk8XO8L04RRgxDMz4+PgwjRERETmagEgsWsBIREZFdMYwQERGRXTGMEBERkV05Rc2IKbRaLdra2uzdDIcnkUgglUo5RZqIiByGS4SRxsZGlJaWQhAEezfFKXh4eCAsLAwymczeTSEiInL+MKLValFaWgoPDw+MGDGCv/H3QxAEaDQaVFZW4uLFixg7dmy/i9AQERHZgtOHkba2NgiCgBEjRsDd3d3ezXF47u7ucHNzQ1FRETQaDRQKhb2bREREw5zL/FrMHhHTsTeEiIgcCT+ViIiIyK4YRoiIiMiuGEacgEgkwmeffWby+e+++y78/Pys1h4iIiJLYhixM6VSiVWrVmHMmDFQKBQICQnBnDlz8MYbb6C5uRkAcPnyZdx4440mv+aiRYtw7tw5azWZiIjIopx+No0zKygowJw5c+Dn54f169dj8uTJkMvlOHnyJLZs2YKIiAjceuutCA0NNet13d3dObOIiGxOEAR8cLAIF6uauh33lEmxYu4o+Lq72all5OhcLowIgoCWNq1d3tvdTWLWrJ5HH30UUqkUR44cgaenp/H4qFGjcNtttxkXcROJRNi5cycWLlyIwsJCxMbG4r///S9ee+01HDp0CGPHjsXmzZuRkpICQD9M88QTT6Curs6i3x8RUX8yzlZg7eene32uUd2OP9060cYtImfhcmGkpU2L+LXf2OW9z7ywAB4y025pdXU1vv32W6xfv75bEOmqv2CzZs0avPLKKxg7dizWrFmDe++9F/n5+ZBKXe6vlIicgCAIeH1vPgBg7rgRmBzhAwCoaWrDv7OK8emREqRdPw4+CvaOUE+sGbGT/Px8CIKA8ePHdzseFBQELy8veHl54Zlnnunz+qeeego33XQTxo0bh+effx5FRUXIz8+3drOJyAoa1e149ds8HCyotndTBi3zQjVySuogl4rx17un4g8L4vCHBXFYf/skjA32QpNGi08Ol9i7meSgXO7XaHc3Cc68sMBu7z1UWVlZ0Ol0WLx4MdRqdZ/nTZkyxfjnsLAwAEBFRQXi4uKG3AYish11uxa//eAIfs6vxtv7L+L731+DMF/nq/natE//y9A9MyIxwltuPC4SifDAVbFYveMk3vm5EPfPjoFUwt+DqTuXCyMikcjkoRJ7GjNmDEQiEfLy8rodHzVqFAAMWIDq5tbZ1WkYztHpdBZuJRFZk1Yn4MntOfg5X98j0qTR4v/tOotNi6fbuWXmOVZci5/zqyEVi7Bi7qgez98+LQJ/2Z2LS3Ut+O5MOW6cHDbga+7Lq0Btswa3TxtpjSaTg2E8tZPAwEDMnz8fr7/+Opqamga+gIhciiAIWPv5KXx1Ugk3iQh/vDkeErEI/zt5GT+cq+xxfqO6Hc/+9wTe+fmiHVrbv3/uuwAAWDgtAiP9PXo8r3CTYHFyNABgqwntr2hoxYr3j+DJ7cdRUtNs2caSQ2IYsaN//vOfaG9vR1JSErZv346zZ88iLy8PH374IXJzcyGRDH3Yh4gc09++P4+PDhVDJAI2LpqGB6+Kxf2zYwAA6z4/hdYuswIb1e24f2sWth0uwYu7zvSYOmtPuUoVvjtTDpEIeGTe6D7PW5ISDTeJCIcLa3GitK7f19yWVYI2rX42YYEDfa9kPY4/nuHCRo8ejWPHjmH9+vVYvXo1SktLIZfLER8fj6eeegqPPvqovZtIRFbwQWYh/pFxHgDwwm2TcNMU/bDFE6ljsetEGQqrm7H5hwt4InWcMYgcKaoFAOgE4M0fLmDDnVP6fH1beqOjV+SXk8IweoRXn+eF+Chw85Rw7Dx2CVv3X8TGe6b1el6bVoePDhUZvy6ubgIwwqJtHs4qG9TY8HUuGlrbejz3zI1x/f4dWpNIMCxm4cBUKhV8fX1RX18PHx+fbs+1trbi4sWLiI2NhUKhsFMLnQvvGZH9ZF6oxn1vH9LXi6SOw6rUsd2e33WiDCs/PgaZVIydj87Gn744jcOFtfBWSPH7+ePwpy/PwE0iwo9PX2v3Qtei6iZc+8o+6ARg1+NXYVKEb7/nnyytxy2v74dULMLPz/4CIT49f/7878RlPPbxUePXK66OxZqb4i3e9uFqzc6T+OhQca/P7Xh0NqZH+Vv0/fr7/O6KPSNERL3Q6gScr2hAu7b772sRfu7w95QN6jWV9a14/N9HodUJWJgQjt9dN6bHOTdNDsP2sSX46XwVFm76GW1aAd4KKT56KBlTRvph92klDhbU4K0fL2LtLT0/pOtb2iASwSbrebyx7wJ0AjBv/IgBgwgATB7pi5kxAcgqrMH7mYX4w4Kes//eO1AIAAj1UUCpakVRNWtGLKWqUY3/ZJcC0PfCdZ31BACRvdT72ArDCBFRF82adnx6pBT/2l+AkpqWHs/LJGIsnBaOFVePwtgQb5NfV9OuwyMfZaOqUYO4UG+k3zGl14UNRSIRnr91Im7Y+BM0Wl23IAIAj107BgcLsvBxVhEeu3Y0Ar06P1DOXlbhni0H4e4mwbdpc60aSPIrGvDJEf26IY//omeo6ssDV8Ugq7AGH2QW4Z4ZUYgM6PwAPFOmQlZhDaRiEZ5IHYtnd5xEMQtYLeb9zCKo23WYOtIXq64ba9aK4dbGAlYiGnYEQYCyvhWltc3Gx4XKRrz6bR5mb9iDdV+cRklNCzxkEoT6KIyPIC85NFodPjlSivl/+xEPvHsYBwuqYcpo94u7zuBYcR18FFK8uSQR7rK+C9RHjfDCS7dPwsyYgG5BBACuGhOEKSN90dqmwzs/FxqPl9Q0Y+nWLNS3tEGpajXWcljLhq9zoROA6+NDkBgdYPJ18+NDMWWkL1St7Xjg3cNQdald+OBgIQBgwaRQzIjVv2ZxTbNJ95f616xpx/uZhQCA38wd7VBBBGDPCBENQxu+zsWbPxb0+XxUgAceujoWdyWO7LFuUXZRDbb8WIBvz5RjT24F9uRWYGFCODbcOQWKPhY+/G92KT44qC/K3HhPAqIDe98Coqu7kyJxd1Jkj+MikQiPzhuDhz/MxnuZhfjNNaOgbtNhyduHUNmgRrC3HBUNamzdfxFLZkUj3M/ydSUHC6rx/dkKSMQiPHOjeQstSsQibFmShNs27cf5ikas/PgYti5LQpNai53HLgEAlqXEYKS/O0QioFmjRVWjpseQApnn0yOlqGtuQ1SAB26YZN7mq7bgMj0jTM6m472i4e5/Jy8D0A+5yKWdj+lRfvjn4unY+9Q8LE2J6XUBxcToALy5JAl7fj8Pi5OjIBWL8FlOGX71ZiaU9a3dzm3X6rAtqxjP7TwJAPjddWPxi7iQIbf/+vgQjA32QkNrO97YdwH3v5OFwupmjPR3x5ePX4WZsQFQt+vw12/PDfm9rqTTCUj/6iwA4N6ZkYOafRHqq8Dby2bA3U2CH89V4sVdZ/Bpdgla23SIC/XGjBh/yKUShHcU6BbXcHrvULRrdfjXfn34fujqWEjEjtUrArhAz4hhLQ6NRjPgqqWk19ysH4Ptuoor0XBRoWpFaW0LxCLg6Nr58JIP7sdgbJAnXrp9Mm6aEobHPjqKE6X1uPX1/XhzSSISIv2wJ7cCG77OxfmKRgDAL+KC8cR1Ywd4VdOIxSI8eu1oPLn9uHE4JshLhg8eTEaIjwLP/XICFm76GTuOleLBq2IRH973LAZz7Tp5GcdL6+Epk2DVdeMG/TqTInyx8Z6Ejh6eInh0DFvdPzvGOIQQGeCOS3UtKKpuNmsoiLrbfVqJkpoW+Hu44e7Enr1tjsDpw4hUKoWHhwcqKyvh5uYGsdhlOnssThAENDc3o6KiAn5+flxUjYalo8X69TrGhXgPOoh0NXt0ED5/7CqseP8I8sobsGjLQcSH+SCnpA4A4Ofhhsd/MRZLZkVDbMHfSG+ZEo6/fnsOpbUt8JJL8e7ymYgN0g//JET64eYpYdh14jLSvz6LDx5Mtsh7qtu1ePmbXADAb68ZPeShkwUTQ/HMDXHY8HUumjVa+CikuC0hwvh8dIAnDhbUmFzEqmptw+8/OY74MB88Mm90n8Nmw4kgCNjSMSS5JCWm31ole3L6MCISiRAWFoaLFy+iqKho4AsIfn5+CA11vDFDIls4WlwHAEiMttx6ClGBHvjvo7ORtj0H354pR05JHWRSMR6YE4tH5o2Gr7vleyGlEjFeuG0i/pGRj9U3xvWYWvv0gjh8c1qJn85X4cdzlZg7bugLh32QWYSSmhYEe8vx0NWxQ349APjt3FEoqGzEJ0dKsSQlutuHZVSgfqZNsYnTe3efVOK7M+X47kw5vjxehvV3TMasUYEWaaezOlhQgxOl9ZBLxViWEm3v5vTJ6cMIAMhkMowdOxYajcbeTXF4bm5u7BGhYS27YyVTSy/u5CWXYvN9idj680WU1rZgxdxRiLBC8WhXv4gL6bMGJSrQA0tmxWDrzxex/quzmDMmaEi1ArVNGry2R78zb9r8cRbbkFQkEmHDHVOwZFYMJoR1nyod1THtt8jEnpHDhTXGPxdUNeGeLQdxz4xIrL5xAnw9ht+wtCAI+GfHbsp3J43sNg3c0bhEGAEAsVjM1UTJIbS2afHZsUu4fmIoAga5OBZZh6Zdh5OX6gEA0y3YM2IgFovw0NU9d621l8d/MQafZpcgV9mAnccu4a7Ewe2Aq9MJePKTHNS3tGFciNegX6cvYrEIk0f2XDQt2tAzYmIYMSyZ//d7EnDoYg0+PlSMbYdL8MXxsgGH5Pw9ZPjTrRORMtp1elL++u05/HS+ChKxCA9d5Tj/LnvDAgsiC3vn50I8u+MkHnj3MNq1Ons3h7o4XVYPTbsOAZ4yxATab7VJW/H3lOHRefoFyf6RcR5tg/z3+PeM89iXVwm5VIy/LUqAVGKbj47oAH0NTGWDGs2a9n7PrWxQ42JVE0QiYN64YKy/fTI++W0KRo3wRLNGi4oGdb+PvPIGPPTe4T438dPpBFysanKa2Yhb91/E63v1vSIv3jYJMUEDTye3J5fpGSFyFAcuVAEAckrqsPmHC1j5C8vMoKCh6xyi8XO4RZ+sZdnsaPzrpwIU1zRj57FL+FUva5f0Z09uOf7esanf+tsnY2L4wMu+W4qvhxt8FFKoWttRXNOMuNC+ZwVlF+mHaMYFexuHZGbGBuCbJ+biQmUjtLq+Q4QgAOu/OosDF6px/zuH8Z+HUzCqy5TlclUrntyegwMXqnF34kj85a7eV8+1pgP5Vfjnvgu4bkIwfpUUCc9+eno+O3YJL+w6AwB46vpx+HVylK2aOWiDirebNm1CTEwMFAoFkpOTkZWV1e/5dXV1eOyxxxAWFga5XI5x48bhq6++GlSDiVS97DbpKLQ6Acc6CiQBYOP353GqY1iA7M/wdzPNwvUijsxDJsVv5uq76DftzTert66ouglPbMsBACyZFY07LTw8YwrDAnED7VFzpFAfNJNiuv/duknEiAv1wcRw3z4fkyJ8sWVpEiZH+KKmSYMlb2cZ14zJOFuOGzb+iAMXqgEAn2aX4o0frLu6bW/Sv87F/vwqPP/lGczesAcvf5OLiobWHuftzavAU58eBwAsnxODx641fal+ezK7Z2T79u1IS0vD5s2bkZycjI0bN2LBggXIy8tDcHBwj/M1Gg3mz5+P4OBg/Oc//0FERASKiorg5+dnifbTMLPlxwvY8HUuFs2IwvrbJzncb7e5ShUa1e3wlkuRMjoQ354px+8/OY7PV87pd5qhpl2HrT9fxL68CnTtBRaLRFg0IxILp0X0eS2ZztAzYsmZNM5gSUo03vyxAEXV+t6R3lZ2vVKLRouHPzwKVWs7pkf54Y8322fn3KhAD5y8VI+SAepGDnf83c6IGdx6JF5yKd5ZPgO/2pyJgqomLN16CLNHB+Hdjo374sN8cN2EYLy2Jx9/2Z2HUUGeuGFS2KDey1wXq5pw8lI9JGIRIv3dUVjdjE17L+CtHy9iaqQvxF1+Dh4vrUO7TsBtCeH4403xDvczsi9mh5FXX30VK1aswPLlywEAmzdvxv/+9z9s3boVzz77bI/zt27dipqaGhw4cMC4yFZMTMzQWk3D0u5Tl7H+K/0aB//OKkZUgAcemTfazq3qzvDb2bRof6TfMRlHi2uRV96Av313Dqt/OaHXa44V12L1jpPIVTb0+vzBi9Xw9XDDteN7hn0yXVldC5SqVkjEIkzppVjSlXnIpPjt3FFI/zoXr+/Nx+3TIgas+3hh1xmcvaxCkJcM/1ycCJnUPiWG0YYZNf30jDRr2nG6owfyyp4RcwR5yfHeAzNx1+YDOFfeiHPl+gXrls+JwbM3xkEulaChtR3vHijEE9tz8Imfe7d9g6xl1/EyAMCcMUF45/4Z+O5MObb8eAFHi+twuONnTlfXjBuBl++aatF1bazNrDCi0WiQnZ2N1atXG4+JxWKkpqYiMzOz12u++OILpKSk4LHHHsPnn3+OESNG4Ne//jWeeeaZPqeYqtVqqNVq49cqlcqcZpILOllajye25wAApkb64XhJHf68OxexQR42++3EFIaphTOi/RHoJcf62yfjNx9kY8tPBUiND+n2W1ujuh2vfJOH9zILIQhAgKcMj/9iTLeFpL47U47Pc8qw6t/HsOvxq43rLpD5DIudTQjztti0VGdiTu9Is6YdO47qt5rfuGgaQn3tN1PRlOm9OSX63oAwX8WQp1NHBnjg/QeS8eu3DkIA8PJdU3DdhM7p03+8OR5F1U3Ym1eJh947gs9XzkGYr3WncH95Qh9GbpkSBolYhBsmheKGSaE4UVrXY6aRp1yKq8YEwc1GRcaWYtb/yKqqKmi1WoSEdJ/XHhISgtzc3F6vKSgowJ49e7B48WJ89dVXyM/Px6OPPoq2tjasW7eu12vS09Px/PPPm9M0cmHK+lY89P5htLbpMG/8CPxraRJe3HUG72UW4YntOfjUz6PXaYG2JgiCMYwkdYSO6yeG4u7Ekfg0uxQPvHMYI3w6g0ZNkwZ1zfr6lzumReD/bo7vMRV4fnwIiqqbkVNSh99+mI0dj8x22BUU7UGnE7D7tBKzRgUOOI3aOEQzjOpFujKnd2T/+Sqo23WIDHDHnDH2nepqCOD9DdN01osEWGRYYnyoN358+lpIJSLIpd3/v0nEIvzj3mm4641M5JU34Jd//wn+Xf7tuYnFuGVqGH4zd7RFepPylA04V94ImUSM6yd2X6xyykg/m/TM2ILVo5NOp0NwcDC2bNmCxMRELFq0CGvWrMHmzZv7vGb16tWor683PkpKSqzdTHJQTep2PPjeYZSr1BgX4oXX7p0GqUSMP94cj2vGjUBrmw4PvX+4xwZl9lBa24JylRpSsQgJkX7G42tviUdkgDsa1O0oqGwyPuqa2xAZ4I73H5iJVxcl9PphKpdK8MZ90xHoKcPZyyqs+eyk00wttIXdp5V49KOjuHfLQajbtf2ea1h51RrriziLJSnRCPCUoai6GZ/llPV53vdnywEAqRNC7F5zYChgLa1t7nNGjLFHcghDNFfylEt7BBEDb4Ub3r4/CcHectQ2t3X7f51X3oBXvj2Hm1/7yRiAh2JXR6/I3HEjrLKSr6Mwq2ckKCgIEokE5eXl3Y6Xl5f3ubx4WFhYj1U/J0yYAKVSCY1GA5mslx/AcjnkcsddKY6s49SleqR/fRatbZ3V/tWNahRWNyPQU4a3l82At0L/n1EqEeO1X0/DXW/ox3YXbvoZEf79d5V6yqWYNSoAc8eOQHyYz6DGU0+W1uO1PefxzI1xPXYrNfzgmRTh2633wlvhhl0rr0ZeefeaEIlYhInhPgPunxHm647Xfj0N9/3rEHYcvYRpkX5YkhJjdttd0fGONSHyyhvw6nfnsPrG3utyWtu0xpoCS6+86kwMM2s2fJ2L1/acx8KE8B69I1qdgIyzFQCA+ROGvsPwUIX6KOAmEaFNK6CsrgWRAd2HKrvOYLNlYfJIfw/sfWoeTpd1LyMorGrCn3fn4lx5I+7afABLZ0XjDzfEDWofJEEQ8GVHvcgtUx1nONoazLo7MpkMiYmJyMjIwMKFCwHoez4yMjKwcuXKXq+ZM2cOPv74Y+h0OuMmdufOnUNYWFivQYSGr7d+KsDP+dU9jsukYmxZmtjjh5CPwg1vL5uB2//5M5SqVihVA/eO/HiuEn/ZnYcATxmuGhPUI8D4KNywfE5MnwHhT1+eRnZRLQQAby1N6vaccYimlx+Ivh5umBk7+F1HZ48OwrM3xmH9V7l4YdcZzIgN6HfNheEiv6PAEAC2/FiA1Akhvc6mOHmpHu06ASO85Rg5QGh1dUtmRePNHy6gqLoZ35+twA2Tuv8imVNSh+omDbwVUswYwr9ZS9HPIPFAQVUTSmqae/wcMMxg85JLbf5/wlMu7fH/emZsAObHh+Clr87iP9mleC+zCN+cLsdTC8bj9mkRZi3Jf+qSCoXVzVC4iZHqAMHQmsyOamlpaVi2bBmSkpIwc+ZMbNy4EU1NTcbZNUuXLkVERATS09MBAI888ghef/11rFq1Co8//jjOnz+P9evX43e/+51lvxNyeobfbv6wYDzGBHf2OkyO8EV4H0VpkQEe+D7tGmRdrMFAgxfK+lb8dL4KmReqUNOkwRfHe++mbm3T4sn5PbdGP3Wp3tj7kXG2vMcPxq7j1taw4upR+Dm/Gj+cq8T/TlxmGAGQX6kPI+NDvJFX3oDff3IcX6+6useCUEeH4WJnffGUS3HvzCj8c98FvJ9Z2COMfHdG3/N97fhghymCjArUh5GimmbMvuI5w/+76dH+Q9p7x5L8PWV45e6pWJgQged2nkRxTTOe+vQ4/vVTAVb/cgKuMXHTQkPh6nVxIf0ucuYKzP7uFi1ahMrKSqxduxZKpRIJCQnYvXu3sai1uLjY2AMCAJGRkfjmm2/w5JNPYsqUKYiIiMCqVavwzDPPWO67IKdX3ag2VoXfNyvarLFRPw9Zj8KuviybHYM2rQ7Hiutw4EIVGlo7l5iualTj85wyvHugECvmjurRrfp+ZqHxzzoB+PBQkXFYoL65zTgMM5Sphf0RiUT45eRQ/HCuEocKaga+wMW1tmmNRY1v3DcdS97OQnFNM1766izW3z6527nW2hzPWS2eFY3NP1zAgQvVOFfegHEhnRvUGetF4h3nN/Gofqb3dp3B5miuGhuEb5+ci/cOFOL1vfnIVTZg2dYsXDUmCC8unITYfpZo1+kE45ReVx+iAQa5HPzKlSv7HJbZt29fj2MpKSk4ePDgYN6KhgnD2P/oEZ5WL9Jyk4gxMzagR/eqVifgZGk9Cqqa8PGhIvxmbucaJrVNGnzeUfD322tG4c0fCrD9cAmeTB0HhZsE2cX6H4ijgjwRZMWdMWfG6mc25JTUobVNO2C9iSsrqGyCTgB83d0QG+SJl++egl+/dQgfHyrG/PgQ47osgiAYi1eH22JnfYnwc8f8+BB8c7oc72cW4v8t1Ie3i1VNyK9ohFQsMvm3d1swhJHimqZux3ubweZoFG4S/Paa0fhVUiQ27c3H+5lF2J9fhQffO4zvnrymz96co8W1KKtvhZdcinnDYI0hx+iDo2Evp+PDIiHSfh8WErEID3csovbWTxfR2tY5O2P7kRKo23WYGO6DpxfEIcLPHXXNbcahHkNXsbU/7GICPTDCWw6NVoeckjqrvpejMwzRjA32gkgkwuzRQXhgTiwA4Hf/PoZbX9+PW1/fj5tf24+qRjXcJCJMirD/FHBHsWx2DABgx9FLxi0WMjp6RZJHBTjUzA3DjJor19ToawabI/L3lOH/bo7H92nXwM/DDQWVTcbi1N7sOnEZAHB9fMiw+KWDYYQcwrGOD9aEKD+7tmNhQgTCfRWobFDjP9n6RZ+0OgEfZBYBAJalxEAiFuG+WdEAgPcOFEIQBGMYGexS1KYSiURI7ujRybo4vIdq8iv0YaRrfdHTN4zH2GAvNLS240RpPU6U1htnOyTHBg6LH+qmShkViLHBXmjWaPHfjn/rhnoRRyuWjA7sHKbpOrXdMPw28YoZbI4sKtADK67W7xX0j4zzvU5X1uoEYxi5eRgM0QAMI+QAdDrB+Fv+NDv/diOTio2bim3+4QLatTrsya3ApboW+Hm44daEcADAohmRkEnFOF2mwsGCGuR0DDNZq16kK4YRvfwKfY1O1zCicJPg04dT8M7yGXjn/s7Hu8tn4J/3TbdXUx2SSCTC0o7ekQ8yi1DTpMGRjg93Rwsjkf76MNLQ2m5cKBBw7HqR/iybHaPvHanqvXfk7f0FqGpUw8/DDVeNcZzhMmtiGCG7K6hqQkNrO+RSMcaHeg98gZUtmhGFQE8ZSmtb8MXxMmPh6qKkSONv1gGeMtw6VR9M1nx2Epp2HQI9Zf0WpFlK8ih93Uh2US3azNiB1dX01jMC6Auarx0fjGvjOh/zxgfDR+E4ww6O4o5pEfCWS1FQ1YT/t+sMtDoBcaHePabP2pu7TILgjm0SDEM1X5+8jC866rhs8UuAJXnJpX32jhwvqcNfducBAJ66frzd9gSyteHxXZJDM/SKTI7wdYiphO4yCR68Wl978PI3efjpfBVEIhiHZgyWdSw8VlCpL6pLjPa3ybTRMSO84O/hhpY2LU52LOQ13LRrdbhYpb/vV4YRMp2nXIo7E0cCAHYcuwTA8XpFDAxDNXnlDXhu50k88tFRNKjbkRjtj2vjnK/Ac2lKdI/ekYbWNjz+72No1wm4cVIoFidH2bmVtmP/n/w07OWU6LuGHakA7b5Z0fBWSHG5Y5n56+JCevy2OHmkL6Z3qXGxdr2IgVgsMs4EGq5TfItqmtGmFeDuJkG4lTcpc3VLU7qHbEea0tuV4f/fmp0n8fGhYgDAw9eMxrbfzOpz2XZH5q1w69E7smbnKRTXNCPCzx0b7pgyrNbEYRghuzPWizjQGhA+CjdjzwcALJsd3et5hhkJgG27ig1TfLMu9lyxdjjoOkTjTNukO6JRI7wwt2Ma7whvOaY46Iyj6AD9EGibVr+S7gcPzsSzN8Y5RG/qYHXtHXnkw2x8cbysYyO+BPh6DK9hRef9WySX0NqmRe5lfSGivWfSXGn5nBiE+SqQFO2POaODej3nxklhmBDmg3EhXjadNmooYj1SWNvn5mH9EQQB2UU13aYvO5O+6kVocFZeOwYyiRj3JUc7bLi7amwgZBIxrh0/Al+vuhpXj3X+ws6uvSPfdsxkSps/DonRjrlmijW59vqy5PBOddkzJNxXYe/mdBPoJcdPT18LsUjU5w9omVSMXY9fBbEINu1SnRDmA2+FFA2t7Th7WWV2ENqwOxdv/lCAR+aNxjM3xFmpldbDMGJZM2MDcPbFGxxmOfXeJEYH4OTz1zvlkEx/lqZE462fClDX3IbZowPx8DWjB77IBbFnhOzKMESTEOmYe4ZIJeIBf1OUiEU2b7tELDLWqBwyc4rvidI6vPVjAQBgb26FxdtmCwwjlufIQcTA1YIIoO8d+fOdU3BbQjg23pPgFH8P1sAwQnZ1zLjyqp9d2+GMOotYTa8badPq8PR/TsAwspNX3oD6Lus2OAOdTmAYIZeyYGIo/n7PNAR7O1bvsC0xjJBdOcpiZ87IEEYOF9ZAZ2LdyJs/XECusgH+Hm4I91VAEIAjRc41I6esvgUtbVq4SUSIdrD1MIhocBhGyG4qGlpxqa4FIpF+miyZZ3KEL9zdJKhtbsP5jp6C/uRXNOIfGfkAgHW3TDQWAGYVOmYY0eoE/JxfBU1794XdDL0isUGekDrxTAoi6sT/yWQ3hs3xxgV7w5urY5rNTSI2bsw30BRfnU7A6h0noNHqMG/8CNyWEI4Zhp4VB11W/q2fCrD4X4ew9vNT3Y5ziIbI9XA2DdlN1+JVGpzk2ADsz6/CN6fLjTub9uZwYQ0OF9bCUybBS7dPhkgkwsyOAtiTl+rRotH22GisvrkNL/7vTLe9QHojEgF3JY7EgomhQ/+GOuh0Aj46pN+c8NPsUjwyb7Tx+zOGkREMI0SugmGE7CbHQXbqdWaGupH9+VXYn1814PnP3BiHCD/9iqWRAe4I8ZGjXKXGsZJazL5iLZV3DxQady4eyJHCGlw7Pthi+2gculiDkpoWAPrhmtf35OPlu6cC6BJGQuy/jxERWQbDCNlErlKF9w4Uob3Lxm6cSTN0STEBuGNaBHKVDQOeOzXSD/cld64kKxLppwfvOnEZhy92DyOCIGDHMX0QuX92TL8bGL763TlUNqixL68C11uod+TT7BIA+n8bOSV12HHsElb+YgyiAjyM9THsGSFyHQwjZBN//joXe/Mqexz3dXfDOP6GO2gSsQivLkoY9PXJsR1h5Ioi1qPFtSiqboaHTIKnbxgPD1nfPyouVjVhy48F2HH0kkXCSKO6HV+fVAIA/njzBPwjIx8/nKvE63vy8fQNcahvaYNIBIwaYf0dkonINhhGyOoEQcCJUv3usg/MiUWQt8z43OzRQcN2kR9HYChiPVpci3atzjg7ZcdR/Q6uN04K6zeIAMDt0yKw5ccC7MmtQF2zBn4esn7PH8j/TpShpU2LUSM8MT3KH6tSx+KHc5XYceyScf+fqAAPKNxcbwEsouGKYYSsrlylRnWTBhKxCE/fMJ4fIg5kXLA3fN3dUN/ShtNlKkyN9IO6XWvc0vyO6REDvsaEMB9MCPPB2csq7DpxGffN6n1TQVN9ekQ/PHRX4kiIRCJMj/LHNeNG4IdzlXjpf2cBcIiGyNVwai9Z3ekyfa/I6BGeDCIORiwWIaljerBhqGbP2QqoWtsR5qvArFGBJr3OnR2hZcdR0wpe+1JQ2YgjRbUQi4A7p480Hl+VOhYAoGptB8BpvUSuhmGErO50mQoAMCmcC5s5IsNQTVbHeiM7jumHaBZOizB5CO3WqeEQi4CjxXUorGrq8XxlgxqN6vYBX+e/HWFm7rgRCPHpXBp7epS/cZt7gGGEyNUwjJDVGXpG4sN97NwS6o1hw73DhTWoblQbN8+7Y9rAQzQGwT4K44quhjBj8HN+Feb8eQ/ueuMABKHvZeu1OgH/zdZfe3diZI/nV1031vhnhhEi18IwQlZ36pK+Z2Qie0Yc0uQIXyjcxKhtbsPfvj+Hdp2AyRG+GGvmLCdDfcnOY6XG0HGitA6/ef8INO065Cob+l22fn9+FZSqVvh5uCE1PrjH84nR/nh03mjcOjUckyP4b4nIlbCA1QW0aLS4UNmISQ74A7quWYNLdfrFq9gz4phkUjGmRfojs6AaHx0qBmBa4eqVro8PhadMgpKaFhwpqkWgpwz3v3MYTRqt8Zy9uRV9TuX+9Ih+bZHbpob3uVX80zfEmd0uInJ87BlxAX/5Jhc3v7Yfu08p7d2UHs501ItEBXjA1537zzgqQ92IIOjXLrllarjZr+Euk+CXk8MAAFt+LMCSt7NQ06TB5AhfPH3DeADA3ryKXq+tb2nDt2fKAQB3J/UcoiEi18Yw4gKyi2oBABlny+3ckp5OddSLTGSviEMz7FMDAPPGjUCQl3xQr3N7R4/Kd2fKcamuBbFBnnhn+QzcPFkfbo4U1kLV2nOvm92nLkPTrsO4EC/+WyEahhhGXEBRdTMA4EhHKHEkhpk0/IBxbNOi/IwzZ+7oMqXWXLNiAxHuq58FE+wtx/sPzESQlxxRgR4YNcIT7ToBP5/vuYfO5zn6dU1uS4iASMRF8IiGG4YRJ1fXrEF9i/43zYtVTahsUNu8DSdL6/H2/ovQ6nrOlDCGEQesZ6FOnnIpfn/9ONwxLQLz40MG/TpisQjP3TQBc8YE4oMHkxEZ4GF87trx+qLUK4dqlPWtyCyoBqCfIkxEww8LWJ2coVfEILuoBjdMCrNpG57YfgwXKpvgJZdg0Ywo4/FmTTsKKvWzJ9gz4vgenTfGIq9z85Rw3DylZ6i4dnww3t5/EXvzKiEIgrEH5MvjZRAEICnav1t4IaLhgz0jTq6opnsYOVxo26GawqomXKjUL3L176ySbs+dvdwAnQCM8JYj2FvR2+U0jMyI9YeHTILKBrWxxwwAPj+uX1vkNjPWNSEi18Iw4uSKq/VBwEuu7+Q6csXuq9a2r0uXe05JHXKVnR8yZzqKVyexV4QAyKUSzBkTBKDz301+RSNOXVJBKhbhpsm27dEjIsfBMOLkDMM0hh/kp8tUaNYMvOy2pezNqwSgX6sCALZ16R3pLF5lvQjpddaN6P/dfJGj7xWZO24EAjyHttsvETkvhhEnZximSRkdiDBfBdp1AnJK6mzy3i0arbHw8KnrxwEAdh67hNY2/SJXnElDV5o3Xr9k/LHiWtQ2afCZcRYNC1eJhjOGESdX3NEzEh3ogaSOtSKO2KhuJLOgCpp2HSL83PHgVaMQ7qtAfUsbvjmtRJtWhzxlAwA45MqwZB/hfu6IC/WGTgD+nnEexTXN8JBJhjSDh4icH8OIE2tt00KpagUARAd6YkZM963gr9Rkwq6p5tibq+9qnzd+BCRikXHlzG1ZJThf3giNVgcfhRQj/d0t+r7k3OZ1DNW8l1kIALg+PgQeMk7sIxrOGEacWEnHEI23XAp/DzckRet7Ro4V1/VY8+Odny9i4rpvsP1wsUXeWxAE43oRhjqAX82IhEgEZBZU46uTlwHo96PhIlbU1bUdQzWGDXw5i4aIGEacWGHHEE1UoAdEIhHGh3rDWy5Fo7q926yWqkY1/vrtOQDAhq9zjYukDcWFykaU1rZAJhVj9phAAECEnzvmdmwjv+WnAgAsXqWepkf7w1uh7wkJ9JThqo4ZNkQ0fDGMOLGijmm90YH6haIkYhGmReuHarrWjfwj4zwaO4Zoapvb8Ma+C0N+b8MQzaxRgd262O+ZoR+q0bTrAACTIli8St25ScSYO04fWm+aEgY3CX8MEQ13/CngxIo7hmmiAjyNx2ZEd68bKahsxMcd28L/Zu4oAMDWny/iUl3LkN67c4hmRLfj100IQZBX5xRN9oxQb1bfGIfHrh2N388fb++mEJEDYBhxYoY1RmICO5fQNsyoOVxYA0EQ8JfdeWjXCbguLhirb4xDcmwANO06/PXbvEG/b0NrmzHsGOpFDGRSMe7s2GhNLhVjVJBnj+uJRvp74A8L4uDr4WbvphCRAxhUGNm0aRNiYmKgUCiQnJyMrKysPs999913IRKJuj0UCi4NbgnGnpEuYSQh0g9SsQjlKjU+zynD7tNKiEXAszfGQSQS4blfTgCgXw/kdMcKqeb6Ob8abVoBsUGeiOklbNw3Kxr+Hm64cVIopOyCJyKiAZj9SbF9+3akpaVh3bp1OHr0KKZOnYoFCxagoqKiz2t8fHxw+fJl46OoqGhIjSZAqxNQWmtYY6QzELjLJMZ1PZ7dcQIAsGhGJMaGeAMApkb64Zap4RAEfTHrYBiW8p53xRCNQWSAB7LWpGLjPdMG9fpERDS8mB1GXn31VaxYsQLLly9HfHw8Nm/eDA8PD2zdurXPa0QiEUJDQ42PkBAucDRUZXUtaNMKkEnECPXp3tOU1FE30tqmg7ubBE+mjuv2/NMLxkMmEeOn81X44VylWe8rCAL2dSzlfeUQTVcsSiQiIlOZtdKQRqNBdnY2Vq9ebTwmFouRmpqKzMzMPq9rbGxEdHQ0dDodpk+fjvXr12PixIl9nq9Wq6FWq41fq1SqPs8drgxDNCMD3CERd1/HIykmAP/afxEAsGLuKARfEVYiAzywNCUa/9p/Eb/797EeYaY/WkGAUtUKdzcJZsYGDPG7ICIiMjOMVFVVQavV9ujZCAkJQW5u713+48ePx9atWzFlyhTU19fjlVdewezZs3H69GmMHDmy12vS09Px/PPPm9O0YafQMK03wKPHc8mxAfBWSOGjcDPOoLnSyl+MwY5jl1DTpBnUuiPz40OgcJOYfR0REdGVrL4Gc0pKClJSUoxfz549GxMmTMCbb76JF198sddrVq9ejbS0NOPXKpUKkZGR1m6qU+nck6ZnAam/pwzfp10DmUQML3nvf8V+HjLsXnU1zlc0mv3eErEICZF+Zl9HRETUG7PCSFBQECQSCcrLy7sdLy8vR2hoqEmv4ebmhmnTpiE/P7/Pc+RyOeRyuTlNG3aKumyQ15sQE4Zegn0UPYZwiIiIbM2sKkOZTIbExERkZGQYj+l0OmRkZHTr/eiPVqvFyZMnERYWZl5LqZuimv7DCBERkbMwe5gmLS0Ny5YtQ1JSEmbOnImNGzeiqakJy5cvBwAsXboUERERSE9PBwC88MILmDVrFsaMGYO6ujq8/PLLKCoqwkMPPWTZ72QYEQQBxR01I11XXyUiInJGZoeRRYsWobKyEmvXroVSqURCQgJ2795tLGotLi6GWNzZ4VJbW4sVK1ZAqVTC398fiYmJOHDgAOLj4y33XQwz1U0aNGm0EImAyAB3ezeHiIhoSESCIAgDn2ZfKpUKvr6+qK+vh48PN17LLqrFnW8cQLivAgdWX2fv5hAREfXK1M9vrkzlhIprOoZoWC9CREQugGHECRVWdRSvsl6EiIhcAMOIEzKsvhodxJ4RIiJyfgwjTqjIuPoqe0aIiMj5MYw4oWKuMUJERC6EYcTJNKrbUdWoAcACViIicg0MI07GsCeNv4cbfBRudm4NERHR0DGMOJmyuhYAwEh/9ooQEZFrYBhxMpfr9WEkzJcb3BERkWtgGLGjkppmZBfVmnXN5fpWAAwjRETkOhhG7ESrE3DvWwdx9+YDKKhsNPk6pSGM+HFPGiIicg0MI3aSdbEGpbUt0AnAkULTe0fKOExDREQuhmHETr48UWb888lL9SZfZ+wZ8WXPCBERuQaGETto0+rw9cnLxq9NDSOCILBmhIiIXA7DiB0cuFCN2uY2yKX623/msgptWt2A19U2t0Hdrj8v2Edu1TYSERHZCsOIHXx5XD9Ec1fiSHjLpdC063C+fOAiVsO03iAvOeRSiVXbSEREZCsMIzambtfim1NKAMBtCRGYGOEDADhlwlDN5ToO0RARkethGLGxH/Iq0aBuR6iPAknR/pgc4QvAtLqRyyqGESIicj0MIzb25Ql94erNU8IgFoswyZwwUsdpvURE5HoYRmyoWdOO78+UAwBunhoOAMaekbOXVWgfoIjVMK03lNN6iYjIhTCM2NCe3Aq0tGkRGeCOqSP1ISQm0BNecinU7Tqcr+i/iNUwrTfcjz0jRETkOhhGbMgwi+aWKeEQiUQAALFYhInh+iLWgYZqDLNpQn0YRoiIyHUwjNiIqrUNe/MqAQC3dAzRGEzp6CXpb0ZN1wXPwrkvDRERuRCGERvZc7YCmnYdRo/wRFyod7fnTCli5YJnRETkqhhGbORosX4zvGvHBxuHaAwMRaxnyvouYu1c8EzGBc+IiMilMIzYyOkyFYDOXpCuTCli7VzwjEM0RETkWhhGbECnE3D2sj6MGIpVuzKliNWw4Fko1xghIiIXwzBiA4XVTWjWaCGXihEb5NnrOYahmr6KWJUdwzThDCNERORiGEZs4ExHr0hcqDekkt5v+eSR/RexGoZpuOAZERG5GoYRGzDUi8SH96wXMZg0wEqshmm9XAqeiIhcDcOIDZwxhpGe9SIGsR1FrK1tOuRX9ixiNcymYRghIiJXwzBiA4aekd6KVw26FbGWdh+q6brgGWfTEBGRq2EYsbKKhlZUNaohEqHHYmdX6quIta7LgmchvlzwjIiIXAvDiJUZhmhGBXnCQybt91xDEWtWYW2342Vc8IyIiFwYw4iVmVK8anD12BEQi/RFrCU1zcbjynquMUJERK6LYcTKzvSz2NmVAjxlmBkbAAD49ky58XgZ60WIiMiFMYxYmXEmTdjAYQQAro8PBQB8e1ppPKbkTBoiInJhDCNW1KhuR2F1E4D+p/V2NT8+BABwuLAGNU0aAOBMGiIicmkMI1aUe1kFQQBCfOQI8jJtFkxkgAcmhvtAJwDfn9UP1XRukseeESIicj0MI1bUWS8ycPFqV51DNfowouQmeURE5MIYRqzo9CXz6kUMrp+oH6r56XwlmtTtKKszbJLHYRoiInI9gwojmzZtQkxMDBQKBZKTk5GVlWXSddu2bYNIJMLChQsH87ZOx5yZNF3FhXojMsAd6nYdvjhexgXPiIjIpZkdRrZv3460tDSsW7cOR48exdSpU7FgwQJUVFT0e11hYSGeeuopXH311YNurDNp0+qQp2wAYHrxqoFIJMKCjqGa9w4UAuCCZ0RE5LrMDiOvvvoqVqxYgeXLlyM+Ph6bN2+Gh4cHtm7d2uc1Wq0WixcvxvPPP49Ro0YNqcHO4kJlIzRaHbzlUkT6e5h9/fUT9WEktyPQsF6EiIhclVlhRKPRIDs7G6mpqZ0vIBYjNTUVmZmZfV73wgsvIDg4GA8++KBJ76NWq6FSqbo9nI1hfZEJYT4Qi0VmX58Y7Y9AT5nx61Af1osQEZFrMiuMVFVVQavVIiQkpNvxkJAQKJXKXq/Zv38/3n77bbz11lsmv096ejp8fX2Nj8jISHOa6RA6l4E3b4jGQCIWIXVC530O92PPCBERuSarzqZpaGjAkiVL8NZbbyEoKMjk61avXo36+nrjo6SkxIqttI4zQwwjQOesGoDDNERE5Lr630b2CkFBQZBIJCgvL+92vLy8HKGhoT3Ov3DhAgoLC3HLLbcYj+l0+pkhUqkUeXl5GD16dI/r5HI55HLnnTkiCIJxJo2503q7mjMmCB4yCZo1Wk7rJSIil2VWz4hMJkNiYiIyMjKMx3Q6HTIyMpCSktLj/Li4OJw8eRI5OTnGx6233oprr70WOTk5Tjn8YoqaJg3qW9oAAGOCvQb9Ogo3CR66ehSiAjwwe3SgpZpHRETkUMzqGQGAtLQ0LFu2DElJSZg5cyY2btyIpqYmLF++HACwdOlSREREID09HQqFApMmTep2vZ+fHwD0OO5KSmr1i5SF+iigcBvadNy0+eOQNn+cJZpFRETkkMwOI4sWLUJlZSXWrl0LpVKJhIQE7N6921jUWlxcDLF4eC/sWlzTDACIDODQChER0UBEgiAI9m7EQFQqFXx9fVFfXw8fn8HXYNjKpr35ePmbPNwxPQKv/irB3s0hIiKyC1M/v4d3F4aVlBh6Rgax2BkREdFwwzBiBSW1+jASFcAwQkRENBCGESvorBlhGCEiIhoIw4iFtWt1KKtrBcCeESIiIlMwjFjY5fpWaHUCZFIxgr2dd+E2IiIiW2EYsTBD8epIf/dBbZBHREQ03DCMWJiheJUzaYiIiEzDMGJhhuJV1osQERGZhmHEwkpq9EvBc/VVIiIi0zCMWFgxFzwjIiIyC8OIhZXWco0RIiIiczCMWFCTuh1VjRoADCNERESmYhixoNJafb2Ir7sbfN3d7NwaIiIi58AwYkGdy8CzeJWIiMhUDCMWVMJpvURERGZjGLEgzqQhIiIyH8OIBXEmDRERkfkYRiyoc8EzhhEiIiJTMYxYiCAIXAqeiIhoEBhGLKS6SYOWNi1EIiDcT2Hv5hARETkNhhELMfSKhPkoIJdK7NwaIiIi58EwYiGGab0jOURDRERkFoYRC+EaI0RERIPDMGIhxpk0XGOEiIjILAwjFsKl4ImIiAaHYcRCSmo5TENERDQYDCMW0KbVoayOC54RERENBsOIBVyua4VOAORSMUZ4ye3dHCIiIqfCMGIBhnqRkf7uEItFdm4NERGRc2EYsQDWixAREQ0ew4gFdM6kYRghIiIyF8OIBZwvbwAAjAn2snNLiIiInA/DiAWcvawPI3GhPnZuCRERkfNhGBkiVWsbLnVM6x0f6m3n1hARETkfhpEhyu3oFYnwc4evu5udW0NEROR8GEaGKFepAgDEsVeEiIhoUBhGhshYLxLGMEJERDQYDCND1NkzwuJVIiKiwWAYGQKdTkCeUt8zMoE9I0RERIPCMDIEJbXNaNZoIZOKERPoae/mEBEROSWGkSEw1IuMC/GCVMJbSURENBiD+gTdtGkTYmJioFAokJycjKysrD7P3bFjB5KSkuDn5wdPT08kJCTggw8+GHSDHQnrRYiIiIbO7DCyfft2pKWlYd26dTh69CimTp2KBQsWoKKiotfzAwICsGbNGmRmZuLEiRNYvnw5li9fjm+++WbIjbe3XOPKq6wXISIiGiyzw8irr76KFStWYPny5YiPj8fmzZvh4eGBrVu39nr+vHnzcPvtt2PChAkYPXo0Vq1ahSlTpmD//v1Dbry9GXpGJoSxZ4SIiGiwzAojGo0G2dnZSE1N7XwBsRipqanIzMwc8HpBEJCRkYG8vDzMnTu3z/PUajVUKlW3h6NpUrejqGO3XvaMEBERDZ5ZYaSqqgparRYhISHdjoeEhECpVPZ5XX19Pby8vCCTyXDTTTfhtddew/z58/s8Pz09Hb6+vsZHZGSkOc20ibzyBggCEOwtR6CX3N7NISIiclo2mQLi7e2NnJwcHD58GC+99BLS0tKwb9++Ps9fvXo16uvrjY+SkhJbNNMsxnoRDtEQERENidSck4OCgiCRSFBeXt7teHl5OUJDQ/u8TiwWY8yYMQCAhIQEnD17Funp6Zg3b16v58vlcsjljt3bYKwX4RANERHRkJjVMyKTyZCYmIiMjAzjMZ1Oh4yMDKSkpJj8OjqdDmq12py3dji53JOGiIjIIszqGQGAtLQ0LFu2DElJSZg5cyY2btyIpqYmLF++HACwdOlSREREID09HYC+/iMpKQmjR4+GWq3GV199hQ8++ABvvPGGZb8TGxIEAWe5xggREZFFmB1GFi1ahMrKSqxduxZKpRIJCQnYvXu3sai1uLgYYnFnh0tTUxMeffRRlJaWwt3dHXFxcfjwww+xaNEiy30XNlZW34qG1nZIxSKMHuFl7+YQERE5NZEgCIK9GzEQlUoFX19f1NfXw8fH/j0RGWfL8eB7RxAX6o3dT/Q9RZmIiGg4M/XzmxuqDEKukiuvEhERWQrDyCCcvdxRL8JpvUREREPGMDII7BkhIiKyHIYRM7W2aVFQ2QiAe9IQERFZAsOImQqrm6ATAB+FFMHejr0wGxERkTNgGDFTYZV+c7zYIE+IRCI7t4aIiMj5MYyYqbimCQAQFehp55YQERG5BoYRMxVW63tGYgI97NwSIiIi18AwYqbijjASFcAwQkREZAkMI2YqrNYP08QEcZiGiIjIEhhGzKBp16GsrgUAEM2eESIiIotgGDFDaW0zdALg7ibBCE7rJSIisgiGETMUddSLRAd6cFovERGRhTCMmKGoo14kmjNpiIiILIZhxAyd03pZvEpERGQpDCNmKK7pmNbLnhEiIiKLYRgxg3FaL3tGiIiILIZhxERanYDSmo5pvewZISIishiGERNdrm+BRquDm0SEMF93ezeHiIjIZTCMmMgwrTcywAMSMaf1EhERWQrDiImMa4xw5VUiIiKLYhgxUecaIyxeJSIisiSGERMVGdcYYc8IERGRJTGMmKiQPSNERERWwTBiAkEQjAuecVovERGRZTGMmKCyUY1mjRZiETDSn2GEiIjIkhhGTGCoFwn3c4dMyltGRERkSfxkNYFxWi+HaIiIiCyOYcQEnNZLRERkPQwjJuC0XiIiIuthGDGBoWckKoA9I0RERJbGMGKCoo5pvTFB7BkhIiKyNIaRAdQ1a1DX3AYAiOK+NERERBbHMDIAQ71IsLccHjKpnVtDRETkehhGBmAcouFMGiIiIqtgGBlAUVVH8Spn0hAREVkFw8gADD0j0awXISIisgqGkQHUNmkAAME+cju3hIiIyDUxjAygUd0OACxeJSIishKGkQE0a7QAAC85wwgREZE1MIwMoKmjZ8STYYSIiMgqBhVGNm3ahJiYGCgUCiQnJyMrK6vPc9966y1cffXV8Pf3h7+/P1JTU/s939E0GsOIxM4tISIick1mh5Ht27cjLS0N69atw9GjRzF16lQsWLAAFRUVvZ6/b98+3Hvvvdi7dy8yMzMRGRmJ66+/HpcuXRpy423B2DPCmhEiIiKrEAmCIJhzQXJyMmbMmIHXX38dAKDT6RAZGYnHH38czz777IDXa7Va+Pv74/XXX8fSpUtNek+VSgVfX1/U19fDx8fHnOYOiU4nYNRzXwEADq9JxQhvzqghIiIylamf32b1jGg0GmRnZyM1NbXzBcRipKamIjMz06TXaG5uRltbGwICAvo8R61WQ6VSdXvYQ0ub1vhnFrASERFZh1lhpKqqClqtFiEhId2Oh4SEQKlUmvQazzzzDMLDw7sFmiulp6fD19fX+IiMjDSnmRZjGKIRiwCFG2t9iYiIrMGmn7AbNmzAtm3bsHPnTigUij7PW716Nerr642PkpISG7ayU2OXmTQikcgubSAiInJ1Zo09BAUFQSKRoLy8vNvx8vJyhIaG9nvtK6+8gg0bNuD777/HlClT+j1XLpdDLrd/fUaTWj9Mw+JVIiIi6zGrZ0QmkyExMREZGRnGYzqdDhkZGUhJSenzur/85S948cUXsXv3biQlJQ2+tTbGab1ERETWZ/av/GlpaVi2bBmSkpIwc+ZMbNy4EU1NTVi+fDkAYOnSpYiIiEB6ejoA4M9//jPWrl2Ljz/+GDExMcbaEi8vL3h5eVnwW7G8Zo0+jLB4lYiIyHrM/pRdtGgRKisrsXbtWiiVSiQkJGD37t3Gotbi4mKIxZ0dLm+88QY0Gg3uuuuubq+zbt06/OlPfxpa662skauvEhERWd2gPmVXrlyJlStX9vrcvn37un1dWFg4mLdwCIaaEW6SR0REZD2cr9oPw9ReL9aMEBERWQ3DSD+aNBymISIisjaGkX509owwjBAREVkLw0g/Gg3rjDCMEBERWQ3DSD8MPSMeMtaMEBERWQvDSD84TENERGR9DCP9YAErERGR9TGM9MOwzgh7RoiIiKyHYaQfrBkhIiKyPoaRfnA5eCIiIutjGOlHs4bDNERERNbGMNIHQRBYwEpERGQDDCN9aNZoIQj6P3tybxoiIiKrYRjpg6F4VSwC3N0YRoiIiKyFYaQPxuJVmRQikcjOrSEiInJdDCN9MBSvsl6EiIjIuhhG+tA5rZdDNERERNbEMNKHJq4xQkREZBMMI33oWjNCRERE1sMw0gfDvjTsGSEiIrIuhpE+NHcseObFmhEiIiKrYhjpg2GYxoM9I0RERFbFMNIHQwEr96UhIiKyLoaRPjQaakZYwEpERGRVDCN9aNZwnREiIiJbYBjpA4dpiIiIbINhpA8sYCUiIrINhpE+GNYZ4dReIiIi62IY6UMTV2AlIiKyCYaRPjRpuDcNERGRLTCM9IHLwRMREdkGw0gvBEHo0jPCmhEiIiJrYhjpRbNGC0HQ/5lTe4mIiKyLYaQXhl4RkQhwd2PPCBERkTUxjPSiqctS8CKRyM6tISIicm0MI70wTutlvQgREZHVMYz0olHNab1ERES2wjDSC+5LQ0REZDsMI71o0nTWjBAREZF1MYz0gjUjREREtjOoMLJp0ybExMRAoVAgOTkZWVlZfZ57+vRp3HnnnYiJiYFIJMLGjRsH21abaWLNCBERkc2YHUa2b9+OtLQ0rFu3DkePHsXUqVOxYMECVFRU9Hp+c3MzRo0ahQ0bNiA0NHTIDbYFFrASERHZjtlh5NVXX8WKFSuwfPlyxMfHY/PmzfDw8MDWrVt7PX/GjBl4+eWXcc8990Aulw+5wbbAAlYiIiLbMSuMaDQaZGdnIzU1tfMFxGKkpqYiMzPT4o2zF0MBq4eMNSNERETWZtav/lVVVdBqtQgJCel2PCQkBLm5uRZrlFqthlqtNn6tUqks9tqmYM8IERGR7TjkbJr09HT4+voaH5GRkTZ9fxawEhER2Y5ZYSQoKAgSiQTl5eXdjpeXl1u0OHX16tWor683PkpKSiz22qZgASsREZHtmBVGZDIZEhMTkZGRYTym0+mQkZGBlJQUizVKLpfDx8en28OWmjtqRry4zggREZHVmf2rf1paGpYtW4akpCTMnDkTGzduRFNTE5YvXw4AWLp0KSIiIpCeng5AX/R65swZ458vXbqEnJwceHl5YcyYMRb8VizH0DPiwRVYiYiIrM7sT9tFixahsrISa9euhVKpREJCAnbv3m0sai0uLoZY3NnhUlZWhmnTphm/fuWVV/DKK6/gmmuuwb59+4b+HVgBC1iJiIhsRyQIgmDvRgxEpVLB19cX9fX1NhmymbTuGzSq27H3qXmIDfK0+vsRERG5IlM/vx1yNo09CYKAJg33piEiIrIVhpErtLRpYegr4q69RERE1scwcgVD8apIxBVYiYiIbIFh5ApNav20Xk+ZFCKRyM6tISIicn0MI1foXH2VvSJERES2wDByBWMYYb0IERGRTTCMXKFzJg3DCBERkS0wjFyh0VAzwmEaIiIim2AYuQJXXyUiIrIthpErNHHHXiIiIptiGLmCYWovN8kjIiKyDYaRKxgKWL1YM0JERGQTDCNXaOQwDRERkU0xjFyBBaxERES2xTByBUMYYc0IERGRbTCMXKGJ64wQERHZFMPIFToLWNkzQkREZAsMI1dgASsREZFtMYxcgRvlERER2RbDyBWaWTNCRERkUwwjXQiCwJoRIiIiG2MY6aKlTQudoP8za0aIiIhsg2GkC0PxqkgEuLtxmIaIiMgWGEa6MG6S5yaBWCyyc2uIiIiGB4aRLpo4rZeIiMjmGEa6KK5pBgCM8JbbuSVERETDB8NIF0eLagEA06L87NsQIiKiYYRhpIvsYn0YmR7lb+eWEBERDR8MIx3U7VqcvqQCwDBCRERkSwwjHU5dUkGj1SHQU4boQA97N4eIiGjYYBjp0Fkv4g+RiNN6iYiIbIVhpMPRjnqRxGgO0RAREdkSwwj0e9IcNRav+tm3MURERMMMwwiAS3UtKFepIRWLMGWkn72bQ0RENKwwjAA4WlwHAIgP94G7jHvSEBER2RLDCDqLVzmll4iIyPYYRtBZvMqVV4mIiGxv2IeR1jYtzpTpFzvjTBoiIiLbG/Zh5ERpPdp1AoK95Yjwc7d3c4iIiIadYR9Gsos61xfhYmdERES2N+zDyFFujkdERGRXgwojmzZtQkxMDBQKBZKTk5GVldXv+Z9++ini4uKgUCgwefJkfPXVV4NqrKUJgoBjhjAS7WffxhAREQ1TZoeR7du3Iy0tDevWrcPRo0cxdepULFiwABUVFb2ef+DAAdx777148MEHcezYMSxcuBALFy7EqVOnhtz4oSquaUZVowYyiRgTw33t3RwiIqJhSSQIgmDOBcnJyZgxYwZef/11AIBOp0NkZCQef/xxPPvssz3OX7RoEZqamrBr1y7jsVmzZiEhIQGbN2826T1VKhV8fX1RX18PHx8fc5rbr53HSvHk9uOYFuWHnY/OsdjrEhERkemf32b1jGg0GmRnZyM1NbXzBcRipKamIjMzs9drMjMzu50PAAsWLOjzfABQq9VQqVTdHtZgLF5lvQgREZHdmBVGqqqqoNVqERIS0u14SEgIlEplr9colUqzzgeA9PR0+Pr6Gh+RkZHmNNNkR4vqAADTub4IERGR3TjkbJrVq1ejvr7e+CgpKbHK+6yYG4t7Z0YhiWGEiIjIbqTmnBwUFASJRILy8vJux8vLyxEaGtrrNaGhoWadDwByuRxyudycpg3K7dNG4vZpI63+PkRERNQ3s3pGZDIZEhMTkZGRYTym0+mQkZGBlJSUXq9JSUnpdj4AfPfdd32eT0RERMOLWT0jAJCWloZly5YhKSkJM2fOxMaNG9HU1ITly5cDAJYuXYqIiAikp6cDAFatWoVrrrkGf/3rX3HTTTdh27ZtOHLkCLZs2WLZ74SIiIicktlhZNGiRaisrMTatWuhVCqRkJCA3bt3G4tUi4uLIRZ3drjMnj0bH3/8Mf7v//4Pzz33HMaOHYvPPvsMkyZNstx3QURERE7L7HVG7MFa64wQERGR9VhlnREiIiIiS2MYISIiIrtiGCEiIiK7YhghIiIiu2IYISIiIrtiGCEiIiK7YhghIiIiu2IYISIiIrtiGCEiIiK7Mns5eHswLBKrUqns3BIiIiIyleFze6DF3p0ijDQ0NAAAIiMj7dwSIiIiMldDQwN8fX37fN4p9qbR6XQoKyuDt7c3RCKRxV5XpVIhMjISJSUl3PPGynivbYf32rZ4v22H99p2LHWvBUFAQ0MDwsPDu22ieyWn6BkRi8UYOXKk1V7fx8eH/7BthPfadnivbYv323Z4r23HEve6vx4RAxawEhERkV0xjBAREZFdDeswIpfLsW7dOsjlcns3xeXxXtsO77Vt8X7bDu+17dj6XjtFASsRERG5rmHdM0JERET2xzBCREREdsUwQkRERHbFMEJERER2NazDyKZNmxATEwOFQoHk5GRkZWXZu0lOLz09HTNmzIC3tzeCg4OxcOFC5OXldTuntbUVjz32GAIDA+Hl5YU777wT5eXldmqxa9iwYQNEIhGeeOIJ4zHeZ8u6dOkS7rvvPgQGBsLd3R2TJ0/GkSNHjM8LgoC1a9ciLCwM7u7uSE1Nxfnz5+3YYuek1Wrxxz/+EbGxsXB3d8fo0aPx4osvdtvbhPd6cH788UfccsstCA8Ph0gkwmeffdbteVPua01NDRYvXgwfHx/4+fnhwQcfRGNj49AbJwxT27ZtE2QymbB161bh9OnTwooVKwQ/Pz+hvLzc3k1zagsWLBDeeecd4dSpU0JOTo7wy1/+UoiKihIaGxuN5zz88MNCZGSkkJGRIRw5ckSYNWuWMHv2bDu22rllZWUJMTExwpQpU4RVq1YZj/M+W05NTY0QHR0t3H///cKhQ4eEgoIC4ZtvvhHy8/ON52zYsEHw9fUVPvvsM+H48ePCrbfeKsTGxgotLS12bLnzeemll4TAwEBh165dwsWLF4VPP/1U8PLyEv7+978bz+G9HpyvvvpKWLNmjbBjxw4BgLBz585uz5tyX2+44QZh6tSpwsGDB4WffvpJGDNmjHDvvfcOuW3DNozMnDlTeOyxx4xfa7VaITw8XEhPT7djq1xPRUWFAED44YcfBEEQhLq6OsHNzU349NNPjeecPXtWACBkZmbaq5lOq6GhQRg7dqzw3XffCddcc40xjPA+W9YzzzwjXHXVVX0+r9PphNDQUOHll182HqurqxPkcrnw73//2xZNdBk33XST8MADD3Q7dscddwiLFy8WBIH32lKuDCOm3NczZ84IAITDhw8bz/n6668FkUgkXLp0aUjtGZbDNBqNBtnZ2UhNTTUeE4vFSE1NRWZmph1b5nrq6+sBAAEBAQCA7OxstLW1dbv3cXFxiIqK4r0fhMceeww33XRTt/sJ8D5b2hdffIGkpCTcfffdCA4OxrRp0/DWW28Zn7948SKUSmW3++3r64vk5GTebzPNnj0bGRkZOHfuHADg+PHj2L9/P2688UYAvNfWYsp9zczMhJ+fH5KSkoznpKamQiwW49ChQ0N6f6fYKM/SqqqqoNVqERIS0u14SEgIcnNz7dQq16PT6fDEE09gzpw5mDRpEgBAqVRCJpPBz8+v27khISFQKpV2aKXz2rZtG44ePYrDhw/3eI732bIKCgrwxhtvIC0tDc899xwOHz6M3/3ud5DJZFi2bJnxnvb2M4X32zzPPvssVCoV4uLiIJFIoNVq8dJLL2Hx4sUAwHttJabcV6VSieDg4G7PS6VSBAQEDPneD8swQrbx2GOP4dSpU9i/f7+9m+JySkpKsGrVKnz33XdQKBT2bo7L0+l0SEpKwvr16wEA06ZNw6lTp7B582YsW7bMzq1zLZ988gk++ugjfPzxx5g4cSJycnLwxBNPIDw8nPfahQ3LYZqgoCBIJJIeMwvKy8sRGhpqp1a5lpUrV2LXrl3Yu3cvRo4caTweGhoKjUaDurq6bufz3psnOzsbFRUVmD59OqRSKaRSKX744Qf84x//gFQqRUhICO+zBYWFhSE+Pr7bsQkTJqC4uBgAjPeUP1OG7g9/+AOeffZZ3HPPPZg8eTKWLFmCJ598Eunp6QB4r63FlPsaGhqKioqKbs+3t7ejpqZmyPd+WIYRmUyGxMREZGRkGI/pdDpkZGQgJSXFji1zfoIgYOXKldi5cyf27NmD2NjYbs8nJibCzc2t273Py8tDcXEx770ZrrvuOpw8eRI5OTnGR1JSEhYvXmz8M++z5cyZM6fHFPVz584hOjoaABAbG4vQ0NBu91ulUuHQoUO832Zqbm6GWNz9o0kikUCn0wHgvbYWU+5rSkoK6urqkJ2dbTxnz5490Ol0SE5OHloDhlT+6sS2bdsmyOVy4d133xXOnDkj/OY3vxH8/PwEpVJp76Y5tUceeUTw9fUV9u3bJ1y+fNn4aG5uNp7z8MMPC1FRUcKePXuEI0eOCCkpKUJKSoodW+0aus6mEQTeZ0vKysoSpFKp8NJLLwnnz58XPvroI8HDw0P48MMPjeds2LBB8PPzEz7//HPhxIkTwm233cbppoOwbNkyISIiwji1d8eOHUJQUJDw9NNPG8/hvR6choYG4dixY8KxY8cEAMKrr74qHDt2TCgqKhIEwbT7esMNNwjTpk0TDh06JOzfv18YO3Ysp/YO1WuvvSZERUUJMplMmDlzpnDw4EF7N8npAej18c477xjPaWlpER599FHB399f8PDwEG6//Xbh8uXL9mu0i7gyjPA+W9aXX34pTJo0SZDL5UJcXJywZcuWbs/rdDrhj3/8oxASEiLI5XLhuuuuE/Ly8uzUWuelUqmEVatWCVFRUYJCoRBGjRolrFmzRlCr1cZzeK8HZ+/evb3+fF62bJkgCKbd1+rqauHee+8VvLy8BB8fH2H58uVCQ0PDkNsmEoQuy9oRERER2diwrBkhIiIix8EwQkRERHbFMEJERER2xTBCREREdsUwQkRERHbFMEJERER2xTBCREREdsUwQkRERHbFMEJERER2xTBCREREdsUwQkRERHbFMEJERER29f8BSR9hV54uPU4AAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] + "text/plain": "
", + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -802,76 +876,20 @@ }, { "cell_type": "code", - "execution_count": 50, - "metadata": {}, + "execution_count": 21, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:15.036738Z", + "end_time": "2023-04-24T21:12:15.045520Z" + } + }, "outputs": [ { "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Wealth
StepAgentID
001
11
21
31
41
\n", - "
" - ], - "text/plain": [ - " Wealth\n", - "Step AgentID \n", - "0 0 1\n", - " 1 1\n", - " 2 1\n", - " 3 1\n", - " 4 1" - ] + "text/plain": " Wealth\nStep AgentID \n0 0 1\n 1 1\n 2 1\n 3 1\n 4 1", + "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Wealth
StepAgentID
001
11
21
31
41
\n
" }, - "execution_count": 50, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -890,25 +908,26 @@ }, { "cell_type": "code", - "execution_count": 51, - "metadata": {}, + "execution_count": 22, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:15.045520Z", + "end_time": "2023-04-24T21:12:15.200806Z" + } + }, "outputs": [ { "data": { - "text/plain": [ - "" - ] + "text/plain": "" }, - "execution_count": 51, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGdCAYAAACyzRGfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAbO0lEQVR4nO3df4zXhX348dfn4Pjg1TscIBwXDkvbVdsaMBVlpM6g8qNsoWXjj06bFI3p0uU0tZemG0tVrprQuKRjSyntkk3WdDe7LQNTE9GTRogZtIIh1iU1Ql2qFVBsuIMjfvyE+3z/8HtHr4fKwefz+ngfHo/kkn7e9+b9efV1J/fM5/PhPoVKpVIJAIAkTfUeAAC4uIgPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACDV5HoP8PuGhobitddei9bW1igUCvUeBwA4B5VKJU6cOBEdHR3R1PTej2184OLjtddei87OznqPAQCch1deeSXmzp37nud84OKjtbU1It4Zvq2trarXLpfL8eSTT8aKFSuiubm5qtfmDHvOYc857DmPXeeo1Z4HBgais7Nz5Of4e/nAxcfwUy1tbW01iY+WlpZoa2vzjV1D9pzDnnPYcx67zlHrPZ/LSya84BQASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU44qPjRs3xnXXXRetra0xa9asWLNmTbz44oujzlm6dGkUCoVRH1/5yleqOjQAMHGNKz527doVXV1dsXfv3ujr64tyuRwrVqyIwcHBUed9+ctfjsOHD498PPTQQ1UdGgCYuMb1xnI7duwYdXvr1q0xa9as2L9/f9x4440jx1taWqK9vb06EwIADeWC3tW2v78/IiKmT58+6vi//du/xY9+9KNob2+P1atXx7333hstLS1nvUapVIpSqTRye2BgICLeede9crl8IeONMXy9al+X0ew5hz3nsOc8dp2jVnsez/UKlUqlcj53MjQ0FJ/73Ofi+PHj8cwzz4wc/6d/+qe44ooroqOjI55//vn467/+67j++uvjv//7v896nQ0bNkRPT8+Y4729ve8aLADAB8upU6fitttui/7+/mhra3vPc887Pv7qr/4qHn/88XjmmWdi7ty573reT3/607jlllvi4MGD8dGPfnTM58/2yEdnZ2ccO3bsfYcfr3K5HH19fXHvvqYoDRWqem3OKDZV4oFFQ7F8+fJobm6u9zgNa/j72Z5ry57z2HWOWu15YGAgZs6ceU7xcV5Pu9x1113x2GOPxe7du98zPCIiFi9eHBHxrvFRLBajWCyOOd7c3Fyzb77SUCFKp8VHrdXya8gZ9pzDnvPYdY5q73k81xpXfFQqlbj77rtj27Zt8fTTT8f8+fPf988cOHAgIiLmzJkznrsCABrUuOKjq6srent749FHH43W1tY4cuRIRERMmzYtLrnkkjh06FD09vbGn/zJn8SMGTPi+eefj6997Wtx4403xoIFC2ryfwAAmFjGFR9btmyJiHd+kdjvevjhh+P222+PKVOmxFNPPRWbNm2KwcHB6OzsjLVr18Y3v/nNqg0MAExs437a5b10dnbGrl27LmggAKCxeW8XACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACDVuOJj48aNcd1110Vra2vMmjUr1qxZEy+++OKoc956663o6uqKGTNmxKWXXhpr166No0ePVnVoAGDiGld87Nq1K7q6umLv3r3R19cX5XI5VqxYEYODgyPnfO1rX4uf/OQn8Z//+Z+xa9eueO211+LP//zPqz44ADAxTR7PyTt27Bh1e+vWrTFr1qzYv39/3HjjjdHf3x///M//HL29vXHzzTdHRMTDDz8cn/jEJ2Lv3r3xR3/0R9WbHACYkC7oNR/9/f0RETF9+vSIiNi/f3+Uy+VYtmzZyDlXXXVVzJs3L/bs2XMhdwUANIhxPfLxu4aGhuKee+6Jz3zmM3H11VdHRMSRI0diypQpcdlll406d/bs2XHkyJGzXqdUKkWpVBq5PTAwEBER5XI5yuXy+Y53VsPXKzZVqnpdRhveb7W/fow2vF97ri17zmPXOWq15/Fc77zjo6urK1544YV45plnzvcSEfHOi1h7enrGHH/yySejpaXlgq79bh5YNFST6zJaX19fvUe4KNhzDnvOY9c5qr3nU6dOnfO55xUfd911Vzz22GOxe/fumDt37sjx9vb2ePvtt+P48eOjHv04evRotLe3n/Va69evj+7u7pHbAwMD0dnZGStWrIi2trbzGe9dlcvl6Ovri3v3NUVpqFDVa3NGsakSDywaiuXLl0dzc3O9x2lYw9/P9lxb9pzHrnPUas/Dz1yci3HFR6VSibvvvju2bdsWTz/9dMyfP3/U56+99tpobm6OnTt3xtq1ayMi4sUXX4xf//rXsWTJkrNes1gsRrFYHHO8ubm5Zt98paFClE6Lj1qr5deQM+w5hz3nsesc1d7zeK41rvjo6uqK3t7eePTRR6O1tXXkdRzTpk2LSy65JKZNmxZ33nlndHd3x/Tp06OtrS3uvvvuWLJkiX/pAgBExDjjY8uWLRERsXTp0lHHH3744bj99tsjIuLv//7vo6mpKdauXRulUilWrlwZ3/ve96oyLAAw8Y37aZf3M3Xq1Ni8eXNs3rz5vIcCABqX93YBAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAg1bjjY/fu3bF69ero6OiIQqEQ27dvH/X522+/PQqFwqiPz372s9WaFwCY4MYdH4ODg7Fw4cLYvHnzu57z2c9+Ng4fPjzy8e///u8XNCQA0Dgmj/cPrFq1KlatWvWe5xSLxWhvbz/voQCAxjXu+DgXTz/9dMyaNSv+4A/+IG6++eZ48MEHY8aMGWc9t1QqRalUGrk9MDAQERHlcjnK5XJV5xq+XrGpUtXrMtrwfqv99WO04f3ac23Zcx67zlGrPY/neoVKpXLeP4kLhUJs27Yt1qxZM3LskUceiZaWlpg/f34cOnQo/vZv/zYuvfTS2LNnT0yaNGnMNTZs2BA9PT1jjvf29kZLS8v5jgYAJDp16lTcdttt0d/fH21tbe95btXj4/f96le/io9+9KPx1FNPxS233DLm82d75KOzszOOHTv2vsOPV7lcjr6+vrh3X1OUhgpVvTZnFJsq8cCioVi+fHk0NzfXe5yGNfz9bM+1Zc957DpHrfY8MDAQM2fOPKf4qMnTLr/rIx/5SMycOTMOHjx41vgoFotRLBbHHG9ubq7ZN19pqBCl0+Kj1mr5NeQMe85hz3nsOke19zyea9X893y8+uqr8eabb8acOXNqfVcAwAQw7kc+Tp48GQcPHhy5/fLLL8eBAwdi+vTpMX369Ojp6Ym1a9dGe3t7HDp0KL7xjW/Exz72sVi5cmVVBwcAJqZxx8e+ffvipptuGrnd3d0dERHr1q2LLVu2xPPPPx//+q//GsePH4+Ojo5YsWJFPPDAA2d9agUAuPiMOz6WLl0a7/Ua1SeeeOKCBgIAGpv3dgEAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUo07Pnbv3h2rV6+Ojo6OKBQKsX379lGfr1Qqcd9998WcOXPikksuiWXLlsVLL71UrXkBgAlu3PExODgYCxcujM2bN5/18w899FD84z/+Y3z/+9+Pn/3sZ/GhD30oVq5cGW+99dYFDwsATHyTx/sHVq1aFatWrTrr5yqVSmzatCm++c1vxuc///mIiPjhD38Ys2fPju3bt8df/MVfXNi0AMCEN+74eC8vv/xyHDlyJJYtWzZybNq0abF48eLYs2fPWeOjVCpFqVQauT0wMBAR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", - "text/plain": [ - "
" - ] + "text/plain": "
", + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -928,25 +947,26 @@ }, { "cell_type": "code", - "execution_count": 52, - "metadata": {}, + "execution_count": 23, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:15.138376Z", + "end_time": "2023-04-24T21:12:15.275867Z" + } + }, "outputs": [ { "data": { - "text/plain": [ - "" - ] + "text/plain": "" }, - "execution_count": 52, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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6OzZs5o+fbrmzp2rG2+8UZMnT5bU2sOxfft2FRUVWc+ZP3++rrnmGpWUlFjHnn76ad1555364IMPrOfdfvvt2rBhg3XO7NmzNX36dP3whz9MzDcHIG7oGQEQMzfccIM++OAD/eIXv9DChQtVWVmp6dOna/PmzRGf88Ybb+j++++3elays7O1YsUK1dTU6MyZM9Z5hYWFYc8rLCykZwQYIJjACiCmMjMzde211+raa6/V3Xffrdtuu0333nuvbr755i7Pb2xs1H333acvfvGLXX4tAAMfPSMA4mrixIny+/2SpPT0dAWDwbDHp0+frqNHj2rcuHHn3dzu9reoffv2hT1v3759uuyyy+L/DQCIO3pGAMTExx9/rKVLl+qrX/2qJk+eLJ/Pp6qqKn3ve9/TkiVLJLWuqKmoqNDVV18tr9eroUOH6p577tEXvvAFjR49Wl/60pfkdrv1xhtv6MiRI/r3f/936+tv27ZNM2fO1Gc/+1k988wz2r9/v5544gm7vl0AMcQEVgAxEQgE9G//9m/61a9+pT/96U9qbm5WQUGBli5dqrvuukuDBg3SL3/5S61du1Z/+ctfNHLkSGtp786dO3X//ffr0KFDSk9P14QJE3TbbbdpxYoVklonsD722GMqKyvTrl27NHz4cH33u9/VP/zDP9j4HQOIFcIIAMfrahUOgIGDOSMAAMBWhBEAAGArJrACcDxGk4GBjZ4RAABgK8IIAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBW/x98HZPml6qnJAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] + "text/plain": "
", + "image/png": "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" }, "metadata": {}, "output_type": "display_data" @@ -968,8 +988,13 @@ }, { "cell_type": "code", - "execution_count": 53, - "metadata": {}, + "execution_count": 24, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:15.246086Z", + "end_time": "2023-04-24T21:12:15.276880Z" + } + }, "outputs": [], "source": [ "# save the model data (stored in the pandas gini object) to CSV\n", @@ -997,8 +1022,13 @@ }, { "cell_type": "code", - "execution_count": 54, - "metadata": {}, + "execution_count": 25, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:15.256575Z", + "end_time": "2023-04-24T21:12:15.276880Z" + } + }, "outputs": [], "source": [ "def compute_gini(model):\n", @@ -1102,14 +1132,19 @@ }, { "cell_type": "code", - "execution_count": 55, - "metadata": {}, + "execution_count": 26, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:15.262311Z", + "end_time": "2023-04-24T21:12:35.235655Z" + } + }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 245/245 [00:48<00:00, 5.10it/s]\n" + "100%|██████████| 245/245 [00:19<00:00, 12.27it/s]\n" ] } ], @@ -1136,8 +1171,13 @@ }, { "cell_type": "code", - "execution_count": 56, - "metadata": {}, + "execution_count": 27, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:35.235655Z", + "end_time": "2023-04-24T21:12:43.579218Z" + } + }, "outputs": [ { "name": "stdout", @@ -1165,25 +1205,26 @@ }, { "cell_type": "code", - "execution_count": 57, - "metadata": {}, + "execution_count": 28, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:43.580227Z", + "end_time": "2023-04-24T21:12:43.703199Z" + } + }, "outputs": [ { "data": { - "text/plain": [ - "" - ] + "text/plain": "" }, - "execution_count": 57, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGiCAYAAAA1LsZRAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABLeklEQVR4nO3df1xUdb4/8NcMMjOiMogIg4SCP8pYFRSEsF97C8PNa7Xb3gfZD32wrX1ztesu3XuTNiVzd7Gt62Xv6tXN1dqbj9K7u5aVLruGWVkYhloSamkoZAyI6IAooMz5/kEzMjJzzufAYQ4zvJ6PxzweOnNm5jOHmXPe5/P5vN8fgyRJEoiIiIh0YtS7AURERDSwMRghIiIiXTEYISIiIl0xGCEiIiJdMRghIiIiXTEYISIiIl0xGCEiIiJdMRghIiIiXTEYISIiIl0xGCEiIiJd9SgYWbt2LRISEmCxWJCRkYGysjLZ7YuKinDDDTdg8ODBiI+Pxy9+8Qu0trb2qMFEREQUXFQHI1u3bkVeXh4KCgpw4MABJCcnIzs7G/X19V63f+2117B06VIUFBTgyJEj2LhxI7Zu3Yqnn366140nIiKiwGdQu1BeRkYGpk+fjjVr1gAAnE4n4uPj8cQTT2Dp0qXdtl+8eDGOHDmCkpIS931PPvkkPvnkE+zdu7eXzSciIqJAN0jNxu3t7SgvL0d+fr77PqPRiKysLJSWlnp9zowZM7B582aUlZUhPT0dX3/9NXbu3IlHHnnE5/u0tbWhra3N/X+n04nGxkaMGDECBoNBTZOJiIhIJ5Ikobm5GaNGjYLR6HswRlUw0tDQgI6ODsTExHjcHxMTg6NHj3p9zoMPPoiGhgbccsstkCQJV65cweOPPy47TFNYWIgVK1aoaRoRERH1UzU1Nbjuuut8Pq4qGOmJPXv24De/+Q3+53/+BxkZGTh+/DiWLFmClStXYtmyZV6fk5+fj7y8PPf/HQ4HRo8ejZqaGoSHh/d1k4mIiEgDTU1NiI+Px7Bhw2S3UxWMREVFISQkBHV1dR7319XVwWazeX3OsmXL8Mgjj+CnP/0pAGDy5MloaWnBY489hl/+8pdeu23MZjPMZnO3+8PDwxmMEBERBRilKRaqsmlMJhNSU1M9JqM6nU6UlJQgMzPT63MuXrzYLeAICQkB0DmWRERERAOb6mGavLw8zJ8/H2lpaUhPT0dRURFaWlqQm5sLAJg3bx7i4uJQWFgIAJgzZw5Wr16NqVOnuodpli1bhjlz5riDEiIiIhq4VAcjOTk5OHPmDJYvXw673Y6UlBQUFxe7J7VWV1d79IQ888wzMBgMeOaZZ3D69GmMHDkSc+bMwa9//WvtPgUREREFLNV1RvTQ1NQEq9UKh8PBOSNEREQBQvT8zbVpiIiISFcMRoiIiEhXDEaIiIhIVwxGiIiISFd9XoGVtNfhlFBW1Yj65lZED7MgPTESIUau2UNERIGJwUiAKa6oxYq3K1HraHXfF2u1oGBOEmZNitWxZURERD3DYZoAUlxRi4WbD3gEIgBgd7Ri4eYDKK6o1allREREPcdgJEB0OCWseLsS3orCuO5b8XYlOpz9vmwMERGRBwYjAaKsqrFbj0hXEoBaRyvKqhr91ygiIiINMBgJEPXNvgORnmxHRETUXzAYCRDRwyyabkdERNRfMBgJEOmJkYi1WuArgdeAzqya9MRIfzaLiIio1xiMBIgQowEFc5IAoFtA4vp/wZwk1hshIqKAw2AkgMyaFIt1D0+Dzeo5FGOzWrDu4WmsM0JERAGJRc8CzKxJsZiZZGMFViIiChoMRgJQiNGAzHEj9G4GERGRJjhMQ0RERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YtGzINbhlFiplYiI+j0GI0GquKIWK96uRK2j1X1frNWCgjlJXMOGiIj6FQ7T+EmHU0LpibPYfug0Sk+cRYdT6rP3Kq6oxcLNBzwCEQCwO1qxcPMBFFfU9tl7ExERqcWeET/wZy9Fh1PCircr4S3UkQAYAKx4uxIzk2wcsiEion6BPSN9zN+9FGVVjd3eqysJQK2jFWVVjZq+LxERUU+xZ6QP6dFLUd/sOxDpyXakH9EJyJyoTESBjsFIH1LTS5E5boQm7xk9zKLpdqQP0aE9TlQmomDAYZo+1JNeit5OdE1PjESs1QJf18UGdJ6s0hMjVb0u+Y/o0B4nKhNRsGDPSB9S20uhxVVuiNGAgjlJWLj5AAyAxxCRK0ApmJPEbvw+1JthE9GhvTsmxnCiMhEFjR71jKxduxYJCQmwWCzIyMhAWVmZz22///3vw2AwdLvNnj27x40OFGp6KbS8yp01KRbrHp4Gm9UzGLJZLVj38DR23/eh4opa3PL8bszdsA9LthzC3A37cMvzu4X/fqJDe6+WnuREZSIKGqp7RrZu3Yq8vDysX78eGRkZKCoqQnZ2No4dO4bo6Ohu22/btg3t7e3u/589exbJycn4l3/5l961PACI9lIA0Pwqd9akWMxMsmkysXEgTJDU4jO6Aspr/46ugFIkEBQd2jvVeFFou/4+UXkgfLeISJnqYGT16tVYsGABcnNzAQDr16/Hjh07sGnTJixdurTb9pGRnnMTtmzZgrCwsAERjABXeymuHX6xdRl+KT1xtk8muoYYDYrbK50MBsIESS0+o1aZU6JDe2Miw4S2688TlQfCd4uIxKgKRtrb21FeXo78/Hz3fUajEVlZWSgtLRV6jY0bN+KBBx7AkCFDfG7T1taGtrY29/+bmprUNLPfUeqlUDvRVauUT6WTgRZX+v2dVp9Rq8wp19Ce3dHqNbAxoDOQfSQzAX/cW6W4ndqJyv7qqRgI3y0iEqcqGGloaEBHRwdiYmI87o+JicHRo0cVn19WVoaKigps3LhRdrvCwkKsWLFCTdN0JXIAl+ulUDPRVauUT6WTwdoHp2LljiOaT5DsT93yWtaB0aq+i+jQnmmQUfOJyv7qqWCVYCK6ll9Tezdu3IjJkycjPT1ddrv8/Hw4HA73raamxk8tVK+3ExYB8Ymu51raNUn53Pl5rezJAACe2V6h+QRJLfaVlrSsVqtlfRfRCchaTlT2Z5owqwQT0bVU9YxERUUhJCQEdXV1HvfX1dXBZrPJPrelpQVbtmzBc889p/g+ZrMZZrNZTdN0oVVXc9erYV+Wzb4RK3dok/K5bHsFzra0e9ni6naNLZcV2w2I9wj0x255LavVig6viA6biE5A1mKisr97KlglmIiupapnxGQyITU1FSUlJe77nE4nSkpKkJmZKfvcP//5z2hra8PDDz/cs5b2M0oHcKDzAC5atGzWpFg8dlsirj3WGw3AY7clYvgQs2Ypn3KBiFoiV/pa7yutaNmb4QooAXTr4fI2bCJS3M41tHdvShwyx43wGQiIbueLv3sqWCWYiK6lOpsmLy8P8+fPR1paGtLT01FUVISWlhZ3ds28efMQFxeHwsJCj+dt3LgR9913H0aM0Kbsud7UTlgUmUz60gdV3U7YkgS89EEVLneInahFUz5FRA4x4VxLe6+v9PUoiy/C1Zsh1zY11WpFMqeAzr/1s29Vwt7UZZtwC569p++ySOS+f/7uqdB6vxNR4FMdjOTk5ODMmTNYvnw57HY7UlJSUFxc7J7UWl1dDaPRs8Pl2LFj2Lt3L/7xj39o0+p+QM0BXGlioEg3+fZD3wq9n2jKZ+SQUNmhmFirBctmJ2HRa72fINlfu+VDjAbckxyLP3xQ5XObe5JjPXozejtsUlxRi8e9DMfZm1rx+OYDWN8Hw1Wdwc8XsDddzVCzhZvx7D3fw6xJsX7vqVC730W1X3Hi1dKTONV4EWMiw/BIZgJMg7jiBVEg6FE5+MWLF2Px4sVeH9uzZ0+3+2644QZIkn+74Pua6IH5ZMNFFL37pexcCetgk9DQSuSQUJxruaxJyuc/T4nFhg/lTwZ3T4nFOuO07lfxKjMs+upk19vMnA6nhLc+k5+Y+dZntfiPWTdiV6VdONPEV+ZUh1PC0m2HZd8vf9thTbNIfAc/be7g546JMTAaALlRMqMBSB0zXJM2qdnvovuhcGclNnxY5fEZfr3zCBbcmoj8u5N601wi8gNeNvSQSAaMLdyM18uqFedKdD3Ry/lhSpz7ta99L8Az5VNuu2Wzb8Q7nyufDK7OY/D8BGoDy75YvE+LzByl4SOgc/hoze6vVGWa+JoPsu/rszh/UX5i8LmLl7Hv67PCn0GOSPCzdNth7D/ZKBuIAJ2BSvmpc5q0S3S/i85RKdxZiT98UNXtMzgl4A8fVKFwZ2VPm0pBrLeLkpK2GIz0kMiExbnpo2UDDddcicYLbT636SoryaZJyqfSZFjA8yTctXsfAOqa2lSle6qd3KlEqzRU0WGhlz86KTz5Vi5IKj0hFmSIbqdk3wnl4Of8xcv4+HiD0OtpNYym5bBd+xWnbA8fAGz4sArtV5xC76mH/npS1LJd/e0zFlfU4uZVJR6/05tXlXClax1x1d5eUJqw2CZ4AIwcYhJOCw0xGnDHxBjFsXG5uQvbD50WapfcSVhtuqdrX/V2yEfLNFTRYaHzl3yf0LtOvnVcapdNX541KcbbS/h4VXG+hqtKvxYLMk6fvyS0nZphNLkhNLXDdnKv9WrpSaFenVdLT+LRW8cKt99ftC40p1VRQS3b1d/K/osMXbL6r/8xGOkluZP+R4JXnNHhFuFqmt5+2H/cW6Vq7oLWJ2F1GTC9G/LRMjNHpDaINSxUsXcBAOyOS/jt34/JBkmffC027JA5NgqA2IlF7kDfvR/Ku1ERgzWtkaI0YVZNNo3SiUw0e0zLLDOtaF17R6uTvpbt6m/1hUSHLln91/84TKMBn3UeRM+zklg1TbXDE+1XnNj44ddYvr0CGz/82t1VLTKHIyIsVKjpaoue9XbIR8sufpHho9wZiULv19jSrhgkNV68jKHmENnXiQgLxU3jRgjNiVH6Pogmktw8PkqzYTTXVee1f2fXVWdxRa07m0bOPcmx2FVpV/y+i2aPiW6nRm+GHrSuvaP22OCr7Vq2qz/WFxIdutzXZai0vw0xBSv2jPShhhaxuSCu7eR6WdQOTyhlFyj1xOTOSMR/vfulYtu1KHrmbWjFV6+A1pk5V4ePPK/kY767kp+ZZMOW/dWKvQaRQ8UqBmckRqLk6Bmfj+ekXec+CV/7frVdriZnJtkU9+nrZdUwGDpr1fhiMADTEyJhGmQUqpEiR/Sq846JMULZNNsPfav4ndn1i9uxcscRxbY9mDFGcRs1etsLoWUPn9rfl1zbRTL7RNvVH+sLiQ5dln7dgJsnRPW7IaZgxmCkD/XkxOlraEXND3vPsTqvNRxc2QUAkH93kuwcDtdJWE1hKl8BhNqDklw3/8wkm6qCWeJj6N77BFy9J97GmF1tL5iThGFmsZ6kgzUO2cflTsKu91vxdiWGWUIV92lds3KlXem7LJnMcSOES8v72qeiV51/+li+SjAAxcdd35mt+6sVPyMAHKo5L1R8UIQWQw9a9vCp+X0pzWvKvTlBs3b1z/pCon9rQ78bYgp2DEb6kJbrlYj+YGvPXxLKLnjyronf/c/7HA61hankriBEJ/K6CsQpTS4TbZfIVY2vA05d09UDjhDBY1yjQil+pZOwaxutMm4Az++W3OrSgPzfueK0fKDlsv+kdm0XnQsiUnxQhFYTqHtyoeIrkBI9NojMaxItrijSfj3L/vvaV5njRmDNe8cVn5+RGIn/+OvnA2Jl6f6ykjqDkT4kuhy8yB9e9Ad7sOacUHbB09s+x18PnPZyEu6cw7H2wamqCoLJXUEsuXO8UNsjw0x4YstB2W2W/vVzWELlv7ZvfVaL5OsisOi1g7JXNSJDHc++9QXkIg3XQek/sm+QbZPWtCwiKHLCA5R7BGZNkl8s0yXMpN1hR3QuyMmGFhS9+5XQVa5cJVethh5SxwxXVWhOrrdQ9NggMq9JtLiiyAWU1otHipILOmcm2RChMCl9eFgojAZDvxti6gmRZUj6yzAUg5E+JrpeiRLRH7boKWpnhV32JPzM9grFlXtrHa3Y9/VZxRP6n0pPCrWpsrZJuZv/0hXg0hXFdj2zvULxqkZkqOPaiZjetql1tCr2eGgtIixUcbjKFm4GYEBdk9jJQOkgrvR3Fi3Wdv+06/BJVaPyd1mSUNfUpknFYbnig12vcn9bfER2rpVWQw/lp8QuGspPnYPjUrtsb+H/PDhV6NggOq/phylx2PjRSa+PuYYlRS6gRFYjV1NfyKU3AfO6h6dh1Y8m+xx2BYDCH00Wnu/XF0NM/krP7m/DUAxG/ECLZd5Fe1lOnxOrGXGxvcPnYxKgGIi4lJ44q3hCP3dRPnhw2X9Sm1VhAfn2uwIILYc6IoeaFU8I4YMHwaEQSImKGmZRHK66N2UUpo4eLpwyLndg+nnWBIG/82VYQo1ovex7WG6IOQQzvsveUWoXAMVtXBWH5bZ7YPpo2cnYru/Dki0HvVYm7jrX6rbro32+TldRCid+NUMrK96RryD79JsV+M19k7HoNfmTvnWwSeg9wwW3E+FajfzaAM9oABbcmtijmiVy88lEhtD2PnUH1nuZL9f1RC16bNB6CQt/pWevfXAqVu440q+GoZja6yeiy7zLpZGJpP8+kpkApe+Otl8t7YYLWi/7DpD6hnZtt4Vb3CdQX+mxP7lZLE1YRPQws9Aw2kyBqr0iKZgv+7hSvpbRIP/tCg3pPOSIfJddJ7JrX9JgAB677eqJTGm7hCixoZwdCkskvPRhFa6IVnJV+GqJnsQaLrQJTQq2Dg7FY7cldvvtG7vsKzVLWMhRm3L8kpdS/a7VyNVUPFVKG1+z+yvhoZVZk2Lx0dI78PqCm/C7B1Lw+oKbsPepO9zfKT2WsFCTni13nhD5PT+zvUJ4X/kLe0Y04M+qh0q9LKZBRiy4NVH2ivmfp9jw9ud2xfZEDjHJDj/EWi3ISBiBNTgh+hFlTRplxd7j2vVWKJk+OhIGwwnZ1FcAiBgcKlsAbnhYqPtv4LoK7Pqahu+uAhffMQFb9tcoZgJJkiQ7PBRrtQCS8mTXrgdeue+MyDwIuc/flVyPG9B54nSNs4uscPzSB1XdDqrO705kU0cPd3c3y23386wJQm1XOr1KErDtoFj1YqVuftHCb6L7ffMnJ1FcUae4r5R6keamj8Z/vfuV7Ht1nSshd+zTslqySNr4Hz74WvZxF1evlNxkbS3n+wG+eypq3T0V07Byh9i+Ulq0U+T3LNrz7c9MJ/aM9JIWC7a5Xkc0KlbqZcm/Own/z8dV0v+7LRFFD0xTLGoWERaK+6fFyW5zT3IsjCHa9bOMGKpd97CIY/XNioEIAFxRuAp0PerrKtB1QthVaUfBnCQY4L33xIDOA9y9KaNk3++e5FhNx7RFDzgRg0M1L5Tn67ssdyJzWfF2JdqvOBW3e72sGrZw+atcs+D3+PR5sewdpZ4P0cJvBsF+zA+/alBMB+9wSoo9UqMFJwTbHZcUj31qJvsqEUkbVwqEXdTWIIoe5nlcih5m6jafoqc9FUDnflgm2FOxZvdxxfOElgFEX2Q6+cKekV7QagKQllcQLvl3J+HJuyZ6zQoQ6mKVJLwtMAwwMdYq1J5rry6uZTQAkUPEJthp5dNTYl2QF9rk53mc/26lXZGT596n7pCd0Owa95bz1me1wnMXoodZFHvcRA84uTf7LoQnQdtCeaInsldL5WuWuCYh/yLrehTJtC0xagiO1l1QbJdpUIiqLBhfmTkdTglbP/1G9r22fvoNfv/AVKx5T7FZuNAmfyLu2psh1yO18UOx3oWPjjd4zcbrWphPTUq/EtFiZUPNIbL7Qm0NooPV53Dmgmfv8JkL7ThYfc6jPEBveiqAziwmES9/1L0HEPA8T/z2/ilCryXS8611ppMcBiM9pGUA0VeVCk2DjF4XByuratQsa0V0xWGl8McpAecviv0glX5EkUNChbohtUwxFZnIKzJsovQ6+O51nB2S0EnxbHMrnthySDHFWWTIYEL0UNl2TYgeojqVs7e1M0TrjCREhclOpDQaDELBSNSQUOEsmD3H6roNH/1qxxE8dlsibpsQLTQXBIBiKmpYqBEXZSYNu9gdypPbRTNudh3pPiTk4uqJefFfkoVeS2RBRNGZbhOih+FgzXmfj6upQVS4s1KxeKRrgrjc7+uSYI+NCJH1wo7am4Vea8bYEXjnsO8Lzq51pPyBwUgPaRlA+LtSoZbdeCIrDosuNie6evE/T4mVLex2X/IobPr4lOL73Zs8Cm8KFnlSJjahT2m8WvRv88nJs0InxeVvfyEUME+KC5f9Pn9vVDhW7vDdY2MAsHLHESybnYRFr4mNs8udEER7a8TrjFz0Oq/ENZFyyZ1i80quixwitN2GD09gt5ey/xI6T2T7BScGflJ1Fjlp18nOAZsSH4F9AoswugJ4uf1uCxfb70pZYWoC5tQxwxWDA9FiZSfPtsg+3lmDaDgWvSYfQNwxMUaoeOTIocpLFohOXB9mCcGF1g7fx1GFuWsupxrl94HLh1/5XpYCuFpHitk0/ZzaAEJuTNHflQq1HAe0WQdrtticyGstm32j1/TLrv5yQGyS4fEzF5TnzgwepDjfINZqca+0q0Rp34v/bcQOECIpzh9/1YCSI/Wyr7P7aL1Q8D18iEkxSwZQniN1rqWtc6KujNjv6oyIZojIZRds/bQG1sHy12YRYaG4ebzY39lbINLVAYVlAVycEhSzpo7Wil0JRw41+9zvtSr2u9J+ctlXJRYwr9ujPA/iprEjFH+rQ82DcE7hokepBhHQGUD86eOTQm2va/bdM+z6TZy/JNbj++Np18n2NomW6hcNHRytygGlP7NpGIz0kJoAQmmiV1+kkckRTfFTukpytck10Ssm3PsJaPEd44U/n1Ka5vAhZsVhjCaFH5lLzblLWPWjybLbrLp/Cp69J0n2IFEwJwk3jRuhyd9Q9LugZdXHvx78RujAK6K+uRWzJsVi71O+0yZFUg9X7jiCf56iPMHTVWcE8B3Azk0f7VFPwtt71jpaFa9gV/1oMm4aq/x3HmrW7rA6PEx+4TpAPNMpephZcSKlq3dLbpL1zBvFKu1+e16s5tHLH51UDA6AzkUk5WQkDhd6P7khXtd3QcuaR6LBQUSY/BDZhOhhQseGqfFi+0EEs2kCgOhJ41xLm2LUL7KUfdfu7d4uaS3yfs/e8z3cm6J8MvDswvNsR9d1bkQ/n1JGyruVyinJosZEhmHWpFisf3hat8Ar1mrBehUVCF2fUSloUeryFN1X0xMilevJCB4FRbMQRER9NwlZLuNLdIjzrwo9XG99ViuUIZIQJTa0khA15LvvgudJwRZudn8XRP7O40bKz61xCVXI4BkeFoqoYWJzOIaYQhRfSzQdXKl36+YJYr1Do4YPFtpOZB7EvhNnFXuI5OaKqBWmsD/VyBwbpdjbpFTfpXMYtBLLZt8o+zoFc5IQGyG230UwmyYAiOShL5udJJw7Llo2XqsKfUrvJ5rVIbc2jWudG1f3vNLnE0nnfOOQ2BCMSPbOI5kJAORrt7jaJPc+rr+hGnKT9UT2VekJ5S5wSeqch3OupV12Ds70hEj8o7JOVft9cQrkSotebYksKiiSIaKmmmbmuBG4Y2KMz7VpRJgHiZ3IpsZHoOzkOZ+PF/5osnDVVKXAU4LYBFZ8t90Pp13X6/05Y2wUth04rcl8stKvGxQDqcaWy5pNbr9/6nV467NvFee7jBxqRn2z/JIFN40bIbvyN6Bc3+XqMKhZsaJt+xWn0FydYeYQOFp9X4hEfFc/yV8YjPSC0knDOli+i/XaSa4iRaDUpBIrpa5pkdWx74Ty2jRdA67eFt9qbLmMoeZBsum2w8NCkTomAu8e8T1uf+eN0R4nGF8TSkWv4l2pvb6oKVoEKBe3Ez2h35cyCi9/dFJ2QukdE2NQ+LcjwkMxcj6pOotbrx8pu02UhincIvshPTFSaHG09MRIr8H+H/dWdQuYfTGgc40lEVkTY/CTWxJly5KLnFgMBuXU3vMXL+PQN+eF2uU6mfv6TYjuT6WTsARgXsYY/LfAxFSlWj8uoyIsssHItNER+OLbZsVJ8jMmRCkWj1xwa6LwcgtKmgSH2t6ttGOTl2Et10TsqaOHwzrYJDTs2iHJt8t/eTSdGIz0ktxJY7vgVbzIEu5qU4lFe1B6m9WhdMVybcAlV/VQ9D2Vrr4lSULFaflJfRWnm9DhlBQPFML7QTC1d83u4yh690uhgFJuX4l2n85MsiE9MVKxx03pwDs1PkKwG1zgEKbhUc61H5QW+VMiAfh7hV0xy0LkAkMpMHAZGW5WDDpFFtQTXcBZNNiMCOvsjelNZWnRuPa0YG/NBcF5YF98Kx8I7j56Br97YCqeeP2g18e7Dqfm352ErxtasKuy++TumUnRyL+7czh1nZd1brz1+Mr56wH5mjNdt1M6B/zHrIlCr6VUP+lcl2rJ/sBgRAO+ThpaZsmoSSV2XGrvdTE2rbM6RE7qou+pWHJcsEaKyA9NfD+IHX5FihaJ1KZRszx7iNGguFCj68Dqq/v3tuuj8dAfP1H8fCIHrgbB2jRKZfhdE4KVF/m7Xqimh8hKz6IHehE2a+fYvhYBugjRGPD8xXbZ4M462CS0P129pnLt+VuF2BwwCVDuIYJyYOaUgNf2ya+94ypoVlxR6zUQAYBdlfUorqh1H0clybPOi9N59f8iRc/OCy6gKbed6xwgWvtJBCewBonUMcMVJxl2rdYoR9UqnwqZCiILXWmd1SFyUhd5T9GS4yJEu/iF9oNgaq/IZD2RdDq1k55FFmrMvzsJR1f+AMtm34h5mWOwbPaNOLryB8i/u3PCrNK8BIMBmJ6gPMYsGuDdojBR0lVOXXmRP/l6ES4iWRaiB/rIIfLfU9HsOC2HtMItYr+db85dkp10v0twErlIr6no5GmlQMT1ekLtqpKf87Lhwypcau9QXAtn6bbD2Pl55+J9dc2e35265nY8/l2CguhcHa1EDA7VbLFUf05gZTDSh0S6WF3VGpWIfikaW9o1WQ9C9GQnkuooeuAVeU/RmiUiRPapaJaMSGpvxGD1a7fIEVn5Vi1X1d7n7p2ER28d655XU37qnOJVpyT4XRYN8D5VSK9867Na7PtaeXhMNPVVhKswn1Lbf3XvJNlthBdZ03BIyyh4tN/+Wfcy78DVk71ooUANpiC5JV8XoeGryXNKwK92fCHU+/Pvf/lMdpul2w6j4YJYnRGtnL90WShwG2pWrqvjzwmsDEb6kJaVVUUP4KLlnEXeU+Rkp/YKvbfvKVKzxFUjRYsASZRQICVYtEjN1YhSTQ+taFnkT2RfPTB9tOzKxUBnUC2a2aEVkcJ8BXOScPeUUZ3fYy9pwmoCRdEhLREiKaYjhpgUC+UpZTm5RAhmAolwaBhQivj8G4fQdi0Cq1Q3XhT7GyodIY0GIEYh1VvNOUBp7h0nsAYRLeeMiC5pLZoKqGblSqX5BqJpyaKU3lNpPzx7z/cAQJNZ7mpSe0XSpbfsr1G1dosIuTkHWlFb5E8kW0huX4kusqbl9fcwyyBcaL0iNAdH/PvuK2S5Sm6iqOh+jxwSinMtl4VSTBd+l93i7Xdxb8oobPropNB7KrYpLFRxXlO4ZZBiJVCgs1dRKYNHKctODatgL6aI2vNigfzsKbGy1aUX3NrZKyw30fye5Fjhkv5KQ2ScwBpE1EwyFCFaq0PL9wTETnYiQYsacu8pGvxoESCpXYOot4GUml4kfxL9Lp9racOi1w5226bWy+RpLWqDZI6Nwl+ValkIrunx42nX4ZWP5dOgu9aC6Ukafl2T535QCtxE9/uy2Tdi0WsHFdvu+u34yv6wDjZpFoycv3RZ8fs+MylGaPmGRoFaJIOMgC3cIltt1xbeWRdEqQbHglvHYu9x7XrdRBai/N0DU9F2pcNn9s5/zLoRqb/aJfs+//fpN3jyronK64UJ/ib8OYGVwUgfEu3NUHPyUToI9sV79keiPTa9DZB6MtSmRSAF9C61Umu9LfIHXF3NtWu2kFwtC5EDeNerfV/tyr05QbaglMtd37MhY6xyGrRLb9PwnU4ophLPmhQr9HueNSkW64wGFcG357u6qiW7Jt0rZq34ftgtcqhZ8ft+6bJTKBg5f7FdaKXxX9w8FkXvfgnAd6/pwepzivVDbpkwUrEnxjzIKNSDFxcxWLFHqmBOEnZV2vGuj+yddyvr8fuSLxX3wbmLl7H/ZKNmvwlWYA0iWg9hAMo9FX3xnkq0qgyrhkiPTW+HMPpiEUORIEmP/amkt0X+APGU6hCjAfckxyp2SXe92lcaHlMKbETToJWI9qaJpBKrqc4s+r2Sq5b886zrNctacQ0XaNEDJrq0QUJUmOK+cu0vX2nsrjT3VT+aLFs19fHbx+F3Jcon9BnjonDzhCjF7+gtz++W3bcb9oplhZWeOIt/y75B9v3umBiD35V8JbSisr8wGPEDrYcw+tt7qq0MG0i0HmpzkQuS+vP+lPtevSFYuEkk1bHDKSmuRdJ1iXPR4TFAubewtwGsFuXu1Q7/uci1XaTH5uWPxU54ojVglIj2gGWOjcKa904ovp6rpL9IXZ0n75ooW/bftW6Vrwq5M5Ns+FPpSdneiojvKtG6Xq+nFa8lAC2CxfRc33Cl9xPN9OSckSDjj0mGeryn2sqwgcbfw159tT+1HPLx9b0SzbIQ2U6kUNS1vSxaDY/1lpZd26LDfyJEemxE1okBgNybE2WHQ7r+JpR6+UQCRVfqvOhFgci+cqWxy1EKApV6T1b9aLLH76y3Fa9FdK151Nv36/dFz9auXYuEhARYLBZkZGSgrKxMdvvz589j0aJFiI2NhdlsxvXXX4+dO3f2qMHUv6iZ4Bmo+qKehy99sT+LK2pxy/O7MXfDPizZcghzN+zDLc/vRnGFfM+DWqIphSLb9cXB0l9p0CJp+EpF0Vz6KrCREzE4VDEtfvEd44V+E65ePrlVy/UoIaCGSMHA3hL9O1sUFmwcYgpx98Ro8X79es7I1q1bkZeXh/Xr1yMjIwNFRUXIzs7GsWPHEB0d3W379vZ2zJw5E9HR0fjLX/6CuLg4nDp1ChEREVq0v19ov+Ls1Sqfgaw/Rth9wV/DXlrvT38O+YimFIps11cHS3/0For0pv3q3klYueOI5sN/ckT3Ve7NCSh69yuhzBy534SaXj49Sgj0lpq0f62Wd7jUfgWtMpNmQwXPO301/NwbqoOR1atXY8GCBcjNzQUArF+/Hjt27MCmTZuwdOnSbttv2rQJjY2N+PjjjxEa2nk1kJCQ0LtW9yOFOyu7TYT69c4jHhOhgllfnTT6UyaJS3+r56HE30NoouP/auYS9KeDpRoiJ06j0eDXrDfRfbr4jgm4wTZM6KQv95tQmxavRwmB3lD7+eSIBLAPTB+N//puaMyX84K1Qfpj1qWqYKS9vR3l5eXIz89332c0GpGVlYXS0lKvz3nrrbeQmZmJRYsWYfv27Rg5ciQefPBBPPXUUwgJCfH6nLa2NrS1Xa1a19QktiS3vxXurPQ6298pXS1ME+wBichy4mrLCvfHTBJ/0fIkrOXBUkTXA5zr9V3UHuD648FSLaUTp7+v9NXsU73S4kXoMf/Om558PrmLLK0KAapdTqK/9DSpCkYaGhrQ0dGBmJgYj/tjYmJw9OhRr8/5+uuvsXv3bjz00EPYuXMnjh8/jp/97Ge4fPkyCgoKvD6nsLAQK1asUNM0v2u/4sRLH8rPPH/pwyo8edfEATNk44ua00V/ziTxBy1PwnoMoWl5gOtvB8trifTeiaTh+/NKX80+7Y9p8f2J2s8nWpm4t2nQWpcZ8Jc+z6ZxOp2Ijo7GSy+9hJCQEKSmpuL06dN44YUXfAYj+fn5yMvLc/+/qakJ8fHxfd1UVf708UmhhcP+9PFJLLhNfsZ2ICurahQqxCNy9R3smTmitDoJ63Uy0PIA158Oll1p2Xvn7yt9f+3TQB9qU6Lm86m5yFIqBBiIy0mIUBWMREVFISQkBHV1dR7319XVwWazeX1ObGwsQkNDPYZkbrzxRtjtdrS3t8Nk6r6Witlshtms3dLZfWH/SbEodf/Js0EdjGh59e3vYYX+TIsThp4nAy0PcP3lYOkSDL13/WUib38fapMj+vkAaHKRFez7U9X4gclkQmpqKkpKStz3OZ1OlJSUIDMz0+tzbr75Zhw/fhxO59Xxri+//BKxsbFeA5FAEWYSi+NEtwtUWl59D5TMHFG9TSnUMx0yWCn13gGdJ5YOpYpSA4Q/0+L1IPL5tEzXD+b9qfpMmZeXh/nz5yMtLQ3p6ekoKipCS0uLO7tm3rx5iIuLQ2FhIQBg4cKFWLNmDZYsWYInnngCX331FX7zm9/gX//1X7X9JH52/7Tr8Oahb4W26896m7Wi5dV3sI8x66G/z7sINOy9U6+/DrVpRenzaX2RFaz7U3UwkpOTgzNnzmD58uWw2+1ISUlBcXGxe1JrdXU1jMarHS7x8fH4+9//jl/84heYMmUK4uLisGTJEjz11FPafQodzBgfhTBTiOwyzENMIZgxPsrn43rTYtxby67DYB9j1kuwHrz0wN67nulvQ21ak/t8fXGRFYz70yBJStMw9dfU1ASr1QqHw4Hw8HC9m+NWXFErWwp4fT/uNvM17u06Pant8tNqQp+rXYD3wCbQuyIpsJWeOIu5G/Ypbvf6gpuC7mRBPdPhlHDL87sVL7L2PnVHUF4giJ6/GYz0UnFFLQq2f4G65qt1UWzhZjx7z/f67UnT9ePw1d3c0x+HVoXKBnKdEerfBvqJhXpmIF9kMRjxIy2rhfqj8mggXN31xwqsRMDAPrFQzw3UiyzR83dwp3r4iVbjd/76sgbCuHcwjolScOCkYOoJzt2Sx2Ckn/Bn7QJmrRD1Dk8s1BO8yPKNwYgCfwwX6LWgGbNWiHqOJxYi7TAYkeGvYRM9FzQLxkp+REQUWAb2Cm4yXMMm1wYJrmGT4opazd5LzwXNgrGSHxERBRb2jHjh72GTYFjQjIiIqKcYjHjh72ETtXM4tJzHwnFvIiLSG4MRL/w9bKJmDsdAzVUnIqLgxTkjXugxbCIyh8Of81iIiIj8hT0jXuiV+io3h8Pf81iIiIj8hT0jXriGTYCrwyQufZ366prDcW9KHDLHjXC/h5p5LERERIGEwYgPeqW+djgllJ44i+2HTqP0xFl0ODv7QgKhhDsREVFPcJhGhr9TX+Ump7KEOxERBSv2jPQTSpNTz7W0IdZq6TZs5GJAZ+DCEu5ERBRo2DMiw19ptCKTU1fuOIJls5Ow6DWWcCciouDCnhEf/JlGKzo5dfgQE0u4ExFR0GHPiBf+TqNVMzn13pQ4lnAnIqKgwmDEC3+Xg1c7OZUl3ImIKJhwmMYLf6fRuoqscXIqERENRAxGvPB3Gq2eRdaIiIj0xmDEi9Qxw6F03jcaOrfTil5F1oiIiPTGOSNelJ86B6e32atdOKXO7bScu+HvImtERET9AYMRL/Qsvc7JqURENNBwmMYLll4nIiLyHwYjXjC7hYiIyH8YjHjB7BYiIiL/YTDiA7NbiIiI/IMTWGUwu4WIiKjvMRhRwOwWIiKivsVhGiIiItIVgxEiIiLSVY+CkbVr1yIhIQEWiwUZGRkoKyvzue0rr7wCg8HgcbNYWJ+DiIiIOqkORrZu3Yq8vDwUFBTgwIEDSE5ORnZ2Nurr630+Jzw8HLW1te7bqVOnetVoIiIiCh6qg5HVq1djwYIFyM3NRVJSEtavX4+wsDBs2rTJ53MMBgNsNpv7FhMT06tGExERUfBQFYy0t7ejvLwcWVlZV1/AaERWVhZKS0t9Pu/ChQsYM2YM4uPjce+99+KLL76QfZ+2tjY0NTV53IiIiCg4qQpGGhoa0NHR0a1nIyYmBna73etzbrjhBmzatAnbt2/H5s2b4XQ6MWPGDHzzzTc+36ewsBBWq9V9i4+PV9NMIiIiCiB9nk2TmZmJefPmISUlBbfffju2bduGkSNH4g9/+IPP5+Tn58PhcLhvNTU1fd1MIiIi0omqomdRUVEICQlBXV2dx/11dXWw2WxCrxEaGoqpU6fi+PHjPrcxm80wm81qmkZEREQBSlXPiMlkQmpqKkpKStz3OZ1OlJSUIDMzU+g1Ojo6cPjwYcTGcm0XIiIi6kE5+Ly8PMyfPx9paWlIT09HUVERWlpakJubCwCYN28e4uLiUFhYCAB47rnncNNNN2H8+PE4f/48XnjhBZw6dQo//elPtf0kREREFJBUByM5OTk4c+YMli9fDrvdjpSUFBQXF7sntVZXV8NovNrhcu7cOSxYsAB2ux3Dhw9HamoqPv74YyQlJWn3KYiIiChgGSRJkvRuhJKmpiZYrVY4HA6Eh4fr3RwiIiISIHr+5to0REREpCsGI0RERKQrBiNERESkKwYjREREpCsGI0RERKQrBiNERESkKwYjREREpCsGI0RERKQr1RVYB5oOp4SyqkbUN7ciepgF6YmRCDEa9G4WERFR0GAwIqO4ohYr3q5EraPVfV+s1YKCOUmYNYkL/REREWmBwzQ+FFfUYuHmAx6BCADYHa1YuPkAiitqdWoZERFRcGEw4kWHU8KKtyvhbdEe130r3q5Eh7PfL+tDRETU7zEY8aKsqrFbj0hXEoBaRyvKqhr91ygiIqIgxWDEi/pm34FIT7YjIiIi3xiMeBE9zKLpdkREROQbgxEvUscMh1L2rtHQuR0RERH1DoMRL8pPnYPS3FSn1LkdERER9Q6DES84Z4SIiMh/GIx4wTkjRERE/sNgxIv0xEjEWi3wNW3EgM5KrOmJkf5sFhERUVBiMOJFiNGAgjlJANAtIHH9v2BOEteoISIi0gCDER9mTYrFuoenwWb1HIqxWS1Y9/A0rk1DRESkES6UJ2PWpFjMTLJx1V4iIqI+xGBEQYjRgMxxI/RuBhERUdDiMA0RERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemqR8HI2rVrkZCQAIvFgoyMDJSVlQk9b8uWLTAYDLjvvvt68rZEREQUhFQHI1u3bkVeXh4KCgpw4MABJCcnIzs7G/X19bLPO3nyJP7t3/4Nt956a48bS0RERMFHdTCyevVqLFiwALm5uUhKSsL69esRFhaGTZs2+XxOR0cHHnroIaxYsQJjx47tVYOJiIgouKgKRtrb21FeXo6srKyrL2A0IisrC6WlpT6f99xzzyE6OhqPPvqo0Pu0tbWhqanJ40ZERETBSVUw0tDQgI6ODsTExHjcHxMTA7vd7vU5e/fuxcaNG7Fhwwbh9yksLITVanXf4uPj1TSTiIiIAkifZtM0NzfjkUcewYYNGxAVFSX8vPz8fDgcDvetpqamD1tJREREehqkZuOoqCiEhISgrq7O4/66ujrYbLZu2584cQInT57EnDlz3Pc5nc7ONx40CMeOHcO4ceO6Pc9sNsNsNqtpGhEREQUoVT0jJpMJqampKCkpcd/ndDpRUlKCzMzMbttPnDgRhw8fxqFDh9y3e+65B//0T/+EQ4cOcfiFiIiI1PWMAEBeXh7mz5+PtLQ0pKeno6ioCC0tLcjNzQUAzJs3D3FxcSgsLITFYsGkSZM8nh8REQEA3e4nIiKigUl1MJKTk4MzZ85g+fLlsNvtSElJQXFxsXtSa3V1NYxGFnYlIiIiMQZJkiS9G6GkqakJVqsVDocD4eHhejeHiIiIBIiev9mFQURERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJE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0FpPtiMiIqLeG1DBSHpiJGKtFvhK4DWgM6smPdH3ujlERESkrQEVjIQYDSiYkwQA3QIS1/8L5iSx3ggREZEfDahgBABmTYrFuoenwWb1HIqxWS1M6yUiItKB6qJnwWDWpFjMTLKxAisREVE/MCCDEaBzyCZz3Ai9m0FERDTgDbhhGiIiIupfGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka56FIysXbsWCQkJsFgsyMjIQFlZmc9tv/jiC9x///1ISEiAwWBAUVFRT9tKREREQUh1MLJ161bk5eWhoKAABw4cQHJyMrKzs1FfX+91+4sXL2Ls2LFYtWoVbDZbrxtMREREwUV1MLJ69WosWLAAubm5SEpKwvr16xEWFoZNmzZ53X769Ol44YUX8MADD8BsNve6wURERBRcVAUj7e3tKC8vR1ZW1tUXMBqRlZWF0tJSzRrV1taGpqYmjxsREREFJ1XBSENDAzo6OhATE+Nxf0xMDOx2u2aNKiwshNVqdd/i4+M1e20iIiLqX/plNk1+fj4cDof7VlNTo3eTiIiIqI8MUrNxVFQUQkJCUFdX53F/XV2dppNTzWYz55cQERENEKp6RkwmE1JTU1FSUuK+z+l0oqSkBJmZmZo3joiIiIKfqp4RAMjLy8P8+fORlpaG9PR0FBUVoaWlBbm5uQCAefPmIS4uDoWFhQA6J71WVla6/3369GkcOnQIQ4cOxfjx4zX8KERERBSIVAcjOTk5OHPmDJYvXw673Y6UlBQUFxe7J7VWV1fDaLza4fLtt99i6tSp7v+/+OKLePHFF3H77bdjz549vf8EREREFNAMkiRJejdCSVNTE6xWKxwOB8LDw/VuDhEREQkQPX/3y2waIiIiGjgYjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4Y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sccekyIiIjxm+pI6zc3N0sGDB6WDBw9KAKTVq1dLBw8elE6dOiVJUmdqb0REhLR9+3bp888/l+69916vqb1Tp06VPvnkE2nv3r3ShAkTmNqrYOHChZLVapX27NnjkXZ38eJF9zaPP/64NHr0aGn37t3Sp59+KmVmZkqZmZnux11pd3fddZd06NAhqbi4WBo5ciTTHWUsXbpUev/996Wqqirp888/l5YuXSoZDAbpH//4hyRJ3Of+0jWbRpK43/vKk08+Ke3Zs0eqqqqSPvroIykrK0uKioqS6uvrJUny334PumBEkiTp97//vTR69GjJZDJJ6enp0r59+/RuUkB77733JADdbvPnz5ckqTO9d9myZVJMTIxkNpulO++8Uzp27JjHa5w9e1aaO3euNHToUCk8PFzKzc2Vmpubdfg0gcPbPgcgvfzyy+5tLl26JP3sZz+Thg8fLoWFhUk//OEPpdraWo/XOXnypPSDH/xAGjx4sBQVFSU9+eST0uXLl/38aQLHT37yE2nMmDGSyWSSRo4cKd15553uQESSuM/95dpghPu9b+Tk5EixsbGSyWSS4uLipJycHOn48ePux/213w2SJEm96tMhIiIi6oWgmjNCREREgYfBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHp6v8Duou0AvfZ0jgAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] + "text/plain": "
", + "image/png": "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QkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhhpQMLJq1SpkZWUhLi4OhYWFqK2tlfq7jRs3wmQyYc6cOQN5WCIiIgpDmoORTZs2obS0FOXl5airq0NeXh6mTZuG48ePq/7doUOH8KMf/QhTpkwZ8M4SERFR+NEcjDzzzDNYuHAhSkpKkJOTgzVr1iA+Ph7r16/3+Tfd3d249957sXz5cowZM2ZQO0xEREThRVMw0tXVhd27d6O4uPjyHZjNKC4uRk1Njc+/e+KJJ5CWlob7779f6nE6OzvhcDg8foiIiCg8aQpGTp48ie7ubqSnp3vcnp6eDrvd7vVvdu7ciXXr1mHt2rXSj1NRUQGr1er+yczM1LKbREREFEL8Opumvb0d9913H9auXYvU1FTpvysrK0NbW5v75+jRo37cSyIiIjJStJaNU1NTERUVhZaWFo/bW1paYLPZ+m1/8OBBHDp0CDNnznTf5nQ6ex44Ohr79+/H2LFj+/1dbGwsYmNjtewaERERhShNmRGLxYKCggJUV1e7b3M6naiurkZRUVG/7ceNG4e9e/eivr7e/TNr1iz8y7/8C+rr6zn8QkRERNoyIwBQWlqKBQsWYOLEiZg0aRJWrlyJjo4OlJSUAADmz5+PkSNHoqKiAnFxccjNzfX4+6SkJADodzsRERFFJs3ByNy5c3HixAksXboUdrsd+fn5qKqqche1HjlyBGYzG7sSERGRHJOiKIrROyHicDhgtVrR1taGxMREo3eHiIiIJMiev5nCICIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRgh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GQsWLMBbb71l9G6FraNHj+KRRx7Btm3bEBcXZ/TuRJRvfOMb7n+PHz8ehYWFGD16NP70pz9hyJAhAduPsBqmSU1NRVRUVL9K35aWFthsNoP2Kry5jqvaMbfZbDh+/LjH7y9duoTW1la+LhIeeughvP7663jjjTdw5ZVXum+32Wzo6urCmTNnPLbve+y9vTau35F3FosFV111FQoKClBRUYG8vDz8+te/5jH3k927d+P48eOYMGECoqOjER0djbfeegu/+c1vEB0djfT0dB73AElKSsI111yDAwcOBPT9HlbBiMViQUFBAaqrq923OZ1OVFdXo6ioyMA9C1/Z2dmw2Wwex9zhcOC9995zH/OioiKcOXMGu3fvdm+zfft2OJ1OFBYWBnyfQ4WiKHjooYfwyiuvYPv27cjOzvb4fUFBAWJiYjyO/f79+3HkyBGPY793716PYHDbtm1ITExETk5OYJ5IGHA6nejs7OQx95M777wTe/fuRX19vftn4sSJuPfee93/5nEPjLNnz+LgwYPIyMgI7Pt9QOW3QWzjxo1KbGys8vvf/15pbGxUvv/97ytJSUkelb6kTXt7u/Lhhx8qH374oQJAeeaZZ5QPP/xQOXz4sKIoPVN7k5KSlC1btih79uxRZs+e7XVq74033qi89957ys6dO5Wrr76aU3sFFi1apFitVuXNN9/0mHZ37tw59zYPPPCAMmrUKGX79u3KBx98oBQVFSlFRUXu37um3X39619X6uvrlaqqKuWKK67gdEcVS5YsUd566y2lqalJ2bNnj7JkyRLFZDIp//jHPxRF4TEPlN6zaRSFx91fHn30UeXNN99UmpqalHfeeUcpLi5WUlNTlePHjyuKErjjHnbBiKIoyrPPPquMGjVKsVgsyqRJk5Rdu3YZvUsh7Y033lAA9PtZsGCBoig903sff/xxJT09XYmNjVXuvPNOZf/+/R73cerUKeWee+5Rhg0bpiQmJiolJSVKe3u7Ac8mdHg75gCUF154wb3N+fPnlQcffFBJTk5W4uPjlW9+85tKc3Ozx/0cOnRI+cY3vqEMGTJESU1NVR599FHl4sWLAX42oeO73/2uMnr0aMVisShXXHGFcuedd7oDEUXhMQ+UvsEIj7t/zJ07V8nIyFAsFosycuRIZe7cucqBAwfcvw/UcTcpiqIMKqdDRERENAhhVTNCREREoYfBCBERERmKwQgREREZisEIERERGYrBCBERERmKwQgREREZisEIERERGYrBCBERERmKwQgREREZisEIERERGYrBCBERERmKwQgREREZ6v8BZVeubZ+ZWrsAAAAASUVORK5CYII=" }, "metadata": {}, "output_type": "display_data" @@ -1208,8 +1249,13 @@ }, { "cell_type": "code", - "execution_count": 58, - "metadata": {}, + "execution_count": 29, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:43.702198Z", + "end_time": "2023-04-24T21:12:43.722421Z" + } + }, "outputs": [ { "name": "stdout", @@ -1230,7 +1276,7 @@ " 1 1 1\n", " ... ... ...\n", " 99 8 1\n", - " 99 9 0\n", + " 99 9 1\n", " 100 0 0\n", " 100 1 1\n", " 100 2 1\n", @@ -1238,9 +1284,9 @@ " 100 4 1\n", " 100 5 2\n", " 100 6 1\n", - " 100 7 2\n", + " 100 7 1\n", " 100 8 1\n", - " 100 9 0\n" + " 100 9 1\n" ] } ], @@ -1267,8 +1313,13 @@ }, { "cell_type": "code", - "execution_count": 59, - "metadata": {}, + "execution_count": 30, + "metadata": { + "ExecuteTime": { + "start_time": "2023-04-24T21:12:43.722421Z", + "end_time": "2023-04-24T21:12:43.778860Z" + } + }, "outputs": [ { "name": "stdout", @@ -1288,18 +1339,18 @@ " 10 0.00\n", " 11 0.00\n", " ... ...\n", - " 89 0.18\n", - " 90 0.18\n", - " 91 0.18\n", - " 92 0.18\n", - " 93 0.18\n", - " 94 0.18\n", - " 95 0.18\n", - " 96 0.18\n", - " 97 0.18\n", - " 98 0.18\n", - " 99 0.18\n", - " 100 0.18\n" + " 89 0.32\n", + " 90 0.32\n", + " 91 0.32\n", + " 92 0.32\n", + " 93 0.32\n", + " 94 0.32\n", + " 95 0.32\n", + " 96 0.32\n", + " 97 0.32\n", + " 98 0.32\n", + " 99 0.32\n", + " 100 0.32\n" ] } ], @@ -1323,47 +1374,13 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "`virtual environment`: http://docs.python-guide.org/en/latest/dev/virtualenvs/\n", + "### References\n", + "----------\n", "\n", "[Comer2014] Comer, Kenneth W. “Who Goes First? An Examination of the Impact of Activation on Outcome Behavior in AgentBased Models.” George Mason University, 2014. http://mars.gmu.edu/bitstream/handle/1920/9070/Comer_gmu_0883E_10539.pdf\n", "\n", "[Dragulescu2002] Drăgulescu, Adrian A., and Victor M. Yakovenko. “Statistical Mechanics of Money, Income, and Wealth: A Short Survey.” arXiv Preprint Cond-mat/0211175, 2002. http://arxiv.org/abs/cond-mat/0211175." ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { diff --git a/docs/tutorials/intro_tutorial.rst b/docs/tutorials/intro_tutorial.rst index 912305f42f2..3b91383dc7d 100644 --- a/docs/tutorials/intro_tutorial.rst +++ b/docs/tutorials/intro_tutorial.rst @@ -6,88 +6,117 @@ Tutorial Description `Mesa `__ is a Python framework for `agent-based -modeling `__. This tutorial will assist you in getting started. -Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked -through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone -find any errors, bugs, have a suggest or just are looking for clarification, `please let us +modeling `__. This +tutorial will assist you in getting started. Working through the +tutorial will help you discover the core features of Mesa. Through the +tutorial, you are walked through creating a starter-level model. +Functionality is added progressively as the process unfolds. Should +anyone find any errors, bugs, have a suggestion, or just are looking for +clarification, `let us know `__! -The premise of this tutorial is to create a a starter-level model representing agents exchanging -money. This exchange of money affects wealth. Next, *space* is added to allow agents to move -based on the change in wealth as time progresses. +The premise of this tutorial is to create a starter-level model +representing agents exchanging money. This exchange of money affects +wealth. Next, *space* is added to allow agents to move based on the +change in wealth as time progresses. -Two of Mesa's analytic tools: the *data collector* and *batch runner* will be used to examine this movement. -After that an *interactive visualization* is added which allows model viewing as it runs. - -Finally, the creation of a custom visualization module in JavaScript is explored. +Two of Mesa’s analytic tools: the *data collector* and *batch runner* +will be used to examine this movement. After that an *interactive +visualization* is added which allows model viewing as it runs. +Finally, the creation of a custom visualization module in JavaScript is +explored. Model Description ------------------------- +----------------- + +This is a starter-level simulated agent-based economy. In an agent-based +economy, the behavior of an individual economic agent, such as a +consumer or producer, is studied in a market environment. This model is +drawn from the field econophysics, specifically a paper prepared by +Drăgulescu et al. for additional information on the modeling assumptions +used in this model. [Drăgulescu, 2002]. -The tutorial model is a very simple simulated agent-based economy, drawn -from econophysics and presenting a statistical mechanics approach to -wealth distribution [Dragulescu2002]. The rules of our tutorial model: +The assumption that govern this model are: 1. There are some number of agents. 2. All agents begin with 1 unit of money. -3. At every step of the model, an agent gives 1 unit of money (if they - have it) to some other agent. - -Despite its simplicity, this model yields results that are often -unexpected to those not familiar with it. For our purposes, it also -easily demonstrates Mesa's core features. +3. At every step of the model, an agent with money gives 1 unit of + money. -Let's get started. +Even as a starter-level model the yielded results are both interesting +and unexpected to individuals unfamiliar with it the specific topic. As +such, this model is a good starting point to examine Mesa’s core +features. Installation ~~~~~~~~~~~~ -To start, install Mesa. We recommend doing this in a `virtual -environment `__, -but make sure your environment is set up with Python 3. Mesa requires -Python3 and does not work in Python 2 environments. +Create and activate a `virtual +environment `__. +*Python version 3.8 or higher is required*. -To install Mesa, simply: +Install Mesa: .. code:: bash - pip install mesa + pip install mesa -When you do that, it will install Mesa itself, as well as any -dependencies that aren't in your setup yet. Additional dependencies -required by this tutorial can be found in the -**examples/boltzmann_wealth_model/requirements.txt** file, which can be -installed directly form the github repository by running: +Install Jupyter Notebook (optional): .. code:: bash - pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt + pip install jupyter + +Install matplotlib: + +.. code:: bash + + pip install matplotlib + +Building the Sample Model +------------------------- + +After Mesa is installed a model can be built. A jupyter notebook is +recommended for this tutorial, this allows for small segments of codes +to be examined one at a time. As an option this can be created using +python script files. -| This will install the dependencies listed in the requirements.txt file - which are: -| - jupyter (Ipython interactive notebook) -| - matplotlib (Python's visualization library) -| - mesa (this ABM library – if not installed) -| - numpy (Python's numerical python library) +**Good Practice:** Place a model in its own folder/directory. This is +not specifically required for the starter_model, but as other models +become more complicated and expand multiple python scripts, +documentation, discussions and notebooks may be added. -Building a sample model ------------------------ +Create New Folder/Directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Once Mesa is installed, you can start building our model. You can write -models in two different ways: +- Using operating system commands create a new folder/directory named + ‘starter_model’. -1. Write the code in its own file with your favorite text editor, or -2. Write the model interactively in `Jupyter - Notebook `__ cells. +- Change to the new folder/directory. -Either way, it's good practice to put your model in its own folder – -especially if the project will end up consisting of multiple files (for -example, Python files for the model and the visualization, a Notebook -for analysis, and a Readme with some documentation and discussion). +Creating Model With Jupyter Notebook +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Begin by creating a folder, and either launch a Notebook or create a new -Python source file. We will use the name ``money_model.py`` here. +Write the model interactively in `Jupyter +Notebook `__ cells. + +- Start Jupyter Notebook: + +.. code:: bash + + jupyter notebook + +- Create a new Notebook named ``money_model.ipynb`` (or whatever you + want to call it). + +Creating Model With Script File (IDE, Text Editor, Colab, etc.) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Create a new file called ``money_model.py`` (or whatever you want to + call it). + +*Code will be added as the tutorial progresses.* Setting up the model ~~~~~~~~~~~~~~~~~~~~ @@ -98,7 +127,7 @@ model-level attributes, manages the agents, and generally handles the global level of our model. Each instantiation of the model class will be a specific model run. Each model will contain multiple agents, all of which are instantiations of the agent class. Both the model and agent -classes are child classes of Mesa's generic ``Model`` and ``Agent`` +classes are child classes of Mesa’s generic ``Model`` and ``Agent`` classes. This is seen in the code with ``class MoneyModel(mesa.Model)`` or ``class MoneyAgent(mesa.Agent)``. If you want you can specifically the class being imported by looking at the @@ -158,13 +187,13 @@ model uses, and see whether it changes the model behavior. This may not seem important, but scheduling patterns can have an impact on your results [Comer2014]. -For now, let's use one of the simplest ones: ``RandomActivation``\ \*, +For now, let’s use one of the simplest ones: ``RandomActivation``\ \*, which activates all the agents once per step, in random order. Every agent is expected to have a ``step`` method. The step method is the action the agent takes when it is activated by the model schedule. We add an agent to the schedule using the ``add`` method; when we call the -schedule's ``step`` method, the model shuffles the order of the agents, -then activates and executes each agent's ``step`` method. +schedule’s ``step`` method, the model shuffles the order of the agents, +then activates and executes each agent’s ``step`` method. \*Unlike ``mesa.model`` or ``mesa.agent``, ``mesa.time`` has multiple classes (e.g. ``RandomActivation``, ``StagedActivation`` etc). To ensure @@ -209,8 +238,8 @@ this: """Advance the model by one step.""" self.schedule.step() -At this point, we have a model which runs – it just doesn't do anything. -You can see for yourself with a few easy lines. If you've been working +At this point, we have a model which runs – it just doesn’t do anything. +You can see for yourself with a few easy lines. If you’ve been working in an interactive session, you can create a model object directly. Otherwise, you need to open an interactive session in the same directory as your source code file, and import the classes. For example, if your @@ -230,16 +259,16 @@ Then create the model object, and run it for one step: .. parsed-literal:: - Hi, I am agent 6. - Hi, I am agent 2. - Hi, I am agent 1. + Hi, I am agent 5. Hi, I am agent 0. + Hi, I am agent 1. + Hi, I am agent 7. + Hi, I am agent 8. + Hi, I am agent 2. Hi, I am agent 4. - Hi, I am agent 5. - Hi, I am agent 3. Hi, I am agent 9. - Hi, I am agent 8. - Hi, I am agent 7. + Hi, I am agent 3. + Hi, I am agent 6. Exercise @@ -256,12 +285,12 @@ Now we just need to have the agents do what we intend for them to do: check their wealth, and if they have the money, give one unit of it away to another random agent. To allow the agent to choose another agent at random, we use the ``model.random`` random-number generator. This works -just like Python's ``random`` module, but with a fixed seed set when the +just like Python’s ``random`` module, but with a fixed seed set when the model is instantiated, that can be used to replicate a specific model run later. To pick an agent at random, we need a list of all agents. Notice that -there isn't such a list explicitly in the model. The scheduler, however, +there isn’t such a list explicitly in the model. The scheduler, however, does have an internal list of all the agents it is scheduled to activate. @@ -286,21 +315,21 @@ With that in mind, we rewrite the agent ``step`` method, like this: Running your first model ~~~~~~~~~~~~~~~~~~~~~~~~ -With that last piece in hand, it's time for the first rudimentary run of +With that last piece in hand, it’s time for the first rudimentary run of the model. -If you've written the code in its own file (``money_model.py`` or a +If you’ve written the code in its own file (``money_model.py`` or a different name), launch an interpreter in the same directory as the file (either the plain Python command-line interpreter, or the IPython interpreter), or launch a Jupyter Notebook there. Then import the classes you created. (If you wrote the code in a Notebook, obviously -this step isn't necessary). +this step isn’t necessary). .. code:: python from money_model import * -Now let's create a model with 10 agents, and run it for 10 steps. +Now let’s create a model with 10 agents, and run it for 10 steps. .. code:: ipython3 @@ -309,11 +338,11 @@ Now let's create a model with 10 agents, and run it for 10 steps. model.step() Next, we need to get some data out of the model. Specifically, we want -to see the distribution of the agent's wealth. We can get the wealth +to see the distribution of the agent’s wealth. We can get the wealth values with list comprehension, and then use matplotlib (or another graphics library) to visualize the data in a histogram. -If you are running from a text editor or IDE, you'll also need to add +If you are running from a text editor or IDE, you’ll also need to add this line, to make the graph appear. .. code:: python @@ -336,17 +365,17 @@ this line, to make the graph appear. .. parsed-literal:: - (array([2., 0., 0., 0., 0., 6., 0., 0., 0., 2.]), - array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 1.2, 1.4, 1.6, 1.8, 2. ]), + (array([6., 0., 1., 0., 0., 1., 0., 1., 0., 1.]), + array([0. , 0.4, 0.8, 1.2, 1.6, 2. , 2.4, 2.8, 3.2, 3.6, 4. ]), ) -.. image:: intro_tutorial_files/output_19_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_19_1.png -You'll should see something like the distribution above. Yours will +You’ll should see something like the distribution above. Yours will almost certainly look at least slightly different, since each run of the model is random, after all. @@ -375,21 +404,21 @@ can do this with a nested for loop: .. parsed-literal:: - (array([433., 304., 150., 71., 29., 13.]), - array([0, 1, 2, 3, 4, 5, 6]), - ) + (array([431., 303., 153., 76., 26., 9., 1., 1.]), + array([0., 1., 2., 3., 4., 5., 6., 7., 8.]), + ) -.. image:: intro_tutorial_files/output_22_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_22_1.png This runs 100 instantiations of the model, and runs each for 10 steps. (Notice that we set the histogram bins to be integers, since agents can only have whole numbers of wealth). This distribution looks a lot smoother. By running the model 100 times, we smooth out some of the -‘noise'of randomness, and get to the model's overall expected behavior. +‘noise’ of randomness, and get to the model’s overall expected behavior. This outcome might be surprising. Despite the fact that all agents, on average, give and receive one unit of money every step, the model @@ -411,9 +440,9 @@ those on the left edge, and the top to the bottom. This prevents some cells having fewer neighbors than others, or agents being able to go off the edge of the environment. -Let's add a simple spatial element to our model by putting our agents on +Let’s add a simple spatial element to our model by putting our agents on a grid and make them walk around at random. Instead of giving their unit -of money to any random agent, they'll give it to an agent on the same +of money to any random agent, they’ll give it to an agent on the same cell. Mesa has two main types of grids: ``SingleGrid`` and ``MultiGrid``\ \*. @@ -428,9 +457,9 @@ Similar to ``mesa.time`` context is retained with `mesa.space `__ We instantiate a grid with width and height parameters, and a boolean as -to whether the grid is toroidal. Let's make width and height model +to whether the grid is toroidal. Let’s make width and height model parameters, in addition to the number of agents, and have the grid -always be toroidal. We can place agents on a grid with the grid's +always be toroidal. We can place agents on a grid with the grid’s ``place_agent`` method, which takes an agent and an (x, y) tuple of the coordinates to place the agent. @@ -454,16 +483,16 @@ coordinates to place the agent. y = self.random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) -Under the hood, each agent's position is stored in two ways: the agent +Under the hood, each agent’s position is stored in two ways: the agent is contained in the grid in the cell it is currently in, and the agent has a ``pos`` variable with an (x, y) coordinate tuple. The ``place_agent`` method adds the coordinate to the agent automatically. -Now we need to add to the agents'behaviors, letting them move around +Now we need to add to the agents’ behaviors, letting them move around and only give money to other agents in the same cell. -First let's handle movement, and have the agents move to a neighboring -cell. The grid object provides a ``move_agent`` method, which like you'd +First let’s handle movement, and have the agents move to a neighboring +cell. The grid object provides a ``move_agent`` method, which like you’d imagine, moves an agent to a given cell. That still leaves us to get the possible neighboring cells to move to. There are a couple ways to do this. One is to use the current coordinates, and loop over all @@ -477,7 +506,7 @@ coordinates +/- 1 away from it. For example: for dy in [-1, 0, 1]: neighbors.append((x+dx, y+dy)) -But there's an even simpler way, using the grid's built-in +But there’s an even simpler way, using the grid’s built-in ``get_neighborhood`` method, which returns all the neighbors of a given cell. This method can get two types of cell neighborhoods: `Moore `__ (includes @@ -486,7 +515,7 @@ Neumann `__\ (only up/down/left/right). It also needs an argument as to whether to include the center cell itself as one of the neighbors. -With that in mind, the agent's ``move`` method looks like this: +With that in mind, the agent’s ``move`` method looks like this: .. code:: python @@ -502,7 +531,7 @@ With that in mind, the agent's ``move`` method looks like this: Next, we need to get all the other agents present in a cell, and give one of them some money. We can get the contents of one or more cells -using the grid's ``get_cell_list_contents`` method, or by accessing a +using the grid’s ``get_cell_list_contents`` method, or by accessing a cell directly. The method accepts a list of cell coordinate tuples, or a single tuple if we only care about one cell. @@ -517,7 +546,7 @@ single tuple if we only care about one cell. other.wealth += 1 self.wealth -= 1 -And with those two methods, the agent's ``step`` method becomes: +And with those two methods, the agent’s ``step`` method becomes: .. code:: python @@ -578,7 +607,7 @@ Now, putting that all together should look like this: def step(self): self.schedule.step() -Let's create a model with 50 agents on a 10x10 grid, and run it for 20 +Let’s create a model with 50 agents on a 10x10 grid, and run it for 20 steps. .. code:: ipython3 @@ -587,11 +616,11 @@ steps. for i in range(20): model.step() -Now let's use matplotlib and numpy to visualize the number of agents +Now let’s use matplotlib and numpy to visualize the number of agents residing in each cell. To do that, we create a numpy array of the same -size as the grid, filled with zeros. Then we use the grid object's +size as the grid, filled with zeros. Then we use the grid object’s ``coord_iter()`` feature, which lets us loop over every cell in the -grid, giving us each cell's coordinates and contents in turn. +grid, giving us each cell’s coordinates and contents in turn. .. code:: ipython3 @@ -613,21 +642,21 @@ grid, giving us each cell's coordinates and contents in turn. .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_32_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_32_1.png Collecting Data ~~~~~~~~~~~~~~~ -So far, at the end of every model run, we've had to go and write our own -code to get the data out of the model. This has two problems: it isn't +So far, at the end of every model run, we’ve had to go and write our own +code to get the data out of the model. This has two problems: it isn’t very efficient, and it only gives us end results. If we wanted to know -the wealth of each agent at each step, we'd have to add that to the loop +the wealth of each agent at each step, we’d have to add that to the loop of executing steps, and figure out some way to store the data. Since one of the main goals of agent-based modeling is generating data @@ -641,19 +670,19 @@ collector along with a function for collecting them. Model-level collection functions take a model object as an input, while agent-level collection functions take an agent object as an input. Both then return a value computed from the model or each agent at their current state. -When the data collector's ``collect`` method is called, with a model +When the data collector’s ``collect`` method is called, with a model object as its argument, it applies each model-level collection function to the model, and stores the results in a dictionary, associating the current value with the current step of the model. Similarly, the method applies each agent-level collection function to each agent currently in the schedule, associating the resulting value with the step of the -model, and the agent's ``unique_id``. +model, and the agent’s ``unique_id``. -Let's add a DataCollector to the model with -`mesa.DataCollector `__, +Let’s add a DataCollector to the model with +```mesa.DataCollector`` `__, and collect two variables. At the agent level, we want to collect every -agent's wealth at every step. At the model level, let's measure the -model's `Gini +agent’s wealth at every step. At the model level, let’s measure the +model’s `Gini Coefficient `__, a measure of wealth inequality. @@ -683,10 +712,15 @@ measure of wealth inequality. def give_money(self): cellmates = self.model.grid.get_cell_list_contents([self.pos]) + cellmates.pop( + cellmates.index(self) + ) # Ensure agent is not giving money to itself if len(cellmates) > 1: other = self.random.choice(cellmates) other.wealth += 1 self.wealth -= 1 + if other == self: + print("I JUST GAVE MONEY TO MYSELF HEHEHE!") def step(self): self.move() @@ -720,7 +754,7 @@ measure of wealth inequality. self.schedule.step() At every step of the model, the datacollector will collect and store the -model-level current Gini coefficient, as well as each agent's wealth, +model-level current Gini coefficient, as well as each agent’s wealth, associating each with the current step. We run the model just as we did above. Now is when an interactive @@ -753,12 +787,12 @@ To get the series of Gini coefficients as a pandas DataFrame: .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_38_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_38_1.png Similarly, we can get the agent-wealth data: @@ -828,9 +862,9 @@ Similarly, we can get the agent-wealth data: -You'll see that the DataFrame's index is pairings of model step and +You’ll see that the DataFrame’s index is pairings of model step and agent ID. You can analyze it the way you would any other DataFrame. For -example, to get a histogram of agent wealth at the model's end: +example, to get a histogram of agent wealth at the model’s end: .. code:: ipython3 @@ -842,12 +876,12 @@ example, to get a histogram of agent wealth at the model's end: .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_42_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_42_1.png Or to plot the wealth of a given agent (in this example, agent 14): @@ -862,12 +896,12 @@ Or to plot the wealth of a given agent (in this example, agent 14): .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_44_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_44_1.png You can also use pandas to export the data to a CSV (comma separated @@ -889,11 +923,12 @@ directory. After you run the code below you will see two files appear Batch Run ~~~~~~~~~ -Like we mentioned above, you usually won't run a model only once, but +Like we mentioned above, you usually won’t run a model only once, but multiple times, with fixed parameters to find the overall distributions the model generates, and with varying parameters to analyze how they -drive the model's outputs and behaviors. Instead of needing to write -nested for-loops for each model, Mesa provides a `batch_run `__ +drive the model’s outputs and behaviors. Instead of needing to write +nested for-loops for each model, Mesa provides a +```batch_run`` `__ function which automates it for you. The batch runner also requires an additional variable ``self.running`` @@ -977,7 +1012,7 @@ of the model with each number of agents, and to run each for 100 steps. We want to keep track of 1. the Gini coefficient value and -2. the individual agent's wealth development. +2. the individual agent’s wealth development. Since for the latter changes at each time step might be interesting, we set ``data_collection_period = 1``. @@ -991,9 +1026,9 @@ iteration). **Note for Windows OS users:** If you are running this tutorial in Jupyter, make sure that you set ``number_processes = 1`` (single process). If ``number_processes`` is greater than 1, it is less -straightforward to set up. You can read `Mesa's collection of useful +straightforward to set up. You can read `Mesa’s collection of useful snippets `__, -in ‘Using multi-process ``batch_run`` on Windows'section for how to do +in ‘Using multi-process ``batch_run`` on Windows’ section for how to do it. .. code:: ipython3 @@ -1013,7 +1048,7 @@ it. .. parsed-literal:: - 245it [00:34, 7.02it/s] + 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 245/245 [00:48<00:00, 5.10it/s] To further analyze the return of the ``batch_run`` function, we convert @@ -1055,15 +1090,15 @@ calling the batch run. .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_57_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_57_1.png -Second, we want to display the agent's wealth at each time step of one +Second, we want to display the agent’s wealth at each time step of one specific episode. To do this, we again filter our large data frame, this time with a fixed number of agents and only for a specific iteration of that population. To print the results, we convert the filtered data @@ -1102,21 +1137,21 @@ can use the ``to_html()`` function which takes the same arguments as 0 7 1 0 8 1 0 9 1 - 1 0 2 + 1 0 1 1 1 1 - ... ... ... - 99 8 4 - 99 9 1 + ... ... ... + 99 8 1 + 99 9 0 100 0 0 - 100 1 0 + 100 1 1 100 2 1 - 100 3 0 + 100 3 1 100 4 1 - 100 5 1 - 100 6 0 + 100 5 2 + 100 6 1 100 7 2 - 100 8 4 - 100 9 1 + 100 8 1 + 100 9 0 Lastly, we want to take a look at the development of the Gini @@ -1136,30 +1171,30 @@ episode. Step Gini 0 0.00 - 1 0.18 - 2 0.18 - 3 0.18 - 4 0.18 - 5 0.32 - 6 0.32 - 7 0.32 - 8 0.42 - 9 0.42 - 10 0.42 - 11 0.42 - ... ... - 89 0.66 - 90 0.66 - 91 0.66 - 92 0.66 - 93 0.56 - 94 0.56 - 95 0.56 - 96 0.56 - 97 0.56 - 98 0.56 - 99 0.56 - 100 0.56 + 1 0.00 + 2 0.00 + 3 0.00 + 4 0.00 + 5 0.00 + 6 0.00 + 7 0.00 + 8 0.00 + 9 0.00 + 10 0.00 + 11 0.00 + ... ... + 89 0.18 + 90 0.18 + 91 0.18 + 92 0.18 + 93 0.18 + 94 0.18 + 95 0.18 + 96 0.18 + 97 0.18 + 98 0.18 + 99 0.18 + 100 0.18 Happy Modeling! @@ -1181,3 +1216,8 @@ http://mars.gmu.edu/bitstream/handle/1920/9070/Comer_gmu_0883E_10539.pdf “Statistical Mechanics of Money, Income, and Wealth: A Short Survey.” arXiv Preprint Cond-mat/0211175, 2002. http://arxiv.org/abs/cond-mat/0211175. + + + + + From 2adb81e4acd96878b03d92ced1cf59e0453c1907 Mon Sep 17 00:00:00 2001 From: Jackie Kazil Date: Tue, 25 Apr 2023 11:54:30 -0400 Subject: [PATCH 085/214] Revert "Rewrite intro tutorial text and generate rst for advanced tutorial (#1650)" (#1652) This reverts commit 3b5749ae6078aa32745628ff3004eb3b4b59ad50. --- docs/tutorials/adv_tutorial.rst | 158 ++++----- docs/tutorials/intro_tutorial.ipynb | 533 ++++++++++++++-------------- docs/tutorials/intro_tutorial.rst | 366 +++++++++---------- 3 files changed, 500 insertions(+), 557 deletions(-) diff --git a/docs/tutorials/adv_tutorial.rst b/docs/tutorials/adv_tutorial.rst index 031c2ee70ba..5f23a0e864b 100644 --- a/docs/tutorials/adv_tutorial.rst +++ b/docs/tutorials/adv_tutorial.rst @@ -17,10 +17,9 @@ create a new visualization element. **Note for Jupyter users: Due to conflicts with the tornado server Mesa uses and Jupyter, the interactive browser of your model will load but -likely not work. This will require you to use run the code from .py +likely not work. This will require you to run the code from .py files. The Mesa development team is working to develop a** `Jupyter -compatible -interface `__.*\* +compatible interface `_. First, a quick explanation of how Mesa’s interactive visualization works. Visualization is done in a browser window, using JavaScript to @@ -36,6 +35,7 @@ server and turns a model state into JSON data; and a JavaScript side, which takes that JSON data and draws it in the browser window. Mesa comes with a few modules built in, and let you add your own as well. + Grid Visualization ^^^^^^^^^^^^^^^^^^ @@ -328,9 +328,9 @@ the class itself: .. code:: javascript - const HistogramModule = function(bins, canvas_width, canvas_height) { - // The actual code will go here. - }; + const HistogramModule = function(bins, canvas_width, canvas_height) { + // The actual code will go here. + }; Note that our object is instantiated with three arguments: the number of integer bins, and the width and height (in pixels) the chart will take @@ -339,26 +339,27 @@ up in the visualization window. When the visualization object is instantiated, the first thing it needs to do is prepare to draw on the current page. To do so, it adds a `canvas `__ -tag to the page. It also gets the canvas’ context, which is required for -doing anything with it. +tag to the page. It also gets the canvas' context, which is required for doing +anything with it. .. code:: javascript - const HistogramModule = function(bins, canvas_width, canvas_height) { - // Create the canvas object: - const canvas = document.createElement("canvas"); - Object.assign(canvas, { - width: canvas_width, - height: canvas_height, - style: "border:1px dotted", - }); - // Append it to #elements: - const elements = document.getElementById("elements"); - elements.appendChild(canvas); - - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - }; + const HistogramModule = function(bins, canvas_width, canvas_height) { + // Create the canvas object: + const canvas = document.createElement("canvas"); + Object.assign(canvas, { + width: canvas_width, + height: canvas_height, + style: "border:1px dotted", + }); + // Append it to #elements: + const elements = document.getElementById("elements"); + elements.appendChild(canvas); + + // Create the context and the drawing controller: + const context = canvas.getContext("2d"); + }; + Look at the Charts.js `bar chart documentation `__. @@ -372,49 +373,49 @@ created, we can create the chart object. .. code:: javascript - const HistogramModule = function(bins, canvas_width, canvas_height) { - // Create the canvas object: - const canvas = document.createElement("canvas"); - Object.assign(canvas, { - width: canvas_width, - height: canvas_height, - style: "border:1px dotted", - }); - // Append it to #elements: - const elements = document.getElementById("elements"); - elements.appendChild(canvas); - - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - - // Prep the chart properties and series: - const datasets = [{ - label: "Data", - fillColor: "rgba(151,187,205,0.5)", - strokeColor: "rgba(151,187,205,0.8)", - highlightFill: "rgba(151,187,205,0.75)", - highlightStroke: "rgba(151,187,205,1)", - data: [] - }]; - - // Add a zero value for each bin - for (var i in bins) - datasets[0].data.push(0); - - const data = { - labels: bins, - datasets: datasets - }; - - const options = { - scaleBeginsAtZero: true - }; - - // Create the chart object - const chart = new Chart(context, {type: 'bar', data: data, options: options}); - - // Now what? - }; + const HistogramModule = function(bins, canvas_width, canvas_height) { + // Create the canvas object: + const canvas = document.createElement("canvas"); + Object.assign(canvas, { + width: canvas_width, + height: canvas_height, + style: "border:1px dotted", + }); + // Append it to #elements: + const elements = document.getElementById("elements"); + elements.appendChild(canvas); + + // Create the context and the drawing controller: + const context = canvas.getContext("2d"); + + // Prep the chart properties and series: + const datasets = [{ + label: "Data", + fillColor: "rgba(151,187,205,0.5)", + strokeColor: "rgba(151,187,205,0.8)", + highlightFill: "rgba(151,187,205,0.75)", + highlightStroke: "rgba(151,187,205,1)", + data: [] + }]; + + // Add a zero value for each bin + for (var i in bins) + datasets[0].data.push(0); + + const data = { + labels: bins, + datasets: datasets + }; + + const options = { + scaleBeginsAtZero: true + }; + + // Create the chart object + const chart = new Chart(context, {type: 'bar', data: data, options: options}); + + // Now what? + }; There are two methods every client-side visualization class must implement to be able to work: ``render(data)`` to render the incoming @@ -432,19 +433,18 @@ With that in mind, we can add these two methods to the class: .. code:: javascript - const HistogramModule = function(bins, canvas_width, canvas_height) { - // ...Everything from above... - this.render = function(data) { - datasets[0].data = data; - chart.update(); - }; - - this.reset = function() { - chart.destroy(); - chart = new Chart(context, {type: 'bar', data: data, options: options}); - }; - }; - + const HistogramModule = function(bins, canvas_width, canvas_height) { + // ...Everything from above... + this.render = function(data) { + datasets[0].data = data; + chart.update(); + }; + + this.reset = function() { + chart.destroy(); + chart = new Chart(context, {type: 'bar', data: data, options: options}); + }; + }; Note the ``this``. before the method names. This makes them public and ensures that they are accessible outside of the object itself. All the other variables inside the class are only accessible inside the object diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index d0ce846f550..4c1e8c367a5 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -13,98 +13,74 @@ "source": [ "## Tutorial Description\n", "\n", - "[Mesa](https://github.com/projectmesa/mesa) is a Python framework for [agent-based modeling](https://en.wikipedia.org/wiki/Agent-based_model). This tutorial will assist you in getting started. Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone find any errors, bugs, have a suggestion, or just are looking for clarification, [let us know](https://github.com/projectmesa/mesa/issues)!\n", + "[Mesa](https://github.com/projectmesa/mesa) is a Python framework for [agent-based modeling](https://en.wikipedia.org/wiki/Agent-based_model). Getting started with Mesa is easy. In this tutorial, we will walk through creating a simple model and progressively add functionality which will illustrate Mesa's core features.\n", "\n", - "The premise of this tutorial is to create a starter-level model representing agents exchanging money. This exchange of money affects wealth. Next, *space* is added to allow agents to move based on the change in wealth as time progresses.\n", + "**Note:** This tutorial is a work-in-progress. If you find any errors or bugs, or just find something unclear or confusing, [let us know](https://github.com/projectmesa/mesa/issues)!\n", "\n", - "Two of Mesa's analytic tools: the *data collector* and *batch runner* will be used to examine this movement. After that an *interactive visualization* is added which allows model viewing as it runs.\n", + "The base for this tutorial is a very simple model of agents exchanging money. Next, we add *space* to allow agents to move. Then, we'll cover two of Mesa's analytic tools: the *data collector* and *batch runner*. After that, we'll add an *interactive visualization* which lets us watch the model as it runs. Finally, we go over how to write your own visualization module, for users who are comfortable with JavaScript.\n", "\n", - "Finally, the creation of a custom visualization module in JavaScript is explored." + "You can also find all the code this tutorial describes in the **examples/boltzmann_wealth_model** directory of the Mesa repository." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "'## Model Description\n", + "## Sample Model Description\n", "\n", - "This is a starter-level simulated agent-based economy. In an agent-based economy, the behavior of an\n", - "individual economic agent, such as a consumer or producer, is studied in a market environment.\n", - "This model is drawn from the field econophysics, specifically a paper prepared by Drăgulescu et al.\n", - "for additional information on the modeling assumptions used in this model.\n", - "[Drăgulescu, 2002].\n", - "\n", - "The assumption that govern this model are:\n", + "The tutorial model is a very simple simulated agent-based economy, drawn from econophysics and presenting a statistical mechanics approach to wealth distribution [Dragulescu2002]. The rules of our tutorial model:\n", "\n", "1. There are some number of agents.\n", "2. All agents begin with 1 unit of money.\n", - "3. At every step of the model, an agent with money gives 1 unit of money.\n", + "3. At every step of the model, an agent gives 1 unit of money (if they have it) to some other agent.\n", + "\n", + "Despite its simplicity, this model yields results that are often unexpected to those not familiar with it. For our purposes, it also easily demonstrates Mesa's core features.\n", "\n", - "Even as a starter-level model the yielded results are both interesting and unexpected to individuals unfamiliar\n", - "with it the specific topic. As such, this model is a good starting point to examine Mesa's core features." + "Let's get started." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Prerequisites Setup\n", + "### Installation\n", "\n", - "Create and activate a [virtual environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/). *Python version 3.8 or higher is required*.\n", - "\n", - "Install Mesa:\n", + "To start, install Mesa. We recommend doing this in a [virtual environment](https://virtualenvwrapper.readthedocs.org/en/stable/), but make sure your environment is set up with Python 3. Mesa requires Python3 and does not work in Python 2 environments.\n", "\n", + "To install Mesa, simply:\n", "\n", "```bash\n", - "pip install mesa\n", + " pip install mesa\n", "```\n", "\n", - "Install Jupyter Notebook (optional):\n", + "When you do that, it will install Mesa itself, as well as any dependencies that aren't in your setup yet. Additional dependencies required by this tutorial can be found in the **examples/boltzmann_wealth_model/requirements.txt** file, which can be installed directly form the github repository by running:\n", "\n", "```bash\n", - "pip install jupyter\n", + " pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt\n", "```\n", "\n", - "Install matplotlib:\n", - "\n", - "```bash\n", - "pip install matplotlib\n", - "```\n" + "This will install the dependencies listed in the requirements.txt file which are: \n", + "- jupyter (Ipython interactive notebook) \n", + "- matplotlib (Python's visualization library) \n", + "- mesa (this ABM library -- if not installed) \n", + "- numpy (Python's numerical python library) " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Building the Sample Model\n", - "\n", - "After Mesa is installed a model can be built. A jupyter notebook is recommended for this tutorial, this allows for small segments of codes to be examined one at a time. As an option this can be created using python script files.\n", - "\n", - "**Good Practice:** Place a model in its own folder/directory. This is not specifically required for the starter_model, but as other models become more complicated and expand multiple python scripts, documentation, discussions and notebooks may be added.\n", - "\n", - "### Create New Folder/Directory\n", - "\n", - "- Using operating system commands create a new folder/directory named 'starter_model'.\n", - "\n", - "- Change to the new folder/directory.\n", - "\n", - "\n", - "### Creating Model With Jupyter Notebook\n", - "\n", - "Write the model interactively in [Jupyter Notebook](http://jupyter.org/) cells.\n", - "\n", - "- Start Jupyter Notebook:\n", - "```bash\n", - "jupyter notebook\n", - "```\n", + "## Building a sample model\n", "\n", - "- Create a new Notebook named `money_model.ipynb` (or whatever you want to call it).\n", + "Once Mesa is installed, you can start building our model. You can write models in two different ways:\n", "\n", - "### Creating Model With Script File (IDE, Text Editor, Colab, etc.)\n", + "1. Write the code in its own file with your favorite text editor, or\n", + "2. Write the model interactively in [Jupyter Notebook](http://jupyter.org/) cells.\n", "\n", - "- Create a new file called `money_model.py` (or whatever you want to call it).\n", + "Either way, it's good practice to put your model in its own folder -- especially if the project will end up consisting of multiple files (for example, Python files for the model and the visualization, a Notebook for analysis, and a Readme with some documentation and discussion).\n", "\n", - "*Code will be added as the tutorial progresses.*" + "Begin by creating a folder, and either launch a Notebook or create a new Python source file. We will use the name `money_model.py` here.\n", + "\n" ] }, { @@ -124,13 +100,8 @@ }, { "cell_type": "code", - "execution_count": 7, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.193952Z", - "end_time": "2023-04-24T21:12:14.397412Z" - } - }, + "execution_count": 36, + "metadata": {}, "outputs": [], "source": [ "import mesa\n", @@ -173,13 +144,8 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.198980Z", - "end_time": "2023-04-24T21:12:14.398410Z" - } - }, + "execution_count": 37, + "metadata": {}, "outputs": [], "source": [ "import mesa\n", @@ -228,28 +194,23 @@ }, { "cell_type": "code", - "execution_count": 9, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.203792Z", - "end_time": "2023-04-24T21:12:14.398410Z" - } - }, + "execution_count": 38, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "Hi, I am agent 3.\n", + "Hi, I am agent 5.\n", + "Hi, I am agent 0.\n", + "Hi, I am agent 1.\n", + "Hi, I am agent 7.\n", "Hi, I am agent 8.\n", "Hi, I am agent 2.\n", "Hi, I am agent 4.\n", "Hi, I am agent 9.\n", - "Hi, I am agent 6.\n", - "Hi, I am agent 1.\n", - "Hi, I am agent 7.\n", - "Hi, I am agent 5.\n", - "Hi, I am agent 0.\n" + "Hi, I am agent 3.\n", + "Hi, I am agent 6.\n" ] } ], @@ -287,13 +248,8 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.222303Z", - "end_time": "2023-04-24T21:12:14.500557Z" - } - }, + "execution_count": 39, + "metadata": {}, "outputs": [], "source": [ "class MoneyAgent(mesa.Agent):\n", @@ -330,13 +286,8 @@ }, { "cell_type": "code", - "execution_count": 11, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.227319Z", - "end_time": "2023-04-24T21:12:14.501575Z" - } - }, + "execution_count": 40, + "metadata": {}, "outputs": [], "source": [ "model = MoneyModel(10)\n", @@ -364,26 +315,27 @@ }, { "cell_type": "code", - "execution_count": 12, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.238809Z", - "end_time": "2023-04-24T21:12:14.613244Z" - } - }, + "execution_count": 41, + "metadata": {}, "outputs": [ { "data": { - "text/plain": "(array([5., 0., 0., 1., 0., 0., 3., 0., 0., 1.]),\n array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7, 3. ]),\n )" + "text/plain": [ + "(array([6., 0., 1., 0., 0., 1., 0., 1., 0., 1.]),\n", + " array([0. , 0.4, 0.8, 1.2, 1.6, 2. , 2.4, 2.8, 3.2, 3.6, 4. ]),\n", + " )" + ] }, - "execution_count": 12, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" }, { "data": { - "text/plain": "
", - "image/png": "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" + "image/png": "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QI0YMSJGjBjRcfnCCy+M1157Le6999742c9+1ouT7V9jY2O8+OKL8Yc//KG3R6mKA91f0R6HI0aMiObm5mhra4tf//rXMXXq1FixYkWXP3iLKM8ei3b+WltbY9q0abFs2bJD+k2mhQiL7nxc+wknnJBrfW/qzv52d8QRR8Q555wTmzZtqsaIPa6r81dXVxdHHnlkL01VfePGjTvkf1jfdttt8eSTT8bKlStj2LBh+1xbpMfhLnn2t7tD/XFYW1sbp556akREnHfeebFmzZq4//77Y/78+XusLeK5i8i3x90d6udv3bp1sXXr1jj33HM7rtu5c2esXLkyfvSjH0WlUom+fft2uk1vnMdCvBTSnY9rHz9+fKf1ERHLli07JD/ePcXH0e/cuTNaWlpiyJAh1RqzRxXp/KXU3Nx8yJ7DLMvitttui0WLFsVzzz0XJ5988n5vU6Tz2J397a5oj8P29vaoVCp7/bcinbt92dced3eon79LL700Wlpaorm5ueMYO3ZsTJkyJZqbm/eIioheOo9Ve1toYgsXLsxKpVK2YMGC7OWXX86+8pWvZMccc0y2ZcuWLMuy7MYbb8xmzpzZsf7555/P+vXrl/3gBz/IXnnllWz27NnZEUcckbW0tPTWFvYp7/7uvPPObOnSpdlrr72WrVu3Lvvc5z6X9e/fP3vppZd6awv7tG3btmzDhg3Zhg0bsojI7rnnnmzDhg3Zm2++mWVZls2cOTO78cYbO9a//vrr2VFHHZXdcccd2SuvvJLNnTs369u3b7ZkyZLe2sJ+5d3jvffemy1evDh79dVXs5aWlmzatGlZnz59smeeeaa3trBPt956a1ZfX58tX748e++99zqOf/3rXx1rivw47M7+ivQ4nDlzZrZixYps8+bN2Z///Ods5syZWU1NTfa73/0uy7Jin7td8u6xSOevK7v/VsihcB4LExZZlmU//OEPsxNPPDGrra3Nxo0bl61evbrj3y666KJs6tSpndb/6le/yk477bSstrY2GzVqVPbUU0/18MT55Nnf9OnTO9Yef/zx2ac//els/fr1vTD1gdn1q5W7H7v2NHXq1Oyiiy7a4zZnn312Vltbm51yyinZww8/3ONz55F3j3fffXf2sY99LOvfv382cODAbOLEidlzzz3XO8MfgL3tLSI6nZciPw67s78iPQ6/+MUvZsOHD89qa2uzQYMGZZdeemnHD9wsK/a52yXvHot0/rqye1gcCufRx6YDAMkU4j0WAEAxCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBk/h9X3YomdlYsdQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] }, "metadata": {}, "output_type": "display_data" @@ -416,26 +368,27 @@ }, { "cell_type": "code", - "execution_count": 13, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.602152Z", - "end_time": "2023-04-24T21:12:14.700218Z" - } - }, + "execution_count": 42, + "metadata": {}, "outputs": [ { "data": { - "text/plain": "(array([437., 292., 155., 80., 26., 10.]),\n array([0., 1., 2., 3., 4., 5., 6.]),\n )" + "text/plain": [ + "(array([431., 303., 153., 76., 26., 9., 1., 1.]),\n", + " array([0., 1., 2., 3., 4., 5., 6., 7., 8.]),\n", + " )" + ] }, - "execution_count": 13, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" }, { "data": { - "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAb9klEQVR4nO3df6yW9X3/8Rc/PAcFzkGonCMD1KWuyBTaQoUzu1/KYOzUaMTNLswxR9qMHJxI5iyJw9UtPcRt1dog1G4Tl5XQuQQ7adASbDGLx1/HkCBOVjcbyOg52DjOAb7hgJz7+8fGnZ7iWg5C78+hj0dyJd7Xdd33/b6uGM/T61z3fYZVKpVKAAAKMrzWAwAA/CiBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHFG1nqAM9Hf35/9+/dn7NixGTZsWK3HAQBOQ6VSyaFDhzJp0qQMH/7jr5EMyUDZv39/pkyZUusxAIAzsG/fvkyePPnH7jMkA2Xs2LFJ/ucAGxoaajwNAHA6ent7M2XKlOrP8R9nSAbKyV/rNDQ0CBQAGGJO5/YMN8kCAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcUbWeoASXf65b9Z6hCHje2taaz0CAOchV1AAgOIIFACgOAIFACiOQAEAiiNQAIDiCBQAoDgCBQAojkABAIojUACA4ggUAKA4AgUAKI5AAQCKI1AAgOIIFACgOAIFACiOQAEAiiNQAIDiCBQAoDgCBQAojkABAIojUACA4ggUAKA4AgUAKI5AAQCKI1AAgOJ8oEBZs2ZNhg0blhUrVlTXHT16NG1tbZkwYULGjBmTRYsWpbu7e8Dz9u7dm9bW1lx00UWZOHFi7rnnnrz33nsfZBQA4DxyxoHyyiuv5Ctf+UpmzJgxYP3dd9+dp59+Ok8++WR27NiR/fv355ZbbqluP3HiRFpbW3Ps2LG88MILeeKJJ7Jhw4asXr36zI8CADivnFGgHD58OIsXL85Xv/rVXHzxxdX1PT09+bu/+7t88YtfzPXXX59Zs2bl8ccfzwsvvJAXX3wxSfKtb30rb7zxRv7xH/8xH/3oR7Nw4cL8xV/8RdauXZtjx46dnaMCAIa0MwqUtra2tLa2Zt68eQPWd3Z25vjx4wPWT5s2LVOnTk1HR0eSpKOjI9dcc02ampqq+yxYsCC9vb3ZvXv3+75fX19fent7BywAwPlr5GCfsGnTprz22mt55ZVXTtnW1dWVurq6jBs3bsD6pqamdHV1Vff54Tg5uf3ktvfT3t6ez3/+84MdFQAYogZ1BWXfvn2566678rWvfS2jRo06VzOdYtWqVenp6aku+/bt+6m9NwDw0zeoQOns7MyBAwfy8Y9/PCNHjszIkSOzY8eOPPLIIxk5cmSamppy7NixHDx4cMDzuru709zcnCRpbm4+5VM9Jx+f3OdH1dfXp6GhYcACAJy/BhUoN9xwQ3bt2pWdO3dWl9mzZ2fx4sXVf77ggguyffv26nP27NmTvXv3pqWlJUnS0tKSXbt25cCBA9V9tm3bloaGhkyfPv0sHRYAMJQN6h6UsWPH5uqrrx6wbvTo0ZkwYUJ1/dKlS7Ny5cqMHz8+DQ0NufPOO9PS0pK5c+cmSebPn5/p06fn9ttvz4MPPpiurq7cd999aWtrS319/Vk6LABgKBv0TbI/yUMPPZThw4dn0aJF6evry4IFC/Loo49Wt48YMSJbtmzJsmXL0tLSktGjR2fJkiV54IEHzvYoAMAQNaxSqVRqPcRg9fb2prGxMT09PefkfpTLP/fNs/6a56vvrWmt9QgADBGD+fntb/EAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxRlUoKxbty4zZsxIQ0NDGhoa0tLSkq1bt1a3Hz16NG1tbZkwYULGjBmTRYsWpbu7e8Br7N27N62trbnooosyceLE3HPPPXnvvffOztEAAOeFQQXK5MmTs2bNmnR2dubVV1/N9ddfn5tuuim7d+9Oktx99915+umn8+STT2bHjh3Zv39/brnllurzT5w4kdbW1hw7diwvvPBCnnjiiWzYsCGrV68+u0cFAAxpwyqVSuWDvMD48ePzV3/1V7n11ltzySWXZOPGjbn11luTJG+++WauuuqqdHR0ZO7cudm6dWs+9alPZf/+/WlqakqSrF+/Pvfee2/eeeed1NXVndZ79vb2prGxMT09PWloaPgg47+vyz/3zbP+muer761prfUIAAwRg/n5fcb3oJw4cSKbNm3KkSNH0tLSks7Ozhw/fjzz5s2r7jNt2rRMnTo1HR0dSZKOjo5cc8011ThJkgULFqS3t7d6FQYAYORgn7Br1660tLTk6NGjGTNmTDZv3pzp06dn586dqaury7hx4wbs39TUlK6uriRJV1fXgDg5uf3ktv9LX19f+vr6qo97e3sHOzYAMIQM+grKRz7ykezcuTMvvfRSli1bliVLluSNN944F7NVtbe3p7GxsbpMmTLlnL4fAFBbgw6Uurq6fPjDH86sWbPS3t6emTNn5ktf+lKam5tz7NixHDx4cMD+3d3daW5uTpI0Nzef8qmek49P7vN+Vq1alZ6enuqyb9++wY4NAAwhH/h7UPr7+9PX15dZs2blggsuyPbt26vb9uzZk71796alpSVJ0tLSkl27duXAgQPVfbZt25aGhoZMnz79/3yP+vr66kebTy4AwPlrUPegrFq1KgsXLszUqVNz6NChbNy4Md/5znfy7LPPprGxMUuXLs3KlSszfvz4NDQ05M4770xLS0vmzp2bJJk/f36mT5+e22+/PQ8++GC6urpy3333pa2tLfX19efkAAGAoWdQgXLgwIH8/u//fr7//e+nsbExM2bMyLPPPpvf+I3fSJI89NBDGT58eBYtWpS+vr4sWLAgjz76aPX5I0aMyJYtW7Js2bK0tLRk9OjRWbJkSR544IGze1QAwJD2gb8HpRZ8D0o5fA8KAKdrMD+/B/0xY/hhYu70iTmA0+ePBQIAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFGVSgtLe35xOf+ETGjh2biRMn5uabb86ePXsG7HP06NG0tbVlwoQJGTNmTBYtWpTu7u4B++zduzetra256KKLMnHixNxzzz157733PvjRAADnhUEFyo4dO9LW1pYXX3wx27Zty/HjxzN//vwcOXKkus/dd9+dp59+Ok8++WR27NiR/fv355ZbbqluP3HiRFpbW3Ps2LG88MILeeKJJ7Jhw4asXr367B0VADCkDatUKpUzffI777yTiRMnZseOHfmVX/mV9PT05JJLLsnGjRtz6623JknefPPNXHXVVeno6MjcuXOzdevWfOpTn8r+/fvT1NSUJFm/fn3uvffevPPOO6mrq/uJ79vb25vGxsb09PSkoaHhTMf/P13+uW+e9deE761prfUIADU1mJ/fH+gelJ6eniTJ+PHjkySdnZ05fvx45s2bV91n2rRpmTp1ajo6OpIkHR0dueaaa6pxkiQLFixIb29vdu/e/b7v09fXl97e3gELAHD+OuNA6e/vz4oVK3Ldddfl6quvTpJ0dXWlrq4u48aNG7BvU1NTurq6qvv8cJyc3H5y2/tpb29PY2NjdZkyZcqZjg0ADAFnHChtbW15/fXXs2nTprM5z/tatWpVenp6qsu+ffvO+XsCALUz8kyetHz58mzZsiXPP/98Jk+eXF3f3NycY8eO5eDBgwOuonR3d6e5ubm6z8svvzzg9U5+yufkPj+qvr4+9fX1ZzIqADAEDeoKSqVSyfLly7N58+Y899xzueKKKwZsnzVrVi644IJs3769um7Pnj3Zu3dvWlpakiQtLS3ZtWtXDhw4UN1n27ZtaWhoyPTp0z/IsQAA54lBXUFpa2vLxo0b841vfCNjx46t3jPS2NiYCy+8MI2NjVm6dGlWrlyZ8ePHp6GhIXfeeWdaWloyd+7cJMn8+fMzffr03H777XnwwQfT1dWV++67L21tba6SAABJBhko69atS5L82q/92oD1jz/+eP7gD/4gSfLQQw9l+PDhWbRoUfr6+rJgwYI8+uij1X1HjBiRLVu2ZNmyZWlpacno0aOzZMmSPPDAAx/sSACA88YH+h6UWvE9KAxFvgcF+Fn3U/seFACAc0GgAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRnZK0HgJ8Vl3/um7UeYcj43prWWo8A1JgrKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFGXSgPP/887nxxhszadKkDBs2LE899dSA7ZVKJatXr86ll16aCy+8MPPmzct3v/vdAfu8++67Wbx4cRoaGjJu3LgsXbo0hw8f/kAHAgCcPwYdKEeOHMnMmTOzdu3a993+4IMP5pFHHsn69evz0ksvZfTo0VmwYEGOHj1a3Wfx4sXZvXt3tm3bli1btuT555/PZz/72TM/CgDgvDJysE9YuHBhFi5c+L7bKpVKHn744dx333256aabkiT/8A//kKampjz11FP59Kc/nX/7t3/LM888k1deeSWzZ89Oknz5y1/Ob/3Wb+Wv//qvM2nSpA9wOADA+eCs3oPy9ttvp6urK/Pmzauua2xszJw5c9LR0ZEk6ejoyLhx46pxkiTz5s3L8OHD89JLL73v6/b19aW3t3fAAgCcv85qoHR1dSVJmpqaBqxvamqqbuvq6srEiRMHbB85cmTGjx9f3edHtbe3p7GxsbpMmTLlbI4NABRmSHyKZ9WqVenp6aku+/btq/VIAMA5dFYDpbm5OUnS3d09YH13d3d1W3Nzcw4cODBg+3vvvZd33323us+Pqq+vT0NDw4AFADh/ndVAueKKK9Lc3Jzt27dX1/X29uall15KS0tLkqSlpSUHDx5MZ2dndZ/nnnsu/f39mTNnztkcBwAYogb9KZ7Dhw/nrbfeqj5+++23s3PnzowfPz5Tp07NihUr8pd/+Ze58sorc8UVV+TP/uzPMmnSpNx8881Jkquuuiq/+Zu/mc985jNZv359jh8/nuXLl+fTn/60T/AAAEnOIFBeffXV/Pqv/3r18cqVK5MkS5YsyYYNG/Knf/qnOXLkSD772c/m4MGD+eQnP5lnnnkmo0aNqj7na1/7WpYvX54bbrghw4cPz6JFi/LII4+chcMBAM4HwyqVSqXWQwxWb29vGhsb09PTc07uR7n8c988668JnL7vrWmt9QjAOTCYn99D4lM8AMDPFoECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFCcQX/VPcC55tucT59v3eV85QoKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHEECgBQHIECABRnZK0HAODMXf65b9Z6hCHje2taaz0Cg+AKCgBQHIECABRHoAAAxREoAEBxBAoAUByBAgAUR6AAAMURKABAcQQKAFAcgQIAFEegAADFESgAQHH8sUAAfib4w4qDU+s/rugKCgBQHIECABRHoAAAxalpoKxduzaXX355Ro0alTlz5uTll1+u5TgAQCFqFihf//rXs3Llytx///157bXXMnPmzCxYsCAHDhyo1UgAQCFqFihf/OIX85nPfCZ33HFHpk+fnvXr1+eiiy7K3//939dqJACgEDX5mPGxY8fS2dmZVatWVdcNHz488+bNS0dHxyn79/X1pa+vr/q4p6cnSdLb23tO5uvv+3/n5HUBYKg4Fz9jT75mpVL5ifvWJFB+8IMf5MSJE2lqahqwvqmpKW+++eYp+7e3t+fzn//8KeunTJlyzmYE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" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAigAAAGdCAYAAAA44ojeAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAd4ElEQVR4nO3de3BU9fnH8U8uJNyyGxNNQkoCeKkQuaiJwIq9SUqaRkaHaLGT2iiMTpnFAplSSYtQtRqkreIFQa0FO5pBbQsWHMQ01jAOAUJoOgEFtVWTadwEx7IL6bCB5Pz+6I+dRqga2HCebN+vmTPjnnN29/kOat6c7CXOcRxHAAAAhsS7PQAAAMCnESgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwJ9HtAc5ET0+P2tralJKSori4OLfHAQAAX4DjODpy5Iiys7MVH//Z10gGZKC0tbUpJyfH7TEAAMAZaG1t1ciRIz/znAEZKCkpKZL+vUCPx+PyNAAA4IsIhULKycmJ/Bz/LAMyUE7+Wsfj8RAoAAAMMF/k5Rm8SBYAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwJ9HtASwaveQVt0foVx+sKHF7BAAAPhNXUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAnLMKlBUrViguLk4LFy6M7Dt27Jj8fr/S09M1fPhwlZaWqr29vdf9WlpaVFJSoqFDhyojI0OLFy/WiRMnzmYUAAAQQ844UBoaGvTkk09q4sSJvfYvWrRImzdv1ksvvaS6ujq1tbVp1qxZkePd3d0qKSlRV1eXduzYoWeffVbr16/XsmXLznwVAAAgppxRoBw9elRlZWV6+umndd5550X2B4NBPfPMM3rooYd07bXXKj8/X+vWrdOOHTu0c+dOSdJrr72mt956S88995wuv/xyFRcX67777tPq1avV1dUVnVUBAIAB7YwCxe/3q6SkRIWFhb32NzY26vjx4732jx07Vrm5uaqvr5ck1dfXa8KECcrMzIycU1RUpFAopP3795/JOAAAIMYk9vUOGzZs0N69e9XQ0HDKsUAgoKSkJKWmpvban5mZqUAgEDnnP+Pk5PGTx04nHA4rHA5HbodCob6ODQAABpA+XUFpbW3VggUL9Pzzz2vw4MH9NdMpqqqq5PV6I1tOTs45e24AAHDu9SlQGhsb1dHRoSuvvFKJiYlKTExUXV2dHn30USUmJiozM1NdXV06fPhwr/u1t7crKytLkpSVlXXKu3pO3j55zqdVVlYqGAxGttbW1r6MDQAABpg+Bcr06dPV3NyspqamyFZQUKCysrLIPw8aNEi1tbWR+xw8eFAtLS3y+XySJJ/Pp+bmZnV0dETOqampkcfjUV5e3mmfNzk5WR6Pp9cGAABiV59eg5KSkqLx48f32jds2DClp6dH9s+dO1cVFRVKS0uTx+PRnXfeKZ/Pp6lTp0qSZsyYoby8PN1yyy1auXKlAoGAli5dKr/fr+Tk5CgtCwAADGR9fpHs53n44YcVHx+v0tJShcNhFRUV6YknnogcT0hI0JYtWzRv3jz5fD4NGzZM5eXluvfee6M9CgAAGKDiHMdx3B6ir0KhkLxer4LBYL/8umf0klei/piWfLCixO0RAAD/g/ry85vv4gEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5fQqUNWvWaOLEifJ4PPJ4PPL5fNq6dWvk+LFjx+T3+5Wenq7hw4ertLRU7e3tvR6jpaVFJSUlGjp0qDIyMrR48WKdOHEiOqsBAAAxoU+BMnLkSK1YsUKNjY3as2ePrr32Wl1//fXav3+/JGnRokXavHmzXnrpJdXV1amtrU2zZs2K3L+7u1slJSXq6urSjh079Oyzz2r9+vVatmxZdFcFAAAGtDjHcZyzeYC0tDT94he/0I033qgLLrhA1dXVuvHGGyVJBw4c0Lhx41RfX6+pU6dq69atuu6669TW1qbMzExJ0tq1a3XXXXfp0KFDSkpK+kLPGQqF5PV6FQwG5fF4zmb80xq95JWoPybOnQ9WlLg9AgDgNPry8/uMX4PS3d2tDRs2qLOzUz6fT42NjTp+/LgKCwsj54wdO1a5ubmqr6+XJNXX12vChAmROJGkoqIihUKhyFWY0wmHwwqFQr02AAAQu/ocKM3NzRo+fLiSk5P1gx/8QBs3blReXp4CgYCSkpKUmpra6/zMzEwFAgFJUiAQ6BUnJ4+fPPbfVFVVyev1RracnJy+jg0AAAaQPgfKpZdeqqamJu3atUvz5s1TeXm53nrrrf6YLaKyslLBYDCytba29uvzAQAAdyX29Q5JSUm6+OKLJUn5+flqaGjQI488otmzZ6urq0uHDx/udRWlvb1dWVlZkqSsrCzt3r271+OdfJfPyXNOJzk5WcnJyX0dFQAADFBn/TkoPT09CofDys/P16BBg1RbWxs5dvDgQbW0tMjn80mSfD6fmpub1dHRETmnpqZGHo9HeXl5ZzsKAACIEX26glJZWani4mLl5ubqyJEjqq6u1htvvKFt27bJ6/Vq7ty5qqioUFpamjwej+688075fD5NnTpVkjRjxgzl5eXplltu0cqVKxUIBLR06VL5/X6ukAAAgIg+BUpHR4e+//3v66OPPpLX69XEiRO1bds2ffOb35QkPfzww4qPj1dpaanC4bCKior0xBNPRO6fkJCgLVu2aN68efL5fBo2bJjKy8t17733RndVAABgQDvrz0FxA5+Dgs/C56AAgE3n5HNQAAAA+guBAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJjTp0CpqqrSVVddpZSUFGVkZOiGG27QwYMHe51z7Ngx+f1+paena/jw4SotLVV7e3uvc1paWlRSUqKhQ4cqIyNDixcv1okTJ85+NQAAICb0KVDq6urk9/u1c+dO1dTU6Pjx45oxY4Y6Ozsj5yxatEibN2/WSy+9pLq6OrW1tWnWrFmR493d3SopKVFXV5d27NihZ599VuvXr9eyZcuityoAADCgxTmO45zpnQ8dOqSMjAzV1dXpq1/9qoLBoC644AJVV1frxhtvlCQdOHBA48aNU319vaZOnaqtW7fquuuuU1tbmzIzMyVJa9eu1V133aVDhw4pKSnpc583FArJ6/UqGAzK4/Gc6fj/1eglr0T9MXHufLCixO0RAACn0Zef32f1GpRgMChJSktLkyQ1Njbq+PHjKiwsjJwzduxY5ebmqr6+XpJUX1+vCRMmROJEkoqKihQKhbR///7TPk84HFYoFOq1AQCA2HXGgdLT06OFCxdq2rRpGj9+vCQpEAgoKSlJqampvc7NzMxUIBCInPOfcXLy+Mljp1NVVSWv1xvZcnJyznRsAAAwAJxxoPj9fu3bt08bNmyI5jynVVlZqWAwGNlaW1v7/TkBAIB7Es/kTvPnz9eWLVu0fft2jRw5MrI/KytLXV1dOnz4cK+rKO3t7crKyoqcs3v37l6Pd/JdPifP+bTk5GQlJyefyagAAGAA6tMVFMdxNH/+fG3cuFGvv/66xowZ0+t4fn6+Bg0apNra2si+gwcPqqWlRT6fT5Lk8/nU3Nysjo6OyDk1NTXyeDzKy8s7m7UAAIAY0acrKH6/X9XV1Xr55ZeVkpISec2I1+vVkCFD5PV6NXfuXFVUVCgtLU0ej0d33nmnfD6fpk6dKkmaMWOG8vLydMstt2jlypUKBAJaunSp/H4/V0kAAICkPgbKmjVrJElf//rXe+1ft26dbr31VknSww8/rPj4eJWWliocDquoqEhPPPFE5NyEhARt2bJF8+bNk8/n07Bhw1ReXq5777337FYCAABixll9Dopb+BwUfBY+BwUAbDpnn4MCAADQHwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwJxEtwcAom30klfcHqHffbCixO0RAKBfcQUFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMKfPgbJ9+3bNnDlT2dnZiouL06ZNm3oddxxHy5Yt04gRIzRkyBAVFhbq3Xff7XXOJ598orKyMnk8HqWmpmru3Lk6evToWS0EAADEjj4HSmdnpyZNmqTVq1ef9vjKlSv16KOPau3atdq1a5eGDRumoqIiHTt2LHJOWVmZ9u/fr5qaGm3ZskXbt2/XHXfccearAAAAMSWxr3coLi5WcXHxaY85jqNVq1Zp6dKluv766yVJv/3tb5WZmalNmzbp5ptv1ttvv61XX31VDQ0NKigokCQ99thj+va3v61f/vKXys7OPovlAACAWBDV16C8//77CgQCKiwsjOzzer2aMmWK6uvrJUn19fVKTU2NxIkkFRYWKj4+Xrt27YrmOAAAYIDq8xWUzxIIBCRJmZmZvfZnZmZGjgUCAWVkZPQeIjFRaWlpkXM+LRwOKxwOR26HQqFojg0AAIwZEO/iqaqqktfrjWw5OTlujwQAAPpRVAMlKytLktTe3t5rf3t7e+RYVlaWOjo6eh0/ceKEPvnkk8g5n1ZZWalgMBjZWltbozk2AAAwJqqBMmbMGGVlZam2tjayLxQKadeuXfL5fJIkn8+nw4cPq7GxMXLO66+/rp6eHk2ZMuW0j5ucnCyPx9NrAwAAsavPr0E5evSo3nvvvcjt999/X01NTUpLS1Nubq4WLlyon//857rkkks0ZswY3X333crOztYNN9wgSRo3bpy+9a1v6fbbb9fatWt1/PhxzZ8/XzfffDPv4AEAAJLOIFD27Nmjb3zjG5HbFRUVkqTy8nKtX79eP/7xj9XZ2ak77rhDhw8f1jXXXKNXX31VgwcPjtzn+eef1/z58zV9+nTFx8ertLRUjz76aBSWAwAAYkGc4ziO20P0VSgUktfrVTAY7Jdf94xe8krUHxOIpg9WlLg9AgD0WV9+fg+Id/EAAID/LQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJiT6PYAAPou1r9xm29rBsAVFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMIFAAAYA6BAgAAzCFQAACAOQQKAAAwh0ABAADmECgAAMAcAgUAAJhDoAAAAHMS3R4AAD5t9JJX3B6hX32wosTtEQDzuIICAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBwCBQAAmEOgAAAAcwgUAABgDoECAADMIVAAAIA5BAoAADCHQAEAAOYQKAAAwBy+zRgAzrFY/7ZmiW9sxtnjCgoAADCHQAEAAOa4GiirV6/W6NGjNXjwYE2ZMkW7d+92cxwAAGCEa4HywgsvqKKiQsuXL9fevXs1adIkFRUVqaOjw62RAACAEa4FykMPPaTbb79dt912m/Ly8rR27VoNHTpUv/nNb9waCQAAGOHKu3i6urrU2NioysrKyL74+HgVFhaqvr7+lPPD4bDC4XDkdjAYlCSFQqF+ma8n/K9+eVwA+F+Ru+glt0fAWdp3T1HUH/Pkz23HcT73XFcC5eOPP1Z3d7cyMzN77c/MzNSBAwdOOb+qqkr33HPPKftzcnL6bUYAAP6XeVf132MfOXJEXq/3M88ZEJ+DUllZqYqKisjtnp4effLJJ0pPT1dcXFxUnysUCiknJ0etra3yeDxRfWwLWN/AF+trZH0DX6yvMdbXJ/XfGh3H0ZEjR5Sdnf2557oSKOeff74SEhLU3t7ea397e7uysrJOOT85OVnJycm99qWmpvbniPJ4PDH7L57E+mJBrK+R9Q18sb7GWF+f1D9r/LwrJye58iLZpKQk5efnq7a2NrKvp6dHtbW18vl8bowEAAAMce1XPBUVFSovL1dBQYEmT56sVatWqbOzU7fddptbIwEAACNcC5TZs2fr0KFDWrZsmQKBgC6//HK9+uqrp7xw9lxLTk7W8uXLT/mVUqxgfQNfrK+R9Q18sb7GWF+fZGONcc4Xea8PAADAOcR38QAAAHMIFAAAYA6BAgAAzCFQAACAOQTKf1i9erVGjx6twYMHa8qUKdq9e7fbI0XN9u3bNXPmTGVnZysuLk6bNm1ye6Soqqqq0lVXXaWUlBRlZGTohhtu0MGDB90eK2rWrFmjiRMnRj40yefzaevWrW6P1W9WrFihuLg4LVy40O1RouZnP/uZ4uLiem1jx451e6yo+sc//qHvfe97Sk9P15AhQzRhwgTt2bPH7bGiZvTo0af8GcbFxcnv97s9WlR0d3fr7rvv1pgxYzRkyBBddNFFuu+++77Q9+b0BwLl/73wwguqqKjQ8uXLtXfvXk2aNElFRUXq6Ohwe7So6Ozs1KRJk7R69Wq3R+kXdXV18vv92rlzp2pqanT8+HHNmDFDnZ2dbo8WFSNHjtSKFSvU2NioPXv26Nprr9X111+v/fv3uz1a1DU0NOjJJ5/UxIkT3R4l6i677DJ99NFHke3NN990e6So+ec//6lp06Zp0KBB2rp1q9566y396le/0nnnnef2aFHT0NDQ68+vpqZGknTTTTe5PFl0PPjgg1qzZo0ef/xxvf3223rwwQe1cuVKPfbYY+4M5MBxHMeZPHmy4/f7I7e7u7ud7Oxsp6qqysWp+ockZ+PGjW6P0a86OjocSU5dXZ3bo/Sb8847z/n1r3/t9hhRdeTIEeeSSy5xampqnK997WvOggUL3B4papYvX+5MmjTJ7TH6zV133eVcc801bo9xTi1YsMC56KKLnJ6eHrdHiYqSkhJnzpw5vfbNmjXLKSsrc2UerqBI6urqUmNjowoLCyP74uPjVVhYqPr6ehcnw5kKBoOSpLS0NJcnib7u7m5t2LBBnZ2dMffVEH6/XyUlJb3+W4wl7777rrKzs3XhhReqrKxMLS0tbo8UNX/84x9VUFCgm266SRkZGbriiiv09NNPuz1Wv+nq6tJzzz2nOXPmRP1La91y9dVXq7a2Vu+8844k6a9//avefPNNFRcXuzLPgPg24/728ccfq7u7+5RPsc3MzNSBAwdcmgpnqqenRwsXLtS0adM0fvx4t8eJmubmZvl8Ph07dkzDhw/Xxo0blZeX5/ZYUbNhwwbt3btXDQ0Nbo/SL6ZMmaL169fr0ksv1UcffaR77rlHX/nKV7Rv3z6lpKS4Pd5Z+/vf/641a9aooqJCP/nJT9TQ0KAf/vCHSkpKUnl5udvjRd2mTZt0+PBh3XrrrW6PEjVLlixRKBTS2LFjlZCQoO7ubt1///0qKytzZR4CBTHH7/dr3759MfX7fUm69NJL1dTUpGAwqN/97ncqLy9XXV1dTERKa2urFixYoJqaGg0ePNjtcfrFf/4tdOLEiZoyZYpGjRqlF198UXPnznVxsujo6elRQUGBHnjgAUnSFVdcoX379mnt2rUxGSjPPPOMiouLlZ2d7fYoUfPiiy/q+eefV3V1tS677DI1NTVp4cKFys7OduXPkECRdP755yshIUHt7e299re3tysrK8ulqXAm5s+fry1btmj79u0aOXKk2+NEVVJSki6++GJJUn5+vhoaGvTII4/oySefdHmys9fY2KiOjg5deeWVkX3d3d3avn27Hn/8cYXDYSUkJLg4YfSlpqbqy1/+st577z23R4mKESNGnBLL48aN0+9//3uXJuo/H374of70pz/pD3/4g9ujRNXixYu1ZMkS3XzzzZKkCRMm6MMPP1RVVZUrgcJrUPTv//Hn5+ertrY2sq+np0e1tbUx9zv+WOU4jubPn6+NGzfq9ddf15gxY9weqd/19PQoHA67PUZUTJ8+Xc3NzWpqaopsBQUFKisrU1NTU8zFiSQdPXpUf/vb3zRixAi3R4mKadOmnfLW/nfeeUejRo1yaaL+s27dOmVkZKikpMTtUaLqX//6l+Lje2dBQkKCenp6XJmHKyj/r6KiQuXl5SooKNDkyZO1atUqdXZ26rbbbnN7tKg4evRor7+pvf/++2pqalJaWppyc3NdnCw6/H6/qqur9fLLLyslJUWBQECS5PV6NWTIEJenO3uVlZUqLi5Wbm6ujhw5ourqar3xxhvatm2b26NFRUpKyimvFxo2bJjS09Nj5nVEP/rRjzRz5kyNGjVKbW1tWr58uRISEvTd737X7dGiYtGiRbr66qv1wAMP6Dvf+Y52796tp556Sk899ZTbo0VVT0+P1q1bp/LyciUmxtaP0JkzZ+r+++9Xbm6uLrvsMv3lL3/RQw89pDlz5rgzkCvvHTLqsccec3Jzc52kpCRn8uTJzs6dO90eKWr+/Oc/O5JO2crLy90eLSpOtzZJzrp169weLSrmzJnjjBo1yklKSnIuuOACZ/r06c5rr73m9lj9KtbeZjx79mxnxIgRTlJSkvOlL33JmT17tvPee++5PVZUbd682Rk/fryTnJzsjB071nnqqafcHinqtm3b5khyDh486PYoURcKhZwFCxY4ubm5zuDBg50LL7zQ+elPf+qEw2FX5olzHJc+Ig4AAOC/4DUoAADAHAIFAACYQ6AAAABzCBQAAGAOgQIAAMwhUAAAgDkECgAAMIdAAQAA5hAoAADAHAIFAACYQ6AAAABzCBQAAGDO/wGRqdsvlAUptwAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] }, "metadata": {}, "output_type": "display_data" @@ -490,13 +443,8 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.702142Z", - "end_time": "2023-04-24T21:12:14.705415Z" - } - }, + "execution_count": 43, + "metadata": {}, "outputs": [], "source": [ "class MoneyModel(mesa.Model):\n", @@ -581,13 +529,8 @@ }, { "cell_type": "code", - "execution_count": 15, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.705415Z", - "end_time": "2023-04-24T21:12:14.711263Z" - } - }, + "execution_count": 44, + "metadata": {}, "outputs": [], "source": [ "class MoneyAgent(mesa.Agent):\n", @@ -646,13 +589,8 @@ }, { "cell_type": "code", - "execution_count": 16, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.715462Z", - "end_time": "2023-04-24T21:12:14.716977Z" - } - }, + "execution_count": 45, + "metadata": {}, "outputs": [], "source": [ "model = MoneyModel(50, 10, 10)\n", @@ -669,26 +607,25 @@ }, { "cell_type": "code", - "execution_count": 17, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.719887Z", - "end_time": "2023-04-24T21:12:14.900840Z" - } - }, + "execution_count": 46, + "metadata": {}, "outputs": [ { "data": { - "text/plain": "" + "text/plain": [ + "" + ] }, - "execution_count": 17, + "execution_count": 46, "metadata": {}, "output_type": "execute_result" }, { "data": { - "text/plain": "
", - "image/png": "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" + "image/png": "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", + "text/plain": [ + "
" + ] }, "metadata": {}, "output_type": "display_data" @@ -726,13 +663,8 @@ }, { "cell_type": "code", - "execution_count": 18, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.904858Z", - "end_time": "2023-04-24T21:12:14.909247Z" - } - }, + "execution_count": 47, + "metadata": {}, "outputs": [], "source": [ "def compute_gini(model):\n", @@ -814,13 +746,8 @@ }, { "cell_type": "code", - "execution_count": 19, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.910833Z", - "end_time": "2023-04-24T21:12:14.926306Z" - } - }, + "execution_count": 48, + "metadata": {}, "outputs": [], "source": [ "model = MoneyModel(50, 10, 10)\n", @@ -837,26 +764,25 @@ }, { "cell_type": "code", - "execution_count": 20, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:14.926306Z", - "end_time": "2023-04-24T21:12:15.033738Z" - } - }, + "execution_count": 49, + "metadata": {}, "outputs": [ { "data": { - "text/plain": "" + "text/plain": [ + "" + ] }, - "execution_count": 20, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" }, { "data": { - "text/plain": "
", - "image/png": "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" + "image/png": "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", + "text/plain": [ + "
" + ] }, "metadata": {}, "output_type": "display_data" @@ -876,20 +802,76 @@ }, { "cell_type": "code", - "execution_count": 21, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:15.036738Z", - "end_time": "2023-04-24T21:12:15.045520Z" - } - }, + "execution_count": 50, + "metadata": {}, "outputs": [ { "data": { - "text/plain": " Wealth\nStep AgentID \n0 0 1\n 1 1\n 2 1\n 3 1\n 4 1", - "text/html": "
\n\n\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
Wealth
StepAgentID
001
11
21
31
41
\n
" + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Wealth
StepAgentID
001
11
21
31
41
\n", + "
" + ], + "text/plain": [ + " Wealth\n", + "Step AgentID \n", + "0 0 1\n", + " 1 1\n", + " 2 1\n", + " 3 1\n", + " 4 1" + ] }, - "execution_count": 21, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -908,26 +890,25 @@ }, { "cell_type": "code", - "execution_count": 22, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:15.045520Z", - "end_time": "2023-04-24T21:12:15.200806Z" - } - }, + "execution_count": 51, + "metadata": {}, "outputs": [ { "data": { - "text/plain": "" + "text/plain": [ + "" + ] }, - "execution_count": 22, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" }, { "data": { - "text/plain": "
", - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGdCAYAAACyzRGfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAcHElEQVR4nO3df5TVdZ348dedYbhAzqCAOEwCopWUJpUES9rmDwQ5HjY3T5m45+CPs/vPWNqcaqPWgKxIz8l1M8Isw/bszmr7A9o6R2mkDY4nSMClYrdMzMpCcbWYgeF4vWfms398D+PS8EUHP/f94V4fj3Pmj/uZD5/7enlH5+mdO9xSlmVZAAAk0lT0AADAa4v4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApEYVPcAfGxwcjD179kRra2uUSqWixwEAXoEsy2L//v3R0dERTU1Hf27juIuPPXv2xNSpU4seAwA4Bk899VSceuqpRz3nuIuP1tbWiPh/w7e1teV67Wq1Gt/73vdiwYIF0dLSkuu1jweNvl9E4+9ov/rX6Dvar/7Vase+vr6YOnXq0Pfxoznu4uPQj1ra2tpqEh/jxo2Ltra2hvyiavT9Ihp/R/vVv0bf0X71r9Y7vpKXTHjBKQCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgqVFFD1CEs1dsiMrAy7/lb70pN2dx25yipwCAo/PMBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUiOKj1WrVsU73/nOaG1tjcmTJ8fll18ejz322GHnvPDCC9HZ2RkTJ06ME044Ia644orYu3dvrkMDAPVrRPGxadOm6OzsjK1bt0ZPT09Uq9VYsGBB9Pf3D53zkY98JL7zne/EP//zP8emTZtiz5498b73vS/3wQGA+jRqJCc/+OCDh92+9957Y/LkybFjx4740z/90+jt7Y177rknuru746KLLoqIiLVr18ab3/zm2Lp1a/zJn/xJfpMDAHVpRPHxx3p7eyMiYsKECRERsWPHjqhWqzF//vyhc2bOnBnTpk2LLVu2HDE+KpVKVCqVodt9fX0REVGtVqNarb6a8YY5dL1yU5brdY8Xh/bK+5/b8eTQbo26o/3qX6PvaL/6V6sdR3K9UpZlx/SdeHBwMP7sz/4s9u3bFw8//HBERHR3d8e11157WExERMyZMycuvPDCuPXWW4ddZ8WKFbFy5cphx7u7u2PcuHHHMhoAkNjBgwdjyZIl0dvbG21tbUc995if+ejs7Ixdu3YNhcexWrZsWXR1dQ3d7uvri6lTp8aCBQtedviRqlar0dPTEzdvb4rKYCnXax8Pyk1Z3DJ7MC655JJoaWkpepyaOPQYNuqO9qt/jb6j/epfrXY89JOLV+KY4uOGG26I7373u7F58+Y49dRTh463t7fHiy++GPv27YsTTzxx6PjevXujvb39iNcql8tRLpeHHW9paanZA18ZLEVloPHi45Ba/rM7XjT6jvarf42+o/3qX947juRaI/ptlyzL4oYbboh169bF97///ZgxY8Zhnz/33HOjpaUlNm7cOHTssccei9/85jcxb968kdwVANCgRvTMR2dnZ3R3d8e3v/3taG1tjWeeeSYiIsaPHx9jx46N8ePHx/XXXx9dXV0xYcKEaGtriw996EMxb948v+kCAETECONjzZo1ERFxwQUXHHZ87dq1cc0110RExN/+7d9GU1NTXHHFFVGpVGLhwoXxla98JZdhAYD6N6L4eCW/GDNmzJhYvXp1rF69+piHAgAal/d2AQCSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJIaVfQA5O/sFRuiMlAqeoyaKDdncducoqcA4NXwzAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFIjjo/NmzfH4sWLo6OjI0qlUqxfv/6wz19zzTVRKpUO+7j00kvzmhcAqHMjjo/+/v6YNWtWrF69+v97zqWXXhpPP/300Mc//dM/vaohAYDGMWqkf2DRokWxaNGio55TLpejvb39mIcCABrXiOPjlfjBD34QkydPjpNOOikuuuii+OxnPxsTJ0484rmVSiUqlcrQ7b6+voiIqFarUa1Wc53r0PXKTVmu1z1eHNqrUfeLeGm3vL82jheH9rJf/Wr0He1X/2q140iuV8qy7Ji/U5VKpVi3bl1cfvnlQ8fuu+++GDduXMyYMSOeeOKJ+OQnPxknnHBCbNmyJZqbm4ddY8WKFbFy5cphx7u7u2PcuHHHOhoAkNDBgwdjyZIl0dvbG21tbUc9N/f4+GO//OUv44wzzoiHHnooLr744mGfP9IzH1OnTo3nnnvuZYcfqWq1Gj09PXHz9qaoDJZyvfbxoNyUxS2zBxt2v4iXdrzkkkuipaWl6HFyd+hr1H71q9F3tF/9q9WOfX19MWnSpFcUHzX5scv/dfrpp8ekSZNi9+7dR4yPcrkc5XJ52PGWlpaaPfCVwVJUBhrzm3NE4+8XUduvj+OB/epfo+9ov/qX944juVbN/56P3/72t/H888/HlClTan1XAEAdGPEzHwcOHIjdu3cP3X7yySdj586dMWHChJgwYUKsXLkyrrjiimhvb48nnngiPv7xj8cb3vCGWLhwYa6DAwD1acTxsX379rjwwguHbnd1dUVExNKlS2PNmjXxk5/8JL75zW/Gvn37oqOjIxYsWBC33HLLEX+0AgC89ow4Pi644II42mtUN2zY8KoGAgAam/d2AQCSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACCpEcfH5s2bY/HixdHR0RGlUinWr19/2OezLItPf/rTMWXKlBg7dmzMnz8/Hn/88bzmBQDq3Ijjo7+/P2bNmhWrV68+4udvu+22+NKXvhR33XVX/OhHP4rXve51sXDhwnjhhRde9bAAQP0bNdI/sGjRoli0aNERP5dlWdxxxx3xN3/zN/He9743IiL+/u//Pk455ZRYv359fPCDH3x10wIAdW/E8XE0Tz75ZDzzzDMxf/78oWPjx4+PuXPnxpYtW44YH5VKJSqVytDtvr6+iIioVqtRrVbzHG/oeuWmLNfrHi8O7dWo+0W8tFveXxvHi0N72a9+NfqO9qt/tdpxJNcrZVl2zN+pSqVSrFu3Li6//PKIiPjhD38Y5513XuzZsyemTJkydN4HPvCBKJVKcf/99w+7xooVK2LlypXDjnd3d8e4ceOOdTQAIKGDBw/GkiVLore3N9ra2o56bq7PfByLZcuWRVdX19Dtvr6+mDp1aixYsOBlhx+parUaPT09cfP2pqgMlnK99vGg3JTFLbMHG3a/iJd2vOSSS6KlpaXocXJ36GvUfvWr0Xe0X/2r1Y6HfnLxSuQaH+3t7RERsXfv3sOe+di7d2+87W1vO+KfKZfLUS6Xhx1vaWmp2QNfGSxFZaAxvzlHNP5+EbX9+jge2K/+NfqO9qt/ee84kmvl+vd8zJgxI9rb22Pjxo1Dx/r6+uJHP/pRzJs3L8+7AgDq1Iif+Thw4EDs3r176PaTTz4ZO3fujAkTJsS0adPipptuis9+9rPxxje+MWbMmBE333xzdHR0DL0uBAB4bRtxfGzfvj0uvPDCoduHXq+xdOnSuPfee+PjH/949Pf3x1/91V/Fvn374vzzz48HH3wwxowZk9/UAEDdGnF8XHDBBXG0X5AplUrxmc98Jj7zmc+8qsEAgMbkvV0AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASCr3+FixYkWUSqXDPmbOnJn33QAAdWpULS561llnxUMPPfTSnYyqyd0AAHWoJlUwatSoaG9vr8WlAYA6V5P4ePzxx6OjoyPGjBkT8+bNi1WrVsW0adOOeG6lUolKpTJ0u6+vLyIiqtVqVKvVXOc6dL1yU5brdY8Xh/Zq1P0iXtot76+N48WhvexXvxp9R/vVv1rtOJLrlbIsy/U71QMPPBAHDhyIM888M55++ulYuXJl/O53v4tdu3ZFa2vrsPNXrFgRK1euHHa8u7s7xo0bl+doAECNHDx4MJYsWRK9vb3R1tZ21HNzj48/tm/fvpg+fXrcfvvtcf311w/7/JGe+Zg6dWo899xzLzv8SFWr1ejp6YmbtzdFZbCU67WPB+WmLG6ZPdiw+0U0/o6H9rvkkkuipaWl6HFyd+jfwUbdL6Lxd7Rf/avVjn19fTFp0qRXFB81fyXoiSeeGG9605ti9+7dR/x8uVyOcrk87HhLS0vNHvjKYCkqA433jeuQRt8vovF3rOXX//Gg0feLaPwd7Vf/8t5xJNeq+d/zceDAgXjiiSdiypQptb4rAKAO5B4fH/3oR2PTpk3xq1/9Kn74wx/Gn//5n0dzc3NcddVVed8VAFCHcv+xy29/+9u46qqr4vnnn4+TTz45zj///Ni6dWucfPLJed8VAFCHco+P++67L+9LAgANxHu7AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJjSp6AGC4s1dsiMpAqegxclduzuK2OUVPARTNMx8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQ1KiiBwBee85esSEqA6Wix6iJcnMWt80pego4vnnmAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJCU+AAAkhIfAEBS4gMASEp8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJIaVfQAANSfs1dsiMpAqegxclduzuK2OUVP0fg88wEAJCU+AICkxAcAkJT4AACSEh8AQFLiAwBISnwAAEmJDwAgKfEBACQlPgCApMQHAJBUzeJj9erVcdppp8WYMWNi7ty58cgjj9TqrgCAOlKT+Lj//vujq6srli9fHo8++mjMmjUrFi5cGM8++2wt7g4AqCM1iY/bb789/vIv/zKuvfbaeMtb3hJ33XVXjBs3Lr7xjW/U4u4AgDoyKu8Lvvjii7Fjx45YtmzZ0LGmpqaYP39+bNmyZdj5lUolKpXK0O3e3t6IiPj9738f1Wo119mq1WocPHgwRlWbYmCw8d4KetRgFgcPDjbsfhGNv6P96t+hHZ9//vloaWkpepzcvVb+O9qoj1/ES49h3jvu378/IiKyLHvZc3OPj+eeey4GBgbilFNOOez4KaecEj//+c+Hnb9q1apYuXLlsOMzZszIe7TXhCVFD5BAo+9ov/r3WtixkXn8Xp39+/fH+PHjj3pO7vExUsuWLYuurq6h24ODg/H73/8+Jk6cGKVSvlXd19cXU6dOjaeeeira2tpyvfbxoNH3i2j8He1X/xp9R/vVv1rtmGVZ7N+/Pzo6Ol723NzjY9KkSdHc3Bx79+497PjevXujvb192PnlcjnK5fJhx0488cS8xzpMW1tbw35RRTT+fhGNv6P96l+j72i/+leLHV/uGY9Dcn/B6ejRo+Pcc8+NjRs3Dh0bHByMjRs3xrx58/K+OwCgztTkxy5dXV2xdOnSmD17dsyZMyfuuOOO6O/vj2uvvbYWdwcA1JGaxMeVV14Z//M//xOf/vSn45lnnom3ve1t8eCDDw57EWpq5XI5li9fPuzHPI2i0feLaPwd7Vf/Gn1H+9W/42HHUvZKficGACAn3tsFAEhKfAAASYkPACAp8QEAJPWaiY/Vq1fHaaedFmPGjIm5c+fGI488UvRIudm8eXMsXrw4Ojo6olQqxfr164seKVerVq2Kd77zndHa2hqTJ0+Oyy+/PB577LGix8rVmjVr4pxzzhn6S3/mzZsXDzzwQNFj1cwXvvCFKJVKcdNNNxU9Si5WrFgRpVLpsI+ZM2cWPVbufve738Vf/MVfxMSJE2Ps2LHx1re+NbZv3170WLk47bTThj2GpVIpOjs7ix4tFwMDA3HzzTfHjBkzYuzYsXHGGWfELbfc8oreh6UWXhPxcf/990dXV1csX748Hn300Zg1a1YsXLgwnn322aJHy0V/f3/MmjUrVq9eXfQoNbFp06bo7OyMrVu3Rk9PT1Sr1ViwYEH09/cXPVpuTj311PjCF74QO3bsiO3bt8dFF10U733ve+O//uu/ih4td9u2bYuvfvWrcc455xQ9Sq7OOuusePrpp4c+Hn744aJHytUf/vCHOO+886KlpSUeeOCB+O///u/44he/GCeddFLRo+Vi27Zthz1+PT09ERHx/ve/v+DJ8nHrrbfGmjVr4stf/nL87Gc/i1tvvTVuu+22uPPOO4sZKHsNmDNnTtbZ2Tl0e2BgIOvo6MhWrVpV4FS1ERHZunXrih6jpp599tksIrJNmzYVPUpNnXTSSdnXv/71osfI1f79+7M3vvGNWU9PT/ae97wnu/HGG4seKRfLly/PZs2aVfQYNfXXf/3X2fnnn1/0GMnceOON2RlnnJENDg4WPUouLrvssuy666477Nj73ve+7Oqrry5knoZ/5uPFF1+MHTt2xPz584eONTU1xfz582PLli0FTsax6u3tjYiICRMmFDxJbQwMDMR9990X/f39DfeWBJ2dnXHZZZcd9u9jo3j88cejo6MjTj/99Lj66qvjN7/5TdEj5erf//3fY/bs2fH+978/Jk+eHG9/+9vja1/7WtFj1cSLL74Y//AP/xDXXXdd7m9wWpR3vetdsXHjxvjFL34RERE//vGP4+GHH45FixYVMk/h72pba88991wMDAwM+9tVTznllPj5z39e0FQcq8HBwbjpppvivPPOi7PPPrvocXL105/+NObNmxcvvPBCnHDCCbFu3bp4y1veUvRYubnvvvvi0UcfjW3bthU9Su7mzp0b9957b5x55pnx9NNPx8qVK+Pd73537Nq1K1pbW4seLxe//OUvY82aNdHV1RWf/OQnY9u2bfHhD384Ro8eHUuXLi16vFytX78+9u3bF9dcc03Ro+TmE5/4RPT19cXMmTOjubk5BgYG4nOf+1xcffXVhczT8PFBY+ns7Ixdu3Y13M/TIyLOPPPM2LlzZ/T29sa//Mu/xNKlS2PTpk0NESBPPfVU3HjjjdHT0xNjxowpepzc/d//ezznnHNi7ty5MX369PjWt74V119/fYGT5WdwcDBmz54dn//85yMi4u1vf3vs2rUr7rrrroaLj3vuuScWLVr0it4avl5861vfin/8x3+M7u7uOOuss2Lnzp1x0003RUdHRyGPX8PHx6RJk6K5uTn27t172PG9e/dGe3t7QVNxLG644Yb47ne/G5s3b45TTz216HFyN3r06HjDG94QERHnnntubNu2Lf7u7/4uvvrVrxY82au3Y8eOePbZZ+Md73jH0LGBgYHYvHlzfPnLX45KpRLNzc0FTpivE088Md70pjfF7t27ix4lN1OmTBkWwm9+85vjX//1XwuaqDZ+/etfx0MPPRT/9m//VvQoufrYxz4Wn/jEJ+KDH/xgRES89a1vjV//+texatWqQuKj4V/zMXr06Dj33HNj48aNQ8cGBwdj48aNDffz9EaVZVnccMMNsW7duvj+978fM2bMKHqkJAYHB6NSqRQ9Ri4uvvji+OlPfxo7d+4c+pg9e3ZcffXVsXPnzoYKj4iIAwcOxBNPPBFTpkwpepTcnHfeecN+xf0Xv/hFTJ8+vaCJamPt2rUxefLkuOyyy4oeJVcHDx6MpqbDv+U3NzfH4OBgIfM0/DMfERFdXV2xdOnSmD17dsyZMyfuuOOO6O/vj2uvvbbo0XJx4MCBw/4P68knn4ydO3fGhAkTYtq0aQVOlo/Ozs7o7u6Ob3/729Ha2hrPPPNMRESMHz8+xo4dW/B0+Vi2bFksWrQopk2bFvv374/u7u74wQ9+EBs2bCh6tFy0trYOe43O6173upg4cWJDvHbnox/9aCxevDimT58ee/bsieXLl0dzc3NcddVVRY+Wm4985CPxrne9Kz7/+c/HBz7wgXjkkUfi7rvvjrvvvrvo0XIzODgYa9eujaVLl8aoUY317XHx4sXxuc99LqZNmxZnnXVW/Od//mfcfvvtcd111xUzUCG/Y1OAO++8M5s2bVo2evTobM6cOdnWrVuLHik3//Ef/5FFxLCPpUuXFj1aLo60W0Rka9euLXq03Fx33XXZ9OnTs9GjR2cnn3xydvHFF2ff+973ih6rphrpV22vvPLKbMqUKdno0aOz17/+9dmVV16Z7d69u+ixcved73wnO/vss7NyuZzNnDkzu/vuu4seKVcbNmzIIiJ77LHHih4ld319fdmNN96YTZs2LRszZkx2+umnZ5/61KeySqVSyDylLCvorzcDAF6TGv41HwDA8UV8AABJiQ8AICnxAQAkJT4AgKTEBwCQlPgAAJISHwBAUuIDAEhKfAAASYkPACAp8QEAJPW/RKk1YELbhE4AAAAASUVORK5CYII=" + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGdCAYAAACyzRGfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAbO0lEQVR4nO3df4zXhX348dfn4Pjg1TscIBwXDkvbVdsaMBVlpM6g8qNsoWXjj06bFI3p0uU0tZemG0tVrprQuKRjSyntkk3WdDe7LQNTE9GTRogZtIIh1iU1Ql2qFVBsuIMjfvyE+3z/8HtHr4fKwefz+ngfHo/kkn7e9+b9efV1J/fM5/PhPoVKpVIJAIAkTfUeAAC4uIgPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACDV5HoP8PuGhobitddei9bW1igUCvUeBwA4B5VKJU6cOBEdHR3R1PTej2184OLjtddei87OznqPAQCch1deeSXmzp37nud84OKjtbU1It4Zvq2trarXLpfL8eSTT8aKFSuiubm5qtfmDHvOYc857DmPXeeo1Z4HBgais7Nz5Of4e/nAxcfwUy1tbW01iY+WlpZoa2vzjV1D9pzDnnPYcx67zlHrPZ/LSya84BQASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU44qPjRs3xnXXXRetra0xa9asWLNmTbz44oujzlm6dGkUCoVRH1/5yleqOjQAMHGNKz527doVXV1dsXfv3ujr64tyuRwrVqyIwcHBUed9+ctfjsOHD498PPTQQ1UdGgCYuMb1xnI7duwYdXvr1q0xa9as2L9/f9x4440jx1taWqK9vb06EwIADeWC3tW2v78/IiKmT58+6vi//du/xY9+9KNob2+P1atXx7333hstLS1nvUapVIpSqTRye2BgICLeede9crl8IeONMXy9al+X0ew5hz3nsOc8dp2jVnsez/UKlUqlcj53MjQ0FJ/73Ofi+PHj8cwzz4wc/6d/+qe44ooroqOjI55//vn467/+67j++uvjv//7v896nQ0bNkRPT8+Y4729ve8aLADAB8upU6fitttui/7+/mhra3vPc887Pv7qr/4qHn/88XjmmWdi7ty573reT3/607jlllvi4MGD8dGPfnTM58/2yEdnZ2ccO3bsfYcfr3K5HH19fXHvvqYoDRWqem3OKDZV4oFFQ7F8+fJobm6u9zgNa/j72Z5ry57z2HWOWu15YGAgZs6ceU7xcV5Pu9x1113x2GOPxe7du98zPCIiFi9eHBHxrvFRLBajWCyOOd7c3Fyzb77SUCFKp8VHrdXya8gZ9pzDnvPYdY5q73k81xpXfFQqlbj77rtj27Zt8fTTT8f8+fPf988cOHAgIiLmzJkznrsCABrUuOKjq6srent749FHH43W1tY4cuRIRERMmzYtLrnkkjh06FD09vbGn/zJn8SMGTPi+eefj6997Wtx4403xoIFC2ryfwAAmFjGFR9btmyJiHd+kdjvevjhh+P222+PKVOmxFNPPRWbNm2KwcHB6OzsjLVr18Y3v/nNqg0MAExs437a5b10dnbGrl27LmggAKCxeW8XACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACDVuOJj48aNcd1110Vra2vMmjUr1qxZEy+++OKoc956663o6uqKGTNmxKWXXhpr166No0ePVnVoAGDiGld87Nq1K7q6umLv3r3R19cX5XI5VqxYEYODgyPnfO1rX4uf/OQn8Z//+Z+xa9eueO211+LP//zPqz44ADAxTR7PyTt27Bh1e+vWrTFr1qzYv39/3HjjjdHf3x///M//HL29vXHzzTdHRMTDDz8cn/jEJ2Lv3r3xR3/0R9WbHACYkC7oNR/9/f0RETF9+vSIiNi/f3+Uy+VYtmzZyDlXXXVVzJs3L/bs2XMhdwUANIhxPfLxu4aGhuKee+6Jz3zmM3H11VdHRMSRI0diypQpcdlll406d/bs2XHkyJGzXqdUKkWpVBq5PTAwEBER5XI5yuXy+Y53VsPXKzZVqnpdRhveb7W/fow2vF97ri17zmPXOWq15/Fc77zjo6urK1544YV45plnzvcSEfHOi1h7enrGHH/yySejpaXlgq79bh5YNFST6zJaX19fvUe4KNhzDnvOY9c5qr3nU6dOnfO55xUfd911Vzz22GOxe/fumDt37sjx9vb2ePvtt+P48eOjHv04evRotLe3n/Va69evj+7u7pHbAwMD0dnZGStWrIi2trbzGe9dlcvl6Ovri3v3NUVpqFDVa3NGsakSDywaiuXLl0dzc3O9x2lYw9/P9lxb9pzHrnPUas/Dz1yci3HFR6VSibvvvju2bdsWTz/9dMyfP3/U56+99tpobm6OnTt3xtq1ayMi4sUXX4xf//rXsWTJkrNes1gsRrFYHHO8ubm5Zt98paFClE6Lj1qr5deQM+w5hz3nsesc1d7zeK41rvjo6uqK3t7eePTRR6O1tXXkdRzTpk2LSy65JKZNmxZ33nlndHd3x/Tp06OtrS3uvvvuWLJkiX/pAgBExDjjY8uWLRERsXTp0lHHH3744bj99tsjIuLv//7vo6mpKdauXRulUilWrlwZ3/ve96oyLAAw8Y37aZf3M3Xq1Ni8eXNs3rz5vIcCABqX93YBAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAg1bjjY/fu3bF69ero6OiIQqEQ27dvH/X522+/PQqFwqiPz372s9WaFwCY4MYdH4ODg7Fw4cLYvHnzu57z2c9+Ng4fPjzy8e///u8XNCQA0Dgmj/cPrFq1KlatWvWe5xSLxWhvbz/voQCAxjXu+DgXTz/9dMyaNSv+4A/+IG6++eZ48MEHY8aMGWc9t1QqRalUGrk9MDAQERHlcjnK5XJV5xq+XrGpUtXrMtrwfqv99WO04f3ac23Zcx67zlGrPY/neoVKpXLeP4kLhUJs27Yt1qxZM3LskUceiZaWlpg/f34cOnQo/vZv/zYuvfTS2LNnT0yaNGnMNTZs2BA9PT1jjvf29kZLS8v5jgYAJDp16lTcdttt0d/fH21tbe95btXj4/f96le/io9+9KPx1FNPxS233DLm82d75KOzszOOHTv2vsOPV7lcjr6+vrh3X1OUhgpVvTZnFJsq8cCioVi+fHk0NzfXe5yGNfz9bM+1Zc957DpHrfY8MDAQM2fOPKf4qMnTLr/rIx/5SMycOTMOHjx41vgoFotRLBbHHG9ubq7ZN19pqBCl0+Kj1mr5NeQMe85hz3nsOke19zyea9X893y8+uqr8eabb8acOXNqfVcAwAQw7kc+Tp48GQcPHhy5/fLLL8eBAwdi+vTpMX369Ojp6Ym1a9dGe3t7HDp0KL7xjW/Exz72sVi5cmVVBwcAJqZxx8e+ffvipptuGrnd3d0dERHr1q2LLVu2xPPPPx//+q//GsePH4+Ojo5YsWJFPPDAA2d9agUAuPiMOz6WLl0a7/Ua1SeeeOKCBgIAGpv3dgEAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUo07Pnbv3h2rV6+Ojo6OKBQKsX379lGfr1Qqcd9998WcOXPikksuiWXLlsVLL71UrXkBgAlu3PExODgYCxcujM2bN5/18w899FD84z/+Y3z/+9+Pn/3sZ/GhD30oVq5cGW+99dYFDwsATHyTx/sHVq1aFatWrTrr5yqVSmzatCm++c1vxuc///mIiPjhD38Ys2fPju3bt8df/MVfXNi0AMCEN+74eC8vv/xyHDlyJJYtWzZybNq0abF48eLYs2fPWeOjVCpFqVQauT0wMBAR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", + "text/plain": [ + "
" + ] }, "metadata": {}, "output_type": "display_data" @@ -947,26 +928,25 @@ }, { "cell_type": "code", - "execution_count": 23, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:15.138376Z", - "end_time": "2023-04-24T21:12:15.275867Z" - } - }, + "execution_count": 52, + "metadata": {}, "outputs": [ { "data": { - "text/plain": "" + "text/plain": [ + "" + ] }, - "execution_count": 23, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" }, { "data": { - "text/plain": "
", - "image/png": "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" + "image/png": "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6OzZs5o+fbrmzp2rG2+8UZMnT5bU2sOxfft2FRUVWc+ZP3++rrnmGpWUlFjHnn76ad1555364IMPrOfdfvvt2rBhg3XO7NmzNX36dP3whz9MzDcHIG7oGQEQMzfccIM++OAD/eIXv9DChQtVWVmp6dOna/PmzRGf88Ybb+j++++3elays7O1YsUK1dTU6MyZM9Z5hYWFYc8rLCykZwQYIJjACiCmMjMzde211+raa6/V3Xffrdtuu0333nuvbr755i7Pb2xs1H333acvfvGLXX4tAAMfPSMA4mrixIny+/2SpPT0dAWDwbDHp0+frqNHj2rcuHHn3dzu9reoffv2hT1v3759uuyyy+L/DQCIO3pGAMTExx9/rKVLl+qrX/2qJk+eLJ/Pp6qqKn3ve9/TkiVLJLWuqKmoqNDVV18tr9eroUOH6p577tEXvvAFjR49Wl/60pfkdrv1xhtv6MiRI/r3f/936+tv27ZNM2fO1Gc/+1k988wz2r9/v5544gm7vl0AMcQEVgAxEQgE9G//9m/61a9+pT/96U9qbm5WQUGBli5dqrvuukuDBg3SL3/5S61du1Z/+ctfNHLkSGtp786dO3X//ffr0KFDSk9P14QJE3TbbbdpxYoVklonsD722GMqKyvTrl27NHz4cH33u9/VP/zDP9j4HQOIFcIIAMfrahUOgIGDOSMAAMBWhBEAAGArJrACcDxGk4GBjZ4RAABgK8IIAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBW/x98HZPml6qnJAAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] }, "metadata": {}, "output_type": "display_data" @@ -988,13 +968,8 @@ }, { "cell_type": "code", - "execution_count": 24, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:15.246086Z", - "end_time": "2023-04-24T21:12:15.276880Z" - } - }, + "execution_count": 53, + "metadata": {}, "outputs": [], "source": [ "# save the model data (stored in the pandas gini object) to CSV\n", @@ -1022,13 +997,8 @@ }, { "cell_type": "code", - "execution_count": 25, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:15.256575Z", - "end_time": "2023-04-24T21:12:15.276880Z" - } - }, + "execution_count": 54, + "metadata": {}, "outputs": [], "source": [ "def compute_gini(model):\n", @@ -1132,19 +1102,14 @@ }, { "cell_type": "code", - "execution_count": 26, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:15.262311Z", - "end_time": "2023-04-24T21:12:35.235655Z" - } - }, + "execution_count": 55, + "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 245/245 [00:19<00:00, 12.27it/s]\n" + "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 245/245 [00:48<00:00, 5.10it/s]\n" ] } ], @@ -1171,13 +1136,8 @@ }, { "cell_type": "code", - "execution_count": 27, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:35.235655Z", - "end_time": "2023-04-24T21:12:43.579218Z" - } - }, + "execution_count": 56, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1205,26 +1165,25 @@ }, { "cell_type": "code", - "execution_count": 28, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:43.580227Z", - "end_time": "2023-04-24T21:12:43.703199Z" - } - }, + "execution_count": 57, + "metadata": {}, "outputs": [ { "data": { - "text/plain": "" + "text/plain": [ + "" + ] }, - "execution_count": 28, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" }, { "data": { - "text/plain": "
", - "image/png": "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QkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhmIwQkRERIZiMEJERESGYjBCREREhhpQMLJq1SpkZWUhLi4OhYWFqK2tlfq7jRs3wmQyYc6cOQN5WCIiIgpDmoORTZs2obS0FOXl5airq0NeXh6mTZuG48ePq/7doUOH8KMf/QhTpkwZ8M4SERFR+NEcjDzzzDNYuHAhSkpKkJOTgzVr1iA+Ph7r16/3+Tfd3d249957sXz5cowZM2ZQO0xEREThRVMw0tXVhd27d6O4uPjyHZjNKC4uRk1Njc+/e+KJJ5CWlob7779f6nE6OzvhcDg8foiIiCg8aQpGTp48ie7ubqSnp3vcnp6eDrvd7vVvdu7ciXXr1mHt2rXSj1NRUQGr1er+yczM1LKbREREFEL8Opumvb0d9913H9auXYvU1FTpvysrK0NbW5v75+jRo37cSyIiIjJStJaNU1NTERUVhZaWFo/bW1paYLPZ+m1/8OBBHDp0CDNnznTf5nQ6ex44Ohr79+/H2LFj+/1dbGwsYmNjtewaERERhShNmRGLxYKCggJUV1e7b3M6naiurkZRUVG/7ceNG4e9e/eivr7e/TNr1iz8y7/8C+rr6zn8QkRERNoyIwBQWlqKBQsWYOLEiZg0aRJWrlyJjo4OlJSUAADmz5+PkSNHoqKiAnFxccjNzfX4+6SkJADodzsRERFFJs3ByNy5c3HixAksXboUdrsd+fn5qKqqche1HjlyBGYzG7sSERGRHJOiKIrROyHicDhgtVrR1taGxMREo3eHiIiIJMiev5nCICIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRghIiIiQzEYISIiIkMxGCEiIiJDMRgh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GQsWLMBbb71l9G6FraNHj+KRRx7Btm3bEBcXZ/TuRJRvfOMb7n+PHz8ehYWFGD16NP70pz9hyJAhAduPsBqmSU1NRVRUVL9K35aWFthsNoP2Kry5jqvaMbfZbDh+/LjH7y9duoTW1la+LhIeeughvP7663jjjTdw5ZVXum+32Wzo6urCmTNnPLbve+y9vTau35F3FosFV111FQoKClBRUYG8vDz8+te/5jH3k927d+P48eOYMGECoqOjER0djbfeegu/+c1vEB0djfT0dB73AElKSsI111yDAwcOBPT9HlbBiMViQUFBAaqrq923OZ1OVFdXo6ioyMA9C1/Z2dmw2Wwex9zhcOC9995zH/OioiKcOXMGu3fvdm+zfft2OJ1OFBYWBnyfQ4WiKHjooYfwyiuvYPv27cjOzvb4fUFBAWJiYjyO/f79+3HkyBGPY793716PYHDbtm1ITExETk5OYJ5IGHA6nejs7OQx95M777wTe/fuRX19vftn4sSJuPfee93/5nEPjLNnz+LgwYPIyMgI7Pt9QOW3QWzjxo1KbGys8vvf/15pbGxUvv/97ytJSUkelb6kTXt7u/Lhhx8qH374oQJAeeaZZ5QPP/xQOXz4sKIoPVN7k5KSlC1btih79uxRZs+e7XVq74033qi89957ys6dO5Wrr76aU3sFFi1apFitVuXNN9/0mHZ37tw59zYPPPCAMmrUKGX79u3KBx98oBQVFSlFRUXu37um3X39619X6uvrlaqqKuWKK67gdEcVS5YsUd566y2lqalJ2bNnj7JkyRLFZDIp//jHPxRF4TEPlN6zaRSFx91fHn30UeXNN99UmpqalHfeeUcpLi5WUlNTlePHjyuKErjjHnbBiKIoyrPPPquMGjVKsVgsyqRJk5Rdu3YZvUsh7Y033lAA9PtZsGCBoig903sff/xxJT09XYmNjVXuvPNOZf/+/R73cerUKeWee+5Rhg0bpiQmJiolJSVKe3u7Ac8mdHg75gCUF154wb3N+fPnlQcffFBJTk5W4uPjlW9+85tKc3Ozx/0cOnRI+cY3vqEMGTJESU1NVR599FHl4sWLAX42oeO73/2uMnr0aMVisShXXHGFcuedd7oDEUXhMQ+UvsEIj7t/zJ07V8nIyFAsFosycuRIZe7cucqBAwfcvw/UcTcpiqIMKqdDRERENAhhVTNCREREoYfBCBERERmKwQgREREZisEIERERGYrBCBERERmKwQgREREZisEIERERGYrBCBERERmKwQgREREZisEIERERGYrBCBERERmKwQgREREZ6v8BZVeubZ+ZWrsAAAAASUVORK5CYII=" + "image/png": "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Z4SIiMh/GIx4wTkjRERE/sNgxIv0xEjEWi3wNW3EgM5KrOmJkf5sFhERUVBiMOJFiNGAgjlJANAtIHH9v2BOEteoISIi0gCDER9mTYrFuoenwWb1HIqxWS1Y9/A0rk1DRESkES6UJ2PWpFjMTLJx1V4iIqI+xGBEQYjRgMxxI/RuBhERUdDiMA0RERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemqR8HI2rVrkZCQAIvFgoyMDJSVlQk9b8uWLTAYDLjvvvt68rZEREQUhFQHI1u3bkVeXh4KCgpw4MABJCcnIzs7G/X19bLPO3nyJP7t3/4Nt956a48bS0RERMFHdTCyevVqLFiwALm5uUhKSsL69esRFhaGTZs2+XxOR0cHHnroIaxYsQJjx47tVYOJiIgouKgKRtrb21FeXo6srKyrL2A0IisrC6WlpT6f99xzzyE6OhqPPvqo0Pu0tbWhqanJ40ZERETBSVUw0tDQgI6ODsTExHjcHxMTA7vd7vU5e/fuxcaNG7Fhwwbh9yksLITVanXf4uPj1TSTiIiIAkifZtM0NzfjkUcewYYNGxAVFSX8vPz8fDgcDvetpqamD1tJREREehqkZuOoqCiEhISgrq7O4/66ujrYbLZu2584cQInT57EnDlz3Pc5nc7ONx40CMeOHcO4ceO6Pc9sNsNsNqtpGhEREQUoVT0jJpMJqampKCkpcd/ndDpRUlKCzMzMbttPnDgRhw8fxqFDh9y3e+65B//0T/+EQ4cOcfiFiIiI1PWMAEBeXh7mz5+PtLQ0pKeno6ioCC0tLcjNzQUAzJs3D3FxcSgsLITFYsGkSZM8nh8REQEA3e4nIiKigUl1MJKTk4MzZ85g+fLlsNvtSElJQXFxsXtSa3V1NYxGFnYlIiIiMQZJkiS9G6GkqakJVqsVDocD4eHhejeHiIiIBIiev9mFQURERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJE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0FpPtiMiIqLeG1DBSHpiJGKtFvhK4DWgM6smPdH3ujlERESkrQEVjIQYDSiYkwQA3QIS1/8L5iSx3ggREZEfDahgBABmTYrFuoenwWb1HIqxWS1M6yUiItKB6qJnwWDWpFjMTLKxAisREVE/MCCDEaBzyCZz3Ai9m0FERDTgDbhhGiIiIupfGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka56FIysXbsWCQkJsFgsyMjIQFlZmc9tv/jiC9x///1ISEiAwWBAUVFRT9tKREREQUh1MLJ161bk5eWhoKAABw4cQHJyMrKzs1FfX+91+4sXL2Ls2LFYtWoVbDZbrxtMREREwUV1MLJ69WosWLAAubm5SEpKwvr16xEWFoZNmzZ53X769Ol44YUX8MADD8BsNve6wURERBRcVAUj7e3tKC8vR1ZW1tUXMBqRlZWF0tJSzRrV1taGpqYmjxsREREFJ1XBSENDAzo6OhATE+Nxf0xMDOx2u2aNKiwshNVqdd/i4+M1e20iIiLqX/plNk1+fj4cDof7VlNTo3eTiIiIqI8MUrNxVFQUQkJCUFdX53F/XV2dppNTzWYz55cQERENEKp6RkwmE1JTU1FSUuK+z+l0oqSkBJmZmZo3joiIiIKfqp4RAMjLy8P8+fORlpaG9PR0FBUVoaWlBbm5uQCAefPmIS4uDoWFhQA6J71WVla6/3369GkcOnQIQ4cOxfjx4zX8KERERBSIVAcjOTk5OHPmDJYvXw673Y6UlBQUFxe7J7VWV1fDaLza4fLtt99i6tSp7v+/+OKLePHFF3H77bdjz549vf8EREREFNAMkiRJejdCSVNTE6xWKxwOB8LDw/VuDhEREQkQPX/3y2waIiIiGjgYjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuBundAL10OCWUVTWivrkV0cMsSE+MRIjRoHeziIiIBpwe9YysXbsWCQkJsFgsyMjIQFlZmez2f/7znzFx4kRYLBZMnjwZO3fu7FFjtVJcUYtbnt+NuRv2YcmWQ5i7YR9ueX43iitqdW0XERHRQKQ6GNm6dSvy8vJQUFCAAwcOIDk5GdnZ2aivr/e6/ccff4y5c+fi0UcfxcGDB3HffffhvvvuQ0VFRa8b3xPFFbVYuPkAah2tHvfbHa1YuPkAAxIiIiI/M0iSJKl5QkZGBqZPn441a9YAAJxOJ+Lj4/HEE09g6dKl3bbPyclBS0sL3nnnHfd9N910E1JSUrB+/Xqh92xqaoLVaoXD4UB4eLia5nrocEq45fnd3QIRFwMAm9WCvU/dwSEbIiKiXhI9f6vqGWlvb0d5eTmysrKuvoDRiKysLJSWlnp9Tmlpqcf2AJCdne1zewBoa2tDU1OTx00LZVWNPgMRAJAA1DpaUVbVqMn7ERERkTJVwUhDQwM6OjoQExPjcX9MTAzsdrvX59jtdlXbA0BhYSGsVqv7Fh8fr6aZPtU3+w5EerIdERER9V6/TO3Nz8+Hw+Fw32pqajR53ehhFk23IyIiot5TldobFRWFkJAQ1NXVedxfV1cHm83m9Tk2m03V9gBgNpthNpvVNE1IemIkYq0W2B2t8DZRxjVnJD0xUvP3JiIiIu9U9YyYTCakpqaipKTEfZ/T6URJSQkyMzO9PiczM9NjewDYtWuXz+37UojRgII5SQA6A4+uXP8vmJPEyatERER+pHqYJi8vDxs2bMCf/vQnHDlyBAsXLkRLSwtyc3MBAPPmzUN+fr57+yVLlqC4uBj/+Z//iaNHj+LZZ5/Fp59+isWLF2v3KVSYNSkW6x6eBpvVcyjGZrVg3cPTMGtSrC7tIiIiGqhUV2DNycnBmTNnsHz5ctjtdqSkpKC4uNg9SbW6uhpG49UYZ8aMGXjttdfwzDPP4Omnn8aECRPw5ptvYtKkSdp9CpVmTYrFzCQbK7ASERH1A6rrjOhBqzojRERE5D99UmeEiIiISGsMRoiIiEhXDEaIiIhIVwxGiIiISFcMRoiIiEhXDEaIiIhIVwxGiIiISFcMRoiIiEhXDEaIiIhIV6rLwevBVSS2qalJ55YQERGRKNd5W6nYe0AEI83NzQCA+Ph4nVtCREREajU3N8Nqtfp8PCDWpnE6nfj2228xbNgwGAxii9k1NTUhPj4eNTU1XM/Gj7jf9cH9rg/ud31wv+ujJ/tdkiQ0Nzdj1KhRHovoXisgekaMRiOuu+66Hj03PDycX1YdcL/rg/tdH9zv+uB+14fa/S7XI+LCCaxERESkKwYjREREpKugDUbMZjMKCgpgNpv1bsqAwv2uD+53fXC/64P7XR99ud8DYgIrERERBa+g7RkhIiKiwMBghIiIiHTFYISIiIh0xWCEiIiIdBWUwcjatWuRkJAAi8WCjIwMlJWV6d2kgPbBBx9gzpw5GDVqFAwGA958802PxyVJwvLlyxEbG4vBgwcjKysLX331lcc2jY2NeOihhxAeHo6IiAg8+uijuHDhgh8/ReApLCzE9OnTMWzYMERHR+O+++7DsWPHPLZpbW3FokWLMGLECAwdOhT3338/6urqPLaprq7G7NmzERYWhujoaPz7v/87rly54s+PElDWrVuHKVOmuAs7ZWZm4m9/+5v7ce7zvrdq1SoYDAb8/Oc/d9/H/d43nn32WRgMBo/bxIkT3Y/7bb9LQWbLli2SyWSSNm3aJH3xxRfSggULpIiICKmurk7vpgWsnTt3Sr/85S+lbdu2SQCkN954w+PxVatWSVarVXrzzTelzz77TLrnnnukxMRE6dKlS+5tZs2aJSUnJ0v79u2TPvzwQ2n8+PHS3Llz/fxJAkt2drb08ssvSxUVFdKhQ4eku+++Wxo9erR04cIF9zaPP/64FB8fL5WUlEiffvqpdNNNN0kzZsxwP37lyhVp0qRJUlZWlnTw4EFp586dUlRUlJSfn6/HRwoIb731lrRjxw7pyy+/lI4dOyY9/fTTUmhoqFRRUSFJEvd5XysrK5MSEhKkKVOmSEuWLHHfz/3eNwoKCqTvfe97Um1trft25swZ9+P+2u9BF4ykp6dLixYtcv+/o6NDGjVqlFRYWKhjq4LHtcGI0+mUbDab9MILL7jvO3/+vGQ2m6XXX39dkiRJqqyslABI+/fvd2/zt7/9TTIYDNLp06f91vZAV19fLwGQ3n//fUmSOvdzaGio9Oc//9m9zZEjRyQAUmlpqSRJnYGk0WiU7Ha7e5t169ZJ4eHhUltbm38/QAAbPny49Mc//pH7vI81NzdLEyZMkHbt2iXdfvvt7mCE+73vFBQUSMnJyV4f8+d+D6phmvb2dpSXlyMrK8t9n9FoRFZWFkpLS3VsWfCqqqqC3W732OdWqxUZGRnufV5aWoqIiAikpaW5t8nKyoLRaMQnn3zi9zYHKofDAQCIjIwEAJSXl+Py5cse+37ixIkYPXq0x76fPHkyYmJi3NtkZ2ejqakJX3zxhR9bH5g6OjqwZcsWtLS0IDMzk/u8jy1atAizZ8/22L8Av+t97auvvsKoUaMwduxYPPTQQ6iurgbg3/0eEAvliWpoaEBHR4fHTgGAmJgYHD16VKdWBTe73Q4AXve56zG73Y7o6GiPxwcNGoTIyEj3NiTP6XTi5z//OW6++WZMmjQJQOd+NZlMiIiI8Nj22n3v7W/jeoy8O3z4MDIzM9Ha2oqhQ4fijTfeQFJSEg4dOsR93ke2bNmCAwcOYP/+/d0e43e972RkZOCVV17BDTfcgNraWqxYsQK33norKioq/LrfgyoYIQpWixYtQkVFBfbu3at3UwaEG264AYcOHYLD4cBf/vIXzJ8/H++//77ezQpaNTU1WLJkCXbt2gWLxaJ3cwaUH/zgB+5/T5kyBRkZGRgzZgz+7//+D4MHD/ZbO4JqmCYqKgohISHdZvrW1dXBZrPp1Krg5tqvcvvcZrOhvr7e4/ErV66gsbGRfxcBixcvxjvvvIP33nsP1113nft+m82G9vZ2nD9/3mP7a/e9t7+N6zHyzmQyYfz48UhNTUVhYSGSk5Pxu9/9jvu8j5SXl6O+vh7Tpk3DoEGDMGjQILz//vv47//+bwwaNAgxMTHc734SERGB66+/HsePH/fr9z2oghGTyYTU1FSUlJS473M6nSgpKUFmZqaOLQteiYmJsNlsHvu8qakJn3zyiXufZ2Zm4vz58ygvL3dvs3v3bjidTmRkZPi9zYFCkiQsXrwYb7zxBnbv3o3ExESPx1NTUxEaGuqx748dO4bq6mqPfX/48GGPYHDXrl0IDw9HUlKSfz5IEHA6nWhra+M+7yN33nknDh8+jEOHDrlvaWlpeOihh9z/5n73jwsXLuDEiROIjY317/e9R9Nv+7EtW7ZIZrNZeuWVV6TKykrpsccekyIiIjxm+pI6zc3N0sGDB6WDBw9KAKTVq1dLBw8elE6dOiVJUmdqb0REhLR9+3bp888/l+69916vqb1Tp06VPvnkE2nv3r3ShAkTmNqrYOHChZLVapX27NnjkXZ38eJF9zaPP/64NHr0aGn37t3Sp59+KmVmZkqZmZnux11pd3fddZd06NAhqbi4WBo5ciTTHWUsXbpUev/996Wqqirp888/l5YuXSoZDAbpH//4hyRJ3Of+0jWbRpK43/vKk08+Ke3Zs0eqqqqSPvroIykrK0uKioqS6uvrJUny334PumBEkiTp97//vTR69GjJZDJJ6enp0r59+/RuUkB77733JADdbvPnz5ckqTO9d9myZVJMTIxkNpulO++8Uzp27JjHa5w9e1aaO3euNHToUCk8PFzKzc2Vmpubdfg0gcPbPgcgvfzyy+5tLl26JP3sZz+Thg8fLoWFhUk//OEPpdraWo/XOXnypPSDH/xAGjx4sBQVFSU9+eST0uXLl/38aQLHT37yE2nMmDGSyWSSRo4cKd15553uQESSuM/95dpghPu9b+Tk5EixsbGSyWSS4uLipJycHOn48ePux/213w2SJEm96tMhIiIi6oWgmjNCREREgYfBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHp6v8Duou0AvfZ0jgAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] }, "metadata": {}, "output_type": "display_data" @@ -1249,13 +1208,8 @@ }, { "cell_type": "code", - "execution_count": 29, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:43.702198Z", - "end_time": "2023-04-24T21:12:43.722421Z" - } - }, + "execution_count": 58, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1276,7 +1230,7 @@ " 1 1 1\n", " ... ... ...\n", " 99 8 1\n", - " 99 9 1\n", + " 99 9 0\n", " 100 0 0\n", " 100 1 1\n", " 100 2 1\n", @@ -1284,9 +1238,9 @@ " 100 4 1\n", " 100 5 2\n", " 100 6 1\n", - " 100 7 1\n", + " 100 7 2\n", " 100 8 1\n", - " 100 9 1\n" + " 100 9 0\n" ] } ], @@ -1313,13 +1267,8 @@ }, { "cell_type": "code", - "execution_count": 30, - "metadata": { - "ExecuteTime": { - "start_time": "2023-04-24T21:12:43.722421Z", - "end_time": "2023-04-24T21:12:43.778860Z" - } - }, + "execution_count": 59, + "metadata": {}, "outputs": [ { "name": "stdout", @@ -1339,18 +1288,18 @@ " 10 0.00\n", " 11 0.00\n", " ... ...\n", - " 89 0.32\n", - " 90 0.32\n", - " 91 0.32\n", - " 92 0.32\n", - " 93 0.32\n", - " 94 0.32\n", - " 95 0.32\n", - " 96 0.32\n", - " 97 0.32\n", - " 98 0.32\n", - " 99 0.32\n", - " 100 0.32\n" + " 89 0.18\n", + " 90 0.18\n", + " 91 0.18\n", + " 92 0.18\n", + " 93 0.18\n", + " 94 0.18\n", + " 95 0.18\n", + " 96 0.18\n", + " 97 0.18\n", + " 98 0.18\n", + " 99 0.18\n", + " 100 0.18\n" ] } ], @@ -1374,13 +1323,47 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "### References\n", - "----------\n", + "`virtual environment`: http://docs.python-guide.org/en/latest/dev/virtualenvs/\n", "\n", "[Comer2014] Comer, Kenneth W. “Who Goes First? An Examination of the Impact of Activation on Outcome Behavior in AgentBased Models.” George Mason University, 2014. http://mars.gmu.edu/bitstream/handle/1920/9070/Comer_gmu_0883E_10539.pdf\n", "\n", "[Dragulescu2002] Drăgulescu, Adrian A., and Victor M. Yakovenko. “Statistical Mechanics of Money, Income, and Wealth: A Short Survey.” arXiv Preprint Cond-mat/0211175, 2002. http://arxiv.org/abs/cond-mat/0211175." ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/docs/tutorials/intro_tutorial.rst b/docs/tutorials/intro_tutorial.rst index 3b91383dc7d..912305f42f2 100644 --- a/docs/tutorials/intro_tutorial.rst +++ b/docs/tutorials/intro_tutorial.rst @@ -6,117 +6,88 @@ Tutorial Description `Mesa `__ is a Python framework for `agent-based -modeling `__. This -tutorial will assist you in getting started. Working through the -tutorial will help you discover the core features of Mesa. Through the -tutorial, you are walked through creating a starter-level model. -Functionality is added progressively as the process unfolds. Should -anyone find any errors, bugs, have a suggestion, or just are looking for -clarification, `let us +modeling `__. This tutorial will assist you in getting started. +Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked +through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone +find any errors, bugs, have a suggest or just are looking for clarification, `please let us know `__! -The premise of this tutorial is to create a starter-level model -representing agents exchanging money. This exchange of money affects -wealth. Next, *space* is added to allow agents to move based on the -change in wealth as time progresses. +The premise of this tutorial is to create a a starter-level model representing agents exchanging +money. This exchange of money affects wealth. Next, *space* is added to allow agents to move +based on the change in wealth as time progresses. -Two of Mesa’s analytic tools: the *data collector* and *batch runner* -will be used to examine this movement. After that an *interactive -visualization* is added which allows model viewing as it runs. +Two of Mesa's analytic tools: the *data collector* and *batch runner* will be used to examine this movement. +After that an *interactive visualization* is added which allows model viewing as it runs. -Finally, the creation of a custom visualization module in JavaScript is -explored. +Finally, the creation of a custom visualization module in JavaScript is explored. -Model Description ------------------ -This is a starter-level simulated agent-based economy. In an agent-based -economy, the behavior of an individual economic agent, such as a -consumer or producer, is studied in a market environment. This model is -drawn from the field econophysics, specifically a paper prepared by -Drăgulescu et al. for additional information on the modeling assumptions -used in this model. [Drăgulescu, 2002]. +Model Description +------------------------ -The assumption that govern this model are: +The tutorial model is a very simple simulated agent-based economy, drawn +from econophysics and presenting a statistical mechanics approach to +wealth distribution [Dragulescu2002]. The rules of our tutorial model: 1. There are some number of agents. 2. All agents begin with 1 unit of money. -3. At every step of the model, an agent with money gives 1 unit of - money. +3. At every step of the model, an agent gives 1 unit of money (if they + have it) to some other agent. + +Despite its simplicity, this model yields results that are often +unexpected to those not familiar with it. For our purposes, it also +easily demonstrates Mesa's core features. -Even as a starter-level model the yielded results are both interesting -and unexpected to individuals unfamiliar with it the specific topic. As -such, this model is a good starting point to examine Mesa’s core -features. +Let's get started. Installation ~~~~~~~~~~~~ -Create and activate a `virtual -environment `__. -*Python version 3.8 or higher is required*. +To start, install Mesa. We recommend doing this in a `virtual +environment `__, +but make sure your environment is set up with Python 3. Mesa requires +Python3 and does not work in Python 2 environments. -Install Mesa: +To install Mesa, simply: .. code:: bash - pip install mesa + pip install mesa -Install Jupyter Notebook (optional): +When you do that, it will install Mesa itself, as well as any +dependencies that aren't in your setup yet. Additional dependencies +required by this tutorial can be found in the +**examples/boltzmann_wealth_model/requirements.txt** file, which can be +installed directly form the github repository by running: .. code:: bash - pip install jupyter - -Install matplotlib: - -.. code:: bash - - pip install matplotlib - -Building the Sample Model -------------------------- - -After Mesa is installed a model can be built. A jupyter notebook is -recommended for this tutorial, this allows for small segments of codes -to be examined one at a time. As an option this can be created using -python script files. + pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt -**Good Practice:** Place a model in its own folder/directory. This is -not specifically required for the starter_model, but as other models -become more complicated and expand multiple python scripts, -documentation, discussions and notebooks may be added. +| This will install the dependencies listed in the requirements.txt file + which are: +| - jupyter (Ipython interactive notebook) +| - matplotlib (Python's visualization library) +| - mesa (this ABM library – if not installed) +| - numpy (Python's numerical python library) -Create New Folder/Directory -~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Building a sample model +----------------------- -- Using operating system commands create a new folder/directory named - ‘starter_model’. +Once Mesa is installed, you can start building our model. You can write +models in two different ways: -- Change to the new folder/directory. +1. Write the code in its own file with your favorite text editor, or +2. Write the model interactively in `Jupyter + Notebook `__ cells. -Creating Model With Jupyter Notebook -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +Either way, it's good practice to put your model in its own folder – +especially if the project will end up consisting of multiple files (for +example, Python files for the model and the visualization, a Notebook +for analysis, and a Readme with some documentation and discussion). -Write the model interactively in `Jupyter -Notebook `__ cells. - -- Start Jupyter Notebook: - -.. code:: bash - - jupyter notebook - -- Create a new Notebook named ``money_model.ipynb`` (or whatever you - want to call it). - -Creating Model With Script File (IDE, Text Editor, Colab, etc.) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- Create a new file called ``money_model.py`` (or whatever you want to - call it). - -*Code will be added as the tutorial progresses.* +Begin by creating a folder, and either launch a Notebook or create a new +Python source file. We will use the name ``money_model.py`` here. Setting up the model ~~~~~~~~~~~~~~~~~~~~ @@ -127,7 +98,7 @@ model-level attributes, manages the agents, and generally handles the global level of our model. Each instantiation of the model class will be a specific model run. Each model will contain multiple agents, all of which are instantiations of the agent class. Both the model and agent -classes are child classes of Mesa’s generic ``Model`` and ``Agent`` +classes are child classes of Mesa's generic ``Model`` and ``Agent`` classes. This is seen in the code with ``class MoneyModel(mesa.Model)`` or ``class MoneyAgent(mesa.Agent)``. If you want you can specifically the class being imported by looking at the @@ -187,13 +158,13 @@ model uses, and see whether it changes the model behavior. This may not seem important, but scheduling patterns can have an impact on your results [Comer2014]. -For now, let’s use one of the simplest ones: ``RandomActivation``\ \*, +For now, let's use one of the simplest ones: ``RandomActivation``\ \*, which activates all the agents once per step, in random order. Every agent is expected to have a ``step`` method. The step method is the action the agent takes when it is activated by the model schedule. We add an agent to the schedule using the ``add`` method; when we call the -schedule’s ``step`` method, the model shuffles the order of the agents, -then activates and executes each agent’s ``step`` method. +schedule's ``step`` method, the model shuffles the order of the agents, +then activates and executes each agent's ``step`` method. \*Unlike ``mesa.model`` or ``mesa.agent``, ``mesa.time`` has multiple classes (e.g. ``RandomActivation``, ``StagedActivation`` etc). To ensure @@ -238,8 +209,8 @@ this: """Advance the model by one step.""" self.schedule.step() -At this point, we have a model which runs – it just doesn’t do anything. -You can see for yourself with a few easy lines. If you’ve been working +At this point, we have a model which runs – it just doesn't do anything. +You can see for yourself with a few easy lines. If you've been working in an interactive session, you can create a model object directly. Otherwise, you need to open an interactive session in the same directory as your source code file, and import the classes. For example, if your @@ -259,16 +230,16 @@ Then create the model object, and run it for one step: .. parsed-literal:: - Hi, I am agent 5. - Hi, I am agent 0. - Hi, I am agent 1. - Hi, I am agent 7. - Hi, I am agent 8. + Hi, I am agent 6. Hi, I am agent 2. + Hi, I am agent 1. + Hi, I am agent 0. Hi, I am agent 4. - Hi, I am agent 9. + Hi, I am agent 5. Hi, I am agent 3. - Hi, I am agent 6. + Hi, I am agent 9. + Hi, I am agent 8. + Hi, I am agent 7. Exercise @@ -285,12 +256,12 @@ Now we just need to have the agents do what we intend for them to do: check their wealth, and if they have the money, give one unit of it away to another random agent. To allow the agent to choose another agent at random, we use the ``model.random`` random-number generator. This works -just like Python’s ``random`` module, but with a fixed seed set when the +just like Python's ``random`` module, but with a fixed seed set when the model is instantiated, that can be used to replicate a specific model run later. To pick an agent at random, we need a list of all agents. Notice that -there isn’t such a list explicitly in the model. The scheduler, however, +there isn't such a list explicitly in the model. The scheduler, however, does have an internal list of all the agents it is scheduled to activate. @@ -315,21 +286,21 @@ With that in mind, we rewrite the agent ``step`` method, like this: Running your first model ~~~~~~~~~~~~~~~~~~~~~~~~ -With that last piece in hand, it’s time for the first rudimentary run of +With that last piece in hand, it's time for the first rudimentary run of the model. -If you’ve written the code in its own file (``money_model.py`` or a +If you've written the code in its own file (``money_model.py`` or a different name), launch an interpreter in the same directory as the file (either the plain Python command-line interpreter, or the IPython interpreter), or launch a Jupyter Notebook there. Then import the classes you created. (If you wrote the code in a Notebook, obviously -this step isn’t necessary). +this step isn't necessary). .. code:: python from money_model import * -Now let’s create a model with 10 agents, and run it for 10 steps. +Now let's create a model with 10 agents, and run it for 10 steps. .. code:: ipython3 @@ -338,11 +309,11 @@ Now let’s create a model with 10 agents, and run it for 10 steps. model.step() Next, we need to get some data out of the model. Specifically, we want -to see the distribution of the agent’s wealth. We can get the wealth +to see the distribution of the agent's wealth. We can get the wealth values with list comprehension, and then use matplotlib (or another graphics library) to visualize the data in a histogram. -If you are running from a text editor or IDE, you’ll also need to add +If you are running from a text editor or IDE, you'll also need to add this line, to make the graph appear. .. code:: python @@ -365,17 +336,17 @@ this line, to make the graph appear. .. parsed-literal:: - (array([6., 0., 1., 0., 0., 1., 0., 1., 0., 1.]), - array([0. , 0.4, 0.8, 1.2, 1.6, 2. , 2.4, 2.8, 3.2, 3.6, 4. ]), + (array([2., 0., 0., 0., 0., 6., 0., 0., 0., 2.]), + array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 1.2, 1.4, 1.6, 1.8, 2. ]), ) -.. image:: intro_tutorial_files%5Cintro_tutorial_19_1.png +.. image:: intro_tutorial_files/output_19_1.png -You’ll should see something like the distribution above. Yours will +You'll should see something like the distribution above. Yours will almost certainly look at least slightly different, since each run of the model is random, after all. @@ -404,21 +375,21 @@ can do this with a nested for loop: .. parsed-literal:: - (array([431., 303., 153., 76., 26., 9., 1., 1.]), - array([0., 1., 2., 3., 4., 5., 6., 7., 8.]), - ) + (array([433., 304., 150., 71., 29., 13.]), + array([0, 1, 2, 3, 4, 5, 6]), + ) -.. image:: intro_tutorial_files%5Cintro_tutorial_22_1.png +.. image:: intro_tutorial_files/output_22_1.png This runs 100 instantiations of the model, and runs each for 10 steps. (Notice that we set the histogram bins to be integers, since agents can only have whole numbers of wealth). This distribution looks a lot smoother. By running the model 100 times, we smooth out some of the -‘noise’ of randomness, and get to the model’s overall expected behavior. +‘noise'of randomness, and get to the model's overall expected behavior. This outcome might be surprising. Despite the fact that all agents, on average, give and receive one unit of money every step, the model @@ -440,9 +411,9 @@ those on the left edge, and the top to the bottom. This prevents some cells having fewer neighbors than others, or agents being able to go off the edge of the environment. -Let’s add a simple spatial element to our model by putting our agents on +Let's add a simple spatial element to our model by putting our agents on a grid and make them walk around at random. Instead of giving their unit -of money to any random agent, they’ll give it to an agent on the same +of money to any random agent, they'll give it to an agent on the same cell. Mesa has two main types of grids: ``SingleGrid`` and ``MultiGrid``\ \*. @@ -457,9 +428,9 @@ Similar to ``mesa.time`` context is retained with `mesa.space `__ We instantiate a grid with width and height parameters, and a boolean as -to whether the grid is toroidal. Let’s make width and height model +to whether the grid is toroidal. Let's make width and height model parameters, in addition to the number of agents, and have the grid -always be toroidal. We can place agents on a grid with the grid’s +always be toroidal. We can place agents on a grid with the grid's ``place_agent`` method, which takes an agent and an (x, y) tuple of the coordinates to place the agent. @@ -483,16 +454,16 @@ coordinates to place the agent. y = self.random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) -Under the hood, each agent’s position is stored in two ways: the agent +Under the hood, each agent's position is stored in two ways: the agent is contained in the grid in the cell it is currently in, and the agent has a ``pos`` variable with an (x, y) coordinate tuple. The ``place_agent`` method adds the coordinate to the agent automatically. -Now we need to add to the agents’ behaviors, letting them move around +Now we need to add to the agents'behaviors, letting them move around and only give money to other agents in the same cell. -First let’s handle movement, and have the agents move to a neighboring -cell. The grid object provides a ``move_agent`` method, which like you’d +First let's handle movement, and have the agents move to a neighboring +cell. The grid object provides a ``move_agent`` method, which like you'd imagine, moves an agent to a given cell. That still leaves us to get the possible neighboring cells to move to. There are a couple ways to do this. One is to use the current coordinates, and loop over all @@ -506,7 +477,7 @@ coordinates +/- 1 away from it. For example: for dy in [-1, 0, 1]: neighbors.append((x+dx, y+dy)) -But there’s an even simpler way, using the grid’s built-in +But there's an even simpler way, using the grid's built-in ``get_neighborhood`` method, which returns all the neighbors of a given cell. This method can get two types of cell neighborhoods: `Moore `__ (includes @@ -515,7 +486,7 @@ Neumann `__\ (only up/down/left/right). It also needs an argument as to whether to include the center cell itself as one of the neighbors. -With that in mind, the agent’s ``move`` method looks like this: +With that in mind, the agent's ``move`` method looks like this: .. code:: python @@ -531,7 +502,7 @@ With that in mind, the agent’s ``move`` method looks like this: Next, we need to get all the other agents present in a cell, and give one of them some money. We can get the contents of one or more cells -using the grid’s ``get_cell_list_contents`` method, or by accessing a +using the grid's ``get_cell_list_contents`` method, or by accessing a cell directly. The method accepts a list of cell coordinate tuples, or a single tuple if we only care about one cell. @@ -546,7 +517,7 @@ single tuple if we only care about one cell. other.wealth += 1 self.wealth -= 1 -And with those two methods, the agent’s ``step`` method becomes: +And with those two methods, the agent's ``step`` method becomes: .. code:: python @@ -607,7 +578,7 @@ Now, putting that all together should look like this: def step(self): self.schedule.step() -Let’s create a model with 50 agents on a 10x10 grid, and run it for 20 +Let's create a model with 50 agents on a 10x10 grid, and run it for 20 steps. .. code:: ipython3 @@ -616,11 +587,11 @@ steps. for i in range(20): model.step() -Now let’s use matplotlib and numpy to visualize the number of agents +Now let's use matplotlib and numpy to visualize the number of agents residing in each cell. To do that, we create a numpy array of the same -size as the grid, filled with zeros. Then we use the grid object’s +size as the grid, filled with zeros. Then we use the grid object's ``coord_iter()`` feature, which lets us loop over every cell in the -grid, giving us each cell’s coordinates and contents in turn. +grid, giving us each cell's coordinates and contents in turn. .. code:: ipython3 @@ -642,21 +613,21 @@ grid, giving us each cell’s coordinates and contents in turn. .. parsed-literal:: - + -.. image:: intro_tutorial_files%5Cintro_tutorial_32_1.png +.. image:: intro_tutorial_files/output_32_1.png Collecting Data ~~~~~~~~~~~~~~~ -So far, at the end of every model run, we’ve had to go and write our own -code to get the data out of the model. This has two problems: it isn’t +So far, at the end of every model run, we've had to go and write our own +code to get the data out of the model. This has two problems: it isn't very efficient, and it only gives us end results. If we wanted to know -the wealth of each agent at each step, we’d have to add that to the loop +the wealth of each agent at each step, we'd have to add that to the loop of executing steps, and figure out some way to store the data. Since one of the main goals of agent-based modeling is generating data @@ -670,19 +641,19 @@ collector along with a function for collecting them. Model-level collection functions take a model object as an input, while agent-level collection functions take an agent object as an input. Both then return a value computed from the model or each agent at their current state. -When the data collector’s ``collect`` method is called, with a model +When the data collector's ``collect`` method is called, with a model object as its argument, it applies each model-level collection function to the model, and stores the results in a dictionary, associating the current value with the current step of the model. Similarly, the method applies each agent-level collection function to each agent currently in the schedule, associating the resulting value with the step of the -model, and the agent’s ``unique_id``. +model, and the agent's ``unique_id``. -Let’s add a DataCollector to the model with -```mesa.DataCollector`` `__, +Let's add a DataCollector to the model with +`mesa.DataCollector `__, and collect two variables. At the agent level, we want to collect every -agent’s wealth at every step. At the model level, let’s measure the -model’s `Gini +agent's wealth at every step. At the model level, let's measure the +model's `Gini Coefficient `__, a measure of wealth inequality. @@ -712,15 +683,10 @@ measure of wealth inequality. def give_money(self): cellmates = self.model.grid.get_cell_list_contents([self.pos]) - cellmates.pop( - cellmates.index(self) - ) # Ensure agent is not giving money to itself if len(cellmates) > 1: other = self.random.choice(cellmates) other.wealth += 1 self.wealth -= 1 - if other == self: - print("I JUST GAVE MONEY TO MYSELF HEHEHE!") def step(self): self.move() @@ -754,7 +720,7 @@ measure of wealth inequality. self.schedule.step() At every step of the model, the datacollector will collect and store the -model-level current Gini coefficient, as well as each agent’s wealth, +model-level current Gini coefficient, as well as each agent's wealth, associating each with the current step. We run the model just as we did above. Now is when an interactive @@ -787,12 +753,12 @@ To get the series of Gini coefficients as a pandas DataFrame: .. parsed-literal:: - + -.. image:: intro_tutorial_files%5Cintro_tutorial_38_1.png +.. image:: intro_tutorial_files/output_38_1.png Similarly, we can get the agent-wealth data: @@ -862,9 +828,9 @@ Similarly, we can get the agent-wealth data: -You’ll see that the DataFrame’s index is pairings of model step and +You'll see that the DataFrame's index is pairings of model step and agent ID. You can analyze it the way you would any other DataFrame. For -example, to get a histogram of agent wealth at the model’s end: +example, to get a histogram of agent wealth at the model's end: .. code:: ipython3 @@ -876,12 +842,12 @@ example, to get a histogram of agent wealth at the model’s end: .. parsed-literal:: - + -.. image:: intro_tutorial_files%5Cintro_tutorial_42_1.png +.. image:: intro_tutorial_files/output_42_1.png Or to plot the wealth of a given agent (in this example, agent 14): @@ -896,12 +862,12 @@ Or to plot the wealth of a given agent (in this example, agent 14): .. parsed-literal:: - + -.. image:: intro_tutorial_files%5Cintro_tutorial_44_1.png +.. image:: intro_tutorial_files/output_44_1.png You can also use pandas to export the data to a CSV (comma separated @@ -923,12 +889,11 @@ directory. After you run the code below you will see two files appear Batch Run ~~~~~~~~~ -Like we mentioned above, you usually won’t run a model only once, but +Like we mentioned above, you usually won't run a model only once, but multiple times, with fixed parameters to find the overall distributions the model generates, and with varying parameters to analyze how they -drive the model’s outputs and behaviors. Instead of needing to write -nested for-loops for each model, Mesa provides a -```batch_run`` `__ +drive the model's outputs and behaviors. Instead of needing to write +nested for-loops for each model, Mesa provides a `batch_run `__ function which automates it for you. The batch runner also requires an additional variable ``self.running`` @@ -1012,7 +977,7 @@ of the model with each number of agents, and to run each for 100 steps. We want to keep track of 1. the Gini coefficient value and -2. the individual agent’s wealth development. +2. the individual agent's wealth development. Since for the latter changes at each time step might be interesting, we set ``data_collection_period = 1``. @@ -1026,9 +991,9 @@ iteration). **Note for Windows OS users:** If you are running this tutorial in Jupyter, make sure that you set ``number_processes = 1`` (single process). If ``number_processes`` is greater than 1, it is less -straightforward to set up. You can read `Mesa’s collection of useful +straightforward to set up. You can read `Mesa's collection of useful snippets `__, -in ‘Using multi-process ``batch_run`` on Windows’ section for how to do +in ‘Using multi-process ``batch_run`` on Windows'section for how to do it. .. code:: ipython3 @@ -1048,7 +1013,7 @@ it. .. parsed-literal:: - 100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 245/245 [00:48<00:00, 5.10it/s] + 245it [00:34, 7.02it/s] To further analyze the return of the ``batch_run`` function, we convert @@ -1090,15 +1055,15 @@ calling the batch run. .. parsed-literal:: - + -.. image:: intro_tutorial_files%5Cintro_tutorial_57_1.png +.. image:: intro_tutorial_files/output_57_1.png -Second, we want to display the agent’s wealth at each time step of one +Second, we want to display the agent's wealth at each time step of one specific episode. To do this, we again filter our large data frame, this time with a fixed number of agents and only for a specific iteration of that population. To print the results, we convert the filtered data @@ -1137,21 +1102,21 @@ can use the ``to_html()`` function which takes the same arguments as 0 7 1 0 8 1 0 9 1 - 1 0 1 + 1 0 2 1 1 1 - ... ... ... - 99 8 1 - 99 9 0 + ... ... ... + 99 8 4 + 99 9 1 100 0 0 - 100 1 1 + 100 1 0 100 2 1 - 100 3 1 + 100 3 0 100 4 1 - 100 5 2 - 100 6 1 + 100 5 1 + 100 6 0 100 7 2 - 100 8 1 - 100 9 0 + 100 8 4 + 100 9 1 Lastly, we want to take a look at the development of the Gini @@ -1171,30 +1136,30 @@ episode. Step Gini 0 0.00 - 1 0.00 - 2 0.00 - 3 0.00 - 4 0.00 - 5 0.00 - 6 0.00 - 7 0.00 - 8 0.00 - 9 0.00 - 10 0.00 - 11 0.00 - ... ... - 89 0.18 - 90 0.18 - 91 0.18 - 92 0.18 - 93 0.18 - 94 0.18 - 95 0.18 - 96 0.18 - 97 0.18 - 98 0.18 - 99 0.18 - 100 0.18 + 1 0.18 + 2 0.18 + 3 0.18 + 4 0.18 + 5 0.32 + 6 0.32 + 7 0.32 + 8 0.42 + 9 0.42 + 10 0.42 + 11 0.42 + ... ... + 89 0.66 + 90 0.66 + 91 0.66 + 92 0.66 + 93 0.56 + 94 0.56 + 95 0.56 + 96 0.56 + 97 0.56 + 98 0.56 + 99 0.56 + 100 0.56 Happy Modeling! @@ -1216,8 +1181,3 @@ http://mars.gmu.edu/bitstream/handle/1920/9070/Comer_gmu_0883E_10539.pdf “Statistical Mechanics of Money, Income, and Wealth: A Short Survey.” arXiv Preprint Cond-mat/0211175, 2002. http://arxiv.org/abs/cond-mat/0211175. - - - - - From b5fcdd5775c0c96a7dffd718cb06c3bfe8555f63 Mon Sep 17 00:00:00 2001 From: Jackie Kazil Date: Tue, 25 Apr 2023 13:44:03 -0400 Subject: [PATCH 086/214] Modify Intro Tutorial (#1656) (#1658) Completed the following updates: Tutorial Description - reworked the text, removed the term 'easy' and replaced with 'starter-level', removed note indicating this is work in progress Model Description - Defined some of the terms associated with agent-based economy. Installation - Renamed to Prerequisites Setup, removed dependency on requirements.txt, marked Jupyter install as optional, added matploylib as a requiremnt. Changed virtual enivronments link. This allowed them to be removed from the example folder project. Building the Sample Model - Completly reworked to more clearly define working in Juypter notebook and script files. Co-authored-by: Glenn Lehman <55802043+glnnlhmn@users.noreply.github.com> --- docs/tutorials/intro_tutorial.ipynb | 448 +++++++++++------- docs/tutorials/intro_tutorial.rst | 332 +++++++------ .../intro_tutorial_19_1.png | Bin 0 -> 5734 bytes .../intro_tutorial_22_1.png | Bin 0 -> 9932 bytes .../intro_tutorial_32_1.png | Bin 0 -> 10048 bytes .../intro_tutorial_38_1.png | Bin 0 -> 20884 bytes .../intro_tutorial_42_1.png | Bin 0 -> 7671 bytes .../intro_tutorial_44_1.png | Bin 0 -> 13631 bytes .../intro_tutorial_57_1.png | Bin 0 -> 19255 bytes 9 files changed, 460 insertions(+), 320 deletions(-) create mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_19_1.png create mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_22_1.png create mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_32_1.png create mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_38_1.png create mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_42_1.png create mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_44_1.png create mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_57_1.png diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 4c1e8c367a5..88390835311 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -13,74 +13,99 @@ "source": [ "## Tutorial Description\n", "\n", - "[Mesa](https://github.com/projectmesa/mesa) is a Python framework for [agent-based modeling](https://en.wikipedia.org/wiki/Agent-based_model). Getting started with Mesa is easy. In this tutorial, we will walk through creating a simple model and progressively add functionality which will illustrate Mesa's core features.\n", + "[Mesa](https://github.com/projectmesa/mesa) is a Python framework for [agent-based modeling](https://en.wikipedia.org/wiki/Agent-based_model). This tutorial will assist you in getting started. Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone find any errors, bugs, have a suggestion, or just are looking for clarification, [let us know](https://github.com/projectmesa/mesa/issues)!\n", "\n", - "**Note:** This tutorial is a work-in-progress. If you find any errors or bugs, or just find something unclear or confusing, [let us know](https://github.com/projectmesa/mesa/issues)!\n", + "The premise of this tutorial is to create a starter-level model representing agents exchanging money. This exchange of money affects wealth. Next, *space* is added to allow agents to move based on the change in wealth as time progresses.\n", "\n", - "The base for this tutorial is a very simple model of agents exchanging money. Next, we add *space* to allow agents to move. Then, we'll cover two of Mesa's analytic tools: the *data collector* and *batch runner*. After that, we'll add an *interactive visualization* which lets us watch the model as it runs. Finally, we go over how to write your own visualization module, for users who are comfortable with JavaScript.\n", + "Two of Mesa's analytic tools: the *data collector* and *batch runner* will be used to examine this movement. After that an *interactive visualization* is added which allows model viewing as it runs.\n", "\n", - "You can also find all the code this tutorial describes in the **examples/boltzmann_wealth_model** directory of the Mesa repository." + "Finally, the creation of a custom visualization module in JavaScript is explored." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Sample Model Description\n", + "## Model Description\n", "\n", - "The tutorial model is a very simple simulated agent-based economy, drawn from econophysics and presenting a statistical mechanics approach to wealth distribution [Dragulescu2002]. The rules of our tutorial model:\n", + "This is a starter-level simulated agent-based economy. In an agent-based economy, the behavior of an\n", + "individual economic agent, such as a consumer or producer, is studied in a market environment.\n", + "This model is drawn from the field econophysics, specifically a paper prepared by Drăgulescu et al.\n", + "for additional information on the modeling assumptions used in this model.\n", + "[Drăgulescu, 2002].\n", + "\n", + "The assumption that govern this model are:\n", "\n", "1. There are some number of agents.\n", "2. All agents begin with 1 unit of money.\n", - "3. At every step of the model, an agent gives 1 unit of money (if they have it) to some other agent.\n", - "\n", - "Despite its simplicity, this model yields results that are often unexpected to those not familiar with it. For our purposes, it also easily demonstrates Mesa's core features.\n", + "3. At every step of the model, an agent gives 1 unit of money (if they\n", + " have it) to some other agent.\n", "\n", - "Let's get started." + "Even as a starter-level model the yielded results are both interesting and unexpected to individuals unfamiliar\n", + "with it the specific topic. As such, this model is a good starting point to examine Mesa's core features." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "### Installation\n", + "### Tutorial Setup\n", "\n", - "To start, install Mesa. We recommend doing this in a [virtual environment](https://virtualenvwrapper.readthedocs.org/en/stable/), but make sure your environment is set up with Python 3. Mesa requires Python3 and does not work in Python 2 environments.\n", + "Create and activate a [virtual environment](http://docs.python-guide.org/en/latest/dev/virtualenvs/). *Python version 3.8 or higher is required*.\n", "\n", - "To install Mesa, simply:\n", + "Install Mesa:\n", "\n", "```bash\n", - " pip install mesa\n", + "python3 -m pip install mesa\n", "```\n", "\n", - "When you do that, it will install Mesa itself, as well as any dependencies that aren't in your setup yet. Additional dependencies required by this tutorial can be found in the **examples/boltzmann_wealth_model/requirements.txt** file, which can be installed directly form the github repository by running:\n", + "Install Jupyter Notebook (optional):\n", "\n", "```bash\n", - " pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt\n", + "python3 -m pip install jupyter\n", "```\n", "\n", - "This will install the dependencies listed in the requirements.txt file which are: \n", - "- jupyter (Ipython interactive notebook) \n", - "- matplotlib (Python's visualization library) \n", - "- mesa (this ABM library -- if not installed) \n", - "- numpy (Python's numerical python library) " + "Install matplotlib:\n", + "\n", + "```bash\n", + "python3 -m pip install matplotlib\n", + "```\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "## Building a sample model\n", + "## Building the Sample Model\n", + "\n", + "After Mesa is installed a model can be built. A jupyter notebook is recommended for this tutorial, this allows for small segments of codes to be examined one at a time. As an option this can be created using python script files.\n", + "\n", + "**Good Practice:** Place a model in its own folder/directory. This is not specifically required for the starter_model, but as other models become more complicated and expand multiple python scripts, documentation, discussions and notebooks may be added.\n", + "\n", + "### Create New Folder/Directory\n", + "\n", + "- Using operating system commands create a new folder/directory named 'starter_model'.\n", + "\n", + "- Change into the new folder/directory.\n", + "\n", + "\n", + "### Creating Model With Jupyter Notebook\n", + "\n", + "Write the model interactively in [Jupyter Notebook](http://jupyter.org/) cells.\n", + "\n", + "Start Jupyter Notebook:\n", + "\n", + "```bash\n", + "jupyter notebook\n", + "```\n", "\n", - "Once Mesa is installed, you can start building our model. You can write models in two different ways:\n", + "Create a new Notebook named `money_model.ipynb` (or whatever you want to call it).\n", "\n", - "1. Write the code in its own file with your favorite text editor, or\n", - "2. Write the model interactively in [Jupyter Notebook](http://jupyter.org/) cells.\n", + "### Creating Model With Script File (IDE, Text Editor, Colab, etc.)\n", "\n", - "Either way, it's good practice to put your model in its own folder -- especially if the project will end up consisting of multiple files (for example, Python files for the model and the visualization, a Notebook for analysis, and a Readme with some documentation and discussion).\n", + "Create a new file called `money_model.py` (or whatever you want to call it)\n", "\n", - "Begin by creating a folder, and either launch a Notebook or create a new Python source file. We will use the name `money_model.py` here.\n", - "\n" + "*Code will be added as the tutorial progresses.*\n" ] }, { @@ -100,8 +125,13 @@ }, { "cell_type": "code", - "execution_count": 36, - "metadata": {}, + "execution_count": 1, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:36.563710Z", + "start_time": "2023-04-25T11:12:36.203356Z" + } + }, "outputs": [], "source": [ "import mesa\n", @@ -144,8 +174,13 @@ }, { "cell_type": "code", - "execution_count": 37, - "metadata": {}, + "execution_count": 2, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:36.567908Z", + "start_time": "2023-04-25T11:12:36.565717Z" + } + }, "outputs": [], "source": [ "import mesa\n", @@ -194,20 +229,25 @@ }, { "cell_type": "code", - "execution_count": 38, - "metadata": {}, + "execution_count": 3, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:36.577676Z", + "start_time": "2023-04-25T11:12:36.569344Z" + } + }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Hi, I am agent 5.\n", + "Hi, I am agent 2.\n", + "Hi, I am agent 4.\n", + "Hi, I am agent 8.\n", "Hi, I am agent 0.\n", "Hi, I am agent 1.\n", "Hi, I am agent 7.\n", - "Hi, I am agent 8.\n", - "Hi, I am agent 2.\n", - "Hi, I am agent 4.\n", "Hi, I am agent 9.\n", "Hi, I am agent 3.\n", "Hi, I am agent 6.\n" @@ -248,8 +288,13 @@ }, { "cell_type": "code", - "execution_count": 39, - "metadata": {}, + "execution_count": 4, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:36.581436Z", + "start_time": "2023-04-25T11:12:36.579668Z" + } + }, "outputs": [], "source": [ "class MoneyAgent(mesa.Agent):\n", @@ -286,8 +331,13 @@ }, { "cell_type": "code", - "execution_count": 40, - "metadata": {}, + "execution_count": 5, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:36.584738Z", + "start_time": "2023-04-25T11:12:36.582444Z" + } + }, "outputs": [], "source": [ "model = MoneyModel(10)\n", @@ -315,24 +365,29 @@ }, { "cell_type": "code", - "execution_count": 41, - "metadata": {}, + "execution_count": 6, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:36.927134Z", + "start_time": "2023-04-25T11:12:36.587055Z" + } + }, "outputs": [ { "data": { "text/plain": [ - "(array([6., 0., 1., 0., 0., 1., 0., 1., 0., 1.]),\n", - " array([0. , 0.4, 0.8, 1.2, 1.6, 2. , 2.4, 2.8, 3.2, 3.6, 4. ]),\n", + "(array([5., 0., 0., 2., 0., 0., 1., 0., 0., 2.]),\n", + " array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7, 3. ]),\n", " )" ] }, - "execution_count": 41, + "execution_count": 6, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAAGdCAYAAABO2DpVAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAYf0lEQVR4nO3da4xU9f348c8C7qCVXcWCQlhRa0UB8YYYpK14j0UjPmitoZZYe9GsFUpqC31Qaky7mDRe0hKktkrS1mKtARutUNQCqUjLbdP1EiqKul6QXuwO0GZq2PN/8A/76wILnOU7uxz6eiXnwQzf2fl89zDZd2Zmd2qyLMsCACCBPr09AABw+BAWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQTL+evsP29vZ49913Y8CAAVFTU9PTdw8AdEOWZbFt27YYOnRo9OnT9fMSPR4W7777bjQ0NPT03QIACbS2tsawYcO6/PceD4sBAwZExP8frK6urqfvHgDohnK5HA0NDR0/x7vS42Gx6+WPuro6YQEABbO/tzF48yYAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkskdFu+88058/vOfj+OOOy6OPPLIOPPMM2Pt2rXVmA0AKJhcnxXywQcfxIQJE+Liiy+Op59+OgYNGhSvvvpqHHvssdWaDwAokFxhcffdd0dDQ0M8/PDDHdedfPLJyYcCAIop10shv/nNb2Ls2LHxmc98JgYPHhznnHNOPPjgg/u8TaVSiXK53OkAAA5PuZ6xeP3112PevHkxY8aM+Pa3vx1r1qyJ22+/PWpra2Pq1Kl7vU1TU1PceeedSYbdn5NmPtUj95PSG3Mm9fYIAJBMTZZl2YEurq2tjbFjx8aqVas6rrv99ttjzZo18cILL+z1NpVKJSqVSsflcrkcDQ0N0dbWFnV1dQcx+p6EBQBUR7lcjvr6+v3+/M71UsiQIUNi5MiRna4744wz4q233uryNqVSKerq6jodAMDhKVdYTJgwITZu3Njpur/85S8xfPjwpEMBAMWUKyy+/vWvx+rVq+P73/9+bNq0KR555JH48Y9/HI2NjdWaDwAokFxhcf7558eiRYvil7/8ZYwePTruuuuuuO+++2LKlCnVmg8AKJBcvxUSEXH11VfH1VdfXY1ZAICC81khAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkskVFt/97nejpqam03H66adXazYAoGD65b3BqFGj4plnnvm/L9Av95cAAA5TuaugX79+ccIJJ1RjFgCg4HK/x+LVV1+NoUOHximnnBJTpkyJt956a5/rK5VKlMvlTgcAcHjKFRYXXHBBLFiwIJYsWRLz5s2LzZs3xyc/+cnYtm1bl7dpamqK+vr6jqOhoeGghwYADk01WZZl3b3xP//5zxg+fHjcc889cfPNN+91TaVSiUql0nG5XC5HQ0NDtLW1RV1dXXfveq9OmvlU0q/XE96YM6m3RwCA/SqXy1FfX7/fn98H9c7LY445Jk477bTYtGlTl2tKpVKUSqWDuRsAoCAO6u9YbN++PV577bUYMmRIqnkAgALLFRbf+MY3YsWKFfHGG2/EqlWr4rrrrou+ffvGDTfcUK35AIACyfVSyNtvvx033HBD/P3vf49BgwbFJz7xiVi9enUMGjSoWvMBAAWSKywWLlxYrTkAgMOAzwoBAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGQOKizmzJkTNTU1MX369ETjAABF1u2wWLNmTcyfPz/GjBmTch4AoMC6FRbbt2+PKVOmxIMPPhjHHnts6pkAgILqVlg0NjbGpEmT4rLLLks9DwBQYP3y3mDhwoWxfv36WLNmzQGtr1QqUalUOi6Xy+W8dwkAFESuZyxaW1tj2rRp8Ytf/CL69+9/QLdpamqK+vr6jqOhoaFbgwIAh76aLMuyA128ePHiuO6666Jv374d1+3cuTNqamqiT58+UalUOv1bxN6fsWhoaIi2traoq6tLsIX/c9LMp5J+vZ7wxpxJvT0CAOxXuVyO+vr6/f78zvVSyKWXXhotLS2drrvpppvi9NNPj29961t7REVERKlUilKplOduAICCyhUWAwYMiNGjR3e67iMf+Ugcd9xxe1wPAPzv8Zc3AYBkcv9WyO6WL1+eYAwA4HDgGQsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJ5AqLefPmxZgxY6Kuri7q6upi/Pjx8fTTT1drNgCgYHKFxbBhw2LOnDmxbt26WLt2bVxyySVx7bXXxksvvVSt+QCAAumXZ/E111zT6fL3vve9mDdvXqxevTpGjRqVdDAAoHhyhcV/27lzZzz22GOxY8eOGD9+fJfrKpVKVCqVjsvlcrm7dwkAHOJyv3mzpaUljj766CiVSnHLLbfEokWLYuTIkV2ub2pqivr6+o6joaHhoAYGAA5ducNixIgR0dzcHH/84x/j1ltvjalTp8bLL7/c5fpZs2ZFW1tbx9Ha2npQAwMAh67cL4XU1tbGqaeeGhER5513XqxZsybuv//+mD9//l7Xl0qlKJVKBzclAFAIB/13LNrb2zu9hwIA+N+V6xmLWbNmxVVXXRUnnnhibNu2LR555JFYvnx5LF26tFrzAQAFkisstm7dGl/4whfivffei/r6+hgzZkwsXbo0Lr/88mrNBwAUSK6w+OlPf1qtOQCAw4DPCgEAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJBMrrBoamqK888/PwYMGBCDBw+OyZMnx8aNG6s1GwBQMLnCYsWKFdHY2BirV6+OZcuWxYcffhhXXHFF7Nixo1rzAQAF0i/P4iVLlnS6vGDBghg8eHCsW7cuPvWpTyUdDAAonlxhsbu2traIiBg4cGCXayqVSlQqlY7L5XL5YO4SADiEdfvNm+3t7TF9+vSYMGFCjB49ust1TU1NUV9f33E0NDR09y4BgENct8OisbExXnzxxVi4cOE+182aNSva2to6jtbW1u7eJQBwiOvWSyG33XZbPPnkk7Fy5coYNmzYPteWSqUolUrdGg4AKJZcYZFlWXzta1+LRYsWxfLly+Pkk0+u1lwAQAHlCovGxsZ45JFH4oknnogBAwbEli1bIiKivr4+jjzyyKoMCAAUR673WMybNy/a2tpi4sSJMWTIkI7j0UcfrdZ8AECB5H4pBACgKz4rBABIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSyR0WK1eujGuuuSaGDh0aNTU1sXjx4iqMBQAUUe6w2LFjR5x11lkxd+7caswDABRYv7w3uOqqq+Kqq66qxiwAQMF5jwUAkEzuZyzyqlQqUalUOi6Xy+Vq3yUA0EuqHhZNTU1x5513Vvtu6EEnzXyqt0fI7Y05k3p7hP8Z/n/0DN/nnuH7nF/VXwqZNWtWtLW1dRytra3VvksAoJdU/RmLUqkUpVKp2ncDABwCcofF9u3bY9OmTR2XN2/eHM3NzTFw4MA48cQTkw4HABRL7rBYu3ZtXHzxxR2XZ8yYERERU6dOjQULFiQbDAAontxhMXHixMiyrBqzAAAF5+9YAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASEZYAADJCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBkhAUAkIywAACSERYAQDLCAgBIRlgAAMkICwAgGWEBACQjLACAZIQFAJCMsAAAkhEWAEAywgIASKZbYTF37tw46aSTon///nHBBRfEn/70p9RzAQAFlDssHn300ZgxY0bMnj071q9fH2eddVZceeWVsXXr1mrMBwAUSO6wuOeee+LLX/5y3HTTTTFy5Mh44IEH4qijjoqHHnqoGvMBAAXSL8/i//znP7Fu3bqYNWtWx3V9+vSJyy67LF544YW93qZSqUSlUum43NbWFhER5XK5O/PuU3vlX8m/ZrVV4/tQbb7P7Iv/Hz3D97ln+D7v+XWzLNv3wiyHd955J4uIbNWqVZ2uv+OOO7Jx48bt9TazZ8/OIsLhcDgcDsdhcLS2tu6zFXI9Y9Eds2bNihkzZnRcbm9vj3/84x9x3HHHRU1NTbL7KZfL0dDQEK2trVFXV5fs6x5KDvc92l/xHe57tL/iO9z3WM39ZVkW27Zti6FDh+5zXa6w+OhHPxp9+/aN999/v9P177//fpxwwgl7vU2pVIpSqdTpumOOOSbP3eZSV1d3WP5n+W+H+x7tr/gO9z3aX/Ed7nus1v7q6+v3uybXmzdra2vjvPPOi2effbbjuvb29nj22Wdj/Pjx+ScEAA4ruV8KmTFjRkydOjXGjh0b48aNi/vuuy927NgRN910UzXmAwAKJHdYXH/99fHXv/41vvOd78SWLVvi7LPPjiVLlsTxxx9fjfkOWKlUitmzZ+/xssvh5HDfo/0V3+G+R/srvsN9j4fC/mqy/f7eCADAgfFZIQBAMsICAEhGWAAAyQgLACCZQoVF3o9rf+yxx+L000+P/v37x5lnnhm//e1ve2jS7smzvwULFkRNTU2no3///j04bT4rV66Ma665JoYOHRo1NTWxePHi/d5m+fLlce6550apVIpTTz01FixYUPU5D0bePS5fvnyPc1hTUxNbtmzpmYFzampqivPPPz8GDBgQgwcPjsmTJ8fGjRv3e7uiPA67s78iPQ7nzZsXY8aM6fjDSePHj4+nn356n7cpyrnbJe8ei3T+9mbOnDlRU1MT06dP3+e6nj6PhQmLvB/XvmrVqrjhhhvi5ptvjg0bNsTkyZNj8uTJ8eKLL/bw5AemOx9HX1dXF++9917H8eabb/bgxPns2LEjzjrrrJg7d+4Brd+8eXNMmjQpLr744mhubo7p06fHl770pVi6dGmVJ+2+vHvcZePGjZ3O4+DBg6s04cFZsWJFNDY2xurVq2PZsmXx4YcfxhVXXBE7duzo8jZFehx2Z38RxXkcDhs2LObMmRPr1q2LtWvXxiWXXBLXXnttvPTSS3tdX6Rzt0vePUYU5/ztbs2aNTF//vwYM2bMPtf1ynnM8yFkvWncuHFZY2Njx+WdO3dmQ4cOzZqamva6/rOf/Ww2adKkTtddcMEF2Ve/+tWqztldeff38MMPZ/X19T00XVoRkS1atGifa775zW9mo0aN6nTd9ddfn1155ZVVnCydA9nj73//+ywisg8++KBHZkpt69atWURkK1as6HJN0R6H/+1A9lfkx2GWZdmxxx6b/eQnP9nrvxX53P23fe2xqOdv27Zt2cc//vFs2bJl2UUXXZRNmzaty7W9cR4L8YzFro9rv+yyyzqu29/Htb/wwgud1kdEXHnllV2u703d2V9ExPbt22P48OHR0NCw3yovmiKdv4N19tlnx5AhQ+Lyyy+P559/vrfHOWBtbW0RETFw4MAu1xT5PB7I/iKK+TjcuXNnLFy4MHbs2NHlxzEU+dxFHNgeI4p5/hobG2PSpEl7nJ+96Y3zWIiw+Nvf/hY7d+7c4697Hn/88V2+Hr1ly5Zc63tTd/Y3YsSIeOihh+KJJ56In//859He3h4XXnhhvP322z0xctV1df7K5XL8+9//7qWp0hoyZEg88MAD8fjjj8fjjz8eDQ0NMXHixFi/fn1vj7Zf7e3tMX369JgwYUKMHj26y3VFehz+twPdX9Eehy0tLXH00UdHqVSKW265JRYtWhQjR47c69qinrs8eyza+YuIWLhwYaxfvz6ampoOaH1vnMeqf2w61TF+/PhOFX7hhRfGGWecEfPnz4+77rqrFyfjQI0YMSJGjBjRcfnCCy+M1157Le6999742c9+1ouT7V9jY2O8+OKL8Yc//KG3R6mKA91f0R6HI0aMiObm5mhra4tf//rXMXXq1FixYkWXP3iLKM8ei3b+WltbY9q0abFs2bJD+k2mhQiL7nxc+wknnJBrfW/qzv52d8QRR8Q555wTmzZtqsaIPa6r81dXVxdHHnlkL01VfePGjTvkf1jfdttt8eSTT8bKlStj2LBh+1xbpMfhLnn2t7tD/XFYW1sbp556akREnHfeebFmzZq4//77Y/78+XusLeK5i8i3x90d6udv3bp1sXXr1jj33HM7rtu5c2esXLkyfvSjH0WlUom+fft2uk1vnMdCvBTSnY9rHz9+fKf1ERHLli07JD/ePcXH0e/cuTNaWlpiyJAh1RqzRxXp/KXU3Nx8yJ7DLMvitttui0WLFsVzzz0XJ5988n5vU6Tz2J397a5oj8P29vaoVCp7/bcinbt92dced3eon79LL700Wlpaorm5ueMYO3ZsTJkyJZqbm/eIioheOo9Ve1toYgsXLsxKpVK2YMGC7OWXX86+8pWvZMccc0y2ZcuWLMuy7MYbb8xmzpzZsf7555/P+vXrl/3gBz/IXnnllWz27NnZEUcckbW0tPTWFvYp7/7uvPPObOnSpdlrr72WrVu3Lvvc5z6X9e/fP3vppZd6awv7tG3btmzDhg3Zhg0bsojI7rnnnmzDhg3Zm2++mWVZls2cOTO78cYbO9a//vrr2VFHHZXdcccd2SuvvJLNnTs369u3b7ZkyZLe2sJ+5d3jvffemy1evDh79dVXs5aWlmzatGlZnz59smeeeaa3trBPt956a1ZfX58tX748e++99zqOf/3rXx1rivw47M7+ivQ4nDlzZrZixYps8+bN2Z///Ods5syZWU1NTfa73/0uy7Jin7td8u6xSOevK7v/VsihcB4LExZZlmU//OEPsxNPPDGrra3Nxo0bl61evbrj3y666KJs6tSpndb/6le/yk477bSstrY2GzVqVPbUU0/18MT55Nnf9OnTO9Yef/zx2ac//els/fr1vTD1gdn1q5W7H7v2NHXq1Oyiiy7a4zZnn312Vltbm51yyinZww8/3ONz55F3j3fffXf2sY99LOvfv382cODAbOLEidlzzz3XO8MfgL3tLSI6nZciPw67s78iPQ6/+MUvZsOHD89qa2uzQYMGZZdeemnHD9wsK/a52yXvHot0/rqye1gcCufRx6YDAMkU4j0WAEAxCAsAIBlhAQAkIywAgGSEBQCQjLAAAJIRFgBAMsICAEhGWAAAyQgLACAZYQEAJCMsAIBk/h9X3YomdlYsdQAAAABJRU5ErkJggg==", + "image/png": "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5pgolUpx0003xapVq+Kss84acH1HR0c0Nzf33VpaWg5pYABg5Ko4LKZOnRqbN2+O3/72t3HzzTfHwoUL4+WXXx5wfVtbW3R1dfXdOjs7D2lgAGDkqvhHIY2NjXHGGWdERMS5554bL774Ytx1111xzz33HHB9qVSKUql0aFMCAHXhkP+ORW9vb7/PUAAAo1dF71i0tbXFVVddFSeffHLs2bMn7r///lizZk2sXr26WvMBAHWkorDYvXt3fP7zn4+dO3dGc3NzzJw5M1avXh2XX355teYDAOpIRWHxk5/8pFpzAACHAdcKAQDSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSVBQWHR0dcd5558X48eNj4sSJMX/+/Ni+fXu1ZgMA6kxFYbF27dpobW2N9evXx5NPPhnvvvtuXHHFFdHT01Ot+QCAOjK2ksVPPPFEv/srVqyIiRMnxoYNG+JTn/pU6mAAQP2pKCzer6urKyIiJkyYMOCacrkc5XK57353d/ehvCQAMIINOSx6e3tj0aJFceGFF8aMGTMGXNfR0RHt7e1DfRlIceqSx2o9wpC8sXRerUeAUa0ezx21Pm8M+bdCWltbY+vWrbFy5cqDrmtra4uurq6+W2dn51BfEgAY4Yb0jsUtt9wSjz76aKxbty6mTJly0LWlUilKpdKQhgMA6ktFYVEURXz1q1+NVatWxZo1a+K0006r1lwAQB2qKCxaW1vj/vvvj0ceeSTGjx8fu3btioiI5ubmOOqoo6oyIABQPyr6jMXy5cujq6sr5s6dG5MmTeq7PfDAA9WaDwCoIxX/KAQAYCCuFQIApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAECaisNi3bp1cfXVV8fkyZOjoaEhHn744SqMBQDUo4rDoqenJ2bNmhXLli2rxjwAQB0bW+kTrrrqqrjqqquqMQsAUOcqDotKlcvlKJfLffe7u7ur/ZIAQI1UPSw6Ojqivb292i8DMGSnLnms1iNU7I2l82o9AhxQ1X8rpK2tLbq6uvpunZ2d1X5JAKBGqv6ORalUilKpVO2XAQBGAH/HAgBIU/E7Fnv37o0dO3b03X/99ddj8+bNMWHChDj55JNThwMA6kvFYfHSSy/FxRdf3Hd/8eLFERGxcOHCWLFiRdpgAED9qTgs5s6dG0VRVGMWAKDO+YwFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBmSGGxbNmyOPXUU2PcuHFx/vnnx+9+97vsuQCAOlRxWDzwwAOxePHiuO2222Ljxo0xa9asuPLKK2P37t3VmA8AqCMVh8Udd9wRX/rSl+KGG26Is846K+6+++44+uij4957763GfABAHRlbyeL//Oc/sWHDhmhra+t77IgjjojLLrssXnjhhQM+p1wuR7lc7rvf1dUVERHd3d1Dmfegesv/Sv+e1VaNfWB/9XhsRDg+hks9Hh+OjeHh2Nj/+xZFcdB1FYXFX//619i3b1+ceOKJ/R4/8cQT4w9/+MMBn9PR0RHt7e37Pd7S0lLJSx+2mr9f6wkYyRwfDMSxwUCqfWzs2bMnmpubB/x6RWExFG1tbbF48eK++729vfH3v/89jj/++GhoaEh7ne7u7mhpaYnOzs5oampK+76HI3s1ePaqMvZr8OzV4NmrwavmXhVFEXv27InJkycfdF1FYfHhD384xowZE++8806/x99555046aSTDvicUqkUpVKp32PHHntsJS9bkaamJgfeINmrwbNXlbFfg2evBs9eDV619upg71S8p6IPbzY2Nsa5554bTz/9dN9jvb298fTTT8ecOXMqnxAAOKxU/KOQxYsXx8KFC+MTn/hEzJ49O77//e9HT09P3HDDDdWYDwCoIxWHxbXXXht/+ctf4lvf+lbs2rUrzj777HjiiSf2+0DncCuVSnHbbbft92MX9mevBs9eVcZ+DZ69Gjx7NXgjYa8aig/6vREAgEFyrRAAII2wAADSCAsAII2wAADS1FVYVHq59gcffDCmTZsW48aNi4997GPxq1/9apgmrb1K9mrFihXR0NDQ7zZu3LhhnLZ21q1bF1dffXVMnjw5Ghoa4uGHH/7A56xZsybOOeecKJVKccYZZ8SKFSuqPudIUOlerVmzZr/jqqGhIXbt2jU8A9dQR0dHnHfeeTF+/PiYOHFizJ8/P7Zv3/6BzxuN56yh7NVoPWctX748Zs6c2ffHr+bMmROPP/74QZ9Ti2OqbsKi0su1P//887FgwYK48cYbY9OmTTF//vyYP39+bN26dZgnH35DubR9U1NT7Ny5s+/25ptvDuPEtdPT0xOzZs2KZcuWDWr966+/HvPmzYuLL744Nm/eHIsWLYovfvGLsXr16ipPWnuV7tV7tm/f3u/YmjhxYpUmHDnWrl0bra2tsX79+njyySfj3XffjSuuuCJ6enoGfM5oPWcNZa8iRuc5a8qUKbF06dLYsGFDvPTSS3HJJZfENddcE9u2bTvg+podU0WdmD17dtHa2tp3f9++fcXkyZOLjo6OA67/7Gc/W8ybN6/fY+eff37xla98papzjgSV7tV9991XNDc3D9N0I1dEFKtWrTromm984xvF9OnT+z127bXXFldeeWUVJxt5BrNXzz77bBERxT/+8Y9hmWkk2717dxERxdq1awdcM5rPWf9rMHvlnPV/jjvuuOLHP/7xAb9Wq2OqLt6xeO9y7ZdddlnfYx90ufYXXnih3/qIiCuvvHLA9YeLoexVRMTevXvjlFNOiZaWloMW8Gg3Wo+rQ3H22WfHpEmT4vLLL4/nnnuu1uPURFdXV0RETJgwYcA1jq3/GsxeRThn7du3L1auXBk9PT0DXlKjVsdUXYTFwS7XPtDPa3ft2lXR+sPFUPZq6tSpce+998YjjzwSP/vZz6K3tzcuuOCCeOutt4Zj5Loy0HHV3d0d//73v2s01cg0adKkuPvuu+Ohhx6Khx56KFpaWmLu3LmxcePGWo82rHp7e2PRokVx4YUXxowZMwZcN1rPWf9rsHs1ms9ZW7ZsiWOOOSZKpVLcdNNNsWrVqjjrrLMOuLZWx1TVL5vOyDdnzpx+xXvBBRfERz/60bjnnnvi29/+dg0no55NnTo1pk6d2nf/ggsuiNdeey3uvPPO+OlPf1rDyYZXa2trbN26NX7zm9/UepQRb7B7NZrPWVOnTo3NmzdHV1dX/PKXv4yFCxfG2rVrB4yLWqiLdyyGcrn2k046qaL1h4uh7NX7HXnkkfHxj388duzYUY0R69pAx1VTU1McddRRNZqqfsyePXtUHVe33HJLPProo/Hss8/GlClTDrp2tJ6z3lPJXr3faDpnNTY2xhlnnBHnnntudHR0xKxZs+Kuu+464NpaHVN1ERZDuVz7nDlz+q2PiHjyyScP+8u7Z1zaft++fbFly5aYNGlStcasW6P1uMqyefPmUXFcFUURt9xyS6xatSqeeeaZOO200z7wOaP12BrKXr3faD5n9fb2RrlcPuDXanZMVfWjoYlWrlxZlEqlYsWKFcXLL79cfPnLXy6OPfbYYteuXUVRFMX1119fLFmypG/9c889V4wdO7b43ve+V7zyyivFbbfdVhx55JHFli1bavVPGDaV7lV7e3uxevXq4rXXXis2bNhQfO5znyvGjRtXbNu2rVb/hGGzZ8+eYtOmTcWmTZuKiCjuuOOOYtOmTcWbb75ZFEVRLFmypLj++uv71v/xj38sjj766OLrX/968corrxTLli0rxowZUzzxxBO1+icMm0r36s477ywefvjh4tVXXy22bNlS3HrrrcURRxxRPPXUU7X6Jwybm2++uWhubi7WrFlT7Ny5s+/2r3/9q2+Nc9Z/DWWvRus5a8mSJcXatWuL119/vfj9739fLFmypGhoaCh+/etfF0Uxco6pugmLoiiKH/zgB8XJJ59cNDY2FrNnzy7Wr1/f97WLLrqoWLhwYb/1v/jFL4ozzzyzaGxsLKZPn1489thjwzxx7VSyV4sWLepbe+KJJxaf/vSni40bN9Zg6uH33q9Evv/23v4sXLiwuOiii/Z7ztlnn100NjYWp59+enHfffcN+9y1UOleffe73y0+8pGPFOPGjSsmTJhQzJ07t3jmmWdqM/wwO9A+RUS/Y8U567+Gslej9Zz1hS98oTjllFOKxsbG4oQTTiguvfTSvqgoipFzTLlsOgCQpi4+YwEA1AdhAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCk+X+5C1FNEpisiAAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -368,24 +423,29 @@ }, { "cell_type": "code", - "execution_count": 42, - "metadata": {}, + "execution_count": 7, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.018897Z", + "start_time": "2023-04-25T11:12:36.942189Z" + } + }, "outputs": [ { "data": { "text/plain": [ - "(array([431., 303., 153., 76., 26., 9., 1., 1.]),\n", - " array([0., 1., 2., 3., 4., 5., 6., 7., 8.]),\n", - " )" + "(array([416., 324., 155., 68., 25., 12.]),\n", + " array([0., 1., 2., 3., 4., 5., 6.]),\n", + " )" ] }, - "execution_count": 42, + "execution_count": 7, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -443,8 +503,13 @@ }, { "cell_type": "code", - "execution_count": 43, - "metadata": {}, + "execution_count": 8, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.023426Z", + "start_time": "2023-04-25T11:12:37.020889Z" + } + }, "outputs": [], "source": [ "class MoneyModel(mesa.Model):\n", @@ -529,8 +594,13 @@ }, { "cell_type": "code", - "execution_count": 44, - "metadata": {}, + "execution_count": 9, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.029893Z", + "start_time": "2023-04-25T11:12:37.028385Z" + } + }, "outputs": [], "source": [ "class MoneyAgent(mesa.Agent):\n", @@ -589,8 +659,13 @@ }, { "cell_type": "code", - "execution_count": 45, - "metadata": {}, + "execution_count": 10, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.036370Z", + "start_time": "2023-04-25T11:12:37.030892Z" + } + }, "outputs": [], "source": [ "model = MoneyModel(50, 10, 10)\n", @@ -607,22 +682,27 @@ }, { "cell_type": "code", - "execution_count": 46, - "metadata": {}, + "execution_count": 11, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.168601Z", + "start_time": "2023-04-25T11:12:37.036888Z" + } + }, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 46, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -663,8 +743,13 @@ }, { "cell_type": "code", - "execution_count": 47, - "metadata": {}, + "execution_count": 12, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.176667Z", + "start_time": "2023-04-25T11:12:37.174646Z" + } + }, "outputs": [], "source": [ "def compute_gini(model):\n", @@ -746,8 +831,13 @@ }, { "cell_type": "code", - "execution_count": 48, - "metadata": {}, + "execution_count": 13, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.191765Z", + "start_time": "2023-04-25T11:12:37.177667Z" + } + }, "outputs": [], "source": [ "model = MoneyModel(50, 10, 10)\n", @@ -764,8 +854,13 @@ }, { "cell_type": "code", - "execution_count": 49, - "metadata": {}, + "execution_count": 14, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.293683Z", + "start_time": "2023-04-25T11:12:37.194765Z" + } + }, "outputs": [ { "data": { @@ -773,13 +868,13 @@ "" ] }, - "execution_count": 49, + "execution_count": 14, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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", + "image/png": "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", "text/plain": [ "
" ] @@ -802,8 +897,13 @@ }, { "cell_type": "code", - "execution_count": 50, - "metadata": {}, + "execution_count": 15, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.302598Z", + "start_time": "2023-04-25T11:12:37.293683Z" + } + }, "outputs": [ { "data": { @@ -871,7 +971,7 @@ " 4 1" ] }, - "execution_count": 50, + "execution_count": 15, "metadata": {}, "output_type": "execute_result" } @@ -890,8 +990,13 @@ }, { "cell_type": "code", - "execution_count": 51, - "metadata": {}, + "execution_count": 16, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.452734Z", + "start_time": "2023-04-25T11:12:37.306761Z" + } + }, "outputs": [ { "data": { @@ -899,13 +1004,13 @@ "" ] }, - "execution_count": 51, + "execution_count": 16, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGdCAYAAACyzRGfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAbO0lEQVR4nO3df4zXhX348dfn4Pjg1TscIBwXDkvbVdsaMBVlpM6g8qNsoWXjj06bFI3p0uU0tZemG0tVrprQuKRjSyntkk3WdDe7LQNTE9GTRogZtIIh1iU1Ql2qFVBsuIMjfvyE+3z/8HtHr4fKwefz+ngfHo/kkn7e9+b9efV1J/fM5/PhPoVKpVIJAIAkTfUeAAC4uIgPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACDV5HoP8PuGhobitddei9bW1igUCvUeBwA4B5VKJU6cOBEdHR3R1PTej2184OLjtddei87OznqPAQCch1deeSXmzp37nud84OKjtbU1It4Zvq2trarXLpfL8eSTT8aKFSuiubm5qtfmDHvOYc857DmPXeeo1Z4HBgais7Nz5Of4e/nAxcfwUy1tbW01iY+WlpZoa2vzjV1D9pzDnnPYcx67zlHrPZ/LSya84BQASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU4gMASCU+AIBU44qPjRs3xnXXXRetra0xa9asWLNmTbz44oujzlm6dGkUCoVRH1/5yleqOjQAMHGNKz527doVXV1dsXfv3ujr64tyuRwrVqyIwcHBUed9+ctfjsOHD498PPTQQ1UdGgCYuMb1xnI7duwYdXvr1q0xa9as2L9/f9x4440jx1taWqK9vb06EwIADeWC3tW2v78/IiKmT58+6vi//du/xY9+9KNob2+P1atXx7333hstLS1nvUapVIpSqTRye2BgICLeede9crl8IeONMXy9al+X0ew5hz3nsOc8dp2jVnsez/UKlUqlcj53MjQ0FJ/73Ofi+PHj8cwzz4wc/6d/+qe44ooroqOjI55//vn467/+67j++uvjv//7v896nQ0bNkRPT8+Y4729ve8aLADAB8upU6fitttui/7+/mhra3vPc887Pv7qr/4qHn/88XjmmWdi7ty573reT3/607jlllvi4MGD8dGPfnTM58/2yEdnZ2ccO3bsfYcfr3K5HH19fXHvvqYoDRWqem3OKDZV4oFFQ7F8+fJobm6u9zgNa/j72Z5ry57z2HWOWu15YGAgZs6ceU7xcV5Pu9x1113x2GOPxe7du98zPCIiFi9eHBHxrvFRLBajWCyOOd7c3Fyzb77SUCFKp8VHrdXya8gZ9pzDnvPYdY5q73k81xpXfFQqlbj77rtj27Zt8fTTT8f8+fPf988cOHAgIiLmzJkznrsCABrUuOKjq6srent749FHH43W1tY4cuRIRERMmzYtLrnkkjh06FD09vbGn/zJn8SMGTPi+eefj6997Wtx4403xoIFC2ryfwAAmFjGFR9btmyJiHd+kdjvevjhh+P222+PKVOmxFNPPRWbNm2KwcHB6OzsjLVr18Y3v/nNqg0MAExs437a5b10dnbGrl27LmggAKCxeW8XACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACDVuOJj48aNcd1110Vra2vMmjUr1qxZEy+++OKoc956663o6uqKGTNmxKWXXhpr166No0ePVnVoAGDiGld87Nq1K7q6umLv3r3R19cX5XI5VqxYEYODgyPnfO1rX4uf/OQn8Z//+Z+xa9eueO211+LP//zPqz44ADAxTR7PyTt27Bh1e+vWrTFr1qzYv39/3HjjjdHf3x///M//HL29vXHzzTdHRMTDDz8cn/jEJ2Lv3r3xR3/0R9WbHACYkC7oNR/9/f0RETF9+vSIiNi/f3+Uy+VYtmzZyDlXXXVVzJs3L/bs2XMhdwUANIhxPfLxu4aGhuKee+6Jz3zmM3H11VdHRMSRI0diypQpcdlll406d/bs2XHkyJGzXqdUKkWpVBq5PTAwEBER5XI5yuXy+Y53VsPXKzZVqnpdRhveb7W/fow2vF97ri17zmPXOWq15/Fc77zjo6urK1544YV45plnzvcSEfHOi1h7enrGHH/yySejpaXlgq79bh5YNFST6zJaX19fvUe4KNhzDnvOY9c5qr3nU6dOnfO55xUfd911Vzz22GOxe/fumDt37sjx9vb2ePvtt+P48eOjHv04evRotLe3n/Va69evj+7u7pHbAwMD0dnZGStWrIi2trbzGe9dlcvl6Ovri3v3NUVpqFDVa3NGsakSDywaiuXLl0dzc3O9x2lYw9/P9lxb9pzHrnPUas/Dz1yci3HFR6VSibvvvju2bdsWTz/9dMyfP3/U56+99tpobm6OnTt3xtq1ayMi4sUXX4xf//rXsWTJkrNes1gsRrFYHHO8ubm5Zt98paFClE6Lj1qr5deQM+w5hz3nsesc1d7zeK41rvjo6uqK3t7eePTRR6O1tXXkdRzTpk2LSy65JKZNmxZ33nlndHd3x/Tp06OtrS3uvvvuWLJkiX/pAgBExDjjY8uWLRERsXTp0lHHH3744bj99tsjIuLv//7vo6mpKdauXRulUilWrlwZ3/ve96oyLAAw8Y37aZf3M3Xq1Ni8eXNs3rz5vIcCABqX93YBAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAglfgAAFKJDwAg1bjjY/fu3bF69ero6OiIQqEQ27dvH/X522+/PQqFwqiPz372s9WaFwCY4MYdH4ODg7Fw4cLYvHnzu57z2c9+Ng4fPjzy8e///u8XNCQA0Dgmj/cPrFq1KlatWvWe5xSLxWhvbz/voQCAxjXu+DgXTz/9dMyaNSv+4A/+IG6++eZ48MEHY8aMGWc9t1QqRalUGrk9MDAQERHlcjnK5XJV5xq+XrGpUtXrMtrwfqv99WO04f3ac23Zcx67zlGrPY/neoVKpXLeP4kLhUJs27Yt1qxZM3LskUceiZaWlpg/f34cOnQo/vZv/zYuvfTS2LNnT0yaNGnMNTZs2BA9PT1jjvf29kZLS8v5jgYAJDp16lTcdttt0d/fH21tbe95btXj4/f96le/io9+9KPx1FNPxS233DLm82d75KOzszOOHTv2vsOPV7lcjr6+vrh3X1OUhgpVvTZnFJsq8cCioVi+fHk0NzfXe5yGNfz9bM+1Zc957DpHrfY8MDAQM2fOPKf4qMnTLr/rIx/5SMycOTMOHjx41vgoFotRLBbHHG9ubq7ZN19pqBCl0+Kj1mr5NeQMe85hz3nsOke19zyea9X893y8+uqr8eabb8acOXNqfVcAwAQw7kc+Tp48GQcPHhy5/fLLL8eBAwdi+vTpMX369Ojp6Ym1a9dGe3t7HDp0KL7xjW/Exz72sVi5cmVVBwcAJqZxx8e+ffvipptuGrnd3d0dERHr1q2LLVu2xPPPPx//+q//GsePH4+Ojo5YsWJFPPDAA2d9agUAuPiMOz6WLl0a7/Ua1SeeeOKCBgIAGpv3dgEAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUokPACCV+AAAUo07Pnbv3h2rV6+Ojo6OKBQKsX379lGfr1Qqcd9998WcOXPikksuiWXLlsVLL71UrXkBgAlu3PExODgYCxcujM2bN5/18w899FD84z/+Y3z/+9+Pn/3sZ/GhD30oVq5cGW+99dYFDwsATHyTx/sHVq1aFatWrTrr5yqVSmzatCm++c1vxuc///mIiPjhD38Ys2fPju3bt8df/MVfXNi0AMCEN+74eC8vv/xyHDlyJJYtWzZybNq0abF48eLYs2fPWeOjVCpFqVQauT0wMBAR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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGdCAYAAACyzRGfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAdZElEQVR4nO3df6zV9X3H8dcFLgeoXBQQLncCUmtrWyftRBjTOav8KGtY2UjntEvQui5trk4lXRuXWqA2pTFZ57ownFuHXTpmt2XgaiJ4pRViCrVgiHVLmFC2WhWcdnDhkl5PuGd/LNyOgj+unvs5Xs7jkZyk53u/fs/78+mxPHvO4Z6WWq1WCwBAIcMaPQAA0FzEBwBQlPgAAIoSHwBAUeIDAChKfAAARYkPAKAo8QEAFDWi0QP8or6+vjz//PMZO3ZsWlpaGj0OAPAG1Gq1HDlyJB0dHRk27LVf23jbxcfzzz+fqVOnNnoMAOBNePbZZ3Peeee95jlvu/gYO3Zskv8bvq2tra7XrlareeSRR7JgwYK0trbW9dpDQbOvP7EHzb7+xB5Yf3OvPxm8Peju7s7UqVP7/xx/LW+7+DjxVktbW9ugxMeYMWPS1tbWlE+6Zl9/Yg+aff2JPbD+5l5/Mvh78EY+MuEDpwBAUeIDAChKfAAARYkPAKAo8QEAFCU+AICixAcAUJT4AACKEh8AQFHiAwAoSnwAAEWJDwCgKPEBABQlPgCAokY0eoBGuHjl5vQef/2v/D3TVIbXcvfsRk8BQLPzygcAUJT4AACKEh8AQFHiAwAoSnwAAEWJDwCgKPEBABQlPgCAosQHAFCU+AAAihIfAEBR4gMAKEp8AABFiQ8AoCjxAQAUJT4AgKLEBwBQlPgAAIoSHwBAUeIDAChKfAAARQ0oPlavXp3LLrssY8eOzaRJk7JkyZLs2bPnpHOuuuqqtLS0nHT71Kc+VdehAYCha0DxsXXr1nR2dmbHjh3p6upKtVrNggUL0tPTc9J5n/zkJ/PCCy/03+6+++66Dg0ADF0jBnLypk2bTrp///33Z9KkSdm1a1euvPLK/uNjxoxJe3t7fSYEAM4oA4qPX3T48OEkyfjx4086/vd///f55je/mfb29ixevDh33nlnxowZc9pr9Pb2pre3t/9+d3d3kqRaraZarb6V8U5x4nqVYbW6XneoOLHueu/rUHJi7c26B82+/sQeWH9zrz8ZvD0YyPVaarXam/qTuK+vL7/1W7+VQ4cO5fHHH+8/ft9992X69Onp6OjIU089lc997nOZPXt2/uVf/uW011m5cmVWrVp1yvH169e/arAAAG8vx44dy/XXX5/Dhw+nra3tNc990/Hx6U9/Og8//HAef/zxnHfeea963ne+851cc8012bt3by644IJTfn66Vz6mTp2al1566XWHH6hqtZqurq7cuXNYevta6nrtoaAyrJa7ZvVl/vz5aW1tbfQ4DXHiOdCse9Ds60/sgfU39/qTwduD7u7uTJw48Q3Fx5t62+Xmm2/OQw89lG3btr1meCTJnDlzkuRV46NSqaRSqZxyvLW1ddCeGL19Lek93nzxccJg7u1Q0ex70OzrT+yB9Tf3+pP678FArjWg+KjVarnllluyYcOGPPbYY5kxY8br/jO7d+9OkkyZMmUgDwUAnKEGFB+dnZ1Zv359HnzwwYwdOzYHDhxIkowbNy6jR4/Ovn37sn79+vzmb/5mJkyYkKeeeiq33357rrzyylxyySWDsgAAYGgZUHysXbs2yf/9IrH/b926dbnhhhsycuTIPProo7nnnnvS09OTqVOnZunSpfn85z9ft4EBgKFtwG+7vJapU6dm69atb2kgAODM5rtdAICixAcAUJT4AACKEh8AQFHiAwAoSnwAAEWJDwCgKPEBABQlPgCAosQHAFCU+AAAihIfAEBR4gMAKEp8AABFiQ8AoCjxAQAUJT4AgKLEBwBQlPgAAIoSHwBAUeIDAChKfAAARYkPAKAo8QEAFCU+AICixAcAUJT4AACKEh8AQFHiAwAoSnwAAEWJDwCgKPEBABQlPgCAosQHAFCU+AAAihIfAEBR4gMAKEp8AABFiQ8AoCjxAQAUJT4AgKLEBwBQlPgAAIoSHwBAUeIDAChKfAAARYkPAKAo8QEAFCU+AICixAcAUJT4AACKEh8AQFHiAwAoSnwAAEUNKD5Wr16dyy67LGPHjs2kSZOyZMmS7Nmz56Rzfvazn6WzszMTJkzIWWedlaVLl+bgwYN1HRoAGLoGFB9bt25NZ2dnduzYka6urlSr1SxYsCA9PT3959x+++359re/nX/6p3/K1q1b8/zzz+d3fud36j44ADA0jRjIyZs2bTrp/v33359JkyZl165dufLKK3P48OF8/etfz/r163P11Vcn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", "text/plain": [ "
" ] @@ -928,8 +1033,13 @@ }, { "cell_type": "code", - "execution_count": 52, - "metadata": {}, + "execution_count": 17, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.531920Z", + "start_time": "2023-04-25T11:12:37.400372Z" + } + }, "outputs": [ { "data": { @@ -937,13 +1047,13 @@ "" ] }, - "execution_count": 52, + "execution_count": 17, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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6OzZs5o+fbrmzp2rG2+8UZMnT5bU2sOxfft2FRUVWc+ZP3++rrnmGpWUlFjHnn76ad1555364IMPrOfdfvvt2rBhg3XO7NmzNX36dP3whz9MzDcHIG7oGQEQMzfccIM++OAD/eIXv9DChQtVWVmp6dOna/PmzRGf88Ybb+j++++3elays7O1YsUK1dTU6MyZM9Z5hYWFYc8rLCykZwQYIJjACiCmMjMzde211+raa6/V3Xffrdtuu0333nuvbr755i7Pb2xs1H333acvfvGLXX4tAAMfPSMA4mrixIny+/2SpPT0dAWDwbDHp0+frqNHj2rcuHHn3dzu9reoffv2hT1v3759uuyyy+L/DQCIO3pGAMTExx9/rKVLl+qrX/2qJk+eLJ/Pp6qqKn3ve9/TkiVLJLWuqKmoqNDVV18tr9eroUOH6p577tEXvvAFjR49Wl/60pfkdrv1xhtv6MiRI/r3f/936+tv27ZNM2fO1Gc/+1k988wz2r9/v5544gm7vl0AMcQEVgAxEQgE9G//9m/61a9+pT/96U9qbm5WQUGBli5dqrvuukuDBg3SL3/5S61du1Z/+ctfNHLkSGtp786dO3X//ffr0KFDSk9P14QJE3TbbbdpxYoVklonsD722GMqKyvTrl27NHz4cH33u9/VP/zDP9j4HQOIFcIIAMfrahUOgIGDOSMAAMBWhBEAAGArJrACcDxGk4GBjZ4RAABgK8IIAACwFWEEAADYijACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBW/x98HZPml6qnJAAAAABJRU5ErkJggg==", + "image/png": "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ucyekTibMyK1hhF6RgAAcLDIF312HPaMMGcEAIAk0JQAPSNOLnxGGAEAJD2/OYE1zeaWXCgSkJxcEp4wAgBIevFaZ0RKjsXyCCMAgKQX+aKP6zkj9IwAAOBckTCSGYdhJDP97CrC9IwAAOBgcV1nJOPsMA11RgAAcLDAuXVfsuNyzkhkmIa1aQAAcKQzobBOt4QlxWfPCHVGAABwuKZgyPzveJwz0lpnJHSZPRMXYQQAkNT854ZoPKlupafG39dia50RhmkAAHAk87beOJwvIrXebhwInpFhGDa3JjYIIwCApBaI4ztppNaekbAhnWpx5lANYQQAkNT8cbwujST1SEuR23X2v51a+IwwAgBIapEv+Mz0+AwjLpfLnFjr1FojhBEAQFKL9zkj0nnzRugZkVatWqWRI0cqJydHOTk58nq9euONNy55zIYNGzR06FBlZGRoxIgRev3117vUYAAArBTvc0ak1iEkp9YaiSqM9O/fX8uWLdO+fftUXV2tP/3TP9XMmTP17rvvXnT/nTt3avbs2br77rt14MABFRUVqaioSIcOHbKk8QAAdFUgzueMSOdVYXVoGInqyt96661tni9dulSrVq3S7t27dd11112w/1NPPaVbbrlF3/ve9yRJjz32mCoqKvT0009r9erVXWg2ACBRfOoPKngmfu8CafCdliRledJsbkn7IuvTHPvipD768mRMzvGVbI88qSkxee/L6XQMDIVC2rBhg5qamuT1ei+6z65du1RSUtJm27Rp01ReXn7J9w4GgwoGg+Zzn8/X2WYCAGy09q0P9c8/e8/uZnRIlseeL+KOiMwZefznv9HjP/9NTM7x6t9N0tgBvWPy3pcTdRg5ePCgvF6vTp8+raysLG3cuFHDhg276L719fXKy8trsy0vL0/19fWXPEdZWZmWLFkSbdMAAHGm+uiXkqRUt0spkftT41BujzRN/l997G5Gu24Znq9f/+5TBc+EY3YOO//vRB1GhgwZopqaGjU2Nurll1/W3LlztW3btnYDSWeUlpa26VHx+XwqLCy07P0BAN0jMseh7FsjNGs8/4531q2j+urWUX3tbkbMRB1G0tPTNXjwYEnSuHHjtHfvXj311FN69tlnL9g3Pz9fDQ0NbbY1NDQoPz//kufweDzyeDzRNg0AEGcid6rE822zsF+X64yEw+E28zvO5/V6VVlZ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ffqoqKhIhw8fbrPP6dOntWDBAl155ZXKysrSbbfdpoaGBpta7AzLli2Ty+XS4sWLzW1cZ2t9/PHH+uu//mtdeeWV6tGjh0aMGKHq6mrzdcMw9PDDD6ugoEA9evTQ1KlT9bvf/c7GFiemUCikhx56SIMGDVKPHj30J3/yJ3rsscfarG3Cte6c7du369Zbb1Xfvn3lcrlUXl7e5vWOXNcvvvhCc+bMUU5Ojnr16qW7775bgUCg640zktT69euN9PR047nnnjPeffddY/78+UavXr2MhoYGu5uW0KZNm2Y8//zzxqFDh4yamhrjz/7sz4wBAwYYgUDA3Oeee+4xCgsLjcrKSqO6utqYOHGiMWnSJBtbndj27NljXH311cbIkSONRYsWmdu5ztb54osvjIEDBxp33nmn8fbbbxsffPCBsWXLFuPIkSPmPsuWLTNyc3ON8vJy45133jH+4i/+whg0aJBx6tQpG1ueeJYuXWpceeWVxubNm40PP/zQ2LBhg5GVlWU89dRT5j5c6855/fXXjQcffNB49dVXDUnGxo0b27zeket6yy23GKNGjTJ2795t/PrXvzYGDx5szJ49u8ttS9owcsMNNxgLFiwwn4dCIaNv375GWVmZja1ynuPHjxuSjG3bthmGYRgnTpww0tLSjA0bNpj7/OY3vzEkGbt27bKrmQnL7/cb11xzjVFRUWFMnjzZDCNcZ2v90z/9k/G1r32t3dfD4bCRn59vPPHEE+a2EydOGB6Px/jv//7v7miiY8yYMcP4zne+02bbt771LWPOnDmGYXCtrfLHYaQj1/W9994zJBl79+4193njjTcMl8tlfPzxx11qT1IO0zQ3N2vfvn2aOnWquc3tdmvq1KnatWuXjS1znsbGRknSFVdcIUnat2+fWlpa2lz7oUOHasCAAVz7TliwYIFmzJjR5npKXGervfbaaxo/frxmzZqlPn36aMyYMfqP//gP8/UPP/xQ9fX1ba53bm6uJkyYwPWO0qRJk1RZWanf/va3kqR33nlHO3bs0PTp0yVxrWOlI9d1165d6tWrl8aPH2/uM3XqVLndbr399ttdOn9CLJRntc8++0yhUEh5eXlttufl5en999+3qVXOEw6HtXjxYt14440aPny4JKm+vl7p6enq1atXm33z8vJUX19vQysT1/r167V//37t3bv3gte4ztb64IMPtGrVKpWUlOiBBx7Q3r179Q//8A9KT0/X3LlzzWt6sX9TuN7Ruf/+++Xz+TR06FClpKQoFApp6dKlmjNnjiRxrWOkI9e1vr5effr0afN6amqqrrjiii5f+6QMI+geCxYs0KFDh7Rjxw67m+I4x44d06JFi1RRUaGMjAy7m+N44XBY48eP17/8y79IksaMGaNDhw5p9erVmjt3rs2tc5af/vSnevHFF7Vu3Tpdd911qqmp0eLFi9W3b1+utYMl5TDNVVddpZSUlAvuLGhoaFB+fr5NrXKWhQsXavPmzfrVr36l/v37m9vz8/PV3NysEydOtNmfax+dffv26fjx4xo7dqxSU1OVmpqqbdu26d/+7d+UmpqqvLw8rrOFCgoKNGzYsDbbrr32WtXW1kqSeU35N6Xrvve97+n+++/XHXfcoREjRuhv/uZv9N3vfldlZWWSuNax0pHrmp+fr+PHj7d5/cyZM/riiy+6fO2TMoykp6dr3LhxqqysNLeFw2FVVlbK6/Xa2LLEZxiGFi5cqI0bN2rr1q0aNGhQm9fHjRuntLS0Ntf+8OHDqq2t5dpH4aabbtLBgwdVU1NjPsaPH685c+aY/811ts6NN954wS3qv/3tbzVw4EBJ0qBBg5Sfn9/mevt8Pr399ttc7yidPHlSbnfbr6aUlBSFw2FJXOtY6ch19Xq9OnHihPbt22fus3XrVoXDYU2YMKFrDejS9NcEtn79esPj8Rhr16413nvvPeNv//ZvjV69ehn19fV2Ny2h3XvvvUZubq5RVVVl1NXVmY+TJ0+a+9xzzz3GgAEDjK1btxrV1dWG1+s1vF6vja12hvPvpjEMrrOV9uzZY6SmphpLly41fve73xkvvvii0bNnT+OFF14w91m2bJnRq1cvY9OmTcb//M//GDNnzuR2006YO3eu0a9fP/PW3ldffdW46qqrjPvuu8/ch2vdOX6/3zhw4IBx4MABQ5Lxwx/+0Dhw4IBx9OhRwzA6dl1vueUWY8yYMcbbb79t7Nixw7jmmmu4tber/v3f/90YMGCAkZ6ebtxwww3G7t277W5SwpN00cfzzz9v7nPq1Cnj7/7u74zevXsbPXv2NP7yL//SqKurs6/RDvHHYYTrbK2f/exnxvDhww2Px2MMHTrUWLNmTZvXw+Gw8dBDDxl5eXmGx+MxbrrpJuPw4cM2tTZx+Xw+Y9GiRcaAAQOMjIwM46tf/arx4IMPGsFg0NyHa905v/rVry767/PcuXMNw+jYdf3888+N2bNnG1lZWUZOTo5x1113GX6/v8ttcxnGeWXtAAAAullSzhkBAADxgzACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBWhBEAAGArwggAALAVYQQAANiKMALAMp9++qnuvfdeDRgwQB6PR/n5+Zo2bZreeustSZLL5VJ5ebm9jQQQd1LtbgAA57jtttvU3Nysn/zkJ/rqV7+qhoYGVVZW6vPPP7e7aQDiGGvTALDEiRMn1Lt3b1VVVWny5MkXvH711Vfr6NGj5vOBAwfqD3/4gyRp06ZNWrJkid577z317dtXc+fO1YMPPqjU1LN/L7lcLj3zzDN67bXXVFVVpYKCAv3gBz/Qt7/97W75bABii2EaAJbIyspSVlaWysvLFQwGL3h97969kqTnn39edXV15vNf//rXKi4u1qJFi/Tee+/p2Wef1dq1a7V06dI2xz/00EO67bbb9M4772jOnDm644479Jvf/Cb2HwxAzNEzAsAyr7zyiubPn69Tp05p7Nixmjx5su644w6NHDlS0tkejo0bN6qoqMg8ZurUqbrppptUWlpqbnvhhRd033336ZNPPjGPu+eee7Rq1Spzn4kTJ2rs2LF65plnuufDAYgZekYAWOa2227TJ598otdee0233HKLqqqqNHbsWK1du7bdY9555x09+uijZs9KVlaW5s+fr7q6Op08edLcz+v1tjnO6/XSMwI4BBNYAVgqIyNDN998s26++WY99NBDmjdvnh555BHdeeedF90/EAhoyZIl+ta3vnXR9wLgfPSMAIipYcOGqampSZKUlpamUCjU5vWxY8fq8OHDGjx48AUPt7v1n6jdu3e3OW737t269tprY/8BAMQcPSMALPH5559r1qxZ+s53vqORI0cqOztb1dXV+sEPfqCZM2dKOntHTWVlpW688UZ5PB717t1bDz/8sP78z/9cAwYM0Le//W253W698847OnTokB5//HHz/Tds2KDx48fra1/7ml588UXt2bNHP/7xj+36uAAsxARWAJYIBoP653/+Z/3iF7/Q73//e7W0tKiwsFCzZs3SAw88oB49euhnP/uZSkpK9Ic//EH9+vUzb+3dsmWLHn30UR04cEBpaWkaOnSo5s2bp/nz50s6O4F15cqVKi8v1/bt21VQUKB//dd/1V/91V/Z+IkBWIUwAiDuXewuHADOwZwRAABgK8IIAACwFRNYAcQ9RpMBZ6NnBAAA2IowAgAAbEUYAQAAtiKMAAAAWxFGAACArQgjAADAVoQRAABgK8IIAACw1f8HeEL5KDpBvXEAAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -968,8 +1078,13 @@ }, { "cell_type": "code", - "execution_count": 53, - "metadata": {}, + "execution_count": 18, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.531920Z", + "start_time": "2023-04-25T11:12:37.496452Z" + } + }, "outputs": [], "source": [ "# save the model data (stored in the pandas gini object) to CSV\n", @@ -997,8 +1112,13 @@ }, { "cell_type": "code", - "execution_count": 54, - "metadata": {}, + "execution_count": 19, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:37.532920Z", + "start_time": "2023-04-25T11:12:37.506964Z" + } + }, "outputs": [], "source": [ "def compute_gini(model):\n", @@ -1102,14 +1222,19 @@ }, { "cell_type": "code", - "execution_count": 55, - "metadata": {}, + "execution_count": 20, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:12:57.683743Z", + "start_time": "2023-04-25T11:12:37.514528Z" + } + }, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ - "100%|███████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 245/245 [00:48<00:00, 5.10it/s]\n" + "100%|████████████████████████████████████████████████████████████████████████████████| 245/245 [00:21<00:00, 11.21it/s]\n" ] } ], @@ -1136,8 +1261,13 @@ }, { "cell_type": "code", - "execution_count": 56, - "metadata": {}, + "execution_count": 21, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:13:05.938682Z", + "start_time": "2023-04-25T11:12:57.685253Z" + } + }, "outputs": [ { "name": "stdout", @@ -1165,22 +1295,27 @@ }, { "cell_type": "code", - "execution_count": 57, - "metadata": {}, + "execution_count": 22, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:13:06.089635Z", + "start_time": "2023-04-25T11:13:05.940686Z" + } + }, "outputs": [ { "data": { "text/plain": [ - "" + "" ] }, - "execution_count": 57, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" }, { "data": { - "image/png": "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Z4SIiMh/GIx4wTkjRERE/sNgxIv0xEjEWi3wNW3EgM5KrOmJkf5sFhERUVBiMOJFiNGAgjlJANAtIHH9v2BOEteoISIi0gCDER9mTYrFuoenwWb1HIqxWS1Y9/A0rk1DRESkES6UJ2PWpFjMTLJx1V4iIqI+xGBEQYjRgMxxI/RuBhERUdDiMA0RERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemqR8HI2rVrkZCQAIvFgoyMDJSVlQk9b8uWLTAYDLjvvvt68rZEREQUhFQHI1u3bkVeXh4KCgpw4MABJCcnIzs7G/X19bLPO3nyJP7t3/4Nt956a48bS0RERMFHdTCyevVqLFiwALm5uUhKSsL69esRFhaGTZs2+XxOR0cHHnroIaxYsQJjx47tVYOJiIgouKgKRtrb21FeXo6srKyrL2A0IisrC6WlpT6f99xzzyE6OhqPPvqo0Pu0tbWhqanJ40ZERETBSVUw0tDQgI6ODsTExHjcHxMTA7vd7vU5e/fuxcaNG7Fhwwbh9yksLITVanXf4uPj1TSTiIiIAkifZtM0NzfjkUcewYYNGxAVFSX8vPz8fDgcDvetpqamD1tJREREehqkZuOoqCiEhISgrq7O4/66ujrYbLZu2584cQInT57EnDlz3Pc5nc7ONx40CMeOHcO4ceO6Pc9sNsNsNqtpGhEREQUoVT0jJpMJqampKCkpcd/ndDpRUlKCzMzMbttPnDgRhw8fxqFDh9y3e+65B//0T/+EQ4cOcfiFiIiI1PWMAEBeXh7mz5+PtLQ0pKeno6ioCC0tLcjNzQUAzJs3D3FxcSgsLITFYsGkSZM8nh8REQEA3e4nIiKigUl1MJKTk4MzZ85g+fLlsNvtSElJQXFxsXtSa3V1NYxGFnYlIiIiMQZJkiS9G6GkqakJVqsVDocD4eHhejeHiIiIBIiev9mFQURERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJERES6YjBCREREumIwQkRERLpiMEJE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0FpPtiMiIqLeG1DBSHpiJGKtFvhK4DWgM6smPdH3ujlERESkrQEVjIQYDSiYkwQA3QIS1/8L5iSx3ggREZEfDahgBABmTYrFuoenwWb1HIqxWS1M6yUiItKB6qJnwWDWpFjMTLKxAisREVE/MCCDEaBzyCZz3Ai9m0FERDTgDbhhGiIiIupfGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka56FIysXbsWCQkJsFgsyMjIQFlZmc9tv/jiC9x///1ISEiAwWBAUVFRT9tKREREQUh1MLJ161bk5eWhoKAABw4cQHJyMrKzs1FfX+91+4sXL2Ls2LFYtWoVbDZbrxtMREREwUV1MLJ69WosWLAAubm5SEpKwvr16xEWFoZNmzZ53X769Ol44YUX8MADD8BsNve6wURERBRcVAUj7e3tKC8vR1ZW1tUXMBqRlZWF0tJSzRrV1taGpqYmjxsREREFJ1XBSENDAzo6OhATE+Nxf0xMDOx2u2aNKiwshNVqdd/i4+M1e20iIiLqX/plNk1+fj4cDof7VlNTo3eTiIiIqI8MUrNxVFQUQkJCUFdX53F/XV2dppNTzWYz55cQERENEKp6RkwmE1JTU1FSUuK+z+l0oqSkBJmZmZo3joiIiIKfqp4RAMjLy8P8+fORlpaG9PR0FBUVoaWlBbm5uQCAefPmIS4uDoWFhQA6J71WVla6/3369GkcOnQIQ4cOxfjx4zX8KERERBSIVAcjOTk5OHPmDJYvXw673Y6UlBQUFxe7J7VWV1fDaLza4fLtt99i6tSp7v+/+OKLePHFF3H77bdjz549vf8EREREFNAMkiRJejdCSVNTE6xWKxwOB8LDw/VuDhEREQkQPX/3y2waIiIiGjgYjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4YjBAREZGuGIwQERGRrhiMEBERka4Y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sccekyIiIjxm+pI6zc3N0sGDB6WDBw9KAKTVq1dLBw8elE6dOiVJUmdqb0REhLR9+3bp888/l+69916vqb1Tp06VPvnkE2nv3r3ShAkTmNqrYOHChZLVapX27NnjkXZ38eJF9zaPP/64NHr0aGn37t3Sp59+KmVmZkqZmZnux11pd3fddZd06NAhqbi4WBo5ciTTHWUsXbpUev/996Wqqirp888/l5YuXSoZDAbpH//4hyRJ3Of+0jWbRpK43/vKk08+Ke3Zs0eqqqqSPvroIykrK0uKioqS6uvrJUny334PumBEkiTp97//vTR69GjJZDJJ6enp0r59+/RuUkB77733JADdbvPnz5ckqTO9d9myZVJMTIxkNpulO++8Uzp27JjHa5w9e1aaO3euNHToUCk8PFzKzc2Vmpubdfg0gcPbPgcgvfzyy+5tLl26JP3sZz+Thg8fLoWFhUk//OEPpdraWo/XOXnypPSDH/xAGjx4sBQVFSU9+eST0uXLl/38aQLHT37yE2nMmDGSyWSSRo4cKd15553uQESSuM/95dpghPu9b+Tk5EixsbGSyWSS4uLipJycHOn48ePux/213w2SJEm96tMhIiIi6oWgmjNCREREgYfBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHpisEIERER6YrBCBEREemKwQgRERHp6v8Duou0AvfZ0jgAAAAASUVORK5CYII=", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGdCAYAAADAAnMpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABKpElEQVR4nO3dfXhU1bk3/u9MwiQEyCQhkgkYSBArplEiwWBEbKuhUHjwpT3noR4RntTSUxRrm75I6iORetro8VweepQf2FS0R38WTlsVrDQVgyhoEASixFAUDC9iJgECSQgkgcx+/ogzJmRm73tn1syeSb6f68qlzOyZWbNnZu97r3Wve9k0TdNAREREZBG71Q0gIiKiwY3BCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVkq1uoGSHg8Hnz++ecYMWIEbDab1c0hIiIiAU3T0NraitGjR8NuD9z/ERXByOeff46MjAyrm0FERET9cPToUVx66aUB74+KYGTEiBEAut9MYmKixa0hIiIiiZaWFmRkZPjO44FERTDiHZpJTExkMEJERBRljFIsmMBKRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJaKiqJn1FuXR8OOuiY0trZj1Ih45GelIMbONXuIiCg6MRiJMhU19Vj+ai3qm9t9t6U741E6NxuzctItbBkREVH/cJgmilTU1GPxC7t7BSIA4G5ux+IXdqOipt6ilhEREfUfg5Eo0eXRsPzVWmh+7vPetvzVWnR5/G1BREQUuRiMRIkddU19ekR60gDUN7djR11T+BpFRESkAIORKNHYGjgQ6c92REREkYLBSJQYNSJe6XZERESRgsFIlMjPSkG6Mx6BJvDa0D2rJj8rJZzNIiIiChqDkSgRY7ehdG42APQJSLz/Lp2bzXojREQUdRiMRJFZOelYNX8yXM7eQzEuZzxWzZ/MOiNERBSVWPQsyszKSceMbBcrsBIR0YDBYCQKxdhtKLhspNXNICIiUoLDNERERGQpBiNERERkKQYjREREZCkGI0RERGQpBiNERERkKQYjREREZCkGI0RERGQpBiNERERkKQYjREREZCkGI0RERGQpBiNERERkKQYjREREZCkGI0RERGQpBiNERERkqVir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WYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVkq1uoGWKXzggfPVx3C4aazGJeSgLsKMuGI7RubdXk07KhrQmNrO0aNiEd+Vgpi7DYLWkxERDQwDcpgpGxjLcq31sGjfXnbrzfuw6LpWSiZne27raKmHstfrUV9c7vvtnRnPErnZmNWTno4m0xERDRgDbphmrKNtXj67d6BCAB4NODpt+tQtrEWQHcgsviF3b0CEQBwN7dj8Qu7UVFTH64mExERDWiDKhjpvOBB+dY63W3Kt9bhXGcXlr9aC83P/d7blr9ai66LIxoiIiIybVAFI89XHerTI3Ixjwb8ZmNtnx6RnjQA9c3t2FHXpLaBREREg9CgCkYON50VbXfopGy7xtbAAQsRERHJDKpgZFxKgmi7zJGy7UaNiA+mOURERIRBFozcVZAJo1m5dhvwy9nZSHfGI9CmNnTPqsnPSlHdRCIiokFnUAUjjlg7Fk3P0t1m0fQsDHXEoHRu9xTfiwMS779L52az3ggREZECgyoYAYCS2dn41xuz+vSQ2G3Av974ZZ2RWTnpWDV/MlzO3kMxLmc8Vs2fzDojREREitg0TYv4+aktLS1wOp1obm5GYmKikudkBVYiIqLQkp6/B2UFVqB7yObu6eMNt4ux21Bw2cgwtIiIiGhwGnTDNERERBRZ+hWMrFy5EpmZmYiPj8fUqVOxY8eOgNu+9NJLmDJlCpKSkjBs2DDk5ubi+eef73eDiYiIaGAxHYysW7cOxcXFKC0txe7duzFp0iTMnDkTjY2NfrdPSUnBgw8+iKqqKnz44YcoKipCUVER/v73vwfdeCIiIop+phNYp06dimuvvRZPPfUUAMDj8SAjIwP33Xcfli5dKnqOyZMnY86cOXjkkUdE24cigZWIiIhCS3r+NtUz0tnZiV27dqGwsPDLJ7DbUVhYiKqqKsPHa5qGyspK7N+/HzfeeGPA7To6OtDS0tLrj4iIiAYmU8HIiRMn0NXVhbS0tF63p6Wlwe12B3xcc3Mzhg8fDofDgTlz5uDJJ5/EjBkzAm5fVlYGp9Pp+8vIyDDTTCIiIooiYZlNM2LECFRXV2Pnzp349a9/jeLiYmzZsiXg9iUlJWhubvb9HT16NBzNJCIiIguYqjOSmpqKmJgYNDQ09Lq9oaEBLpcr4OPsdjsmTJgAAMjNzcW+fftQVlaGr3/96363j4uLQ1xcnJmmERERUZQy1TPicDiQl5eHyspK320ejweVlZUoKCgQP4/H40FHR4eZlyYiIqIBynQF1uLiYixcuBBTpkxBfn4+VqxYgba2NhQVFQEAFixYgDFjxqCsrAxAd/7HlClTcNlll6GjowMbN27E888/j1WrVql9J0RERBSVTAcj8+bNw/Hjx7Fs2TK43W7k5uaioqLCl9R65MgR2O1fdri0tbXhnnvuwWeffYahQ4di4sSJeOGFFzBv3jx174KIiIii1qBdKI+IiIhCKyR1RoiIiIhUYzBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJbqVzCycuVKZGZmIj4+HlOnTsWOHTsCblteXo7p06cjOTkZycnJKCws1N2eiIiIBhfTwci6detQXFyM0tJS7N69G5MmTcLMmTPR2Njod/stW7bgjjvuwJtvvomqqipkZGTgm9/8Jo4dOxZ044mIiCj62TRN08w8YOrUqbj22mvx1FNPAQA8Hg8yMjJw3333YenSpYaP7+rqQnJyMp566iksWLBA9JotLS1wOp1obm5GYmKimeYSERGR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", "text/plain": [ "
" ] @@ -1208,8 +1343,13 @@ }, { "cell_type": "code", - "execution_count": 58, - "metadata": {}, + "execution_count": 23, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:13:06.089635Z", + "start_time": "2023-04-25T11:13:06.051884Z" + } + }, "outputs": [ { "name": "stdout", @@ -1229,18 +1369,18 @@ " 1 0 1\n", " 1 1 1\n", " ... ... ...\n", - " 99 8 1\n", - " 99 9 0\n", - " 100 0 0\n", + " 99 8 2\n", + " 99 9 1\n", + " 100 0 1\n", " 100 1 1\n", " 100 2 1\n", " 100 3 1\n", " 100 4 1\n", - " 100 5 2\n", + " 100 5 1\n", " 100 6 1\n", - " 100 7 2\n", - " 100 8 1\n", - " 100 9 0\n" + " 100 7 0\n", + " 100 8 2\n", + " 100 9 1\n" ] } ], @@ -1267,8 +1407,13 @@ }, { "cell_type": "code", - "execution_count": 59, - "metadata": {}, + "execution_count": 24, + "metadata": { + "ExecuteTime": { + "end_time": "2023-04-25T11:13:06.099624Z", + "start_time": "2023-04-25T11:13:06.072169Z" + } + }, "outputs": [ { "name": "stdout", @@ -1276,30 +1421,30 @@ "text": [ " Step Gini\n", " 0 0.00\n", - " 1 0.00\n", - " 2 0.00\n", - " 3 0.00\n", - " 4 0.00\n", - " 5 0.00\n", - " 6 0.00\n", - " 7 0.00\n", - " 8 0.00\n", - " 9 0.00\n", - " 10 0.00\n", - " 11 0.00\n", + " 1 0.18\n", + " 2 0.18\n", + " 3 0.18\n", + " 4 0.18\n", + " 5 0.18\n", + " 6 0.18\n", + " 7 0.18\n", + " 8 0.18\n", + " 9 0.18\n", + " 10 0.18\n", + " 11 0.18\n", " ... ...\n", - " 89 0.18\n", - " 90 0.18\n", - " 91 0.18\n", - " 92 0.18\n", - " 93 0.18\n", - " 94 0.18\n", - " 95 0.18\n", - " 96 0.18\n", - " 97 0.18\n", - " 98 0.18\n", - " 99 0.18\n", - " 100 0.18\n" + " 89 0.54\n", + " 90 0.54\n", + " 91 0.56\n", + " 92 0.56\n", + " 93 0.56\n", + " 94 0.56\n", + " 95 0.56\n", + " 96 0.56\n", + " 97 0.56\n", + " 98 0.56\n", + " 99 0.56\n", + " 100 0.56\n" ] } ], @@ -1323,47 +1468,10 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "`virtual environment`: http://docs.python-guide.org/en/latest/dev/virtualenvs/\n", - "\n", "[Comer2014] Comer, Kenneth W. “Who Goes First? An Examination of the Impact of Activation on Outcome Behavior in AgentBased Models.” George Mason University, 2014. http://mars.gmu.edu/bitstream/handle/1920/9070/Comer_gmu_0883E_10539.pdf\n", "\n", "[Dragulescu2002] Drăgulescu, Adrian A., and Victor M. Yakovenko. “Statistical Mechanics of Money, Income, and Wealth: A Short Survey.” arXiv Preprint Cond-mat/0211175, 2002. http://arxiv.org/abs/cond-mat/0211175." ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { @@ -1383,7 +1491,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.16" + "version": "3.11.2" }, "widgets": { "state": {}, diff --git a/docs/tutorials/intro_tutorial.rst b/docs/tutorials/intro_tutorial.rst index 912305f42f2..f3a24de80e3 100644 --- a/docs/tutorials/intro_tutorial.rst +++ b/docs/tutorials/intro_tutorial.rst @@ -6,88 +6,117 @@ Tutorial Description `Mesa `__ is a Python framework for `agent-based -modeling `__. This tutorial will assist you in getting started. -Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked -through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone -find any errors, bugs, have a suggest or just are looking for clarification, `please let us +modeling `__. This +tutorial will assist you in getting started. Working through the +tutorial will help you discover the core features of Mesa. Through the +tutorial, you are walked through creating a starter-level model. +Functionality is added progressively as the process unfolds. Should +anyone find any errors, bugs, have a suggestion, or just are looking for +clarification, `let us know `__! -The premise of this tutorial is to create a a starter-level model representing agents exchanging -money. This exchange of money affects wealth. Next, *space* is added to allow agents to move -based on the change in wealth as time progresses. +The premise of this tutorial is to create a starter-level model +representing agents exchanging money. This exchange of money affects +wealth. Next, *space* is added to allow agents to move based on the +change in wealth as time progresses. -Two of Mesa's analytic tools: the *data collector* and *batch runner* will be used to examine this movement. -After that an *interactive visualization* is added which allows model viewing as it runs. - -Finally, the creation of a custom visualization module in JavaScript is explored. +Two of Mesa’s analytic tools: the *data collector* and *batch runner* +will be used to examine this movement. After that an *interactive +visualization* is added which allows model viewing as it runs. +Finally, the creation of a custom visualization module in JavaScript is +explored. Model Description ------------------------- +----------------- + +This is a starter-level simulated agent-based economy. In an agent-based +economy, the behavior of an individual economic agent, such as a +consumer or producer, is studied in a market environment. This model is +drawn from the field econophysics, specifically a paper prepared by +Drăgulescu et al. for additional information on the modeling assumptions +used in this model. [Drăgulescu, 2002]. -The tutorial model is a very simple simulated agent-based economy, drawn -from econophysics and presenting a statistical mechanics approach to -wealth distribution [Dragulescu2002]. The rules of our tutorial model: +The assumption that govern this model are: 1. There are some number of agents. 2. All agents begin with 1 unit of money. 3. At every step of the model, an agent gives 1 unit of money (if they have it) to some other agent. -Despite its simplicity, this model yields results that are often -unexpected to those not familiar with it. For our purposes, it also -easily demonstrates Mesa's core features. +Even as a starter-level model the yielded results are both interesting +and unexpected to individuals unfamiliar with it the specific topic. As +such, this model is a good starting point to examine Mesa’s core +features. -Let's get started. +Tutorial Setup +~~~~~~~~~~~~~~ -Installation -~~~~~~~~~~~~ +Create and activate a `virtual +environment `__. +*Python version 3.8 or higher is required*. + +Install Mesa: -To start, install Mesa. We recommend doing this in a `virtual -environment `__, -but make sure your environment is set up with Python 3. Mesa requires -Python3 and does not work in Python 2 environments. +.. code:: bash + + python3 -m pip install mesa -To install Mesa, simply: +Install Jupyter Notebook (optional): .. code:: bash - pip install mesa + python3 -m pip install jupyter -When you do that, it will install Mesa itself, as well as any -dependencies that aren't in your setup yet. Additional dependencies -required by this tutorial can be found in the -**examples/boltzmann_wealth_model/requirements.txt** file, which can be -installed directly form the github repository by running: +Install matplotlib: .. code:: bash - pip install -r https://raw.githubusercontent.com/projectmesa/mesa-examples/main/examples/Boltzmann_Wealth_Model/requirements.txt + python3 -m pip install matplotlib + +Building the Sample Model +------------------------- + +After Mesa is installed a model can be built. A jupyter notebook is +recommended for this tutorial, this allows for small segments of codes +to be examined one at a time. As an option this can be created using +python script files. -| This will install the dependencies listed in the requirements.txt file - which are: -| - jupyter (Ipython interactive notebook) -| - matplotlib (Python's visualization library) -| - mesa (this ABM library – if not installed) -| - numpy (Python's numerical python library) +**Good Practice:** Place a model in its own folder/directory. This is +not specifically required for the starter_model, but as other models +become more complicated and expand multiple python scripts, +documentation, discussions and notebooks may be added. -Building a sample model ------------------------ +Create New Folder/Directory +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +- Using operating system commands create a new folder/directory named + ‘starter_model’. + +- Change into the new folder/directory. + +Creating Model With Jupyter Notebook +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Write the model interactively in `Jupyter +Notebook `__ cells. + +Start Jupyter Notebook: + +.. code:: bash -Once Mesa is installed, you can start building our model. You can write -models in two different ways: + jupyter notebook -1. Write the code in its own file with your favorite text editor, or -2. Write the model interactively in `Jupyter - Notebook `__ cells. +Create a new Notebook named ``money_model.ipynb`` (or whatever you want +to call it). -Either way, it's good practice to put your model in its own folder – -especially if the project will end up consisting of multiple files (for -example, Python files for the model and the visualization, a Notebook -for analysis, and a Readme with some documentation and discussion). +Creating Model With Script File (IDE, Text Editor, Colab, etc.) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -Begin by creating a folder, and either launch a Notebook or create a new -Python source file. We will use the name ``money_model.py`` here. +Create a new file called ``money_model.py`` (or whatever you want to +call it) + +*Code will be added as the tutorial progresses.* Setting up the model ~~~~~~~~~~~~~~~~~~~~ @@ -98,7 +127,7 @@ model-level attributes, manages the agents, and generally handles the global level of our model. Each instantiation of the model class will be a specific model run. Each model will contain multiple agents, all of which are instantiations of the agent class. Both the model and agent -classes are child classes of Mesa's generic ``Model`` and ``Agent`` +classes are child classes of Mesa’s generic ``Model`` and ``Agent`` classes. This is seen in the code with ``class MoneyModel(mesa.Model)`` or ``class MoneyAgent(mesa.Agent)``. If you want you can specifically the class being imported by looking at the @@ -158,13 +187,13 @@ model uses, and see whether it changes the model behavior. This may not seem important, but scheduling patterns can have an impact on your results [Comer2014]. -For now, let's use one of the simplest ones: ``RandomActivation``\ \*, +For now, let’s use one of the simplest ones: ``RandomActivation``\ \*, which activates all the agents once per step, in random order. Every agent is expected to have a ``step`` method. The step method is the action the agent takes when it is activated by the model schedule. We add an agent to the schedule using the ``add`` method; when we call the -schedule's ``step`` method, the model shuffles the order of the agents, -then activates and executes each agent's ``step`` method. +schedule’s ``step`` method, the model shuffles the order of the agents, +then activates and executes each agent’s ``step`` method. \*Unlike ``mesa.model`` or ``mesa.agent``, ``mesa.time`` has multiple classes (e.g. ``RandomActivation``, ``StagedActivation`` etc). To ensure @@ -209,8 +238,8 @@ this: """Advance the model by one step.""" self.schedule.step() -At this point, we have a model which runs – it just doesn't do anything. -You can see for yourself with a few easy lines. If you've been working +At this point, we have a model which runs – it just doesn’t do anything. +You can see for yourself with a few easy lines. If you’ve been working in an interactive session, you can create a model object directly. Otherwise, you need to open an interactive session in the same directory as your source code file, and import the classes. For example, if your @@ -230,16 +259,16 @@ Then create the model object, and run it for one step: .. parsed-literal:: - Hi, I am agent 6. + Hi, I am agent 5. Hi, I am agent 2. - Hi, I am agent 1. - Hi, I am agent 0. Hi, I am agent 4. - Hi, I am agent 5. - Hi, I am agent 3. - Hi, I am agent 9. Hi, I am agent 8. + Hi, I am agent 0. + Hi, I am agent 1. Hi, I am agent 7. + Hi, I am agent 9. + Hi, I am agent 3. + Hi, I am agent 6. Exercise @@ -256,12 +285,12 @@ Now we just need to have the agents do what we intend for them to do: check their wealth, and if they have the money, give one unit of it away to another random agent. To allow the agent to choose another agent at random, we use the ``model.random`` random-number generator. This works -just like Python's ``random`` module, but with a fixed seed set when the +just like Python’s ``random`` module, but with a fixed seed set when the model is instantiated, that can be used to replicate a specific model run later. To pick an agent at random, we need a list of all agents. Notice that -there isn't such a list explicitly in the model. The scheduler, however, +there isn’t such a list explicitly in the model. The scheduler, however, does have an internal list of all the agents it is scheduled to activate. @@ -286,21 +315,21 @@ With that in mind, we rewrite the agent ``step`` method, like this: Running your first model ~~~~~~~~~~~~~~~~~~~~~~~~ -With that last piece in hand, it's time for the first rudimentary run of +With that last piece in hand, it’s time for the first rudimentary run of the model. -If you've written the code in its own file (``money_model.py`` or a +If you’ve written the code in its own file (``money_model.py`` or a different name), launch an interpreter in the same directory as the file (either the plain Python command-line interpreter, or the IPython interpreter), or launch a Jupyter Notebook there. Then import the classes you created. (If you wrote the code in a Notebook, obviously -this step isn't necessary). +this step isn’t necessary). .. code:: python from money_model import * -Now let's create a model with 10 agents, and run it for 10 steps. +Now let’s create a model with 10 agents, and run it for 10 steps. .. code:: ipython3 @@ -309,11 +338,11 @@ Now let's create a model with 10 agents, and run it for 10 steps. model.step() Next, we need to get some data out of the model. Specifically, we want -to see the distribution of the agent's wealth. We can get the wealth +to see the distribution of the agent’s wealth. We can get the wealth values with list comprehension, and then use matplotlib (or another graphics library) to visualize the data in a histogram. -If you are running from a text editor or IDE, you'll also need to add +If you are running from a text editor or IDE, you’ll also need to add this line, to make the graph appear. .. code:: python @@ -336,17 +365,17 @@ this line, to make the graph appear. .. parsed-literal:: - (array([2., 0., 0., 0., 0., 6., 0., 0., 0., 2.]), - array([0. , 0.2, 0.4, 0.6, 0.8, 1. , 1.2, 1.4, 1.6, 1.8, 2. ]), + (array([5., 0., 0., 2., 0., 0., 1., 0., 0., 2.]), + array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7, 3. ]), ) -.. image:: intro_tutorial_files/output_19_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_19_1.png -You'll should see something like the distribution above. Yours will +You’ll should see something like the distribution above. Yours will almost certainly look at least slightly different, since each run of the model is random, after all. @@ -375,21 +404,21 @@ can do this with a nested for loop: .. parsed-literal:: - (array([433., 304., 150., 71., 29., 13.]), - array([0, 1, 2, 3, 4, 5, 6]), + (array([416., 324., 155., 68., 25., 12.]), + array([0., 1., 2., 3., 4., 5., 6.]), ) -.. image:: intro_tutorial_files/output_22_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_22_1.png This runs 100 instantiations of the model, and runs each for 10 steps. (Notice that we set the histogram bins to be integers, since agents can only have whole numbers of wealth). This distribution looks a lot smoother. By running the model 100 times, we smooth out some of the -‘noise'of randomness, and get to the model's overall expected behavior. +‘noise’ of randomness, and get to the model’s overall expected behavior. This outcome might be surprising. Despite the fact that all agents, on average, give and receive one unit of money every step, the model @@ -411,9 +440,9 @@ those on the left edge, and the top to the bottom. This prevents some cells having fewer neighbors than others, or agents being able to go off the edge of the environment. -Let's add a simple spatial element to our model by putting our agents on +Let’s add a simple spatial element to our model by putting our agents on a grid and make them walk around at random. Instead of giving their unit -of money to any random agent, they'll give it to an agent on the same +of money to any random agent, they’ll give it to an agent on the same cell. Mesa has two main types of grids: ``SingleGrid`` and ``MultiGrid``\ \*. @@ -428,9 +457,9 @@ Similar to ``mesa.time`` context is retained with `mesa.space `__ We instantiate a grid with width and height parameters, and a boolean as -to whether the grid is toroidal. Let's make width and height model +to whether the grid is toroidal. Let’s make width and height model parameters, in addition to the number of agents, and have the grid -always be toroidal. We can place agents on a grid with the grid's +always be toroidal. We can place agents on a grid with the grid’s ``place_agent`` method, which takes an agent and an (x, y) tuple of the coordinates to place the agent. @@ -454,16 +483,16 @@ coordinates to place the agent. y = self.random.randrange(self.grid.height) self.grid.place_agent(a, (x, y)) -Under the hood, each agent's position is stored in two ways: the agent +Under the hood, each agent’s position is stored in two ways: the agent is contained in the grid in the cell it is currently in, and the agent has a ``pos`` variable with an (x, y) coordinate tuple. The ``place_agent`` method adds the coordinate to the agent automatically. -Now we need to add to the agents'behaviors, letting them move around +Now we need to add to the agents’ behaviors, letting them move around and only give money to other agents in the same cell. -First let's handle movement, and have the agents move to a neighboring -cell. The grid object provides a ``move_agent`` method, which like you'd +First let’s handle movement, and have the agents move to a neighboring +cell. The grid object provides a ``move_agent`` method, which like you’d imagine, moves an agent to a given cell. That still leaves us to get the possible neighboring cells to move to. There are a couple ways to do this. One is to use the current coordinates, and loop over all @@ -477,7 +506,7 @@ coordinates +/- 1 away from it. For example: for dy in [-1, 0, 1]: neighbors.append((x+dx, y+dy)) -But there's an even simpler way, using the grid's built-in +But there’s an even simpler way, using the grid’s built-in ``get_neighborhood`` method, which returns all the neighbors of a given cell. This method can get two types of cell neighborhoods: `Moore `__ (includes @@ -486,7 +515,7 @@ Neumann `__\ (only up/down/left/right). It also needs an argument as to whether to include the center cell itself as one of the neighbors. -With that in mind, the agent's ``move`` method looks like this: +With that in mind, the agent’s ``move`` method looks like this: .. code:: python @@ -502,7 +531,7 @@ With that in mind, the agent's ``move`` method looks like this: Next, we need to get all the other agents present in a cell, and give one of them some money. We can get the contents of one or more cells -using the grid's ``get_cell_list_contents`` method, or by accessing a +using the grid’s ``get_cell_list_contents`` method, or by accessing a cell directly. The method accepts a list of cell coordinate tuples, or a single tuple if we only care about one cell. @@ -517,7 +546,7 @@ single tuple if we only care about one cell. other.wealth += 1 self.wealth -= 1 -And with those two methods, the agent's ``step`` method becomes: +And with those two methods, the agent’s ``step`` method becomes: .. code:: python @@ -578,7 +607,7 @@ Now, putting that all together should look like this: def step(self): self.schedule.step() -Let's create a model with 50 agents on a 10x10 grid, and run it for 20 +Let’s create a model with 50 agents on a 10x10 grid, and run it for 20 steps. .. code:: ipython3 @@ -587,11 +616,11 @@ steps. for i in range(20): model.step() -Now let's use matplotlib and numpy to visualize the number of agents +Now let’s use matplotlib and numpy to visualize the number of agents residing in each cell. To do that, we create a numpy array of the same -size as the grid, filled with zeros. Then we use the grid object's +size as the grid, filled with zeros. Then we use the grid object’s ``coord_iter()`` feature, which lets us loop over every cell in the -grid, giving us each cell's coordinates and contents in turn. +grid, giving us each cell’s coordinates and contents in turn. .. code:: ipython3 @@ -613,21 +642,21 @@ grid, giving us each cell's coordinates and contents in turn. .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_32_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_32_1.png Collecting Data ~~~~~~~~~~~~~~~ -So far, at the end of every model run, we've had to go and write our own -code to get the data out of the model. This has two problems: it isn't +So far, at the end of every model run, we’ve had to go and write our own +code to get the data out of the model. This has two problems: it isn’t very efficient, and it only gives us end results. If we wanted to know -the wealth of each agent at each step, we'd have to add that to the loop +the wealth of each agent at each step, we’d have to add that to the loop of executing steps, and figure out some way to store the data. Since one of the main goals of agent-based modeling is generating data @@ -641,19 +670,19 @@ collector along with a function for collecting them. Model-level collection functions take a model object as an input, while agent-level collection functions take an agent object as an input. Both then return a value computed from the model or each agent at their current state. -When the data collector's ``collect`` method is called, with a model +When the data collector’s ``collect`` method is called, with a model object as its argument, it applies each model-level collection function to the model, and stores the results in a dictionary, associating the current value with the current step of the model. Similarly, the method applies each agent-level collection function to each agent currently in the schedule, associating the resulting value with the step of the -model, and the agent's ``unique_id``. +model, and the agent’s ``unique_id``. -Let's add a DataCollector to the model with -`mesa.DataCollector `__, +Let’s add a DataCollector to the model with +```mesa.DataCollector`` `__, and collect two variables. At the agent level, we want to collect every -agent's wealth at every step. At the model level, let's measure the -model's `Gini +agent’s wealth at every step. At the model level, let’s measure the +model’s `Gini Coefficient `__, a measure of wealth inequality. @@ -683,10 +712,15 @@ measure of wealth inequality. def give_money(self): cellmates = self.model.grid.get_cell_list_contents([self.pos]) + cellmates.pop( + cellmates.index(self) + ) # Ensure agent is not giving money to itself if len(cellmates) > 1: other = self.random.choice(cellmates) other.wealth += 1 self.wealth -= 1 + if other == self: + print("I JUST GAVE MONEY TO MYSELF HEHEHE!") def step(self): self.move() @@ -720,7 +754,7 @@ measure of wealth inequality. self.schedule.step() At every step of the model, the datacollector will collect and store the -model-level current Gini coefficient, as well as each agent's wealth, +model-level current Gini coefficient, as well as each agent’s wealth, associating each with the current step. We run the model just as we did above. Now is when an interactive @@ -753,12 +787,12 @@ To get the series of Gini coefficients as a pandas DataFrame: .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_38_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_38_1.png Similarly, we can get the agent-wealth data: @@ -828,9 +862,9 @@ Similarly, we can get the agent-wealth data: -You'll see that the DataFrame's index is pairings of model step and +You’ll see that the DataFrame’s index is pairings of model step and agent ID. You can analyze it the way you would any other DataFrame. For -example, to get a histogram of agent wealth at the model's end: +example, to get a histogram of agent wealth at the model’s end: .. code:: ipython3 @@ -842,12 +876,12 @@ example, to get a histogram of agent wealth at the model's end: .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_42_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_42_1.png Or to plot the wealth of a given agent (in this example, agent 14): @@ -862,12 +896,12 @@ Or to plot the wealth of a given agent (in this example, agent 14): .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_44_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_44_1.png You can also use pandas to export the data to a CSV (comma separated @@ -889,11 +923,12 @@ directory. After you run the code below you will see two files appear Batch Run ~~~~~~~~~ -Like we mentioned above, you usually won't run a model only once, but +Like we mentioned above, you usually won’t run a model only once, but multiple times, with fixed parameters to find the overall distributions the model generates, and with varying parameters to analyze how they -drive the model's outputs and behaviors. Instead of needing to write -nested for-loops for each model, Mesa provides a `batch_run `__ +drive the model’s outputs and behaviors. Instead of needing to write +nested for-loops for each model, Mesa provides a +```batch_run`` `__ function which automates it for you. The batch runner also requires an additional variable ``self.running`` @@ -977,7 +1012,7 @@ of the model with each number of agents, and to run each for 100 steps. We want to keep track of 1. the Gini coefficient value and -2. the individual agent's wealth development. +2. the individual agent’s wealth development. Since for the latter changes at each time step might be interesting, we set ``data_collection_period = 1``. @@ -991,9 +1026,9 @@ iteration). **Note for Windows OS users:** If you are running this tutorial in Jupyter, make sure that you set ``number_processes = 1`` (single process). If ``number_processes`` is greater than 1, it is less -straightforward to set up. You can read `Mesa's collection of useful +straightforward to set up. You can read `Mesa’s collection of useful snippets `__, -in ‘Using multi-process ``batch_run`` on Windows'section for how to do +in ‘Using multi-process ``batch_run`` on Windows’ section for how to do it. .. code:: ipython3 @@ -1013,7 +1048,7 @@ it. .. parsed-literal:: - 245it [00:34, 7.02it/s] + 100%|████████████████████████████████████████████████████████████████████████████████| 245/245 [00:21<00:00, 11.21it/s] To further analyze the return of the ``batch_run`` function, we convert @@ -1055,15 +1090,15 @@ calling the batch run. .. parsed-literal:: - + -.. image:: intro_tutorial_files/output_57_1.png +.. image:: intro_tutorial_files%5Cintro_tutorial_57_1.png -Second, we want to display the agent's wealth at each time step of one +Second, we want to display the agent’s wealth at each time step of one specific episode. To do this, we again filter our large data frame, this time with a fixed number of agents and only for a specific iteration of that population. To print the results, we convert the filtered data @@ -1102,20 +1137,20 @@ can use the ``to_html()`` function which takes the same arguments as 0 7 1 0 8 1 0 9 1 - 1 0 2 + 1 0 1 1 1 1 - ... ... ... - 99 8 4 + ... ... ... + 99 8 2 99 9 1 - 100 0 0 - 100 1 0 + 100 0 1 + 100 1 1 100 2 1 - 100 3 0 + 100 3 1 100 4 1 100 5 1 - 100 6 0 - 100 7 2 - 100 8 4 + 100 6 1 + 100 7 0 + 100 8 2 100 9 1 @@ -1140,18 +1175,18 @@ episode. 2 0.18 3 0.18 4 0.18 - 5 0.32 - 6 0.32 - 7 0.32 - 8 0.42 - 9 0.42 - 10 0.42 - 11 0.42 - ... ... - 89 0.66 - 90 0.66 - 91 0.66 - 92 0.66 + 5 0.18 + 6 0.18 + 7 0.18 + 8 0.18 + 9 0.18 + 10 0.18 + 11 0.18 + ... ... + 89 0.54 + 90 0.54 + 91 0.56 + 92 0.56 93 0.56 94 0.56 95 0.56 @@ -1169,9 +1204,6 @@ This document is a work in progress. If you see any errors, exclusions or have any problems please contact `us `__. -``virtual environment``: -http://docs.python-guide.org/en/latest/dev/virtualenvs/ - [Comer2014] Comer, Kenneth W. “Who Goes First? An Examination of the Impact of Activation on Outcome Behavior in AgentBased Models.” George Mason University, 2014. diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_19_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_19_1.png new file mode 100644 index 0000000000000000000000000000000000000000..844165fba4ef1b382e1f8ef9b3f472953c63954d GIT binary patch literal 5734 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 F{{V-b)6xI{ literal 0 HcmV?d00001 diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_32_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_32_1.png new file mode 100644 index 0000000000000000000000000000000000000000..f687da712492a707a06926cc8d52210e88881f56 GIT binary patch literal 10048 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 ZNcDv(MX~YRho{64Nin%Q*|+XL{(pV8`V{~G literal 0 HcmV?d00001 diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_38_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_38_1.png new file mode 100644 index 0000000000000000000000000000000000000000..9118c60cc4b6c9888f0abc2cba149c6751744bfe GIT binary patch literal 20884 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 Z%w0AlCk1taLs-b_Tib6+zdrN*e*jB)HGu#C literal 0 HcmV?d00001 diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_44_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_44_1.png new file mode 100644 index 0000000000000000000000000000000000000000..aa2c9e026b3e3534ea7754a92b50bb88fb52ba42 GIT binary patch literal 13631 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 literal 0 HcmV?d00001 diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_57_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_57_1.png new file mode 100644 index 0000000000000000000000000000000000000000..bafdc71c7ca43fb50776dd7b7066b252fddc9218 GIT binary patch literal 19255 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 literal 0 HcmV?d00001 From beb9c484f10a7b01266a90f544e2ed4213ef636a Mon Sep 17 00:00:00 2001 From: Jatin Khilnani <43829573+jatinkhilnani@users.noreply.github.com> Date: Tue, 25 Apr 2023 13:52:53 -0400 Subject: [PATCH 087/214] Update pip statement for zsh & advanced tutorial notebook (#1648) --- docs/tutorials/MoneyModel.py | 65 ++++++++++ docs/tutorials/adv_tutorial.ipynb | 198 ++++++++++++++++++------------ 2 files changed, 186 insertions(+), 77 deletions(-) create mode 100644 docs/tutorials/MoneyModel.py diff --git a/docs/tutorials/MoneyModel.py b/docs/tutorials/MoneyModel.py new file mode 100644 index 00000000000..d6b8de0d66e --- /dev/null +++ b/docs/tutorials/MoneyModel.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python3 + + +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 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() + + +class MoneyModel(mesa.Model): + """A model with some number of agents.""" + + def __init__(self, N, width, height): + self.num_agents = N + self.grid = mesa.space.MultiGrid(width, height, True) + self.schedule = mesa.time.RandomActivation(self) + + # 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.datacollector = mesa.DataCollector( + model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} + ) + + def step(self): + self.datacollector.collect(self) + self.schedule.step() diff --git a/docs/tutorials/adv_tutorial.ipynb b/docs/tutorials/adv_tutorial.ipynb index db213e34e27..6610db9d6c6 100644 --- a/docs/tutorials/adv_tutorial.ipynb +++ b/docs/tutorials/adv_tutorial.ipynb @@ -8,6 +8,7 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -15,7 +16,7 @@ "\n", "So far, we've built a model, run it, and analyzed some output afterwards. However, one of the advantages of agent-based models is that we can often watch them run step by step, potentially spotting unexpected patterns, behaviors or bugs, or developing new intuitions, hypotheses, or insights. Other times, watching a model run can explain it to an unfamiliar audience better than static explanations. Like many ABM frameworks, Mesa allows you to create an interactive visualization of the model. In this section we'll walk through creating a visualization using built-in components, and (for advanced users) how to create a new visualization element.\n", "\n", - "**Note for Jupyter users: Due to conflicts with the tornado server Mesa uses and Jupyter, the interactive browser of your model will load but likely not work. This will require you to use run the code from .py files. The Mesa development team is working to develop a** [Jupyter compatible interface](https://github.com/projectmesa/mesa/issues/1363).**\n", + "**Note for Jupyter users: Due to conflicts with the tornado server Mesa uses and Jupyter, the interactive browser of your model will load but likely not work. This will require you to use run the code from .py files. The Mesa development team is working to develop a** [Jupyter compatible interface](https://github.com/projectmesa/mesa/issues/1363).\n", "\n", "First, a quick explanation of how Mesa's interactive visualization works. Visualization is done in a browser window, using JavaScript to draw the different things being visualized at each step of the model. To do this, Mesa launches a small web server, which runs the model, turns each step into a JSON object (essentially, structured plain text) and sends those steps to the browser.\n", "\n", @@ -23,38 +24,39 @@ ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### Grid Visualization\n", "\n", - "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file; for example, `MoneyModel.py`. Next, either in the same file or in a new one (e.g. `MoneyModel_Viz.py`) import the server class and the Canvas Grid class (so-called because it uses HTML5 canvas to draw a grid). If you're in a new file, you'll also need to import the actual model object." + "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n", + "Next, in a new source file (e.g. `MoneyModel_Viz.py`) include the code shown in the following cells to run and avoid Jupyter compatibility issue." ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "metadata": { "tags": [] }, "outputs": [], "source": [ - "import mesa\n", - "\n", "# If MoneyModel.py is where your code is:\n", - "# from MoneyModel import MoneyModel" + "from MoneyModel import mesa, MoneyModel" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "`CanvasGrid` works by looping over every cell in a grid, and generating a portrayal for every agent it finds. A portrayal is a dictionary (which can easily be turned into a JSON object) which tells the JavaScript side how to draw it. The only thing we need to provide is a function which takes an agent, and returns a portrayal object. Here's the simplest one: it'll draw each agent as a red, filled circle which fills half of each cell." + "Mesa's `CanvasGrid` visualization class works by looping over every cell in a grid, and generating a portrayal for every agent it finds. A portrayal is a dictionary (which can easily be turned into a JSON object) which tells the JavaScript side how to draw it. The only thing we need to provide is a function which takes an agent, and returns a portrayal object. Here's the simplest one: it'll draw each agent as a red, filled circle which fills half of each cell." ] }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": { "tags": [] }, @@ -80,7 +82,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "metadata": { "tags": [] }, @@ -90,63 +92,49 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "attachments": {}, + "cell_type": "markdown", "metadata": {}, - "outputs": [], "source": [ - "\"\"\"\n", - "The full code should now look like:\n", - "\"\"\"\n", - "# from MoneyModel import *\n", - "import mesa\n", - "\n", - "\n", - "def agent_portrayal(agent):\n", - " portrayal = {\n", - " \"Shape\": \"circle\",\n", - " \"Filled\": \"true\",\n", - " \"Layer\": 0,\n", - " \"Color\": \"red\",\n", - " \"r\": 0.5,\n", - " }\n", - " return portrayal\n", + "Now we create and launch the actual server. We do this with the following arguments:\n", "\n", + "* The model class we're running and visualizing; in this case, `MoneyModel`.\n", + "* A list of module objects to include in the visualization; here, just `[grid]`\n", + "* The title of the model: \"Money Model\"\n", + "* Any inputs or arguments for the model itself. In this case, 100 agents, and height and width of 10.\n", "\n", - "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)\n", + "Once we create the server, we set the port (use default 8521 here) for it to listen on (you can treat this as just a piece of the URL you’ll open in the browser). " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ "server = mesa.visualization.ModularServer(\n", " MoneyModel, [grid], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", ")\n", - "server.port = 8521 # The default\n", - "server.launch()" + "server.port = 8521 # the default" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Now we create and launch the actual server. We do this with the following arguments:\n", - "\n", - "* The model class we're running and visualizing; in this case, `MoneyModel`.\n", - "* A list of module objects to include in the visualization; here, just `[grid]`\n", - "* The title of the model: \"Money Model\"\n", - "* Any inputs or arguments for the model itself. In this case, 100 agents, and height and width of 10.\n", - "\n", - "Once we create the server, we set the port for it to listen on (you can treat this as just a piece of the URL you'll open in the browser). Finally, when you're ready to run the visualization, use the server's `launch()` method.\n", - "\n", - "```python\n", - "server = ModularServer(MoneyModel,\n", - " [grid],\n", - " \"Money Model\",\n", - " {\"N\":100, \"width\":10, \"height\":10})\n", - "server.port = 8521 # The default\n", - "server.launch()\n", - "```\n", - "The full code should now look like:\n", + "Finally, when you’re ready to run the visualization, use the server’s launch() method." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The full code for source file `MoneyModel_Viz.py` should now look like:\n", "\n", "```python\n", - "from MoneyModel import *\n", - "import mesa\n", + "from MoneyModel import mesa, MoneyModel\n", "\n", "\n", "def agent_portrayal(agent):\n", @@ -158,9 +146,7 @@ " return portrayal\n", "\n", "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)\n", - "server = mesa.visualization.ModularServer(\n", - " MoneyModel, [grid], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", - ")server = ModularServer(MoneyModel,\n", + "server = mesa.visualization.ModularServer(MoneyModel,\n", " [grid],\n", " \"Money Model\",\n", " {\"N\":100, \"width\":10, \"height\":10})\n", @@ -173,16 +159,17 @@ "\n", "![Empty Visualization](files/viz_empty.png)\n", "\n", - "Click the 'reset' button on the control panel, and you should see the grid fill up with red circles, representing agents.\n", + "Click the `Reset` button on the control panel, and you should see the grid fill up with red circles, representing agents.\n", "\n", "![Redcircles Visualization](files/viz_redcircles.png)\n", "\n", - "Click 'step' to advance the model by one step, and the agents will move around. Click 'run' and the agents will keep moving around, at the rate set by the 'fps' (frames per second) slider at the top. Try moving it around and see how the speed of the model changes. Pressing 'pause' will (as you'd expect) pause the model; presing 'run' again will restart it. Finally, 'reset' will start a new instantiation of the model.\n", + "Click `Step` to advance the model by one step, and the agents will move around. Click `Start` and the agents will keep moving around, at the rate set by the 'fps' (frames per second) slider at the top. Try moving it around and see how the speed of the model changes. Pressing `Stop` will pause the model; presing `Start` again will restart it. Finally, `Reset` will start a new instantiation of the model.\n", "\n", "To stop the visualization server, go back to the terminal where you launched it, and press Control+c." ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -190,13 +177,17 @@ "\n", "In the visualization above, all we could see is the agents moving around -- but not how much money they had, or anything else of interest. Let's change it so that agents who are broke (wealth 0) are drawn in grey, smaller, and above agents who still have money.\n", "\n", - "To do this, we go back to our `agent_portrayal` code and add some code to change the portrayal based on the agent properties.\n", - "\n", - "```python\n", + "To do this, we go back to our `agent_portrayal` code and add some code to change the portrayal based on the agent properties and launch the server again." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ "def agent_portrayal(agent):\n", - " portrayal = {\"Shape\": \"circle\",\n", - " \"Filled\": \"true\",\n", - " \"r\": 0.5}\n", + " portrayal = {\"Shape\": \"circle\", \"Filled\": \"true\", \"r\": 0.5}\n", "\n", " if agent.wealth > 0:\n", " portrayal[\"Color\"] = \"red\"\n", @@ -205,15 +196,21 @@ " portrayal[\"Color\"] = \"grey\"\n", " portrayal[\"Layer\"] = 1\n", " portrayal[\"r\"] = 0.2\n", - " return portrayal\n", - "```\n", - "\n", - "Now launch the server again - this will open a new browser window pointed at the updated visualization. Initially it looks the same, but advance the model and smaller grey circles start to appear. Note that since the zero-wealth agents have a higher layer number, they are drawn on top of the red agents.\n", + " return portrayal" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will open a new browser window pointed at the updated visualization. Initially it looks the same, but advance the model and smaller grey circles start to appear. Note that since the zero-wealth agents have a higher layer number, they are drawn on top of the red agents.\n", "\n", "![Greycircles Visualization](files/viz_greycircles.png)" ] }, { + "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -223,20 +220,67 @@ "\n", "The basic chart pulls data from the model's DataCollector, and draws it as a line graph using the [Charts.js](http://www.chartjs.org/) JavaScript libraries. We instantiate a chart element with a list of series for the chart to track. Each series is defined in a dictionary, and has a `Label` (which must match the name of a model-level variable collected by the DataCollector) and a `Color` name. We can also give the chart the name of the DataCollector object in the model.\n", "\n", - "Finally, we add the chart to the list of elements in the server. The elements are added to the visualization in the order they appear, so the chart will appear underneath the grid.\n", + "Finally, we add the chart to the list of elements in the server. The elements are added to the visualization in the order they appear, so the chart will appear underneath the grid." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "chart = mesa.visualization.ChartModule(\n", + " [{\"Label\": \"Gini\", \"Color\": \"Black\"}], data_collector_name=\"datacollector\"\n", + ")\n", + "\n", + "server = mesa.visualization.ModularServer(\n", + " MoneyModel, [grid, chart], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Launch the visualization and start a model run, either by launching the server here or through the full code for source file `MoneyModel_Viz.py`.\n", "\n", "```python\n", - "chart = mesa.visualization.ChartModule([{\"Label\": \"Gini\", \n", - " \"Color\": \"Black\"}],\n", - " data_collector_name='datacollector')\n", + "from MoneyModel import mesa, MoneyModel\n", "\n", - "server = mesa.visualization.ModularServer(MoneyModel, \n", - " [grid, chart], \n", - " \"Money Model\", \n", - " {\"N\":100, \"width\":10, \"height\":10})\n", - "```\n", "\n", - "Launch the visualization and start a model run, and you'll see a line chart underneath the grid. Every step of the model, the line chart updates along with the grid. Reset the model, and the chart resets too.\n", + "def agent_portrayal(agent):\n", + " portrayal = {\"Shape\": \"circle\", \"Filled\": \"true\", \"r\": 0.5}\n", + "\n", + " if agent.wealth > 0:\n", + " portrayal[\"Color\"] = \"red\"\n", + " portrayal[\"Layer\"] = 0\n", + " else:\n", + " portrayal[\"Color\"] = \"grey\"\n", + " portrayal[\"Layer\"] = 1\n", + " portrayal[\"r\"] = 0.2\n", + " return portrayal\n", + "\n", + "\n", + "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)\n", + "chart = mesa.visualization.ChartModule(\n", + " [{\"Label\": \"Gini\", \"Color\": \"Black\"}], data_collector_name=\"datacollector\"\n", + ")\n", + "\n", + "server = mesa.visualization.ModularServer(\n", + " MoneyModel, [grid, chart], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", + ")\n", + "server.port = 8521 # The default\n", + "server.launch()\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You'll see a line chart underneath the grid. Every step of the model, the line chart updates along with the grid. Reset the model, and the chart resets too.\n", "\n", "![Chart Visualization](files/viz_chart.png)\n", "\n", @@ -468,7 +512,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.6" + "version": "3.11.3" } }, "nbformat": 4, From 15ed2b6c497f660870213f50610f534b9a3abfed Mon Sep 17 00:00:00 2001 From: Martin Breuss Date: Wed, 26 Apr 2023 03:41:59 +0200 Subject: [PATCH 088/214] =?UTF-8?q?Fix=20codespell=20linter=20error=20?= =?UTF-8?q?=F0=9F=A4=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/codespell.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 7fc3f4f0256..c24b80a6e98 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -16,3 +16,4 @@ jobs: with: ignore_words_file: .codespellignore skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map + ignore_regex: ^\s*"image\/png":\s.* From f4a8e6d9d868a33fec1d281837d2e5d5ae3544cc Mon Sep 17 00:00:00 2001 From: Martin Breuss Date: Wed, 26 Apr 2023 04:16:20 +0200 Subject: [PATCH 089/214] Exclude notebook files from linter --- .github/workflows/codespell.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index c24b80a6e98..009ca6e9ab5 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -15,5 +15,4 @@ jobs: - uses: codespell-project/actions-codespell@master with: ignore_words_file: .codespellignore - skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map - ignore_regex: ^\s*"image\/png":\s.* + skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map,./docs/*.ipynb From 99a0c4652a952f855f2511e48673f95d6359bcc8 Mon Sep 17 00:00:00 2001 From: Martin Breuss Date: Wed, 26 Apr 2023 04:33:37 +0200 Subject: [PATCH 090/214] Fix codespell, try different argument --regex (#1669) * Try different argument --ignore-regex IGNORE_REGEX regular expression that is used to find patterns to ignore by treating as whitespace. When writing regular expressions, consider ensuring there are boundary non-word chars, e.g., "\bmatch\b". Defaults to empty/disabled. ... -r REGEX, --regex REGEX regular expression that is used to find words. By default any alphanumeric character, the underscore, the hyphen, and the apostrophe is used to build words. This option cannot be specified together with --write-changes. * Goose chase source code * Attempt to use args * Try default argparse generation Missing meta variable * Fix quotes * Try underscores --- .github/workflows/codespell.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 009ca6e9ab5..4fe61ce77a0 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -15,4 +15,5 @@ jobs: - uses: codespell-project/actions-codespell@master with: ignore_words_file: .codespellignore - skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map,./docs/*.ipynb + skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map + IGNORE_REGEX: ^\s*"image\/png":\s.* From 529e73c2d5bd810cad306f40abc1a2f7ce075320 Mon Sep 17 00:00:00 2001 From: Jackie Kazil Date: Tue, 25 Apr 2023 22:40:36 -0400 Subject: [PATCH 091/214] Revert "Fix codespell, try different argument --regex (#1669)" (#1671) This reverts commit 99a0c4652a952f855f2511e48673f95d6359bcc8. --- .github/workflows/codespell.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 4fe61ce77a0..009ca6e9ab5 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -15,5 +15,4 @@ jobs: - uses: codespell-project/actions-codespell@master with: ignore_words_file: .codespellignore - skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map - IGNORE_REGEX: ^\s*"image\/png":\s.* + skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map,./docs/*.ipynb From dc4d5fcfe5f2e5d25e20dcf5eebe9e1f5aee80e3 Mon Sep 17 00:00:00 2001 From: ItsQuinnMoore Date: Wed, 26 Apr 2023 11:37:12 -0600 Subject: [PATCH 092/214] Update slider to start at 1 FPS. --- mesa/visualization/templates/js/runcontrol.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mesa/visualization/templates/js/runcontrol.js b/mesa/visualization/templates/js/runcontrol.js index 8cc7176f94d..ef448166a51 100644 --- a/mesa/visualization/templates/js/runcontrol.js +++ b/mesa/visualization/templates/js/runcontrol.js @@ -99,11 +99,11 @@ function ModelController(tick = 0, fps = 3, running = false, finished = false) { */ const fpsControl = new Slider("#fps", { max: 20, - min: 0, + min: 1, value: controller.fps, - ticks: [0, 20], - ticks_labels: [0, 20], - ticks_position: [0, 100], + ticks: [1, 20], + ticks_labels: [1, 20], + ticks_position: [1, 100], }); fpsControl.on("change", () => controller.updateFPS(fpsControl.getValue())); From ab677a89b76a8856bfb58d5232bf194c6ec6ea4e Mon Sep 17 00:00:00 2001 From: Jackie Kazil Date: Wed, 3 May 2023 05:35:11 -0400 Subject: [PATCH 093/214] Update LICENSE to 2023 --- LICENSE | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LICENSE b/LICENSE index 939717b7778..b130ab1f87c 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2022 Core Mesa Team and contributors +Copyright 2023 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. From bddc8f22775f6809ee5500b6628b3b6dc0c89427 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 2 May 2023 05:01:16 +0000 Subject: [PATCH 094/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.3.1 → v3.3.2](https://github.com/asottile/pyupgrade/compare/v3.3.1...v3.3.2) --- .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 8083bcb5841..e3deaf9500a 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.3.1 + rev: v3.3.2 hooks: - id: pyupgrade args: [--py38-plus] From 3b679953724b49e9dc73f7ba38be05493a498d70 Mon Sep 17 00:00:00 2001 From: electric-souperman Date: Thu, 27 Apr 2023 11:28:49 -0600 Subject: [PATCH 095/214] Replace const chart for let chart in advanced tutorial JS --- docs/tutorials/adv_tutorial.ipynb | 2 +- docs/tutorials/adv_tutorial.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/adv_tutorial.ipynb b/docs/tutorials/adv_tutorial.ipynb index 6610db9d6c6..49be784cb38 100644 --- a/docs/tutorials/adv_tutorial.ipynb +++ b/docs/tutorials/adv_tutorial.ipynb @@ -386,7 +386,7 @@ " };\n", "\n", " // Create the chart object\n", - " const chart = new Chart(context, {type: 'bar', data: data, options: options});\n", + " let chart = new Chart(context, {type: 'bar', data: data, options: options});\n", "\n", " // Now what?\n", "};\n", diff --git a/docs/tutorials/adv_tutorial.rst b/docs/tutorials/adv_tutorial.rst index 5f23a0e864b..242445d70be 100644 --- a/docs/tutorials/adv_tutorial.rst +++ b/docs/tutorials/adv_tutorial.rst @@ -412,7 +412,7 @@ created, we can create the chart object. }; // Create the chart object - const chart = new Chart(context, {type: 'bar', data: data, options: options}); + let chart = new Chart(context, {type: 'bar', data: data, options: options}); // Now what? }; From 0e9f818d5281cf7f37d73baec52409571622874e Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Tue, 9 May 2023 06:34:10 +0200 Subject: [PATCH 096/214] Make HexGrid private and create HexSingleGrid and HexMultiGrid (#1581) * Make HexGrid private and create HexSingleGrid and HexMultiGrid * docstrings for classes * Update test_grid.py * Update test_grid.py * Update test_grid.py * Update space.py * Update space.py * Update space.py * Update space.py * Update test_grid.py * Fix typo --------- Co-authored-by: rht --- mesa/space.py | 53 ++++++++++++++++++++++++++++++++++++++++++++-- tests/test_grid.py | 16 ++++++-------- 2 files changed, 57 insertions(+), 12 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 165d5ed70f7..9dc642456b3 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -646,8 +646,8 @@ def iter_cell_list_contents( ) -class HexGrid(SingleGrid): - """Hexagonal Grid: Extends SingleGrid to handle hexagonal neighbors. +class _HexGrid: + """Hexagonal Grid which handles hexagonal neighbors. Functions according to odd-q rules. See http://www.redblobgames.com/grids/hexagons/#coordinates for more. @@ -821,6 +821,55 @@ def get_neighbors( return list(self.iter_neighbors(pos, include_center, radius)) +class HexSingleGrid(_HexGrid, SingleGrid): + """Hexagonal SingleGrid: a SingleGrid where neighbors are computed + according to a hexagonal tiling of the grid. + + Functions according to odd-q rules. + See http://www.redblobgames.com/grids/hexagons/#coordinates for more. + + Properties: + width, height: The grid's width and height. + torus: Boolean which determines whether to treat the grid as a torus. + """ + + +class HexMultiGrid(_HexGrid, MultiGrid): + """Hexagonal MultiGrid: a MultiGrid where neighbors are computed + according to a hexagonal tiling of the grid. + + Functions according to odd-q rules. + See http://www.redblobgames.com/grids/hexagons/#coordinates for more. + + Properties: + width, height: The grid's width and height. + torus: Boolean which determines whether to treat the grid as a torus. + """ + + +class HexGrid(HexSingleGrid): + """Hexagonal Grid: a Grid where neighbors are computed + according to a hexagonal tiling of the grid. + + Functions according to odd-q rules. + See http://www.redblobgames.com/grids/hexagons/#coordinates for more. + + Properties: + width, height: The grid's width and height. + torus: Boolean which determines whether to treat the grid as a torus. + """ + + def __init__(self, width: int, height: int, torus: bool) -> None: + super().__init__(width, height, torus) + warn( + ( + "HexGrid is being deprecated; use instead HexSingleGrid or HexMultiGrid " + "depending on your use case." + ), + DeprecationWarning, + ) + + class ContinuousSpace: """Continuous space where each agent can have an arbitrary position. diff --git a/tests/test_grid.py b/tests/test_grid.py index f9eb6dbe6a1..7b4c590dc54 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -5,7 +5,7 @@ import unittest from unittest.mock import Mock, patch -from mesa.space import HexGrid, MultiGrid, SingleGrid +from mesa.space import HexSingleGrid, MultiGrid, SingleGrid # Initial agent positions for testing # @@ -153,7 +153,7 @@ def test_agent_move(self): self.grid.move_agent(agent, (1, 0)) assert agent.pos == (1, 0) # move it off the torus and check for the exception - if not self.torus: + if not self.grid.torus: with self.assertRaises(Exception): self.grid.move_agent(agent, [-1, 1]) with self.assertRaises(Exception): @@ -397,7 +397,7 @@ def test_neighbors(self): assert len(neighbors) == 11 -class TestHexGrid(unittest.TestCase): +class TestHexSingleGrid(unittest.TestCase): """ Testing a hexagonal singlegrid. """ @@ -408,7 +408,7 @@ def setUp(self): """ width = 3 height = 5 - self.grid = HexGrid(width, height, torus=False) + self.grid = HexSingleGrid(width, height, torus=False) self.agents = [] counter = 0 for x in range(width): @@ -425,7 +425,6 @@ def test_neighbors(self): """ Test the hexagonal neighborhood methods on the non-toroid. """ - neighborhood = self.grid.get_neighborhood((1, 1)) assert len(neighborhood) == 6 @@ -452,20 +451,18 @@ def test_neighbors(self): assert sum(x + y for x, y in neighborhood) == 39 -class TestHexGridTorus(TestSingleGrid): +class TestHexSingleGridTorus(TestSingleGrid): """ Testing a hexagonal toroidal singlegrid. """ - torus = True - def setUp(self): """ Create a test non-toroidal grid and populate it with Mock Agents """ width = 3 height = 5 - self.grid = HexGrid(width, height, torus=True) + self.grid = HexSingleGrid(width, height, torus=True) self.agents = [] counter = 0 for x in range(width): @@ -482,7 +479,6 @@ def test_neighbors(self): """ Test the hexagonal neighborhood methods on the toroid. """ - neighborhood = self.grid.get_neighborhood((1, 1)) assert len(neighborhood) == 6 From c9d0c431d27352a0203903c6b7ef655cf7b9e2b5 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 9 May 2023 01:05:21 -0400 Subject: [PATCH 097/214] Modify ChartModule series to allow dynamically named properties (#1685) Original work: https://github.com/projectmesa/mesa/pull/714 and https://github.com/projectmesa/mesa/pull/1673. Co-authored-by: Catherine Devlin Co-authored-by: Karen Gonzalez --- mesa/visualization/templates/js/ChartModule.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mesa/visualization/templates/js/ChartModule.js b/mesa/visualization/templates/js/ChartModule.js index 78680258c1e..be96cd60c1e 100644 --- a/mesa/visualization/templates/js/ChartModule.js +++ b/mesa/visualization/templates/js/ChartModule.js @@ -28,11 +28,12 @@ const ChartModule = function (series, canvas_width, canvas_height) { for (const i in series) { const s = series[i]; const new_series = { - label: s.Label, - borderColor: s.Color, backgroundColor: convertColorOpacity(s.Color), data: [], }; + for (const property in s){ + new_series[property] = s[property]; + } datasets.push(new_series); } From a86414b50b83816d4926459d6b083476e33b91e9 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Tue, 9 May 2023 14:53:49 +0200 Subject: [PATCH 098/214] Raise exception when pos is out of bounds in Grid.get_neighborhood (#1524) * Raise exception when pos is out of bounds in Grid.get_neighborhood * Update test_grid.py * Update space.py --- mesa/space.py | 5 ++++- tests/test_grid.py | 7 ++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 9dc642456b3..22ca214a025 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -268,6 +268,9 @@ def get_neighborhood( if neighborhood is not None: return neighborhood + if self.out_of_bounds(pos): + raise Exception("The `pos` tuple passed is out of bounds.") + # 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 @@ -309,7 +312,7 @@ def get_neighborhood( neighborhood.append((nx, ny)) - if not include_center and neighborhood: + if not include_center: neighborhood.remove(pos) self._neighborhood_cache[cache_key] = neighborhood diff --git a/tests/test_grid.py b/tests/test_grid.py index 7b4c590dc54..a24db60a594 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -119,11 +119,8 @@ def test_neighbors(self): neighborhood = self.grid.get_neighborhood((0, 0), moore=False) assert len(neighborhood) == 2 - neighbors = self.grid.get_neighbors((4, 1), moore=False) - assert len(neighbors) == 0 - - neighbors = self.grid.get_neighbors((4, 1), moore=True) - assert len(neighbors) == 0 + with self.assertRaises(Exception): + neighbors = self.grid.get_neighbors((4, 1), moore=False) neighbors = self.grid.get_neighbors((1, 1), moore=False, include_center=True) assert len(neighbors) == 3 From a999396b708bc02790c52b114466292fc35efd54 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Thu, 11 May 2023 08:09:42 +0200 Subject: [PATCH 099/214] New strategy to choose optimal cutoff in Grid.move_to_empty (#1565) --- mesa/space.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 22ca214a025..1214a0f746a 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -119,6 +119,11 @@ def __init__(self, width: int, height: int, torus: bool) -> None: # Neighborhood Cache self._neighborhood_cache: dict[Any, list[Coordinate]] = {} + # Cutoff used inside self.move_to_empty. The parameters are fitted on Python + # 3.11 and it was verified that they are roughly the same for 3.10. Refer to + # the code in PR#1565 to check for their stability when a new release gets out. + self.cutoff_empties = 7.953 * self.num_cells**0.384 + @staticmethod def default_val() -> None: """Default value for new cell elements.""" @@ -464,9 +469,7 @@ def is_cell_empty(self, pos: Coordinate) -> bool: x, y = pos return self._grid[x][y] == self.default_val() - def move_to_empty( - self, agent: Agent, cutoff: float = 0.998, num_agents: int | None = None - ) -> None: + def move_to_empty(self, agent: Agent, num_agents: int | None = None) -> None: """Moves agent to a random empty cell, vacating agent's old cell.""" if num_agents is not None: warn( @@ -482,10 +485,10 @@ def move_to_empty( # 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: + # https://github.com/projectmesa/mesa/issues/1052 and + # https://github.com/projectmesa/mesa/pull/1565. The cutoff value provided + # is the break-even comparison with the time taken in the else branching point. + if num_empty_cells > self.cutoff_empties: while True: new_pos = ( agent.random.randrange(self.width), From 9b4ba3aea74060c4c100aed7f4734b55a8b3f2c3 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 14 May 2023 08:09:03 -0400 Subject: [PATCH 100/214] docs: Use nbsphinx on adv_tutorial --- docs/conf.py | 1 + docs/index.rst | 2 +- docs/tutorials/adv_tutorial.rst | 560 -------------------------------- setup.py | 2 +- 4 files changed, 3 insertions(+), 562 deletions(-) delete mode 100644 docs/tutorials/adv_tutorial.rst diff --git a/docs/conf.py b/docs/conf.py index 102ce58fe79..79610fe2250 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,6 +42,7 @@ "sphinx.ext.mathjax", "sphinx.ext.ifconfig", "sphinx.ext.viewcode", + "nbsphinx", ] # Add any paths that contain templates here, relative to this directory. diff --git a/docs/index.rst b/docs/index.rst index bbabb4d5d01..2ea1281675d 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -96,7 +96,7 @@ ABM features users have shared that you may want to use in your model Mesa Overview tutorials/intro_tutorial - tutorials/adv_tutorial + tutorials/adv_tutorial.ipynb Best Practices Useful Snippets API Documentation diff --git a/docs/tutorials/adv_tutorial.rst b/docs/tutorials/adv_tutorial.rst deleted file mode 100644 index 242445d70be..00000000000 --- a/docs/tutorials/adv_tutorial.rst +++ /dev/null @@ -1,560 +0,0 @@ -Advanced Tutorial -================= - -Adding visualization -~~~~~~~~~~~~~~~~~~~~ - -So far, we’ve built a model, run it, and analyzed some output -afterwards. However, one of the advantages of agent-based models is that -we can often watch them run step by step, potentially spotting -unexpected patterns, behaviors or bugs, or developing new intuitions, -hypotheses, or insights. Other times, watching a model run can explain -it to an unfamiliar audience better than static explanations. Like many -ABM frameworks, Mesa allows you to create an interactive visualization -of the model. In this section we’ll walk through creating a -visualization using built-in components, and (for advanced users) how to -create a new visualization element. - -**Note for Jupyter users: Due to conflicts with the tornado server Mesa -uses and Jupyter, the interactive browser of your model will load but -likely not work. This will require you to run the code from .py -files. The Mesa development team is working to develop a** `Jupyter -compatible interface `_. - -First, a quick explanation of how Mesa’s interactive visualization -works. Visualization is done in a browser window, using JavaScript to -draw the different things being visualized at each step of the model. To -do this, Mesa launches a small web server, which runs the model, turns -each step into a JSON object (essentially, structured plain text) and -sends those steps to the browser. - -A visualization is built up of a few different modules: for example, a -module for drawing agents on a grid, and another one for drawing a chart -of some variable. Each module has a Python part, which runs on the -server and turns a model state into JSON data; and a JavaScript side, -which takes that JSON data and draws it in the browser window. Mesa -comes with a few modules built in, and let you add your own as well. - - -Grid Visualization -^^^^^^^^^^^^^^^^^^ - -To start with, let’s have a visualization where we can watch the agents -moving around the grid. For this, you will need to put your model code -in a separate Python source file; for example, ``MoneyModel.py``. Next, -either in the same file or in a new one (e.g. ``MoneyModel_Viz.py``) -import the server class and the Canvas Grid class (so-called because it -uses HTML5 canvas to draw a grid). If you’re in a new file, you’ll also -need to import the actual model object. - -.. code:: ipython3 - - import mesa - - # If MoneyModel.py is where your code is: - # from MoneyModel import MoneyModel - -``CanvasGrid`` works by looping over every cell in a grid, and -generating a portrayal for every agent it finds. A portrayal is a -dictionary (which can easily be turned into a JSON object) which tells -the JavaScript side how to draw it. The only thing we need to provide is -a function which takes an agent, and returns a portrayal object. Here’s -the simplest one: it’ll draw each agent as a red, filled circle which -fills half of each cell. - -.. code:: ipython3 - - def agent_portrayal(agent): - portrayal = { - "Shape": "circle", - "Color": "red", - "Filled": "true", - "Layer": 0, - "r": 0.5, - } - return portrayal - -In addition to the portrayal method, we instantiate a canvas grid with -its width and height in cells, and in pixels. In this case, let’s create -a 10x10 grid, drawn in 500 x 500 pixels. - -.. code:: ipython3 - - grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500) - -.. code:: ipython3 - - """ - The full code should now look like: - """ - # from MoneyModel import * - import mesa - - - def agent_portrayal(agent): - portrayal = { - "Shape": "circle", - "Filled": "true", - "Layer": 0, - "Color": "red", - "r": 0.5, - } - return portrayal - - - grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500) - server = mesa.visualization.ModularServer( - MoneyModel, [grid], "Money Model", {"N": 100, "width": 10, "height": 10} - ) - server.port = 8521 # The default - server.launch() - -Now we create and launch the actual server. We do this with the -following arguments: - -- The model class we’re running and visualizing; in this case, - ``MoneyModel``. -- A list of module objects to include in the visualization; here, just - ``[grid]`` -- The title of the model: “Money Model” -- Any inputs or arguments for the model itself. In this case, 100 - agents, and height and width of 10. - -Once we create the server, we set the port for it to listen on (you can -treat this as just a piece of the URL you’ll open in the browser). -Finally, when you’re ready to run the visualization, use the server’s -``launch()`` method. - -.. code:: python - - server = ModularServer(MoneyModel, - [grid], - "Money Model", - {"N":100, "width":10, "height":10}) - server.port = 8521 # The default - server.launch() - -The full code should now look like: - -.. code:: python - - from MoneyModel import * - import mesa - - - def agent_portrayal(agent): - portrayal = {"Shape": "circle", - "Filled": "true", - "Layer": 0, - "Color": "red", - "r": 0.5} - return portrayal - - grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500) - server = mesa.visualization.ModularServer( - MoneyModel, [grid], "Money Model", {"N": 100, "width": 10, "height": 10} - )server = ModularServer(MoneyModel, - [grid], - "Money Model", - {"N":100, "width":10, "height":10}) - server.port = 8521 # The default - server.launch() - -Now run this file; this should launch the interactive visualization -server and open your web browser automatically. (If the browser doesn’t -open automatically, try pointing it at http://127.0.0.1:8521 manually. -If this doesn’t show you the visualization, something may have gone -wrong with the server launch.) - -You should see something like the figure below: the model title, an -empty space where the grid will be, and a control panel off to the -right. - -.. figure:: files/viz_empty.png - :alt: Empty Visualization - - Empty Visualization - -Click the ‘reset’ button on the control panel, and you should see the -grid fill up with red circles, representing agents. - -.. figure:: files/viz_redcircles.png - :alt: Redcircles Visualization - - Redcircles Visualization - -Click ‘step’ to advance the model by one step, and the agents will move -around. Click ‘run’ and the agents will keep moving around, at the rate -set by the ‘fps’ (frames per second) slider at the top. Try moving it -around and see how the speed of the model changes. Pressing ‘pause’ will -(as you’d expect) pause the model; presing ‘run’ again will restart it. -Finally, ‘reset’ will start a new instantiation of the model. - -To stop the visualization server, go back to the terminal where you -launched it, and press Control+c. - -Changing the agents -^^^^^^^^^^^^^^^^^^^ - -In the visualization above, all we could see is the agents moving around -– but not how much money they had, or anything else of interest. Let’s -change it so that agents who are broke (wealth 0) are drawn in grey, -smaller, and above agents who still have money. - -To do this, we go back to our ``agent_portrayal`` code and add some code -to change the portrayal based on the agent properties. - -.. code:: python - - 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 - -Now launch the server again - this will open a new browser window -pointed at the updated visualization. Initially it looks the same, but -advance the model and smaller grey circles start to appear. Note that -since the zero-wealth agents have a higher layer number, they are drawn -on top of the red agents. - -.. figure:: files/viz_greycircles.png - :alt: Greycircles Visualization - - Greycircles Visualization - -Adding a chart -^^^^^^^^^^^^^^ - -Next, let’s add another element to the visualization: a chart, tracking -the model’s Gini Coefficient. This is another built-in element that Mesa -provides. - -The basic chart pulls data from the model’s DataCollector, and draws it -as a line graph using the `Charts.js `__ -JavaScript libraries. We instantiate a chart element with a list of -series for the chart to track. Each series is defined in a dictionary, -and has a ``Label`` (which must match the name of a model-level variable -collected by the DataCollector) and a ``Color`` name. We can also give -the chart the name of the DataCollector object in the model. - -Finally, we add the chart to the list of elements in the server. The -elements are added to the visualization in the order they appear, so the -chart will appear underneath the grid. - -.. code:: python - - chart = mesa.visualization.ChartModule([{"Label": "Gini", - "Color": "Black"}], - data_collector_name='datacollector') - - server = mesa.visualization.ModularServer(MoneyModel, - [grid, chart], - "Money Model", - {"N":100, "width":10, "height":10}) - -Launch the visualization and start a model run, and you’ll see a line -chart underneath the grid. Every step of the model, the line chart -updates along with the grid. Reset the model, and the chart resets too. - -.. figure:: files/viz_chart.png - :alt: Chart Visualization - - Chart Visualization - -**Note:** You might notice that the chart line only starts after a -couple of steps; this is due to a bug in Charts.js which will hopefully -be fixed soon. - -Building your own visualization component -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -**Note:** This section is for users who have a basic familiarity with -JavaScript. If that’s not you, don’t worry! (If you’re an advanced -JavaScript coder and find things that we’ve done wrong or inefficiently, -please `let us know `__!) - -If the visualization elements provided by Mesa aren’t enough for you, -you can build your own and plug them into the model server. - -First, you need to understand how the visualization works under the -hood. Remember that each visualization module has two sides: a Python -object that runs on the server and generates JSON data from the model -state (the server side), and a JavaScript object that runs in the -browser and turns the JSON into something it renders on the screen (the -client side). - -Obviously, the two sides of each visualization must be designed in -tandem. They result in one Python class, and one JavaScript ``.js`` -file. The path to the JavaScript file is a property of the Python class. - -For this example, let’s build a simple histogram visualization, which -can count the number of agents with each value of wealth. We’ll use the -`Charts.js `__ JavaScript library, which is -already included with Mesa. If you go and look at its documentation, -you’ll see that it had no histogram functionality, which means we have -to build our own out of a bar chart. We’ll keep the histogram as simple -as possible, giving it a fixed number of integer bins. If you were -designing a more general histogram to add to the Mesa repository for -everyone to use across different models, obviously you’d want something -more general. - -Client-Side Code -^^^^^^^^^^^^^^^^ - -In general, the server- and client-side are written in tandem. However, -if you’re like me and more comfortable with Python than JavaScript, it -makes sense to figure out how to get the JavaScript working first, and -then write the Python to be compatible with that. - -In the same directory as your model, create a new file called -``HistogramModule.js``. This will store the JavaScript code for the -client side of the new module. - -JavaScript classes can look alien to people coming from other languages -– specifically, they can look like functions. (The Mozilla `Introduction -to Object-Oriented -JavaScript `__ -is a good starting point). In ``HistogramModule.js``, start by creating -the class itself: - -.. code:: javascript - - const HistogramModule = function(bins, canvas_width, canvas_height) { - // The actual code will go here. - }; - -Note that our object is instantiated with three arguments: the number of -integer bins, and the width and height (in pixels) the chart will take -up in the visualization window. - -When the visualization object is instantiated, the first thing it needs -to do is prepare to draw on the current page. To do so, it adds a -`canvas `__ -tag to the page. It also gets the canvas' context, which is required for doing -anything with it. - -.. code:: javascript - - const HistogramModule = function(bins, canvas_width, canvas_height) { - // Create the canvas object: - const canvas = document.createElement("canvas"); - Object.assign(canvas, { - width: canvas_width, - height: canvas_height, - style: "border:1px dotted", - }); - // Append it to #elements: - const elements = document.getElementById("elements"); - elements.appendChild(canvas); - - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - }; - - -Look at the Charts.js `bar chart -documentation `__. -You’ll see some of the boilerplate needed to get a chart set up. -Especially important is the ``data`` object, which includes the -datasets, labels, and color options. In this case, we want just one -dataset (we’ll keep things simple and name it “Data”); it has ``bins`` -for categories, and the value of each category starts out at zero. -Finally, using these boilerplate objects and the canvas context we -created, we can create the chart object. - -.. code:: javascript - - const HistogramModule = function(bins, canvas_width, canvas_height) { - // Create the canvas object: - const canvas = document.createElement("canvas"); - Object.assign(canvas, { - width: canvas_width, - height: canvas_height, - style: "border:1px dotted", - }); - // Append it to #elements: - const elements = document.getElementById("elements"); - elements.appendChild(canvas); - - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - - // Prep the chart properties and series: - const datasets = [{ - label: "Data", - fillColor: "rgba(151,187,205,0.5)", - strokeColor: "rgba(151,187,205,0.8)", - highlightFill: "rgba(151,187,205,0.75)", - highlightStroke: "rgba(151,187,205,1)", - data: [] - }]; - - // Add a zero value for each bin - for (var i in bins) - datasets[0].data.push(0); - - const data = { - labels: bins, - datasets: datasets - }; - - const options = { - scaleBeginsAtZero: true - }; - - // Create the chart object - let chart = new Chart(context, {type: 'bar', data: data, options: options}); - - // Now what? - }; - -There are two methods every client-side visualization class must -implement to be able to work: ``render(data)`` to render the incoming -data, and ``reset()`` which is called to clear the visualization when -the user hits the reset button and starts a new model run. - -In this case, the easiest way to pass data to the histogram is as an -array, one value for each bin. We can then just loop over the array and -update the values in the chart’s dataset. - -There are a few ways to reset the chart, but the easiest is probably to -destroy it and create a new chart object in its place. - -With that in mind, we can add these two methods to the class: - -.. code:: javascript - - const HistogramModule = function(bins, canvas_width, canvas_height) { - // ...Everything from above... - this.render = function(data) { - datasets[0].data = data; - chart.update(); - }; - - this.reset = function() { - chart.destroy(); - chart = new Chart(context, {type: 'bar', data: data, options: options}); - }; - }; -Note the ``this``. before the method names. This makes them public and -ensures that they are accessible outside of the object itself. All the -other variables inside the class are only accessible inside the object -itself, but not outside of it. - -Server-Side Code -^^^^^^^^^^^^^^^^ - -Can we get back to Python code? Please? - -Every JavaScript visualization element has an equal and opposite -server-side Python element. The Python class needs to also have a -``render`` method, to get data out of the model object and into a -JSON-ready format. It also needs to point towards the code where the -relevant JavaScript lives, and add the JavaScript object to the model -page. - -In a Python file (either its own, or in the same file as your -visualization code), import the ``VisualizationElement`` class we’ll -inherit from, and create the new visualization class. - -.. code:: python - - from mesa.visualization.ModularVisualization import VisualizationElement, CHART_JS_FILE - - class HistogramModule(VisualizationElement): - package_includes = [CHART_JS_FILE] - local_includes = ["HistogramModule.js"] - - def __init__(self, bins, canvas_height, canvas_width): - self.canvas_height = canvas_height - self.canvas_width = canvas_width - self.bins = bins - new_element = "new HistogramModule({}, {}, {})" - new_element = new_element.format(bins, - canvas_width, - canvas_height) - self.js_code = "elements.push(" + new_element + ");" - -There are a few things going on here. ``package_includes`` is a list of -JavaScript files that are part of Mesa itself that the visualization -element relies on. You can see the included files in -`mesa/visualization/templates/ `__. -Similarly, ``local_includes`` is a list of JavaScript files in the same -directory as the class code itself. Note that both of these are class -variables, not object variables – they hold for all particular objects. - -Next, look at the ``__init__`` method. It takes three arguments: the -number of bins, and the width and height for the histogram. It then uses -these values to populate the ``js_code`` property; this is code that the -server will insert into the visualization page, which will run when the -page loads. In this case, it creates a new HistogramModule (the class we -created in JavaScript in the step above) with the desired bins, width -and height; it then appends (``push``\ es) this object to ``elements``, -the list of visualization elements that the visualization page itself -maintains. - -Now, the last thing we need is the ``render`` method. If we were making -a general-purpose visualization module we’d want this to be more -general, but in this case we can hard-code it to our model. - -.. code:: python - - import numpy as np - - class HistogramModule(VisualizationElement): - # ... Everything from above... - - def render(self, model): - wealth_vals = [agent.wealth for agent in model.schedule.agents] - hist = np.histogram(wealth_vals, bins=self.bins)[0] - return [int(x) for x in hist] - -Every time the render method is called (with a model object as the -argument) it uses numpy to generate counts of agents with each wealth -value in the bins, and then returns a list of these values. Note that -the ``render`` method doesn’t return a JSON string – just an object that -can be turned into JSON, in this case a Python list (with Python -integers as the values; the ``json`` library doesn’t like dealing with -numpy’s integer type). - -Now, you can create your new HistogramModule and add it to the server: - -.. code:: python - - histogram = mesa.visualization.HistogramModule(list(range(10)), 200, 500) - server = mesa.visualization.ModularServer(MoneyModel, - [grid, histogram, chart], - "Money Model", - {"N":100, "width":10, "height":10}) - server.launch() - -Run this code, and you should see your brand-new histogram added to the -visualization and updating along with the model! - -.. figure:: files/viz_histogram.png - :alt: Histogram Visualization - - Histogram Visualization - -If you’ve felt comfortable with this section, it might be instructive to -read the code for the -`ModularServer `__ -and the -`modular_template `__ -to get a better idea of how all the pieces fit together. - -Happy Modeling! -~~~~~~~~~~~~~~~ - -This document is a work in progress. If you see any errors, exclusions -or have any problems please contact -`us `__. diff --git a/setup.py b/setup.py index 6760e56ef9b..8b8a6bdebea 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ "pytest-cov", "sphinx", ], - "docs": ["sphinx", "ipython"], + "docs": ["sphinx", "ipython", "nbsphinx"], } version = "" From 16bd5c6696c9f7fd9eb04cc2fe8d8d674049fbec Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 14 May 2023 09:34:38 -0400 Subject: [PATCH 101/214] fix: Restrict Sphinx to be <7 See https://github.com/readthedocs/readthedocs.org/issues/10279 --- setup.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 8b8a6bdebea..05f4deab5b2 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,9 @@ "pytest-cov", "sphinx", ], - "docs": ["sphinx", "ipython", "nbsphinx"], + # Constrain sphinx version until https://github.com/readthedocs/readthedocs.org/issues/10279 + # is fixed. + "docs": ["sphinx<7", "ipython", "nbsphinx"], } version = "" From 23f7799ead9c24a558581a606dc71809c7224c50 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 9 May 2023 02:38:39 -0400 Subject: [PATCH 102/214] Correct get_heading for toroidal space --- mesa/space.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 1214a0f746a..a1b9ff43c67 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -1012,11 +1012,21 @@ def get_heading( """ one = np.array(pos_1) two = np.array(pos_2) - if self.torus: - one = (one - self.center) % self.size - two = (two - self.center) % self.size heading = two - one - if isinstance(pos_1, tuple): + if self.torus: + inverse_heading = heading - np.sign(heading) * self.size + + def get_min_abs(x, y): + return x if abs(x) < abs(y) else y + + # Choose the smaller heading based on their absolute value for + # each dimension independently. + heading = tuple( + get_min_abs(heading[i], inverse_heading[i]) for i in range(2) + ) + if isinstance(pos_1, np.ndarray): + heading = np.ndarray(heading) + else: heading = tuple(heading) return heading From bf72a469984307e037b335b1bc129496cc39cc49 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 14 May 2023 20:48:32 -0400 Subject: [PATCH 103/214] Enable codespell on Jupyter notebooks Fixes #1661. --- .github/workflows/codespell.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 009ca6e9ab5..7fc3f4f0256 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -15,4 +15,4 @@ jobs: - uses: codespell-project/actions-codespell@master with: ignore_words_file: .codespellignore - skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map,./docs/*.ipynb + skip: .*bootstrap.*,*.js,.*bootstrap-theme.css.map From 02592f3396c0c17bc1a61d24249ac0a6e573b24c Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 11 May 2023 02:18:57 -0400 Subject: [PATCH 104/214] Remove Pipfile and Pipfile.lock --- Pipfile | 14 -- Pipfile.lock | 492 --------------------------------------------------- 2 files changed, 506 deletions(-) delete mode 100644 Pipfile delete mode 100644 Pipfile.lock diff --git a/Pipfile b/Pipfile deleted file mode 100644 index da535315557..00000000000 --- a/Pipfile +++ /dev/null @@ -1,14 +0,0 @@ -[[source]] -name = "pypi" -url = "https://pypi.org/simple" -verify_ssl = true - -[dev-packages] -pytest = "*" -pytest-cov = "*" - -[packages] -mesa = "*" - -[requires] -python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock deleted file mode 100644 index 1b26342f271..00000000000 --- a/Pipfile.lock +++ /dev/null @@ -1,492 +0,0 @@ -{ - "_meta": { - "hash": { - "sha256": "3d8caf3ab53cebc0f6b41160cf246e4fbb7332bca299c7297c66f4c72b72d979" - }, - "pipfile-spec": 6, - "requires": { - "python_version": "3.9" - }, - "sources": [ - { - "name": "pypi", - "url": "https://pypi.org/simple", - "verify_ssl": true - } - ] - }, - "default": { - "arrow": { - "hashes": [ - "sha256:3934b30ca1b9f292376d9db15b19446088d12ec58629bc3f0da28fd55fb633a1", - "sha256:5a49ab92e3b7b71d96cd6bfcc4df14efefc9dfa96ea19045815914a6ab6b1fe2" - ], - "markers": "python_version >= '3.6'", - "version": "==1.2.3" - }, - "binaryornot": { - "hashes": [ - "sha256:359501dfc9d40632edc9fac890e19542db1a287bbcfa58175b66658392018061", - "sha256:b8b71173c917bddcd2c16070412e369c3ed7f0528926f70cac18a6c97fd563e4" - ], - "version": "==0.4.4" - }, - "certifi": { - "hashes": [ - "sha256:0d9c601124e5a6ba9712dbc60d9c53c21e34f5f641fe83002317394311bdce14", - "sha256:90c1a32f1d68f940488354e36370f6cca89f0f106db09518524c88d6ed83f382" - ], - "markers": "python_version >= '3.6'", - "version": "==2022.9.24" - }, - "chardet": { - "hashes": [ - "sha256:0368df2bfd78b5fc20572bb4e9bb7fb53e2c094f60ae9993339e8671d0afb8aa", - "sha256:d3e64f022d254183001eccc5db4040520c0f23b1a3f33d6413e099eb7f126557" - ], - "markers": "python_version >= '3.6'", - "version": "==5.0.0" - }, - "charset-normalizer": { - "hashes": [ - "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845", - "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f" - ], - "markers": "python_full_version >= '3.6.0'", - "version": "==2.1.1" - }, - "click": { - "hashes": [ - "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", - "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" - ], - "markers": "python_version >= '3.7'", - "version": "==8.1.3" - }, - "cookiecutter": { - "hashes": [ - "sha256:9f3ab027cec4f70916e28f03470bdb41e637a3ad354b4d65c765d93aad160022", - "sha256:f3982be8d9c53dac1261864013fdec7f83afd2e42ede6f6dd069c5e149c540d5" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.1" - }, - "idna": { - "hashes": [ - "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4", - "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2" - ], - "markers": "python_version >= '3.5'", - "version": "==3.4" - }, - "jinja2": { - "hashes": [ - "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", - "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" - ], - "markers": "python_version >= '3.7'", - "version": "==3.1.2" - }, - "jinja2-time": { - "hashes": [ - "sha256:d14eaa4d315e7688daa4969f616f226614350c48730bfa1692d2caebd8c90d40", - "sha256:d3eab6605e3ec8b7a0863df09cc1d23714908fa61aa6986a845c20ba488b4efa" - ], - "version": "==0.2.0" - }, - "markupsafe": { - "hashes": [ - "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", - "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", - "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", - "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", - "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", - "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", - "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", - "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", - "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", - "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", - "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", - "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", - "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", - "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", - "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", - "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", - "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", - "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", - "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", - "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", - "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", - "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", - "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", - "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", - "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", - "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", - "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", - "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", - "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", - "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", - "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", - "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", - "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", - "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", - "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", - "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", - "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", - "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", - "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", - "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" - ], - "markers": "python_version >= '3.7'", - "version": "==2.1.1" - }, - "mesa": { - "hashes": [ - "sha256:14f40b3c2948f02295b1a719b380cb1676a29154b0adebd4e537d431ebd78126", - "sha256:65757c5fe189a1abd87829f1a4aed496bd08874ab77f4d326e0a83e76707d14d" - ], - "index": "pypi", - "version": "==1.1.1" - }, - "networkx": { - "hashes": [ - "sha256:15cdf7f7c157637107ea690cabbc488018f8256fa28242aed0fb24c93c03a06d", - "sha256:815383fd52ece0a7024b5fd8408cc13a389ea350cd912178b82eed8b96f82cd3" - ], - "markers": "python_version >= '3.8'", - "version": "==2.8.7" - }, - "numpy": { - "hashes": [ - "sha256:0fe563fc8ed9dc4474cbf70742673fc4391d70f4363f917599a7fa99f042d5a8", - "sha256:12ac457b63ec8ded85d85c1e17d85efd3c2b0967ca39560b307a35a6703a4735", - "sha256:2341f4ab6dba0834b685cce16dad5f9b6606ea8a00e6da154f5dbded70fdc4dd", - "sha256:296d17aed51161dbad3c67ed6d164e51fcd18dbcd5dd4f9d0a9c6055dce30810", - "sha256:488a66cb667359534bc70028d653ba1cf307bae88eab5929cd707c761ff037db", - "sha256:4d52914c88b4930dafb6c48ba5115a96cbab40f45740239d9f4159c4ba779962", - "sha256:5e13030f8793e9ee42f9c7d5777465a560eb78fa7e11b1c053427f2ccab90c79", - "sha256:61be02e3bf810b60ab74e81d6d0d36246dbfb644a462458bb53b595791251911", - "sha256:7607b598217745cc40f751da38ffd03512d33ec06f3523fb0b5f82e09f6f676d", - "sha256:7a70a7d3ce4c0e9284e92285cba91a4a3f5214d87ee0e95928f3614a256a1488", - "sha256:7ab46e4e7ec63c8a5e6dbf5c1b9e1c92ba23a7ebecc86c336cb7bf3bd2fb10e5", - "sha256:8981d9b5619569899666170c7c9748920f4a5005bf79c72c07d08c8a035757b0", - "sha256:8c053d7557a8f022ec823196d242464b6955a7e7e5015b719e76003f63f82d0f", - "sha256:926db372bc4ac1edf81cfb6c59e2a881606b409ddc0d0920b988174b2e2a767f", - "sha256:95d79ada05005f6f4f337d3bb9de8a7774f259341c70bc88047a1f7b96a4bcb2", - "sha256:95de7dc7dc47a312f6feddd3da2500826defdccbc41608d0031276a24181a2c0", - "sha256:a0882323e0ca4245eb0a3d0a74f88ce581cc33aedcfa396e415e5bba7bf05f68", - "sha256:a8365b942f9c1a7d0f0dc974747d99dd0a0cdfc5949a33119caf05cb314682d3", - "sha256:a8aae2fb3180940011b4862b2dd3756616841c53db9734b27bb93813cd79fce6", - "sha256:c237129f0e732885c9a6076a537e974160482eab8f10db6292e92154d4c67d71", - "sha256:c67b833dbccefe97cdd3f52798d430b9d3430396af7cdb2a0c32954c3ef73894", - "sha256:ce03305dd694c4873b9429274fd41fc7eb4e0e4dea07e0af97a933b079a5814f", - "sha256:d331afac87c92373826af83d2b2b435f57b17a5c74e6268b79355b970626e329", - "sha256:dada341ebb79619fe00a291185bba370c9803b1e1d7051610e01ed809ef3a4ba", - "sha256:ed2cc92af0efad20198638c69bb0fc2870a58dabfba6eb722c933b48556c686c", - "sha256:f260da502d7441a45695199b4e7fd8ca87db659ba1c78f2bbf31f934fe76ae0e", - "sha256:f2f390aa4da44454db40a1f0201401f9036e8d578a25f01a6e237cea238337ef", - "sha256:f76025acc8e2114bb664294a07ede0727aa75d63a06d2fae96bf29a81747e4a7" - ], - "markers": "python_version >= '3.8'", - "version": "==1.23.4" - }, - "pandas": { - "hashes": [ - "sha256:04e51b01d5192499390c0015630975f57836cc95c7411415b499b599b05c0c96", - "sha256:05c527c64ee02a47a24031c880ee0ded05af0623163494173204c5b72ddce658", - "sha256:0a78e05ec09731c5b3bd7a9805927ea631fe6f6cb06f0e7c63191a9a778d52b4", - "sha256:17da7035d9e6f9ea9cdc3a513161f8739b8f8489d31dc932bc5a29a27243f93d", - "sha256:249cec5f2a5b22096440bd85c33106b6102e0672204abd2d5c014106459804ee", - "sha256:2c25e5c16ee5c0feb6cf9d982b869eec94a22ddfda9aa2fbed00842cbb697624", - "sha256:32e3d9f65606b3f6e76555bfd1d0b68d94aff0929d82010b791b6254bf5a4b96", - "sha256:36aa1f8f680d7584e9b572c3203b20d22d697c31b71189322f16811d4ecfecd3", - "sha256:5b0c970e2215572197b42f1cff58a908d734503ea54b326412c70d4692256391", - "sha256:5cee0c74e93ed4f9d39007e439debcaadc519d7ea5c0afc3d590a3a7b2edf060", - "sha256:669c8605dba6c798c1863157aefde959c1796671ffb342b80fcb80a4c0bc4c26", - "sha256:66a1ad667b56e679e06ba73bb88c7309b3f48a4c279bd3afea29f65a766e9036", - "sha256:683779e5728ac9138406c59a11e09cd98c7d2c12f0a5fc2b9c5eecdbb4a00075", - "sha256:6bb391659a747cf4f181a227c3e64b6d197100d53da98dcd766cc158bdd9ec68", - "sha256:81f0674fa50b38b6793cd84fae5d67f58f74c2d974d2cb4e476d26eee33343d0", - "sha256:927e59c694e039c75d7023465d311277a1fc29ed7236b5746e9dddf180393113", - "sha256:932d2d7d3cab44cfa275601c982f30c2d874722ef6396bb539e41e4dc4618ed4", - "sha256:a52419d9ba5906db516109660b114faf791136c94c1a636ed6b29cbfff9187ee", - "sha256:b156a971bc451c68c9e1f97567c94fd44155f073e3bceb1b0d195fd98ed12048", - "sha256:bcf1a82b770b8f8c1e495b19a20d8296f875a796c4fe6e91da5ef107f18c5ecb", - "sha256:cb2a9cf1150302d69bb99861c5cddc9c25aceacb0a4ef5299785d0f5389a3209", - "sha256:d8c709f4700573deb2036d240d140934df7e852520f4a584b2a8d5443b71f54d", - "sha256:db45b94885000981522fb92349e6b76f5aee0924cc5315881239c7859883117d", - "sha256:ddf46b940ef815af4e542697eaf071f0531449407a7607dd731bf23d156e20a7", - "sha256:e675f8fe9aa6c418dc8d3aac0087b5294c1a4527f1eacf9fe5ea671685285454", - "sha256:eb7e8cf2cf11a2580088009b43de84cabbf6f5dae94ceb489f28dba01a17cb77", - "sha256:f340331a3f411910adfb4bbe46c2ed5872d9e473a783d7f14ecf49bc0869c594" - ], - "markers": "python_version >= '3.8'", - "version": "==1.5.1" - }, - "python-dateutil": { - "hashes": [ - "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86", - "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==2.8.2" - }, - "python-slugify": { - "hashes": [ - "sha256:272d106cb31ab99b3496ba085e3fea0e9e76dcde967b5e9992500d1f785ce4e1", - "sha256:7b2c274c308b62f4269a9ba701aa69a797e9bca41aeee5b3a9e79e36b6656927" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'", - "version": "==6.1.2" - }, - "pytz": { - "hashes": [ - "sha256:222439474e9c98fced559f1709d89e6c9cbf8d79c794ff3eb9f8800064291427", - "sha256:e89512406b793ca39f5971bc999cc538ce125c0e51c27941bef4568b460095e2" - ], - "version": "==2022.6" - }, - "pyyaml": { - "hashes": [ - "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf", - "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", - "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", - "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", - "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", - "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", - "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", - "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", - "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", - "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", - "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", - "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", - "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782", - "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", - "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", - "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", - "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", - "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", - "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1", - "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", - "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", - "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", - "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", - "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", - "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", - "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d", - "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", - "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", - "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7", - "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", - "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", - "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", - "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358", - "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", - "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", - "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", - "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", - "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f", - "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", - "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" - ], - "markers": "python_version >= '3.6'", - "version": "==6.0" - }, - "requests": { - "hashes": [ - "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983", - "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349" - ], - "markers": "python_version >= '3.7' and python_version < '4'", - "version": "==2.28.1" - }, - "six": { - "hashes": [ - "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", - "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==1.16.0" - }, - "text-unidecode": { - "hashes": [ - "sha256:1311f10e8b895935241623731c2ba64f4c455287888b18189350b67134a822e8", - "sha256:bad6603bb14d279193107714b288be206cac565dfa49aa5b105294dd5c4aab93" - ], - "version": "==1.3" - }, - "tornado": { - "hashes": [ - "sha256:1d54d13ab8414ed44de07efecb97d4ef7c39f7438cf5e976ccd356bebb1b5fca", - "sha256:20f638fd8cc85f3cbae3c732326e96addff0a15e22d80f049e00121651e82e72", - "sha256:5c87076709343557ef8032934ce5f637dbb552efa7b21d08e89ae7619ed0eb23", - "sha256:5f8c52d219d4995388119af7ccaa0bcec289535747620116a58d830e7c25d8a8", - "sha256:6fdfabffd8dfcb6cf887428849d30cf19a3ea34c2c248461e1f7d718ad30b66b", - "sha256:87dcafae3e884462f90c90ecc200defe5e580a7fbbb4365eda7c7c1eb809ebc9", - "sha256:9b630419bde84ec666bfd7ea0a4cb2a8a651c2d5cccdbdd1972a0c859dfc3c13", - "sha256:b8150f721c101abdef99073bf66d3903e292d851bee51910839831caba341a75", - "sha256:ba09ef14ca9893954244fd872798b4ccb2367c165946ce2dd7376aebdde8e3ac", - "sha256:d3a2f5999215a3a06a4fc218026cd84c61b8b2b40ac5296a6db1f1451ef04c1e", - "sha256:e5f923aa6a47e133d1cf87d60700889d7eae68988704e20c75fb2d65677a8e4b" - ], - "markers": "python_version >= '3.7'", - "version": "==6.2" - }, - "tqdm": { - "hashes": [ - "sha256:5f4f682a004951c1b450bc753c710e9280c5746ce6ffedee253ddbcbf54cf1e4", - "sha256:6fee160d6ffcd1b1c68c65f14c829c22832bc401726335ce92c52d395944a6a1" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", - "version": "==4.64.1" - }, - "urllib3": { - "hashes": [ - "sha256:3fa96cf423e6987997fc326ae8df396db2a8b7c667747d47ddd8ecba91f4a74e", - "sha256:b930dd878d5a8afb066a637fbb35144fe7901e3b209d1cd4f524bd0e9deee997" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", - "version": "==1.26.12" - } - }, - "develop": { - "attrs": { - "hashes": [ - "sha256:29adc2665447e5191d0e7c568fde78b21f9672d344281d0c6e1ab085429b22b6", - "sha256:86efa402f67bf2df34f51a335487cf46b1ec130d02b8d39fd248abfd30da551c" - ], - "markers": "python_version >= '3.5'", - "version": "==22.1.0" - }, - "coverage": { - "extras": [ - "toml" - ], - "hashes": [ - "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79", - "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a", - "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f", - "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a", - "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa", - "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398", - "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba", - "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d", - "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf", - "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b", - "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518", - "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d", - "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795", - "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2", - "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e", - "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32", - "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745", - "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b", - "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e", - "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d", - "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f", - "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660", - "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62", - "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6", - "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04", - "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c", - "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5", - "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef", - "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc", - "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae", - "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578", - "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466", - "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4", - "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91", - "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0", - "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4", - "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b", - "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe", - "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b", - "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75", - "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b", - "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c", - "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72", - "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b", - "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f", - "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e", - "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53", - "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3", - "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84", - "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987" - ], - "markers": "python_version >= '3.7'", - "version": "==6.5.0" - }, - "exceptiongroup": { - "hashes": [ - "sha256:2ac84b496be68464a2da60da518af3785fff8b7ec0d090a581604bc870bdee41", - "sha256:affbabf13fb6e98988c38d9c5650e701569fe3c1de3233cfb61c5f33774690ad" - ], - "markers": "python_version < '3.11'", - "version": "==1.0.0" - }, - "iniconfig": { - "hashes": [ - "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", - "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" - ], - "version": "==1.1.1" - }, - "packaging": { - "hashes": [ - "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", - "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" - ], - "markers": "python_version >= '3.6'", - "version": "==21.3" - }, - "pluggy": { - "hashes": [ - "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", - "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" - ], - "markers": "python_version >= '3.6'", - "version": "==1.0.0" - }, - "py": { - "hashes": [ - "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", - "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" - ], - "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", - "version": "==1.11.0" - }, - "pyparsing": { - "hashes": [ - "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", - "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" - ], - "markers": "python_full_version >= '3.6.8'", - "version": "==3.0.9" - }, - "pytest": { - "hashes": [ - "sha256:892f933d339f068883b6fd5a459f03d85bfcb355e4981e146d2c7616c21fef71", - "sha256:c4014eb40e10f11f355ad4e3c2fb2c6c6d1919c73f3b5a433de4708202cade59" - ], - "index": "pypi", - "version": "==7.2.0" - }, - "pytest-cov": { - "hashes": [ - "sha256:2feb1b751d66a8bd934e5edfa2e961d11309dc37b73b0eabe73b5945fee20f6b", - "sha256:996b79efde6433cdbd0088872dbc5fb3ed7fe1578b68cdbba634f14bb8dd0470" - ], - "index": "pypi", - "version": "==4.0.0" - }, - "tomli": { - "hashes": [ - "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", - "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" - ], - "markers": "python_version < '3.11'", - "version": "==2.0.1" - } - } -} From cbd7a43adeeaf54a03ed031648a78b2ca78c8450 Mon Sep 17 00:00:00 2001 From: Glenn Lehman Date: Tue, 25 Apr 2023 18:33:17 -0600 Subject: [PATCH 105/214] Update tutorial - add background information, - add model-specific information - add technical information - update code comments - update exercise 1 - delete .rst and .pngs due to addition of nbsphinx Co-authored-by: rht Co-authored-by: tpike3 --- docs/README.md | 12 +- docs/index.rst | 2 +- docs/tutorials/files/output_19_1.png | Bin 4406 -> 0 bytes docs/tutorials/intro_tutorial.ipynb | 747 ++++------ docs/tutorials/intro_tutorial.rst | 1215 ----------------- .../intro_tutorial_19_1.png | Bin 5734 -> 0 bytes .../intro_tutorial_22_1.png | Bin 9932 -> 0 bytes .../intro_tutorial_32_1.png | Bin 10048 -> 0 bytes .../intro_tutorial_38_1.png | Bin 20884 -> 0 bytes .../intro_tutorial_42_1.png | Bin 7671 -> 0 bytes .../intro_tutorial_44_1.png | Bin 13631 -> 0 bytes .../intro_tutorial_57_1.png | Bin 19255 -> 0 bytes .../intro_tutorial_files/output_19_1.png | Bin 3636 -> 0 bytes .../intro_tutorial_files/output_22_1.png | Bin 3485 -> 0 bytes .../intro_tutorial_files/output_32_1.png | Bin 4946 -> 0 bytes .../intro_tutorial_files/output_38_1.png | Bin 13563 -> 0 bytes .../intro_tutorial_files/output_42_1.png | Bin 3745 -> 0 bytes .../intro_tutorial_files/output_44_1.png | Bin 9453 -> 0 bytes .../intro_tutorial_files/output_57_1.png | Bin 10520 -> 0 bytes 19 files changed, 297 insertions(+), 1679 deletions(-) delete mode 100644 docs/tutorials/files/output_19_1.png delete mode 100644 docs/tutorials/intro_tutorial.rst delete mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_19_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_22_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_32_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_38_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_42_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_44_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/intro_tutorial_57_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/output_19_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/output_22_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/output_32_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/output_38_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/output_42_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/output_44_1.png delete mode 100644 docs/tutorials/intro_tutorial_files/output_57_1.png diff --git a/docs/README.md b/docs/README.md index 854106731c6..0a85e858703 100644 --- a/docs/README.md +++ b/docs/README.md @@ -9,21 +9,13 @@ This folder contains the docs that build the docs for the core mesa code on read Updating docs can be confusing. Here are the basic setups. -#### Create the rST files from ipynb files -1. Change to the appropriate directory (usually docs/tutorials) - * `cd tutorials` -1. Create rST files using nbconvert - * `jupyter nbconvert --to rST *.ipynb` - * **Requires** - * jupyter: `pip install jupyter` - * [pandoc](http://pandoc.org/installing.html) - ##### Submit a pull request with updates 1. Create branch (either via branching or fork of repo) -- try to use a descriptive name. * `git checkout -b doc-updates` 1. Update the docs. Save. 1. Build the docs, from the inside of the docs folder. * **Requires** sphinx: `pip install sphinx` + * **Requires** nbsphinx: `pip install nbsphinx` (this will render the images from jupyter in the docs) * `make html` 1. Commit the changes. If there are new files, you will have to explicit add them. * `git commit -am "Updating docs."` @@ -33,7 +25,7 @@ Updating docs can be confusing. Here are the basic setups. ##### Update read the docs -From this point, you will need to find someone that has access to readthedocs. Currently, that is [@jackiekazil](https://github.com/jackiekazil) and [@dmasad](https://github.com/dmasad). +From this point, you will need to find someone that has access to readthedocs. Currently, that is [@jackiekazil](https://github.com/jackiekazil), [@rht](https://github.com/rht), and [@tpike3](https://github.com/dmasad). 1. Accept the pull request into main. 1. Log into readthedocs and launch a new build -- builds take about 10 minutes or so. diff --git a/docs/index.rst b/docs/index.rst index 2ea1281675d..5388c1712ff 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -95,7 +95,7 @@ ABM features users have shared that you may want to use in your model :maxdepth: 7 Mesa Overview - tutorials/intro_tutorial + tutorials/intro_tutorial.ipynb tutorials/adv_tutorial.ipynb Best Practices Useful Snippets diff --git a/docs/tutorials/files/output_19_1.png b/docs/tutorials/files/output_19_1.png deleted file mode 100644 index 010be9f0c5a9c9ccc1175083c25211da04eec40d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4406 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 0:\n", + " other_agent = self.random.choice(self.model.schedule.agents)\n", + " if other_agent is not None:\n", + " other_agent.wealth += 1\n", + " self.wealth -= 1" ] }, { @@ -320,10 +467,10 @@ "\n", "With that last piece in hand, it's time for the first rudimentary run of the model.\n", "\n", - "If you've written the code in its own file (`money_model.py` or a different name), launch an interpreter in the same directory as the file (either the plain Python command-line interpreter, or the IPython interpreter), or launch a Jupyter Notebook there. Then import the classes you created. (If you wrote the code in a Notebook, obviously this step isn't necessary).\n", + "If you've written the code in its own script file (`money_model.py` or a different name) you can now modify your ``run.py`` or even launch a Jupyter Notebook. You then just follow the same three steps of (1) import your model class ``MoneyModel``, (2) create the model object and (3) run it for a few steps. If you wrote the code in one Notebook then step 1, importing, is not necessary.\n", "\n", "```python\n", - "from money_model import *\n", + "from money_model import MoneyModel\n", "```\n", "\n", "Now let's create a model with 10 agents, and run it for 10 steps." @@ -331,11 +478,11 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:36.584738Z", - "start_time": "2023-04-25T11:12:36.582444Z" + "end_time": "2023-04-25T18:23:41.790021Z", + "start_time": "2023-04-25T18:23:41.724975Z" } }, "outputs": [], @@ -365,37 +512,14 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:36.927134Z", - "start_time": "2023-04-25T11:12:36.587055Z" + "end_time": "2023-04-25T18:23:41.882917Z", + "start_time": "2023-04-25T18:23:41.727492Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "(array([5., 0., 0., 2., 0., 0., 1., 0., 0., 2.]),\n", - " array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7, 3. ]),\n", - " )" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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5pgolUpx0003xapVq+Kss84acH1HR0c0Nzf33VpaWg5pYABg5Ko4LKZOnRqbN2+O3/72t3HzzTfHwoUL4+WXXx5wfVtbW3R1dfXdOjs7D2lgAGDkqvhHIY2NjXHGGWdERMS5554bL774Ytx1111xzz33HHB9qVSKUql0aFMCAHXhkP+ORW9vb7/PUAAAo1dF71i0tbXFVVddFSeffHLs2bMn7r///lizZk2sXr26WvMBAHWkorDYvXt3fP7zn4+dO3dGc3NzzJw5M1avXh2XX355teYDAOpIRWHxk5/8pFpzAACHAdcKAQDSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSCAsAII2wAADSVBQWHR0dcd5558X48eNj4sSJMX/+/Ni+fXu1ZgMA6kxFYbF27dpobW2N9evXx5NPPhnvvvtuXHHFFdHT01Ot+QCAOjK2ksVPPPFEv/srVqyIiRMnxoYNG+JTn/pU6mAAQP2pKCzer6urKyIiJkyYMOCacrkc5XK57353d/ehvCQAMIINOSx6e3tj0aJFceGFF8aMGTMGXNfR0RHt7e1DfRlIceqSx2o9wpC8sXRerUeAUa0ezx21Pm8M+bdCWltbY+vWrbFy5cqDrmtra4uurq6+W2dn51BfEgAY4Yb0jsUtt9wSjz76aKxbty6mTJly0LWlUilKpdKQhgMA6ktFYVEURXz1q1+NVatWxZo1a+K0006r1lwAQB2qKCxaW1vj/vvvj0ceeSTGjx8fu3btioiI5ubmOOqoo6oyIABQPyr6jMXy5cujq6sr5s6dG5MmTeq7PfDAA9WaDwCoIxX/KAQAYCCuFQIApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAEAaYQEApBEWAECaisNi3bp1cfXVV8fkyZOjoaEhHn744SqMBQDUo4rDoqenJ2bNmhXLli2rxjwAQB0bW+kTrrrqqrjqqquqMQsAUOcqDotKlcvlKJfLffe7u7ur/ZIAQI1UPSw6Ojqivb292i8DMGSnLnms1iNU7I2l82o9AhxQ1X8rpK2tLbq6uvpunZ2d1X5JAKBGqv6ORalUilKpVO2XAQBGAH/HAgBIU/E7Fnv37o0dO3b03X/99ddj8+bNMWHChDj55JNThwMA6kvFYfHSSy/FxRdf3Hd/8eLFERGxcOHCWLFiRdpgAED9qTgs5s6dG0VRVGMWAKDO+YwFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBGWAAAaYQFAJBmSGGxbNmyOPXUU2PcuHFx/vnnx+9+97vsuQCAOlRxWDzwwAOxePHiuO2222Ljxo0xa9asuPLKK2P37t3VmA8AqCMVh8Udd9wRX/rSl+KGG26Is846K+6+++44+uij4957763GfABAHRlbyeL//Oc/sWHDhmhra+t77IgjjojLLrssXnjhhQM+p1wuR7lc7rvf1dUVERHd3d1Dmfegesv/Sv+e1VaNfWB/9XhsRDg+hks9Hh+OjeHh2Nj/+xZFcdB1FYXFX//619i3b1+ceOKJ/R4/8cQT4w9/+MMBn9PR0RHt7e37Pd7S0lLJSx+2mr9f6wkYyRwfDMSxwUCqfWzs2bMnmpubB/x6RWExFG1tbbF48eK++729vfH3v/89jj/++GhoaEh7ne7u7mhpaYnOzs5oampK+76HI3s1ePaqMvZr8OzV4NmrwavmXhVFEXv27InJkycfdF1FYfHhD384xowZE++8806/x99555046aSTDvicUqkUpVKp32PHHntsJS9bkaamJgfeINmrwbNXlbFfg2evBs9eDV619upg71S8p6IPbzY2Nsa5554bTz/9dN9jvb298fTTT8ecOXMqnxAAOKxU/KOQxYsXx8KFC+MTn/hEzJ49O77//e9HT09P3HDDDdWYDwCoIxWHxbXXXht/+ctf4lvf+lbs2rUrzj777HjiiSf2+0DncCuVSnHbbbft92MX9mevBs9eVcZ+DZ69Gjx7NXgjYa8aig/6vREAgEFyrRAAII2wAADSCAsAII2wAADS1FVYVHq59gcffDCmTZsW48aNi4997GPxq1/9apgmrb1K9mrFihXR0NDQ7zZu3LhhnLZ21q1bF1dffXVMnjw5Ghoa4uGHH/7A56xZsybOOeecKJVKccYZZ8SKFSuqPudIUOlerVmzZr/jqqGhIXbt2jU8A9dQR0dHnHfeeTF+/PiYOHFizJ8/P7Zv3/6BzxuN56yh7NVoPWctX748Zs6c2ffHr+bMmROPP/74QZ9Ti2OqbsKi0su1P//887FgwYK48cYbY9OmTTF//vyYP39+bN26dZgnH35DubR9U1NT7Ny5s+/25ptvDuPEtdPT0xOzZs2KZcuWDWr966+/HvPmzYuLL744Nm/eHIsWLYovfvGLsXr16ipPWnuV7tV7tm/f3u/YmjhxYpUmHDnWrl0bra2tsX79+njyySfj3XffjSuuuCJ6enoGfM5oPWcNZa8iRuc5a8qUKbF06dLYsGFDvPTSS3HJJZfENddcE9u2bTvg+podU0WdmD17dtHa2tp3f9++fcXkyZOLjo6OA67/7Gc/W8ybN6/fY+eff37xla98papzjgSV7tV9991XNDc3D9N0I1dEFKtWrTromm984xvF9OnT+z127bXXFldeeWUVJxt5BrNXzz77bBERxT/+8Y9hmWkk2717dxERxdq1awdcM5rPWf9rMHvlnPV/jjvuuOLHP/7xAb9Wq2OqLt6xeO9y7ZdddlnfYx90ufYXXnih3/qIiCuvvHLA9YeLoexVRMTevXvjlFNOiZaWloMW8Gg3Wo+rQ3H22WfHpEmT4vLLL4/nnnuu1uPURFdXV0RETJgwYcA1jq3/GsxeRThn7du3L1auXBk9PT0DXlKjVsdUXYTFwS7XPtDPa3ft2lXR+sPFUPZq6tSpce+998YjjzwSP/vZz6K3tzcuuOCCeOutt4Zj5Loy0HHV3d0d//73v2s01cg0adKkuPvuu+Ohhx6Khx56KFpaWmLu3LmxcePGWo82rHp7e2PRokVx4YUXxowZMwZcN1rPWf9rsHs1ms9ZW7ZsiWOOOSZKpVLcdNNNsWrVqjjrrLMOuLZWx1TVL5vOyDdnzpx+xXvBBRfERz/60bjnnnvi29/+dg0no55NnTo1pk6d2nf/ggsuiNdeey3uvPPO+OlPf1rDyYZXa2trbN26NX7zm9/UepQRb7B7NZrPWVOnTo3NmzdHV1dX/PKXv4yFCxfG2rVrB4yLWqiLdyyGcrn2k046qaL1h4uh7NX7HXnkkfHxj388duzYUY0R69pAx1VTU1McddRRNZqqfsyePXtUHVe33HJLPProo/Hss8/GlClTDrp2tJ6z3lPJXr3faDpnNTY2xhlnnBHnnntudHR0xKxZs+Kuu+464NpaHVN1ERZDuVz7nDlz+q2PiHjyyScP+8u7Z1zaft++fbFly5aYNGlStcasW6P1uMqyefPmUXFcFUURt9xyS6xatSqeeeaZOO200z7wOaP12BrKXr3faD5n9fb2RrlcPuDXanZMVfWjoYlWrlxZlEqlYsWKFcXLL79cfPnLXy6OPfbYYteuXUVRFMX1119fLFmypG/9c889V4wdO7b43ve+V7zyyivFbbfdVhx55JHFli1bavVPGDaV7lV7e3uxevXq4rXXXis2bNhQfO5znyvGjRtXbNu2rVb/hGGzZ8+eYtOmTcWmTZuKiCjuuOOOYtOmTcWbb75ZFEVRLFmypLj++uv71v/xj38sjj766OLrX/968corrxTLli0rxowZUzzxxBO1+icMm0r36s477ywefvjh4tVXXy22bNlS3HrrrcURRxxRPPXUU7X6Jwybm2++uWhubi7WrFlT7Ny5s+/2r3/9q2+Nc9Z/DWWvRus5a8mSJcXatWuL119/vfj9739fLFmypGhoaCh+/etfF0Uxco6pugmLoiiKH/zgB8XJJ59cNDY2FrNnzy7Wr1/f97WLLrqoWLhwYb/1v/jFL4ozzzyzaGxsLKZPn1489thjwzxx7VSyV4sWLepbe+KJJxaf/vSni40bN9Zg6uH33q9Evv/23v4sXLiwuOiii/Z7ztlnn100NjYWp59+enHfffcN+9y1UOleffe73y0+8pGPFOPGjSsmTJhQzJ07t3jmmWdqM/wwO9A+RUS/Y8U567+Gslej9Zz1hS98oTjllFOKxsbG4oQTTiguvfTSvqgoipFzTLlsOgCQpi4+YwEA1AdhAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCkERYAQBphAQCk+X+5C1FNEpisiAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# For a jupyter notebook add the following line:\n", "%matplotlib inline\n", @@ -423,37 +547,14 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.018897Z", - "start_time": "2023-04-25T11:12:36.942189Z" + "end_time": "2023-04-25T18:23:42.027373Z", + "start_time": "2023-04-25T18:23:41.886918Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "(array([416., 324., 155., 68., 25., 12.]),\n", - " array([0., 1., 2., 3., 4., 5., 6.]),\n", - " )" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "all_wealth = []\n", "# This runs the model 100 times, each model executing 10 steps.\n", @@ -503,11 +604,11 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.023426Z", - "start_time": "2023-04-25T11:12:37.020889Z" + "end_time": "2023-04-25T18:23:42.030776Z", + "start_time": "2023-04-25T18:23:42.028374Z" } }, "outputs": [], @@ -594,11 +695,11 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.029893Z", - "start_time": "2023-04-25T11:12:37.028385Z" + "end_time": "2023-04-25T18:23:42.036559Z", + "start_time": "2023-04-25T18:23:42.033158Z" } }, "outputs": [], @@ -659,11 +760,11 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.036370Z", - "start_time": "2023-04-25T11:12:37.030892Z" + "end_time": "2023-04-25T18:23:42.042251Z", + "start_time": "2023-04-25T18:23:42.040741Z" } }, "outputs": [], @@ -682,35 +783,14 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.168601Z", - "start_time": "2023-04-25T11:12:37.036888Z" + "end_time": "2023-04-25T18:23:42.207690Z", + "start_time": "2023-04-25T18:23:42.043252Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeMAAAGiCAYAAADUc67xAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAmrUlEQVR4nO3df3RU9Z3/8dckhUnUTBbEJPwIkBULYvj9yyTfo7iyUoqu2bNLrYceWKrYH0kLpkdLulVsWY1o+eEKEqJi3K0pSHuAliI2jSdkWcICgfSAbbGuLkwtE6THJhLLhM7c7x/AtNP8IDc3mfuZzPNxzuf05HI/c98MlLfv9+dz7/VYlmUJAAC4JsntAAAASHQkYwAAXEYyBgDAZSRjAABcRjIGAMBlJGMAAFxGMgYAwGUkYwAAXEYyBgDAZSRjAABcRjIGAEDSpk2bNHHiRPl8Pvl8PuXl5emNN97ocs727ds1btw4paSkaMKECdqzZ0+Prk0yBgBA0ogRI/T000+roaFBR44c0d/93d/p3nvv1dtvv93h+QcOHND999+vBx54QMeOHVNhYaEKCwt14sQJ29f28KIIAAA6NnjwYD377LN64IEH2v3afffdp9bWVu3evTty7NZbb9XkyZNVXl5u6zqfchypTeFwWL/73e+UlpYmj8cT68sDABywLEsff/yxhg0bpqSkvmuuXrhwQW1tbY4/x7KsdrnG6/XK6/V2OS8UCmn79u1qbW1VXl5eh+fU19erpKQk6tjcuXO1c+dO23HGPBn/7ne/U3Z2dqwvCwDoRX6/XyNGjOiTz75w4YJyRl2nwNmQ48+67rrrdP78+ahjK1eu1BNPPNHh+cePH1deXp4uXLig6667Tjt27ND48eM7PDcQCCgzMzPqWGZmpgKBgO04Y56M09LSJEn/T5/VpzQg1pdHP/T7JTPdDgE9NGD+ObdDaMf3z++5HYLR/qSL2q89kX/L+0JbW5sCZ0N6v2GUfGk9r75bPg4rZ9op+f1++Xy+yPGuquKxY8eqsbFRzc3N+uEPf6jFixdr3759nSbk3hLzZHylXfApDdCnPCRjOJc8MMXtENBDydd23Sp0A/8uXcXlXUaxWGb0pSU5SsaRz7m8O7o7Bg4cqDFjxkiSpk2bpsOHD+u5557T5s2b252blZWlpqamqGNNTU3KysqyHSO7qQEARgpZYcfDqXA4rGAw2OGv5eXlqaamJupYdXV1p2vMXYl5ZQwAQHeEZSmsnt/wY3duaWmp5s2bp5EjR+rjjz9WVVWVamtr9eabb0qSFi1apOHDh6usrEyStGzZMt1+++1as2aN5s+fr61bt+rIkSOqqKiwHSvJGABgpLDCclLb2p199uxZLVq0SGfOnFF6eromTpyoN998U3//938vSTp9+nTUDvL8/HxVVVXp29/+tr71rW/ppptu0s6dO5Wbm2s7VpIxAACSXn755S5/vba2tt2xBQsWaMGCBY6vTTIGABgpZFkKOXgulZO5sUYyBgAYKdZrxm5iNzUAAC6jMgYAGCksS6EEqYxJxgAAI9GmBgAAMUNlDAAwUiLtpu5RZbxx40aNHj1aKSkpmjVrlg4dOtTbcQEAEly4F0a8sJ2Mt23bppKSEq1cuVJHjx7VpEmTNHfuXJ09e7Yv4gMAoN+znYzXrl2rpUuXasmSJRo/frzKy8t1zTXXaMuWLX0RHwAgQYUu76Z2MuKFrTXjtrY2NTQ0qLS0NHIsKSlJc+bMUX19fYdzgsFg1BsvWlpaehgqACCRhKxLw8n8eGGrMj537pxCoZAyMzOjjmdmZioQCHQ4p6ysTOnp6ZGRnZ3d82gBAAmDNeNeVFpaqubm5sjw+/19fUkAAOKKrTb1kCFDlJycrKampqjjTU1NysrK6nCO1+uV1+vteYQAgIQUlkcheRzNjxe2KuOBAwdq2rRpqqmpiRwLh8OqqalRXl5erwcHAEhcYcv5iBe2H/pRUlKixYsXa/r06Zo5c6bWr1+v1tZWLVmypC/iAwCg37OdjO+77z59+OGHevzxxxUIBDR58mTt3bu33aYuAACcCDlsUzuZG2s9ehxmcXGxiouLezsWAAAiEikZ86IIAABcxosiAABGClsehS0Hu6kdzI01kjEAwEi0qQEAQMxQGQMAjBRSkkIOasZQL8bS10jGAAAjWQ7XjC3WjAEAcIY1YwAAEDNUxgAAI4WsJIUsB2vG/fnZ1AAAxEJYHoUdNHDDip9sTJsaAACXuVYZ/37JTCUPTHHr8u0Mqah3O4R2zj3Eaym7w8Q/OxM17xnjdgjtpH/2XbdDaIf/33Ut1HZBemVXbK6VQBu4aFMDAIzkfM2YNjUAAOgmKmMAgJEubeBy8KII2tQAADgTdvg4THZTAwCAbqMyBgAYKZE2cJGMAQBGCispYR76QTIGABgpZHkUcvDmJSdzY401YwAAXEZlDAAwUsjhbuoQbWoAAJwJW0kKO9jAFY6jDVy0qQEAcBmVMQDASLSpAQBwWVjOdkSHey+UPkebGgAAl1EZAwCM5PyhH/FTb5KMAQBGcv44zPhJxvETKQAA/RSVMQDASLzPGAAAlyVSm5pkDAAwkvP7jOMnGcdPpAAA9KGysjLNmDFDaWlpysjIUGFhoU6ePNnlnMrKSnk8nqiRkpJi+9okYwCAkcKWx/GwY9++fSoqKtLBgwdVXV2tixcv6q677lJra2uX83w+n86cORMZp06dsv17pU0NADBS2GGb+sp9xi0tLVHHvV6vvF5vu/P37t0b9XNlZaUyMjLU0NCg2267rdPreDweZWVl9ThOicoYANDPZWdnKz09PTLKysq6Na+5uVmSNHjw4C7PO3/+vEaNGqXs7Gzde++9evvtt23HSGUMADCS81coXprr9/vl8/kixzuqitvNDYe1fPlyFRQUKDc3t9Pzxo4dqy1btmjixIlqbm7W9773PeXn5+vtt9/WiBEjuh0ryRgAYKSQPAo5uFf4ylyfzxeVjLujqKhIJ06c0P79+7s8Ly8vT3l5eZGf8/PzdfPNN2vz5s1atWpVt69HMgYA4C8UFxdr9+7dqqurs1XdStKAAQM0ZcoUvfvuu7bmsWYMADDSlTa1k2GHZVkqLi7Wjh079NZbbyknJ8d2zKFQSMePH9fQoUNtzaMyBgAYKSQ5bFPbU1RUpKqqKu3atUtpaWkKBAKSpPT0dKWmpkqSFi1apOHDh0c2gX33u9/VrbfeqjFjxugPf/iDnn32WZ06dUoPPvigrWuTjAEAkLRp0yZJ0uzZs6OOv/LKK/qXf/kXSdLp06eVlPTnivujjz7S0qVLFQgENGjQIE2bNk0HDhzQ+PHjbV2bZAwAMFJv7abuLsuyrnpObW1t1M/r1q3TunXrbF2nIyRjAICReFEEAAAusxy+QtGKo1coxs9/NgAA0E9RGQMAjESbOgbqSl+WL82cL2puxWS3Q2hnSEW92yGgH7m48wa3Q+iAvQcjJCqT/i34k3UxZtfqyZuX/np+vDAnGwIAkKBoUwMAjBRy+ApFJ3NjjWQMADASbWoAABAzVMYAACOFlaSwg5rRydxYIxkDAIwUsjwKOWg1O5kba/Hznw0AAPRTVMYAACMl0gYukjEAwEiWw7c2WTyBCwAAZ0LyKOTgZQ9O5sZa/PxnAwAA/RSVMQDASGHL2bpv2OrFYPoYyRgAYKSwwzVjJ3NjLX4iBQCgn7KVjMvKyjRjxgylpaUpIyNDhYWFOnnyZF/FBgBIYGF5HI94YSsZ79u3T0VFRTp48KCqq6t18eJF3XXXXWptbe2r+AAACerKE7icjHhha8147969UT9XVlYqIyNDDQ0Nuu2223o1MAAAEoWjDVzNzc2SpMGDB3d6TjAYVDAYjPzc0tLi5JIAgATBBq5uCIfDWr58uQoKCpSbm9vpeWVlZUpPT4+M7Ozsnl4SAJBAwvJEHonZo9Ff14z/UlFRkU6cOKGtW7d2eV5paamam5sjw+/39/SSAAD0Sz1qUxcXF2v37t2qq6vTiBEjujzX6/XK6/X2KDgAQOKyHO6ItuKoMraVjC3L0te+9jXt2LFDtbW1ysnJ6au4AAAJjrc2daKoqEhVVVXatWuX0tLSFAgEJEnp6elKTU3tkwABAImJDVyd2LRpk5qbmzV79mwNHTo0MrZt29ZX8QEA0O/ZblMDABALtKkBAHCZ00daJsStTQAAoHdQGQMAjESbGgAAlyVSMqZNDQCAy6iMAQBGSqTKmGQMADBSIiVj2tQAALiMyhgAYCRLzu4VjqfHVJGMAQBGSqQ2NckYAGAkknEM/OOnJ+hTngFuXR79yLmH8twOoZ0hFfVuh9DOgMIP3Q6hnXMy788OcAOVMQDASFTGAAC4LJGSMbc2AQDgMipjAICRLMsjy0F162RurJGMAQBG4n3GAAAgZqiMAQBGSqQNXCRjAICREmnNmDY1AACSysrKNGPGDKWlpSkjI0OFhYU6efLkVedt375d48aNU0pKiiZMmKA9e/bYvjbJGABgpCttaifDjn379qmoqEgHDx5UdXW1Ll68qLvuukutra2dzjlw4IDuv/9+PfDAAzp27JgKCwtVWFioEydO2Lo2bWoAgJFi3abeu3dv1M+VlZXKyMhQQ0ODbrvttg7nPPfcc/rMZz6jRx55RJK0atUqVVdXa8OGDSovL+/2tamMAQBGshxWxVeScUtLS9QIBoPdun5zc7MkafDgwZ2eU19frzlz5kQdmzt3rurr7T2fnmQMAOjXsrOzlZ6eHhllZWVXnRMOh7V8+XIVFBQoNze30/MCgYAyMzOjjmVmZioQCNiKkTY1AMBIliTLcjZfkvx+v3w+X+S41+u96tyioiKdOHFC+/fv73kANpCMAQBGCssjTy88gcvn80Ul46spLi7W7t27VVdXpxEjRnR5blZWlpqamqKONTU1KSsry1astKkBAJBkWZaKi4u1Y8cOvfXWW8rJybnqnLy8PNXU1EQdq66uVl6evXd1UxkDAIwU693URUVFqqqq0q5du5SWlhZZ901PT1dqaqokadGiRRo+fHhk3XnZsmW6/fbbtWbNGs2fP19bt27VkSNHVFFRYevaVMYAACPF+j7jTZs2qbm5WbNnz9bQoUMjY9u2bZFzTp8+rTNnzkR+zs/PV1VVlSoqKjRp0iT98Ic/1M6dO7vc9NURKmMAAHSpTX01tbW17Y4tWLBACxYscHRtkjEAwEiW5XA3tYO5sUYyBgAYiRdFAACAmKEyBgAYKZEqY5IxAMBIYcsjj4OEanc3tZtIxgAAIyXSBi7WjAEAcBmVMQDASJcqYydrxr0YTB8jGQMAjJRIG7hoUwMA4DIqYwCAkSz9+Z3EPZ0fL0jGAAAj0aYGAAAxQ2UMADBTAvWpScYAADM5bFMrjtrUJGMAgJF4AhcAAIgZKmPYcu6hPLdDaGdIRb3bIbRj4veknW4H0J6Jf3YmMunvU6jtgvTKrphcK5F2U5OMAQBmsjzO1n3jKBnTpgYAwGVUxgAAIyXSBi6SMQDATAl0nzFtagAAXEZlDAAwErupAQAwQRy1mp2gTQ0AgMuojAEARqJNDQCA2xJoNzXJGABgKM/l4WR+fGDNGAAAl1EZAwDMRJsaAACXJVAydtSmfvrpp+XxeLR8+fJeCgcAgMTT48r48OHD2rx5syZOnNib8QAAcAmvUOza+fPntXDhQr344osaNGhQb8cEAEDkrU1ORrzoUTIuKirS/PnzNWfOnKueGwwG1dLSEjUAAMCf2W5Tb926VUePHtXhw4e7dX5ZWZm+853v2A4MAJDg2MDVMb/fr2XLlum1115TSkpKt+aUlpaqubk5Mvx+f48CBQAkmCtrxk5GnLBVGTc0NOjs2bOaOnVq5FgoFFJdXZ02bNigYDCo5OTkqDler1der7d3ogUAoB+ylYzvvPNOHT9+POrYkiVLNG7cOH3zm99sl4gBAOgpj3VpOJkfL2wl47S0NOXm5kYdu/baa3X99de3Ow4AgCMJtGbME7gAAGZKoPuMHSfj2traXggDAIDERWUMADATbWoAAFyWQMmY9xkDAOAyKmMAgJkSqDImGQMAzJRAu6lpUwMA4DIqYwCAkXgCFwAAbkugNWPa1AAAXFZXV6d77rlHw4YNk8fj0c6dO7s8v7a2Vh6Pp90IBAK2rksyBgDgstbWVk2aNEkbN260Ne/kyZM6c+ZMZGRkZNiaT5saAGAkjxyuGV/+35aWlqjjXb3ad968eZo3b57ta2VkZOhv/uZvbM+7wrVk/PslM5U8MMWty7czpKLe7RDiAt9T/BpQ+KHbIbRzTnluh9COiX/HTYrpT9bF2F2sl25tys7Ojjq8cuVKPfHEEw4Ca2/y5MkKBoPKzc3VE088oYKCAlvzqYwBAP2a3++Xz+eL/NxZVdwTQ4cOVXl5uaZPn65gMKiXXnpJs2fP1v/8z/9o6tSp3f4ckjEAwEy9tJva5/NFJePeNHbsWI0dOzbyc35+vv73f/9X69at03/+5392+3PYwAUAMJPVC8MFM2fO1LvvvmtrDskYAIBe1NjYqKFDh9qaQ5saAGAkN57Adf78+aiq9v3331djY6MGDx6skSNHqrS0VB988IH+4z/+Q5K0fv165eTk6JZbbtGFCxf00ksv6a233tLPfvYzW9clGQMAzOTCE7iOHDmiO+64I/JzSUmJJGnx4sWqrKzUmTNndPr06civt7W16Rvf+IY++OADXXPNNZo4caJ+/vOfR31Gd5CMAQC4bPbs2bKszrN4ZWVl1M+PPvqoHn30UcfXJRkDAMyUQM+mJhkDAIyUSG9tYjc1AAAuozIGAJiplx6HGQ9IxgAAM7FmDACAu1gzBgAAMUNlDAAwE21qAABc5rBNHU/JmDY1AAAuozIGAJiJNjUAAC5LoGRMmxoAAJdRGQMAjMR9xgAAIGZIxgAAuIw2NQDATAm0gYtkDAAwUiKtGZOMAQDmiqOE6gRrxgAAuIzKGABgJtaMAQBwVyKtGdOmBgDAZVTGAAAz0aYGAMBdtKkBAEDMUBkDAMxEmxoAAJclUDKmTQ0AgMuojC8791Ce2yG0M6Si3u0Q4oKJf3YmSv/su26H0M65h25wOwQYLJE2cJGMAQBmSqA2NckYAGCmBErGrBkDAOAyKmMAgJFYMwYAwG20qQEAQKxQGQMAjESbGgAAt9GmBgAAsUJlDAAwUwJVxiRjAICRPJeHk/nxgjY1AAAuozIGAJiJNjUAAO5KpFubbLepP/jgA33hC1/Q9ddfr9TUVE2YMEFHjhzpi9gAAInM6oURJ2xVxh999JEKCgp0xx136I033tANN9yg3/zmNxo0aFBfxQcAQL9nKxmvXr1a2dnZeuWVVyLHcnJyej0oAAAkxVV164StNvWPf/xjTZ8+XQsWLFBGRoamTJmiF198scs5wWBQLS0tUQMAgKu5smbsZMQLW8n4vffe06ZNm3TTTTfpzTff1Fe+8hV9/etf16uvvtrpnLKyMqWnp0dGdna246ABAOhPbCXjcDisqVOn6qmnntKUKVP00EMPaenSpSovL+90TmlpqZqbmyPD7/c7DhoAkAASaAOXrWQ8dOhQjR8/PurYzTffrNOnT3c6x+v1yufzRQ0AAK7GjTZ1XV2d7rnnHg0bNkwej0c7d+686pza2lpNnTpVXq9XY8aMUWVlpe3r2krGBQUFOnnyZNSxd955R6NGjbJ9YQAATNPa2qpJkyZp48aN3Tr//fff1/z583XHHXeosbFRy5cv14MPPqg333zT1nVt7aZ++OGHlZ+fr6eeekqf+9zndOjQIVVUVKiiosLWRQEAuCoXnsA1b948zZs3r9vnl5eXKycnR2vWrJF0qVu8f/9+rVu3TnPnzu3259iqjGfMmKEdO3boBz/4gXJzc7Vq1SqtX79eCxcutPMxAABcVW+1qf/6jp5gMNhrMdbX12vOnDlRx+bOnav6+npbn2P7cZh333237r77brvTAABwxV/fxbNy5Uo98cQTvfLZgUBAmZmZUccyMzPV0tKiP/7xj0pNTe3W5/BsagCAmXqpTe33+6M2D3u9Xkdh9QWSMQDATL2UjPvyTp6srCw1NTVFHWtqapLP5+t2VSyRjAEAhoqHtzbl5eVpz549Uceqq6uVl5dn63Nsv7UJAID+6vz582psbFRjY6OkS7cuNTY2Rp6nUVpaqkWLFkXO//KXv6z33ntPjz76qH7961/rhRde0Ouvv66HH37Y1nWpjAEAZnLh1qYjR47ojjvuiPxcUlIiSVq8eLEqKyt15syZqAdd5eTk6Kc//akefvhhPffccxoxYoReeuklW7c1SSRjAIChPJYlj9XzbNyTubNnz5bVxbyOnq41e/ZsHTt2zPa1/hJtagAAXEZlDAAwkwttareQjAEARoqH3dS9hTY1AAAuozIGAJiJNnXiGVJh76HeQFf4+9Q9Jn5P5x6y97CGWDDxe4oF2tQAACBmqIwBAGaiTQ0AgLsSqU1NMgYAmCmBKmPWjAEAcBmVMQDAWPHUanaCZAwAMJNlXRpO5scJ2tQAALiMyhgAYCR2UwMA4DZ2UwMAgFihMgYAGMkTvjSczI8XJGMAgJloUwMAgFihMgYAGInd1AAAuC2BHvpBMgYAGCmRKmPWjAEAcBmVMQDATAm0m5pkDAAwEm1qAAAQM1TGAAAzsZsaAAB30aYGAAAxQ2UMADATu6kBAHAXbWoAABAzVMYAADOFrUvDyfw4QTIGAJiJNWMAANzlkcM1416LpO+xZgwAgMuojAEAZuIJXAAAuItbmwAAQMxQGQMAzMRuagAA3OWxLHkcrPs6mRtrriXjAfPPKflar1uXb6/C7QDaa94zxu0Q2rm48wa3Q2hnSEW92yGgHzHx75NJ/xaEWoPSP7sdRf9DZQwAMFP48nAyP06QjAEARkqkNjW7qQEAcBmVMQDATOymBgDAZTyBCwAAd/EELgAAEDNUxgAAMyVQm5rKGABgJE/Y+eiJjRs3avTo0UpJSdGsWbN06NChTs+trKyUx+OJGikpKbavSTIGAOCybdu2qaSkRCtXrtTRo0c1adIkzZ07V2fPnu10js/n05kzZyLj1KlTtq9LMgYAmOlKm9rJsGnt2rVaunSplixZovHjx6u8vFzXXHONtmzZ0ukcj8ejrKysyMjMzLR9XZIxAMBMVi8MSS0tLVEjGAx2eLm2tjY1NDRozpw5kWNJSUmaM2eO6us7f2b5+fPnNWrUKGVnZ+vee+/V22+/bfu3SjIGAPRr2dnZSk9Pj4yysrIOzzt37pxCoVC7yjYzM1OBQKDDOWPHjtWWLVu0a9cuff/731c4HFZ+fr5++9vf2oqR3dQAACP11rOp/X6/fD5f5LjX23tvDMzLy1NeXl7k5/z8fN18883avHmzVq1a1e3PsVUZh0IhPfbYY8rJyVFqaqpuvPFGrVq1SlYcbR8HAMSJXloz9vl8UaOzZDxkyBAlJyerqakp6nhTU5OysrK6FfKAAQM0ZcoUvfvuu7Z+q7aS8erVq7Vp0yZt2LBBv/rVr7R69Wo988wzev75521dFAAA0wwcOFDTpk1TTU1N5Fg4HFZNTU1U9duVUCik48ePa+jQobaubatNfeDAAd17772aP3++JGn06NH6wQ9+0OU9WAAA9IglZ+8k7kHTtqSkRIsXL9b06dM1c+ZMrV+/Xq2trVqyZIkkadGiRRo+fHhk3fm73/2ubr31Vo0ZM0Z/+MMf9Oyzz+rUqVN68MEHbV3XVjLOz89XRUWF3nnnHX3605/WL37xC+3fv19r167tdE4wGIzaudbS0mIrQABAYnLjfcb33XefPvzwQz3++OMKBAKaPHmy9u7dG9nUdfr0aSUl/bmp/NFHH2np0qUKBAIaNGiQpk2bpgMHDmj8+PG2rmsrGa9YsUItLS0aN26ckpOTFQqF9OSTT2rhwoWdzikrK9N3vvMdW0EBAHDp9iQnj8Ps2bTi4mIVFxd3+Gu1tbVRP69bt07r1q3r2YX+gq0149dff12vvfaaqqqqdPToUb366qv63ve+p1dffbXTOaWlpWpubo4Mv9/vOGgAAPoTW5XxI488ohUrVujzn/+8JGnChAk6deqUysrKtHjx4g7neL3eXt1GDgBIEAn0oghbyfiTTz6J6pVLUnJyssJhJyvsAAB0ICzJ43B+nLCVjO+55x49+eSTGjlypG655RYdO3ZMa9eu1Re/+MW+ig8AgH7PVjJ+/vnn9dhjj+mrX/2qzp49q2HDhulLX/qSHn/88b6KDwCQoNzYTe0WW8k4LS1N69ev1/r16/soHAAALkugNWNeFAEAgMt4UQQAwEwJVBmTjAEAZkqgZEybGgAAl1EZAwDMxH3GAAC4i1ubAABwG2vGAAAgVqiMAQBmCluSx0F1G46fyphkDAAwE21qAAAQK1TGBru48wa3Q2hnSEW92yEACcekfwtCbRdieDWHlbHipzImGQMAzESbGgAAxAqVMQDATGFLjlrN7KYGAMAhK3xpOJkfJ2hTAwDgMipjAICZEmgDF8kYAGAm1owBAHBZAlXGrBkDAOAyKmMAgJksOayMey2SPkcyBgCYiTY1AACIFSpjAICZwmFJDh7cEY6fh36QjAEAZqJNDQAAYoXKGABgpgSqjEnGAAAzJdATuGhTAwDgMipjAICRLCssy8FrEJ3MjTWSMQDATJblrNXMmjEAAA5ZDteM4ygZs2YMAIDLqIwBAGYKhyWPg3Vf1owBAHCINjUAAIgVKmMAgJGscFiWgzY1tzYBAOAUbWoAABArVMYAADOFLcmTGJUxyRgAYCbLkuTk1qb4Sca0qQEAcBmVMQDASFbYkuWgTW3FUWVMMgYAmMkKy1mbOn5ubaJNDQAwkhW2HI+e2Lhxo0aPHq2UlBTNmjVLhw4d6vL87du3a9y4cUpJSdGECRO0Z88e29ckGQMAcNm2bdtUUlKilStX6ujRo5o0aZLmzp2rs2fPdnj+gQMHdP/99+uBBx7QsWPHVFhYqMLCQp04ccLWdWPepr7Sww99Eoz1pbv0J+ui2yG0E2q74HYI7Zj4PQH9nUn/FlyJJRbrsX+ygo5azX/SpX+vWlpaoo57vV55vd4O56xdu1ZLly7VkiVLJEnl5eX66U9/qi1btmjFihXtzn/uuef0mc98Ro888ogkadWqVaqurtaGDRtUXl7e/WCtGPP7/VceqcJgMBiMOB1+v7/P8sQf//hHKysrq1fivO6669odW7lyZYfXDQaDVnJysrVjx46o44sWLbL+4R/+ocM52dnZ1rp166KOPf7449bEiRNt/Z5jXhkPGzZMfr9faWlp8ng8Pf6clpYWZWdny+/3y+fz9WKE/QvfU/fwPXUP31P39OfvybIsffzxxxo2bFifXSMlJUXvv/++2traHH+WZVntck1nVfG5c+cUCoWUmZkZdTwzM1O//vWvO5wTCAQ6PD8QCNiKM+bJOCkpSSNGjOi1z/P5fP3uL3tf4HvqHr6n7uF76p7++j2lp6f3+TVSUlKUkpLS59cxBRu4AACQNGTIECUnJ6upqSnqeFNTk7Kysjqck5WVZev8zpCMAQCQNHDgQE2bNk01NTWRY+FwWDU1NcrLy+twTl5eXtT5klRdXd3p+Z2J24d+eL1erVy5stPePy7he+oevqfu4XvqHr6n+FVSUqLFixdr+vTpmjlzptavX6/W1tbI7upFixZp+PDhKisrkyQtW7ZMt99+u9asWaP58+dr69atOnLkiCoqKmxd12NZcfS8MAAA+tiGDRv07LPPKhAIaPLkyfr3f/93zZo1S5I0e/ZsjR49WpWVlZHzt2/frm9/+9v6v//7P91000165pln9NnPftbWNUnGAAC4jDVjAABcRjIGAMBlJGMAAFxGMgYAwGVxm4ztvuIq0ZSVlWnGjBlKS0tTRkaGCgsLdfLkSbfDMtrTTz8tj8ej5cuXux2KcT744AN94Qtf0PXXX6/U1FRNmDBBR44ccTsso4RCIT322GPKyclRamqqbrzxRq1atSquXnAP98RlMrb7iqtEtG/fPhUVFengwYOqrq7WxYsXddddd6m1tdXt0Ix0+PBhbd68WRMnTnQ7FON89NFHKigo0IABA/TGG2/ol7/8pdasWaNBgwa5HZpRVq9erU2bNmnDhg361a9+pdWrV+uZZ57R888/73ZoiANxeWvTrFmzNGPGDG3YsEHSpSekZGdn62tf+1qHr7iC9OGHHyojI0P79u3Tbbfd5nY4Rjl//rymTp2qF154Qf/2b/+myZMna/369W6HZYwVK1bov//7v/Vf//VfboditLvvvluZmZl6+eWXI8f+6Z/+Sampqfr+97/vYmSIB3FXGbe1tamhoUFz5syJHEtKStKcOXNUX1/vYmRma25uliQNHjzY5UjMU1RUpPnz50f9ncKf/fjHP9b06dO1YMECZWRkaMqUKXrxxRfdDss4+fn5qqmp0TvvvCNJ+sUvfqH9+/dr3rx5LkeGeBB3j8PsySuuEl04HNby5ctVUFCg3Nxct8MxytatW3X06FEdPnzY7VCM9d5772nTpk0qKSnRt771LR0+fFhf//rXNXDgQC1evNjt8IyxYsUKtbS0aNy4cUpOTlYoFNKTTz6phQsXuh0a4kDcJWPYV1RUpBMnTmj//v1uh2IUv9+vZcuWqbq6OqFe1WZXOBzW9OnT9dRTT0mSpkyZohMnTqi8vJxk/Bdef/11vfbaa6qqqtItt9yixsZGLV++XMOGDeN7wlXFXTLuySuuEllxcbF2796turq6Xn2PdH/Q0NCgs2fPaurUqZFjoVBIdXV12rBhg4LBoJKTk12M0AxDhw7V+PHjo47dfPPN+tGPfuRSRGZ65JFHtGLFCn3+85+XJE2YMEGnTp1SWVkZyRhXFXdrxj15xVUisixLxcXF2rFjh9566y3l5OS4HZJx7rzzTh0/flyNjY2RMX36dC1cuFCNjY0k4ssKCgra3Rb3zjvvaNSoUS5FZKZPPvlESUnR/6QmJycrHA67FBHiSdxVxtLVX3GFS63pqqoq7dq1S2lpaQoEApKk9PR0paamuhydGdLS0tqtoV977bW6/vrrWVv/Cw8//LDy8/P11FNP6XOf+5wOHTqkiooK26+I6+/uuecePfnkkxo5cqRuueUWHTt2TGvXrtUXv/hFt0NDPLDi1PPPP2+NHDnSGjhwoDVz5kzr4MGDbodkFEkdjldeecXt0Ix2++23W8uWLXM7DOP85Cc/sXJzcy2v12uNGzfOqqiocDsk47S0tFjLli2zRo4caaWkpFh/+7d/a/3rv/6rFQwG3Q4NcSAu7zMGAKA/ibs1YwAA+huSMQAALiMZAwDgMpIxAAAuIxkDAOAykjEAAC4jGQMA4DKSMQAALiMZAwDgMpIxAAAuIxkDAOCy/w9K2pZTnC83YAAAAABJRU5ErkJggg==", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "import numpy as np\n", "\n", @@ -743,11 +823,11 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.176667Z", - "start_time": "2023-04-25T11:12:37.174646Z" + "end_time": "2023-04-25T18:23:42.215443Z", + "start_time": "2023-04-25T18:23:42.214438Z" } }, "outputs": [], @@ -831,11 +911,11 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.191765Z", - "start_time": "2023-04-25T11:12:37.177667Z" + "end_time": "2023-04-25T18:23:42.232493Z", + "start_time": "2023-04-25T18:23:42.216442Z" } }, "outputs": [], @@ -854,35 +934,14 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.293683Z", - "start_time": "2023-04-25T11:12:37.194765Z" + "end_time": "2023-04-25T18:23:42.372795Z", + "start_time": "2023-04-25T18:23:42.233495Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "gini = model.datacollector.get_model_vars_dataframe()\n", "gini.plot()" @@ -897,85 +956,14 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.302598Z", - "start_time": "2023-04-25T11:12:37.293683Z" + "end_time": "2023-04-25T18:23:42.385021Z", + "start_time": "2023-04-25T18:23:42.372795Z" } }, - "outputs": [ - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "
Wealth
StepAgentID
001
11
21
31
41
\n", - "
" - ], - "text/plain": [ - " Wealth\n", - "Step AgentID \n", - "0 0 1\n", - " 1 1\n", - " 2 1\n", - " 3 1\n", - " 4 1" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "agent_wealth = model.datacollector.get_agent_vars_dataframe()\n", "agent_wealth.head()" @@ -990,35 +978,14 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.452734Z", - "start_time": "2023-04-25T11:12:37.306761Z" + "end_time": "2023-04-25T18:23:42.558338Z", + "start_time": "2023-04-25T18:23:42.383031Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh8AAAGdCAYAAACyzRGfAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAdZElEQVR4nO3df6zV9X3H8dcFLgeoXBQQLncCUmtrWyftRBjTOav8KGtY2UjntEvQui5trk4lXRuXWqA2pTFZ57ownFuHXTpmt2XgaiJ4pRViCrVgiHVLmFC2WhWcdnDhkl5PuGd/LNyOgj+unvs5Xs7jkZyk53u/fs/78+mxPHvO4Z6WWq1WCwBAIcMaPQAA0FzEBwBQlPgAAIoSHwBAUeIDAChKfAAARYkPAKAo8QEAFDWi0QP8or6+vjz//PMZO3ZsWlpaGj0OAPAG1Gq1HDlyJB0dHRk27LVf23jbxcfzzz+fqVOnNnoMAOBNePbZZ3Peeee95jlvu/gYO3Zskv8bvq2tra7XrlareeSRR7JgwYK0trbW9dpDQbOvP7EHzb7+xB5Yf3OvPxm8Peju7s7UqVP7/xx/LW+7+DjxVktbW9ugxMeYMWPS1tbWlE+6Zl9/Yg+aff2JPbD+5l5/Mvh78EY+MuEDpwBAUeIDAChKfAAARYkPAKAo8QEAFCU+AICixAcAUJT4AACKEh8AQFHiAwAoSnwAAEWJDwCgKPEBABQlPgCAokY0eoBGuHjl5vQef/2v/D3TVIbXcvfsRk8BQLPzygcAUJT4AACKEh8AQFHiAwAoSnwAAEWJDwCgKPEBABQlPgCAosQHAFCU+AAAihIfAEBR4gMAKEp8AABFiQ8AoCjxAQAUJT4AgKLEBwBQlPgAAIoSHwBAUeIDAChKfAAARQ0oPlavXp3LLrssY8eOzaRJk7JkyZLs2bPnpHOuuuqqtLS0nHT71Kc+VdehAYCha0DxsXXr1nR2dmbHjh3p6upKtVrNggUL0tPTc9J5n/zkJ/PCCy/03+6+++66Dg0ADF0jBnLypk2bTrp///33Z9KkSdm1a1euvPLK/uNjxoxJe3t7fSYEAM4oA4qPX3T48OEkyfjx4086/vd///f55je/mfb29ixevDh33nlnxowZc9pr9Pb2pre3t/9+d3d3kqRaraZarb6V8U5x4nqVYbW6XneoOLHueu/rUHJi7c26B82+/sQeWH9zrz8ZvD0YyPVaarXam/qTuK+vL7/1W7+VQ4cO5fHHH+8/ft9992X69Onp6OjIU089lc997nOZPXt2/uVf/uW011m5cmVWrVp1yvH169e/arAAAG8vx44dy/XXX5/Dhw+nra3tNc990/Hx6U9/Og8//HAef/zxnHfeea963ne+851cc8012bt3by644IJTfn66Vz6mTp2al1566XWHH6hqtZqurq7cuXNYevta6nrtoaAyrJa7ZvVl/vz5aW1tbfQ4DXHiOdCse9Ds60/sgfU39/qTwduD7u7uTJw48Q3Fx5t62+Xmm2/OQw89lG3btr1meCTJnDlzkuRV46NSqaRSqZxyvLW1ddCeGL19Lek93nzxccJg7u1Q0ex70OzrT+yB9Tf3+pP678FArjWg+KjVarnllluyYcOGPPbYY5kxY8br/jO7d+9OkkyZMmUgDwUAnKEGFB+dnZ1Zv359HnzwwYwdOzYHDhxIkowbNy6jR4/Ovn37sn79+vzmb/5mJkyYkKeeeiq33357rrzyylxyySWDsgAAYGgZUHysXbs2yf/9IrH/b926dbnhhhsycuTIPProo7nnnnvS09OTqVOnZunSpfn85z9ft4EBgKFtwG+7vJapU6dm69atb2kgAODM5rtdAICixAcAUJT4AACKEh8AQFHiAwAoSnwAAEWJDwCgKPEBABQlPgCAosQHAFCU+AAAihIfAEBR4gMAKEp8AABFiQ8AoCjxAQAUJT4AgKLEBwBQlPgAAIoSHwBAUeIDAChKfAAARYkPAKAo8QEAFCU+AICixAcAUJT4AACKEh8AQFHiAwAoSnwAAEWJDwCgKPEBABQlPgCAosQHAFCU+AAAihIfAEBR4gMAKEp8AABFiQ8AoCjxAQAUJT4AgKLEBwBQlPgAAIoSHwBAUeIDAChKfAAARYkPAKAo8QEAFCU+AICixAcAUJT4AACKEh8AQFHiAwAoSnwAAEUNKD5Wr16dyy67LGPHjs2kSZOyZMmS7Nmz56Rzfvazn6WzszMTJkzIWWedlaVLl+bgwYN1HRoAGLoGFB9bt25NZ2dnduzYka6urlSr1SxYsCA9PT3959x+++359re/nX/6p3/K1q1b8/zzz+d3fud36j44ADA0jRjIyZs2bTrp/v33359JkyZl165dufLKK3P48OF8/etfz/r163P11Vcn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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "end_wealth = agent_wealth.xs(99, level=\"Step\")[\"Wealth\"]\n", "end_wealth.hist(bins=range(agent_wealth.Wealth.max() + 1))" @@ -1033,35 +1000,14 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.531920Z", - "start_time": "2023-04-25T11:12:37.400372Z" + "end_time": "2023-04-25T18:23:42.702477Z", + "start_time": "2023-04-25T18:23:42.520333Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 17, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "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ucyekTibMyK1hhF6RgAAcLDIF312HPaMMGcEAIAk0JQAPSNOLnxGGAEAJD2/OYE1zeaWXCgSkJxcEp4wAgBIevFaZ0RKjsXyCCMAgKQX+aKP6zkj9IwAAOBckTCSGYdhJDP97CrC9IwAAOBgcV1nJOPsMA11RgAAcLDAuXVfsuNyzkhkmIa1aQAAcKQzobBOt4QlxWfPCHVGAABwuKZgyPzveJwz0lpnJHSZPRMXYQQAkNT854ZoPKlupafG39dia50RhmkAAHAk87beOJwvIrXebhwInpFhGDa3JjYIIwCApBaI4ztppNaekbAhnWpx5lANYQQAkNT8cbwujST1SEuR23X2v51a+IwwAgBIapEv+Mz0+AwjLpfLnFjr1FojhBEAQFKL9zkj0nnzRugZkVatWqWRI0cqJydHOTk58nq9euONNy55zIYNGzR06FBlZGRoxIgRev3117vUYAAArBTvc0ak1iEkp9YaiSqM9O/fX8uWLdO+fftUXV2tP/3TP9XMmTP17rvvXnT/nTt3avbs2br77rt14MABFRUVqaioSIcOHbKk8QAAdFUgzueMSOdVYXVoGInqyt96661tni9dulSrVq3S7t27dd11112w/1NPPaVbbrlF3/ve9yRJjz32mCoqKvT0009r9erVXWg2ACBRfOoPKngmfu8CafCdliRledJsbkn7IuvTHPvipD768mRMzvGVbI88qSkxee/L6XQMDIVC2rBhg5qamuT1ei+6z65du1RSUtJm27Rp01ReXn7J9w4GgwoGg+Zzn8/X2WYCAGy09q0P9c8/e8/uZnRIlseeL+KOiMwZefznv9HjP/9NTM7x6t9N0tgBvWPy3pcTdRg5ePCgvF6vTp8+raysLG3cuFHDhg276L719fXKy8trsy0vL0/19fWXPEdZWZmWLFkSbdMAAHGm+uiXkqRUt0spkftT41BujzRN/l997G5Gu24Znq9f/+5TBc+EY3YOO//vRB1GhgwZopqaGjU2Nurll1/W3LlztW3btnYDSWeUlpa26VHx+XwqLCy07P0BAN0jMseh7FsjNGs8/4531q2j+urWUX3tbkbMRB1G0tPTNXjwYEnSuHHjtHfvXj311FN69tlnL9g3Pz9fDQ0NbbY1NDQoPz//kufweDzyeDzRNg0AEGcid6rE822zsF+X64yEw+E28zvO5/V6VVlZ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ffqoqKhIhw8fbrPP6dOntWDBAl155ZXKysrSbbfdpoaGBpta7AzLli2Ty+XS4sWLzW1cZ2t9/PHH+uu//mtdeeWV6tGjh0aMGKHq6mrzdcMw9PDDD6ugoEA9evTQ1KlT9bvf/c7GFiemUCikhx56SIMGDVKPHj30J3/yJ3rsscfarG3Cte6c7du369Zbb1Xfvn3lcrlUXl7e5vWOXNcvvvhCc+bMUU5Ojnr16qW7775bgUCg640zktT69euN9PR047nnnjPeffddY/78+UavXr2MhoYGu5uW0KZNm2Y8//zzxqFDh4yamhrjz/7sz4wBAwYYgUDA3Oeee+4xCgsLjcrKSqO6utqYOHGiMWnSJBtbndj27NljXH311cbIkSONRYsWmdu5ztb54osvjIEDBxp33nmn8fbbbxsffPCBsWXLFuPIkSPmPsuWLTNyc3ON8vJy45133jH+4i/+whg0aJBx6tQpG1ueeJYuXWpceeWVxubNm40PP/zQ2LBhg5GVlWU89dRT5j5c6855/fXXjQcffNB49dVXDUnGxo0b27zeket6yy23GKNGjTJ2795t/PrXvzYGDx5szJ49u8ttS9owcsMNNxgLFiwwn4dCIaNv375GWVmZja1ynuPHjxuSjG3bthmGYRgnTpww0tLSjA0bNpj7/OY3vzEkGbt27bKrmQnL7/cb11xzjVFRUWFMnjzZDCNcZ2v90z/9k/G1r32t3dfD4bCRn59vPPHEE+a2EydOGB6Px/jv//7v7miiY8yYMcP4zne+02bbt771LWPOnDmGYXCtrfLHYaQj1/W9994zJBl79+4193njjTcMl8tlfPzxx11qT1IO0zQ3N2vfvn2aOnWquc3tdmvq1KnatWuXjS1znsbGRknSFVdcIUnat2+fWlpa2lz7oUOHasCAAVz7TliwYIFmzJjR5npKXGervfbaaxo/frxmzZqlPn36aMyYMfqP//gP8/UPP/xQ9fX1ba53bm6uJkyYwPWO0qRJk1RZWanf/va3kqR33nlHO3bs0PTp0yVxrWOlI9d1165d6tWrl8aPH2/uM3XqVLndbr399ttdOn9CLJRntc8++0yhUEh5eXlttufl5en999+3qVXOEw6HtXjxYt14440aPny4JKm+vl7p6enq1atXm33z8vJUX19vQysT1/r167V//37t3bv3gte4ztb64IMPtGrVKpWUlOiBBx7Q3r179Q//8A9KT0/X3LlzzWt6sX9TuN7Ruf/+++Xz+TR06FClpKQoFApp6dKlmjNnjiRxrWOkI9e1vr5effr0afN6amqqrrjiii5f+6QMI+geCxYs0KFDh7Rjxw67m+I4x44d06JFi1RRUaGMjAy7m+N44XBY48eP17/8y79IksaMGaNDhw5p9erVmjt3rs2tc5af/vSnevHFF7Vu3Tpdd911qqmp0eLFi9W3b1+utYMl5TDNVVddpZSUlAvuLGhoaFB+fr5NrXKWhQsXavPmzfrVr36l/v37m9vz8/PV3NysEydOtNmfax+dffv26fjx4xo7dqxSU1OVmpqqbdu26d/+7d+UmpqqvLw8rrOFCgoKNGzYsDbbrr32WtXW1kqSeU35N6Xrvve97+n+++/XHXfcoREjRuhv/uZv9N3vfldlZWWSuNax0pHrmp+fr+PHj7d5/cyZM/riiy+6fO2TMoykp6dr3LhxqqysNLeFw2FVVlbK6/Xa2LLEZxiGFi5cqI0bN2rr1q0aNGhQm9fHjRuntLS0Ntf+8OHDqq2t5dpH4aabbtLBgwdVU1NjPsaPH685c+aY/811ts6NN954wS3qv/3tbzVw4EBJ0qBBg5Sfn9/mevt8Pr399ttc7yidPHlSbnfbr6aUlBSFw2FJXOtY6ch19Xq9OnHihPbt22fus3XrVoXDYU2YMKFrDejS9NcEtn79esPj8Rhr16413nvvPeNv//ZvjV69ehn19fV2Ny2h3XvvvUZubq5RVVVl1NXVmY+TJ0+a+9xzzz3GgAEDjK1btxrV1dWG1+s1vF6vja12hvPvpjEMrrOV9uzZY6SmphpLly41fve73xkvvvii0bNnT+OFF14w91m2bJnRq1cvY9OmTcb//M//GDNnzuR2006YO3eu0a9fP/PW3ldffdW46qqrjPvuu8/ch2vdOX6/3zhw4IBx4MABQ5Lxwx/+0Dhw4IBx9OhRwzA6dl1vueUWY8yYMcbbb79t7Nixw7jmmmu4tber/v3f/90YMGCAkZ6ebtxwww3G7t277W5SwpN00cfzzz9v7nPq1Cnj7/7u74zevXsbPXv2NP7yL//SqKurs6/RDvHHYYTrbK2f/exnxvDhww2Px2MMHTrUWLNmTZvXw+Gw8dBDDxl5eXmGx+MxbrrpJuPw4cM2tTZx+Xw+Y9GiRcaAAQOMjIwM46tf/arx4IMPGsFg0NyHa905v/rVry767/PcuXMNw+jYdf3888+N2bNnG1lZWUZOTo5x1113GX6/v8ttcxnGeWXtAAAAullSzhkBAADxgzACAABsRRgBAAC2IowAAABbEUYAAICtCCMAAMBWhBEAAGArwggAALAVYQQAANiKMALAMp9++qnuvfdeDRgwQB6PR/n5+Zo2bZreeustSZLL5VJ5ebm9jQQQd1LtbgAA57jtttvU3Nysn/zkJ/rqV7+qhoYGVVZW6vPPP7e7aQDiGGvTALDEiRMn1Lt3b1VVVWny5MkXvH711Vfr6NGj5vOBAwfqD3/4gyRp06ZNWrJkid577z317dtXc+fO1YMPPqjU1LN/L7lcLj3zzDN67bXXVFVVpYKCAv3gBz/Qt7/97W75bABii2EaAJbIyspSVlaWysvLFQwGL3h97969kqTnn39edXV15vNf//rXKi4u1qJFi/Tee+/p2Wef1dq1a7V06dI2xz/00EO67bbb9M4772jOnDm644479Jvf/Cb2HwxAzNEzAsAyr7zyiubPn69Tp05p7Nixmjx5su644w6NHDlS0tkejo0bN6qoqMg8ZurUqbrppptUWlpqbnvhhRd033336ZNPPjGPu+eee7Rq1Spzn4kTJ2rs2LF65plnuufDAYgZekYAWOa2227TJ598otdee0233HKLqqqqNHbsWK1du7bdY9555x09+uijZs9KVlaW5s+fr7q6Op08edLcz+v1tjnO6/XSMwI4BBNYAVgqIyNDN998s26++WY99NBDmjdvnh555BHdeeedF90/EAhoyZIl+ta3vnXR9wLgfPSMAIipYcOGqampSZKUlpamUCjU5vWxY8fq8OHDGjx48AUPt7v1n6jdu3e3OW737t269tprY/8BAMQcPSMALPH5559r1qxZ+s53vqORI0cqOztb1dXV+sEPfqCZM2dKOntHTWVlpW688UZ5PB717t1bDz/8sP78z/9cAwYM0Le//W253W698847OnTokB5//HHz/Tds2KDx48fra1/7ml588UXt2bNHP/7xj+36uAAsxARWAJYIBoP653/+Z/3iF7/Q73//e7W0tKiwsFCzZs3SAw88oB49euhnP/uZSkpK9Ic//EH9+vUzb+3dsmWLHn30UR04cEBpaWkaOnSo5s2bp/nz50s6O4F15cqVKi8v1/bt21VQUKB//dd/1V/91V/Z+IkBWIUwAiDuXewuHADOwZwRAABgK8IIAACwFRNYAcQ9RpMBZ6NnBAAA2IowAgAAbEUYAQAAtiKMAAAAWxFGAACArQgjAADAVoQRAABgK8IIAACw1f8HeEL5KDpBvXEAAAAASUVORK5CYII=", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "one_agent_wealth = agent_wealth.xs(14, level=\"AgentID\")\n", "one_agent_wealth.Wealth.plot()" @@ -1078,11 +1024,11 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.531920Z", - "start_time": "2023-04-25T11:12:37.496452Z" + "end_time": "2023-04-25T18:23:42.711736Z", + "start_time": "2023-04-25T18:23:42.702477Z" } }, "outputs": [], @@ -1112,11 +1058,11 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:37.532920Z", - "start_time": "2023-04-25T11:12:37.506964Z" + "end_time": "2023-04-25T18:23:42.716831Z", + "start_time": "2023-04-25T18:23:42.714736Z" } }, "outputs": [], @@ -1222,22 +1168,14 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:12:57.683743Z", - "start_time": "2023-04-25T11:12:37.514528Z" + "end_time": "2023-04-25T18:24:02.422337Z", + "start_time": "2023-04-25T18:23:42.717833Z" } }, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "100%|████████████████████████████████████████████████████████████████████████████████| 245/245 [00:21<00:00, 11.21it/s]\n" - ] - } - ], + "outputs": [], "source": [ "params = {\"width\": 10, \"height\": 10, \"N\": range(10, 500, 10)}\n", "\n", @@ -1261,24 +1199,14 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:13:05.938682Z", - "start_time": "2023-04-25T11:12:57.685253Z" + "end_time": "2023-04-25T18:24:10.090556Z", + "start_time": "2023-04-25T18:24:02.423340Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Index(['RunId', 'iteration', 'Step', 'width', 'height', 'N', 'Gini', 'AgentID',\n", - " 'Wealth'],\n", - " dtype='object')\n" - ] - } - ], + "outputs": [], "source": [ "import pandas as pd\n", "\n", @@ -1295,35 +1223,14 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:13:06.089635Z", - "start_time": "2023-04-25T11:13:05.940686Z" + "end_time": "2023-04-25T18:24:10.237362Z", + "start_time": "2023-04-25T18:24:10.090556Z" } }, - "outputs": [ - { - "data": { - "text/plain": [ - "" - ] - }, - "execution_count": 22, - "metadata": {}, - "output_type": "execute_result" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiMAAAGdCAYAAADAAnMpAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMSwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/bCgiHAAAACXBIWXMAAA9hAAAPYQGoP6dpAABKpElEQVR4nO3dfXhU1bk3/u9MwiQEyCQhkgkYSBArplEiwWBEbKuhUHjwpT3noR4RntTSUxRrm75I6iORetro8VweepQf2FS0R38WTlsVrDQVgyhoEASixFAUDC9iJgECSQgkgcx+/ogzJmRm73tn1syeSb6f68qlzOyZWbNnZu97r3Wve9k0TdNAREREZBG71Q0gIiKiwY3BCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVkq1uoGSHg8Hnz++ecYMWIEbDab1c0hIiIiAU3T0NraitGjR8NuD9z/ERXByOeff46MjAyrm0FERET9cPToUVx66aUB74+KYGTEiBEAut9MYmKixa0hIiIiiZaWFmRkZPjO44FERTDiHZpJTExkMEJERBRljFIsmMBKRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJaKiqJn1FuXR8OOuiY0trZj1Ih45GelIMbONXuIiCg6MRiJMhU19Vj+ai3qm9t9t6U741E6NxuzctItbBkREVH/cJgmilTU1GPxC7t7BSIA4G5ux+IXdqOipt6ilhEREfUfg5Eo0eXRsPzVWmh+7vPetvzVWnR5/G1BREQUuRiMRIkddU19ekR60gDUN7djR11T+BpFRESkAIORKNHYGjgQ6c92REREkYLBSJQYNSJe6XZERESRgsFIlMjPSkG6Mx6BJvDa0D2rJj8rJZzNIiIiChqDkSgRY7ehdG42APQJSLz/Lp2bzXojREQUdRiMRJFZOelYNX8yXM7eQzEuZzxWzZ/MOiNERBSVWPQsyszKSceMbBcrsBIR0YDBYCQKxdhtKLhspNXNICIiUoLDNERERGQpBiNERERkKQYjREREZCkGI0RERGQpBiNERERkKQYjREREZCkGI0RERGQpBiNERERkKQYjREREZCkGI0RERGQpBiNERERkKQYjREREZCkGI0RERGQpBiNERERkqVir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WYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVmKwQgRERFZisEIERERWYrBCBEREVkq1uoGWKXzggfPVx3C4aazGJeSgLsKMuGI7RubdXk07KhrQmNrO0aNiEd+Vgpi7DYLWkxERDQwDcpgpGxjLcq31sGjfXnbrzfuw6LpWSiZne27raKmHstfrUV9c7vvtnRnPErnZmNWTno4m0xERDRgDbphmrKNtXj67d6BCAB4NODpt+tQtrEWQHcgsviF3b0CEQBwN7dj8Qu7UVFTH64mExERDWiDKhjpvOBB+dY63W3Kt9bhXGcXlr9aC83P/d7blr9ai66LIxoiIiIybVAFI89XHerTI3Ixjwb8ZmNtnx6RnjQA9c3t2FHXpLaBREREg9CgCkYON50VbXfopGy7xtbAAQsRERHJDKpgZFxKgmi7zJGy7UaNiA+mOURERIRBFozcVZAJo1m5dhvwy9nZSHfGI9CmNnTPqsnPSlHdRCIiokFnUAUjjlg7Fk3P0t1m0fQsDHXEoHRu9xTfiwMS779L52az3ggREZECgyoYAYCS2dn41xuz+vSQ2G3Av974ZZ2RWTnpWDV/MlzO3kMxLmc8Vs2fzDojREREitg0TYv4+aktLS1wOp1obm5GYmKikudkBVYiIqLQkp6/B2UFVqB7yObu6eMNt4ux21Bw2cgwtIiIiGhwGnTDNERERBRZ+hWMrFy5EpmZmYiPj8fUqVOxY8eOgNu+9NJLmDJlCpKSkjBs2DDk5ubi+eef73eDiYiIaGAxHYysW7cOxcXFKC0txe7duzFp0iTMnDkTjY2NfrdPSUnBgw8+iKqqKnz44YcoKipCUVER/v73vwfdeCIiIop+phNYp06dimuvvRZPPfUUAMDj8SAjIwP33Xcfli5dKnqOyZMnY86cOXjkkUdE24cigZWIiIhCS3r+NtUz0tnZiV27dqGwsPDLJ7DbUVhYiKqqKsPHa5qGyspK7N+/HzfeeGPA7To6OtDS0tLrj4iIiAYmU8HIiRMn0NXVhbS0tF63p6Wlwe12B3xcc3Mzhg8fDofDgTlz5uDJJ5/EjBkzAm5fVlYGp9Pp+8vIyDDTTCIiIooiYZlNM2LECFRXV2Pnzp349a9/jeLiYmzZsiXg9iUlJWhubvb9HT16NBzNJCIiIguYqjOSmpqKmJgYNDQ09Lq9oaEBLpcr4OPsdjsmTJgAAMjNzcW+fftQVlaGr3/96363j4uLQ1xcnJmmERERUZQy1TPicDiQl5eHyspK320ejweVlZUoKCgQP4/H40FHR4eZlyYiIqIBynQF1uLiYixcuBBTpkxBfn4+VqxYgba2NhQVFQEAFixYgDFjxqCsrAxAd/7HlClTcNlll6GjowMbN27E888/j1WrVql9J0RERBSVTAcj8+bNw/Hjx7Fs2TK43W7k5uaioqLCl9R65MgR2O1fdri0tbXhnnvuwWeffYahQ4di4sSJeOGFFzBv3jx174KIiIii1qBdKI+IiIhCKyR1RoiIiIhUYzBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJZiMEJERESWYjBCRERElmIwQkRERJbqVzCycuVKZGZmIj4+HlOnTsWOHTsCblteXo7p06cjOTkZycnJKCws1N2eiIiIBhfTwci6detQXFyM0tJS7N69G5MmTcLMmTPR2Njod/stW7bgjjvuwJtvvomqqipkZGTgm9/8Jo4dOxZ044mIiCj62TRN08w8YOrUqbj22mvx1FNPAQA8Hg8yMjJw3333YenSpYaP7+rqQnJyMp566iksWLBA9JotLS1wOp1obm5GYmKimeYSERGR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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "results_filtered = results_df[(results_df.AgentID == 0) & (results_df.Step == 100)]\n", "N_values = results_filtered.N.values\n", @@ -1343,47 +1250,14 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:13:06.089635Z", - "start_time": "2023-04-25T11:13:06.051884Z" + "end_time": "2023-04-25T18:24:10.257241Z", + "start_time": "2023-04-25T18:24:10.239363Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Step AgentID Wealth\n", - " 0 0 1\n", - " 0 1 1\n", - " 0 2 1\n", - " 0 3 1\n", - " 0 4 1\n", - " 0 5 1\n", - " 0 6 1\n", - " 0 7 1\n", - " 0 8 1\n", - " 0 9 1\n", - " 1 0 1\n", - " 1 1 1\n", - " ... ... ...\n", - " 99 8 2\n", - " 99 9 1\n", - " 100 0 1\n", - " 100 1 1\n", - " 100 2 1\n", - " 100 3 1\n", - " 100 4 1\n", - " 100 5 1\n", - " 100 6 1\n", - " 100 7 0\n", - " 100 8 2\n", - " 100 9 1\n" - ] - } - ], + "outputs": [], "source": [ "# First, we filter the results\n", "one_episode_wealth = results_df[(results_df.N == 10) & (results_df.iteration == 2)]\n", @@ -1407,47 +1281,14 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": null, "metadata": { "ExecuteTime": { - "end_time": "2023-04-25T11:13:06.099624Z", - "start_time": "2023-04-25T11:13:06.072169Z" + "end_time": "2023-04-25T18:24:10.314122Z", + "start_time": "2023-04-25T18:24:10.258232Z" } }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - " Step Gini\n", - " 0 0.00\n", - " 1 0.18\n", - " 2 0.18\n", - " 3 0.18\n", - " 4 0.18\n", - " 5 0.18\n", - " 6 0.18\n", - " 7 0.18\n", - " 8 0.18\n", - " 9 0.18\n", - " 10 0.18\n", - " 11 0.18\n", - " ... ...\n", - " 89 0.54\n", - " 90 0.54\n", - " 91 0.56\n", - " 92 0.56\n", - " 93 0.56\n", - " 94 0.56\n", - " 95 0.56\n", - " 96 0.56\n", - " 97 0.56\n", - " 98 0.56\n", - " 99 0.56\n", - " 100 0.56\n" - ] - } - ], + "outputs": [], "source": [ "results_one_episode = results_df[\n", " (results_df.N == 10) & (results_df.iteration == 1) & (results_df.AgentID == 0)\n", @@ -1477,7 +1318,7 @@ "metadata": { "anaconda-cloud": {}, "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -1491,7 +1332,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.2" + "version": "3.9.6" }, "widgets": { "state": {}, diff --git a/docs/tutorials/intro_tutorial.rst b/docs/tutorials/intro_tutorial.rst deleted file mode 100644 index f3a24de80e3..00000000000 --- a/docs/tutorials/intro_tutorial.rst +++ /dev/null @@ -1,1215 +0,0 @@ -Introductory Tutorial -===================== - -Tutorial Description --------------------- - -`Mesa `__ is a Python framework for -`agent-based -modeling `__. This -tutorial will assist you in getting started. Working through the -tutorial will help you discover the core features of Mesa. Through the -tutorial, you are walked through creating a starter-level model. -Functionality is added progressively as the process unfolds. Should -anyone find any errors, bugs, have a suggestion, or just are looking for -clarification, `let us -know `__! - -The premise of this tutorial is to create a starter-level model -representing agents exchanging money. This exchange of money affects -wealth. Next, *space* is added to allow agents to move based on the -change in wealth as time progresses. - -Two of Mesa’s analytic tools: the *data collector* and *batch runner* -will be used to examine this movement. After that an *interactive -visualization* is added which allows model viewing as it runs. - -Finally, the creation of a custom visualization module in JavaScript is -explored. - -Model Description ------------------ - -This is a starter-level simulated agent-based economy. In an agent-based -economy, the behavior of an individual economic agent, such as a -consumer or producer, is studied in a market environment. This model is -drawn from the field econophysics, specifically a paper prepared by -Drăgulescu et al. for additional information on the modeling assumptions -used in this model. [Drăgulescu, 2002]. - -The assumption that govern this model are: - -1. There are some number of agents. -2. All agents begin with 1 unit of money. -3. At every step of the model, an agent gives 1 unit of money (if they - have it) to some other agent. - -Even as a starter-level model the yielded results are both interesting -and unexpected to individuals unfamiliar with it the specific topic. As -such, this model is a good starting point to examine Mesa’s core -features. - -Tutorial Setup -~~~~~~~~~~~~~~ - -Create and activate a `virtual -environment `__. -*Python version 3.8 or higher is required*. - -Install Mesa: - -.. code:: bash - - python3 -m pip install mesa - -Install Jupyter Notebook (optional): - -.. code:: bash - - python3 -m pip install jupyter - -Install matplotlib: - -.. code:: bash - - python3 -m pip install matplotlib - -Building the Sample Model -------------------------- - -After Mesa is installed a model can be built. A jupyter notebook is -recommended for this tutorial, this allows for small segments of codes -to be examined one at a time. As an option this can be created using -python script files. - -**Good Practice:** Place a model in its own folder/directory. This is -not specifically required for the starter_model, but as other models -become more complicated and expand multiple python scripts, -documentation, discussions and notebooks may be added. - -Create New Folder/Directory -~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -- Using operating system commands create a new folder/directory named - ‘starter_model’. - -- Change into the new folder/directory. - -Creating Model With Jupyter Notebook -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Write the model interactively in `Jupyter -Notebook `__ cells. - -Start Jupyter Notebook: - -.. code:: bash - - jupyter notebook - -Create a new Notebook named ``money_model.ipynb`` (or whatever you want -to call it). - -Creating Model With Script File (IDE, Text Editor, Colab, etc.) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Create a new file called ``money_model.py`` (or whatever you want to -call it) - -*Code will be added as the tutorial progresses.* - -Setting up the model -~~~~~~~~~~~~~~~~~~~~ - -To begin writing the model code, we start with two core classes: one for -the overall model, the other for the agents. The model class holds the -model-level attributes, manages the agents, and generally handles the -global level of our model. Each instantiation of the model class will be -a specific model run. Each model will contain multiple agents, all of -which are instantiations of the agent class. Both the model and agent -classes are child classes of Mesa’s generic ``Model`` and ``Agent`` -classes. This is seen in the code with ``class MoneyModel(mesa.Model)`` -or ``class MoneyAgent(mesa.Agent)``. If you want you can specifically -the class being imported by looking at the -`model `__ -or -`agent `__ -code in the mesa repo. - -Each agent has only one variable: how much wealth it currently has. -(Each agent will also have a unique identifier (i.e., a name), stored in -the ``unique_id`` variable. Giving each agent a unique id is a good -practice when doing agent-based modeling.) - -There is only one model-level parameter: how many agents the model -contains. When a new model is started, we want it to populate itself -with the given number of agents. - -The beginning of both classes looks like this: - -.. code:: ipython3 - - import mesa - - - class MoneyAgent(mesa.Agent): - """An agent with fixed initial wealth.""" - - def __init__(self, unique_id, model): - super().__init__(unique_id, model) - self.wealth = 1 - - - class MoneyModel(mesa.Model): - """A model with some number of agents.""" - - def __init__(self, N): - self.num_agents = N - # Create agents - for i in range(self.num_agents): - a = MoneyAgent(i, self) - -Adding the scheduler -~~~~~~~~~~~~~~~~~~~~ - -Time in most agent-based models moves in steps, sometimes also called -**ticks**. At each step of the model, one or more of the agents – -usually all of them – are activated and take their own step, changing -internally and/or interacting with one another or the environment. - -The **scheduler** is a special model component which controls the order -in which agents are activated. For example, all the agents may activate -in the same order every step; their order might be shuffled; we may try -to simulate all the agents acting at the same time; and more. Mesa -offers a few different built-in scheduler classes, with a common -interface. That makes it easy to change the activation regime a given -model uses, and see whether it changes the model behavior. This may not -seem important, but scheduling patterns can have an impact on your -results [Comer2014]. - -For now, let’s use one of the simplest ones: ``RandomActivation``\ \*, -which activates all the agents once per step, in random order. Every -agent is expected to have a ``step`` method. The step method is the -action the agent takes when it is activated by the model schedule. We -add an agent to the schedule using the ``add`` method; when we call the -schedule’s ``step`` method, the model shuffles the order of the agents, -then activates and executes each agent’s ``step`` method. - -\*Unlike ``mesa.model`` or ``mesa.agent``, ``mesa.time`` has multiple -classes (e.g. ``RandomActivation``, ``StagedActivation`` etc). To ensure -context, time is used in the import as evidenced below with -``mesa.time.Randomactivation``. You can see the different time classes -as -`mesa.time `__. - -With that in mind, the model code with the scheduler added looks like -this: - -.. code:: ipython3 - - import mesa - - - 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 step(self): - # The agent's step will go here. - # For demonstration purposes we will print the agent's unique_id - print("Hi, I am agent " + str(self.unique_id) + ".") - - - class MoneyModel(mesa.Model): - """A model with some number of agents.""" - - def __init__(self, N): - self.num_agents = N - self.schedule = mesa.time.RandomActivation(self) - # Create agents - for i in range(self.num_agents): - a = MoneyAgent(i, self) - self.schedule.add(a) - - def step(self): - """Advance the model by one step.""" - self.schedule.step() - -At this point, we have a model which runs – it just doesn’t do anything. -You can see for yourself with a few easy lines. If you’ve been working -in an interactive session, you can create a model object directly. -Otherwise, you need to open an interactive session in the same directory -as your source code file, and import the classes. For example, if your -code is in ``money_model.py``: - -.. code:: python - - from money_model import MoneyModel - -Then create the model object, and run it for one step: - -.. code:: ipython3 - - empty_model = MoneyModel(10) - empty_model.step() - - -.. parsed-literal:: - - Hi, I am agent 5. - Hi, I am agent 2. - Hi, I am agent 4. - Hi, I am agent 8. - Hi, I am agent 0. - Hi, I am agent 1. - Hi, I am agent 7. - Hi, I am agent 9. - Hi, I am agent 3. - Hi, I am agent 6. - - -Exercise -^^^^^^^^ - -Try modifying the code above to have every agent print out its -``wealth`` when it is activated. Run a few steps of the model to see how -the agent activation order is shuffled each step. - -Agent Step -~~~~~~~~~~ - -Now we just need to have the agents do what we intend for them to do: -check their wealth, and if they have the money, give one unit of it away -to another random agent. To allow the agent to choose another agent at -random, we use the ``model.random`` random-number generator. This works -just like Python’s ``random`` module, but with a fixed seed set when the -model is instantiated, that can be used to replicate a specific model -run later. - -To pick an agent at random, we need a list of all agents. Notice that -there isn’t such a list explicitly in the model. The scheduler, however, -does have an internal list of all the agents it is scheduled to -activate. - -With that in mind, we rewrite the agent ``step`` method, like this: - -.. code:: ipython3 - - 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 step(self): - if self.wealth == 0: - return - other_agent = self.random.choice(self.model.schedule.agents) - other_agent.wealth += 1 - self.wealth -= 1 - -Running your first model -~~~~~~~~~~~~~~~~~~~~~~~~ - -With that last piece in hand, it’s time for the first rudimentary run of -the model. - -If you’ve written the code in its own file (``money_model.py`` or a -different name), launch an interpreter in the same directory as the file -(either the plain Python command-line interpreter, or the IPython -interpreter), or launch a Jupyter Notebook there. Then import the -classes you created. (If you wrote the code in a Notebook, obviously -this step isn’t necessary). - -.. code:: python - - from money_model import * - -Now let’s create a model with 10 agents, and run it for 10 steps. - -.. code:: ipython3 - - model = MoneyModel(10) - for i in range(10): - model.step() - -Next, we need to get some data out of the model. Specifically, we want -to see the distribution of the agent’s wealth. We can get the wealth -values with list comprehension, and then use matplotlib (or another -graphics library) to visualize the data in a histogram. - -If you are running from a text editor or IDE, you’ll also need to add -this line, to make the graph appear. - -.. code:: python - - plt.show() - -.. code:: ipython3 - - # For a jupyter notebook add the following line: - %matplotlib inline - - # The below is needed for both notebooks and scripts - import matplotlib.pyplot as plt - - agent_wealth = [a.wealth for a in model.schedule.agents] - plt.hist(agent_wealth) - - - - -.. parsed-literal:: - - (array([5., 0., 0., 2., 0., 0., 1., 0., 0., 2.]), - array([0. , 0.3, 0.6, 0.9, 1.2, 1.5, 1.8, 2.1, 2.4, 2.7, 3. ]), - ) - - - - -.. image:: intro_tutorial_files%5Cintro_tutorial_19_1.png - - -You’ll should see something like the distribution above. Yours will -almost certainly look at least slightly different, since each run of the -model is random, after all. - -To get a better idea of how a model behaves, we can create multiple -model runs and see the distribution that emerges from all of them. We -can do this with a nested for loop: - -.. code:: ipython3 - - all_wealth = [] - # This runs the model 100 times, each model executing 10 steps. - for j in range(100): - # Run the model - model = MoneyModel(10) - for i in range(10): - model.step() - - # Store the results - for agent in model.schedule.agents: - all_wealth.append(agent.wealth) - - plt.hist(all_wealth, bins=range(max(all_wealth) + 1)) - - - - -.. parsed-literal:: - - (array([416., 324., 155., 68., 25., 12.]), - array([0., 1., 2., 3., 4., 5., 6.]), - ) - - - - -.. image:: intro_tutorial_files%5Cintro_tutorial_22_1.png - - -This runs 100 instantiations of the model, and runs each for 10 steps. -(Notice that we set the histogram bins to be integers, since agents can -only have whole numbers of wealth). This distribution looks a lot -smoother. By running the model 100 times, we smooth out some of the -‘noise’ of randomness, and get to the model’s overall expected behavior. - -This outcome might be surprising. Despite the fact that all agents, on -average, give and receive one unit of money every step, the model -converges to a state where most agents have a small amount of money and -a small number have a lot of money. - -Adding space -~~~~~~~~~~~~ - -Many ABMs have a spatial element, with agents moving around and -interacting with nearby neighbors. Mesa currently supports two overall -kinds of spaces: grid, and continuous. Grids are divided into cells, and -agents can only be on a particular cell, like pieces on a chess board. -Continuous space, in contrast, allows agents to have any arbitrary -position. Both grids and continuous spaces are frequently -`toroidal `__, meaning -that the edges wrap around, with cells on the right edge connected to -those on the left edge, and the top to the bottom. This prevents some -cells having fewer neighbors than others, or agents being able to go off -the edge of the environment. - -Let’s add a simple spatial element to our model by putting our agents on -a grid and make them walk around at random. Instead of giving their unit -of money to any random agent, they’ll give it to an agent on the same -cell. - -Mesa has two main types of grids: ``SingleGrid`` and ``MultiGrid``\ \*. -``SingleGrid`` enforces at most one agent per cell; ``MultiGrid`` allows -multiple agents to be in the same cell. Since we want agents to be able -to share a cell, we use ``MultiGrid``. - -\*However there are more types of space to include ``HexGrid``, -``NetworkGrid``, and the previously mentioned ``ContinuousSpace``. -Similar to ``mesa.time`` context is retained with -``mesa.space.[enter class]``. You can see the different classes as -`mesa.space `__ - -We instantiate a grid with width and height parameters, and a boolean as -to whether the grid is toroidal. Let’s make width and height model -parameters, in addition to the number of agents, and have the grid -always be toroidal. We can place agents on a grid with the grid’s -``place_agent`` method, which takes an agent and an (x, y) tuple of the -coordinates to place the agent. - -.. code:: ipython3 - - class MoneyModel(mesa.Model): - """A model with some number of agents.""" - - def __init__(self, N, width, height): - self.num_agents = N - self.grid = mesa.space.MultiGrid(width, height, True) - self.schedule = mesa.time.RandomActivation(self) - - # 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)) - -Under the hood, each agent’s position is stored in two ways: the agent -is contained in the grid in the cell it is currently in, and the agent -has a ``pos`` variable with an (x, y) coordinate tuple. The -``place_agent`` method adds the coordinate to the agent automatically. - -Now we need to add to the agents’ behaviors, letting them move around -and only give money to other agents in the same cell. - -First let’s handle movement, and have the agents move to a neighboring -cell. The grid object provides a ``move_agent`` method, which like you’d -imagine, moves an agent to a given cell. That still leaves us to get the -possible neighboring cells to move to. There are a couple ways to do -this. One is to use the current coordinates, and loop over all -coordinates +/- 1 away from it. For example: - -.. code:: python - - neighbors = [] - x, y = self.pos - for dx in [-1, 0, 1]: - for dy in [-1, 0, 1]: - neighbors.append((x+dx, y+dy)) - -But there’s an even simpler way, using the grid’s built-in -``get_neighborhood`` method, which returns all the neighbors of a given -cell. This method can get two types of cell neighborhoods: -`Moore `__ (includes -all 8 surrounding squares), and `Von -Neumann `__\ (only -up/down/left/right). It also needs an argument as to whether to include -the center cell itself as one of the neighbors. - -With that in mind, the agent’s ``move`` method looks like this: - -.. code:: python - - class MoneyAgent(mesa.Agent): - #... - 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) - -Next, we need to get all the other agents present in a cell, and give -one of them some money. We can get the contents of one or more cells -using the grid’s ``get_cell_list_contents`` method, or by accessing a -cell directly. The method accepts a list of cell coordinate tuples, or a -single tuple if we only care about one cell. - -.. code:: python - - class MoneyAgent(mesa.Agent): - #... - 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 - -And with those two methods, the agent’s ``step`` method becomes: - -.. code:: python - - class MoneyAgent(mesa.Agent): - # ... - def step(self): - self.move() - if self.wealth > 0: - self.give_money() - -Now, putting that all together should look like this: - -.. code:: ipython3 - - 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_agent = self.random.choice(cellmates) - other_agent.wealth += 1 - self.wealth -= 1 - - def step(self): - self.move() - if self.wealth > 0: - self.give_money() - - - class MoneyModel(mesa.Model): - """A model with some number of agents.""" - - def __init__(self, N, width, height): - self.num_agents = N - self.grid = mesa.space.MultiGrid(width, height, True) - self.schedule = mesa.time.RandomActivation(self) - # 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)) - - def step(self): - self.schedule.step() - -Let’s create a model with 50 agents on a 10x10 grid, and run it for 20 -steps. - -.. code:: ipython3 - - model = MoneyModel(50, 10, 10) - for i in range(20): - model.step() - -Now let’s use matplotlib and numpy to visualize the number of agents -residing in each cell. To do that, we create a numpy array of the same -size as the grid, filled with zeros. Then we use the grid object’s -``coord_iter()`` feature, which lets us loop over every cell in the -grid, giving us each cell’s coordinates and contents in turn. - -.. code:: ipython3 - - import numpy as np - - agent_counts = np.zeros((model.grid.width, model.grid.height)) - for cell in model.grid.coord_iter(): - cell_content, x, y = cell - agent_count = len(cell_content) - agent_counts[x][y] = agent_count - plt.imshow(agent_counts, interpolation="nearest") - plt.colorbar() - - # If running from a text editor or IDE, remember you'll need the following: - # plt.show() - - - - -.. parsed-literal:: - - - - - - -.. image:: intro_tutorial_files%5Cintro_tutorial_32_1.png - - -Collecting Data -~~~~~~~~~~~~~~~ - -So far, at the end of every model run, we’ve had to go and write our own -code to get the data out of the model. This has two problems: it isn’t -very efficient, and it only gives us end results. If we wanted to know -the wealth of each agent at each step, we’d have to add that to the loop -of executing steps, and figure out some way to store the data. - -Since one of the main goals of agent-based modeling is generating data -for analysis, Mesa provides a class which can handle data collection and -storage for us and make it easier to analyze. - -The data collector stores three categories of data: model-level -variables, agent-level variables, and tables (which are a catch-all for -everything else). Model- and agent-level variables are added to the data -collector along with a function for collecting them. Model-level -collection functions take a model object as an input, while agent-level -collection functions take an agent object as an input. Both then return -a value computed from the model or each agent at their current state. -When the data collector’s ``collect`` method is called, with a model -object as its argument, it applies each model-level collection function -to the model, and stores the results in a dictionary, associating the -current value with the current step of the model. Similarly, the method -applies each agent-level collection function to each agent currently in -the schedule, associating the resulting value with the step of the -model, and the agent’s ``unique_id``. - -Let’s add a DataCollector to the model with -```mesa.DataCollector`` `__, -and collect two variables. At the agent level, we want to collect every -agent’s wealth at every step. At the model level, let’s measure the -model’s `Gini -Coefficient `__, a -measure of wealth inequality. - -.. code:: ipython3 - - 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 MoneyAgent(mesa.Agent): - """An agent with fixed initial wealth.""" - - def __init__(self, unique_id, model): - super().__init__(unique_id, model) - self.wealth = 1 - - def move(self): - possible_steps = self.model.grid.get_neighborhood( - self.pos, moore=True, include_center=False - ) - new_position = self.random.choice(possible_steps) - self.model.grid.move_agent(self, new_position) - - def give_money(self): - cellmates = self.model.grid.get_cell_list_contents([self.pos]) - cellmates.pop( - cellmates.index(self) - ) # Ensure agent is not giving money to itself - if len(cellmates) > 1: - other = self.random.choice(cellmates) - other.wealth += 1 - self.wealth -= 1 - if other == self: - print("I JUST GAVE MONEY TO MYSELF HEHEHE!") - - def step(self): - self.move() - if self.wealth > 0: - self.give_money() - - - class MoneyModel(mesa.Model): - """A model with some number of agents.""" - - def __init__(self, N, width, height): - self.num_agents = N - self.grid = mesa.space.MultiGrid(width, height, True) - self.schedule = mesa.time.RandomActivation(self) - - # 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.datacollector = mesa.DataCollector( - model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} - ) - - def step(self): - self.datacollector.collect(self) - self.schedule.step() - -At every step of the model, the datacollector will collect and store the -model-level current Gini coefficient, as well as each agent’s wealth, -associating each with the current step. - -We run the model just as we did above. Now is when an interactive -session, especially via a Notebook, comes in handy: the DataCollector -can export the data its collected as a pandas\* DataFrame, for easy -interactive analysis. - -\*If you are new to Python, please be aware that pandas is already -installed as a dependency of Mesa and that -`pandas `__ is a “fast, powerful, -flexible and easy to use open source data analysis and manipulation -tool”. pandas is great resource to help analyze the data collected in -your models - -.. code:: ipython3 - - model = MoneyModel(50, 10, 10) - for i in range(100): - model.step() - -To get the series of Gini coefficients as a pandas DataFrame: - -.. code:: ipython3 - - gini = model.datacollector.get_model_vars_dataframe() - gini.plot() - - - - -.. parsed-literal:: - - - - - - -.. image:: intro_tutorial_files%5Cintro_tutorial_38_1.png - - -Similarly, we can get the agent-wealth data: - -.. code:: ipython3 - - agent_wealth = model.datacollector.get_agent_vars_dataframe() - agent_wealth.head() - - - - -.. raw:: html - -
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Wealth
StepAgentID
001
11
21
31
41
-
- - - -You’ll see that the DataFrame’s index is pairings of model step and -agent ID. You can analyze it the way you would any other DataFrame. For -example, to get a histogram of agent wealth at the model’s end: - -.. code:: ipython3 - - end_wealth = agent_wealth.xs(99, level="Step")["Wealth"] - end_wealth.hist(bins=range(agent_wealth.Wealth.max() + 1)) - - - - -.. parsed-literal:: - - - - - - -.. image:: intro_tutorial_files%5Cintro_tutorial_42_1.png - - -Or to plot the wealth of a given agent (in this example, agent 14): - -.. code:: ipython3 - - one_agent_wealth = agent_wealth.xs(14, level="AgentID") - one_agent_wealth.Wealth.plot() - - - - -.. parsed-literal:: - - - - - - -.. image:: intro_tutorial_files%5Cintro_tutorial_44_1.png - - -You can also use pandas to export the data to a CSV (comma separated -value), which can be opened by any common spreadsheet application or -opened by pandas. - -If you do not specify a file path, the file will be saved in the local -directory. After you run the code below you will see two files appear -(*model_data.csv* and *agent_data.csv*) - -.. code:: ipython3 - - # save the model data (stored in the pandas gini object) to CSV - gini.to_csv("model_data.csv") - - # save the agent data (stored in the pandas agent_wealth object) to CSV - agent_wealth.to_csv("agent_data.csv") - -Batch Run -~~~~~~~~~ - -Like we mentioned above, you usually won’t run a model only once, but -multiple times, with fixed parameters to find the overall distributions -the model generates, and with varying parameters to analyze how they -drive the model’s outputs and behaviors. Instead of needing to write -nested for-loops for each model, Mesa provides a -```batch_run`` `__ -function which automates it for you. - -The batch runner also requires an additional variable ``self.running`` -for the MoneyModel class. This variable enables conditional shut off of -the model once a condition is met. In this example it will be set as -True indefinitely. - -.. code:: ipython3 - - 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 MoneyModel(mesa.Model): - """A model with some number of agents.""" - - def __init__(self, N, width, height): - self.num_agents = N - self.grid = mesa.space.MultiGrid(width, height, True) - self.schedule = mesa.time.RandomActivation(self) - self.running = True - - # 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.datacollector = mesa.DataCollector( - model_reporters={"Gini": compute_gini}, agent_reporters={"Wealth": "wealth"} - ) - - def step(self): - self.datacollector.collect(self) - self.schedule.step() - -We call ``batch_run`` with the following arguments: - -- ``model_cls`` The model class that is used for the batch run. - -- ``parameters`` A dictionary containing all the parameters of the - model class and desired values to use for the batch run as key-value - pairs. Each value can either be fixed ( - e.g. ``{"height": 10, "width": 10}``) or an iterable - (e.g. ``{"N": range(10, 500, 10)}``). ``batch_run`` will then - generate all possible parameter combinations based on this dictionary - and run the model ``iterations`` times for each combination. - -- ``number_processes`` If not specified, defaults to 1. Set it to - ``None`` to use all the available processors. Note: Multiprocessing - does make debugging challenging. If your parameter sweeps are - resulting in unexpected errors set ``number_processes = 1``. - -- ``iterations`` The number of iterations to run each parameter - combination for. Optional. If not specified, defaults to 1. - -- ``data_collection_period`` The length of the period (number of steps) - after which the model and agent reporters collect data. Optional. If - not specified, defaults to -1, i.e. only at the end of each episode. - -- ``max_steps`` The maximum number of time steps after which the model - halts. An episode does either end when ``self.running`` of the model - class is set to ``False`` or when - ``model.schedule.steps == max_steps`` is reached. Optional. If not - specified, defaults to 1000. - -- ``display_progress`` Display the batch run progress. Optional. If not - specified, defaults to ``True``. - -In the following example, we hold the height and width fixed, and vary -the number of agents. We tell the batch runner to run 5 instantiations -of the model with each number of agents, and to run each for 100 steps. - -We want to keep track of - -1. the Gini coefficient value and -2. the individual agent’s wealth development. - -Since for the latter changes at each time step might be interesting, we -set ``data_collection_period = 1``. - -Note: The total number of runs is 245 (= 49 different populations \* 5 -iterations per population). However, the resulting list of dictionaries -will be of length 6186250 (= 250 average agents per population \* 49 -different populations \* 5 iterations per population \* 101 steps per -iteration). - -**Note for Windows OS users:** If you are running this tutorial in -Jupyter, make sure that you set ``number_processes = 1`` (single -process). If ``number_processes`` is greater than 1, it is less -straightforward to set up. You can read `Mesa’s collection of useful -snippets `__, -in ‘Using multi-process ``batch_run`` on Windows’ section for how to do -it. - -.. code:: ipython3 - - params = {"width": 10, "height": 10, "N": range(10, 500, 10)} - - results = mesa.batch_run( - MoneyModel, - parameters=params, - iterations=5, - max_steps=100, - number_processes=1, - data_collection_period=1, - display_progress=True, - ) - - -.. parsed-literal:: - - 100%|████████████████████████████████████████████████████████████████████████████████| 245/245 [00:21<00:00, 11.21it/s] - - -To further analyze the return of the ``batch_run`` function, we convert -the list of dictionaries to a Pandas DataFrame and print its keys. - -.. code:: ipython3 - - import pandas as pd - - results_df = pd.DataFrame(results) - print(results_df.keys()) - - -.. parsed-literal:: - - Index(['RunId', 'iteration', 'Step', 'width', 'height', 'N', 'Gini', 'AgentID', - 'Wealth'], - dtype='object') - - -First, we want to take a closer look at how the Gini coefficient at the -end of each episode changes as we increase the size of the population. -For this, we filter our results to only contain the data of one agent -(the Gini coefficient will be the same for the entire population at any -time) at the 100th step of each episode and then scatter-plot the values -for the Gini coefficient over the the number of agents. Notice there are -five values for each population size since we set ``iterations=5`` when -calling the batch run. - -.. code:: ipython3 - - results_filtered = results_df[(results_df.AgentID == 0) & (results_df.Step == 100)] - N_values = results_filtered.N.values - gini_values = results_filtered.Gini.values - plt.scatter(N_values, gini_values) - - - - -.. parsed-literal:: - - - - - - -.. image:: intro_tutorial_files%5Cintro_tutorial_57_1.png - - -Second, we want to display the agent’s wealth at each time step of one -specific episode. To do this, we again filter our large data frame, this -time with a fixed number of agents and only for a specific iteration of -that population. To print the results, we convert the filtered data -frame to a string specifying the desired columns to print. - -Pandas has built-in functions to convert to a lot of different data -formats. For example, to display as a table in a Jupyter Notebook, we -can use the ``to_html()`` function which takes the same arguments as -``to_string()`` (see commented lines). - -.. code:: ipython3 - - # First, we filter the results - one_episode_wealth = results_df[(results_df.N == 10) & (results_df.iteration == 2)] - # Then, print the columns of interest of the filtered data frame - print( - one_episode_wealth.to_string( - index=False, columns=["Step", "AgentID", "Wealth"], max_rows=25 - ) - ) - # For a prettier display we can also convert the data frame to html, uncomment to test in a Jupyter Notebook - # from IPython.display import display, HTML - # display(HTML(one_episode_wealth.to_html(index=False, columns=['Step', 'AgentID', 'Wealth'], max_rows=25))) - - -.. parsed-literal:: - - Step AgentID Wealth - 0 0 1 - 0 1 1 - 0 2 1 - 0 3 1 - 0 4 1 - 0 5 1 - 0 6 1 - 0 7 1 - 0 8 1 - 0 9 1 - 1 0 1 - 1 1 1 - ... ... ... - 99 8 2 - 99 9 1 - 100 0 1 - 100 1 1 - 100 2 1 - 100 3 1 - 100 4 1 - 100 5 1 - 100 6 1 - 100 7 0 - 100 8 2 - 100 9 1 - - -Lastly, we want to take a look at the development of the Gini -coefficient over the course of one iteration. Filtering and printing -looks almost the same as above, only this time we choose a different -episode. - -.. code:: ipython3 - - results_one_episode = results_df[ - (results_df.N == 10) & (results_df.iteration == 1) & (results_df.AgentID == 0) - ] - print(results_one_episode.to_string(index=False, columns=["Step", "Gini"], max_rows=25)) - - -.. parsed-literal:: - - Step Gini - 0 0.00 - 1 0.18 - 2 0.18 - 3 0.18 - 4 0.18 - 5 0.18 - 6 0.18 - 7 0.18 - 8 0.18 - 9 0.18 - 10 0.18 - 11 0.18 - ... ... - 89 0.54 - 90 0.54 - 91 0.56 - 92 0.56 - 93 0.56 - 94 0.56 - 95 0.56 - 96 0.56 - 97 0.56 - 98 0.56 - 99 0.56 - 100 0.56 - - -Happy Modeling! -~~~~~~~~~~~~~~~ - -This document is a work in progress. If you see any errors, exclusions -or have any problems please contact -`us `__. - -[Comer2014] Comer, Kenneth W. “Who Goes First? An Examination of the -Impact of Activation on Outcome Behavior in AgentBased Models.” George -Mason University, 2014. -http://mars.gmu.edu/bitstream/handle/1920/9070/Comer_gmu_0883E_10539.pdf - -[Dragulescu2002] Drăgulescu, Adrian A., and Victor M. Yakovenko. -“Statistical Mechanics of Money, Income, and Wealth: A Short Survey.” -arXiv Preprint Cond-mat/0211175, 2002. -http://arxiv.org/abs/cond-mat/0211175. diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_19_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_19_1.png deleted file mode 100644 index 844165fba4ef1b382e1f8ef9b3f472953c63954d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 5734 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 F{{V-b)6xI{ diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_32_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_32_1.png deleted file mode 100644 index f687da712492a707a06926cc8d52210e88881f56..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10048 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 ZNcDv(MX~YRho{64Nin%Q*|+XL{(pV8`V{~G diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_38_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_38_1.png deleted file mode 100644 index 9118c60cc4b6c9888f0abc2cba149c6751744bfe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 20884 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 Z%w0AlCk1taLs-b_Tib6+zdrN*e*jB)HGu#C diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_44_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_44_1.png deleted file mode 100644 index aa2c9e026b3e3534ea7754a92b50bb88fb52ba42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13631 zcmb7r2{@H)*Y-^+dPq_kDrAcWM5dCl5FvA93}u_=c}gWiwmD=TGRsh=kPxBFGntBv z+mJc)zwX`heee4m-}k@A{~t%s!@gbjHLr7>>s)L5E8e|LafJQ|f*=$!(h|xDLX1HW zq5{%G@WhSlQ4RdKZZD}}uVQ6n@1$@02$9#fw>G!3H#ae0a(rZKXJTc^!zsYY&CXdQ?vc+B*JVNRVosd~e-?u{$N`3S{qD;{Y69nOXBO@WM>Kr#e{McD_ zB7S43P0mw}sY;AP#>>loi70e5iK{=O{bT?;J}mc9gzs?D$@PxsI>zhSW<|3p@AUDv zI&`QWYNb_tHBcgp%;u$_I6-tyOuPM#Y%vKR*&Z_w^@HO^tt*F{$u5#y{SqA)&9m6& z(Bg0%+Zt`x#;q?>wz%zX6{Sz>1Jg4fE2$tt5T-IJDFpG#C526>zUNJaAP+8oB1Mpb zllmkG(*J^s7@9_yIvo&XK64Zwt{|f1=vbo788zGjFQ0ut>$BrowY5HfxwK+;FW8$D zljy$ef8?*f<}G16r@ajug0dd(@6CTqIHu2Xl- z=EEKv!-Ce|y#DKv z=Ff1N;f?h{D|Ho>aDJ;1bIaIkHYrlU47oiy>Q2+{B8duRWo4%3<`0vF5V2;yL^AyQ z{UVDgr!wtJjbB0rJ!TG0j_KU4s=85Zy7=_qU%ZC(M|qde|5;M66r$rvC5;gV-QX&(2;)xk`Qo(U=<}z5Mo3 znYE2uYxv^F`=0Z|6{WYf`}+DWU%8UsQu5)0P)}c9shiyW`_Yl?_m5k2!HK3Jv|rb$PNWOinzqLh@>n{TO|ot^xZ7h!47pFiim z!H6JKPr9%yGL>%WGu5@DbL%U8dOUO)=ppF2^S(al zteNFs&u@PF=uwg2xn*+jnRr@iYSDF8QBge#3JQx7K3?7nqM}v9E6zd;E$F^3SZ z^E)+#`Ye}UtMJg3vp3nTf8aQGtAGke7Mp0e6 z$z~;a`Idv?5@8>7^DkY#JZPSinHkcDpVw?6Lj0acw@F8`wM4F9(@TAls>>eyv{(B4 zac<6>f>Go{OG$IHoQ}&x#N_(en86ypPvOy{M`5Xw0jErse}2*Y=w4>j{3;oq=aiRg zn)SYUI}K*^AYK}2T<-6}{BW#z8J&=lJ{dxs(KemUJ{5dILxd-zq~xB1gTv&COgO8_ zaJhqKp;4ezAbnbtp6C6Ei3u8SdIT90H*50~uH{w6AEOe+oK;m*bNDh9#%tEg-V(!? zlaoP=FimPs;re;xF!2;ODUN)7EO`ABsTrK0bwc_Bp;cUJu}MG|Y8uxR9vZU@rdOD- zTpSG7uH{87ymdU-_c~^(b)F41H+NziZaKR=toL=8J4<5q%qf}mlfvPe&lH*Z4YW>| zQv(oqt-P|6GM}USl8%KBKTk&Ugqax9etFmmNb%+JgOzW*oi4S!tAy^xOkepVz$jq> z`n0){ZDUDT?-VTb9Q{4IK=Y;#%Ph{G4Mmu#I8pYn=)3Wu3pc@>zsIep&pA}+loVqI z?Hn!WC(*thjwNqpcYouteRn-ovA>|0WB3W+QT6p)J}G5$i-yf91)mWrLSIF_v@(Uo zXA`A!Et!W_-|_Bgr~Y{$rD-aT$@+_XqSm%(B%B5!C|Q%H*T zAamiW95szFh8*@V#GP4hXTYYNC(L3USJXcg){lYytb;`F&f=tVg)yG4(3>;^QDLj! zu0BinpM7hjd8|#*)y_kVh%Ng?+5Va;eL~m;Y3=8skJpa!RdZD zNmwV#P5oPJv2790f&&sl3!nHAUaZ^H+lo#&aazmvlmj~6)ljamgt16AzN4QS%fuww z8X4~wn?^_loym%eJ2Ua~XCr=XHc(*1*lEAmtncfDZL!P4hY#(`IuJyIBY`#lj)kuY zd2K-%-`kTtCl^PnsvT#FII2Z`SxIL}%c`=tWhG5E%Csk2?OUarQ-yxr zgzZHz+Z7HHBcdKuc8=t$-$*{it1O3dA(nD_*q<42wsdG5OM@$r4Zua#8j zF&%5KesVxfLnAYq2Rl3LG+H_5cKDh2ru#>JN(3>uIO1b6WEu6YGyHY0bhrM`?={c9 ze*GHOFy0}&G4wj{w18=Ufj-T!Q)9?E*M+jFB9kuG%3q)UI^tU!!)p>IvNio$HsSS~ zH*$c&ldlU3xV{+?BgY~dx#S(=R&ml(YWfbp&+;kxhMv3SlbN0_WmUCXvQW99+Y*06 zeX(+5L~>)zwek`tXYX{!Yd)9dhm~8iIa^=VKc-+4J=VjfE^u(D85+Lcyas-usD%V! zy0mu!-m1 zMGdu$FEx%YNeuELh#*HoZ<>D+Huc%39|ccq9{8?PJijUE{443mkt5}$-zDMjSyon-&tuD;f|g6nvq}%&o2#Ox7Exk8z$^okzE|bBt9tJqtBmVv$8A2F zF$oF=Aq}>>aqZL!Ldmah2b+$3aOr(~96`#NINhX3F*GPTzX%9WQ&-Oz8qy9tb6r`h z#JrE6y}c7DG4;z%W*<P6)jg%fnpkLxn<$bwMMIhvS)b92Wte+Fv5Vn7f?rQUH%E!d2Iw z@WEziUpO8Tr+qwuFs~&|ti+w5&&z*?-b@L`U&j!(M;ow$&R3s>XLs(>C&H5lHab|A z@i{IE6iciNs>>Yo-i_O~Nf_%U{aMPngsx zzwsL;sl5rI1?S>TZ6+1bbA?W9K5z!ItVzp;4mzw?#cTe?q02E1z4E(x^*1?&GpZ?0 z1K+KQLHAiXVB~+(7yELWRTkYNC@TPiAFiO8Xu?`!00}ZmSwo>(n2dFsAjN{W|K3 z&$URnqEoJ#b1%1ZVYtF!*r89SU|&T+I8Dp4OmcL4WP?Q}Sa|)_rAwDqSGyF<)@FNj zjQJ5JCHGm%OU|sGi^L|s;v-oexpbOa%4{>^yUuKnrHX7VKa^`-iZBlr%DTwyve-0Z zPfiXDsX+f(OiawrHP;fpMuUKHs=B(KUp9^&dUoW{vq`(ew!Q&?HS?nN zT*|dJQMa}QBYWw{hJ-6)L4M72E<00I9pj?=@-V>csC;KXQKjMXNEEdL?R9_^phkBS zMRMP~VYe81uLP92V*2CS+8U5evqFw5R}|p)D52Z`Mip(+x2B`QQdLW3FNSWbj4N6O z^UCk(dCXZ#Ranv`G(s2ptbHgr6CV}DWNtdtKqT}n(0Js&^+EFeekP?=q^#bAl! z0U0bHV9AWq(x&?*cJ`vVT_R){bG0FPP7A|pCbS>q)SLTyYqnR(W8PA#8T#q`UvSiQ zeE@r=1#%3_?37)%Vo_M27lp@p*Z$yNxzRec=Bjr8{(zB| zq@-kpogDBYgORM`4dJ)+t1?qeO-!=V6%zA^4jgJ++%N;4mYtp52|klW=JV&zvwiuA z;C@(S0#0#s{`?YN?7nH+@{0QwzAsS185^4e_t+F}WBKM}?0K2u;$qthts7db@-e9n_}-cA(fyAM&fkf2>{|FP zUAfZt>ql+w?g_-}{Y%mwd418SPy|o4?JhDgIhpSylt=YbQ{_SicYM|E0WrrSy*~Isdf>^#j9{wAs3%R|lXrZ-iW>8)Ux&3=iu11hRfM9VADOFEyAo=laz*#_&f~s! z6524~l3o*j@f-RkOItH>4B&>l;{-58K_9|fb~^N~Qr2{Y7p(;-1kni7kL+)BV;Vs@ zE4O$X>7VERy;^SfStz4W2&|Lv(xU=vf;@UZUWT^j5ju^?6_~$Ff@*1}%4T*=+7dM}QlRa%$?Qi=cv-rJ$H%yx z3MS??p($HKEoJRO_?@_$R{R7k329O?Bq?-cwBSs@&vW#I z7nrFvqIsSM{b@Nv_eDa=jXe30L#+#i( zn0`pyn|}mnlOk&;MSuGAc^P$Wo6qMr{vsi!zh<2Tfjpnxq+BInV$_f*E&11}m0}kJ zIeyqd>R2E|&(_!8d=8+&*PpiPh8sFKZ1sO49#s|aq+HX@AJQCOjjmb zAl7tKg-8%WS6-uL+OyYfQ-?i-7=ncu6tECp77S!XhJ2dZYfi(GoTcjF3e>07d~co1 zZ#k5it5s^+o+Pf6$aUh#46x5j7cTg1X&_=d0Vjm)DpDCty6I{rBv0O_fwKY=Ht9;2 zN6*>Aqmt#KfPlKN>uLzgNgA3Qy(&+LLBm;ufs)36MsSFd;SDw0-D!Weg!YctxB09_ zgaSm5AWRGrrnrZ$$`iI_=9U!4#UX44f*qE6Q6;@-U~gxo4c~fW@g+-7Z|~Q6%X#a~ z<)$lqkN4amqR(+!9HAGmjB{KV%F?g(ambwj+;|xIbm}g>y`rk<#k0|PO*OTi$;}rh zFBchnevwIk5RulF7r!?rX|EA`2zhW(luzo|d4PjzAS zRKR=(HJG$lJWq{LYmQ98l60V|4*R2CIvF-xk|DwDcoWGkJF~LOe1+iitOPnx`pnR= zK#7C*?9ZT<8{Oz0isLQqRQ~-!yx~_kj4p>~b}`{ECGq!0=OV{~pRW#L z+ScmK>y#Acvm)-l-!)kD7l?!2q$VZhBZZ?)?$di**+p>(sbj0<|>QG0(C7k(S ziTq!u*xP}1s3|EWM@F9VScx4~RZ&r?XqX!+Qz^u@^+NoV3(;IAP#ExTfQp#prKYB~lz^3FefaQU*zjph68zg7np$y4 zi$+37mi$` zlxz$kO(~D9=OX9Oey@01z_PnHSBuZMJ*kOd{9nA|`3GGqdya#MM2q(OpmIh(L#17N zPK^RF2;cxeVqeC`TeNOB!gdEU2>-tlkEPp0$j)0(wOI8@Y}m6CLe+BaWdTc2NtaU+ z7k{$pJd+mH>|ttV2H_o6BoU%xm{|s&S+8+-rjqOCM3fpzDrF#!Gn1IaamhV%KGmau zy?rMPR4APS3*a^}d}jA}L4(p@I%Xa!;o?{)&{Mqr6DTvT#($=NpBw+n#zLG=8!_vCKx+Tb6!ht5aRl;Fo&3jrJKqLd z_f46m`}@5LlaLF9reO0a=+q88|Kt-Bb1Muy*bX|q1AKgm-21VxNGG)aq{d_NnlEHQ zkc!nhpiPgC?e`@|ng#tE)14YjD7w%mF%SUq#%d93GY}dm|N7G)3903kW+F5drPc@) zLkAXvRsMO2xaO1$eBcH)Tw*nv2MUau$M$+e(}c2%%jWrw{Os(orit?JN_u*u1N;XN z&S$V@7LYd5(^&)Q`R-g+&%@2W2jY>Fm)GjPI@87WP2~V0*!YI$CapNJ-&Te_AX1Eo zhif<6Y*$9G>)PzeH5pb{ZqnCsaO&+ zrrE?!F{UKU?Vwj}qC3BC^>lYn12(mL5gky~)XWCiU1NVUasQ%>>+%;iFk2=ar(p*{ zkF6rl)z^$BV4ho_uXySYXCTNcpw)uZ{4CsZ&+3k?DVPBL|F{%r^{NeHcVn_OJ_op6 zc3$3HVEB{xmX?;ShQ~X%Js@o`J5ZQ`ig$W@%TX312S7D${HZ4MRgMkEv{wdkjyiQTj;e-t|vW?M%ri4 zO+a;sYjRBe%>MKk=`-l1qAjc^b?RgiN3Wp%&YS)K62e>ltElkc{}eTzuDWtwX)Z!z zQKv4Z8bVWIxcg5bMdMYg2<=chf0X(`E9r4^!ccI_P<7dw7A&cQ(4JWsGmwB@Z+k+E zpK=%8sBoM^@2(B{cX2SE$;Vh%Wpi_~b{q87kj8l4nJh~eWipI3IpIO?CNp`*-_6l` z-%KNJfX?9A?=0@8Bq$j;Or~f^Lgt4~=2ILM3p2V}QeliVD$-aV15(2bjKb@g1c7TZ z6W#@H4>wr?m7NbS@yI><(~rs7bp6k|EJx7}r12#8fcT7|hW_l8FowZZD&$(qQOsbe zRbOuDNH$0aps<4L+IiOiuxK*Z{}v)mdLie6-aMT_|8!!66VzU5iGNt#T>BoQ=kH`R zV_@YzemXlVt7GhQ0802Q2Hu^Yt%sKa2`}vzK`5@%m7%csYcEGVzc)$zSrG)7GM&YJ zx;bZWI2wO`aWaPALbE8NwA*Ii1d>Tw%eul{Dh`rH<>waI!$OPH~O#<4cybjPi+hWgJW9UqkXQ@WcbW zUK_hA7!?_rotj#Q&IbU)c+*99t4ddP1Ur!*H0{sfdy=$7W7!ko>~?2;^Sw;Jr;_rn znkD7JDWW*$Nqv@o_s48(Qz@0qVLz9g;h(`UZ<9p55yr3>dDCY4B-(~U<*Qv5hV_h_mql71#i<$awg_uqeL#ic6 zlJ+gB9c;`pkKBQ4r}XQXL#ujYUhxpuN#M~?!K>?-6nj_;$vviYW^vJ2efnw}m?AeS z?TX2fTsTZgcxRI29;8ts|MI0c7!zH6B52(Q=sdTU_acNWV(hTuZb7Tm&y$fz z>zF_-X)Ui8HI)?mOL^F8)@FVBg><4b{#*vh?52STHx*s_7o%3v@6z5>e@U|TZNf3I zOSX}IH;2z}ZLb_V?Fp+6lA?X9_%oj$HmTS2NGA{RH9Gyeo50i%?wYtill}?<%`xiG zk$SmL&{_Nu46uam9(BE39FAQ66_>c%`kaZ==)da}eOT?3y>#(n2PRl3Wp{hS3|J+ARZI}0=*R+^SIW-G;bFWT%rLL~di*3v&G`8J z?U&JaVQ(+YaxK>~?kTQJwe^9&O@rvw1Y}dxQzp1AlsN|5lxcr$YZG8}adFWxwf~UP zO8THuTEWoB^#$xC@GPcNAhUs!{Z?v~n0`0@+twk=Quvt-@kX(m_W#T<{d9q#!(Bu# z_BONM%WT-k2WKJnMH(Q;atBTE$n%>ttL8r^CbB+$+)HU7r@JcACcF{HZ}BaaM%P*V zhqVSuQ*JYQbT7C7j)2%;CIXVlYm;<~Odev5D`J6E)k_AR0)c&aH4y$#*7PNu4G-Je zqyBOEr117!A#fp}8};>4D>mRmGz7Yi55Uc3@6+^%kX(lCoK*$I; zXt99lMnpt_Xbx{HULd5%laiRqBU!zgS3XH!Glw{*nr_D^FccUoG1I-2J(bs~sSEeGa2_K& zE9|NYdQ0JYc-@n^71n-W$4dOeU&W$|Tj_pmkXdI#UBZ0nXr6-Tt}3PIPQff5kH(ml zAcsz-_5!$;4dJiUDl^aT7lzdIsD1*GXSJU!-w1`u0M^Z`sldHYbSdV z|6B;h#FUv})a76!*+#`*a{cJz7KJr6ri%bTk zbHn#37CkhB>NuX8ZKUlcdv>*P%``-D`6~JkC%#6ML+&>sG&Grvn$@^1K{#sY@&Bwa ziZ?Y+Njf9c?vU%$30yZL;6LG_XUDD+h;;*nSUlvSdck|6(lDwCLD^DMV6Zxza(P}> zG(AS_oi)TT5_Z+zWYqSM=EBcpBu274uhoY_s@cW;y+w!p-3`5hebllb+_M<@Ffhaq z3Ew~lVNGC?3+>lEL=tW7?0Wk9GvngojAP6K+75^Zy6r$FM{jh4qU@F1VF`=zz2M>x zW2G@7LV7uSu5nQ6)lLQ?PSC7E(B!1i{9vgx=*u+1M(}?CeUn0q zZ$&1Mv9Q_@`WLlKF-f0qWgOod75yC%$U$_Rky?XjDqyyBr?5>#<-!SNU0vPSdBAj6 znEVx2IS-Fr$&LK{eCPwJZ8YCKl;M>RxD1hW3+??Ry^nnloK(ELdojY2U3pEV6?BLJ zJLmpS$iQ24#g6^qjfMd!rOW%c;tDdz>Uw8>YE)C#NqhP71RrnFZa@_QLuvN$=S4+D zvv<#b`IB_-f(&#>5=b_z!ooTLG%AlCrIm4{bZ0>5Ts*ydl!U2i;XpJjab%y)CN9U#1qaT6TgklSq^YFRuBD#)Onb#4VsUj_Oj_+;Of zvM@6h(9ZuZ`#bMIyW}k7`ZM%45d1mI7vujN#D1grt{uq><`vgt^FFhq!m`M+-i zbaB7THZAYtmMNM!{^U*etq>bLEPuxtu3b5l2}Z=n=R)QL6;XXD87&LjX1X$pAJh<| z1!3UtJ1t6w(|vpn#cIxxLnbf0X`&P2xfTm;tQi{MZ{e%<%+RK@9j|>+F?V*>bj1Pk z6u)XIbCQz8AS2KY(b!tW=x%HMDYkU*$-WG(($5Z2D1+L!%Jz7f&2{c=!?!mUpNbda z!0Pb5T2^YXX^;|tyzj{5T99_%m5;w~q6Kbu?*@TTlUA|#QR|6V5|jo`A*s=V%0-a zz__@9%mjY@dIxe$-(Eqgg4?8%<+N6OKcvYGwLR8)>Y->Q5NhF~2g_P}H^&05-~hVl zokmyG^6{kCvP7$of?N`lOnJ~Jb*lbaAC*eokqC##;)vrqJ=2OX14p{X{`k3 zNKPG7ZP>2*$(Gnapaktu(DN)bBI1z{l*&wsXyaR50PzA3GrG4sul1-?$kf*do*w)# z5{h?i;wYtsAnB^(;)ul#RdDa1^KcUv2L|ijA9v4g%(PIniWIiyNo=wCw_wl$7-4 z5`+fGF>u6CJ+K?TRGjaTJKR_MqaZXf(Mo8kj(V~Lk~y>t!41DrI9@PLF>HRtUHiM$ z>0_7CVCly-4#U>C>puF{xw*>?`l*<{HAs8$u+X_}Ebwi(Ks5t-RMR7OxIH?i3d%>j zcm)K^;KXAD9kR<><{iyuyR*JQEodNzT>@}Mna-2y+UI0YwyUYB`AvHpPSUj6i%5I> zu13LwCm=L*OuF9D#^zM6e>0;O_55Je5|f6e0%1y9!TY}{SJq&k6(O~6k>>=dIaaw> z)oVe3GU2NC?`Mtft(S(S!fD=yj9feDgy(H?4bw1v4gmGDJvmYJ!HiZ5JPxx^Lt|M9 z;H4w@_$K>HY;l{&wp_BJG~l^K$<)Ne%rjYt5|j8Y2W+S-mUM?D2q&cE=9b2IgNteNTu5v<^C_qI|>SYP({#U=KA)r z%KMRyvi-en$oQdkAZe+o7fu{8ndwZ6;lT#6z_ekbi(x#EtyPm1S5wpMy!T@-IQ6zu z$uZLmXetFZ4m8B06|rO28iLMtrAP*-LS_YGpNiOVXCTgSQ~IiQnyRG3&^R3uG-zBB zbHgdGCD$%8ac?!lb?GxrvE{HJDii)K?DUjc>E#taJ3qnI^2hzW{A+<7NW7w_=STTm z+sMB&l3P9bo@sl#+p&WyD=R%iLufLvXduyyF&;$S8KBcZVr7Im=ZASr)uuh4`9%OUGpih9TZ&RZ&f3b*fQSZNcrTCf8s9bepASj2&qoECOl=iOVrvfr7R zlT(=AadfFc-y3h9o47lNt%uX12yQSWKP%X+QD`I|i$d5^`z=a3Fk7F7ac;}<8P;)V z#9z#Bv*|8k2zB3ttpbx6&(Yh>UIMG62j!5kpene-oLh@4V2%{E)BO&+ke&_%HhcR1 z{rghjnQS1Mr}M0;YO=7St9N-c4`uu1_mx|r0uEV1GqL}4%V;L}KYbse`f8%*Ru)>E ziB>x@1mA{$7!?>sT=5(-kokZtNLghd$uTG8c}u7=em`F_*bQctxbu^3YgWA~8{8k< z=~>vYQi!<5XrXGl05Y6x-4#w&P^E|#cGG>%GxDWK;G)Ru@em^YQUJ7^Ja?>0!2+*fTA{)Pc zRlavG5^AbWk{`O_>;ZL?VX0Gzk97&<;ClO8eQ1R#fX^z7F@t_B0l>c4ex{SZL3FkK zRynL>6|(-u;6cjAgKn(Gj_$CoGm?unckmEbU(Nrp`~r=YRWk0PdUTAC%e#g5<44iz z&CUUaATV2~+;0>z0v}R7lO79c4pU3FAJ1v*M?E%XfVpx9SAf$;q1G-D4d$T_*U4yX z`BW{DALOqQjmeTh%PzJ=_mnb+W@GDLca)+t5Z=jY&haFbr!8@FlxI4YeXjk zl__A&okML<-+R3kzlI7=BtZQ&E@3dYfZ7&Xe%v$?hK0olSS2vH{G=LonTn?w7ClC4 z{3Y!CR&VJ?hmH;6@9q%t`k|E!Sy@>!gnC)U3*D1zZw(t};evwZ{_7xRX~kM%CRE6j zUboy>7%m5QXSfd)Lua5ulznM)DiKQa?{9$*evMWS!uL?X+P7*bJ(Hl+_>Q34ZF%7O zmLHa+T!a`~$pfkVw))&$gx>?_qbxXsX)LOjdDGQD#AbVxdt>=K)HgB#_Fd zv7+12@U@OrU(p@$-K{kqtNGz~j{+DxR+Y|PvlcTkF+sn!0&FN_b7e|a@WvAd2tLtj zFn~cdMa87YyM2#)^mcpn27db!X*}m5W?Imh<2-}3f~fbeukpZ!*9l976Ja5W|Nn^w cdiD=H-`u<)C|+p;_KL_z-j&F^`QYjQ0bA7N`~Uy| diff --git a/docs/tutorials/intro_tutorial_files/intro_tutorial_57_1.png b/docs/tutorials/intro_tutorial_files/intro_tutorial_57_1.png deleted file mode 100644 index bafdc71c7ca43fb50776dd7b7066b252fddc9218..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19255 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 diff --git a/docs/tutorials/intro_tutorial_files/output_19_1.png b/docs/tutorials/intro_tutorial_files/output_19_1.png deleted file mode 100644 index 67febb9ea3a6c839a172b1975aa6c8eac5cd52ed..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3636 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 KlKDgAg?|C)$geyA diff --git a/docs/tutorials/intro_tutorial_files/output_22_1.png b/docs/tutorials/intro_tutorial_files/output_22_1.png deleted file mode 100644 index cf21c1a82828e46c1bd921f3d51283fd7bd18c57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3485 zcmd55I7S6&}7qImz0s*v4pe+y-WLIdb7Dhy^Fe(r>!y*(&0>~19fVA~mS%)hW zgiu>A$gmTZC|jgYNii%cMYaIZuske600V(QxWR6=Ui;kpuRmtyd*(UkTh4jU`=0OG z>63Qbx2SJHp-|gF`=e)2D0HN3ELKvGy;VOgu#~jIgo6ZQgS{8S!zp1_Mn=KE84$>!{zm$V&(6yxd5dE25{5!;-MRUo zQ8~GLWGij~kACDFmHT|iJ!$MxEN6b%Znrkl17+{1s-H}=A4v01>OZ|L`M?g1T{^@& zUsNAgY8!s2aAapajdX&BHV<@ES8o|8E8X>0;NA<`N?s+Z-!S7qKds44jEbkWyi&9#Z%+RfE#pKb@%Rqm51<)D|P9%PpEGTQvfp7V@tz3VmB{; zSW_?)V@?Ue(YN(sagJ5ks9nPEicS$g*Y11%vydnO8E}WLvv;u@A(1~jmTk@PK8KD> z8nfnDkv1A3$SKbie$ikSmM`(d_Uc)-JTMhuNfI=_h9nWOr5(xdfmSYhB6H{BDF=2| z>jH@_uV*o?%C0Zie-jA!xakN4tN#z-{u`LQjBEg0uDe2m?ckWGAY1D;ESLhUoPbqR zpBt%7+qFu6FpS#{-=65JOdv^BSuBJBhFS@402^C~Dl!0sI4&%+P1%kD$th&&5?F=` zk&}jBA1C`#H{6+8-ho=pPanIa?rr?0^zyieK0~dzFtr@B35!7$Ojha`w^bH#JZM~+ zfCr}sADS)$h4|<_7RtQsYvw{{=&-f2<(3(BZhgESs#RK;(97T-SjM{?uc~fnXed(M z2d)Nde03RGM-UGc8Rk$@FXkyCWPYw^7#XOqsHo__ngNuCo{8i-_c+P&tP!6F#)Wmn z$)I7Nja@<|t(Rls4kml!K@Dblz3*;#2FEqR4sXNx;TPs`SKZ}#2Quey(@E>gXCX(b zANbhHEVQoOg?%3=tX$Ae97LwJf?D#j5SX)%uoI|jXnsD7L&t3+R?3GtzzohkYB@qi zP2_n=NF_u%eRjzcHJ9@s9KL-`tPH(E3x5M7{6Gv*0ib2ng1@ z)N#1&U5b_vMe&0UTSmlcTZ9?RVHoE{Bs)&+gw2yCzyZw6%*r|y5duD5?30mL14e8h zY(iEir(z-K!@TyL=XyGznmS~OU|^8jo_ZX9QhAG|XPI}Sf{q|I=4Kmw^DMX4WdTWY zz^e<<9yweK!Y9*{d4@arC<+U)sS51PV?(5s5BMq2;E45X&fOhRw4N~y@bUW!o+OB-1*-bwXBUYm(=~cQb#D0C3b1L&$53}iofvuRZRXL zlTH3uISLEFrkcRP&tP8OXuUvE%vl0fqF^1cDSdRU6z2hlU(m1&+pyyscJw7Bvd#LpsGg%4`2Jq(O%G9|s2`Cwm4pV#EzhHM46GrT17|@*7OdIWH-;6hb zaq>2DJQOc;RGojBUC(*yDL1SjD|TgKqr8bs7b`<}#ZwPVBiuu;khZcSm`6a^j1zh1 z_|L-s<`iBWGCu$s`COOG<%pD=7RB4MI%#rwA2aU$(lWf+^UWpuh7{6|K(qwm-2JAC zsPR8{CVyCFGJNAcG5efE_$D^3{YaoWWDnQ=#g@NVZ!-Sb}~= zuJ!YYblG41PM#4e#kq$Bi5&Q6`BjEdTHs^#+Gioz?(Ch#Pf{`T7raDx2D_Bp;}6I9 zw@g@L#ZN!CtsfG**5(t+u`z~&XF_;CeYVnA6VX0%_DAHN8Y`Nan&DG~ES>beoxNBS zWb5vsD6=jur5U=~{n6q!>)(1Z~ZWqf0!($|6 zIOg59d0-?*WNH&sue6l-_a_0VdMh$k$S~-j zF+hBO_UIBYax&DSO`~8{yh!VajcLD6N0a@=lFxWz%SsSCf2%$@xZ&+61+fpb{`me3Ecz)@Y$#4pp5mIy{+o@lsn-1YAr!6*No}VwI_#^`d?wDvsp&VfHkM9Y z%{dLleuG4u>v>mygTZry=r)2%UD^}h9g U&g?0WU6oMav6Dy3KK8ouALv23&;S4c diff --git a/docs/tutorials/intro_tutorial_files/output_32_1.png b/docs/tutorials/intro_tutorial_files/output_32_1.png deleted file mode 100644 index 0a776f4cbe122712960e5c7086830bd663403ac5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4946 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 btY#G<)mMLt?|>JPP-G?FNtC=b`uu+YoA}qW diff --git a/docs/tutorials/intro_tutorial_files/output_42_1.png b/docs/tutorials/intro_tutorial_files/output_42_1.png deleted file mode 100644 index cdab93196b7a43ca8da1a4c4de01e2b0ed3c3b3e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3745 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 diff --git a/docs/tutorials/intro_tutorial_files/output_44_1.png b/docs/tutorials/intro_tutorial_files/output_44_1.png deleted file mode 100644 index c6f98a951ca592983f9ee4c92f633eb7102940fc..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9453 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 Date: Sun, 14 May 2023 14:57:14 -0400 Subject: [PATCH 106/214] fix: Add ipykernel dependency (No such kernel named python3) This should be a temporary fix for https://stackoverflow.com/questions/69759351/error-jupyter-client-kernelspec-nosuchkernel-no-such-kernel-named-python3-occu --- setup.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 05f4deab5b2..696b96c5d53 100644 --- a/setup.py +++ b/setup.py @@ -21,7 +21,10 @@ ], # Constrain sphinx version until https://github.com/readthedocs/readthedocs.org/issues/10279 # is fixed. - "docs": ["sphinx<7", "ipython", "nbsphinx"], + # Explicitly install ipykernel for Python 3.8. + # See https://stackoverflow.com/questions/28831854/how-do-i-add-python3-kernel-to-jupyter-ipython + # Could be removed in the future + "docs": ["sphinx<7", "ipython", "nbsphinx", "ipykernel"], } version = "" From 68b8e6ca601ae643bad547ffd0cfc59f8121116a Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 14 May 2023 15:23:01 -0400 Subject: [PATCH 107/214] intro_tutorial: Add back fix from #1665 comments --- docs/tutorials/intro_tutorial.ipynb | 45 +++++++++++++++-------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 27114ece89c..8f053d17b71 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -66,7 +66,6 @@ "\n", "```bash\n", "pip install matplotlib\n", - "pip install seaborn\n", "```\n" ] }, @@ -131,11 +130,15 @@ "source": [ "import mesa\n", "\n", - "# Visualization tools needed to explore the model.\n", + "# Data visualization tool.\n", "import matplotlib.pyplot as plt\n", + "\n", + "# Has multi-dimensional arrays and matrices. Has a large collection of\n", + "# mathematical functions to operate on these arrays.\n", "import numpy as np\n", - "import pandas as pd\n", - "import seaborn as sns" + "\n", + "# Data manipulation and analysis.\n", + "import pandas as pd" ] }, { @@ -146,11 +149,11 @@ "\n", "First create the agent. As the tutorial progresses, more functionality will be added to the agent.\n", "\n", - "**Background:** Agents are the individual entities that act in the model.IT is a good modeling practice to make certain each Agent can be uniquely identified.\n", + "**Background:** Agents are the individual entities that act in the model. It is a good modeling practice to make certain each Agent can be uniquely identified.\n", "\n", - "**Model Specific Information:** Agents are the individuals that exchange money, in this case the amount of money an individual agent has is represented as wealth. Additionally, agents each have a unique identifier.\n", + "**Model-specific information:** Agents are the individuals that exchange money, in this case the amount of money an individual agent has is represented as wealth. Additionally, agents each have a unique identifier.\n", "\n", - "**Technical Details:** This is done by creating a new class (or object) that extends `mesa.Agent` creating a subclass of the `Agent` class from mesa. The new class is named `MoneyAgent`. The technical details about the agent object can be found in the [mesa repo](https://github.com/projectmesa/mesa/blob/main/mesa/agent.py).\n", + "**Code implementation:** This is done by creating a new class (or object) that extends `mesa.Agent` creating a subclass of the `Agent` class from mesa. The new class is named `MoneyAgent`. The technical details about the agent object can be found in the [mesa repo](https://github.com/projectmesa/mesa/blob/main/mesa/agent.py).\n", "\n", "\n", "The `MoneyAgent` class is created with the following code:\n" @@ -188,13 +191,13 @@ "source": [ "### Create Model\n", "\n", - "Next create the model. Again as the tutorial progresses, more functionality will be added to the model.\n", + "Next, create the model. Again, as the tutorial progresses, more functionality will be added to the model.\n", "\n", - "**Background:** The model can be visualized as a grid containing all the agents. The model creates, holds and manages all the agents on the grid. Running the model will work through all the time cycles.\n", + "**Background:** The model can be visualized as a grid containing all the agents. The model creates, holds and manages all the agents on the grid. The model evolves in discrete time steps.\n", "\n", - "**Model Specific Information:** When a model is created the number of agents within the model is specified. The model then creates the agents and places them on the grid. The model also contains a scheduler which controls the order in which agents are activated. The scheduler is also responsible for advancing the model by one step. The model also contains a data collector which collects data from the model. These topics will be covered in more detail later in the tutorial.\n", + "**Model-specific information:** When a model is created the number of agents within the model is specified. The model then creates the agents and places them on the grid. The model also contains a scheduler which controls the order in which agents are activated. The scheduler is also responsible for advancing the model by one step. The model also contains a data collector which collects data from the model. These topics will be covered in more detail later in the tutorial.\n", "\n", - "**Technical Details:** This is done by creating a new class (or object) that extends `mesa.Model` creating a subclass of the `Model` class from mesa. The new class is named `MoneyModel`. The technical details about the agent object can be found in the [mesa repo](https://github.com/projectmesa/mesa/blob/main/mesa/model.py).\n", + "**Code implementation:** This is done by creating a new class (or object) that extends `mesa.Model` creating a subclass of the `Model` class from mesa. The new class is named `MoneyModel`. The technical details about the agent object can be found in the [mesa repo](https://github.com/projectmesa/mesa/blob/main/mesa/model.py).\n", "\n", "The `MoneyModel` class is created with the following code:" ] @@ -225,15 +228,15 @@ "metadata": {}, "source": [ "### Adding the Scheduler\n", - "Now the model will me modified to add a scheduler.\n", + "Now the model will be modified to add a scheduler.\n", "\n", "**Background:** The scheduler controls the order in which agents are activated, causing the agent to take their defined action. The scheduler is also responsible for advancing the model by one step. A step is the smallest unit of time in the model, and is often referred to as a tick. The scheduler can be configured to activate agents in different orders. This can be important as the order in which agents are activated can impact the results of the model [Comer2014]. At each step of the model, one or more of the agents -- usually all of them -- are activated and take their own step, changing internally and/or interacting with one another or the environment.\n", "\n", - "**Model Specific Information:** A new class is named `RandomActivationByAgent` is created which extends `mesa.time.RandomActivation` creating a subclass of the `RandomActivation` class from mesa. This class activates all the agents once per step, in random order. Every agent is expected to have a ``step`` method. The step method is the action the agent takes when it is activated by the model schedule. We add an agent to the schedule using the `add` method; when we call the schedule's `step` method, the model shuffles the order of the agents, then activates and executes each agent's ```step``` method. The scheduler is then added to the model.\n", + "**Model-specific information:** A new class is named `RandomActivationByAgent` is created which extends `mesa.time.RandomActivation` creating a subclass of the `RandomActivation` class from Mesa. This class activates all the agents once per step, in random order. Every agent is expected to have a ``step`` method. The step method is the action the agent takes when it is activated by the model schedule. We add an agent to the schedule using the `add` method; when we call the schedule's `step` method, the model shuffles the order of the agents, then activates and executes each agent's ```step``` method. The scheduler is then added to the model.\n", "\n", - "**Technical Details:** The technical details about the timer object can be found in the [mesa repo](https://github.com/projectmesa/mesa/blob/main/mesa/time.py). Mesa offers a few different built-in scheduler classes, with a common interface. That makes it easy to change the activation regime a given model uses, and see whether it changes the model behavior. The details pertaining to the scheduler interface can be located the same [mesa repo](https://github.com/projectmesa/mesa/blob/main/mesa/time.py).\n", + "**Code implementation:** The technical details about the timer object can be found in the [mesa repo](https://github.com/projectmesa/mesa/blob/main/mesa/time.py). Mesa offers a few different built-in scheduler classes, with a common interface. That makes it easy to change the activation regime a given model uses, and see whether it changes the model behavior. The details pertaining to the scheduler interface can be located the same [mesa repo](https://github.com/projectmesa/mesa/blob/main/mesa/time.py).\n", "\n", - "With that in mind, the MoneyAgent code is modified below to visually show when a new agent is created. The MoneyModel code is modified by adding the RandomActivation method to the model. with the scheduler added looks like this:" + "With that in mind, the `MoneyAgent` code is modified below to visually show when a new agent is created. The MoneyModel code is modified by adding the RandomActivation method to the model. with the scheduler added looks like this:" ] }, { @@ -254,7 +257,7 @@ " # Pass the parameters to the parent class.\n", " super().__init__(unique_id, model)\n", "\n", - " # Create the agent's variable and set the initial values.\n", + " # Create the agent's attribute and set the initial values.\n", " self.wealth = 1\n", "\n", " def step(self):\n", @@ -268,7 +271,7 @@ "\n", " def __init__(self, N):\n", " self.num_agents = N\n", - " # Scheduler is created and added to the model\n", + " # Create scheduler and assign it to the model\n", " self.schedule = mesa.time.RandomActivation(self)\n", "\n", " # Create agents\n", @@ -289,7 +292,7 @@ "metadata": {}, "source": [ "### Running the Model\n", - "A basic model has now been created. The model can be run by creating a model object and calling the step method. The model will run for one step and print the unique_id of each agent. The model can be run for multiple steps by calling the step method multiple times.\n", + "A basic model has now been created. The model can be run by creating a model object and calling the step method. The model will run for one step and print the unique_id of each agent. You may run the model for multiple steps by calling the step method multiple times.\n", "\n", "Note: If you are using `.py` (script) files instead of `.ipynb` (Jupyter), the common convention is\n", "to have a `run.py` in the same directory as your model code. You then (1) import the ``MoneyModel`` class,\n", @@ -408,13 +411,13 @@ "source": [ "### Agent Step\n", "\n", - "Looping back to the MoneyAgent the actual step process is now going to be created.\n", + "Returning back to the MoneyAgent the actual step process is now going to be created.\n", "\n", "**Background:** This is where the agent's behavior as it relates to each step or tick of the model is defined.\n", "\n", - "**Model Specific Information:** In this case, the agent will check its wealth, and if it has money, give one unit of it away to another random agent.\n", + "**Model-specific information:** In this case, the agent will check its wealth, and if it has money, give one unit of it away to another random agent.\n", "\n", - "**Technical Details:** The agent's step method is called by the scheduler during each step of the model. To allow the agent to choose another agent at random, we use the `model.random` random-number generator. This works just like Python's `random` module, but with a fixed seed set when the model is instantiated, that can be used to replicate a specific model run later." + "**Code implementation:** The agent's step method is called by the scheduler during each step of the model. To allow the agent to choose another agent at random, we use the `model.random` random-number generator. This works just like Python's `random` module, but with a fixed seed set when the model is instantiated, that can be used to replicate a specific model run later." ] }, { From e3af2a508080d343eb44f07c5fe5cc9f487a19aa Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 16 May 2023 05:06:13 -0400 Subject: [PATCH 108/214] sphinx: Use PyData theme --- docs/conf.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 79610fe2250..8365cb5257d 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -119,7 +119,7 @@ # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. -html_theme = "default" +html_theme = "pydata_sphinx_theme" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the diff --git a/setup.py b/setup.py index 696b96c5d53..00e88cbe18c 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ # Explicitly install ipykernel for Python 3.8. # See https://stackoverflow.com/questions/28831854/how-do-i-add-python3-kernel-to-jupyter-ipython # Could be removed in the future - "docs": ["sphinx<7", "ipython", "nbsphinx", "ipykernel"], + "docs": ["sphinx<7", "ipython", "nbsphinx", "ipykernel", "pydata_sphinx_theme"], } version = "" From 6f08b070e4e530f82ca526b3152bfb60d3188259 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 21 May 2023 04:16:46 -0400 Subject: [PATCH 109/214] refactor: Simplify _new_agent_reporter --- mesa/datacollection.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index fcaedc7c8c8..d58133f1ad8 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -36,7 +36,6 @@ """ import itertools import types -from functools import partial from operator import attrgetter import pandas as pd @@ -132,7 +131,10 @@ def _new_agent_reporter(self, name, reporter): """ if type(reporter) is str: attribute_name = reporter - reporter = partial(self._getattr, reporter) + + def reporter(agent): + return getattr(agent, attribute_name, None) + reporter.attribute_name = attribute_name self.agent_reporters[name] = reporter @@ -205,11 +207,6 @@ def add_table_row(self, table_name, row, ignore_missing=False): else: raise Exception("Could not insert row with missing column") - @staticmethod - def _getattr(name, _object): - """Turn around arguments of getattr to make it partially callable.""" - return getattr(_object, name, None) - def get_model_vars_dataframe(self): """Create a pandas DataFrame from the model variables. From f78f80f662ff73ec9e22d792eec5ffbe73176852 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 20 May 2023 20:17:33 -0400 Subject: [PATCH 110/214] feat: Implement exclude_none_values in DataCollector --- mesa/datacollection.py | 27 ++++++++++++++- mesa/model.py | 7 +++- tests/test_datacollector.py | 65 ++++++++++++++++++++++++++++++++++--- 3 files changed, 92 insertions(+), 7 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index d58133f1ad8..d383f018e0f 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -51,7 +51,13 @@ class DataCollector: one and stores the results. """ - def __init__(self, model_reporters=None, agent_reporters=None, tables=None): + def __init__( + self, + model_reporters=None, + agent_reporters=None, + tables=None, + exclude_none_values=False, + ): """Instantiate a DataCollector with lists of model and agent reporters. Both model_reporters and agent_reporters accept a dictionary mapping a variable name to either an attribute name, or a method. @@ -74,6 +80,8 @@ def __init__(self, model_reporters=None, agent_reporters=None, tables=None): model_reporters: Dictionary of reporter names and attributes/funcs agent_reporters: Dictionary of reporter names and attributes/funcs. tables: Dictionary of table names to lists of column names. + exclude_none_values: Boolean of whether to drop records which values + are None, in the final result. Notes: If you want to pickle your model you must not use lambda functions. @@ -97,6 +105,7 @@ class attributes of a model self.model_vars = {} self._agent_records = {} self.tables = {} + self.exclude_none_values = exclude_none_values if model_reporters is not None: for name, reporter in model_reporters.items(): @@ -151,7 +160,23 @@ def _new_table(self, table_name, table_columns): def _record_agents(self, model): """Record agents data in a mapping of functions and agents.""" rep_funcs = self.agent_reporters.values() + if self.exclude_none_values: + # Drop records which values are None. + + def get_reports(agent): + _prefix = (agent.model.schedule.steps, agent.unique_id) + reports = (rep(agent) for rep in rep_funcs) + reports_without_none = tuple(r for r in reports if r is not None) + if len(reports_without_none) == 0: + return None + return _prefix + reports_without_none + + agent_records = (get_reports(agent) for agent in model.schedule.agents) + agent_records_without_none = (r for r in agent_records if r is not None) + return agent_records_without_none + if all(hasattr(rep, "attribute_name") for rep in rep_funcs): + # This branch is for performance optimization purpose. prefix = ["model.schedule.steps", "unique_id"] attributes = [func.attribute_name for func in rep_funcs] get_reports = attrgetter(*prefix + attributes) diff --git a/mesa/model.py b/mesa/model.py index be6072663bf..15374f70796 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -66,7 +66,11 @@ def reset_randomizer(self, seed: int | None = None) -> None: self._seed = seed def initialize_data_collector( - self, model_reporters=None, agent_reporters=None, tables=None + self, + model_reporters=None, + agent_reporters=None, + tables=None, + exclude_none_values=False, ) -> None: if not hasattr(self, "schedule") or self.schedule is None: raise RuntimeError( @@ -80,6 +84,7 @@ def initialize_data_collector( model_reporters=model_reporters, agent_reporters=agent_reporters, tables=tables, + exclude_none_values=exclude_none_values, ) # Collect data for the first time during initialization. self.datacollector.collect(self) diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py index 7ba72c73df5..c44c708e31d 100644 --- a/tests/test_datacollector.py +++ b/tests/test_datacollector.py @@ -32,6 +32,14 @@ def write_final_values(self): self.model.datacollector.add_table_row("Final_Values", row) +class DifferentMockAgent(MockAgent): + # We define a different MockAgent to test for attributes that are present + # only in 1 type of agent, but not the other. + def __init__(self, unique_id, model, val=0): + super().__init__(unique_id, model, val=val) + self.val3 = val + 42 + + class MockModel(Model): """ Minimalistic model for testing purposes. @@ -39,13 +47,20 @@ class MockModel(Model): schedule = BaseScheduler(None) - def __init__(self): + def __init__(self, test_exclude_none_values=False): self.schedule = BaseScheduler(self) self.model_val = 100 - for i in range(10): - a = MockAgent(i, self, val=i) - self.schedule.add(a) + self.n = 10 + for i in range(self.n): + self.schedule.add(MockAgent(i, self, val=i)) + if test_exclude_none_values: + self.schedule.add(DifferentMockAgent(self.n + i, self, val=i)) + if test_exclude_none_values: + # Only DifferentMockAgent has val3. + agent_reporters = {"value": lambda a: a.val, "value3": "val3"} + else: + agent_reporters = {"value": lambda a: a.val, "value2": "val2"} self.initialize_data_collector( { "total_agents": lambda m: m.schedule.get_agent_count(), @@ -54,8 +69,9 @@ def __init__(self): "model_calc_comp": [self.test_model_calc_comp, [3, 4]], "model_calc_fail": [self.test_model_calc_comp, [12, 0]], }, - {"value": lambda a: a.val, "value2": "val2"}, + agent_reporters, {"Final_Values": ["agent_id", "final_value"]}, + exclude_none_values=test_exclude_none_values, ) def test_model_calc_comp(self, input1, input2): @@ -195,5 +211,44 @@ def test_initialize_before_agents_added_to_scheduler(self): ) +class TestDataCollectorExcludeNone(unittest.TestCase): + def setUp(self): + """ + Create the model and run it a set number of steps. + """ + self.model = MockModel(test_exclude_none_values=True) + for i in range(7): + if i == 4: + self.model.schedule.remove(self.model.schedule._agents[3]) + self.model.step() + + def test_agent_records(self): + """ + Test agent-level variable collection. + """ + data_collector = self.model.datacollector + agent_table = data_collector.get_agent_vars_dataframe() + + assert len(data_collector._agent_records) == 8 + for step, records in data_collector._agent_records.items(): + if step < 5: + assert len(records) == 20 + else: + assert len(records) == 19 + + for values in records: + agent_id = values[1] + if agent_id < self.model.n: + assert len(values) == 3 + else: + # Agents with agent_id >= self.model.n are + # DifferentMockAgent, which additionally contains val3. + assert len(values) == 4 + + assert "value" in list(agent_table.columns) + assert "value2" not in list(agent_table.columns) + assert "value3" in list(agent_table.columns) + + if __name__ == "__main__": unittest.main() From 1df1ffebf09dd78551cec57b5706d07ce8dab62c Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 29 May 2023 06:42:23 -0400 Subject: [PATCH 111/214] doc: Fix typo in overview.rst Fixes #1705 --- docs/overview.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/overview.rst b/docs/overview.rst index 9c1e3b0c080..7dd52cb5c23 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -43,7 +43,7 @@ The skeleton of a model might look like this: class MyModel(mesa.Model): def __init__(self, n_agents): super().__init__() - self.schedule = mesa.timeRandomActivation(self) + self.schedule = mesa.time.RandomActivation(self) self.grid = mesa.space.MultiGrid(10, 10, torus=True) for i in range(n_agents): a = MyAgent(i, self) From 1078de87f1c0bbf7ef7bee611b477cd3cc29b022 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 6 Jun 2023 04:53:50 +0000 Subject: [PATCH 112/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.3.2 → v3.4.0](https://github.com/asottile/pyupgrade/compare/v3.3.2...v3.4.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 e3deaf9500a..93e82fc2f96 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.3.2 + rev: v3.4.0 hooks: - id: pyupgrade args: [--py38-plus] From ea7163ecceacc9fff75b3c8827f4f6028bb559a2 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 3 Jun 2023 21:46:08 -0400 Subject: [PATCH 113/214] refactor: Abstract out code to get shuffled agent keys --- mesa/time.py | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/mesa/time.py b/mesa/time.py index b079e1439f0..648e1cce0dc 100644 --- a/mesa/time.py +++ b/mesa/time.py @@ -91,15 +91,21 @@ def get_agent_count(self) -> int: def agents(self) -> list[Agent]: return list(self._agents.values()) + def get_agent_keys(self, shuffle: bool = False) -> list[int]: + # 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 shuffle: + self.model.random.shuffle(agent_keys) + return agent_keys + 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. """ # 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: - self.model.random.shuffle(agent_keys) + # it's necessary for the keys view to be a list. + agent_keys = self.get_agent_keys(shuffled) for agent_key in agent_keys: if agent_key in self._agents: @@ -137,14 +143,12 @@ class SimultaneousActivation(BaseScheduler): def step(self) -> None: """Step all agents, then advance them.""" - # 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()) + agent_keys = self.get_agent_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()) + agent_keys = self.get_agent_keys() for agent_key in agent_keys: self._agents[agent_key].advance() self.steps += 1 @@ -190,19 +194,15 @@ def __init__( def step(self) -> None: """Executes all the stages for all agents.""" # 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: - self.model.random.shuffle(agent_keys) + # it's necessary for the keys view to be a list. + agent_keys = self.get_agent_keys(self.shuffle) for stage in self.stage_list: for agent_key in agent_keys: 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()) - if self.shuffle_between_stages: - self.model.random.shuffle(agent_keys) + agent_keys = self.get_agent_keys(self.shuffle_between_stages) self.time += self.stage_time self.steps += 1 From 42fc96ee4d78b0dcc1126c6ea998ccc4a498d4d4 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 3 Jun 2023 21:58:12 -0400 Subject: [PATCH 114/214] feat: Implement do_each --- mesa/time.py | 34 +++++++++++++--------------------- 1 file changed, 13 insertions(+), 21 deletions(-) diff --git a/mesa/time.py b/mesa/time.py index 648e1cce0dc..d8ae8183ad3 100644 --- a/mesa/time.py +++ b/mesa/time.py @@ -28,7 +28,7 @@ from collections import defaultdict # mypy -from typing import Iterator, Union +from typing import Union from mesa.agent import Agent from mesa.model import Model @@ -78,8 +78,9 @@ def remove(self, agent: Agent) -> None: def step(self) -> None: """Execute the step of all the agents, one at a time.""" - for agent in self.agent_buffer(shuffled=False): - agent.step() + # To be able to remove and/or add agents during stepping + # it's necessary for the keys view to be a list. + self.do_each("step") self.steps += 1 self.time += 1 @@ -99,17 +100,12 @@ def get_agent_keys(self, shuffle: bool = False) -> list[int]: self.model.random.shuffle(agent_keys) return agent_keys - 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. - """ - # To be able to remove and/or add agents during stepping - # it's necessary for the keys view to be a list. - agent_keys = self.get_agent_keys(shuffled) - + def do_each(self, method, agent_keys=None): + if agent_keys is None: + agent_keys = self.get_agent_keys() for agent_key in agent_keys: if agent_key in self._agents: - yield self._agents[agent_key] + getattr(self._agents[agent_key], method)() class RandomActivation(BaseScheduler): @@ -127,8 +123,8 @@ def step(self) -> None: random order. """ - for agent in self.agent_buffer(shuffled=True): - agent.step() + agent_keys = self.get_agent_keys(shuffle=True) + self.do_each("step", agent_keys=agent_keys) self.steps += 1 self.time += 1 @@ -144,13 +140,11 @@ class SimultaneousActivation(BaseScheduler): def step(self) -> None: """Step all agents, then advance them.""" agent_keys = self.get_agent_keys() - for agent_key in agent_keys: - self._agents[agent_key].step() + self.do_each("step", agent_keys=agent_keys) # We recompute the keys because some agents might have been removed in # the previous loop. agent_keys = self.get_agent_keys() - for agent_key in agent_keys: - self._agents[agent_key].advance() + self.do_each("advance", agent_keys=agent_keys) self.steps += 1 self.time += 1 @@ -197,9 +191,7 @@ def step(self) -> None: # it's necessary for the keys view to be a list. agent_keys = self.get_agent_keys(self.shuffle) for stage in self.stage_list: - for agent_key in agent_keys: - if agent_key in self._agents: - getattr(self._agents[agent_key], stage)() # Run stage + self.do_each(stage, agent_keys=agent_keys) # We recompute the keys because some agents might have been removed # in the previous loop. agent_keys = self.get_agent_keys(self.shuffle_between_stages) From dddb8bd08c83cf6677c80cf3f2a0a8c9626f9184 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 3 Jun 2023 22:08:27 -0400 Subject: [PATCH 115/214] refactor: Simplify usage of do_each --- mesa/time.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/mesa/time.py b/mesa/time.py index d8ae8183ad3..da2ded48349 100644 --- a/mesa/time.py +++ b/mesa/time.py @@ -100,9 +100,11 @@ def get_agent_keys(self, shuffle: bool = False) -> list[int]: self.model.random.shuffle(agent_keys) return agent_keys - def do_each(self, method, agent_keys=None): + def do_each(self, method, agent_keys=None, shuffle=False): if agent_keys is None: agent_keys = self.get_agent_keys() + if shuffle: + self.model.random.shuffle(agent_keys) for agent_key in agent_keys: if agent_key in self._agents: getattr(self._agents[agent_key], method)() @@ -123,8 +125,7 @@ def step(self) -> None: random order. """ - agent_keys = self.get_agent_keys(shuffle=True) - self.do_each("step", agent_keys=agent_keys) + self.do_each("step", shuffle=True) self.steps += 1 self.time += 1 @@ -139,12 +140,11 @@ class SimultaneousActivation(BaseScheduler): def step(self) -> None: """Step all agents, then advance them.""" - agent_keys = self.get_agent_keys() - self.do_each("step", agent_keys=agent_keys) - # We recompute the keys because some agents might have been removed in + self.do_each("step") + # do_each recomputes the agent_keys from scratch whenever it is called. + # It can handle the case when some agents might have been removed in # the previous loop. - agent_keys = self.get_agent_keys() - self.do_each("advance", agent_keys=agent_keys) + self.do_each("advance") self.steps += 1 self.time += 1 From 000408ddc9fe344d81ed712032a8653f175bb77b Mon Sep 17 00:00:00 2001 From: subhamonsey Date: Sat, 3 Jun 2023 12:58:44 +0530 Subject: [PATCH 116/214] Add feature in mesa.time StagedActivation Added a feature which enables implementing Model Level Functions in Staged Activation. Add the prefix "model." before function name to indicate its a model level function. Also added a section in "Useful Snippets" in docs. Included unit tests. Signed-off-by: subhamonsey --- docs/useful-snippets/snippets.rst | 10 ++++++++++ mesa/time.py | 7 +++++-- tests/test_time.py | 16 ++++++++++------ 3 files changed, 25 insertions(+), 8 deletions(-) diff --git a/docs/useful-snippets/snippets.rst b/docs/useful-snippets/snippets.rst index 4c7e8e418f1..728c4e65f19 100644 --- a/docs/useful-snippets/snippets.rst +++ b/docs/useful-snippets/snippets.rst @@ -12,6 +12,16 @@ If you have `Multiple` type agents and one of them has time attribute you can st if self.model.schedule.time in self.discrete_time: self.model.space.move_agent(self, new_pos) +Implementing Model Level Functions in Staged Activation +------------------------------------------------------- +In staged activation, if you may want a function to be implemented only on the model level and not at the level of agents. +For such functions, include the prefix "model." before the model function name, when defining the function list. +For example, consider a central employment exchange which adjust the wage rate common to all laborers +in the direction of excess demand. + +.. code:: python + stage_list=[Send_Labour_Supply, Send_Labour_Demand, model.Adjust_Wage_Rate] + self.schedule = StagedActivation(self,stage_list,shuffle=True) Using ```numpy.random``` ------------- diff --git a/mesa/time.py b/mesa/time.py index da2ded48349..f0816cd68dc 100644 --- a/mesa/time.py +++ b/mesa/time.py @@ -63,7 +63,7 @@ def add(self, agent: Agent) -> None: """ if agent.unique_id in self._agents: raise Exception( - f"Agent with unique id {repr(agent.unique_id)} already added to scheduler" + f"Agent with unique id {agent.unique_id!r} already added to scheduler" ) self._agents[agent.unique_id] = agent @@ -191,7 +191,10 @@ def step(self) -> None: # it's necessary for the keys view to be a list. agent_keys = self.get_agent_keys(self.shuffle) for stage in self.stage_list: - self.do_each(stage, agent_keys=agent_keys) + if stage.startswith("model."): + getattr(self.model, stage[6:])() + else: + self.do_each(stage, agent_keys=agent_keys) # We recompute the keys because some agents might have been removed # in the previous loop. agent_keys = self.get_agent_keys(self.shuffle_between_stages) diff --git a/tests/test_time.py b/tests/test_time.py index 3014a290fbb..60dbf506658 100644 --- a/tests/test_time.py +++ b/tests/test_time.py @@ -75,7 +75,7 @@ def __init__(self, shuffle=False, activation=STAGED, enable_kill_other_agent=Fal # Make scheduler if activation == STAGED: - model_stages = ["stage_one", "stage_two"] + model_stages = ["stage_one", "model.model_stage", "stage_two"] self.schedule = StagedActivation(self, model_stages, shuffle=shuffle) elif activation == RANDOM: self.schedule = RandomActivation(self) @@ -94,13 +94,16 @@ def __init__(self, shuffle=False, activation=STAGED, enable_kill_other_agent=Fal def step(self): self.schedule.step() + def model_stage(self): + self.log.append("model_stage") + class TestStagedActivation(TestCase): """ Test the staged activation. """ - expected_output = ["A_1", "B_1", "A_2", "B_2"] + expected_output = ["A_1", "B_1", "model_stage", "A_2", "B_2"] def test_no_shuffle(self): """ @@ -109,7 +112,7 @@ def test_no_shuffle(self): model = MockModel(shuffle=False) model.step() model.step() - assert all(i == j for i, j in zip(model.log[:4], model.log[4:])) + assert all(i == j for i, j in zip(model.log[:5], model.log[5:])) def test_shuffle(self): """ @@ -119,8 +122,9 @@ def test_shuffle(self): model.step() for output in self.expected_output[:2]: assert output in model.log[:2] - for output in self.expected_output[2:]: - assert output in model.log[2:] + for output in self.expected_output[3:]: + assert output in model.log[3:] + assert self.expected_output[2] == model.log[2] def test_shuffle_shuffles_agents(self): model = MockModel(shuffle=True) @@ -146,7 +150,7 @@ def test_intrastep_remove(self): """ model = MockModel(shuffle=True, enable_kill_other_agent=True) model.step() - assert len(model.log) == 2 + assert len(model.log) == 3 def test_add_existing_agent(self): model = MockModel() From 1de1627f74f67099ef37d54344b3b1eb7279b49f Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 9 May 2023 09:01:47 -0400 Subject: [PATCH 117/214] breaking: Remove deprecated BatchRunner --- docs/overview.rst | 14 +- mesa/batchrunner.py | 572 +----------------------------------- tests/test_batch_run.py | 15 +- tests/test_batchrunner.py | 328 --------------------- tests/test_batchrunnerMP.py | 271 ----------------- tests/test_visualization.py | 18 +- 6 files changed, 34 insertions(+), 1184 deletions(-) delete mode 100644 tests/test_batchrunner.py delete mode 100644 tests/test_batchrunnerMP.py diff --git a/docs/overview.rst b/docs/overview.rst index 7dd52cb5c23..b2b9ddfc6e6 100644 --- a/docs/overview.rst +++ b/docs/overview.rst @@ -103,16 +103,18 @@ The data collector will collect the specified model- and agent-level data at eac agent_df = model.dc.get_agent_vars_dataframe() -To batch-run the model while varying, for example, the n_agents parameter, you'd use the batchrunner: +To batch-run the model while varying, for example, the n_agents parameter, you'd use the `batch_run` function: .. code:: python - from mesa.batchrunner import BatchRunner + import mesa parameters = {"n_agents": range(1, 20)} - batch_run = BatchRunner(MyModel, parameters, max_steps=10, - model_reporters={"n_agents": lambda m: m.schedule.get_agent_count()}) - batch_run.run_all() + mesa.batch_run( + MyModel, + parameters, + max_steps=10, + ) As with the data collector, once the runs are all over, you can extract the data as a data frame. @@ -152,5 +154,3 @@ To quickly spin up a model visualization, you might do something like: server.launch() This will launch the browser-based visualization, on the default port 8521. - - diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index 338f182dee6..b4a96aebcc0 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -1,15 +1,6 @@ -""" -Batchrunner -=========== - -A single class to manage a batch run or parameter sweep of a given model. -""" -import copy import itertools -import random from functools import partial -from itertools import count, product -from multiprocessing import Pool, cpu_count +from multiprocessing import Pool from typing import ( Any, Dict, @@ -21,9 +12,7 @@ Type, Union, ) -from warnings import warn -import pandas as pd from tqdm import tqdm from mesa.model import Model @@ -209,562 +198,3 @@ def _collect_data( agent_dict.update(zip(dc.agent_reporters, data[2:])) all_agents_data.append(agent_dict) return model_data, all_agents_data - - -class ParameterError(TypeError): - MESSAGE = ( - "Parameters must map a name to a value. " - "These names did not match parameters: {}" - ) - - def __init__(self, bad_names): - self.bad_names = bad_names - - def __str__(self): - return self.MESSAGE.format(self.bad_names) - - -class VariableParameterError(ParameterError): - MESSAGE = ( - "Variable_parameters must map a name to a sequence of values. " - "These parameters were given with non-sequence values: {}" - ) - - -class FixedBatchRunner: - """This class is instantiated with a model class, and model parameters - associated with one or more values. It is also instantiated with model and - agent-level reporters, dictionaries mapping a variable name to a function - which collects some data from the model or its agents at the end of the run - and stores it. - - 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__( - self, - model_cls, - parameters_list=None, - fixed_parameters=None, - iterations=1, - max_steps=1000, - model_reporters=None, - agent_reporters=None, - display_progress=True, - ): - """Create a new BatchRunner for a given model with the given - parameters. - - Args: - model_cls: The class of model to batch-run. - parameters_list: A list of dictionaries of parameter sets. - The model will be run with dictionary of parameters. - For example, given parameters_list of - [{"homophily": 3, "density": 0.8, "minority_pc": 0.2}, - {"homophily": 2, "density": 0.9, "minority_pc": 0.1}, - {"homophily": 4, "density": 0.6, "minority_pc": 0.5}] - 3 models will be run, one for each provided set of parameters. - fixed_parameters: Dictionary of parameters that stay same through - all batch runs. For example, given fixed_parameters of - {"constant_parameter": 3}, - every instantiated model will be passed constant_parameter=3 - as a kwarg. - iterations: The total number of times to run the model for each set - of parameters. - max_steps: Upper limit of steps above which each run will be halted - if it hasn't halted on its own. - model_reporters: The dictionary of variables to collect on each run - at the end, with variable names mapped to a function to collect - them. For example: - {"agent_count": lambda m: m.schedule.get_agent_count()} - agent_reporters: Like model_reporters, but each variable is now - 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: - parameters_list = [] - self.parameters_list = list(parameters_list) - self.fixed_parameters = fixed_parameters or {} - self._include_fixed = len(self.fixed_parameters) > 0 - self.iterations = iterations - self.max_steps = max_steps - - for params in self.parameters_list: - if list(params) != list(self.parameters_list[0]): - msg = "parameter names in parameters_list are not equal across the list" - raise ValueError(msg) - - self.model_reporters = model_reporters - self.agent_reporters = agent_reporters - - if self.model_reporters: - self.model_vars = {} - - if self.agent_reporters: - self.agent_vars = {} - - self.datacollector_model_reporters = {} - self.datacollector_agent_reporters = {} - - self.display_progress = display_progress - - def _make_model_args(self): - """Prepare all combinations of parameter values for `run_all` - - Returns: - Tuple with the form: - (total_iterations, all_kwargs, all_param_values) - """ - total_iterations = self.iterations - all_kwargs = [] - all_param_values = [] - - count = len(self.parameters_list) - if count: - for params in self.parameters_list: - kwargs = params.copy() - kwargs.update(self.fixed_parameters) - all_kwargs.append(kwargs) - all_param_values.append(list(params.values())) - - elif len(self.fixed_parameters): - count = 1 - kwargs = self.fixed_parameters.copy() - all_kwargs.append(kwargs) - all_param_values.append(list(kwargs.values())) - - total_iterations *= count - - return total_iterations, all_kwargs, all_param_values - - def run_all(self): - """Run the model at all parameter combinations and store results.""" - run_count = count() - total_iterations, all_kwargs, all_param_values = self._make_model_args() - - with tqdm(total_iterations, disable=not self.display_progress) as pbar: - for i, kwargs in enumerate(all_kwargs): - param_values = all_param_values[i] - for _ in range(self.iterations): - self.run_iteration(kwargs, param_values, next(run_count)) - pbar.update() - - def run_iteration(self, kwargs, param_values, run_count): - model = self.model_cls(**kwargs) - results = self.run_model(model) - if param_values is not None: - model_key = (*tuple(param_values), run_count) - else: - model_key = (run_count,) - - if self.model_reporters: - self.model_vars[model_key] = self.collect_model_vars(model) - if self.agent_reporters: - agent_vars = self.collect_agent_vars(model) - for agent_id, reports in agent_vars.items(): - agent_key = (*model_key, agent_id) - self.agent_vars[agent_key] = reports - # Collects data from datacollector object in model - if results is not None: - if results.model_reporters is not None: - self.datacollector_model_reporters[ - model_key - ] = results.get_model_vars_dataframe() - if results.agent_reporters is not None: - self.datacollector_agent_reporters[ - model_key - ] = results.get_agent_vars_dataframe() - - return ( - getattr(self, "model_vars", None), - getattr(self, "agent_vars", None), - getattr(self, "datacollector_model_reporters", None), - getattr(self, "datacollector_agent_reporters", None), - ) - - def run_model(self, model): - """Run a model object to completion, or until reaching max steps. - - 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() - - if hasattr(model, "datacollector"): - return model.datacollector - else: - return None - - def collect_model_vars(self, model): - """Run reporters and collect model-level variables.""" - 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 = { - var: getattr(agent, reporter) - for var, reporter in self.agent_reporters.items() - } - agent_vars[agent.unique_id] = agent_record - return agent_vars - - def get_model_vars_dataframe(self): - """Generate a pandas DataFrame from the model-level variables - collected. - """ - - return self._prepare_report_table(self.model_vars) - - def get_agent_vars_dataframe(self): - """Generate a pandas DataFrame from the agent-level variables - collected. - """ - - return self._prepare_report_table(self.agent_vars, extra_cols=["AgentId"]) - - def get_collector_model(self): - """ - Passes pandas dataframes from datacollector module in dictionary format of model reporters - :return: dict {(Param1, Param2,...,iteration): } - """ - - return self.datacollector_model_reporters - - def get_collector_agents(self): - """ - Passes pandas dataframes from datacollector module in dictionary format of agent reporters - :return: dict {(Param1, Param2,...,iteration): } - """ - return self.datacollector_agent_reporters - - def _prepare_report_table(self, vars_dict, extra_cols=None): - """ - Creates a dataframe from collected records and sorts it using 'Run' - column as a key. - """ - extra_cols = ["Run"] + (extra_cols or []) - index_cols = [] - if self.parameters_list: - index_cols = list(self.parameters_list[0].keys()) - index_cols += extra_cols - - records = [] - for param_key, values in vars_dict.items(): - record = dict(zip(index_cols, param_key)) - record.update(values) - records.append(record) - - df = pd.DataFrame(records) - rest_cols = set(df.columns) - set(index_cols) - ordered = df[index_cols + sorted(rest_cols)] - ordered.sort_values(by="Run", inplace=True) - if self._include_fixed: - for param, val in self.fixed_parameters.items(): - # avoid error when val is an iterable - vallist = [val for i in range(ordered.shape[0])] - ordered[param] = vallist - return ordered - - -class ParameterProduct: - def __init__(self, variable_parameters): - self.param_names, self.param_lists = zip( - *(copy.deepcopy(variable_parameters)).items() - ) - self._product = product(*self.param_lists) - - def __iter__(self): - return self - - def __next__(self): - return dict(zip(self.param_names, next(self._product))) - - -# Roughly inspired by sklearn.model_selection.ParameterSampler. Does not handle -# distributions, only lists. -class ParameterSampler: - def __init__(self, parameter_lists, n, random_state=None): - self.param_names, self.param_lists = zip( - *(copy.deepcopy(parameter_lists)).items() - ) - self.n = n - if random_state is None: - self.random_state = random.Random() - elif isinstance(random_state, int): - self.random_state = random.Random(random_state) - else: - self.random_state = random_state - self.count = 0 - - def __iter__(self): - return self - - def __next__(self): - self.count += 1 - if self.count <= self.n: - return dict( - zip( - self.param_names, - [self.random_state.choice(p_list) for p_list in self.param_lists], - ) - ) - raise StopIteration() - - -class BatchRunner(FixedBatchRunner): - """DEPRECATION WARNING: BatchRunner Class has been replaced batch_run function - This class is instantiated with a model class, and model parameters - associated with one or more values. It is also instantiated with model and - agent-level reporters, dictionaries mapping a variable name to a function - which collects some data from the model or its agents at the end of the run - and stores it. - - 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__( - self, - model_cls, - variable_parameters=None, - fixed_parameters=None, - iterations=1, - max_steps=1000, - model_reporters=None, - agent_reporters=None, - display_progress=True, - ): - """Create a new BatchRunner for a given model with the given - parameters. - - Args: - model_cls: The class of model to batch-run. - variable_parameters: Dictionary of parameters to lists of values. - The model will be run with every combo of these parameters. - For example, given variable_parameters of - {"param_1": range(5), - "param_2": [1, 5, 10]} - models will be run with {param_1=1, param_2=1}, - {param_1=2, param_2=1}, ..., {param_1=4, param_2=10}. - fixed_parameters: Dictionary of parameters that stay same through - all batch runs. For example, given fixed_parameters of - {"constant_parameter": 3}, - every instantiated model will be passed constant_parameter=3 - as a kwarg. - iterations: The total number of times to run the model for each - combination of parameters. - max_steps: Upper limit of steps above which each run will be halted - if it hasn't halted on its own. - model_reporters: The dictionary of variables to collect on each run - at the end, with variable names mapped to a function to collect - them. For example: - {"agent_count": lambda m: m.schedule.get_agent_count()} - agent_reporters: Like model_reporters, but each variable is now - 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.", - DeprecationWarning, - 2, - ) - if variable_parameters is None: - super().__init__( - model_cls, - variable_parameters, - fixed_parameters, - iterations, - max_steps, - model_reporters, - agent_reporters, - display_progress, - ) - else: - super().__init__( - model_cls, - ParameterProduct(variable_parameters), - fixed_parameters, - iterations, - max_steps, - model_reporters, - agent_reporters, - display_progress, - ) - - -class BatchRunnerMP(BatchRunner): # pragma: no cover - """DEPRECATION WARNING: BatchRunner class has been replaced by batch_run - Child class of BatchRunner, extended with multiprocessing support.""" - - def __init__(self, model_cls, nr_processes=None, **kwargs): - """Create a new BatchRunnerMP for a given model with the given - parameters. - - model_cls: The class of model to batch-run. - nr_processes: int - the number of separate processes the BatchRunner - should start, all running in parallel. - kwargs: the kwargs required for the parent BatchRunner class - """ - warn( - "BatchRunnerMP class has been replaced by batch_run function. Please see documentation.", - DeprecationWarning, - 2, - ) - if nr_processes is None: - # identify the number of processors available on users machine - available_processors = cpu_count() - self.processes = available_processors - print(f"BatchRunner MP will use {self.processes} processors.") - else: - self.processes = nr_processes - - super().__init__(model_cls, **kwargs) - self.pool = Pool(self.processes) - - def _make_model_args_mp(self): - """Prepare all combinations of parameter values for `run_all` - Due to multiprocessing requirements of @StaticMethod takes different input, hence the similar function - Returns: - List of list with the form: - [[model_object, dictionary_of_kwargs, max_steps, iterations]] - """ - total_iterations = self.iterations - all_kwargs = [] - - count = len(self.parameters_list) - if count: - for params in self.parameters_list: - kwargs = params.copy() - kwargs.update(self.fixed_parameters) - # run each iterations specific number of times - for iter in range(self.iterations): - kwargs_repeated = kwargs.copy() - all_kwargs.append( - [self.model_cls, kwargs_repeated, self.max_steps, iter] - ) - - elif len(self.fixed_parameters): - count = 1 - kwargs = self.fixed_parameters.copy() - all_kwargs.append(kwargs) - - total_iterations *= count - - return all_kwargs, total_iterations - - @staticmethod - def _run_wrappermp(iter_args): - """ - Based on requirement of Python multiprocessing requires @staticmethod decorator; - this is primarily to ensure functionality on Windows OS and does not impact MAC or Linux distros - - :param iter_args: List of arguments for model run - iter_args[0] = model object - iter_args[1] = key word arguments needed for model object - iter_args[2] = maximum number of steps for model - iter_args[3] = number of time to run model for stochastic/random variation with same parameters - :return: - tuple of param values which serves as a unique key for model results - model object - """ - - model_i = iter_args[0] - kwargs = iter_args[1] - max_steps = iter_args[2] - iteration = iter_args[3] - - # instantiate version of model with correct parameters - model = model_i(**kwargs) - while model.running and model.schedule.steps < max_steps: - model.step() - - # add iteration number to dictionary to make unique_key - kwargs["iteration"] = iteration - - # convert kwargs dict to tuple to make consistent - param_values = tuple(kwargs.values()) - - return param_values, model - - def _result_prep_mp(self, results): - """ - Helper Function - :param results: Takes results dictionary from Processpool and single processor debug run and fixes format to - make compatible with BatchRunner Output - :updates model_vars and agents_vars so consistent across all batchrunner - """ - # Take results and convert to dictionary so dataframe can be called - for model_key, model in results.items(): - if self.model_reporters: - self.model_vars[model_key] = self.collect_model_vars(model) - if self.agent_reporters: - agent_vars = self.collect_agent_vars(model) - for agent_id, reports in agent_vars.items(): - agent_key = (*model_key, agent_id) - self.agent_vars[agent_key] = reports - if hasattr(model, "datacollector"): - if model.datacollector.model_reporters is not None: - self.datacollector_model_reporters[ - model_key - ] = model.datacollector.get_model_vars_dataframe() - if model.datacollector.agent_reporters is not None: - self.datacollector_agent_reporters[ - model_key - ] = model.datacollector.get_agent_vars_dataframe() - - # Make results consistent - if len(self.datacollector_model_reporters) == 0: - self.datacollector_model_reporters = None - if len(self.datacollector_agent_reporters) == 0: - self.datacollector_agent_reporters = None - - def run_all(self): - """ - Run the model at all parameter combinations and store results, - overrides run_all from BatchRunner. - """ - - run_iter_args, total_iterations = self._make_model_args_mp() - # register the process pool and init a queue - # store results in ordered dictionary - results = {} - - if self.processes > 1: - with tqdm(total_iterations, disable=not self.display_progress) as pbar: - for params, model in self.pool.imap_unordered( - self._run_wrappermp, run_iter_args - ): - results[params] = model - pbar.update() - - self._result_prep_mp(results) - # For debugging model due to difficulty of getting errors during multiprocessing - else: - for run in run_iter_args: - params, model_data = self._run_wrappermp(run) - results[params] = model_data - - self._result_prep_mp(results) - - # Close multi-processing - self.pool.close() - - return ( - getattr(self, "model_vars", None), - getattr(self, "agent_vars", None), - getattr(self, "datacollector_model_reporters", None), - getattr(self, "datacollector_agent_reporters", None), - ) diff --git a/tests/test_batch_run.py b/tests/test_batch_run.py index fffd422d077..31efb87dd2d 100644 --- a/tests/test_batch_run.py +++ b/tests/test_batch_run.py @@ -1,5 +1,6 @@ +import mesa from mesa.agent import Agent -from mesa.batchrunner import _make_model_kwargs, batch_run +from mesa.batchrunner import _make_model_kwargs from mesa.datacollection import DataCollector from mesa.model import Model from mesa.time import BaseScheduler @@ -87,7 +88,7 @@ def step(self): def test_batch_run(): - result = batch_run(MockModel, {}, number_processes=2) + result = mesa.batch_run(MockModel, {}, number_processes=2) assert result == [ { "RunId": 0, @@ -120,7 +121,7 @@ def test_batch_run(): def test_batch_run_with_params(): - batch_run( + mesa.batch_run( MockModel, { "variable_model_params": range(5), @@ -131,7 +132,9 @@ def test_batch_run_with_params(): def test_batch_run_no_agent_reporters(): - result = batch_run(MockModel, {"enable_agent_reporters": False}, number_processes=2) + result = mesa.batch_run( + MockModel, {"enable_agent_reporters": False}, number_processes=2 + ) print(result) assert result == [ { @@ -145,11 +148,11 @@ def test_batch_run_no_agent_reporters(): def test_batch_run_single_core(): - batch_run(MockModel, {}, number_processes=1, iterations=10) + mesa.batch_run(MockModel, {}, number_processes=1, iterations=10) def test_batch_run_unhashable_param(): - result = batch_run( + result = mesa.batch_run( MockModel, { "n_agents": 2, diff --git a/tests/test_batchrunner.py b/tests/test_batchrunner.py deleted file mode 100644 index 761598b477c..00000000000 --- a/tests/test_batchrunner.py +++ /dev/null @@ -1,328 +0,0 @@ -""" -Test the BatchRunner -""" -import unittest -from functools import reduce -from operator import mul - -from mesa import Agent, Model -from mesa.batchrunner import ( - BatchRunner, - FixedBatchRunner, - ParameterProduct, - ParameterSampler, -) -from mesa.datacollection import DataCollector -from mesa.time import BaseScheduler - -NUM_AGENTS = 7 - - -class MockAgent(Agent): - """ - Minimalistic agent implementation for testing purposes - """ - - def __init__(self, unique_id, model, val): - super().__init__(unique_id, model) - self.unique_id = unique_id - self.val = val - self.local = 0 - - def step(self): - self.val += 1 - self.local += 0.25 - - -class MockModel(Model): - """ - Minimalistic model for testing purposes - """ - - def __init__( - self, - variable_model_param=None, - variable_agent_param=None, - fixed_model_param=None, - schedule=None, - **kwargs - ): - super().__init__() - self.schedule = BaseScheduler(None) if schedule is None else schedule - self.variable_model_param = variable_model_param - self.variable_agent_param = variable_agent_param - self.fixed_model_param = fixed_model_param - self.n_agents = kwargs.get("n_agents", NUM_AGENTS) - self.datacollector = DataCollector( - model_reporters={"reported_model_param": self.get_local_model_param}, - agent_reporters={"agent_id": "unique_id", "agent_local": "local"}, - ) - self.running = True - self.init_agents() - - def init_agents(self): - if self.variable_agent_param is None: - agent_val = 1 - else: - agent_val = self.variable_agent_param - for i in range(self.n_agents): - self.schedule.add(MockAgent(i, self, agent_val)) - - def get_local_model_param(self): - return 42 - - def step(self): - self.datacollector.collect(self) - self.schedule.step() - - -class MockMixedModel(Model): - def __init__(self, **other_params): - super().__init__() - self.variable_name = other_params.get("variable_name", 42) - self.fixed_name = other_params.get("fixed_name") - self.running = True - self.schedule = BaseScheduler(None) - self.schedule.add(MockAgent(1, self, 0)) - - def step(self): - self.schedule.step() - - -class TestBatchRunner(unittest.TestCase): - """ - Test that BatchRunner is running batches - """ - - def setUp(self): - self.mock_model = MockModel - self.model_reporters = { - "reported_variable_value": lambda m: m.variable_model_param, - "reported_fixed_value": lambda m: m.fixed_model_param, - } - self.agent_reporters = {"agent_id": "unique_id", "agent_val": "val"} - self.variable_params = { - "variable_model_param": range(3), - "variable_agent_param": [1, 8], - } - self.fixed_params = None - self.iterations = 17 - self.max_steps = 3 - - def launch_batch_processing(self): - batch = BatchRunner( - self.mock_model, - variable_parameters=self.variable_params, - fixed_parameters=self.fixed_params, - iterations=self.iterations, - max_steps=self.max_steps, - model_reporters=self.model_reporters, - agent_reporters=self.agent_reporters, - ) - batch.run_all() - return batch - - def launch_batch_processing_fixed(self): - # Adding second batchrun to test fixed params increase coverage - batch = BatchRunner( - self.mock_model, - fixed_parameters={"fixed": "happy"}, - iterations=4, - max_steps=self.max_steps, - model_reporters=self.model_reporters, - agent_reporters=None, - ) - - batch.run_all() - return batch - - def launch_batch_processing_fixed_list(self): - batch = FixedBatchRunner( - self.mock_model, - parameters_list=self.variable_params, - fixed_parameters=self.fixed_params, - iterations=self.iterations, - max_steps=self.max_steps, - model_reporters=self.model_reporters, - agent_reporters=self.agent_reporters, - ) - batch.run_all() - return batch - - @property - def model_runs(self): - """ - Returns total number of batch runner's iterations. - """ - if isinstance(self.variable_params, list): - return len(self.variable_params) * self.iterations - else: - return ( - reduce(mul, map(len, self.variable_params.values())) * self.iterations - ) - - def test_model_level_vars(self): - """ - Test that model-level variable collection is of the correct size - """ - batch = self.launch_batch_processing() - model_vars = batch.get_model_vars_dataframe() - model_collector = batch.get_collector_model() - expected_cols = ( - len(self.variable_params) + len(self.model_reporters) + 1 - ) # extra column with run index - self.assertEqual(model_vars.shape, (self.model_runs, expected_cols)) - self.assertEqual(len(model_collector.keys()), self.model_runs) - for var, values in self.variable_params.items(): - self.assertEqual(set(model_vars[var].unique()), set(values)) - if self.fixed_params: - for var, values in self.fixed_params.items(): - self.assertEqual(set(model_vars[var].unique()), set(values)) - - def test_agent_level_vars(self): - """ - Test that agent-level variable collection is of the correct size - """ - batch = self.launch_batch_processing() - agent_vars = batch.get_agent_vars_dataframe() - agent_collector = batch.get_collector_agents() - # extra columns with run index and agentId - expected_cols = len(self.variable_params) + len(self.agent_reporters) + 2 - self.assertEqual( - agent_vars.shape, (self.model_runs * NUM_AGENTS, expected_cols) - ) - assert "agent_val" in list(agent_vars.columns) - assert "val_non_existent" not in list(agent_vars.columns) - assert "agent_id" in list(agent_collector[(0, 1, 1)].columns) - assert "Step" in list(agent_collector[(0, 1, 5)].index.names) - assert "nose" not in list(agent_collector[(0, 1, 1)].columns) - for var, values in self.variable_params.items(): - self.assertEqual(set(agent_vars[var].unique()), set(values)) - - self.assertEqual( - agent_collector[(0, 1, 0)].shape, (NUM_AGENTS * self.max_steps, 2) - ) - - with self.assertRaises(KeyError): - agent_collector[(900, "k", 3)] - - def test_model_with_fixed_parameters_as_kwargs(self): - """ - Test that model with fixed parameters passed like kwargs is - properly handled - """ - self.fixed_params = {"fixed_model_param": "Fixed", "n_agents": 1} - batch = self.launch_batch_processing() - model_vars = batch.get_model_vars_dataframe() - agent_vars = batch.get_agent_vars_dataframe() - self.assertEqual(len(model_vars), len(agent_vars)) - self.assertEqual(len(model_vars), self.model_runs) - self.assertEqual(model_vars["reported_fixed_value"].unique(), ["Fixed"]) - - def test_model_with_only_fixed_parameters(self): - """ - Test that model with only fixed parameters and multiple iterations is - properly handled - """ - batch = self.launch_batch_processing_fixed() - model_vars = batch.get_model_vars_dataframe() - self.assertEqual(len(model_vars), 4) - self.assertEqual(model_vars["fixed"].unique(), ["happy"]) - - with self.assertRaises(AttributeError): - batch.get_agent_vars_dataframe() - - def test_model_with_variable_and_fixed_kwargs(self): - self.mock_model = MockMixedModel - self.model_reporters = { - "reported_fixed_param": lambda m: m.fixed_name, - "reported_variable_param": lambda m: m.variable_name, - } - self.fixed_params = {"fixed_name": "Fixed"} - self.variable_params = {"variable_name": [1, 2, 3]} - batch = self.launch_batch_processing() - model_vars = batch.get_model_vars_dataframe() - expected_cols = ( - len(self.variable_params) - + len(self.fixed_params) - + len(self.model_reporters) - + 1 - ) - self.assertEqual(model_vars.shape, (self.model_runs, expected_cols)) - self.assertEqual( - model_vars["reported_fixed_param"].iloc[0], self.fixed_params["fixed_name"] - ) - - def test_model_with_variable_kwargs_list(self): - self.variable_params = [ - {"variable_model_param": 1, "variable_agent_param": 1}, - {"variable_model_param": 2, "variable_agent_param": 1}, - {"variable_model_param": 2, "variable_agent_param": 8}, - {"variable_model_param": 3, "variable_agent_param": 8}, - ] - n_params = len(self.variable_params[0]) - batch = self.launch_batch_processing_fixed_list() - - model_vars = batch.get_model_vars_dataframe() - expected_cols = n_params + len(self.model_reporters) + 1 - self.assertEqual(model_vars.shape, (self.model_runs, expected_cols)) - - agent_vars = batch.get_agent_vars_dataframe() - expected_cols = n_params + len(self.agent_reporters) + 2 - self.assertEqual( - agent_vars.shape, (self.model_runs * NUM_AGENTS, expected_cols) - ) - - def test_model_with_variable_kwargs_list_mixed_length(self): - self.variable_params = [ - {"variable_model_param": 1}, - {"variable_model_param": 2}, - {"variable_model_param": 2, "variable_agent_param": 8}, - {"variable_model_param": 3, "variable_agent_param": 8}, - {"variable_agent_param": 1}, - ] - # This is currently not supported. Check that it raises the correct error. - msg = "parameter names in parameters_list are not equal across the list" - with self.assertRaises(ValueError, msg=msg): - self.launch_batch_processing_fixed_list() - - -class TestParameters(unittest.TestCase): - def test_product(self): - params = ParameterProduct({"var_alpha": ["a", "b", "c"], "var_num": [10, 20]}) - - lp = list(params) - self.assertCountEqual( - lp, - [ - {"var_alpha": "a", "var_num": 10}, - {"var_alpha": "a", "var_num": 20}, - {"var_alpha": "b", "var_num": 10}, - {"var_alpha": "b", "var_num": 20}, - {"var_alpha": "c", "var_num": 10}, - {"var_alpha": "c", "var_num": 20}, - ], - ) - - def test_sampler(self): - params1 = ParameterSampler( - { - "var_alpha": ["a", "b", "c", "d", "e"], - "var_num": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - }, - n=10, - random_state=1, - ) - params2 = ParameterSampler( - {"var_alpha": ["a", "b", "c", "d", "e"], "var_num": range(16)}, - n=10, - random_state=1, - ) - - lp = list(params1) - self.assertEqual(10, len(lp)) - self.assertEqual(lp, list(params2)) - - -if __name__ == "__main__": - unittest.main() diff --git a/tests/test_batchrunnerMP.py b/tests/test_batchrunnerMP.py deleted file mode 100644 index 935e61ed505..00000000000 --- a/tests/test_batchrunnerMP.py +++ /dev/null @@ -1,271 +0,0 @@ -""" -Test the BatchRunner -""" -import unittest -from functools import reduce -from multiprocessing import cpu_count, freeze_support -from operator import mul - -from mesa import Agent, Model -from mesa.batchrunner import BatchRunnerMP, ParameterProduct, ParameterSampler -from mesa.datacollection import DataCollector -from mesa.time import BaseScheduler - -NUM_AGENTS = 7 - - -class MockAgent(Agent): - """ - Minimalistic agent implementation for testing purposes - """ - - def __init__(self, unique_id, model, val): - super().__init__(unique_id, model) - self.unique_id = unique_id - self.val = val - self.local = 0 - - def step(self): - self.val += 1 - self.local += 0.25 - - -class MockModel(Model): - """ - Minimalistic model for testing purposes - """ - - def __init__( - self, - variable_model_param, - variable_agent_param, - fixed_model_param=None, - schedule=None, - **kwargs - ): - super().__init__() - self.schedule = BaseScheduler(None) if schedule is None else schedule - self.variable_model_param = variable_model_param - self.variable_agent_param = variable_agent_param - self.fixed_model_param = fixed_model_param - self.n_agents = kwargs.get("n_agents", NUM_AGENTS) - self.datacollector = DataCollector( - model_reporters={"reported_model_param": self.get_local_model_param}, - agent_reporters={"agent_id": "unique_id", "agent_local": "local"}, - ) - self.running = True - self.init_agents() - - def get_local_model_param(self): - return 42 - - def init_agents(self): - for i in range(self.n_agents): - self.schedule.add(MockAgent(i, self, self.variable_agent_param)) - - def step(self): - self.datacollector.collect(self) - self.schedule.step() - - -class MockMixedModel(Model): - def __init__(self, **other_params): - super().__init__() - self.variable_name = other_params.get("variable_name", 42) - self.fixed_name = other_params.get("fixed_name") - self.running = True - self.schedule = BaseScheduler(None) - self.schedule.add(MockAgent(1, self, 0)) - - def step(self): - self.schedule.step() - - -class TestBatchRunnerMP(unittest.TestCase): - """ - Test that BatchRunner is running batches - """ - - def setUp(self): - self.skipTest("Disabled due to consistent hangs") - self.mock_model = MockModel - self.model_reporters = { - "reported_variable_value": lambda m: m.variable_model_param, - "reported_fixed_value": lambda m: m.fixed_model_param, - } - self.agent_reporters = {"agent_id": "unique_id", "agent_val": "val"} - self.variable_params = { - "variable_model_param": range(3), - "variable_agent_param": [1, 8], - } - self.fixed_params = None - self.iterations = 17 - self.max_steps = 3 - - def launch_batch_processing(self): - batch = BatchRunnerMP( - self.mock_model, - nr_processes=None, - variable_parameters=self.variable_params, - fixed_parameters=self.fixed_params, - iterations=self.iterations, - max_steps=self.max_steps, - model_reporters=self.model_reporters, - agent_reporters=self.agent_reporters, - ) - - batch.run_all() - return batch - - def launch_batch_processing_debug(self): - """ - Tests with one processor for debugging purposes - """ - - batch = BatchRunnerMP( - self.mock_model, - nr_processes=1, - variable_parameters=self.variable_params, - fixed_parameters=self.fixed_params, - iterations=self.iterations, - max_steps=self.max_steps, - model_reporters=self.model_reporters, - agent_reporters=self.agent_reporters, - ) - - batch.run_all() - return batch - - @property - def model_runs(self): - """ - Returns total number of batch runner's iterations. - """ - return reduce(mul, map(len, self.variable_params.values())) * self.iterations - - def batch_model_vars(self, results): - model_vars = results.get_model_vars_dataframe() - model_collector = results.get_collector_model() - expected_cols = ( - len(self.variable_params) + len(self.model_reporters) + 1 - ) # extra column with run index - self.assertEqual(model_vars.shape, (self.model_runs, expected_cols)) - self.assertEqual(len(model_collector.keys()), self.model_runs) - - def test_model_level_vars(self): - """ - Test that model-level variable collection is of the correct size - """ - batch = self.launch_batch_processing() - assert batch.processes == cpu_count() - assert batch.processes != 1 - self.batch_model_vars(batch) - - batch2 = self.launch_batch_processing_debug() - self.batch_model_vars(batch2) - - def batch_agent_vars(self, result): - agent_vars = result.get_agent_vars_dataframe() - agent_collector = result.get_collector_agents() - # extra columns with run index and agentId - expected_cols = len(self.variable_params) + len(self.agent_reporters) + 2 - assert "agent_val" in list(agent_vars.columns) - assert "val_non_existent" not in list(agent_vars.columns) - assert "agent_id" in list(agent_collector[(0, 1, 1)].columns) - assert "Step" in list(agent_collector[(0, 1, 5)].index.names) - assert "nose" not in list(agent_collector[(0, 1, 1)].columns) - - self.assertEqual( - agent_vars.shape, (self.model_runs * NUM_AGENTS, expected_cols) - ) - - self.assertEqual( - agent_collector[(0, 1, 0)].shape, (NUM_AGENTS * self.max_steps, 2) - ) - - def test_agent_level_vars(self): - """ - Test that agent-level variable collection is of the correct size - """ - batch = self.launch_batch_processing() - self.batch_agent_vars(batch) - - batch2 = self.launch_batch_processing_debug() - self.batch_agent_vars(batch2) - - def test_model_with_fixed_parameters_as_kwargs(self): - """ - Test that model with fixed parameters passed like kwargs is - properly handled - """ - self.fixed_params = {"fixed_model_param": "Fixed", "n_agents": 1} - batch = self.launch_batch_processing() - model_vars = batch.get_model_vars_dataframe() - agent_vars = batch.get_agent_vars_dataframe() - - self.assertEqual(len(model_vars), len(agent_vars)) - self.assertEqual(len(model_vars), self.model_runs) - self.assertEqual(model_vars["reported_fixed_value"].unique(), ["Fixed"]) - - def test_model_with_variable_and_fixed_kwargs(self): - self.mock_model = MockMixedModel - self.model_reporters = { - "reported_fixed_param": lambda m: m.fixed_name, - "reported_variable_param": lambda m: m.variable_name, - } - self.fixed_params = {"fixed_name": "Fixed"} - self.variable_params = {"variable_name": [1, 2, 3]} - batch = self.launch_batch_processing() - model_vars = batch.get_model_vars_dataframe() - expected_cols = ( - len(self.variable_params) - + len(self.fixed_params) - + len(self.model_reporters) - + 1 - ) - self.assertEqual(model_vars.shape, (self.model_runs, expected_cols)) - self.assertEqual( - model_vars["reported_fixed_param"].iloc[0], self.fixed_params["fixed_name"] - ) - - -class TestParameters(unittest.TestCase): - def test_product(self): - params = ParameterProduct({"var_alpha": ["a", "b", "c"], "var_num": [10, 20]}) - - lp = list(params) - self.assertCountEqual( - lp, - [ - {"var_alpha": "a", "var_num": 10}, - {"var_alpha": "a", "var_num": 20}, - {"var_alpha": "b", "var_num": 10}, - {"var_alpha": "b", "var_num": 20}, - {"var_alpha": "c", "var_num": 10}, - {"var_alpha": "c", "var_num": 20}, - ], - ) - - def test_sampler(self): - params1 = ParameterSampler( - { - "var_alpha": ["a", "b", "c", "d", "e"], - "var_num": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], - }, - n=10, - random_state=1, - ) - params2 = ParameterSampler( - {"var_alpha": ["a", "b", "c", "d", "e"], "var_num": range(16)}, - n=10, - random_state=1, - ) - - lp = list(params1) - self.assertEqual(10, len(lp)) - self.assertEqual(lp, list(params2)) - - -if __name__ == "__main__": - freeze_support() - unittest.main() diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 5799131d6de..86e05b0c0b3 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -1,13 +1,29 @@ from collections import defaultdict from unittest import TestCase +import mesa from mesa.model import Model from mesa.space import MultiGrid from mesa.time import SimultaneousActivation from mesa.visualization.ModularVisualization import ModularServer from mesa.visualization.modules import CanvasGrid, TextElement from mesa.visualization.UserParam import UserSettableParameter -from tests.test_batchrunner import MockAgent + + +class MockAgent(mesa.Agent): + """ + Minimalistic agent implementation for testing purposes + """ + + def __init__(self, unique_id, model, val): + super().__init__(unique_id, model) + self.unique_id = unique_id + self.val = val + self.local = 0 + + def step(self): + self.val += 1 + self.local += 0.25 class MockModel(Model): From 2a50370d323db2e54d5fc5e3fb46f15a45d32eea Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 9 May 2023 08:22:17 -0400 Subject: [PATCH 118/214] breaking: space: Remove deprecated neighbor_Iter --- mesa/space.py | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index a1b9ff43c67..0820aee31ea 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -199,21 +199,6 @@ def coord_iter(self) -> Iterator[tuple[GridContent, int, int]]: for col in range(self.height): yield self._grid[row][col], row, col # agent, x, y - def neighbor_iter(self, pos: Coordinate, moore: bool = True) -> Iterator[Agent]: - """Iterate over position neighbors. - - Args: - pos: (x,y) coords tuple for the position to get the neighbors of. - moore: Boolean for whether to use Moore neighborhood (including - diagonals) or Von Neumann (only up/down/left/right). - """ - - warn( - "`neighbor_iter` is deprecated in favor of `iter_neighbors` " - "and will be removed in the subsequent version." - ) - return self.iter_neighbors(pos, moore) - def iter_neighborhood( self, pos: Coordinate, @@ -761,19 +746,6 @@ def get_neighborhood( return neighborhood - def neighbor_iter(self, pos: Coordinate) -> Iterator[Agent]: - """Iterate over position neighbors. - - Args: - pos: (x,y) coords tuple for the position to get the neighbors of. - """ - - warn( - "`neighbor_iter` is deprecated in favor of `iter_neighbors` " - "and will be removed in the subsequent version." - ) - return self.iter_neighbors(pos) - def iter_neighborhood( self, pos: Coordinate, include_center: bool = False, radius: int = 1 ) -> Iterator[Coordinate]: From 82fc3c5de93315b3b856178747bc323b82a91d3f Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Sun, 18 Jun 2023 09:05:30 +0200 Subject: [PATCH 119/214] breaking: NetworkGrid: modify get_neighbors and create get_neighborhood (#1542) --- mesa/space.py | 15 ++++++++++----- tests/test_space.py | 12 ++++++------ 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 0820aee31ea..abdb1869f58 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -1069,22 +1069,27 @@ def place_agent(self, agent: Agent, node_id: int) -> None: self.G.nodes[node_id]["agent"].append(agent) agent.pos = node_id - def get_neighbors( + def get_neighborhood( self, node_id: int, include_center: bool = False, radius: int = 1 ) -> list[int]: """Get all adjacent nodes within a certain radius""" if radius == 1: - neighbors = list(self.G.neighbors(node_id)) + neighborhood = list(self.G.neighbors(node_id)) if include_center: - neighbors.append(node_id) + neighborhood.append(node_id) else: neighbors_with_distance = nx.single_source_shortest_path_length( self.G, node_id, radius ) if not include_center: del neighbors_with_distance[node_id] - neighbors = sorted(neighbors_with_distance.keys()) - return neighbors + neighborhood = sorted(neighbors_with_distance.keys()) + return neighborhood + + def get_neighbors(self, node_id: int, include_center: bool = False) -> list[Agent]: + """Get all agents in adjacent nodes.""" + neighborhood = self.get_neighborhood(node_id, include_center) + return self.get_cell_list_contents(neighborhood) def move_agent(self, agent: Agent, node_id: int) -> None: """Move an agent from its current node to a new node.""" diff --git a/tests/test_space.py b/tests/test_space.py index a7aba2529f2..c445135c0aa 100644 --- a/tests/test_space.py +++ b/tests/test_space.py @@ -354,10 +354,10 @@ def test_agent_positions(self): assert a.pos == pos def test_get_neighbors(self): - assert len(self.space.get_neighbors(0, include_center=True)) == 3 - assert len(self.space.get_neighbors(0, include_center=False)) == 2 - assert len(self.space.get_neighbors(2, include_center=True, radius=3)) == 7 - assert len(self.space.get_neighbors(2, include_center=False, radius=3)) == 6 + assert len(self.space.get_neighborhood(0, include_center=True)) == 3 + assert len(self.space.get_neighborhood(0, include_center=False)) == 2 + assert len(self.space.get_neighborhood(2, include_center=True, radius=3)) == 7 + assert len(self.space.get_neighborhood(2, include_center=False, radius=3)) == 6 def test_move_agent(self): initial_pos = 1 @@ -426,11 +426,11 @@ def test_agent_positions(self): def test_get_neighbors(self): assert ( - len(self.space.get_neighbors(0, include_center=True)) + len(self.space.get_neighborhood(0, include_center=True)) == TestMultipleNetworkGrid.GRAPH_SIZE ) assert ( - len(self.space.get_neighbors(0, include_center=False)) + len(self.space.get_neighborhood(0, include_center=False)) == TestMultipleNetworkGrid.GRAPH_SIZE - 1 ) From 92c7b147f066ca7a4b7154b56bb44ef7552bfbfc Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 12 May 2023 02:34:00 -0400 Subject: [PATCH 120/214] breaking: Remove UserSettableParameter --- mesa/visualization/ModularVisualization.py | 6 +- mesa/visualization/UserParam.py | 129 --------------------- tests/test_usersettableparam.py | 65 +++++------ tests/test_visualization.py | 15 +-- 4 files changed, 37 insertions(+), 178 deletions(-) diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py index 6e594f79f51..2df25938971 100644 --- a/mesa/visualization/ModularVisualization.py +++ b/mesa/visualization/ModularVisualization.py @@ -106,7 +106,7 @@ import tornado.web import tornado.websocket -from mesa.visualization.UserParam import UserParam, UserSettableParameter +from mesa.visualization.UserParam import UserParam # Suppress several pylint warnings for this file. # Attributes being defined outside of init is a Tornado feature. @@ -121,9 +121,7 @@ def is_user_param(val): - return isinstance(val, UserSettableParameter) or issubclass( - val.__class__, UserParam - ) + return issubclass(val.__class__, UserParam) class VisualizationElement: diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py index 7fd2103b993..d97d196cead 100644 --- a/mesa/visualization/UserParam.py +++ b/mesa/visualization/UserParam.py @@ -1,5 +1,4 @@ import numbers -from warnings import warn NUMBER = "number" CHECKBOX = "checkbox" @@ -8,134 +7,6 @@ STATIC_TEXT = "static_text" -class UserSettableParameter: - """A class for providing options to a visualization for a given parameter. - - UserSettableParameter can be used instead of keyword arguments when specifying model parameters in an - instance of a `ModularServer` so that the parameter can be adjusted in the UI without restarting the server. - - Validation of correctly-specified params happens on startup of a `ModularServer`. Each param is handled - individually in the UI and sends callback events to the server when an option is updated. That option is then - re-validated, in the `value.setter` property method to ensure input is correct from the UI to `reset_model` - callback. - - Parameter types include: - - 'number' - a simple numerical input - - 'checkbox' - boolean checkbox - - 'choice' - String-based dropdown input, for selecting choices within a model - - 'slider' - A number-based slider input with settable increment - - 'static_text' - A non-input textbox for displaying model info. - - Examples: - - # Simple number input - number_option = UserSettableParameter('number', 'My Number', value=123) - - # Checkbox input - boolean_option = UserSettableParameter('checkbox', 'My Boolean', value=True) - - # Choice input - choice_option = UserSettableParameter('choice', 'My Choice', value='Default choice', - choices=['Default Choice', 'Alternate Choice']) - - # Slider input - slider_option = UserSettableParameter('slider', 'My Slider', value=123, min_value=10, max_value=200, step=0.1) - - # Static text - static_text = UserSettableParameter('static_text', value="This is a descriptive textbox") - """ - - NUMBER = NUMBER - CHECKBOX = CHECKBOX - CHOICE = CHOICE - SLIDER = SLIDER - STATIC_TEXT = STATIC_TEXT - - TYPES = (NUMBER, CHECKBOX, CHOICE, SLIDER, STATIC_TEXT) - - _ERROR_MESSAGE = "Missing or malformed inputs for '{}' Option '{}'" - - def __init__( - self, - param_type=None, - name="", - value=None, - min_value=None, - max_value=None, - step=1, - choices=None, - description=None, - ): - warn( - "UserSettableParameter is deprecated in favor of UserParam objects " - "such as Slider, Checkbox, Choice, StaticText, NumberInput. " - "See the examples folder for how to use them. " - "UserSettableParameter will be removed in the next major release." - ) - if choices is None: - choices = [] - if param_type not in self.TYPES: - raise ValueError(f"{param_type} is not a valid Option type") - self.param_type = param_type - self.name = name - self._value = value - self.min_value = min_value - self.max_value = max_value - self.step = step - self.choices = choices - self.description = description - - # Validate option types to make sure values are supplied properly - msg = self._ERROR_MESSAGE.format(self.param_type, name) - valid = True - - if self.param_type == self.NUMBER: - valid = self.value is not None - - elif self.param_type == self.SLIDER: - valid = not ( - self.value is None or self.min_value is None or self.max_value is None - ) - - elif self.param_type == self.CHOICE: - valid = not (self.value is None or len(self.choices) == 0) - - elif self.param_type == self.CHECKBOX: - valid = isinstance(self.value, bool) - - elif self.param_type == self.STATIC_TEXT: - valid = isinstance(self.value, str) - - if not valid: - raise ValueError(msg) - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = value - if self.param_type == self.SLIDER: - if self._value < self.min_value: - self._value = self.min_value - elif self._value > self.max_value: - self._value = self.max_value - elif (self.param_type == self.CHOICE) and self._value not in self.choices: - print( - "Selected choice value not in available choices, selected first choice from 'choices' list" - ) - self._value = self.choices[0] - - @property - def json(self): - result = self.__dict__.copy() - result["value"] = result.pop( - "_value" - ) # Return _value as value, value is the same - return result - - class UserParam: _ERROR_MESSAGE = "Missing or malformed inputs for '{}' Option '{}'" diff --git a/tests/test_usersettableparam.py b/tests/test_usersettableparam.py index 9106d050763..8f02fa4b0b6 100644 --- a/tests/test_usersettableparam.py +++ b/tests/test_usersettableparam.py @@ -6,61 +6,50 @@ NumberInput, Slider, StaticText, - UserSettableParameter, ) class TestOption(TestCase): def setUp(self): - self.number_option = UserSettableParameter("number", value=123) - self.number_option_standalone = NumberInput("number", value=123) - self.checkbox_option = UserSettableParameter("checkbox", value=True) - self.checkbox_option_standalone = Checkbox(value=True) - self.choice_option = UserSettableParameter( - "choice", + self.number_option = NumberInput("number", value=123) + self.checkbox_option = Checkbox(value=True) + self.choice_option = Choice( value="I am your default choice", choices=["I am your default choice", "I am your other choice"], ) - self.choice_option_standalone = Choice( - value="I am your default choice", - choices=["I am your default choice", "I am your other choice"], - ) - self.slider_option = UserSettableParameter( - "slider", value=123, min_value=100, max_value=200 - ) - self.slider_option_standalone = Slider(value=123, min_value=100, max_value=200) + self.slider_option = Slider(value=123, min_value=100, max_value=200) self.static_text_option = StaticText("Hurr, Durr Im'a Sheep") def test_number(self): - for option in [self.number_option, self.number_option_standalone]: - assert option.value == 123 - option.value = 321 - assert option.value == 321 + option = self.number_option + assert option.value == 123 + option.value = 321 + assert option.value == 321 def test_checkbox(self): - for option in [self.checkbox_option, self.checkbox_option_standalone]: - assert option.value - option.value = False - assert not option.value + option = self.checkbox_option + assert option.value + option.value = False + assert not option.value def test_choice(self): - for option in [self.choice_option, self.choice_option_standalone]: - assert option.value == "I am your default choice" - option.value = "I am your other choice" - assert option.value == "I am your other choice" - option.value = "I am not an available choice" - assert option.value == "I am your default choice" + option = self.choice_option + assert option.value == "I am your default choice" + option.value = "I am your other choice" + assert option.value == "I am your other choice" + option.value = "I am not an available choice" + assert option.value == "I am your default choice" def test_slider(self): - for option in [self.slider_option, self.slider_option_standalone]: - assert option.value == 123 - option.value = 150 - assert option.value == 150 - option.value = 0 - assert option.value == 100 - option.value = 300 - assert option.value == 200 - assert option.json["value"] == 200 + option = self.slider_option + assert option.value == 123 + option.value = 150 + assert option.value == 150 + option.value = 0 + assert option.value == 100 + option.value = 300 + assert option.value == 200 + assert option.json["value"] == 200 with self.assertRaises(ValueError): Slider() diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 86e05b0c0b3..2c3ebd4cdc7 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -7,7 +7,10 @@ from mesa.time import SimultaneousActivation from mesa.visualization.ModularVisualization import ModularServer from mesa.visualization.modules import CanvasGrid, TextElement -from mesa.visualization.UserParam import UserSettableParameter +from mesa.visualization.UserParam import ( + NumberInput, + Slider, +) class MockAgent(mesa.Agent): @@ -65,8 +68,8 @@ def setUp(self): self.user_params = { "width": 1, "height": 1, - "key1": UserSettableParameter("number", "Test Parameter", 101), - "key2": UserSettableParameter("slider", "Test Parameter", 200, 0, 300, 10), + "key1": NumberInput("Test Parameter", 101), + "key2": Slider("Test Parameter", 200, 0, 300, 10), } self.viz_elements = [ @@ -95,8 +98,6 @@ def test_text_render_model_state(self): def test_user_params(self): print(self.server.user_params) assert self.server.user_params == { - "key1": UserSettableParameter("number", "Test Parameter", 101).json, - "key2": UserSettableParameter( - "slider", "Test Parameter", 200, 0, 300, 10 - ).json, + "key1": NumberInput("Test Parameter", 101).json, + "key2": Slider("Test Parameter", 200, 0, 300, 10).json, } From 5c9172591881a6951c79aa49d9c89a122a8ef7d4 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Sat, 17 Jun 2023 02:16:30 -0400 Subject: [PATCH 121/214] intro tutorial: Switch to using Seaborn Co-authored-by: rht --- docs/tutorials/intro_tutorial.ipynb | 68 ++++++++++++++++++++--------- setup.py | 9 +++- 2 files changed, 55 insertions(+), 22 deletions(-) diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 8f053d17b71..fe76ea032be 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -53,7 +53,7 @@ "Install Mesa:\n", "\n", "```bash\n", - "pip install mesa\n", + "pip install --upgrade mesa\n", "```\n", "\n", "Install Jupyter Notebook (optional):\n", @@ -130,8 +130,8 @@ "source": [ "import mesa\n", "\n", - "# Data visualization tool.\n", - "import matplotlib.pyplot as plt\n", + "# Data visualization tools.\n", + "import seaborn as sns\n", "\n", "# Has multi-dimensional arrays and matrices. Has a large collection of\n", "# mathematical functions to operate on these arrays.\n", @@ -509,6 +509,7 @@ "If you are running from a text editor or IDE, you'll also need to add this line, to make the graph appear.\n", "\n", "```python\n", + "import matplotlib.pyplot as plt\n", "plt.show()\n", "```" ] @@ -531,7 +532,11 @@ "import matplotlib.pyplot as plt\n", "\n", "agent_wealth = [a.wealth for a in model.schedule.agents]\n", - "plt.hist(agent_wealth)" + "# Create a histogram with seaborn\n", + "g = sns.histplot(agent_wealth, discrete=True)\n", + "g.set(\n", + " title=\"Wealth distribution\", xlabel=\"Wealth\", ylabel=\"Number of agents\"\n", + "); # The semicolon is just to avoid printing the object representation" ] }, { @@ -571,7 +576,9 @@ " for agent in model.schedule.agents:\n", " all_wealth.append(agent.wealth)\n", "\n", - "plt.hist(all_wealth, bins=range(max(all_wealth) + 1))" + "# Use seaborn\n", + "g = sns.histplot(all_wealth, discrete=True)\n", + "g.set(title=\"Wealth distribution\", xlabel=\"Wealth\", ylabel=\"Number of agents\");" ] }, { @@ -758,7 +765,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Let's create a model with 50 agents on a 10x10 grid, and run it for 20 steps." + "Let's create a model with 100 agents on a 10x10 grid, and run it for 20 steps." ] }, { @@ -772,7 +779,7 @@ }, "outputs": [], "source": [ - "model = MoneyModel(50, 10, 10)\n", + "model = MoneyModel(100, 10, 10)\n", "for i in range(20):\n", " model.step()" ] @@ -802,11 +809,10 @@ " cell_content, x, y = cell\n", " agent_count = len(cell_content)\n", " agent_counts[x][y] = agent_count\n", - "plt.imshow(agent_counts, interpolation=\"nearest\")\n", - "plt.colorbar()\n", - "\n", - "# If running from a text editor or IDE, remember you'll need the following:\n", - "# plt.show()" + "# Plot using seaborn, with a size of 5x5\n", + "g = sns.heatmap(agent_counts, cmap=\"viridis\", annot=True, cbar=False, square=True)\n", + "g.figure.set_size_inches(4, 4)\n", + "g.set(title=\"Number of agents on each cell of the grid\");" ] }, { @@ -923,7 +929,7 @@ }, "outputs": [], "source": [ - "model = MoneyModel(50, 10, 10)\n", + "model = MoneyModel(100, 10, 10)\n", "for i in range(100):\n", " model.step()" ] @@ -947,7 +953,9 @@ "outputs": [], "source": [ "gini = model.datacollector.get_model_vars_dataframe()\n", - "gini.plot()" + "# Plot the Gini coefficient over time\n", + "g = sns.lineplot(data=gini)\n", + "g.set(title=\"Gini Coefficient over Time\", ylabel=\"Gini Coefficient\");" ] }, { @@ -976,7 +984,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "You'll see that the DataFrame's index is pairings of model step and agent ID. You can analyze it the way you would any other DataFrame. For example, to get a histogram of agent wealth at the model's end:" + "You'll see that the DataFrame's index is pairings of model step and agent ID. This is because the data collector stores the data in a dictionary, with the step number as the key, and a dictionary of agent ID and variable value pairs as the value. The data collector then converts this dictionary into a DataFrame, which is why the index is a pair of (model step, agent ID). You can analyze it the way you would any other DataFrame. For example, to get a histogram of agent wealth at the model's end:" ] }, { @@ -990,8 +998,15 @@ }, "outputs": [], "source": [ - "end_wealth = agent_wealth.xs(99, level=\"Step\")[\"Wealth\"]\n", - "end_wealth.hist(bins=range(agent_wealth.Wealth.max() + 1))" + "last_step = agent_wealth.index.get_level_values(\"Step\").max()\n", + "end_wealth = agent_wealth.xs(last_step, level=\"Step\")[\"Wealth\"]\n", + "# Create a histogram of wealth at the last step\n", + "g = sns.histplot(end_wealth, discrete=True)\n", + "g.set(\n", + " title=\"Distribution of wealth at the end of simulation\",\n", + " xlabel=\"Wealth\",\n", + " ylabel=\"Number of agents\",\n", + ");" ] }, { @@ -1012,8 +1027,12 @@ }, "outputs": [], "source": [ + "# Get the wealth of agent 14 over time\n", "one_agent_wealth = agent_wealth.xs(14, level=\"AgentID\")\n", - "one_agent_wealth.Wealth.plot()" + "\n", + "# Plot the wealth of agent 14 over time\n", + "g = sns.lineplot(data=one_agent_wealth, x=\"Step\", y=\"Wealth\")\n", + "g.set(title=\"Wealth of agent 14 over time\");" ] }, { @@ -1235,10 +1254,17 @@ }, "outputs": [], "source": [ + "# Filter the results to only contain the data of one agent (the Gini coefficient will be the same for the entire population at any time) at the 100th step of each episode\n", "results_filtered = results_df[(results_df.AgentID == 0) & (results_df.Step == 100)]\n", - "N_values = results_filtered.N.values\n", - "gini_values = results_filtered.Gini.values\n", - "plt.scatter(N_values, gini_values)" + "results_filtered[[\"iteration\", \"N\", \"Gini\"]].reset_index(\n", + " drop=True\n", + ").head() # Create a scatter plot\n", + "g = sns.scatterplot(data=results_filtered, x=\"N\", y=\"Gini\")\n", + "g.set(\n", + " xlabel=\"Number of agents\",\n", + " ylabel=\"Gini coefficient\",\n", + " title=\"Gini coefficient vs. number of agents\",\n", + ");" ] }, { diff --git a/setup.py b/setup.py index 00e88cbe18c..7eff8bb317c 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,14 @@ # Explicitly install ipykernel for Python 3.8. # See https://stackoverflow.com/questions/28831854/how-do-i-add-python3-kernel-to-jupyter-ipython # Could be removed in the future - "docs": ["sphinx<7", "ipython", "nbsphinx", "ipykernel", "pydata_sphinx_theme"], + "docs": [ + "sphinx<7", + "ipython", + "nbsphinx", + "ipykernel", + "pydata_sphinx_theme", + "seaborn", + ], } version = "" From a43725e083f838e271e7c90d4c29b876bc1ebfc1 Mon Sep 17 00:00:00 2001 From: tpike3 Date: Mon, 19 Jun 2023 15:56:21 -0400 Subject: [PATCH 122/214] fix broken schelling link in best-practices --- docs/best-practices.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/best-practices.rst b/docs/best-practices.rst index 4519d6881ff..a73c125f899 100644 --- a/docs/best-practices.rst +++ b/docs/best-practices.rst @@ -27,7 +27,7 @@ organize them. For example, if the visualization uses image files, put those in an ``images`` directory. The `Schelling -`_ model is +`_ model is a good example of a small well-packaged model. It's easy to create a cookiecutter mesa model by running ``mesa startproject`` From 95682481813057763e0490fe86d657aebe3bf3a5 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 9 May 2023 08:38:50 -0400 Subject: [PATCH 123/214] breaking: space: Remove deprecated find_empty --- mesa/space.py | 20 -------------------- tests/test_space.py | 2 -- 2 files changed, 22 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index abdb1869f58..0d950fdb037 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -486,26 +486,6 @@ def move_to_empty(self, agent: Agent, num_agents: int | None = None) -> None: self.remove_agent(agent) self.place_agent(agent, new_pos) - def find_empty(self) -> Coordinate | None: - """Pick a random empty cell.""" - import random - - warn( - ( - "`find_empty` is being phased out since it uses the global " - "`random` instead of the model-level random-number generator. " - "Consider replacing it with having a model or agent object " - "explicitly pick one of the grid's list of empty cells." - ), - DeprecationWarning, - ) - - if self.exists_empty_cells(): - pos = random.choice(sorted(self.empties)) - return pos - else: - return None - def exists_empty_cells(self) -> bool: """Return True if any cells empty else False.""" return len(self.empties) > 0 diff --git a/tests/test_space.py b/tests/test_space.py index c445135c0aa..bfa49b3706b 100644 --- a/tests/test_space.py +++ b/tests/test_space.py @@ -306,11 +306,9 @@ def test_remove_agent(self): def test_empty_cells(self): if self.space.exists_empty_cells(): - pytest.deprecated_call(self.space.find_empty) for i, pos in enumerate(list(self.space.empties)): a = MockAgent(-i, pos) self.space.position_agent(a, x=pos[0], y=pos[1]) - assert self.space.find_empty() is None with self.assertRaises(Exception): self.space.move_to_empty(a) From a754eb99b97251204cc40652da8ab8fffb01af65 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 9 May 2023 08:40:30 -0400 Subject: [PATCH 124/214] breaking: space: Remove deprecated num_agents in move_to_empty --- mesa/space.py | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 0d950fdb037..147cda93820 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -454,16 +454,8 @@ def is_cell_empty(self, pos: Coordinate) -> bool: x, y = pos return self._grid[x][y] == self.default_val() - def move_to_empty(self, agent: Agent, num_agents: int | None = None) -> None: + def move_to_empty(self, agent: Agent) -> None: """Moves agent to a random empty cell, vacating agent's old cell.""" - 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") From 0db8f7c7ad1277be64637ff7180722fad81df1d3 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 9 May 2023 08:42:52 -0400 Subject: [PATCH 125/214] breaking: space: Remove deprecated position_agent --- mesa/space.py | 36 ------------------------------------ tests/test_grid.py | 29 +++++------------------------ tests/test_space.py | 2 +- 3 files changed, 6 insertions(+), 61 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 147cda93820..96e1a4da317 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -495,42 +495,6 @@ class SingleGrid(_Grid): torus: Boolean which determines whether to treat the grid as a torus. """ - def position_agent( - self, agent: Agent, x: int | str = "random", y: int | str = "random" - ) -> None: - """Position an agent on the grid. - This is used when first placing agents! Setting either x or y to "random" - gives the same behavior as 'move_to_empty()' to get a random position. - 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'." - 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": - self.move_to_empty(agent) - else: - coords = (x, y) - self.place_agent(agent, coords) - def place_agent(self, agent: Agent, pos: Coordinate) -> None: """Place the agent at the specified location, and set its pos variable.""" if self.is_cell_empty(pos): diff --git a/tests/test_grid.py b/tests/test_grid.py index a24db60a594..d263c90bdcf 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -265,25 +265,6 @@ 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): """ @@ -297,28 +278,28 @@ def test_enforcement(self, mock_model): # Place the agent in an empty cell mock_model.schedule.get_agent_count = Mock(side_effect=lambda: len(self.agents)) - self.grid.position_agent(a) + self.grid.move_to_empty(a) self.num_agents += 1 # Test whether after placing, the empty cells are reduced by 1 assert a.pos not in self.grid.empties assert len(self.grid.empties) == 8 for _i in range(10): - self.grid.move_to_empty(a, num_agents=self.num_agents) + self.grid.move_to_empty(a) assert len(self.grid.empties) == 8 # Place agents until the grid is full empty_cells = len(self.grid.empties) for i in range(empty_cells): a = MockAgent(101 + i, None) - self.grid.position_agent(a) + self.grid.move_to_empty(a) self.num_agents += 1 assert len(self.grid.empties) == 0 a = MockAgent(110, None) with self.assertRaises(Exception): - self.grid.position_agent(a) + self.grid.move_to_empty(a) with self.assertRaises(Exception): - self.move_to_empty(self.agents[0], num_agents=self.num_agents) + self.move_to_empty(self.agents[0]) # Number of agents at each position for testing diff --git a/tests/test_space.py b/tests/test_space.py index bfa49b3706b..8691dc45f0a 100644 --- a/tests/test_space.py +++ b/tests/test_space.py @@ -308,7 +308,7 @@ def test_empty_cells(self): if self.space.exists_empty_cells(): for i, pos in enumerate(list(self.space.empties)): a = MockAgent(-i, pos) - self.space.position_agent(a, x=pos[0], y=pos[1]) + self.space.place_agent(a, pos) with self.assertRaises(Exception): self.space.move_to_empty(a) From f7d91d13c8ecbf1cf992bd1dcd28ea1dfadb28a8 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Tue, 20 Jun 2023 23:40:44 -0400 Subject: [PATCH 126/214] breaking: Change coord_iter to return tuple[content, pos] Co-authored-by: rht --- docs/tutorials/intro_tutorial.ipynb | 5 ++--- mesa/space.py | 6 +++--- tests/test_grid.py | 11 +++++------ tests/test_visualization.py | 3 ++- 4 files changed, 12 insertions(+), 13 deletions(-) diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index fe76ea032be..22703cf4127 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -788,7 +788,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Now let's use matplotlib and numpy to visualize the number of agents residing in each cell. To do that, we create a numpy array of the same size as the grid, filled with zeros. Then we use the grid object's `coord_iter()` feature, which lets us loop over every cell in the grid, giving us each cell's coordinates and contents in turn." + "Now let's use matplotlib and numpy to visualize the number of agents residing in each cell. To do that, we create a numpy array of the same size as the grid, filled with zeros. Then we use the grid object's `coord_iter()` feature, which lets us loop over every cell in the grid, giving us each cell's positions and contents in turn." ] }, { @@ -805,8 +805,7 @@ "import numpy as np\n", "\n", "agent_counts = np.zeros((model.grid.width, model.grid.height))\n", - "for cell in model.grid.coord_iter():\n", - " cell_content, x, y = cell\n", + "for cell_content, (x, y) in model.grid.coord_iter():\n", " agent_count = len(cell_content)\n", " agent_counts[x][y] = agent_count\n", "# Plot using seaborn, with a size of 5x5\n", diff --git a/mesa/space.py b/mesa/space.py index 96e1a4da317..9e9522e2da9 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -193,11 +193,11 @@ def __iter__(self) -> Iterator[GridContent]: as if it is one list:""" return itertools.chain(*self._grid) - def coord_iter(self) -> Iterator[tuple[GridContent, int, int]]: - """An iterator that returns coordinates as well as cell contents.""" + def coord_iter(self) -> Iterator[tuple[GridContent, Coordinate]]: + """An iterator that returns positions as well as cell contents.""" for row in range(self.width): for col in range(self.height): - yield self._grid[row][col], row, col # agent, x, y + yield self._grid[row][col], (row, col) # agent, position def iter_neighborhood( self, diff --git a/tests/test_grid.py b/tests/test_grid.py index d263c90bdcf..686d8e78b59 100644 --- a/tests/test_grid.py +++ b/tests/test_grid.py @@ -134,15 +134,13 @@ def test_coord_iter(self): # no agent in first space first = next(ci) assert first[0] is None - assert first[1] == 0 - assert first[2] == 0 + assert first[1] == (0, 0) # first agent in the second space second = next(ci) assert second[0].unique_id == 1 assert second[0].pos == (0, 1) - assert second[1] == 0 - assert second[2] == 1 + assert second[1] == (0, 1) def test_agent_move(self): # get the agent at [0, 1] @@ -480,8 +478,9 @@ def test_neighbors(self): class TestIndexing: # Create a grid where the content of each coordinate is a tuple of its coordinates grid = SingleGrid(3, 5, True) - for _, x, y in grid.coord_iter(): - grid._grid[x][y] = (x, y) + for _, pos in grid.coord_iter(): + x, y = pos + grid._grid[x][y] = pos def test_int(self): assert self.grid[0][0] == (0, 0) diff --git a/tests/test_visualization.py b/tests/test_visualization.py index 2c3ebd4cdc7..55e53cdb7a0 100644 --- a/tests/test_visualization.py +++ b/tests/test_visualization.py @@ -40,7 +40,8 @@ def __init__(self, width, height, key1=103, key2=104): self.schedule = SimultaneousActivation(self) self.grid = MultiGrid(width, height, torus=True) - for _c, x, y in self.grid.coord_iter(): + for _c, pos in self.grid.coord_iter(): + x, y = pos a = MockAgent(x + y * 100, self, x * y * 3) self.grid.place_agent(a, (x, y)) self.schedule.add(a) From cd02fefbf3b3a9f8c8b132924046585e60a0c53f Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 25 Jun 2023 09:03:17 -0400 Subject: [PATCH 127/214] Add reminder to update Ruff config for mesa-examples --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index db2f6b43e68..3dbd506a55d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -44,4 +44,5 @@ extend-ignore = [ ] extend-exclude = ["docs", "build"] # Hardcode to Python 3.8. +# Reminder to update mesa-examples if the value below is changed. target-version = "py38" From 4e36a6a47f423fd38b2b2b8ad9aecb9ea7b960ae Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 25 Jun 2023 09:17:27 -0400 Subject: [PATCH 128/214] Update to pass Ruff 0.0.275 --- .github/workflows/build_lint.yml | 2 +- mesa/space.py | 1 + mesa/visualization/ModularVisualization.py | 11 ++++---- .../modules/BarChartVisualization.py | 3 +- pyproject.toml | 3 ++ tests/test_import_namespace.py | 28 +++++++++---------- 6 files changed, 27 insertions(+), 21 deletions(-) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 382c2e0fea1..898a37decf3 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -68,7 +68,7 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.10" - - run: pip install ruff==0.0.254 + - run: pip install ruff==0.0.275 - name: Lint with ruff # Include `--format=github` to enable automatic inline annotations. # Use settings from pyproject.toml. diff --git a/mesa/space.py b/mesa/space.py index 9e9522e2da9..75532e37168 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -781,6 +781,7 @@ def __init__(self, width: int, height: int, torus: bool) -> None: "depending on your use case." ), DeprecationWarning, + stacklevel=2, ) diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py index 2df25938971..746e5b39eea 100644 --- a/mesa/visualization/ModularVisualization.py +++ b/mesa/visualization/ModularVisualization.py @@ -98,6 +98,7 @@ import os import platform import webbrowser +from typing import ClassVar import tornado.autoreload import tornado.escape @@ -145,10 +146,10 @@ class VisualizationElement: to the client. """ - package_includes = [] - local_includes = [] + package_includes: ClassVar = [] + local_includes: ClassVar = [] js_code = "" - render_args = {} + render_args: ClassVar = {} local_dir = "" def __init__(self): @@ -171,7 +172,7 @@ class TextElement(VisualizationElement): Module for drawing live-updating text. """ - package_includes = ["TextModule.js"] + package_includes: ClassVar = ["TextModule.js"] js_code = "elements.push(new TextModule());" @@ -283,7 +284,7 @@ def __init__( self.port = port else: # Default port to listen on - self.port = int(os.getenv("PORT", 8521)) + self.port = int(os.getenv("PORT", "8521")) # Handlers and other globals: page_handler = (r"/", PageHandler) diff --git a/mesa/visualization/modules/BarChartVisualization.py b/mesa/visualization/modules/BarChartVisualization.py index 20fd5e7039c..cfee85dd1a6 100644 --- a/mesa/visualization/modules/BarChartVisualization.py +++ b/mesa/visualization/modules/BarChartVisualization.py @@ -5,6 +5,7 @@ Module for drawing live-updating bar charts using d3.js """ import json +from typing import ClassVar from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement @@ -26,7 +27,7 @@ class BarChartModule(VisualizationElement): data_collector_name: Name of the DataCollector object in the model to retrieve data from. """ - package_includes = [D3_JS_FILE, "BarChartModule.js"] + package_includes: ClassVar = [D3_JS_FILE, "BarChartModule.js"] def __init__( self, diff --git a/pyproject.toml b/pyproject.toml index 3dbd506a55d..7b7b1d6d026 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,6 +41,9 @@ extend-ignore = [ "B905", # `zip()` without an explicit `strict=` parameter "N802", # Function name should be lowercase "N999", # Invalid module name. We should revisit this in the future, TODO + "RUF012", # Mutable class attributes should be annotated with `typing.ClassVar` TODO + "S310", # Audit URL open for permitted schemes. Allowing use of `file:` or custom schemes is often unexpected. + "S603", # `subprocess` call: check for execution of untrusted input ] extend-exclude = ["docs", "build"] # Hardcode to Python 3.8. diff --git a/tests/test_import_namespace.py b/tests/test_import_namespace.py index 09c8162d26a..f9489711004 100644 --- a/tests/test_import_namespace.py +++ b/tests/test_import_namespace.py @@ -5,29 +5,29 @@ def test_import(): import mesa.flat as mf from mesa.time import RandomActivation - mesa.time.RandomActivation - RandomActivation - mf.RandomActivation + _ = mesa.time.RandomActivation + _ = RandomActivation + _ = mf.RandomActivation from mesa.space import MultiGrid - mesa.space.MultiGrid - MultiGrid - mf.MultiGrid + _ = mesa.space.MultiGrid + _ = MultiGrid + _ = mf.MultiGrid from mesa.visualization.ModularVisualization import ModularServer - mesa.visualization.ModularServer - ModularServer - mf.ModularServer + _ = mesa.visualization.ModularServer + _ = ModularServer + _ = mf.ModularServer from mesa.datacollection import DataCollector - DataCollector - mesa.DataCollector - mf.DataCollector + _ = DataCollector + _ = mesa.DataCollector + _ = mf.DataCollector from mesa.batchrunner import batch_run - batch_run - mesa.batch_run + _ = batch_run + _ = mesa.batch_run From 65c5e6c28249c6e7e39595adce59761811dd81da Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 4 Jul 2023 07:13:12 +0000 Subject: [PATCH 129/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.4.0 → v3.8.0](https://github.com/asottile/pyupgrade/compare/v3.4.0...v3.8.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 93e82fc2f96..662b22db6a7 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.4.0 + rev: v3.8.0 hooks: - id: pyupgrade args: [--py38-plus] From 5aa03efeab5824343e67f6a4c0854d278d0ab911 Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 29 Jun 2023 07:37:15 -0400 Subject: [PATCH 130/214] Initialize Solara-based adv_tutorial --- docs/index.rst | 1 + .../tutorials/adv_tutorial_experimental.ipynb | 276 ++++++++++++++++++ 2 files changed, 277 insertions(+) create mode 100644 docs/tutorials/adv_tutorial_experimental.ipynb diff --git a/docs/index.rst b/docs/index.rst index 5388c1712ff..221fd0fdaa8 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -101,6 +101,7 @@ ABM features users have shared that you may want to use in your model Useful Snippets API Documentation Mesa Packages + tutorials/adv_tutorial_experimental.ipynb Indices and tables ================== diff --git a/docs/tutorials/adv_tutorial_experimental.ipynb b/docs/tutorials/adv_tutorial_experimental.ipynb new file mode 100644 index 00000000000..8c4967f1158 --- /dev/null +++ b/docs/tutorials/adv_tutorial_experimental.ipynb @@ -0,0 +1,276 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Advanced Tutorial (Experimental)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To execute this tutorial online: [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/adv_tutorial_experimental.ipynb)\n", + "\n", + "### Adding visualization\n", + "\n", + "So far, we've built a model, run it, and analyzed some output afterwards. However, one of the advantages of agent-based models is that we can often watch them run step by step, potentially spotting unexpected patterns, behaviors or bugs, or developing new intuitions, hypotheses, or insights. Other times, watching a model run can explain it to an unfamiliar audience better than static explanations. Like many ABM frameworks, Mesa allows you to create an interactive visualization of the model. In this section we'll walk through creating a visualization using built-in components, and (for advanced users) how to create a new visualization element.\n", + "\n", + "First, a quick explanation of how Mesa's interactive visualization works. The visualization is done in a browser window, using the [Solara](https://solara.dev/) framework, a pure Python, React-style web framework. Running `solara run app.py` will launch a web server, which runs the model, and displays model detail at each step via the Matplotlib plotting library." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Grid Visualization\n", + "\n", + "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n", + "Next, in a new source file (e.g. `MoneyModel_Viz.py`) include the code shown in the following cells to run and avoid Jupyter compatibility issue." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "%pip install --quiet mesa\n", + "import mesa\n", + "\n", + "# If MoneyModel.py is where your code is, do this instead:\n", + "# from MoneyModel import MoneyModel\n", + "\n", + "# To make this tutorial notebook executable in Binder/Colab,\n", + "# we install mesa_models.\n", + "%pip install --quiet -U -e git+https://github.com/projectmesa/mesa-examples#egg=mesa-models\n", + "%pip install --quiet solara\n", + "from mesa_models.boltzmann_wealth_model.model import BoltzmannWealthModel" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mesa's grid visualizer works by looping over every cell in a grid, and generating a portrayal for every agent it finds. A portrayal is a dictionary (which can easily be turned into a JSON object) which tells Matplotlib the color and size of the scatterplot markers (each signifying an agent). The only thing we need to provide is a function which takes an agent, and returns a portrayal dictionary. Here's the simplest one: it'll draw each agent as a blue, filled circle, with a radius size of 50." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def agent_portrayal(agent):\n", + " return {\n", + " \"color\": \"tab:blue\",\n", + " \"size\": 50,\n", + " }" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition to the portrayal method, we instantiate the model parameters, some of which are modifiable by user inputs. In this case, the number of agents, N, is specified as a slider of integers." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "model_params = {\n", + " \"N\": {\n", + " \"type\": \"SliderInt\",\n", + " \"value\": 50,\n", + " \"label\": \"Number of agents:\",\n", + " \"min\": 10,\n", + " \"max\": 100,\n", + " \"step\": 1,\n", + " },\n", + " \"width\": 10,\n", + " \"height\": 10,\n", + "}" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we instantiate the visualization object which (by default) displays the grid containing the agents, and timeseries of of values computed by the model's data collector. In this example, we specify the Gini coefficient.\n", + "\n", + "There are 3 buttons:\n", + "- the step button, which advances the model by 1 step\n", + "- the play button, which advances the model indefinitely until it is paused, or until `model.running` is False (you may specify the stopping condition)\n", + "- the pause button, which pauses the model\n", + "\n", + "To reset the model, simply change the model parameter from the user input (e.g. the \"Number of agents\" slider)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "from mesa_models.experimental import JupyterViz\n", + "\n", + "page = JupyterViz(\n", + " BoltzmannWealthModel,\n", + " model_params,\n", + " measures=[\"Gini\"],\n", + " name=\"Money Model\",\n", + " agent_portrayal=agent_portrayal,\n", + ")\n", + "# This is required to render the visualization in the Jupyter notebook\n", + "page" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Changing the agents\n", + "\n", + "In the visualization above, all we could see is the agents moving around -- but not how much money they had, or anything else of interest. Let's change it so that agents who are broke (wealth 0) are drawn in red, smaller. (TODO: currently, we can't predict the drawing order of the circles, so a broke agent may be overshadowed by a wealthy agent. We should fix this by doing a hollow circle instead)\n", + "\n", + "To do this, we go back to our `agent_portrayal` code and add some code to change the portrayal based on the agent properties and launch the server again." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "def agent_portrayal(agent):\n", + " size = 10\n", + " color = \"tab:red\"\n", + " if agent.wealth > 0:\n", + " size = 50\n", + " color = \"tab:blue\"\n", + " return {\"size\": size, \"color\": color}" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "page = JupyterViz(\n", + " BoltzmannWealthModel,\n", + " model_params,\n", + " measures=[\"Gini\"],\n", + " name=\"Money Model\",\n", + " agent_portrayal=agent_portrayal,\n", + ")\n", + "# This is required to render the visualization in the Jupyter notebook\n", + "page" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Building your own visualization component\n", + "\n", + "**Note:** This section is for users who have a basic familiarity with Python's Matplotlib plotting library.\n", + "\n", + "If the visualization elements provided by Mesa aren't enough for you, you can build your own and plug them into the model server.\n", + "\n", + "For this example, let's build a simple histogram visualization, which can count the number of agents with each value of wealth." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import solara\n", + "from matplotlib.figure import Figure\n", + "\n", + "\n", + "def make_histogram(viz):\n", + " # Note: you must initialize a figure using this method instead of\n", + " # plt.figure(), for thread safety purpose\n", + " fig = Figure()\n", + " ax = fig.subplots()\n", + " wealth_vals = [agent.wealth for agent in viz.model.schedule.agents]\n", + " # Note: you have to use Matplotlib's OOP API instead of plt.hist\n", + " # because plt.hist is not thread-safe.\n", + " ax.hist(wealth_vals, bins=10)\n", + " # You have to specify the dependencies as follows, so that the figure\n", + " # auto-updates when viz.model or viz.df is changed.\n", + " solara.FigureMatplotlib(fig, dependencies=[viz.model, viz.df])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Next, we reinitialize the visualization object, but this time with the histogram (see the measures argument)." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "page = JupyterViz(\n", + " BoltzmannWealthModel,\n", + " model_params,\n", + " measures=[\"Gini\", make_histogram],\n", + " name=\"Money Model\",\n", + " agent_portrayal=agent_portrayal,\n", + ")\n", + "# This is required to render the visualization in the Jupyter notebook\n", + "page" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Happy Modeling!\n", + "\n", + "This document is a work in progress. If you see any errors, exclusions or have any problems please contact [us](https://github.com/projectmesa/mesa/issues)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "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.10.12" + }, + "nbsphinx": { + "execute": "never" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 1c76cb3e46efa3d7e8e8e2a85b00f007957943cf Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 8 Jul 2023 08:52:43 -0400 Subject: [PATCH 131/214] Rename advanced tutorial files --- docs/index.rst | 2 +- docs/tutorials/adv_tutorial.ipynb | 470 ++++------------ .../tutorials/adv_tutorial_experimental.ipynb | 276 ---------- docs/tutorials/adv_tutorial_legacy.ipynb | 521 ++++++++++++++++++ 4 files changed, 635 insertions(+), 634 deletions(-) delete mode 100644 docs/tutorials/adv_tutorial_experimental.ipynb create mode 100644 docs/tutorials/adv_tutorial_legacy.ipynb diff --git a/docs/index.rst b/docs/index.rst index 221fd0fdaa8..b8478eda996 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -101,7 +101,7 @@ ABM features users have shared that you may want to use in your model Useful Snippets API Documentation Mesa Packages - tutorials/adv_tutorial_experimental.ipynb + tutorials/adv_tutorial_legacy.ipynb Indices and tables ================== diff --git a/docs/tutorials/adv_tutorial.ipynb b/docs/tutorials/adv_tutorial.ipynb index 49be784cb38..8d4f9fd26be 100644 --- a/docs/tutorials/adv_tutorial.ipynb +++ b/docs/tutorials/adv_tutorial.ipynb @@ -8,23 +8,19 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ + "To execute this tutorial online: [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/adv_tutorial_experimental.ipynb)\n", + "\n", "### Adding visualization\n", "\n", "So far, we've built a model, run it, and analyzed some output afterwards. However, one of the advantages of agent-based models is that we can often watch them run step by step, potentially spotting unexpected patterns, behaviors or bugs, or developing new intuitions, hypotheses, or insights. Other times, watching a model run can explain it to an unfamiliar audience better than static explanations. Like many ABM frameworks, Mesa allows you to create an interactive visualization of the model. In this section we'll walk through creating a visualization using built-in components, and (for advanced users) how to create a new visualization element.\n", "\n", - "**Note for Jupyter users: Due to conflicts with the tornado server Mesa uses and Jupyter, the interactive browser of your model will load but likely not work. This will require you to use run the code from .py files. The Mesa development team is working to develop a** [Jupyter compatible interface](https://github.com/projectmesa/mesa/issues/1363).\n", - "\n", - "First, a quick explanation of how Mesa's interactive visualization works. Visualization is done in a browser window, using JavaScript to draw the different things being visualized at each step of the model. To do this, Mesa launches a small web server, which runs the model, turns each step into a JSON object (essentially, structured plain text) and sends those steps to the browser.\n", - "\n", - "A visualization is built up of a few different modules: for example, a module for drawing agents on a grid, and another one for drawing a chart of some variable. Each module has a Python part, which runs on the server and turns a model state into JSON data; and a JavaScript side, which takes that JSON data and draws it in the browser window. Mesa comes with a few modules built in, and let you add your own as well." + "First, a quick explanation of how Mesa's interactive visualization works. The visualization is done in a browser window, using the [Solara](https://solara.dev/) framework, a pure Python, React-style web framework. Running `solara run app.py` will launch a web server, which runs the model, and displays model detail at each step via the Matplotlib plotting library." ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -36,454 +32,211 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ - "# If MoneyModel.py is where your code is:\n", - "from MoneyModel import mesa, MoneyModel" + "%pip install --quiet mesa\n", + "import mesa\n", + "\n", + "# If MoneyModel.py is where your code is, do this instead:\n", + "# from MoneyModel import MoneyModel\n", + "\n", + "# To make this tutorial notebook executable in Binder/Colab,\n", + "# we install mesa_models.\n", + "%pip install --quiet -U -e git+https://github.com/projectmesa/mesa-examples#egg=mesa-models\n", + "%pip install --quiet solara\n", + "from mesa_models.boltzmann_wealth_model.model import BoltzmannWealthModel" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Mesa's `CanvasGrid` visualization class works by looping over every cell in a grid, and generating a portrayal for every agent it finds. A portrayal is a dictionary (which can easily be turned into a JSON object) which tells the JavaScript side how to draw it. The only thing we need to provide is a function which takes an agent, and returns a portrayal object. Here's the simplest one: it'll draw each agent as a red, filled circle which fills half of each cell." + "Mesa's grid visualizer works by looping over every cell in a grid, and generating a portrayal for every agent it finds. A portrayal is a dictionary (which can easily be turned into a JSON object) which tells Matplotlib the color and size of the scatterplot markers (each signifying an agent). The only thing we need to provide is a function which takes an agent, and returns a portrayal dictionary. Here's the simplest one: it'll draw each agent as a blue, filled circle, with a radius size of 50." ] }, { "cell_type": "code", - "execution_count": 2, + "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "def agent_portrayal(agent):\n", - " portrayal = {\n", - " \"Shape\": \"circle\",\n", - " \"Color\": \"red\",\n", - " \"Filled\": \"true\",\n", - " \"Layer\": 0,\n", - " \"r\": 0.5,\n", - " }\n", - " return portrayal" + " return {\n", + " \"color\": \"tab:blue\",\n", + " \"size\": 50,\n", + " }" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "In addition to the portrayal method, we instantiate a canvas grid with its width and height in cells, and in pixels. In this case, let's create a 10x10 grid, drawn in 500 x 500 pixels." + "In addition to the portrayal method, we instantiate the model parameters, some of which are modifiable by user inputs. In this case, the number of agents, N, is specified as a slider of integers." ] }, { "cell_type": "code", - "execution_count": 3, - "metadata": { - "tags": [] - }, + "execution_count": null, + "metadata": {}, "outputs": [], "source": [ - "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)" + "model_params = {\n", + " \"N\": {\n", + " \"type\": \"SliderInt\",\n", + " \"value\": 50,\n", + " \"label\": \"Number of agents:\",\n", + " \"min\": 10,\n", + " \"max\": 100,\n", + " \"step\": 1,\n", + " },\n", + " \"width\": 10,\n", + " \"height\": 10,\n", + "}" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Now we create and launch the actual server. We do this with the following arguments:\n", + "Next, we instantiate the visualization object which (by default) displays the grid containing the agents, and timeseries of of values computed by the model's data collector. In this example, we specify the Gini coefficient.\n", "\n", - "* The model class we're running and visualizing; in this case, `MoneyModel`.\n", - "* A list of module objects to include in the visualization; here, just `[grid]`\n", - "* The title of the model: \"Money Model\"\n", - "* Any inputs or arguments for the model itself. In this case, 100 agents, and height and width of 10.\n", + "There are 3 buttons:\n", + "- the step button, which advances the model by 1 step\n", + "- the play button, which advances the model indefinitely until it is paused, or until `model.running` is False (you may specify the stopping condition)\n", + "- the pause button, which pauses the model\n", "\n", - "Once we create the server, we set the port (use default 8521 here) for it to listen on (you can treat this as just a piece of the URL you’ll open in the browser). " + "To reset the model, simply change the model parameter from the user input (e.g. the \"Number of agents\" slider)." ] }, { "cell_type": "code", - "execution_count": 4, - "metadata": {}, + "execution_count": null, + "metadata": { + "tags": [] + }, "outputs": [], "source": [ - "server = mesa.visualization.ModularServer(\n", - " MoneyModel, [grid], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", + "from mesa_models.experimental import JupyterViz\n", + "\n", + "page = JupyterViz(\n", + " BoltzmannWealthModel,\n", + " model_params,\n", + " measures=[\"Gini\"],\n", + " name=\"Money Model\",\n", + " agent_portrayal=agent_portrayal,\n", ")\n", - "server.port = 8521 # the default" + "# This is required to render the visualization in the Jupyter notebook\n", + "page" ] }, { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Finally, when you’re ready to run the visualization, use the server’s launch() method." - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "The full code for source file `MoneyModel_Viz.py` should now look like:\n", - "\n", - "```python\n", - "from MoneyModel import mesa, MoneyModel\n", - "\n", - "\n", - "def agent_portrayal(agent):\n", - " portrayal = {\"Shape\": \"circle\",\n", - " \"Filled\": \"true\",\n", - " \"Layer\": 0,\n", - " \"Color\": \"red\",\n", - " \"r\": 0.5}\n", - " return portrayal\n", - "\n", - "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)\n", - "server = mesa.visualization.ModularServer(MoneyModel,\n", - " [grid],\n", - " \"Money Model\",\n", - " {\"N\":100, \"width\":10, \"height\":10})\n", - "server.port = 8521 # The default\n", - "server.launch()\n", - "```\n", - "Now run this file; this should launch the interactive visualization server and open your web browser automatically. (If the browser doesn't open automatically, try pointing it at [http://127.0.0.1:8521](http://127.0.0.1:8521) manually. If this doesn't show you the visualization, something may have gone wrong with the server launch.)\n", - "\n", - "You should see something like the figure below: the model title, an empty space where the grid will be, and a control panel off to the right.\n", - "\n", - "![Empty Visualization](files/viz_empty.png)\n", - "\n", - "Click the `Reset` button on the control panel, and you should see the grid fill up with red circles, representing agents.\n", - "\n", - "![Redcircles Visualization](files/viz_redcircles.png)\n", - "\n", - "Click `Step` to advance the model by one step, and the agents will move around. Click `Start` and the agents will keep moving around, at the rate set by the 'fps' (frames per second) slider at the top. Try moving it around and see how the speed of the model changes. Pressing `Stop` will pause the model; presing `Start` again will restart it. Finally, `Reset` will start a new instantiation of the model.\n", - "\n", - "To stop the visualization server, go back to the terminal where you launched it, and press Control+c." - ] - }, - { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ "#### Changing the agents\n", "\n", - "In the visualization above, all we could see is the agents moving around -- but not how much money they had, or anything else of interest. Let's change it so that agents who are broke (wealth 0) are drawn in grey, smaller, and above agents who still have money.\n", + "In the visualization above, all we could see is the agents moving around -- but not how much money they had, or anything else of interest. Let's change it so that agents who are broke (wealth 0) are drawn in red, smaller. (TODO: currently, we can't predict the drawing order of the circles, so a broke agent may be overshadowed by a wealthy agent. We should fix this by doing a hollow circle instead)\n", "\n", "To do this, we go back to our `agent_portrayal` code and add some code to change the portrayal based on the agent properties and launch the server again." ] }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def agent_portrayal(agent):\n", - " portrayal = {\"Shape\": \"circle\", \"Filled\": \"true\", \"r\": 0.5}\n", - "\n", + " size = 10\n", + " color = \"tab:red\"\n", " if agent.wealth > 0:\n", - " portrayal[\"Color\"] = \"red\"\n", - " portrayal[\"Layer\"] = 0\n", - " else:\n", - " portrayal[\"Color\"] = \"grey\"\n", - " portrayal[\"Layer\"] = 1\n", - " portrayal[\"r\"] = 0.2\n", - " return portrayal" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "This will open a new browser window pointed at the updated visualization. Initially it looks the same, but advance the model and smaller grey circles start to appear. Note that since the zero-wealth agents have a higher layer number, they are drawn on top of the red agents.\n", - "\n", - "![Greycircles Visualization](files/viz_greycircles.png)" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Adding a chart\n", - "\n", - "Next, let's add another element to the visualization: a chart, tracking the model's Gini Coefficient. This is another built-in element that Mesa provides.\n", - "\n", - "The basic chart pulls data from the model's DataCollector, and draws it as a line graph using the [Charts.js](http://www.chartjs.org/) JavaScript libraries. We instantiate a chart element with a list of series for the chart to track. Each series is defined in a dictionary, and has a `Label` (which must match the name of a model-level variable collected by the DataCollector) and a `Color` name. We can also give the chart the name of the DataCollector object in the model.\n", - "\n", - "Finally, we add the chart to the list of elements in the server. The elements are added to the visualization in the order they appear, so the chart will appear underneath the grid." + " size = 50\n", + " color = \"tab:blue\"\n", + " return {\"size\": size, \"color\": color}" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": null, "metadata": {}, "outputs": [], "source": [ - "chart = mesa.visualization.ChartModule(\n", - " [{\"Label\": \"Gini\", \"Color\": \"Black\"}], data_collector_name=\"datacollector\"\n", + "page = JupyterViz(\n", + " BoltzmannWealthModel,\n", + " model_params,\n", + " measures=[\"Gini\"],\n", + " name=\"Money Model\",\n", + " agent_portrayal=agent_portrayal,\n", ")\n", - "\n", - "server = mesa.visualization.ModularServer(\n", - " MoneyModel, [grid, chart], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", - ")" + "# This is required to render the visualization in the Jupyter notebook\n", + "page" ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ - "Launch the visualization and start a model run, either by launching the server here or through the full code for source file `MoneyModel_Viz.py`.\n", - "\n", - "```python\n", - "from MoneyModel import mesa, MoneyModel\n", - "\n", - "\n", - "def agent_portrayal(agent):\n", - " portrayal = {\"Shape\": \"circle\", \"Filled\": \"true\", \"r\": 0.5}\n", - "\n", - " if agent.wealth > 0:\n", - " portrayal[\"Color\"] = \"red\"\n", - " portrayal[\"Layer\"] = 0\n", - " else:\n", - " portrayal[\"Color\"] = \"grey\"\n", - " portrayal[\"Layer\"] = 1\n", - " portrayal[\"r\"] = 0.2\n", - " return portrayal\n", - "\n", + "### Building your own visualization component\n", "\n", - "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)\n", - "chart = mesa.visualization.ChartModule(\n", - " [{\"Label\": \"Gini\", \"Color\": \"Black\"}], data_collector_name=\"datacollector\"\n", - ")\n", + "**Note:** This section is for users who have a basic familiarity with Python's Matplotlib plotting library.\n", "\n", - "server = mesa.visualization.ModularServer(\n", - " MoneyModel, [grid, chart], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", - ")\n", - "server.port = 8521 # The default\n", - "server.launch()\n", - "```" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "You'll see a line chart underneath the grid. Every step of the model, the line chart updates along with the grid. Reset the model, and the chart resets too.\n", - "\n", - "![Chart Visualization](files/viz_chart.png)\n", + "If the visualization elements provided by Mesa aren't enough for you, you can build your own and plug them into the model server.\n", "\n", - "**Note:** You might notice that the chart line only starts after a couple of steps; this is due to a bug in Charts.js which will hopefully be fixed soon." + "For this example, let's build a simple histogram visualization, which can count the number of agents with each value of wealth." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "### Building your own visualization component\n", - "\n", - "**Note:** This section is for users who have a basic familiarity with JavaScript. If that's not you, don't worry! (If you're an advanced JavaScript coder and find things that we've done wrong or inefficiently, please [let us know](https://github.com/projectmesa/mesa/issues)!)\n", - "\n", - "If the visualization elements provided by Mesa aren't enough for you, you can build your own and plug them into the model server.\n", - "\n", - "First, you need to understand how the visualization works under the hood. Remember that each visualization module has two sides: a Python object that runs on the server and generates JSON data from the model state (the server side), and a JavaScript object that runs in the browser and turns the JSON into something it renders on the screen (the client side).\n", - "\n", - "Obviously, the two sides of each visualization must be designed in tandem. They result in one Python class, and one JavaScript `.js` file. The path to the JavaScript file is a property of the Python class.\n", - "\n", - "For this example, let's build a simple histogram visualization, which can count the number of agents with each value of wealth. We'll use the [Charts.js](http://www.chartjs.org/) JavaScript library, which is already included with Mesa. If you go and look at its documentation, you'll see that it had no histogram functionality, which means we have to build our own out of a bar chart. We'll keep the histogram as simple as possible, giving it a fixed number of integer bins. If you were designing a more general histogram to add to the Mesa repository for everyone to use across different models, obviously you'd want something more general." + "import solara\n", + "from matplotlib.figure import Figure\n", + "\n", + "\n", + "def make_histogram(viz):\n", + " # Note: you must initialize a figure using this method instead of\n", + " # plt.figure(), for thread safety purpose\n", + " fig = Figure()\n", + " ax = fig.subplots()\n", + " wealth_vals = [agent.wealth for agent in viz.model.schedule.agents]\n", + " # Note: you have to use Matplotlib's OOP API instead of plt.hist\n", + " # because plt.hist is not thread-safe.\n", + " ax.hist(wealth_vals, bins=10)\n", + " # You have to specify the dependencies as follows, so that the figure\n", + " # auto-updates when viz.model or viz.df is changed.\n", + " solara.FigureMatplotlib(fig, dependencies=[viz.model, viz.df])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ - "#### Client-Side Code\n", - "\n", - "In general, the server- and client-side are written in tandem. However, if you're like me and more comfortable with Python than JavaScript, it makes sense to figure out how to get the JavaScript working first, and then write the Python to be compatible with that.\n", - "\n", - "In the same directory as your model, create a new file called `HistogramModule.js`. This will store the JavaScript code for the client side of the new module.\n", - "\n", - "JavaScript classes can look alien to people coming from other languages -- specifically, they can look like functions. (The Mozilla [Introduction to Object-Oriented JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript) is a good starting point). In `HistogramModule.js`, start by creating the class itself:\n", - "\n", - "```javascript\n", - "const HistogramModule = function(bins, canvas_width, canvas_height) {\n", - " // The actual code will go here.\n", - "};\n", - "```\n", - "\n", - "Note that our object is instantiated with three arguments: the number of integer bins, and the width and height (in pixels) the chart will take up in the visualization window.\n", - "\n", - "When the visualization object is instantiated, the first thing it needs to do is prepare to draw on the current page. To do so, it adds a [canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) tag to the page. It also gets the canvas' context, which is required for doing anything with it.\n", - "\n", - "```javascript\n", - "const HistogramModule = function(bins, canvas_width, canvas_height) {\n", - " // Create the canvas object:\n", - " const canvas = document.createElement(\"canvas\");\n", - " Object.assign(canvas, {\n", - " width: canvas_width,\n", - " height: canvas_height,\n", - " style: \"border:1px dotted\",\n", - " });\n", - " // Append it to #elements:\n", - " const elements = document.getElementById(\"elements\");\n", - " elements.appendChild(canvas);\n", - "\n", - " // Create the context and the drawing controller:\n", - " const context = canvas.getContext(\"2d\");\n", - "};\n", - "```\n", - "\n", - "Look at the Charts.js [bar chart documentation](http://www.chartjs.org/docs/#bar-chart-introduction). You'll see some of the boilerplate needed to get a chart set up. Especially important is the `data` object, which includes the datasets, labels, and color options. In this case, we want just one dataset (we'll keep things simple and name it \"Data\"); it has `bins` for categories, and the value of each category starts out at zero. Finally, using these boilerplate objects and the canvas context we created, we can create the chart object.\n", - "\n", - "```javascript\n", - "const HistogramModule = function(bins, canvas_width, canvas_height) {\n", - " // Create the canvas object:\n", - " const canvas = document.createElement(\"canvas\");\n", - " Object.assign(canvas, {\n", - " width: canvas_width,\n", - " height: canvas_height,\n", - " style: \"border:1px dotted\",\n", - " });\n", - " // Append it to #elements:\n", - " const elements = document.getElementById(\"elements\");\n", - " elements.appendChild(canvas);\n", - "\n", - " // Create the context and the drawing controller:\n", - " const context = canvas.getContext(\"2d\");\n", - "\n", - " // Prep the chart properties and series:\n", - " const datasets = [{\n", - " label: \"Data\",\n", - " fillColor: \"rgba(151,187,205,0.5)\",\n", - " strokeColor: \"rgba(151,187,205,0.8)\",\n", - " highlightFill: \"rgba(151,187,205,0.75)\",\n", - " highlightStroke: \"rgba(151,187,205,1)\",\n", - " data: []\n", - " }];\n", - "\n", - " // Add a zero value for each bin\n", - " for (var i in bins)\n", - " datasets[0].data.push(0);\n", - "\n", - " const data = {\n", - " labels: bins,\n", - " datasets: datasets\n", - " };\n", - "\n", - " const options = {\n", - " scaleBeginsAtZero: true\n", - " };\n", - "\n", - " // Create the chart object\n", - " let chart = new Chart(context, {type: 'bar', data: data, options: options});\n", - "\n", - " // Now what?\n", - "};\n", - "```\n", - "\n", - "There are two methods every client-side visualization class must implement to be able to work: `render(data)` to render the incoming data, and `reset()` which is called to clear the visualization when the user hits the reset button and starts a new model run.\n", - "\n", - "In this case, the easiest way to pass data to the histogram is as an array, one value for each bin. We can then just loop over the array and update the values in the chart's dataset.\n", - "\n", - "There are a few ways to reset the chart, but the easiest is probably to destroy it and create a new chart object in its place.\n", - "\n", - "With that in mind, we can add these two methods to the class:\n", - "\n", - "```javascript\n", - "const HistogramModule = function(bins, canvas_width, canvas_height) {\n", - " // ...Everything from above...\n", - " this.render = function(data) {\n", - " datasets[0].data = data;\n", - " chart.update();\n", - " };\n", - "\n", - " this.reset = function() {\n", - " chart.destroy();\n", - " chart = new Chart(context, {type: 'bar', data: data, options: options});\n", - " };\n", - "};\n", - "```\n", - "\n", - "Note the `this`. before the method names. This makes them public and ensures that they are accessible outside of the object itself. All the other variables inside the class are only accessible inside the object itself, but not outside of it." + "Next, we reinitialize the visualization object, but this time with the histogram (see the measures argument)." ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "#### Server-Side Code\n", - "\n", - "Can we get back to Python code? Please?\n", - "\n", - "Every JavaScript visualization element has an equal and opposite server-side Python element. The Python class needs to also have a `render` method, to get data out of the model object and into a JSON-ready format. It also needs to point towards the code where the relevant JavaScript lives, and add the JavaScript object to the model page.\n", - "\n", - "In a Python file (either its own, or in the same file as your visualization code), import the `VisualizationElement` class we'll inherit from, and create the new visualization class.\n", - "\n", - "```python\n", - " from mesa.visualization.ModularVisualization import VisualizationElement, CHART_JS_FILE\n", - "\n", - " class HistogramModule(VisualizationElement):\n", - " package_includes = [CHART_JS_FILE]\n", - " local_includes = [\"HistogramModule.js\"]\n", - "\n", - " def __init__(self, bins, canvas_height, canvas_width):\n", - " self.canvas_height = canvas_height\n", - " self.canvas_width = canvas_width\n", - " self.bins = bins\n", - " new_element = \"new HistogramModule({}, {}, {})\"\n", - " new_element = new_element.format(bins, \n", - " canvas_width, \n", - " canvas_height)\n", - " self.js_code = \"elements.push(\" + new_element + \");\"\n", - "```\n", - "\n", - "There are a few things going on here. `package_includes` is a list of JavaScript files that are part of Mesa itself that the visualization element relies on. You can see the included files in [mesa/visualization/templates/](https://github.com/projectmesa/mesa/tree/main/mesa/visualization/templates). Similarly, `local_includes` is a list of JavaScript files in the same directory as the class code itself. Note that both of these are class variables, not object variables -- they hold for all particular objects.\n", - "\n", - "Next, look at the `__init__` method. It takes three arguments: the number of bins, and the width and height for the histogram. It then uses these values to populate the `js_code` property; this is code that the server will insert into the visualization page, which will run when the page loads. In this case, it creates a new HistogramModule (the class we created in JavaScript in the step above) with the desired bins, width and height; it then appends (`push`es) this object to `elements`, the list of visualization elements that the visualization page itself maintains.\n", - "\n", - "Now, the last thing we need is the `render` method. If we were making a general-purpose visualization module we'd want this to be more general, but in this case we can hard-code it to our model.\n", - "\n", - "```python\n", - "import numpy as np\n", - "\n", - "class HistogramModule(VisualizationElement):\n", - " # ... Everything from above...\n", - "\n", - " def render(self, model):\n", - " wealth_vals = [agent.wealth for agent in model.schedule.agents]\n", - " hist = np.histogram(wealth_vals, bins=self.bins)[0]\n", - " return [int(x) for x in hist]\n", - "```\n", - "\n", - "Every time the render method is called (with a model object as the argument) it uses numpy to generate counts of agents with each wealth value in the bins, and then returns a list of these values. Note that the `render` method doesn't return a JSON string -- just an object that can be turned into JSON, in this case a Python list (with Python integers as the values; the `json` library doesn't like dealing with numpy's integer type).\n", - "\n", - "Now, you can create your new HistogramModule and add it to the server:\n", - "\n", - "```python\n", - " histogram = mesa.visualization.HistogramModule(list(range(10)), 200, 500)\n", - " server = mesa.visualization.ModularServer(MoneyModel, \n", - " [grid, histogram, chart], \n", - " \"Money Model\", \n", - " {\"N\":100, \"width\":10, \"height\":10})\n", - " server.launch()\n", - "```\n", - "\n", - "Run this code, and you should see your brand-new histogram added to the visualization and updating along with the model!\n", - "\n", - "![Histogram Visualization](files/viz_histogram.png)\n", - "\n", - "If you've felt comfortable with this section, it might be instructive to read the code for the [ModularServer](https://github.com/projectmesa/mesa/blob/main/mesa/visualization/ModularVisualization.py#L259) and the [modular_template](https://github.com/projectmesa/mesa/blob/main/mesa/visualization/templates/modular_template.html) to get a better idea of how all the pieces fit together." + "page = JupyterViz(\n", + " BoltzmannWealthModel,\n", + " model_params,\n", + " measures=[\"Gini\", make_histogram],\n", + " name=\"Money Model\",\n", + " agent_portrayal=agent_portrayal,\n", + ")\n", + "# This is required to render the visualization in the Jupyter notebook\n", + "page" ] }, { @@ -498,7 +251,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -512,7 +265,10 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.10.12" + }, + "nbsphinx": { + "execute": "never" } }, "nbformat": 4, diff --git a/docs/tutorials/adv_tutorial_experimental.ipynb b/docs/tutorials/adv_tutorial_experimental.ipynb deleted file mode 100644 index 8c4967f1158..00000000000 --- a/docs/tutorials/adv_tutorial_experimental.ipynb +++ /dev/null @@ -1,276 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Advanced Tutorial (Experimental)" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "To execute this tutorial online: [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/adv_tutorial_experimental.ipynb)\n", - "\n", - "### Adding visualization\n", - "\n", - "So far, we've built a model, run it, and analyzed some output afterwards. However, one of the advantages of agent-based models is that we can often watch them run step by step, potentially spotting unexpected patterns, behaviors or bugs, or developing new intuitions, hypotheses, or insights. Other times, watching a model run can explain it to an unfamiliar audience better than static explanations. Like many ABM frameworks, Mesa allows you to create an interactive visualization of the model. In this section we'll walk through creating a visualization using built-in components, and (for advanced users) how to create a new visualization element.\n", - "\n", - "First, a quick explanation of how Mesa's interactive visualization works. The visualization is done in a browser window, using the [Solara](https://solara.dev/) framework, a pure Python, React-style web framework. Running `solara run app.py` will launch a web server, which runs the model, and displays model detail at each step via the Matplotlib plotting library." - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Grid Visualization\n", - "\n", - "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n", - "Next, in a new source file (e.g. `MoneyModel_Viz.py`) include the code shown in the following cells to run and avoid Jupyter compatibility issue." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "%pip install --quiet mesa\n", - "import mesa\n", - "\n", - "# If MoneyModel.py is where your code is, do this instead:\n", - "# from MoneyModel import MoneyModel\n", - "\n", - "# To make this tutorial notebook executable in Binder/Colab,\n", - "# we install mesa_models.\n", - "%pip install --quiet -U -e git+https://github.com/projectmesa/mesa-examples#egg=mesa-models\n", - "%pip install --quiet solara\n", - "from mesa_models.boltzmann_wealth_model.model import BoltzmannWealthModel" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Mesa's grid visualizer works by looping over every cell in a grid, and generating a portrayal for every agent it finds. A portrayal is a dictionary (which can easily be turned into a JSON object) which tells Matplotlib the color and size of the scatterplot markers (each signifying an agent). The only thing we need to provide is a function which takes an agent, and returns a portrayal dictionary. Here's the simplest one: it'll draw each agent as a blue, filled circle, with a radius size of 50." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "def agent_portrayal(agent):\n", - " return {\n", - " \"color\": \"tab:blue\",\n", - " \"size\": 50,\n", - " }" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "In addition to the portrayal method, we instantiate the model parameters, some of which are modifiable by user inputs. In this case, the number of agents, N, is specified as a slider of integers." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "model_params = {\n", - " \"N\": {\n", - " \"type\": \"SliderInt\",\n", - " \"value\": 50,\n", - " \"label\": \"Number of agents:\",\n", - " \"min\": 10,\n", - " \"max\": 100,\n", - " \"step\": 1,\n", - " },\n", - " \"width\": 10,\n", - " \"height\": 10,\n", - "}" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we instantiate the visualization object which (by default) displays the grid containing the agents, and timeseries of of values computed by the model's data collector. In this example, we specify the Gini coefficient.\n", - "\n", - "There are 3 buttons:\n", - "- the step button, which advances the model by 1 step\n", - "- the play button, which advances the model indefinitely until it is paused, or until `model.running` is False (you may specify the stopping condition)\n", - "- the pause button, which pauses the model\n", - "\n", - "To reset the model, simply change the model parameter from the user input (e.g. the \"Number of agents\" slider)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": { - "tags": [] - }, - "outputs": [], - "source": [ - "from mesa_models.experimental import JupyterViz\n", - "\n", - "page = JupyterViz(\n", - " BoltzmannWealthModel,\n", - " model_params,\n", - " measures=[\"Gini\"],\n", - " name=\"Money Model\",\n", - " agent_portrayal=agent_portrayal,\n", - ")\n", - "# This is required to render the visualization in the Jupyter notebook\n", - "page" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "#### Changing the agents\n", - "\n", - "In the visualization above, all we could see is the agents moving around -- but not how much money they had, or anything else of interest. Let's change it so that agents who are broke (wealth 0) are drawn in red, smaller. (TODO: currently, we can't predict the drawing order of the circles, so a broke agent may be overshadowed by a wealthy agent. We should fix this by doing a hollow circle instead)\n", - "\n", - "To do this, we go back to our `agent_portrayal` code and add some code to change the portrayal based on the agent properties and launch the server again." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "def agent_portrayal(agent):\n", - " size = 10\n", - " color = \"tab:red\"\n", - " if agent.wealth > 0:\n", - " size = 50\n", - " color = \"tab:blue\"\n", - " return {\"size\": size, \"color\": color}" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "page = JupyterViz(\n", - " BoltzmannWealthModel,\n", - " model_params,\n", - " measures=[\"Gini\"],\n", - " name=\"Money Model\",\n", - " agent_portrayal=agent_portrayal,\n", - ")\n", - "# This is required to render the visualization in the Jupyter notebook\n", - "page" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Building your own visualization component\n", - "\n", - "**Note:** This section is for users who have a basic familiarity with Python's Matplotlib plotting library.\n", - "\n", - "If the visualization elements provided by Mesa aren't enough for you, you can build your own and plug them into the model server.\n", - "\n", - "For this example, let's build a simple histogram visualization, which can count the number of agents with each value of wealth." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "import solara\n", - "from matplotlib.figure import Figure\n", - "\n", - "\n", - "def make_histogram(viz):\n", - " # Note: you must initialize a figure using this method instead of\n", - " # plt.figure(), for thread safety purpose\n", - " fig = Figure()\n", - " ax = fig.subplots()\n", - " wealth_vals = [agent.wealth for agent in viz.model.schedule.agents]\n", - " # Note: you have to use Matplotlib's OOP API instead of plt.hist\n", - " # because plt.hist is not thread-safe.\n", - " ax.hist(wealth_vals, bins=10)\n", - " # You have to specify the dependencies as follows, so that the figure\n", - " # auto-updates when viz.model or viz.df is changed.\n", - " solara.FigureMatplotlib(fig, dependencies=[viz.model, viz.df])" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "Next, we reinitialize the visualization object, but this time with the histogram (see the measures argument)." - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "page = JupyterViz(\n", - " BoltzmannWealthModel,\n", - " model_params,\n", - " measures=[\"Gini\", make_histogram],\n", - " name=\"Money Model\",\n", - " agent_portrayal=agent_portrayal,\n", - ")\n", - "# This is required to render the visualization in the Jupyter notebook\n", - "page" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Happy Modeling!\n", - "\n", - "This document is a work in progress. If you see any errors, exclusions or have any problems please contact [us](https://github.com/projectmesa/mesa/issues)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "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.10.12" - }, - "nbsphinx": { - "execute": "never" - } - }, - "nbformat": 4, - "nbformat_minor": 4 -} diff --git a/docs/tutorials/adv_tutorial_legacy.ipynb b/docs/tutorials/adv_tutorial_legacy.ipynb new file mode 100644 index 00000000000..cd54ed8079f --- /dev/null +++ b/docs/tutorials/adv_tutorial_legacy.ipynb @@ -0,0 +1,521 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Advanced Tutorial\n", + "This is the legacy version of the advanced tutorial. We recommend you to read the newer (current) version because it is easier." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Adding visualization\n", + "\n", + "So far, we've built a model, run it, and analyzed some output afterwards. However, one of the advantages of agent-based models is that we can often watch them run step by step, potentially spotting unexpected patterns, behaviors or bugs, or developing new intuitions, hypotheses, or insights. Other times, watching a model run can explain it to an unfamiliar audience better than static explanations. Like many ABM frameworks, Mesa allows you to create an interactive visualization of the model. In this section we'll walk through creating a visualization using built-in components, and (for advanced users) how to create a new visualization element.\n", + "\n", + "**Note for Jupyter users: Due to conflicts with the tornado server Mesa uses and Jupyter, the interactive browser of your model will load but likely not work. This will require you to use run the code from .py files. The Mesa development team is working to develop a** [Jupyter compatible interface](https://github.com/projectmesa/mesa/issues/1363).\n", + "\n", + "First, a quick explanation of how Mesa's interactive visualization works. Visualization is done in a browser window, using JavaScript to draw the different things being visualized at each step of the model. To do this, Mesa launches a small web server, which runs the model, turns each step into a JSON object (essentially, structured plain text) and sends those steps to the browser.\n", + "\n", + "A visualization is built up of a few different modules: for example, a module for drawing agents on a grid, and another one for drawing a chart of some variable. Each module has a Python part, which runs on the server and turns a model state into JSON data; and a JavaScript side, which takes that JSON data and draws it in the browser window. Mesa comes with a few modules built in, and let you add your own as well." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Grid Visualization\n", + "\n", + "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n", + "Next, in a new source file (e.g. `MoneyModel_Viz.py`) include the code shown in the following cells to run and avoid Jupyter compatibility issue." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "# If MoneyModel.py is where your code is:\n", + "from MoneyModel import mesa, MoneyModel" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Mesa's `CanvasGrid` visualization class works by looping over every cell in a grid, and generating a portrayal for every agent it finds. A portrayal is a dictionary (which can easily be turned into a JSON object) which tells the JavaScript side how to draw it. The only thing we need to provide is a function which takes an agent, and returns a portrayal object. Here's the simplest one: it'll draw each agent as a red, filled circle which fills half of each cell." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "def agent_portrayal(agent):\n", + " portrayal = {\n", + " \"Shape\": \"circle\",\n", + " \"Color\": \"red\",\n", + " \"Filled\": \"true\",\n", + " \"Layer\": 0,\n", + " \"r\": 0.5,\n", + " }\n", + " return portrayal" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "In addition to the portrayal method, we instantiate a canvas grid with its width and height in cells, and in pixels. In this case, let's create a 10x10 grid, drawn in 500 x 500 pixels." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [] + }, + "outputs": [], + "source": [ + "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now we create and launch the actual server. We do this with the following arguments:\n", + "\n", + "* The model class we're running and visualizing; in this case, `MoneyModel`.\n", + "* A list of module objects to include in the visualization; here, just `[grid]`\n", + "* The title of the model: \"Money Model\"\n", + "* Any inputs or arguments for the model itself. In this case, 100 agents, and height and width of 10.\n", + "\n", + "Once we create the server, we set the port (use default 8521 here) for it to listen on (you can treat this as just a piece of the URL you’ll open in the browser). " + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "server = mesa.visualization.ModularServer(\n", + " MoneyModel, [grid], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", + ")\n", + "server.port = 8521 # the default" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Finally, when you’re ready to run the visualization, use the server’s launch() method." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The full code for source file `MoneyModel_Viz.py` should now look like:\n", + "\n", + "```python\n", + "from MoneyModel import mesa, MoneyModel\n", + "\n", + "\n", + "def agent_portrayal(agent):\n", + " portrayal = {\"Shape\": \"circle\",\n", + " \"Filled\": \"true\",\n", + " \"Layer\": 0,\n", + " \"Color\": \"red\",\n", + " \"r\": 0.5}\n", + " return portrayal\n", + "\n", + "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)\n", + "server = mesa.visualization.ModularServer(MoneyModel,\n", + " [grid],\n", + " \"Money Model\",\n", + " {\"N\":100, \"width\":10, \"height\":10})\n", + "server.port = 8521 # The default\n", + "server.launch()\n", + "```\n", + "Now run this file; this should launch the interactive visualization server and open your web browser automatically. (If the browser doesn't open automatically, try pointing it at [http://127.0.0.1:8521](http://127.0.0.1:8521) manually. If this doesn't show you the visualization, something may have gone wrong with the server launch.)\n", + "\n", + "You should see something like the figure below: the model title, an empty space where the grid will be, and a control panel off to the right.\n", + "\n", + "![Empty Visualization](files/viz_empty.png)\n", + "\n", + "Click the `Reset` button on the control panel, and you should see the grid fill up with red circles, representing agents.\n", + "\n", + "![Redcircles Visualization](files/viz_redcircles.png)\n", + "\n", + "Click `Step` to advance the model by one step, and the agents will move around. Click `Start` and the agents will keep moving around, at the rate set by the 'fps' (frames per second) slider at the top. Try moving it around and see how the speed of the model changes. Pressing `Stop` will pause the model; presing `Start` again will restart it. Finally, `Reset` will start a new instantiation of the model.\n", + "\n", + "To stop the visualization server, go back to the terminal where you launched it, and press Control+c." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Changing the agents\n", + "\n", + "In the visualization above, all we could see is the agents moving around -- but not how much money they had, or anything else of interest. Let's change it so that agents who are broke (wealth 0) are drawn in grey, smaller, and above agents who still have money.\n", + "\n", + "To do this, we go back to our `agent_portrayal` code and add some code to change the portrayal based on the agent properties and launch the server again." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def agent_portrayal(agent):\n", + " portrayal = {\"Shape\": \"circle\", \"Filled\": \"true\", \"r\": 0.5}\n", + "\n", + " if agent.wealth > 0:\n", + " portrayal[\"Color\"] = \"red\"\n", + " portrayal[\"Layer\"] = 0\n", + " else:\n", + " portrayal[\"Color\"] = \"grey\"\n", + " portrayal[\"Layer\"] = 1\n", + " portrayal[\"r\"] = 0.2\n", + " return portrayal" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This will open a new browser window pointed at the updated visualization. Initially it looks the same, but advance the model and smaller grey circles start to appear. Note that since the zero-wealth agents have a higher layer number, they are drawn on top of the red agents.\n", + "\n", + "![Greycircles Visualization](files/viz_greycircles.png)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Adding a chart\n", + "\n", + "Next, let's add another element to the visualization: a chart, tracking the model's Gini Coefficient. This is another built-in element that Mesa provides.\n", + "\n", + "The basic chart pulls data from the model's DataCollector, and draws it as a line graph using the [Charts.js](http://www.chartjs.org/) JavaScript libraries. We instantiate a chart element with a list of series for the chart to track. Each series is defined in a dictionary, and has a `Label` (which must match the name of a model-level variable collected by the DataCollector) and a `Color` name. We can also give the chart the name of the DataCollector object in the model.\n", + "\n", + "Finally, we add the chart to the list of elements in the server. The elements are added to the visualization in the order they appear, so the chart will appear underneath the grid." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "chart = mesa.visualization.ChartModule(\n", + " [{\"Label\": \"Gini\", \"Color\": \"Black\"}], data_collector_name=\"datacollector\"\n", + ")\n", + "\n", + "server = mesa.visualization.ModularServer(\n", + " MoneyModel, [grid, chart], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Launch the visualization and start a model run, either by launching the server here or through the full code for source file `MoneyModel_Viz.py`.\n", + "\n", + "```python\n", + "from MoneyModel import mesa, MoneyModel\n", + "\n", + "\n", + "def agent_portrayal(agent):\n", + " portrayal = {\"Shape\": \"circle\", \"Filled\": \"true\", \"r\": 0.5}\n", + "\n", + " if agent.wealth > 0:\n", + " portrayal[\"Color\"] = \"red\"\n", + " portrayal[\"Layer\"] = 0\n", + " else:\n", + " portrayal[\"Color\"] = \"grey\"\n", + " portrayal[\"Layer\"] = 1\n", + " portrayal[\"r\"] = 0.2\n", + " return portrayal\n", + "\n", + "\n", + "grid = mesa.visualization.CanvasGrid(agent_portrayal, 10, 10, 500, 500)\n", + "chart = mesa.visualization.ChartModule(\n", + " [{\"Label\": \"Gini\", \"Color\": \"Black\"}], data_collector_name=\"datacollector\"\n", + ")\n", + "\n", + "server = mesa.visualization.ModularServer(\n", + " MoneyModel, [grid, chart], \"Money Model\", {\"N\": 100, \"width\": 10, \"height\": 10}\n", + ")\n", + "server.port = 8521 # The default\n", + "server.launch()\n", + "```" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You'll see a line chart underneath the grid. Every step of the model, the line chart updates along with the grid. Reset the model, and the chart resets too.\n", + "\n", + "![Chart Visualization](files/viz_chart.png)\n", + "\n", + "**Note:** You might notice that the chart line only starts after a couple of steps; this is due to a bug in Charts.js which will hopefully be fixed soon." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Building your own visualization component\n", + "\n", + "**Note:** This section is for users who have a basic familiarity with JavaScript. If that's not you, don't worry! (If you're an advanced JavaScript coder and find things that we've done wrong or inefficiently, please [let us know](https://github.com/projectmesa/mesa/issues)!)\n", + "\n", + "If the visualization elements provided by Mesa aren't enough for you, you can build your own and plug them into the model server.\n", + "\n", + "First, you need to understand how the visualization works under the hood. Remember that each visualization module has two sides: a Python object that runs on the server and generates JSON data from the model state (the server side), and a JavaScript object that runs in the browser and turns the JSON into something it renders on the screen (the client side).\n", + "\n", + "Obviously, the two sides of each visualization must be designed in tandem. They result in one Python class, and one JavaScript `.js` file. The path to the JavaScript file is a property of the Python class.\n", + "\n", + "For this example, let's build a simple histogram visualization, which can count the number of agents with each value of wealth. We'll use the [Charts.js](http://www.chartjs.org/) JavaScript library, which is already included with Mesa. If you go and look at its documentation, you'll see that it had no histogram functionality, which means we have to build our own out of a bar chart. We'll keep the histogram as simple as possible, giving it a fixed number of integer bins. If you were designing a more general histogram to add to the Mesa repository for everyone to use across different models, obviously you'd want something more general." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Client-Side Code\n", + "\n", + "In general, the server- and client-side are written in tandem. However, if you're like me and more comfortable with Python than JavaScript, it makes sense to figure out how to get the JavaScript working first, and then write the Python to be compatible with that.\n", + "\n", + "In the same directory as your model, create a new file called `HistogramModule.js`. This will store the JavaScript code for the client side of the new module.\n", + "\n", + "JavaScript classes can look alien to people coming from other languages -- specifically, they can look like functions. (The Mozilla [Introduction to Object-Oriented JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Introduction_to_Object-Oriented_JavaScript) is a good starting point). In `HistogramModule.js`, start by creating the class itself:\n", + "\n", + "```javascript\n", + "const HistogramModule = function(bins, canvas_width, canvas_height) {\n", + " // The actual code will go here.\n", + "};\n", + "```\n", + "\n", + "Note that our object is instantiated with three arguments: the number of integer bins, and the width and height (in pixels) the chart will take up in the visualization window.\n", + "\n", + "When the visualization object is instantiated, the first thing it needs to do is prepare to draw on the current page. To do so, it adds a [canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API) tag to the page. It also gets the canvas' context, which is required for doing anything with it.\n", + "\n", + "```javascript\n", + "const HistogramModule = function(bins, canvas_width, canvas_height) {\n", + " // Create the canvas object:\n", + " const canvas = document.createElement(\"canvas\");\n", + " Object.assign(canvas, {\n", + " width: canvas_width,\n", + " height: canvas_height,\n", + " style: \"border:1px dotted\",\n", + " });\n", + " // Append it to #elements:\n", + " const elements = document.getElementById(\"elements\");\n", + " elements.appendChild(canvas);\n", + "\n", + " // Create the context and the drawing controller:\n", + " const context = canvas.getContext(\"2d\");\n", + "};\n", + "```\n", + "\n", + "Look at the Charts.js [bar chart documentation](http://www.chartjs.org/docs/#bar-chart-introduction). You'll see some of the boilerplate needed to get a chart set up. Especially important is the `data` object, which includes the datasets, labels, and color options. In this case, we want just one dataset (we'll keep things simple and name it \"Data\"); it has `bins` for categories, and the value of each category starts out at zero. Finally, using these boilerplate objects and the canvas context we created, we can create the chart object.\n", + "\n", + "```javascript\n", + "const HistogramModule = function(bins, canvas_width, canvas_height) {\n", + " // Create the canvas object:\n", + " const canvas = document.createElement(\"canvas\");\n", + " Object.assign(canvas, {\n", + " width: canvas_width,\n", + " height: canvas_height,\n", + " style: \"border:1px dotted\",\n", + " });\n", + " // Append it to #elements:\n", + " const elements = document.getElementById(\"elements\");\n", + " elements.appendChild(canvas);\n", + "\n", + " // Create the context and the drawing controller:\n", + " const context = canvas.getContext(\"2d\");\n", + "\n", + " // Prep the chart properties and series:\n", + " const datasets = [{\n", + " label: \"Data\",\n", + " fillColor: \"rgba(151,187,205,0.5)\",\n", + " strokeColor: \"rgba(151,187,205,0.8)\",\n", + " highlightFill: \"rgba(151,187,205,0.75)\",\n", + " highlightStroke: \"rgba(151,187,205,1)\",\n", + " data: []\n", + " }];\n", + "\n", + " // Add a zero value for each bin\n", + " for (var i in bins)\n", + " datasets[0].data.push(0);\n", + "\n", + " const data = {\n", + " labels: bins,\n", + " datasets: datasets\n", + " };\n", + "\n", + " const options = {\n", + " scaleBeginsAtZero: true\n", + " };\n", + "\n", + " // Create the chart object\n", + " let chart = new Chart(context, {type: 'bar', data: data, options: options});\n", + "\n", + " // Now what?\n", + "};\n", + "```\n", + "\n", + "There are two methods every client-side visualization class must implement to be able to work: `render(data)` to render the incoming data, and `reset()` which is called to clear the visualization when the user hits the reset button and starts a new model run.\n", + "\n", + "In this case, the easiest way to pass data to the histogram is as an array, one value for each bin. We can then just loop over the array and update the values in the chart's dataset.\n", + "\n", + "There are a few ways to reset the chart, but the easiest is probably to destroy it and create a new chart object in its place.\n", + "\n", + "With that in mind, we can add these two methods to the class:\n", + "\n", + "```javascript\n", + "const HistogramModule = function(bins, canvas_width, canvas_height) {\n", + " // ...Everything from above...\n", + " this.render = function(data) {\n", + " datasets[0].data = data;\n", + " chart.update();\n", + " };\n", + "\n", + " this.reset = function() {\n", + " chart.destroy();\n", + " chart = new Chart(context, {type: 'bar', data: data, options: options});\n", + " };\n", + "};\n", + "```\n", + "\n", + "Note the `this`. before the method names. This makes them public and ensures that they are accessible outside of the object itself. All the other variables inside the class are only accessible inside the object itself, but not outside of it." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### Server-Side Code\n", + "\n", + "Can we get back to Python code? Please?\n", + "\n", + "Every JavaScript visualization element has an equal and opposite server-side Python element. The Python class needs to also have a `render` method, to get data out of the model object and into a JSON-ready format. It also needs to point towards the code where the relevant JavaScript lives, and add the JavaScript object to the model page.\n", + "\n", + "In a Python file (either its own, or in the same file as your visualization code), import the `VisualizationElement` class we'll inherit from, and create the new visualization class.\n", + "\n", + "```python\n", + " from mesa.visualization.ModularVisualization import VisualizationElement, CHART_JS_FILE\n", + "\n", + " class HistogramModule(VisualizationElement):\n", + " package_includes = [CHART_JS_FILE]\n", + " local_includes = [\"HistogramModule.js\"]\n", + "\n", + " def __init__(self, bins, canvas_height, canvas_width):\n", + " self.canvas_height = canvas_height\n", + " self.canvas_width = canvas_width\n", + " self.bins = bins\n", + " new_element = \"new HistogramModule({}, {}, {})\"\n", + " new_element = new_element.format(bins, \n", + " canvas_width, \n", + " canvas_height)\n", + " self.js_code = \"elements.push(\" + new_element + \");\"\n", + "```\n", + "\n", + "There are a few things going on here. `package_includes` is a list of JavaScript files that are part of Mesa itself that the visualization element relies on. You can see the included files in [mesa/visualization/templates/](https://github.com/projectmesa/mesa/tree/main/mesa/visualization/templates). Similarly, `local_includes` is a list of JavaScript files in the same directory as the class code itself. Note that both of these are class variables, not object variables -- they hold for all particular objects.\n", + "\n", + "Next, look at the `__init__` method. It takes three arguments: the number of bins, and the width and height for the histogram. It then uses these values to populate the `js_code` property; this is code that the server will insert into the visualization page, which will run when the page loads. In this case, it creates a new HistogramModule (the class we created in JavaScript in the step above) with the desired bins, width and height; it then appends (`push`es) this object to `elements`, the list of visualization elements that the visualization page itself maintains.\n", + "\n", + "Now, the last thing we need is the `render` method. If we were making a general-purpose visualization module we'd want this to be more general, but in this case we can hard-code it to our model.\n", + "\n", + "```python\n", + "import numpy as np\n", + "\n", + "class HistogramModule(VisualizationElement):\n", + " # ... Everything from above...\n", + "\n", + " def render(self, model):\n", + " wealth_vals = [agent.wealth for agent in model.schedule.agents]\n", + " hist = np.histogram(wealth_vals, bins=self.bins)[0]\n", + " return [int(x) for x in hist]\n", + "```\n", + "\n", + "Every time the render method is called (with a model object as the argument) it uses numpy to generate counts of agents with each wealth value in the bins, and then returns a list of these values. Note that the `render` method doesn't return a JSON string -- just an object that can be turned into JSON, in this case a Python list (with Python integers as the values; the `json` library doesn't like dealing with numpy's integer type).\n", + "\n", + "Now, you can create your new HistogramModule and add it to the server:\n", + "\n", + "```python\n", + " histogram = mesa.visualization.HistogramModule(list(range(10)), 200, 500)\n", + " server = mesa.visualization.ModularServer(MoneyModel, \n", + " [grid, histogram, chart], \n", + " \"Money Model\", \n", + " {\"N\":100, \"width\":10, \"height\":10})\n", + " server.launch()\n", + "```\n", + "\n", + "Run this code, and you should see your brand-new histogram added to the visualization and updating along with the model!\n", + "\n", + "![Histogram Visualization](files/viz_histogram.png)\n", + "\n", + "If you've felt comfortable with this section, it might be instructive to read the code for the [ModularServer](https://github.com/projectmesa/mesa/blob/main/mesa/visualization/ModularVisualization.py#L259) and the [modular_template](https://github.com/projectmesa/mesa/blob/main/mesa/visualization/templates/modular_template.html) to get a better idea of how all the pieces fit together." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Happy Modeling!\n", + "\n", + "This document is a work in progress. If you see any errors, exclusions or have any problems please contact [us](https://github.com/projectmesa/mesa/issues)." + ] + } + ], + "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.11.3" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} From 3745ace8777735bd80281c3f13fe09e67dd3d35f Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 8 Jul 2023 09:35:01 -0400 Subject: [PATCH 132/214] adv tutorial: Elaborate on Colab sentence --- docs/tutorials/adv_tutorial.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/adv_tutorial.ipynb b/docs/tutorials/adv_tutorial.ipynb index 8d4f9fd26be..c12874fa6a2 100644 --- a/docs/tutorials/adv_tutorial.ipynb +++ b/docs/tutorials/adv_tutorial.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "To execute this tutorial online: [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/adv_tutorial_experimental.ipynb)\n", + "We recommend to execute this tutorial online in a Colab notebook, so that you can explore the visualization output: [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/adv_tutorial_experimental.ipynb)\n", "\n", "### Adding visualization\n", "\n", From 569ce6359eba364019c48df3c232b14b8bd5e60e Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 8 Jul 2023 09:38:19 -0400 Subject: [PATCH 133/214] adv tutorial: Remove nonsensical sentence --- docs/tutorials/adv_tutorial.ipynb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/tutorials/adv_tutorial.ipynb b/docs/tutorials/adv_tutorial.ipynb index c12874fa6a2..93b33921ba7 100644 --- a/docs/tutorials/adv_tutorial.ipynb +++ b/docs/tutorials/adv_tutorial.ipynb @@ -26,8 +26,7 @@ "source": [ "#### Grid Visualization\n", "\n", - "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n", - "Next, in a new source file (e.g. `MoneyModel_Viz.py`) include the code shown in the following cells to run and avoid Jupyter compatibility issue." + "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n" ] }, { From 74a455d395ca68b3b4aad357856914324fb0be83 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 8 Jul 2023 10:58:37 -0400 Subject: [PATCH 134/214] Rename advanced tutorial to visualization tutorial --- docs/index.rst | 6 +++--- .../{adv_tutorial.ipynb => visualization_tutorial.ipynb} | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) rename docs/tutorials/{adv_tutorial.ipynb => visualization_tutorial.ipynb} (99%) diff --git a/docs/index.rst b/docs/index.rst index b8478eda996..af039fd8328 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -55,13 +55,13 @@ To launch an example model, clone the `repository tutorials/intro_tutorial.ipynb - tutorials/adv_tutorial.ipynb + tutorials/visualization_tutorial.ipynb Best Practices Useful Snippets API Documentation diff --git a/docs/tutorials/adv_tutorial.ipynb b/docs/tutorials/visualization_tutorial.ipynb similarity index 99% rename from docs/tutorials/adv_tutorial.ipynb rename to docs/tutorials/visualization_tutorial.ipynb index 93b33921ba7..5325f6bfece 100644 --- a/docs/tutorials/adv_tutorial.ipynb +++ b/docs/tutorials/visualization_tutorial.ipynb @@ -4,7 +4,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Advanced Tutorial" + "# Visualization Tutorial" ] }, { From 3c3cd7e92cdab0aac25405968b7c2d5c421670f1 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 9 Jul 2023 09:12:13 -0400 Subject: [PATCH 135/214] Add mesa-models as dependency --- setup.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 7eff8bb317c..e374211cfda 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,16 @@ from setuptools import find_packages, setup -requires = ["click", "cookiecutter", "networkx", "numpy", "pandas", "tornado", "tqdm"] +requires = [ + "click", + "cookiecutter", + "networkx", + "numpy", + "pandas", + "tornado", + "tqdm", + "mesa-models @ git+https://github.com/projectmesa/mesa-examples@53abc8dbd93b2dda817648544f554045f6491147", +] extras_require = { "dev": [ From 03bc937c43050fd6bb81d406c091014adba5ef4b Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 9 Jul 2023 11:33:48 -0400 Subject: [PATCH 136/214] viz tutorial: Remove explicit mesa-models install --- docs/tutorials/visualization_tutorial.ipynb | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/docs/tutorials/visualization_tutorial.ipynb b/docs/tutorials/visualization_tutorial.ipynb index 5325f6bfece..a40e6017230 100644 --- a/docs/tutorials/visualization_tutorial.ipynb +++ b/docs/tutorials/visualization_tutorial.ipynb @@ -17,7 +17,7 @@ "\n", "So far, we've built a model, run it, and analyzed some output afterwards. However, one of the advantages of agent-based models is that we can often watch them run step by step, potentially spotting unexpected patterns, behaviors or bugs, or developing new intuitions, hypotheses, or insights. Other times, watching a model run can explain it to an unfamiliar audience better than static explanations. Like many ABM frameworks, Mesa allows you to create an interactive visualization of the model. In this section we'll walk through creating a visualization using built-in components, and (for advanced users) how to create a new visualization element.\n", "\n", - "First, a quick explanation of how Mesa's interactive visualization works. The visualization is done in a browser window, using the [Solara](https://solara.dev/) framework, a pure Python, React-style web framework. Running `solara run app.py` will launch a web server, which runs the model, and displays model detail at each step via the Matplotlib plotting library." + "First, a quick explanation of how Mesa's interactive visualization works. The visualization is done in a browser window, using the [Solara](https://solara.dev/) framework, a pure Python, React-style web framework. Running `solara run app.py` will launch a web server, which runs the model, and displays model detail at each step via the Matplotlib plotting library. Alternatively, you can execute everything inside a Jupyter environment." ] }, { @@ -26,7 +26,7 @@ "source": [ "#### Grid Visualization\n", "\n", - "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n" + "To start with, let's have a visualization where we can watch the agents moving around the grid. Let us use the same `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html), which is already available when you install Mesa, but named as BoltzmannWealthModel.\n" ] }, { @@ -40,13 +40,7 @@ "%pip install --quiet mesa\n", "import mesa\n", "\n", - "# If MoneyModel.py is where your code is, do this instead:\n", - "# from MoneyModel import MoneyModel\n", - "\n", - "# To make this tutorial notebook executable in Binder/Colab,\n", - "# we install mesa_models.\n", - "%pip install --quiet -U -e git+https://github.com/projectmesa/mesa-examples#egg=mesa-models\n", - "%pip install --quiet solara\n", + "# The Boltzmann wealth model (money model) is already included in Mesa library\n", "from mesa_models.boltzmann_wealth_model.model import BoltzmannWealthModel" ] }, From c71098744181b90a8880788ddf771b2eac32c563 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 12 Jul 2023 07:27:48 -0400 Subject: [PATCH 137/214] Bump mesa-models Git revision hash --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index e374211cfda..f7a240e0907 100644 --- a/setup.py +++ b/setup.py @@ -16,7 +16,7 @@ "pandas", "tornado", "tqdm", - "mesa-models @ git+https://github.com/projectmesa/mesa-examples@53abc8dbd93b2dda817648544f554045f6491147", + "mesa-models @ git+https://github.com/projectmesa/mesa-examples@db2ec0383eb3b1868e91c828101e84cce97bbb63", ] extras_require = { From 53e9f8d0f232bdf86b09c40d19c79d43857167d6 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 12 Jul 2023 07:37:50 -0400 Subject: [PATCH 138/214] Update config API for RTD setting & bump to Python 3.9 --- .readthedocs.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index 292b2461389..c0dbad73c99 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -12,9 +12,13 @@ sphinx: formats: - pdf +build: + os: "ubuntu-22.04" + tools: + python: "3.9" + # Optionally set the version of Python and requirements required to build your docs python: - version: "3.8" install: - method: pip path: . From 950adb8244e41dc4927e7f33f427d27b144057e5 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 12 Jul 2023 07:46:05 -0400 Subject: [PATCH 139/214] Fix visualization_tutorial Colab link --- docs/tutorials/visualization_tutorial.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/tutorials/visualization_tutorial.ipynb b/docs/tutorials/visualization_tutorial.ipynb index a40e6017230..bd473f1e179 100644 --- a/docs/tutorials/visualization_tutorial.ipynb +++ b/docs/tutorials/visualization_tutorial.ipynb @@ -11,7 +11,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We recommend to execute this tutorial online in a Colab notebook, so that you can explore the visualization output: [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/adv_tutorial_experimental.ipynb)\n", + "We recommend to execute this tutorial online in a Colab notebook, so that you can explore the visualization output: [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/visualization_tutorial.ipynb)\n", "\n", "### Adding visualization\n", "\n", From 8556fbc6e9a7328608201598c1e98ae2f9811b89 Mon Sep 17 00:00:00 2001 From: tpike3 Date: Sun, 2 Jul 2023 08:06:58 -0400 Subject: [PATCH 140/214] update history for 2.0 release --- HISTORY.rst | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ mesa/__init__.py | 2 +- 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 88228d4250a..62bf4a020b6 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,71 @@ Release History --------------- +2.0.0 (2023-04-15) Wellton +++++++++++++++++++++++++++ + +**Special notes** + +Mesa 2.0 includes: + * **an experimental pure python user interface/ visualization that is also jupyter compatible please see the** `visualization tutorial`_ + * several breaking changes that provide significant improvements to Mesa. + +.. _visualization tutorial: https://mesa.readthedocs.io/en/latest/tutorials/visualization_tutorial.html +**Breaking Changes:** + +* space: change `coord_iter` to return `(content,(x,y))` instead of `(content, x,y)`; this reduces known errors of scheduler to grid mismatch #1566, #1723 +* space: change NetworkGrid `get_neighbors` to `get_neighborhood`; improves performance #1542 +* space: raise exception when pos is out of bounds in `Grid.get_neighborhood` #1524 +* space: remove deprecations (#1520, #1687, #1688): + * `find_empty()`: convert this to `move_to_empty()` + * `num_agents`: removed parameter from `move_to_empty()` + * `position_agent()`: convert this to `place_agent` + * `neighbor_iter()`: convert this to `iter_neighborhood()` +* batchrunner: remove deprecations #1627 + * `class BatchRunner` and `class BatchRunnerMP`: convert these to `batch_run()` + * Please see this `batch_run() example`_ if you would like to see an an implementation. +* visualization: easier visualization creation #1693 + * `UserSettableParameter(['number', 'slider','checkbox', 'choice', 'StaticText'])`: convert to `NumberInput` , `Slider`, `CheckBox`, `Choice`, `StaticText` + * Please see this `visualization example`_ if you would like to see an implementation. + +.. _batch_run() example: https://github.com/projectmesa/mesa-examples/blob/db2ec0383eb3b1868e91c828101e84cce97bbb63/examples/bank_reserves/batch_run.py#L188-L221 +.. _visualization example: https://github.com/projectmesa/mesa-examples/blob/db2ec0383eb3b1868e91c828101e84cce97bbb63/examples/boltzmann_wealth_model/boltzmann_wealth_model/server.py#L25-L32.) + + +**New Features:** + +* datacollector: can now handle data collection by agent type #1419, #1702 +* time: allows for model level `StageActivation` #1709 +* visualization: `ChartModule` can have dynamically named properties #1685 +* visualization: improved stop server to end visualizations #1646 +* *experimental* python front end option: integrated the initial prototype of the pure python front end option #1698, #1726 + + +**Improvements** + + +* update HexGrid and create HexSingleGrid and HexMultiGrid #1581 +* correct `get_heading` for toroidal space #1686 +* update slider to start at 1FPS #1674 +* update links to examples repo due to creation of mesa_examples #1636, #1637 +* ** CI Improvements** + * update Ruff #1724 + * remove Pipfile and Pipfile.lock #1692 + * enable Codespell in Jupyter #1695 + * improve regex for better build #1669, #1671 + * exclude notebooks form linter #1670 + * updated pip for zsh #1644 + * CLI quality of life improvements #1640 +* **Docs Improvements** + * update to PyData theme #1699 + * remove .rst to create simpler build #1363, #1624 + * use seaborn in tutorials #1718 + * fix types and errors in docs #1624, #1705, #1706, #1720 + * improve tutorials #1636, #1637, #1639, #1641, #1647, #1648, #1650, #1656, #1658, #1659, #1695, #1697, + * add nbsphinx to adv_tutorial #1694 + * replace `const chart` for `var chart` in advanced tutorial #1679 +* update LICENSE to 2023 #1683 + 1.2.1 (2023-03-18) ++++++++++++++++++ diff --git a/mesa/__init__.py b/mesa/__init__.py index e10693a47a6..62b4d039639 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -24,7 +24,7 @@ ] __title__ = "mesa" -__version__ = "1.2.1" +__version__ = "2.0" __license__ = "Apache 2.0" _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year __copyright__ = f"Copyright {_this_year} Project Mesa Team" From 4985ef3326899c5b04b78e2b25ed2f10f414f88a Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 15 Jul 2023 08:59:07 -0400 Subject: [PATCH 141/214] release to PyPI: Fix permission --- .github/workflows/release.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 442b50fcbd7..f310da23ae6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -4,6 +4,9 @@ on: types: - published +permissions: + id-token: write + jobs: release: name: Deploy release to PyPI From ad024a6908dd97d7594ac34dda56aebd09151a2e Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 20 Jul 2023 00:24:43 -0400 Subject: [PATCH 142/214] Initialize Jupyter viz --- docs/tutorials/visualization_tutorial.ipynb | 7 +- mesa/__init__.py | 1 + mesa/experimental/__init__.py | 1 + mesa/experimental/jupyter_viz.py | 225 ++++++++++++++++++++ setup.py | 2 +- 5 files changed, 232 insertions(+), 4 deletions(-) create mode 100644 mesa/experimental/__init__.py create mode 100644 mesa/experimental/jupyter_viz.py diff --git a/docs/tutorials/visualization_tutorial.ipynb b/docs/tutorials/visualization_tutorial.ipynb index bd473f1e179..47bcc798237 100644 --- a/docs/tutorials/visualization_tutorial.ipynb +++ b/docs/tutorials/visualization_tutorial.ipynb @@ -26,7 +26,7 @@ "source": [ "#### Grid Visualization\n", "\n", - "To start with, let's have a visualization where we can watch the agents moving around the grid. Let us use the same `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html), which is already available when you install Mesa, but named as BoltzmannWealthModel.\n" + "To start with, let's have a visualization where we can watch the agents moving around the grid. Let us use the same `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html).\n" ] }, { @@ -40,7 +40,8 @@ "%pip install --quiet mesa\n", "import mesa\n", "\n", - "# The Boltzmann wealth model (money model) is already included in Mesa library\n", + "# You can either define the BoltzmannWealthModel (aka MoneyModel) or install mesa-models:\n", + "%pip install --quiet -U -e git+https://github.com/projectmesa/mesa-examples#egg=mesa-models\n", "from mesa_models.boltzmann_wealth_model.model import BoltzmannWealthModel" ] }, @@ -115,7 +116,7 @@ }, "outputs": [], "source": [ - "from mesa_models.experimental import JupyterViz\n", + "from mesa.experimental import JupyterViz\n", "\n", "page = JupyterViz(\n", " BoltzmannWealthModel,\n", diff --git a/mesa/__init__.py b/mesa/__init__.py index 62b4d039639..8837bc37e97 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -21,6 +21,7 @@ "visualization", "DataCollector", "batch_run", + "experimental", ] __title__ = "mesa" diff --git a/mesa/experimental/__init__.py b/mesa/experimental/__init__.py new file mode 100644 index 00000000000..964dc5d19a3 --- /dev/null +++ b/mesa/experimental/__init__.py @@ -0,0 +1 @@ +from .jupyter_viz import JupyterViz, make_text # noqa diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py new file mode 100644 index 00000000000..e5d0496292c --- /dev/null +++ b/mesa/experimental/jupyter_viz.py @@ -0,0 +1,225 @@ +import threading + +import matplotlib.pyplot as plt +import reacton.ipywidgets as widgets +import solara +from matplotlib.figure import Figure +from matplotlib.ticker import MaxNLocator + +# Avoid interactive backend +plt.switch_backend("agg") + + +class JupyterContainer: + def __init__( + self, + model_class, + model_params, + measures=None, + name="Mesa Model", + agent_portrayal=None, + ): + self.model_class = model_class + self.split_model_params(model_params) + self.measures = measures + self.name = name + self.agent_portrayal = agent_portrayal + self.thread = None + + def split_model_params(self, model_params): + self.model_params_input = {} + self.model_params_fixed = {} + for k, v in model_params.items(): + if self.check_param_is_fixed(v): + self.model_params_fixed[k] = v + else: + self.model_params_input[k] = v + + def check_param_is_fixed(self, param): + if not isinstance(param, dict): + return True + if "type" not in param: + return True + + def do_step(self): + self.model.step() + self.set_df(self.model.datacollector.get_model_vars_dataframe()) + + def do_play(self): + self.model.running = True + while self.model.running: + self.do_step() + + def threaded_do_play(self): + if self.thread is not None and self.thread.is_alive(): + return + self.thread = threading.Thread(target=self.do_play) + self.thread.start() + + def do_pause(self): + if (self.thread is None) or (not self.thread.is_alive()): + return + self.model.running = False + self.thread.join() + + def portray(self, g): + x = [] + y = [] + s = [] # size + c = [] # color + for i in range(g.width): + for j in range(g.height): + content = g._grid[i][j] + if not content: + continue + if not hasattr(content, "__iter__"): + # Is a single grid + content = [content] + for agent in content: + data = self.agent_portrayal(agent) + x.append(i) + y.append(j) + if "size" in data: + s.append(data["size"]) + if "color" in data: + c.append(data["color"]) + out = {"x": x, "y": y} + if len(s) > 0: + out["s"] = s + if len(c) > 0: + out["c"] = c + return out + + +def make_space(viz): + space_fig = Figure() + space_ax = space_fig.subplots() + space_ax.scatter(**viz.portray(viz.model.grid)) + space_ax.set_axis_off() + solara.FigureMatplotlib(space_fig, dependencies=[viz.model, viz.df]) + + +def make_plot(viz, measure): + fig = Figure() + ax = fig.subplots() + ax.plot(viz.df.loc[:, measure]) + ax.set_ylabel(measure) + # Set integer x axis + ax.xaxis.set_major_locator(MaxNLocator(integer=True)) + solara.FigureMatplotlib(fig, dependencies=[viz.model, viz.df]) + + +def make_text(renderer): + def function(viz): + solara.Markdown(renderer(viz.model)) + + return function + + +def make_user_input(user_input, k, v): + if v["type"] == "SliderInt": + solara.SliderInt( + v.get("label", "label"), + value=user_input, + min=v.get("min"), + max=v.get("max"), + step=v.get("step"), + ) + elif v["type"] == "SliderFloat": + solara.SliderFloat( + v.get("label", "label"), + value=user_input, + min=v.get("min"), + max=v.get("max"), + step=v.get("step"), + ) + + +@solara.component +def MesaComponent(viz, space_drawer=None, play_interval=400): + solara.Markdown(viz.name) + + # 1. User inputs + user_inputs = {} + for k, v in viz.model_params_input.items(): + user_input = solara.use_reactive(v["value"]) + user_inputs[k] = user_input.value + make_user_input(user_input, k, v) + + # 2. Model + def make_model(): + return viz.model_class(**user_inputs, **viz.model_params_fixed) + + viz.model = solara.use_memo(make_model, dependencies=list(user_inputs.values())) + viz.df, viz.set_df = solara.use_state( + viz.model.datacollector.get_model_vars_dataframe() + ) + + # 3. Buttons + playing = solara.use_reactive(False) + + def on_value_play(change): + if viz.model.running: + playing.value = True + viz.do_step() + else: + playing.value = False + + with solara.Row(): + solara.Button(label="Step", color="primary", on_click=viz.do_step) + # This style is necessary so that the play widget has almost the same + # height as typical Solara buttons. + solara.Style( + """ + .widget-play { + height: 30px; + } + """ + ) + widgets.Play( + value=0, + interval=play_interval, + repeat=True, + show_repeat=False, + on_value=on_value_play, + playing=playing.value, + on_play=playing.set, + ) + # threaded_do_play is not used for now because it + # doesn't work in Google colab. We use + # ipywidgets.Play until it is fixed. The threading + # version is definite a much better implementation, + # if it works. + # solara.Button(label="▶", color="primary", on_click=viz.threaded_do_play) + # solara.Button(label="⏸︎", color="primary", on_click=viz.do_pause) + # solara.Button(label="Reset", color="primary", on_click=do_reset) + + with solara.GridFixed(columns=2): + # 4. Space + if space_drawer is None: + make_space(viz) + else: + space_drawer(viz) + # 5. Plots + for i, measure in enumerate(viz.measures): + if callable(measure): + # Is a custom object + measure(viz) + else: + make_plot(viz, measure) + + +def JupyterViz( + model_class, + model_params, + measures=None, + name="Mesa Model", + agent_portrayal=None, + space_drawer=None, + play_interval=400, +): + return MesaComponent( + JupyterContainer(model_class, model_params, measures, name, agent_portrayal), + space_drawer=space_drawer, + play_interval=play_interval, + ) diff --git a/setup.py b/setup.py index f7a240e0907..2a58984ccfe 100644 --- a/setup.py +++ b/setup.py @@ -14,9 +14,9 @@ "networkx", "numpy", "pandas", + "solara", "tornado", "tqdm", - "mesa-models @ git+https://github.com/projectmesa/mesa-examples@db2ec0383eb3b1868e91c828101e84cce97bbb63", ] extras_require = { From f08851cda04a0c1fe5d0dd90583b8ef54ed7da4b Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 20 Jul 2023 05:44:46 -0400 Subject: [PATCH 143/214] Fix Ruff lint error --- mesa/experimental/jupyter_viz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index e5d0496292c..78a0c71d118 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -201,7 +201,7 @@ def on_value_play(change): else: space_drawer(viz) # 5. Plots - for i, measure in enumerate(viz.measures): + for measure in viz.measures: if callable(measure): # Is a custom object measure(viz) From e9e8c3445c13725d7455fe6f706472a4f39d5b89 Mon Sep 17 00:00:00 2001 From: tpike3 Date: Thu, 20 Jul 2023 19:48:18 -0400 Subject: [PATCH 144/214] update history for Mesa 2.1 --- HISTORY.rst | 16 +++++++++++++++- mesa/__init__.py | 2 +- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 62bf4a020b6..88efeed763b 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,13 +3,27 @@ Release History --------------- -2.0.0 (2023-04-15) Wellton +2.1.0 (2023-07-22) Youngtown ++++++++++++++++++++++++++++++ + +This release creates `mesa.experimental` namespace, this solves the issue that PyPI release will not allow git-based install. + +**Users should read the Mesa 2.0.0 release note (directly below this), as this contains the details about the breaking +changes and other major changes that were part of Mesa 2.0 release.** + +Changes: + * Creates `mesa.experimental` namespace #1736 + * Fix Ruff lint error #1737 + * Update permissions for PyPI #1732 + +2.0.0 (2023-07-15) Wellton ++++++++++++++++++++++++++ **Special notes** Mesa 2.0 includes: * **an experimental pure python user interface/ visualization that is also jupyter compatible please see the** `visualization tutorial`_ + * an improved `datacollector` that allows collection by agent type * several breaking changes that provide significant improvements to Mesa. .. _visualization tutorial: https://mesa.readthedocs.io/en/latest/tutorials/visualization_tutorial.html diff --git a/mesa/__init__.py b/mesa/__init__.py index 8837bc37e97..b1a8d54c29a 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -25,7 +25,7 @@ ] __title__ = "mesa" -__version__ = "2.0" +__version__ = "2.1" __license__ = "Apache 2.0" _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year __copyright__ = f"Copyright {_this_year} Project Mesa Team" From 0663e0254f46f05389b388e68afdf0d57c557e44 Mon Sep 17 00:00:00 2001 From: Tortar <68152031+Tortar@users.noreply.github.com> Date: Mon, 24 Jul 2023 02:04:01 +0200 Subject: [PATCH 145/214] Fix bug in get_heading --- mesa/space.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/space.py b/mesa/space.py index 75532e37168..e5615d65665 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -934,7 +934,7 @@ def get_min_abs(x, y): get_min_abs(heading[i], inverse_heading[i]) for i in range(2) ) if isinstance(pos_1, np.ndarray): - heading = np.ndarray(heading) + heading = np.asarray(heading) else: heading = tuple(heading) return heading From f1e3e972323a1ff5686d4eb50f60082accd28a71 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 26 Jul 2023 07:50:45 -0400 Subject: [PATCH 146/214] intro_tutorial: Warn user the Mesa version must be up-to-date --- docs/tutorials/intro_tutorial.ipynb | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 22703cf4127..2cc31b59852 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -12,6 +12,7 @@ "metadata": {}, "source": [ "## Tutorial Description\n", + "Important: you must ensure that your Mesa version is up-to-date in order to run this tutorial.\n", "\n", "[Mesa](https://github.com/projectmesa/mesa) is a Python framework for [agent-based modeling](https://en.wikipedia.org/wiki/Agent-based_model). This tutorial will assist you in getting started. Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone find any errors, bugs, have a suggestion, or just are looking for clarification, [let us know](https://github.com/projectmesa/mesa/issues)!\n", "\n", From d8f88834d6e07977e2baf95fc9bf049ae5916771 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 29 Jul 2023 12:30:40 -0400 Subject: [PATCH 147/214] fix: Remove race condition when pause button sometimes doesn't work There is a typo: on_play should be on_playing. As such, the playing=playing.value argument of widgets.Play and `playing.value = True` inside `on_play_value` may sometimes cancel each other. --- mesa/experimental/jupyter_viz.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 78a0c71d118..4f5e0446054 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -160,7 +160,6 @@ def make_model(): def on_value_play(change): if viz.model.running: - playing.value = True viz.do_step() else: playing.value = False @@ -183,7 +182,7 @@ def on_value_play(change): show_repeat=False, on_value=on_value_play, playing=playing.value, - on_play=playing.set, + on_playing=playing.set, ) # threaded_do_play is not used for now because it # doesn't work in Google colab. We use From 814beaf6a143fbeb24b2b27b558870b619d814b3 Mon Sep 17 00:00:00 2001 From: tpike3 Date: Sat, 29 Jul 2023 08:20:37 -0400 Subject: [PATCH 148/214] - update README screenshot and make some minor updates - add Colab badge to intro tutorial and make some minor updates - update visualization tutorial so it references git install of mesa-models --- README.rst | 3 +- docs/images/Mesa_Screenshot.png | Bin 391380 -> 56434 bytes docs/tutorials/intro_tutorial.ipynb | 42 +++++++++++++++----- docs/tutorials/visualization_tutorial.ipynb | 12 ++++-- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/README.rst b/README.rst index 61366e1123c..e4fffb3ef5d 100644 --- a/README.rst +++ b/README.rst @@ -22,8 +22,7 @@ Mesa allows users to quickly create agent-based models using built-in core compo :alt: A screenshot of the Schelling Model in Mesa *Above: A Mesa implementation of the Schelling segregation model, -being visualized in a browser window and analyzed in a Jupyter -notebook.* +this can be displayed in browser windows or Jupyter.* .. _`Mesa` : https://github.com/projectmesa/mesa/ diff --git a/docs/images/Mesa_Screenshot.png b/docs/images/Mesa_Screenshot.png index ce1069a119939420537bb21310fe16ddbf90d705..45be7ba206371191f5bb876214ec96da6d0483e5 100644 GIT binary patch literal 56434 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 akdb|}S-u1x^^b5|gyS*XuaE66B>xvulO&h` literal 391380 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

}D;;PX=>{;w;3w@i<$RkI{x@R!p7D@;Az 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

xQ>?oJy0AnJb1RL`*e?%exR}#x_Z? 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

*fnf9WrEY)U`2-b zGQ&WBUT&T&CM3^*(a_Lw;+O>i=pXL2{rkIqf8Sdzdpqhj?XIdUYT4KE$P;xZ95-go zx*aVoowH_EBL8s5d+G~Zldf-Qojt1(N;L50*EYY3>t);bl_m1Bv*Z8%U$2iV&w1hH zt;I!IWuNSEk3~+LQ7NH;5c3b3kb!kQ-^hV`La+qE z6>F?QP=!l$_uqd%E+1mefvb90!Tj=3K8e@>Cf&pp?^dGmn{8?Xq?pD-a~?3mUSZ{k{YCsw-Crx#A0(z@!MuAMu3T3W`Oddfg7 zw)ce>=+^Cy&Wxg>{PE)tb#?80?D0J>zdCZ%R7l8?(#HaKlN{!w=*02;5)a-P~L}XLckWYkcV8-LJfctL7ue zj%oPqZHOE#Dx7sD2Ra&Hj32P!>M|~DgBhKU zUdD16qX?hCU?~q0^e_Bkpkm-*eUE;`yDWUt0x(=-@L+J_t|zSi0l+u}9&2c<<1u0< zjGq7*w`F6G8jlzc{n0auG{F9&jB#t1>fDET& zd(8ljBMcSvtalCDuAj|R$%h~S=y4<;!qC^S?d|_|4XvI6f(#~*LJv=7!Od=5B+cXe zCR3CW2bnlXkzI`oP`xKucdjE>29nm=__K~Q3?&KRN-tqOslK!JN^lo=oB{ z1^o1{S>X{QJs!b(CLP*=l?n z@QEq~_2MfVSFGImwJ$A36}iKnCNpoQ!u*3N1s}U35+nqhG?~HuBeF)|0v=W#SX+P& zRUMrjc+U(xmYX;`BF|XeV!4B*47wgU!GR!qH+8UuQrZg{ix0q%LtI1z6)yN;HG>;G zaLYAT-k?G-2p9`}Tr(Xpazs~W7m6^19_ttgFvnU7MXcMT3afBbVQq(AgD-f;9vTR6 zgjQfMAWkS@t{ZH{Tr{{Ni$i?CR{QBse~N`8-ZmgVls?&z?SlugB*neXBgfK5 zeE44A!F~Jsdy}}5O>5k~zV@y4Z4C|kmM=fy>t7Q`v?N6+xQ30dCmv|Si-PBq&{I3GP{V~4zHEP}tqs>>afb$Cz!wd%{>H5-@>maC zAn)txiRI?va|Fsf_c8XMCBNoP8ogr%{^0K2c(lmQhaN7wp4ijbiLfyUV2&06 z4}KYe2n_!~4xV^k9$Jd~Pq64u%gl%u6u_644L9B$vAje>XvZ%NSA76kCYho}I78S|YD{&O#Jr&-P;RX`K z00wjxXBq>XzS@W}fcHz#z!%;!VZ+VYU;EnEaIjcv$E)mtr`4)HnL6k4RZJ~h8)Z2QMsSEYD!ysH&)od#$xm&X5E0q{TC=o zCa`wL!}HHS`#1rRJ8mxo@veq!LZ>|}3ly9YrKfOU!GTo;mNL-eY$xC1LMow0EfyvC z7#(^5u;4%uRlws8SNR4Ze2F`J8w@KaAmJJqES1o9v6Xi+)z#Hl^xz65s1QH8NUnk( zI(P`Q60q1J5}3n_2of;BpoJ}3fk@yK5kiAnm}8*~4Qjz4c-f<<8VJB>E!W`dp*^^( z7+-+HS_%Opeg&Xo=oaMMTri^j&|`FA-3dM22F;B;uz53{A2R;apTfEpi&S8Y7rdW2 zbO^UECj+rlbVS zv^wW!dy}nfcuSc~;=Xf~WIYYPS)=GNg;KOQdPUO^9rlo=(*w15r-X~gxH||Ju+iyA zCf3%#pf?4H*s%D<`VQID^xf}#*P^aD%3(L zk;@+B7-{&X0eAqARJeenkfjwzVfg20GDOC7<_v8YjXYEUBqy6t5CQDybitBhH__zG zUWX7X^u25ADp7@^Y)TR$3=*5tl0eZDo0w}D%4Yr0tHH1pigF^QSq)1*$B8X$y)1NQFUzT8#t(Y1E^Q+<4?89{{1r}jt@(Qacvdm0>=3!l*&~i5}d}sJRI;5F%!6_hj@no8A0a05rzlG z>A@SbZlpfhF#j)sku>SG_azKvb6fbnFuQ~*e?ZJMkoX?l;SNz58~z>)O$8z!EM`4+ zM36ns$~api44m`i+=;zxqQM>;&VpdZ!VcH5aB4*G@P0JcuyORKj95^CT;h>NnW=_n zHt7ouUWy@m%WN2!Q)oq_CV?XK&`2%3h^)aVgI>c%HbNORLYF~kUA$Wh8EleFHAi~U zBY^;f2YalEF-4$*(913rs>ln^r0P@zm&%)Iot%p;_8NmQi+Spv9HpEqp@AO3%Ze(% zpm^1bhKis{!%x7(=-nL_cDw`^r_?LbHH~WD3z>y%XgDSX`qoZx%!GjPROm>b&W9lYS2cVv#_RCGaB*Z{zUm_b+YK*N z#^D70p9L66lQ`d(n3c^<%lpFY5~}A65rG_v<9{72&OYk|ZW6mMGGu9O}T@>D9U zsHDm@2?Ymi-%HVL=@7o)0=F(8u+-R;Av_E`3=%h8(5S$FZ!A&wI!k%e+kDT7f|XEf zLwyCQFgEaH7~Eyy@Vl8860G2poN%;AX*nqC&1y9aw^l0MDhu~sLu+xk$$MJys~8oO z8S7~*Rh13^v_flD4?%3P$D3-=57sEx+y$q4SN=eix^t-%EJEPEkwERy6;s1FI-Wmi zD>L+H$wck-4a~y_jvMTVfQ(s?eZUS%cjPG;W^m~h#f+7P!2$$n>UjkKLbrRg=n9C4 zT-(^hTp|=bG$ioUFM2VDiHXhcMKLfFD-)-E5jF|T1G5|Pg}suKEN!t7dYs%1!lOzd zR34?0W-1#5-83F?BzoncqRlo%#YJU9Nz6&$c}=60Duxd_UI4~y4@WZL002M$Nklr!??`19PWP zA}~b+(?NH5r<)J%hl+sfjwoha7Q*Im4mvh~89o>TY4~8A6H~xWM1C<0Oa~nP^C&VC z9jSQ3A`j#^snc2;=V!Z-hS%A8z6VULs1t^>`8&$yj%Nj)5Wqn*5;h^UQ#6p6Luha3 zOn0JlJu9awxusFun(b*EHgd&nmpMwn5`*YnLiG+SBnle@6R@%$RtOoe#s(gZQ`;RN z$MvVfQvNRj+?*f`wY%)cd_%N$+hto9W&2V1u?)2qpt>m7`tnS$Bu?*M@=uo4f<(@&EYuhs8 z3sUix09N)~&Z<Q9nM4b z`(tm2To&cSaV+f}0*UP5i;Mu^UB?ixqpLDqd345qbgMwLf#M%MoMR3TpMk%IKKhGn zBbL|rTnHa?9lno;hd8#v9$udQN>I+yL0T+Fk(@Xidv`XsY{3Pevf`RC>7!*)0KgEr zky=?{gdUJS=4e{kC}mCwh1L}G*VxOYGrbp`XN#ni|ZY-EQUZUa==0w z*wow9)Ur7(Ej>{@acue+vjz-6xhRon>&To|p&>0zb(gA94{jKMm5!?6Fo@ZBRHz*r z&Am;HG&Ulo#goQnjMb1FAc?}<+}qsPvKin+@uZT$!P0~35w@XC6BmX#+1$8yGjagt zL}rODvON;#sG)HYlBz(+ffcnk8dGqr7!F~$<)4ZcUp2!6J@BEqCQT!w{L6=s&M>3Q z^G0LardN7OyG$@pdSDRAcu;mB@w*_rq?9^~O{@#Rh0xs_G5|R!*Q5FgRSFyS60^oh zRvdu$*z%OH0|8K+@NN@D47`QM*? zL>sB*;>9aEMxu1BoJt#>RS8)6joJnuM=~~PoCX%`3+TxX>!q6&u6Zo$ZkBYEVXp@iAFYVd9~{{ zFmDB*e!)qdSKLsYn4Y@m@W8i!Ke3*}n15pDmEQn3H4FpZ3g%l9>$gzM^JjTr6DXmu zN&GLqT=$I|J61Q8;)lY|oL+zPce+lRHGxK!zXL4uYn_OhnH%PD7^O4#3MVs$^&?t0 z#4up9Bi;p58s;~Z)U|SDK}rKR)i3Wrp4Yt#<+eG~kh<|@CwdL^lM2bvwJtRPH;l~< z%iC|iHL+$R#>2L`(>t#E<^&3Zj~Zu*BQbAoc%%LHTM}zG5qS1g0+-K9T@L%fV2*5` zJ+*TXur?HNnsGjOk`42=6>YcQQo=FsIB819mEV|DIU6^AfNHSDs+XaJ!Tkh1RojObCU}GDna-l31BSU=-wq-vQzd89aq!FxWBuu?!r`z_AQ` za2b$^h^8((QPhhl{I@ZX})sQW3Le6LrJ-wP5 zjNnsfLt#+5EmREY?Isvs6)G*iMVVKDNi8um%*6<^%SIXpDMc3`BnHM@0SxJ^BQ+!) zZ?3toRysut9dkAGfjJ?nafN_u4F(ugxf^LXDtz4@pOxTIL0A;vqV>DG-s$=7<PSbH*iS<$q{q-mX1~pZ})* z?8QB!MitPNAZj509=kH%bmjDQFzImUyahd&3=v<}E51 z;SYmjX7JV1R+i??ID^1XH*T-LgJKSV`H8&sEwx~t_7=d*{A|$-59U!c;Q1L_ zA$hiu^Q+A{Khlf95=KjH$M-%q{mlf0Q8caO`CU7<+}1U3VR1Gd*@kCgm|soDy_<6L zgDgz34{TC9*e}lDOOcv12?l#%FuyqC{A#)&v3EDG_j&M0QI$Y~koHpRj2Rargn~3O z?3~f)G6Z2LTUvtI4KmJH;28S6W#LEpN?yXQ8673v2W%FIHc5Fv$V@Fo+T+hoMp7jjRl=n!O9ZJ(7b7zEx(Oad zHTN`*PVoVx8xmEqNf5)-Mk$=wJFQD~?J>AWOD?svXA%!=iJ{;{Fq!m?>bLJ(-l_aZ6(=T3FbKl;^ zjmz+;)#yMPH9{J~6sdu!MqrdL+M=D2-aTudnzXz!a_)S~eA?vuTa}qmhFQTCktG&< z$-V-iD*a*BE%&(V6mMpN$3u_)YpkP8VGmay?n|SEaruiq-%%2k}8QWW$ z=soS^^k~{~Pd&XI59y-E9au1!yTH8e=}E69Y#0g3#)Ium8rd2X|y|%+_mnRnwLgJ&RdYIO7vAH(k}i` z&7(a{b_my4hX#dnMu#*df<(MpKl ziqeGWK@Owl+3Yk9qqnLg?SfM#J)O3z_Bk{s4Mz%0?3AsxMnIWT_Q*vWLc1O3~FA7%#GM8k3?5DAEGnCQ$`} zxqCJ7;YsUtfQMm6f`ro53tly@3kpUy=2T`^sd#0eS{s8P1>@5*vGAwXdYpr#LiyE} zsH{^}QXqB(0tTF5X?tP)?n|lpKOb!T{HuFETDjZP1Go zm71jTsFipW*Phan7(ExMX%dxoAK3`@?R;k+QkWZl!_+pv3@2 z^}bewITAyGNz*)Ak|;L3(#Enn3Kj*T)okUFTU)jAgs`Zz)-W)+tArwCQX&;ka84Ky zLzM~{?$i2AC{9a+m%^g6hVPPtTO^q;3lY+8BD)&YXi)`Kss>IW%br{2ecY*-fS^TV zWn*w(HJbv)J`}7}8kh=hl`;pELaQbUb2VZD?@}v8cdvm3FxGyO9MlKo)s8<7<+tLklSPDYZMQQT43H6F%mw4Q6r z;O0x6PUUdq$yf3l$rqvz%7Q8TU!&b({Uy(Qeji40T{gGS=MJZdB%}tHYQxF zG5D1Jw7V{(Meya~ZA7HhAOlvwNZS>_6gEVqu$~&vI+`tHL461pd5O0sZ~nQnn}@Bb@*Yg^hgvBF6i&*$arRT`^1U#Q*g+T zHzN>poMNtHW`w~pckCqQSR~VA!=}%d-B6_u;hQo7HQ~hoXP_8vV|#>awNp@{e$)nT zSd-q~NqMFQc6Mxgn;S@PHd4DABrXpT4>OW5q=XTR#AAsGod^RxC}Fg9bZmPkR*F1( zVQ9>;#Kg`Gt0`uD=76yQaKowqn3~891asF$Y9Lxi;~5QXcn1tFGqu9qv_&eowaQ!q zW`xm!7(kxU*3sFpIFnakfCwsToQPwSsameSj+z=3)*DaKZ0U4)_Wbh1^xD3_)dur78ytk4KoVF9t&% zsosr2WZi?xl>^MEQo#Rb?>qqOxT^I3)ZJ7pH%Yc-xkygMDQ+PZ0;xdgfrJGTl914L z7s8)V1502#?84G^HzcIe0&#kAdXM8Iu5vGmRot5-+p7QHx#!M#cji6GvSULQGEPQ! z&i&4J&OP_eoiq2%o0q8S|4=y-7!gJ%kJIDoe1T6;Bx5Xd0n1SZ$~xRnb+-n~-QF&G{-#*@&mpN(e(*Z_80p zHe`s=hHDdaZw_0*8OTXPmH*I zNx`5Yxs6-ee|^`o&!1Xz_T|I!htkt%vDV9@r96xpkV!FNT_v3k%=6C${15Z)Cr8}2 zIAA_GV)jqO!tB9ixbc*L2df)**MIJ$nzOHPZe)OAZhdORtr7S)cP;y@fQ!Xv1$XAc z%rBPZ6^t33_u$-h%U6yZHl%Rtw!AqHw9P)M{%=1!skpd=UkK3#%G2P^=iJg!>)W0w zzhzX3QR(0T9Kgk32YH7D655(aDQM z9T2UIhlvsTPLIx!OfX%Y&ns%BfiINb^h)87VY$s)_x$>v`YVs0aNd>01yKWo=djh5 zI1_E+R&py*L^?6s76cnrH{-JGB zx>8N#k=8d@VCFe8X`<7jCO!fIOunbVXE}-DOFO?hkw}6kXKL0aG=AZ}hjZN*E z!`gNk{K6c$(eZNm4fAj#kI?<@f%;D!G5);IlogbA_xAN1wwuZ06Mz5DVe`F7oUs^c zK?(eG*u^FNyrx%lBW>TPXmuxSp!TCR)-0MNbijCYOiE;wGa98LiTc_KA%m993WYbf zHzV+71m29mpCtk^6UrPW6Ipuh@*+2tMbBYN=0j!>ZHYPU&0kHA67gt-i9pE}FWNYi zq7&$)MT4vu1talQk*;bPCRC4z<*)BAQ5FM9)6pdgoApCrah)Ig63! ziZ>Gd)$}P-rl@PkU)!{7fbacT>Eh@4@i@nPdC0tX&z}6q zXVjN2ZTR;eH9q+C@cg{q_no!%^PibKX(BJ|^Zl}S&tCbB7557u&gT^^5)GL1w#|Io zgtIQMD4mc7@7*g<{kaeV;0BL)7f+Oo5 z5FcF$aO0Mh&4g~zb2+(rTTeW_`s~Xq%WASW(6vGwHh$N0S;1;L*1G7FK}c=VjjgMi z?)_QW!e^=6HscKE#zbu$_-x>xmo0ooz|-;hipt>v&dSVt`Wo;1dD(*fgJp(gFj}jGge#f{$7wi8bqfA-~L%O{f)6CQf1y}g|onUjUQ9N);Kbq9bld)=soxuKz9 zu-gvcSJOK>`R?r4v12izZHFhZSzC~6kLVEc`>25&^3O;_%Z+5|=ZwvGOvTv1S}!CD z88neUIdWm*%hp6gz0E}a87114uhW4;Lh?F&cvGDS^wkl4$^TCT?fip(rM1Of@YHHljNNU~xucqh74}A2y4bc03VEt6}Yxyu%W_2m6@Gv^@ z2BdZuR0fk}*!XA{#T}{Lo7$RLt*aP3ZdhuN$mJ?3m*GTeo4y1~5-4*R7@5i3;&BM3 z5_3CJt()4KxDl)vGIm&MurhXPAosY_bK>NTi7ptHjn17-WbC=_DHGKAFvvKA7Qf z4Q$)k)<~gZ$hhKorzDx3*`y~?cPGK2!(z!PX9KGnyHaf}?TrjF#zt{!NH{h^=7R+{ zVZVQ7A|OqR2d%FjOuNz6NV#I@*pk#x=3>ft4wYuu+$JGJ;xcU6b9iVn>`p!+>Ka=$ zwxCclZ0|hg-|(9ZS~nVW^zt(7-Me=U88VcxNGsml-i*MT5qL8Khcg09n4~xS&=qe{ zD=jU}K7U3zl4RJ8a(!f`)fcHzW=l9IznZR6Pv@=#3PO&MvRwXxP134JlF${DUX3Ci z0D_NJk`y3D1CoGSipHsOnRX+$mB-7Y#)#)}HdRn3l}%OH8-w~Fk|+Y+jPu`M-c~U6 zYtxn7YP~IU)dkEj;)5JScS24rEsUHGTVX*^q2Ie6p(^QK5DIlE+?%fXpan2gxt4|S zWH~a|Tb3#=8(J*Y(iSdz@@@oaRGdP6^IA4=tdOLa9w|!=D;rv;%mhPq*()|I2skGV zRyVvy;#XqpElmw8Rc5L2y`ms#M6@}JsnXKn{O4<3ey?poVqor z@pPAHdfOhC@Hx#NHa1|^Xo#ogTZBxEB5$X+hYYo3h#wm^>a-SfYSDX>>g@*{VPty2 zIt_H-T9HQ$w8$A)D%DkutvghsHY7r@!#idjbv>z`UP-TY^HkBHc<&Z#BSH4RV6v-6 z^MND%Ukq8>wp_B^qwED29NLQ`p!2br*y3C=1pt%UWy!25M6t0F=(WPpKKG;mgiuvW{)X$8Y%O8Vji zN!5Y2bRb#@j2%)?b6PODIj;qSspsaQ;jNHROdMu~nNSO+VpdF&wSZRG1TaRJ6>maS zG_dg)zzT*@N#YP83)n1w@bUvwkwP$8`I#$Lojwr4XqHo{{NjwY-wIk69)_;i42uv< zScRermB9d7P(r~o&EmA1!9!%IZai^^6`tO!1LmqXr0GjMNirS97{AIB3?Z1Z)oKA< zb>7T*JYsi+5P+!3E;M93QW+uB!XPF|p{r71x+)?^qEml_=<;fz=JeQ>y^o$Czcn zh2o^o(xuWo`CnLEC``dgd~{4`Ze%j6X+v5;!a5}ZRt73%e4w+SM5-W&226oKioSc>1h(_m)13HoJ)PKr)ZU!|{RCDaDnI_SKwlowv zn@*6{mcs;tC-*0_giJRJ9e6h1_4^d|0+6?`eVFwa?F!}mJT@i6!szfo~ki&b4Bhj6a$5gaV zJrtcl!4=H`t4Q<29gqti`i?;{hD_^fb0Sz;ZV``SV!Bo^uobMz>`H|Drb5wb!@f|V z?A3bYgBtSk$f_|!0{Lqgtqn04G9-S6mu-exSZlD%0#`wOa&#bg1rG#?pK_JWktbF& zjb=(bGhrG4eBf?i=#9nPZvzRMhU<{{Y;0k)4}0kPZJ>FgZg9BY;UNR*0EcLIms@)d z22$2g=-2=(=T3Z-+MyP7Z|4*RTtdiRH1I~ zv=DM4K}F7@sQDffIAc8ERIF%tyt?~}uqsmJ9!HcTlbCxH#F}_3JLOTI6|8&e4%K$Z z2@k?CZL%t6^(Tr6m9b9ckSaE*a8;De0hQR{p(?0}v^fk$T^X9t5!;s!;&*ITa z94e0j`eHC!2D5@yg$4ZKP|byMHI0@18Va=rUOr5;19#w0fc(pABcr09MrEmT=Vdr^ zW`>=&twqAj0yQ$*Nn*-E21oMHZM2D8&wNvis`Rh9>kA=_GEAYw4$~Io3`}^h5_JhH zR!CEYJZdHGl_1?siA)f#IglhE!W@~V;o?IGQCgLlIi(1k7lC1{958!8$Pks&E}1Q> zQpW@#?9v5lMfU+e0P$R1ifTPrZ0~0y0$k?V?+53q$qD#-Sin8qsqVwWeE59+OJ~;W z)&RTv@PWG-8v`D7JnDQPV7V~Ao(4uIC?vw9YzT=4v}_c#tOeb>?Aq@XgJS09PwmVK zB<_4g*H=b=k*BrXoS>NW1mb2Go$jbgTu#!2Zq6}Xhm(HsB!R{KOlN8QJ z^HoOElvc$dGZGHN+JvP4S|v=VhYX>5|8})j32kr8iCW~OeF)ho)8$%FMJ;Py$iPUj zsV?8I>ged;E{)hFs6rCTF=lr~Ft48|?QZP3{yL?aB{84~f!7f@7z{G;3+I!u5 zbLMq3y>SWt8ro>lr?Kg5%A~F!Gi%?QJcms!%38>Z7NH~2KWVU=$92f2O&B^lZxaBQ zt;n^httfBOXQJut9;XG@G(W)(S|664vva2bN-M2x`x ztaRrl4sx1=-*hDPcmK!)WYhdw*RX-v2_BI5TXd(Io!8N1=Hud{LoRN{}XrOw5hV9bn>Lp9Fh*T@7Y&WD1!(R={2`Sou@~!y=GBiE=3seQ&$0yJ9p$g z0K||o5;hGZ1(4#c`zS+e*?XfASQXu(g?wfOSneTWz-$x#Kgs+jk*93H5Z#l>eWQgwFTV#y@XcLMIP_CvKkbjb-MH&f)#@5}IT2D=SRSV$l0JmY5@?N>&r}H|l=U#OAb&;T7_1Q1DnQiS z*5{-U&6Pst{!=9T>;=I`P_Ds#*=|7K=xXcTRDKS8A%j_Q+1-%3v>TF2M4J+tmXSpD z>8r0|R5J#1jw;^RJXmqg=SUKBL-8Kvywb9OGa9H2o+koxD3SzN4ufrfU{4Zh2%-8M z#Yt-vW_uH+3mIjgI@vZy^P2dKaZ2gT}5621s3JfwUIwCiIEKNXlh_wieglS zR^zlhLxeVz(}|>U3%hpjz46w0*M9Gz|M=Nc4I8!t!m&MjJ3Bi4YO6ZstckI$+gh)= z_JR6kElRg~&8Dwi_r$8zoAvO;`3-ybcHR2hR~9YVfCQ4?{$b&7|M-g5irdj9*}mHl zXn{SZHAB)pX%-vl+V<_+$(GoV6F5>R-UC2_MJ*W~ZzG|AIw=$h85mSJgc(93Cre{3 zbL-Zv0FZNwLprEhNW@ZZ?xszf5Ws{LKrN_H_fT!v zu;J=2Uwz9hxBU3WKb|*lp5}kFNOMOPgO@K~{`AvNvkI%RA)O^9rItg=4?g%{Q&SVo z#4aiCH^2GK8*jYv7r*$$6Hh#Whcp>O+9GL`8&?DePdG$QtsVk~4IUzZ4;l?>L0T}u z-+c4UJ9g~AtCcHP-f+VW4?pzq$3On@ojZ5xA~6m?izm&^&CfpjEKYI=vu40VPYL<` z0rKx#Z@pC)xdCv8@atdyniN?bBc3HCC1{zGbQwZwIh){D&ry8lnP;xP`fB8J=g$4k zcfJEZwL}j9tsq1iPnZD!PwwQk)yg`sX6x>~kEW|c`O0Lw(GJcL;} z^R!J@^+9C-3J#J!HSExQ(1OWBT~#-Usf4tBDXNw^AL@pLDpwNQ8=(J!afSse_oJ&A z_FCYjWPO1RqkzPr31jNCIT{kl88RL%6|UvvLT~vY3lUFo99sq)SmeLgxOdvev44 zNRsv{TBJ$%V~0YRE%G57O_OVWd^5#$>l;?CXyy`gmV@FO6!AhR?5cQm#EZ`)obIf&9Q)grI7G=hb z(b7C-C-pqgN}E9F05M4hQUhfe3b-nviyCNE(!f-zsk5nN%PKAsRU_-hZOOg?T94Y0|9A+6KaXq5|y`56|> z=jP64VNT^#jjSD$Kc*jM)D=vi0aqy4g)T9cG#Z&;TCQ;x*Q2RszhHA4mA=5rmeq|G z8n|sW06Il|Ho~x=%z$~{Hm+5ToKjg($r80h8eDV&dJ~L-yxgWejex<79$qste{@bq zM@UH7+L)AU6apzoKUAV|sFi&&1gcWRI}IoMveLmhfOle<}>)x)#SJhG#s ztA52M6Zs{X;>eLB z$?eE-=ggTyURYaO`>uDriv`{L?z^w1rsl;LU%c?b3t1EW&2N4~IyHX$_|r~1?fUDl z-@SV`c^f$vwI`i)l4f|2lVI`E@%!(;AKLo*`im~Q=nsGR1DqfDzz10Vg!76ku6X|W z=SkFHyWoNgSP=c)?|z3O+2{M-_rBL&dkr_Ls;cnkV;}q2lqpki35S(=!)qI!f8lvj zI!zo|n#CMVAY4fBwyw4g5)x$$;nm}hKTcMStq*?igRE-bcH3?H_U&!R<(U>-bZaB)d7Z6w`Ckyolyxa87HX3d;M(o6#@ zDk@-O)p^2%33y19Xc{jp`}xm*PFf6@O!xTXkN?V7zJkvL>XJ(?LFc*HpE!j3JJx; zAuyTi;+yGOAzr_qBh$((^slM-foK^<2@{7VOk4thB_!UTWB_8}Rg8SoQO$&fzATxU zL~u>l8+LyTtilkZRhfWLopKrwjn%Xy33UmF6B@<}Ay$RH$^|IV76m7DBc#Mi%khil z{z(K8r9RGvQ90LrUzuI&6tLzqLdv?gMyVt`w6YZpqg0{A*ku|oqEM@@*?Rnm`Y(U0 zZE54U&Q$N&)7E|G8+%STeiH5&K=H1XspV^)*>lr%RrTwkTX)h4Z5MuJV&z*LR8YRD zQjmWu);!aG^Y;MrF1B^EPiXt_S0+|Duw#tEY~W|xVP3XQm<9a*CRR<$#w_eB);!yO zGj2HWtmDOv0Bq@wvm-kn94YxzMdGFrF+U=h^d@POlM@7|3YWm45~;M{E4}-{)N_?abqRS zF|dN7y)v&_|Ke`q3^Vo`@hfJ^l34$+5^OdEVf<>#jpx6KD=e@80&dw~-N&FD_lWl%$F!Sh6~6 zCaaqyUdxs(`_P9z#2PKx7)TaYc~*fO@z`UJMeE^*AO5?)`#TbAY>~*4>5|2gShM(g z@x>RD(tY^C4{vPQ_{(4Z@>}2fmaf`r6MA}kNVu^@KF#94wF?Q(=jG?IczpNWcek{( zkfUpcM`FEd)vA|Xdg=Y|e?Pe(w*<6~?4M-u&O7h??svaS{*41HQRD50Km1{;XgC=* zjn&0qgw@s6B(Ip}?&9~q|2?zp|j6E```cl--!wNH}@n* zAAR(tmtIN(Nx_kzOZKm8;Y*e*nLd3ww+96F-S2t#ZMWV=8jX9T&j0=2|BZ)$i4W;1 z_aWR2{O#ZV?GJzOLjwEQV~;hw)^PDf7ysagKg12fMG{Of2n9i)$=o|>XXz|^=9y>G zzjXA75hHLC%%z{Y^q#x#VX+xo8#iwJ;upX8t6%+!Zlde|<3Iib>ogXhpM2^`c>ezH z|DL>@ZX#~0SFgqm`i_O|HEY)V8{5DB+v+u|zyJO3(^IqsSK-%Ap*!dc4z<6mrw+aV zar{bp@KkSD&A)~=2En({63C<&%t4O2tK2yn5Gdd3CiDWc49(%UyJHR&QL;_1%l6Z9Fw+`l&q6IQn<<7PNde|7*V* zUowd=dil$*oSa;t0Wa(X+;U3JjFXX!`Ta`^n?L*7*CQ|;;!=?`__~XxHJ_X_;}l`` z;G5FSI%7KKHJcW8TzB!b=C=yCw`cSnFn^}u8#h&#kQ_vA^wnTD5^N0jjq)|wW9!{m z)N$R%rZvvWnRyYd82!ij&W)R^OPw1uP&Wl{Ufl7$zd3Hh%n1DQ!lo-5zIjV^>6Dny z5qKZqhUq!8E_Tc>FKW7S{Wou!AYh4Y$h>y*;(g!$o8#6`&zUW5jG6Na%Xg!8t2yH!s=y{ZAaX?u4A#;>MV}VgB^`YhkX9F)Mh>lD+@-im5U8F95ve+qX?BKcc6rGwhVy{DL(b7w!4MRmZM8B4_3YSrQ&Ocj4m3tJi<= zzs8qN=?cLT>-fgx8g@ijc}TD8WFDkV&==(A9yN8;&>=;mM-}Jir=Q7ATb8fu#cZR< zZ`1=ozGrX8X{Xe6c6LAU^s2x4=uCw2*h2JUXQc9Ho-vtq@ibe!#I=o#kd&AGgqjIF z&%lx-57&}*v*=4L=@A(usU8?qNugNHC2M4Do0JzJ85>z91+p~ow2M6(86-=yD3ZsM z+LG*&asJD{{0my-vSe&4R;-vfX(ESjfBV~EAQxx#l)Rjrl0+O^tcK!7VL>6OHn}6I zoBA#}bY33!2Ka`2$j~8}3!BBs1(+pdFr?0`>^}9>Q>^>q38^Q+gBBHT56A_141;8v z4TniXVJ71w2PJJ~gBBIm@>xh$UDWZJO)V0QnwlE+L;y}KLd791!W_A&G@-EYU;p)A zkweZ(GaG3-888+Bs1?d|zTlXervuwDEO!fct`o%Z3?eu#r`yH&<4X8qz8QDPHzYS1}2pr75BdFtnv~Zm%Q)Q6D0h zQ*s8KZlK}2*YgXtY|7)by;o|~+jS@~3Xr{16p7nHm=Q`%gcN|YY_(#Pe{NF}+sA|s zBTTkMRH1sh(y?%;Dqc~wLk=ZWpCT0_QFYTbsb?)r02^C#60-3R(r5A&)MC)Ja8kU?Gi2(%3tM6OB}P?Q4{Iq#~ z8%rLleXc5XUev(%o>q5fThsDKJGgbwXn3j!{A^_!{JvAC-o3YJ`D1CYrw806%ukO= zop(|cx>M`s>}_85s6@lPtvqr?z#Er7T>H$J)cGEq``%OQ?&$z5%rS7J6dzZ{SlNr zOCU)Xv$-F2UK|2gRcgXAd-mlL<;`!kTK*)DE!xd{TIu+ zix;!H{KVr=P(X+xi^MnGd=o+zl6i=Nho@m!yLK&m zVnv{M?t;Z#5^;jV%>&PEbGyPLF{tnW20Y}@4?Xk{kAk4iQaaw!n|vK`^JaOZ9=CaX znL7}kDnUpV%Z>BtHy`_Z+?YnTgRH|Yt1vWY04oT$`V;rc z0GWks?!;uM*kYThtCjR&prOK0E2xazi$n@GelL!kPx43dL=ckd5jlTjZkT!i$QmxI zPdJb`91y>8^-<{P=xk}p8CO|QAWr~tQS2=m)LmO$w6R6rfz6!jn&6akQk|*J-fb-f z6UX)x73OwzbFJ>?fhC4=O4eJE3!1 zi-IBbXK@71pEQnF8|3o6rW^+zU(>nGGrOMi%w5W?ZYc9$VeZ=Qnfo~_Vm;DFOhi52 zS+U4Vl{`&EGXbT>Y`Xc$;S*Olr-UYHmgE0{Zg~*1 zd(fc5ewhm9}WFGYB?d={^SXf<6@Zl-S;bl9~IB?yK&jmHr4C9lKkur6uQmIB^K3A!`d$MsF!iJXct zL{_cr0~qsr=0tasU02NCB=h=4J*3kr{@nHI{4!k&0y`B9FZl*z#oai zs?D;!$t! zph5Z1-t`f_>=`*2`wN(~PZL2hQ64X5rSrV=&g01eQm%8)Jr@8I6PYN}5s5ept|VA2 zX_Nl)AOy>(GiS~ubApsyiWC`j&8MC5UUF#GVtIOjhYCo|u|)>SDlADVc{~ff$jQ_> zWHA+fo*3Z}Pk26oB$AbK%#o3kPj_{8S+A(uxMYpNIzkTlA1fn9j^H^53IqgZy#4$a zKEGx279OW&F%=>C>zBUtB~oqHW$}dPylFBdq}tHpBq|X8=*K_8D;|D;8Ocxn>nG@v zkD|_UFX5uGJWbA$EgKKFu(HdO$|#~mGR+}iIEes{g0Q^JodCfkj8s8IYzg4ce)cn- z;YQ{1%P-%tb0^O`^9(eRWJUiQ-}nakHxWRWm2cvSw?F*h4~aPo(pO%2C69~ncn@uC z-_y=h<2(Sv(l~*|AG(j55ZX&u;wNnfpiAZ){^GxXfe)Yj6hzD>1ONOUX{yHqJ}WXm5TbP^6d3}rcp zyf6TxHq!Ng5azTzAW{*Fk%~8$@XQ@bl|y=rX>qLAk%%O%?Vhrg6De}u*^io_iiU=e zEkMelVIp&8-M8IorOOF*q41l!kyN)@*E(&8XN#&L{dkljf6}P9Cr08ex~3>t^N+M* zG?oSr9#mJCbK`H?ryn=8q@;_c?A~4Q&?{Rn`T#SPd?ZQ}T>He6K{2UgQ`3@_*7k(^DC$9sed>L zuKZXga}nSwzz+>QeY$sJ%kJ$>^NV8ObX%oAb)38A&=8fR5g8?Eb(pERIQz3v$}+mt z=l1&hhMh6P+}P1Lzo_b?g{cA`4QI=PiN-R()4R$`p~&C5d&h>Ciz+{o1`Dq+O9Lwb z-!trOGt%JK6l`%ea8Z8c9}bzrCfeB{EpcZ`Vt7PuIOgSZhV7mi zF}Ln~?Ull+4;G~gqwY?-QBXCdZTX$WyJmHb&<3{dZ0HkQST(tA#UIk(ZGc}btolG< zHs-?0DFVig5oHoO4}QObbs&k)te&Qd9P{sscjJbFckf&evj1C7Zy~#2ct4J>?0grypUg5IA&7& z%HIv!J!9YS60**Gz>W0<74Iy}E9~jzRb}di^F26%*4Skgr!_z9Ml%~!@hx#{o%HVhYs8b_`51oG8n6+y+A9M7$o}S+F;loT9WLfDD z{zF0)dFH--U2E2Eo;`g$5}7(2zyK6M>%RRJP6<@BQ6}Ld%Op|e%Xxfuthki?NHkmQay#=zI)KMXV#4#{kO*6@ysU{9VVx@FM3-18fIUs1Yk?6B*oYyDD(4Wb zi4!NnfEI_`Dv00i#s&$FQOhzsPk!SJq0khTDmHis7ZN?>wt;~JGj6aaT-+Xz)JmC0 zO@<8R9e4?rS~$_+_8J zYVxpQ#Q?xFF)`_L?aXJsFCEpe`>7GPE-n~6BzME+w%hMq_IIaGJnOPy`9n3-fO%~b zzykg+8{M!A@S=jjLvtH9x8FWz+2>EKIqUL1z@yghe!BeTg@r>R@E1-A!Ca_t!!wrx zey04E1%;mZu4SJ;x#sLE0_INKV0N}(Qb_cOg~j;{R=|RBU9Yu%RY1BgmbPeE(qL^ z_PGYe#|-V#@#HLk0_(UhdB+Fk>Qy0OGmHmc)9%g7Ym0J z(H>>H{JTU6~vdjUHOfq57jF z2vGqk)lpltfKd6vL$ri9xLdXHBi7~J?h-xqhj0Pb zaM5cR<&eGlOhFaZG+K&8mol5mf$2sn&ln(Y#t=w^VAzAFy_8i*4~i!dIR&eQP=kqz z+)VJYcSk~+&Yc6bC<0)E&B`h{p#4GXVz)2T+7M$_WMAL|^7i1s=1Hcy_sxxJY7?Gk z;u8{8?}tBuKO~13g@;i}@`Gt^usKv8^a>&qrp#6T>JOd?835llk?VJ~C7h9gC`9p0 zD*;Mda!wc+PR!_DRn!6XRkajAIa6p7LuN1Z;Tv|9RtnO336OWm#X(JnRsf_iPK!ey zKD`=53=ZhQ@<+HM6A?KQGZ$J{x!^Tifgyz9rr~nDY3B`~arP!$sIAHhTFYTyFyz#l zCmNHq#dvIugeU4)p5fC&Q(UsN;otwW@!@BN=jZi);M}dByJ||!gbJ(GRVCI5lz+qK zj2EzL@P5GWxNJ=6{=iFK=*@xo?KJ|fHs-y1_R8;- zq}^B{ZY+6Tz%$=2ZYbD=D+H(G>0H$KX#6#{Avo-pv|#~Z{3F32x8|XG}S=oH=&q@|Q$H~5ZCg96s;3V_P=KFqDvM2_I7V>Ov z0RFjvb8@y%cg$sh8*yeOQ4c*~G*q%NuWG*kXC(`j`HbqbF0U-BNjEUg%$w;N@B4Y# z!e?pywiyEgmpbtF>8DqpeT8Rs11S+_WzGWDIA^+X|1U}xKEt}iHkk2w_{6?_Ufp=V z^SL**ZTe}|XI-&B@GnXiJS`18;neYGUp}^cG8c21XLvNfy`997QOXsb6#lhM%NrlO zzIf4dOvBsWGP~mRkI~)x_U+~JPuO{$m1{qnPWmk3k`9t&aLAPafNr?W#!Svel0l*{ zwrXrap*+47L1KUDL5{VSG6g@R8T@SQRL>oW#11K_Fd`1;J>hYi7DUmuNKuRmzZj*G z>yl$|4v8IqHGziths2s?MMCGD^e7`e-~vX88z-g`sy^W&Zb%N8&M9PR?7>JtNlomvv|)fE9x|D_B{14!XaxHUTSa z;W?ER)|U|7gg?nH3p8br`?Dw}MFx*d4*B_dz6aG(BwUPDJ00*=?>`BkAu~O(s?5Uc z1nZ=cl~(VKaTVXN5_WVZJ|R&&LWV&istKeXX0{@Ggd^n;AC{@-5YI_|u*>IER;vZ} z^pUjMbvLc!3kI^BS6GlIS+-BIBRl@De87|tB@X*{SHSLs?+`1@nizH!gU1a^$xHrqoWOJVziBtH8_k$$Q*&&Hnlfp2M1|lUtk=7U)&e~*g7Qydj^e0G+JWO z$Y9=;YTMY}MB^(4k1b9#un#a+#taxtTnXI(nGYOXRh$|U>y&IhZ`#w$D6SYX5ODj( z_C^RRhK?;sNsdToX{Tt0P439WlBAdgknKG?TXr|~cK3`JGJ06okdDrM;HgFB^=H^} zJ;-`Rhkalh=YXCSR>;&76G8eVEp_y1UTM&uE>^g@{nn;w@4ladqb&@^vKeIhwbL6&=>=}$!Q6QCJ zcq8P)1V%fD4Et59R&i?ZnE+`fX9ul@A!~#Ys$2=xfe@?!>5~HHPQ}}#klLvkX z7uPrf%i~EL>Ju-3*>DeLaXM(YQc+8RO=}6R+}}n*sf*fu_#tWh-q}RG> zU@7|oj{$Ubmi7)U8#d^W>zn{GF^9-RArpe1xJAqNRrcNj z=vCxw(0PXYYB?FKy%AX?k!D2~2G>NL?p!gpQ=em^gF1jmH}mA6Xwpisr4Rn1OVYo5 zKXOnJNhHISbdbFyH+i|d?VDVg1xwNtG9>nL1L^RTd4-5(Ia3#IkM|oE2lv7T@I$%`zjU-&nAm~@MlJqR219|p>8eC~4aGFGOb&|jdyx3R0dIpLL*=`+ zWdA%|#Huiy5|o@A4mA$utsdg`z7FvRPr&n|NdNE;|G*&@(NZ61rj{{*^B}3E3K)&z zP_1h#)F+-=(l(-t95+y5Q-5d_c2)AoBaiS!Tfk)4zCG@bTGY8w=EIE5&5{|?NFbk= zX}NzK%>b4FBUFjW!I&^CX9`0}R%8h1$2UwXQZc5b1v|uqZHX#Xv?e4}I7HDmGYYU_ ztdMElv=SI|Lkn7)P&bUox=AG>|fTgdmyUd3lIDznUUh$wQ3i+d8DP-H!uGXdvTshaok@qQI@KHi7O7Y10NBqb%`W5t@(iudci{q1k(B_zBFk(E~x7tE0^lP2CT=Mi3VB~7Z%X02v#inL2Ce2j zlFvQ&+@3vq-6@`f19(N0l#?b?AY3r>3KT-eTNG(8$tbXkFTR+t@OGCYj+lxkG?TX( z@@_=@=Y=i2`J3Dt$9PjDk4_N~Jm;+}V3;mQA9uIze*Ce=SptWIjzEH({vA@9!cYR(4l@g8&a7xbTd2B* zQFU{}*g{OA!m;wd##egPVX3HIdruY0PC5^>B*@o z|G5)W4x`Ogq-S7%A_qv7gnKt7q-QE&a}$E@ZMxvl%3vsq0}dIoX<3n>iXxF~l`@3j zOcKLrGsDJ);Diqxn#7moPb?-XH=$esgAkbSllC9-MF!ZJ;^Tan0WlvCm{r46x4UExpn(SljuN8*5X+l!fej=Yzy<=g zDPSOA^RO?lu^EzrPPGH{`EY^<8af?F+Ju3)oz>^8q3AmrX**idJu&j8SA51+{l0X_@AO)jM%s(bS90mVk z!oxxR7tP6l|Jqm&8$XO`IWP;|CyFMaEUq zEY?0rv-Fo8NdeI%0i}vmn=)_oBdsJIzij1yWwk;PDntTq5}+C#K1~2%HeH zK>md{FWp9?i=}_*ANMym2*Mh`eZO)R^N=k2caZ_)n{82ohq>Z$)u;1Seo3fH|tnG>2?D zkFn1>N7*c=;KV+^pSfD0s!t2Wk{jYifzV3?6iw{_tr!8)NR$;2z(kgTG>MtZUWk@> zwKlEg_|u1hvI>(`)`SelP)#)4Hz9D_)0-{@QIlQLkjTm1{cI>>k^Xb);j0WwS((;*-tl)v$TAn{?2KqZN1*odu|y*|OBY%ZpmvNNaa)1`iy z(=aU^C|Kt>W097{nq@UoV6IF?qYs4xb>o1dk?`5On9Y-Z-AHx{7ZUw?hqXByrLPpQ zdnVGr{S^8IC*9z)%Ipp7!vL2k@X5+-t~2uvoH;Alop(C${c5q=MXt<}RI_TwKXUEv z9u~2kgv{HowSy;a?CBadIX~Tivm>e`dyXCx}I1}n-OL_D#6so$`hGnzcS0N-Z3_{9%@g!9Akkf}8i-c+Nl*uG{q(?B2 zsFC838rF}Ly06zD->&)7$Vms4JCIc@!~up z$s>(p2@@m%pwZVg(;~8i8~T022-=EbE%2$S5MgRc3G8fSlBv7lD&%Z`;0& zH+k>ewUZ`1_UL0gHBnhnIqkS<`C<;~)RXGC$EH|3(*w@dvM1fG4>eJY?|988cZ+#vBd5?bh2!{a3DBb0W8jqlG$ejb3F<#*SZPz_+Fz1lmlemNDAq_a`8aj)v;WLD?;bpi{ zQ&YqJ35hQl+KxK6A3T!5M-aeR7$z}gJ)X!DG#-z+?6S*P%O|JTD;-vJgz#(zp&+TH zU&f6a$7dZp-9c_mO{gNDQWiBEju-~R32=rW=~Eg3wH(QB;1BjMq4f=R#7ceDa> zHW>I-*JIyuEVpX>zz{$1Sw5n?xv80kGxit&s1sgV#BB^?Qa^&=Q5<>`uNdt#0dENr zH&FzT#F_@;C-X2yZ@>L^93v4Y2pE;KBb-TOh7yULH8!;(v5mMvrPICyASPX&XIkD~q$`*VEfZ!h^x>PiBn1};Q#Uu%7V>a22hve+E-5CV zBIImQyeJyEl(j99ho-Aq8Wt%B>cSRnN|kUvsuV+dok#@P02D%tSz!rJe0Ny5BF~jm z+1PV#GyWK++5}gtOx}1P7av#~U|QrfR=K;dr?2Y_Um9a31_Cxu_5*g4NMFp}WVSz< zK4(LD8Q{cQN?hR68ot%hz-9s>6a$P#;$0$X(u|V05ob<7ENJ+X2~KuOJah@>0|IA_ zhWeK2Mg|z5J3U;*>v7|Nd_EwswM9A_P4BOWGaHy-CVQ5%Et&us&s>KX8(cXgzm}gp z|Q05XZ=Rv5QOP!^=t2(c(k~j8`mjlPJT4xvR?clTwHufb4hlI6-dI4I^^6Q0EUe;kzQaz0E{l2B=4ah9A4kQhE7#Eb;0S8dygWi|lcG*UnwVCB@jq zBDXO-Ld|o`^elZ%W5tl0K)@3p_=yksH1XlH1sHH1LfXPd6V>CZIV7c~7>o2c z<_NF)hGX;#!;&rr!+_y26)duG$a6B#`lh)8hdL*!94eXsp#OJ?#NO7YF-n}MUMMk? zDB33SXwG#tQkR)BO5o#2K*FPYqqD=HAT1GGdQ)d~kJJ}T1(B7tV21{F7#};=97xPS z=)X4&16vy64vp3dPKp97pK-na8j zVF3npMnV@d>%8Vn7(ji{X2!tYji%O8k)lAC@TFW7u zny@NYkbZQoC_gAYSzJ_-Z|id`Z|?s55ukT;0F$-g4Ve(>2twVEP|Ke241mO!e^fCJ zNyXU6Z?u*@a@6(A;#@W=B&08>!>nwaHRNHagMpcA4T(Fe<2*CKYC1gN0c)-D`{~#H zdKxNLD`$k;5NZK+%B0sEVuG}rg5hxkAG8)L0JIDbNDdW=iXLinlv&Gof@36q2r28W zFGkq_=ms+Yn6agD#UpV*4~9Y8NVwopDi{);uoO+6iX1$ec*qhy=_cO+J^hT+xd}&( z5P$}OJ&q|DyU=Qxy>1u<43*ktUSa``m3!=}6@>xd5M~Z35F9q`aU|4o2)Sxuk=O#W z*kW4agFjg0b_+uq0bppc98UVn+BQFZ+ScBNA@G2yC4lOr+SOKQeAFu(P^6N>1~Xu4 z0V7n?z(~yz>Kz>b^_;!VPI{;o2l77bJ0DT57 z`6Qh2#|s!Xb813e5xKP9UH06qg?G`ZODyEr%gnbBgKq+Jn>7J|ITnHysgg7~x{*0w z1*L9IZVaKiQ}T^Ou5OsRSMe;;fXr}(7>zJ+2?iCK_NrUro%|CkPU5K)sgfuHk-_9& zanB91$DgRb`kQUbo5$_#={4yX-aIYqoCo z@og7=Wn$Gd*Hwb@jTfK;uX$$AuK=&d$@K#P!(89Mtk`hUaqSmgAnV z>)5uBe09>;<189|xiJuVvMtWI4Q6AA+<30-=I>Q4;j`V|h8f4UeH1s2_anogQKzh2 z`&_Gl*P`1nz;4D`TJFiSE0CJ`qB98-H3O01b(6QmhV+A zT1}ic%s9${Pt5c=4qhc-WgedeY`*I_mw?;wwYsRF%&XSFxSP-|S_yW;^dkvfZS@Q; z0BCXodaRDHyu4Lwp5Jx*zf>$v?;sa`-KTr(^CGWUJ>sB{dE@1OB*37@p3mO zcvca~fVaIhP<(W?ij)Yr^Fm~qn~uFe#dFf=2yauX6|BmeaP~5}550saWoyKtH*_c% z6K8bd@K9Jd01m7(m`PAHp&>OP{mrTmfUodsJ_i6r_I-h=RVp2^=(3T4aYzA+ES*B8 z?+)US)$4Q4JqL2qBLJwA{FxgZB4>0l2z8@Y2C(zNU|LY2cZ`?(IE=BWb#ogq>o}&! zp&===QKbcK2xZbrZ3R`737x4c##Hz+Bh!)9QZT!aS`~7E`)+o(A@;gYje$9YbQ zr*25Ei5u(I>ZG+otrIS@Yb{bqIFqA`|Ev(Ra*P8?6~Uz8w8)q_WJ@=}MFw?*s9;eG z1_&Faeadn*i7#M0C%5Knk!X<_@7XJ#1VR}T^gF*+OegH>K_gMksMuj#hAPE8+10GKm{CZSfTvKOKRG2x!MBn6qO zB@*7H$PJ?nbv?#uA(!(|!{ouip_te zYx&w&@49VNUEKs8CS`tOuyVi1cQNRI)f*S={mQ$K+H_jZjH9@1?0)p6g{N#8`SqK| zmrNopW(G@wuX)eWn@&wlci_k8FMR9fzQGIkUi0pwHlFI40iU#K#5ZoLE+N+snDt>5 z7g!6FFSv+HmoXp3f1buiAl@Su7G1;U<9)*U@+&J(JZH~G&pGy}BN+dwmlt%uy6n~a ze>>ucsWlo6W+>(bjmDZyi`u_>!O=}8<;U@ zd2!p<-goqC4$Q4T;1e2#Uwdl;tkHON!K&lm)pp_Ajy>i`UO$(5by4^83m44&ec6#m zP70%;-~|g-9ry0G3r|1xn4>)SnFR|Ty`$`?I9NlscFU61uf6}6^(W-aI)>)=JoW0r zX=}^AeOpagoey0Q=e1jwwtoHn$E-a*XSN3~nzp9&+qVliY@nO39ry#steuuK`&eb} z3oNIw8%V?xq>datuQ_B(#ChG8`rY67;4y2C6=s&!o>{o)n3W~h-Zp8(kz8BpZZ0QG z;tURc=zYz`MLWNJ(UGf8!Y9E=6#G%RmGzB`BvB&gAej}GB_x-kTG#-!} zeyF%XqKb4nxeg;a(doM8YIwsiJhEC8KXY>NkWC|&pC{{`%)trftm>~*_-E`RX}J5j z$FK>63po-35Xuj(EHUN7is*f#2S?Js78b~)^=<(q@x*glp#{o^qZ2WTMUo%L6KmK= zq|t6pq`)BqZy-;J@LFR=w_H4IuM*p96{Nx&2Q%S{mhLTJ;h`&|s2HyvXr-+;af&Rs31Os|`9V(*yIomCZ` zr@b|=qi5-eD<0?0J*U^b zX40E+;KdKt&YReKb_9O!X?1_x*|g%ZY~aq#0<1IbaU`*AQtl(~WgwCWJ-4 z$X`{FzO%9v-R4?J-1qHkyX&6j*~iw+n%-M6rn9o5^OTeL_1dWqKGeWBF^GoK_B!pU z&P_`ms(pT}^SOd(b&xx6#rbH=$6-0` zsV;HjnK7wzXLn8TZq(h=(Y(yL5qkl-fHy9Exc2GM9z3BU_dTc7-P_e%|L8usp!&p) zKoX6Pj<$R5ZJIHqZuX3nM&sn!xn(6&?|*Ou%Oy586x`X_e)oM%GbY8rBTJ{={{Ucl zgRHu(;oqZfJUJqD-bv!KxN&mb{k<*XMvsLKW?C=c`iEs=H;`+}g*AbDYnexbg0{);(OX zvHlUFn(JPTs9i-dG~x8)K5?xb#{-$Jf*i00mx`<8~bR8^6p$tRW;* zXH;vgN?_Kz(1b2Ro`+`N-X=SF1*Y3FnN>nQ-NpBZd*t>EDjrtZwP#mHcL#C~2hi5n z1^E-0=*vHiCw&xdzr?a`<(D-C&`y zjP~@0kOO8+FN6$OB?FkAAv3C+fM8{>c%4#d@u6WpR6MVg+90~ zY;Ye8I-xMDZ`_zuU0F~lKLsVx7*y0fd3;ezi#%i|s_uSL(+Z;D8aR$$&`L*RTy=NU zKo^ZZ>XP*I_{(Gqlu>zW)o$@x6d`<%;{@c@sn?%uw! zo9^{JDD7k8mCY?1Q`J?4MSg7XP-gd}v4t%y@_8r#KMO0kqibV}fTPgm78Q0+982f` z`+?*dElt3U?(Lfjal-}@t5~=Z_gU})?jodx6UV0;Smb<;;_PVM&NBn1fkli@8KS%x zH(>T{OY6AE#1mWsn+(1i&qS%9W*pCi=ID6Fjd9~#cgG%BBu(LAYZ_QEVO-CkXl(E^ zYn4?!J2vwJG_Q9?3dVEN&!Amzv|^U19wrxqbL22tIE1u`!H_sSgogG@#pz5=%$^s? z$@J&eO8N34iaeHp9N+lb922uXeanJ0a?0%GvDe&OsU1YUJi1MpHyo2U%Oef$QqGI9 zd95DbmDI&ckYK1)Lwr+>@{plJs3pTwDg+#|ai|+4q|x)%%)9Ki%U3n8BDdzhB@Iiy zcGuVc^Wh)uY~T4ONV5NoeMDbiorr}S65aOO#RCUGIB@VmAmI<2K-st9Fx$=Qsy6l@ z=F;5sCDVmVC@8r9v@Fyvm(_yR>Y+CohB~?iCjm|#@*7p6046j|txDLNGos=)MJ5=+ zL(>IrV0e@Yq!gk}0qkS7E*i>d455iwS*ZdQsby1%UZ-8TAF8xVaUOL{6~PG1s3N;2 zv_cva`QV+gL)8uT)UXLdD#esIwJzt?Wh@@>qq6)=RPXNH`47Fid&(5%Px*F;!bG7r zH7GSGud24aesNxFTTfn2Z(dH`j@H(ed4npa6sGd&ST~pitKj^qDIT1!;N81hUK&*8 z!44M#?^z0Sdk;&+xiIhE-INBWnbT_N9+#jwSAAIAK>&0`4T#$tUSg${E*ebwb+tJ! z*SEK|@j<_fMr-~93wGDl${Y9rM*K2riH0C3{ah}hnc=?;z6BNjJ`QbjR0=#-tiTQ|H?R8^aHLo9f3f%v>IZ&#a-GvEgGxzPKp z%}k~E(T2L(+<8mdTHAQZOwmlVQXEj|89_Y7=gL+)zt}DLMJ~w-hr3cQ4dyTz{yx!FX*XfV?$df4dkDTGtBKP(#-kd^TNWhwHPI(Fk;F@G`ez9 z+p2~6#M!w)p?$;Bf{KZSd4+tItv)CAKAuLDsJ26E0iL-o5Em&RkN)E@A+ah7+EOn; zDX!ZmG9nKF$`&J3e!O*fA&<7O9Le7N`-X@BgO+zqaQ5E01F&=ecRLJw=67F@NecU`@(54a902>9kssg z+47qg6b>Ge+o;T^)?_eqvn$OH#-T++#IE1j^OX~Rq}qccg!M=ykiQPxl`HEeIeB>n z6=U)qd35cPWg~_SDcZ6%?~f0(opN;j7d|_wxVS{4!R0}2xbt%I`9@0FsP*m7m*4nu z;gF&D+iMS!p;rVEgBByflOGmBU z_j395FN)6|d|dT;S2?f@Q45^|k6zpPO8GBe@XWutxBkj$S;4OR<R^cVjxnnn zC8O4My;}ai!kpK7FzN)%KVFmlQi{8ST6I8x>QDNK=WmU5Fvf-&Gh$umc6hE-dh%B1mv`o>=F!`wR@ z#JwN|kHh~ejuOpJ9R4XofN8pe_r&p52i`NyNakI2yrhCF1w)hc0Q| z@AI}b-mP}sb=QI66~(MT^K2_Wkl&W#u?rmB8QWgQ?YnKqD<1v%b`J%$Md@? zyyuwo{k4YIpt|IeOE^Sj^qA3j!uQ^ISv9XZ=0|Ptgx4N(g79iQo-3G8GoiG&v|&@j z;DW&uD<Dymq8yQH9PevD? zjnIL1uIb44b7C~z@nH*cxqOq!L|dZU(;0T6B5{biNi4D|Ojf0V-oFq4Nv4(wvlsdp ztVU}EQ`c*Q9a0FlOb0R<3?Ejh(8xLTRFQ`Uwt``dFf4>rnMjSZHxa}?#t_N-f@OLV zlSG~?r;%8Ot@85n(%I9ycze!Wb6Z!e-F(4WYrp=LN#n+iaTh-c0_h906$vYnl0jwN z$4p;6dPD1u=e7=RYPjsYiSPg7sG+>wPxBNH*4a(LYsNITZhvO$;KqheomcbzFOF8Q z5N88d0De}OKXq=+2fj3VXq97@$9TOP`^~Uj$4Sc7SB4#iI1L6a4IDnaWcG}nHLF+5 zncKR2^_KJBzUG@>oj87c1y%Hh`pk(+IFJ+hE27~Q2r;OYv6*sD!8$}z|fAXyA4_!Sb;YQl$(p~{?Y~AtfwxY)M zpL|F42fsX~IPNoG7mc#AlG)RH*R5MV=i%06tGAqe`kHIMRx_bmx;qvP4=(2U_4N*1 zziP`_r#bM146qMfY3k?`SB>4&#@N8;Pnk!D zf%%4I@35j0$Gkbs{Ne0i83=Ad)2LdIMea81?#PXj9DE+PrEUAuTMA!W|2Joh`_PxG zN++-g2OOsV&)%EB=}}dA|IgEF*6u8j?sRvO&JGEiWCIBTgD8TExQqte#vS)}#!*2L z6!bsN%;)|4&VT&RIO7H`;JBdd2r5e;h%DJ4D_J|;*%OjIRhu@x9I|QCR?>s9V^1A4Xedgo ze1;zrpl>>U>d^OJzqO-{4|pdHY#dw|AYh=fcO%rp9eB#n_g$9{_AvdM0#6^2H+jf=zqYlb zm1rb=b_M}+9>I&F6E{Xq8v35EZV@+%&JB4nuN;hw# z>0iDvIKoX$rwkd=TQp9X+Vs!g81m@|eDaVXwccgQpE@9NJfKp?7eeYbAB~JbJ{$!IxjN<(;#1Wc>K? zJVTAof!ygqUees$%)7E#vF2?cq}^oL>{)VVaa!|WlHfxJ<#jMDb@Qrl-Wx)e%v~j{ zb|a9obDIb6SmAbRc-VgSv!Ag#&dnht<-8o^_SG;xozsiiubXJ}SR#E<1` zYKBj%86FkoR++eol;nIKoQZ}<`LOtM`me;#wovqj21q#>+ChLDHHE=7>4V~_J)4%H z>J99T3_NrwkLMevMH?GQR8*oi0hU`7N^VE+IcazF;6XKmc#3&AM3anKaxo_$q-BCl z1}badLI<-O{D*l$qff5`+lWLS`P5n4GkDnGsbt|SU3Ydz&&tixsz!Y%Rok!cV+8D! z=_9@$rV$`{WWZz3eUX3@H(rG?j_W6KNkSea(I znl}q_>o)JZhKeuQct|rDHaAkUq)n#F(l|LZ4~`%;*B20yppyr)fK7AUBEoGXyjGC2 zfR$`Aa&mp{KjD5A-j2%M+v23AeLaOOySC09dltWE*s*6v!|;appY^_<{po+E*H4=> zX%6>mv3CBouYHYgWjPbs`cEr8zWw_bJp%5Xj(UmXLSllp;|42}QF7Ta>4TZ5KxK-@ z091u?k|t5g!a{O`<|?h$1cRwYua~J#g&6hU!HgpoK$fafkW!^EW@Q54P>vh$QZGDN zLj_}+YS)v=U$ZhnYt7abb-H%9*OUb#+#|lg&+%2cM(a8FIRM9wAK0DsrGx`OF&W(%w3so-jQ4J)#&h?o~ zGCx}49-A;+M-}Iz0*9@Hvg;uCbS73S=%D$moz*>GOmmvB7N`r`EZrgSoi+b-lcIYl z6;1H^ViFhb2&4&y8Ltoa6{!MVwUJK$k(a<-zxTxnr24L)E91}bId6u=xRz|;y=SsAv# z^jd%b7!v@<;FZ?))s^y!M~a+G2fGec$RoVP6Mh5<4`&vSKp|zv)*_&`6!8Js1l8wi zRLNdVLS$BHwWS30Bt?}1r!ZDDZ&fgT*4+5b1dQQLNExPzxiC|zU?$e0X>tXu6d4L2 zk7%xVZB)Si?9R3lV_aRTh%G~udq1eO=V7B%m`iBl8I>W&=Rm5$x2DUGlSw(A>D)UV z#U(dc$9mVkFNOwG*vu{I z%}DUsU}El@2xS8sQu@n3>37U4Ca!3}Nf zqrYaW0(&Y_4l6VoNfKMx7(*5o1(e!@#P&r8!wEC{vd&csio(N3xAwydqY3l4ljoPw$9Wv_&OQqsmplM|WgCJm9 zbPcJwxFJJO+&I`N?}Jn%x5@Gmw(F3bQ`F)xA3<3f#~(DQO|4LxV1TS97bZNtRX|%^ z8*Ymfw_=5&#oZ-1E$$R|3dJqByBBx2;_g!1o#5^s+}%&Uf1kZ?SF%Wl|QnTXuFtoDSrqcfDxv zk{EvpJji=k$6OX41kH;XVk)oBcuz(Ap+~JSN~q#}rv_e=P zphn?tswqAKu~du=vmn%Zo%`K2Qi2jf-Mb{RkKP0I^_UQbH3P@~%&JUNU zKflxKdjdKtaFQ8RB>EK1@cy7dTYlk{*UmLY^(j*_UO}jLpw?!PdP}h$GcTMOw-{8X zABI1~dvC|wf~pM`D&W+%!pCNnCYxp*7BxXEQpd}cFBK`Hm2TQKbQ_Z;+CL3vrvw%5 z0V)eW!Fr6}MUCe_f80xc`wA^w&!#n^>z0_I)3*>dSGdSWTr6d6xu9(^aJu-y^2`XF4 zZumH*0aye)#`*Mf6vgSRHSwBCl{nRumVc~ME2ynyk@CH3OMcCyv8PrzY1b2AJ|0)b zFpRq!IREsh`asEFO-1GaQT1rH6Lq^kh_|nKPlPC7{3}qqMhhc}GWS&_7SOKo$~ftq zl;=#-@Z*JL{%ZDB3O7k$3J^nAIc8?+B*O?r(;J79eCS@I54M@K2v{|qF;_m~5+AR* zyT*{KoO!1rmYRFkRZk*_J|)_D{(J``hloh?*+83+ds~#~*pAj->8DS0A1&lDvt zD|8OMgvhSu#K6RzO6Zb%s4SfR{2v%IeJzh^;F^9xs$rNfxT(tXs>t^w=KB#MtG!Zb zg4&nDMy(FZHG(F>;=1_Uk35y`s9%Or3u0`nRhqF3HHUqtLIlmx2x&J#9S&CImO#o} zWo;ydF+>Z3MfJU~1I3IsJF!&8j5~#Krx@kHt@l5$ow=WJoqw##H||8-FA{x*A5NP3 zRGp|@>N<`PK)?6bt+27`oR{DB#9UAkUB|dwRr;e|eK69%k*LV!bG%1sO&VKwC}(XZ zTpNitR33zxC|OhYvD^c$ACSzCTm5mPrNHVs&kJuryKf+j0lu^wTi&XUsF{}EJ;knf z{f0-R%up`@Uo)r@vFweEx`k+;til>Tcw?N*T@a`5#SQp$*u4g4d{#!MCu<||^;pRi zuxZ7&BQWxVQZ>MJ$&On3ThOpLbE729v_qr5df%h&-9G*iC;T6{s0>?K^DK!DEQ&?K zKK}G~JsfBWar_8XwhF!?_Os(qQ9CC=KQadm`^mxe7TSP@H^` zBjUZ9`D$BAtlH*svT`AO9Y{w}-0pl|7+8j2v(|LghT&W7?smgJ`KS|boBk7Qgu^Dd zAA|Wc7IdF9HAFiHgz@sJPpB{~H=(6v@j%Nv4Rl<)r8P-yMX8DZlbbQM{;M`#MND{l z<@WecSB4}uy(F_ojnVWAuut5vfphOX;!l}w$IXbpUPWxX9e=NE zkC0tN+(WL6muVEPPmg|Xg>_Uh?pW$5ACr%CP%Ny!-&>aO>`f#t&Ri76<*XO%@%8kN z-#>>Ahb&yr9}=eUlbYoR+Fii65L|{8xBK0OBO?Nd7>Tsm%;LJhbKEE2`5XCAZEo9N zdg}VqtPV1I1GFYhMJggz2h`mH2kYJ8id+!D`)U(3^DcBi{-|Uy$%(%*gU#<*G81Mi z0d&Bzh&C)rJS{FtrJt+77;R+tr?~*q$kyQ~(To+B3R8I+2hq?RLT9kcaK%>0ZqeLs zh!rzAK=^iv7qCJ}FaWuP%(DjLz!C%?YzYWi$CG=W{^f*YR=LvL;j=j zESn4%Q=aju$ANw}BXS^oMlBrQjh0BL^qYNxKLIl}7^Stx@pR!)?cw4%LGLbw8+<1z(!ZSf+X)QF+7x zGA;bVma zQpDVDAs3(ty4B7h@8zgW18}COIXr4qX!f8$YTzv08W5$jhg>Ik4=$Ii-5%8#CP}v6 zqK$^4#rt9~Yj>)ED{zra1p0y;c2nQO3syK~;>WQdRkX0>2pazc{vCxoRz1G=ZTcGT z2Sk|Z-(Fz&yx0`C+iZsvljy%+26HgK#)Q7#>>S~-*yufPT_dpg%nuMF>=1VL0m)}$ zCAvSs8gqp&&h{JRIejG3%snvoVf=M1hs+qj8OTM7jpnSkbXpnACaL=)M~x4^+H9#i z9)=CTinJvfAtSgrq@D^mZ=X_oQ3fYPR0pV`6_RRU=n-p;($T9QXhX}ftup|#L3>vZ<~irGn!!cim_)Y;+) zv!@N%0?V=RGY(R~x}<6eFe>>1xkj>-;q#!`!qq-_ zke9|4)P_~##M%-DV6FBp*o8-6zO~Lg>-ljwHynt!CDAu~a_PQXk+5>0$6I^kbhSLh z3)2RDGxfUv@tEW-3*dIa3pbNrzKpWsGo1uOfF#bnm%Nk@izLL z<)?PbvwRr+R`|SRt=UXMj((&i=L&7ug>f@RdS{T0{c-DrYf6%a*sh%diX!Scm^;oh%xb18}0JE{PR`f2%>pZ#JSk1ZW&5_Lh@OaAkF}!BHFn2#ym4%UrM_Le0lyXQue` zHT7RUVOMaMPgi~YhU3sf_Ko3TfHJuDw@l6NvZS9m456cmHkzNP&0HKfbkahu!Qqqw zdg0@5%QoqpoLcIdsLbn5k@l#id3AYmk!S!d@pRP~l>yKQiea!OU#Q%l-4G#ZLo^*b zweKuIcPC1oRm~&9Jhgo`?z2=}Z0f4$;Lih(U-GbgD=LW?0BmVO#(!3j1wIY_{aJJB z9f-uxAVgjJZ$w>0whu8Z-otS^m8bzP<7WnHqH1Z_SLA*;m7a;C)Smfc=DNF}IF71` z->YN1SC7~XAK;m)u}z0(9Tu=cTQ4@s3E10h`wUQ-!CshF-A$$XqnR85hGv`!*BB$a zzC_c@Wh}3%dYs?pr6oA+;4-0B>7PKY+szQb>uIo-3mfHC|8tNs;bW_Nv5lv^A=rxO zabzGv7&DMU-$;MWdEKNKDA>lc>hJrux6>43+x#p!$hc;Wd9yQd!Yu4Owzk9AEtu~U zNT^}`cpWpipr8!ML+86puY?h@8C&bEsAsEw8OM%TsamqTi!rJ}sbH^OZrz)@OUYTe zKi;uM=)?t7X?`S;#ssGQe13=9a?M$BF}1(fHS9#Y99r<*r-5;q{<#D7nysQU`KPCn zRL2($lmNj4*y?y3KD!QW@n>tV-A~^wzN&T~hlsYl<3gkwZ|SZi9*a-KT~`6@eD8C4 zt+F4$Y40vg$DOctNhaisZeowKrb2|S_l;TZ3Ht0S;MG%faDtZiehEV%?IRM2GpHGn(vvs$ut{UlG0yIhV=)2*&vZ4YRahQqQ&8B~wkO*=0oOYiy zd0u{Pp5{2-7?9=mHXd&T$m6^3Y*$T)u({Rz&4gJ|XN0qPpq8K!XYNNkHgAT9=8XEP zRgIln|A&p{KDWH(19tRDSZcz;TAfo={iwu8Ca|HhHC0QZ&?3nupV1*+3i#C%>hyWZX(x=6J*dK56r zolh2`NQJX;Z}0D2fIkd(;j!GIAm)?YD$&i>laF@8>l4K+x8r0zo#rcTBL<49;_gK& z$ZqvuAvSqRrgi>LCjH1a21CCNtmnhQ08);p-GT=#+3XEG8(=i{*QoacW9gfxgQn(D zi-M92>`y~r6Rt>TGW?d54*|1&gAjaovz?;G0g5{5!02HzJ=;tX{@;z@E-NYP_HPT( z->@1|mm`8xd27x(5~3#;JxmCQ0^=!ba{V^GhUk5cGFTVBu#=u7o+C(Fm@|{aIyW#d z6|gh7sJ31Ax<93>H@%&UIwsQFbiXejYx6AHaJ-NJHbnT0?PP{q2Qxdc1I5>!pNDHj zYCG)AS7$t_g?VcadYAQ#PFm9?Q+_$zCvKiv=(!8CY|!4D^X1KvygmkHFHVMa+wyvj zRzEaV{^{&rt4*x&&Vn_>7^XZj69c^pU} z0F8TygC{Cweff_RjXZG#sya~tz}1{F#;sys{-+otWLqB5R5$_G(KCdVxhRodGz?(m z8k8I-P#MBaMjJLjzYlhz5oeCx-lQrGp_nRV}<*}X)z&C`u=0Ftk2&BLyj@%2S( z^RrE_t_Uch<7jwCJ3ifG=UJ~)SHTezk$G3(dg{&PdiSRbQ9HTnd_4?o&xagBUG`KQ ziMVs*8;%EgAM*m%nDP{!uBQsRheR8F@Y)}EWvSDUl7q-pL9_T4&#}4}>P=x(FmOoM z3mb$>XL=#=0N#h(__xVzy8J5F(b{`?pgQ}yjlP?xx-Ppv0j*%#Jc6p4xA)hgE>a{# z{o_gxrEO!#v&Kv;-xyuyIu~n>U#*^58^{f`?|n0Y}UKg z;pwoVRiJ>YBejC2}#RHkf(TaUl3~c%lGED-CAM6L3xUHRxE~VPpRQ` z=}uCgN5eY1g%;_Z;`$SMR<*A^U{tM1WhKY~(JxvtAm2fSQKR z7QB%Wv%k+KYsQG!pIQpSH}^@4{;uc>LBQQnL`Ruvh?D-v}5xj ziL_M5=|pGbf86JvjOWKT*f?mH$ve1Zf;mv3<3cL*7wHAQKUFRaC$TaTNCc^h`b)?; z-N@6b&3#s@q9Ds?HkLV_@B1Sjo~OjU`8hLgre{tO(>sw|1g$foAV16vM3sAIs7h9V z%oP{bdeEU}XHH%RBoQ6a<%%EfXQk&6?JMbPoy;=l<8S+CO=o3UQdVV}$GF>de0(l# zRQ}Iq-(T`tU^-E4Pp3%<@6**RZ+Fgw$ia4&4t)lp`z#a&Ra(V0#gQ0L1Xl@1~*d+@?sW-=rcK&L*OPiS9<&@UW>G^KqRR32>hY* zX5DJL6sGn@TiwTOyXXkS{cwVE(2iWwcA$ARc_Y}ILSK?KI$)%&0Ei_NRlxLgd)@vp zeyh?CiP%2DtPj1Uih!fa^WaNFz2AE34i%P86V6a%RYld#%rIO$DUEZCZP`k@_{Qm$ z!F1+#gd=wEQfnX|<8&;An`ZPDzR|sXva|#`U^9mmPs@^oZhrjMnDyT-;iq_q}&A8)^&y? zup&F2_siAWrZEtK<(~J2Cn?L;796-j^hRGof2pU88(&$I`%|6GLa}??=abELta!ZK z1hxb!M{GoC-Uprf)bYx1YcKv}<#Ql3#)O*%iExPP%ait+S-P9iEysC53l<*zZ&OQ) z`)klu#ovqNu7_rS3PngldvT}jo@7Z@hcw4?U^4reRG*XMA*e+_XD_^7_&!3sYp)Xi zK%PIvDRUB@^mPm2$)KCxzS$?ZgM0ZCiio->?kJIOU<{$v)3vTklqbMDR zTG<%zeDN#DTE+h8#CxffQRYas6kWW|u_v9=51DLmyjzCCvl2PDnYjcliJpc>w7^Bb zKBORx7kjXm=)1+cQOZ6ZgRdG_c?n!?Vk=HPuBbp=1MZrhQNs$*xX=b}T{=$8z!%SA zFYLqUmHILL&~}*mJxGCP*bjPMr5LBv)$t`#1s%Pj|1Ru77hk~UGR}4g z%%p;~Z)yA#b{HT}7n|Owu`=W&62`gcmX#;3#aKrO(YZV7_rSei8#F*y-NN9Y=2>@| zwG?tkkb6-393H^M)~ns}R>1I{I1!fVKSH!CDlZ;bo72hep@^o85UiFy=@2bRTtOsQ zYP4*FnQCsm+19onR`%=~oO`o%JQ^ZRJZ6h~d8wVJT`Sr>k9Hx~yiLu6{*XvFu-dsQ zru(>hniFeVumv?x)9$?WZTIl`s#ejzX7F>+6rk6((#oQ+TlNvrzQXe=s)b zE1VBLHn7HbgHWe)<^=$u=nfV1uIH;AK1yo4RDQvmic=oRxtRNLOwQ>7g3kG12_EZA-PwVJqILf#|g zb<#`v?^|-7J*DzCVAt4TPXh3;GX-PYT3OBTaprxJQY{3$>@Kre9rLw6=P%F@@LF0V0TsczqblKj%Z^l2KQ#9hG6J_;a&rEYGwTy%CO+-WlK z!DrihA}rAC73+bJ+M;86DWdgM)9QQ{6WMNWxZmAl!nC?#+iLP;Hkoz#90QfCbhiE{ z)k@N4>Em!1(nE;8frtTbrUPaZ7C!5tA*!fg%Aa;=uCU;{mk*vD1Vz=JNJ7Ay$AH-{ zj`urKr0y)_Dm$U-3pb~JjGPJr0kO45B#>jquZQj2!8ImgCn3BsyWK4#3ZF~2`EIg_ zC>;$ia0M7e7`fg#))~huq@~?uu5}m<;r*y`OxT%vD>W?zK6ES4m*D*^_3Py$fHJM6 zoWj({x*vnInqV*07v?6@zE^qm+GV3bKt>a^E1 zZZjq+a8WQ<37#iX9nM13(&SGoIYFyg*QsN!m6f7w0P=RhgfA4P0J<`RFj zXb?)%Hd<`#GM=+Lha=9b2Izio6;xO%go@@#wAIJEDMjvPjQNe)y6xZx|D zFlWak&`{K>Baath*Va{Ry4ej&&Z(h>lC}--y^?T1btTbzPMZwKiL`neJap=}1b4Lp zCmt{C@1#vrWjF%CwRv2*0Y@qV@S-{Ko&eDH$n6LLWKgGDRTg*t<^)2NO)JqyGqM|G z&{hS|)k!83L3472{o%_UKr9}3I&tr)}IqOdZi9r*h7J~{HrIXjPZ(JAa2N4!2xdUEAqOGYFIu8n)Y7~qJ zB;kGWjK{S-(x|R|MUu`GfZtN{hX^7U+vL>U@fS^PSzxH7S5eFUcFZ!M%zFDj9(1$# zMGGc9E&yboLHw&PZJT3^`}M+i)#>8ZkWr`Wt~V>^@o>+6_qZbY;!z4tDp<|P+VF(^ zTGN;Aizw?rvJC@Z^C^c*QCJ41$?D!}sOug!-*Rw|9Tu!*ZR3|h|M&`C^r=zksVc;| zDLN}(Ie*14WKOJ{jYf$+*h9(mg~(_j1?AnYCyXb-dTIQRRM z>;*7%PIi^JZXMvUKZOvDU<^$&u}Jj63KAg2p%&2|LGT4464kKt@u#W?FC5mquz8m) zOF`_&;d$uHOw7aY^gBC1(Q)f}dXh%}7AwjXx}>aN)2exy@=?{KxYSM@R6&Cqu_2kY z-|T(dSYtJ!tAcBMwH6Fk2oU=`38#69eSmaf6IfqnYQd=HCQu@Wp~3dd+$3@V!~DO< zCCGs{Wfv<@uk~|agvlK&+Np%k3|cxjYXot=kzGHL=~7{j!{Ih>jj!-SzxhnXmSFHb z6Y(`MEfCdSh{6h?Z*HMmhq!qna9`vv=m#Hg3+G13-_x~^1At103Q-B&xw8P`8?3kg za&pb1k?2ww{FCN%@H$_bnLf@l1a&yoC>ks^4A`-ll?RD7=>nS0ZTE*4F;2&$bWIWO z98y<%I)bB_URJy3(5&$Zf~$-Xpl#2)qq19nL~#ZG01b$o4{|7K@HBnpewcRc*x|4a zv7A`i{`P7D1Xj86Hc#LuYkhaw!8SwApj}^*D2k7XWMtt`V?TWwKG?#JM3sMWtFPjryFn8jQqLD#JIF zg_+7?{jGWaE%W42p(81X`9_;l>b3NvN+

FTRb$v{IRXA%qwy9akwd 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

Ze^13dpy2~rtGLPi9*T&+Y0 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%0AcTS?nh0K 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 Z{1?ZqTkB5)+I|24002ovPDHLkV1lubidX;u diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 2cc31b59852..cf3b4d5785a 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -4,23 +4,34 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Introductory Tutorial" + "# Introductory Tutorial\n", + "\n", + "### The Boltzmann Wealth Model " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ + "**Important:** \n", + "- If you are just exploring Mesa and want the fastest way to execute the code we recommend executing this tutorial online in a Colab notebook. [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/intro_tutorial.ipynb)\n", + "- If you have installed mesa and are running locally, please ensure that your [Mesa version](https://pypi.org/project/Mesa/) is up-to-date in order to run this tutorial.\n", + "\n", "## Tutorial Description\n", - "Important: you must ensure that your Mesa version is up-to-date in order to run this tutorial.\n", "\n", "[Mesa](https://github.com/projectmesa/mesa) is a Python framework for [agent-based modeling](https://en.wikipedia.org/wiki/Agent-based_model). This tutorial will assist you in getting started. Working through the tutorial will help you discover the core features of Mesa. Through the tutorial, you are walked through creating a starter-level model. Functionality is added progressively as the process unfolds. Should anyone find any errors, bugs, have a suggestion, or just are looking for clarification, [let us know](https://github.com/projectmesa/mesa/issues)!\n", "\n", - "The premise of this tutorial is to create a starter-level model representing agents exchanging money. This exchange of money affects wealth. Next, *space* is added to allow agents to move based on the change in wealth as time progresses.\n", + "The premise of this tutorial is to create a starter-level model representing agents exchanging money. This exchange of money affects wealth. \n", + "\n", + "Next, *space* is added to allow agents to move based on the change in wealth as time progresses.\n", + "\n", + "Two of Mesa's analytic tools: the *data collector* and *batch runner* are then used to examine the dynamics of this simple model. \n", + "\n", + "### More Tutorials: \n", "\n", - "Two of Mesa's analytic tools: the *data collector* and *batch runner* will be used to examine this movement. After that an *interactive visualization* is added which allows model viewing as it runs.\n", + "Visualization: There is a separate [visualization tutorial](https://mesa.readthedocs.io/en/stable/tutorials/visualization_tutorial.html) that will take users through building a visualization for this model (aka Boltzmann Wealth Model).\n", "\n", - "Finally, the creation of a custom visualization module in JavaScript is explored." + "Advanced Visualization (legacy): There is also an [advanced visualization tutorial](https://mesa.readthedocs.io/en/stable/tutorials/adv_tutorial_legacy.html) that will show users how to use the JavaScript based visualization option, which also uses this model as its base. " ] }, { @@ -67,7 +78,22 @@ "\n", "```bash\n", "pip install matplotlib\n", - "```\n" + "```\n", + "\n", + "\n", + "**If running in Google Colab run the below cell to install Mesa.** (This will also work in a locally installed version of Jupyter.)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# SKIP THIS CELL unless running in colab\n", + "\n", + "!pip install mesa\n", + "# The exclamation points tell jupyter to do the command via the command line" ] }, { @@ -803,8 +829,6 @@ }, "outputs": [], "source": [ - "import numpy as np\n", - "\n", "agent_counts = np.zeros((model.grid.width, model.grid.height))\n", "for cell_content, (x, y) in model.grid.coord_iter():\n", " agent_count = len(cell_content)\n", @@ -1230,8 +1254,6 @@ }, "outputs": [], "source": [ - "import pandas as pd\n", - "\n", "results_df = pd.DataFrame(results)\n", "print(results_df.keys())" ] diff --git a/docs/tutorials/visualization_tutorial.ipynb b/docs/tutorials/visualization_tutorial.ipynb index 47bcc798237..b9e714cf124 100644 --- a/docs/tutorials/visualization_tutorial.ipynb +++ b/docs/tutorials/visualization_tutorial.ipynb @@ -11,7 +11,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "We recommend to execute this tutorial online in a Colab notebook, so that you can explore the visualization output: [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/visualization_tutorial.ipynb)\n", + "**Important:** \n", + "- If you are just exploring Mesa and want the fastest way to execute the code we recommend executing this tutorial online in a Colab notebook. [![Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/projectmesa/mesa/blob/main/docs/tutorials/visualization_tutorial.ipynb)\n", + "- If you have installed mesa and are running locally, please ensure that your [Mesa version](https://pypi.org/project/Mesa/) is up-to-date in order to run this tutorial.\n", "\n", "### Adding visualization\n", "\n", @@ -42,6 +44,10 @@ "\n", "# You can either define the BoltzmannWealthModel (aka MoneyModel) or install mesa-models:\n", "%pip install --quiet -U -e git+https://github.com/projectmesa/mesa-examples#egg=mesa-models\n", + "\n", + "# For Colab you must change the directory to reference mesa-models installed from github on the previous line.\n", + "%cd src\n", + "\n", "from mesa_models.boltzmann_wealth_model.model import BoltzmannWealthModel" ] }, @@ -245,7 +251,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -259,7 +265,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.12" + "version": "3.9.6" }, "nbsphinx": { "execute": "never" From 2cec83074ed3f742d79cd9ab3ae1a5536df18bf8 Mon Sep 17 00:00:00 2001 From: tpike3 Date: Sun, 30 Jul 2023 07:56:35 -0400 Subject: [PATCH 149/214] update history for 2.1.1 release --- HISTORY.rst | 11 +++++++++++ mesa/__init__.py | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 88efeed763b..929e0f5b705 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,17 @@ Release History --------------- +2.1.1 (2023-08-02) ++++++++++++++++++++ + +This release improves the introductory and visualization tutorial. Ensures both are Google Colab compatible with +working badges. + +Changes: + * Update `intro_tutorial` to warn users to ensure up to date version, and make colab compatible #1739, #1744 + * Improve new/experimental Solara based visualization to ensure pause button works #1745 + * Fix bug in `space.py` -> `get_heading()` #1739 + 2.1.0 (2023-07-22) Youngtown +++++++++++++++++++++++++++++ diff --git a/mesa/__init__.py b/mesa/__init__.py index b1a8d54c29a..b0969e389e0 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -25,7 +25,7 @@ ] __title__ = "mesa" -__version__ = "2.1" +__version__ = "2.1.1" __license__ = "Apache 2.0" _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year __copyright__ = f"Copyright {_this_year} Project Mesa Team" From 839bf83cc22df73aa67cffd065e2c51f243a97f4 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 31 Jul 2023 12:51:22 -0400 Subject: [PATCH 150/214] fix: Add Matplotlib as dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 2a58984ccfe..a13d9b2c1bd 100644 --- a/setup.py +++ b/setup.py @@ -11,6 +11,7 @@ requires = [ "click", "cookiecutter", + "matplotlib", "networkx", "numpy", "pandas", From b53aa2fd0d379774d7ec9e6d573fcb2a1c983018 Mon Sep 17 00:00:00 2001 From: Corvince Date: Thu, 3 Aug 2023 21:14:19 +0200 Subject: [PATCH 151/214] perf: Access grid only once --- mesa/space.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index e5615d65665..eebcb3c1758 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -394,8 +394,9 @@ def iter_cell_list_contents( """ # iter_cell_list_contents returns only non-empty contents. return ( - self._grid[x][y] - for x, y in itertools.filterfalse(self.is_cell_empty, cell_list) + cell + for x, y in cell_list + if (cell := self._grid[x][y]) != self.default_val() ) @accept_tuple_argument @@ -568,8 +569,9 @@ def iter_cell_list_contents( An iterator of the agents contained in the cells identified in `cell_list`. """ return itertools.chain.from_iterable( - self._grid[x][y] - for x, y in itertools.filterfalse(self.is_cell_empty, cell_list) + cell + for x, y in cell_list + if (cell := self._grid[x][y]) != self.default_val() ) From 856d4bc7cd7cd4a6cd34903ede468d258bc65655 Mon Sep 17 00:00:00 2001 From: tpike3 Date: Thu, 3 Aug 2023 20:07:53 -0400 Subject: [PATCH 152/214] fix install for visualization tutorial --- docs/tutorials/adv_tutorial_legacy.ipynb | 13 +------------ docs/tutorials/visualization_tutorial.ipynb | 5 +---- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/docs/tutorials/adv_tutorial_legacy.ipynb b/docs/tutorials/adv_tutorial_legacy.ipynb index cd54ed8079f..c05d5819c75 100644 --- a/docs/tutorials/adv_tutorial_legacy.ipynb +++ b/docs/tutorials/adv_tutorial_legacy.ipynb @@ -9,7 +9,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -25,7 +24,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -48,7 +46,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -93,7 +90,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -120,7 +116,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -128,7 +123,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -170,7 +164,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -201,7 +194,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -211,7 +203,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -240,7 +231,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -277,7 +267,6 @@ ] }, { - "attachments": {}, "cell_type": "markdown", "metadata": {}, "source": [ @@ -513,7 +502,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.3" + "version": "3.9.6" } }, "nbformat": 4, diff --git a/docs/tutorials/visualization_tutorial.ipynb b/docs/tutorials/visualization_tutorial.ipynb index b9e714cf124..37564241599 100644 --- a/docs/tutorials/visualization_tutorial.ipynb +++ b/docs/tutorials/visualization_tutorial.ipynb @@ -43,10 +43,7 @@ "import mesa\n", "\n", "# You can either define the BoltzmannWealthModel (aka MoneyModel) or install mesa-models:\n", - "%pip install --quiet -U -e git+https://github.com/projectmesa/mesa-examples#egg=mesa-models\n", - "\n", - "# For Colab you must change the directory to reference mesa-models installed from github on the previous line.\n", - "%cd src\n", + "%pip install --quiet -U git+https://github.com/projectmesa/mesa-examples#egg=mesa-models\n", "\n", "from mesa_models.boltzmann_wealth_model.model import BoltzmannWealthModel" ] From d13963ca2191787f169fe17615864843fb6814c8 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 1 Aug 2023 09:28:19 +0000 Subject: [PATCH 153/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/psf/black: 23.3.0 → 23.7.0](https://github.com/psf/black/compare/23.3.0...23.7.0) - [github.com/asottile/pyupgrade: v3.8.0 → v3.10.1](https://github.com/asottile/pyupgrade/compare/v3.8.0...v3.10.1) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 662b22db6a7..772620fc72d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,12 +4,12 @@ ci: repos: - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 23.7.0 hooks: - id: black-jupyter exclude: ^mesa/cookiecutter-mesa/ - repo: https://github.com/asottile/pyupgrade - rev: v3.8.0 + rev: v3.10.1 hooks: - id: pyupgrade args: [--py38-plus] From cc3b397db067e43e790b291eb7522c21f8fece7d Mon Sep 17 00:00:00 2001 From: Wang Boyu Date: Sat, 5 Aug 2023 11:45:49 +0800 Subject: [PATCH 154/214] docs: use myst-nb to compile notebooks at build time --- docs/conf.py | 3 +++ setup.py | 1 + 2 files changed, 4 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index 8365cb5257d..c269e016fbc 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,6 +43,7 @@ "sphinx.ext.ifconfig", "sphinx.ext.viewcode", "nbsphinx", + "myst_nb", ] # Add any paths that contain templates here, relative to this directory. @@ -114,6 +115,8 @@ # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False +nb_execution_timeout = 60 +nb_execution_mode = "cache" # -- Options for HTML output ---------------------------------------------- diff --git a/setup.py b/setup.py index a13d9b2c1bd..f5d07fad688 100644 --- a/setup.py +++ b/setup.py @@ -41,6 +41,7 @@ "ipykernel", "pydata_sphinx_theme", "seaborn", + "myst-nb", ], } From d174b9ca7dcaa9c95c74d03ba6b4aaca227aac12 Mon Sep 17 00:00:00 2001 From: Wang Boyu Date: Sat, 5 Aug 2023 17:46:11 +0800 Subject: [PATCH 155/214] docs: shorten intro tutorial cell outputs --- docs/tutorials/intro_tutorial.ipynb | 2 +- mesa/batchrunner.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index cf3b4d5785a..404ae8d25bf 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -92,7 +92,7 @@ "source": [ "# SKIP THIS CELL unless running in colab\n", "\n", - "!pip install mesa\n", + "%pip install --quiet mesa\n", "# The exclamation points tell jupyter to do the command via the command line" ] }, diff --git a/mesa/batchrunner.py b/mesa/batchrunner.py index b4a96aebcc0..c9c528470a3 100644 --- a/mesa/batchrunner.py +++ b/mesa/batchrunner.py @@ -13,7 +13,7 @@ Union, ) -from tqdm import tqdm +from tqdm.auto import tqdm from mesa.model import Model From d65f68bef7f4ac7390d4c51924ef20205c180ce8 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 7 Aug 2023 00:32:58 -0400 Subject: [PATCH 156/214] docs: Remove nbsphinx and explicit .ipynb suffix --- docs/conf.py | 1 - docs/index.rst | 4 ++-- setup.py | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index c269e016fbc..1cf3b779086 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -42,7 +42,6 @@ "sphinx.ext.mathjax", "sphinx.ext.ifconfig", "sphinx.ext.viewcode", - "nbsphinx", "myst_nb", ] diff --git a/docs/index.rst b/docs/index.rst index af039fd8328..6b2e4831ccd 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -95,8 +95,8 @@ ABM features users have shared that you may want to use in your model :maxdepth: 7 Mesa Overview - tutorials/intro_tutorial.ipynb - tutorials/visualization_tutorial.ipynb + tutorials/intro_tutorial + tutorials/visualization_tutorial Best Practices Useful Snippets API Documentation diff --git a/setup.py b/setup.py index f5d07fad688..72f97b4a7d4 100644 --- a/setup.py +++ b/setup.py @@ -37,7 +37,6 @@ "docs": [ "sphinx<7", "ipython", - "nbsphinx", "ipykernel", "pydata_sphinx_theme", "seaborn", From 09cde4373d72d3d97c5ca420780b0883ffd420b1 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 31 Jul 2023 09:49:44 -0400 Subject: [PATCH 157/214] Move viz stuff to mesa-viz-tornado Git repo --- mesa/flat/visualization.py | 8 +- mesa/visualization/ModularVisualization.py | 436 ------------------ mesa/visualization/TextVisualization.py | 125 ----- mesa/visualization/UserParam.py | 165 ------- mesa/visualization/__init__.py | 9 - .../modules/BarChartVisualization.py | 95 ---- .../modules/CanvasGridVisualization.py | 107 ----- .../modules/ChartVisualization.py | 85 ---- .../modules/HexGridVisualization.py | 87 ---- .../modules/NetworkVisualization.py | 28 -- .../modules/PieChartVisualization.py | 69 --- mesa/visualization/modules/__init__.py | 14 - .../templates/css/visualization.css | 20 - .../templates/js/BarChartModule.js | 182 -------- .../templates/js/CanvasHexModule.js | 69 --- .../templates/js/CanvasModule.js | 70 --- .../visualization/templates/js/ChartModule.js | 99 ---- mesa/visualization/templates/js/GridDraw.js | 417 ----------------- mesa/visualization/templates/js/HexDraw.js | 262 ----------- .../templates/js/InteractionHandler.js | 184 -------- .../templates/js/NetworkModule_d3.js | 126 ----- .../templates/js/PieChartModule.js | 111 ----- mesa/visualization/templates/js/TextModule.js | 15 - mesa/visualization/templates/js/runcontrol.js | 360 --------------- .../templates/modular_template.html | 106 ----- setup.py | 81 ---- 26 files changed, 4 insertions(+), 3326 deletions(-) delete mode 100644 mesa/visualization/ModularVisualization.py delete mode 100644 mesa/visualization/TextVisualization.py delete mode 100644 mesa/visualization/UserParam.py delete mode 100644 mesa/visualization/__init__.py delete mode 100644 mesa/visualization/modules/BarChartVisualization.py delete mode 100644 mesa/visualization/modules/CanvasGridVisualization.py delete mode 100644 mesa/visualization/modules/ChartVisualization.py delete mode 100644 mesa/visualization/modules/HexGridVisualization.py delete mode 100644 mesa/visualization/modules/NetworkVisualization.py delete mode 100644 mesa/visualization/modules/PieChartVisualization.py delete mode 100644 mesa/visualization/modules/__init__.py delete mode 100644 mesa/visualization/templates/css/visualization.css delete mode 100644 mesa/visualization/templates/js/BarChartModule.js delete mode 100644 mesa/visualization/templates/js/CanvasHexModule.js delete mode 100644 mesa/visualization/templates/js/CanvasModule.js delete mode 100644 mesa/visualization/templates/js/ChartModule.js delete mode 100644 mesa/visualization/templates/js/GridDraw.js delete mode 100644 mesa/visualization/templates/js/HexDraw.js delete mode 100644 mesa/visualization/templates/js/InteractionHandler.js delete mode 100644 mesa/visualization/templates/js/NetworkModule_d3.js delete mode 100644 mesa/visualization/templates/js/PieChartModule.js delete mode 100644 mesa/visualization/templates/js/TextModule.js delete mode 100644 mesa/visualization/templates/js/runcontrol.js delete mode 100644 mesa/visualization/templates/modular_template.html diff --git a/mesa/flat/visualization.py b/mesa/flat/visualization.py index d5222109121..2eb7b802c5e 100644 --- a/mesa/flat/visualization.py +++ b/mesa/flat/visualization.py @@ -1,5 +1,5 @@ # This collects all of Mesa visualization components under a flat namespace. -from mesa.visualization.ModularVisualization import * # noqa -from mesa.visualization.modules import * # noqa -from mesa.visualization.UserParam import * # noqa -from mesa.visualization.TextVisualization import * # noqa +from mesa_viz_tornado.ModularVisualization import * # noqa +from mesa_viz_tornado.modules import * # noqa +from mesa_viz_tornado.UserParam import * # noqa +from mesa_viz_tornado.TextVisualization import * # noqa diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py deleted file mode 100644 index 746e5b39eea..00000000000 --- a/mesa/visualization/ModularVisualization.py +++ /dev/null @@ -1,436 +0,0 @@ -""" -ModularServer -============= - -A visualization server which renders a model via one or more elements. - -The concept for the modular visualization server as follows: -A visualization is composed of VisualizationElements, each of which defines how -to generate some visualization from a model instance and render it on the -client. VisualizationElements may be anything from a simple text display to -a multilayered HTML5 canvas. - -The actual server is launched with one or more VisualizationElements; -it runs the model object through each of them, generating data to be sent to -the client. The client page is also generated based on the JavaScript code -provided by each element. - -This file consists of the following classes: - -VisualizationElement: Parent class for all other visualization elements, with - the minimal necessary options. -PageHandler: The handler for the visualization page, generated from a template - and built from the various visualization elements. -SocketHandler: Handles the websocket connection between the client page and - the server. -ModularServer: The overall visualization application class which stores and - controls the model and visualization instance. - - -ModularServer should *not* need to be subclassed on a model-by-model basis; it -should be primarily a pass-through for VisualizationElement subclasses, which -define the actual visualization specifics. - -For example, suppose we have created two visualization elements for our model, -called canvasvis and graphvis; we would launch a server with: - - server = ModularServer(MyModel, [canvasvis, graphvis], name="My Model") - server.launch() - -The client keeps track of what step it is showing. Clicking the Step button in -the browser sends a message requesting the viz_state corresponding to the next -step position, which is then sent back to the client via the websocket. - -The websocket protocol is as follows: -Each message is a JSON object, with a "type" property which defines the rest of -the structure. - -Server -> Client: - Send over the model state to visualize. - Model state is a list, with each element corresponding to a div; each div - is expected to have a render function associated with it, which knows how - to render that particular data. The example below includes two elements: - the first is data for a CanvasGrid, the second for a raw text display. - - { - "type": "viz_state", - "data": [{0:[ {"Shape": "circle", "x": 0, "y": 0, "r": 0.5, - "Color": "#AAAAAA", "Filled": "true", "Layer": 0, - "text": 'A', "text_color": "white" }]}, - "Shape Count: 1"] - } - - Informs the client that the model is over. - {"type": "end"} - - Informs the client of the current model's parameters - { - "type": "model_params", - "params": 'dict' of model params, (i.e. {arg_1: val_1, ...}) - } - -Client -> Server: - Reset the model. - TODO: Allow this to come with parameters - { - "type": "reset" - } - - Get a given state. - { - "type": "get_step", - "step:" index of the step to get. - } - - Submit model parameter updates - { - "type": "submit_params", - "param": name of model parameter - "value": new value for 'param' - } - - Get the model's parameters - { - "type": "get_params" - } -""" -import asyncio -import os -import platform -import webbrowser -from typing import ClassVar - -import tornado.autoreload -import tornado.escape -import tornado.gen -import tornado.ioloop -import tornado.web -import tornado.websocket - -from mesa.visualization.UserParam import UserParam - -# Suppress several pylint warnings for this file. -# Attributes being defined outside of init is a Tornado feature. -# pylint: disable=attribute-defined-outside-init - -# Change the event loop policy for windows -if platform.system() == "Windows" and platform.python_version_tuple() >= ("3", "7"): - asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - -D3_JS_FILE = "external/d3-7.4.3.min.js" -CHART_JS_FILE = "external/chart-3.6.1.min.js" - - -def is_user_param(val): - return issubclass(val.__class__, UserParam) - - -class VisualizationElement: - """ - Defines an element of the visualization. - - Attributes: - package_includes: A list of external JavaScript and CSS files to - include that are part of the Mesa packages. - local_includes: A list of JavaScript and CSS files that are local to - the directory that the server is being run in. - js_code: A JavaScript code string to instantiate the element. - local_dir: A full path to the directory containing the local includes. - If a relative path is given, it is relative to the working - directory where the server is being run. If an absolute path - is given, it is used as-is. Default is the current working - directory. - - Methods: - render: Takes a model object, and produces JSON data which can be sent - to the client. - """ - - package_includes: ClassVar = [] - local_includes: ClassVar = [] - js_code = "" - render_args: ClassVar = {} - local_dir = "" - - def __init__(self): - pass - - def render(self, model): - """Build visualization data from a model object. - - Args: - model: A model object - - Returns: - A JSON-ready object. - """ - return "VisualizationElement goes here." - - -class TextElement(VisualizationElement): - """ - Module for drawing live-updating text. - """ - - package_includes: ClassVar = ["TextModule.js"] - js_code = "elements.push(new TextModule());" - - -# ============================================================================= -# Actual Tornado code starts here: - - -class PageHandler(tornado.web.RequestHandler): - """Handler for the HTML template which holds the visualization.""" - - def get(self): - elements = self.application.visualization_elements - for i, element in enumerate(elements): - element.index = i - self.render( - "modular_template.html", - port=self.application.port, - model_name=self.application.model_name, - description=self.application.description, - package_js_includes=self.application.package_js_includes, - package_css_includes=self.application.package_css_includes, - local_js_includes=self.application.local_js_includes, - local_css_includes=self.application.local_css_includes, - scripts=self.application.js_code, - ) - - -class SocketHandler(tornado.websocket.WebSocketHandler): - """Handler for websocket.""" - - def open(self): - if self.application.verbose: - print("Socket opened!") - self.write_message( - {"type": "model_params", "params": self.application.user_params} - ) - - def check_origin(self, origin): - return True - - @property - def viz_state_message(self): - return {"type": "viz_state", "data": self.application.render_model()} - - def on_message(self, message): - """Receiving a message from the websocket, parse, and act accordingly.""" - if self.application.verbose: - print(message) - msg = tornado.escape.json_decode(message) - - if msg["type"] == "get_step": - if not self.application.model.running: - self.write_message({"type": "end"}) - else: - self.application.model.step() - self.write_message(self.viz_state_message) - - elif msg["type"] == "reset": - self.application.reset_model() - self.write_message(self.viz_state_message) - - elif msg["type"] == "submit_params": - param = msg["param"] - value = msg["value"] - - # Is the param editable? - if param in self.application.user_params: - if is_user_param(self.application.model_kwargs[param]): - self.application.model_kwargs[param].value = value - else: - self.application.model_kwargs[param] = value - - else: - if self.application.verbose: - print("Unexpected message!") - - -class ModularServer(tornado.web.Application): - """Main visualization application.""" - - EXCLUDE_LIST = ("width", "height") - - def __init__( - self, - model_cls, - visualization_elements, - name="Mesa Model", - model_params=None, - port=None, - ): - """ - Args: - model_cls: Mesa model class - visualization_elements: visualisation elements - name: A String for the model name - port: Port the webserver listens to (int) - Order of configuration: - 1. Parameter to ModularServer.launch - 2. Parameter to ModularServer() - 3. Environment var PORT - 4. Default value (8521) - model_params: A dict of model parameters - """ - - self.verbose = True - self.max_steps = 100000 - - if port is not None: - self.port = port - else: - # Default port to listen on - self.port = int(os.getenv("PORT", "8521")) - - # Handlers and other globals: - page_handler = (r"/", PageHandler) - socket_handler = (r"/ws", SocketHandler) - static_handler = ( - r"/static/(.*)", - tornado.web.StaticFileHandler, - {"path": os.path.dirname(__file__) + "/templates"}, - ) - custom_handler = ( - r"/local/custom/(.*)", - tornado.web.StaticFileHandler, - {"path": ""}, - ) - self.handlers = [page_handler, socket_handler, static_handler, custom_handler] - - self.settings = { - "debug": True, - "autoreload": False, - "template_path": os.path.dirname(__file__) + "/templates", - } - - """Create a new visualization server with the given elements.""" - if model_params is None: - model_params = {} - # Prep visualization elements: - self.visualization_elements = self._auto_convert_functions_to_TextElements( - visualization_elements - ) - self.package_js_includes = set() - self.package_css_includes = set() - self.local_js_includes = set() - self.local_css_includes = set() - self.js_code = [] - for element in self.visualization_elements: - for include_file in element.package_includes: - if self._is_stylesheet(include_file): - self.package_css_includes.add(include_file) - else: - self.package_js_includes.add(include_file) - if element.local_includes: - mapped_local_dir = element.__class__.__name__ - element_file_handler = ( - rf"/local/{mapped_local_dir}/(.*)", - tornado.web.StaticFileHandler, - {"path": element.local_dir}, - ) - self.handlers.append(element_file_handler) - for include_file in element.local_includes: - include_file_path = f"{mapped_local_dir}/{include_file}" - if self._is_stylesheet(include_file): - self.local_css_includes.add(include_file_path) - else: - self.local_js_includes.add(include_file_path) - self.js_code.append(element.js_code) - - # Initializing the model - self.model_name = name - self.model_cls = model_cls - self.description = "No description available" - if hasattr(model_cls, "description"): - self.description = model_cls.description - elif model_cls.__doc__ is not None: - self.description = model_cls.__doc__ - - self.model_kwargs = model_params - self.reset_model() - - # Initializing the application itself: - super().__init__(self.handlers, **self.settings) - - @property - def user_params(self): - result = {} - for param, val in self.model_kwargs.items(): - if is_user_param(val): - result[param] = val.json - - return result - - def reset_model(self): - """Reinstantiate the model object, using the current parameters.""" - - model_params = {} - for key, val in self.model_kwargs.items(): - if is_user_param(val): - if val.param_type == "static_text": - # static_text is never used for setting params - continue - model_params[key] = val.value - else: - model_params[key] = val - - self.model = self.model_cls(**model_params) - # We specify the `running` attribute here so that the user doesn't have - # to define it explicitly in their model's __init__. - self.model.running = True - - def render_model(self): - """Turn the current state of the model into a dictionary of - visualizations - """ - visualization_state = [] - for element in self.visualization_elements: - element_state = element.render(self.model) - visualization_state.append(element_state) - return visualization_state - - def launch(self, port=None, open_browser=True): - """Run the app.""" - if port is not None: - self.port = port - url = f"http://127.0.0.1:{self.port}" - print(f"Interface starting at {url}") - self.listen(self.port) - if open_browser: - webbrowser.open(url) - tornado.autoreload.start() - try: - tornado.ioloop.IOLoop.current().start() - except KeyboardInterrupt: - tornado.ioloop.IOLoop.current().stop() - - @staticmethod - def _is_stylesheet(filename): - return filename.lower().endswith(".css") - - def _auto_convert_fn_to_TextElement(self, x): - """ - Automatically convert a function to a TextElement object. - See https://github.com/projectmesa/mesa/issues/1233. - """ - - # Note: a class constructor is also a callable. - if not callable(x): - # i.e. not a function - return x - - class MyTextElement(TextElement): - def render(self, model): - return x(model) - - return MyTextElement() - - def _auto_convert_functions_to_TextElements(self, visualization_elements): - out_elements = [ - self._auto_convert_fn_to_TextElement(e) for e in visualization_elements - ] - return out_elements diff --git a/mesa/visualization/TextVisualization.py b/mesa/visualization/TextVisualization.py deleted file mode 100644 index 79e68b2b950..00000000000 --- a/mesa/visualization/TextVisualization.py +++ /dev/null @@ -1,125 +0,0 @@ -""" -Text Visualization -================== - -Base classes for ASCII-only visualizations of a model. -These are useful for quick debugging, and can readily be rendered in an IPython -Notebook or via text alone in a browser window. - -Classes: - -TextVisualization: Class meant to wrap around a Model object and render it -in some way using Elements, which are stored in a list and rendered in that -order. Each element, in turn, renders a particular piece of information as -text. - -ASCIIElement: Parent class for all other ASCII elements. render() returns its -representative string, which can be printed via the overloaded __str__ method. - -TextData: Uses getattr to get the value of a particular property of a model -and prints it, along with its name. - -TextGrid: Prints a grid, assuming that the value of each cell maps to exactly -one ASCII character via a converter method. This (as opposed to a dictionary) -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 - - -class TextVisualization: - """ASCII-Only visualization of a model. - - Properties: - - 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): - """Create a new Text Visualization object.""" - self.model = model - self.elements = [] - - def render(self): - """Render all the text elements, in order.""" - for element in self.elements: - print(element) - - def step(self): - """Advance the model by a step and print the results.""" - self.model.step() - self.render() - - -class ASCIIElement: - """Base class for all TextElements to render. - - Methods: - render: 'Renders' some data into ASCII and returns. - __str__: Displays render() by default. - """ - - def __init__(self): - pass - - def render(self): - """Render the element as text.""" - return "Placeholder!" - - def __str__(self): - return self.render() - - -class TextData(ASCIIElement): - """Prints the value of one particular variable from the base model.""" - - def __init__(self, model, var_name): - """Create a new data renderer.""" - self.model = model - self.var_name = var_name - - def render(self): - return self.var_name + ": " + str(getattr(self.model, self.var_name)) - - -class TextGrid(ASCIIElement): - """Class for creating an ASCII visualization of a basic grid object. - - By default, assume that each cell is represented by one character, and - that empty cells are rendered as ' ' characters. When printed, the TextGrid - results in a width x height grid of ascii characters. - - Properties: - grid: The underlying grid object. - """ - - grid = None - - def __init__(self, grid, converter): - """Create a new ASCII grid visualization. - - Args: - grid: The underlying Grid object. - converter: function for converting the content of each cell - to ascii. Takes the contents of a cell, and returns - a single character. - """ - self.grid = grid - self.converter = converter - - def render(self): - """What to show when printed.""" - viz = "" - for y in range(self.grid.height): - for x in range(self.grid.width): - c = self.grid[y][x] - if c is None: - viz += " " - else: - viz += self.converter(c) - viz += "\n" - return viz diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py deleted file mode 100644 index d97d196cead..00000000000 --- a/mesa/visualization/UserParam.py +++ /dev/null @@ -1,165 +0,0 @@ -import numbers - -NUMBER = "number" -CHECKBOX = "checkbox" -CHOICE = "choice" -SLIDER = "slider" -STATIC_TEXT = "static_text" - - -class UserParam: - _ERROR_MESSAGE = "Missing or malformed inputs for '{}' Option '{}'" - - @property - def json(self): - result = self.__dict__.copy() - result["value"] = result.pop( - "_value" - ) # Return _value as value, value is the same - return result - - def maybe_raise_error(self, valid): - if not valid: - msg = self._ERROR_MESSAGE.format(self.param_type, self.name) - raise ValueError(msg) - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = value - - -class Slider(UserParam): - """ - A number-based slider input with settable increment. - - Example: - - slider_option = Slider("My Slider", value=123, min_value=10, max_value=200, step=0.1) - """ - - def __init__( - self, - name="", - value=None, - min_value=None, - max_value=None, - step=1, - description=None, - ): - self.param_type = SLIDER - self.name = name - self._value = value - self.min_value = min_value - self.max_value = max_value - self.step = step - self.description = description - - # Validate option type to make sure values are supplied properly - valid = not ( - self.value is None or self.min_value is None or self.max_value is None - ) - self.maybe_raise_error(valid) - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = value - if self._value < self.min_value: - self._value = self.min_value - elif self._value > self.max_value: - self._value = self.max_value - - -class Checkbox(UserParam): - """ - Boolean checkbox. - - Example: - - boolean_option = Checkbox('My Boolean', True) - """ - - def __init__(self, name="", value=None, description=None): - self.param_type = CHECKBOX - self.name = name - self._value = value - self.description = description - - # Validate option type to make sure values are supplied properly - valid = isinstance(self.value, bool) - self.maybe_raise_error(valid) - - -class Choice(UserParam): - """ - String-based dropdown input, for selecting choices within a model - - Example: - choice_option = Choice( - 'My Choice', - value='Default choice', - choices=['Default Choice', 'Alternate Choice'] - ) - """ - - def __init__(self, name="", value=None, choices=None, description=None): - self.param_type = CHOICE - self.name = name - self._value = value - self.choices = choices - self.description = description - - # Validate option type to make sure values are supplied properly - valid = not (self.value is None or len(self.choices) == 0) - self.maybe_raise_error(valid) - - @property - def value(self): - return self._value - - @value.setter - def value(self, value): - self._value = value - if self._value not in self.choices: - print( - "Selected choice value not in available choices, selected first choice from 'choices' list" - ) - self._value = self.choices[0] - - -class StaticText(UserParam): - """ - A non-input textbox for displaying model info. - - Example: - static_text = StaticText("This is a descriptive textbox") - """ - - def __init__(self, value=None): - self.param_type = STATIC_TEXT - self._value = value - valid = isinstance(self.value, str) - self.maybe_raise_error(valid) - - -class NumberInput(UserParam): - """ - a simple numerical input - - Example: - number_option = NumberInput("My Number", value=123) - """ - - def __init__(self, name="", value=None, description=None): - self.param_type = NUMBER - self.name = name - self._value = value - valid = isinstance(self.value, numbers.Number) - self.maybe_raise_error(valid) diff --git a/mesa/visualization/__init__.py b/mesa/visualization/__init__.py deleted file mode 100644 index 6f3a73fb8bc..00000000000 --- a/mesa/visualization/__init__.py +++ /dev/null @@ -1,9 +0,0 @@ -""" -Mesa Visualization Module -------------------------- - -TextVisualization: Base class for writing ASCII visualizations of model state. - -TextServer: Class which takes a TextVisualization child class as an input, and -renders it in-browser, along with an interface. -""" diff --git a/mesa/visualization/modules/BarChartVisualization.py b/mesa/visualization/modules/BarChartVisualization.py deleted file mode 100644 index cfee85dd1a6..00000000000 --- a/mesa/visualization/modules/BarChartVisualization.py +++ /dev/null @@ -1,95 +0,0 @@ -""" -Pie Chart Module -============ - -Module for drawing live-updating bar charts using d3.js -""" -import json -from typing import ClassVar - -from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement - - -class BarChartModule(VisualizationElement): - """Each bar chart can either visualize model-level or agent-level fields from a datcollector - with a bar chart. - - Attributes: - scope: whether to visualize agent-level or model-level fields - fields: A List of Dictionaries containing information about each field to be charted, - including the name of the datacollector field and the desired color of the - corresponding bar. - Ex: [{"Label":"", "Color":""}] - sorting: Whether to sort ascending, descending, or neither when charting agent fields - sort_by: The agent field to sort by - 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: ClassVar = [D3_JS_FILE, "BarChartModule.js"] - - def __init__( - self, - fields, - scope="model", - sorting="none", - sort_by="none", - canvas_height=400, - canvas_width=800, - data_collector_name="datacollector", - ): - """ - Create a new bar chart visualization. - - Args: - scope: "model" if visualizing model-level fields, "agent" if visualizing agent-level - fields. - fields: A List of Dictionaries containing information about each field to be charted, - including the name of the datacollector field and the desired color of the - corresponding bar. - Ex: [{"Label":"", "Color":""}] - sorting: "ascending", "descending", or "none" - sort_by: The agent field to sort by - canvas_height, canvas_width: Size in pixels of the chart to draw. - data_collector_name: Name of the DataCollector to use. - """ - - self.scope = scope - self.fields = fields - self.sorting = sorting - self.canvas_height = canvas_height - self.canvas_width = canvas_width - self.data_collector_name = data_collector_name - - fields_json = json.dumps(self.fields) - new_element = "new BarChartModule({}, {}, {}, '{}', '{}')" - new_element = new_element.format( - fields_json, canvas_width, canvas_height, sorting, sort_by - ) - self.js_code = "elements.push(" + new_element + ")" - - def render(self, model): - current_values = [] - data_collector = getattr(model, self.data_collector_name) - - if self.scope == "agent": - df = data_collector.get_agent_vars_dataframe().astype("float") - latest_step = df.index.levels[0][-1] - label_strings = [f["Label"] for f in self.fields] - dict = df.loc[latest_step].T.loc[label_strings].to_dict() - current_values = list(dict.values()) - - elif self.scope == "model": - out_dict = {} - for s in self.fields: - name = s["Label"] - try: - val = data_collector.model_vars[name][-1] - except (IndexError, KeyError): - val = 0 - out_dict[name] = val - current_values.append(out_dict) - else: - raise ValueError("scope must be 'agent' or 'model'") - return current_values diff --git a/mesa/visualization/modules/CanvasGridVisualization.py b/mesa/visualization/modules/CanvasGridVisualization.py deleted file mode 100644 index b8b4b462c3b..00000000000 --- a/mesa/visualization/modules/CanvasGridVisualization.py +++ /dev/null @@ -1,107 +0,0 @@ -""" -Modular Canvas Rendering -======================== - -Module for visualizing model objects in grid cells. -""" -from collections import defaultdict - -from mesa.visualization.ModularVisualization import VisualizationElement - - -class CanvasGrid(VisualizationElement): - """A CanvasGrid object uses a user-provided portrayal method to generate a - portrayal for each object. A portrayal is a JSON-ready dictionary which - tells the relevant JavaScript code (GridDraw.js) where to draw what shape. - - The render method returns a dictionary, keyed on layers, with values as - lists of portrayals to draw. Portrayals themselves are generated by the - user-provided portrayal_method, which accepts an object as an input and - produces a portrayal of it. - - A portrayal as a dictionary with the following structure: - "x", "y": Coordinates for the cell in which the object is placed. - "Shape": Can be either "circle", "rect", "arrowHead" or a custom image. - For Circles: - "r": The radius, defined as a fraction of cell size. r=1 will - fill the entire cell. - "xAlign", "yAlign": Alignment of the circle within the cell. - Defaults to 0.5 (center). - For Rectangles: - "w", "h": The width and height of the rectangle, which are in - fractions of cell width and height. - "xAlign", "yAlign": Alignment of the rectangle within the - cell. Defaults to 0.5 (center). - For arrowHead: - "scale": Proportion scaling as a fraction of cell size. - "heading_x": represents x direction unit vector. - "heading_y": represents y direction unit vector. - For an image: - The image must be placed in the same directory from which the - server is launched. An image has the attributes "x", "y", - "scale", "text" and "text_color". - "Color": The color to draw the shape in; needs to be a valid HTML - color, e.g."Red" or "#AA08F8" - "Filled": either "true" or "false", and determines whether the shape is - filled or not. - "Layer": Layer number of 0 or above; higher-numbered layers are drawn - above lower-numbered layers. - "text": The text to be inscribed inside the Shape. Normally useful for - showing the unique_id of the agent. - "text_color": The color to draw the inscribed text. Should be given in - conjunction of "text" property. - - - Attributes: - portrayal_method: Function which generates portrayals from objects, as - described above. - grid_height, grid_width: Size of the grid to visualize, in cells. - 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"] - - def __init__( - self, - portrayal_method, - grid_width, - grid_height, - canvas_width=500, - canvas_height=500, - ): - """Instantiate a new CanvasGrid. - - Args: - portrayal_method: function to convert each object on the grid to - a portrayal, as described above. - 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 - self.grid_height = grid_height - self.canvas_width = canvas_width - self.canvas_height = canvas_height - - new_element = "new CanvasModule({}, {}, {}, {})".format( - self.canvas_width, self.canvas_height, self.grid_width, self.grid_height - ) - - self.js_code = "elements.push(" + new_element + ");" - - def render(self, model): - grid_state = defaultdict(list) - for x in range(model.grid.width): - for y in range(model.grid.height): - cell_objects = model.grid.get_cell_list_contents([(x, y)]) - for obj in cell_objects: - portrayal = self.portrayal_method(obj) - if portrayal: - portrayal["x"] = x - portrayal["y"] = y - grid_state[portrayal["Layer"]].append(portrayal) - - return grid_state diff --git a/mesa/visualization/modules/ChartVisualization.py b/mesa/visualization/modules/ChartVisualization.py deleted file mode 100644 index 8f356fa4826..00000000000 --- a/mesa/visualization/modules/ChartVisualization.py +++ /dev/null @@ -1,85 +0,0 @@ -""" -Chart Module -============ - -Module for drawing live-updating line charts using Charts.js -""" -import json - -from mesa.visualization.ModularVisualization import CHART_JS_FILE, VisualizationElement - - -class ChartModule(VisualizationElement): - """Each chart can visualize one or more model-level series as lines - with the data value on the Y axis and the step number as the X axis. - - At the moment, each call to the render method returns a list of the most - recent values of each series. - - Attributes: - series: A list of dictionaries containing information on series to - plot. Each dictionary must contain (at least) the "Label" and - "Color" keys. The "Label" value must correspond to a - model-level series collected by the model's DataCollector, and - "Color" must have a valid HTML color. - canvas_height, canvas_width: The width and height to draw the chart on - the page, in pixels. Default to 200 x 500 - data_collector_name: Name of the DataCollector object in the model to - retrieve data from. - template: "chart_module.html" stores the HTML template for the module. - - - Example: - schelling_chart = ChartModule([{"Label": "happy", "Color": "Black"}], - data_collector_name="datacollector") - - TODO: - Have it be able to handle agent-level variables as well. - - 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"] - - def __init__( - self, - series, - canvas_height=200, - canvas_width=500, - data_collector_name="datacollector", - ): - """ - Create a new line chart visualization. - - Args: - series: A list of dictionaries containing series names and - HTML colors to chart them in, e.g. - [{"Label": "happy", "Color": "Black"},] - canvas_height, canvas_width: Size in pixels of the chart to draw. - data_collector_name: Name of the DataCollector to use. - """ - - self.series = series - self.canvas_height = canvas_height - self.canvas_width = canvas_width - self.data_collector_name = data_collector_name - - series_json = json.dumps(self.series) - new_element = "new ChartModule({}, {}, {})" - new_element = new_element.format(series_json, canvas_width, canvas_height) - self.js_code = "elements.push(" + new_element + ");" - - def render(self, model): - current_values = [] - data_collector = getattr(model, self.data_collector_name) - - for s in self.series: - name = s["Label"] - try: - val = data_collector.model_vars[name][-1] # Latest value - except (IndexError, KeyError): - val = 0 - current_values.append(val) - return current_values diff --git a/mesa/visualization/modules/HexGridVisualization.py b/mesa/visualization/modules/HexGridVisualization.py deleted file mode 100644 index 8e318bafbb0..00000000000 --- a/mesa/visualization/modules/HexGridVisualization.py +++ /dev/null @@ -1,87 +0,0 @@ -""" -Modular Canvas Rendering -======================== - -Module for visualizing model objects in hexagonal grid cells. -""" -from collections import defaultdict - -from mesa.visualization.ModularVisualization import VisualizationElement - - -class CanvasHexGrid(VisualizationElement): - """A CanvasHexGrid object functions similarly to a CanvasGrid object. It takes a portrayal dictionary and talks to HexDraw.js to draw that shape. - - A portrayal as a dictionary with the following structure: - "x", "y": Coordinates for the cell in which the object is placed. - "Shape": Can be either "hex" or "circle" - "r": The radius, defined as a fraction of cell size. r=1 will - fill the entire cell. - "Color": The color to draw the shape in; needs to be a valid HTML - color, e.g."Red" or "#AA08F8" - "Filled": either "true" or "false", and determines whether the shape is - filled or not. - "Layer": Layer number of 0 or above; higher-numbered layers are drawn - above lower-numbered layers. - "text": The text to be inscribed inside the Shape. Normally useful for - showing the unique_id of the agent. - "text_color": The color to draw the inscribed text. Should be given in - conjunction of "text" property. - - - Attributes: - portrayal_method: Function which generates portrayals from objects, as - described above. - grid_height, grid_width: Size of the grid to visualize, in cells. - 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"] - portrayal_method = None # Portrayal function - canvas_width = 500 - canvas_height = 500 - - def __init__( - self, - portrayal_method, - grid_width, - grid_height, - canvas_width=500, - canvas_height=500, - ): - """Instantiate a new CanvasGrid. - - Args: - portrayal_method: function to convert each object on the grid to - a portrayal, as described above. - 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 - self.grid_height = grid_height - self.canvas_width = canvas_width - self.canvas_height = canvas_height - - new_element = "new CanvasHexModule({}, {}, {}, {})".format( - self.canvas_width, self.canvas_height, self.grid_width, self.grid_height - ) - - self.js_code = "elements.push(" + new_element + ");" - - def render(self, model): - grid_state = defaultdict(list) - for x in range(model.grid.width): - for y in range(model.grid.height): - cell_objects = model.grid.get_cell_list_contents([(x, y)]) - for obj in cell_objects: - portrayal = self.portrayal_method(obj) - if portrayal: - portrayal["x"] = x - portrayal["y"] = y - grid_state[portrayal["Layer"]].append(portrayal) - - return grid_state diff --git a/mesa/visualization/modules/NetworkVisualization.py b/mesa/visualization/modules/NetworkVisualization.py deleted file mode 100644 index fa0ea61db39..00000000000 --- a/mesa/visualization/modules/NetworkVisualization.py +++ /dev/null @@ -1,28 +0,0 @@ -""" -Network Visualization Module -============ - -Module for rendering the network, using [d3.js](https://d3js.org/) framework. -""" -from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement - - -class NetworkModule(VisualizationElement): - package_includes = [] - - def __init__( - self, - portrayal_method, - canvas_height=500, - canvas_width=500, - ): - NetworkModule.package_includes = ["NetworkModule_d3.js", D3_JS_FILE] - - self.portrayal_method = portrayal_method - self.canvas_height = canvas_height - self.canvas_width = canvas_width - new_element = f"new NetworkModule({self.canvas_width}, {self.canvas_height})" - self.js_code = "elements.push(" + new_element + ");" - - def render(self, model): - return self.portrayal_method(model.G) diff --git a/mesa/visualization/modules/PieChartVisualization.py b/mesa/visualization/modules/PieChartVisualization.py deleted file mode 100644 index b976292203b..00000000000 --- a/mesa/visualization/modules/PieChartVisualization.py +++ /dev/null @@ -1,69 +0,0 @@ -""" -Pie Chart Module -============ - -Module for drawing live-updating pie charts using d3.js -""" -import json - -from mesa.visualization.ModularVisualization import D3_JS_FILE, VisualizationElement - - -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 - "Color" keys. The "Label" value must correspond to a - model-level field collected by the model's DataCollector, and - "Color" must have a valid HTML color. - canvas_height, canvas_width: The width and height to draw the chart on - 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"] - - def __init__( - self, - fields, - canvas_height=500, - canvas_width=500, - data_collector_name="datacollector", - ): - """ - Create a new line chart visualization. - - Args: - fields: A list of dictionaries containing fields names and - HTML colors to chart them in, e.g. - [{"Label": "happy", "Color": "Black"},] - canvas_height, canvas_width: Size in pixels of the chart to draw. - data_collector_name: Name of the DataCollector to use. - """ - - self.fields = fields - self.canvas_height = canvas_height - self.canvas_width = canvas_width - self.data_collector_name = data_collector_name - - fields_json = json.dumps(self.fields) - new_element = "new PieChartModule({}, {}, {})" - new_element = new_element.format(fields_json, canvas_width, canvas_height) - self.js_code = "elements.push(" + new_element + ");" - - def render(self, model): - current_values = [] - data_collector = getattr(model, self.data_collector_name) - - for s in self.fields: - name = s["Label"] - try: - val = data_collector.model_vars[name][-1] # Latest value - except (IndexError, KeyError): - val = 0 - current_values.append(val) - return current_values diff --git a/mesa/visualization/modules/__init__.py b/mesa/visualization/modules/__init__.py deleted file mode 100644 index 8cc89e23f9f..00000000000 --- a/mesa/visualization/modules/__init__.py +++ /dev/null @@ -1,14 +0,0 @@ -""" -Container for all built-in visualization modules. -""" - -from mesa.visualization.modules.CanvasGridVisualization import CanvasGrid # noqa -from mesa.visualization.modules.ChartVisualization import ChartModule # noqa -from mesa.visualization.modules.PieChartVisualization import PieChartModule # noqa -from mesa.visualization.modules.BarChartVisualization import BarChartModule # noqa -from mesa.visualization.modules.HexGridVisualization import CanvasHexGrid # noqa -from mesa.visualization.modules.NetworkVisualization import NetworkModule # noqa - -# Delete this line in the next major release, once the simpler namespace has -# become widely adopted. -from mesa.visualization.ModularVisualization import TextElement # noqa diff --git a/mesa/visualization/templates/css/visualization.css b/mesa/visualization/templates/css/visualization.css deleted file mode 100644 index 9ae0af8a3b8..00000000000 --- a/mesa/visualization/templates/css/visualization.css +++ /dev/null @@ -1,20 +0,0 @@ -.model-parameter { - margin-bottom: 15px; -} - -/* This is specific to the Network visualization */ -div.d3tooltip { - position: absolute; - text-align: center; - padding: 1px; - font: 20px sans-serif; - background: lightsteelblue; - border: 3px; - border-radius: 8px; - pointer-events: none; -} - -canvas.world-grid { - position:absolute; - border:1px dotted -} diff --git a/mesa/visualization/templates/js/BarChartModule.js b/mesa/visualization/templates/js/BarChartModule.js deleted file mode 100644 index c5cb763ca46..00000000000 --- a/mesa/visualization/templates/js/BarChartModule.js +++ /dev/null @@ -1,182 +0,0 @@ -"use strict"; -// Note: This grouped bar chart is based off the example found here: -// https://bl.ocks.org/mbostock/3887051 -const BarChartModule = function ( - fields, - canvas_width, - canvas_height, - sorting, - sortingKey -) { - // Create the overall chart div - const chartDiv = document.createElement("div"); - chartDiv.className = "bar chart"; - chartDiv.setAttribute("width", canvas_width); - const elements = document.getElementById("elements"); - elements.appendChild(chartDiv); - - // Create the svg element: - const svg = d3.create("svg"); - svg - .attr("width", canvas_width) - .attr("height", canvas_height) - .style("border", "1px dotted"); - chartDiv.appendChild(svg.node()); - - //create the legend - const legend = d3.create("div"); - legend - .attr("class", "legend") - .attr( - "style", - `display:block;width:${canvas_width}px;text-align:center` - ); - - chartDiv.appendChild(legend.node()); - - legend - .selectAll("span") - .data(fields) - .enter() - .append("span") - .html(function (d) { - return ( - "" + - " " + - d["Label"].replace(" ", " ") - ); - }) - .attr("style", "padding-left:10px;padding-right:10px;"); - - // setup the d3 svg - const margin = { top: 20, right: 20, bottom: 30, left: 40 }; - const width = +svg.attr("width") - margin.left - margin.right; - const height = +svg.attr("height") - margin.top - margin.bottom; - const g = svg - .append("g") - .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - // Setup the bar chart - const x0 = d3.scaleBand().rangeRound([0, width]).paddingInner(0.1); - const x1 = d3.scaleBand().padding(0.05); - const y = d3.scaleLinear().rangeRound([height, 0]); - const colorScale = d3.scaleOrdinal(fields.map((field) => field["Color"])); - const keys = fields.map((f) => f["Label"]); - const chart = g.append("g"); - const axisBottom = g.append("g"); - const axisLeft = g.append("g"); - - axisBottom - .attr("class", "axis") - .attr("transform", "translate(0," + height + ")") - .call(d3.axisBottom(x0)); - - axisLeft.attr("class", "axis").call(d3.axisLeft(y).ticks(null, "s")); - - //Render step - this.render = function (data) { - //Axes - let minY = d3.min(data, function (d) { - return d3.min(keys, function (key) { - return d[key]; - }); - }); - if (minY > 0) { - minY = 0; - } - const maxY = d3.max(data, function (d) { - return d3.max(keys, function (key) { - return d[key]; - }); - }); - - x0.domain( - data.map(function (d, i) { - return i; - }) - ); - x1.domain(keys).rangeRound([0, x0.bandwidth()]); - y.domain([minY, maxY]).nice(); - - if (data.length > 1) { - axisBottom - .attr("transform", "translate(0," + y(0) + ")") - .call(d3.axisBottom(x0)); - } - - axisLeft.call(d3.axisLeft(y).ticks(null, "s")); - - //Sorting - if (sorting != "none") { - if (sorting == "ascending") { - data.sort((a, b) => b[sortingKey] - a[sortingKey]); - } else if (sorting == "descending") { - data.sort((a, b) => a[sortingKey] - b[sortingKey]); - } - } - - //Draw Chart - const rects = chart - .selectAll("g") - .data(data) - .enter() - .append("g") - .attr("transform", function (d, i) { - return "translate(" + x0(i) + ",0)"; - }) - .selectAll("rect"); - - rects - .data(function (d) { - return keys.map(function (key) { - return { key: key, value: d[key] }; - }); - }) - .enter() - .append("rect") - .attr("x", function (d) { - return x1(d.key); - }) - .attr("width", x1.bandwidth()) - .attr("fill", function (d) { - return colorScale(d.key); - }) - .attr("y", function (d) { - return Math.min(y(d.value), y(0)); - }) - .attr("height", function (d) { - return Math.abs(y(d.value) - y(0)); - }) - .append("title") - .text(function (d) { - return d.value; - }); - - //Update chart - chart - .selectAll("g") - .data(data) - .selectAll("rect") - .data(function (d) { - return keys.map(function (key) { - return { key: key, value: d[key] }; - }); - }) - .attr("y", function (d) { - return Math.min(y(d.value), y(0)); - }) - .attr("height", function (d) { - return Math.abs(y(d.value) - y(0)); - }) - .select("title") - .text(function (d) { - return d.value; - }); - }; - - this.reset = function () { - chart.selectAll("g").data([]).exit().remove(); - }; -}; diff --git a/mesa/visualization/templates/js/CanvasHexModule.js b/mesa/visualization/templates/js/CanvasHexModule.js deleted file mode 100644 index 0823462b1a6..00000000000 --- a/mesa/visualization/templates/js/CanvasHexModule.js +++ /dev/null @@ -1,69 +0,0 @@ -const CanvasHexModule = function ( - canvas_width, - canvas_height, - grid_width, - grid_height -) { - const createElement = (tagName, attrs) => { - const element = document.createElement(tagName); - Object.assign(element, attrs); - return element; - }; - - // Create the element - // ------------------ - const parent = createElement("div", { - style: `height:${canvas_height}px;`, - className: "world-grid-parent", - }); - - // Create the tag with absolute positioning : - const createCanvas = () => { - const el = createElement("canvas", { - width: canvas_width, - height: canvas_height, - className: "world-grid", - }); - return el; - }; - const canvas = createCanvas(); - const interaction_canvas = createCanvas(); - - // Append it to parent: - parent.appendChild(canvas); - parent.appendChild(interaction_canvas); - - // Append it to #elements - const elements = document.getElementById("elements"); - elements.appendChild(parent); - - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - - const interactionHandler = new InteractionHandler( - canvas_width, - canvas_height, - grid_width, - grid_height, - interaction_canvas.getContext("2d") - ); - - const canvasDraw = new HexVisualization( - canvas_width, - canvas_height, - grid_width, - grid_height, - context, - interactionHandler - ); - - this.render = (data) => { - canvasDraw.resetCanvas(); - for (const layer in data) canvasDraw.drawLayer(data[layer]); - canvasDraw.drawGridLines("#eee"); - }; - - this.reset = () => { - canvasDraw.resetCanvas(); - }; -}; diff --git a/mesa/visualization/templates/js/CanvasModule.js b/mesa/visualization/templates/js/CanvasModule.js deleted file mode 100644 index 69b9db7d9e2..00000000000 --- a/mesa/visualization/templates/js/CanvasModule.js +++ /dev/null @@ -1,70 +0,0 @@ -const CanvasModule = function ( - canvas_width, - canvas_height, - grid_width, - grid_height -) { - const createElement = (tagName, attrs) => { - const element = document.createElement(tagName); - Object.assign(element, attrs); - return element; - }; - - // Create the element - // ------------------ - // - const parent = createElement("div", { - style: `height:${canvas_height}px;`, - className: "world-grid-parent", - }); - - // Create the tag with absolute positioning : - const createCanvas = () => { - const el = createElement("canvas", { - width: canvas_width, - height: canvas_height, - className: "world-grid", - }); - return el; - }; - const canvas = createCanvas(); - const interaction_canvas = createCanvas(); - - // Append it to parent: - parent.appendChild(canvas); - parent.appendChild(interaction_canvas); - - // Append it to #elements - const elements = document.getElementById("elements"); - elements.appendChild(parent); - - // Create the context for the agents and interactions and the drawing controller: - const context = canvas.getContext("2d"); - - // Create an interaction handler using the - const interactionHandler = new InteractionHandler( - canvas_width, - canvas_height, - grid_width, - grid_height, - interaction_canvas.getContext("2d") - ); - const canvasDraw = new GridVisualization( - canvas_width, - canvas_height, - grid_width, - grid_height, - context, - interactionHandler - ); - - this.render = (data) => { - canvasDraw.resetCanvas(); - for (const layer in data) canvasDraw.drawLayer(data[layer]); - canvasDraw.drawGridLines("#eee"); - }; - - this.reset = () => { - canvasDraw.resetCanvas(); - }; -}; diff --git a/mesa/visualization/templates/js/ChartModule.js b/mesa/visualization/templates/js/ChartModule.js deleted file mode 100644 index be96cd60c1e..00000000000 --- a/mesa/visualization/templates/js/ChartModule.js +++ /dev/null @@ -1,99 +0,0 @@ -const ChartModule = function (series, canvas_width, canvas_height) { - const canvas = document.createElement("canvas"); - Object.assign(canvas, { - width: canvas_width, - height: canvas_height, - style: "border:1px dotted", - }); - // Append it to #elements - const elements = document.getElementById("elements"); - elements.appendChild(canvas); - // Create the context and the drawing controller: - const context = canvas.getContext("2d"); - - const convertColorOpacity = (hex) => { - if (hex.indexOf("#") != 0) { - return "rgba(0,0,0,0.1)"; - } - - hex = hex.replace("#", ""); - const r = parseInt(hex.substring(0, 2), 16); - const g = parseInt(hex.substring(2, 4), 16); - const b = parseInt(hex.substring(4, 6), 16); - return `rgba(${r},${g},${b},0.1)`; - }; - - // Prep the chart properties and series: - const datasets = []; - for (const i in series) { - const s = series[i]; - const new_series = { - backgroundColor: convertColorOpacity(s.Color), - data: [], - }; - for (const property in s){ - new_series[property] = s[property]; - } - datasets.push(new_series); - } - - const chartData = { - labels: [], - datasets: datasets, - }; - - const chartOptions = { - responsive: true, - tooltips: { - mode: "index", - intersect: false, - }, - hover: { - mode: "nearest", - intersect: true, - }, - scales: { - x: { - display: true, - title: { - display: true, - }, - ticks: { - maxTicksLimit: 11, - }, - }, - y: { - display: true, - title: { - display: true, - }, - }, - }, - }; - - const chart = new Chart(context, { - type: "line", - data: chartData, - options: chartOptions, - }); - - this.render = (data) => { - chart.data.labels.push(control.tick); - for (let i = 0; i < data.length; i++) { - chart.data.datasets[i].data.push(data[i]); - } - chart.update(); - }; - - this.reset = () => { - while (chart.data.labels.length) { - chart.data.labels.pop(); - } - chart.data.datasets.forEach((dataset) => { - while (dataset.data.length) { - dataset.data.pop(); - } - }); - chart.update(); - }; -}; diff --git a/mesa/visualization/templates/js/GridDraw.js b/mesa/visualization/templates/js/GridDraw.js deleted file mode 100644 index 614ed6eb815..00000000000 --- a/mesa/visualization/templates/js/GridDraw.js +++ /dev/null @@ -1,417 +0,0 @@ -/** -Mesa Canvas Grid Visualization -==================================================================== - -This is JavaScript code to visualize a Mesa Grid or MultiGrid state using the -HTML5 Canvas. Here's how it works: - -On the server side, the model developer will have assigned a portrayal to each -agent type. The visualization then loops through the grid, for each object adds -a JSON object to an inner list (keyed on layer) of lists to be sent to the -browser. - -Each JSON object to be drawn contains the following fields: Shape (currently -only rectanges and circles are supported), x, y, Color, Filled (boolean), -Layer; circles also get a Radius, while rectangles get x and y sizes. The -latter values are all between [0, 1] and get scaled to the grid cell. - -The browser (this code, in fact) then iteratively draws them in, one layer at a -time. Thus, it should be possible to turn different layers on and off. - -Here's a sample input, for a 2x2 grid with one layer being cell colors and the -other agent locations, represented by circles: - -{"Shape": "rect", "x": 0, "y": 0, "Color": ["#00aa00", "#aa00aa"], "stroke_color": "red", "Filled": "true", "Layer": 0} - -{0:[ - {"Shape": "rect", "x": 0, "y": 0, "w": 1, "h": 1,"Color": ["#00aa00", "#aa00aa"], "stroke_color": "red", "Filled": "true", "Layer": 0}, - {"Shape": "rect", "x": 0, "y": 1, "w": 1, "h": 1, "Color": ["#00aa00", "#aa00aa"], "stroke_color": "red", "Filled": "true", "Layer": 0}, - {"Shape": "rect", "x": 1, "y": 0, "w": 1, "h": 1, "Color": ["#00aa00", "#aa00aa"], "stroke_color": "red", "Filled": "true", "Layer": 0}, - {"Shape": "rect", "x": 1, "y": 1, "w": 1, "h": 1, "Color": ["#00aa00", "#aa00aa"], "stroke_color": "red", "Filled": "true", "Layer": 0} - ], - 1:[ - {"Shape": "circle", "x": 0, "y": 0, "r": 0.5, "Color": ["#00aa00", "#aa00aa"], "stroke_color": "red", "Filled": "true", "Layer": 1, "text": 'A', "text_color": "white"}, - {"Shape": "circle", "x": 1, "y": 1, "r": 0.5, "Color": ["#00aa00", "#aa00aa"], "stroke_color": "red", "Filled": "true", "Layer": 1, "text": 'B', "text_color": "white"} - {"Shape": "arrowHead", "x": 1, "y": 0, "heading_x": -1, heading_y: 0, "scale": 0.5, "Color": ["#00aa00", "#aa00aa"], "stroke_color": "red", "Filled": "true", "Layer": 1, "text": 'C', "text_color": "white"} - ] -} - -*/ - -const GridVisualization = function ( - width, - height, - gridWidth, - gridHeight, - context, - interactionHandler -) { - // Find cell size: - const cellWidth = Math.floor(width / gridWidth); - const cellHeight = Math.floor(height / gridHeight); - - // Find max radius of the circle that can be inscribed (fit) into the - // cell of the grid. - const maxR = Math.min(cellHeight, cellWidth) / 2 - 1; - - // Calls the appropriate shape(agent) - this.drawLayer = function (portrayalLayer) { - // Re-initialize the lookup table - if (interactionHandler) interactionHandler.mouseoverLookupTable.init(); - - for (const i in portrayalLayer) { - const p = portrayalLayer[i]; - - // If p.Color is a string scalar, cast it to an array. - // This is done to maintain backwards compatibility - if (!Array.isArray(p.Color)) p.Color = [p.Color]; - - // Does the inversion of y positioning because of html5 - // canvas y direction is from top to bottom. But we - // normally keep y-axis in plots from bottom to top. - p.y = gridHeight - p.y - 1; - - // if a handler exists, add coordinates for the portrayalLayer index - if (interactionHandler) interactionHandler.mouseoverLookupTable.set(p.x, p.y, i); - - // If the stroke color is not defined, then the first color in the colors array is the stroke color. - if (!p.stroke_color) p.stroke_color = p.Color[0]; - - // Default alignments to 0.5 (center of a cell) - p.xAlign ??= 0.5; - p.yAlign ??= 0.5; - - if (p.Shape == "rect") - this.drawRectangle( - p.x, - p.y, - p.xAlign, - p.yAlign, - p.w, - p.h, - p.Color, - p.stroke_color, - p.Filled, - p.text, - p.text_color - ); - else if (p.Shape == "circle") - this.drawCircle( - p.x, - p.y, - p.xAlign, - p.yAlign, - p.r, - p.Color, - p.stroke_color, - p.Filled, - p.text, - p.text_color - ); - else if (p.Shape == "arrowHead") - this.drawArrowHead( - p.x, - p.y, - p.heading_x, - p.heading_y, - p.scale, - p.Color, - p.stroke_color, - p.Filled, - p.text, - p.text_color - ); - else - this.drawCustomImage(p.Shape, p.x, p.y, p.scale, p.text, p.text_color); - } - // if a handler exists, update its mouse listeners with the new data - if (interactionHandler) interactionHandler.updateMouseListeners(portrayalLayer); - }; - - // DRAWING METHODS - // ===================================================================== - - /** - Draw a circle in the specified grid cell. - x, y: Grid coords - xAlign, yAlign: Alignment within the cell, defaults to 0.5 (center) - r: Radius, as a multiple of cell size - colors: List of colors for the gradient. Providing only one color will fill the shape with only that color, not gradient. - stroke_color: Color to stroke the shape - fill: Boolean for whether or not to fill the circle. - text: Inscribed text in rectangle. - text_color: Color of the inscribed text. - */ - this.drawCircle = function ( - x, - y, - xAlign, - yAlign, - radius, - colors, - stroke_color, - fill, - text, - text_color - ) { - // Prevent circle from being drawn outside cell bounds. - // Since a radius of 1 corresponds to a circle that fills - // the entire cell, it is necessary to divide radius by 2. - xAlign = clamp(xAlign, radius / 2, 1 - radius / 2); - yAlign = clamp(yAlign, radius / 2, 1 - radius / 2); - - const cx = (x + xAlign) * cellWidth; - const cy = (y + yAlign) * cellHeight; - const r = radius * maxR; - - context.beginPath(); - context.arc(cx, cy, r, 0, Math.PI * 2, false); - context.closePath(); - - context.strokeStyle = stroke_color; - context.stroke(); - - if (fill) { - const gradient = context.createRadialGradient(cx, cy, r, cx, cy, 0); - - for (let i = 0; i < colors.length; i++) { - gradient.addColorStop(i / colors.length, colors[i]); - } - - context.fillStyle = gradient; - context.fill(); - } - - // This part draws the text inside the Circle - if (text !== undefined) { - context.fillStyle = text_color; - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillText(text, cx, cy); - } - }; - - /** - Draw a rectangle in the specified grid cell. - x, y: Grid coords - xAlign, yAlign: Alignment within the cell, defaults to 0.5 (center) - w, h: Width and height, [0, 1] - colors: List of colors for the gradient. Providing only one color will fill the shape with only that color, not gradient. - stroke_color: Color to stroke the shape - fill: Boolean, whether to fill or not. - text: Inscribed text in rectangle. - text_color: Color of the inscribed text. - */ - this.drawRectangle = function ( - x, - y, - xAlign, - yAlign, - w, - h, - colors, - stroke_color, - fill, - text, - text_color - ) { - context.beginPath(); - const dx = w * cellWidth; - const dy = h * cellHeight; - - // Prevent rect from being drawn outside cell bounds. - xAlign = clamp(xAlign, w / 2, 1 - w / 2); - yAlign = clamp(yAlign, h / 2, 1 - h / 2); - - const x0 = (x + xAlign) * cellWidth - dx / 2; - const y0 = (y + yAlign) * cellHeight - dy / 2; - - context.strokeStyle = stroke_color; - context.strokeRect(x0, y0, dx, dy); - - if (fill) { - const gradient = context.createLinearGradient( - x0, - y0, - x0 + cellWidth, - y0 + cellHeight - ); - - for (let i = 0; i < colors.length; i++) { - gradient.addColorStop(i / colors.length, colors[i]); - } - - // Fill with gradient - context.fillStyle = gradient; - context.fillRect(x0, y0, dx, dy); - } else { - context.fillStyle = color; - context.strokeRect(x0, y0, dx, dy); - } - // This part draws the text inside the Rectangle - if (text !== undefined) { - const cx = (x + 0.5) * cellWidth; - const cy = (y + 0.5) * cellHeight; - context.fillStyle = text_color; - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillText(text, cx, cy); - } - }; - - /** - Draw an arrow head in the specified grid cell. - x, y: Grid coords - s: Scaling of the arrowHead with respect to cell size[0, 1] - colors: List of colors for the gradient. Providing only one color will fill the shape with only that color, not gradient. - stroke_color: Color to stroke the shape - fill: Boolean, whether to fill or not. - text: Inscribed text in shape. - text_color: Color of the inscribed text. - */ - this.drawArrowHead = function ( - x, - y, - heading_x, - heading_y, - scale, - colors, - stroke_color, - fill, - text, - text_color - ) { - const arrowR = maxR * scale; - const cx = (x + 0.5) * cellWidth; - const cy = (y + 0.5) * cellHeight; - if (heading_x === 0 && heading_y === 1) { - p1_x = cx; - p1_y = cy - arrowR; - p2_x = cx - arrowR; - p2_y = cy + arrowR; - p3_x = cx; - p3_y = cy + 0.8 * arrowR; - p4_x = cx + arrowR; - p4_y = cy + arrowR; - } else if (heading_x === 1 && heading_y === 0) { - p1_x = cx + arrowR; - p1_y = cy; - p2_x = cx - arrowR; - p2_y = cy - arrowR; - p3_x = cx - 0.8 * arrowR; - p3_y = cy; - p4_x = cx - arrowR; - p4_y = cy + arrowR; - } else if (heading_x === 0 && heading_y === -1) { - p1_x = cx; - p1_y = cy + arrowR; - p2_x = cx - arrowR; - p2_y = cy - arrowR; - p3_x = cx; - p3_y = cy - 0.8 * arrowR; - p4_x = cx + arrowR; - p4_y = cy - arrowR; - } else if (heading_x === -1 && heading_y === 0) { - p1_x = cx - arrowR; - p1_y = cy; - p2_x = cx + arrowR; - p2_y = cy - arrowR; - p3_x = cx + 0.8 * arrowR; - p3_y = cy; - p4_x = cx + arrowR; - p4_y = cy + arrowR; - } - - context.beginPath(); - context.moveTo(p1_x, p1_y); - context.lineTo(p2_x, p2_y); - context.lineTo(p3_x, p3_y); - context.lineTo(p4_x, p4_y); - context.closePath(); - - context.strokeStyle = stroke_color; - context.stroke(); - - if (fill) { - const gradient = context.createLinearGradient(p1_x, p1_y, p3_x, p3_y); - - for (let i = 0; i < colors.length; i++) { - gradient.addColorStop(i / colors.length, colors[i]); - } - - // Fill with gradient - context.fillStyle = gradient; - context.fill(); - } - - // This part draws the text inside the ArrowHead - if (text !== undefined) { - context.fillStyle = text_color; - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillText(text, cx, cy); - } - }; - - this.drawCustomImage = function (shape, x, y, scale, text, text_color_) { - const img = new Image(); - img.src = "local/custom/".concat(shape); - if (scale === undefined) { - scale = 1; - } - // Calculate coordinates so the image is always centered - const dWidth = cellWidth * scale; - const dHeight = cellHeight * scale; - const cx = x * cellWidth + cellWidth / 2 - dWidth / 2; - const cy = y * cellHeight + cellHeight / 2 - dHeight / 2; - - // Coordinates for the text - const tx = (x + 0.5) * cellWidth; - const ty = (y + 0.5) * cellHeight; - - img.onload = function () { - context.drawImage(img, cx, cy, dWidth, dHeight); - // This part draws the text on the image - if (text !== undefined) { - // ToDo: Fix fillStyle - // context.fillStyle = text_color; - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillText(text, tx, ty); - } - }; - }; - - /** - Draw Grid lines in the full gird - */ - - this.drawGridLines = function () { - context.beginPath(); - context.strokeStyle = "#eee"; - const maxX = cellWidth * gridWidth; - const maxY = cellHeight * gridHeight; - - // Draw horizontal grid lines: - for (let y = 0; y <= maxY; y += cellHeight) { - context.moveTo(0, y + 0.5); - context.lineTo(maxX, y + 0.5); - } - - for (let x = 0; x <= maxX; x += cellWidth) { - context.moveTo(x + 0.5, 0); - context.lineTo(x + 0.5, maxY); - } - - context.stroke(); - }; - - this.resetCanvas = function () { - context.clearRect(0, 0, width, height); - context.beginPath(); - }; -}; - -const clamp = function (val, min, max) { - return Math.min(Math.max(min, val), max); -} diff --git a/mesa/visualization/templates/js/HexDraw.js b/mesa/visualization/templates/js/HexDraw.js deleted file mode 100644 index ad496389f9a..00000000000 --- a/mesa/visualization/templates/js/HexDraw.js +++ /dev/null @@ -1,262 +0,0 @@ -/** -Mesa Canvas Grid Visualization -==================================================================== - -This is JavaScript code to visualize a Mesa Grid or MultiGrid state using the -HTML5 Canvas. Here's how it works: - -On the server side, the model developer will have assigned a portrayal to each -agent type. The visualization then loops through the grid, for each object adds -a JSON object to an inner list (keyed on layer) of lists to be sent to the -browser. - -Each JSON object to be drawn contains the following fields: Shape (currently -only rectanges and circles are supported), x, y, Color, Filled (boolean), -Layer; circles also get a Radius, while rectangles get x and y sizes. The -latter values are all between [0, 1] and get scaled to the grid cell. - -The browser (this code, in fact) then iteratively draws them in, one layer at a -time. Thus, it should be possible to turn different layers on and off. - -Here's a sample input, for a 2x2 grid with one layer being cell colors and the -other agent locations, represented by circles: - -{"Shape": "rect", "x": 0, "y": 0, "Color": "#00aa00", "Filled": "true", "Layer": 0} - -{0:[ - {"Shape": "rect", "x": 0, "y": 0, "w": 1, "h": 1, "Color": "#00aa00", "Filled": "true", "Layer": 0}, - {"Shape": "rect", "x": 0, "y": 1, "w": 1, "h": 1, "Color": "#00aa00", "Filled": "true", "Layer": 0}, - {"Shape": "rect", "x": 1, "y": 0, "w": 1, "h": 1, "Color": "#00aa00", "Filled": "true", "Layer": 0}, - {"Shape": "rect", "x": 1, "y": 1, "w": 1, "h": 1, "Color": "#00aa00", "Filled": "true", "Layer": 0} - ], - 1:[ - {"Shape": "circle", "x": 0, "y": 0, "r": 0.5, "Color": "#AAAAAA", "Filled": "true", "Layer": 1, "text": 'A', "text_color": "white"}, - {"Shape": "circle", "x": 1, "y": 1, "r": 0.5, "Color": "#AAAAAA", "Filled": "true", "Layer": 1, "text": 'B', "text_color": "white"} - {"Shape": "arrowHead", "x": 1, "y": 0, "heading_x": -1, heading_y: 0, "scale": 0.5, "Color": "green", "Filled": "true", "Layer": 1, "text": 'C', "text_color": "white"} - ] -} - -*/ - -const HexVisualization = function ( - width, - height, - gridWidth, - gridHeight, - context, - interactionHandler -) { - // Find cell size: - const cellWidth = Math.floor(width / gridWidth); - const cellHeight = Math.floor(height / gridHeight); - - // Find max radius of the circle that can be inscribed (fit) into the - // cell of the grid. - const maxR = Math.min(cellHeight, cellWidth) / 2 - 1; - - // Configure the interaction handler to use a hex coordinate mapper - if (interactionHandler) interactionHandler.setCoordinateMapper("hex"); - - // Calls the appropriate shape(agent) - this.drawLayer = function (portrayalLayer) { - // Re-initialize the lookup table - if (interactionHandler) interactionHandler.mouseoverLookupTable.init(); - for (const i in portrayalLayer) { - const p = portrayalLayer[i]; - // Does the inversion of y positioning because of html5 - // canvas y direction is from top to bottom. But we - // normally keep y-axis in plots from bottom to top. - p.y = gridHeight - p.y - 1; - - // if a handler exists, add coordinates for the portrayalLayer index - if (interactionHandler) interactionHandler.mouseoverLookupTable.set(p.x, p.y, i); - - if (p.Shape == "hex") - this.drawHex(p.x, p.y, p.r, p.Color, p.Filled, p.text, p.text_color); - else if (p.Shape == "circle") - this.drawCircle(p.x, p.y, p.r, p.Color, p.Filled, p.text, p.text_color); - else if (p.Shape == "arrowHead") - this.drawArrowHead( - p.x, - p.y, - p.heading_x, - p.heading_y, - p.scale, - p.Color, - p.Filled, - p.text, - p.text_color - ); - else - this.drawCustomImage(p.Shape, p.x, p.y, p.scale, p.text, p.text_color); - } - // if a handler exists, update its mouse listeners with the new data - if (interactionHandler) interactionHandler.updateMouseListeners(portrayalLayer); - }; - - // DRAWING METHODS - // ===================================================================== - - /** - Draw a circle in the specified grid cell. - x, y: Grid coords - r: Radius, as a multiple of cell size - color: Code for the fill color - fill: Boolean for whether or not to fill the circle. - text: Inscribed text in rectangle. - text_color: Color of the inscribed text. - */ - this.drawCircle = function (x, y, radius, color, fill, text, text_color) { - const cx = (x + 0.5) * cellWidth; - let cy; - if (x % 2 == 0) { - cy = (y + 0.5) * cellHeight; - } else { - cy = (y + 0.5) * cellHeight + cellHeight / 2; - } - const r = radius * maxR; - - 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 part draws the text inside the Circle - if (text !== undefined) { - context.fillStyle = text_color; - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillText(text, cx, cy); - } - }; - - /** - Draw a hexagon in the specified grid cell. - x, y: Grid coords - r: Radius, as a multiple of cell size - color: Code for the fill color - fill: Boolean for whether or not to fill the circle. - text: Inscribed text in rectangle. - text_color: Color of the inscribed text. - */ - this.drawHex = function (x, y, radius, color, fill, text, text_color) { - const cx = (x + 0.5) * cellWidth; - let cy; - if (x % 2 == 0) { - cy = (y + 0.5) * cellHeight; - } else { - cy = (y + 0.5) * cellHeight + cellHeight / 2; - } - const maxHexRadius = cellHeight / Math.sqrt(3); - const r = radius * maxHexRadius; - - function hex_corner(x, y, size, i) { - const angle_deg = 60 * i; - const angle_rad = (Math.PI / 180) * angle_deg; - return [ - x + size * Math.cos(angle_rad) * 1.2, - y + size * Math.sin(angle_rad), - ]; - } - - context.beginPath(); - let [px, py] = hex_corner(cx, cy, r, 1); - // console.log(px,py) - context.moveTo(px, py); - //for i in range(5): - Array.from(new Array(5), (n, i) => { - [px, py] = hex_corner(cx, cy, r, i + 2); - // console.log(px,py) - context.lineTo(px, py); - }); - context.closePath(); - - context.strokeStyle = color; - context.stroke(); - - if (fill) { - context.fillStyle = color; - context.fill(); - } - // This part draws the text inside the Circle - if (text !== undefined) { - context.fillStyle = text_color; - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillText(text, cx, cy); - } - }; - - this.drawCustomImage = function (shape, x, y, scale, text, text_color_) { - const img = new Image(); - img.src = "local/".concat(shape); - if (scale === undefined) { - scale = 1; - } - // Calculate coordinates so the image is always centered - const dWidth = cellWidth * scale; - const dHeight = cellHeight * scale; - const cx = x * cellWidth + cellWidth / 2 - dWidth / 2; - const cy = y * cellHeight + cellHeight / 2 - dHeight / 2; - - // Coordinates for the text - const tx = (x + 0.5) * cellWidth; - const ty = (y + 0.5) * cellHeight; - - img.onload = function () { - context.drawImage(img, cx, cy, dWidth, dHeight); - // This part draws the text on the image - if (text !== undefined) { - // ToDo: Fix fillStyle - // context.fillStyle = text_color; - context.textAlign = "center"; - context.textBaseline = "middle"; - context.fillText(text, tx, ty); - } - }; - }; - - /** - Draw Grid lines in the full gird - */ - - this.drawGridLines = function (strokeColor) { - context.beginPath(); - context.strokeStyle = strokeColor || "#eee"; - const maxX = cellWidth * gridWidth; - const maxY = cellHeight * gridHeight; - - const xStep = cellWidth * 0.33; - const yStep = cellHeight * 0.5; - - let yStart = yStep; - for (let x = cellWidth / 2; x <= maxX; x += cellWidth) { - for (let y = yStart; y <= maxY; y += cellHeight) { - context.moveTo(x - 2 * xStep, y); - - context.lineTo(x - xStep, y - yStep); - context.lineTo(x + xStep, y - yStep); - context.lineTo(x + 2 * xStep, y); - - context.lineTo(x + xStep, y + yStep); - context.lineTo(x - xStep, y + yStep); - context.lineTo(x - 2 * xStep, y); - } - yStart = yStart === 0 ? yStep : 0; - } - - context.stroke(); - }; - - this.resetCanvas = function () { - context.clearRect(0, 0, width, height); - context.beginPath(); - }; -}; diff --git a/mesa/visualization/templates/js/InteractionHandler.js b/mesa/visualization/templates/js/InteractionHandler.js deleted file mode 100644 index f98906d1a59..00000000000 --- a/mesa/visualization/templates/js/InteractionHandler.js +++ /dev/null @@ -1,184 +0,0 @@ -/** -Mesa Visualization InteractionHandler -==================================================================== - -This uses the context of an additional canvas laid overtop of another canvas -visualization and maps mouse movements to agent position, displaying any agent -attributes included in the portrayal that are not listed in the ignoredFeatures. - -The following portrayal will yield tooltips with wealth, id, and pos: - -portrayal = { - "Shape": "circle", - "Filled": "true", - "Layer": 0, - "Color": colors[agent.wealth] if agent.wealth < len(colors) else '#a0a', - "r": 0.3 + 0.1 * agent.wealth, - "wealth": agent.wealth, - "id": agent.unique_id, - 'pos': agent.pos -} - -**/ - -const InteractionHandler = function (width, height, gridWidth, gridHeight, ctx) { - // Find cell size: - const cellWidth = Math.floor(width / gridWidth); - const cellHeight = Math.floor(height / gridHeight); - - const lineHeight = 10; - - // list of standard rendering features to ignore (and key-values in the portrayal will be added ) - const ignoredFeatures = [ - "Shape", - "Filled", - "Color", - "r", - "x", - "y", - "xAlign", - "yAlign", - "w", - "h", - "width", - "height", - "heading_x", - "heading_y", - "stroke_color", - "text_color", - ]; - - // Set a variable to hold the lookup table and make it accessible to draw scripts - const mouseoverLookupTable = (this.mouseoverLookupTable = buildLookupTable( - gridWidth, - gridHeight - )); - function buildLookupTable(gridWidth, gridHeight) { - let lookupTable; - this.init = function () { - lookupTable = [...Array(gridHeight).keys()].map((i) => Array(gridWidth)); - }; - - this.set = function (x, y, value) { - if (lookupTable[y][x]) lookupTable[y][x].push(value); - else lookupTable[y][x] = [value]; - }; - - this.get = function (x, y) { - if (lookupTable[y]) return lookupTable[y][x] || []; - return []; - }; - - return this; - } - - let coordinateMapper; - this.setCoordinateMapper = function (mapperName) { - if (mapperName === "hex") { - coordinateMapper = function (event) { - const x = Math.floor(event.offsetX / cellWidth); - const y = - x % 2 === 0 - ? Math.floor(event.offsetY / cellHeight) - : Math.floor((event.offsetY - cellHeight / 2) / cellHeight); - return { x: x, y: y }; - }; - return; - } - - // default coordinate mapper for grids - coordinateMapper = function (event) { - return { - x: Math.floor(event.offsetX / cellWidth), - y: Math.floor(event.offsetY / cellHeight), - }; - }; - }; - - this.setCoordinateMapper("grid"); - - // wrap the rect styling in a function - function drawTooltipBox(ctx, x, y, width, height) { - ctx.fillStyle = "#F0F0F0"; - ctx.beginPath(); - ctx.shadowOffsetX = -3; - ctx.shadowOffsetY = 2; - ctx.shadowBlur = 6; - ctx.shadowColor = "#33333377"; - ctx.rect(x, y, width, height); - ctx.fill(); - ctx.shadowColor = "transparent"; - } - - let listener; - let tmp; - this.updateMouseListeners = function (portrayalLayer) { - tmp = portrayalLayer; - - // Remove the prior event listener to avoid creating a new one every step - ctx.canvas.removeEventListener("mousemove", listener); - - // define the event litser for this step - listener = function (event) { - // clear the previous interaction - ctx.clearRect(0, 0, width, height); - - // map the event to x,y coordinates - const position = coordinateMapper(event); - - // look up the portrayal items the coordinates refer to and draw a tooltip - mouseoverLookupTable - .get(position.x, position.y) - .forEach((portrayalIndex, nthAgent) => { - const agent = portrayalLayer[portrayalIndex]; - const features = Object.keys(agent).filter( - (k) => ignoredFeatures.indexOf(k) < 0 - ); - const textWidth = Math.max.apply( - null, - features.map((k) => ctx.measureText(`${k}: ${agent[k]}`).width) - ); - const textHeight = features.length * lineHeight; - const y = Math.max( - lineHeight * 2, - Math.min(height - textHeight, event.offsetY - textHeight / 2) - ); - const rectMargin = 2 * lineHeight; - let x = 0; - let rectX = 0; - - if (event.offsetX < width / 2) { - x = - event.offsetX + rectMargin + nthAgent * (textWidth + rectMargin); - ctx.textAlign = "left"; - rectX = x - rectMargin / 2; - } else { - x = - event.offsetX - - rectMargin - - nthAgent * (textWidth + rectMargin + lineHeight); - ctx.textAlign = "right"; - rectX = x - textWidth - rectMargin / 2; - } - - // draw a background box - drawTooltipBox( - ctx, - rectX, - y - rectMargin, - textWidth + rectMargin, - textHeight + rectMargin - ); - - // set the color and draw the text - ctx.fillStyle = "black"; - features.forEach((k, i) => { - ctx.fillText(`${k}: ${agent[k]}`, x, y + i * lineHeight); - }); - }); - }; - ctx.canvas.addEventListener("mousemove", listener); - }; - - return this; -}; diff --git a/mesa/visualization/templates/js/NetworkModule_d3.js b/mesa/visualization/templates/js/NetworkModule_d3.js deleted file mode 100644 index 83032083224..00000000000 --- a/mesa/visualization/templates/js/NetworkModule_d3.js +++ /dev/null @@ -1,126 +0,0 @@ -const NetworkModule = function (svg_width, svg_height) { - // Create the svg element: - const svg = d3.create("svg"); - svg - .attr("class", "NetworkModule_d3") - .attr("width", svg_width) - .attr("height", svg_height) - .style("border", "1px dotted"); - - // Append svg to #elements: - document.getElementById("elements").appendChild(svg.node()); - - const width = +svg.attr("width"); - const height = +svg.attr("height"); - const g = svg - .append("g") - .classed("network_root", true); - - const tooltip = d3 - .select("body") - .append("div") - .attr("class", "d3tooltip") - .style("opacity", 0); - - const zoom = d3.zoom() - .on("zoom", (event) => { - g.attr("transform", event.transform); - }); - - svg.call(zoom); - - svg.call( - zoom.transform, - d3.zoomIdentity.translate(width / 2, height / 2) - ); - - const links = g.append("g").attr("class", "links"); - - const nodes = g.append("g").attr("class", "nodes"); - - this.render = (data) => { - const graph = JSON.parse(JSON.stringify(data)); - - const simulation = d3 - .forceSimulation() - .nodes(graph.nodes) - .force("charge", d3.forceManyBody().strength(-80).distanceMin(2)) - .force("link", d3.forceLink(graph.edges)) - .force("center", d3.forceCenter()) - .stop(); - - for ( - let i = 0, - n = Math.ceil( - Math.log(simulation.alphaMin()) / - Math.log(1 - simulation.alphaDecay()) - ); - i < n; - ++i - ) { - simulation.tick(); - } - - links.selectAll("line").data(graph.edges).enter().append("line"); - - links - .selectAll("line") - .data(graph.edges) - .attr("x1", function (d) { - return d.source.x; - }) - .attr("y1", function (d) { - return d.source.y; - }) - .attr("x2", function (d) { - return d.target.x; - }) - .attr("y2", function (d) { - return d.target.y; - }) - .attr("stroke-width", function (d) { - return d.width; - }) - .attr("stroke", function (d) { - return d.color; - }); - - links.selectAll("line").data(graph.edges).exit().remove(); - - nodes - .selectAll("circle") - .data(graph.nodes) - .enter() - .append("circle") - .on("mouseover", function (event, d) { - tooltip.transition().duration(200).style("opacity", 0.9); - tooltip - .html(d.tooltip) - .style("left", event.pageX + "px") - .style("top", event.pageY + "px"); - }) - .on("mouseout", function () { - tooltip.transition().duration(500).style("opacity", 0); - }); - - nodes - .selectAll("circle") - .data(graph.nodes) - .attr("cx", function (d) { - return d.x; - }) - .attr("cy", function (d) { - return d.y; - }) - .attr("r", function (d) { - return d.size; - }) - .attr("fill", function (d) { - return d.color; - }); - - nodes.selectAll("circle").data(graph.nodes).exit().remove(); - }; - - this.reset = () => {}; -}; diff --git a/mesa/visualization/templates/js/PieChartModule.js b/mesa/visualization/templates/js/PieChartModule.js deleted file mode 100644 index 16237d31f7f..00000000000 --- a/mesa/visualization/templates/js/PieChartModule.js +++ /dev/null @@ -1,111 +0,0 @@ -"use strict"; -//Note: This pie chart is based off the example found here: -//https://bl.ocks.org/mbostock/3887235 - -const PieChartModule = function (fields, canvas_width, canvas_height) { - const createElement = (tagName, attrs) => { - const element = document.createElement(tagName); - Object.assign(element, attrs); - return element; - }; - - // Create the overall chart div - const chartDiv = createElement("div", { - className: "pie chart", - width: canvas_width, - }); - document.getElementById("elements").appendChild(chartDiv); - - // Create the svg element: - const svg = d3.create("svg"); - svg - .attr("width", canvas_width) - .attr("height", canvas_height) - .style("border", "1px dotted"); - chartDiv.appendChild(svg.node()); - - //create the legend - const legend = d3.create("div"); - legend - .attr("class", "legend") - .attr("style", `display:block;width:${canvas_width}px;text-align:center`); - chartDiv.appendChild(legend.node()); - - legend - .selectAll("span") - .data(fields) - .enter() - .append("span") - .html(function (d) { - return ( - "" + - " " + - d["Label"].replace(" ", " ") - ); - }) - .attr("style", "padding-left:10px;padding-right:10px;"); - - // setup the d3 svg selection - const width = +svg.attr("width"); - const height = +svg.attr("height"); - const maxRadius = Math.min(width, height) / 2; - const g = svg - .append("g") - .attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"); - - // Create the base chart and helper methods - const color = d3.scaleOrdinal(fields.map((field) => field["Color"])); - const pie = d3 - .pie() - .sort(null) - .value(function (d) { - return d; - }); - const path = d3.arc().outerRadius(maxRadius).innerRadius(0); - const arc = g - .selectAll(".arc") - .data(pie(fields.map((field) => 0))) //Initialize the pie chart with dummy data - .enter() - .append("g") - .attr("class", "arc"); - - arc - .append("path") - .attr("d", path) - .style("fill", function (d, i) { - return color(i); - }) - .append("title") - .text(function (d) { - return d.value; - }); - - this.render = function (data) { - //Update the pie chart each time new data comes in - arc - .data(pie(data)) - .select("path") - .attr("d", path) - .select("title") - .text(function (d) { - return ( - d.value + - " : " + - (((d.endAngle - d.startAngle) * 100.0) / (Math.PI * 2)).toFixed(2) + - "%" - ); - }); - }; - - this.reset = function () { - //Reset the chart by setting each field to 0 - arc - .data(pie(fields.map((field) => 0))) - .enter() - .select("g"); - - arc.select("path").attr("d", path); - }; -}; diff --git a/mesa/visualization/templates/js/TextModule.js b/mesa/visualization/templates/js/TextModule.js deleted file mode 100644 index c06888517f7..00000000000 --- a/mesa/visualization/templates/js/TextModule.js +++ /dev/null @@ -1,15 +0,0 @@ -const TextModule = function () { - const text = document.createElement("p"); - text.className = "lead"; - - // Append text tag to #elements: - document.getElementById("elements").appendChild(text); - - this.render = function (data) { - text.innerHTML = data; - }; - - this.reset = function () { - text.innerHTML = ""; - }; -}; diff --git a/mesa/visualization/templates/js/runcontrol.js b/mesa/visualization/templates/js/runcontrol.js deleted file mode 100644 index ef448166a51..00000000000 --- a/mesa/visualization/templates/js/runcontrol.js +++ /dev/null @@ -1,360 +0,0 @@ -/* runcontrol.js - Users can reset() the model, advance it by one step(), or start() it. reset() and - step() send a message to the server, which then sends back the appropriate data. - start() just calls the step() method at fixed intervals. - - The model parameters are controlled via the ModelController object. -*/ - -/* - * Variable definitions - */ -const controller = new ModelController(); -const vizElements = []; -const startModelButton = document.getElementById("play-pause"); -const stepModelButton = document.getElementById("step"); -const resetModelButton = document.getElementById("reset"); -const stepDisplay = document.getElementById("currentStep"); - -/** - * A ModelController that defines the model state. - * @param {number} tick=0 - Initial step of the model - * @param {number} fps=3 - Run the model with this number of frames per second - * @param {boolean} running=false - Initialize the model in a running state? - * @param {boolean} finished=false - Initialize the model in a finished state? - */ -function ModelController(tick = 0, fps = 3, running = false, finished = false) { - this.tick = tick; - this.fps = fps; - this.running = running; - this.finished = finished; - - /** Start the model and keep it running until stopped */ - this.start = function start() { - this.running = true; - this.step(); - startModelButton.firstElementChild.innerText = "Stop"; - }; - - /** Stop the model */ - this.stop = function stop() { - this.running = false; - startModelButton.firstElementChild.innerText = "Start"; - }; - - /** - * Step the model one step ahead. - * - * If the model is in a running state this function will be called repeatedly - * after the visualization elements are rendered. */ - this.step = function step() { - this.tick += 1; - stepDisplay.innerText = this.tick; - send({ type: "get_step", step: this.tick }); - }; - - /** Reset the model and visualization state but keep its running state */ - this.reset = function reset() { - this.tick = 0; - stepDisplay.innerText = this.tick; - // Reset all the visualizations - vizElements.forEach((element) => element.reset()); - if (this.finished) { - this.finished = false; - startModelButton.firstElementChild.innerText = "Start"; - } - clearTimeout(this.timeout); - send({ type: "reset" }); - }; - - /** Stops the model and put it into a finished state */ - this.done = function done() { - this.stop(); - this.finished = true; - startModelButton.firstElementChild.innerText = "Done"; - }; - - /** - * Render visualisation elements with new data. - * @param {any[]} data Model state data passed to the visualization elements - */ - this.render = function render(data) { - vizElements.forEach((element, index) => element.render(data[index])); - if (this.running) { - this.timeout = setTimeout(() => this.step(), 1000 / this.fps); - } - }; - - /** - * Update the frames per second - * @param {number} val - The new value of frames per second - */ - this.updateFPS = function (val) { - this.fps = Number(val); - }; -} - -/* - * Set up the the FPS control - */ -const fpsControl = new Slider("#fps", { - max: 20, - min: 1, - value: controller.fps, - ticks: [1, 20], - ticks_labels: [1, 20], - ticks_position: [1, 100], -}); -fpsControl.on("change", () => controller.updateFPS(fpsControl.getValue())); - -/* - * Button logic for start, stop and reset buttons - */ -startModelButton.onclick = () => { - if (controller.running) { - controller.stop(); - } else if (!controller.finished) { - controller.start(); - } -}; -stepModelButton.onclick = () => { - if (!controller.running & !controller.finished) { - controller.step(); - } -}; -resetModelButton.onclick = () => controller.reset(); - -/* - * Websocket opening and message handling - */ - -/** Open the websocket connection; support TLS-specific URLs when appropriate */ -const ws = new WebSocket( - (window.location.protocol === "https:" ? "wss://" : "ws://") + - location.host + - "/ws" -); - -/** - * Parse and handle an incoming message on the WebSocket connection. - * @param {string} message - the message received from the WebSocket - */ -ws.onmessage = function (message) { - const msg = JSON.parse(message.data); - switch (msg["type"]) { - case "viz_state": - // Update visualization state - controller.render(msg["data"]); - break; - case "end": - // We have reached the end of the model - controller.done(); - break; - case "model_params": - // Create GUI elements for each model parameter and reset everything - initGUI(msg["params"]); - controller.reset(); - break; - default: - // There shouldn't be any other message - console.log("Unexpected message."); - console.log(msg); - } -}; - -/** - * Turn an object into a string to send to the server, and send it. - * @param {string} message - The message to send to the Python server - */ -const send = function (message) { - const msg = JSON.stringify(message); - ws.send(msg); -}; - -/* - * GUI initialization (for input parameters) - */ - -/** - * Create the GUI with user-settable parameters - * @param {object} model_params - Create the GUI from these model parameters - */ -const initGUI = function (model_params) { - const sidebar = document.getElementById("sidebar"); - - const onSubmitCallback = function (param_name, value) { - send({ type: "submit_params", param: param_name, value: value }); - }; - - const addBooleanInput = function (param, obj) { - const domID = param + "_id"; - const _switch = document.createElement("div"); - _switch.className = "form-check form-switch"; - - const label = ` - - `; - _switch.innerHTML += label; - - const input = document.createElement("input"); - Object.assign(input, { - className: "form-check-input model-parameter", - type: "checkbox", - id: domID, - checked: obj.value, - }); - input.setAttribute("role", "switch"); - input.addEventListener("change", (event) => - onSubmitCallback(param, event.currentTarget.checked) - ); - _switch.appendChild(input); - - sidebar.appendChild(_switch); - }; - - const addNumberInput = function (param, obj) { - const domID = param + "_id"; - const div = document.createElement("div"); - div.innerHTML = ` -

- -

- - `; - sidebar.appendChild(div); - const numberInput = document.getElementById(domID); - numberInput.value = obj.value; - numberInput.onchange = () => { - onSubmitCallback(param, Number(numberInput.value)); - }; - }; - - const addSliderInput = function (param, obj) { - const domID = param + "_id"; - const tooltipID = domID + "_tooltip"; - let tooltip = ""; - // Enable tooltip label - if (obj.description !== null) { - tooltip = `title='${obj.description}'`; - } - - const div = document.createElement("div"); - div.innerHTML = ` -

- - ${obj.name} - -

- - `; - sidebar.appendChild(div); - - // Setup slider - const sliderInput = new Slider("#" + domID, { - min: obj.min_value, - max: obj.max_value, - value: obj.value, - step: obj.step, - ticks: [obj.min_value, obj.max_value], - ticks_labels: [obj.min_value, obj.max_value], - ticks_positions: [0, 100], - }); - sliderInput.on("change", () => { - onSubmitCallback(param, Number(sliderInput.getValue())); - }); - }; - - const addChoiceInput = function (param, obj) { - const domID = param + "_id"; - const template = [ - `

- -

`, - `"); - - // Finally render the dropdown and activate choice listeners - const div = document.createElement("div"); - div.innerHTML = template.join(""); - sidebar.appendChild(div); - - const select = document.getElementById(domID); - select.onchange = () => onSubmitCallback(param, obj.choices[select.value]); - }; - - const addTextBox = function (param, obj) { - const well = document.createElement("div"); - well.className = "well"; - well.innerHTML = obj.value; - sidebar.appendChild(well); - }; - - const addParamInput = function (param, option) { - switch (option["param_type"]) { - case "checkbox": - addBooleanInput(param, option); - break; - - case "slider": - addSliderInput(param, option); - break; - - case "choice": - addChoiceInput(param, option); - break; - - case "number": - addNumberInput(param, option); // Behaves the same as just a simple number - break; - - case "static_text": - addTextBox(param, option); - break; - } - }; - - for (const option in model_params) { - const type = typeof model_params[option]; - const param_str = String(option); - - switch (type) { - case "boolean": - addBooleanInput(param_str, { - value: model_params[option], - name: param_str, - }); - break; - case "number": - addNumberInput(param_str, { - value: model_params[option], - name: param_str, - }); - break; - case "object": - addParamInput(param_str, model_params[option]); // catch-all for params that use Option class - break; - } - } -}; - -// Backward-Compatibility aliases -const control = controller; -const elements = vizElements; diff --git a/mesa/visualization/templates/modular_template.html b/mesa/visualization/templates/modular_template.html deleted file mode 100644 index 7d6e56fd0fc..00000000000 --- a/mesa/visualization/templates/modular_template.html +++ /dev/null @@ -1,106 +0,0 @@ - - - {{ model_name }} (Mesa visualization) - - - - - - {% for file_name in package_css_includes %} - - {% end %} - {% for file_name in local_css_includes %} - - {% end %} - - - - - - -
-
- -
-
-
- - -
-

Current Step: 0 -

-
-
-
- - - - - - - - - - {% for file_name in package_js_includes %} - - {% end %} - {% for file_name in local_js_includes %} - - {% end %} - - - - - - - - diff --git a/setup.py b/setup.py index 72f97b4a7d4..474b91a6054 100644 --- a/setup.py +++ b/setup.py @@ -1,9 +1,5 @@ #!/usr/bin/env python -import os import re -import shutil -import urllib.request -import zipfile from codecs import open from setuptools import find_packages, setup @@ -16,7 +12,6 @@ "numpy", "pandas", "solara", - "tornado", "tqdm", ] @@ -53,76 +48,6 @@ with open("README.rst", "rb", encoding="utf-8") as f: readme = f.read() -# Ensure JS dependencies are downloaded -external_dir = "mesa/visualization/templates/external" -# We use a different path for single-file JS because some of them are loaded -# the same way as Mesa JS files -external_dir_single = "mesa/visualization/templates/js/external" -# First, ensure that the external directories exists -os.makedirs(external_dir, exist_ok=True) -os.makedirs(external_dir_single, exist_ok=True) - - -def ensure_js_dep(dirname, url): - dst_path = os.path.join(external_dir, dirname) - if os.path.isdir(dst_path): - # Do nothing if already downloaded - return - print(f"Downloading the {dirname} dependency from the internet...") - zip_file = dirname + ".zip" - urllib.request.urlretrieve(url, zip_file) - with zipfile.ZipFile(zip_file, "r") as zip_ref: - zip_ref.extractall() - shutil.move(dirname, dst_path) - # Cleanup - os.remove(zip_file) - print("Done") - - -def ensure_js_dep_single(url, out_name=None): - # Used for downloading e.g. D3.js single file - if out_name is None: - out_name = url.split("/")[-1] - dst_path = os.path.join(external_dir_single, out_name) - if os.path.isfile(dst_path): - return - print(f"Downloading the {out_name} dependency from the internet...") - urllib.request.urlretrieve(url, out_name) - shutil.move(out_name, dst_path) - - -# Important: when you update JS dependency version, make sure to also update the -# hardcoded included files and versions in: mesa/visualization/templates/modular_template.html - -# Ensure Bootstrap -bootstrap_version = "5.1.3" -ensure_js_dep( - f"bootstrap-{bootstrap_version}-dist", - f"https://github.com/twbs/bootstrap/releases/download/v{bootstrap_version}/bootstrap-{bootstrap_version}-dist.zip", -) - -# Ensure Bootstrap Slider -bootstrap_slider_version = "11.0.2" -ensure_js_dep( - f"bootstrap-slider-{bootstrap_slider_version}", - f"https://github.com/seiyria/bootstrap-slider/archive/refs/tags/v{bootstrap_slider_version}.zip", -) - -# Important: when updating the D3 version, make sure to update the constant -# D3_JS_FILE in mesa/visualization/ModularVisualization.py. -d3_version = "7.4.3" -ensure_js_dep_single( - f"https://cdnjs.cloudflare.com/ajax/libs/d3/{d3_version}/d3.min.js", - out_name=f"d3-{d3_version}.min.js", -) -# Important: Make sure to update CHART_JS_FILE in -# mesa/visualization/ModularVisualization.py. -chartjs_version = "3.6.1" -ensure_js_dep_single( - f"https://cdn.jsdelivr.net/npm/chart.js@{chartjs_version}/dist/chart.min.js", - out_name=f"chart-{chartjs_version}.min.js", -) - setup( name="Mesa", @@ -134,12 +59,6 @@ def ensure_js_dep_single(url, out_name=None): url="https://github.com/projectmesa/mesa", packages=find_packages(), package_data={ - "mesa": [ - "visualization/templates/*.html", - "visualization/templates/css/*", - "visualization/templates/js/*", - "visualization/templates/external/**/*", - ], "cookiecutter-mesa": ["cookiecutter-mesa/*"], }, include_package_data=True, From 1c3f6288cf594c1918f0d5c3c5f7d5e94820a3ec Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 31 Jul 2023 10:04:01 -0400 Subject: [PATCH 158/214] Add mesa-viz-tornado as dependency --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 474b91a6054..b2c7a996783 100644 --- a/setup.py +++ b/setup.py @@ -8,6 +8,7 @@ "click", "cookiecutter", "matplotlib", + "mesa_viz_tornado @ git+https://github.com/rht/mesa-viz-tornado@a4d9242e90ef7f1fcd388fb5c6d72a4177a76bdd", "networkx", "numpy", "pandas", From 1ea0dc7960ceb7175d5f967de841fc2c101b9f73 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 31 Jul 2023 12:18:12 -0400 Subject: [PATCH 159/214] Maintain backward compatibility with old mesa viz API --- mesa/__init__.py | 2 +- mesa/visualization/ModularVisualization.py | 1 + mesa/visualization/TextVisualization.py | 1 + mesa/visualization/UserParam.py | 1 + mesa/visualization/__init__.py | 4 ++++ mesa/visualization/modules.py | 1 + 6 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 mesa/visualization/ModularVisualization.py create mode 100644 mesa/visualization/TextVisualization.py create mode 100644 mesa/visualization/UserParam.py create mode 100644 mesa/visualization/__init__.py create mode 100644 mesa/visualization/modules.py diff --git a/mesa/__init__.py b/mesa/__init__.py index b0969e389e0..675bfabc40b 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -5,9 +5,9 @@ """ import datetime -import mesa.flat.visualization as visualization import mesa.space as space import mesa.time as time +import mesa.visualization as visualization from mesa.agent import Agent from mesa.batchrunner import batch_run from mesa.datacollection import DataCollector diff --git a/mesa/visualization/ModularVisualization.py b/mesa/visualization/ModularVisualization.py new file mode 100644 index 00000000000..dbdbf3b3f9f --- /dev/null +++ b/mesa/visualization/ModularVisualization.py @@ -0,0 +1 @@ +from mesa_viz_tornado.ModularVisualization import * # noqa diff --git a/mesa/visualization/TextVisualization.py b/mesa/visualization/TextVisualization.py new file mode 100644 index 00000000000..f41c90ea595 --- /dev/null +++ b/mesa/visualization/TextVisualization.py @@ -0,0 +1 @@ +from mesa_viz_tornado.TextVisualization import * # noqa diff --git a/mesa/visualization/UserParam.py b/mesa/visualization/UserParam.py new file mode 100644 index 00000000000..d41362389f4 --- /dev/null +++ b/mesa/visualization/UserParam.py @@ -0,0 +1 @@ +from mesa_viz_tornado.UserParam import * # noqa diff --git a/mesa/visualization/__init__.py b/mesa/visualization/__init__.py new file mode 100644 index 00000000000..754dca50064 --- /dev/null +++ b/mesa/visualization/__init__.py @@ -0,0 +1,4 @@ +from mesa_viz_tornado.ModularVisualization import * # noqa +from mesa_viz_tornado.modules import * # noqa +from mesa_viz_tornado.UserParam import * # noqa +from mesa_viz_tornado.TextVisualization import * # noqa diff --git a/mesa/visualization/modules.py b/mesa/visualization/modules.py new file mode 100644 index 00000000000..40fc193a522 --- /dev/null +++ b/mesa/visualization/modules.py @@ -0,0 +1 @@ +from mesa_viz_tornado.modules import * # noqa From 0bb908d7cac997ddbb64c8a54a1f3a045e0e08a0 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 7 Aug 2023 10:56:01 -0400 Subject: [PATCH 160/214] fix: Allow multiple connections in Solara --- mesa/experimental/jupyter_viz.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 4f5e0446054..fffcda5ebca 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -208,6 +208,9 @@ def on_value_play(change): make_plot(viz, measure) +# JupyterViz has to be a Solara component, so that each browser tabs runs in +# their own, separate simulation thread. See https://github.com/projectmesa/mesa/issues/856. +@solara.component def JupyterViz( model_class, model_params, From 96db39545499dbd14704589bc5dbbedb261aa239 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 17 Jun 2023 02:37:27 -0400 Subject: [PATCH 161/214] rtd: Use gruvbox-dark as style --- docs/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/conf.py b/docs/conf.py index 1cf3b779086..d5459150139 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -106,7 +106,7 @@ # show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = "sphinx" +pygments_style = "gruvbox-dark" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] From 85db5131b000e45a7f4bf91842d184efc3a9d1aa Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 4 Aug 2023 02:57:45 -0400 Subject: [PATCH 162/214] docs: Remove sphinx<7 restriction This is because https://github.com/readthedocs/sphinx_rtd_theme/issues/1463 has been resolved. --- setup.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/setup.py b/setup.py index b2c7a996783..2c142681c16 100644 --- a/setup.py +++ b/setup.py @@ -25,13 +25,11 @@ "pytest-cov", "sphinx", ], - # Constrain sphinx version until https://github.com/readthedocs/readthedocs.org/issues/10279 - # is fixed. # Explicitly install ipykernel for Python 3.8. # See https://stackoverflow.com/questions/28831854/how-do-i-add-python3-kernel-to-jupyter-ipython # Could be removed in the future "docs": [ - "sphinx<7", + "sphinx", "ipython", "ipykernel", "pydata_sphinx_theme", From f615ac0fbfea7e3db02a7de86629156c36da938f Mon Sep 17 00:00:00 2001 From: rht Date: Fri, 4 Aug 2023 03:59:05 -0400 Subject: [PATCH 163/214] Ensure sphinx>=7 --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2c142681c16..1ec0ac24570 100644 --- a/setup.py +++ b/setup.py @@ -29,7 +29,8 @@ # See https://stackoverflow.com/questions/28831854/how-do-i-add-python3-kernel-to-jupyter-ipython # Could be removed in the future "docs": [ - "sphinx", + "sphinx>=7", + "sphinx-rtd-theme>=1.3.0rc1", "ipython", "ipykernel", "pydata_sphinx_theme", From 8a23d624c69805ad940c9da31a5699b9d6f67f4b Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 7 Aug 2023 10:19:11 -0400 Subject: [PATCH 164/214] fix: Ensure hooks are called at the top level This fix is recommended by @mariobuikhuizen at Solara Discord. See https://solara.dev/docs/understanding/rules-of-hooks. --- mesa/experimental/jupyter_viz.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index fffcda5ebca..6fa539762df 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -116,7 +116,10 @@ def function(viz): return function -def make_user_input(user_input, k, v): +@solara.component +def make_user_input(user_inputs, k, v): + user_input = solara.use_reactive(v["value"]) + user_inputs[k] = user_input.value if v["type"] == "SliderInt": solara.SliderInt( v.get("label", "label"), @@ -142,9 +145,7 @@ def MesaComponent(viz, space_drawer=None, play_interval=400): # 1. User inputs user_inputs = {} for k, v in viz.model_params_input.items(): - user_input = solara.use_reactive(v["value"]) - user_inputs[k] = user_input.value - make_user_input(user_input, k, v) + make_user_input(user_inputs, k, v) # 2. Model def make_model(): From 4abbde49c0acca87f3aa74806bcab7ea4e193055 Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 10 Aug 2023 06:14:02 -0400 Subject: [PATCH 165/214] Revert "fix: Ensure hooks are called at the top level" This reverts commit 8a23d624c69805ad940c9da31a5699b9d6f67f4b. --- mesa/experimental/jupyter_viz.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 6fa539762df..fffcda5ebca 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -116,10 +116,7 @@ def function(viz): return function -@solara.component -def make_user_input(user_inputs, k, v): - user_input = solara.use_reactive(v["value"]) - user_inputs[k] = user_input.value +def make_user_input(user_input, k, v): if v["type"] == "SliderInt": solara.SliderInt( v.get("label", "label"), @@ -145,7 +142,9 @@ def MesaComponent(viz, space_drawer=None, play_interval=400): # 1. User inputs user_inputs = {} for k, v in viz.model_params_input.items(): - make_user_input(user_inputs, k, v) + user_input = solara.use_reactive(v["value"]) + user_inputs[k] = user_input.value + make_user_input(user_input, k, v) # 2. Model def make_model(): From e2c407fe9ca93b0b9eb237e8c83dc9b3b400ce84 Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 10 Aug 2023 06:19:23 -0400 Subject: [PATCH 166/214] Revert "Ensure sphinx>=7" This reverts commit f615ac0fbfea7e3db02a7de86629156c36da938f. --- setup.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 1ec0ac24570..2c142681c16 100644 --- a/setup.py +++ b/setup.py @@ -29,8 +29,7 @@ # See https://stackoverflow.com/questions/28831854/how-do-i-add-python3-kernel-to-jupyter-ipython # Could be removed in the future "docs": [ - "sphinx>=7", - "sphinx-rtd-theme>=1.3.0rc1", + "sphinx", "ipython", "ipykernel", "pydata_sphinx_theme", From 84e51508ca306475ef0fd10cf67d169b25e892df Mon Sep 17 00:00:00 2001 From: Corvince Date: Sun, 13 Aug 2023 09:18:31 +0200 Subject: [PATCH 167/214] simplify get neighborhood (#1760) * simplify get neighborhood * use dict instead of set to keep insertion order * remove radius=1 special case to keep code simple --- mesa/space.py | 74 ++++++++++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 36 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index eebcb3c1758..a9d3d569970 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -261,53 +261,55 @@ def get_neighborhood( if self.out_of_bounds(pos): raise Exception("The `pos` tuple passed is out of bounds.") - # 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 = [] + # we use a dict to keep insertion order + neighborhood = {} x, y = pos - if self.torus: - 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: + + # First we check if the neighborhood is inside the grid + if ( + x >= radius + and self.width - x > radius + and y >= radius + and self.height - y > radius + ): + # If the radius is smaller than the distance from the borders, we + # can skip boundary checks. + x_range = range(x - radius, x + radius + 1) + y_range = range(y - radius, y + radius + 1) + + for new_x in x_range: + for new_y in y_range: + if not moore and abs(new_x - x) + abs(new_y - y) > 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)) + neighborhood[(new_x, new_y)] = True - for nx in x_range: - for ny in y_range: - if not moore and abs(nx - x) + abs(ny - y) > radius: + else: + # If the radius is larger than the distance from the borders, we + # must use a slower method, that takes into account the borders + # and the torus property. + for dx in range(-radius, radius + 1): + for dy in range(-radius, radius + 1): + if not moore and abs(dx) + abs(dy) > radius: continue - neighborhood.append((nx, ny)) + new_x = x + dx + new_y = y + dy + + if self.torus: + new_x %= self.width + new_y %= self.height + + if not self.out_of_bounds((new_x, new_y)): + neighborhood[(new_x, new_y)] = True if not include_center: - neighborhood.remove(pos) + neighborhood.pop(pos, None) - self._neighborhood_cache[cache_key] = neighborhood + self._neighborhood_cache[cache_key] = list(neighborhood.keys()) - return neighborhood + return list(neighborhood.keys()) def iter_neighbors( self, From 1ee5cda4caa7ae64587a318e95e3e9522fa6580c Mon Sep 17 00:00:00 2001 From: Tom Pike Date: Wed, 16 Aug 2023 22:58:09 -0400 Subject: [PATCH 168/214] update README pic (#1763) --- docs/images/Mesa_Screenshot.png | Bin 56434 -> 72557 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/docs/images/Mesa_Screenshot.png b/docs/images/Mesa_Screenshot.png index 45be7ba206371191f5bb876214ec96da6d0483e5..5159d3501eb2fc2c4fc6792353d850bf631e74e8 100644 GIT binary patch literal 72557 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 literal 56434 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 akdb|}S-u1x^^b5|gyS*XuaE66B>xvulO&h` From 24a1df30a12a399cc54d8ff2d578db57e0f37a4a Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 17 Aug 2023 08:45:53 -0400 Subject: [PATCH 169/214] solara: Implement visualization for network grid (#1767) --- mesa/experimental/jupyter_viz.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index fffcda5ebca..7a49d7aa64f 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -1,11 +1,14 @@ import threading import matplotlib.pyplot as plt +import networkx as nx import reacton.ipywidgets as widgets import solara from matplotlib.figure import Figure from matplotlib.ticker import MaxNLocator +import mesa + # Avoid interactive backend plt.switch_backend("agg") @@ -91,10 +94,24 @@ def portray(self, g): return out +def _draw_network_grid(viz, space_ax): + graph = viz.model.grid.G + pos = nx.spring_layout(graph, seed=0) + nx.draw( + graph, + ax=space_ax, + pos=pos, + **viz.agent_portrayal(graph), + ) + + def make_space(viz): space_fig = Figure() space_ax = space_fig.subplots() - space_ax.scatter(**viz.portray(viz.model.grid)) + if isinstance(viz.model.grid, mesa.space.NetworkGrid): + _draw_network_grid(viz, space_ax) + else: + space_ax.scatter(**viz.portray(viz.model.grid)) space_ax.set_axis_off() solara.FigureMatplotlib(space_fig, dependencies=[viz.model, viz.df]) From cedbefb79c5d2c8de5894de698b25e8ee2bd1c0b Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Tue, 8 Aug 2023 11:37:35 -0400 Subject: [PATCH 170/214] Add support for switch input type --- mesa/experimental/jupyter_viz.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 7a49d7aa64f..c4265f5798e 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -150,6 +150,12 @@ def make_user_input(user_input, k, v): max=v.get("max"), step=v.get("step"), ) + elif v["type"] == "Select": + solara.Select( + v.get("label", "label"), + value=v.get("value"), + values=v.get("values"), + ) @solara.component From 83536419b1b3beac4b21dd7b4b19dc8155e4a05d Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 28 Aug 2023 03:39:01 -0400 Subject: [PATCH 171/214] space: Ensure get_neighborhood output & cache are immutable --- mesa/space.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index a9d3d569970..286f63c8fa3 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -117,7 +117,7 @@ def __init__(self, width: int, height: int, torus: bool) -> None: self._empties_built = False # Neighborhood Cache - self._neighborhood_cache: dict[Any, list[Coordinate]] = {} + self._neighborhood_cache: dict[Any, Sequence[Coordinate]] = {} # Cutoff used inside self.move_to_empty. The parameters are fitted on Python # 3.11 and it was verified that they are roughly the same for 3.10. Refer to @@ -233,7 +233,7 @@ def get_neighborhood( moore: bool, include_center: bool = False, radius: int = 1, - ) -> list[Coordinate]: + ) -> Sequence[Coordinate]: """Return a list of cells that are in the neighborhood of a certain point. @@ -307,9 +307,9 @@ def get_neighborhood( if not include_center: neighborhood.pop(pos, None) - self._neighborhood_cache[cache_key] = list(neighborhood.keys()) + self._neighborhood_cache[cache_key] = tuple(neighborhood.keys()) - return list(neighborhood.keys()) + return tuple(neighborhood.keys()) def iter_neighbors( self, @@ -681,7 +681,7 @@ def get_neighborhood( else: coordinates.discard(pos) - neighborhood = sorted(coordinates) + neighborhood = tuple(sorted(coordinates)) self._neighborhood_cache[cache_key] = neighborhood return neighborhood From 4085d6bd0595659f8c75746062d5d76901c82837 Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Tue, 8 Aug 2023 11:37:35 -0400 Subject: [PATCH 172/214] Add step count display to JupyterViz --- mesa/experimental/jupyter_viz.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index c4265f5798e..1d4f7491757 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -181,9 +181,14 @@ def make_model(): # 3. Buttons playing = solara.use_reactive(False) + # 4. Status indicators + #: current round step count + step = solara.use_reactive(False) + def on_value_play(change): if viz.model.running: viz.do_step() + step.value = viz.model.schedule.steps else: playing.value = False @@ -207,6 +212,12 @@ def on_value_play(change): playing=playing.value, on_playing=playing.set, ) + widgets.IntText( + value=step.value, + description="Step:", + disabled=True, + interval=play_interval, + ) # threaded_do_play is not used for now because it # doesn't work in Google colab. We use # ipywidgets.Play until it is fixed. The threading From a385d17b40caaacba8744881b4f676051e7f87f3 Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Fri, 25 Aug 2023 13:14:56 -0400 Subject: [PATCH 173/214] Use Solara markdown instead of int text to display step count --- mesa/experimental/jupyter_viz.py | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 1d4f7491757..94469c7609f 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -183,7 +183,7 @@ def make_model(): # 4. Status indicators #: current round step count - step = solara.use_reactive(False) + step = solara.use_reactive(True) def on_value_play(change): if viz.model.running: @@ -212,12 +212,7 @@ def on_value_play(change): playing=playing.value, on_playing=playing.set, ) - widgets.IntText( - value=step.value, - description="Step:", - disabled=True, - interval=play_interval, - ) + solara.Markdown(md_text="**Step:** %d" % step.value) # threaded_do_play is not used for now because it # doesn't work in Google colab. We use # ipywidgets.Play until it is fixed. The threading From 79bde5a259cfab4adbbfb2a9bd0bb4219af2713c Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Mon, 28 Aug 2023 12:25:20 -0400 Subject: [PATCH 174/214] Reference model step count directly for markdown output per @rht feedback --- mesa/experimental/jupyter_viz.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 94469c7609f..866f65c6117 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -181,14 +181,9 @@ def make_model(): # 3. Buttons playing = solara.use_reactive(False) - # 4. Status indicators - #: current round step count - step = solara.use_reactive(True) - def on_value_play(change): if viz.model.running: viz.do_step() - step.value = viz.model.schedule.steps else: playing.value = False @@ -212,7 +207,7 @@ def on_value_play(change): playing=playing.value, on_playing=playing.set, ) - solara.Markdown(md_text="**Step:** %d" % step.value) + solara.Markdown(md_text=f"**Step:** {viz.model.schedule.steps}") # threaded_do_play is not used for now because it # doesn't work in Google colab. We use # ipywidgets.Play until it is fixed. The threading From c9be99b1b4f519ec260d6aadbe7a2ebc55dee6f7 Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 31 Aug 2023 00:06:29 -0400 Subject: [PATCH 175/214] fix: Use .pytemplate for name for cookiecutter This avoids conda install problem. Fixes https://github.com/projectmesa/mesa/discussions/1774 --- mesa/cookiecutter-mesa/hooks/post_gen_project.py | 11 +++++++++++ .../{{cookiecutter.snake}}/{run.py => run.pytemplate} | 0 .../{setup.py => setup.pytemplate} | 0 .../{model.py => model.pytemplate} | 0 .../{server.py => server.pytemplate} | 0 5 files changed, 11 insertions(+) create mode 100644 mesa/cookiecutter-mesa/hooks/post_gen_project.py rename mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{run.py => run.pytemplate} (100%) rename mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{setup.py => setup.pytemplate} (100%) rename mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/{model.py => model.pytemplate} (100%) rename mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/{server.py => server.pytemplate} (100%) diff --git a/mesa/cookiecutter-mesa/hooks/post_gen_project.py b/mesa/cookiecutter-mesa/hooks/post_gen_project.py new file mode 100644 index 00000000000..d867f624675 --- /dev/null +++ b/mesa/cookiecutter-mesa/hooks/post_gen_project.py @@ -0,0 +1,11 @@ +import glob +import os + +file_list = glob.glob('**/*.pytemplate', recursive=True) + +for file_path in file_list: + # Check if the file is a regular file + if not os.path.isfile(file_path): + continue + # Rename the file + os.rename(file_path, file_path.replace(".pytemplate", ".py")) diff --git a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.py b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate similarity index 100% rename from mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.py rename to mesa/cookiecutter-mesa/{{cookiecutter.snake}}/run.pytemplate diff --git a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate similarity index 100% rename from mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.py rename to mesa/cookiecutter-mesa/{{cookiecutter.snake}}/setup.pytemplate diff --git a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.py b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate similarity index 100% rename from mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.py rename to mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/model.pytemplate diff --git a/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.py b/mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate similarity index 100% rename from mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.py rename to mesa/cookiecutter-mesa/{{cookiecutter.snake}}/{{cookiecutter.snake}}/server.pytemplate From ea4b21352f3a88777f765fa467b90663dda139e6 Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 31 Aug 2023 04:48:18 -0400 Subject: [PATCH 176/214] Fix readthedocs deprecation See https://blog.readthedocs.com/drop-support-system-packages/ --- .readthedocs.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.readthedocs.yml b/.readthedocs.yml index c0dbad73c99..02b1d4649a5 100644 --- a/.readthedocs.yml +++ b/.readthedocs.yml @@ -24,4 +24,3 @@ python: path: . extra_requirements: - docs - system_packages: true From fb81c1ae9200a849e570ab2c535d8b6e75a9c89d Mon Sep 17 00:00:00 2001 From: Corvince Date: Fri, 1 Sep 2023 09:49:23 +0200 Subject: [PATCH 177/214] Simplify solara code (#1786) * Simplify solara code * re-introduced backend switch and removed self --- mesa/experimental/jupyter_viz.py | 348 +++++++++++++++---------------- 1 file changed, 166 insertions(+), 182 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 866f65c6117..c048be35d14 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -13,59 +13,162 @@ plt.switch_backend("agg") -class JupyterContainer: - def __init__( - self, - model_class, - model_params, - measures=None, - name="Mesa Model", - agent_portrayal=None, - ): - self.model_class = model_class - self.split_model_params(model_params) - self.measures = measures - self.name = name - self.agent_portrayal = agent_portrayal - self.thread = None - - def split_model_params(self, model_params): - self.model_params_input = {} - self.model_params_fixed = {} - for k, v in model_params.items(): - if self.check_param_is_fixed(v): - self.model_params_fixed[k] = v +@solara.component +def JupyterViz( + model_class, + model_params, + measures=None, + name="Mesa Model", + agent_portrayal=None, + space_drawer=None, + play_interval=400, +): + current_step, set_current_step = solara.use_state(0) + + solara.Markdown(name) + + # 0. Split model params + model_params_input, model_params_fixed = split_model_params(model_params) + + # 1. User inputs + user_inputs = {} + for k, v in model_params_input.items(): + user_input = solara.use_reactive(v["value"]) + user_inputs[k] = user_input.value + make_user_input(user_input, k, v) + + # 2. Model + def make_model(): + return model_class(**user_inputs, **model_params_fixed) + + model = solara.use_memo(make_model, dependencies=list(user_inputs.values())) + + # 3. Buttons + ModelController(model, play_interval, current_step, set_current_step) + + with solara.GridFixed(columns=2): + # 4. Space + if space_drawer is None: + make_space(model, agent_portrayal) + else: + space_drawer(model, agent_portrayal) + # 5. Plots + for measure in measures: + if callable(measure): + # Is a custom object + measure(model) else: - self.model_params_input[k] = v + make_plot(model, measure) + - def check_param_is_fixed(self, param): - if not isinstance(param, dict): - return True - if "type" not in param: - return True +@solara.component +def ModelController(model, play_interval, current_step, set_current_step): + playing = solara.use_reactive(False) + thread = solara.use_reactive(None) + + def on_value_play(change): + if model.running: + do_step() + else: + playing.value = False - def do_step(self): - self.model.step() - self.set_df(self.model.datacollector.get_model_vars_dataframe()) + def do_step(): + model.step() + set_current_step(model.schedule.steps) - def do_play(self): - self.model.running = True - while self.model.running: - self.do_step() + def do_play(): + model.running = True + while model.running: + do_step() - def threaded_do_play(self): - if self.thread is not None and self.thread.is_alive(): + def threaded_do_play(): + if thread is not None and thread.is_alive(): return - self.thread = threading.Thread(target=self.do_play) - self.thread.start() + thread.value = threading.Thread(target=do_play) + thread.start() - def do_pause(self): - if (self.thread is None) or (not self.thread.is_alive()): + def do_pause(): + if (thread is None) or (not thread.is_alive()): return - self.model.running = False - self.thread.join() + model.running = False + thread.join() - def portray(self, g): + with solara.Row(): + solara.Button(label="Step", color="primary", on_click=do_step) + # This style is necessary so that the play widget has almost the same + # height as typical Solara buttons. + solara.Style( + """ + .widget-play { + height: 30px; + } + """ + ) + widgets.Play( + value=0, + interval=play_interval, + repeat=True, + show_repeat=False, + on_value=on_value_play, + playing=playing.value, + on_playing=playing.set, + ) + solara.Markdown(md_text=f"**Step:** {current_step}") + # threaded_do_play is not used for now because it + # doesn't work in Google colab. We use + # ipywidgets.Play until it is fixed. The threading + # version is definite a much better implementation, + # if it works. + # solara.Button(label="▶", color="primary", on_click=viz.threaded_do_play) + # solara.Button(label="⏸︎", color="primary", on_click=viz.do_pause) + # solara.Button(label="Reset", color="primary", on_click=do_reset) + + +def split_model_params(model_params): + model_params_input = {} + model_params_fixed = {} + for k, v in model_params.items(): + if check_param_is_fixed(v): + model_params_fixed[k] = v + else: + model_params_input[k] = v + return model_params_input, model_params_fixed + + +def check_param_is_fixed(param): + if not isinstance(param, dict): + return True + if "type" not in param: + return True + + +def make_user_input(user_input, k, v): + if v["type"] == "SliderInt": + solara.SliderInt( + v.get("label", "label"), + value=user_input, + min=v.get("min"), + max=v.get("max"), + step=v.get("step"), + ) + elif v["type"] == "SliderFloat": + solara.SliderFloat( + v.get("label", "label"), + value=user_input, + min=v.get("min"), + max=v.get("max"), + step=v.get("step"), + ) + elif v["type"] == "Select": + solara.Select( + v.get("label", "label"), + value=v.get("value"), + values=v.get("values"), + ) + + +def make_space(model, agent_portrayal): + def portray(g): x = [] y = [] s = [] # size @@ -79,7 +182,7 @@ def portray(self, g): # Is a single grid content = [content] for agent in content: - data = self.agent_portrayal(agent) + data = agent_portrayal(agent) x.append(i) y.append(j) if "size" in data: @@ -93,159 +196,40 @@ def portray(self, g): out["c"] = c return out + space_fig = Figure() + space_ax = space_fig.subplots() + if isinstance(model.grid, mesa.space.NetworkGrid): + _draw_network_grid(model, space_ax, agent_portrayal) + else: + space_ax.scatter(**portray(model.grid)) + space_ax.set_axis_off() + solara.FigureMatplotlib(space_fig) + -def _draw_network_grid(viz, space_ax): - graph = viz.model.grid.G +def _draw_network_grid(model, space_ax, agent_portrayal): + graph = model.grid.G pos = nx.spring_layout(graph, seed=0) nx.draw( graph, ax=space_ax, pos=pos, - **viz.agent_portrayal(graph), + **agent_portrayal(graph), ) -def make_space(viz): - space_fig = Figure() - space_ax = space_fig.subplots() - if isinstance(viz.model.grid, mesa.space.NetworkGrid): - _draw_network_grid(viz, space_ax) - else: - space_ax.scatter(**viz.portray(viz.model.grid)) - space_ax.set_axis_off() - solara.FigureMatplotlib(space_fig, dependencies=[viz.model, viz.df]) - - -def make_plot(viz, measure): +def make_plot(model, measure): fig = Figure() ax = fig.subplots() - ax.plot(viz.df.loc[:, measure]) + df = model.datacollector.get_model_vars_dataframe() + ax.plot(df.loc[:, measure]) ax.set_ylabel(measure) # Set integer x axis ax.xaxis.set_major_locator(MaxNLocator(integer=True)) - solara.FigureMatplotlib(fig, dependencies=[viz.model, viz.df]) + solara.FigureMatplotlib(fig) def make_text(renderer): - def function(viz): - solara.Markdown(renderer(viz.model)) + def function(model): + solara.Markdown(renderer(model)) return function - - -def make_user_input(user_input, k, v): - if v["type"] == "SliderInt": - solara.SliderInt( - v.get("label", "label"), - value=user_input, - min=v.get("min"), - max=v.get("max"), - step=v.get("step"), - ) - elif v["type"] == "SliderFloat": - solara.SliderFloat( - v.get("label", "label"), - value=user_input, - min=v.get("min"), - max=v.get("max"), - step=v.get("step"), - ) - elif v["type"] == "Select": - solara.Select( - v.get("label", "label"), - value=v.get("value"), - values=v.get("values"), - ) - - -@solara.component -def MesaComponent(viz, space_drawer=None, play_interval=400): - solara.Markdown(viz.name) - - # 1. User inputs - user_inputs = {} - for k, v in viz.model_params_input.items(): - user_input = solara.use_reactive(v["value"]) - user_inputs[k] = user_input.value - make_user_input(user_input, k, v) - - # 2. Model - def make_model(): - return viz.model_class(**user_inputs, **viz.model_params_fixed) - - viz.model = solara.use_memo(make_model, dependencies=list(user_inputs.values())) - viz.df, viz.set_df = solara.use_state( - viz.model.datacollector.get_model_vars_dataframe() - ) - - # 3. Buttons - playing = solara.use_reactive(False) - - def on_value_play(change): - if viz.model.running: - viz.do_step() - else: - playing.value = False - - with solara.Row(): - solara.Button(label="Step", color="primary", on_click=viz.do_step) - # This style is necessary so that the play widget has almost the same - # height as typical Solara buttons. - solara.Style( - """ - .widget-play { - height: 30px; - } - """ - ) - widgets.Play( - value=0, - interval=play_interval, - repeat=True, - show_repeat=False, - on_value=on_value_play, - playing=playing.value, - on_playing=playing.set, - ) - solara.Markdown(md_text=f"**Step:** {viz.model.schedule.steps}") - # threaded_do_play is not used for now because it - # doesn't work in Google colab. We use - # ipywidgets.Play until it is fixed. The threading - # version is definite a much better implementation, - # if it works. - # solara.Button(label="▶", color="primary", on_click=viz.threaded_do_play) - # solara.Button(label="⏸︎", color="primary", on_click=viz.do_pause) - # solara.Button(label="Reset", color="primary", on_click=do_reset) - - with solara.GridFixed(columns=2): - # 4. Space - if space_drawer is None: - make_space(viz) - else: - space_drawer(viz) - # 5. Plots - for measure in viz.measures: - if callable(measure): - # Is a custom object - measure(viz) - else: - make_plot(viz, measure) - - -# JupyterViz has to be a Solara component, so that each browser tabs runs in -# their own, separate simulation thread. See https://github.com/projectmesa/mesa/issues/856. -@solara.component -def JupyterViz( - model_class, - model_params, - measures=None, - name="Mesa Model", - agent_portrayal=None, - space_drawer=None, - play_interval=400, -): - return MesaComponent( - JupyterContainer(model_class, model_params, measures, name, agent_portrayal), - space_drawer=space_drawer, - play_interval=play_interval, - ) From 47637a7edd391678a4102aba573bc2160c8b7979 Mon Sep 17 00:00:00 2001 From: Rebecca Sutton Koeser Date: Fri, 1 Sep 2023 11:16:15 -0400 Subject: [PATCH 178/214] Add docstring for jupyterviz make_user_input that documents supported inputs (#1784) * Add docstring for make_user_input that documents supported inputs * Use field name as user input fallback label; error on unsupported type * Preliminary unit tests for jupyter viz make_user_input method * Remove unused variable flagged by ruff lint --- mesa/experimental/jupyter_viz.py | 53 ++++++++++++++++++++------------ tests/test_jupyter_viz.py | 48 +++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+), 19 deletions(-) create mode 100644 tests/test_jupyter_viz.py diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index c048be35d14..6007b9a8c8f 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -32,10 +32,10 @@ def JupyterViz( # 1. User inputs user_inputs = {} - for k, v in model_params_input.items(): - user_input = solara.use_reactive(v["value"]) - user_inputs[k] = user_input.value - make_user_input(user_input, k, v) + for name, options in model_params_input.items(): + user_input = solara.use_reactive(options["value"]) + user_inputs[name] = user_input.value + make_user_input(user_input, name, options) # 2. Model def make_model(): @@ -142,29 +142,44 @@ def check_param_is_fixed(param): return True -def make_user_input(user_input, k, v): - if v["type"] == "SliderInt": +def make_user_input(user_input, name, options): + """Initialize a user input for configurable model parameters. + Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`, + and :class:`solara.Select`. + + Args: + user_input: :class:`solara.reactive` object with initial value + name: field name; used as fallback for label if 'label' is not in options + options: dictionary with options for the input, including label, + min and max values, and other fields specific to the input type. + """ + # label for the input is "label" from options or name + label = options.get("label", name) + input_type = options.get("type") + if input_type == "SliderInt": solara.SliderInt( - v.get("label", "label"), + label, value=user_input, - min=v.get("min"), - max=v.get("max"), - step=v.get("step"), + min=options.get("min"), + max=options.get("max"), + step=options.get("step"), ) - elif v["type"] == "SliderFloat": + elif input_type == "SliderFloat": solara.SliderFloat( - v.get("label", "label"), + label, value=user_input, - min=v.get("min"), - max=v.get("max"), - step=v.get("step"), + min=options.get("min"), + max=options.get("max"), + step=options.get("step"), ) - elif v["type"] == "Select": + elif input_type == "Select": solara.Select( - v.get("label", "label"), - value=v.get("value"), - values=v.get("values"), + label, + value=options.get("value"), + values=options.get("values"), ) + else: + raise ValueError(f"{input_type} is not a supported input type") def make_space(model, agent_portrayal): diff --git a/tests/test_jupyter_viz.py b/tests/test_jupyter_viz.py new file mode 100644 index 00000000000..b2e701df7fd --- /dev/null +++ b/tests/test_jupyter_viz.py @@ -0,0 +1,48 @@ +import unittest +from unittest.mock import patch + +from mesa.experimental.jupyter_viz import make_user_input + + +class TestMakeUserInput(unittest.TestCase): + def test_unsupported_type(self): + """unsupported input type should raise ValueError""" + # bogus type + with self.assertRaisesRegex(ValueError, "not a supported input type"): + make_user_input(10, "input", {"type": "bogus"}) + # no type is specified + with self.assertRaisesRegex(ValueError, "not a supported input type"): + make_user_input(10, "input", {}) + + @patch("mesa.experimental.jupyter_viz.solara") + def test_slider_int(self, mock_solara): + value = 10 + name = "num_agents" + options = { + "type": "SliderInt", + "label": "number of agents", + "min": 10, + "max": 20, + "step": 1, + } + make_user_input(value, name, options) + mock_solara.SliderInt.assert_called_with( + options["label"], + value=value, + min=options["min"], + max=options["max"], + step=options["step"], + ) + + @patch("mesa.experimental.jupyter_viz.solara") + def test_label_fallback(self, mock_solara): + """name should be used as fallback label""" + value = 10 + name = "num_agents" + options = { + "type": "SliderInt", + } + make_user_input(value, name, options) + mock_solara.SliderInt.assert_called_with( + name, value=value, min=None, max=None, step=None + ) From 494c917df84382fcb35fa77724cf8375b93bd359 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 4 Sep 2023 22:41:31 +0000 Subject: [PATCH 179/214] build(deps): bump actions/checkout from 3 to 4 Bumps [actions/checkout](https://github.com/actions/checkout) from 3 to 4. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v3...v4) --- updated-dependencies: - dependency-name: actions/checkout dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/build_lint.yml | 6 +++--- .github/workflows/codespell.yml | 2 +- .github/workflows/release.yml | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 898a37decf3..9e89ad99b9c 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -43,7 +43,7 @@ jobs: # python-version: 'pypy-3.8' steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v4 with: @@ -63,7 +63,7 @@ jobs: lint-ruff: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.10 uses: actions/setup-python@v4 with: @@ -77,7 +77,7 @@ jobs: lint-black: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Set up Python 3.10 uses: actions/setup-python@v4 with: diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index 7fc3f4f0256..119fe8904e6 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -11,7 +11,7 @@ jobs: codespell: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - uses: codespell-project/actions-codespell@master with: ignore_words_file: .codespellignore diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f310da23ae6..a16e39209af 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout source - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Python uses: actions/setup-python@v4 with: From 9572e651e6764051f85ce3dfbe1e580f63538a37 Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Tue, 5 Sep 2023 14:11:32 -0400 Subject: [PATCH 180/214] Revise, test, & document JupyterViz options for drawing agent space Correct linter rerors flagged by ruff check --- mesa/experimental/jupyter_viz.py | 24 ++++++++++++-- tests/test_jupyter_viz.py | 57 ++++++++++++++++++++++++++++++-- 2 files changed, 76 insertions(+), 5 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 6007b9a8c8f..93b0d85207a 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -20,9 +20,23 @@ def JupyterViz( measures=None, name="Mesa Model", agent_portrayal=None, - space_drawer=None, + space_drawer="default", play_interval=400, ): + """Initialize a component to visualize a model. + Args: + model_class: class of the model to instantiate + model_params: parameters for initializing the model + measures: list of callables or data attributes to plot + name: name for display + agent_portrayal: options for rendering agents (dictionary) + space_drawer: method to render the agent space for + the model; default implementation is :meth:`make_space`; + simulations with no space to visualize should + specify `space_drawer=False` + play_interval: play interval (default: 400) + """ + current_step, set_current_step = solara.use_state(0) solara.Markdown(name) @@ -48,10 +62,14 @@ def make_model(): with solara.GridFixed(columns=2): # 4. Space - if space_drawer is None: + if space_drawer == "default": + # draw with the default implementation make_space(model, agent_portrayal) - else: + elif space_drawer: + # if specified, draw agent space with an alternate renderer space_drawer(model, agent_portrayal) + # otherwise, do nothing (do not draw space) + # 5. Plots for measure in measures: if callable(measure): diff --git a/tests/test_jupyter_viz.py b/tests/test_jupyter_viz.py index b2e701df7fd..4e26d569981 100644 --- a/tests/test_jupyter_viz.py +++ b/tests/test_jupyter_viz.py @@ -1,7 +1,9 @@ import unittest -from unittest.mock import patch +from unittest.mock import Mock, patch -from mesa.experimental.jupyter_viz import make_user_input +import solara + +from mesa.experimental.jupyter_viz import JupyterViz, make_user_input class TestMakeUserInput(unittest.TestCase): @@ -46,3 +48,54 @@ def test_label_fallback(self, mock_solara): mock_solara.SliderInt.assert_called_with( name, value=value, min=None, max=None, step=None ) + + +class TestJupyterViz(unittest.TestCase): + @patch("mesa.experimental.jupyter_viz.make_space") + def test_call_space_drawer(self, mock_make_space): + mock_model_class = Mock() + agent_portrayal = { + "Shape": "circle", + "color": "gray", + } + # initialize with space drawer unspecified (use default) + # component must be rendered for code to run + solara.render( + JupyterViz( + model_class=mock_model_class, + model_params={}, + agent_portrayal=agent_portrayal, + ) + ) + # should call default method with class instance and agent portrayal + mock_make_space.assert_called_with( + mock_model_class.return_value, agent_portrayal + ) + + # specify no space should be drawn; any false value should work + for falsy_value in [None, False, 0]: + mock_make_space.reset_mock() + solara.render( + JupyterViz( + model_class=mock_model_class, + model_params={}, + agent_portrayal=agent_portrayal, + space_drawer=falsy_value, + ) + ) + # should call default method with class instance and agent portrayal + assert mock_make_space.call_count == 0 + + # specify a custom space method + altspace_drawer = Mock() + solara.render( + JupyterViz( + model_class=mock_model_class, + model_params={}, + agent_portrayal=agent_portrayal, + space_drawer=altspace_drawer, + ) + ) + altspace_drawer.assert_called_with( + mock_model_class.return_value, agent_portrayal + ) From d451ef0f6dda27c4b8f10e785cba88c7c8638ccc Mon Sep 17 00:00:00 2001 From: Corvince Date: Thu, 7 Sep 2023 14:51:56 +0200 Subject: [PATCH 181/214] Add UserInputs component (#1788) * Add UserInputs component * update tests * remove global state dependency from UserInputs * Simplify UserInputs change handler --- mesa/experimental/jupyter_viz.py | 106 +++++++++++++++++-------------- tests/test_jupyter_viz.py | 64 ++++++++++++------- 2 files changed, 99 insertions(+), 71 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 93b0d85207a..fd2d045e53d 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -39,25 +39,26 @@ def JupyterViz( current_step, set_current_step = solara.use_state(0) - solara.Markdown(name) - - # 0. Split model params - model_params_input, model_params_fixed = split_model_params(model_params) - - # 1. User inputs - user_inputs = {} - for name, options in model_params_input.items(): - user_input = solara.use_reactive(options["value"]) - user_inputs[name] = user_input.value - make_user_input(user_input, name, options) + # 1. Set up model parameters + user_params, fixed_params = split_model_params(model_params) + model_parameters, set_model_parameters = solara.use_state( + fixed_params | {k: v["value"] for k, v in user_params.items()} + ) - # 2. Model + # 2. Set up Model def make_model(): - return model_class(**user_inputs, **model_params_fixed) + model = model_class(**model_parameters) + set_current_step(0) + return model - model = solara.use_memo(make_model, dependencies=list(user_inputs.values())) + model = solara.use_memo(make_model, dependencies=list(model_parameters.values())) - # 3. Buttons + def handle_change_model_params(name: str, value: any): + set_model_parameters(model_parameters | {name: value}) + + # 3. Set up UI + solara.Markdown(name) + UserInputs(user_params, on_change=handle_change_model_params) ModelController(model, play_interval, current_step, set_current_step) with solara.GridFixed(columns=2): @@ -160,44 +161,53 @@ def check_param_is_fixed(param): return True -def make_user_input(user_input, name, options): - """Initialize a user input for configurable model parameters. +@solara.component +def UserInputs(user_params, on_change=None): + """Initialize user inputs for configurable model parameters. Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`, and :class:`solara.Select`. - Args: - user_input: :class:`solara.reactive` object with initial value - name: field name; used as fallback for label if 'label' is not in options - options: dictionary with options for the input, including label, + Props: + user_params: dictionary with options for the input, including label, min and max values, and other fields specific to the input type. + on_change: function to be called with (name, value) when the value of an input changes. """ - # label for the input is "label" from options or name - label = options.get("label", name) - input_type = options.get("type") - if input_type == "SliderInt": - solara.SliderInt( - label, - value=user_input, - min=options.get("min"), - max=options.get("max"), - step=options.get("step"), - ) - elif input_type == "SliderFloat": - solara.SliderFloat( - label, - value=user_input, - min=options.get("min"), - max=options.get("max"), - step=options.get("step"), - ) - elif input_type == "Select": - solara.Select( - label, - value=options.get("value"), - values=options.get("values"), - ) - else: - raise ValueError(f"{input_type} is not a supported input type") + + for name, options in user_params.items(): + # label for the input is "label" from options or name + label = options.get("label", name) + input_type = options.get("type") + + def change_handler(value, name=name): + on_change(name, value) + + if input_type == "SliderInt": + solara.SliderInt( + label, + value=options.get("value"), + on_value=change_handler, + min=options.get("min"), + max=options.get("max"), + step=options.get("step"), + ) + elif input_type == "SliderFloat": + solara.SliderFloat( + label, + value=options.get("value"), + on_value=change_handler, + min=options.get("min"), + max=options.get("max"), + step=options.get("step"), + ) + elif input_type == "Select": + solara.Select( + label, + value=options.get("value"), + on_value=change_handler, + values=options.get("values"), + ) + else: + raise ValueError(f"{input_type} is not a supported input type") def make_space(model, agent_portrayal): diff --git a/tests/test_jupyter_viz.py b/tests/test_jupyter_viz.py index 4e26d569981..f1d7371c40e 100644 --- a/tests/test_jupyter_viz.py +++ b/tests/test_jupyter_viz.py @@ -1,53 +1,71 @@ import unittest from unittest.mock import Mock, patch +import ipyvuetify as vw import solara -from mesa.experimental.jupyter_viz import JupyterViz, make_user_input +from mesa.experimental.jupyter_viz import JupyterViz, UserInputs class TestMakeUserInput(unittest.TestCase): def test_unsupported_type(self): + @solara.component + def Test(user_params): + UserInputs(user_params) + """unsupported input type should raise ValueError""" # bogus type with self.assertRaisesRegex(ValueError, "not a supported input type"): - make_user_input(10, "input", {"type": "bogus"}) + solara.render(Test({"mock": {"type": "bogus"}}), handle_error=False) + # no type is specified with self.assertRaisesRegex(ValueError, "not a supported input type"): - make_user_input(10, "input", {}) + solara.render(Test({"mock": {}}), handle_error=False) + + def test_slider_int(self): + @solara.component + def Test(user_params): + UserInputs(user_params) - @patch("mesa.experimental.jupyter_viz.solara") - def test_slider_int(self, mock_solara): - value = 10 - name = "num_agents" options = { "type": "SliderInt", + "value": 10, "label": "number of agents", "min": 10, "max": 20, "step": 1, } - make_user_input(value, name, options) - mock_solara.SliderInt.assert_called_with( - options["label"], - value=value, - min=options["min"], - max=options["max"], - step=options["step"], - ) + user_params = {"num_agents": options} + _, rc = solara.render(Test(user_params), handle_error=False) + slider_int = rc.find(vw.Slider).widget + + assert slider_int.v_model == options["value"] + assert slider_int.label == options["label"] + assert slider_int.min == options["min"] + assert slider_int.max == options["max"] + assert slider_int.step == options["step"] - @patch("mesa.experimental.jupyter_viz.solara") - def test_label_fallback(self, mock_solara): + def test_label_fallback(self): """name should be used as fallback label""" - value = 10 - name = "num_agents" + + @solara.component + def Test(user_params): + UserInputs(user_params) + options = { "type": "SliderInt", + "value": 10, } - make_user_input(value, name, options) - mock_solara.SliderInt.assert_called_with( - name, value=value, min=None, max=None, step=None - ) + + user_params = {"num_agents": options} + _, rc = solara.render(Test(user_params), handle_error=False) + slider_int = rc.find(vw.Slider).widget + + assert slider_int.v_model == options["value"] + assert slider_int.label == "num_agents" + assert slider_int.min is None + assert slider_int.max is None + assert slider_int.step is None class TestJupyterViz(unittest.TestCase): From 20dbf9e0643e11cff36a095d1123b5ec895b0d11 Mon Sep 17 00:00:00 2001 From: Corvince Date: Thu, 7 Sep 2023 21:30:09 +0200 Subject: [PATCH 182/214] Fix: Remove dict merge operator, python 3.8 compat --- mesa/experimental/jupyter_viz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index fd2d045e53d..43d2d58444d 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -42,7 +42,7 @@ def JupyterViz( # 1. Set up model parameters user_params, fixed_params = split_model_params(model_params) model_parameters, set_model_parameters = solara.use_state( - fixed_params | {k: v["value"] for k, v in user_params.items()} + {**fixed_params, **{k: v["value"] for k, v in user_params.items()}} ) # 2. Set up Model @@ -54,7 +54,7 @@ def make_model(): model = solara.use_memo(make_model, dependencies=list(model_parameters.values())) def handle_change_model_params(name: str, value: any): - set_model_parameters(model_parameters | {name: value}) + set_model_parameters({**model_parameters, name: value}) # 3. Set up UI solara.Markdown(name) From e97e829f151617543872639e6dc1794a0aba8686 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 10 Sep 2023 05:49:45 -0400 Subject: [PATCH 183/214] feat: Add reset button to JupyterViz --- mesa/experimental/jupyter_viz.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 43d2d58444d..a7eefb51624 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -51,7 +51,10 @@ def make_model(): set_current_step(0) return model - model = solara.use_memo(make_model, dependencies=list(model_parameters.values())) + reset_counter = solara.use_reactive(0) + model = solara.use_memo( + make_model, dependencies=[*list(model_parameters.values()), reset_counter.value] + ) def handle_change_model_params(name: str, value: any): set_model_parameters({**model_parameters, name: value}) @@ -59,7 +62,7 @@ def handle_change_model_params(name: str, value: any): # 3. Set up UI solara.Markdown(name) UserInputs(user_params, on_change=handle_change_model_params) - ModelController(model, play_interval, current_step, set_current_step) + ModelController(model, play_interval, current_step, set_current_step, reset_counter) with solara.GridFixed(columns=2): # 4. Space @@ -81,7 +84,9 @@ def handle_change_model_params(name: str, value: any): @solara.component -def ModelController(model, play_interval, current_step, set_current_step): +def ModelController( + model, play_interval, current_step, set_current_step, reset_counter +): playing = solara.use_reactive(False) thread = solara.use_reactive(None) @@ -112,6 +117,9 @@ def do_pause(): model.running = False thread.join() + def do_reset(): + reset_counter.value += 1 + with solara.Row(): solara.Button(label="Step", color="primary", on_click=do_step) # This style is necessary so that the play widget has almost the same @@ -132,6 +140,7 @@ def do_pause(): playing=playing.value, on_playing=playing.set, ) + solara.Button(label="Reset", color="primary", on_click=do_reset) solara.Markdown(md_text=f"**Step:** {current_step}") # threaded_do_play is not used for now because it # doesn't work in Google colab. We use From ccde77bd31b27a7d72152eb9cc41fbee16364d38 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 10 Sep 2023 06:00:23 -0400 Subject: [PATCH 184/214] Apply Black to cookiecutter code --- mesa/cookiecutter-mesa/hooks/post_gen_project.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/cookiecutter-mesa/hooks/post_gen_project.py b/mesa/cookiecutter-mesa/hooks/post_gen_project.py index d867f624675..24c615d517d 100644 --- a/mesa/cookiecutter-mesa/hooks/post_gen_project.py +++ b/mesa/cookiecutter-mesa/hooks/post_gen_project.py @@ -1,7 +1,7 @@ import glob import os -file_list = glob.glob('**/*.pytemplate', recursive=True) +file_list = glob.glob("**/*.pytemplate", recursive=True) for file_path in file_list: # Check if the file is a regular file From a90b29b5c23c10761e5eebe34f07209831be863a Mon Sep 17 00:00:00 2001 From: rlskoeser Date: Tue, 12 Sep 2023 15:28:12 -0400 Subject: [PATCH 185/214] Add support for solara.Checkbox user input --- mesa/experimental/jupyter_viz.py | 7 ++++++- tests/test_jupyter_viz.py | 13 +++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index a7eefb51624..f2b9522953e 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -174,7 +174,7 @@ def check_param_is_fixed(param): def UserInputs(user_params, on_change=None): """Initialize user inputs for configurable model parameters. Currently supports :class:`solara.SliderInt`, :class:`solara.SliderFloat`, - and :class:`solara.Select`. + :class:`solara.Select`, and :class:`solara.Checkbox`. Props: user_params: dictionary with options for the input, including label, @@ -215,6 +215,11 @@ def change_handler(value, name=name): on_value=change_handler, values=options.get("values"), ) + elif input_type == "Checkbox": + solara.Checkbox( + label=label, + value=options.get("value"), + ) else: raise ValueError(f"{input_type} is not a supported input type") diff --git a/tests/test_jupyter_viz.py b/tests/test_jupyter_viz.py index f1d7371c40e..9702125229f 100644 --- a/tests/test_jupyter_viz.py +++ b/tests/test_jupyter_viz.py @@ -45,6 +45,19 @@ def Test(user_params): assert slider_int.max == options["max"] assert slider_int.step == options["step"] + def test_checkbox(self): + @solara.component + def Test(user_params): + UserInputs(user_params) + + options = {"type": "Checkbox", "value": True, "label": "On"} + user_params = {"num_agents": options} + _, rc = solara.render(Test(user_params), handle_error=False) + checkbox = rc.find(vw.Checkbox).widget + + assert checkbox.v_model == options["value"] + assert checkbox.label == options["label"] + def test_label_fallback(self): """name should be used as fallback label""" From 9c9a02e25df7c83c866ae9a23416c5d32e76a48d Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 13 Sep 2023 00:28:48 -0400 Subject: [PATCH 186/214] viz tutorial: Update custom plot to reflect new code --- docs/tutorials/visualization_tutorial.ipynb | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/tutorials/visualization_tutorial.ipynb b/docs/tutorials/visualization_tutorial.ipynb index 37564241599..cead72ace93 100644 --- a/docs/tutorials/visualization_tutorial.ipynb +++ b/docs/tutorials/visualization_tutorial.ipynb @@ -198,18 +198,16 @@ "from matplotlib.figure import Figure\n", "\n", "\n", - "def make_histogram(viz):\n", + "def make_histogram(model):\n", " # Note: you must initialize a figure using this method instead of\n", " # plt.figure(), for thread safety purpose\n", " fig = Figure()\n", " ax = fig.subplots()\n", - " wealth_vals = [agent.wealth for agent in viz.model.schedule.agents]\n", + " wealth_vals = [agent.wealth for agent in model.schedule.agents]\n", " # Note: you have to use Matplotlib's OOP API instead of plt.hist\n", " # because plt.hist is not thread-safe.\n", " ax.hist(wealth_vals, bins=10)\n", - " # You have to specify the dependencies as follows, so that the figure\n", - " # auto-updates when viz.model or viz.df is changed.\n", - " solara.FigureMatplotlib(fig, dependencies=[viz.model, viz.df])" + " solara.FigureMatplotlib(fig)" ] }, { From 7f5a32e63db93b9be2b00a705d0c28a66bc5a9dd Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 10 Sep 2023 08:10:56 -0400 Subject: [PATCH 187/214] fix: Don't continue playing when a model is reset --- mesa/experimental/jupyter_viz.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index f2b9522953e..96970b8c56b 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -89,15 +89,25 @@ def ModelController( ): playing = solara.use_reactive(False) thread = solara.use_reactive(None) + # We track the previous step to detect if user resets the model via + # clicking the reset button or changing the parameters. If previous_step > + # current_step, it means a model reset happens while the simulation is + # still playing. + previous_step = solara.use_reactive(0) def on_value_play(change): - if model.running: + if previous_step.value > current_step and current_step == 0: + # We add extra checks for current_step == 0, just to be sure. + # We automatically stop the playing if a model is reset. + playing.value = False + elif model.running: do_step() else: playing.value = False def do_step(): model.step() + previous_step.value = current_step set_current_step(model.schedule.steps) def do_play(): From 9223c206409eb677ad6fc167ab2f915ca546dd05 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Mon, 18 Sep 2023 09:30:00 +0200 Subject: [PATCH 188/214] HISTORY.rst: Correct neighbor_iter() replacement in 2.0.0 The 2.0.0 release notes said incorrectly to replace neighbor_iter() with iter_neighborhood() instead of the correct iter_neighbors(). This commit fixes this. --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 929e0f5b705..5b6574ab174 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -47,7 +47,7 @@ Mesa 2.0 includes: * `find_empty()`: convert this to `move_to_empty()` * `num_agents`: removed parameter from `move_to_empty()` * `position_agent()`: convert this to `place_agent` - * `neighbor_iter()`: convert this to `iter_neighborhood()` + * `neighbor_iter()`: convert this to `iter_neighbors()` * batchrunner: remove deprecations #1627 * `class BatchRunner` and `class BatchRunnerMP`: convert these to `batch_run()` * Please see this `batch_run() example`_ if you would like to see an an implementation. From bc5a5cd949992fcaa750ab5c6a8fc1cdb3b0c968 Mon Sep 17 00:00:00 2001 From: Corvince Date: Mon, 18 Sep 2023 14:21:54 +0200 Subject: [PATCH 189/214] remove attrgetter performace optimization --- mesa/datacollection.py | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index d383f018e0f..dbd52793184 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -36,7 +36,6 @@ """ import itertools import types -from operator import attrgetter import pandas as pd @@ -138,7 +137,7 @@ def _new_agent_reporter(self, name, reporter): reporter: Attribute string, or function object that returns the variable when given a model instance. """ - if type(reporter) is str: + if isinstance(reporter, str): attribute_name = reporter def reporter(agent): @@ -175,17 +174,10 @@ def get_reports(agent): agent_records_without_none = (r for r in agent_records if r is not None) return agent_records_without_none - if all(hasattr(rep, "attribute_name") for rep in rep_funcs): - # This branch is for performance optimization purpose. - prefix = ["model.schedule.steps", "unique_id"] - attributes = [func.attribute_name for func in rep_funcs] - get_reports = attrgetter(*prefix + attributes) - else: - - def get_reports(agent): - _prefix = (agent.model.schedule.steps, agent.unique_id) - reports = tuple(rep(agent) for rep in rep_funcs) - return _prefix + reports + def get_reports(agent): + _prefix = (agent.model.schedule.steps, agent.unique_id) + reports = tuple(rep(agent) for rep in rep_funcs) + return _prefix + reports agent_records = map(get_reports, model.schedule.agents) return agent_records From 3dbabfe9bbc5609caedf0cdc4a0c5d4b54ec4345 Mon Sep 17 00:00:00 2001 From: Corvince Date: Tue, 19 Sep 2023 08:35:28 +0200 Subject: [PATCH 190/214] docs: Always link to stable version --- HISTORY.rst | 2 +- README.rst | 4 ++-- docs/tutorials/adv_tutorial_legacy.ipynb | 2 +- docs/tutorials/visualization_tutorial.ipynb | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 5b6574ab174..471e094743e 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -37,7 +37,7 @@ Mesa 2.0 includes: * an improved `datacollector` that allows collection by agent type * several breaking changes that provide significant improvements to Mesa. -.. _visualization tutorial: https://mesa.readthedocs.io/en/latest/tutorials/visualization_tutorial.html +.. _visualization tutorial: https://mesa.readthedocs.io/en/stable/tutorials/visualization_tutorial.html **Breaking Changes:** * space: change `coord_iter` to return `(content,(x,y))` instead of `(content, x,y)`; this reduces known errors of scheduler to grid mismatch #1566, #1723 diff --git a/README.rst b/README.rst index e4fffb3ef5d..67132bffcc8 100644 --- a/README.rst +++ b/README.rst @@ -65,10 +65,10 @@ For resources or help on using Mesa, check out the following: * `Discussions`_ (GitHub threaded discussions about Mesa) * `Matrix Chat`_ (Chat Forum via Matrix to talk about Mesa) -.. _`Intro to Mesa Tutorial` : http://mesa.readthedocs.org/en/main/tutorials/intro_tutorial.html +.. _`Intro to Mesa Tutorial` : http://mesa.readthedocs.org/en/stable/tutorials/intro_tutorial.html .. _`Complexity Explorer Tutorial` : https://www.complexityexplorer.org/courses/172-agent-based-models-with-python-an-introduction-to-mesa .. _`Mesa Examples` : https://github.com/projectmesa/mesa-examples/tree/main/examples -.. _`Docs` : http://mesa.readthedocs.org/en/main/ +.. _`Docs` : http://mesa.readthedocs.org/ .. _`Discussions` : https://github.com/projectmesa/mesa/discussions .. _`Matrix Chat` : https://matrix.to/#/#project-mesa:matrix.org diff --git a/docs/tutorials/adv_tutorial_legacy.ipynb b/docs/tutorials/adv_tutorial_legacy.ipynb index c05d5819c75..c0c26e422fb 100644 --- a/docs/tutorials/adv_tutorial_legacy.ipynb +++ b/docs/tutorials/adv_tutorial_legacy.ipynb @@ -29,7 +29,7 @@ "source": [ "#### Grid Visualization\n", "\n", - "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n", + "To start with, let's have a visualization where we can watch the agents moving around the grid. For this, you will need to put your model code in a separate Python source file. For now, let us use the `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/stable/tutorials/intro_tutorial.html) saved to `MoneyModel.py` file provided.\n", "Next, in a new source file (e.g. `MoneyModel_Viz.py`) include the code shown in the following cells to run and avoid Jupyter compatibility issue." ] }, diff --git a/docs/tutorials/visualization_tutorial.ipynb b/docs/tutorials/visualization_tutorial.ipynb index cead72ace93..f19b21421ef 100644 --- a/docs/tutorials/visualization_tutorial.ipynb +++ b/docs/tutorials/visualization_tutorial.ipynb @@ -28,7 +28,7 @@ "source": [ "#### Grid Visualization\n", "\n", - "To start with, let's have a visualization where we can watch the agents moving around the grid. Let us use the same `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/main/tutorials/intro_tutorial.html).\n" + "To start with, let's have a visualization where we can watch the agents moving around the grid. Let us use the same `MoneyModel` created in the [Introductory Tutorial](https://mesa.readthedocs.io/en/stable/tutorials/intro_tutorial.html).\n" ] }, { From 2be2f05ee6e5a68e0fcabd90f1f43c3ed22b4c21 Mon Sep 17 00:00:00 2001 From: rht Date: Mon, 7 Aug 2023 09:22:26 -0400 Subject: [PATCH 191/214] Docker: Update to use Solara viz --- Dockerfile | 40 +++++++++++++++++----------------------- README.rst | 8 ++++---- docker-compose.yml | 8 ++++++-- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/Dockerfile b/Dockerfile index 4d9fed52a1b..61348e690b9 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,25 @@ -FROM python:3.10-slim +# We can't use slim because we need either git/wget/curl to +# download mesa-examples, and so installing them requires +# updating the system anyway. +# We can't use alpine because NumPy doesn't support musllinux yet. +# But it's in the RC version https://github.com/numpy/numpy/issues/20089. +FROM python:bookworm LABEL maintainer="rht " # To use this Dockerfile: # 1. `docker build . -t mymesa_image` -# 2. `docker run --name mymesa_instance -p 8521:8521 -it mymesa_image` -# 3. In your browser, visit http://127.0.0.1:8521 +# 2. `docker run --name mymesa_instance -p 8765:8765 -it mymesa_image` +# 3. In your browser, visit http://127.0.0.1:8765 # -# Currently, this Dockerfile defaults to running the Wolf-Sheep model, as an +# Currently, this Dockerfile defaults to running the Schelling model, as an # illustration. If you want to run a different example, simply change the # MODEL_DIR variable below to point to another model, e.g. -# examples/sugarscape_cg or path to your custom model. +# /mesa-examples/examples/sugarscape_cg or path to your custom model. # You specify the MODEL_DIR (relative to this Git repo) by doing: -# `docker run --name mymesa_instance -p 8521:8521 -e MODEL_DIR=examples/sugarscape_cg -it mymesa_image` -# Note: the model directory MUST contain a run.py file. +# `docker run --name mymesa_instance -p 8765:8765 -e MODEL_DIR=/mesa-examples/examples/sugarscape_cg -it mymesa_image` +# Note: the model directory MUST contain an app.py file. -ENV MODEL_DIR=examples/wolf_sheep +ENV MODEL_DIR=/mesa-examples/examples/schelling_experimental # Don't buffer output: # https://docs.python.org/3.10/using/cmdline.html?highlight=pythonunbuffered#envvar-PYTHONUNBUFFERED @@ -24,21 +29,10 @@ WORKDIR /opt/mesa COPY . /opt/mesa -EXPOSE 8521/tcp - -# Important: we don't install python3-dev, python3-pip and so on because doing -# so will install Python 3.9 instead of the already available Python 3.10 from -# the base image. -# The following RUN command is still provided for context. -# RUN apt-get update && apt-get upgrade -y -o Dpkg::Options::="--force-confold" \ -# && apt-get install -y --no-install-recommends \ -# build-essential \ -# python3-dev \ -# python3-pip \ -# python3-setuptools \ -# python3-wheel \ -# && rm -rf /var/lib/apt/lists/* +RUN cd / && git clone https://github.com/projectmesa/mesa-examples.git + +EXPOSE 8765/tcp RUN pip3 install -e /opt/mesa -CMD ["sh", "-c", "cd $MODEL_DIR && python3 run.py"] +CMD ["sh", "-c", "cd $MODEL_DIR && solara run app.py --host=0.0.0.0"] diff --git a/README.rst b/README.rst index 67132bffcc8..b3278af6ecc 100644 --- a/README.rst +++ b/README.rst @@ -85,21 +85,21 @@ If you are a Mesa developer, first `install Docker Compose Date: Wed, 20 Sep 2023 05:20:10 -0400 Subject: [PATCH 192/214] Remove exclude_none_values This feature is functional only if the agent records are in the form of ``` {'satisfication': (('A0', 1), ('B0', None), ('A1', 1), ('B1', None), ('A2', 1), ('B2', None)), 'unique_id': (('A0', 'A0'), ('B0', 'B0'), ('A1', 'A1'), ('B1', 'B1'), ('A2', 'A2'), ('B2', 'B2'))} ``` instead of ``` ((1, 'A0', 1, 'A0'), (1, 'B0', None, 'B0'), (1, 'A1', 1, 'A1'), (1, 'B1', None, 'B1'), (1, 'A2', 1, 'A2'), (1, 'B2', None, 'B2')) ``` A more explicit solution instead of implicitly ignoring a group of agents just because of their attribute returns `None`, would be to add a filter to the data collector itself. i.e., instead of ```python agent_records = map(get_reports, model.schedule.agents) ``` we should do ```python agent_records = map(get_reports, filter(agent_filter, model.schedule.agents)) ``` Where `agent_filter` can be `lambda a: isinstance(a, Trader)` . And this, too, is only a few lines of code of change. --- mesa/datacollection.py | 18 ------------- mesa/model.py | 2 -- tests/test_datacollector.py | 50 ++----------------------------------- 3 files changed, 2 insertions(+), 68 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index dbd52793184..7f3dc847111 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -55,7 +55,6 @@ def __init__( model_reporters=None, agent_reporters=None, tables=None, - exclude_none_values=False, ): """Instantiate a DataCollector with lists of model and agent reporters. Both model_reporters and agent_reporters accept a dictionary mapping a @@ -79,8 +78,6 @@ def __init__( model_reporters: Dictionary of reporter names and attributes/funcs agent_reporters: Dictionary of reporter names and attributes/funcs. tables: Dictionary of table names to lists of column names. - exclude_none_values: Boolean of whether to drop records which values - are None, in the final result. Notes: If you want to pickle your model you must not use lambda functions. @@ -104,7 +101,6 @@ class attributes of a model self.model_vars = {} self._agent_records = {} self.tables = {} - self.exclude_none_values = exclude_none_values if model_reporters is not None: for name, reporter in model_reporters.items(): @@ -159,20 +155,6 @@ def _new_table(self, table_name, table_columns): def _record_agents(self, model): """Record agents data in a mapping of functions and agents.""" rep_funcs = self.agent_reporters.values() - if self.exclude_none_values: - # Drop records which values are None. - - def get_reports(agent): - _prefix = (agent.model.schedule.steps, agent.unique_id) - reports = (rep(agent) for rep in rep_funcs) - reports_without_none = tuple(r for r in reports if r is not None) - if len(reports_without_none) == 0: - return None - return _prefix + reports_without_none - - agent_records = (get_reports(agent) for agent in model.schedule.agents) - agent_records_without_none = (r for r in agent_records if r is not None) - return agent_records_without_none def get_reports(agent): _prefix = (agent.model.schedule.steps, agent.unique_id) diff --git a/mesa/model.py b/mesa/model.py index 15374f70796..12446b5df23 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -70,7 +70,6 @@ def initialize_data_collector( model_reporters=None, agent_reporters=None, tables=None, - exclude_none_values=False, ) -> None: if not hasattr(self, "schedule") or self.schedule is None: raise RuntimeError( @@ -84,7 +83,6 @@ def initialize_data_collector( model_reporters=model_reporters, agent_reporters=agent_reporters, tables=tables, - exclude_none_values=exclude_none_values, ) # Collect data for the first time during initialization. self.datacollector.collect(self) diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py index c44c708e31d..8cdee4401e5 100644 --- a/tests/test_datacollector.py +++ b/tests/test_datacollector.py @@ -47,20 +47,14 @@ class MockModel(Model): schedule = BaseScheduler(None) - def __init__(self, test_exclude_none_values=False): + def __init__(self): self.schedule = BaseScheduler(self) self.model_val = 100 self.n = 10 for i in range(self.n): self.schedule.add(MockAgent(i, self, val=i)) - if test_exclude_none_values: - self.schedule.add(DifferentMockAgent(self.n + i, self, val=i)) - if test_exclude_none_values: - # Only DifferentMockAgent has val3. - agent_reporters = {"value": lambda a: a.val, "value3": "val3"} - else: - agent_reporters = {"value": lambda a: a.val, "value2": "val2"} + agent_reporters = {"value": lambda a: a.val, "value2": "val2"} self.initialize_data_collector( { "total_agents": lambda m: m.schedule.get_agent_count(), @@ -71,7 +65,6 @@ def __init__(self, test_exclude_none_values=False): }, agent_reporters, {"Final_Values": ["agent_id", "final_value"]}, - exclude_none_values=test_exclude_none_values, ) def test_model_calc_comp(self, input1, input2): @@ -211,44 +204,5 @@ def test_initialize_before_agents_added_to_scheduler(self): ) -class TestDataCollectorExcludeNone(unittest.TestCase): - def setUp(self): - """ - Create the model and run it a set number of steps. - """ - self.model = MockModel(test_exclude_none_values=True) - for i in range(7): - if i == 4: - self.model.schedule.remove(self.model.schedule._agents[3]) - self.model.step() - - def test_agent_records(self): - """ - Test agent-level variable collection. - """ - data_collector = self.model.datacollector - agent_table = data_collector.get_agent_vars_dataframe() - - assert len(data_collector._agent_records) == 8 - for step, records in data_collector._agent_records.items(): - if step < 5: - assert len(records) == 20 - else: - assert len(records) == 19 - - for values in records: - agent_id = values[1] - if agent_id < self.model.n: - assert len(values) == 3 - else: - # Agents with agent_id >= self.model.n are - # DifferentMockAgent, which additionally contains val3. - assert len(values) == 4 - - assert "value" in list(agent_table.columns) - assert "value2" not in list(agent_table.columns) - assert "value3" in list(agent_table.columns) - - if __name__ == "__main__": unittest.main() From bd86cac0192eec6f705d9e44fe5f5072a097ef19 Mon Sep 17 00:00:00 2001 From: rht Date: Tue, 19 Sep 2023 05:04:18 -0400 Subject: [PATCH 193/214] Update version to 2.1.2 --- HISTORY.rst | 50 ++++++++++++++++++++++++++++++++++++++++++++++++ mesa/__init__.py | 2 +- 2 files changed, 51 insertions(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index 471e094743e..c40ec41f1ae 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,6 +3,56 @@ Release History --------------- +2.1.2 (2023-09-20) +++++++++++++++++++ + +This release contains fixes, and several improvements and new features to +the JupyterViz/Solara frontend. It's a patch release instead of a minor release +because the JupyterViz frontend is still considered experimental. + +**Improvements** + +* perf: Access grid only once #1751 +* docs: compile notebooks at build time #1753 +* docs: Remove nbsphinx and explicit .ipynb suffix #1754 +* rtd: Use gruvbox-dark as style #1719 +* build(deps): bump actions/checkout from 3 to 4 #1790 + +**Solara/JupyterViz** + +* solara: Implement visualization for network grid #1767 +* Add support for select input type #1779 +* Add step count display to JupyterViz #1775 +* Simplify solara code #1786 +* Add docstring for jupyterviz make_user_input that documents supported inputs #1784 +* Revise, test, & document JupyterViz options for drawing agent space #1783 +* Add UserInputs component #1788 +* Fix: Remove dict merge operator, python 3.8 compat #1793 +* feat: Add reset button to JupyterViz #1795 +* Add support for solara.Checkbox user input #1798 +* viz tutorial: Update custom plot to reflect new code #1799 +* fix: Don't continue playing when a model is reset #1796 +* Docker: Update to use Solara viz #1757 + +**Refactors** + +* Move viz stuff to mesa-viz-tornado Git repo #1746 +* simplify get neighborhood #1760 +* remove attrgetter performance optimization #1809 + +**Fixes** + +* fix: Add Matplotlib as dependency #1747 +* fix install for visualization tutorial in colab #1752 +* fix: Allow multiple connections in Solara #1759 +* Revert "Ensure sphinx>=7" #1762 +* fix README pic to remove line on left side #1763 +* space: Ensure get_neighborhood output & cache are immutable #1780 +* fix: Use .pytemplate for name for cookiecutter #1785 +* HISTORY.rst: Correct neighbor_iter() replacement in 2.0.0 #1807 +* docs: Always link to stable version #1810 +* Remove exclude_none_values #1813 + 2.1.1 (2023-08-02) +++++++++++++++++++ diff --git a/mesa/__init__.py b/mesa/__init__.py index 675bfabc40b..3cfaaff8709 100644 --- a/mesa/__init__.py +++ b/mesa/__init__.py @@ -25,7 +25,7 @@ ] __title__ = "mesa" -__version__ = "2.1.1" +__version__ = "2.1.2" __license__ = "Apache 2.0" _this_year = datetime.datetime.now(tz=datetime.timezone.utc).date().year __copyright__ = f"Copyright {_this_year} Project Mesa Team" From 3bb9d8e3020b0fc36784e1bc0e670329286dbc93 Mon Sep 17 00:00:00 2001 From: Tom Pike Date: Sat, 23 Sep 2023 09:46:43 -0400 Subject: [PATCH 194/214] Update setup.py remove mesa_viz_tornado git install --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 2c142681c16..1aa7860d237 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ "click", "cookiecutter", "matplotlib", - "mesa_viz_tornado @ git+https://github.com/rht/mesa-viz-tornado@a4d9242e90ef7f1fcd388fb5c6d72a4177a76bdd", + "mesa_viz_tornado, "networkx", "numpy", "pandas", From 578e1667de7da7cc9d4aee499ec14d1c6f70dd86 Mon Sep 17 00:00:00 2001 From: Tom Pike Date: Sat, 23 Sep 2023 12:00:12 -0400 Subject: [PATCH 195/214] Update HISTORY.rst --- HISTORY.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/HISTORY.rst b/HISTORY.rst index c40ec41f1ae..d0bfb96a0e5 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -3,7 +3,7 @@ Release History --------------- -2.1.2 (2023-09-20) +2.1.2 (2023-09-23) ++++++++++++++++++ This release contains fixes, and several improvements and new features to From 734496c641d0dcc5d05832d5c530b4badad08e4a Mon Sep 17 00:00:00 2001 From: Tom Pike Date: Sat, 23 Sep 2023 12:22:15 -0400 Subject: [PATCH 196/214] Update setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 1aa7860d237..3ab90619d9b 100644 --- a/setup.py +++ b/setup.py @@ -8,7 +8,7 @@ "click", "cookiecutter", "matplotlib", - "mesa_viz_tornado, + "mesa_viz_tornado", "networkx", "numpy", "pandas", From 4a3d75731b661b93a5ccdd54c4cf7a2347bf5eb1 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 23 Sep 2023 20:21:49 -0400 Subject: [PATCH 197/214] perf: Speed up Solara space render --- mesa/experimental/jupyter_viz.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 96970b8c56b..265bb9f44c2 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -270,7 +270,7 @@ def portray(g): else: space_ax.scatter(**portray(model.grid)) space_ax.set_axis_off() - solara.FigureMatplotlib(space_fig) + solara.FigureMatplotlib(space_fig, format="png") def _draw_network_grid(model, space_ax, agent_portrayal): From a75a353f1bf6e53ef3823ef267b0a694fac811ee Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 23 Sep 2023 20:28:42 -0400 Subject: [PATCH 198/214] Solara: Reduce default interval to 150 --- mesa/experimental/jupyter_viz.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 265bb9f44c2..d3bc9c0d676 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -21,7 +21,7 @@ def JupyterViz( name="Mesa Model", agent_portrayal=None, space_drawer="default", - play_interval=400, + play_interval=150, ): """Initialize a component to visualize a model. Args: @@ -34,7 +34,7 @@ def JupyterViz( the model; default implementation is :meth:`make_space`; simulations with no space to visualize should specify `space_drawer=False` - play_interval: play interval (default: 400) + play_interval: play interval (default: 150) """ current_step, set_current_step = solara.use_state(0) From 6a39efd15ecacf8f44cc21e75228eb4f723199b6 Mon Sep 17 00:00:00 2001 From: rht Date: Wed, 20 Sep 2023 09:29:53 -0400 Subject: [PATCH 199/214] model: Ensure the seed is initialized with current timestamp when it is None --- mesa/model.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mesa/model.py b/mesa/model.py index 12446b5df23..1ac0edd6460 100644 --- a/mesa/model.py +++ b/mesa/model.py @@ -21,7 +21,11 @@ class Model: def __new__(cls, *args: Any, **kwargs: Any) -> Any: """Create a new model object and instantiate its RNG automatically.""" obj = object.__new__(cls) - obj._seed = kwargs.get("seed", None) + obj._seed = kwargs.get("seed") + if obj._seed is None: + # We explicitly specify the seed here so that we know its value in + # advance. + obj._seed = random.random() # noqa: S311 obj.random = random.Random(obj._seed) return obj From b2f642fd1a986f3114ba6f6f6e9bc119c037062f Mon Sep 17 00:00:00 2001 From: Wang Boyu Date: Tue, 26 Sep 2023 10:03:39 -0400 Subject: [PATCH 200/214] update ruff version in setup.py --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3ab90619d9b..73e82f80da8 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ extras_require = { "dev": [ "black", - "ruff==0.0.254", + "ruff==0.0.275", "coverage", "pytest >= 4.6", "pytest-cov", From d888a8cab397286c8fb3b496f1af8e79bb1b844d Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 3 Oct 2023 11:35:14 +0000 Subject: [PATCH 201/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/psf/black: 23.7.0 → 23.9.1](https://github.com/psf/black/compare/23.7.0...23.9.1) - [github.com/asottile/pyupgrade: v3.10.1 → v3.14.0](https://github.com/asottile/pyupgrade/compare/v3.10.1...v3.14.0) --- .pre-commit-config.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 772620fc72d..f97b4c7298d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -4,12 +4,12 @@ ci: repos: - repo: https://github.com/psf/black - rev: 23.7.0 + rev: 23.9.1 hooks: - id: black-jupyter exclude: ^mesa/cookiecutter-mesa/ - repo: https://github.com/asottile/pyupgrade - rev: v3.10.1 + rev: v3.14.0 hooks: - id: pyupgrade args: [--py38-plus] From 3cbe56f27fc1f9461876ad7f08514701950636e9 Mon Sep 17 00:00:00 2001 From: maskarb Date: Sat, 7 Oct 2023 18:23:03 -0400 Subject: [PATCH 202/214] Fix issue #1831: check if positions values are tuples --- mesa/space.py | 7 +++---- tests/test_space.py | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/mesa/space.py b/mesa/space.py index 286f63c8fa3..6ed7c09697e 100644 --- a/mesa/space.py +++ b/mesa/space.py @@ -68,10 +68,9 @@ def accept_tuple_argument(wrapped_function: F) -> F: single-item list rather than forcing user to do it.""" def wrapper(grid_instance, positions) -> Any: - if isinstance(positions, tuple) and len(positions) == 2: - return wrapped_function(grid_instance, [positions]) - else: - return wrapped_function(grid_instance, positions) + if len(positions) == 2 and not isinstance(positions[0], tuple): + positions = [positions] + return wrapped_function(grid_instance, positions) return cast(F, wrapper) diff --git a/tests/test_space.py b/tests/test_space.py index 8691dc45f0a..f8f2cc9440c 100644 --- a/tests/test_space.py +++ b/tests/test_space.py @@ -327,6 +327,28 @@ def move_agent(self): assert self.space[initial_pos[0]][initial_pos[1]] is None assert self.space[final_pos[0]][final_pos[1]] == _agent + def test_iter_cell_list_contents(self): + """ + Test neighborhood retrieval + """ + cell_list_1 = list(self.space.iter_cell_list_contents(TEST_AGENTS_GRID[0])) + assert len(cell_list_1) == 1 + + cell_list_2 = list( + self.space.iter_cell_list_contents( + (TEST_AGENTS_GRID[0], TEST_AGENTS_GRID[1]) + ) + ) + assert len(cell_list_2) == 2 + + cell_list_3 = list(self.space.iter_cell_list_contents(tuple(TEST_AGENTS_GRID))) + assert len(cell_list_3) == 3 + + cell_list_4 = list( + self.space.iter_cell_list_contents((TEST_AGENTS_GRID[0], (0, 0))) + ) + assert len(cell_list_4) == 1 + class TestSingleNetworkGrid(unittest.TestCase): GRAPH_SIZE = 10 From 477778c2cc7dcaa4c70b735f8bdc1b788b1657f3 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 7 Oct 2023 04:36:25 -0400 Subject: [PATCH 203/214] solara: Implement drawer for continuous space --- mesa/experimental/jupyter_viz.py | 41 ++++++++++++++++++++++++++++---- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index d3bc9c0d676..87459dcfa30 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -265,16 +265,22 @@ def portray(g): space_fig = Figure() space_ax = space_fig.subplots() - if isinstance(model.grid, mesa.space.NetworkGrid): - _draw_network_grid(model, space_ax, agent_portrayal) + space = getattr(model, "grid", None) + if space is None: + # Sometimes the space is defined as model.space instead of model.grid + space = model.space + if isinstance(space, mesa.space.NetworkGrid): + _draw_network_grid(space, space_ax, agent_portrayal) + elif isinstance(space, mesa.space.ContinuousSpace): + _draw_continuous_space(space, space_ax, agent_portrayal) else: - space_ax.scatter(**portray(model.grid)) + space_ax.scatter(**portray(space)) space_ax.set_axis_off() solara.FigureMatplotlib(space_fig, format="png") -def _draw_network_grid(model, space_ax, agent_portrayal): - graph = model.grid.G +def _draw_network_grid(space, space_ax, agent_portrayal): + graph = space.G pos = nx.spring_layout(graph, seed=0) nx.draw( graph, @@ -284,6 +290,31 @@ def _draw_network_grid(model, space_ax, agent_portrayal): ) +def _draw_continuous_space(space, space_ax, agent_portrayal): + def portray(space): + x = [] + y = [] + s = [] # size + c = [] # color + for agent in space._agent_to_index: + data = agent_portrayal(agent) + _x, _y = agent.pos + x.append(_x) + y.append(_y) + if "size" in data: + s.append(data["size"]) + if "color" in data: + c.append(data["color"]) + out = {"x": x, "y": y} + if len(s) > 0: + out["s"] = s + if len(c) > 0: + out["c"] = c + return out + + space_ax.scatter(**portray(space)) + + def make_plot(model, measure): fig = Figure() ax = fig.subplots() From 44ab8cd8c364e3aae4ecd37af07d853b9af34239 Mon Sep 17 00:00:00 2001 From: rht Date: Sat, 7 Oct 2023 06:07:48 -0400 Subject: [PATCH 204/214] refactor: Move default grid drawer to separate function --- mesa/experimental/jupyter_viz.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 87459dcfa30..3408ba11c6a 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -235,6 +235,23 @@ def change_handler(value, name=name): def make_space(model, agent_portrayal): + space_fig = Figure() + space_ax = space_fig.subplots() + space = getattr(model, "grid", None) + if space is None: + # Sometimes the space is defined as model.space instead of model.grid + space = model.space + if isinstance(space, mesa.space.NetworkGrid): + _draw_network_grid(space, space_ax, agent_portrayal) + elif isinstance(space, mesa.space.ContinuousSpace): + _draw_continuous_space(space, space_ax, agent_portrayal) + else: + _draw_grid(space, space_ax, agent_portrayal) + space_ax.set_axis_off() + solara.FigureMatplotlib(space_fig, format="png") + + +def _draw_grid(space, space_ax, agent_portrayal): def portray(g): x = [] y = [] @@ -263,20 +280,7 @@ def portray(g): out["c"] = c return out - space_fig = Figure() - space_ax = space_fig.subplots() - space = getattr(model, "grid", None) - if space is None: - # Sometimes the space is defined as model.space instead of model.grid - space = model.space - if isinstance(space, mesa.space.NetworkGrid): - _draw_network_grid(space, space_ax, agent_portrayal) - elif isinstance(space, mesa.space.ContinuousSpace): - _draw_continuous_space(space, space_ax, agent_portrayal) - else: - space_ax.scatter(**portray(space)) - space_ax.set_axis_off() - solara.FigureMatplotlib(space_fig, format="png") + space_ax.scatter(**portray(space)) def _draw_network_grid(space, space_ax, agent_portrayal): From d40bc5b475f667f65efc3c6dd256f56b1fe3f724 Mon Sep 17 00:00:00 2001 From: rht Date: Sun, 15 Oct 2023 01:20:34 -0400 Subject: [PATCH 205/214] intro tutorial: Explain how to plot reporter of multiple agents This is a section taken from #1717. Co-authored-by: Ewout ter Hoeven --- docs/tutorials/intro_tutorial.ipynb | 58 ++++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 404ae8d25bf..1aa8d4d1c4b 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -1059,6 +1059,60 @@ "g.set(title=\"Wealth of agent 14 over time\");" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "You can also plot a reporter of multiple agents over time." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "agent_list = [3, 14, 25]\n", + "\n", + "# Get the wealth of multiple agents over time\n", + "multiple_agents_wealth = agent_wealth[\n", + " agent_wealth.index.get_level_values(\"AgentID\").isin(agent_list)\n", + "]\n", + "# Plot the wealth of multiple agents over time\n", + "g = sns.lineplot(data=multiple_agents_wealth, x=\"Step\", y=\"Wealth\", hue=\"AgentID\")\n", + "g.set(title=\"Wealth of agents 3, 14 and 25 over time\");" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We can also plot the average of all agents, with a 95% confidence interval for that average." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# Transform the data to a long format\n", + "agent_wealth_long = agent_wealth.T.unstack().reset_index()\n", + "agent_wealth_long.columns = [\"Step\", \"AgentID\", \"Variable\", \"Value\"]\n", + "agent_wealth_long.head(3)\n", + "\n", + "# Plot the average wealth over time\n", + "g = sns.lineplot(data=agent_wealth_long, x=\"Step\", y=\"Value\", errorbar=(\"ci\", 95))\n", + "g.set(title=\"Average wealth over time\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Which is exactly 1, as expected in this model, since each agent starts with one wealth unit, and each agent gives one wealth unit to another agent at each step." + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1369,7 +1423,7 @@ "metadata": { "anaconda-cloud": {}, "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1383,7 +1437,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.6" + "version": "3.10.12" }, "widgets": { "state": {}, From 7343401312f915c81a93566ef91c20957d2784c7 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Wed, 18 Oct 2023 16:24:29 +0200 Subject: [PATCH 206/214] DataCollector: Allow agent reporters to take class methods and functions with parameter lists Modify the `DataCollector` class to allow agent reporters to take methods of a class/instance and functions with parameters placed in a list (like model reporters), by extending the `_new_agent_reporter` method. This implementation starts by checking if the reporter is an attribute string. If so, it creates a function to retrieve the attribute from an agent. Next, it checks if the reporter is a list. If it is, this indicates that we have a function with parameters, so it wraps that function to pass those parameters when called. For any other type (like lambdas or methods), we assume they're directly suitable to be used as reporters. Now, with this modification, agent reporters in the `DataCollector` class can take: 1. Attribute strings 2. Function objects (like lambdas) 3. Methods of a class/instance 4. Functions with parameters placed in a list This approach ensures backward compatibility because the existing checks for attribute strings and function objects remain unchanged. The added functionality only extends the capabilities of the class without altering the existing behavior. --- mesa/datacollection.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index 7f3dc847111..be3b23905a2 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -125,22 +125,37 @@ def _new_model_reporter(self, name, reporter): self.model_reporters[name] = reporter self.model_vars[name] = [] - def _new_agent_reporter(self, name, reporter): - """Add a new agent-level reporter to collect. +def _new_agent_reporter(self, name, reporter): + """Add a new agent-level reporter to collect. + + Args: + name: Name of the agent-level variable to collect. + reporter: Attribute string, function object, method of a class/instance, or + function with parameters placed in a list that returns the + variable when given an agent instance. + """ + # Check if the reporter is an attribute string + if isinstance(reporter, str): + attribute_name = reporter - Args: - 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 isinstance(reporter, str): - attribute_name = reporter + def attr_reporter(agent): + return getattr(agent, attribute_name, None) + + reporter = attr_reporter + + # Check if the reporter is a function with arguments placed in a list + elif isinstance(reporter, list): + func, params = reporter[0], reporter[1] + + def func_with_params(agent): + return func(agent, *params) + + reporter = func_with_params - def reporter(agent): - return getattr(agent, attribute_name, None) + # For other types (like lambda functions, method of a class/instance), + # it's already suitable to be used as a reporter directly. - reporter.attribute_name = attribute_name - self.agent_reporters[name] = reporter + self.agent_reporters[name] = reporter def _new_table(self, table_name, table_columns): """Add a new table that objects can write to. From 28d7ec2e33a62a97d5068f024209c9ce813a90bf Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Wed, 18 Oct 2023 16:41:13 +0200 Subject: [PATCH 207/214] DataCollector: Add tests for class instance method and function lists --- tests/test_datacollector.py | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py index 8cdee4401e5..d2f2d36a2e6 100644 --- a/tests/test_datacollector.py +++ b/tests/test_datacollector.py @@ -24,6 +24,9 @@ def step(self): self.val += 1 self.val2 += 1 + def double_val(self): + return self.val * 2 + def write_final_values(self): """ Write the final value to the appropriate table. @@ -31,6 +34,8 @@ def write_final_values(self): row = {"agent_id": self.unique_id, "final_value": self.val} self.model.datacollector.add_table_row("Final_Values", row) +def agent_function_with_params(agent, multiplier, offset): + return (agent.val * multiplier) + offset class DifferentMockAgent(MockAgent): # We define a different MockAgent to test for attributes that are present @@ -56,17 +61,20 @@ def __init__(self): self.schedule.add(MockAgent(i, self, val=i)) agent_reporters = {"value": lambda a: a.val, "value2": "val2"} self.initialize_data_collector( - { + model_reporters={ "total_agents": lambda m: m.schedule.get_agent_count(), "model_value": "model_val", "model_calc": self.schedule.get_agent_count, "model_calc_comp": [self.test_model_calc_comp, [3, 4]], "model_calc_fail": [self.test_model_calc_comp, [12, 0]], }, - agent_reporters, - {"Final_Values": ["agent_id", "final_value"]}, + agent_reporters={ + "value": lambda a: a.val, + "value2": "val2", + "double_value": MockAgent.double_val, + "value_with_params": [agent_function_with_params, [2, 3]] + } ) - def test_model_calc_comp(self, input1, input2): if input2 > 0: return (self.model_val * input1) / input2 @@ -132,6 +140,19 @@ def test_agent_records(self): data_collector = self.model.datacollector agent_table = data_collector.get_agent_vars_dataframe() + assert "double_value" in list(agent_table.columns) + assert "value_with_params" in list(agent_table.columns) + + # Check the double_value column + for step, agent_id, value in agent_table["double_value"].items(): + expected_value = agent_id * 2 + self.assertEqual(value, expected_value) + + # Check the value_with_params column + for step, agent_id, value in agent_table["value_with_params"].items(): + expected_value = (agent_id * 2) + 3 + self.assertEqual(value, expected_value) + assert len(data_collector._agent_records) == 8 for step, records in data_collector._agent_records.items(): if step < 5: From f8ad79bd44e9a40c28111d143268bef26af14fdc Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Thu, 19 Oct 2023 14:22:39 +0200 Subject: [PATCH 208/214] datacollection: Fix _new_agent_reporter indentation _new_agent_reporter wasn't intended into the DataCollector class, so thus not seen as a method. --- mesa/datacollection.py | 48 +++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index be3b23905a2..4aa4533a698 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -125,37 +125,37 @@ def _new_model_reporter(self, name, reporter): self.model_reporters[name] = reporter self.model_vars[name] = [] -def _new_agent_reporter(self, name, reporter): - """Add a new agent-level reporter to collect. - - Args: - name: Name of the agent-level variable to collect. - reporter: Attribute string, function object, method of a class/instance, or - function with parameters placed in a list that returns the - variable when given an agent instance. - """ - # Check if the reporter is an attribute string - if isinstance(reporter, str): - attribute_name = reporter + def _new_agent_reporter(self, name, reporter): + """Add a new agent-level reporter to collect. + + Args: + name: Name of the agent-level variable to collect. + reporter: Attribute string, function object, method of a class/instance, or + function with parameters placed in a list that returns the + variable when given an agent instance. + """ + # Check if the reporter is an attribute string + if isinstance(reporter, str): + attribute_name = reporter - def attr_reporter(agent): - return getattr(agent, attribute_name, None) + def attr_reporter(agent): + return getattr(agent, attribute_name, None) - reporter = attr_reporter + reporter = attr_reporter - # Check if the reporter is a function with arguments placed in a list - elif isinstance(reporter, list): - func, params = reporter[0], reporter[1] + # Check if the reporter is a function with arguments placed in a list + elif isinstance(reporter, list): + func, params = reporter[0], reporter[1] - def func_with_params(agent): - return func(agent, *params) + def func_with_params(agent): + return func(agent, *params) - reporter = func_with_params + reporter = func_with_params - # For other types (like lambda functions, method of a class/instance), - # it's already suitable to be used as a reporter directly. + # For other types (like lambda functions, method of a class/instance), + # it's already suitable to be used as a reporter directly. - self.agent_reporters[name] = reporter + self.agent_reporters[name] = reporter def _new_table(self, table_name, table_columns): """Add a new table that objects can write to. From 17bc8dec5b4c3f6f2b36fda42100a42b1c466324 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Thu, 19 Oct 2023 14:26:02 +0200 Subject: [PATCH 209/214] Fix DataCollector tests for new agent reporter types - Move agent_reporters specification into initialize_data_collector() - Add back the tables argument (accidentally deleted in previous commit) - Use parentheses to parse step and agent_id from agent records dataframe, since those are the multi-index key - Update expected values for new agent reporter types - Update length values of new agent table and vars (both increase by 2 due to 2 new agent reporter columns) --- tests/test_datacollector.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/tests/test_datacollector.py b/tests/test_datacollector.py index d2f2d36a2e6..a45d8891971 100644 --- a/tests/test_datacollector.py +++ b/tests/test_datacollector.py @@ -34,9 +34,11 @@ def write_final_values(self): row = {"agent_id": self.unique_id, "final_value": self.val} self.model.datacollector.add_table_row("Final_Values", row) + def agent_function_with_params(agent, multiplier, offset): return (agent.val * multiplier) + offset + class DifferentMockAgent(MockAgent): # We define a different MockAgent to test for attributes that are present # only in 1 type of agent, but not the other. @@ -59,7 +61,6 @@ def __init__(self): self.n = 10 for i in range(self.n): self.schedule.add(MockAgent(i, self, val=i)) - agent_reporters = {"value": lambda a: a.val, "value2": "val2"} self.initialize_data_collector( model_reporters={ "total_agents": lambda m: m.schedule.get_agent_count(), @@ -72,9 +73,11 @@ def __init__(self): "value": lambda a: a.val, "value2": "val2", "double_value": MockAgent.double_val, - "value_with_params": [agent_function_with_params, [2, 3]] - } + "value_with_params": [agent_function_with_params, [2, 3]], + }, + tables={"Final_Values": ["agent_id", "final_value"]}, ) + def test_model_calc_comp(self, input1, input2): if input2 > 0: return (self.model_val * input1) / input2 @@ -144,13 +147,13 @@ def test_agent_records(self): assert "value_with_params" in list(agent_table.columns) # Check the double_value column - for step, agent_id, value in agent_table["double_value"].items(): - expected_value = agent_id * 2 + for (step, agent_id), value in agent_table["double_value"].items(): + expected_value = (step + agent_id) * 2 self.assertEqual(value, expected_value) # Check the value_with_params column - for step, agent_id, value in agent_table["value_with_params"].items(): - expected_value = (agent_id * 2) + 3 + for (step, agent_id), value in agent_table["value_with_params"].items(): + expected_value = ((step + agent_id) * 2) + 3 self.assertEqual(value, expected_value) assert len(data_collector._agent_records) == 8 @@ -161,7 +164,7 @@ def test_agent_records(self): assert len(records) == 9 for values in records: - assert len(values) == 4 + assert len(values) == 6 assert "value" in list(agent_table.columns) assert "value2" in list(agent_table.columns) @@ -196,7 +199,7 @@ def test_exports(self): agent_vars = data_collector.get_agent_vars_dataframe() table_df = data_collector.get_table_dataframe("Final_Values") assert model_vars.shape == (8, 5) - assert agent_vars.shape == (77, 2) + assert agent_vars.shape == (77, 4) assert table_df.shape == (9, 2) with self.assertRaises(Exception): From 6616cdae8f5c1cfded7609100a2a691137923cd7 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Thu, 19 Oct 2023 14:40:18 +0200 Subject: [PATCH 210/214] DataCollector: Update docs with new agent reporter syntax --- mesa/datacollection.py | 59 +++++++++++++++++++++++------------------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/mesa/datacollection.py b/mesa/datacollection.py index 4aa4533a698..8bddfa23da2 100644 --- a/mesa/datacollection.py +++ b/mesa/datacollection.py @@ -56,44 +56,49 @@ def __init__( agent_reporters=None, tables=None, ): - """Instantiate a DataCollector with lists of model and agent reporters. + """ + Instantiate a DataCollector with lists of model and agent reporters. Both model_reporters and agent_reporters accept a dictionary mapping a - variable name to either an attribute name, or a method. - For example, if there was only one model-level reporter for number of - agents, it might look like: - {"agent_count": lambda m: m.schedule.get_agent_count() } - If there was only one agent-level reporter (e.g. the agent's energy), - it might look like this: - {"energy": "energy"} - or like this: - {"energy": lambda a: a.energy} + variable name to either an attribute name, a function, a method of a class/instance, + or a function with parameters placed in a list. + + Model reporters can take four types of arguments: + 1. Lambda function: + {"agent_count": lambda m: m.schedule.get_agent_count()} + 2. Method of a class/instance: + {"agent_count": self.get_agent_count} # self here is a class instance + {"agent_count": Model.get_agent_count} # Model here is a class + 3. Class attributes of a model: + {"model_attribute": "model_attribute"} + 4. Functions with parameters that have been placed in a list: + {"Model_Function": [function, [param_1, param_2]]} + + Agent reporters can similarly take: + 1. Attribute name (string) referring to agent's attribute: + {"energy": "energy"} + 2. Lambda function: + {"energy": lambda a: a.energy} + 3. Method of an agent class/instance: + {"agent_action": self.do_action} # self here is an agent class instance + {"agent_action": Agent.do_action} # Agent here is a class + 4. Functions with parameters placed in a list: + {"Agent_Function": [function, [param_1, param_2]]} The tables arg accepts a dictionary mapping names of tables to lists of columns. For example, if we want to allow agents to write their age when they are destroyed (to keep track of lifespans), it might look like: - {"Lifespan": ["unique_id", "age"]} + {"Lifespan": ["unique_id", "age"]} Args: - model_reporters: Dictionary of reporter names and attributes/funcs - agent_reporters: Dictionary of reporter names and attributes/funcs. + model_reporters: Dictionary of reporter names and attributes/funcs/methods. + agent_reporters: Dictionary of reporter names and attributes/funcs/methods. tables: Dictionary of table names to lists of column names. Notes: - If you want to pickle your model you must not use lambda functions. - If your model includes a large number of agents, you should *only* - use attribute names for the agent reporter, it will be much faster. - - Model reporters can take four types of arguments: - lambda like above: - {"agent_count": lambda m: m.schedule.get_agent_count() } - method of a class/instance: - {"agent_count": self.get_agent_count} # self here is a class instance - {"agent_count": Model.get_agent_count} # Model here is a class - class attributes of a model - {"model_attribute": "model_attribute"} - functions with parameters that have placed in a list - {"Model_Function":[function, [param_1, param_2]]} + - If you want to pickle your model you must not use lambda functions. + - If your model includes a large number of agents, it is recommended to + use attribute names for the agent reporter, as it will be faster. """ self.model_reporters = {} self.agent_reporters = {} From cd18cdf440736039be4e5816230d660577c5b36b Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Mon, 23 Oct 2023 11:58:18 +0200 Subject: [PATCH 211/214] CI: Update GHA workflows to Python 3.12 Start testing Python 3.12 in CI --- .github/workflows/build_lint.yml | 12 +++++++----- .github/workflows/release.yml | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 9e89ad99b9c..919c07f267c 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -30,8 +30,10 @@ jobs: fail-fast: False matrix: os: [windows, ubuntu, macos] - python-version: ["3.11"] + python-version: ["3.12"] include: + - os: ubuntu + python-version: "3.11" - os: ubuntu python-version: "3.10" - os: ubuntu @@ -64,10 +66,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.10 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.12" - run: pip install ruff==0.0.275 - name: Lint with ruff # Include `--format=github` to enable automatic inline annotations. @@ -78,10 +80,10 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: Set up Python 3.10 + - name: Set up Python 3.12 uses: actions/setup-python@v4 with: - python-version: "3.10" + python-version: "3.12" - run: pip install black[jupyter] - name: Lint with black run: black --check --exclude=mesa/cookiecutter-mesa/* . diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a16e39209af..7600a15de93 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Set up Python uses: actions/setup-python@v4 with: - python-version: '3.10' + python-version: "3.12" - name: Install dependencies run: pip install -U pip wheel setuptools - name: Build package From d92b7427eb6e5033b4b182c33b71e590f40401b8 Mon Sep 17 00:00:00 2001 From: Ewout ter Hoeven Date: Mon, 23 Oct 2023 16:36:59 +0200 Subject: [PATCH 212/214] Update to Ruff 0.1.1 (#1841) * Update to Ruff 0.1.1 Note that Ruff has adopted a new version policy (https://docs.astral.sh/ruff/versioning/), similar to SemVer. So we can now do >=0.1.1,<0.2.0 to get bugfixes and deprecation warnings, but don't get new (syntax) features. * CI: Use --output-format=github for Ruff in CLI Update the CLI syntax to --output-format=github for Ruff See https://github.com/astral-sh/ruff/pull/8014 * Use ~= syntax for Ruff version --- .github/workflows/build_lint.yml | 4 ++-- setup.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build_lint.yml b/.github/workflows/build_lint.yml index 919c07f267c..ab9f22cdade 100644 --- a/.github/workflows/build_lint.yml +++ b/.github/workflows/build_lint.yml @@ -70,11 +70,11 @@ jobs: uses: actions/setup-python@v4 with: python-version: "3.12" - - run: pip install ruff==0.0.275 + - run: pip install ruff~=0.1.1 # Update periodically - name: Lint with ruff # Include `--format=github` to enable automatic inline annotations. # Use settings from pyproject.toml. - run: ruff . --format=github --extend-exclude 'mesa/cookiecutter-mesa/*' + run: ruff . --output-format=github --extend-exclude 'mesa/cookiecutter-mesa/*' lint-black: runs-on: ubuntu-latest diff --git a/setup.py b/setup.py index 73e82f80da8..6024165dc50 100644 --- a/setup.py +++ b/setup.py @@ -19,7 +19,7 @@ extras_require = { "dev": [ "black", - "ruff==0.0.275", + "ruff~=0.1.1", # Update periodically "coverage", "pytest >= 4.6", "pytest-cov", From b58c63400c44bb6d93d6e6ef46a270a49399b7d2 Mon Sep 17 00:00:00 2001 From: rht Date: Thu, 19 Oct 2023 00:14:19 -0400 Subject: [PATCH 213/214] docs: Rename useful-snippets to how-to guide --- docs/{useful-snippets/snippets.rst => howto.rst} | 6 +++--- docs/index.rst | 2 +- docs/tutorials/intro_tutorial.ipynb | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) rename docs/{useful-snippets/snippets.rst => howto.rst} (94%) diff --git a/docs/useful-snippets/snippets.rst b/docs/howto.rst similarity index 94% rename from docs/useful-snippets/snippets.rst rename to docs/howto.rst index 728c4e65f19..09792af5bbd 100644 --- a/docs/useful-snippets/snippets.rst +++ b/docs/howto.rst @@ -1,7 +1,7 @@ -Useful Snippets -=============== +How-to Guide +============ -A collection of useful code snippets. Here you can find code that allows you to get to get started on common tasks in Mesa. +Here you can find code that allows you to get to get started on common tasks in Mesa. Models with Discrete Time ------------------------- diff --git a/docs/index.rst b/docs/index.rst index 6b2e4831ccd..83382e1bdaa 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -98,7 +98,7 @@ ABM features users have shared that you may want to use in your model tutorials/intro_tutorial tutorials/visualization_tutorial Best Practices - Useful Snippets + How-to Guide API Documentation Mesa Packages tutorials/adv_tutorial_legacy.ipynb diff --git a/docs/tutorials/intro_tutorial.ipynb b/docs/tutorials/intro_tutorial.ipynb index 1aa8d4d1c4b..a77f4a13610 100644 --- a/docs/tutorials/intro_tutorial.ipynb +++ b/docs/tutorials/intro_tutorial.ipynb @@ -1263,7 +1263,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "**Note for Windows OS users:** If you are running this tutorial in Jupyter, make sure that you set `number_processes = 1` (single process). If `number_processes` is greater than 1, it is less straightforward to set up. You can read [Mesa's collection of useful snippets](https://github.com/projectmesa/mesa/blob/main/docs/useful-snippets/snippets.rst), in 'Using multi-process `batch_run` on Windows' section for how to do it." + "**Note for Windows OS users:** If you are running this tutorial in Jupyter, make sure that you set `number_processes = 1` (single process). If `number_processes` is greater than 1, it is less straightforward to set up. You can read [Mesa's how-to guide](https://github.com/projectmesa/mesa/blob/main/docs/howto.rst), in 'Using multi-process `batch_run` on Windows' section for how to do it." ] }, { From 3b580a38690d72a5b7e9fcaeab5151950a004b0b Mon Sep 17 00:00:00 2001 From: Rebecca Sutton Koeser Date: Wed, 25 Oct 2023 02:47:35 -0400 Subject: [PATCH 214/214] fix: Configure change handler for checkbox input (#1844) --- mesa/experimental/jupyter_viz.py | 1 + 1 file changed, 1 insertion(+) diff --git a/mesa/experimental/jupyter_viz.py b/mesa/experimental/jupyter_viz.py index 3408ba11c6a..de207bf2926 100644 --- a/mesa/experimental/jupyter_viz.py +++ b/mesa/experimental/jupyter_viz.py @@ -228,6 +228,7 @@ def change_handler(value, name=name): elif input_type == "Checkbox": solara.Checkbox( label=label, + on_value=change_handler, value=options.get("value"), ) else: