2
2
from .mle import fixed_poi_fit , fit
3
3
from ..exceptions import UnspecifiedPOI
4
4
5
+ import logging
6
+
7
+ log = logging .getLogger (__name__ )
8
+
9
+
10
+ def _qmu_like (mu , data , pdf , init_pars , par_bounds ):
11
+ """
12
+ Clipped version of _tmu_like where the returned test statistic
13
+ is 0 if muhat > 0 else tmu_like_stat.
14
+
15
+ If the lower bound of the POI is 0 this automatically implments
16
+ qmu_tilde. Otherwise this is qmu (no tilde).
17
+ """
18
+ tensorlib , optimizer = get_backend ()
19
+ tmu_like_stat , (_ , muhatbhat ) = _tmu_like (
20
+ mu , data , pdf , init_pars , par_bounds , return_fitted_pars = True
21
+ )
22
+ qmu_like_stat = tensorlib .where (
23
+ muhatbhat [pdf .config .poi_index ] > mu , tensorlib .astensor (0.0 ), tmu_like_stat
24
+ )
25
+ return qmu_like_stat
26
+
27
+
28
+ def _tmu_like (mu , data , pdf , init_pars , par_bounds , return_fitted_pars = False ):
29
+ """
30
+ Basic Profile Likelihood test statistic.
31
+
32
+ If the lower bound of the POI is 0 this automatically implments
33
+ tmu_tilde. Otherwise this is tmu (no tilde).
34
+ """
35
+ tensorlib , optimizer = get_backend ()
36
+ mubhathat , fixed_poi_fit_lhood_val = fixed_poi_fit (
37
+ mu , data , pdf , init_pars , par_bounds , return_fitted_val = True
38
+ )
39
+ muhatbhat , unconstrained_fit_lhood_val = fit (
40
+ data , pdf , init_pars , par_bounds , return_fitted_val = True
41
+ )
42
+ log_likelihood_ratio = fixed_poi_fit_lhood_val - unconstrained_fit_lhood_val
43
+ tmu_like_stat = tensorlib .astensor (
44
+ tensorlib .clip (log_likelihood_ratio , 0.0 , max_value = None )
45
+ )
46
+ if return_fitted_pars :
47
+ return tmu_like_stat , (mubhathat , muhatbhat )
48
+ return tmu_like_stat
49
+
5
50
6
51
def qmu (mu , data , pdf , init_pars , par_bounds ):
7
52
r"""
@@ -30,8 +75,9 @@ def qmu(mu, data, pdf, init_pars, par_bounds):
30
75
>>> test_mu = 1.0
31
76
>>> init_pars = model.config.suggested_init()
32
77
>>> par_bounds = model.config.suggested_bounds()
78
+ >>> par_bounds[model.config.poi_index] = [-10.0, 10.0]
33
79
>>> pyhf.infer.test_statistics.qmu(test_mu, data, model, init_pars, par_bounds)
34
- 3.938244920380498
80
+ array(3.9549891)
35
81
36
82
Args:
37
83
mu (Number or Tensor): The signal strength parameter
@@ -47,16 +93,136 @@ def qmu(mu, data, pdf, init_pars, par_bounds):
47
93
raise UnspecifiedPOI (
48
94
'No POI is defined. A POI is required for profile likelihood based test statistics.'
49
95
)
96
+ if par_bounds [pdf .config .poi_index ][0 ] == 0 :
97
+ log .warning (
98
+ 'qmu test statistic used for fit configuration with POI bounded at zero.\n '
99
+ + 'Use the qmu_tilde test statistic (pyhf.infer.test_statistics.qmu_tilde) instead.'
100
+ )
101
+ return _qmu_like (mu , data , pdf , init_pars , par_bounds )
50
102
51
- tensorlib , optimizer = get_backend ()
52
- mubhathat , fixed_poi_fit_lhood_val = fixed_poi_fit (
53
- mu , data , pdf , init_pars , par_bounds , return_fitted_val = True
54
- )
55
- muhatbhat , unconstrained_fit_lhood_val = fit (
56
- data , pdf , init_pars , par_bounds , return_fitted_val = True
57
- )
58
- qmu = fixed_poi_fit_lhood_val - unconstrained_fit_lhood_val
59
- qmu = tensorlib .where (
60
- muhatbhat [pdf .config .poi_index ] > mu , tensorlib .astensor (0.0 ), qmu
61
- )
62
- return tensorlib .clip (qmu , 0 , max_value = None )
103
+
104
+ def qmu_tilde (mu , data , pdf , init_pars , par_bounds ):
105
+ r"""
106
+ The test statistic, :math:`\tilde{q}_{\mu}`, for establishing an upper
107
+ limit on the strength parameter, :math:`\mu`, for models with
108
+ bounded POI, as defiend in Equation (16) in :xref:`arXiv:1007.1727`.
109
+
110
+ Example:
111
+ >>> import pyhf
112
+ >>> pyhf.set_backend("numpy")
113
+ >>> model = pyhf.simplemodels.hepdata_like(
114
+ ... signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]
115
+ ... )
116
+ >>> observations = [51, 48]
117
+ >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)
118
+ >>> test_mu = 1.0
119
+ >>> init_pars = model.config.suggested_init()
120
+ >>> par_bounds = model.config.suggested_bounds()
121
+ >>> pyhf.infer.test_statistics.qmu_tilde(test_mu, data, model, init_pars, par_bounds)
122
+ array(3.93824492)
123
+
124
+ Args:
125
+ mu (Number or Tensor): The signal strength parameter
126
+ data (Tensor): The data to be considered
127
+ pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json
128
+ init_pars (`list`): Values to initialize the model parameters at for the fit
129
+ par_bounds (`list` of `list`\s or `tuple`\s): The extrema of values the model parameters are allowed to reach in the fit
130
+
131
+ Returns:
132
+ Float: The calculated test statistic, :math:`\tilde{q}_{\mu}`
133
+ """
134
+ if pdf .config .poi_index is None :
135
+ raise UnspecifiedPOI (
136
+ 'No POI is defined. A POI is required for profile likelihood based test statistics.'
137
+ )
138
+ if par_bounds [pdf .config .poi_index ][0 ] != 0 :
139
+ log .warning (
140
+ 'qmu_tilde test statistic used for fit configuration with POI not bounded at zero.\n '
141
+ + 'Use the qmu test statistic (pyhf.infer.test_statistics.qmu) instead.'
142
+ )
143
+ return _qmu_like (mu , data , pdf , init_pars , par_bounds )
144
+
145
+
146
+ def tmu (mu , data , pdf , init_pars , par_bounds ):
147
+ r"""
148
+ The test statistic, :math:`t_{\mu}`, for establishing a two-sided
149
+ interval on the strength parameter, :math:`\mu`, as defiend in Equation (10)
150
+ in :xref:`arXiv:1007.1727`.
151
+
152
+ Example:
153
+ >>> import pyhf
154
+ >>> pyhf.set_backend("numpy")
155
+ >>> model = pyhf.simplemodels.hepdata_like(
156
+ ... signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]
157
+ ... )
158
+ >>> observations = [51, 48]
159
+ >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)
160
+ >>> test_mu = 1.0
161
+ >>> init_pars = model.config.suggested_init()
162
+ >>> par_bounds = model.config.suggested_bounds()
163
+ >>> par_bounds[model.config.poi_index] = [-10.0, 10.0]
164
+ >>> pyhf.infer.test_statistics.tmu(test_mu, data, model, init_pars, par_bounds)
165
+ array(3.9549891)
166
+
167
+ Args:
168
+ mu (Number or Tensor): The signal strength parameter
169
+ data (Tensor): The data to be considered
170
+ pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json
171
+ init_pars (`list`): Values to initialize the model parameters at for the fit
172
+ par_bounds (`list` of `list`\s or `tuple`\s): The extrema of values the model parameters are allowed to reach in the fit
173
+
174
+ Returns:
175
+ Float: The calculated test statistic, :math:`t_{\mu}`
176
+ """
177
+ if pdf .config .poi_index is None :
178
+ raise UnspecifiedPOI (
179
+ 'No POI is defined. A POI is required for profile likelihood based test statistics.'
180
+ )
181
+ if par_bounds [pdf .config .poi_index ][0 ] == 0 :
182
+ log .warning (
183
+ 'tmu test statistic used for fit configuration with POI bounded at zero.\n '
184
+ + 'Use the tmu_tilde test statistic (pyhf.infer.test_statistics.tmu_tilde) instead.'
185
+ )
186
+ return _tmu_like (mu , data , pdf , init_pars , par_bounds )
187
+
188
+
189
+ def tmu_tilde (mu , data , pdf , init_pars , par_bounds ):
190
+ r"""
191
+ The test statistic, :math:`t_{\mu}`, for establishing a two-sided
192
+ interval on the strength parameter, :math:`\mu`, for models with
193
+ bounded POI, as defiend in Equation (11) in :xref:`arXiv:1007.1727`.
194
+
195
+ Example:
196
+ >>> import pyhf
197
+ >>> pyhf.set_backend("numpy")
198
+ >>> model = pyhf.simplemodels.hepdata_like(
199
+ ... signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]
200
+ ... )
201
+ >>> observations = [51, 48]
202
+ >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)
203
+ >>> test_mu = 1.0
204
+ >>> init_pars = model.config.suggested_init()
205
+ >>> par_bounds = model.config.suggested_bounds()
206
+ >>> pyhf.infer.test_statistics.tmu_tilde(test_mu, data, model, init_pars, par_bounds)
207
+ array(3.93824492)
208
+
209
+ Args:
210
+ mu (Number or Tensor): The signal strength parameter
211
+ data (Tensor): The data to be considered
212
+ pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json
213
+ init_pars (`list`): Values to initialize the model parameters at for the fit
214
+ par_bounds (`list` of `list`\s or `tuple`\s): The extrema of values the model parameters are allowed to reach in the fit
215
+
216
+ Returns:
217
+ Float: The calculated test statistic, :math:`\tilde{t}_{\mu}`
218
+ """
219
+ if pdf .config .poi_index is None :
220
+ raise UnspecifiedPOI (
221
+ 'No POI is defined. A POI is required for profile likelihood based test statistics.'
222
+ )
223
+ if par_bounds [pdf .config .poi_index ][0 ] != 0 :
224
+ log .warning (
225
+ 'tmu_tilde test statistic used for fit configuration with POI not bounded at zero.\n '
226
+ + 'Use the tmu test statistic (pyhf.infer.test_statistics.tmu) instead.'
227
+ )
228
+ return _tmu_like (mu , data , pdf , init_pars , par_bounds )
0 commit comments