2.18. Extending the DSGE functionalities
RISE is designed to be pluggable at every step of the standard DSGE
workflow – solve, filter, optimize, sample. The user can replace any of
those steps with a custom routine without touching the rest of the
toolbox. This chapter lists the hooks that the @dsge model object
exposes for that purpose. For users who need to plug in a whole
alternative computational strategy (occbin-style piecewise
linearisation, recursive local linearisation, k-th order Taylor
projection, …), the broader interface is described in
Extending RISE through paradigms; the hooks below
are the lighter, per-routine extension points.
2.18.1. Extracting the structural form
The matrices of the parsed and solved model are reachable via get on
the model object: get(m, 'definitions'), get(m, 'parameters'),
get(m, 'endogenous'), get(m, 'exo_list'), get(m, 'obs_list'),
get(m, 'sstate') (steady state by regime), get(m, 'par_list'),
get(m, 'chain_list') (Markov chain names), and so on. The full list
of accessor strings is documented on the get method of @dsge.
2.18.2. Extracting the solution
After solve succeeds, the solution lives in obj.state_space{1}
(one entry per parameterization) and includes the regime-specific
transition matrices T, the shock-impact matrices R, and the
steady state. The convenience map_solution method packs the
state-space matrices into a struct keyed by Markov regime; together with
get(obj, 'sstate') it is the canonical interface for any
post-processing that does not want to depend on internal field layouts.
2.18.3. Your own solver
Set the solver option to a function handle to replace the built-in
fixed-point iteration with a user routine:
m = set(m, 'solver', @my_solver);
The signature is documented in
Stochastic solution via perturbation. The
bundled solvers (rise_1, mfi, mn, mnk, mfi_full,
loose_commitment) all conform to that signature; the simplest way
to write your own is to copy one of them and modify the inner loop.
2.18.4. Your own filtering algorithm
The Kalman / smoother family is replaceable through the kf_user_algo
option:
m = set(m, 'kf_user_algo', @my_filter);
m = set(m, 'kf_user_algo', {@my_filter, opt1, opt2, ...});
The expected signature is [LogLik, Incr, retcode, Filters] =
my_filter(syst, y, U, z, options, vargs{:}) where syst is the
solved state-space (or model object when the filter needs more than the
default), y and U are the observable and exogenous data, z
is a callable that maps states to observables at each time, and
options carries the filter options. A * prefix on the function
name ('*my_filter') tells RISE that the filter wants the model
object rather than the state-space struct – useful when the user
algorithm needs to call back into the toolbox. The bundled
switching_unscented_kalman_filter,
switching_divided_difference_filter and
switching_cubature_kalman_filter are the reference implementations;
see Nonlinear filtering.
2.18.5. Your own optimizer
Posterior maximization, mode search and global optimisation are routed
through the optimizer option, which accepts a function handle or a
{handle, options-struct} cell:
m = set(m, 'optimizer', @my_opt);
m = set(m, 'optimizer', {@my_opt, struct('MaxIter', 500)});
The expected signature is
[x, fval, exitflag, output] = my_opt(objfun, x0, lb, ub, options).
The bundled optimizers in m/optimizers/ (wcsminwel, wnewrat,
wgmhmaxlik, bee_gate, the +globalopt family rise_de /
rise_cma_es / rise_abc / rise_lshade / rise_jso / …)
all conform to that signature and serve as templates; see
Posterior maximization.
2.18.6. Your own Likelihood function
The Bayesian estimation entry point routes the likelihood through
obj.routines.likelihood. To plug in a custom likelihood – e.g. a
DSGE-VAR likelihood or a moment-matching pseudo-likelihood – set that
field after constructing the model and before calling estimate:
obj = add_to_routines(obj, 'likelihood', @my_likelihood);
The expected signature is
[LogLik, Incr, retcode, obj, filtration] = my_likelihood(params, obj).
The default is the Kalman-based likelihood for a linearised model (the
configured nonlinear filter is invoked automatically at higher order),
and the DSGE-VAR variant likelihood_dsge_var shows how a non-Kalman
likelihood plugs in.
Relation to Indirect Inference
The indirect_inference entry point (see
Indirect Inference) is a thin wrapper around the
same optimization machinery, but with a criterion function in place
of the likelihood. The user supplies the criterion via
indirect_inference(m, myobjective, 'priors', priors), where
[critmin, retcode] = myobjective(m) returns the (weighted) distance
between the model’s auxiliary statistics and their data counterparts.
Replace myobjective to switch between GMM (theoretical moments),
SMM (sample moments from simulation) and IRF matching (simulated vs
empirical IRFs) without touching anything else in the pipeline.
2.18.7. Your own posterior sampler
The MCMC samplers (rsamplers.rwmh, rsamplers.imh,
rsamplers.slice, rsamplers.apt) are independent of the model
object – they take a target-log-density function handle, an initial
point, bounds, and an options struct. To use a custom sampler, build a
small wrapper that calls pull_objective(m) to obtain the target
handle and then drives the sampler. The rsamplers_* files in
m/+rsamplers/ are the reference implementations and demonstrate
the contract that utils.sampling infrastructure (progress monitor,
checkpointing, diagnostics) expects.
2.18.8. Where this is not enough
The hooks above replace one routine at a time. If the goal is to plug in a whole solution paradigm – piecewise-linear OccBin, recursive local linearisation, k-th order Taylor projection – the per-routine extension surface is too small, because such paradigms need to own the solution representation as well. The Extending RISE through paradigms chapter covers that broader extension surface and is the right place to start for a new computational strategy.