Specifying models#
Models are specified in a YAML file, which uses the YAML markup language. The YAML format is widely used due to its intuitive handling, for example for configuration files or in applications where data is being stored or transmitted which should be human readable. For general information about the format and its syntax see Wikipedia.
The YAML file contains all relevant information from model equations, variable declarations and steady state values.
Models specified as a YAML files can be parsed into a econpizza.PizzaModel
using econpizza.parse()
or econpizza.load()
.
An instance of econpizza.PizzaModel
holds all the relevant information and functionality of the model.
The YAML file#
The YAML files follow a simple structure:
list all variables, parameters and shocks
provide the nonlinear equations. Note that each equation starts with a
~
.define the values of the parameters and fixed steady state values in the
steady_state
sectionoptionally provide auxiliary equations that are not directly part of the nonlinear system
optionally provide initial guesses for all other steady state values and parameters
I will first briefly discuss the YAML of the small scale representative agents NK model from the quickstart tutorial and then turn to a more complex HANK model. A collection of examples is provided with the package.
YAML: representative agent models#
The GitHub version of the YAML file for the small scale NK model can be found here. The first block (variables
and shocks
) is self explanatory:
variables: [y, c, pi, r, rn, beta, w]
shocks: [e_beta]
Note that it is not necessary to define shocks. You can also simply set the initial values of any (exogenous) state.
parameters: [ theta, psi, phi_pi, phi_y, rho, h, sigma_l, rho_beta, chi ]
Use the parameters
block to define any parameters. Parameters are treated the same as variables, but they are time invariant. During steady state search they are treated exactly equally. For this reason their values are provided in the steady_state
block.
definitions: |
from jax.numpy import log, maximum
The second block (definitions
) defines general definitions and imports, which are available at all stages.
equations:
~ w = chi*(c - h*cLag)*y**sigma_l # labor supply
~ 1 = r*betaPrime*(c - h*cLag)/(cPrime - h*c)/piPrime # euler equation
~ psi*(pi/piSS - 1)*pi/piSS = (1-theta) + theta*w + psi*betaPrime*(c-h*cLag)/(cPrime-h*c)*(piPrime/piSS - 1)*piPrime/piSS*yPrime/y # Phillips curve
~ c = (1-psi*(pi/piSS - 1)**2/2)*y # market clearing
~ rn = (rSS*((pi/piSS)**phi_pi)*((y/yLag)**phi_y))**(1-rho)*rnLag**rho # monetary policy rule
~ r = maximum(1, rn) # zero lower bound on nominal rates
~ log(beta) = (1-rho_beta)*log(betaSS) + rho_beta*log(betaLag) + e_beta # exogenous discount factor shock
equations
. The most central part of the yaml. Here you define the model equations, which will then be parsed such that each equation prefixed by a ~ must hold. Use xPrime
for variable x in t+1 and xLag
for t-1. Access steady-state values with xSS
. You could specify a representative agent model with just stating the equations block (additional to variables). Importantly, equations
are not executed subsequently but simultaneously!
Note that you need one equation for each variable defined in variables
.
steady_state:
fixed_values:
# parameters
theta: 6. # demand elasticity
psi: 60 # price adjustment costs
phi_pi: 1.5 # monetary policy rule coefficient #1
phi_y: 0.1 # monetary policy rule coefficient #2
rho: .8 # interest rate smoothing
h: .74 # habit formation
sigma_l: 2 # inverse Frisch elasticity
rho_beta: .9 # autocorrelation of discount factor shock
# steady state values
beta: 0.995
y: .33
pi: 1.02^.25
init_guesses: # the default initial guess for values not specified here is always 0.95
chi: 6
Finally, the steady_state
block allows to fix parameters and, if desired, some steady state values, and provide initial guesses for others. Note that the default initial guess for any variable/parameter not specified here will be 0.95
.
YAML: heterogeneous agent models#
Let us have a look of the YAML of a hank model we will discuss in the tutorial. The GitHub version of the file (link) also contains exhaustive additional comments. The first line reads:
functions_file: 'hank_functions.py'
The relative path to a functions-file, which may provide additional functions.
The GitHub version of the functions file for this model can be found here.
In this example, the file defines the functions transfers
, wages
, hh
, labor_supply
and hh_init
.
definitions: |
from jax.numpy import log, maximum
from econpizza.tools import percentile, jax_print
General definitions and imports (as above). These are available during all three stages (decisions, distributions, equations).
We will use the percentile
function to get some distributional statistics. jax_print
is a JAX-jit-able print function that can be used during call
stages for debugging.
variables: [ div, y, y_prod, w, pi, R, Rn, Rr, Rstar, tax, z, beta, C, n, B, Top10C, Top10A ]
All the aggregate variables that are being tracked on a global level. If a variable is not listed here, you will not be able to recover it later. Since these are aggregate variables, they have dimensionality one.
parameters: [ sigma_c, sigma_l, theta, psi, phi_pi, phi_y, rho, rho_beta, rho_r, rho_z ]
shocks: [ e_beta, e_rstar, e_z ]
Define the model parameters and shocks, as above.
distributions:
# the name of the first distribution
dist:
# ordering matters. The ordering here is corresponds to the shape of the axis of the distribution
# the naming of the dimensions (skills, a) is arbitrary
skills:
# first dimension
type: exogenous_rouwenhorst
rho: 0.966
sigma: 0.6
n: 4
a:
# second dimension. Endogenous distribution objects require inputs from the decisions stage. An object named 'a' assumes that the decisions stage returns a variable named 'a'
type: endogenous_log
min: 0.0
max: 50
n: 50
The distributions block. Defines a distribution (here dist
) and all its dimensions. The information provided here is used to construct the distribution-forward-functions. If this is not supplied, econpizza assumes that you are providing a representative agent model.
Exogenous grids are grids for idiosyncratic shocks. A grid type “exogenous_rouwenhorst” requires the parameters rho
, sigma
and n
. Alternatively, a grid type “exogenous_generic” only needs n
and expects the grid variable and the transition matrix to be defined somewhere else.
Endogenous grids are grids for idiosyncratic state variables. A grid type “exogenous_log” requires the parameters min
, max
and n
. Based on these, a log grid will be created. Alternatively, a grid type “endogenous_generic” only needs n
and expects the grid variable to be defined somewhere else.
decisions:
# define the multidimensional input "WaPrime", in addition to all aggregated variables (defined in 'variables')
inputs: [WaPrime]
# calls executed during the decisions stage
calls: |
# these functions are defined in functions_file
tfs = transfers(skills_stationary, div, tax, skills_grid)
WaPrimeExp = skills_transition @ WaPrime
Wa, a, c = egm_step(WaPrimeExp, a_grid, skills_grid, w, n, tfs, Rr, beta, sigma_c, sigma_l)
# the 'outputs' values are stored for the following stages
outputs: [a,c]
The decisions block. Only relevant for heterogeneous agents models. It is important to correctly specify the dynamic inputs (here: marginals of the value function) and outputs, i.e. those variables that are needed as inputs for the distribution stage. Note that calls are evaluated one after another.
aux_equations: |
# `dist` here corresponds to the dist *at the beginning of the period*
aggr_a = jnp.sum(dist*a, axis=(0,1))
aggr_c = jnp.sum(dist*c, axis=(0,1))
# calculate consumption and wealth share of top-10%
top10c = 1 - percentile(c, dist, .9)
top10a = 1 - percentile(a, dist, .9)
Auxiliary equations. This again works exactly as for the representative agent model. These are executed before the equations
block, and can be used for all sorts of definitions that you may not want to keep track of. For heterogeneous agents models, this is a good place to do aggregation. Auxiliary equations are also executed subsequently.
The distribution (dist
) corresponds to the distribution at the beginning of the period, i.e. the distribution from last period. This is because the outputs of the decisions stage correspond to the asset holdings (on grid) at the beginning of the period, while the distribution calculated from the decision outputs holds for the next period.
# final/main stage: aggregate equations
equations:
# definitions
~ C = aggr_c
~ Top10C = top10c
~ Top10A = top10a
# firms
~ n = y_prod/z # production function
~ div = -w*n + (1 - psi*(pi/piSS - 1)**2/2)*y_prod # dividends
~ y = (1 - psi*(pi/piSS - 1)**2/2)*y_prod # "effective" output
~ psi*(pi/piSS - 1)*pi/piSS = (1-theta) + theta*w + psi*piPrime/R*(piPrime/piSS - 1)*piPrime/piSS*y_prodPrime/y_prod # NKPC
# government
~ tax = (Rr-1)*BLag # balanced budget
~ Rr = RLag/pi # real ex-post bond return
~ Rn = (Rstar*((pi/piSS)**phi_pi)*((y/yLag)**phi_y))**(1-rho)*RnLag**rho # MP rule on shadow nominal rate
~ R = maximum(1, Rn) # ZLB
# clearings
~ C = y # market clearing
~ B = aggr_a # bond market clearing
~ n**sigma_l = w # labor market clearing
# exogenous
~ beta = betaSS*(betaLag/betaSS)**rho_beta*exp(e_beta) # exogenous beta
~ Rstar = RstarSS*(RstarLag/RstarSS)**rho_r*exp(e_rstar) # exogenous rstar
~ z = zSS*(zLag/zSS)**rho_z*exp(e_z) # exogenous technology
Equations. This also works exactly as for representative agents models.
steady_state:
fixed_values:
# parameters:
sigma_c: 2 # intertemporal elasticity of substitution
sigma_l: 2 # inverse Frisch elasticity of labour supply
theta: 6. # elasticity of substitution
psi: 60. # parameter on the costs of price adjustment
phi_pi: 1.5 # Taylor rule coefficient on inflation
phi_y: 0.1 # Taylor rule coefficient on output
rho: 0.8 # persistence in (notional) nominal interest rate
rho_beta: 0.9 # persistence of discount factor shock
rho_r: 0.9 # persistence of MP shock
rho_z: 0.9 # persistence of technology shocks
# steady state
y: 1.0 # effective output
y_prod: 1.0 # output
C: 1.0 # consumption
pi: 1.0 # inflation
beta: 0.98 # discount factor
B: 5.6 # bond supply
# definitions can be recursive: theta is defined above
w: (theta-1)/theta # wages
n: w**(1/sigma_l) # labor supply
div: 1 - w*n # dividends
z: y/n # technology
init_guesses:
Rstar: 1.002 # steady state target rage
Rr: Rstar # steady state real rage
Rn: Rstar # steady state notional rage
R: Rstar # steady state nominal rage
tax: 0.028
WaPrime: egm_init(a_grid, skills_stationary)
The steady state block. fixed_values
are those steady state values that are fixed ex-ante. init_guesses
are initial guesses for steady state finding. Values are defined from the top to the bottom, so it is possible to use recursive definitions, such as n: w**frisch
.
Note that for heterogeneous agents models it is required that the initial value of inputs to the decisions-stage are given (here WaPrime
).
Note
Econpizza is written in JAX, which is a machine learning framework for Python developed by Google. JAX provides automatic differentiation and just-in-time compilation (“jitting”), which makes the package fast and robust. However, running jitted JAX code brings along a few limitations. Check the common gotchas in JAX for details.
Model parsing#
Models specified as a YAML files can be parsed and loaded using econpizza.parse()
and econpizza.load()
.
- econpizza.parse(mfile)#
Parse model dictionary from yaml file. This can be desirable if values should be exchanged before loading the model.
- Parameters:
mfile (string) – path to a yaml file to be parsed
- Returns:
mdict – the parsed yaml as a dictionary
- Return type:
dict
This returns a dictionary containing all the informations provided in the YAML file. Parsing before loading allows to change some features of the model manually. The dictionary can then be forwarded to econpizza.load()
:
- econpizza.load(model_ref, raise_errors=True, verbose=True)#
Load a model from a dictionary or a YAML file.
- Parameters:
model_ref (dict or string) – either a dictionary or the path to a YAML file to be parsed
raise_errors (bool, optional) – whether to raise errors while checking. False will let the model fail siliently for debugging. Defaults to True
verbose (bool, optional) – inform that parsing is done. Defaults to True
- Returns:
model – The parsed model
- Return type:
If desired, econpizza.load()
can also parse the YAML-file directly. The function then returns an instance of
econpizza.PizzaModel
, which holds all the relevant information and functionality of the model:
- class econpizza.PizzaModel(mdict, *args, **kwargs)#
Base class for models. Contains all necessary methods and informations.