Skip to content

Commit 51b34d9

Browse files
authored
fix: boltzmann wealth model network (#270)
1 parent 57f0880 commit 51b34d9

File tree

7 files changed

+167
-132
lines changed

7 files changed

+167
-132
lines changed

examples/boltzmann_wealth_model_network/README.md

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,31 +10,29 @@ In this network implementation, agents must be located on a node, with a limit o
1010

1111
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.
1212

13-
JavaScript library used in this example to render the network: [sigma.js](http://sigmajs.org/).
14-
1513
## Installation
1614

17-
To install the dependencies use pip and the requirements.txt in this directory. e.g.
15+
To install the dependencies use `pip` to install `mesa[rec]`
1816

19-
```
20-
$ pip install -r requirements.txt
17+
```bash
18+
$ pip install mesa[rec]
2119
```
2220

2321
## How to Run
2422

25-
To run the model interactively, run ``mesa runserver`` in this directory. e.g.
23+
To run the model interactively, run ``solara run`` in this directory. e.g.
2624

27-
```
28-
$ mesa runserver
25+
```bash
26+
$ solara run app.py
2927
```
3028

31-
Then open your browser to [http://127.0.0.1:8521/](http://127.0.0.1:8521/) and press Reset, then Run.
29+
Then open your browser to [http://localhost:8765/](http://localhost:8765/) and press Reset, then Run.
3230

3331
## Files
3432

35-
* ``run.py``: Launches a model visualization server.
36-
* ``model.py``: Contains the agent class, and the overall model class.
37-
* ``server.py``: Defines classes for visualizing the model (network layout) in the browser via Mesa's modular server, and instantiates a visualization server.
33+
* ``model.py``: Contains creation of agents, the network, and management of agent execution.
34+
* ``agents.py``: Contains logic for giving money, and moving on the network.
35+
* ``app.py``: Contains the code for the interactive Solara visualization.
3836

3937
## Further Reading
4038

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
from boltzmann_wealth_model_network.model import BoltzmannWealthModelNetwork
2+
from mesa.mesa_logging import INFO, log_to_stderr
3+
from mesa.visualization import (
4+
SolaraViz,
5+
make_plot_component,
6+
make_space_component,
7+
)
8+
9+
log_to_stderr(INFO)
10+
11+
12+
# Tells Solara how to draw each agent.
13+
def agent_portrayal(agent):
14+
return {
15+
"color": agent.wealth, # using a colormap to convert wealth to color
16+
"size": 50,
17+
}
18+
19+
20+
model_params = {
21+
"seed": {
22+
"type": "InputText",
23+
"value": 42,
24+
"label": "Random seed",
25+
},
26+
"n": {
27+
"type": "SliderInt",
28+
"value": 7,
29+
"label": "Number of agents",
30+
"min": 2,
31+
"max": 10,
32+
"step": 1,
33+
# "description": "Choose how many agents to include in the model",
34+
},
35+
"num_nodes": {
36+
"type": "SliderInt",
37+
"value": 10,
38+
"label": "Number of nodes",
39+
"min": 3,
40+
"max": 12,
41+
"step": 1,
42+
# "description": "Choose how many nodes to include in the model, with at least the same number of agents",
43+
},
44+
}
45+
46+
47+
def post_process(ax):
48+
ax.get_figure().colorbar(ax.collections[0], label="wealth", ax=ax)
49+
50+
51+
# Create initial model instance
52+
money_model = BoltzmannWealthModelNetwork(n=7, num_nodes=10, seed=42)
53+
54+
# Create visualization elements. The visualization elements are Solara
55+
# components that receive the model instance as a "prop" and display it in a
56+
# certain way. Under the hood these are just classes that receive the model
57+
# instance. You can also author your own visualization elements, which can also
58+
# be functions that receive the model instance and return a valid Solara
59+
# component.
60+
61+
SpaceGraph = make_space_component(
62+
agent_portrayal, cmap="viridis", vmin=0, vmax=10, post_process=post_process
63+
)
64+
GiniPlot = make_plot_component("Gini")
65+
66+
# Create the SolaraViz page. This will automatically create a server and display
67+
# the visualization elements in a web browser.
68+
#
69+
# Display it using the following command in the example directory:
70+
# solara run app.py
71+
# It will automatically update and display any changes made to this file.
72+
73+
page = SolaraViz(
74+
money_model,
75+
components=[SpaceGraph, GiniPlot],
76+
model_params=model_params,
77+
name="Boltzmann Wealth Model: Network",
78+
)
79+
page # noqa
80+
81+
82+
# In a notebook environment, we can also display the visualization elements
83+
# directly.
84+
#
85+
# SpaceGraph(model1)
86+
# GiniPlot(model1)
87+
88+
# The plots will be static. If you want to pick up model steps,
89+
# you have to make the model reactive first
90+
#
91+
# reactive_model = solara.reactive(model1)
92+
# SpaceGraph(reactive_model)
93+
94+
# In a different notebook block:
95+
#
96+
# reactive_model.value.step()
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from mesa.discrete_space import CellAgent
2+
3+
4+
class MoneyAgent(CellAgent):
5+
"""An agent with fixed initial wealth.
6+
7+
Each agent starts with 1 unit of wealth and can give 1 unit to other agents
8+
if they occupy the same cell.
9+
10+
Attributes:
11+
wealth (int): The agent's current wealth (starts at 1)
12+
"""
13+
14+
def __init__(self, model):
15+
"""Create a new agent.
16+
17+
Args:
18+
model (Model): The model instance that contains the agent
19+
"""
20+
super().__init__(model)
21+
self.wealth = 1
22+
23+
def give_money(self):
24+
neighbors = [agent for agent in self.cell.neighborhood.agents if agent != self]
25+
if len(neighbors) > 0:
26+
other = self.random.choice(neighbors)
27+
other.wealth += 1
28+
self.wealth -= 1
29+
30+
def step(self):
31+
empty_neighbors = [cell for cell in self.cell.neighborhood if cell.is_empty]
32+
if empty_neighbors:
33+
self.cell = self.random.choice(empty_neighbors)
34+
35+
if self.wealth > 0:
36+
self.give_money()
Lines changed: 25 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,72 +1,45 @@
1-
import mesa
21
import networkx as nx
2+
from mesa import Model
3+
from mesa.datacollection import DataCollector
4+
from mesa.discrete_space import Network
35

6+
from .agents import MoneyAgent
47

5-
def compute_gini(model):
6-
agent_wealths = [agent.wealth for agent in model.agents]
7-
x = sorted(agent_wealths)
8-
N = model.num_agents
9-
B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x))
10-
return 1 + (1 / N) - 2 * B
118

12-
13-
class BoltzmannWealthModelNetwork(mesa.Model):
9+
class BoltzmannWealthModelNetwork(Model):
1410
"""A model with some number of agents."""
1511

16-
def __init__(self, num_agents=7, num_nodes=10):
17-
super().__init__()
18-
self.num_agents = num_agents
12+
def __init__(self, n=7, num_nodes=10, seed=None):
13+
super().__init__(seed=seed)
14+
15+
self.num_agents = n
1916
self.num_nodes = num_nodes if num_nodes >= self.num_agents else self.num_agents
2017
self.G = nx.erdos_renyi_graph(n=self.num_nodes, p=0.5)
21-
self.grid = mesa.experimental.cell_space.Network(
22-
self.G, random=self.random, capacity=1
23-
)
18+
self.grid = Network(self.G, capacity=1, random=self.random)
2419

25-
self.datacollector = mesa.DataCollector(
26-
model_reporters={"Gini": compute_gini},
27-
agent_reporters={"Wealth": lambda _: _.wealth},
20+
# Set up data collection
21+
self.datacollector = DataCollector(
22+
model_reporters={"Gini": self.compute_gini},
23+
agent_reporters={"Wealth": "wealth"},
2824
)
2925

26+
# Create agents; add the agent to a random node
27+
# TODO: change to MoneyAgent.create_agents(...)
3028
list_of_random_nodes = self.random.sample(list(self.G), self.num_agents)
31-
32-
# Create agents
3329
for position in list_of_random_nodes:
3430
agent = MoneyAgent(self)
35-
36-
# Add the agent to a random node
3731
agent.move_to(self.grid[position])
3832

3933
self.running = True
4034
self.datacollector.collect(self)
4135

4236
def step(self):
43-
self.agents.shuffle_do("step")
44-
# collect data
45-
self.datacollector.collect(self)
46-
47-
def run_model(self, n):
48-
for i in range(n):
49-
self.step()
50-
51-
52-
class MoneyAgent(mesa.experimental.cell_space.CellAgent):
53-
"""An agent with fixed initial wealth."""
54-
55-
def __init__(self, model):
56-
super().__init__(model)
57-
self.wealth = 1
58-
59-
def give_money(self):
60-
neighbors = [agent for agent in self.cell.neighborhood.agents if not self]
61-
if len(neighbors) > 0:
62-
other = self.random.choice(neighbors)
63-
other.wealth += 1
64-
self.wealth -= 1
65-
66-
def step(self):
67-
empty_neighbors = [cell for cell in self.cell.neighborhood if cell.is_empty]
68-
if empty_neighbors:
69-
self.cell = self.random.choice(empty_neighbors)
70-
71-
if self.wealth > 0:
72-
self.give_money()
37+
self.agents.shuffle_do("step") # Activate all agents in random order
38+
self.datacollector.collect(self) # collect data
39+
40+
def compute_gini(self):
41+
agent_wealths = [agent.wealth for agent in self.agents]
42+
x = sorted(agent_wealths)
43+
N = self.num_agents
44+
B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x))
45+
return 1 + (1 / N) - 2 * B

examples/boltzmann_wealth_model_network/boltzmann_wealth_model_network/server.py

Lines changed: 0 additions & 60 deletions
This file was deleted.

examples/boltzmann_wealth_model_network/requirements.txt

Lines changed: 0 additions & 5 deletions
This file was deleted.

examples/boltzmann_wealth_model_network/run.py

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)