Skip to content
Open
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
103 changes: 23 additions & 80 deletions examples/bank_reserves/bank_reserves/model.py
Original file line number Diff line number Diff line change
@@ -1,124 +1,70 @@
"""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 mesa.discrete_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.
"""

# Start of datacollector functions


def get_num_rich_agents(model):
"""Return number of rich agents"""
rich_agents = [a for a in model.agents if a.savings > model.rich_threshold]
return len(rich_agents)
return len([a for a in model.agents if a.savings > model.rich_threshold])


def get_num_poor_agents(model):
"""Return number of poor agents"""
poor_agents = [a for a in model.agents if a.loans > 10]
return len(poor_agents)
return len([a for a in model.agents if a.loans > 10])


def get_num_mid_agents(model):
"""Return number of middle class agents"""
mid_agents = [
a for a in model.agents if a.loans < 10 and a.savings < model.rich_threshold
]
return len(mid_agents)
return len([
a for a in model.agents
if a.loans < 10 and a.savings < model.rich_threshold
])


def get_total_savings(model):
"""Sum of all agents' savings"""
agent_savings = [a.savings for a in model.agents]
# return the sum of agents' savings
return np.sum(agent_savings)
return np.sum([a.savings for a in model.agents])


def get_total_wallets(model):
"""Sum of amounts of all agents' wallets"""
agent_wallets = [a.wallet for a in model.agents]
# return the sum of all agents' wallets
return np.sum(agent_wallets)
return np.sum([a.wallet for a in model.agents])


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
return get_total_wallets(model) + get_total_savings(model)


def get_total_loans(model):
# list of amounts of all agents' loans
agent_loans = [a.loans for a in model.agents]
# return sum of all agents' loans
return np.sum(agent_loans)
return np.sum([a.loans for a in model.agents])


class BankReservesModel(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,
init_people=25, # ✅ FIXED (was 2)
rich_threshold=10,
reserve_percent=50,
rng=None,
):
super().__init__(rng=rng)

self.height = height
self.width = width
self.init_people = init_people

self.grid = OrthogonalMooreGrid(
(self.width, self.height), torus=True, random=self.random
(self.width, self.height),
torus=True,
random=self.random
)
# 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,
Expand All @@ -129,30 +75,27 @@ def __init__(
"Money": get_total_money,
"Loans": get_total_loans,
},
agent_reporters={"Wealth": lambda x: getattr(x, "wealth", None)},
agent_reporters={
"Wealth": lambda x: getattr(x, "wealth", None)
},
)

# create a single bank for the model
self.bank = Bank(self, self.reserve_percent)

# create people for the model according to number of people set by user
for _ 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(self, True, self.bank, self.rich_threshold)
# place the Person object on the grid at coordinates (x, y)
p.move_to(self.grid[(x, y)])

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

def step(self):
# tell all the agents in the model to run their step function
self.agents.shuffle_do("step")
# collect data
self.datacollector.collect(self)

def run_model(self):
for _ in range(self.run_time):
self.step()
self.step()