We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Update modifierclass.rst template to accept functions that aren't methods of a class
# Description

First noticed in PR #750, while trying to get the docs to show the full signatures and docstrings:

```rst
.. autosummary::
   :toctree: _generated/
   :nosignatures:
   :template: modifierclass.rst
```

I'm getting the following warnings (which we treat as errors) when I try to build the docs:
```
WARNING: error while formatting arguments for pyhf.infer.calculators.generate_asimov_data: 'function' object has no attribute '__mro__'
WARNING: error while formatting arguments for pyhf.infer.hypotest: 'function' object has no attribute '__mro__'
WARNING: error while formatting arguments for pyhf.infer.mle.fit: 'function' object has no attribute '__mro__'
WARNING: error while formatting arguments for pyhf.infer.mle.fixed_poi_fit: 'function' object has no attribute '__mro__'
WARNING: error while formatting arguments for pyhf.infer.mle.twice_nll: 'function' object has no attribute '__mro__'
WARNING: error while formatting arguments for pyhf.infer.test_statistics.qmu: 'function' object has no attribute '__mro__'
```

which I believe is happening as `__mro__` only exists on the class, and these functions exist in the source code outside of a class definition.

This means that the [`modifierclass.rst` template](https://github.com/scikit-hep/pyhf/blob/1ee6e38d42d9551220f20de483e0049b28c848b0/docs/_templates/modifierclass.rst) will need to get updated to deal with functions that aren't methods of a class.
Fix up docstring for _minimize() functions in the optimizers
# Description

From https://github.com/scikit-hep/pyhf/pull/1338#pullrequestreview-596818258

> I'm not sure if it can be fixed here, but the "Minimizer Options" aren't being displayed correctly for the optimizer _minimize methods.

![_minimize](https://user-images.githubusercontent.com/5142394/108911722-42011c00-75ed-11eb-805d-80d0d5a20ca2.png)

> we've never been able to see the `_minimize` methods before, so it isn't surprising they might not look perfect.

--- 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/optimize/opt_minuit.py`
Content:
```
1 """Minuit Optimizer Class."""
2 from .. import exceptions
3 from .mixins import OptimizerMixin
4 import scipy
5 import iminuit
6 
7 
8 class minuit_optimizer(OptimizerMixin):
9     """
10     Optimizer that minimizes via :meth:`iminuit.Minuit.migrad`.
11     """
12 
13     __slots__ = ['name', 'errordef', 'steps', 'strategy', 'tolerance']
14 
15     def __init__(self, *args, **kwargs):
16         """
17         Create :class:`iminuit.Minuit` optimizer.
18 
19         .. note::
20 
21             ``errordef`` should be 1.0 for a least-squares cost function and 0.5
22             for negative log-likelihood function. See page 37 of
23             http://hep.fi.infn.it/minuit.pdf. This parameter is sometimes
24             called ``UP`` in the ``MINUIT`` docs.
25 
26 
27         Args:
28             errordef (:obj:`float`): See minuit docs. Default is 1.0.
29             steps (:obj:`int`): Number of steps for the bounds. Default is 1000.
30             strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is None.
31             tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is 0.1.
32         """
33         self.name = 'minuit'
34         self.errordef = kwargs.pop('errordef', 1)
35         self.steps = kwargs.pop('steps', 1000)
36         self.strategy = kwargs.pop('strategy', None)
37         self.tolerance = kwargs.pop('tolerance', 0.1)
38         super().__init__(*args, **kwargs)
39 
40     def _get_minimizer(
41         self, objective_and_grad, init_pars, init_bounds, fixed_vals=None, do_grad=False
42     ):
43 
44         step_sizes = [(b[1] - b[0]) / float(self.steps) for b in init_bounds]
45         fixed_vals = fixed_vals or []
46         # Minuit wants True/False for each parameter
47         fixed_bools = [False] * len(init_pars)
48         for index, val in fixed_vals:
49             fixed_bools[index] = True
50             init_pars[index] = val
51             step_sizes[index] = 0.0
52 
53         # Minuit requires jac=callable
54         if do_grad:
55             wrapped_objective = lambda pars: objective_and_grad(pars)[0]  # noqa: E731
56             jac = lambda pars: objective_and_grad(pars)[1]  # noqa: E731
57         else:
58             wrapped_objective = objective_and_grad
59             jac = None
60 
61         minuit = iminuit.Minuit(wrapped_objective, init_pars, grad=jac)
62         minuit.errors = step_sizes
63         minuit.limits = init_bounds
64         minuit.fixed = fixed_bools
65         minuit.print_level = self.verbose
66         minuit.errordef = self.errordef
67         return minuit
68 
69     def _minimize(
70         self,
71         minimizer,
72         func,
73         x0,
74         do_grad=False,
75         bounds=None,
76         fixed_vals=None,
77         options={},
78     ):
79 
80         """
81         Same signature as :func:`scipy.optimize.minimize`.
82 
83         Note: an additional `minuit` is injected into the fitresult to get the
84         underlying minimizer.
85 
86         Minimizer Options:
87             maxiter (:obj:`int`): maximum number of iterations. Default is 100000.
88             strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is to configure in response to `do_grad`.
89             tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is 0.1.
90 
91         Returns:
92             fitresult (scipy.optimize.OptimizeResult): the fit result
93         """
94         maxiter = options.pop('maxiter', self.maxiter)
95         # 0: Fast, user-provided gradient
96         # 1: Default, no user-provided gradient
97         strategy = options.pop(
98             'strategy', self.strategy if self.strategy else not do_grad
99         )
100         tolerance = options.pop('tolerance', self.tolerance)
101         if options:
102             raise exceptions.Unsupported(
103                 f"Unsupported options were passed in: {list(options.keys())}."
104             )
105 
106         minimizer.strategy = strategy
107         minimizer.tol = tolerance
108         minimizer.migrad(ncall=maxiter)
109         # Following lines below come from:
110         # https://github.com/scikit-hep/iminuit/blob/23bad7697e39d363f259ca8349684df939b1b2e6/src/iminuit/_minimize.py#L111-L130
111         message = "Optimization terminated successfully."
112         if not minimizer.valid:
113             message = "Optimization failed."
114             fmin = minimizer.fmin
115             if fmin.has_reached_call_limit:
116                 message += " Call limit was reached."
117             if fmin.is_above_max_edm:
118                 message += " Estimated distance to minimum too large."
119 
120         hess_inv = None
121         corr = None
122         unc = None
123         if minimizer.valid:
124             # Extra call to hesse() after migrad() is always needed for good error estimates. If you pass a user-provided gradient to MINUIT, convergence is faster.
125             minimizer.hesse()
126             hess_inv = minimizer.covariance
127             corr = hess_inv.correlation()
128             unc = minimizer.errors
129 
130         return scipy.optimize.OptimizeResult(
131             x=minimizer.values,
132             unc=unc,
133             corr=corr,
134             success=minimizer.valid,
135             fun=minimizer.fval,
136             hess_inv=hess_inv,
137             message=message,
138             nfev=minimizer.nfcn,
139             njev=minimizer.ngrad,
140             minuit=minimizer,
141         )
142
```

Path: `src/pyhf/optimize/opt_scipy.py`
Content:
```
1 """SciPy Optimizer Class."""
2 from .. import exceptions
3 from .mixins import OptimizerMixin
4 import scipy
5 
6 
7 class scipy_optimizer(OptimizerMixin):
8     """
9     Optimizer that uses :func:`scipy.optimize.minimize`.
10     """
11 
12     __slots__ = ['name', 'tolerance']
13 
14     def __init__(self, *args, **kwargs):
15         """
16         Initialize the scipy_optimizer.
17 
18         See :class:`pyhf.optimize.mixins.OptimizerMixin` for other configuration options.
19 
20         Args:
21             tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is None.
22         """
23         self.name = 'scipy'
24         self.tolerance = kwargs.pop('tolerance', None)
25         super().__init__(*args, **kwargs)
26 
27     def _get_minimizer(
28         self, objective_and_grad, init_pars, init_bounds, fixed_vals=None, do_grad=False
29     ):
30         return scipy.optimize.minimize
31 
32     def _minimize(
33         self,
34         minimizer,
35         func,
36         x0,
37         do_grad=False,
38         bounds=None,
39         fixed_vals=None,
40         options={},
41     ):
42         """
43         Same signature as :func:`scipy.optimize.minimize`.
44 
45         Minimizer Options:
46             maxiter (:obj:`int`): maximum number of iterations. Default is 100000.
47             verbose (:obj:`bool`): print verbose output during minimization. Default is off.
48             method (:obj:`str`): minimization routine. Default is 'SLSQP'.
49             tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is None.
50 
51         Returns:
52             fitresult (scipy.optimize.OptimizeResult): the fit result
53         """
54         maxiter = options.pop('maxiter', self.maxiter)
55         verbose = options.pop('verbose', self.verbose)
56         method = options.pop('method', 'SLSQP')
57         tolerance = options.pop('tolerance', self.tolerance)
58         if options:
59             raise exceptions.Unsupported(
60                 f"Unsupported options were passed in: {list(options.keys())}."
61             )
62 
63         fixed_vals = fixed_vals or []
64         indices = [i for i, _ in fixed_vals]
65         values = [v for _, v in fixed_vals]
66         if fixed_vals:
67             constraints = [{'type': 'eq', 'fun': lambda v: v[indices] - values}]
68             # update the initial values to the fixed value for any fixed parameter
69             for idx, fixed_val in fixed_vals:
70                 x0[idx] = fixed_val
71         else:
72             constraints = []
73 
74         return minimizer(
75             func,
76             x0,
77             method=method,
78             jac=do_grad,
79             bounds=bounds,
80             constraints=constraints,
81             tol=tolerance,
82             options=dict(maxiter=maxiter, disp=bool(verbose)),
83         )
84
```
--- 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/optimize/opt_minuit.py b/src/pyhf/optimize/opt_minuit.py
--- a/src/pyhf/optimize/opt_minuit.py
+++ b/src/pyhf/optimize/opt_minuit.py
@@ -25,10 +25,12 @@
 
 
         Args:
-            errordef (:obj:`float`): See minuit docs. Default is 1.0.
-            steps (:obj:`int`): Number of steps for the bounds. Default is 1000.
-            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is None.
-            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is 0.1.
+            errordef (:obj:`float`): See minuit docs. Default is ``1.0``.
+            steps (:obj:`int`): Number of steps for the bounds. Default is ``1000``.
+            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is ``None``.
+            tolerance (:obj:`float`): Tolerance for termination.
+              See specific optimizer for detailed meaning.
+              Default is ``0.1``.
         """
         self.name = 'minuit'
         self.errordef = kwargs.pop('errordef', 1)
@@ -84,9 +86,12 @@
         underlying minimizer.
 
         Minimizer Options:
-            maxiter (:obj:`int`): maximum number of iterations. Default is 100000.
-            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is to configure in response to `do_grad`.
-            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is 0.1.
+          * maxiter (:obj:`int`): Maximum number of iterations. Default is ``100000``.
+          * strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`.
+            Default is to configure in response to ``do_grad``.
+          * tolerance (:obj:`float`): Tolerance for termination.
+            See specific optimizer for detailed meaning.
+            Default is ``0.1``.
 
         Returns:
             fitresult (scipy.optimize.OptimizeResult): the fit result
diff --git a/src/pyhf/optimize/opt_scipy.py b/src/pyhf/optimize/opt_scipy.py
--- a/src/pyhf/optimize/opt_scipy.py
+++ b/src/pyhf/optimize/opt_scipy.py
@@ -18,7 +18,9 @@
         See :class:`pyhf.optimize.mixins.OptimizerMixin` for other configuration options.
 
         Args:
-            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is None.
+            tolerance (:obj:`float`): Tolerance for termination.
+              See specific optimizer for detailed meaning.
+              Default is ``None``.
         """
         self.name = 'scipy'
         self.tolerance = kwargs.pop('tolerance', None)
@@ -43,10 +45,13 @@
         Same signature as :func:`scipy.optimize.minimize`.
 
         Minimizer Options:
-            maxiter (:obj:`int`): maximum number of iterations. Default is 100000.
-            verbose (:obj:`bool`): print verbose output during minimization. Default is off.
-            method (:obj:`str`): minimization routine. Default is 'SLSQP'.
-            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is None.
+          * maxiter (:obj:`int`): Maximum number of iterations. Default is ``100000``.
+          * verbose (:obj:`bool`): Print verbose output during minimization.
+            Default is ``False``.
+          * method (:obj:`str`): Minimization routine. Default is ``'SLSQP'``.
+          * tolerance (:obj:`float`): Tolerance for termination. See specific optimizer
+            for detailed meaning.
+            Default is ``None``.
 
         Returns:
             fitresult (scipy.optimize.OptimizeResult): the fit result

{"golden_diff": "diff --git a/src/pyhf/optimize/opt_minuit.py b/src/pyhf/optimize/opt_minuit.py\n--- a/src/pyhf/optimize/opt_minuit.py\n+++ b/src/pyhf/optimize/opt_minuit.py\n@@ -25,10 +25,12 @@\n \n \n         Args:\n-            errordef (:obj:`float`): See minuit docs. Default is 1.0.\n-            steps (:obj:`int`): Number of steps for the bounds. Default is 1000.\n-            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is None.\n-            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is 0.1.\n+            errordef (:obj:`float`): See minuit docs. Default is ``1.0``.\n+            steps (:obj:`int`): Number of steps for the bounds. Default is ``1000``.\n+            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is ``None``.\n+            tolerance (:obj:`float`): Tolerance for termination.\n+              See specific optimizer for detailed meaning.\n+              Default is ``0.1``.\n         \"\"\"\n         self.name = 'minuit'\n         self.errordef = kwargs.pop('errordef', 1)\n@@ -84,9 +86,12 @@\n         underlying minimizer.\n \n         Minimizer Options:\n-            maxiter (:obj:`int`): maximum number of iterations. Default is 100000.\n-            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is to configure in response to `do_grad`.\n-            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is 0.1.\n+          * maxiter (:obj:`int`): Maximum number of iterations. Default is ``100000``.\n+          * strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`.\n+            Default is to configure in response to ``do_grad``.\n+          * tolerance (:obj:`float`): Tolerance for termination.\n+            See specific optimizer for detailed meaning.\n+            Default is ``0.1``.\n \n         Returns:\n             fitresult (scipy.optimize.OptimizeResult): the fit result\ndiff --git a/src/pyhf/optimize/opt_scipy.py b/src/pyhf/optimize/opt_scipy.py\n--- a/src/pyhf/optimize/opt_scipy.py\n+++ b/src/pyhf/optimize/opt_scipy.py\n@@ -18,7 +18,9 @@\n         See :class:`pyhf.optimize.mixins.OptimizerMixin` for other configuration options.\n \n         Args:\n-            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is None.\n+            tolerance (:obj:`float`): Tolerance for termination.\n+              See specific optimizer for detailed meaning.\n+              Default is ``None``.\n         \"\"\"\n         self.name = 'scipy'\n         self.tolerance = kwargs.pop('tolerance', None)\n@@ -43,10 +45,13 @@\n         Same signature as :func:`scipy.optimize.minimize`.\n \n         Minimizer Options:\n-            maxiter (:obj:`int`): maximum number of iterations. Default is 100000.\n-            verbose (:obj:`bool`): print verbose output during minimization. Default is off.\n-            method (:obj:`str`): minimization routine. Default is 'SLSQP'.\n-            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is None.\n+          * maxiter (:obj:`int`): Maximum number of iterations. Default is ``100000``.\n+          * verbose (:obj:`bool`): Print verbose output during minimization.\n+            Default is ``False``.\n+          * method (:obj:`str`): Minimization routine. Default is ``'SLSQP'``.\n+          * tolerance (:obj:`float`): Tolerance for termination. See specific optimizer\n+            for detailed meaning.\n+            Default is ``None``.\n \n         Returns:\n             fitresult (scipy.optimize.OptimizeResult): the fit result\n", "issue": "Update modifierclass.rst template to accept functions that aren't methods of a class\n# Description\r\n\r\nFirst noticed in PR #750, while trying to get the docs to show the full signatures and docstrings:\r\n\r\n```rst\r\n.. autosummary::\r\n   :toctree: _generated/\r\n   :nosignatures:\r\n   :template: modifierclass.rst\r\n```\r\n\r\nI'm getting the following warnings (which we treat as errors) when I try to build the docs:\r\n```\r\nWARNING: error while formatting arguments for pyhf.infer.calculators.generate_asimov_data: 'function' object has no attribute '__mro__'\r\nWARNING: error while formatting arguments for pyhf.infer.hypotest: 'function' object has no attribute '__mro__'\r\nWARNING: error while formatting arguments for pyhf.infer.mle.fit: 'function' object has no attribute '__mro__'\r\nWARNING: error while formatting arguments for pyhf.infer.mle.fixed_poi_fit: 'function' object has no attribute '__mro__'\r\nWARNING: error while formatting arguments for pyhf.infer.mle.twice_nll: 'function' object has no attribute '__mro__'\r\nWARNING: error while formatting arguments for pyhf.infer.test_statistics.qmu: 'function' object has no attribute '__mro__'\r\n```\r\n\r\nwhich I believe is happening as `__mro__` only exists on the class, and these functions exist in the source code outside of a class definition.\r\n\r\nThis means that the [`modifierclass.rst` template](https://github.com/scikit-hep/pyhf/blob/1ee6e38d42d9551220f20de483e0049b28c848b0/docs/_templates/modifierclass.rst) will need to get updated to deal with functions that aren't methods of a class.\nFix up docstring for _minimize() functions in the optimizers\n# Description\r\n\r\nFrom https://github.com/scikit-hep/pyhf/pull/1338#pullrequestreview-596818258\r\n\r\n> I'm not sure if it can be fixed here, but the \"Minimizer Options\" aren't being displayed correctly for the optimizer _minimize methods.\r\n\r\n![_minimize](https://user-images.githubusercontent.com/5142394/108911722-42011c00-75ed-11eb-805d-80d0d5a20ca2.png)\r\n\r\n> we've never been able to see the `_minimize` methods before, so it isn't surprising they might not look perfect.\n", "before_files": [{"content": "\"\"\"Minuit Optimizer Class.\"\"\"\nfrom .. import exceptions\nfrom .mixins import OptimizerMixin\nimport scipy\nimport iminuit\n\n\nclass minuit_optimizer(OptimizerMixin):\n    \"\"\"\n    Optimizer that minimizes via :meth:`iminuit.Minuit.migrad`.\n    \"\"\"\n\n    __slots__ = ['name', 'errordef', 'steps', 'strategy', 'tolerance']\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"\n        Create :class:`iminuit.Minuit` optimizer.\n\n        .. note::\n\n            ``errordef`` should be 1.0 for a least-squares cost function and 0.5\n            for negative log-likelihood function. See page 37 of\n            http://hep.fi.infn.it/minuit.pdf. This parameter is sometimes\n            called ``UP`` in the ``MINUIT`` docs.\n\n\n        Args:\n            errordef (:obj:`float`): See minuit docs. Default is 1.0.\n            steps (:obj:`int`): Number of steps for the bounds. Default is 1000.\n            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is None.\n            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is 0.1.\n        \"\"\"\n        self.name = 'minuit'\n        self.errordef = kwargs.pop('errordef', 1)\n        self.steps = kwargs.pop('steps', 1000)\n        self.strategy = kwargs.pop('strategy', None)\n        self.tolerance = kwargs.pop('tolerance', 0.1)\n        super().__init__(*args, **kwargs)\n\n    def _get_minimizer(\n        self, objective_and_grad, init_pars, init_bounds, fixed_vals=None, do_grad=False\n    ):\n\n        step_sizes = [(b[1] - b[0]) / float(self.steps) for b in init_bounds]\n        fixed_vals = fixed_vals or []\n        # Minuit wants True/False for each parameter\n        fixed_bools = [False] * len(init_pars)\n        for index, val in fixed_vals:\n            fixed_bools[index] = True\n            init_pars[index] = val\n            step_sizes[index] = 0.0\n\n        # Minuit requires jac=callable\n        if do_grad:\n            wrapped_objective = lambda pars: objective_and_grad(pars)[0]  # noqa: E731\n            jac = lambda pars: objective_and_grad(pars)[1]  # noqa: E731\n        else:\n            wrapped_objective = objective_and_grad\n            jac = None\n\n        minuit = iminuit.Minuit(wrapped_objective, init_pars, grad=jac)\n        minuit.errors = step_sizes\n        minuit.limits = init_bounds\n        minuit.fixed = fixed_bools\n        minuit.print_level = self.verbose\n        minuit.errordef = self.errordef\n        return minuit\n\n    def _minimize(\n        self,\n        minimizer,\n        func,\n        x0,\n        do_grad=False,\n        bounds=None,\n        fixed_vals=None,\n        options={},\n    ):\n\n        \"\"\"\n        Same signature as :func:`scipy.optimize.minimize`.\n\n        Note: an additional `minuit` is injected into the fitresult to get the\n        underlying minimizer.\n\n        Minimizer Options:\n            maxiter (:obj:`int`): maximum number of iterations. Default is 100000.\n            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is to configure in response to `do_grad`.\n            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is 0.1.\n\n        Returns:\n            fitresult (scipy.optimize.OptimizeResult): the fit result\n        \"\"\"\n        maxiter = options.pop('maxiter', self.maxiter)\n        # 0: Fast, user-provided gradient\n        # 1: Default, no user-provided gradient\n        strategy = options.pop(\n            'strategy', self.strategy if self.strategy else not do_grad\n        )\n        tolerance = options.pop('tolerance', self.tolerance)\n        if options:\n            raise exceptions.Unsupported(\n                f\"Unsupported options were passed in: {list(options.keys())}.\"\n            )\n\n        minimizer.strategy = strategy\n        minimizer.tol = tolerance\n        minimizer.migrad(ncall=maxiter)\n        # Following lines below come from:\n        # https://github.com/scikit-hep/iminuit/blob/23bad7697e39d363f259ca8349684df939b1b2e6/src/iminuit/_minimize.py#L111-L130\n        message = \"Optimization terminated successfully.\"\n        if not minimizer.valid:\n            message = \"Optimization failed.\"\n            fmin = minimizer.fmin\n            if fmin.has_reached_call_limit:\n                message += \" Call limit was reached.\"\n            if fmin.is_above_max_edm:\n                message += \" Estimated distance to minimum too large.\"\n\n        hess_inv = None\n        corr = None\n        unc = None\n        if minimizer.valid:\n            # Extra call to hesse() after migrad() is always needed for good error estimates. If you pass a user-provided gradient to MINUIT, convergence is faster.\n            minimizer.hesse()\n            hess_inv = minimizer.covariance\n            corr = hess_inv.correlation()\n            unc = minimizer.errors\n\n        return scipy.optimize.OptimizeResult(\n            x=minimizer.values,\n            unc=unc,\n            corr=corr,\n            success=minimizer.valid,\n            fun=minimizer.fval,\n            hess_inv=hess_inv,\n            message=message,\n            nfev=minimizer.nfcn,\n            njev=minimizer.ngrad,\n            minuit=minimizer,\n        )\n", "path": "src/pyhf/optimize/opt_minuit.py"}, {"content": "\"\"\"SciPy Optimizer Class.\"\"\"\nfrom .. import exceptions\nfrom .mixins import OptimizerMixin\nimport scipy\n\n\nclass scipy_optimizer(OptimizerMixin):\n    \"\"\"\n    Optimizer that uses :func:`scipy.optimize.minimize`.\n    \"\"\"\n\n    __slots__ = ['name', 'tolerance']\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"\n        Initialize the scipy_optimizer.\n\n        See :class:`pyhf.optimize.mixins.OptimizerMixin` for other configuration options.\n\n        Args:\n            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is None.\n        \"\"\"\n        self.name = 'scipy'\n        self.tolerance = kwargs.pop('tolerance', None)\n        super().__init__(*args, **kwargs)\n\n    def _get_minimizer(\n        self, objective_and_grad, init_pars, init_bounds, fixed_vals=None, do_grad=False\n    ):\n        return scipy.optimize.minimize\n\n    def _minimize(\n        self,\n        minimizer,\n        func,\n        x0,\n        do_grad=False,\n        bounds=None,\n        fixed_vals=None,\n        options={},\n    ):\n        \"\"\"\n        Same signature as :func:`scipy.optimize.minimize`.\n\n        Minimizer Options:\n            maxiter (:obj:`int`): maximum number of iterations. Default is 100000.\n            verbose (:obj:`bool`): print verbose output during minimization. Default is off.\n            method (:obj:`str`): minimization routine. Default is 'SLSQP'.\n            tolerance (:obj:`float`): tolerance for termination. See specific optimizer for detailed meaning. Default is None.\n\n        Returns:\n            fitresult (scipy.optimize.OptimizeResult): the fit result\n        \"\"\"\n        maxiter = options.pop('maxiter', self.maxiter)\n        verbose = options.pop('verbose', self.verbose)\n        method = options.pop('method', 'SLSQP')\n        tolerance = options.pop('tolerance', self.tolerance)\n        if options:\n            raise exceptions.Unsupported(\n                f\"Unsupported options were passed in: {list(options.keys())}.\"\n            )\n\n        fixed_vals = fixed_vals or []\n        indices = [i for i, _ in fixed_vals]\n        values = [v for _, v in fixed_vals]\n        if fixed_vals:\n            constraints = [{'type': 'eq', 'fun': lambda v: v[indices] - values}]\n            # update the initial values to the fixed value for any fixed parameter\n            for idx, fixed_val in fixed_vals:\n                x0[idx] = fixed_val\n        else:\n            constraints = []\n\n        return minimizer(\n            func,\n            x0,\n            method=method,\n            jac=do_grad,\n            bounds=bounds,\n            constraints=constraints,\n            tol=tolerance,\n            options=dict(maxiter=maxiter, disp=bool(verbose)),\n        )\n", "path": "src/pyhf/optimize/opt_scipy.py"}], "after_files": [{"content": "\"\"\"Minuit Optimizer Class.\"\"\"\nfrom .. import exceptions\nfrom .mixins import OptimizerMixin\nimport scipy\nimport iminuit\n\n\nclass minuit_optimizer(OptimizerMixin):\n    \"\"\"\n    Optimizer that minimizes via :meth:`iminuit.Minuit.migrad`.\n    \"\"\"\n\n    __slots__ = ['name', 'errordef', 'steps', 'strategy', 'tolerance']\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"\n        Create :class:`iminuit.Minuit` optimizer.\n\n        .. note::\n\n            ``errordef`` should be 1.0 for a least-squares cost function and 0.5\n            for negative log-likelihood function. See page 37 of\n            http://hep.fi.infn.it/minuit.pdf. This parameter is sometimes\n            called ``UP`` in the ``MINUIT`` docs.\n\n\n        Args:\n            errordef (:obj:`float`): See minuit docs. Default is ``1.0``.\n            steps (:obj:`int`): Number of steps for the bounds. Default is ``1000``.\n            strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`. Default is ``None``.\n            tolerance (:obj:`float`): Tolerance for termination.\n              See specific optimizer for detailed meaning.\n              Default is ``0.1``.\n        \"\"\"\n        self.name = 'minuit'\n        self.errordef = kwargs.pop('errordef', 1)\n        self.steps = kwargs.pop('steps', 1000)\n        self.strategy = kwargs.pop('strategy', None)\n        self.tolerance = kwargs.pop('tolerance', 0.1)\n        super().__init__(*args, **kwargs)\n\n    def _get_minimizer(\n        self, objective_and_grad, init_pars, init_bounds, fixed_vals=None, do_grad=False\n    ):\n\n        step_sizes = [(b[1] - b[0]) / float(self.steps) for b in init_bounds]\n        fixed_vals = fixed_vals or []\n        # Minuit wants True/False for each parameter\n        fixed_bools = [False] * len(init_pars)\n        for index, val in fixed_vals:\n            fixed_bools[index] = True\n            init_pars[index] = val\n            step_sizes[index] = 0.0\n\n        # Minuit requires jac=callable\n        if do_grad:\n            wrapped_objective = lambda pars: objective_and_grad(pars)[0]  # noqa: E731\n            jac = lambda pars: objective_and_grad(pars)[1]  # noqa: E731\n        else:\n            wrapped_objective = objective_and_grad\n            jac = None\n\n        minuit = iminuit.Minuit(wrapped_objective, init_pars, grad=jac)\n        minuit.errors = step_sizes\n        minuit.limits = init_bounds\n        minuit.fixed = fixed_bools\n        minuit.print_level = self.verbose\n        minuit.errordef = self.errordef\n        return minuit\n\n    def _minimize(\n        self,\n        minimizer,\n        func,\n        x0,\n        do_grad=False,\n        bounds=None,\n        fixed_vals=None,\n        options={},\n    ):\n\n        \"\"\"\n        Same signature as :func:`scipy.optimize.minimize`.\n\n        Note: an additional `minuit` is injected into the fitresult to get the\n        underlying minimizer.\n\n        Minimizer Options:\n          * maxiter (:obj:`int`): Maximum number of iterations. Default is ``100000``.\n          * strategy (:obj:`int`): See :attr:`iminuit.Minuit.strategy`.\n            Default is to configure in response to ``do_grad``.\n          * tolerance (:obj:`float`): Tolerance for termination.\n            See specific optimizer for detailed meaning.\n            Default is ``0.1``.\n\n        Returns:\n            fitresult (scipy.optimize.OptimizeResult): the fit result\n        \"\"\"\n        maxiter = options.pop('maxiter', self.maxiter)\n        # 0: Fast, user-provided gradient\n        # 1: Default, no user-provided gradient\n        strategy = options.pop(\n            'strategy', self.strategy if self.strategy else not do_grad\n        )\n        tolerance = options.pop('tolerance', self.tolerance)\n        if options:\n            raise exceptions.Unsupported(\n                f\"Unsupported options were passed in: {list(options.keys())}.\"\n            )\n\n        minimizer.strategy = strategy\n        minimizer.tol = tolerance\n        minimizer.migrad(ncall=maxiter)\n        # Following lines below come from:\n        # https://github.com/scikit-hep/iminuit/blob/23bad7697e39d363f259ca8349684df939b1b2e6/src/iminuit/_minimize.py#L111-L130\n        message = \"Optimization terminated successfully.\"\n        if not minimizer.valid:\n            message = \"Optimization failed.\"\n            fmin = minimizer.fmin\n            if fmin.has_reached_call_limit:\n                message += \" Call limit was reached.\"\n            if fmin.is_above_max_edm:\n                message += \" Estimated distance to minimum too large.\"\n\n        hess_inv = None\n        corr = None\n        unc = None\n        if minimizer.valid:\n            # Extra call to hesse() after migrad() is always needed for good error estimates. If you pass a user-provided gradient to MINUIT, convergence is faster.\n            minimizer.hesse()\n            hess_inv = minimizer.covariance\n            corr = hess_inv.correlation()\n            unc = minimizer.errors\n\n        return scipy.optimize.OptimizeResult(\n            x=minimizer.values,\n            unc=unc,\n            corr=corr,\n            success=minimizer.valid,\n            fun=minimizer.fval,\n            hess_inv=hess_inv,\n            message=message,\n            nfev=minimizer.nfcn,\n            njev=minimizer.ngrad,\n            minuit=minimizer,\n        )\n", "path": "src/pyhf/optimize/opt_minuit.py"}, {"content": "\"\"\"SciPy Optimizer Class.\"\"\"\nfrom .. import exceptions\nfrom .mixins import OptimizerMixin\nimport scipy\n\n\nclass scipy_optimizer(OptimizerMixin):\n    \"\"\"\n    Optimizer that uses :func:`scipy.optimize.minimize`.\n    \"\"\"\n\n    __slots__ = ['name', 'tolerance']\n\n    def __init__(self, *args, **kwargs):\n        \"\"\"\n        Initialize the scipy_optimizer.\n\n        See :class:`pyhf.optimize.mixins.OptimizerMixin` for other configuration options.\n\n        Args:\n            tolerance (:obj:`float`): Tolerance for termination.\n              See specific optimizer for detailed meaning.\n              Default is ``None``.\n        \"\"\"\n        self.name = 'scipy'\n        self.tolerance = kwargs.pop('tolerance', None)\n        super().__init__(*args, **kwargs)\n\n    def _get_minimizer(\n        self, objective_and_grad, init_pars, init_bounds, fixed_vals=None, do_grad=False\n    ):\n        return scipy.optimize.minimize\n\n    def _minimize(\n        self,\n        minimizer,\n        func,\n        x0,\n        do_grad=False,\n        bounds=None,\n        fixed_vals=None,\n        options={},\n    ):\n        \"\"\"\n        Same signature as :func:`scipy.optimize.minimize`.\n\n        Minimizer Options:\n          * maxiter (:obj:`int`): Maximum number of iterations. Default is ``100000``.\n          * verbose (:obj:`bool`): Print verbose output during minimization.\n            Default is ``False``.\n          * method (:obj:`str`): Minimization routine. Default is ``'SLSQP'``.\n          * tolerance (:obj:`float`): Tolerance for termination. See specific optimizer\n            for detailed meaning.\n            Default is ``None``.\n\n        Returns:\n            fitresult (scipy.optimize.OptimizeResult): the fit result\n        \"\"\"\n        maxiter = options.pop('maxiter', self.maxiter)\n        verbose = options.pop('verbose', self.verbose)\n        method = options.pop('method', 'SLSQP')\n        tolerance = options.pop('tolerance', self.tolerance)\n        if options:\n            raise exceptions.Unsupported(\n                f\"Unsupported options were passed in: {list(options.keys())}.\"\n            )\n\n        fixed_vals = fixed_vals or []\n        indices = [i for i, _ in fixed_vals]\n        values = [v for _, v in fixed_vals]\n        if fixed_vals:\n            constraints = [{'type': 'eq', 'fun': lambda v: v[indices] - values}]\n            # update the initial values to the fixed value for any fixed parameter\n            for idx, fixed_val in fixed_vals:\n                x0[idx] = fixed_val\n        else:\n            constraints = []\n\n        return minimizer(\n            func,\n            x0,\n            method=method,\n            jac=do_grad,\n            bounds=bounds,\n            constraints=constraints,\n            tol=tolerance,\n            options=dict(maxiter=maxiter, disp=bool(verbose)),\n        )\n", "path": "src/pyhf/optimize/opt_scipy.py"}]}