5. Forecasting and simulation
Once a model is solved, the two main entry points for forward-looking
analysis are simulate and forecast. Both return
structs of ts time-series objects keyed by endogenous-variable
name and respect the model’s regime structure when there is one.
For deterministic scenarios, perfect_foresight is the
third entry point.
All three consume the same simulation plan – the universal mechanism for choosing initial conditions, pinning endogenous variables, activating shocks, announcing anticipated shocks, or imposing algebraic constraints. This chapter is the reference for the three consumers. It assumes you have read Simulation plans; the constructions used below are all from that chapter.
5.1. simulate
simulate propagates the perturbation solution forward
through a plan. With no plan it draws an unconditional path:
sim = simulate(m, simul_periods = 200);
plot(sim.PAI, sim.Y);
With a plan, every choice on that path is the plan’s: pinned
shocks stay pinned, NaN shocks are backed out, pinned
endogenous variables hit their conditioning values, anticipated
shocks act through the perturbation solution’s anticipation
horizon:
sp = simplan(m, [rq(2020,1), rq(2030,4)], 1, 'shockInit', 'zero');
sp = append(sp, 'EETA', rq(2020,2), 2.0);
sim = simulate(m, simul_historical_data = sp);
For a single-regime constant-parameter model, sim is a struct
of ts keyed by endogenous-variable name. For a
regime-switching model the output structure depends on
simul_to_time_series (see below).
5.1.1. Common options
Option |
Effect |
|---|---|
|
Number of periods to simulate. Default |
|
Burn-in periods discarded from the head of the returned series. Default |
|
Seed forwarded to MATLAB’s RNG before drawing. |
|
When |
|
Initial conditions and / or a conditioning path. Accepts a struct of |
|
Last date of the history segment (the point from which the simulation continues forward). Inferred from the plan when one is passed. |
|
Pin the regime path. Pass an integer for a constant regime, a vector for a fixed path, or a function handle for a state-dependent switch rule. |
|
When |
|
Output shape – see below. Default |
5.1.2. Output shape
By default (simul_to_time_series = true) the output is a struct
of ts keyed by variable name. For switching models with multiple
parameterizations or solution branches, the underlying numeric
representation has extra dimensions for parameterization and
regime path; the ts object carries them in its pages.
When you need the raw numeric arrays – for instance to feed into a
hand-rolled diagnostic – set simul_to_time_series = false:
[db, info] = simulate(m, ...
simul_periods = 200, ...
simul_to_time_series = false);
db is then a numeric matrix and info carries the layout
information needed to reconstruct the ts if you ever want it
back.
Note
Even with simul_to_time_series = true, regime-switching
simulations can in some configurations return a numeric matrix
rather than a ts struct (an artefact of the underlying
solution shape). Confirm with isstruct(sim) before reaching
for sim.varname; if you got a matrix, the columns are the
variables in the order returned by get(m, 'endo_list').
5.1.3. Returning the regime path
simulate returns the realised composite-regime sequence
as its second output:
[sim, states] = simulate(m, simul_periods = 200);
states is a vector of integer regime IDs over the simulation
horizon. Plot it to see when each regime was active.
5.2. Theoretical and sample moments
For linear solved models the unconditional first and second
moments are available in closed form via the Lyapunov equation,
returned by theoretical_autocovariances and the
theoretical_autocorrelations wrapper:
[Acov, info, retcode] = theoretical_autocovariances(m, 10);
Acorr = theoretical_autocorrelations(m, autocorr_ar = 10);
For higher-order or regime-switching models, simulate a long path
and compute sample moments from the ts (mean, var,
cov, corrcoef are overloaded on ts). The two should
agree at order 1 up to Monte Carlo error.
The variance-decomposition routine is documented in the legacy chapter Master stoch simul and remains canonical for the modern toolbox.
5.3. Pruned simulations (higher order)
RISE solves and simulates regime-switching DSGE models up to
fifth-order perturbation. At order \(\geq 2\) a straight
perturbation simulation is not guaranteed to be bounded: the
policy is a polynomial in the state, and iterating it forward lets the
higher-order terms feed on themselves, so an unlucky shock sequence can
send the state off to inf. Pruning (Andreasen, Fernández-Villaverde
and Rubio-Ramírez, 2018, generalized here to regime switching) removes
those spurious higher-order terms at simulation time without changing
the policy function, keeping the path bounded and the implied moments
well defined.
Pruning is controlled by the simul_pruned option, honored by
simulate, irf, forecast and any path that iterates the
solution forward:
sim = simulate(m, simul_periods = 200, simul_pruned = true);
simul_pruned accepts:
false(default) — no pruning;true— prune with the built-in automatic scheme (one_step_pruning_automatic);the name of a pruning routine (
'one_step_pruning'or'one_step_pruning_automatic'), or a function handle, to select a specific scheme.
Pruning applies only at order \(\geq 2\) (at order 1 the recursion is already linear and bounded) and is implemented up to order 5.
Note
Pruning changes the object you simulate: the pruned recursion is a stabilized companion of the policy, not the policy itself. It is the right tool for moments and long simulations at high order, but it is a modeling choice — an order-\(\geq 2\) path that explodes without pruning is often telling you that perturbation is being pushed outside its region of validity, where a global method (Taylor projection, Extending RISE through paradigms) may be the better answer. See Solution accuracy for how pruning interacts with the accuracy measures.
5.4. forecast
forecast produces forward paths from the end of history,
with or without conditions and with or without shock uncertainty.
The unconditional case is the trivial one:
fkst = forecast(m, ...
forecast_nsteps = 12, ...
forecast_shock_uncertainty = true);
The conditional case is where forecast earns its keep. The
conditioning is supplied through a simulation plan passed in simul_historical_data:
sp = simplan(m, [rq(2020,1), rq(2030,4)], 1, 'shockInit', 'zero');
sp = append(sp, 'ER', cond_dates, NaN); % activate
sp = append(sp, 'PAI', cond_dates, PAI_target); % condition
fkst = forecast(m, ...
simul_historical_data = sp, ...
forecast_nsteps = numel(cond_dates) + 4, ...
forecast_shock_uncertainty = true);
The plan alone carries the conditioning – the conditioned variables and the freed shocks are read from it, so no separate list of conditioned variables is needed.
Two knobs matter at the consumer side:
Anticipated vs unanticipated – per-condition, encoded in the plan’s pages. Page
1is contemporaneous (unanticipated); pagekis announced \(k-1\) periods ahead. The perturbation solution honours the announcement horizon as long asanticipationHorizoncovered it at plan construction.Surprises vs no surprises past the conditioning window –
forecast_shock_uncertainty.truedraws structural shocks past the window;falsezeroes them and returns the deterministic continuation.
5.4.1. Hard and soft conditions
A hard condition pins a variable to a specific value at a specific date. The identification rule (see Simulation plans) determines whether a plan is solvable.
A soft condition restricts a variable to a band [lo hi]
appended to the plan (see Simulation plans for the band
syntax and free_shocks). Because the conditioned variable is
moved through the freed shock(s), the model assigns it a predictive
distribution at the conditioning date – a normal
\(N(a,\sigma^2)\) whose mean \(a\) is the deterministic
forecast and whose standard deviation \(\sigma\) is the freed
shock’s impulse response. The band truncates that distribution.
The simul_soft_conditioning option chooses what is returned:
'density'(the default once a band is present) runssimul_nsimplans – each a draw from the truncated distribution, pinned and solved as a hard condition – and returns the propagated ensemble.fanchartthen collapses it (below).'mean'returns a single path, the conditional mean (the mean of that ensemble).
Where the band sits relative to the forecast \(a\) is what
matters. A band that straddles \(a\) is mildly truncated and
roughly symmetric. An off-center band clips one tail, so the
distribution is asymmetric and its mean, its median and the hard
pin at the midpoint are three different numbers. A one-sided
band – a floor [lo NaN] – gives a strongly skewed,
zero-lower-bound-style pile-up at the bound. A hard pin discards
this; the soft condition keeps the model’s view of where in the
band the variable is likely to sit:
sp = simplan(m, [rq(2020,1), rq(2023,1)], 1, 'shockInit', 'zero');
sp = free_shocks(sp, 'EPS_D', rq(2020,2)); % free the channel
sp = append(sp, {'Y', rq(2020,2), [0.10 0.70]}); % an off-center band
fkst = forecast(m, simul_historical_data = sp, ...
simul_soft_conditioning = 'density', ...
simul_nsim = 2000);
Setting simul_soft_conditioning = 'mean' instead returns the
single conditional-mean path for the same plan.
5.4.2. The option surface
Option |
Effect |
|---|---|
|
Number of forecasting steps (default |
|
Date when the forecasts start (end of history + 1). |
|
How a band |
|
Number of draws/plans in |
|
Draw structural shocks over the forecast horizon. Default |
|
When |
|
The history-plus-conditioning database. May be a |
|
Last date of the history segment. |
5.5. perfect_foresight
perfect_foresight solves the full nonlinear model as a
boundary-value problem over the plan’s horizon. The plan supplies
the boundary data; the solver returns a deterministic path
consistent with every condition in the plan and with the model
equations at every period, without the perturbation
approximation:
pfs = perfect_foresight(m, simul_historical_data = sp);
Because perfect_foresight and simulate consume the
same plan, a side-by-side comparison is the canonical check on
the approximation error of the perturbation solution: when the
two outputs agree, the deviation is small enough that the
nonlinearity does not matter for the scenario; when they diverge,
the nonlinear path is the accurate one and the perturbation path
is the local approximation.
Occasionally-binding constraints are honoured exactly by
perfect_foresight; see
Occasionally-binding constraints
for the deterministic-OBC patterns.
5.6. Fan charts
A forecast that carries multiple draws – the 'density' soft
conditioning above, or a stochastic forecast with
forecast_shock_uncertainty = true – is collapsed into central
tendency and probability bands by fanchart and
plot_fanchart:
fkst = forecast(m, simul_historical_data = sp, ...
simul_soft_conditioning = 'density', ...
simul_nsim = 1000);
out = fanchart(fkst.PAI, [30 50 68 90]);
plot_fanchart(out, [244 122 66]/255);
The percentages are the central probability mass enclosed by each band. The plotting tools live in the legacy chapter Plotting tools and apply unchanged to the modern toolbox.
5.7. Relative-entropy tilting
Relative-entropy tilting is a fourth mechanism, distinct from
the plan-based routes above: it does not produce a new forecast
path; it reweights an existing forecast ensemble (e.g. the
output of forecast(..., forecast_shock_uncertainty = true))
so that its moments satisfy a user-specified constraint.
The algorithm and its option surface are documented in the legacy chapter Conditional forecasting Using Relative Entropy. Use it when you already have an unconditional ensemble and want to constrain its moments (e.g. “the mean inflation forecast over the next 8 quarters equals 2%”) rather than its paths.
5.8. Smoother-based conditional forecasts
For linear models with conditions on observed variables, the
Kalman smoother imputes the conditioning path directly from the
data, without a simulation plan – treat the conditioning values
as observations on the forecast horizon, mark every other
observable as NaN for those dates, and run filter.
The output is the smoothed path for every endogenous variable.
This route is based on least squares and inherits its tradeoffs. It is documented in Filtering; use it when the conditions are observed-side and the model is linear. For anything else – hard endogenous constraints on unobserved states, nonlinear solutions, regime switching, anticipated shocks, soft conditions – use a simulation plan.
5.9. See also
Simulation plans – the construction reference (this chapter is its consumer side).
Filtering – the smoother-based alternative for linear observed-side conditioning.
Deterministic and quasi-deterministic solutions – the deterministic context in full.
Occasionally-binding constraints – scenarios with OBC.
Solution accuracy – how pruning interacts with the Euler-error accuracy measures.
Legacy Master stoch simul – variance decomposition and the impulse-response surface (
irf_type,irf_periods,irf_shock_sign, generalized IRFs, anticipation horizons).Legacy Resimulation and counterfactuals – the
+resim_bridgesurface for Viterbi / FFBS regime histories, counterfactual shock kills, Shapley shock decomposition.Legacy Plotting tools –
quick_irfs,fanchart,plot_fanchart, and the multi-panel layouts.