7. Failure diagnosis: diagnose(m) and friends
Models fail: a steady state that will not solve, a solution that does not exist or is not stable, a simulation that quietly turns into NaNs, a perfect-foresight path the solver cannot reach. RISE’s failure-diagnosis suite turns each of those from a terse return code into a diagnosis: what check failed, observed versus required, who is implicated, and the next command to run. Two principles run through it:
no silent failures — anything that used to fail quietly now says where and why it died (governable, see the option below);
the right vocabulary per model class — eigenvalue (Blanchard-Kahn) counting is only ever used for constant-parameter models; for regime-switching models stability is judged by mean-square stability (MSS), and the diagnosis speaks that language exclusively.
7.1. One entry point
diagnose(m) % prints the diagnosis for whatever fails first
report = diagnose(m) % same, as a struct
diagnose checks the steady state first, then the solution. What you
get, by failure class:
Steady state fails. The worst equations ranked against the engine’s acceptance floor, labeled by their tag (or by the equation text itself for untagged models), the variables and parameters entering them, and a structural-rank check that names declared-but-never-used variables outright. When most equations are above the floor, the report says the failure is widespread — one global culprit (a bad discount/depreciation /growth number, a bad starting point) is more likely than the top-ranked equations — instead of overattributing.
No solution / unstable solution. For a constant-parameter model: root counting, with the root table (the failed solve retains its eigenvalues), the stable-versus-predetermined comparison, and — for the indeterminate case — a clearly-labeled heuristic (in New-Keynesian models, check the Taylor principle). For a regime-switching model: the MSS spectral radius against the criterion, each regime’s conditional spectral radius and persistence, and the culprit regime:
No mean-square-stable solution: ... The MSS spectral radius is 1.666,
above the criterion 1 ...
regime 1: conditional radius 0.9402, persistence 0.95
regime 2: conditional radius 1.322, persistence 0.95
Regime(s) 2 are conditionally explosive and, with the persistence
above, the process spends long enough there for second moments to
explode on average.
Note what that example teaches: a regime may be conditionally explosive and the model still be MSS — the mix is what matters, and only the MSS radius decides.
Simulation diverges. No more NaN-filled databases without a word: the failure site reports the first non-finite value (period, variable, regime), the growth profile of the run-up, and the pruning hint when simulating above order 1 unpruned.
Perfect foresight does not converge. The stacked residual at the solver’s final iterate is reshaped to (equation x period) and the worst cells are reported, plus the periods carrying 80% of the residual mass — “equation 3 around periods 12–15” localizes what “did not converge” cannot.
7.2. Fix-actions
Diagnosis ends with something to do, and two of the actions are automated:
Steady-state homotopy. Every successful steady-state solve remembers its calibration. When a later calibration fails:
[report, mfixed] = diagnose(m, 'fix', "homotopy");
walks the parameters gradually from the last working calibration to the
failing one, warm-starting each step from the previous solution. On
success, mfixed comes back solved at the target. On a stall, the
breakpoint is the diagnosis: “solvable up to 47% of the way — the
target calibration likely admits no steady state”, with mfixed solved
at the breakpoint mix so you can inspect how the steady state degenerates.
Perfect-foresight shock continuation. With simul_homotopy=true, a
failed stacked solve is retried by walking the shock size up from zero,
each step warm-started from the previous path. A stall short of full size
is reported as “solvable up to X% of the shock size” — the shock, not the
solver, is the problem.
7.3. The volume knob
m = set(m, 'diagnostics_on_failure', 'warn'); % default
% 'error' % throw instead
% 'off' % legacy silence
This gates every failure-site diagnostic (simulation divergence, PF worst cells, continuation summaries). Estimation is untouched by design — the likelihood loop stays silent at any setting.