We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Metrics objects are not pickleable
Pickling Metrics objects fails due to infinite loop.

The reason for that is the following:

To make metrics composable, the base class Metric has [methods](https://github.com/pytorch/ignite/blob/master/ignite/metrics/metric.py#L86) to return a `MetricsLambda` object. However, this means that pickling `Metrics` requires pickling `MetricsLambda`, but since `MetricsLambda` depends on `Metrics` we enter an infinite loop.



--- 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/metric.py`
Content:
```
1 import numbers
2 from abc import ABCMeta, abstractmethod
3 from functools import wraps
4 from collections.abc import Mapping
5 import warnings
6 
7 from typing import Callable, Union, Optional, Any
8 
9 import torch
10 import torch.distributed as dist
11 
12 from ignite.engine import Events, Engine
13 
14 __all__ = ["Metric"]
15 
16 
17 class Metric(metaclass=ABCMeta):
18     """
19     Base class for all Metrics.
20 
21     Args:
22         output_transform (callable, optional): a callable that is used to transform the
23             :class:`~ignite.engine.Engine`'s `process_function`'s output into the
24             form expected by the metric. This can be useful if, for example, you have a multi-output model and
25             you want to compute the metric with respect to one of the outputs.
26             By default, metrics require the output as `(y_pred, y)` or `{'y_pred': y_pred, 'y': y}`.
27         device (str of torch.device, optional): device specification in case of distributed computation usage.
28             In most of the cases, it can be defined as "cuda:local_rank" or "cuda"
29             if already set `torch.cuda.set_device(local_rank)`. By default, if a distributed process group is
30             initialized and available, device is set to `cuda`.
31 
32     """
33 
34     _required_output_keys = ("y_pred", "y")
35 
36     def __init__(self, output_transform: Callable = lambda x: x, device: Optional[Union[str, torch.device]] = None):
37         self._output_transform = output_transform
38 
39         # Check device if distributed is initialized:
40         if dist.is_available() and dist.is_initialized():
41 
42             # check if reset and update methods are decorated. Compute may not be decorated
43             if not (hasattr(self.reset, "_decorated") and hasattr(self.update, "_decorated")):
44                 warnings.warn(
45                     "{} class does not support distributed setting. Computed result is not collected "
46                     "across all computing devices".format(self.__class__.__name__),
47                     RuntimeWarning,
48                 )
49             if device is None:
50                 device = "cuda"
51             device = torch.device(device)
52         self._device = device
53         self._is_reduced = False
54         self.reset()
55 
56     @abstractmethod
57     def reset(self) -> None:
58         """
59         Resets the metric to it's initial state.
60 
61         This is called at the start of each epoch.
62         """
63         pass
64 
65     @abstractmethod
66     def update(self, output) -> None:
67         """
68         Updates the metric's state using the passed batch output.
69 
70         This is called once for each batch.
71 
72         Args:
73             output: the is the output from the engine's process function.
74         """
75         pass
76 
77     @abstractmethod
78     def compute(self) -> Any:
79         """
80         Computes the metric based on it's accumulated state.
81 
82         This is called at the end of each epoch.
83 
84         Returns:
85             Any: the actual quantity of interest.
86 
87         Raises:
88             NotComputableError: raised when the metric cannot be computed.
89         """
90         pass
91 
92     def _sync_all_reduce(self, tensor: Union[torch.Tensor, numbers.Number]) -> Union[torch.Tensor, numbers.Number]:
93         if not (dist.is_available() and dist.is_initialized()):
94             # Nothing to reduce
95             return tensor
96 
97         tensor_to_number = False
98         if isinstance(tensor, numbers.Number):
99             tensor = torch.tensor(tensor, device=self._device)
100             tensor_to_number = True
101 
102         if isinstance(tensor, torch.Tensor):
103             # check if the tensor is at specified device
104             if tensor.device != self._device:
105                 tensor = tensor.to(self._device)
106         else:
107             raise TypeError("Unhandled input type {}".format(type(tensor)))
108 
109         # synchronize and reduce
110         dist.barrier()
111         dist.all_reduce(tensor)
112 
113         if tensor_to_number:
114             return tensor.item()
115         return tensor
116 
117     def started(self, engine: Engine) -> None:
118         self.reset()
119 
120     @torch.no_grad()
121     def iteration_completed(self, engine: Engine) -> None:
122         output = self._output_transform(engine.state.output)
123         if isinstance(output, Mapping):
124             if self._required_output_keys is None:
125                 raise TypeError(
126                     "Transformed engine output for {} metric should be a tuple/list, but given {}".format(
127                         self.__class__.__name__, type(output)
128                     )
129                 )
130             if not all([k in output for k in self._required_output_keys]):
131                 raise ValueError(
132                     "When transformed engine's output is a mapping, "
133                     "it should contain {} keys, but given {}".format(self._required_output_keys, list(output.keys()))
134                 )
135             output = tuple(output[k] for k in self._required_output_keys)
136         self.update(output)
137 
138     def completed(self, engine: Engine, name: str) -> None:
139         result = self.compute()
140         if torch.is_tensor(result) and len(result.shape) == 0:
141             result = result.item()
142         engine.state.metrics[name] = result
143 
144     def attach(self, engine: Engine, name: str) -> None:
145         """
146         Attaches current metric to provided engine. On the end of engine's run,
147         `engine.state.metrics` dictionary will contain computed metric's value under provided name.
148 
149         Args:
150             engine (Engine): the engine to which the metric must be attached
151             name (str): the name of the metric to attach
152 
153         Example:
154 
155         .. code-block:: python
156 
157             metric = ...
158             metric.attach(engine, "mymetric")
159 
160             assert "mymetric" in engine.run(data).metrics
161 
162             assert metric.is_attached(engine)
163         """
164         engine.add_event_handler(Events.EPOCH_COMPLETED, self.completed, name)
165         if not engine.has_event_handler(self.started, Events.EPOCH_STARTED):
166             engine.add_event_handler(Events.EPOCH_STARTED, self.started)
167         if not engine.has_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED):
168             engine.add_event_handler(Events.ITERATION_COMPLETED, self.iteration_completed)
169 
170     def detach(self, engine: Engine) -> None:
171         """
172         Detaches current metric from the engine and no metric's computation is done during the run.
173         This method in conjunction with :meth:`~ignite.metrics.Metric.attach` can be useful if several
174         metrics need to be computed with different periods. For example, one metric is computed every training epoch
175         and another metric (e.g. more expensive one) is done every n-th training epoch.
176 
177         Args:
178             engine (Engine): the engine from which the metric must be detached
179 
180         Example:
181 
182         .. code-block:: python
183 
184             metric = ...
185             engine = ...
186             metric.detach(engine)
187 
188             assert "mymetric" not in engine.run(data).metrics
189 
190             assert not metric.is_attached(engine)
191         """
192         if engine.has_event_handler(self.completed, Events.EPOCH_COMPLETED):
193             engine.remove_event_handler(self.completed, Events.EPOCH_COMPLETED)
194         if engine.has_event_handler(self.started, Events.EPOCH_STARTED):
195             engine.remove_event_handler(self.started, Events.EPOCH_STARTED)
196         if engine.has_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED):
197             engine.remove_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED)
198 
199     def is_attached(self, engine: Engine) -> bool:
200         """
201         Checks if current metric is attached to provided engine. If attached, metric's computed
202         value is written to `engine.state.metrics` dictionary.
203 
204         Args:
205             engine (Engine): the engine checked from which the metric should be attached
206         """
207         return engine.has_event_handler(self.completed, Events.EPOCH_COMPLETED)
208 
209     def __add__(self, other):
210         from ignite.metrics import MetricsLambda
211 
212         return MetricsLambda(lambda x, y: x + y, self, other)
213 
214     def __radd__(self, other):
215         from ignite.metrics import MetricsLambda
216 
217         return MetricsLambda(lambda x, y: x + y, other, self)
218 
219     def __sub__(self, other):
220         from ignite.metrics import MetricsLambda
221 
222         return MetricsLambda(lambda x, y: x - y, self, other)
223 
224     def __rsub__(self, other):
225         from ignite.metrics import MetricsLambda
226 
227         return MetricsLambda(lambda x, y: x - y, other, self)
228 
229     def __mul__(self, other):
230         from ignite.metrics import MetricsLambda
231 
232         return MetricsLambda(lambda x, y: x * y, self, other)
233 
234     def __rmul__(self, other):
235         from ignite.metrics import MetricsLambda
236 
237         return MetricsLambda(lambda x, y: x * y, other, self)
238 
239     def __pow__(self, other):
240         from ignite.metrics import MetricsLambda
241 
242         return MetricsLambda(lambda x, y: x ** y, self, other)
243 
244     def __rpow__(self, other):
245         from ignite.metrics import MetricsLambda
246 
247         return MetricsLambda(lambda x, y: x ** y, other, self)
248 
249     def __mod__(self, other):
250         from ignite.metrics import MetricsLambda
251 
252         return MetricsLambda(lambda x, y: x % y, self, other)
253 
254     def __div__(self, other):
255         from ignite.metrics import MetricsLambda
256 
257         return MetricsLambda(lambda x, y: x.__div__(y), self, other)
258 
259     def __rdiv__(self, other):
260         from ignite.metrics import MetricsLambda
261 
262         return MetricsLambda(lambda x, y: x.__div__(y), other, self)
263 
264     def __truediv__(self, other):
265         from ignite.metrics import MetricsLambda
266 
267         return MetricsLambda(lambda x, y: x.__truediv__(y), self, other)
268 
269     def __rtruediv__(self, other):
270         from ignite.metrics import MetricsLambda
271 
272         return MetricsLambda(lambda x, y: x.__truediv__(y), other, self)
273 
274     def __floordiv__(self, other):
275         from ignite.metrics import MetricsLambda
276 
277         return MetricsLambda(lambda x, y: x // y, self, other)
278 
279     def __getattr__(self, attr: str) -> Callable:
280         from ignite.metrics import MetricsLambda
281 
282         def fn(x, *args, **kwargs):
283             return getattr(x, attr)(*args, **kwargs)
284 
285         def wrapper(*args, **kwargs):
286             return MetricsLambda(fn, self, *args, **kwargs)
287 
288         return wrapper
289 
290     def __getitem__(self, index: Any):
291         from ignite.metrics import MetricsLambda
292 
293         return MetricsLambda(lambda x: x[index], self)
294 
295 
296 def sync_all_reduce(*attrs) -> Callable:
297     def wrapper(func: Callable) -> Callable:
298         @wraps(func)
299         def another_wrapper(self: Metric, *args, **kwargs) -> Callable:
300             if not isinstance(self, Metric):
301                 raise RuntimeError(
302                     "Decorator sync_all_reduce should be used on " "ignite.metric.Metric class methods only"
303                 )
304 
305             if len(attrs) > 0 and not self._is_reduced:
306                 for attr in attrs:
307                     t = getattr(self, attr, None)
308                     if t is not None:
309                         t = self._sync_all_reduce(t)
310                         self._is_reduced = True
311                         setattr(self, attr, t)
312 
313             return func(self, *args, **kwargs)
314 
315         return another_wrapper
316 
317     wrapper._decorated = True
318     return wrapper
319 
320 
321 def reinit__is_reduced(func: Callable) -> Callable:
322     @wraps(func)
323     def wrapper(self, *args, **kwargs):
324         func(self, *args, **kwargs)
325         self._is_reduced = False
326 
327     wrapper._decorated = True
328     return wrapper
329
```
--- 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/metric.py b/ignite/metrics/metric.py
--- a/ignite/metrics/metric.py
+++ b/ignite/metrics/metric.py
@@ -292,6 +292,12 @@
 
         return MetricsLambda(lambda x: x[index], self)
 
+    def __getstate__(self):
+        return self.__dict__
+
+    def __setstate__(self, d):
+        self.__dict__.update(d)
+
 
 def sync_all_reduce(*attrs) -> Callable:
     def wrapper(func: Callable) -> Callable:

{"golden_diff": "diff --git a/ignite/metrics/metric.py b/ignite/metrics/metric.py\n--- a/ignite/metrics/metric.py\n+++ b/ignite/metrics/metric.py\n@@ -292,6 +292,12 @@\n \n         return MetricsLambda(lambda x: x[index], self)\n \n+    def __getstate__(self):\n+        return self.__dict__\n+\n+    def __setstate__(self, d):\n+        self.__dict__.update(d)\n+\n \n def sync_all_reduce(*attrs) -> Callable:\n     def wrapper(func: Callable) -> Callable:\n", "issue": "Metrics objects are not pickleable\nPickling Metrics objects fails due to infinite loop.\r\n\r\nThe reason for that is the following:\r\n\r\nTo make metrics composable, the base class Metric has [methods](https://github.com/pytorch/ignite/blob/master/ignite/metrics/metric.py#L86) to return a `MetricsLambda` object. However, this means that pickling `Metrics` requires pickling `MetricsLambda`, but since `MetricsLambda` depends on `Metrics` we enter an infinite loop.\r\n\r\n\n", "before_files": [{"content": "import numbers\nfrom abc import ABCMeta, abstractmethod\nfrom functools import wraps\nfrom collections.abc import Mapping\nimport warnings\n\nfrom typing import Callable, Union, Optional, Any\n\nimport torch\nimport torch.distributed as dist\n\nfrom ignite.engine import Events, Engine\n\n__all__ = [\"Metric\"]\n\n\nclass Metric(metaclass=ABCMeta):\n    \"\"\"\n    Base class for all Metrics.\n\n    Args:\n        output_transform (callable, optional): a callable that is used to transform the\n            :class:`~ignite.engine.Engine`'s `process_function`'s output into the\n            form expected by the metric. This can be useful if, for example, you have a multi-output model and\n            you want to compute the metric with respect to one of the outputs.\n            By default, metrics require the output as `(y_pred, y)` or `{'y_pred': y_pred, 'y': y}`.\n        device (str of torch.device, optional): device specification in case of distributed computation usage.\n            In most of the cases, it can be defined as \"cuda:local_rank\" or \"cuda\"\n            if already set `torch.cuda.set_device(local_rank)`. By default, if a distributed process group is\n            initialized and available, device is set to `cuda`.\n\n    \"\"\"\n\n    _required_output_keys = (\"y_pred\", \"y\")\n\n    def __init__(self, output_transform: Callable = lambda x: x, device: Optional[Union[str, torch.device]] = None):\n        self._output_transform = output_transform\n\n        # Check device if distributed is initialized:\n        if dist.is_available() and dist.is_initialized():\n\n            # check if reset and update methods are decorated. Compute may not be decorated\n            if not (hasattr(self.reset, \"_decorated\") and hasattr(self.update, \"_decorated\")):\n                warnings.warn(\n                    \"{} class does not support distributed setting. Computed result is not collected \"\n                    \"across all computing devices\".format(self.__class__.__name__),\n                    RuntimeWarning,\n                )\n            if device is None:\n                device = \"cuda\"\n            device = torch.device(device)\n        self._device = device\n        self._is_reduced = False\n        self.reset()\n\n    @abstractmethod\n    def reset(self) -> None:\n        \"\"\"\n        Resets the metric to it's initial state.\n\n        This is called at the start of each epoch.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def update(self, output) -> None:\n        \"\"\"\n        Updates the metric's state using the passed batch output.\n\n        This is called once for each batch.\n\n        Args:\n            output: the is the output from the engine's process function.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def compute(self) -> Any:\n        \"\"\"\n        Computes the metric based on it's accumulated state.\n\n        This is called at the end of each epoch.\n\n        Returns:\n            Any: the actual quantity of interest.\n\n        Raises:\n            NotComputableError: raised when the metric cannot be computed.\n        \"\"\"\n        pass\n\n    def _sync_all_reduce(self, tensor: Union[torch.Tensor, numbers.Number]) -> Union[torch.Tensor, numbers.Number]:\n        if not (dist.is_available() and dist.is_initialized()):\n            # Nothing to reduce\n            return tensor\n\n        tensor_to_number = False\n        if isinstance(tensor, numbers.Number):\n            tensor = torch.tensor(tensor, device=self._device)\n            tensor_to_number = True\n\n        if isinstance(tensor, torch.Tensor):\n            # check if the tensor is at specified device\n            if tensor.device != self._device:\n                tensor = tensor.to(self._device)\n        else:\n            raise TypeError(\"Unhandled input type {}\".format(type(tensor)))\n\n        # synchronize and reduce\n        dist.barrier()\n        dist.all_reduce(tensor)\n\n        if tensor_to_number:\n            return tensor.item()\n        return tensor\n\n    def started(self, engine: Engine) -> None:\n        self.reset()\n\n    @torch.no_grad()\n    def iteration_completed(self, engine: Engine) -> None:\n        output = self._output_transform(engine.state.output)\n        if isinstance(output, Mapping):\n            if self._required_output_keys is None:\n                raise TypeError(\n                    \"Transformed engine output for {} metric should be a tuple/list, but given {}\".format(\n                        self.__class__.__name__, type(output)\n                    )\n                )\n            if not all([k in output for k in self._required_output_keys]):\n                raise ValueError(\n                    \"When transformed engine's output is a mapping, \"\n                    \"it should contain {} keys, but given {}\".format(self._required_output_keys, list(output.keys()))\n                )\n            output = tuple(output[k] for k in self._required_output_keys)\n        self.update(output)\n\n    def completed(self, engine: Engine, name: str) -> None:\n        result = self.compute()\n        if torch.is_tensor(result) and len(result.shape) == 0:\n            result = result.item()\n        engine.state.metrics[name] = result\n\n    def attach(self, engine: Engine, name: str) -> None:\n        \"\"\"\n        Attaches current metric to provided engine. On the end of engine's run,\n        `engine.state.metrics` dictionary will contain computed metric's value under provided name.\n\n        Args:\n            engine (Engine): the engine to which the metric must be attached\n            name (str): the name of the metric to attach\n\n        Example:\n\n        .. code-block:: python\n\n            metric = ...\n            metric.attach(engine, \"mymetric\")\n\n            assert \"mymetric\" in engine.run(data).metrics\n\n            assert metric.is_attached(engine)\n        \"\"\"\n        engine.add_event_handler(Events.EPOCH_COMPLETED, self.completed, name)\n        if not engine.has_event_handler(self.started, Events.EPOCH_STARTED):\n            engine.add_event_handler(Events.EPOCH_STARTED, self.started)\n        if not engine.has_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED):\n            engine.add_event_handler(Events.ITERATION_COMPLETED, self.iteration_completed)\n\n    def detach(self, engine: Engine) -> None:\n        \"\"\"\n        Detaches current metric from the engine and no metric's computation is done during the run.\n        This method in conjunction with :meth:`~ignite.metrics.Metric.attach` can be useful if several\n        metrics need to be computed with different periods. For example, one metric is computed every training epoch\n        and another metric (e.g. more expensive one) is done every n-th training epoch.\n\n        Args:\n            engine (Engine): the engine from which the metric must be detached\n\n        Example:\n\n        .. code-block:: python\n\n            metric = ...\n            engine = ...\n            metric.detach(engine)\n\n            assert \"mymetric\" not in engine.run(data).metrics\n\n            assert not metric.is_attached(engine)\n        \"\"\"\n        if engine.has_event_handler(self.completed, Events.EPOCH_COMPLETED):\n            engine.remove_event_handler(self.completed, Events.EPOCH_COMPLETED)\n        if engine.has_event_handler(self.started, Events.EPOCH_STARTED):\n            engine.remove_event_handler(self.started, Events.EPOCH_STARTED)\n        if engine.has_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED):\n            engine.remove_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED)\n\n    def is_attached(self, engine: Engine) -> bool:\n        \"\"\"\n        Checks if current metric is attached to provided engine. If attached, metric's computed\n        value is written to `engine.state.metrics` dictionary.\n\n        Args:\n            engine (Engine): the engine checked from which the metric should be attached\n        \"\"\"\n        return engine.has_event_handler(self.completed, Events.EPOCH_COMPLETED)\n\n    def __add__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x + y, self, other)\n\n    def __radd__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x + y, other, self)\n\n    def __sub__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x - y, self, other)\n\n    def __rsub__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x - y, other, self)\n\n    def __mul__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x * y, self, other)\n\n    def __rmul__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x * y, other, self)\n\n    def __pow__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x ** y, self, other)\n\n    def __rpow__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x ** y, other, self)\n\n    def __mod__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x % y, self, other)\n\n    def __div__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x.__div__(y), self, other)\n\n    def __rdiv__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x.__div__(y), other, self)\n\n    def __truediv__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x.__truediv__(y), self, other)\n\n    def __rtruediv__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x.__truediv__(y), other, self)\n\n    def __floordiv__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x // y, self, other)\n\n    def __getattr__(self, attr: str) -> Callable:\n        from ignite.metrics import MetricsLambda\n\n        def fn(x, *args, **kwargs):\n            return getattr(x, attr)(*args, **kwargs)\n\n        def wrapper(*args, **kwargs):\n            return MetricsLambda(fn, self, *args, **kwargs)\n\n        return wrapper\n\n    def __getitem__(self, index: Any):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x: x[index], self)\n\n\ndef sync_all_reduce(*attrs) -> Callable:\n    def wrapper(func: Callable) -> Callable:\n        @wraps(func)\n        def another_wrapper(self: Metric, *args, **kwargs) -> Callable:\n            if not isinstance(self, Metric):\n                raise RuntimeError(\n                    \"Decorator sync_all_reduce should be used on \" \"ignite.metric.Metric class methods only\"\n                )\n\n            if len(attrs) > 0 and not self._is_reduced:\n                for attr in attrs:\n                    t = getattr(self, attr, None)\n                    if t is not None:\n                        t = self._sync_all_reduce(t)\n                        self._is_reduced = True\n                        setattr(self, attr, t)\n\n            return func(self, *args, **kwargs)\n\n        return another_wrapper\n\n    wrapper._decorated = True\n    return wrapper\n\n\ndef reinit__is_reduced(func: Callable) -> Callable:\n    @wraps(func)\n    def wrapper(self, *args, **kwargs):\n        func(self, *args, **kwargs)\n        self._is_reduced = False\n\n    wrapper._decorated = True\n    return wrapper\n", "path": "ignite/metrics/metric.py"}], "after_files": [{"content": "import numbers\nfrom abc import ABCMeta, abstractmethod\nfrom functools import wraps\nfrom collections.abc import Mapping\nimport warnings\n\nfrom typing import Callable, Union, Optional, Any\n\nimport torch\nimport torch.distributed as dist\n\nfrom ignite.engine import Events, Engine\n\n__all__ = [\"Metric\"]\n\n\nclass Metric(metaclass=ABCMeta):\n    \"\"\"\n    Base class for all Metrics.\n\n    Args:\n        output_transform (callable, optional): a callable that is used to transform the\n            :class:`~ignite.engine.Engine`'s `process_function`'s output into the\n            form expected by the metric. This can be useful if, for example, you have a multi-output model and\n            you want to compute the metric with respect to one of the outputs.\n            By default, metrics require the output as `(y_pred, y)` or `{'y_pred': y_pred, 'y': y}`.\n        device (str of torch.device, optional): device specification in case of distributed computation usage.\n            In most of the cases, it can be defined as \"cuda:local_rank\" or \"cuda\"\n            if already set `torch.cuda.set_device(local_rank)`. By default, if a distributed process group is\n            initialized and available, device is set to `cuda`.\n\n    \"\"\"\n\n    _required_output_keys = (\"y_pred\", \"y\")\n\n    def __init__(self, output_transform: Callable = lambda x: x, device: Optional[Union[str, torch.device]] = None):\n        self._output_transform = output_transform\n\n        # Check device if distributed is initialized:\n        if dist.is_available() and dist.is_initialized():\n\n            # check if reset and update methods are decorated. Compute may not be decorated\n            if not (hasattr(self.reset, \"_decorated\") and hasattr(self.update, \"_decorated\")):\n                warnings.warn(\n                    \"{} class does not support distributed setting. Computed result is not collected \"\n                    \"across all computing devices\".format(self.__class__.__name__),\n                    RuntimeWarning,\n                )\n            if device is None:\n                device = \"cuda\"\n            device = torch.device(device)\n        self._device = device\n        self._is_reduced = False\n        self.reset()\n\n    @abstractmethod\n    def reset(self) -> None:\n        \"\"\"\n        Resets the metric to it's initial state.\n\n        This is called at the start of each epoch.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def update(self, output) -> None:\n        \"\"\"\n        Updates the metric's state using the passed batch output.\n\n        This is called once for each batch.\n\n        Args:\n            output: the is the output from the engine's process function.\n        \"\"\"\n        pass\n\n    @abstractmethod\n    def compute(self) -> Any:\n        \"\"\"\n        Computes the metric based on it's accumulated state.\n\n        This is called at the end of each epoch.\n\n        Returns:\n            Any: the actual quantity of interest.\n\n        Raises:\n            NotComputableError: raised when the metric cannot be computed.\n        \"\"\"\n        pass\n\n    def _sync_all_reduce(self, tensor: Union[torch.Tensor, numbers.Number]) -> Union[torch.Tensor, numbers.Number]:\n        if not (dist.is_available() and dist.is_initialized()):\n            # Nothing to reduce\n            return tensor\n\n        tensor_to_number = False\n        if isinstance(tensor, numbers.Number):\n            tensor = torch.tensor(tensor, device=self._device)\n            tensor_to_number = True\n\n        if isinstance(tensor, torch.Tensor):\n            # check if the tensor is at specified device\n            if tensor.device != self._device:\n                tensor = tensor.to(self._device)\n        else:\n            raise TypeError(\"Unhandled input type {}\".format(type(tensor)))\n\n        # synchronize and reduce\n        dist.barrier()\n        dist.all_reduce(tensor)\n\n        if tensor_to_number:\n            return tensor.item()\n        return tensor\n\n    def started(self, engine: Engine) -> None:\n        self.reset()\n\n    @torch.no_grad()\n    def iteration_completed(self, engine: Engine) -> None:\n        output = self._output_transform(engine.state.output)\n        if isinstance(output, Mapping):\n            if self._required_output_keys is None:\n                raise TypeError(\n                    \"Transformed engine output for {} metric should be a tuple/list, but given {}\".format(\n                        self.__class__.__name__, type(output)\n                    )\n                )\n            if not all([k in output for k in self._required_output_keys]):\n                raise ValueError(\n                    \"When transformed engine's output is a mapping, \"\n                    \"it should contain {} keys, but given {}\".format(self._required_output_keys, list(output.keys()))\n                )\n            output = tuple(output[k] for k in self._required_output_keys)\n        self.update(output)\n\n    def completed(self, engine: Engine, name: str) -> None:\n        result = self.compute()\n        if torch.is_tensor(result) and len(result.shape) == 0:\n            result = result.item()\n        engine.state.metrics[name] = result\n\n    def attach(self, engine: Engine, name: str) -> None:\n        \"\"\"\n        Attaches current metric to provided engine. On the end of engine's run,\n        `engine.state.metrics` dictionary will contain computed metric's value under provided name.\n\n        Args:\n            engine (Engine): the engine to which the metric must be attached\n            name (str): the name of the metric to attach\n\n        Example:\n\n        .. code-block:: python\n\n            metric = ...\n            metric.attach(engine, \"mymetric\")\n\n            assert \"mymetric\" in engine.run(data).metrics\n\n            assert metric.is_attached(engine)\n        \"\"\"\n        engine.add_event_handler(Events.EPOCH_COMPLETED, self.completed, name)\n        if not engine.has_event_handler(self.started, Events.EPOCH_STARTED):\n            engine.add_event_handler(Events.EPOCH_STARTED, self.started)\n        if not engine.has_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED):\n            engine.add_event_handler(Events.ITERATION_COMPLETED, self.iteration_completed)\n\n    def detach(self, engine: Engine) -> None:\n        \"\"\"\n        Detaches current metric from the engine and no metric's computation is done during the run.\n        This method in conjunction with :meth:`~ignite.metrics.Metric.attach` can be useful if several\n        metrics need to be computed with different periods. For example, one metric is computed every training epoch\n        and another metric (e.g. more expensive one) is done every n-th training epoch.\n\n        Args:\n            engine (Engine): the engine from which the metric must be detached\n\n        Example:\n\n        .. code-block:: python\n\n            metric = ...\n            engine = ...\n            metric.detach(engine)\n\n            assert \"mymetric\" not in engine.run(data).metrics\n\n            assert not metric.is_attached(engine)\n        \"\"\"\n        if engine.has_event_handler(self.completed, Events.EPOCH_COMPLETED):\n            engine.remove_event_handler(self.completed, Events.EPOCH_COMPLETED)\n        if engine.has_event_handler(self.started, Events.EPOCH_STARTED):\n            engine.remove_event_handler(self.started, Events.EPOCH_STARTED)\n        if engine.has_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED):\n            engine.remove_event_handler(self.iteration_completed, Events.ITERATION_COMPLETED)\n\n    def is_attached(self, engine: Engine) -> bool:\n        \"\"\"\n        Checks if current metric is attached to provided engine. If attached, metric's computed\n        value is written to `engine.state.metrics` dictionary.\n\n        Args:\n            engine (Engine): the engine checked from which the metric should be attached\n        \"\"\"\n        return engine.has_event_handler(self.completed, Events.EPOCH_COMPLETED)\n\n    def __add__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x + y, self, other)\n\n    def __radd__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x + y, other, self)\n\n    def __sub__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x - y, self, other)\n\n    def __rsub__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x - y, other, self)\n\n    def __mul__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x * y, self, other)\n\n    def __rmul__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x * y, other, self)\n\n    def __pow__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x ** y, self, other)\n\n    def __rpow__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x ** y, other, self)\n\n    def __mod__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x % y, self, other)\n\n    def __div__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x.__div__(y), self, other)\n\n    def __rdiv__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x.__div__(y), other, self)\n\n    def __truediv__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x.__truediv__(y), self, other)\n\n    def __rtruediv__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x.__truediv__(y), other, self)\n\n    def __floordiv__(self, other):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x, y: x // y, self, other)\n\n    def __getattr__(self, attr: str) -> Callable:\n        from ignite.metrics import MetricsLambda\n\n        def fn(x, *args, **kwargs):\n            return getattr(x, attr)(*args, **kwargs)\n\n        def wrapper(*args, **kwargs):\n            return MetricsLambda(fn, self, *args, **kwargs)\n\n        return wrapper\n\n    def __getitem__(self, index: Any):\n        from ignite.metrics import MetricsLambda\n\n        return MetricsLambda(lambda x: x[index], self)\n\n    def __getstate__(self):\n        return self.__dict__\n\n    def __setstate__(self, d):\n        self.__dict__.update(d)\n\n\ndef sync_all_reduce(*attrs) -> Callable:\n    def wrapper(func: Callable) -> Callable:\n        @wraps(func)\n        def another_wrapper(self: Metric, *args, **kwargs) -> Callable:\n            if not isinstance(self, Metric):\n                raise RuntimeError(\n                    \"Decorator sync_all_reduce should be used on \" \"ignite.metric.Metric class methods only\"\n                )\n\n            if len(attrs) > 0 and not self._is_reduced:\n                for attr in attrs:\n                    t = getattr(self, attr, None)\n                    if t is not None:\n                        t = self._sync_all_reduce(t)\n                        self._is_reduced = True\n                        setattr(self, attr, t)\n\n            return func(self, *args, **kwargs)\n\n        return another_wrapper\n\n    wrapper._decorated = True\n    return wrapper\n\n\ndef reinit__is_reduced(func: Callable) -> Callable:\n    @wraps(func)\n    def wrapper(self, *args, **kwargs):\n        func(self, *args, **kwargs)\n        self._is_reduced = False\n\n    wrapper._decorated = True\n    return wrapper\n", "path": "ignite/metrics/metric.py"}]}