Skip to content

Commit ed91b0a

Browse files
lukasheinrichmatthewfeickert
authored andcommitted
feat: change qmu API and remove test_stat return (#712)
* Make 'pyhf.infer.test_statistics.qmu' return a scalar * Remove API to return test statistics from hypothesis tests (which only makes sense for asymptotics)
1 parent 29c7e72 commit ed91b0a

File tree

9 files changed

+132
-162
lines changed

9 files changed

+132
-162
lines changed

docs/examples/notebooks/binderexample/StatisticalAnalysis.ipynb

Lines changed: 104 additions & 75 deletions
Large diffs are not rendered by default.

docs/examples/notebooks/hello-world.ipynb

Lines changed: 9 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
"name": "stdout",
4242
"output_type": "stream",
4343
"text": [
44-
"Observed: [0.05290116], Expected: [0.06445521]\n"
44+
"Observed: 0.05290116224852556, Expected: 0.06445521290832805\n"
4545
]
4646
}
4747
],
@@ -67,7 +67,7 @@
6767
"name": "stdout",
6868
"output_type": "stream",
6969
"text": [
70-
"Observed CL_s: [0.05290116], CL_sb: [0.0236], CL_b: [0.44611493]\n"
70+
"Observed CL_s: 0.05290116224852556, CL_sb: 0.023599998519978738, CL_b: 0.4461149342826869\n"
7171
]
7272
}
7373
],
@@ -120,13 +120,13 @@
120120
"name": "stdout",
121121
"output_type": "stream",
122122
"text": [
123-
"Observed CL_s: [0.05290116]\n",
123+
"Observed CL_s: 0.05290116224852556\n",
124124
"\n",
125-
"Expected CL_s(-2 σ): [0.00260641]\n",
126-
"Expected CL_s(-1 σ): [0.01382066]\n",
127-
"Expected CL_s : [0.06445521]\n",
128-
"Expected CL_s(1 σ): [0.23526104]\n",
129-
"Expected CL_s(2 σ): [0.57304182]\n"
125+
"Expected CL_s(-2 σ): 0.0026064088679947964\n",
126+
"Expected CL_s(-1 σ): 0.013820657528619273\n",
127+
"Expected CL_s : 0.06445521290832805\n",
128+
"Expected CL_s(1 σ): 0.23526103626937836\n",
129+
"Expected CL_s(2 σ): 0.5730418174887743\n"
130130
]
131131
}
132132
],
@@ -136,31 +136,6 @@
136136
"for p_value, n_sigma in enumerate(np.arange(-2,3)):\n",
137137
" print('Expected CL_s{}: {}'.format(' ' if n_sigma==0 else '({} σ)'.format(n_sigma),CLs_exp_band[p_value]))"
138138
]
139-
},
140-
{
141-
"cell_type": "markdown",
142-
"metadata": {},
143-
"source": [
144-
"**Returning the test statistics for the observed and Asimov data**"
145-
]
146-
},
147-
{
148-
"cell_type": "code",
149-
"execution_count": 7,
150-
"metadata": {},
151-
"outputs": [
152-
{
153-
"name": "stdout",
154-
"output_type": "stream",
155-
"text": [
156-
"q_mu: [3.93824492], Asimov q_mu: [3.41886758]\n"
157-
]
158-
}
159-
],
160-
"source": [
161-
"CLs_obs, test_statistics = pyhf.infer.hypotest(1.0, [51, 48] + pdf.config.auxdata, pdf, return_test_statistics=True)\n",
162-
"print('q_mu: {}, Asimov q_mu: {}'.format(test_statistics[0], test_statistics[1]))"
163-
]
164139
}
165140
],
166141
"metadata": {
@@ -179,7 +154,7 @@
179154
"name": "python",
180155
"nbconvert_exporter": "python",
181156
"pygments_lexer": "ipython3",
182-
"version": "3.6.6"
157+
"version": "3.7.5"
183158
}
184159
},
185160
"nbformat": 4,

