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 this is no longer needed:

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

but instead this can be written:

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 the elements should not be added up but multiplied this can be done:

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 elements together the multi-country model equations of the Backus, Kehoe, and Kydland (1992) model can be written 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 parameter values can also be declared, including by means of calibration equations, in the parameter block.

In the above example the production function was defined for countries H and F. Implicitly there are two parameters alpha and their value can be defined 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 also applies to calibration equations; for example:

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 indices can be omitted and the package understands that both indices are referred to:

y[ss] = 1 | alpha

Making use of the indices a level of y for country H with alpha for country H could also be targeted and the ratio of the two ys targeted 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 the parameters can be defined 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.321 seconds
Set up non-stochastic steady state problem:				1.821 seconds
Find non-stochastic steady state:					13.483 seconds
Take symbolic derivatives up to first order:				0.95 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