Implementation Details

This section describes important functions and implementation features in greater detail. Additional documentation can also be found in function documentation or in-line.

This section focuses on what the code does and why. Docstrings and the code itself (including comments) provide detailed information regarding how these basic procedures are implemented.

The AbstractModel Type

The AbstractModel type provides a common interface for all model objects, which greatly facilitates the implementation of new model specifications. Any concrete subtype of AbstractModel can be passed to any function defined for AbstractModel, provided that the concrete type has the fields that the function expects to be available.

If a user wants to define a new subclass of models, say regression models, then the user could create a new AbstractRegressionModel type as a subtype of AbstractModel. Functions defined for AbstractRegressionModel would only apply to concrete subtypes of AbstractRegressionModel, but functions defined for AbstractModel will still work on these concrete subtypes.

The AbstractParameter Type

The AbstractParameter type implements our notion of a model parameter: a time-invariant, unobserved value that has significance in the model, e.g. for likelihood computation and estimation.

Though all parameters are time-invariant, they can have different features. Some parameters are scaled for use when solving the model[1] and constructing the model's measurement equations[2]. You can also use broadcasting for elementary mathematical operations (e.g. multiplication, addition) on any concrete subtype of AbstractParameter.

During optimization, parameters may be transformed from model space to the real line via one of three different transformations: Untransformed, SquareRoot, and Exponential. These transformations are also defined as types, and require additional information for each parameter. Typically, we have two "hyperparameters" for these transformations, a, and b.

  • Untransformed: a and b do nothing
  • SquareRoot: a and b specify the bounds the parameter takes, i.e. $x\in (a, b)$
  • Exponential: a and b are the parameters in the transformation $a + exp(x - b)$

In some models, steady state values might be relevant parameters. They are typically functions of other parameters, so they do not need to be estimated directly.

While parameters are "time-invariant", we do allow regime switching. As an example, suppose that we have a linear regression with data from time periods $t = 1,\dots, T$, where $T > 4$, and in $t = 3$, the intercept of the regression is assumed to change values because of a structural break in the time series. We can model the intercept as a parameter with regime-switching. The parameter has one value in periods $t = 1, 2$ and a different value in periods $t = 3,\dots, T$. Currently, only regime-switching in the values of the parameter has been tested, but we have implemented regime switching in all the features. For example, you may want a different prior in each regime. See Regime-Switching Interface for documentation on the interface for regime-switching parameters.

The various requirements on parameters are nicely addressed using a parameterized type hierarchy.

  • AbstractParameter{T<:Number}: The common abstract supertype for all parameters.
    • Parameter{T<:Number, U<:Transform}: The abstract supertype for parameters that are directly estimated.
      • UnscaledParameter{T<:Number, U:<Transform}: Concrete type for parameters that do not need to be scaled for equilibrium conditions.
      • ScaledParameter{T<:Number, U:<Transform}: Concrete type for parameters that are scaled for equilibrium conditions.
    • SteadyStateParameter{T<:Number}: Concrete type for steady-state parameters.

All Parameters have the fields defined in UnscaledParameter:

Missing docstring.

Missing docstring for UnscaledParameter. Check Documenter's build log for details.

ScaledParameters also have the following fields:

  • scaledvalue::T: Parameter value scaled for use in eqcond.jl
  • scaling::Function: Function used to scale parameter value for use in equilibrium conditions.

Note: Though not strictly necessary, defining a scaling with the parameter object allows for much a much cleaner definition of the equilibrium conditions.

Because the values of SteadyStateParameters are directly computed as a function of ScaledParameters and UnscaledParameters, they only require 4 fields:

ModelConstructors.SteadyStateParameterType
SteadyStateParameter{T} <: AbstractParameter{T}

Steady-state model parameter whose value depends upon the value of other (non-steady-state) Parameters. SteadyStateParameters must be constructed and added to an instance of a model object m after all other model Parameters have been defined. Once added to m, SteadyStateParameters are stored in m.steady_state. Their values are calculated and set by steadystate!(m), rather than being estimated directly. SteadyStateParameters do not require transformations from the model space to the real line or scalings for use in equilibrium conditions.