src/pyhf/infer/__init__.py

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ def hypotest(
2727
return_tail_probs (bool): Bool for returning :math:`\textrm{CL}_{s+b}` and :math:`\textrm{CL}_{b}`
2828
return_expected (bool): Bool for returning :math:`\textrm{CL}_{\textrm{exp}}`
2929
return_expected_set (bool): Bool for returning the :math:`(-2,-1,0,1,2)\sigma` :math:`\textrm{CL}_{\textrm{exp}}` --- the "Brazil band"
30-
return_test_statistics (bool): Bool for returning :math:`q_{\mu}` and :math:`q_{\mu,A}`
3130
3231
Returns:
3332
Tuple of Floats and lists of Floats:
@@ -74,8 +73,6 @@ def hypotest(
7473
7574
for :math:`\mu'=0` and :math:`N \in \left\{-2, -1, 0, 1, 2\right\}`. These values define the boundaries of an uncertainty band sometimes referred to as the "Brazil band". Only returned when ``return_expected_set`` is ``True``.
7675
77-
- :math:`\left[q_{\mu}, q_{\mu,A}\right]`: The test statistics for the observed and Asimov datasets respectively. Only returned when ``return_test_statistics`` is ``True``.
78-
7976
"""
8077
init_pars = init_pars or pdf.config.suggested_init()
8178
par_bounds = par_bounds or pdf.config.suggested_bounds()
@@ -84,14 +81,10 @@ def hypotest(
8481
asimov_mu = 0.0
8582
asimov_data = generate_asimov_data(asimov_mu, data, pdf, init_pars, par_bounds)
8683

87-
qmu_v = tensorlib.clip(
88-
qmu(poi_test, data, pdf, init_pars, par_bounds), 0, max_value=None
89-
)
84+
qmu_v = qmu(poi_test, data, pdf, init_pars, par_bounds)
9085
sqrtqmu_v = tensorlib.sqrt(qmu_v)
9186

92-
qmuA_v = tensorlib.clip(
93-
qmu(poi_test, asimov_data, pdf, init_pars, par_bounds), 0, max_value=None
94-
)
87+
qmuA_v = qmu(poi_test, asimov_data, pdf, init_pars, par_bounds)
9588
sqrtqmuA_v = tensorlib.sqrt(qmuA_v)
9689

9790
CLsb, CLb, CLs = pvals_from_teststat(sqrtqmu_v, sqrtqmuA_v, qtilde=qtilde)
@@ -109,8 +102,6 @@ def hypotest(
109102
_returns.append(CLs_exp)
110103
elif kwargs.get('return_expected'):
111104
_returns.append(pvals_from_teststat_expected(sqrtqmuA_v)[-1])
112-
if kwargs.get('return_test_statistics'):
113-
_returns.append([qmu_v, qmuA_v])
114105
# Enforce a consistent return type of the observed CLs
115106
return tuple(_returns) if len(_returns) > 1 else _returns[0]
116107

src/pyhf/infer/test_statistics.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,6 @@ def qmu(mu, data, pdf, init_pars, par_bounds):
3838
)
3939
qmu = fixed_poi_fit_lhood_val - unconstrained_fit_lhood_val
4040
qmu = tensorlib.where(
41-
muhatbhat[pdf.config.poi_index] > mu, tensorlib.astensor([0]), qmu
42-
)
43-
return qmu
41+
muhatbhat[pdf.config.poi_index] > mu, tensorlib.astensor(0.0), qmu
42+
)[0]
43+
return tensorlib.clip(qmu, 0, max_value=None)

src/pyhf/infer/utils.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,16 @@ def _false_case():
5555
return nullval, altval
5656

5757
nullval, altval = tensorlib.conditional(
58-
(sqrtqmu_v < sqrtqmuA_v)[0], _true_case, _false_case
58+
(sqrtqmu_v < sqrtqmuA_v), _true_case, _false_case
5959
)
6060
CLsb = 1 - tensorlib.normal_cdf(nullval)
6161
CLb = 1 - tensorlib.normal_cdf(altval)
6262
CLs = CLsb / CLb
63-
return CLsb, CLb, CLs
63+
return (
64+
tensorlib.reshape(CLsb, (1,)),
65+
tensorlib.reshape(CLb, (1,)),
66+
tensorlib.reshape(CLs, (1,)),
67+
)
6468

6569

6670
def pvals_from_teststat_expected(sqrtqmuA_v, nsigma=0):
@@ -86,4 +90,8 @@ def pvals_from_teststat_expected(sqrtqmuA_v, nsigma=0):
8690
CLsb = tensorlib.normal_cdf(nsigma - sqrtqmuA_v)
8791
CLb = tensorlib.normal_cdf(nsigma)
8892
CLs = CLsb / CLb
89-
return CLsb, CLb, CLs
93+
return (
94+
tensorlib.reshape(CLsb, (1,)),
95+
tensorlib.reshape(CLb, (1,)),
96+
tensorlib.reshape(CLs, (1,)),
97+
)

tests/benchmarks/test_benchmark.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,6 @@ def hypotest(pdf, data):
6262
return_tail_probs=True,
6363
return_expected=True,
6464
return_expected_set=True,
65-
return_test_statistics=True,
6665
)
6766

6867

tests/test_backend_consistency.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,9 @@ def test_hypotest_q_mu(
117117
backend.session = tf.compat.v1.Session()
118118
pyhf.set_backend(backend)
119119

120-
q_mu = pyhf.infer.hypotest(
121-
1.0,
122-
data,
123-
pdf,
124-
pdf.config.suggested_init(),
125-
pdf.config.suggested_bounds(),
126-
return_test_statistics=True,
127-
)[-1][0]
120+
q_mu = pyhf.infer.test_statistics.qmu(
121+
1.0, data, pdf, pdf.config.suggested_init(), pdf.config.suggested_bounds(),
122+
)
128123
test_statistic.append(pyhf.tensorlib.tolist(q_mu))
129124

130125
# compare to NumPy/SciPy

tests/test_infer.py

Lines changed: 0 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -86,29 +86,3 @@ def test_hypotest_return_expected_set(tmpdir, hypotest_args):
8686
assert isinstance(result[2], type(tb.astensor(result[2])))
8787
assert len(result[3]) == 5
8888
assert check_uniform_type(result[3])
89-
90-
91-
def test_hypotest_return_test_statistics(tmpdir, hypotest_args):
92-
"""
93-
Check that the return structure of pyhf.infer.hypotest with the
94-
additon of the return_test_statistics keyword arg is as expected
95-
"""
96-
tb = pyhf.tensorlib
97-
98-
kwargs = {
99-
'return_tail_probs': True,
100-
'return_expected': True,
101-
'return_expected_set': True,
102-
'return_test_statistics': True,
103-
}
104-
result = pyhf.infer.hypotest(*hypotest_args, **kwargs)
105-
# CLs_obs, [CLsb, CLb], CLs_exp, CLs_exp @[-2, -1, 0, +1, +2]sigma, [q_mu, q_mu_Asimov]
106-
assert len(list(result)) == 5
107-
assert isinstance(result[0], type(tb.astensor(result[0])))
108-
assert len(result[1]) == 2
109-
assert check_uniform_type(result[1])
110-
assert isinstance(result[2], type(tb.astensor(result[2])))
111-
assert len(result[3]) == 5
112-
assert check_uniform_type(result[3])
113-
assert len(result[4]) == 2
114-
assert check_uniform_type(result[4])

tests/test_public_api.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ def test_hypotest(backend, model_setup):
7373
init_pars,
7474
model.config.suggested_bounds(),
7575
return_expected_set=True,
76-
return_test_statistics=True,
7776
)
7877

7978

0 commit comments

Comments
 (0)