Programmatic model writing

Programmatic model writing is a powerful tool to write complex models using concise code. More specifically, the @model and @parameters macros allow for the use of indexed variables and for-loops.

Model block

for loops for time indices

In practice this means that you no longer need to write this:

Y_annual[0] = Y[0] + Y[-1] + Y[-2] + Y[-3]

but instead you can write this:

Y_annual[0] = for lag in -3:0 Y[lag] end

In the background the package expands the for loop and adds up the elements for the different values of lag.

In case you don't want the elements to be added up but multiply the items you can do so:

R_annual[0] = for operator = :*, lag in -3:0 R[lag] end

for loops for variables / parameter specific indices

Another use-case are models with repetitive equations such as multi-sector or multi-country models.

For example, defining the production function for two countries (home country H and foreign country F) would look as follows without the use of programmatic features:

y_H[0] = A_H[0] * k_H[-1]^alpha_H
y_F[0] = A_F[0] * k_F[-1]^alpha_F

and this can be written more conveniently using loops:

for co in [H, F] y{co}[0] = A{co}[0] * k{co}[-1]^alpha{co} end

Note that the package internally writes out the for loop and creates two equations; one each for country H and F. The variables and parameters are indexed using the curly braces {}. These can also be used outside loops. When using more than one index it is important to make sure the indices are in the right order.

Example model block

Putting these these elements together we can write the multi-country model equations of the Backus, Kehoe and Kydland (1992) model like this:

julia> using MacroModelling
julia> @model Backus_Kehoe_Kydland_1992 begin for co in [H, F] Y{co}[0] = ((LAMBDA{co}[0] * K{co}[-4]^theta{co} * N{co}[0]^(1 - theta{co}))^(-nu{co}) + sigma{co} * Z{co}[-1]^(-nu{co}))^(-1 / nu{co}) K{co}[0] = (1 - delta{co}) * K{co}[-1] + S{co}[0] X{co}[0] = for lag in (-4+1):0 phi{co} * S{co}[lag] end A{co}[0] = (1 - eta{co}) * A{co}[-1] + N{co}[0] L{co}[0] = 1 - alpha{co} * N{co}[0] - (1 - alpha{co}) * eta{co} * A{co}[-1] U{co}[0] = (C{co}[0]^mu{co} * L{co}[0]^(1 - mu{co}))^gamma{co} psi{co} * mu{co} / C{co}[0] * U{co}[0] = LGM[0] psi{co} * (1 - mu{co}) / L{co}[0] * U{co}[0] * (-alpha{co}) = - LGM[0] * (1 - theta{co}) / N{co}[0] * (LAMBDA{co}[0] * K{co}[-4]^theta{co} * N{co}[0]^(1 - theta{co}))^(-nu{co}) * Y{co}[0]^(1 + nu{co}) for lag in 0:(4-1) beta{co}^lag * LGM[lag]*phi{co} end + for lag in 1:4 -beta{co}^lag * LGM[lag] * phi{co} * (1 - delta{co}) end = beta{co}^4 * LGM[+4] * theta{co} / K{co}[0] * (LAMBDA{co}[+4] * K{co}[0]^theta{co} * N{co}[+4]^(1 - theta{co})) ^ (-nu{co}) * Y{co}[+4]^(1 + nu{co}) LGM[0] = beta{co} * LGM[+1] * (1 + sigma{co} * Z{co}[0]^(-nu{co} - 1) * Y{co}[+1]^(1 + nu{co})) NX{co}[0] = (Y{co}[0] - (C{co}[0] + X{co}[0] + Z{co}[0] - Z{co}[-1])) / Y{co}[0] end (LAMBDA{H}[0] - 1) = rho{H}{H} * (LAMBDA{H}[-1] - 1) + rho{H}{F} * (LAMBDA{F}[-1] - 1) + Z_E{H} * E{H}[x] (LAMBDA{F}[0] - 1) = rho{F}{F} * (LAMBDA{F}[-1] - 1) + rho{F}{H} * (LAMBDA{H}[-1] - 1) + Z_E{F} * E{F}[x] for co in [H,F] C{co}[0] + X{co}[0] + Z{co}[0] - Z{co}[-1] end = for co in [H,F] Y{co}[0] end endModel: Backus_Kehoe_Kydland_1992 Variables Total: 56 Auxiliary: 31 States: 20 Auxiliary: 10 Jumpers: 28 Auxiliary: 21 Shocks: 2 Parameters: 28

Parameter block

Having defined parameters and variables with indices in the model block we can also declare parameter values, including by means of calibration equations, in the parameter block.

In the above example we defined the production function fro countries H and F. Implicitly we have two parameters alpha and we can define their value individually by setting

alpha{H} = 0.3
alpha{F} = 0.3

or jointly by writing

alpha = 0.3

By not using the index, the package understands that there are two parameters with this name and different indices and will set both accordingly.

This logic extends to calibration equations. We can write:

y{H}[ss] = 1 | alpha{H}
y{F}[ss] = 1 | alpha{F}

to find the value of alpha that corresponds to y being equal to 1 in the non-stochastic steady state. Alternatively we can not use indices and the package understands that we refer to both indices:

y[ss] = 1 | alpha

Making use of the indices we could also target a level of y for country H with alpha for country H and target ratio of the two ys with the alpha for country F:

y{H}[ss] = 1 | alpha{H}
y{H}[ss] / y{F}[ss] = y_ratio | alpha{F}
y_ratio =  0.9

Example parameter block

Making use of this and continuing the example of the Backus, Kehoe and Kydland (1992) model we can define the parameters as follows:

julia> @parameters Backus_Kehoe_Kydland_1992 begin
           K_ss = 11
           K[ss] = K_ss | beta
       
           mu      =    0.34
           gamma   =    -1.0
           alpha   =    1
           eta     =    0.5
           theta   =    0.36
           nu      =    3
           sigma   =    0.01
           delta   =    0.025
           phi     =    1/4
           psi     =    0.5
       
           Z_E = 0.00852
       
           rho{H}{H} = 0.906
           rho{F}{F} = rho{H}{H}
           rho{H}{F} = 0.088
           rho{F}{H} = rho{H}{F}
       endRemove redundant variables in non stochastic steady state problem:	0.689 seconds
Set up non stochastic steady state problem:				0.433 seconds
Take symbolic derivatives up to first order:				12.521 seconds
Find non stochastic steady state:					7.196 seconds
Model:        Backus_Kehoe_Kydland_1992
Variables
 Total:       56
  Auxiliary:  31
 States:      20
  Auxiliary:  10
 Jumpers:     28
  Auxiliary:  21
Shocks:       2
Parameters:   28
Calibration
equations:    2