Fields

  • key::Symbol: Parameter name. Should conform to the guidelines established in the DSGE Style Guide.
  • value::T: The parameter's steady-state value.
  • description::String: Short description of the parameter's economic significance.
  • tex_label::String: String for printing parameter name to LaTeX.
source

The Observable and PseudoObservable Types

We similarly encapsulate information about observables and pseudo-observables (unobserved linear combinations of states, e.g. the output gap) into the Observable and PseudoObservable types. Each type has identifier fields key, name, and longname.

Most importantly, both Observables and PseudoObservables include the information needed for transformations to and from model units. For Observables, these are the input_series, fwd_transform, and rev_transform fields. "Forward transformations" are applied to transform the raw input data series specified in input_series to model units. The model is estimated and forecasted in model units, and then we apply "reverse transformations" to get human-readable units before computing means and bands or plotting. Pseudo-observables are not observed, so they do not have input_series or fwd_transforms, but they may however have rev_transforms.

As an example, the :obs_gdp Observable uses as input_series aggregate nominal GDP in levels, the GDP price index, and population in levels, all from FRED.[3] These series are fwd_transformed to get quarter-over-quarter log growth rates of per-capita real GDP, which are the Observable's model units. The reverse transformation then converts :obs_gdp into annualized quarter-over-quarter percent changes of aggregate real GDP.

ModelConstructors.ObservableType
mutable struct Observable

Fields

  • key::Symbol
  • input_series::Vector{Symbol}: vector of mnemonics, each in the form :MNEMONIC__SOURCE (e.g. :GDP__FRED). This vector is parsed to determine source (e.g. per-capita consumption gets population and consumption).
  • fwd_transform::Function: Extracts appropriate input_series from a DataFrame of levels, and transforms data to model units (for example, computes per-capita growth rates from levels).
  • rev_transform::Function: Transforms a series from model units into observable units. May take kwargs.
  • name::String: e.g. "Real GDP growth"
  • longname::String: e.g. "Real GDP growth per capita"
source
ModelConstructors.PseudoObservableType
mutable struct PseudoObservable

Fields

  • key::Symbol
  • name::String: e.g. "Flexible Output Growth"
  • longname::String: e.g. "Output that would prevail in a flexible-price economy"
  • rev_transform::Function: Transforms a series from model units into observable units. May take kwargs.
source

Model Settings

The Setting type implements computational settings that affect how the code runs without affecting the mathematical definition of the model. Depending on the model, these may include flags (e.g. whether or not to recompute the Hessian), parameterization for the Metropolis-Hastings algorithm (e.g. number of times to draw from the posterior distribution), and the vintage of data being used (Setting is a parametric type - a Setting{T<:Any}, so Booleans, Numbers, and Strings can all be turned into Settings). If settings exist for a model type, then they should be stored centrally in the settings dictionary within the model object.

Why implement a Setting type when we could put their values directly into the source code or dictionary? The most obvious answer is that the parametric type allows us to implement a single interface for all Settings (Booleans, Strings, etc.), so that when we access a particular setting during the estimation and forecast steps, we don't have to think about the setting's type.

