We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Add commas around constrained maximal likelihood in docstring for clarity
# Description

In PR #905 the docstring for [`pyhf.infer.mle.fixed_poi_fit`](https://scikit-hep.org/pyhf/_generated/pyhf.infer.mle.fixed_poi_fit.html) was amended, but the lines

https://github.com/scikit-hep/pyhf/blob/fd7930cce36cbc3a2d0ee1828f060d7382129579/src/pyhf/infer/mle.py#L134-L135

are missing commas around the likelihood, making it difficult to read

![missing_commas](https://user-images.githubusercontent.com/5142394/102830397-d9a87b00-43ae-11eb-8acc-502370cf9353.png)

It should read

```
,:math:`L\left(\mu, \hat{\hat{\boldsymbol{\theta}}}\right)`, in the profile 
```

Add commas around constrained maximal likelihood in docstring for clarity
# Description

In PR #905 the docstring for [`pyhf.infer.mle.fixed_poi_fit`](https://scikit-hep.org/pyhf/_generated/pyhf.infer.mle.fixed_poi_fit.html) was amended, but the lines

https://github.com/scikit-hep/pyhf/blob/fd7930cce36cbc3a2d0ee1828f060d7382129579/src/pyhf/infer/mle.py#L134-L135

are missing commas around the likelihood, making it difficult to read

![missing_commas](https://user-images.githubusercontent.com/5142394/102830397-d9a87b00-43ae-11eb-8acc-502370cf9353.png)

It should read

```
,:math:`L\left(\mu, \hat{\hat{\boldsymbol{\theta}}}\right)`, in the profile 
```


--- 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/mle.py`
Content:
```
1 """Module for Maximum Likelihood Estimation."""
2 from .. import get_backend
3 from ..exceptions import UnspecifiedPOI
4 
5 
6 def twice_nll(pars, data, pdf):
7     r"""
8     Two times the negative log-likelihood of the model parameters, :math:`\left(\mu, \boldsymbol{\theta}\right)`, given the observed data.
9     It is used in the calculation of the test statistic, :math:`t_{\mu}`, as defiend in Equation (8) in :xref:`arXiv:1007.1727`
10 
11     .. math::
12 
13        t_{\mu} = -2\ln\lambda\left(\mu\right)
14 
15     where :math:`\lambda\left(\mu\right)` is the profile likelihood ratio as defined in Equation (7)
16 
17     .. math::
18 
19        \lambda\left(\mu\right) = \frac{L\left(\mu, \hat{\hat{\boldsymbol{\theta}}}\right)}{L\left(\hat{\mu}, \hat{\boldsymbol{\theta}}\right)}\,.
20 
21     It serves as the objective function to minimize in :func:`~pyhf.infer.mle.fit`
22     and :func:`~pyhf.infer.mle.fixed_poi_fit`.
23 
24     Example:
25         >>> import pyhf
26         >>> pyhf.set_backend("numpy")
27         >>> model = pyhf.simplemodels.hepdata_like(
28         ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]
29         ... )
30         >>> observations = [51, 48]
31         >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)
32         >>> parameters = model.config.suggested_init()  # nominal parameters
33         >>> twice_nll = pyhf.infer.mle.twice_nll(parameters, data, model)
34         >>> twice_nll
35         array([30.77525435])
36         >>> -2 * model.logpdf(parameters, data) == twice_nll
37         array([ True])
38 
39     Args:
40         pars (:obj:`tensor`): The parameters of the HistFactory model
41         data (:obj:`tensor`): The data to be considered
42         pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json
43 
44     Returns:
45         Tensor: Two times the negative log-likelihood, :math:`-2\ln L\left(\mu, \boldsymbol{\theta}\right)`
46     """
47     return -2 * pdf.logpdf(pars, data)
48 
49 
50 def _validate_fit_inputs(init_pars, par_bounds, fixed_params):
51     for par_idx, (value, bound) in enumerate(zip(init_pars, par_bounds)):
52         if not (bound[0] <= value <= bound[1]):
53             raise ValueError(
54                 f"fit initialization parameter (index: {par_idx}, value: {value}) lies outside of its bounds: {bound}"
55                 + "\nTo correct this adjust the initialization parameter values in the model spec or those given"
56                 + "\nas arguments to pyhf.infer.fit. If this value is intended, adjust the range of the parameter"
57                 + "\nbounds."
58             )
59 
60 
61 def fit(data, pdf, init_pars=None, par_bounds=None, fixed_params=None, **kwargs):
62     r"""
63     Run a maximum likelihood fit.
64     This is done by minimizing the objective function :func:`~pyhf.infer.mle.twice_nll`
65     of the model parameters given the observed data.
66     This is used to produce the maximal likelihood :math:`L\left(\hat{\mu}, \hat{\boldsymbol{\theta}}\right)`
67     in the profile likelihood ratio in Equation (7) in :xref:`arXiv:1007.1727`
68 
69     .. math::
70 
71        \lambda\left(\mu\right) = \frac{L\left(\mu, \hat{\hat{\boldsymbol{\theta}}}\right)}{L\left(\hat{\mu}, \hat{\boldsymbol{\theta}}\right)}
72 
73 
74     .. note::
75 
76         :func:`twice_nll` is the objective function given to the optimizer and
77         is returned evaluated at the best fit model parameters when the optional
78         kwarg ``return_fitted_val`` is ``True``.
79 
80     Example:
81         >>> import pyhf
82         >>> pyhf.set_backend("numpy")
83         >>> model = pyhf.simplemodels.hepdata_like(
84         ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]
85         ... )
86         >>> observations = [51, 48]
87         >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)
88         >>> bestfit_pars, twice_nll = pyhf.infer.mle.fit(data, model, return_fitted_val=True)
89         >>> bestfit_pars
90         array([0.        , 1.0030512 , 0.96266961])
91         >>> twice_nll
92         array(24.98393521)
93         >>> -2 * model.logpdf(bestfit_pars, data) == twice_nll
94         array([ True])
95 
96     Args:
97         data (:obj:`tensor`): The data
98         pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json
99         init_pars (:obj:`list`): Values to initialize the model parameters at for the fit
100         par_bounds (:obj:`list` of :obj:`list`\s or :obj:`tuple`\s): The extrema of values the model parameters are allowed to reach in the fit
101         fixed_params (:obj:`list`): Parameters to be held constant in the fit.
102         kwargs: Keyword arguments passed through to the optimizer API
103 
104     Returns:
105         See optimizer API
106 
107     """
108     _, opt = get_backend()
109     init_pars = init_pars or pdf.config.suggested_init()
110     par_bounds = par_bounds or pdf.config.suggested_bounds()
111     fixed_params = fixed_params or pdf.config.suggested_fixed()
112 
113     _validate_fit_inputs(init_pars, par_bounds, fixed_params)
114 
115     # get fixed vals from the model
116     fixed_vals = [
117         (index, init)
118         for index, (init, is_fixed) in enumerate(zip(init_pars, fixed_params))
119         if is_fixed
120     ]
121 
122     return opt.minimize(
123         twice_nll, data, pdf, init_pars, par_bounds, fixed_vals, **kwargs
124     )
125 
126 
127 def fixed_poi_fit(
128     poi_val, data, pdf, init_pars=None, par_bounds=None, fixed_params=None, **kwargs
129 ):
130     r"""
131     Run a maximum likelihood fit with the POI value fixed.
132     This is done by minimizing the objective function of :func:`~pyhf.infer.mle.twice_nll`
133     of the model parameters given the observed data, for a given fixed value of :math:`\mu`.
134     This is used to produce the constrained maximal likelihood for the given :math:`\mu`
135     :math:`L\left(\mu, \hat{\hat{\boldsymbol{\theta}}}\right)` in the profile
136     likelihood ratio in Equation (7) in :xref:`arXiv:1007.1727`
137 
138     .. math::
139 
140        \lambda\left(\mu\right) = \frac{L\left(\mu, \hat{\hat{\boldsymbol{\theta}}}\right)}{L\left(\hat{\mu}, \hat{\boldsymbol{\theta}}\right)}
141 
142     .. note::
143 
144         :func:`twice_nll` is the objective function given to the optimizer and
145         is returned evaluated at the best fit model parameters when the optional
146         kwarg ``return_fitted_val`` is ``True``.
147 
148     Example:
149         >>> import pyhf
150         >>> pyhf.set_backend("numpy")
151         >>> model = pyhf.simplemodels.hepdata_like(
152         ...     signal_data=[12.0, 11.0], bkg_data=[50.0, 52.0], bkg_uncerts=[3.0, 7.0]
153         ... )
154         >>> observations = [51, 48]
155         >>> data = pyhf.tensorlib.astensor(observations + model.config.auxdata)
156         >>> test_poi = 1.0
157         >>> bestfit_pars, twice_nll = pyhf.infer.mle.fixed_poi_fit(
158         ...     test_poi, data, model, return_fitted_val=True
159         ... )
160         >>> bestfit_pars
161         array([1.        , 0.97224597, 0.87553894])
162         >>> twice_nll
163         array(28.92218013)
164         >>> -2 * model.logpdf(bestfit_pars, data) == twice_nll
165         array([ True])
166 
167     Args:
168         data: The data
169         pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json
170         init_pars (:obj:`list`): Values to initialize the model parameters at for the fit
171         par_bounds (:obj:`list` of :obj:`list`\s or :obj:`tuple`\s): The extrema of values the model parameters are allowed to reach in the fit
172         fixed_params (:obj:`list`): Parameters to be held constant in the fit.
173         kwargs: Keyword arguments passed through to the optimizer API
174 
175     Returns:
176         See optimizer API
177 
178     """
179     if pdf.config.poi_index is None:
180         raise UnspecifiedPOI(
181             'No POI is defined. A POI is required to fit with a fixed POI.'
182         )
183 
184     init_pars = [*(init_pars or pdf.config.suggested_init())]
185     fixed_params = [*(fixed_params or pdf.config.suggested_fixed())]
186 
187     init_pars[pdf.config.poi_index] = poi_val
188     fixed_params[pdf.config.poi_index] = True
189 
190     return fit(data, pdf, init_pars, par_bounds, fixed_params, **kwargs)
191
```
--- 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/mle.py b/src/pyhf/infer/mle.py
--- a/src/pyhf/infer/mle.py
+++ b/src/pyhf/infer/mle.py
@@ -131,8 +131,8 @@
     Run a maximum likelihood fit with the POI value fixed.
     This is done by minimizing the objective function of :func:`~pyhf.infer.mle.twice_nll`
     of the model parameters given the observed data, for a given fixed value of :math:`\mu`.
-    This is used to produce the constrained maximal likelihood for the given :math:`\mu`
-    :math:`L\left(\mu, \hat{\hat{\boldsymbol{\theta}}}\right)` in the profile
+    This is used to produce the constrained maximal likelihood for the given :math:`\mu`,
+    :math:`L\left(\mu, \hat{\hat{\boldsymbol{\theta}}}\right)`, in the profile
     likelihood ratio in Equation (7) in :xref:`arXiv:1007.1727`
 
     .. math::

{"golden_diff": "diff --git a/src/pyhf/infer/mle.py b/src/pyhf/infer/mle.py\n--- a/src/pyhf/infer/mle.py\n+++ b/src/pyhf/infer/mle.py\n@@ -131,8 +131,8 @@\n     Run a maximum likelihood fit with the POI value fixed.\n     This is done by minimizing the objective function of :func:`~pyhf.infer.mle.twice_nll`\n     of the model parameters given the observed data, for a given fixed value of :math:`\\mu`.\n-    This is used to produce the constrained maximal likelihood for the given :math:`\\mu`\n-    :math:`L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)` in the profile\n+    This is used to produce the constrained maximal likelihood for the given :math:`\\mu`,\n+    :math:`L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)`, in the profile\n     likelihood ratio in Equation (7) in :xref:`arXiv:1007.1727`\n \n     .. math::\n", "issue": "Add commas around constrained maximal likelihood in docstring for clarity\n# Description\r\n\r\nIn PR #905 the docstring for [`pyhf.infer.mle.fixed_poi_fit`](https://scikit-hep.org/pyhf/_generated/pyhf.infer.mle.fixed_poi_fit.html) was amended, but the lines\r\n\r\nhttps://github.com/scikit-hep/pyhf/blob/fd7930cce36cbc3a2d0ee1828f060d7382129579/src/pyhf/infer/mle.py#L134-L135\r\n\r\nare missing commas around the likelihood, making it difficult to read\r\n\r\n![missing_commas](https://user-images.githubusercontent.com/5142394/102830397-d9a87b00-43ae-11eb-8acc-502370cf9353.png)\r\n\r\nIt should read\r\n\r\n```\r\n,:math:`L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)`, in the profile \r\n```\r\n\nAdd commas around constrained maximal likelihood in docstring for clarity\n# Description\r\n\r\nIn PR #905 the docstring for [`pyhf.infer.mle.fixed_poi_fit`](https://scikit-hep.org/pyhf/_generated/pyhf.infer.mle.fixed_poi_fit.html) was amended, but the lines\r\n\r\nhttps://github.com/scikit-hep/pyhf/blob/fd7930cce36cbc3a2d0ee1828f060d7382129579/src/pyhf/infer/mle.py#L134-L135\r\n\r\nare missing commas around the likelihood, making it difficult to read\r\n\r\n![missing_commas](https://user-images.githubusercontent.com/5142394/102830397-d9a87b00-43ae-11eb-8acc-502370cf9353.png)\r\n\r\nIt should read\r\n\r\n```\r\n,:math:`L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)`, in the profile \r\n```\r\n\n", "before_files": [{"content": "\"\"\"Module for Maximum Likelihood Estimation.\"\"\"\nfrom .. import get_backend\nfrom ..exceptions import UnspecifiedPOI\n\n\ndef twice_nll(pars, data, pdf):\n    r\"\"\"\n    Two times the negative log-likelihood of the model parameters, :math:`\\left(\\mu, \\boldsymbol{\\theta}\\right)`, given the observed data.\n    It is used in the calculation of the test statistic, :math:`t_{\\mu}`, as defiend in Equation (8) in :xref:`arXiv:1007.1727`\n\n    .. math::\n\n       t_{\\mu} = -2\\ln\\lambda\\left(\\mu\\right)\n\n    where :math:`\\lambda\\left(\\mu\\right)` is the profile likelihood ratio as defined in Equation (7)\n\n    .. math::\n\n       \\lambda\\left(\\mu\\right) = \\frac{L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)}{L\\left(\\hat{\\mu}, \\hat{\\boldsymbol{\\theta}}\\right)}\\,.\n\n    It serves as the objective function to minimize in :func:`~pyhf.infer.mle.fit`\n    and :func:`~pyhf.infer.mle.fixed_poi_fit`.\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        >>> parameters = model.config.suggested_init()  # nominal parameters\n        >>> twice_nll = pyhf.infer.mle.twice_nll(parameters, data, model)\n        >>> twice_nll\n        array([30.77525435])\n        >>> -2 * model.logpdf(parameters, data) == twice_nll\n        array([ True])\n\n    Args:\n        pars (:obj:`tensor`): The parameters of the HistFactory model\n        data (:obj:`tensor`): The data to be considered\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json\n\n    Returns:\n        Tensor: Two times the negative log-likelihood, :math:`-2\\ln L\\left(\\mu, \\boldsymbol{\\theta}\\right)`\n    \"\"\"\n    return -2 * pdf.logpdf(pars, data)\n\n\ndef _validate_fit_inputs(init_pars, par_bounds, fixed_params):\n    for par_idx, (value, bound) in enumerate(zip(init_pars, par_bounds)):\n        if not (bound[0] <= value <= bound[1]):\n            raise ValueError(\n                f\"fit initialization parameter (index: {par_idx}, value: {value}) lies outside of its bounds: {bound}\"\n                + \"\\nTo correct this adjust the initialization parameter values in the model spec or those given\"\n                + \"\\nas arguments to pyhf.infer.fit. If this value is intended, adjust the range of the parameter\"\n                + \"\\nbounds.\"\n            )\n\n\ndef fit(data, pdf, init_pars=None, par_bounds=None, fixed_params=None, **kwargs):\n    r\"\"\"\n    Run a maximum likelihood fit.\n    This is done by minimizing the objective function :func:`~pyhf.infer.mle.twice_nll`\n    of the model parameters given the observed data.\n    This is used to produce the maximal likelihood :math:`L\\left(\\hat{\\mu}, \\hat{\\boldsymbol{\\theta}}\\right)`\n    in the profile likelihood ratio in Equation (7) in :xref:`arXiv:1007.1727`\n\n    .. math::\n\n       \\lambda\\left(\\mu\\right) = \\frac{L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)}{L\\left(\\hat{\\mu}, \\hat{\\boldsymbol{\\theta}}\\right)}\n\n\n    .. note::\n\n        :func:`twice_nll` is the objective function given to the optimizer and\n        is returned evaluated at the best fit model parameters when the optional\n        kwarg ``return_fitted_val`` is ``True``.\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        >>> bestfit_pars, twice_nll = pyhf.infer.mle.fit(data, model, return_fitted_val=True)\n        >>> bestfit_pars\n        array([0.        , 1.0030512 , 0.96266961])\n        >>> twice_nll\n        array(24.98393521)\n        >>> -2 * model.logpdf(bestfit_pars, data) == twice_nll\n        array([ True])\n\n    Args:\n        data (:obj:`tensor`): The data\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json\n        init_pars (:obj:`list`): Values to initialize the model parameters at for the fit\n        par_bounds (:obj:`list` of :obj:`list`\\s or :obj:`tuple`\\s): The extrema of values the model parameters are allowed to reach in the fit\n        fixed_params (:obj:`list`): Parameters to be held constant in the fit.\n        kwargs: Keyword arguments passed through to the optimizer API\n\n    Returns:\n        See optimizer API\n\n    \"\"\"\n    _, opt = get_backend()\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    _validate_fit_inputs(init_pars, par_bounds, fixed_params)\n\n    # get fixed vals from the model\n    fixed_vals = [\n        (index, init)\n        for index, (init, is_fixed) in enumerate(zip(init_pars, fixed_params))\n        if is_fixed\n    ]\n\n    return opt.minimize(\n        twice_nll, data, pdf, init_pars, par_bounds, fixed_vals, **kwargs\n    )\n\n\ndef fixed_poi_fit(\n    poi_val, data, pdf, init_pars=None, par_bounds=None, fixed_params=None, **kwargs\n):\n    r\"\"\"\n    Run a maximum likelihood fit with the POI value fixed.\n    This is done by minimizing the objective function of :func:`~pyhf.infer.mle.twice_nll`\n    of the model parameters given the observed data, for a given fixed value of :math:`\\mu`.\n    This is used to produce the constrained maximal likelihood for the given :math:`\\mu`\n    :math:`L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)` in the profile\n    likelihood ratio in Equation (7) in :xref:`arXiv:1007.1727`\n\n    .. math::\n\n       \\lambda\\left(\\mu\\right) = \\frac{L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)}{L\\left(\\hat{\\mu}, \\hat{\\boldsymbol{\\theta}}\\right)}\n\n    .. note::\n\n        :func:`twice_nll` is the objective function given to the optimizer and\n        is returned evaluated at the best fit model parameters when the optional\n        kwarg ``return_fitted_val`` is ``True``.\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        >>> test_poi = 1.0\n        >>> bestfit_pars, twice_nll = pyhf.infer.mle.fixed_poi_fit(\n        ...     test_poi, data, model, return_fitted_val=True\n        ... )\n        >>> bestfit_pars\n        array([1.        , 0.97224597, 0.87553894])\n        >>> twice_nll\n        array(28.92218013)\n        >>> -2 * model.logpdf(bestfit_pars, data) == twice_nll\n        array([ True])\n\n    Args:\n        data: The data\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json\n        init_pars (:obj:`list`): Values to initialize the model parameters at for the fit\n        par_bounds (:obj:`list` of :obj:`list`\\s or :obj:`tuple`\\s): The extrema of values the model parameters are allowed to reach in the fit\n        fixed_params (:obj:`list`): Parameters to be held constant in the fit.\n        kwargs: Keyword arguments passed through to the optimizer API\n\n    Returns:\n        See optimizer API\n\n    \"\"\"\n    if pdf.config.poi_index is None:\n        raise UnspecifiedPOI(\n            'No POI is defined. A POI is required to fit with a fixed POI.'\n        )\n\n    init_pars = [*(init_pars or pdf.config.suggested_init())]\n    fixed_params = [*(fixed_params or pdf.config.suggested_fixed())]\n\n    init_pars[pdf.config.poi_index] = poi_val\n    fixed_params[pdf.config.poi_index] = True\n\n    return fit(data, pdf, init_pars, par_bounds, fixed_params, **kwargs)\n", "path": "src/pyhf/infer/mle.py"}], "after_files": [{"content": "\"\"\"Module for Maximum Likelihood Estimation.\"\"\"\nfrom .. import get_backend\nfrom ..exceptions import UnspecifiedPOI\n\n\ndef twice_nll(pars, data, pdf):\n    r\"\"\"\n    Two times the negative log-likelihood of the model parameters, :math:`\\left(\\mu, \\boldsymbol{\\theta}\\right)`, given the observed data.\n    It is used in the calculation of the test statistic, :math:`t_{\\mu}`, as defiend in Equation (8) in :xref:`arXiv:1007.1727`\n\n    .. math::\n\n       t_{\\mu} = -2\\ln\\lambda\\left(\\mu\\right)\n\n    where :math:`\\lambda\\left(\\mu\\right)` is the profile likelihood ratio as defined in Equation (7)\n\n    .. math::\n\n       \\lambda\\left(\\mu\\right) = \\frac{L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)}{L\\left(\\hat{\\mu}, \\hat{\\boldsymbol{\\theta}}\\right)}\\,.\n\n    It serves as the objective function to minimize in :func:`~pyhf.infer.mle.fit`\n    and :func:`~pyhf.infer.mle.fixed_poi_fit`.\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        >>> parameters = model.config.suggested_init()  # nominal parameters\n        >>> twice_nll = pyhf.infer.mle.twice_nll(parameters, data, model)\n        >>> twice_nll\n        array([30.77525435])\n        >>> -2 * model.logpdf(parameters, data) == twice_nll\n        array([ True])\n\n    Args:\n        pars (:obj:`tensor`): The parameters of the HistFactory model\n        data (:obj:`tensor`): The data to be considered\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json\n\n    Returns:\n        Tensor: Two times the negative log-likelihood, :math:`-2\\ln L\\left(\\mu, \\boldsymbol{\\theta}\\right)`\n    \"\"\"\n    return -2 * pdf.logpdf(pars, data)\n\n\ndef _validate_fit_inputs(init_pars, par_bounds, fixed_params):\n    for par_idx, (value, bound) in enumerate(zip(init_pars, par_bounds)):\n        if not (bound[0] <= value <= bound[1]):\n            raise ValueError(\n                f\"fit initialization parameter (index: {par_idx}, value: {value}) lies outside of its bounds: {bound}\"\n                + \"\\nTo correct this adjust the initialization parameter values in the model spec or those given\"\n                + \"\\nas arguments to pyhf.infer.fit. If this value is intended, adjust the range of the parameter\"\n                + \"\\nbounds.\"\n            )\n\n\ndef fit(data, pdf, init_pars=None, par_bounds=None, fixed_params=None, **kwargs):\n    r\"\"\"\n    Run a maximum likelihood fit.\n    This is done by minimizing the objective function :func:`~pyhf.infer.mle.twice_nll`\n    of the model parameters given the observed data.\n    This is used to produce the maximal likelihood :math:`L\\left(\\hat{\\mu}, \\hat{\\boldsymbol{\\theta}}\\right)`\n    in the profile likelihood ratio in Equation (7) in :xref:`arXiv:1007.1727`\n\n    .. math::\n\n       \\lambda\\left(\\mu\\right) = \\frac{L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)}{L\\left(\\hat{\\mu}, \\hat{\\boldsymbol{\\theta}}\\right)}\n\n\n    .. note::\n\n        :func:`twice_nll` is the objective function given to the optimizer and\n        is returned evaluated at the best fit model parameters when the optional\n        kwarg ``return_fitted_val`` is ``True``.\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        >>> bestfit_pars, twice_nll = pyhf.infer.mle.fit(data, model, return_fitted_val=True)\n        >>> bestfit_pars\n        array([0.        , 1.0030512 , 0.96266961])\n        >>> twice_nll\n        array(24.98393521)\n        >>> -2 * model.logpdf(bestfit_pars, data) == twice_nll\n        array([ True])\n\n    Args:\n        data (:obj:`tensor`): The data\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json\n        init_pars (:obj:`list`): Values to initialize the model parameters at for the fit\n        par_bounds (:obj:`list` of :obj:`list`\\s or :obj:`tuple`\\s): The extrema of values the model parameters are allowed to reach in the fit\n        fixed_params (:obj:`list`): Parameters to be held constant in the fit.\n        kwargs: Keyword arguments passed through to the optimizer API\n\n    Returns:\n        See optimizer API\n\n    \"\"\"\n    _, opt = get_backend()\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    _validate_fit_inputs(init_pars, par_bounds, fixed_params)\n\n    # get fixed vals from the model\n    fixed_vals = [\n        (index, init)\n        for index, (init, is_fixed) in enumerate(zip(init_pars, fixed_params))\n        if is_fixed\n    ]\n\n    return opt.minimize(\n        twice_nll, data, pdf, init_pars, par_bounds, fixed_vals, **kwargs\n    )\n\n\ndef fixed_poi_fit(\n    poi_val, data, pdf, init_pars=None, par_bounds=None, fixed_params=None, **kwargs\n):\n    r\"\"\"\n    Run a maximum likelihood fit with the POI value fixed.\n    This is done by minimizing the objective function of :func:`~pyhf.infer.mle.twice_nll`\n    of the model parameters given the observed data, for a given fixed value of :math:`\\mu`.\n    This is used to produce the constrained maximal likelihood for the given :math:`\\mu`,\n    :math:`L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)`, in the profile\n    likelihood ratio in Equation (7) in :xref:`arXiv:1007.1727`\n\n    .. math::\n\n       \\lambda\\left(\\mu\\right) = \\frac{L\\left(\\mu, \\hat{\\hat{\\boldsymbol{\\theta}}}\\right)}{L\\left(\\hat{\\mu}, \\hat{\\boldsymbol{\\theta}}\\right)}\n\n    .. note::\n\n        :func:`twice_nll` is the objective function given to the optimizer and\n        is returned evaluated at the best fit model parameters when the optional\n        kwarg ``return_fitted_val`` is ``True``.\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        >>> test_poi = 1.0\n        >>> bestfit_pars, twice_nll = pyhf.infer.mle.fixed_poi_fit(\n        ...     test_poi, data, model, return_fitted_val=True\n        ... )\n        >>> bestfit_pars\n        array([1.        , 0.97224597, 0.87553894])\n        >>> twice_nll\n        array(28.92218013)\n        >>> -2 * model.logpdf(bestfit_pars, data) == twice_nll\n        array([ True])\n\n    Args:\n        data: The data\n        pdf (~pyhf.pdf.Model): The statistical model adhering to the schema model.json\n        init_pars (:obj:`list`): Values to initialize the model parameters at for the fit\n        par_bounds (:obj:`list` of :obj:`list`\\s or :obj:`tuple`\\s): The extrema of values the model parameters are allowed to reach in the fit\n        fixed_params (:obj:`list`): Parameters to be held constant in the fit.\n        kwargs: Keyword arguments passed through to the optimizer API\n\n    Returns:\n        See optimizer API\n\n    \"\"\"\n    if pdf.config.poi_index is None:\n        raise UnspecifiedPOI(\n            'No POI is defined. A POI is required to fit with a fixed POI.'\n        )\n\n    init_pars = [*(init_pars or pdf.config.suggested_init())]\n    fixed_params = [*(fixed_params or pdf.config.suggested_fixed())]\n\n    init_pars[pdf.config.poi_index] = poi_val\n    fixed_params[pdf.config.poi_index] = True\n\n    return fit(data, pdf, init_pars, par_bounds, fixed_params, **kwargs)\n", "path": "src/pyhf/infer/mle.py"}]}