From 13b5c8a08f6680a00ce59ada80a3eda6ed5725f3 Mon Sep 17 00:00:00 2001 From: Spartan-71 Date: Sun, 16 Mar 2025 15:32:58 +0530 Subject: [PATCH 01/10] refactor: adapt boltzmman example for mesa 3.1.4 --- .../boltzmann_wealth_model_network/agent.py | 21 +++++++ .../boltzmann_wealth_model_network/app.py | 52 ++++++++++++++++ .../boltzmann_wealth_model_network/model.py | 35 ++--------- .../boltzmann_wealth_model_network/server.py | 60 ------------------- .../boltzmann_wealth_model_network/run.py | 3 - 5 files changed, 77 insertions(+), 94 deletions(-) create mode 100644 examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py create mode 100644 examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py delete mode 100644 examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/server.py delete mode 100644 examples/boltzmann_wealth_model_network/run.py diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py new file mode 100644 index 00000000..dfab8f6c --- /dev/null +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py @@ -0,0 +1,21 @@ +from mesa.experimental.cell_space import CellAgent + +class MoneyAgent(CellAgent): + """ An agent with fixed initial wealth""" + + def __init__(self,model): + super().__init__(model) + self.wealth = 1 + + def give_money(self): + neighbours = [agent for agent in self.cell.neighborhood.agents] + if len(neighbours) > 0: + other = self.random.choice(neighbours) + other.wealth += 1 + self.wealth -= 1 + + def step(self): + if self.wealth > 0: + self.give_money() + + diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py new file mode 100644 index 00000000..c58eeef0 --- /dev/null +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py @@ -0,0 +1,52 @@ +from mesa.visualization import ( + Slider, + SolaraViz, + make_space_component, + make_plot_component, +) + +from mesa.space import NetworkGrid +from model import BoltzmannWealthModelNetwork + +def agent_portrayal(agent): + return { + "color": "red" if agent.wealth == 0 else "green", + "size" : 30, + } + +model_params = { + "num_agents": Slider( + label = "Number of agents", + value = 10, + min = 5, + max = 20, + step = 1, + ), + "num_nodes" : Slider( + label = "Number of nodes", + value = 10, + min = 5, + max = 20, + step = 1, + ), +} + +def post_process_lineplot(ax): + ax.set_ylim(ymin=0) + ax.set_xlim(xmin=0) + +SpacePlot = make_space_component(agent_portrayal) +GiniPlot = make_plot_component("Gini",post_process=post_process_lineplot) + +model = BoltzmannWealthModelNetwork() + +page = SolaraViz( + model, + components = [ + GiniPlot, + SpacePlot + ], + model_params = model_params, + name = "Boltzmann_wealth_model_network", +) +page \ No newline at end of file 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 index fa671ce3..6740af08 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py @@ -1,6 +1,7 @@ import mesa import networkx as nx - +from mesa.experimental.cell_space import Network +from agent import MoneyAgent def compute_gini(model): agent_wealths = [agent.wealth for agent in model.agents] @@ -13,18 +14,17 @@ def compute_gini(model): class BoltzmannWealthModelNetwork(mesa.Model): """A model with some number of agents.""" - def __init__(self, num_agents=7, num_nodes=10): + def __init__(self, num_agents=10, num_nodes=10): super().__init__() 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.experimental.cell_space.Network( + self.grid = Network( self.G, random=self.random, capacity=1 ) 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) @@ -32,7 +32,6 @@ def __init__(self, num_agents=7, num_nodes=10): # Create agents for position in list_of_random_nodes: agent = MoneyAgent(self) - # Add the agent to a random node agent.move_to(self.grid[position]) @@ -44,29 +43,3 @@ def step(self): # collect data self.datacollector.collect(self) - def run_model(self, n): - for i in range(n): - self.step() - - -class MoneyAgent(mesa.experimental.cell_space.CellAgent): - """An agent with fixed initial wealth.""" - - def __init__(self, model): - super().__init__(model) - self.wealth = 1 - - def give_money(self): - neighbors = [agent for agent in self.cell.neighborhood.agents if not self] - if len(neighbors) > 0: - other = self.random.choice(neighbors) - other.wealth += 1 - self.wealth -= 1 - - def step(self): - empty_neighbors = [cell for cell in self.cell.neighborhood if cell.is_empty] - if empty_neighbors: - self.cell = self.random.choice(empty_neighbors) - - 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 50a019ce..00000000 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/server.py +++ /dev/null @@ -1,60 +0,0 @@ -import mesa - -from .model import BoltzmannWealthModelNetwork - - -def network_portrayal(G): - # The model ensures there is 0 or 1 agent per node - - portrayal = {} - 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/run.py b/examples/boltzmann_wealth_model_network/run.py deleted file mode 100644 index eb60c904..00000000 --- a/examples/boltzmann_wealth_model_network/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from boltzmann_wealth_model_network.server import server - -server.launch(open_browser=True) From 863cc10b6f9611e28b94b2859ba54c01d15f2345 Mon Sep 17 00:00:00 2001 From: Spartan-71 Date: Sun, 16 Mar 2025 15:35:24 +0530 Subject: [PATCH 02/10] chore: updated requirements.txt for Mesa 3.1.4 compatibility --- examples/boltzmann_wealth_model_network/requirements.txt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/boltzmann_wealth_model_network/requirements.txt b/examples/boltzmann_wealth_model_network/requirements.txt index e9403e6c..64d4aebd 100644 --- a/examples/boltzmann_wealth_model_network/requirements.txt +++ b/examples/boltzmann_wealth_model_network/requirements.txt @@ -1,5 +1,5 @@ -jupyter -matplotlib -mesa~=2.0 -numpy +mesa==3.1.4 +solara networkx +matplotlib +altair \ No newline at end of file From db3c30f70cb79705d48eaf1aa133ea5e740cbd99 Mon Sep 17 00:00:00 2001 From: Spartan-71 Date: Sun, 16 Mar 2025 15:46:25 +0530 Subject: [PATCH 03/10] chore: updated readme for new file structure --- examples/boltzmann_wealth_model_network/README.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/examples/boltzmann_wealth_model_network/README.md b/examples/boltzmann_wealth_model_network/README.md index cd3bcd8d..577c3d5f 100644 --- a/examples/boltzmann_wealth_model_network/README.md +++ b/examples/boltzmann_wealth_model_network/README.md @@ -10,7 +10,6 @@ In this network implementation, agents must be located on a node, with a limit o 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 @@ -22,19 +21,19 @@ To install the dependencies use pip and the requirements.txt in this directory. ## How to Run -To run the model interactively, run ``mesa runserver`` in this directory. e.g. +To run the model interactively, run ``solara run app.py`` in this directory. ``` - $ mesa runserver + $ solara run app.py ``` -Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run. +Then open your browser to [http://127.0.0.1:8765/](http://127.0.0.1:8765/) 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. +* ``agent.py``: Defines the agent class. +* ``model.py``: Defines the model class. +* ``app.py`` : Defines the visualization and spins up a solara server. ## Further Reading From fce868237faf91e4acf34b628f5c604ff5b2cb7f Mon Sep 17 00:00:00 2001 From: Spartan-71 Date: Sun, 16 Mar 2025 15:51:53 +0530 Subject: [PATCH 04/10] style: format code using Black --- .../boltzmann_wealth_model_network/agent.py | 7 ++- .../boltzmann_wealth_model_network/app.py | 43 ++++++++++--------- .../boltzmann_wealth_model_network/model.py | 6 +-- 3 files changed, 27 insertions(+), 29 deletions(-) diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py index dfab8f6c..11257e5d 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py @@ -1,9 +1,10 @@ from mesa.experimental.cell_space import CellAgent + class MoneyAgent(CellAgent): - """ An agent with fixed initial wealth""" + """An agent with fixed initial wealth""" - def __init__(self,model): + def __init__(self, model): super().__init__(model) self.wealth = 1 @@ -17,5 +18,3 @@ def give_money(self): def step(self): if self.wealth > 0: self.give_money() - - diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py index c58eeef0..f179eec7 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py @@ -8,45 +8,46 @@ from mesa.space import NetworkGrid from model import BoltzmannWealthModelNetwork + def agent_portrayal(agent): return { "color": "red" if agent.wealth == 0 else "green", - "size" : 30, - } + "size": 30, + } + model_params = { "num_agents": Slider( - label = "Number of agents", - value = 10, - min = 5, - max = 20, - step = 1, + label="Number of agents", + value=10, + min=5, + max=20, + step=1, ), - "num_nodes" : Slider( - label = "Number of nodes", - value = 10, - min = 5, - max = 20, - step = 1, + "num_nodes": Slider( + label="Number of nodes", + value=10, + min=5, + max=20, + step=1, ), } + def post_process_lineplot(ax): ax.set_ylim(ymin=0) ax.set_xlim(xmin=0) + SpacePlot = make_space_component(agent_portrayal) -GiniPlot = make_plot_component("Gini",post_process=post_process_lineplot) +GiniPlot = make_plot_component("Gini", post_process=post_process_lineplot) model = BoltzmannWealthModelNetwork() page = SolaraViz( model, - components = [ - GiniPlot, - SpacePlot - ], - model_params = model_params, - name = "Boltzmann_wealth_model_network", + components=[GiniPlot, SpacePlot], + model_params=model_params, + name="Boltzmann_wealth_model_network", ) -page \ No newline at end of file +page 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 index 6740af08..d5523349 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py @@ -3,6 +3,7 @@ from mesa.experimental.cell_space import Network from agent import MoneyAgent + def compute_gini(model): agent_wealths = [agent.wealth for agent in model.agents] x = sorted(agent_wealths) @@ -19,9 +20,7 @@ def __init__(self, num_agents=10, 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 = Network( - self.G, random=self.random, capacity=1 - ) + self.grid = Network(self.G, random=self.random, capacity=1) self.datacollector = mesa.DataCollector( model_reporters={"Gini": compute_gini}, @@ -42,4 +41,3 @@ def step(self): self.agents.shuffle_do("step") # collect data self.datacollector.collect(self) - From 96edde418a7bf86d1025bbbf4b915fad8deba245 Mon Sep 17 00:00:00 2001 From: Spartan-71 Date: Sun, 16 Mar 2025 16:23:11 +0530 Subject: [PATCH 05/10] chore: fix pre-commit errors --- .../boltzmann_wealth_model_network/agent.py | 2 +- .../boltzmann_wealth_model_network/app.py | 5 +---- .../boltzmann_wealth_model_network/model.py | 2 +- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py index 11257e5d..f44339df 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py @@ -9,7 +9,7 @@ def __init__(self, model): self.wealth = 1 def give_money(self): - neighbours = [agent for agent in self.cell.neighborhood.agents] + neighbours = list(self.cell.neighborhood.agents) if len(neighbours) > 0: other = self.random.choice(neighbours) other.wealth += 1 diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py index f179eec7..fadefc1b 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py @@ -1,11 +1,9 @@ from mesa.visualization import ( Slider, SolaraViz, - make_space_component, make_plot_component, + make_space_component, ) - -from mesa.space import NetworkGrid from model import BoltzmannWealthModelNetwork @@ -50,4 +48,3 @@ def post_process_lineplot(ax): model_params=model_params, name="Boltzmann_wealth_model_network", ) -page 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 index d5523349..b0551ab0 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py +++ b/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py @@ -1,7 +1,7 @@ import mesa import networkx as nx -from mesa.experimental.cell_space import Network from agent import MoneyAgent +from mesa.experimental.cell_space import Network def compute_gini(model): From 009a8cc73ad3a86cd817662ece4e5beae1f03060 Mon Sep 17 00:00:00 2001 From: Spartan-71 Date: Wed, 19 Mar 2025 19:54:27 +0530 Subject: [PATCH 06/10] chore: restructured file structure --- .../{boltzmann_wealth_model_network => }/__init__.py | 0 .../{boltzmann_wealth_model_network => }/agent.py | 1 - .../{boltzmann_wealth_model_network => }/app.py | 0 .../{boltzmann_wealth_model_network => }/model.py | 2 +- 4 files changed, 1 insertion(+), 2 deletions(-) rename examples/boltzmann_wealth_model_network/{boltzmann_wealth_model_network => }/__init__.py (100%) rename examples/boltzmann_wealth_model_network/{boltzmann_wealth_model_network => }/agent.py (99%) rename examples/boltzmann_wealth_model_network/{boltzmann_wealth_model_network => }/app.py (100%) rename examples/boltzmann_wealth_model_network/{boltzmann_wealth_model_network => }/model.py (97%) diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/__init__.py b/examples/boltzmann_wealth_model_network/__init__.py similarity index 100% rename from examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/__init__.py rename to examples/boltzmann_wealth_model_network/__init__.py diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py b/examples/boltzmann_wealth_model_network/agent.py similarity index 99% rename from examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py rename to examples/boltzmann_wealth_model_network/agent.py index f44339df..8e5ddb81 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/agent.py +++ b/examples/boltzmann_wealth_model_network/agent.py @@ -1,6 +1,5 @@ from mesa.experimental.cell_space import CellAgent - class MoneyAgent(CellAgent): """An agent with fixed initial wealth""" diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py b/examples/boltzmann_wealth_model_network/app.py similarity index 100% rename from examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/app.py rename to examples/boltzmann_wealth_model_network/app.py diff --git a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py b/examples/boltzmann_wealth_model_network/model.py similarity index 97% rename from examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py rename to examples/boltzmann_wealth_model_network/model.py index b0551ab0..ea174624 100644 --- a/examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/model.py +++ b/examples/boltzmann_wealth_model_network/model.py @@ -1,6 +1,6 @@ import mesa import networkx as nx -from agent import MoneyAgent +from .agent import MoneyAgent from mesa.experimental.cell_space import Network From 9657b57282bf4b25ce32248249da5fdfbdcdbdd4 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 19 Mar 2025 14:25:00 +0000 Subject: [PATCH 07/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/boltzmann_wealth_model_network/agent.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/boltzmann_wealth_model_network/agent.py b/examples/boltzmann_wealth_model_network/agent.py index 8e5ddb81..f44339df 100644 --- a/examples/boltzmann_wealth_model_network/agent.py +++ b/examples/boltzmann_wealth_model_network/agent.py @@ -1,5 +1,6 @@ from mesa.experimental.cell_space import CellAgent + class MoneyAgent(CellAgent): """An agent with fixed initial wealth""" From 4ae6426f66478623af1e42404f5ec0065be5ed86 Mon Sep 17 00:00:00 2001 From: Spartan-71 Date: Wed, 19 Mar 2025 19:59:09 +0530 Subject: [PATCH 08/10] style: formatted using ruff --- examples/boltzmann_wealth_model_network/model.py | 3 ++- examples/hotelling_law/tests.py | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/examples/boltzmann_wealth_model_network/model.py b/examples/boltzmann_wealth_model_network/model.py index ea174624..d26c45df 100644 --- a/examples/boltzmann_wealth_model_network/model.py +++ b/examples/boltzmann_wealth_model_network/model.py @@ -1,8 +1,9 @@ import mesa import networkx as nx -from .agent import MoneyAgent from mesa.experimental.cell_space import Network +from .agent import MoneyAgent + def compute_gini(model): agent_wealths = [agent.wealth for agent in model.agents] diff --git a/examples/hotelling_law/tests.py b/examples/hotelling_law/tests.py index 3d5a25fe..7787fa8a 100644 --- a/examples/hotelling_law/tests.py +++ b/examples/hotelling_law/tests.py @@ -32,9 +32,9 @@ def test_decreasing_price_variance(): df_model = model.datacollector.get_model_vars_dataframe() - assert check_slope( - df_model["Price Variance"], increasing=False - ), "The price variance should decrease over time." + assert check_slope(df_model["Price Variance"], increasing=False), ( + "The price variance should decrease over time." + ) def test_constant_price_variance(): @@ -53,6 +53,6 @@ def test_constant_price_variance(): df_model = model.datacollector.get_model_vars_dataframe() - assert ( - get_slope(df_model["Price Variance"]) == 0 - ), "The price variance constant over time." + assert get_slope(df_model["Price Variance"]) == 0, ( + "The price variance constant over time." + ) From fb53edf26493d72c39a73b9ee85adf323bb43b3a Mon Sep 17 00:00:00 2001 From: Spartan-71 Date: Tue, 25 Mar 2025 17:10:10 +0530 Subject: [PATCH 09/10] feat: bank_reserve example support for mesa 3.1.4 --- examples/bank_reserves/Readme.md | 22 +++-- examples/bank_reserves/__init__.py | 0 .../{bank_reserves => }/agents.py | 0 examples/bank_reserves/app.py | 70 +++++++++++++++ .../bank_reserves/bank_reserves/server.py | 90 ------------------- examples/bank_reserves/batch_run.py | 12 +-- .../{bank_reserves => }/model.py | 18 +--- examples/bank_reserves/requirements.txt | 9 +- examples/bank_reserves/run.py | 3 - 9 files changed, 96 insertions(+), 128 deletions(-) create mode 100644 examples/bank_reserves/__init__.py rename examples/bank_reserves/{bank_reserves => }/agents.py (100%) create mode 100644 examples/bank_reserves/app.py delete mode 100644 examples/bank_reserves/bank_reserves/server.py rename examples/bank_reserves/{bank_reserves => }/model.py (95%) delete mode 100644 examples/bank_reserves/run.py diff --git a/examples/bank_reserves/Readme.md b/examples/bank_reserves/Readme.md index 27570d20..3cae6f5f 100644 --- a/examples/bank_reserves/Readme.md +++ b/examples/bank_reserves/Readme.md @@ -2,14 +2,14 @@ ## 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. +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 app.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 + - CellAgent object inheritance + - OrthogonalMooreGrid 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 + - Solara for visualization of agent interaction - Using a BatchRunner to collect data on multiple combinations of model parameters ## Installation @@ -22,13 +22,13 @@ To install the dependencies use pip and the requirements.txt in this directory. ## Interactive Model Run -To run the model interactively, use `mesa runserver` in this directory: +To run the model interactively, use `solara` in this directory: ``` - $ mesa runserver + $ solara run app.py ``` -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. +Then open your browser to [http://127.0.0.1:8765/](http://127.0.0.1:8765/), select the model parameters, press Reset, then Start. ## Batch Run @@ -43,11 +43,9 @@ To update the parameters to test other parameter sweeps, edit the list of parame ## 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. +* ``agents.py``: Defines the People and Bank classes. +* ``model.py``: Defines the Bank Reserves model and the DataCollector functions. +* ``app.py``: Sets up the interactive solara server for visualization. * ``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 diff --git a/examples/bank_reserves/__init__.py b/examples/bank_reserves/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/examples/bank_reserves/bank_reserves/agents.py b/examples/bank_reserves/agents.py similarity index 100% rename from examples/bank_reserves/bank_reserves/agents.py rename to examples/bank_reserves/agents.py diff --git a/examples/bank_reserves/app.py b/examples/bank_reserves/app.py new file mode 100644 index 00000000..f2697fe0 --- /dev/null +++ b/examples/bank_reserves/app.py @@ -0,0 +1,70 @@ + +from agents import Person +from mesa.visualization import ( + Slider, + SolaraViz, + make_plot_component, + make_space_component, +) +from model import BankReservesModel + +""" +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 +""" + +def person_portrayal(agent): + if agent is None: + return + + portrayal = {} + + # update portrayal characteristics for each Person object + if isinstance(agent, Person): + color = "tab:blue" + # set agent color based on savings and loans + if agent.savings > agent.model.rich_threshold: + color = "green" + if agent.savings < 10 and agent.loans < 10: + color = "tab:blue" + if agent.loans > 10: + color = "tab:red" + + portrayal["color"] = color + + return portrayal + +model_params = { + "init_people": Slider( + label="People", + value=25, + min=1, + max=200, + ), + "rich_threshold": Slider( + label="Rich Threshold", + value=10, + min=1, + max=20, + ), + "reserve_percent":Slider( + label="Reserves", + value=50, + min=1, + max=100, + ) +} + +SpacePlot = make_space_component(person_portrayal) +CategoryPlot = make_plot_component(["Rich","Poor","Middle Class"]) +model = BankReservesModel() + +page = SolaraViz( + BankReservesModel(), + components=[SpacePlot,CategoryPlot], + model_params=model_params, + name="Bank Reserves", +) diff --git a/examples/bank_reserves/bank_reserves/server.py b/examples/bank_reserves/bank_reserves/server.py deleted file mode 100644 index 6fea0561..00000000 --- a/examples/bank_reserves/bank_reserves/server.py +++ /dev/null @@ -1,90 +0,0 @@ -import mesa - -from .agents import Person -from .model import BankReservesModel - -""" -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( - BankReservesModel, - [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 index 2903fd59..7412c0d3 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -24,23 +24,25 @@ every step of every run. """ -import mesa import pandas as pd -from bank_reserves.model import BankReservesModel +from mesa.batchrunner import batch_run +from model import BankReservesModel def main(): # parameter lists for each parameter to be tested in batch run br_params = { + "width": 20, + "height": 20, "init_people": [25, 100], "rich_threshold": [5, 10], "reserve_percent": 5, } # The existing batch run logic here - data = mesa.batch_run( - BankReservesModel, - br_params, + data = batch_run( + model_cls=BankReservesModel, + parameters=br_params ) br_df = pd.DataFrame(data) br_df.to_csv("BankReservesModel_Data.csv") diff --git a/examples/bank_reserves/bank_reserves/model.py b/examples/bank_reserves/model.py similarity index 95% rename from examples/bank_reserves/bank_reserves/model.py rename to examples/bank_reserves/model.py index 2671980d..0070fa93 100644 --- a/examples/bank_reserves/bank_reserves/model.py +++ b/examples/bank_reserves/model.py @@ -12,10 +12,9 @@ import mesa import numpy as np +from agents import Bank, Person from mesa.experimental.cell_space import OrthogonalMooreGrid -from .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. @@ -97,19 +96,14 @@ class BankReservesModel(mesa.Model): 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, + height=20, + width=20, + init_people=5, rich_threshold=10, reserve_percent=50, ): @@ -158,7 +152,3 @@ def step(self): self.agents.shuffle_do("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/requirements.txt b/examples/bank_reserves/requirements.txt index d398c6ed..514c5cfd 100644 --- a/examples/bank_reserves/requirements.txt +++ b/examples/bank_reserves/requirements.txt @@ -1,4 +1,5 @@ -itertools -mesa~=2.0 -numpy -pandas +mesa==3.1.4 +solara +matplotlib +altair +networkx diff --git a/examples/bank_reserves/run.py b/examples/bank_reserves/run.py deleted file mode 100644 index 64a572ee..00000000 --- a/examples/bank_reserves/run.py +++ /dev/null @@ -1,3 +0,0 @@ -from bank_reserves.server import server - -server.launch(open_browser=True) From 159ba8c2736013896ac4bfbdff351c175fcfec28 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Tue, 25 Mar 2025 11:43:47 +0000 Subject: [PATCH 10/10] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/bank_reserves/app.py | 11 ++++++----- examples/bank_reserves/batch_run.py | 5 +---- examples/hotelling_law/tests.py | 12 ++++++------ 3 files changed, 13 insertions(+), 15 deletions(-) diff --git a/examples/bank_reserves/app.py b/examples/bank_reserves/app.py index f2697fe0..ab1950d2 100644 --- a/examples/bank_reserves/app.py +++ b/examples/bank_reserves/app.py @@ -1,4 +1,3 @@ - from agents import Person from mesa.visualization import ( Slider, @@ -16,6 +15,7 @@ Author of original code: Taylor Mutch """ + def person_portrayal(agent): if agent is None: return @@ -37,6 +37,7 @@ def person_portrayal(agent): return portrayal + model_params = { "init_people": Slider( label="People", @@ -50,21 +51,21 @@ def person_portrayal(agent): min=1, max=20, ), - "reserve_percent":Slider( + "reserve_percent": Slider( label="Reserves", value=50, min=1, max=100, - ) + ), } SpacePlot = make_space_component(person_portrayal) -CategoryPlot = make_plot_component(["Rich","Poor","Middle Class"]) +CategoryPlot = make_plot_component(["Rich", "Poor", "Middle Class"]) model = BankReservesModel() page = SolaraViz( BankReservesModel(), - components=[SpacePlot,CategoryPlot], + components=[SpacePlot, CategoryPlot], model_params=model_params, name="Bank Reserves", ) diff --git a/examples/bank_reserves/batch_run.py b/examples/bank_reserves/batch_run.py index 7412c0d3..d0c7ce45 100644 --- a/examples/bank_reserves/batch_run.py +++ b/examples/bank_reserves/batch_run.py @@ -40,10 +40,7 @@ def main(): } # The existing batch run logic here - data = batch_run( - model_cls=BankReservesModel, - parameters=br_params - ) + data = batch_run(model_cls=BankReservesModel, parameters=br_params) br_df = pd.DataFrame(data) br_df.to_csv("BankReservesModel_Data.csv") diff --git a/examples/hotelling_law/tests.py b/examples/hotelling_law/tests.py index 7787fa8a..3d5a25fe 100644 --- a/examples/hotelling_law/tests.py +++ b/examples/hotelling_law/tests.py @@ -32,9 +32,9 @@ def test_decreasing_price_variance(): df_model = model.datacollector.get_model_vars_dataframe() - assert check_slope(df_model["Price Variance"], increasing=False), ( - "The price variance should decrease over time." - ) + assert check_slope( + df_model["Price Variance"], increasing=False + ), "The price variance should decrease over time." def test_constant_price_variance(): @@ -53,6 +53,6 @@ def test_constant_price_variance(): df_model = model.datacollector.get_model_vars_dataframe() - assert get_slope(df_model["Price Variance"]) == 0, ( - "The price variance constant over time." - ) + assert ( + get_slope(df_model["Price Variance"]) == 0 + ), "The price variance constant over time."