Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 10 additions & 12 deletions examples/boltzmann_wealth_model_network/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,31 +10,29 @@ 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

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

```
$ pip install -r requirements.txt
```bash
$ pip install mesa[rec]
```

## How to Run

To run the model interactively, run ``mesa runserver`` in this directory. e.g.
To run the model interactively, run ``solara run`` in this directory. e.g.

```
$ mesa runserver
```bash
$ 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://localhost:8765/](http://localhost: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.
* ``model.py``: Contains creation of agents, the network, and management of agent execution.
* ``agents.py``: Contains logic for giving money, and moving on the network.
* ``app.py``: Contains the code for the interactive Solara visualization.

## Further Reading

Expand Down
96 changes: 96 additions & 0 deletions examples/boltzmann_wealth_model_network/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from boltzmann_wealth_model_network.model import BoltzmannWealthModelNetwork
from mesa.mesa_logging import INFO, log_to_stderr
from mesa.visualization import (
SolaraViz,
make_plot_component,
make_space_component,
)

log_to_stderr(INFO)


# Tells Solara how to draw each agent.
def agent_portrayal(agent):
return {
"color": agent.wealth, # using a colormap to convert wealth to color
"size": 50,
}


model_params = {
"seed": {
"type": "InputText",
"value": 42,
"label": "Random seed",
},
"n": {
"type": "SliderInt",
"value": 7,
"label": "Number of agents",
"min": 2,
"max": 10,
"step": 1,
# "description": "Choose how many agents to include in the model",
},
"num_nodes": {
"type": "SliderInt",
"value": 10,
"label": "Number of nodes",
"min": 3,
"max": 12,
"step": 1,
# "description": "Choose how many nodes to include in the model, with at least the same number of agents",
},
}


def post_process(ax):
ax.get_figure().colorbar(ax.collections[0], label="wealth", ax=ax)


# Create initial model instance
money_model = BoltzmannWealthModelNetwork(n=7, num_nodes=10, seed=42)

# Create visualization elements. The visualization elements are Solara
# components that receive the model instance as a "prop" and display it in a
# certain way. Under the hood these are just classes that receive the model
# instance. You can also author your own visualization elements, which can also
# be functions that receive the model instance and return a valid Solara
# component.

SpaceGraph = make_space_component(
agent_portrayal, cmap="viridis", vmin=0, vmax=10, post_process=post_process
)
GiniPlot = make_plot_component("Gini")

# Create the SolaraViz page. This will automatically create a server and display
# the visualization elements in a web browser.
#
# Display it using the following command in the example directory:
# solara run app.py
# It will automatically update and display any changes made to this file.

page = SolaraViz(
money_model,
components=[SpaceGraph, GiniPlot],
model_params=model_params,
name="Boltzmann Wealth Model: Network",
)
page # noqa


# In a notebook environment, we can also display the visualization elements
# directly.
#
# SpaceGraph(model1)
# GiniPlot(model1)

# The plots will be static. If you want to pick up model steps,
# you have to make the model reactive first
#
# reactive_model = solara.reactive(model1)
# SpaceGraph(reactive_model)

# In a different notebook block:
#
# reactive_model.value.step()
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from mesa.discrete_space import CellAgent


class MoneyAgent(CellAgent):
"""An agent with fixed initial wealth.

Each agent starts with 1 unit of wealth and can give 1 unit to other agents
if they occupy the same cell.

Attributes:
wealth (int): The agent's current wealth (starts at 1)
"""

def __init__(self, model):
"""Create a new agent.

Args:
model (Model): The model instance that contains the agent
"""
super().__init__(model)
self.wealth = 1

def give_money(self):
neighbors = [agent for agent in self.cell.neighborhood.agents if agent != 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()
Original file line number Diff line number Diff line change
@@ -1,72 +1,45 @@
import mesa
import networkx as nx
from mesa import Model
from mesa.datacollection import DataCollector
from mesa.discrete_space import Network

from .agents import MoneyAgent

def compute_gini(model):
agent_wealths = [agent.wealth for agent in model.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):
class BoltzmannWealthModelNetwork(Model):
"""A model with some number of agents."""

def __init__(self, num_agents=7, num_nodes=10):
super().__init__()
self.num_agents = num_agents
def __init__(self, n=7, num_nodes=10, seed=None):
super().__init__(seed=seed)

self.num_agents = n
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.G, random=self.random, capacity=1
)
self.grid = Network(self.G, capacity=1, random=self.random)

self.datacollector = mesa.DataCollector(
model_reporters={"Gini": compute_gini},
agent_reporters={"Wealth": lambda _: _.wealth},
# Set up data collection
self.datacollector = DataCollector(
model_reporters={"Gini": self.compute_gini},
agent_reporters={"Wealth": "wealth"},
)

# Create agents; add the agent to a random node
# TODO: change to MoneyAgent.create_agents(...)
list_of_random_nodes = self.random.sample(list(self.G), self.num_agents)

# 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])

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

def step(self):
self.agents.shuffle_do("step")
# 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()
self.agents.shuffle_do("step") # Activate all agents in random order
self.datacollector.collect(self) # collect data

def compute_gini(self):
agent_wealths = [agent.wealth for agent in self.agents]
x = sorted(agent_wealths)
N = self.num_agents
B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x))
return 1 + (1 / N) - 2 * B

This file was deleted.

5 changes: 0 additions & 5 deletions examples/boltzmann_wealth_model_network/requirements.txt

This file was deleted.

3 changes: 0 additions & 3 deletions examples/boltzmann_wealth_model_network/run.py

This file was deleted.

2 changes: 1 addition & 1 deletion gis/agents_and_networks/src/space/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ def _segmented(linestring: LineString) -> list[LineString]:
# reference: https://gis.stackexchange.com/questions/367228/using-shapely-interpolate-to-evenly-re-sample-points-on-a-linestring-geodatafram
def redistribute_vertices(geom, distance):
if isinstance(geom, LineString):
if (num_vert := int(round(geom.length / distance))) == 0:
if (num_vert := round(geom.length / distance)) == 0:
num_vert = 1
return LineString(
[
Expand Down
8 changes: 4 additions & 4 deletions rl/Tutorials.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
{
"cell_type": "code",
"execution_count": 1,
"execution_count": null,
"metadata": {},
"outputs": [
{
Expand All @@ -52,14 +52,14 @@
],
"source": [
"# ### Step 1: Importing the Necessary Modules\n",
"# To begin, lets import the required modules for the Epstein Civil Violence model:\n",
"# To begin, let's import the required modules for the Epstein Civil Violence model:\n",
"\n",
"from epstein_civil_violence.model import EpsteinCivilViolenceRL\n",
"from epstein_civil_violence.server import run_model\n",
"from epstein_civil_violence.train_config import config\n",
"from train import train_model\n",
"\n",
"# Heres a breakdown of the modules:\n",
"# Here's a breakdown of the modules:\n",
"# - `EpsteinCivilViolenceRL`: Contains the core model and environment.\n",
"# - `run_model`: Configures and runs the model for inference.\n",
"# - `config`: Defines the parameters for training the model.\n",
Expand Down Expand Up @@ -93,7 +93,7 @@
"# ### Step 3: Running the Environment with Random Actions\n",
"\n",
"# To get a feel for how the environment operates, let's run it for a few steps using random actions.\n",
"# Well sample the action space for these actions:\n",
"# We'll sample the action space for these actions:\n",
"\n",
"for _ in range(10):\n",
" action_dict = {}\n",
Expand Down
Loading