We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`BootStrapper(F1Score(average=None))` update fails when only some classes are present in batch
## 🐛 Bug

When `average=None`, `BootStrapper` throws a `RuntimeError`. Therefore, it is not possible to compute bootstrapped classwise metrics. 

```python
import torch
from pprint import pprint

from torchmetrics.wrappers import BootStrapper
from torchmetrics.classification import MulticlassF1Score

_ = torch.manual_seed(42)
preds = torch.randn(3, 3).softmax(dim=-1)
target = torch.LongTensor([0, 0, 0])

f1_score = MulticlassF1Score(num_classes=3, average=None)
bootstrap_f1 = BootStrapper(MulticlassF1Score(num_classes=3, average=None), num_bootstraps=20)

f1_score(preds, target) # works
bootstrap_f1(preds, target)  # does not work

# RuntimeError: cannot reshape tensor of 0 elements into shape [0, -1] because the
# unspecified dimension size -1 can be any value and is ambiguous
```

--- 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/torchmetrics/wrappers/bootstrapping.py`
Content:
```
1 # Copyright The Lightning team.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 from copy import deepcopy
15 from typing import Any, Dict, Optional, Sequence, Union
16 
17 import torch
18 from lightning_utilities import apply_to_collection
19 from torch import Tensor
20 from torch.nn import ModuleList
21 
22 from torchmetrics.metric import Metric
23 from torchmetrics.utilities.imports import _MATPLOTLIB_AVAILABLE
24 from torchmetrics.utilities.plot import _AX_TYPE, _PLOT_OUT_TYPE
25 from torchmetrics.wrappers.abstract import WrapperMetric
26 
27 if not _MATPLOTLIB_AVAILABLE:
28     __doctest_skip__ = ["BootStrapper.plot"]
29 
30 
31 def _bootstrap_sampler(
32     size: int,
33     sampling_strategy: str = "poisson",
34 ) -> Tensor:
35     """Resample a tensor along its first dimension with replacement.
36 
37     Args:
38         size: number of samples
39         sampling_strategy: the strategy to use for sampling, either ``'poisson'`` or ``'multinomial'``
40 
41     Returns:
42         resampled tensor
43 
44     """
45     if sampling_strategy == "poisson":
46         p = torch.distributions.Poisson(1)
47         n = p.sample((size,))
48         return torch.arange(size).repeat_interleave(n.long(), dim=0)
49     if sampling_strategy == "multinomial":
50         return torch.multinomial(torch.ones(size), num_samples=size, replacement=True)
51     raise ValueError("Unknown sampling strategy")
52 
53 
54 class BootStrapper(WrapperMetric):
55     r"""Using `Turn a Metric into a Bootstrapped`_.
56 
57     That can automate the process of getting confidence intervals for metric values. This wrapper
58     class basically keeps multiple copies of the same base metric in memory and whenever ``update`` or
59     ``forward`` is called, all input tensors are resampled (with replacement) along the first dimension.
60 
61     Args:
62         base_metric: base metric class to wrap
63         num_bootstraps: number of copies to make of the base metric for bootstrapping
64         mean: if ``True`` return the mean of the bootstraps
65         std: if ``True`` return the standard diviation of the bootstraps
66         quantile: if given, returns the quantile of the bootstraps. Can only be used with pytorch version 1.6 or higher
67         raw: if ``True``, return all bootstrapped values
68         sampling_strategy:
69             Determines how to produce bootstrapped samplings. Either ``'poisson'`` or ``multinomial``.
70             If ``'possion'`` is chosen, the number of times each sample will be included in the bootstrap
71             will be given by :math:`n\sim Poisson(\lambda=1)`, which approximates the true bootstrap distribution
72             when the number of samples is large. If ``'multinomial'`` is chosen, we will apply true bootstrapping
73             at the batch level to approximate bootstrapping over the hole dataset.
74         kwargs: Additional keyword arguments, see :ref:`Metric kwargs` for more info.
75 
76     Example::
77         >>> from pprint import pprint
78         >>> from torchmetrics.wrappers import BootStrapper
79         >>> from torchmetrics.classification import MulticlassAccuracy
80         >>> _ = torch.manual_seed(123)
81         >>> base_metric = MulticlassAccuracy(num_classes=5, average='micro')
82         >>> bootstrap = BootStrapper(base_metric, num_bootstraps=20)
83         >>> bootstrap.update(torch.randint(5, (20,)), torch.randint(5, (20,)))
84         >>> output = bootstrap.compute()
85         >>> pprint(output)
86         {'mean': tensor(0.2205), 'std': tensor(0.0859)}
87 
88     """
89     full_state_update: Optional[bool] = True
90 
91     def __init__(
92         self,
93         base_metric: Metric,
94         num_bootstraps: int = 10,
95         mean: bool = True,
96         std: bool = True,
97         quantile: Optional[Union[float, Tensor]] = None,
98         raw: bool = False,
99         sampling_strategy: str = "poisson",
100         **kwargs: Any,
101     ) -> None:
102         super().__init__(**kwargs)
103         if not isinstance(base_metric, Metric):
104             raise ValueError(
105                 f"Expected base metric to be an instance of torchmetrics.Metric but received {base_metric}"
106             )
107 
108         self.metrics = ModuleList([deepcopy(base_metric) for _ in range(num_bootstraps)])
109         self.num_bootstraps = num_bootstraps
110 
111         self.mean = mean
112         self.std = std
113         self.quantile = quantile
114         self.raw = raw
115 
116         allowed_sampling = ("poisson", "multinomial")
117         if sampling_strategy not in allowed_sampling:
118             raise ValueError(
119                 f"Expected argument ``sampling_strategy`` to be one of {allowed_sampling}"
120                 f" but recieved {sampling_strategy}"
121             )
122         self.sampling_strategy = sampling_strategy
123 
124     def update(self, *args: Any, **kwargs: Any) -> None:
125         """Update the state of the base metric.
126 
127         Any tensor passed in will be bootstrapped along dimension 0.
128 
129         """
130         for idx in range(self.num_bootstraps):
131             args_sizes = apply_to_collection(args, Tensor, len)
132             kwargs_sizes = list(apply_to_collection(kwargs, Tensor, len))
133             if len(args_sizes) > 0:
134                 size = args_sizes[0]
135             elif len(kwargs_sizes) > 0:
136                 size = kwargs_sizes[0]
137             else:
138                 raise ValueError("None of the input contained tensors, so could not determine the sampling size")
139             sample_idx = _bootstrap_sampler(size, sampling_strategy=self.sampling_strategy).to(self.device)
140             new_args = apply_to_collection(args, Tensor, torch.index_select, dim=0, index=sample_idx)
141             new_kwargs = apply_to_collection(kwargs, Tensor, torch.index_select, dim=0, index=sample_idx)
142             self.metrics[idx].update(*new_args, **new_kwargs)
143 
144     def compute(self) -> Dict[str, Tensor]:
145         """Compute the bootstrapped metric values.
146 
147         Always returns a dict of tensors, which can contain the following keys: ``mean``, ``std``, ``quantile`` and
148         ``raw`` depending on how the class was initialized.
149 
150         """
151         computed_vals = torch.stack([m.compute() for m in self.metrics], dim=0)
152         output_dict = {}
153         if self.mean:
154             output_dict["mean"] = computed_vals.mean(dim=0)
155         if self.std:
156             output_dict["std"] = computed_vals.std(dim=0)
157         if self.quantile is not None:
158             output_dict["quantile"] = torch.quantile(computed_vals, self.quantile)
159         if self.raw:
160             output_dict["raw"] = computed_vals
161         return output_dict
162 
163     def forward(self, *args: Any, **kwargs: Any) -> Any:
164         """Use the original forward method of the base metric class."""
165         return super(WrapperMetric, self).forward(*args, **kwargs)
166 
167     def plot(
168         self, val: Optional[Union[Tensor, Sequence[Tensor]]] = None, ax: Optional[_AX_TYPE] = None
169     ) -> _PLOT_OUT_TYPE:
170         """Plot a single or multiple values from the metric.
171 
172         Args:
173             val: Either a single result from calling `metric.forward` or `metric.compute` or a list of these results.
174                 If no value is provided, will automatically call `metric.compute` and plot that result.
175             ax: An matplotlib axis object. If provided will add plot to that axis
176 
177         Returns:
178             Figure and Axes object
179 
180         Raises:
181             ModuleNotFoundError:
182                 If `matplotlib` is not installed
183 
184         .. plot::
185             :scale: 75
186 
187             >>> # Example plotting a single value
188             >>> import torch
189             >>> from torchmetrics.wrappers import BootStrapper
190             >>> from torchmetrics.regression import MeanSquaredError
191             >>> metric = BootStrapper(MeanSquaredError(), num_bootstraps=20)
192             >>> metric.update(torch.randn(100,), torch.randn(100,))
193             >>> fig_, ax_ = metric.plot()
194 
195         .. plot::
196             :scale: 75
197 
198             >>> # Example plotting multiple values
199             >>> import torch
200             >>> from torchmetrics.wrappers import BootStrapper
201             >>> from torchmetrics.regression import MeanSquaredError
202             >>> metric = BootStrapper(MeanSquaredError(), num_bootstraps=20)
203             >>> values = [ ]
204             >>> for _ in range(3):
205             ...     values.append(metric(torch.randn(100,), torch.randn(100,)))
206             >>> fig_, ax_ = metric.plot(values)
207 
208         """
209         return self._plot(val, ax)
210
```
--- 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/torchmetrics/wrappers/bootstrapping.py b/src/torchmetrics/wrappers/bootstrapping.py
--- a/src/torchmetrics/wrappers/bootstrapping.py
+++ b/src/torchmetrics/wrappers/bootstrapping.py
@@ -127,16 +127,19 @@
         Any tensor passed in will be bootstrapped along dimension 0.
 
         """
+        args_sizes = apply_to_collection(args, Tensor, len)
+        kwargs_sizes = list(apply_to_collection(kwargs, Tensor, len))
+        if len(args_sizes) > 0:
+            size = args_sizes[0]
+        elif len(kwargs_sizes) > 0:
+            size = kwargs_sizes[0]
+        else:
+            raise ValueError("None of the input contained tensors, so could not determine the sampling size")
+
         for idx in range(self.num_bootstraps):
-            args_sizes = apply_to_collection(args, Tensor, len)
-            kwargs_sizes = list(apply_to_collection(kwargs, Tensor, len))
-            if len(args_sizes) > 0:
-                size = args_sizes[0]
-            elif len(kwargs_sizes) > 0:
-                size = kwargs_sizes[0]
-            else:
-                raise ValueError("None of the input contained tensors, so could not determine the sampling size")
             sample_idx = _bootstrap_sampler(size, sampling_strategy=self.sampling_strategy).to(self.device)
+            if sample_idx.numel() == 0:
+                continue
             new_args = apply_to_collection(args, Tensor, torch.index_select, dim=0, index=sample_idx)
             new_kwargs = apply_to_collection(kwargs, Tensor, torch.index_select, dim=0, index=sample_idx)
             self.metrics[idx].update(*new_args, **new_kwargs)

{"golden_diff": "diff --git a/src/torchmetrics/wrappers/bootstrapping.py b/src/torchmetrics/wrappers/bootstrapping.py\n--- a/src/torchmetrics/wrappers/bootstrapping.py\n+++ b/src/torchmetrics/wrappers/bootstrapping.py\n@@ -127,16 +127,19 @@\n         Any tensor passed in will be bootstrapped along dimension 0.\n \n         \"\"\"\n+        args_sizes = apply_to_collection(args, Tensor, len)\n+        kwargs_sizes = list(apply_to_collection(kwargs, Tensor, len))\n+        if len(args_sizes) > 0:\n+            size = args_sizes[0]\n+        elif len(kwargs_sizes) > 0:\n+            size = kwargs_sizes[0]\n+        else:\n+            raise ValueError(\"None of the input contained tensors, so could not determine the sampling size\")\n+\n         for idx in range(self.num_bootstraps):\n-            args_sizes = apply_to_collection(args, Tensor, len)\n-            kwargs_sizes = list(apply_to_collection(kwargs, Tensor, len))\n-            if len(args_sizes) > 0:\n-                size = args_sizes[0]\n-            elif len(kwargs_sizes) > 0:\n-                size = kwargs_sizes[0]\n-            else:\n-                raise ValueError(\"None of the input contained tensors, so could not determine the sampling size\")\n             sample_idx = _bootstrap_sampler(size, sampling_strategy=self.sampling_strategy).to(self.device)\n+            if sample_idx.numel() == 0:\n+                continue\n             new_args = apply_to_collection(args, Tensor, torch.index_select, dim=0, index=sample_idx)\n             new_kwargs = apply_to_collection(kwargs, Tensor, torch.index_select, dim=0, index=sample_idx)\n             self.metrics[idx].update(*new_args, **new_kwargs)\n", "issue": "`BootStrapper(F1Score(average=None))` update fails when only some classes are present in batch\n## \ud83d\udc1b Bug\r\n\r\nWhen `average=None`, `BootStrapper` throws a `RuntimeError`. Therefore, it is not possible to compute bootstrapped classwise metrics. \r\n\r\n```python\r\nimport torch\r\nfrom pprint import pprint\r\n\r\nfrom torchmetrics.wrappers import BootStrapper\r\nfrom torchmetrics.classification import MulticlassF1Score\r\n\r\n_ = torch.manual_seed(42)\r\npreds = torch.randn(3, 3).softmax(dim=-1)\r\ntarget = torch.LongTensor([0, 0, 0])\r\n\r\nf1_score = MulticlassF1Score(num_classes=3, average=None)\r\nbootstrap_f1 = BootStrapper(MulticlassF1Score(num_classes=3, average=None), num_bootstraps=20)\r\n\r\nf1_score(preds, target) # works\r\nbootstrap_f1(preds, target)  # does not work\r\n\r\n# RuntimeError: cannot reshape tensor of 0 elements into shape [0, -1] because the\r\n# unspecified dimension size -1 can be any value and is ambiguous\r\n```\n", "before_files": [{"content": "# Copyright The Lightning team.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom copy import deepcopy\nfrom typing import Any, Dict, Optional, Sequence, Union\n\nimport torch\nfrom lightning_utilities import apply_to_collection\nfrom torch import Tensor\nfrom torch.nn import ModuleList\n\nfrom torchmetrics.metric import Metric\nfrom torchmetrics.utilities.imports import _MATPLOTLIB_AVAILABLE\nfrom torchmetrics.utilities.plot import _AX_TYPE, _PLOT_OUT_TYPE\nfrom torchmetrics.wrappers.abstract import WrapperMetric\n\nif not _MATPLOTLIB_AVAILABLE:\n    __doctest_skip__ = [\"BootStrapper.plot\"]\n\n\ndef _bootstrap_sampler(\n    size: int,\n    sampling_strategy: str = \"poisson\",\n) -> Tensor:\n    \"\"\"Resample a tensor along its first dimension with replacement.\n\n    Args:\n        size: number of samples\n        sampling_strategy: the strategy to use for sampling, either ``'poisson'`` or ``'multinomial'``\n\n    Returns:\n        resampled tensor\n\n    \"\"\"\n    if sampling_strategy == \"poisson\":\n        p = torch.distributions.Poisson(1)\n        n = p.sample((size,))\n        return torch.arange(size).repeat_interleave(n.long(), dim=0)\n    if sampling_strategy == \"multinomial\":\n        return torch.multinomial(torch.ones(size), num_samples=size, replacement=True)\n    raise ValueError(\"Unknown sampling strategy\")\n\n\nclass BootStrapper(WrapperMetric):\n    r\"\"\"Using `Turn a Metric into a Bootstrapped`_.\n\n    That can automate the process of getting confidence intervals for metric values. This wrapper\n    class basically keeps multiple copies of the same base metric in memory and whenever ``update`` or\n    ``forward`` is called, all input tensors are resampled (with replacement) along the first dimension.\n\n    Args:\n        base_metric: base metric class to wrap\n        num_bootstraps: number of copies to make of the base metric for bootstrapping\n        mean: if ``True`` return the mean of the bootstraps\n        std: if ``True`` return the standard diviation of the bootstraps\n        quantile: if given, returns the quantile of the bootstraps. Can only be used with pytorch version 1.6 or higher\n        raw: if ``True``, return all bootstrapped values\n        sampling_strategy:\n            Determines how to produce bootstrapped samplings. Either ``'poisson'`` or ``multinomial``.\n            If ``'possion'`` is chosen, the number of times each sample will be included in the bootstrap\n            will be given by :math:`n\\sim Poisson(\\lambda=1)`, which approximates the true bootstrap distribution\n            when the number of samples is large. If ``'multinomial'`` is chosen, we will apply true bootstrapping\n            at the batch level to approximate bootstrapping over the hole dataset.\n        kwargs: Additional keyword arguments, see :ref:`Metric kwargs` for more info.\n\n    Example::\n        >>> from pprint import pprint\n        >>> from torchmetrics.wrappers import BootStrapper\n        >>> from torchmetrics.classification import MulticlassAccuracy\n        >>> _ = torch.manual_seed(123)\n        >>> base_metric = MulticlassAccuracy(num_classes=5, average='micro')\n        >>> bootstrap = BootStrapper(base_metric, num_bootstraps=20)\n        >>> bootstrap.update(torch.randint(5, (20,)), torch.randint(5, (20,)))\n        >>> output = bootstrap.compute()\n        >>> pprint(output)\n        {'mean': tensor(0.2205), 'std': tensor(0.0859)}\n\n    \"\"\"\n    full_state_update: Optional[bool] = True\n\n    def __init__(\n        self,\n        base_metric: Metric,\n        num_bootstraps: int = 10,\n        mean: bool = True,\n        std: bool = True,\n        quantile: Optional[Union[float, Tensor]] = None,\n        raw: bool = False,\n        sampling_strategy: str = \"poisson\",\n        **kwargs: Any,\n    ) -> None:\n        super().__init__(**kwargs)\n        if not isinstance(base_metric, Metric):\n            raise ValueError(\n                f\"Expected base metric to be an instance of torchmetrics.Metric but received {base_metric}\"\n            )\n\n        self.metrics = ModuleList([deepcopy(base_metric) for _ in range(num_bootstraps)])\n        self.num_bootstraps = num_bootstraps\n\n        self.mean = mean\n        self.std = std\n        self.quantile = quantile\n        self.raw = raw\n\n        allowed_sampling = (\"poisson\", \"multinomial\")\n        if sampling_strategy not in allowed_sampling:\n            raise ValueError(\n                f\"Expected argument ``sampling_strategy`` to be one of {allowed_sampling}\"\n                f\" but recieved {sampling_strategy}\"\n            )\n        self.sampling_strategy = sampling_strategy\n\n    def update(self, *args: Any, **kwargs: Any) -> None:\n        \"\"\"Update the state of the base metric.\n\n        Any tensor passed in will be bootstrapped along dimension 0.\n\n        \"\"\"\n        for idx in range(self.num_bootstraps):\n            args_sizes = apply_to_collection(args, Tensor, len)\n            kwargs_sizes = list(apply_to_collection(kwargs, Tensor, len))\n            if len(args_sizes) > 0:\n                size = args_sizes[0]\n            elif len(kwargs_sizes) > 0:\n                size = kwargs_sizes[0]\n            else:\n                raise ValueError(\"None of the input contained tensors, so could not determine the sampling size\")\n            sample_idx = _bootstrap_sampler(size, sampling_strategy=self.sampling_strategy).to(self.device)\n            new_args = apply_to_collection(args, Tensor, torch.index_select, dim=0, index=sample_idx)\n            new_kwargs = apply_to_collection(kwargs, Tensor, torch.index_select, dim=0, index=sample_idx)\n            self.metrics[idx].update(*new_args, **new_kwargs)\n\n    def compute(self) -> Dict[str, Tensor]:\n        \"\"\"Compute the bootstrapped metric values.\n\n        Always returns a dict of tensors, which can contain the following keys: ``mean``, ``std``, ``quantile`` and\n        ``raw`` depending on how the class was initialized.\n\n        \"\"\"\n        computed_vals = torch.stack([m.compute() for m in self.metrics], dim=0)\n        output_dict = {}\n        if self.mean:\n            output_dict[\"mean\"] = computed_vals.mean(dim=0)\n        if self.std:\n            output_dict[\"std\"] = computed_vals.std(dim=0)\n        if self.quantile is not None:\n            output_dict[\"quantile\"] = torch.quantile(computed_vals, self.quantile)\n        if self.raw:\n            output_dict[\"raw\"] = computed_vals\n        return output_dict\n\n    def forward(self, *args: Any, **kwargs: Any) -> Any:\n        \"\"\"Use the original forward method of the base metric class.\"\"\"\n        return super(WrapperMetric, self).forward(*args, **kwargs)\n\n    def plot(\n        self, val: Optional[Union[Tensor, Sequence[Tensor]]] = None, ax: Optional[_AX_TYPE] = None\n    ) -> _PLOT_OUT_TYPE:\n        \"\"\"Plot a single or multiple values from the metric.\n\n        Args:\n            val: Either a single result from calling `metric.forward` or `metric.compute` or a list of these results.\n                If no value is provided, will automatically call `metric.compute` and plot that result.\n            ax: An matplotlib axis object. If provided will add plot to that axis\n\n        Returns:\n            Figure and Axes object\n\n        Raises:\n            ModuleNotFoundError:\n                If `matplotlib` is not installed\n\n        .. plot::\n            :scale: 75\n\n            >>> # Example plotting a single value\n            >>> import torch\n            >>> from torchmetrics.wrappers import BootStrapper\n            >>> from torchmetrics.regression import MeanSquaredError\n            >>> metric = BootStrapper(MeanSquaredError(), num_bootstraps=20)\n            >>> metric.update(torch.randn(100,), torch.randn(100,))\n            >>> fig_, ax_ = metric.plot()\n\n        .. plot::\n            :scale: 75\n\n            >>> # Example plotting multiple values\n            >>> import torch\n            >>> from torchmetrics.wrappers import BootStrapper\n            >>> from torchmetrics.regression import MeanSquaredError\n            >>> metric = BootStrapper(MeanSquaredError(), num_bootstraps=20)\n            >>> values = [ ]\n            >>> for _ in range(3):\n            ...     values.append(metric(torch.randn(100,), torch.randn(100,)))\n            >>> fig_, ax_ = metric.plot(values)\n\n        \"\"\"\n        return self._plot(val, ax)\n", "path": "src/torchmetrics/wrappers/bootstrapping.py"}], "after_files": [{"content": "# Copyright The Lightning team.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom copy import deepcopy\nfrom typing import Any, Dict, Optional, Sequence, Union\n\nimport torch\nfrom lightning_utilities import apply_to_collection\nfrom torch import Tensor\nfrom torch.nn import ModuleList\n\nfrom torchmetrics.metric import Metric\nfrom torchmetrics.utilities.imports import _MATPLOTLIB_AVAILABLE\nfrom torchmetrics.utilities.plot import _AX_TYPE, _PLOT_OUT_TYPE\nfrom torchmetrics.wrappers.abstract import WrapperMetric\n\nif not _MATPLOTLIB_AVAILABLE:\n    __doctest_skip__ = [\"BootStrapper.plot\"]\n\n\ndef _bootstrap_sampler(\n    size: int,\n    sampling_strategy: str = \"poisson\",\n) -> Tensor:\n    \"\"\"Resample a tensor along its first dimension with replacement.\n\n    Args:\n        size: number of samples\n        sampling_strategy: the strategy to use for sampling, either ``'poisson'`` or ``'multinomial'``\n\n    Returns:\n        resampled tensor\n\n    \"\"\"\n    if sampling_strategy == \"poisson\":\n        p = torch.distributions.Poisson(1)\n        n = p.sample((size,))\n        return torch.arange(size).repeat_interleave(n.long(), dim=0)\n    if sampling_strategy == \"multinomial\":\n        return torch.multinomial(torch.ones(size), num_samples=size, replacement=True)\n    raise ValueError(\"Unknown sampling strategy\")\n\n\nclass BootStrapper(WrapperMetric):\n    r\"\"\"Using `Turn a Metric into a Bootstrapped`_.\n\n    That can automate the process of getting confidence intervals for metric values. This wrapper\n    class basically keeps multiple copies of the same base metric in memory and whenever ``update`` or\n    ``forward`` is called, all input tensors are resampled (with replacement) along the first dimension.\n\n    Args:\n        base_metric: base metric class to wrap\n        num_bootstraps: number of copies to make of the base metric for bootstrapping\n        mean: if ``True`` return the mean of the bootstraps\n        std: if ``True`` return the standard diviation of the bootstraps\n        quantile: if given, returns the quantile of the bootstraps. Can only be used with pytorch version 1.6 or higher\n        raw: if ``True``, return all bootstrapped values\n        sampling_strategy:\n            Determines how to produce bootstrapped samplings. Either ``'poisson'`` or ``multinomial``.\n            If ``'possion'`` is chosen, the number of times each sample will be included in the bootstrap\n            will be given by :math:`n\\sim Poisson(\\lambda=1)`, which approximates the true bootstrap distribution\n            when the number of samples is large. If ``'multinomial'`` is chosen, we will apply true bootstrapping\n            at the batch level to approximate bootstrapping over the hole dataset.\n        kwargs: Additional keyword arguments, see :ref:`Metric kwargs` for more info.\n\n    Example::\n        >>> from pprint import pprint\n        >>> from torchmetrics.wrappers import BootStrapper\n        >>> from torchmetrics.classification import MulticlassAccuracy\n        >>> _ = torch.manual_seed(123)\n        >>> base_metric = MulticlassAccuracy(num_classes=5, average='micro')\n        >>> bootstrap = BootStrapper(base_metric, num_bootstraps=20)\n        >>> bootstrap.update(torch.randint(5, (20,)), torch.randint(5, (20,)))\n        >>> output = bootstrap.compute()\n        >>> pprint(output)\n        {'mean': tensor(0.2205), 'std': tensor(0.0859)}\n\n    \"\"\"\n    full_state_update: Optional[bool] = True\n\n    def __init__(\n        self,\n        base_metric: Metric,\n        num_bootstraps: int = 10,\n        mean: bool = True,\n        std: bool = True,\n        quantile: Optional[Union[float, Tensor]] = None,\n        raw: bool = False,\n        sampling_strategy: str = \"poisson\",\n        **kwargs: Any,\n    ) -> None:\n        super().__init__(**kwargs)\n        if not isinstance(base_metric, Metric):\n            raise ValueError(\n                f\"Expected base metric to be an instance of torchmetrics.Metric but received {base_metric}\"\n            )\n\n        self.metrics = ModuleList([deepcopy(base_metric) for _ in range(num_bootstraps)])\n        self.num_bootstraps = num_bootstraps\n\n        self.mean = mean\n        self.std = std\n        self.quantile = quantile\n        self.raw = raw\n\n        allowed_sampling = (\"poisson\", \"multinomial\")\n        if sampling_strategy not in allowed_sampling:\n            raise ValueError(\n                f\"Expected argument ``sampling_strategy`` to be one of {allowed_sampling}\"\n                f\" but recieved {sampling_strategy}\"\n            )\n        self.sampling_strategy = sampling_strategy\n\n    def update(self, *args: Any, **kwargs: Any) -> None:\n        \"\"\"Update the state of the base metric.\n\n        Any tensor passed in will be bootstrapped along dimension 0.\n\n        \"\"\"\n        args_sizes = apply_to_collection(args, Tensor, len)\n        kwargs_sizes = list(apply_to_collection(kwargs, Tensor, len))\n        if len(args_sizes) > 0:\n            size = args_sizes[0]\n        elif len(kwargs_sizes) > 0:\n            size = kwargs_sizes[0]\n        else:\n            raise ValueError(\"None of the input contained tensors, so could not determine the sampling size\")\n\n        for idx in range(self.num_bootstraps):\n            sample_idx = _bootstrap_sampler(size, sampling_strategy=self.sampling_strategy).to(self.device)\n            if sample_idx.numel() == 0:\n                continue\n            new_args = apply_to_collection(args, Tensor, torch.index_select, dim=0, index=sample_idx)\n            new_kwargs = apply_to_collection(kwargs, Tensor, torch.index_select, dim=0, index=sample_idx)\n            self.metrics[idx].update(*new_args, **new_kwargs)\n\n    def compute(self) -> Dict[str, Tensor]:\n        \"\"\"Compute the bootstrapped metric values.\n\n        Always returns a dict of tensors, which can contain the following keys: ``mean``, ``std``, ``quantile`` and\n        ``raw`` depending on how the class was initialized.\n\n        \"\"\"\n        computed_vals = torch.stack([m.compute() for m in self.metrics], dim=0)\n        output_dict = {}\n        if self.mean:\n            output_dict[\"mean\"] = computed_vals.mean(dim=0)\n        if self.std:\n            output_dict[\"std\"] = computed_vals.std(dim=0)\n        if self.quantile is not None:\n            output_dict[\"quantile\"] = torch.quantile(computed_vals, self.quantile)\n        if self.raw:\n            output_dict[\"raw\"] = computed_vals\n        return output_dict\n\n    def forward(self, *args: Any, **kwargs: Any) -> Any:\n        \"\"\"Use the original forward method of the base metric class.\"\"\"\n        return super(WrapperMetric, self).forward(*args, **kwargs)\n\n    def plot(\n        self, val: Optional[Union[Tensor, Sequence[Tensor]]] = None, ax: Optional[_AX_TYPE] = None\n    ) -> _PLOT_OUT_TYPE:\n        \"\"\"Plot a single or multiple values from the metric.\n\n        Args:\n            val: Either a single result from calling `metric.forward` or `metric.compute` or a list of these results.\n                If no value is provided, will automatically call `metric.compute` and plot that result.\n            ax: An matplotlib axis object. If provided will add plot to that axis\n\n        Returns:\n            Figure and Axes object\n\n        Raises:\n            ModuleNotFoundError:\n                If `matplotlib` is not installed\n\n        .. plot::\n            :scale: 75\n\n            >>> # Example plotting a single value\n            >>> import torch\n            >>> from torchmetrics.wrappers import BootStrapper\n            >>> from torchmetrics.regression import MeanSquaredError\n            >>> metric = BootStrapper(MeanSquaredError(), num_bootstraps=20)\n            >>> metric.update(torch.randn(100,), torch.randn(100,))\n            >>> fig_, ax_ = metric.plot()\n\n        .. plot::\n            :scale: 75\n\n            >>> # Example plotting multiple values\n            >>> import torch\n            >>> from torchmetrics.wrappers import BootStrapper\n            >>> from torchmetrics.regression import MeanSquaredError\n            >>> metric = BootStrapper(MeanSquaredError(), num_bootstraps=20)\n            >>> values = [ ]\n            >>> for _ in range(3):\n            ...     values.append(metric(torch.randn(100,), torch.randn(100,)))\n            >>> fig_, ax_ = metric.plot(values)\n\n        \"\"\"\n        return self._plot(val, ax)\n", "path": "src/torchmetrics/wrappers/bootstrapping.py"}]}