Skip to content

Convergence Diagnostics (`src.diagnostics.convergence`)

run_convergence_diagnostics performs structured posterior convergence checks and writes machine-readable outputs to 50_diagnostics/. Its converged field is the primary convergence gate consumed downstream.

from src.diagnostics.convergence import run_convergence_diagnostics
run_convergence_diagnostics(
model: Any,
config: dict[str, Any],
results_dir: str,
) -> dict[str, Any]
ParameterDescription
modelFitted model object exposing idata (or fit_result) with posterior and sample stats.
configRun config dictionary (currently not used for thresholds).
results_dirRun root directory; artefacts are saved under 50_diagnostics/.
FilenameStage folderDescription
convergence_report.json50_diagnostics/Structured report with per-check outcomes and top-level converged.
convergence_report.csv50_diagnostics/Flat check table (check, value, threshold, ok).
rank_trace.png50_diagnostics/Rank-normalised trace plot (az.plot_trace(..., kind="rank_vlines")).
energy_diagnostic.png50_diagnostics/Energy diagnostic overlay (az.plot_energy).

Module constants:

ConstantValueMeaning
RHAT_WARN1.01Preferred upper bound for split rank-normalised R-hat.
RHAT_FAIL1.05Severe R-hat boundary for escalation policy.
ESS_PER_CHAIN100Minimum effective draws per chain; total threshold = 100 * n_chains.
DIVERGENCE_WARN1Any divergence indicates geometry risk.
DIVERGENCE_FAIL10Severe divergence count threshold.

Current converged logic in code is:

  • R-hat: all parameters <= RHAT_WARN.
  • ESS bulk and tail: all parameters >= ESS_PER_CHAIN * n_chains.
  • Divergences: exactly 0.
{
"converged": true,
"n_chains": 4,
"rhat": {
"max": 1.003,
"ok": true,
"problematic_params": []
},
"ess_bulk": {
"min": 910,
"ok": true,
"threshold": 400,
"problematic_params": []
},
"ess_tail": {
"min": 855,
"ok": true,
"threshold": 400,
"problematic_params": []
},
"divergences": {
"count": 0,
"pct": 0.0,
"ok": true
},
"recommendation": "Model has converged. Results are reliable."
}
  • converged = true: posterior exploration is acceptable for downstream interpretation.
  • converged = false: inspect problematic_params and divergence counts before trusting decomposition or optimisation.
  • Energy and rank plots are diagnostic evidence; they do not replace the machine gate fields.
from src.diagnostics.convergence import run_convergence_diagnostics
report = run_convergence_diagnostics(
model=driver.model,
config=driver.config,
results_dir=driver.results_dir,
)
if not report["converged"]:
print(report["recommendation"])
  • Stage: 50_diagnostics/.
  • Feeds convergence gates (g2/g3/g4) through convergence_report.json.
  • converged is the key machine-readable status used by the workflow (including strict gating behaviour when configured).