Troubleshooting

This page gathers the most common things that go wrong in a RISE workflow, how to diagnose them, and what to try. The single most useful diagnostic is decipher – always run it first when a retcode comes back non-zero.

Deciphering return codes

Most RISE entry points (solve, filter, estimate, forecast, simulate) return a retcode integer alongside their main outputs. A non-zero value signals a problem; decipher turns it into a sentence:

[m, retcode] = solve(m);

if retcode ~= 0

    decipher(retcode)        % prints the explanation

    msg = decipher(retcode); % or capture as a string

end

decipher accepts a scalar or a vector of codes. Codes are organised by the stage of the pipeline that emits them.

Codes by category

Range

Stage

Codes

1-7, 11

Evaluating the system

1 steady state could not solve; 2 NaNs in Jacobian; 3 problem in transition matrix; 4 parameter restrictions violated; 5 NaN/Inf/complex definitions; 6 NaN/Inf in planner objective; 7 bounds or restrictions violated; 11 complex or NaN steady-state residuals.

21-28, 201-263

Solving the system

21 max iterations or multiple solutions; 22 NaNs in solution / no solution; 23 explosion limit reached; 24 explosive solution; 25 system unstable; 26 VAR approximation to the DSGE failed; 27 log-expansion of a near-zero steady state; 28 transition matrix must be diagonal for this solver; 201/202 non-DSGE max iters / NaNs in solution; 210/230 discretionary outer loop hit max-iter / explosion-limit; 223 complex solution; 251 DSGE-VAR solution: system unstable; 261 DSGE prior not proper (need lambda*T > k); 262 DSGE-VAR covariance not positive definite; 263 SVAR solution matrix not invertible.

301-313

Filtering / likelihood

301 Lyapunov solve out of iterations; 302 NaNs in Lyapunov; 303 Lyapunov explosion limit reached; 304 failure of covariance in sigma points; 305 covariance of forecast errors not positive definite; 306 unlikely parameter vector; 307 NaN/Inf/complex log prior; 308 inconsistent ergodic probabilities; 309 user-defined endogenous priors failed; 310 NaN/Inf in initial state vector; 311 NaN/Inf in initial covariance matrix; 312 NaN/Inf/complex likelihood; 313 forecast covariance positive definite but determinant 0.

-403 to 405, 4002

Optimization (mode search)

400 too many function evaluations or iterations; 402-405 step / objective / search-direction change too small (normal convergence stops, not errors); -401 terminated by output / plot function; -402 no feasible point / bounds inconsistent; -403 trust-region radius too small (problem unbounded); 4002 finding hyperparameters failed.

500

Data

500 no actual data or simulation provided for filtering / estimation.

603-604

Code generation (writing functions to disk)

603 empty cell; 604 cell content is not a function handle.

701-707

Forecasting / simulation

701 constraint violation; 702 rank deficiency in null and column spaces; 703 no feasible path; 704 invalid simulation (complex solutions or pruning needed); 705 more restrictions than shocks; 706 too few forward-looking steps to handle the constraint(s); 707 invalid simulation (generic).

801

VAR

801 no suitable rotation could be found.

The canonical table (the single source of truth) lives in m/+utils/+error/registry.m; decipher reads it and prints the message for a given code. decipher is also re-exposed as a top-level shortcut (m/shortcuts/decipher.m), and the codes are available as named constants via utils.error.codes.

When the steady state does not solve

The first thing solve does is build a steady state. The most common causes of failure (retcodes 1, 11):

  1. Parameters not all assigned. Check any(isnan(m.parameter_values)) (or isnan(m) for the same thing on the model object). Missing parameters surface as NaN propagation through the residual function.

  2. Non-stationary model on a stationary solver. If the model has a balanced growth path, tell RISE:

    m = set(m, 'solve_bgp', true);
    
  3. User-supplied analytical steady state. If a @steady_state_model block or an external sstate_file is in play, the most likely cause of failure is a bug in that block or file rather than in RISE. Verify against a small test case (parameters where the steady state is known by hand).

  4. Variables that can only be positive (consumption, capital, prices) freely sampled into the negative orthant by the steady-state solver. Either set their bounds accordingly, or declare them as log variables:

    @endogenous(log) C, K, P
    
  5. Starting point. The default solve_initialization='backward' suits most models; for a model where backward solution diverges, try 'zeros' or 'random':

    m = set(m, 'solve_initialization', 'zeros');
    
  6. Tolerances or iteration caps. The steady-state solver is controlled by fix_point_TolFun and fix_point_maxiter. Loosen the tolerance or bump the iteration cap if the residuals are merely close to zero.

When the model does not solve (but the steady state does)

