General structure of examples
Example folder structure
The folder of each example typically consists of the following:
Model/
: Contains the model files specified by the user. These include:
input_aggregate_model.mod
: contains the user's model equations.input_aggregate_names.jl
: contains the naming of the variables, in particular, which variables are states, choices, and shocks is specified here.input_aggregate_steady_state.mod
: contains the equations for the steady state of the model.input_compute_args_hh_prob_ss.jl
: provides the function that computes the arguments of the household problem in the steady state.input_functions.jl
: contains the functions that can be user specified. CAN THE INPUTS BE CHANGED?input_parameters.jl
: specifies the parameters. Keep in mind that you cannot take out parameters for the household model part.
Data/
: Contains some data used in the baseline for the estimation. If you are not interested in estimation, this folder is not strictly required.
main.jl
: The main file that runs the example and calls the methods of the toolbox. Occasionally this file is called main_noestim.jl
to explicitly indicate that the corresponding model is not estimated.
Setup of the model
The following guides through the files used to setup the aggregate model. They are located in the Model/
folder of any example.
Setup of the variables and names
The first step in setting up the model is to define the variables and their names. This is done in the input_aggregate_names.jl
file. The user should usually specify or simply adjust only the following objects:
shock_names
: list of aggregate shocks as symbols (without any duplication).state_names
: list of aggregate states as strings (without duplication). These names should be the same as the variable names used ininput_aggregate_model.mod
.control_names
: list of aggregate controls as strings (without duplication). These names should be the same as the variable names used ininput_aggregate_model.mod
.args_hh_prob_names
: list of arguments of the household problem as strings. These names should be the same as the variable names used ininput_aggregate_model.mod
. They are a subset of the variables defined above. !!! danger Importantly, some variables must exist in this list and with exactly these names as the household problem cannot be solved without them being specified. Naturally, those variables correspond to the variables showing up in the household's budget constraint like prices and taxes. See also the in-detail explanation of the household problem.
n_rep
: integer specifying the duplicates, e.g. number of sectors or countries.dup_state_names
: list of aggregate states as strings that should be duplicated.dup_control_names
: list of aggregate controls as strings that should be duplicated.
The following objects need to be changed only if changes outside the example structure are made:
distr_names
: list of cross-sectional controls or distributional summary variables as strings, which are also controls (but not listed there).
These are only the aggregate variables so that the distribution (state) and the marginal utilities/value functions (controls) are not included here.
Setup of the steady state
The setup of the steady state distinguishes between two kinds of variables: household problem arguments and aggregate variables. The former are the variables the household needs to know to solve her individual planning problem, usually the variables that enter her budget constraint.
The set of variables comprising the household problem arguments are the ones specified in args_hh_prob_names
in input_aggregate_names.jl
. They are computed separately from the other steady state values (see below) in input_compute_args_hh_prob_ss.jl
. They must be computable based on the capital stock in steady state and the model parameters. (Therefore those are the inputs of the function BASEforHANK.IncomesETC.compute_args_hh_prob_ss()
.) To facilitate computations, the user can use functions provided in input_functions.jl
.
The steady state of all other aggregate variables is defined in input_aggregate_steady_state.mod
. The syntax here is to refer to the variables as specified in input_aggregate_names.jl
and add $SS
to the variable name to refer to its steady state value. Once defined, a variable's steady state can be used for further definitions as if it is a value. Also here, the user can make use of functions provided in input_functions.jl
.
The command @R$1
need to be changed only if the multisector/-country feature is used. In this case, the number should be set to the number of sectors/countries.
BASEforHANK.IncomesETC.compute_args_hh_prob_ss
— Functioncompute_args_hh_prob_ss(K, m_par, n_par)
This function calculates the arguments (parameters, variables) that are needed for the household problem, stored in args_hh_prob
based on the list args_hh_prob_names
from input_aggregate_names.jl
. These arguments will be packed and unpacked using the macros @write_args_hh_prob
(or @write_args_hh_prob_ss
) and @read_args_hh_prob
(or @read_args_hh_prob_ss
).
This function takes in the current capital stock K
and the model and numerical parameters as well as (user-specified) functions and then computes the relevant steady state values. That means, all the necessary arguments must follow from K
or steady state assumptions in the parameter structs.
The function is used in BASEforHANK.SteadyState.Kdiff()
to find the stationary equilibrium of the household block of the model and in BASEforHANK.PerturbationSolution.prepare_linearization()
to prepare the linearization of the model.
Arguments
K::Float64
: Capital stockm_par::ModelParameters
,n_par::NumericalParameters
Returns
args_hh_prob::Vector
: Vector of arguments for the household problem, see listargs_hh_prob_names
ininput_aggregate_names.jl
and the macros@write_args_hh_prob
and@write_args_hh_prob_ss
More details on input_functions.jl
The file input_functions.jl
includes multiple functions. Some of these are optional whereas others are used as part of the package. The functions are
output()
: Calculates firm's total output from production inputs.profits_E_ss()
: Uses total output of the model economy to calculate the entrepreneurs' profits in steady state, which enter the budget considerations of the households.interest()
: Defines the net real interest rates, which households receive, also net of depreciation. In the baseline model it is equal to the marginal product of capital minus depreciation.wage()
: These are real wages, which are paid by the firm. Importantly these are not wages received by the household, which connects to the union profits, specified in the function below.profits_U_()
: Function, which use the difference in the wages paid by the firm and the wages paid to households to calculate union profits.
Please note that using these functions at the right places in your model equations in input_aggregate_model.mod
ensures consistency of your model equations with the functions' specifications.
Due to some current functionality, the input arguments to the functions output()
, wage()
, interest()
, profits_E_ss()
and profits_U_()
cannot be changed. Keep this in mind when changing the aggregate model.
Setup of the model parameters
The parameters are defined in the file input_parameters.jl
. We discuss the structure concerning the parameters in more detail in the section Parameters.
Setup of the aggregate model equations
Finally, the file input_aggregate_model.mod
contains the aggregate model equations. The user has to provide the equations in the form of
F[equation_number] = (lhs) - (rhs)
where the equation_number
is based on a variable-specific index, following the pattern indexes.<variable_name>
. The <variable_name>
must be unique, i.e. each equation_number
is used only once.
The variables defined in args_hh_prob_names
must exist as variables throughout the model, i.e. they must be defined (though possibly implicitly) in input_aggregate_model.mod
.
Lags and leads
The toolbox comes with support only for future variables ("Prime"), i.e. when solving the model, they are automatically defined. To use them when writing down the equations, the user adds the suffix Prime
to the variable name. So for example, the equation for capital accumulation, $K_{t+1} = (1-\delta_0)K_t + I_t$, is written as:
# Capital accumulation equation
F[indexes.I] = (log(KPrime)) - (log(K * (1.0 - m_par.δ_0) + I))
Of course, the user can also use lags by previously defining them accordingly. For example, to define real wage inflation, $\frac{w^F_t}{w^F_{t-1}} = \frac{\pi^w_t}{\pi_t}$, the user would define a variable wFlag
first by using wFlagPrime
and then use this new variable in the equation itself:
# Definition of lagged real wage
F[indexes.wFlag] = (log(wFlagPrime)) - (log(wF))
# Definition of real wage inflation
F[indexes.πw] = (log(wF / wFlag)) - (log(πw / π))
Auxiliary variables
You can specify auxiliary variables inside input_aggregate_model.mod
by regular Julia code. They go without an equation number:
# Auxiliary variable
# Mass of households in each productivity state, distribution is (nb, nk, nh)
distr_h = sum(distrSS, dims = (1,2))
Setting variables entering the household problem
As mentioned above, the variables that enter the household problem must be defined in input_aggregate_model.mod
. If the user does not want to use them, they can be set to a constant:
# Constant idiosyncratic income risk
F[indexes.σ] = (log(σ)) - (XSS[indexes.σSS])
Closing the aggregate model
In some (aggregate) equations, the households' decisions enter, e.g. market clearing conditions. They must be stated also in input_aggregate_model.mod
, but are replaced in BASEforHANK.PerturbationSolution.Fsys()
by the right aggregation during the preprocessing. However, for the estimation, they are used and are indeed correct as for the estimation only the derivative w.r.t. aggregates is important.
Special case: Multiple economies or sectors
The structure of the problem at hand allows for multiple economies or sectors in the aggregate model part as it would be nothing else than stating additional equations for each of them. To simplify writing up such models, the preprocessor allows using a certain syntax to mark the equations that would need to be repeated or copied for each economy and numbered accordingly. Instead, the preprocessor automatically copies marked equations. Because many ASCII characters are reserved for other purposes, the syntax uses the ?
symbol.
The HANK economy is considered to be economy 1, not receiving an additional number. Any repeated equation will then get an additional number, starting with 2. The preprocessor will then automatically replace the ?
symbol with the respective number and will repeat the equation accordingly.
E.g., the following two code snippets from input_aggregate_model.mod
are equivalent for the preprocessor:
# TFP HANK economy
F[indexes.Z] = (log(ZPrime)) - (m_par.ρ_Z * log(Z))
# TFP economy 2
F[indexes.Z2] = (log(Z2Prime)) - (m_par.ρ_Z * log(Z2))
# TFP HANK and all other economies
F[indexes.Z?] = (log(Z?Prime)) - (m_par.ρ_Z * log(Z?))
At the beginning of the input_aggregate_model.mod
file, the user has to specify the number of economies or sectors in their model by the following line:
# Setting the number of economies to 2.
@R?2
The same logic applies when specifying the variable names in input_aggregate_names.jl
and input_aggregate_steady_state.mod
. Consequently, the syntax can be also used when referring to steady state variables during writing down the model. E.g., Z?SS
refers to the steady state value of variable Z
for each economy.
In order to enable convenient syntax highlighting and because the ?
character serves other purposes in Julia
, .mod
files are used instead of .jl
files for the user-specified model equations. In VS Code, you can still set the syntax highlighting to Julia for .mod
files by clicking on the language mode in the bottom right corner of the editor.
Structuring parameters
The file input_parameters.jl
contains three structures to provide 1.) the model parameters, 2.) the numerical parameters, and 3.) the estimation settings.
Model parameters
Each element of the struct
ModelParameters
consists of a parameter name, its value, its ascii name, its long name, its LaTeX name, its prior (please see the Distributions.jl
-package for available options), and a Boolean whether the parameter should be estimated or not. If the parameter is not estimated, a prior may be suppressed by typing _
instead of the prior.
Model parameters that only affect the aggregate dynamics can be freely adjusted.
Parameters that (also) affect the household problem have to exist (with exactly these names and definitions). The reason for that is that these variables play a crucial role in the household problem, see also the in-detail explanation of the household problem.
Numerical parameters
The struct
NumericalParameters
contains several types of parameters such as grids and meshes, variable types, and other parameters that determine the numerical approximation or solution technique, like reduc
or sol_algo
.
The user can modify some of the parameters and default values in input_parameters.jl
. They serve as basis for call_find_steadystate()
, which sets other parameters automatically and adds further parameters from the discretization results like the transition matrix for productivity and the joint distribution.
In particular, nh
, nk
, and nb
control the resolution for the individual productivity, illiquid asset, and liquid asset grid. The resolution of the copula used in the linearization does not need to coincide with that grid and is controlled by nh_copula
, nk_copula
, and nb_copula
, respectively. Note, however, that the copula resolution should not exceed the actual grid size.
Estimation settings
The struct
EstimationSettings
contains the settings for the estimation.
See also the section Settings.
The main file
Header
Usually, the user does not need to change the header of the main file. The location of the files of the toolbox and the model are usually inferred from the path of the (activated) environment. See also Installation
In this section, we set up the paths and pre-process the user's model inputs for the current example in the block
include(paths["src"] * "/Preprocessor/PreprocessInputs.jl");
Notably, this already pre-processes the aggregate model equations as well as the steady state file that are located in the folder examples/<your_example>/Model/
and produces the generated functions in the folder bld/<your_example>/Preprocessor/generated_fcns/
. For more details on the pre-processing, see [...].
Importantly, this pre-processing has to be performed before loading the BASEforHANK module defined in BASEforHANK.jl
, which is then loaded via
include("BASEforHANK.jl")
using .BASEforHANK
BASEforHANK.jl
is the key module file as it loads in the code base, sets up structures, and exports a number of functions and macros.
Parameter and estimation preparation
The next step is to set up the model parameters, done by
m_par = ModelParameters();
e_set = BASEforHANK.e_set;
Using the toolbox methods
After the steps described above, you are ready to use the toolbox' methods. A typical example could look as follows:
# Compute the steady state
ss_full = call_find_steadystate(m_par);
# Prepare the linearization, including the sparse DCT representation
sr_full = call_prepare_linearization(ss_full, m_par);
# Linearize the full model, i.e. find sparse state-space representation
lr_full = linearize_full_model(sr_full, m_par);
For more details, consider the complete example main files or the overview of Methods provided by the toolbox.
List of examples
We implemented the following examples. They may be a good starting point for your own model.
- The baseline example. This is a two-asset model. Additionally, we provide a one-asset and a complete-markets version of the baseline example.
- A simpler version of the baseline example: two assets, but simpler aggregate model part.
- A Krusell-Smith like model: one asset model with relatively simple aggregate model part.
- A two-sector model with a service sector with monopolistic competition and nominal rigidities and a housing sector with a competitive producer.