We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
SSIM update does not work for the last batch (if batch size is different)
## 🐛 Bug description
![Screenshot 2022-03-28 at 4 27 36 PM](https://user-images.githubusercontent.com/13629918/160447093-d5c0980e-4951-4eaa-930b-6dd9ea7992a4.png)

<!-- A clear and concise description of what the bug is. -->
<!-- Please, add steps on how to reproduce it. -->
<!-- If you have a code sample, error messages, stack traces, please provide it here as well -->
<!-- A clear and concise description of what you expected to happen. -->

## Environment

- PyTorch Version :1.10.1
- Ignite Version ():0.4.8
- OS : Ubuntu
- How you installed Ignite (`conda`, `pip`, source): 
- Python version: 3.9
- Any other relevant information:

If the previous batch and the current batch are different then it throws the error. Attaching the screen shot of error. 

Screenshot of the basic code is also attached.
![Screenshot 2022-03-28 at 7 04 08 PM](https://user-images.githubusercontent.com/13629918/160450347-141131ed-ef66-4532-a36a-a21c27028b0e.png)

Implementation of ReduceLROnPlateau scheduler 
## 🚀 Feature

While there might be some workarounds in using `ReduceLROnPlateau` with ignite's optimizer , it would be nicer to have a standalone feature like other schedulers in contrib.

[Relevant issue and code sample](https://github.com/pytorch/ignite/issues/462#issuecomment-771907302).

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `ignite/metrics/ssim.py`
Content:
```
1 from typing import Callable, Sequence, Union
2 
3 import torch
4 import torch.nn.functional as F
5 
6 from ignite.exceptions import NotComputableError
7 from ignite.metrics.metric import Metric, reinit__is_reduced, sync_all_reduce
8 
9 __all__ = ["SSIM"]
10 
11 
12 class SSIM(Metric):
13     """
14     Computes Structual Similarity Index Measure
15 
16     Args:
17         data_range: Range of the image. Typically, ``1.0`` or ``255``.
18         kernel_size: Size of the kernel. Default: (11, 11)
19         sigma: Standard deviation of the gaussian kernel.
20             Argument is used if ``gaussian=True``. Default: (1.5, 1.5)
21         k1: Parameter of SSIM. Default: 0.01
22         k2: Parameter of SSIM. Default: 0.03
23         gaussian: ``True`` to use gaussian kernel, ``False`` to use uniform kernel
24         output_transform: A callable that is used to transform the
25             :class:`~ignite.engine.engine.Engine`'s ``process_function``'s output into the
26             form expected by the metric.
27         device: specifies which device updates are accumulated on. Setting the metric's
28             device to be the same as your ``update`` arguments ensures the ``update`` method is non-blocking. By
29             default, CPU.
30 
31     Examples:
32         To use with ``Engine`` and ``process_function``, simply attach the metric instance to the engine.
33         The output of the engine's ``process_function`` needs to be in the format of
34         ``(y_pred, y)`` or ``{'y_pred': y_pred, 'y': y, ...}``.
35 
36         ``y_pred`` and ``y`` can be un-normalized or normalized image tensors. Depending on that, the user might need
37         to adjust ``data_range``. ``y_pred`` and ``y`` should have the same shape.
38 
39         For more information on how metric works with :class:`~ignite.engine.engine.Engine`, visit :ref:`attach-engine`.
40 
41         .. include:: defaults.rst
42             :start-after: :orphan:
43 
44         .. testcode::
45 
46             metric = SSIM(data_range=1.0)
47             metric.attach(default_evaluator, 'ssim')
48             preds = torch.rand([4, 3, 16, 16])
49             target = preds * 0.75
50             state = default_evaluator.run([[preds, target]])
51             print(state.metrics['ssim'])
52 
53         .. testoutput::
54 
55             0.9218971...
56 
57     .. versionadded:: 0.4.2
58     """
59 
60     def __init__(
61         self,
62         data_range: Union[int, float],
63         kernel_size: Union[int, Sequence[int]] = (11, 11),
64         sigma: Union[float, Sequence[float]] = (1.5, 1.5),
65         k1: float = 0.01,
66         k2: float = 0.03,
67         gaussian: bool = True,
68         output_transform: Callable = lambda x: x,
69         device: Union[str, torch.device] = torch.device("cpu"),
70     ):
71         if isinstance(kernel_size, int):
72             self.kernel_size = [kernel_size, kernel_size]  # type: Sequence[int]
73         elif isinstance(kernel_size, Sequence):
74             self.kernel_size = kernel_size
75         else:
76             raise ValueError("Argument kernel_size should be either int or a sequence of int.")
77 
78         if isinstance(sigma, float):
79             self.sigma = [sigma, sigma]  # type: Sequence[float]
80         elif isinstance(sigma, Sequence):
81             self.sigma = sigma
82         else:
83             raise ValueError("Argument sigma should be either float or a sequence of float.")
84 
85         if any(x % 2 == 0 or x <= 0 for x in self.kernel_size):
86             raise ValueError(f"Expected kernel_size to have odd positive number. Got {kernel_size}.")
87 
88         if any(y <= 0 for y in self.sigma):
89             raise ValueError(f"Expected sigma to have positive number. Got {sigma}.")
90 
91         super(SSIM, self).__init__(output_transform=output_transform, device=device)
92         self.gaussian = gaussian
93         self.c1 = (k1 * data_range) ** 2
94         self.c2 = (k2 * data_range) ** 2
95         self.pad_h = (self.kernel_size[0] - 1) // 2
96         self.pad_w = (self.kernel_size[1] - 1) // 2
97         self._kernel = self._gaussian_or_uniform_kernel(kernel_size=self.kernel_size, sigma=self.sigma)
98 
99     @reinit__is_reduced
100     def reset(self) -> None:
101         self._sum_of_ssim = torch.tensor(0.0, device=self._device)
102         self._num_examples = 0
103         self._kernel = self._gaussian_or_uniform_kernel(kernel_size=self.kernel_size, sigma=self.sigma)
104 
105     def _uniform(self, kernel_size: int) -> torch.Tensor:
106         max, min = 2.5, -2.5
107         ksize_half = (kernel_size - 1) * 0.5
108         kernel = torch.linspace(-ksize_half, ksize_half, steps=kernel_size, device=self._device)
109         for i, j in enumerate(kernel):
110             if min <= j <= max:
111                 kernel[i] = 1 / (max - min)
112             else:
113                 kernel[i] = 0
114 
115         return kernel.unsqueeze(dim=0)  # (1, kernel_size)
116 
117     def _gaussian(self, kernel_size: int, sigma: float) -> torch.Tensor:
118         ksize_half = (kernel_size - 1) * 0.5
119         kernel = torch.linspace(-ksize_half, ksize_half, steps=kernel_size, device=self._device)
120         gauss = torch.exp(-0.5 * (kernel / sigma).pow(2))
121         return (gauss / gauss.sum()).unsqueeze(dim=0)  # (1, kernel_size)
122 
123     def _gaussian_or_uniform_kernel(self, kernel_size: Sequence[int], sigma: Sequence[float]) -> torch.Tensor:
124         if self.gaussian:
125             kernel_x = self._gaussian(kernel_size[0], sigma[0])
126             kernel_y = self._gaussian(kernel_size[1], sigma[1])
127         else:
128             kernel_x = self._uniform(kernel_size[0])
129             kernel_y = self._uniform(kernel_size[1])
130 
131         return torch.matmul(kernel_x.t(), kernel_y)  # (kernel_size, 1) * (1, kernel_size)
132 
133     @reinit__is_reduced
134     def update(self, output: Sequence[torch.Tensor]) -> None:
135         y_pred, y = output[0].detach(), output[1].detach()
136 
137         if y_pred.dtype != y.dtype:
138             raise TypeError(
139                 f"Expected y_pred and y to have the same data type. Got y_pred: {y_pred.dtype} and y: {y.dtype}."
140             )
141 
142         if y_pred.shape != y.shape:
143             raise ValueError(
144                 f"Expected y_pred and y to have the same shape. Got y_pred: {y_pred.shape} and y: {y.shape}."
145             )
146 
147         if len(y_pred.shape) != 4 or len(y.shape) != 4:
148             raise ValueError(
149                 f"Expected y_pred and y to have BxCxHxW shape. Got y_pred: {y_pred.shape} and y: {y.shape}."
150             )
151 
152         channel = y_pred.size(1)
153         if len(self._kernel.shape) < 4:
154             self._kernel = self._kernel.expand(channel, 1, -1, -1).to(device=y_pred.device)
155 
156         y_pred = F.pad(y_pred, [self.pad_w, self.pad_w, self.pad_h, self.pad_h], mode="reflect")
157         y = F.pad(y, [self.pad_w, self.pad_w, self.pad_h, self.pad_h], mode="reflect")
158 
159         input_list = torch.cat([y_pred, y, y_pred * y_pred, y * y, y_pred * y])
160         outputs = F.conv2d(input_list, self._kernel, groups=channel)
161 
162         output_list = [outputs[x * y_pred.size(0) : (x + 1) * y_pred.size(0)] for x in range(len(outputs))]
163 
164         mu_pred_sq = output_list[0].pow(2)
165         mu_target_sq = output_list[1].pow(2)
166         mu_pred_target = output_list[0] * output_list[1]
167 
168         sigma_pred_sq = output_list[2] - mu_pred_sq
169         sigma_target_sq = output_list[3] - mu_target_sq
170         sigma_pred_target = output_list[4] - mu_pred_target
171 
172         a1 = 2 * mu_pred_target + self.c1
173         a2 = 2 * sigma_pred_target + self.c2
174         b1 = mu_pred_sq + mu_target_sq + self.c1
175         b2 = sigma_pred_sq + sigma_target_sq + self.c2
176 
177         ssim_idx = (a1 * a2) / (b1 * b2)
178         self._sum_of_ssim += torch.mean(ssim_idx, (1, 2, 3), dtype=torch.float64).sum().to(self._device)
179 
180         self._num_examples += y.shape[0]
181 
182     @sync_all_reduce("_sum_of_ssim", "_num_examples")
183     def compute(self) -> torch.Tensor:
184         if self._num_examples == 0:
185             raise NotComputableError("SSIM must have at least one example before it can be computed.")
186         return self._sum_of_ssim / self._num_examples
187
```
--- 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/ignite/metrics/ssim.py b/ignite/metrics/ssim.py
--- a/ignite/metrics/ssim.py
+++ b/ignite/metrics/ssim.py
@@ -98,7 +98,7 @@
 
     @reinit__is_reduced
     def reset(self) -> None:
-        self._sum_of_ssim = torch.tensor(0.0, device=self._device)
+        self._sum_of_ssim = torch.tensor(0.0, dtype=torch.float64, device=self._device)
         self._num_examples = 0
         self._kernel = self._gaussian_or_uniform_kernel(kernel_size=self.kernel_size, sigma=self.sigma)
 
@@ -180,7 +180,7 @@
         self._num_examples += y.shape[0]
 
     @sync_all_reduce("_sum_of_ssim", "_num_examples")
-    def compute(self) -> torch.Tensor:
+    def compute(self) -> float:
         if self._num_examples == 0:
             raise NotComputableError("SSIM must have at least one example before it can be computed.")
-        return self._sum_of_ssim / self._num_examples
+        return (self._sum_of_ssim / self._num_examples).item()

{"golden_diff": "diff --git a/ignite/metrics/ssim.py b/ignite/metrics/ssim.py\n--- a/ignite/metrics/ssim.py\n+++ b/ignite/metrics/ssim.py\n@@ -98,7 +98,7 @@\n \n     @reinit__is_reduced\n     def reset(self) -> None:\n-        self._sum_of_ssim = torch.tensor(0.0, device=self._device)\n+        self._sum_of_ssim = torch.tensor(0.0, dtype=torch.float64, device=self._device)\n         self._num_examples = 0\n         self._kernel = self._gaussian_or_uniform_kernel(kernel_size=self.kernel_size, sigma=self.sigma)\n \n@@ -180,7 +180,7 @@\n         self._num_examples += y.shape[0]\n \n     @sync_all_reduce(\"_sum_of_ssim\", \"_num_examples\")\n-    def compute(self) -> torch.Tensor:\n+    def compute(self) -> float:\n         if self._num_examples == 0:\n             raise NotComputableError(\"SSIM must have at least one example before it can be computed.\")\n-        return self._sum_of_ssim / self._num_examples\n+        return (self._sum_of_ssim / self._num_examples).item()\n", "issue": "SSIM update does not work for the last batch (if batch size is different)\n## \ud83d\udc1b Bug description\r\n![Screenshot 2022-03-28 at 4 27 36 PM](https://user-images.githubusercontent.com/13629918/160447093-d5c0980e-4951-4eaa-930b-6dd9ea7992a4.png)\r\n\r\n<!-- A clear and concise description of what the bug is. -->\r\n<!-- Please, add steps on how to reproduce it. -->\r\n<!-- If you have a code sample, error messages, stack traces, please provide it here as well -->\r\n<!-- A clear and concise description of what you expected to happen. -->\r\n\r\n## Environment\r\n\r\n- PyTorch Version :1.10.1\r\n- Ignite Version ():0.4.8\r\n- OS : Ubuntu\r\n- How you installed Ignite (`conda`, `pip`, source): \r\n- Python version: 3.9\r\n- Any other relevant information:\r\n\r\nIf the previous batch and the current batch are different then it throws the error. Attaching the screen shot of error. \r\n\r\nScreenshot of the basic code is also attached.\r\n![Screenshot 2022-03-28 at 7 04 08 PM](https://user-images.githubusercontent.com/13629918/160450347-141131ed-ef66-4532-a36a-a21c27028b0e.png)\r\n\nImplementation of ReduceLROnPlateau scheduler \n## \ud83d\ude80 Feature\r\n\r\nWhile there might be some workarounds in using `ReduceLROnPlateau` with ignite's optimizer , it would be nicer to have a standalone feature like other schedulers in contrib.\r\n\r\n[Relevant issue and code sample](https://github.com/pytorch/ignite/issues/462#issuecomment-771907302).\n", "before_files": [{"content": "from typing import Callable, Sequence, Union\n\nimport torch\nimport torch.nn.functional as F\n\nfrom ignite.exceptions import NotComputableError\nfrom ignite.metrics.metric import Metric, reinit__is_reduced, sync_all_reduce\n\n__all__ = [\"SSIM\"]\n\n\nclass SSIM(Metric):\n    \"\"\"\n    Computes Structual Similarity Index Measure\n\n    Args:\n        data_range: Range of the image. Typically, ``1.0`` or ``255``.\n        kernel_size: Size of the kernel. Default: (11, 11)\n        sigma: Standard deviation of the gaussian kernel.\n            Argument is used if ``gaussian=True``. Default: (1.5, 1.5)\n        k1: Parameter of SSIM. Default: 0.01\n        k2: Parameter of SSIM. Default: 0.03\n        gaussian: ``True`` to use gaussian kernel, ``False`` to use uniform kernel\n        output_transform: A callable that is used to transform the\n            :class:`~ignite.engine.engine.Engine`'s ``process_function``'s output into the\n            form expected by the metric.\n        device: specifies which device updates are accumulated on. Setting the metric's\n            device to be the same as your ``update`` arguments ensures the ``update`` method is non-blocking. By\n            default, CPU.\n\n    Examples:\n        To use with ``Engine`` and ``process_function``, simply attach the metric instance to the engine.\n        The output of the engine's ``process_function`` needs to be in the format of\n        ``(y_pred, y)`` or ``{'y_pred': y_pred, 'y': y, ...}``.\n\n        ``y_pred`` and ``y`` can be un-normalized or normalized image tensors. Depending on that, the user might need\n        to adjust ``data_range``. ``y_pred`` and ``y`` should have the same shape.\n\n        For more information on how metric works with :class:`~ignite.engine.engine.Engine`, visit :ref:`attach-engine`.\n\n        .. include:: defaults.rst\n            :start-after: :orphan:\n\n        .. testcode::\n\n            metric = SSIM(data_range=1.0)\n            metric.attach(default_evaluator, 'ssim')\n            preds = torch.rand([4, 3, 16, 16])\n            target = preds * 0.75\n            state = default_evaluator.run([[preds, target]])\n            print(state.metrics['ssim'])\n\n        .. testoutput::\n\n            0.9218971...\n\n    .. versionadded:: 0.4.2\n    \"\"\"\n\n    def __init__(\n        self,\n        data_range: Union[int, float],\n        kernel_size: Union[int, Sequence[int]] = (11, 11),\n        sigma: Union[float, Sequence[float]] = (1.5, 1.5),\n        k1: float = 0.01,\n        k2: float = 0.03,\n        gaussian: bool = True,\n        output_transform: Callable = lambda x: x,\n        device: Union[str, torch.device] = torch.device(\"cpu\"),\n    ):\n        if isinstance(kernel_size, int):\n            self.kernel_size = [kernel_size, kernel_size]  # type: Sequence[int]\n        elif isinstance(kernel_size, Sequence):\n            self.kernel_size = kernel_size\n        else:\n            raise ValueError(\"Argument kernel_size should be either int or a sequence of int.\")\n\n        if isinstance(sigma, float):\n            self.sigma = [sigma, sigma]  # type: Sequence[float]\n        elif isinstance(sigma, Sequence):\n            self.sigma = sigma\n        else:\n            raise ValueError(\"Argument sigma should be either float or a sequence of float.\")\n\n        if any(x % 2 == 0 or x <= 0 for x in self.kernel_size):\n            raise ValueError(f\"Expected kernel_size to have odd positive number. Got {kernel_size}.\")\n\n        if any(y <= 0 for y in self.sigma):\n            raise ValueError(f\"Expected sigma to have positive number. Got {sigma}.\")\n\n        super(SSIM, self).__init__(output_transform=output_transform, device=device)\n        self.gaussian = gaussian\n        self.c1 = (k1 * data_range) ** 2\n        self.c2 = (k2 * data_range) ** 2\n        self.pad_h = (self.kernel_size[0] - 1) // 2\n        self.pad_w = (self.kernel_size[1] - 1) // 2\n        self._kernel = self._gaussian_or_uniform_kernel(kernel_size=self.kernel_size, sigma=self.sigma)\n\n    @reinit__is_reduced\n    def reset(self) -> None:\n        self._sum_of_ssim = torch.tensor(0.0, device=self._device)\n        self._num_examples = 0\n        self._kernel = self._gaussian_or_uniform_kernel(kernel_size=self.kernel_size, sigma=self.sigma)\n\n    def _uniform(self, kernel_size: int) -> torch.Tensor:\n        max, min = 2.5, -2.5\n        ksize_half = (kernel_size - 1) * 0.5\n        kernel = torch.linspace(-ksize_half, ksize_half, steps=kernel_size, device=self._device)\n        for i, j in enumerate(kernel):\n            if min <= j <= max:\n                kernel[i] = 1 / (max - min)\n            else:\n                kernel[i] = 0\n\n        return kernel.unsqueeze(dim=0)  # (1, kernel_size)\n\n    def _gaussian(self, kernel_size: int, sigma: float) -> torch.Tensor:\n        ksize_half = (kernel_size - 1) * 0.5\n        kernel = torch.linspace(-ksize_half, ksize_half, steps=kernel_size, device=self._device)\n        gauss = torch.exp(-0.5 * (kernel / sigma).pow(2))\n        return (gauss / gauss.sum()).unsqueeze(dim=0)  # (1, kernel_size)\n\n    def _gaussian_or_uniform_kernel(self, kernel_size: Sequence[int], sigma: Sequence[float]) -> torch.Tensor:\n        if self.gaussian:\n            kernel_x = self._gaussian(kernel_size[0], sigma[0])\n            kernel_y = self._gaussian(kernel_size[1], sigma[1])\n        else:\n            kernel_x = self._uniform(kernel_size[0])\n            kernel_y = self._uniform(kernel_size[1])\n\n        return torch.matmul(kernel_x.t(), kernel_y)  # (kernel_size, 1) * (1, kernel_size)\n\n    @reinit__is_reduced\n    def update(self, output: Sequence[torch.Tensor]) -> None:\n        y_pred, y = output[0].detach(), output[1].detach()\n\n        if y_pred.dtype != y.dtype:\n            raise TypeError(\n                f\"Expected y_pred and y to have the same data type. Got y_pred: {y_pred.dtype} and y: {y.dtype}.\"\n            )\n\n        if y_pred.shape != y.shape:\n            raise ValueError(\n                f\"Expected y_pred and y to have the same shape. Got y_pred: {y_pred.shape} and y: {y.shape}.\"\n            )\n\n        if len(y_pred.shape) != 4 or len(y.shape) != 4:\n            raise ValueError(\n                f\"Expected y_pred and y to have BxCxHxW shape. Got y_pred: {y_pred.shape} and y: {y.shape}.\"\n            )\n\n        channel = y_pred.size(1)\n        if len(self._kernel.shape) < 4:\n            self._kernel = self._kernel.expand(channel, 1, -1, -1).to(device=y_pred.device)\n\n        y_pred = F.pad(y_pred, [self.pad_w, self.pad_w, self.pad_h, self.pad_h], mode=\"reflect\")\n        y = F.pad(y, [self.pad_w, self.pad_w, self.pad_h, self.pad_h], mode=\"reflect\")\n\n        input_list = torch.cat([y_pred, y, y_pred * y_pred, y * y, y_pred * y])\n        outputs = F.conv2d(input_list, self._kernel, groups=channel)\n\n        output_list = [outputs[x * y_pred.size(0) : (x + 1) * y_pred.size(0)] for x in range(len(outputs))]\n\n        mu_pred_sq = output_list[0].pow(2)\n        mu_target_sq = output_list[1].pow(2)\n        mu_pred_target = output_list[0] * output_list[1]\n\n        sigma_pred_sq = output_list[2] - mu_pred_sq\n        sigma_target_sq = output_list[3] - mu_target_sq\n        sigma_pred_target = output_list[4] - mu_pred_target\n\n        a1 = 2 * mu_pred_target + self.c1\n        a2 = 2 * sigma_pred_target + self.c2\n        b1 = mu_pred_sq + mu_target_sq + self.c1\n        b2 = sigma_pred_sq + sigma_target_sq + self.c2\n\n        ssim_idx = (a1 * a2) / (b1 * b2)\n        self._sum_of_ssim += torch.mean(ssim_idx, (1, 2, 3), dtype=torch.float64).sum().to(self._device)\n\n        self._num_examples += y.shape[0]\n\n    @sync_all_reduce(\"_sum_of_ssim\", \"_num_examples\")\n    def compute(self) -> torch.Tensor:\n        if self._num_examples == 0:\n            raise NotComputableError(\"SSIM must have at least one example before it can be computed.\")\n        return self._sum_of_ssim / self._num_examples\n", "path": "ignite/metrics/ssim.py"}], "after_files": [{"content": "from typing import Callable, Sequence, Union\n\nimport torch\nimport torch.nn.functional as F\n\nfrom ignite.exceptions import NotComputableError\nfrom ignite.metrics.metric import Metric, reinit__is_reduced, sync_all_reduce\n\n__all__ = [\"SSIM\"]\n\n\nclass SSIM(Metric):\n    \"\"\"\n    Computes Structual Similarity Index Measure\n\n    Args:\n        data_range: Range of the image. Typically, ``1.0`` or ``255``.\n        kernel_size: Size of the kernel. Default: (11, 11)\n        sigma: Standard deviation of the gaussian kernel.\n            Argument is used if ``gaussian=True``. Default: (1.5, 1.5)\n        k1: Parameter of SSIM. Default: 0.01\n        k2: Parameter of SSIM. Default: 0.03\n        gaussian: ``True`` to use gaussian kernel, ``False`` to use uniform kernel\n        output_transform: A callable that is used to transform the\n            :class:`~ignite.engine.engine.Engine`'s ``process_function``'s output into the\n            form expected by the metric.\n        device: specifies which device updates are accumulated on. Setting the metric's\n            device to be the same as your ``update`` arguments ensures the ``update`` method is non-blocking. By\n            default, CPU.\n\n    Examples:\n        To use with ``Engine`` and ``process_function``, simply attach the metric instance to the engine.\n        The output of the engine's ``process_function`` needs to be in the format of\n        ``(y_pred, y)`` or ``{'y_pred': y_pred, 'y': y, ...}``.\n\n        ``y_pred`` and ``y`` can be un-normalized or normalized image tensors. Depending on that, the user might need\n        to adjust ``data_range``. ``y_pred`` and ``y`` should have the same shape.\n\n        For more information on how metric works with :class:`~ignite.engine.engine.Engine`, visit :ref:`attach-engine`.\n\n        .. include:: defaults.rst\n            :start-after: :orphan:\n\n        .. testcode::\n\n            metric = SSIM(data_range=1.0)\n            metric.attach(default_evaluator, 'ssim')\n            preds = torch.rand([4, 3, 16, 16])\n            target = preds * 0.75\n            state = default_evaluator.run([[preds, target]])\n            print(state.metrics['ssim'])\n\n        .. testoutput::\n\n            0.9218971...\n\n    .. versionadded:: 0.4.2\n    \"\"\"\n\n    def __init__(\n        self,\n        data_range: Union[int, float],\n        kernel_size: Union[int, Sequence[int]] = (11, 11),\n        sigma: Union[float, Sequence[float]] = (1.5, 1.5),\n        k1: float = 0.01,\n        k2: float = 0.03,\n        gaussian: bool = True,\n        output_transform: Callable = lambda x: x,\n        device: Union[str, torch.device] = torch.device(\"cpu\"),\n    ):\n        if isinstance(kernel_size, int):\n            self.kernel_size = [kernel_size, kernel_size]  # type: Sequence[int]\n        elif isinstance(kernel_size, Sequence):\n            self.kernel_size = kernel_size\n        else:\n            raise ValueError(\"Argument kernel_size should be either int or a sequence of int.\")\n\n        if isinstance(sigma, float):\n            self.sigma = [sigma, sigma]  # type: Sequence[float]\n        elif isinstance(sigma, Sequence):\n            self.sigma = sigma\n        else:\n            raise ValueError(\"Argument sigma should be either float or a sequence of float.\")\n\n        if any(x % 2 == 0 or x <= 0 for x in self.kernel_size):\n            raise ValueError(f\"Expected kernel_size to have odd positive number. Got {kernel_size}.\")\n\n        if any(y <= 0 for y in self.sigma):\n            raise ValueError(f\"Expected sigma to have positive number. Got {sigma}.\")\n\n        super(SSIM, self).__init__(output_transform=output_transform, device=device)\n        self.gaussian = gaussian\n        self.c1 = (k1 * data_range) ** 2\n        self.c2 = (k2 * data_range) ** 2\n        self.pad_h = (self.kernel_size[0] - 1) // 2\n        self.pad_w = (self.kernel_size[1] - 1) // 2\n        self._kernel = self._gaussian_or_uniform_kernel(kernel_size=self.kernel_size, sigma=self.sigma)\n\n    @reinit__is_reduced\n    def reset(self) -> None:\n        self._sum_of_ssim = torch.tensor(0.0, dtype=torch.float64, device=self._device)\n        self._num_examples = 0\n        self._kernel = self._gaussian_or_uniform_kernel(kernel_size=self.kernel_size, sigma=self.sigma)\n\n    def _uniform(self, kernel_size: int) -> torch.Tensor:\n        max, min = 2.5, -2.5\n        ksize_half = (kernel_size - 1) * 0.5\n        kernel = torch.linspace(-ksize_half, ksize_half, steps=kernel_size, device=self._device)\n        for i, j in enumerate(kernel):\n            if min <= j <= max:\n                kernel[i] = 1 / (max - min)\n            else:\n                kernel[i] = 0\n\n        return kernel.unsqueeze(dim=0)  # (1, kernel_size)\n\n    def _gaussian(self, kernel_size: int, sigma: float) -> torch.Tensor:\n        ksize_half = (kernel_size - 1) * 0.5\n        kernel = torch.linspace(-ksize_half, ksize_half, steps=kernel_size, device=self._device)\n        gauss = torch.exp(-0.5 * (kernel / sigma).pow(2))\n        return (gauss / gauss.sum()).unsqueeze(dim=0)  # (1, kernel_size)\n\n    def _gaussian_or_uniform_kernel(self, kernel_size: Sequence[int], sigma: Sequence[float]) -> torch.Tensor:\n        if self.gaussian:\n            kernel_x = self._gaussian(kernel_size[0], sigma[0])\n            kernel_y = self._gaussian(kernel_size[1], sigma[1])\n        else:\n            kernel_x = self._uniform(kernel_size[0])\n            kernel_y = self._uniform(kernel_size[1])\n\n        return torch.matmul(kernel_x.t(), kernel_y)  # (kernel_size, 1) * (1, kernel_size)\n\n    @reinit__is_reduced\n    def update(self, output: Sequence[torch.Tensor]) -> None:\n        y_pred, y = output[0].detach(), output[1].detach()\n\n        if y_pred.dtype != y.dtype:\n            raise TypeError(\n                f\"Expected y_pred and y to have the same data type. Got y_pred: {y_pred.dtype} and y: {y.dtype}.\"\n            )\n\n        if y_pred.shape != y.shape:\n            raise ValueError(\n                f\"Expected y_pred and y to have the same shape. Got y_pred: {y_pred.shape} and y: {y.shape}.\"\n            )\n\n        if len(y_pred.shape) != 4 or len(y.shape) != 4:\n            raise ValueError(\n                f\"Expected y_pred and y to have BxCxHxW shape. Got y_pred: {y_pred.shape} and y: {y.shape}.\"\n            )\n\n        channel = y_pred.size(1)\n        if len(self._kernel.shape) < 4:\n            self._kernel = self._kernel.expand(channel, 1, -1, -1).to(device=y_pred.device)\n\n        y_pred = F.pad(y_pred, [self.pad_w, self.pad_w, self.pad_h, self.pad_h], mode=\"reflect\")\n        y = F.pad(y, [self.pad_w, self.pad_w, self.pad_h, self.pad_h], mode=\"reflect\")\n\n        input_list = torch.cat([y_pred, y, y_pred * y_pred, y * y, y_pred * y])\n        outputs = F.conv2d(input_list, self._kernel, groups=channel)\n\n        output_list = [outputs[x * y_pred.size(0) : (x + 1) * y_pred.size(0)] for x in range(len(outputs))]\n\n        mu_pred_sq = output_list[0].pow(2)\n        mu_target_sq = output_list[1].pow(2)\n        mu_pred_target = output_list[0] * output_list[1]\n\n        sigma_pred_sq = output_list[2] - mu_pred_sq\n        sigma_target_sq = output_list[3] - mu_target_sq\n        sigma_pred_target = output_list[4] - mu_pred_target\n\n        a1 = 2 * mu_pred_target + self.c1\n        a2 = 2 * sigma_pred_target + self.c2\n        b1 = mu_pred_sq + mu_target_sq + self.c1\n        b2 = sigma_pred_sq + sigma_target_sq + self.c2\n\n        ssim_idx = (a1 * a2) / (b1 * b2)\n        self._sum_of_ssim += torch.mean(ssim_idx, (1, 2, 3), dtype=torch.float64).sum().to(self._device)\n\n        self._num_examples += y.shape[0]\n\n    @sync_all_reduce(\"_sum_of_ssim\", \"_num_examples\")\n    def compute(self) -> float:\n        if self._num_examples == 0:\n            raise NotComputableError(\"SSIM must have at least one example before it can be computed.\")\n        return (self._sum_of_ssim / self._num_examples).item()\n", "path": "ignite/metrics/ssim.py"}]}