If the steady state is fine but the policy / state-space solution fails, you are looking at a Blanchard-Kahn-style problem (retcodes 22, 24, 25) or numerical breakdown (23, 223):

  • Indeterminacy / no solution (22, 25): typically a mis-specified expectations channel or a missing equation pinning a forward-looking variable. Check that the number of forward-looking variables matches the number of structural shocks plus exogenous jumps.

  • Explosive solution (24): a sunspot in disguise, or a parameter region where the system has no stable rational-expectations equilibrium. Move the parameter vector, or constrain the prior.

  • Explosion limit reached (23): the solver’s iterates grew beyond fix_point_explosion_limit (default 1e12); often a symptom of a bad starting policy from regime switching with too few outer iterations – increase fix_point_maxiter.

  • Complex solution (223): generally means the chosen solver is not appropriate for the model class (e.g. a non-Markovian solver on a regime-switching model). Try a different solver:

    m = set(m, 'solver', 'mfi');
    
  • Log-expansion of a near-zero steady state (27): a variable declared @endogenous(log) has a steady state too close to zero. Either remove the log annotation or rescale the model so the steady state is well away from the floor.

When filtering or likelihood fails

The estimation engine wraps the filter inside the objective; a retcode from the filter propagates up as a -Inf log likelihood. The frequent codes:

  • 305 / 313 (covariance of forecast errors not positive definite, or positive definite but determinant 0): typically a sign that two observables are nearly collinear, or that an observable has near-zero variance in the simulated distribution. Check cov(data) and add a small measurement error if needed.

  • 306 (unlikely parameter vector): the prior puts essentially zero mass on the candidate. Verify the prior bounds match the model scale.

  • 307 (NaN/Inf/complex log prior): a prior distribution returned a non-finite value on the candidate vector. Print the candidate and check it against each prior’s support.

  • 308 (inconsistent ergodic probabilities): the regime-switching transition matrix is mis-built; verify sum(m.markov_chains.transition_matrix, 2) == 1.

  • 309 (endogenous priors failed): the user-supplied estim_endogenous_priors callback errored or returned a non-finite value.

  • 310 / 311 (NaN/Inf in initial state vector / covariance matrix): the unconditional moments don’t exist; usually a unit-root variable that was not declared as such.

  • 312 (NaN/Inf/complex likelihood): the filter completed but the log-likelihood is not a real finite scalar. Usually a downstream consequence of a near-singular forecast covariance.

Debug toggles

Toggle

Effect

debug

general verbose mode; prints intermediate diagnostics in solve and estimate:

m = set(m, 'debug', true);

parse_debug

detailed parser tracing (model-file processing):

m = rise('mymodel', 'parse_debug', true);

fix_point_verbose

per-iteration diagnostics from the fixed-point solvers (steady state, regime-switching solve, loose commitment):

m = set(m, 'fix_point_verbose', true);

Display

'iter' / 'on' / 'off' for any bundled optimizer; surfaces objective values and step sizes during the mode search.

Tightening tolerances and iteration caps

When a solve / filter / estimate “almost works”, these are the knobs:

Option

Controls

Default

fix_point_TolFun

convergence tolerance for fixed-point solvers (steady state, MS solve, loose commitment)

sqrt(eps)

fix_point_maxiter

max iterations for the same

1000

fix_point_discretion_maxiter

max iterations for the discretion / stochastic-replanning fixed point

100

fix_point_explosion_limit

absolute-value cutoff that triggers a “diverged” early stop

1e12

TolFun / TolX

optimizer-side tolerances

1e-6 / 1e-6

MaxIter / MaxFunEvals

optimizer-side iteration / eval caps

optimizer-specific

Profiling

When a workflow is slow but correct, the recipe is the same as for any MATLAB code:

profile -timer 'cpu' on
% the slow call (e.g. an estimation)
m = estimate(m, 'data', db, 'priors', priors);
profile off
profile viewer

Reading the resulting report, the bottleneck candidates ranked by how often they bite in RISE:

  1. Symbolic differentiation (one-shot, parse time). Dominant on large models – 5000+ equations may take minutes. Once paid, it is cached in the model’s +routines package and not redone unless the model file changes.

  2. Evaluation of compiled derivatives, per iteration of the objective. The standard bottleneck inside MCMC and posterior maximization.

  3. Forecasting step inside filtration. For multi-regime models the regime-conditional forecast is the most expensive part of the likelihood evaluation.

  4. Loops in filtration for long sample / many regimes – usually vectorised already, but worth checking after a user-defined filter change.

  5. Automatic differentiation if solve_order >= 2 and higher-order derivatives are recomputed every iteration – consider caching at compile time where the model structure allows.

  6. Inaccuracies in numerical derivatives – not a speed problem per se, but a convergence problem that looks like one when the optimizer wastes iterations on noisy gradients.

  7. Disk I/O if solve_function_mode = 'disk' is set; default is in-memory. Disk mode is opt-in; switch back if your model is small enough.

The estimation summary printed by print_estimation_results reports the wall-clock time, the number of function evaluations, and the optimizer used – a useful first cut before reaching for profile.