Skip to content

Commit

Permalink
feat: Introduce mesa.models
Browse files Browse the repository at this point in the history
This is so that people can easily reuse these canned models, for
experimentation, classroom demo, etc.
For now, it contains only boltzmann_wealth_model.
  • Loading branch information
rht committed May 16, 2023
1 parent 68b8e6c commit af8bc85
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 0 deletions.
Empty file added mesa/models/__init__.py
Empty file.
78 changes: 78 additions & 0 deletions mesa/models/boltzmann_wealth_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Important: always sync the content of this file with
# https://github.com/projectmesa/mesa-examples/blob/main/examples/boltzmann_wealth_model/boltzmann_wealth_model/model.py
import mesa


def compute_gini(model):
agent_wealths = [agent.wealth for agent in model.schedule.agents]
x = sorted(agent_wealths)
N = model.num_agents # noqa: N806
B = sum(xi * (N - i) for i, xi in enumerate(x)) / (N * sum(x)) # noqa: N806
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): # noqa: N803
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])
cellmates.pop(
cellmates.index(self)
) # Ensure agent is not giving money to itself
if len(cellmates) > 0:
other = self.random.choice(cellmates)
other.wealth += 1
self.wealth -= 1

def step(self):
self.move()
if self.wealth > 0:
self.give_money()
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ 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
"B007", # Loop control variable `i` not used within loop body. Revisit, TODO
]
extend-exclude = ["docs", "build"]
# Hardcode to Python 3.8.
Expand Down
7 changes: 7 additions & 0 deletions tests/test_canned_models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from mesa.models import boltzmann_wealth_model


def test_boltzmann_wealth_model():
model = boltzmann_wealth_model.BoltzmannWealthModel()
assert model.running is True
boltzmann_wealth_model.compute_gini(model)

0 comments on commit af8bc85

Please sign in to comment.