We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
workspaces with fixed poi fail in hypotest (of course)
# Description

some workspaces (like the one @elskorda provided) have a *fixed* POI in the workspace definition - if you naively put that through `pyhf cls` of course the fit doesn't really do anythinig useful.

options

* in readxml warn that POI is fixed
* in hypotest ensurer to un-fix POI
* others?


the least we could do is throw a warning  in the places where an unconstrained fit iss expected but a fixed POI is observed

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/pyhf/infer/__init__.py`
Content:
```
1 """Inference for Statistical Models."""
2 
3 from . import utils
4 from .. import get_backend
5 
6 
7 def hypotest(
8     poi_test,
9     data,
10     pdf,
11     init_pars=None,
12     par_bounds=None,
13     fixed_params=None,
14     calctype="asymptotics",
15     return_tail_probs=False,
16     return_expected=False,
17     return_expected_set=False,
18     **kwargs,
19 ):
20     r"""
21     Compute :math:`p`-values and test statistics for a single value of the parameter of interest.
22 
23     See :py:class:`~pyhf.infer.calculators.AsymptoticCalculator` and :py:class:`~pyhf.infer.calculators.ToyCalculator` on additional keyword arguments to be specified.
24 
25     Example:
26         >>> import pyhf
27         >>> pyhf.set_backend("numpy")
28         >>> model = pyhf.simplemodels.hepdata_like(
29         ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]
30         ... )
31         >>> observations = [51, 48]
32         >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)
33         >>> mu_test = 1.0
34         >>> CLs_obs, CLs_exp_band = pyhf.infer.hypotest(
35         ...     mu_test, data, model, return_expected_set=True, test_stat="qtilde"
36         ... )
37         >>> CLs_obs
38         array(0.05251497)
39         >>> CLs_exp_band
40         [array(0.00260626), array(0.01382005), array(0.06445321), array(0.23525644), array(0.57303621)]
41 
42     Args:
43         poi_test (Number or Tensor): The value of the parameter of interest (POI)
44         data (Number or Tensor): The data considered
45         pdf (~pyhf.pdf.Model): The statistical model adhering to the schema ``model.json``
46         init_pars (:obj:`tensor` of :obj:`float`): The starting values of the model parameters for minimization.
47         par_bounds (:obj:`tensor`): The extrema of values the model parameters
48             are allowed to reach in the fit.
49             The shape should be ``(n, 2)`` for ``n`` model parameters.
50         fixed_params (:obj:`tensor` of :obj:`bool`): The flag to set a parameter constant to its starting
51             value during minimization.
52         calctype (:obj:`str`): The calculator to create. Choose either 'asymptotics' (default) or 'toybased'.
53         return_tail_probs (:obj:`bool`): Bool for returning :math:`\mathrm{CL}_{s+b}` and :math:`\mathrm{CL}_{b}`
54         return_expected (:obj:`bool`): Bool for returning :math:`\mathrm{CL}_{\mathrm{exp}}`
55         return_expected_set (:obj:`bool`): Bool for returning the :math:`(-2,-1,0,1,2)\sigma` :math:`\mathrm{CL}_{\mathrm{exp}}` --- the "Brazil band"
56 
57     Returns:
58         Tuple of Floats and lists of Floats:
59 
60             - :math:`\mathrm{CL}_{s}`: The modified :math:`p`-value compared to
61               the given threshold :math:`\alpha`, typically taken to be :math:`0.05`,
62               defined in :xref:`arXiv:1007.1727` as
63 
64             .. math::
65 
66                 \mathrm{CL}_{s} = \frac{\mathrm{CL}_{s+b}}{\mathrm{CL}_{b}} = \frac{p_{s+b}}{1-p_{b}}
67 
68             to protect against excluding signal models in which there is little
69             sensitivity. In the case that :math:`\mathrm{CL}_{s} \leq \alpha`
70             the given signal model is excluded.
71 
72             - :math:`\left[\mathrm{CL}_{s+b}, \mathrm{CL}_{b}\right]`: The
73               signal + background model hypothesis :math:`p`-value
74 
75             .. math::
76 
77                 \mathrm{CL}_{s+b} = p_{s+b}
78                 = p\left(q \geq q_{\mathrm{obs}}\middle|s+b\right)
79                 = \int\limits_{q_{\mathrm{obs}}}^{\infty} f\left(q\,\middle|s+b\right)\,dq
80                 = 1 - F\left(q_{\mathrm{obs}}(\mu)\,\middle|\mu'\right)
81 
82             and 1 minus the background only model hypothesis :math:`p`-value
83 
84             .. math::
85 
86                 \mathrm{CL}_{b} = 1- p_{b}
87                 = p\left(q \geq q_{\mathrm{obs}}\middle|b\right)
88                 = 1 - \int\limits_{-\infty}^{q_{\mathrm{obs}}} f\left(q\,\middle|b\right)\,dq
89                 = 1 - F\left(q_{\mathrm{obs}}(\mu)\,\middle|0\right)
90 
91             for signal strength :math:`\mu` and model hypothesis signal strength
92             :math:`\mu'`, where the cumulative density functions
93             :math:`F\left(q(\mu)\,\middle|\mu'\right)` are given by Equations (57)
94             and (65) of :xref:`arXiv:1007.1727` for upper-limit-like test
95             statistic :math:`q \in \{q_{\mu}, \tilde{q}_{\mu}\}`.
96             Only returned when ``return_tail_probs`` is ``True``.
97 
98             .. note::
99 
100                 The definitions of the :math:`\mathrm{CL}_{s+b}` and
101                 :math:`\mathrm{CL}_{b}` used are based on profile likelihood
102                 ratio test statistics.
103                 This procedure is common in the LHC-era, but differs from
104                 procedures used in the LEP and Tevatron eras, as briefly
105                 discussed in :math:`\S` 3.8 of :xref:`arXiv:1007.1727`.
106 
107             - :math:`\mathrm{CL}_{s,\mathrm{exp}}`: The expected :math:`\mathrm{CL}_{s}`
108               value corresponding to the test statistic under the background
109               only hypothesis :math:`\left(\mu=0\right)`.
110               Only returned when ``return_expected`` is ``True``.
111 
112             - :math:`\mathrm{CL}_{s,\mathrm{exp}}` band: The set of expected
113               :math:`\mathrm{CL}_{s}` values corresponding to the median
114               significance of variations of the signal strength from the
115               background only hypothesis :math:`\left(\mu=0\right)` at
116               :math:`(-2,-1,0,1,2)\sigma`.
117               That is, the :math:`p`-values that satisfy Equation (89) of
118               :xref:`arXiv:1007.1727`
119 
120             .. math::
121 
122                 \mathrm{band}_{N\sigma} = \mu' + \sigma\,\Phi^{-1}\left(1-\alpha\right) \pm N\sigma
123 
124             for :math:`\mu'=0` and :math:`N \in \left\{-2, -1, 0, 1, 2\right\}`.
125             These values define the boundaries of an uncertainty band sometimes
126             referred to as the "Brazil band".
127             Only returned when ``return_expected_set`` is ``True``.
128 
129     """
130     init_pars = init_pars or pdf.config.suggested_init()
131     par_bounds = par_bounds or pdf.config.suggested_bounds()
132     fixed_params = fixed_params or pdf.config.suggested_fixed()
133 
134     calc = utils.create_calculator(
135         calctype,
136         data,
137         pdf,
138         init_pars,
139         par_bounds,
140         fixed_params,
141         **kwargs,
142     )
143 
144     teststat = calc.teststatistic(poi_test)
145     sig_plus_bkg_distribution, bkg_only_distribution = calc.distributions(poi_test)
146 
147     tb, _ = get_backend()
148     CLsb_obs, CLb_obs, CLs_obs = tuple(
149         tb.astensor(pvalue)
150         for pvalue in calc.pvalues(
151             teststat, sig_plus_bkg_distribution, bkg_only_distribution
152         )
153     )
154     CLsb_exp, CLb_exp, CLs_exp = calc.expected_pvalues(
155         sig_plus_bkg_distribution, bkg_only_distribution
156     )
157 
158     is_q0 = kwargs.get('test_stat', 'qtilde') == 'q0'
159 
160     _returns = [CLsb_obs if is_q0 else CLs_obs]
161     if return_tail_probs:
162         if is_q0:
163             _returns.append([CLb_obs])
164         else:
165             _returns.append([CLsb_obs, CLb_obs])
166 
167     pvalues_exp_band = [
168         tb.astensor(pvalue) for pvalue in (CLsb_exp if is_q0 else CLs_exp)
169     ]
170     if return_expected_set:
171         if return_expected:
172             _returns.append(tb.astensor(pvalues_exp_band[2]))
173         _returns.append(pvalues_exp_band)
174     elif return_expected:
175         _returns.append(tb.astensor(pvalues_exp_band[2]))
176     # Enforce a consistent return type of the observed CLs
177     return tuple(_returns) if len(_returns) > 1 else _returns[0]
178 
179 
180 from . import intervals  # noqa: F401
181 
182 __all__ = ["hypotest"]
183
```

Path: `src/pyhf/infer/utils.py`
Content:
```
1 """Inference for Statistical Models."""
2 
3 from .calculators import AsymptoticCalculator, ToyCalculator
4 from ..exceptions import InvalidTestStatistic
5 from .test_statistics import q0, qmu, qmu_tilde
6 
7 import logging
8 
9 log = logging.getLogger(__name__)
10 
11 
12 def create_calculator(calctype, *args, **kwargs):
13     """
14     Creates a calculator object of the specified `calctype`.
15 
16     See :py:class:`~pyhf.infer.calculators.AsymptoticCalculator` and
17     :py:class:`~pyhf.infer.calculators.ToyCalculator` on additional arguments
18     to be specified.
19 
20     Example:
21 
22         >>> import pyhf
23         >>> import numpy.random as random
24         >>> random.seed(0)
25         >>> model = pyhf.simplemodels.hepdata_like(
26         ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0],
27         ... )
28         >>> observations = [51, 48]
29         >>> data = observations + model.config.auxdata
30         >>> mu_test = 1.0
31         >>> toy_calculator = pyhf.infer.utils.create_calculator(
32         ...     "toybased", data, model, ntoys=100, test_stat="qtilde", track_progress=False
33         ... )
34         >>> qmu_sig, qmu_bkg = toy_calculator.distributions(mu_test)
35         >>> qmu_sig.pvalue(mu_test), qmu_bkg.pvalue(mu_test)
36         (array(0.14), array(0.76))
37 
38     Args:
39         calctype (:obj:`str`): The calculator to create. Choose either
40         'asymptotics' or 'toybased'.
41 
42     Returns:
43         calculator (:obj:`object`): A calculator.
44     """
45     return {'asymptotics': AsymptoticCalculator, 'toybased': ToyCalculator}[calctype](
46         *args, **kwargs
47     )
48 
49 
50 def get_test_stat(name):
51     """
52     Get the test statistic function by name. The following test statistics are supported:
53 
54     - :func:`~pyhf.infer.test_statistics.q0`
55     - :func:`~pyhf.infer.test_statistics.qmu`
56     - :func:`~pyhf.infer.test_statistics.qmu_tilde`
57 
58     Example:
59 
60         >>> from pyhf.infer import utils, test_statistics
61         >>> utils.get_test_stat("q0")
62         <function q0 at 0x...>
63         >>> utils.get_test_stat("q0") == test_statistics.q0
64         True
65         >>> utils.get_test_stat("q")
66         <function qmu at 0x...>
67         >>> utils.get_test_stat("q") == test_statistics.qmu
68         True
69         >>> utils.get_test_stat("qtilde")
70         <function qmu_tilde at 0x...>
71         >>> utils.get_test_stat("qtilde") == test_statistics.qmu_tilde
72         True
73 
74     Args:
75         name (:obj:`str`): The name of the test statistic to retrieve
76 
77 
78     Returns:
79         callable: The test statistic function
80     """
81     _mapping = {
82         "q0": q0,
83         "q": qmu,
84         "qtilde": qmu_tilde,
85     }
86     try:
87         return _mapping[name]
88     except KeyError:
89         raise InvalidTestStatistic
90
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/pyhf/infer/__init__.py b/src/pyhf/infer/__init__.py
--- a/src/pyhf/infer/__init__.py
+++ b/src/pyhf/infer/__init__.py
@@ -2,6 +2,19 @@
 
 from . import utils
 from .. import get_backend
