ModelUtils.jl
provides a framework to specify and solve perfect foresight models in Julia. A model is function f(m,X,E)
that must be zero at each date along the transition path. Here, m
is a model environment (described below), X is a vector of length f
should produce a vector of length
A model environment is created by calling the ModelEnvironment
constructor with the following keyword arguments
par
-- a struct of parameters for the modelvars
-- a dictionary of VarList data types (see below)T
-- the length of the transition pathss
-- a dictionary of steady states. At a minimum, the dictionary contains the key"initial"
giving the initial steady state. It may optionally also include"terminal"
giving the terminal steady state. If a terminal steady state is not supplied, we assume the system returns to the initial steady state.
See RBC.jl
for a minimal example of usage.
The model variables are stored in the vectors X
and E
and we want to be able to easibly reference specific parts of these vectors. Suppose we have three endogenous variables: y
, p
, and i
, we then define a data type Vars
to identify our endogeous variables using the macro
@endogenousvariables y p i
We often use the @unpack
macro from Parameters.jl
and the contemp
function from ModelUtils.jl
as follows
@unpack y = contemp(X,m)
The variable y
is then the X
containing the values of y
.
We similarly define exogenous variables and reference them with exogenous(E,m)
@exogenousvariables z
@unpack z = exogenous(E,m)
@endogenousvariables
automatically constructs data types that allow us to reference leads and lags of our endogenous variables. We do so with @unpack y_l = lag(X,m)
. A lag prepends the steady state value and then omits the last element of the transition. A lead can be accessed with @unpack y_p = lead(X,m)
.
If you want to access higher-order lags and leads, you need to prepare for this in setting up the model environment. For example, if you want to reference variables from date t-2 in the equation for date t you use @addlaglead -2 y p i
. Here, the first expression is an integer saying what lag or lead we are adding. The remaining arguments are the names of the endogenous variables (all of them). Then to reference these data you use @unpack y_l2 = lag(X,m,-2)
. See ConvexAdjustCost.jl
for an example.
Consider the example model equation -y .+ p .+ i
. Alternatively, we could just include the broadcast macro @.
at the beginning and let Julia apply all the vectorized operations. In that case we write @. -y + p + i
.
There are three functions for solving the model
-
linearIRFs
-- produces IRFs for a linearized version of the model akin to a first-order perturbation solution $$ f_X dX + f_E dE = 0 \quad \Rightarrow \quad \frac{dX}{dE} = - f_X^{-1} f_E $$ -
nonlineartransition
-- solves for$X$ such that$f(X,E) = 0$ using Newton's method. -
optimaltransitionpath
-- heref
should be the "private sector" block and represent$n-1$ equations. The method solves$\max_X ; U(X_1,E) \quad s.t. \quad f(X_1,X_2,E) = 0,$ where$X_2$ are$T$ policy instruments. Here, you must also supply functions that evaluate the gradient and Hessian of the objective function$U$ . SeeOptimalPolicy_nonlinear.jl
for an example. -
optimalLQpolicy
-- similar tooptimaltransitionpath
but assumes that the Hessian of$U$ and the Jacobian of$f$ are constant as in a linear-quadratic problem.
The IRF results are produced in a vector X
. You may then plot the results plot(X,m)
.
A basic script has the following components
- Specify parameters
- Specify variables using
@endogenousvariables
and@exogenousvariables
- Create the model environment
- Provide the model equations in
f
- Call a solving function