From a3f914ee2cf056306eedf225e145222f9a6dae22 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:55:50 +0500 Subject: [PATCH 1/9] test: cover diagnostics report json output --- python/tests/test_diagnostics/test_report.py | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 python/tests/test_diagnostics/test_report.py diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py new file mode 100644 index 0000000..aef762d --- /dev/null +++ b/python/tests/test_diagnostics/test_report.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +import json + +from hyperion_diagnostics.report import DiagnosticsReport + + +def test_diagnostics_report_json_serializes_core_fields() -> None: + report = DiagnosticsReport( + model_name="normal_model", + inference_method="hmc", + timestamp="2026-05-09T10:00:00", + config={"num_samples": 100}, + summary_stats={"mu": {"mean": 0.1, "std": 1.2}}, + convergence_metrics={"accept_rate": 0.8}, + ) + + payload = json.loads(report.to_json()) + + assert payload["model_name"] == "normal_model" + assert payload["inference_method"] == "hmc" + assert payload["config"]["num_samples"] == 100 + assert payload["summary_stats"]["mu"]["mean"] == 0.1 From f12388f489b43e4934811fb2e91ae1673dc93f6f Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:56:05 +0500 Subject: [PATCH 2/9] test: cover diagnostics markdown summary table --- python/tests/test_diagnostics/test_report.py | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py index aef762d..f18fa45 100644 --- a/python/tests/test_diagnostics/test_report.py +++ b/python/tests/test_diagnostics/test_report.py @@ -21,3 +21,30 @@ def test_diagnostics_report_json_serializes_core_fields() -> None: assert payload["inference_method"] == "hmc" assert payload["config"]["num_samples"] == 100 assert payload["summary_stats"]["mu"]["mean"] == 0.1 + + +def test_diagnostics_report_markdown_renders_configuration_and_summary_table() -> None: + report = DiagnosticsReport( + model_name="coin_model", + inference_method="nuts", + timestamp="2026-05-09T10:00:00", + config={"num_warmup": 50, "num_samples": 100}, + summary_stats={ + "mu": { + "mean": 2.0, + "std": 0.5, + "median": 2.1, + "ci_5.0%": 1.2, + "ci_95.0%": 2.8, + "ess": 88.0, + } + }, + convergence_metrics={"mu/ess": 88.0}, + ) + + markdown = report.to_markdown() + + assert "# Diagnostics Report: coin_model" in markdown + assert "- **num_warmup:** 50" in markdown + assert "| Parameter | mean | std | median | ci_5.0% | ci_95.0% | ess |" in markdown + assert "| mu | 2.0000 | 0.5000 | 2.1000 | 1.2000 | 2.8000 | 88.0000 |" in markdown From 60e18993711169da5d72ff18224b865f3456d278 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:56:19 +0500 Subject: [PATCH 3/9] test: cover clean diagnostics report generation --- python/tests/test_diagnostics/test_report.py | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py index f18fa45..84343df 100644 --- a/python/tests/test_diagnostics/test_report.py +++ b/python/tests/test_diagnostics/test_report.py @@ -2,7 +2,11 @@ import json +import numpy as np + from hyperion_diagnostics.report import DiagnosticsReport +from hyperion_diagnostics.report import generate_report +from hyperion_inference.base import InferenceResult def test_diagnostics_report_json_serializes_core_fields() -> None: @@ -48,3 +52,22 @@ def test_diagnostics_report_markdown_renders_configuration_and_summary_table() - assert "- **num_warmup:** 50" in markdown assert "| Parameter | mean | std | median | ci_5.0% | ci_95.0% | ess |" in markdown assert "| mu | 2.0000 | 0.5000 | 2.1000 | 1.2000 | 2.8000 | 88.0000 |" in markdown + + +def test_generate_report_adds_clean_conclusion_when_diagnostics_pass() -> None: + result = InferenceResult( + samples={"mu": np.linspace(-1.0, 1.0, 200)}, + diagnostics={"accept_rate": 0.75, "num_divergences": 0}, + ) + + report = generate_report( + result, + model_name="normal_model", + inference_method="hmc", + config={"num_samples": 200}, + ) + + assert report.model_name == "normal_model" + assert report.warnings == [] + assert report.conclusions == ["No convergence issues detected. Results appear reliable."] + assert "mu" in report.summary_stats From bb0ae11201e89cd9c4afd4c0da5a74ec592c7ae5 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:56:35 +0500 Subject: [PATCH 4/9] test: cover low ess report warning --- python/tests/test_diagnostics/test_report.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py index 84343df..c176d56 100644 --- a/python/tests/test_diagnostics/test_report.py +++ b/python/tests/test_diagnostics/test_report.py @@ -71,3 +71,17 @@ def test_generate_report_adds_clean_conclusion_when_diagnostics_pass() -> None: assert report.warnings == [] assert report.conclusions == ["No convergence issues detected. Results appear reliable."] assert "mu" in report.summary_stats + + +def test_generate_report_warns_on_low_effective_sample_size() -> None: + result = InferenceResult( + samples={"mu": np.ones(150)}, + diagnostics={"accept_rate": 0.8, "num_divergences": 0}, + ) + + report = generate_report(result, model_name="sticky_model", inference_method="hmc") + + assert any("Low ESS for mu/ess" in warning for warning in report.warnings) + assert report.conclusions == [ + "1 potential issue(s) detected. Review warnings before trusting results." + ] From bc32dd608618318ef804ea462a4b32a4c20d3b6a Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:56:52 +0500 Subject: [PATCH 5/9] test: cover sampler diagnostics warnings --- python/tests/test_diagnostics/test_report.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py index c176d56..e07d462 100644 --- a/python/tests/test_diagnostics/test_report.py +++ b/python/tests/test_diagnostics/test_report.py @@ -85,3 +85,21 @@ def test_generate_report_warns_on_low_effective_sample_size() -> None: assert report.conclusions == [ "1 potential issue(s) detected. Review warnings before trusting results." ] + + +def test_generate_report_warns_on_sampler_diagnostics() -> None: + rng = np.random.default_rng(7) + result = InferenceResult( + samples={"mu": rng.normal(size=500)}, + diagnostics={ + "accept_rate": 0.4, + "num_divergences": 2, + "energy": np.cumsum(rng.normal(0.0, 0.01, size=500)), + }, + ) + + report = generate_report(result, model_name="difficult_model", inference_method="nuts") + + assert any("Low acceptance rate" in warning for warning in report.warnings) + assert any("divergent transitions" in warning for warning in report.warnings) + assert any("Low BFMI" in warning for warning in report.warnings) From 0c37c9dc8b4336dcff35cb491e684ce2ba3ef5ae Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:57:05 +0500 Subject: [PATCH 6/9] test: cover multichain vector diagnostics report --- python/tests/test_diagnostics/test_report.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py index e07d462..00659cf 100644 --- a/python/tests/test_diagnostics/test_report.py +++ b/python/tests/test_diagnostics/test_report.py @@ -103,3 +103,21 @@ def test_generate_report_warns_on_sampler_diagnostics() -> None: assert any("Low acceptance rate" in warning for warning in report.warnings) assert any("divergent transitions" in warning for warning in report.warnings) assert any("Low BFMI" in warning for warning in report.warnings) + + +def test_generate_report_includes_multichain_vector_parameters() -> None: + rng = np.random.default_rng(11) + chains = rng.normal(size=(3, 120, 2)) + result = InferenceResult( + samples={"beta": chains.reshape(-1, 2)}, + diagnostics={"accept_rate": 0.9, "num_divergences": 0}, + num_chains=3, + samples_by_chain={"beta": chains}, + ) + + report = generate_report(result, model_name="linear_model", inference_method="hmc") + + assert "beta[0]" in report.summary_stats + assert "beta[1]" in report.summary_stats + assert "beta[0]/r_hat" in report.convergence_metrics + assert "beta[1]/split_r_hat" in report.convergence_metrics From bda27b38ba7fa19db4ebc570fe0abad12ec7ee1d Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 16:57:18 +0500 Subject: [PATCH 7/9] test: cover markdown warnings and conclusions --- python/tests/test_diagnostics/test_report.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py index 00659cf..8d470a3 100644 --- a/python/tests/test_diagnostics/test_report.py +++ b/python/tests/test_diagnostics/test_report.py @@ -121,3 +121,20 @@ def test_generate_report_includes_multichain_vector_parameters() -> None: assert "beta[1]" in report.summary_stats assert "beta[0]/r_hat" in report.convergence_metrics assert "beta[1]/split_r_hat" in report.convergence_metrics + + +def test_diagnostics_report_markdown_renders_warnings_and_conclusions() -> None: + report = DiagnosticsReport( + model_name="warning_model", + inference_method="vi", + timestamp="2026-05-09T10:00:00", + warnings=["Low ESS for z/ess: 30.0 (recommended > 100)"], + conclusions=["Review warnings before trusting results."], + ) + + markdown = report.to_markdown() + + assert "## Warnings" in markdown + assert "Low ESS for z/ess" in markdown + assert "## Conclusions" in markdown + assert "- Review warnings before trusting results." in markdown From 951ff158f5d03c84d01eafb1a25f00f859e4eb13 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 17:24:47 +0500 Subject: [PATCH 8/9] test: avoid inference engine imports in report tests --- python/tests/test_diagnostics/test_report.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py index 8d470a3..f6d34f6 100644 --- a/python/tests/test_diagnostics/test_report.py +++ b/python/tests/test_diagnostics/test_report.py @@ -1,12 +1,12 @@ from __future__ import annotations import json +from types import SimpleNamespace import numpy as np from hyperion_diagnostics.report import DiagnosticsReport from hyperion_diagnostics.report import generate_report -from hyperion_inference.base import InferenceResult def test_diagnostics_report_json_serializes_core_fields() -> None: @@ -55,7 +55,7 @@ def test_diagnostics_report_markdown_renders_configuration_and_summary_table() - def test_generate_report_adds_clean_conclusion_when_diagnostics_pass() -> None: - result = InferenceResult( + result = SimpleNamespace( samples={"mu": np.linspace(-1.0, 1.0, 200)}, diagnostics={"accept_rate": 0.75, "num_divergences": 0}, ) @@ -74,7 +74,7 @@ def test_generate_report_adds_clean_conclusion_when_diagnostics_pass() -> None: def test_generate_report_warns_on_low_effective_sample_size() -> None: - result = InferenceResult( + result = SimpleNamespace( samples={"mu": np.ones(150)}, diagnostics={"accept_rate": 0.8, "num_divergences": 0}, ) @@ -89,7 +89,7 @@ def test_generate_report_warns_on_low_effective_sample_size() -> None: def test_generate_report_warns_on_sampler_diagnostics() -> None: rng = np.random.default_rng(7) - result = InferenceResult( + result = SimpleNamespace( samples={"mu": rng.normal(size=500)}, diagnostics={ "accept_rate": 0.4, @@ -108,7 +108,7 @@ def test_generate_report_warns_on_sampler_diagnostics() -> None: def test_generate_report_includes_multichain_vector_parameters() -> None: rng = np.random.default_rng(11) chains = rng.normal(size=(3, 120, 2)) - result = InferenceResult( + result = SimpleNamespace( samples={"beta": chains.reshape(-1, 2)}, diagnostics={"accept_rate": 0.9, "num_divergences": 0}, num_chains=3, From a51bfb0c39b2f2346996c7b3559a88f55111c6f7 Mon Sep 17 00:00:00 2001 From: Akmal Date: Sat, 9 May 2026 17:25:53 +0500 Subject: [PATCH 9/9] test: use iid samples for clean report case --- python/tests/test_diagnostics/test_report.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/python/tests/test_diagnostics/test_report.py b/python/tests/test_diagnostics/test_report.py index f6d34f6..dd8223e 100644 --- a/python/tests/test_diagnostics/test_report.py +++ b/python/tests/test_diagnostics/test_report.py @@ -55,8 +55,9 @@ def test_diagnostics_report_markdown_renders_configuration_and_summary_table() - def test_generate_report_adds_clean_conclusion_when_diagnostics_pass() -> None: + rng = np.random.default_rng(5) result = SimpleNamespace( - samples={"mu": np.linspace(-1.0, 1.0, 200)}, + samples={"mu": rng.normal(size=500)}, diagnostics={"accept_rate": 0.75, "num_divergences": 0}, ) @@ -64,7 +65,7 @@ def test_generate_report_adds_clean_conclusion_when_diagnostics_pass() -> None: result, model_name="normal_model", inference_method="hmc", - config={"num_samples": 200}, + config={"num_samples": 500}, ) assert report.model_name == "normal_model"