Skip to content

Commit

Permalink
Simplify parameter ID tuples
Browse files Browse the repository at this point in the history
  • Loading branch information
charwick committed Dec 11, 2023
1 parent 8a72b74 commit c7d043e
Show file tree
Hide file tree
Showing 5 changed files with 35 additions and 24 deletions.
10 changes: 8 additions & 2 deletions helipad/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,8 +109,14 @@ def addButton(self, text: str, func, desc=None):
self.shocks.add(text, None, func, 'button', True, desc)

def param(self, param, val=None):
"""Get or set a model parameter, depending on whether there are two or three arguments. https://helipad.dev/functions/model/param/"""
item = param[2] if isinstance(param, tuple) and len(param)>2 else None
"""Get or set a model parameter, depending on whether there is one or two arguments. https://helipad.dev/functions/model/param/"""
if isinstance(param, tuple):
#Deprecated in Helipad 1.7, remove in 1.9
if len(param) > 1 and param[1] in ['breed', 'good']:
warnings.warn(ï('Three-item parameter tuple identifiers have been deprecated. The second parameter can be removed.'), FutureWarning, 2)
item = param[2]
else: item = param[1] if len(param)>1 else None
else: item = None
param = self.params[param[0]] if isinstance(param, tuple) else self.params[param]

