We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Make `MetricsLambda` update it's underlying metrics
## 🚀 Feature

As title.

The current state is that we cannot use `MetricsLambda` as a standard metric because it doesn't update/reset it's underlying metrics.
Adding this will expand the scope and usability of `MetricsLambda`

I've already an idea how to implement this based on our discussion https://github.com/pytorch/ignite/pull/1887#discussion_r610902667

So @vfdev-5 please assign the issue to me.



--- 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/metrics_lambda.py`
Content:
```
1 import itertools
2 from typing import Any, Callable, Optional, Union
3 
4 import torch
5 
6 from ignite.engine import Engine
7 from ignite.metrics.metric import EpochWise, Metric, MetricUsage, reinit__is_reduced
8 
9 __all__ = ["MetricsLambda"]
10 
11 
12 class MetricsLambda(Metric):
13     """
14     Apply a function to other metrics to obtain a new metric.
15     The result of the new metric is defined to be the result
16     of applying the function to the result of argument metrics.
17 
18     When update, this metric does not recursively update the metrics
19     it depends on. When reset, all its dependency metrics would be
20     resetted. When attach, all its dependency metrics would be attached
21     automatically (but partially, e.g :meth:`~ignite.metrics.metric.Metric.is_attached()` will return False).
22 
23     Args:
24         f: the function that defines the computation
25         args: Sequence of other metrics or something
26             else that will be fed to ``f`` as arguments.
27         kwargs: Sequence of other metrics or something
28             else that will be fed to ``f`` as keyword arguments.
29 
30     Example:
31 
32     .. code-block:: python
33 
34         precision = Precision(average=False)
35         recall = Recall(average=False)
36 
37         def Fbeta(r, p, beta):
38             return torch.mean((1 + beta ** 2) * p * r / (beta ** 2 * p + r + 1e-20)).item()
39 
40         F1 = MetricsLambda(Fbeta, recall, precision, 1)
41         F2 = MetricsLambda(Fbeta, recall, precision, 2)
42         F3 = MetricsLambda(Fbeta, recall, precision, 3)
43         F4 = MetricsLambda(Fbeta, recall, precision, 4)
44 
45     When check if the metric is attached, if one of its dependency
46     metrics is detached, the metric is considered detached too.
47 
48     .. code-block:: python
49 
50         engine = ...
51         precision = Precision(average=False)
52 
53         aP = precision.mean()
54 
55         aP.attach(engine, "aP")
56 
57         assert aP.is_attached(engine)
58         # partially attached
59         assert not precision.is_attached(engine)
60 
61         precision.detach(engine)
62 
63         assert not aP.is_attached(engine)
64         # fully attached
65         assert not precision.is_attached(engine)
66 
67     """
68 
69     def __init__(self, f: Callable, *args: Any, **kwargs: Any) -> None:
70         self.function = f
71         self.args = args
72         self.kwargs = kwargs
73         self.engine = None  # type: Optional[Engine]
74         super(MetricsLambda, self).__init__(device="cpu")
75 
76     @reinit__is_reduced
77     def reset(self) -> None:
78         for i in itertools.chain(self.args, self.kwargs.values()):
79             if isinstance(i, Metric):
80                 i.reset()
81 
82     @reinit__is_reduced
83     def update(self, output: Any) -> None:
84         # NB: this method does not recursively update dependency metrics,
85         # which might cause duplicate update issue. To update this metric,
86         # users should manually update its dependencies.
87         pass
88 
89     def compute(self) -> Any:
90         materialized = [_get_value_on_cpu(i) for i in self.args]
91         materialized_kwargs = {k: _get_value_on_cpu(v) for k, v in self.kwargs.items()}
92         return self.function(*materialized, **materialized_kwargs)
93 
94     def _internal_attach(self, engine: Engine, usage: MetricUsage) -> None:
95         self.engine = engine
96         for index, metric in enumerate(itertools.chain(self.args, self.kwargs.values())):
97             if isinstance(metric, MetricsLambda):
98                 metric._internal_attach(engine, usage)
99             elif isinstance(metric, Metric):
100                 # NB : metrics is attached partially
101                 # We must not use is_attached() but rather if these events exist
102                 if not engine.has_event_handler(metric.started, usage.STARTED):
103                     engine.add_event_handler(usage.STARTED, metric.started)
104                 if not engine.has_event_handler(metric.iteration_completed, usage.ITERATION_COMPLETED):
105                     engine.add_event_handler(usage.ITERATION_COMPLETED, metric.iteration_completed)
106 
107     def attach(self, engine: Engine, name: str, usage: Union[str, MetricUsage] = EpochWise()) -> None:
108         usage = self._check_usage(usage)
109         # recursively attach all its dependencies (partially)
110         self._internal_attach(engine, usage)
111         # attach only handler on EPOCH_COMPLETED
112         engine.add_event_handler(usage.COMPLETED, self.completed, name)
113 
114     def detach(self, engine: Engine, usage: Union[str, MetricUsage] = EpochWise()) -> None:
115         usage = self._check_usage(usage)
116         # remove from engine
117         super(MetricsLambda, self).detach(engine, usage)
118         self.engine = None
119 
120     def is_attached(self, engine: Engine, usage: Union[str, MetricUsage] = EpochWise()) -> bool:
121         usage = self._check_usage(usage)
122         # check recursively the dependencies
123         return super(MetricsLambda, self).is_attached(engine, usage) and self._internal_is_attached(engine, usage)
124 
125     def _internal_is_attached(self, engine: Engine, usage: MetricUsage) -> bool:
126         # if no engine, metrics is not attached
127         if engine is None:
128             return False
129         # check recursively if metrics are attached
130         is_detached = False
131         for metric in itertools.chain(self.args, self.kwargs.values()):
132             if isinstance(metric, MetricsLambda):
133                 if not metric._internal_is_attached(engine, usage):
134                     is_detached = True
135             elif isinstance(metric, Metric):
136                 if not engine.has_event_handler(metric.started, usage.STARTED):
137                     is_detached = True
138                 if not engine.has_event_handler(metric.iteration_completed, usage.ITERATION_COMPLETED):
139                     is_detached = True
140         return not is_detached
141 
142 
143 def _get_value_on_cpu(v: Any) -> Any:
144     if isinstance(v, Metric):
145         v = v.compute()
146     if isinstance(v, torch.Tensor):
147         v = v.cpu()
148     return v
149
```
--- 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/metrics_lambda.py b/ignite/metrics/metrics_lambda.py
--- a/ignite/metrics/metrics_lambda.py
+++ b/ignite/metrics/metrics_lambda.py
@@ -15,9 +15,9 @@
     The result of the new metric is defined to be the result
     of applying the function to the result of argument metrics.
 
-    When update, this metric does not recursively update the metrics
+    When update, this metric recursively updates the metrics
     it depends on. When reset, all its dependency metrics would be
-    resetted. When attach, all its dependency metrics would be attached
+    resetted as well. When attach, all its dependency metrics would be attached
     automatically (but partially, e.g :meth:`~ignite.metrics.metric.Metric.is_attached()` will return False).
 
     Args:
@@ -71,6 +71,7 @@
         self.args = args
         self.kwargs = kwargs
         self.engine = None  # type: Optional[Engine]
+        self._updated = False
         super(MetricsLambda, self).__init__(device="cpu")
 
     @reinit__is_reduced
@@ -78,13 +79,21 @@
         for i in itertools.chain(self.args, self.kwargs.values()):
             if isinstance(i, Metric):
                 i.reset()
+        self._updated = False
 
     @reinit__is_reduced
     def update(self, output: Any) -> None:
-        # NB: this method does not recursively update dependency metrics,
-        # which might cause duplicate update issue. To update this metric,
-        # users should manually update its dependencies.
-        pass
+        if self.engine:
+            raise ValueError(
+                "MetricsLambda is already attached to an engine, "
+                "and MetricsLambda can't use update API while it's attached."
+            )
+
+        for i in itertools.chain(self.args, self.kwargs.values()):
+            if isinstance(i, Metric):
+                i.update(output)
+
+        self._updated = True
 
     def compute(self) -> Any:
         materialized = [_get_value_on_cpu(i) for i in self.args]
@@ -105,6 +114,10 @@
                     engine.add_event_handler(usage.ITERATION_COMPLETED, metric.iteration_completed)
 
     def attach(self, engine: Engine, name: str, usage: Union[str, MetricUsage] = EpochWise()) -> None:
+        if self._updated:
+            raise ValueError(
+                "The underlying metrics are already updated, can't attach while using reset/update/compute API."
+            )
         usage = self._check_usage(usage)
         # recursively attach all its dependencies (partially)
         self._internal_attach(engine, usage)

{"golden_diff": "diff --git a/ignite/metrics/metrics_lambda.py b/ignite/metrics/metrics_lambda.py\n--- a/ignite/metrics/metrics_lambda.py\n+++ b/ignite/metrics/metrics_lambda.py\n@@ -15,9 +15,9 @@\n     The result of the new metric is defined to be the result\n     of applying the function to the result of argument metrics.\n \n-    When update, this metric does not recursively update the metrics\n+    When update, this metric recursively updates the metrics\n     it depends on. When reset, all its dependency metrics would be\n-    resetted. When attach, all its dependency metrics would be attached\n+    resetted as well. When attach, all its dependency metrics would be attached\n     automatically (but partially, e.g :meth:`~ignite.metrics.metric.Metric.is_attached()` will return False).\n \n     Args:\n@@ -71,6 +71,7 @@\n         self.args = args\n         self.kwargs = kwargs\n         self.engine = None  # type: Optional[Engine]\n+        self._updated = False\n         super(MetricsLambda, self).__init__(device=\"cpu\")\n \n     @reinit__is_reduced\n@@ -78,13 +79,21 @@\n         for i in itertools.chain(self.args, self.kwargs.values()):\n             if isinstance(i, Metric):\n                 i.reset()\n+        self._updated = False\n \n     @reinit__is_reduced\n     def update(self, output: Any) -> None:\n-        # NB: this method does not recursively update dependency metrics,\n-        # which might cause duplicate update issue. To update this metric,\n-        # users should manually update its dependencies.\n-        pass\n+        if self.engine:\n+            raise ValueError(\n+                \"MetricsLambda is already attached to an engine, \"\n+                \"and MetricsLambda can't use update API while it's attached.\"\n+            )\n+\n+        for i in itertools.chain(self.args, self.kwargs.values()):\n+            if isinstance(i, Metric):\n+                i.update(output)\n+\n+        self._updated = True\n \n     def compute(self) -> Any:\n         materialized = [_get_value_on_cpu(i) for i in self.args]\n@@ -105,6 +114,10 @@\n                     engine.add_event_handler(usage.ITERATION_COMPLETED, metric.iteration_completed)\n \n     def attach(self, engine: Engine, name: str, usage: Union[str, MetricUsage] = EpochWise()) -> None:\n+        if self._updated:\n+            raise ValueError(\n+                \"The underlying metrics are already updated, can't attach while using reset/update/compute API.\"\n+            )\n         usage = self._check_usage(usage)\n         # recursively attach all its dependencies (partially)\n         self._internal_attach(engine, usage)\n", "issue": "Make `MetricsLambda` update it's underlying metrics\n## \ud83d\ude80 Feature\r\n\r\nAs title.\r\n\r\nThe current state is that we cannot use `MetricsLambda` as a standard metric because it doesn't update/reset it's underlying metrics.\r\nAdding this will expand the scope and usability of `MetricsLambda`\r\n\r\nI've already an idea how to implement this based on our discussion https://github.com/pytorch/ignite/pull/1887#discussion_r610902667\r\n\r\nSo @vfdev-5 please assign the issue to me.\r\n\r\n\n", "before_files": [{"content": "import itertools\nfrom typing import Any, Callable, Optional, Union\n\nimport torch\n\nfrom ignite.engine import Engine\nfrom ignite.metrics.metric import EpochWise, Metric, MetricUsage, reinit__is_reduced\n\n__all__ = [\"MetricsLambda\"]\n\n\nclass MetricsLambda(Metric):\n    \"\"\"\n    Apply a function to other metrics to obtain a new metric.\n    The result of the new metric is defined to be the result\n    of applying the function to the result of argument metrics.\n\n    When update, this metric does not recursively update the metrics\n    it depends on. When reset, all its dependency metrics would be\n    resetted. When attach, all its dependency metrics would be attached\n    automatically (but partially, e.g :meth:`~ignite.metrics.metric.Metric.is_attached()` will return False).\n\n    Args:\n        f: the function that defines the computation\n        args: Sequence of other metrics or something\n            else that will be fed to ``f`` as arguments.\n        kwargs: Sequence of other metrics or something\n            else that will be fed to ``f`` as keyword arguments.\n\n    Example:\n\n    .. code-block:: python\n\n        precision = Precision(average=False)\n        recall = Recall(average=False)\n\n        def Fbeta(r, p, beta):\n            return torch.mean((1 + beta ** 2) * p * r / (beta ** 2 * p + r + 1e-20)).item()\n\n        F1 = MetricsLambda(Fbeta, recall, precision, 1)\n        F2 = MetricsLambda(Fbeta, recall, precision, 2)\n        F3 = MetricsLambda(Fbeta, recall, precision, 3)\n        F4 = MetricsLambda(Fbeta, recall, precision, 4)\n\n    When check if the metric is attached, if one of its dependency\n    metrics is detached, the metric is considered detached too.\n\n    .. code-block:: python\n\n        engine = ...\n        precision = Precision(average=False)\n\n        aP = precision.mean()\n\n        aP.attach(engine, \"aP\")\n\n        assert aP.is_attached(engine)\n        # partially attached\n        assert not precision.is_attached(engine)\n\n        precision.detach(engine)\n\n        assert not aP.is_attached(engine)\n        # fully attached\n        assert not precision.is_attached(engine)\n\n    \"\"\"\n\n    def __init__(self, f: Callable, *args: Any, **kwargs: Any) -> None:\n        self.function = f\n        self.args = args\n        self.kwargs = kwargs\n        self.engine = None  # type: Optional[Engine]\n        super(MetricsLambda, self).__init__(device=\"cpu\")\n\n    @reinit__is_reduced\n    def reset(self) -> None:\n        for i in itertools.chain(self.args, self.kwargs.values()):\n            if isinstance(i, Metric):\n                i.reset()\n\n    @reinit__is_reduced\n    def update(self, output: Any) -> None:\n        # NB: this method does not recursively update dependency metrics,\n        # which might cause duplicate update issue. To update this metric,\n        # users should manually update its dependencies.\n        pass\n\n    def compute(self) -> Any:\n        materialized = [_get_value_on_cpu(i) for i in self.args]\n        materialized_kwargs = {k: _get_value_on_cpu(v) for k, v in self.kwargs.items()}\n        return self.function(*materialized, **materialized_kwargs)\n\n    def _internal_attach(self, engine: Engine, usage: MetricUsage) -> None:\n        self.engine = engine\n        for index, metric in enumerate(itertools.chain(self.args, self.kwargs.values())):\n            if isinstance(metric, MetricsLambda):\n                metric._internal_attach(engine, usage)\n            elif isinstance(metric, Metric):\n                # NB : metrics is attached partially\n                # We must not use is_attached() but rather if these events exist\n                if not engine.has_event_handler(metric.started, usage.STARTED):\n                    engine.add_event_handler(usage.STARTED, metric.started)\n                if not engine.has_event_handler(metric.iteration_completed, usage.ITERATION_COMPLETED):\n                    engine.add_event_handler(usage.ITERATION_COMPLETED, metric.iteration_completed)\n\n    def attach(self, engine: Engine, name: str, usage: Union[str, MetricUsage] = EpochWise()) -> None:\n        usage = self._check_usage(usage)\n        # recursively attach all its dependencies (partially)\n        self._internal_attach(engine, usage)\n        # attach only handler on EPOCH_COMPLETED\n        engine.add_event_handler(usage.COMPLETED, self.completed, name)\n\n    def detach(self, engine: Engine, usage: Union[str, MetricUsage] = EpochWise()) -> None:\n        usage = self._check_usage(usage)\n        # remove from engine\n        super(MetricsLambda, self).detach(engine, usage)\n        self.engine = None\n\n    def is_attached(self, engine: Engine, usage: Union[str, MetricUsage] = EpochWise()) -> bool:\n        usage = self._check_usage(usage)\n        # check recursively the dependencies\n        return super(MetricsLambda, self).is_attached(engine, usage) and self._internal_is_attached(engine, usage)\n\n    def _internal_is_attached(self, engine: Engine, usage: MetricUsage) -> bool:\n        # if no engine, metrics is not attached\n        if engine is None:\n            return False\n        # check recursively if metrics are attached\n        is_detached = False\n        for metric in itertools.chain(self.args, self.kwargs.values()):\n            if isinstance(metric, MetricsLambda):\n                if not metric._internal_is_attached(engine, usage):\n                    is_detached = True\n            elif isinstance(metric, Metric):\n                if not engine.has_event_handler(metric.started, usage.STARTED):\n                    is_detached = True\n                if not engine.has_event_handler(metric.iteration_completed, usage.ITERATION_COMPLETED):\n                    is_detached = True\n        return not is_detached\n\n\ndef _get_value_on_cpu(v: Any) -> Any:\n    if isinstance(v, Metric):\n        v = v.compute()\n    if isinstance(v, torch.Tensor):\n        v = v.cpu()\n    return v\n", "path": "ignite/metrics/metrics_lambda.py"}], "after_files": [{"content": "import itertools\nfrom typing import Any, Callable, Optional, Union\n\nimport torch\n\nfrom ignite.engine import Engine\nfrom ignite.metrics.metric import EpochWise, Metric, MetricUsage, reinit__is_reduced\n\n__all__ = [\"MetricsLambda\"]\n\n\nclass MetricsLambda(Metric):\n    \"\"\"\n    Apply a function to other metrics to obtain a new metric.\n    The result of the new metric is defined to be the result\n    of applying the function to the result of argument metrics.\n\n    When update, this metric recursively updates the metrics\n    it depends on. When reset, all its dependency metrics would be\n    resetted as well. When attach, all its dependency metrics would be attached\n    automatically (but partially, e.g :meth:`~ignite.metrics.metric.Metric.is_attached()` will return False).\n\n    Args:\n        f: the function that defines the computation\n        args: Sequence of other metrics or something\n            else that will be fed to ``f`` as arguments.\n        kwargs: Sequence of other metrics or something\n            else that will be fed to ``f`` as keyword arguments.\n\n    Example:\n\n    .. code-block:: python\n\n        precision = Precision(average=False)\n        recall = Recall(average=False)\n\n        def Fbeta(r, p, beta):\n            return torch.mean((1 + beta ** 2) * p * r / (beta ** 2 * p + r + 1e-20)).item()\n\n        F1 = MetricsLambda(Fbeta, recall, precision, 1)\n        F2 = MetricsLambda(Fbeta, recall, precision, 2)\n        F3 = MetricsLambda(Fbeta, recall, precision, 3)\n        F4 = MetricsLambda(Fbeta, recall, precision, 4)\n\n    When check if the metric is attached, if one of its dependency\n    metrics is detached, the metric is considered detached too.\n\n    .. code-block:: python\n\n        engine = ...\n        precision = Precision(average=False)\n\n        aP = precision.mean()\n\n        aP.attach(engine, \"aP\")\n\n        assert aP.is_attached(engine)\n        # partially attached\n        assert not precision.is_attached(engine)\n\n        precision.detach(engine)\n\n        assert not aP.is_attached(engine)\n        # fully attached\n        assert not precision.is_attached(engine)\n\n    \"\"\"\n\n    def __init__(self, f: Callable, *args: Any, **kwargs: Any) -> None:\n        self.function = f\n        self.args = args\n        self.kwargs = kwargs\n        self.engine = None  # type: Optional[Engine]\n        self._updated = False\n        super(MetricsLambda, self).__init__(device=\"cpu\")\n\n    @reinit__is_reduced\n    def reset(self) -> None:\n        for i in itertools.chain(self.args, self.kwargs.values()):\n            if isinstance(i, Metric):\n                i.reset()\n        self._updated = False\n\n    @reinit__is_reduced\n    def update(self, output: Any) -> None:\n        if self.engine:\n            raise ValueError(\n                \"MetricsLambda is already attached to an engine, \"\n                \"and MetricsLambda can't use update API while it's attached.\"\n            )\n\n        for i in itertools.chain(self.args, self.kwargs.values()):\n            if isinstance(i, Metric):\n                i.update(output)\n\n        self._updated = True\n\n    def compute(self) -> Any:\n        materialized = [_get_value_on_cpu(i) for i in self.args]\n        materialized_kwargs = {k: _get_value_on_cpu(v) for k, v in self.kwargs.items()}\n        return self.function(*materialized, **materialized_kwargs)\n\n    def _internal_attach(self, engine: Engine, usage: MetricUsage) -> None:\n        self.engine = engine\n        for index, metric in enumerate(itertools.chain(self.args, self.kwargs.values())):\n            if isinstance(metric, MetricsLambda):\n                metric._internal_attach(engine, usage)\n            elif isinstance(metric, Metric):\n                # NB : metrics is attached partially\n                # We must not use is_attached() but rather if these events exist\n                if not engine.has_event_handler(metric.started, usage.STARTED):\n                    engine.add_event_handler(usage.STARTED, metric.started)\n                if not engine.has_event_handler(metric.iteration_completed, usage.ITERATION_COMPLETED):\n                    engine.add_event_handler(usage.ITERATION_COMPLETED, metric.iteration_completed)\n\n    def attach(self, engine: Engine, name: str, usage: Union[str, MetricUsage] = EpochWise()) -> None:\n        if self._updated:\n            raise ValueError(\n                \"The underlying metrics are already updated, can't attach while using reset/update/compute API.\"\n            )\n        usage = self._check_usage(usage)\n        # recursively attach all its dependencies (partially)\n        self._internal_attach(engine, usage)\n        # attach only handler on EPOCH_COMPLETED\n        engine.add_event_handler(usage.COMPLETED, self.completed, name)\n\n    def detach(self, engine: Engine, usage: Union[str, MetricUsage] = EpochWise()) -> None:\n        usage = self._check_usage(usage)\n        # remove from engine\n        super(MetricsLambda, self).detach(engine, usage)\n        self.engine = None\n\n    def is_attached(self, engine: Engine, usage: Union[str, MetricUsage] = EpochWise()) -> bool:\n        usage = self._check_usage(usage)\n        # check recursively the dependencies\n        return super(MetricsLambda, self).is_attached(engine, usage) and self._internal_is_attached(engine, usage)\n\n    def _internal_is_attached(self, engine: Engine, usage: MetricUsage) -> bool:\n        # if no engine, metrics is not attached\n        if engine is None:\n            return False\n        # check recursively if metrics are attached\n        is_detached = False\n        for metric in itertools.chain(self.args, self.kwargs.values()):\n            if isinstance(metric, MetricsLambda):\n                if not metric._internal_is_attached(engine, usage):\n                    is_detached = True\n            elif isinstance(metric, Metric):\n                if not engine.has_event_handler(metric.started, usage.STARTED):\n                    is_detached = True\n                if not engine.has_event_handler(metric.iteration_completed, usage.ITERATION_COMPLETED):\n                    is_detached = True\n        return not is_detached\n\n\ndef _get_value_on_cpu(v: Any) -> Any:\n    if isinstance(v, Metric):\n        v = v.compute()\n    if isinstance(v, torch.Tensor):\n        v = v.cpu()\n    return v\n", "path": "ignite/metrics/metrics_lambda.py"}]}