Skip to content

Commit ee183eb

Browse files
fix: Guard against nan in test stat calculation (#1993)
* Guard against nan from division by zero in pyhf.infer.calculators.AsymptoticCalculator.teststatistic. * Add use of 'less than or equal to' to docs for tests stats to match equations 14 and 16 of https://arxiv.org/abs/1007.1727. * Add tests to ensure that nan conditions in Issue #529 and Issue #1992 are not possible.
1 parent 287bfae commit ee183eb

File tree

3 files changed

+38
-3
lines changed

3 files changed

+38
-3
lines changed

src/pyhf/infer/calculators.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,9 @@ def _false_case():
418418
teststat = (qmu - qmu_A) / (2 * self.sqrtqmuA_v)
419419
return teststat
420420

421+
# Use '<=' rather than '<' to avoid Issue #1992
421422
teststat = tensorlib.conditional(
422-
(sqrtqmu_v < self.sqrtqmuA_v), _true_case, _false_case
423+
(sqrtqmu_v <= self.sqrtqmuA_v), _true_case, _false_case
423424
)
424425
return tensorlib.astensor(teststat)
425426

src/pyhf/infer/test_statistics.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def qmu(mu, data, pdf, init_pars, par_bounds, fixed_params, return_fitted_pars=F
7171
7272
\begin{equation}
7373
q_{\mu} = \left\{\begin{array}{ll}
74-
-2\ln\lambda\left(\mu\right), &\hat{\mu} < \mu,\\
74+
-2\ln\lambda\left(\mu\right), &\hat{\mu} \leq \mu,\\
7575
0, & \hat{\mu} > \mu
7676
\end{array}\right.
7777
\end{equation}
@@ -160,7 +160,7 @@ def qmu_tilde(
160160
161161
\begin{equation}
162162
\tilde{q}_{\mu} = \left\{\begin{array}{ll}
163-
-2\ln\tilde{\lambda}\left(\mu\right), &\hat{\mu} < \mu,\\
163+
-2\ln\tilde{\lambda}\left(\mu\right), &\hat{\mu} \leq \mu,\\
164164
0, & \hat{\mu} > \mu
165165
\end{array}\right.
166166
\end{equation}

tests/test_infer.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -476,3 +476,37 @@ def test_fixed_poi(tmpdir, hypotest_args):
476476
pdf.config.param_set('mu').suggested_fixed = [True]
477477
with pytest.raises(pyhf.exceptions.InvalidModel):
478478
pyhf.infer.hypotest(*hypotest_args)
479+
480+
481+
def test_teststat_nan_guard():
482+
# Example from Issue #1992
483+
model = pyhf.simplemodels.uncorrelated_background(
484+
signal=[1.0], bkg=[1.0], bkg_uncertainty=[1.0]
485+
)
486+
observations = [2]
487+
test_poi = 0.0
488+
data = observations + model.config.auxdata
489+
init_pars = model.config.suggested_init()
490+
par_bounds = model.config.suggested_bounds()
491+
fixed_params = model.config.suggested_fixed()
492+
493+
test_stat = pyhf.infer.test_statistics.qmu_tilde(
494+
test_poi, data, model, init_pars, par_bounds, fixed_params
495+
)
496+
assert test_stat == pytest.approx(0.0)
497+
asymptotic_calculator = pyhf.infer.calculators.AsymptoticCalculator(
498+
data, model, test_stat="qtilde"
499+
)
500+
# ensure not nan
501+
assert ~np.isnan(asymptotic_calculator.teststatistic(test_poi))
502+
assert asymptotic_calculator.teststatistic(test_poi) == pytest.approx(0.0)
503+
504+
# Example from Issue #529
505+
model = pyhf.simplemodels.uncorrelated_background([0.005], [28.0], [5.0])
506+
test_poi = 1.0
507+
data = [28.0] + model.config.auxdata
508+
509+
test_results = pyhf.infer.hypotest(
510+
test_poi, data, model, test_stat="qtilde", return_expected=True
511+
)
512+
assert all(~np.isnan(result) for result in test_results)

0 commit comments

Comments
 (0)