Settings play an important role in addition to providing useful abstraction. Estimating and forecasting the New York Fed DSGE model takes many hours of computation time and creates a lot of output files. It is useful to be able to compare model output from two different models whose settings differ slightly (for example, consider two identical models that use different vintages of data as input). A central feature of the Setting type is a mechanism that generates unique, meaningful filenames when code is executed with different settings. Specifically, when a setting takes on a non-default value, a user-defined setting code (along with the setting's value) are appended to all output files generated during execution.

The Setting{T<:Any} type is defined as follows:

ModelConstructors.SettingType
Setting{T}

The Setting type is an interface for computational settings that affect how the code runs without affecting the mathematical definition of the model. It also provides support for non-conflicting file names for output of 2 models that differ only in the values of their computational settings.

Fields

  • key::Symbol: Name of setting
  • value::T: Value of setting
  • print::Bool: Indicates whether to append this setting's code and value to output file names. If true, output file names will include a suffix of the form _code1=val1_code2=val2 etc. where codes are listed in alphabetical order.
  • code::String: string to print to output file suffixes when print=true.
  • description::String: Short description of what the setting is used for.
source

We provide two functions default_settings! and default_test_settings! to initialize settings that most models can have. The settings are

  • save root
  • input data root
  • vintage of data to be used
  • dataset id

To update the value of an existing function, the user has two options. First, the user may use the <= syntax. However, for this to work properly, it is essential that the setting's key field be exactly the same as that of an existing entry in m.settings. Otherwise, an additional entry will be added to m.settings and the old setting will be the one accessed from other all routines. A potentially safer, though clunkier, option is to use the update! method.

Type Interfaces

Parameter Interface

Base.randMethod
Distributions.rand(p::Vector{AbstractParameter{Float64}}; regime_switching::Bool = false,
    toggle::Bool = true)

Generate a draw from the prior of each parameter in p.

source
Base.randMethod
Distributions.rand(p::Vector{AbstractParameter{Float64}}, n::Int;
    regime_switching::Bool = false, toggle::Bool = true)

Generate n draws from the priors of each parameter in p.This returns a matrix of size (length(p),n), where each column is a sample. To sample from p when it has regime-switching, set regime_switching = true. The toggle keyword is only relevant for regime-switching sampling. Please see ?ModelConstructors.rand_regime_switching.

source
ModelConstructors.differentiate_transform_to_model_spaceMethod
differentiate_transform_to_model_space{S<:Real,T<:Number, U<:Transform}(p::Parameter{S,T,U}, x::S)

Differentiates the transform of x from the real line to lie between p.valuebounds The transformations are defined as follows, where (a,b) = p.transform_parameterization and c a scalar (default=1):

  • Untransformed: x
  • SquareRoot: (a+b)/2 + (b-a)/2 * c * x/sqrt(1 + c^2 * x^2)
  • Exponential: a + exp(c*(x-b))

Their gradients are therefore

  • Untransformed: 1
  • SquareRoot: (b-a)/2 * c / (1 + c^2 * x^2)^(3/2)
  • Exponential: c * exp(c*(x-b))
source
ModelConstructors.differentiate_transform_to_real_lineMethod
differentiate_transform_to_real_line{S<:Real,T<:Number, U<:Transform}(p::Parameter{S,T,U}, x::S)

Differentiates the transform of x from the model space lying between p.valuebounds to the real line. The transformations are defined as follows, where (a,b) = p.transform_parameterization and c a scalar (default=1):

  • Untransformed: x
  • SquareRoot: (1/c)*cx/sqrt(1 - cx^2), where cx = 2 * (x - (a+b)/2)/(b-a)
  • Exponential: b + (1 / c) * log(x-a)

Their gradients are therefore

  • Untransformed: 1
  • SquareRoot: (1/c) * (1 / ( 1 - cx^2)^(-3/2)) * (2/(b-a))
  • Exponential: 1 / (c * (x - a))
source
ModelConstructors.get_untransformed_valuesMethod
get_untransformed_values(p::AbstractParameter)

returns the untransformed values that are used in mathematical operations. The main use case is returning the scaledvalue if p is a ScaledParameter and value if it is an UnscaledParameter.

This function currently does not work with regime-switching parameters.

Additionally, note that unless p.value is a concrete subtype of AbstractArray or some other type such that p.value is just a reference, then it is advisable to avoid using get_untransformed_values when possible. The reason is that p.value creates an extra allocation when p.value is not a reference. For example, if p = parameter(:a, 1.0), then p.value creates 1 allocation, hence get_untransformed_values(p) creates 1 allocation. This behavior means that while p * p creates 1 allocation, get_untransformed_values(p) * get_untransformed_values(p) creates 3 allocations. If get_untransformd_values is called many times, then these additional allocations can add some extra time to the computations. For example, p.value * rand(1e7) versus p * rand(1e7) can result in 10 additional microseconds.

source
ModelConstructors.momentsMethod
moments(θ::Parameter)

If θ's prior is a RootInverseGamma, τ and ν. Otherwise, returns the mean and standard deviation of the prior. If θ is fixed, returns (θ.value, 0.0).

source
ModelConstructors.parameterMethod
parameter(p::ScaledParameter{S,T,U}, newvalue::S) where {S<:Real, T<:Number,U<:Transform}

Returns a ScaledParameter with value field equal to newvalue and scaledvalue field equal to p.scaling(newvalue). If p is a fixed parameter, it is returned unchanged.

source
ModelConstructors.parameterMethod
parameter(p::UnscaledParameter{S,T,U}, newvalue::S) where {S<:Real,T<:Number,U<:Transform}

Returns an UnscaledParameter with value field equal to newvalue. If p is a fixed parameter, it is returned unchanged.

source
ModelConstructors.parameterMethod
parameter{S,T,U<:Transform}(key::Symbol, value::S, valuebounds = (value,value),
                          transform_parameterization = (value,value),
                          transform = Untransformed(), prior = NullablePrior();
                          fixed = true, scaling::Function = identity, description = "",
                          tex_label::String = "")

By default, returns a fixed UnscaledParameter object with key key and value value. If scaling is given, a ScaledParameter object is returned.

source
ModelConstructors.parameters2namedtupleMethod
parameters2namedtuple(m)

returns the parameters of m as a NamedTuple. The input m e acan be either an AbstractVector{<: AbstractParameter} or an AbstractModel.

This function currently does not work with regime-switching parameters.

source
ModelConstructors.transform_to_model_spaceMethod
transform_to_model_space{S<:Real,T<:Number, U<:Transform}(p::Parameter{S,T,U}, x::S)

Transforms x from the real line to lie between p.valuebounds without updating p.value. The transformations are defined as follows, where (a,b) = p.transform_parameterization and c a scalar (default=1):

  • Untransformed: x
  • SquareRoot: (a+b)/2 + (b-a)/2 * c * x/sqrt(1 + c^2 * x^2)
  • Exponential: a + exp(c*(x-b))
source
ModelConstructors.transform_to_real_lineMethod
transform_to_real_line(p::Parameter{S,T,U}, x::S = p.value) where {S<:Real, T<:Number, U<:Transform}

Transforms p.value from model space (between p.valuebounds) to the real line, without updating p.value. The transformations are defined as follows, where (a,b) = p.transform_parameterization, c a scalar (default=1), and x = p.value:

  • Untransformed: x
  • SquareRoot: (1/c)*cx/sqrt(1 - cx^2), where cx = 2 * (x - (a+b)/2)/(b-a)
  • Exponential: b + (1 / c) * log(x-a)
source
ModelConstructors.update!Method
update!(pvec::ParameterVector, values::AbstractVector{S},
    indices::BitArray{1}; change_value_type::Bool = false) where S

Updates a subset of parameters in pvec specified by indices. Assumes values is sorted in the same order as the parameters in pvec, ignoring parameters that are to be left unchanged.

However, update! will not overwrite fixed parameters, even if indices has a true in an index corresponding to a fixed parameter.

Examples

julia> pvec = ParameterVector{Float64}(undef, 3);
julia> pvec[1] = parameter(:a, 1., (0., 3.), (0., 3.), fixed = false);
julia> pvec[2] = parameter(:b, 1.);
julia> pvec[3] = parameter(:c, 1., (0., 3.), (0., 3.), fixed = false);
julia> values = [2., 2.];
julia> update!(pvec, values, [true, false, true]);
julia> map(x -> x.value, pvec)
3-element Array{Float64,1}:
 2.0
 1.0
 2.0
julia> pvec = ParameterVector{Float64}(undef, 3);
julia> pvec[1] = parameter(:a, 1.);
julia> pvec[2] = parameter(:b, 1.);
julia> pvec[3] = parameter(:c, 1.);
julia> values = [2., 2.];
julia> update!(pvec, values, [true, false, true]);
julia> map(x -> x.value, pvec)
3-element Array{Float64,1}:
 1.0
 1.0
 1.0
source
ModelConstructors.update!Method
update!(pvec::ParameterVector, values::AbstractVector{S}; change_value_type::Bool = false) where S

Update all parameters in pvec that are not fixed with values. Length of values does not need equal length of pvec (as in the case of regime-switching parameters). Function optimized for speed.

source
ModelConstructors.updateMethod
update(pvec::ParameterVector, values::Vector{S}) where S

Returns a copy of pvec where non-fixed parameter values are updated to values. pvec remains unchanged. Length of values must equal length of pvec.

We define the non-mutating version like this because we need the type stability of map!

source

Regime-Switching Interface

To implement regime-switching, we add a field to Parameter types called regimes::Dict{Symbol, OrderedDict{Int, Any}}. The keys of the top level dictionary are the names of the other fields in a Parameter type, e.g. :value. Each key then points to an OrderedDict, whose keys are the numbers of different regimes and values are the corresponding values for each regime.

The field regimes functions as a "storage" of information. When a Parameter type interacts with another object in Julia, e.g. p + 1., where p is a Parameter, what actually happens is p.value + 1.. Only the current fields of p will be used when interacting with other objects. To use a different value (or different fields) from another regime, the user needs to tell the parameter to switch regimes the toggle_regime! function (see below).

By default, the regimes field is empty (see the documentation of the parameter function in Parameter Interface). To add values, either pass in the dictionary as a keyword to parameter or use set_regime_val!. Note that the latter function is not exported.

Note that regimes must be sorted in order because we store the regimes as an OrderedDict, and OrderedDict objects are sorted by insertion order.

ModelConstructors.regime_fixedMethod
regime_fixed(p::Parameter{S}, i::Int) where S <: Real
regime_fixed(p::Parameter{S}, model_regime::Int, d::AbstractDict{Int, Int}) where S <: Real

returns whether p is fixed in regime i for the first method and whether true p is fixed in regime d[model_regime] for the second method.

source
ModelConstructors.regime_priorMethod
regime_prior(p::Parameter{S}, i::Int) where S <: Real
regime_prior(p::Parameter{S}, model_regime::Int, d::AbstractDict{Int, Int}) where S <: Real

returns the prior of p in regime i for the first method and the prior of p in regime d[model_regime] for the second.

source
ModelConstructors.regime_valMethod
regime_val(p::Parameter{S}, i::Int) where S <: Real
regime_val(p::Parameter{S}, model_regime::Int, d::AbstractDict{Int, Int}) where S <: Real

returns the value of p in regime i for the first method and the value of p in regime d[model_regime for the second.

source
ModelConstructors.regime_valueboundsMethod
regime_valuebounds(p::Parameter{S}, i::Int) where S <: Real
regime_valuebounds(p::Parameter{S}, model_regime::Int, d::AbstractDict{Int, Int}) where S <: Real

returns the valuebounds of p in regime i for the first method and the valuebounds of p in regime d[model_regime] for the second method.

source
ModelConstructors.set_regime_fixed!Method
set_regime_fixed!(p::Parameter{S}, i::Int, v::S; update_valuebounds::Interval = (NaN, NaN))

sets whether p is fixed in regime i of p. Set update_valuebounds to true to set the valuebounds to match the fixed value.

The second method allows the user to pass a dictionary to permit the case where there may be differences between the regimes of a regime-switching model and the regimes for the parameters. For example, aside from regime-switching in parameters, the model may also include other forms of regime-switching. To allow estimation of regime-switching parameters in such a model, the dictionary d maps each "model" regime to a "parameter" regime. In this way, the second method specifies which "parameter" regime should be used at a given "model" regime.

source
ModelConstructors.set_regime_prior!Method
set_regime_prior!(p::Parameter{S}, i::Int, v)
set_regime_prior!(p::Parameter{S}, model_regime::Int, v, d::AbstractDict{Int, Int})

sets the prior in regime i of p to be v. The type of v can be a NullablePriorUnivariate, NullablePriorMultivariate, ContinuousUnivariateDistribution, or `ContinuousMultivariateDistribution'.

The second method allows the user to pass a dictionary to permit the case where there may be differences between the regimes of a regime-switching model and the regimes for the parameters. For example, aside from regime-switching in parameters, the model may also include other forms of regime-switching. To allow estimation of regime-switching parameters in such a model, the dictionary d maps each "model" regime to a "parameter" regime. In this way, the second method specifies which "parameter" regime should be used at a given "model" regime.

source
ModelConstructors.set_regime_val!Method
 set_regime_val!(p::Parameter{S},
    i::Int, v::S; override_bounds::Bool = false) where S <: Real
 set_regime_val!(p::Parameter{S},
    model_regime::Int, v::S, d::AbstractDict{Int, Int}; override_bounds::Bool = false) where S <: Real

sets the value in regime i of p to be v. By default, we enforce the bounds that are currently in p, but the bounds can be ignoerd by setting override_bounds = true.

The second method allows the user to pass a dictionary to permit the case where there may be differences between the regimes of a regime-switching model and the regimes for the parameters. For example, aside from regime-switching in parameters, the model may also include other forms of regime-switching. To allow estimation of regime-switching parameters in such a model, the dictionary d maps each "model" regime to a "parameter" regime. In this way, the second method specifies which "parameter" regime should be used at a given "model" regime.

source
ModelConstructors.set_regime_valuebounds!Method
set_regime_valuebounds!(p::Parameter{S}, i::Int, v::S)

sets valuebounds for p in regime i to v.

The second method allows the user to pass a dictionary to permit the case where there may be differences between the regimes of a regime-switching model and the regimes for the parameters. For example, aside from regime-switching in parameters, the model may also include other forms of regime-switching. To allow estimation of regime-switching parameters in such a model, the dictionary d maps each "model" regime to a "parameter" regime. In this way, the second method specifies which "parameter" regime should be used at a given "model" regime.

source
ModelConstructors.toggle_regime!Method
toggle_regime!(p::Parameter{S}, i::Int) where S <: Real
toggle_regime!(pvec::ParameterVector{S}, i::Int) where S <: Real
toggle_regime!(p::Parameter{S}, model_regime::Int, d::AbstractDict{Int, Int}) where S <: Real
toggle_regime!(pvec::ParameterVector{S}, model_regime::Int, d::AbstractDict{Symbol, <: AbstractDict{Int, Int}}) where S <: Real
toggle_regime!(pvec::ParameterVector{S}, model_regime::Int, d::AbstractDict{Int, Int}) where S <: Real

changes the fields of p to regime i.

For example, if

p.regimes[:value] = OrderedDict{Int, Any}(1 => 1, 2 => 3)

then toggle_regime!(p, 1) will cause p.value = 1 and toggle_regime!(p, 2) will cause p.value = 3.

The third method allows the user to pass a dictionary to permit the case where there may be differences between the regimes of a regime-switching model and the regimes for the parameters. For example, aside from regime-switching in parameters, the model may also include other forms of regime-switching. To allow estimation of regime-switching parameters in such a model, the dictionary d maps each "model" regime to a "parameter" regime. In this way, the second method specifies which "parameter" regime should be used at a given "model" regime.

The fourth method extends the third to a ParameterVector, with the possibility that each parameter may have different mappings to the model regimes. Each key of d corresponds to the key of a parameter, and each value of d is the mapping for model regimes to the parameter regimes of p.key. The fifth method is similar to the fourth but assumes any regime-switching parameter has the same mapping from model regimes to parameter regimes, hence the use of a common dictionary.

source
ModelConstructors.get_valuesMethod
get_values(pvec::ParameterVector{S}; regime_switching::Bool = true) where {S <: Real}

constructs a vector of the underlying values in a ParameterVector, including if there are regime-switching values.

source

Setting Interface

Base.:<=Method
(<=)(m::AbstractModel, s::Setting)

Syntax for adding a setting to a model/overwriting a setting via m <= Setting(...)

source
ModelConstructors.update!Method
update!(a::Setting, b::Setting)

Update a with the fields of b if:

  • The key field is updated if a.key == b.key
  • The print boolean and code string are overwritten if a.print is false and b.print is true, or a.print is true, b.print is false, and b.code is non-empty.
  • The description field is updated if b.description is nonempty
source
rng::MersenneTwister
testing::Bool
observable_mappings::Dict{Symbol, Observable}
pseudo_observable_mappings::Dict{Symbol, PseudoObservable}
  • 1By solving the model, we mean a mapping from parameters to some objects of interest. In a state space model, solving the model is a mapping from parameters to a state transition function. By constructing
  • 2In the context of a state space model, a measurement equation is mapping from states to observable data.
  • 3In DSGE.jl, we implement a load_data function that parses input_series to retrieve data from FRED. To take full advantage of the Observable type, users may want to write their own load_data function. For example, it may be convenient to write a load_data function that parses input_series to select column(s) from saved CSV files and combines them into a single data frame.