+from .. import exceptions
+
+
+def _check_hypotest_prerequisites(pdf, data, init_pars, par_bounds, fixed_params):
+    if pdf.config.poi_index is None:
+        raise exceptions.UnspecifiedPOI(
+            'No POI is defined. A POI is required to run a hypothesis test.'
+        )
+
+    if not utils.all_pois_floating(pdf, fixed_params):
+        raise exceptions.InvalidModel(
+            f'POI at index [{pdf.config.poi_index}] is set as fixed, which makes inference impossible. Please unfix the POI to continue.'
+        )
 
 
 def hypotest(
@@ -131,6 +144,8 @@
     par_bounds = par_bounds or pdf.config.suggested_bounds()
     fixed_params = fixed_params or pdf.config.suggested_fixed()
 
+    _check_hypotest_prerequisites(pdf, data, init_pars, par_bounds, fixed_params)
+
     calc = utils.create_calculator(
         calctype,
         data,
diff --git a/src/pyhf/infer/utils.py b/src/pyhf/infer/utils.py
--- a/src/pyhf/infer/utils.py
+++ b/src/pyhf/infer/utils.py
@@ -9,6 +9,24 @@
 log = logging.getLogger(__name__)
 
 
+def all_pois_floating(pdf, fixed_params):
+    r"""
+    Check whether all POI(s) are floating (i.e. not within the fixed set).
+
+    Args:
+        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema
+         ``model.json``.
+        fixed_params (:obj:`list` or `tensor` of :obj:`bool`): Array of
+         :obj:`bool` indicating if model parameters are fixed.
+
+    Returns:
+        :obj:`bool`: The result whether all POIs are floating.
+    """
+
+    poi_fixed = fixed_params[pdf.config.poi_index]
+    return not poi_fixed
+
+
 def create_calculator(calctype, *args, **kwargs):
     """
     Creates a calculator object of the specified `calctype`.

{"golden_diff": "diff --git a/src/pyhf/infer/__init__.py b/src/pyhf/infer/__init__.py\n--- a/src/pyhf/infer/__init__.py\n+++ b/src/pyhf/infer/__init__.py\n@@ -2,6 +2,19 @@\n \n from . import utils\n from .. import get_backend\n+from .. import exceptions\n+\n+\n+def _check_hypotest_prerequisites(pdf, data, init_pars, par_bounds, fixed_params):\n+    if pdf.config.poi_index is None:\n+        raise exceptions.UnspecifiedPOI(\n+            'No POI is defined. A POI is required to run a hypothesis test.'\n+        )\n+\n+    if not utils.all_pois_floating(pdf, fixed_params):\n+        raise exceptions.InvalidModel(\n+            f'POI at index [{pdf.config.poi_index}] is set as fixed, which makes inference impossible. Please unfix the POI to continue.'\n+        )\n \n \n def hypotest(\n@@ -131,6 +144,8 @@\n     par_bounds = par_bounds or pdf.config.suggested_bounds()\n     fixed_params = fixed_params or pdf.config.suggested_fixed()\n \n+    _check_hypotest_prerequisites(pdf, data, init_pars, par_bounds, fixed_params)\n+\n     calc = utils.create_calculator(\n         calctype,\n         data,\ndiff --git a/src/pyhf/infer/utils.py b/src/pyhf/infer/utils.py\n--- a/src/pyhf/infer/utils.py\n+++ b/src/pyhf/infer/utils.py\n@@ -9,6 +9,24 @@\n log = logging.getLogger(__name__)\n \n \n+def all_pois_floating(pdf, fixed_params):\n+    r\"\"\"\n+    Check whether all POI(s) are floating (i.e. not within the fixed set).\n+\n+    Args:\n+        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema\n+         ``model.json``.\n+        fixed_params (:obj:`list` or `tensor` of :obj:`bool`): Array of\n+         :obj:`bool` indicating if model parameters are fixed.\n+\n+    Returns:\n+        :obj:`bool`: The result whether all POIs are floating.\n+    \"\"\"\n+\n+    poi_fixed = fixed_params[pdf.config.poi_index]\n+    return not poi_fixed\n+\n+\n def create_calculator(calctype, *args, **kwargs):\n     \"\"\"\n     Creates a calculator object of the specified `calctype`.\n", "issue": "workspaces with fixed poi fail in hypotest (of course)\n# Description\r\n\r\nsome workspaces (like the one @elskorda provided) have a *fixed* POI in the workspace definition - if you naively put that through `pyhf cls` of course the fit doesn't really do anythinig useful.\r\n\r\noptions\r\n\r\n* in readxml warn that POI is fixed\r\n* in hypotest ensurer to un-fix POI\r\n* others?\r\n\r\n\r\nthe least we could do is throw a warning  in the places where an unconstrained fit iss expected but a fixed POI is observed\n", "before_files": [{"content": "\"\"\"Inference for Statistical Models.\"\"\"\n\nfrom . import utils\nfrom .. import get_backend\n\n\ndef hypotest(\n    poi_test,\n    data,\n    pdf,\n    init_pars=None,\n    par_bounds=None,\n    fixed_params=None,\n    calctype=\"asymptotics\",\n    return_tail_probs=False,\n    return_expected=False,\n    return_expected_set=False,\n    **kwargs,\n):\n    r\"\"\"\n    Compute :math:`p`-values and test statistics for a single value of the parameter of interest.\n\n    See :py:class:`~pyhf.infer.calculators.AsymptoticCalculator` and :py:class:`~pyhf.infer.calculators.ToyCalculator` on additional keyword arguments to be specified.\n\n    Example:\n        >>> import pyhf\n        >>> pyhf.set_backend(\"numpy\")\n        >>> model = pyhf.simplemodels.hepdata_like(\n        ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]\n        ... )\n        >>> observations = [51, 48]\n        >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)\n        >>> mu_test = 1.0\n        >>> CLs_obs, CLs_exp_band = pyhf.infer.hypotest(\n        ...     mu_test, data, model, return_expected_set=True, test_stat=\"qtilde\"\n        ... )\n        >>> CLs_obs\n        array(0.05251497)\n        >>> CLs_exp_band\n        [array(0.00260626), array(0.01382005), array(0.06445321), array(0.23525644), array(0.57303621)]\n\n    Args:\n        poi_test (Number or Tensor): The value of the parameter of interest (POI)\n        data (Number or Tensor): The data considered\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema ``model.json``\n        init_pars (:obj:`tensor` of :obj:`float`): The starting values of the model parameters for minimization.\n        par_bounds (:obj:`tensor`): The extrema of values the model parameters\n            are allowed to reach in the fit.\n            The shape should be ``(n, 2)`` for ``n`` model parameters.\n        fixed_params (:obj:`tensor` of :obj:`bool`): The flag to set a parameter constant to its starting\n            value during minimization.\n        calctype (:obj:`str`): The calculator to create. Choose either 'asymptotics' (default) or 'toybased'.\n        return_tail_probs (:obj:`bool`): Bool for returning :math:`\\mathrm{CL}_{s+b}` and :math:`\\mathrm{CL}_{b}`\n        return_expected (:obj:`bool`): Bool for returning :math:`\\mathrm{CL}_{\\mathrm{exp}}`\n        return_expected_set (:obj:`bool`): Bool for returning the :math:`(-2,-1,0,1,2)\\sigma` :math:`\\mathrm{CL}_{\\mathrm{exp}}` --- the \"Brazil band\"\n\n    Returns:\n        Tuple of Floats and lists of Floats:\n\n            - :math:`\\mathrm{CL}_{s}`: The modified :math:`p`-value compared to\n              the given threshold :math:`\\alpha`, typically taken to be :math:`0.05`,\n              defined in :xref:`arXiv:1007.1727` as\n\n            .. math::\n\n                \\mathrm{CL}_{s} = \\frac{\\mathrm{CL}_{s+b}}{\\mathrm{CL}_{b}} = \\frac{p_{s+b}}{1-p_{b}}\n\n            to protect against excluding signal models in which there is little\n            sensitivity. In the case that :math:`\\mathrm{CL}_{s} \\leq \\alpha`\n            the given signal model is excluded.\n\n            - :math:`\\left[\\mathrm{CL}_{s+b}, \\mathrm{CL}_{b}\\right]`: The\n              signal + background model hypothesis :math:`p`-value\n\n            .. math::\n\n                \\mathrm{CL}_{s+b} = p_{s+b}\n                = p\\left(q \\geq q_{\\mathrm{obs}}\\middle|s+b\\right)\n                = \\int\\limits_{q_{\\mathrm{obs}}}^{\\infty} f\\left(q\\,\\middle|s+b\\right)\\,dq\n                = 1 - F\\left(q_{\\mathrm{obs}}(\\mu)\\,\\middle|\\mu'\\right)\n\n            and 1 minus the background only model hypothesis :math:`p`-value\n\n            .. math::\n\n                \\mathrm{CL}_{b} = 1- p_{b}\n                = p\\left(q \\geq q_{\\mathrm{obs}}\\middle|b\\right)\n                = 1 - \\int\\limits_{-\\infty}^{q_{\\mathrm{obs}}} f\\left(q\\,\\middle|b\\right)\\,dq\n                = 1 - F\\left(q_{\\mathrm{obs}}(\\mu)\\,\\middle|0\\right)\n\n            for signal strength :math:`\\mu` and model hypothesis signal strength\n            :math:`\\mu'`, where the cumulative density functions\n            :math:`F\\left(q(\\mu)\\,\\middle|\\mu'\\right)` are given by Equations (57)\n            and (65) of :xref:`arXiv:1007.1727` for upper-limit-like test\n            statistic :math:`q \\in \\{q_{\\mu}, \\tilde{q}_{\\mu}\\}`.\n            Only returned when ``return_tail_probs`` is ``True``.\n\n            .. note::\n\n                The definitions of the :math:`\\mathrm{CL}_{s+b}` and\n                :math:`\\mathrm{CL}_{b}` used are based on profile likelihood\n                ratio test statistics.\n                This procedure is common in the LHC-era, but differs from\n                procedures used in the LEP and Tevatron eras, as briefly\n                discussed in :math:`\\S` 3.8 of :xref:`arXiv:1007.1727`.\n\n            - :math:`\\mathrm{CL}_{s,\\mathrm{exp}}`: The expected :math:`\\mathrm{CL}_{s}`\n              value corresponding to the test statistic under the background\n              only hypothesis :math:`\\left(\\mu=0\\right)`.\n              Only returned when ``return_expected`` is ``True``.\n\n            - :math:`\\mathrm{CL}_{s,\\mathrm{exp}}` band: The set of expected\n              :math:`\\mathrm{CL}_{s}` values corresponding to the median\n              significance of variations of the signal strength from the\n              background only hypothesis :math:`\\left(\\mu=0\\right)` at\n              :math:`(-2,-1,0,1,2)\\sigma`.\n              That is, the :math:`p`-values that satisfy Equation (89) of\n              :xref:`arXiv:1007.1727`\n\n            .. math::\n\n                \\mathrm{band}_{N\\sigma} = \\mu' + \\sigma\\,\\Phi^{-1}\\left(1-\\alpha\\right) \\pm N\\sigma\n\n            for :math:`\\mu'=0` and :math:`N \\in \\left\\{-2, -1, 0, 1, 2\\right\\}`.\n            These values define the boundaries of an uncertainty band sometimes\n            referred to as the \"Brazil band\".\n            Only returned when ``return_expected_set`` is ``True``.\n\n    \"\"\"\n    init_pars = init_pars or pdf.config.suggested_init()\n    par_bounds = par_bounds or pdf.config.suggested_bounds()\n    fixed_params = fixed_params or pdf.config.suggested_fixed()\n\n    calc = utils.create_calculator(\n        calctype,\n        data,\n        pdf,\n        init_pars,\n        par_bounds,\n        fixed_params,\n        **kwargs,\n    )\n\n    teststat = calc.teststatistic(poi_test)\n    sig_plus_bkg_distribution, bkg_only_distribution = calc.distributions(poi_test)\n\n    tb, _ = get_backend()\n    CLsb_obs, CLb_obs, CLs_obs = tuple(\n        tb.astensor(pvalue)\n        for pvalue in calc.pvalues(\n            teststat, sig_plus_bkg_distribution, bkg_only_distribution\n        )\n    )\n    CLsb_exp, CLb_exp, CLs_exp = calc.expected_pvalues(\n        sig_plus_bkg_distribution, bkg_only_distribution\n    )\n\n    is_q0 = kwargs.get('test_stat', 'qtilde') == 'q0'\n\n    _returns = [CLsb_obs if is_q0 else CLs_obs]\n    if return_tail_probs:\n        if is_q0:\n            _returns.append([CLb_obs])\n        else:\n            _returns.append([CLsb_obs, CLb_obs])\n\n    pvalues_exp_band = [\n        tb.astensor(pvalue) for pvalue in (CLsb_exp if is_q0 else CLs_exp)\n    ]\n    if return_expected_set:\n        if return_expected:\n            _returns.append(tb.astensor(pvalues_exp_band[2]))\n        _returns.append(pvalues_exp_band)\n    elif return_expected:\n        _returns.append(tb.astensor(pvalues_exp_band[2]))\n    # Enforce a consistent return type of the observed CLs\n    return tuple(_returns) if len(_returns) > 1 else _returns[0]\n\n\nfrom . import intervals  # noqa: F401\n\n__all__ = [\"hypotest\"]\n", "path": "src/pyhf/infer/__init__.py"}, {"content": "\"\"\"Inference for Statistical Models.\"\"\"\n\nfrom .calculators import AsymptoticCalculator, ToyCalculator\nfrom ..exceptions import InvalidTestStatistic\nfrom .test_statistics import q0, qmu, qmu_tilde\n\nimport logging\n\nlog = logging.getLogger(__name__)\n\n\ndef create_calculator(calctype, *args, **kwargs):\n    \"\"\"\n    Creates a calculator object of the specified `calctype`.\n\n    See :py:class:`~pyhf.infer.calculators.AsymptoticCalculator` and\n    :py:class:`~pyhf.infer.calculators.ToyCalculator` on additional arguments\n    to be specified.\n\n    Example:\n\n        >>> import pyhf\n        >>> import numpy.random as random\n        >>> random.seed(0)\n        >>> model = pyhf.simplemodels.hepdata_like(\n        ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0],\n        ... )\n        >>> observations = [51, 48]\n        >>> data = observations + model.config.auxdata\n        >>> mu_test = 1.0\n        >>> toy_calculator = pyhf.infer.utils.create_calculator(\n        ...     \"toybased\", data, model, ntoys=100, test_stat=\"qtilde\", track_progress=False\n        ... )\n        >>> qmu_sig, qmu_bkg = toy_calculator.distributions(mu_test)\n        >>> qmu_sig.pvalue(mu_test), qmu_bkg.pvalue(mu_test)\n        (array(0.14), array(0.76))\n\n    Args:\n        calctype (:obj:`str`): The calculator to create. Choose either\n        'asymptotics' or 'toybased'.\n\n    Returns:\n        calculator (:obj:`object`): A calculator.\n    \"\"\"\n    return {'asymptotics': AsymptoticCalculator, 'toybased': ToyCalculator}[calctype](\n        *args, **kwargs\n    )\n\n\ndef get_test_stat(name):\n    \"\"\"\n    Get the test statistic function by name. The following test statistics are supported:\n\n    - :func:`~pyhf.infer.test_statistics.q0`\n    - :func:`~pyhf.infer.test_statistics.qmu`\n    - :func:`~pyhf.infer.test_statistics.qmu_tilde`\n\n    Example:\n\n        >>> from pyhf.infer import utils, test_statistics\n        >>> utils.get_test_stat(\"q0\")\n        <function q0 at 0x...>\n        >>> utils.get_test_stat(\"q0\") == test_statistics.q0\n        True\n        >>> utils.get_test_stat(\"q\")\n        <function qmu at 0x...>\n        >>> utils.get_test_stat(\"q\") == test_statistics.qmu\n        True\n        >>> utils.get_test_stat(\"qtilde\")\n        <function qmu_tilde at 0x...>\n        >>> utils.get_test_stat(\"qtilde\") == test_statistics.qmu_tilde\n        True\n\n    Args:\n        name (:obj:`str`): The name of the test statistic to retrieve\n\n\n    Returns:\n        callable: The test statistic function\n    \"\"\"\n    _mapping = {\n        \"q0\": q0,\n        \"q\": qmu,\n        \"qtilde\": qmu_tilde,\n    }\n    try:\n        return _mapping[name]\n    except KeyError:\n        raise InvalidTestStatistic\n", "path": "src/pyhf/infer/utils.py"}], "after_files": [{"content": "\"\"\"Inference for Statistical Models.\"\"\"\n\nfrom . import utils\nfrom .. import get_backend\nfrom .. import exceptions\n\n\ndef _check_hypotest_prerequisites(pdf, data, init_pars, par_bounds, fixed_params):\n    if pdf.config.poi_index is None:\n        raise exceptions.UnspecifiedPOI(\n            'No POI is defined. A POI is required to run a hypothesis test.'\n        )\n\n    if not utils.all_pois_floating(pdf, fixed_params):\n        raise exceptions.InvalidModel(\n            f'POI at index [{pdf.config.poi_index}] is set as fixed, which makes inference impossible. Please unfix the POI to continue.'\n        )\n\n\ndef hypotest(\n    poi_test,\n    data,\n    pdf,\n    init_pars=None,\n    par_bounds=None,\n    fixed_params=None,\n    calctype=\"asymptotics\",\n    return_tail_probs=False,\n    return_expected=False,\n    return_expected_set=False,\n    **kwargs,\n):\n    r\"\"\"\n    Compute :math:`p`-values and test statistics for a single value of the parameter of interest.\n\n    See :py:class:`~pyhf.infer.calculators.AsymptoticCalculator` and :py:class:`~pyhf.infer.calculators.ToyCalculator` on additional keyword arguments to be specified.\n\n    Example:\n        >>> import pyhf\n        >>> pyhf.set_backend(\"numpy\")\n        >>> model = pyhf.simplemodels.hepdata_like(\n        ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]\n        ... )\n        >>> observations = [51, 48]\n        >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)\n        >>> mu_test = 1.0\n        >>> CLs_obs, CLs_exp_band = pyhf.infer.hypotest(\n        ...     mu_test, data, model, return_expected_set=True, test_stat=\"qtilde\"\n        ... )\n        >>> CLs_obs\n        array(0.05251497)\n        >>> CLs_exp_band\n        [array(0.00260626), array(0.01382005), array(0.06445321), array(0.23525644), array(0.57303621)]\n\n    Args:\n        poi_test (Number or Tensor): The value of the parameter of interest (POI)\n        data (Number or Tensor): The data considered\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema ``model.json``\n        init_pars (:obj:`tensor` of :obj:`float`): The starting values of the model parameters for minimization.\n        par_bounds (:obj:`tensor`): The extrema of values the model parameters\n            are allowed to reach in the fit.\n            The shape should be ``(n, 2)`` for ``n`` model parameters.\n        fixed_params (:obj:`tensor` of :obj:`bool`): The flag to set a parameter constant to its starting\n            value during minimization.\n        calctype (:obj:`str`): The calculator to create. Choose either 'asymptotics' (default) or 'toybased'.\n        return_tail_probs (:obj:`bool`): Bool for returning :math:`\\mathrm{CL}_{s+b}` and :math:`\\mathrm{CL}_{b}`\n        return_expected (:obj:`bool`): Bool for returning :math:`\\mathrm{CL}_{\\mathrm{exp}}`\n        return_expected_set (:obj:`bool`): Bool for returning the :math:`(-2,-1,0,1,2)\\sigma` :math:`\\mathrm{CL}_{\\mathrm{exp}}` --- the \"Brazil band\"\n\n    Returns:\n        Tuple of Floats and lists of Floats:\n\n            - :math:`\\mathrm{CL}_{s}`: The modified :math:`p`-value compared to\n              the given threshold :math:`\\alpha`, typically taken to be :math:`0.05`,\n              defined in :xref:`arXiv:1007.1727` as\n\n            .. math::\n\n                \\mathrm{CL}_{s} = \\frac{\\mathrm{CL}_{s+b}}{\\mathrm{CL}_{b}} = \\frac{p_{s+b}}{1-p_{b}}\n\n            to protect against excluding signal models in which there is little\n            sensitivity. In the case that :math:`\\mathrm{CL}_{s} \\leq \\alpha`\n            the given signal model is excluded.\n\n            - :math:`\\left[\\mathrm{CL}_{s+b}, \\mathrm{CL}_{b}\\right]`: The\n              signal + background model hypothesis :math:`p`-value\n\n            .. math::\n\n                \\mathrm{CL}_{s+b} = p_{s+b}\n                = p\\left(q \\geq q_{\\mathrm{obs}}\\middle|s+b\\right)\n                = \\int\\limits_{q_{\\mathrm{obs}}}^{\\infty} f\\left(q\\,\\middle|s+b\\right)\\,dq\n                = 1 - F\\left(q_{\\mathrm{obs}}(\\mu)\\,\\middle|\\mu'\\right)\n\n            and 1 minus the background only model hypothesis :math:`p`-value\n\n            .. math::\n\n                \\mathrm{CL}_{b} = 1- p_{b}\n                = p\\left(q \\geq q_{\\mathrm{obs}}\\middle|b\\right)\n                = 1 - \\int\\limits_{-\\infty}^{q_{\\mathrm{obs}}} f\\left(q\\,\\middle|b\\right)\\,dq\n                = 1 - F\\left(q_{\\mathrm{obs}}(\\mu)\\,\\middle|0\\right)\n\n            for signal strength :math:`\\mu` and model hypothesis signal strength\n            :math:`\\mu'`, where the cumulative density functions\n            :math:`F\\left(q(\\mu)\\,\\middle|\\mu'\\right)` are given by Equations (57)\n            and (65) of :xref:`arXiv:1007.1727` for upper-limit-like test\n            statistic :math:`q \\in \\{q_{\\mu}, \\tilde{q}_{\\mu}\\}`.\n            Only returned when ``return_tail_probs`` is ``True``.\n\n            .. note::\n\n                The definitions of the :math:`\\mathrm{CL}_{s+b}` and\n                :math:`\\mathrm{CL}_{b}` used are based on profile likelihood\n                ratio test statistics.\n                This procedure is common in the LHC-era, but differs from\n                procedures used in the LEP and Tevatron eras, as briefly\n                discussed in :math:`\\S` 3.8 of :xref:`arXiv:1007.1727`.\n\n            - :math:`\\mathrm{CL}_{s,\\mathrm{exp}}`: The expected :math:`\\mathrm{CL}_{s}`\n              value corresponding to the test statistic under the background\n              only hypothesis :math:`\\left(\\mu=0\\right)`.\n              Only returned when ``return_expected`` is ``True``.\n\n            - :math:`\\mathrm{CL}_{s,\\mathrm{exp}}` band: The set of expected\n              :math:`\\mathrm{CL}_{s}` values corresponding to the median\n              significance of variations of the signal strength from the\n              background only hypothesis :math:`\\left(\\mu=0\\right)` at\n              :math:`(-2,-1,0,1,2)\\sigma`.\n              That is, the :math:`p`-values that satisfy Equation (89) of\n              :xref:`arXiv:1007.1727`\n\n            .. math::\n\n                \\mathrm{band}_{N\\sigma} = \\mu' + \\sigma\\,\\Phi^{-1}\\left(1-\\alpha\\right) \\pm N\\sigma\n\n            for :math:`\\mu'=0` and :math:`N \\in \\left\\{-2, -1, 0, 1, 2\\right\\}`.\n            These values define the boundaries of an uncertainty band sometimes\n            referred to as the \"Brazil band\".\n            Only returned when ``return_expected_set`` is ``True``.\n\n    \"\"\"\n    init_pars = init_pars or pdf.config.suggested_init()\n    par_bounds = par_bounds or pdf.config.suggested_bounds()\n    fixed_params = fixed_params or pdf.config.suggested_fixed()\n\n    _check_hypotest_prerequisites(pdf, data, init_pars, par_bounds, fixed_params)\n\n    calc = utils.create_calculator(\n        calctype,\n        data,\n        pdf,\n        init_pars,\n        par_bounds,\n        fixed_params,\n        **kwargs,\n    )\n\n    teststat = calc.teststatistic(poi_test)\n    sig_plus_bkg_distribution, bkg_only_distribution = calc.distributions(poi_test)\n\n    tb, _ = get_backend()\n    CLsb_obs, CLb_obs, CLs_obs = tuple(\n        tb.astensor(pvalue)\n        for pvalue in calc.pvalues(\n            teststat, sig_plus_bkg_distribution, bkg_only_distribution\n        )\n    )\n    CLsb_exp, CLb_exp, CLs_exp = calc.expected_pvalues(\n        sig_plus_bkg_distribution, bkg_only_distribution\n    )\n\n    is_q0 = kwargs.get('test_stat', 'qtilde') == 'q0'\n\n    _returns = [CLsb_obs if is_q0 else CLs_obs]\n    if return_tail_probs:\n        if is_q0:\n            _returns.append([CLb_obs])\n        else:\n            _returns.append([CLsb_obs, CLb_obs])\n\n    pvalues_exp_band = [\n        tb.astensor(pvalue) for pvalue in (CLsb_exp if is_q0 else CLs_exp)\n    ]\n    if return_expected_set:\n        if return_expected:\n            _returns.append(tb.astensor(pvalues_exp_band[2]))\n        _returns.append(pvalues_exp_band)\n    elif return_expected:\n        _returns.append(tb.astensor(pvalues_exp_band[2]))\n    # Enforce a consistent return type of the observed CLs\n    return tuple(_returns) if len(_returns) > 1 else _returns[0]\n\n\nfrom . import intervals  # noqa: F401\n\n__all__ = [\"hypotest\"]\n", "path": "src/pyhf/infer/__init__.py"}, {"content": "\"\"\"Inference for Statistical Models.\"\"\"\n\nfrom .calculators import AsymptoticCalculator, ToyCalculator\nfrom ..exceptions import InvalidTestStatistic\nfrom .test_statistics import q0, qmu, qmu_tilde\n\nimport logging\n\nlog = logging.getLogger(__name__)\n\n\ndef all_pois_floating(pdf, fixed_params):\n    r\"\"\"\n    Check whether all POI(s) are floating (i.e. not within the fixed set).\n\n    Args:\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema\n         ``model.json``.\n        fixed_params (:obj:`list` or `tensor` of :obj:`bool`): Array of\n         :obj:`bool` indicating if model parameters are fixed.\n\n    Returns:\n        :obj:`bool`: The result whether all POIs are floating.\n    \"\"\"\n\n    poi_fixed = fixed_params[pdf.config.poi_index]\n    return not poi_fixed\n\n\ndef create_calculator(calctype, *args, **kwargs):\n    \"\"\"\n    Creates a calculator object of the specified `calctype`.\n\n    See :py:class:`~pyhf.infer.calculators.AsymptoticCalculator` and\n    :py:class:`~pyhf.infer.calculators.ToyCalculator` on additional arguments\n    to be specified.\n\n    Example:\n\n        >>> import pyhf\n        >>> import numpy.random as random\n        >>> random.seed(0)\n        >>> model = pyhf.simplemodels.hepdata_like(\n        ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0],\n        ... )\n        >>> observations = [51, 48]\n        >>> data = observations + model.config.auxdata\n        >>> mu_test = 1.0\n        >>> toy_calculator = pyhf.infer.utils.create_calculator(\n        ...     \"toybased\", data, model, ntoys=100, test_stat=\"qtilde\", track_progress=False\n        ... )\n        >>> qmu_sig, qmu_bkg = toy_calculator.distributions(mu_test)\n        >>> qmu_sig.pvalue(mu_test), qmu_bkg.pvalue(mu_test)\n        (array(0.14), array(0.76))\n\n    Args:\n        calctype (:obj:`str`): The calculator to create. Choose either\n        'asymptotics' or 'toybased'.\n\n    Returns:\n        calculator (:obj:`object`): A calculator.\n    \"\"\"\n    return {'asymptotics': AsymptoticCalculator, 'toybased': ToyCalculator}[calctype](\n        *args, **kwargs\n    )\n\n\ndef get_test_stat(name):\n    \"\"\"\n    Get the test statistic function by name. The following test statistics are supported:\n\n    - :func:`~pyhf.infer.test_statistics.q0`\n    - :func:`~pyhf.infer.test_statistics.qmu`\n    - :func:`~pyhf.infer.test_statistics.qmu_tilde`\n\n    Example:\n\n        >>> from pyhf.infer import utils, test_statistics\n        >>> utils.get_test_stat(\"q0\")\n        <function q0 at 0x...>\n        >>> utils.get_test_stat(\"q0\") == test_statistics.q0\n        True\n        >>> utils.get_test_stat(\"q\")\n        <function qmu at 0x...>\n        >>> utils.get_test_stat(\"q\") == test_statistics.qmu\n        True\n        >>> utils.get_test_stat(\"qtilde\")\n        <function qmu_tilde at 0x...>\n        >>> utils.get_test_stat(\"qtilde\") == test_statistics.qmu_tilde\n        True\n\n    Args:\n        name (:obj:`str`): The name of the test statistic to retrieve\n\n\n    Returns:\n        callable: The test statistic function\n    \"\"\"\n    _mapping = {\n        \"q0\": q0,\n        \"q\": qmu,\n        \"qtilde\": qmu_tilde,\n    }\n    try:\n        return _mapping[name]\n    except KeyError:\n        raise InvalidTestStatistic\n", "path": "src/pyhf/infer/utils.py"}]}