if val is not None: param.set(val, item)
Expand Down
7 changes: 6 additions & 1 deletion helipad/param.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,12 @@ def add(self, name: str, param, valFunc, timerFunc, active: bool=True, desc=None
"""Register a shock to parameter `param`. `valFunc` takes the current value and returns a new value. `timerFunc` is a function that takes the current model time and returns `bool` (or the string `'button'`, in which case the value is shocked when a control panel button is pressed); `valFunc` will execute whenever `timerFunc` returns `True`. `param` can also be set to `None`, in which case `valFunc` receives the model object. https://helipad.dev/functions/shocks/add/"""
if param is None: item=None
else:
item = param[2] if isinstance(param, tuple) and len(param)>2 else None #The good or breed whose parameter to shock
if isinstance(param, tuple):
#Deprecated in Helipad 1.7, remove in 1.9
if len(param) > 1 and param[1] in ['breed', 'good']:
warnings.warn(ï('Three-item parameter tuple identifiers have been deprecated. The second parameter can be removed.'), FutureWarning, 2)
item = param[2]
else: item = param[1] if len(param)>1 else None
param = self.model.params[param[0]] if isinstance(param, tuple) else self.model.params[param]

super().add(name, self.Shock(
Expand Down
2 changes: 1 addition & 1 deletion sample-models/Helicopter-OMO.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,7 @@ def baseAgentInit(agent, model):
@heli.hook
def agentInit(agent, model):
if model.param('num_bank') > 0:
agent.liqPref = model.param(('liqPref', 'breed', agent.breed, 'agent'))
agent.liqPref = model.param(('liqPref', agent.breed))

@heli.hook
def agentStep(agent, model, stage):
Expand Down
20 changes: 10 additions & 10 deletions sample-models/Helicopter.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ def __init__(self, breed, id, model):
super().__init__(breed, id, model)

#Start with equilibrium prices. Not strictly necessary, but it eliminates the burn-in period. See eq. A7
sm=sum(1/sqrt(model.param(('prod','good',g))) for g in model.goods.nonmonetary) * M0/(model.param('num_agent')*(len(model.goods.nonmonetary)+sum(1+model.param(('rbd','breed',b,'agent')) for b in model.agents['agent'].breeds)))
self.price = {g:sm/(sqrt(model.param(('prod','good',g)))) for g in model.goods.nonmonetary}
sm=sum(1/sqrt(model.param(('prod',g))) for g in model.goods.nonmonetary) * M0/(model.param('num_agent')*(len(model.goods.nonmonetary)+sum(1+model.param(('rbd',b)) for b in model.agents['agent'].breeds)))
self.price = {g:sm/(sqrt(model.param(('prod',g)))) for g in model.goods.nonmonetary}

self.invTarget = {g:model.param(('prod','good',g))*model.param('num_agent')*2 for g in model.goods.nonmonetary}
self.invTarget = {g:model.param(('prod',g))*model.param('num_agent')*2 for g in model.goods.nonmonetary}
self.portion = {g:1/(len(model.goods.nonmonetary)) for g in model.goods.nonmonetary} #Capital allocation
self.wage = 0
self.cashDemand = 0
Expand Down Expand Up @@ -53,11 +53,11 @@ def step(self, stage):
for i in self.model.goods.nonmonetary:

#Just have a fixed inventory target, but update if params do
self.invTarget = {g:self.model.param(('prod','good',g))*self.model.param('num_agent')*2 for g in self.model.goods.nonmonetary}
self.invTarget = {g:self.model.param(('prod',g))*self.model.param('num_agent')*2 for g in self.model.goods.nonmonetary}

#Produce stuff
self.portion[i] = (self.model.param('kImmob') * self.portion[i] + self.price[i]/tPrice) / (self.model.param('kImmob') + 1) #Calculate capital allocation
self.stocks[i] = self.stocks[i] + self.portion[i] * labor * self.model.param(('prod', 'good', i))
self.stocks[i] = self.stocks[i] + self.portion[i] * labor * self.model.param(('prod', i))

#Set prices
#Change in the direction of hitting the inventory target
Expand Down Expand Up @@ -133,10 +133,10 @@ def rbalUpdater(model, var, breed, val):
#Takes as input the slider value, outputs b_g. See equation (A8) in the paper.
def rbaltodemand(breed):
def reporter(model):
rbd = model.param(('rbd', 'breed', breed, 'agent'))
rbd = model.param(('rbd', breed))
beta = rbd/(1+rbd)

return (beta/(1-beta)) * len(model.goods) * sqrt(model.param(('prod','good',AgentGoods[breed]))) / sum(1/sqrt(pr) for pr in model.param(('prod','good')).values())
return (beta/(1-beta)) * len(model.goods) * sqrt(model.param(('prod',AgentGoods[breed]))) / sum(1/sqrt(pr) for pr in model.param('prod').values())

return reporter

Expand Down Expand Up @@ -246,10 +246,10 @@ def terminate(model, data):
def agentInit(agent, model):
agent.store = model.agents['store'][0]
agent.item = AgentGoods[agent.breed]
rbd = model.param(('rbd', 'breed', agent.breed, 'agent'))
rbd = model.param(('rbd', agent.breed))
beta = rbd/(rbd+1)
agent.utility = CES({'good': 1-beta, 'rbal': beta }, agent.model.param('sigma'))
agent.expCons = model.param(('prod', 'good', agent.item))
agent.expCons = model.param(('prod', agent.item))

#Set cash endowment to equilibrium value based on parameters. Not strictly necessary but avoids the burn-in period.
agent.stocks[model.goods.money] = agent.store.price[agent.item] * rbaltodemand(agent.breed)(heli)
Expand Down Expand Up @@ -294,7 +294,7 @@ def modelPostStep(model): model.cb.step() #Step the central bank last
def shock(v):
c = random.normal(v, 4)
return c if c >= 1 else 1
heli.shocks.add('Dwarf real balances', ('rbd','breed','dwarf','agent'), shock, heli.shocks.randn(2), active=False)
heli.shocks.add('Dwarf real balances', ('rbd','dwarf'), shock, heli.shocks.randn(2), active=False)

#Shock the money supply
def mshock(model):
Expand Down
20 changes: 10 additions & 10 deletions sample-notebooks/helicopter.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@
"\t\tsuper().__init__(breed, id, model)\n",
"\n",
"\t\t#Start with equilibrium prices. Not strictly necessary, but it eliminates the burn-in period. See eq. A7\n",
"\t\tsm=sum(1/sqrt(model.param(('prod','good',g))) for g in model.goods.nonmonetary) * M0/(model.param('num_agent')*(len(model.goods.nonmonetary)+sum(1+model.param(('rbd','breed',b,'agent')) for b in model.agents['agent'].breeds)))\n",
"\t\tself.price = {g:sm/(sqrt(model.param(('prod','good',g)))) for g in model.goods.nonmonetary}\n",
"\t\tsm=sum(1/sqrt(model.param(('prod',g))) for g in model.goods.nonmonetary) * M0/(model.param('num_agent')*(len(model.goods.nonmonetary)+sum(1+model.param(('rbd',b,'agent')) for b in model.agents['agent'].breeds)))\n",
"\t\tself.price = {g:sm/(sqrt(model.param(('prod',g)))) for g in model.goods.nonmonetary}\n",
"\n",
"\t\tself.invTarget = {g:model.param(('prod','good',g))*model.param('num_agent')*2 for g in model.goods.nonmonetary}\n",
"\t\tself.invTarget = {g:model.param(('prod',g))*model.param('num_agent')*2 for g in model.goods.nonmonetary}\n",
"\t\tself.portion = {g:1/(len(model.goods.nonmonetary)) for g in model.goods.nonmonetary} #Capital allocation\n",
"\t\tself.wage = 0\n",
"\t\tself.cashDemand = 0\n",
Expand Down Expand Up @@ -82,11 +82,11 @@
"\t\tfor i in self.model.goods.nonmonetary:\n",
"\n",
"\t\t\t#Just have a fixed inventory target, but update if params do\n",
"\t\t\tself.invTarget = {g:self.model.param(('prod','good',g))*self.model.param('num_agent')*2 for g in self.model.goods.nonmonetary}\n",
"\t\t\tself.invTarget = {g:self.model.param(('prod',g))*self.model.param('num_agent')*2 for g in self.model.goods.nonmonetary}\n",
"\n",
"\t\t\t#Produce stuff\n",
"\t\t\tself.portion[i] = (self.model.param('kImmob') * self.portion[i] + self.price[i]/tPrice) / (self.model.param('kImmob') + 1)\t#Calculate capital allocation\n",
"\t\t\tself.stocks[i] = self.stocks[i] + self.portion[i] * labor * self.model.param(('prod', 'good', i))\n",
"\t\t\tself.stocks[i] = self.stocks[i] + self.portion[i] * labor * self.model.param(('prod', i))\n",
"\n",
"\t\t\t#Set prices\n",
"\t\t\t#Change in the direction of hitting the inventory target\n",
Expand Down Expand Up @@ -170,10 +170,10 @@
"#Takes as input the slider value, outputs b_g. See equation (A8) in the paper.\n",
"def rbaltodemand(breed):\n",
"\tdef reporter(model):\n",
"\t\trbd = model.param(('rbd', 'breed', breed, 'agent'))\n",
"\t\trbd = model.param(('rbd', breed, 'agent'))\n",
"\t\tbeta = rbd/(1+rbd)\n",
"\t\t\n",
"\t\treturn (beta/(1-beta)) * len(model.goods) * sqrt(model.param(('prod','good',AgentGoods[breed]))) / sum(1/sqrt(pr) for pr in model.param(('prod','good')).values())\n",
"\t\treturn (beta/(1-beta)) * len(model.goods) * sqrt(model.param(('prod',AgentGoods[breed]))) / sum(1/sqrt(pr) for pr in model.param('prod').values())\n",
"\n",
"\treturn reporter"
]
Expand Down Expand Up @@ -281,10 +281,10 @@
"def agentInit(agent, model):\n",
"\tagent.store = model.agents['store'][0]\n",
"\tagent.item = AgentGoods[agent.breed]\n",
"\trbd = model.param(('rbd', 'breed', agent.breed, 'agent'))\n",
"\trbd = model.param(('rbd', agent.breed, 'agent'))\n",
"\tbeta = rbd/(rbd+1)\n",
"\tagent.utility = CES({'good': 1-beta, 'rbal': beta }, agent.model.param('sigma'))\n",
"\tagent.expCons = model.param(('prod', 'good', agent.item))\n",
"\tagent.expCons = model.param(('prod', agent.item))\n",
"\t\n",
"\t#Set cash endowment to equilibrium value based on parameters. Not strictly necessary but avoids the burn-in period.\n",
"\tagent.stocks[model.goods.money] = agent.store.price[agent.item] * rbaltodemand(agent.breed)(heli)\n",
Expand Down Expand Up @@ -388,7 +388,7 @@
"def shock(v):\n",
"\tc = random.normal(v, 4)\n",
"\treturn c if c >= 1 else 1\n",
"heli.shocks.add('Dwarf real balances', ('rbd','breed','dwarf','agent'), shock, heli.shocks.randn(2))\n",
"heli.shocks.add('Dwarf real balances', ('rbd','dwarf'), shock, heli.shocks.randn(2))\n",
"\n",
"def mshock(model):\n",
"\tpct = random.normal(3, 15) #High mean to counteract the downward bias of (1-%)(1+%)\n",
Expand Down

0 comments on commit c7d043e

Please sign in to comment.