🇬🇧 English · 🇪🇸 Español
Structural solver validation status
Updated 23 September 2026 with a row for the stacked Newton-Krylov and with the rows revised by the post-4.3.0 audit fixes. The 20 September 2026 update followed the independent counterexamples of an internal critical evaluation and of its live Dynare follow-up. Feature availability, numerical convergence, and independent replication are separate claims. Passing a residual check does not validate an economic model.
| Surface | Supported calculation and acceptance contract | Evidence and remaining limits |
|---|---|---|
| DSGE order three | Risk slopes include state timing, Hessian chain terms and third-derivative variance contractions. Parsed models use symbolic derivatives; callable-model fallback has finite-difference accuracy. | Five models executed in live Dynare 7.0 at orders two and three, including correlated shocks, nonlinear states and an RBC model. All unfolded tensors, risk corrections, covariance and common-innovation simulations/moments agree (maximum tensor error 1.15e-13; path error 2.70e-13). Mixed-derivative assembly now preserves each index permutation. This is a bounded benchmark, not general parity. MATLAB completed exports but crashed on shutdown; see tools/reference_validation/README.md. Exact pruned moments (after 4.3.0): the state-space identity reproduces the pruned simulator to 2e-15; means and covariances equal Dynare 8's pruned moments on six models (2e-13) and a closed form (3e-15). Dynare's order-3 autocorrelations omit a correlation term; a closed form and Dynare's own 4e6-period simulations agree with puremacro (tests/test_dsge_order3_moments.py). |
| Dynare parity dashboard | Orders one and two compare steady states and all requested tensors (ghx, ghu, ghxx, ghxu, ghuu, ghs2), aligned by labels. Supplied oo_.mean/var/autocorr are compared with the moments of the object solved at the compared order: at order 2, the risk-corrected mean and first-order second moments, which is the convention of stoch_simul(order=2) without pruning. M_.Sigma_e is compared with the covariance the object declares. Autocorrelation rows of zero-variance variables that are NaN on both sides count as matching and are listed in details["moments_excluded"]. Missing, nonfinite (one-sided), malformed or empty input, including Dynare's initial oo_.dr = [] and record-array loadmat output, is unavailable. |
The live Dynare 7 fixtures were exported with nomoments, so order-2 moment parity is tested against the stoch_simul formulas applied to Dynare's own decision rules (agreement 3.6e-15 mean, 7.0e-14 var, 1.8e-14 autocorr on rbc), not against Dynare-reported moments. A reference from stoch_simul(order=2, pruning) reports pruned second moments: compare it with pruning=True, which uses the exact pruned moments (Dynare 8's pruned means, covariances and autocorrelations reproduced to 7e-14 on the five live models and on a closed-form model). Self-generated serialization fixtures are internal tests, not external Dynare oracles. The parity CLI cannot consume references and exits with an explanatory error; use the Python API. |
| Markov-switching DSGE | Coupled-equation residuals are recomputed at the returned matrices. Convergence is diff <= tol (absolute), or every residual entry within tol or within rtol (64 eps) of the magnitudes of its own cancelling terms (componentwise backward error; the smallest certifying rtol is reported as relative_residual). Failed solutions have unavailable shock impacts, stability radii and ergodic moments. |
A real-root failure must not return finite equilibrium covariance or a stability certificate. Finite moments also require stability after convergence. Tested cases: uniform rescaling by up to 1e9; one equation rescaled by 1e6 and 1e9 on three models (the O(1) equations still meet tol and T stays within 1e-9 of the unscaled solution); variable units spanning 1e14; O(1) models (including a user-tightened tol) bit-identical to the absolute gate. |
| Stacked-time Newton-Krylov | solve_stacked_newton_krylov accepts a path only when the stacked residual satisfies |F|_inf <= tol * s (s the steady-block scale) and the next Newton step is below 1e-10 * max(1, |Y|_inf); step_tol=None drops the step test (with jacobian_scale=1.0 the test is then |F|_inf <= tol). The Newton move is bounded in path units (0.75 * max(1, |Y|_inf)) and accepted by a 24-trial Armijo line search. A direction returned at the Krylov cap is recorded (status and recomputed true linear residual) and line-searched like any other; it is not an error by itself. Failed line searches, iteration limits, errors or nonfinite values inside a linear solve, a residual at a noise floor that meets the tolerance while the step test cannot, and a steady-state stacked matrix that cannot be factored raise StackedNewtonError with the last state attached and the cause in converged_by. Block-Thomas pivots with a LAPACK condition estimate below n * eps, or a factorization whose one-vector backward error exceeds 1e-10, fall back to SuperLU (recorded in the preconditioner name). When both inputs are StackedNewtonResult objects, compare_horizons requires converged inputs, identical boundaries and the same exogenous path held beyond the short horizon; arrays, DataFrames and PerfectForesightResult inputs are compared as given. |
Dense-solve oracles to 1e-12 (constant and per-date blocks, with and without time blocks), exact-inverse identity in one Krylov cycle, parity with solve_perfect_foresight on Ramsey and a 20-sector growth transition to 1e-8 (observed 7e-11 and 3.5e-11), equation-scaling invariance (1e-9) and variable-scaling invariance (units 1e-3 to 1e4, 1e-9 relative, same Newton counts), rounding-level and ill-conditioned pivot regressions (rank-deficient B = U V, stacked condition numbers 616 and 941; B = [[1, 1], [1, 1 + 1e-12]]), and the failure contract including errors inside the Krylov solve and a 1e-12 residual noise floor. Evidence covers analytic DSGE-size fixtures only. Memory is O(T n^2) (about 3.2 GB at n_vars = 1000, T = 200); systems with thousands of unknowns per date need a structured elimination, and none ships. Block structure alone does not establish speed or memory at scale, and the linear identities do not by themselves guarantee numerical accuracy or nonlinear convergence. No large-scale transition is solved here; MCP and the surprise mode of solve_perfect_foresight are unavailable. See usage and evidence. |
| VFI projections | Euler collocation, FEM, Smolyak and spline flags require finite final residuals within the requested tolerance. Optimizer termination alone is insufficient. | A fitting-node residual does not establish off-grid accuracy, feasibility, complementarity or global optimality. Adverse domains can return useful approximations with converged=False. Bellman variants have their own iteration criteria. |
| Collocation auxiliary values | Policy and value use one CRRA curvature (sigma, alias gamma) and the model's productivity in the Euler, Coleman, Bellman and policy-evaluation blocks. User return_fn/euler_residual_fn parameters are passed through uninterpreted. |
Tested cases: the deterministic log-growth closed form under every spelling; the Bellman identity under CRRA(2) and CRRA(3); Euler/Bellman policy agreement (1.1e-6) at sigma = 2; the IFT gamma sensitivity against the sigma column and an FD re-solve. Boundary clamping limits interpretation outside the chosen domain. Since 4.5.0 puremacro.vfi.splines and puremacro.vfi.smolyak follow the same sigma-then-gamma rule; solve_smolyak(method="bellman") covers only log utility with full depreciation and raises otherwise. |
| Deep Macro | Detached-target Euler time iteration. The current-state forward cache is restored before the weight update. | A complete training-step gradient is checked by finite differences. Training success is calibration/seed dependent; even the default deterministic ten-country trajectory can fail the held-out Euler criterion. A small training loss alone is not convergence. |
| VFI sensitivities | IFT linear solves using central-difference residual Jacobians: semi-analytic derivatives. | Accuracy depends on differencing steps and conditioning. Heterogeneous-agent distribution/GE sensitivities are unavailable until differentiated KFE and coupled equilibrium equations are implemented. |
The 110-case validation gallery (release 4.5.0, all passing) contains 59 internal, 31 analytical, 13 package, five SciPy and two published-reference cases. It has multiple tolerance classes. The gallery does not certify every feature, every backend or every deployment.
Puremacro ships no compiled extension of its own. NumPy/SciPy and other base packages include compiled components; the mandatory dependencies also include pandas, Matplotlib and requests. Pyodide importability, realistic browser execution, GPU agreement and bitwise reproducibility require different tests.