Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-09-28 08:27:36

0001 import numpy as np
0002 import pytest
0003 
0004 from optiphy.ana.compare_ab import (
0005     chi2_1d,
0006     compare_hits,
0007     hit_parser,
0008     two_proportion_z_score,
0009 )
0010 
0011 
0012 def test_chi2_1d_ignores_overall_normalization():
0013     """Proportional histograms should match despite different sample totals."""
0014     a = np.array([0.1, 0.2, 1.1, 1.2, 1.3, 1.4])
0015     b = np.array([0.1, 0.2, 0.3, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6])
0016 
0017     chi2, ndf = chi2_1d(a, b, bins=[0.0, 1.0, 2.0])
0018 
0019     assert chi2 == pytest.approx(0.0)
0020     assert ndf == 1
0021 
0022 
0023 def test_chi2_1d_detects_shape_difference():
0024     """Different bin proportions should produce a nonzero shape statistic."""
0025     a = np.array([0.1, 0.2, 0.3, 0.4, 1.1, 1.2])
0026     b = np.array([0.1, 0.2, 1.1, 1.2, 1.3, 1.4])
0027 
0028     chi2, ndf = chi2_1d(a, b, bins=[0.0, 1.0, 2.0])
0029 
0030     assert chi2 == pytest.approx(4.0 / 3.0)
0031     assert ndf == 1
0032 
0033 
0034 def test_two_proportion_z_score_accepts_equal_rates():
0035     """Equal hit fractions should have zero statistical separation."""
0036     assert two_proportion_z_score(250, 1000, 2500, 10000) == pytest.approx(0.0)
0037 
0038 
0039 def test_two_proportion_z_score_detects_rate_difference():
0040     """The count test should measure differing efficiencies, not raw totals."""
0041     assert two_proportion_z_score(226, 1000, 296, 1000) == pytest.approx(3.56402, rel=1e-5)
0042 
0043 
0044 @pytest.mark.parametrize(
0045     ("successes", "trials"),
0046     [(-1, 100), (101, 100), (0, 0)],
0047 )
0048 def test_two_proportion_z_score_rejects_invalid_counts(successes, trials):
0049     """Invalid hit and launched-photon counts should be rejected explicitly."""
0050     with pytest.raises(ValueError):
0051         two_proportion_z_score(successes, trials, 50, 100)
0052 
0053 
0054 @pytest.mark.parametrize(
0055     ("extra_args", "expected_status"),
0056     [([], 1), (["--report-only"], 0)],
0057 )
0058 def test_report_only_preserves_default_enforcement(tmp_path, extra_args, expected_status):
0059     """Statistical mismatches should remain fatal unless explicitly diagnostic."""
0060     g4_path = tmp_path / "g_hits.npy"
0061     gpu_path = tmp_path / "s_hits.npy"
0062     np.save(g4_path, np.zeros((1, 4, 4)))
0063     np.save(gpu_path, np.zeros((9, 4, 4)))
0064     args = hit_parser().parse_args(
0065         [
0066             str(g4_path),
0067             str(gpu_path),
0068             "--count-trials",
0069             "10",
0070             "--count-nsigma",
0071             "3",
0072             *extra_args,
0073         ]
0074     )
0075 
0076     assert compare_hits(args) == expected_status
0077 
0078 
0079 @pytest.mark.parametrize(
0080     ("extra_args", "expected_status"),
0081     [([], 1), (["--report-only"], 0)],
0082 )
0083 def test_report_only_applies_to_shape_mismatches(tmp_path, extra_args, expected_status):
0084     """Diagnostic mode should report differing shapes without weakening its default."""
0085     g4_path = tmp_path / "g_hits.npy"
0086     gpu_path = tmp_path / "s_hits.npy"
0087     g4_hits = np.zeros((10, 4, 4))
0088     gpu_hits = np.zeros((10, 4, 4))
0089     gpu_hits[:, 0, 0] = 1.0
0090     np.save(g4_path, g4_hits)
0091     np.save(gpu_path, gpu_hits)
0092     args = hit_parser().parse_args(
0093         [
0094             str(g4_path),
0095             str(gpu_path),
0096             "--count-trials",
0097             "10",
0098             "--count-nsigma",
0099             "3",
0100             "--chi2-ndf-tolerance",
0101             "5",
0102             *extra_args,
0103         ]
0104     )
0105 
0106     assert compare_hits(args) == expected_status
0107 
0108 
0109 def test_report_only_does_not_suppress_required_hit_failure(tmp_path):
0110     """Diagnostic statistics should still fail when a required hit input is empty."""
0111     g4_path = tmp_path / "g_hits.npy"
0112     gpu_path = tmp_path / "s_hits.npy"
0113     np.save(g4_path, np.zeros((0, 4, 4)))
0114     np.save(gpu_path, np.zeros((1, 4, 4)))
0115     args = hit_parser().parse_args(
0116         [
0117             str(g4_path),
0118             str(gpu_path),
0119             "--count-trials",
0120             "10",
0121             "--count-nsigma",
0122             "3",
0123             "--report-only",
0124             "--require-hits",
0125         ]
0126     )
0127 
0128     assert compare_hits(args) == 1