We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
MetricCollection should enforce order when passed a dict
## 🐛 Bug

Not a clear bug yet, but just thinking about distributed metric computation, in here: https://github.com/PyTorchLightning/metrics/blob/53d570158a503497351ae45ec895ca44a0546068/torchmetrics/collections.py#L81
we should make sure to sort the names before the insert so that we insert things in the same order (ModuleDict is already OrderedDict otherwise). If we don't we will get deadlocks when doing distributed metric updates.

Additionally, we might want to enforce sorting when passed list/tuple, but that might be more on the user end.


### To Reproduce

On each of the workers, pass dictionary with same metrics but in different order, try compute and observe deadlock.



--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `torchmetrics/collections.py`
Content:
```
1 # Copyright The PyTorch Lightning team.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #     http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 
15 from copy import deepcopy
16 from typing import Any, Dict, List, Optional, Tuple, Union
17 
18 from torch import nn
19 
20 from torchmetrics.metric import Metric
21 
22 
23 class MetricCollection(nn.ModuleDict):
24     """
25     MetricCollection class can be used to chain metrics that have the same
26     call pattern into one single class.
27 
28     Args:
29         metrics: One of the following
30 
31             * list or tuple: if metrics are passed in as a list, will use the
32               metrics class name as key for output dict. Therefore, two metrics
33               of the same class cannot be chained this way.
34 
35             * dict: if metrics are passed in as a dict, will use each key in the
36               dict as key for output dict. Use this format if you want to chain
37               together multiple of the same metric with different parameters.
38 
39         prefix: a string to append in front of the keys of the output dict
40 
41     Raises:
42         ValueError:
43             If one of the elements of ``metrics`` is not an instance of ``pl.metrics.Metric``.
44         ValueError:
45             If two elements in ``metrics`` have the same ``name``.
46         ValueError:
47             If ``metrics`` is not a ``list``, ``tuple`` or a ``dict``.
48 
49     Example (input as list):
50         >>> import torch
51         >>> from pprint import pprint
52         >>> from torchmetrics import MetricCollection, Accuracy, Precision, Recall
53         >>> target = torch.tensor([0, 2, 0, 2, 0, 1, 0, 2])
54         >>> preds = torch.tensor([2, 1, 2, 0, 1, 2, 2, 2])
55         >>> metrics = MetricCollection([Accuracy(),
56         ...                             Precision(num_classes=3, average='macro'),
57         ...                             Recall(num_classes=3, average='macro')])
58         >>> metrics(preds, target)
59         {'Accuracy': tensor(0.1250), 'Precision': tensor(0.0667), 'Recall': tensor(0.1111)}
60 
61     Example (input as dict):
62         >>> metrics = MetricCollection({'micro_recall': Recall(num_classes=3, average='micro'),
63         ...                             'macro_recall': Recall(num_classes=3, average='macro')})
64         >>> same_metric = metrics.clone()
65         >>> pprint(metrics(preds, target))
66         {'macro_recall': tensor(0.1111), 'micro_recall': tensor(0.1250)}
67         >>> pprint(same_metric(preds, target))
68         {'macro_recall': tensor(0.1111), 'micro_recall': tensor(0.1250)}
69         >>> metrics.persistent()
70 
71     """
72 
73     def __init__(
74         self,
75         metrics: Union[List[Metric], Tuple[Metric], Dict[str, Metric]],
76         prefix: Optional[str] = None,
77     ):
78         super().__init__()
79         if isinstance(metrics, dict):
80             # Check all values are metrics
81             for name, metric in metrics.items():
82                 if not isinstance(metric, Metric):
83                     raise ValueError(
84                         f"Value {metric} belonging to key {name}"
85                         " is not an instance of `pl.metrics.Metric`"
86                     )
87                 self[name] = metric
88         elif isinstance(metrics, (tuple, list)):
89             for metric in metrics:
90                 if not isinstance(metric, Metric):
91                     raise ValueError(
92                         f"Input {metric} to `MetricCollection` is not a instance"
93                         " of `pl.metrics.Metric`"
94                     )
95                 name = metric.__class__.__name__
96                 if name in self:
97                     raise ValueError(f"Encountered two metrics both named {name}")
98                 self[name] = metric
99         else:
100             raise ValueError("Unknown input to MetricCollection.")
101 
102         self.prefix = self._check_prefix_arg(prefix)
103 
104     def forward(self, *args, **kwargs) -> Dict[str, Any]:  # pylint: disable=E0202
105         """
106         Iteratively call forward for each metric. Positional arguments (args) will
107         be passed to every metric in the collection, while keyword arguments (kwargs)
108         will be filtered based on the signature of the individual metric.
109         """
110         return {self._set_prefix(k): m(*args, **m._filter_kwargs(**kwargs)) for k, m in self.items()}
111 
112     def update(self, *args, **kwargs):  # pylint: disable=E0202
113         """
114         Iteratively call update for each metric. Positional arguments (args) will
115         be passed to every metric in the collection, while keyword arguments (kwargs)
116         will be filtered based on the signature of the individual metric.
117         """
118         for _, m in self.items():
119             m_kwargs = m._filter_kwargs(**kwargs)
120             m.update(*args, **m_kwargs)
121 
122     def compute(self) -> Dict[str, Any]:
123         return {self._set_prefix(k): m.compute() for k, m in self.items()}
124 
125     def reset(self) -> None:
126         """ Iteratively call reset for each metric """
127         for _, m in self.items():
128             m.reset()
129 
130     def clone(self, prefix: Optional[str] = None) -> 'MetricCollection':
131         """ Make a copy of the metric collection
132         Args:
133             prefix: a string to append in front of the metric keys
134         """
135         mc = deepcopy(self)
136         mc.prefix = self._check_prefix_arg(prefix)
137         return mc
138 
139     def persistent(self, mode: bool = True) -> None:
140         """Method for post-init to change if metric states should be saved to
141         its state_dict
142         """
143         for _, m in self.items():
144             m.persistent(mode)
145 
146     def _set_prefix(self, k: str) -> str:
147         return k if self.prefix is None else self.prefix + k
148 
149     @staticmethod
150     def _check_prefix_arg(prefix: str) -> Optional[str]:
151         if prefix is not None:
152             if isinstance(prefix, str):
153                 return prefix
154             else:
155                 raise ValueError('Expected input `prefix` to be a string')
156         return None
157
```
--- 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/torchmetrics/collections.py b/torchmetrics/collections.py
--- a/torchmetrics/collections.py
+++ b/torchmetrics/collections.py
@@ -35,6 +35,7 @@
             * dict: if metrics are passed in as a dict, will use each key in the
               dict as key for output dict. Use this format if you want to chain
               together multiple of the same metric with different parameters.
+              Note that the keys in the output dict will be sorted alphabetically.
 
         prefix: a string to append in front of the keys of the output dict
 
@@ -78,7 +79,9 @@
         super().__init__()
         if isinstance(metrics, dict):
             # Check all values are metrics
-            for name, metric in metrics.items():
+            # Make sure that metrics are added in deterministic order
+            for name in sorted(metrics.keys()):
+                metric = metrics[name]
                 if not isinstance(metric, Metric):
                     raise ValueError(
                         f"Value {metric} belonging to key {name}"

{"golden_diff": "diff --git a/torchmetrics/collections.py b/torchmetrics/collections.py\n--- a/torchmetrics/collections.py\n+++ b/torchmetrics/collections.py\n@@ -35,6 +35,7 @@\n             * dict: if metrics are passed in as a dict, will use each key in the\n               dict as key for output dict. Use this format if you want to chain\n               together multiple of the same metric with different parameters.\n+              Note that the keys in the output dict will be sorted alphabetically.\n \n         prefix: a string to append in front of the keys of the output dict\n \n@@ -78,7 +79,9 @@\n         super().__init__()\n         if isinstance(metrics, dict):\n             # Check all values are metrics\n-            for name, metric in metrics.items():\n+            # Make sure that metrics are added in deterministic order\n+            for name in sorted(metrics.keys()):\n+                metric = metrics[name]\n                 if not isinstance(metric, Metric):\n                     raise ValueError(\n                         f\"Value {metric} belonging to key {name}\"\n", "issue": "MetricCollection should enforce order when passed a dict\n## \ud83d\udc1b Bug\r\n\r\nNot a clear bug yet, but just thinking about distributed metric computation, in here: https://github.com/PyTorchLightning/metrics/blob/53d570158a503497351ae45ec895ca44a0546068/torchmetrics/collections.py#L81\r\nwe should make sure to sort the names before the insert so that we insert things in the same order (ModuleDict is already OrderedDict otherwise). If we don't we will get deadlocks when doing distributed metric updates.\r\n\r\nAdditionally, we might want to enforce sorting when passed list/tuple, but that might be more on the user end.\r\n\r\n\r\n### To Reproduce\r\n\r\nOn each of the workers, pass dictionary with same metrics but in different order, try compute and observe deadlock.\r\n\r\n\n", "before_files": [{"content": "# Copyright The PyTorch Lightning team.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom copy import deepcopy\nfrom typing import Any, Dict, List, Optional, Tuple, Union\n\nfrom torch import nn\n\nfrom torchmetrics.metric import Metric\n\n\nclass MetricCollection(nn.ModuleDict):\n    \"\"\"\n    MetricCollection class can be used to chain metrics that have the same\n    call pattern into one single class.\n\n    Args:\n        metrics: One of the following\n\n            * list or tuple: if metrics are passed in as a list, will use the\n              metrics class name as key for output dict. Therefore, two metrics\n              of the same class cannot be chained this way.\n\n            * dict: if metrics are passed in as a dict, will use each key in the\n              dict as key for output dict. Use this format if you want to chain\n              together multiple of the same metric with different parameters.\n\n        prefix: a string to append in front of the keys of the output dict\n\n    Raises:\n        ValueError:\n            If one of the elements of ``metrics`` is not an instance of ``pl.metrics.Metric``.\n        ValueError:\n            If two elements in ``metrics`` have the same ``name``.\n        ValueError:\n            If ``metrics`` is not a ``list``, ``tuple`` or a ``dict``.\n\n    Example (input as list):\n        >>> import torch\n        >>> from pprint import pprint\n        >>> from torchmetrics import MetricCollection, Accuracy, Precision, Recall\n        >>> target = torch.tensor([0, 2, 0, 2, 0, 1, 0, 2])\n        >>> preds = torch.tensor([2, 1, 2, 0, 1, 2, 2, 2])\n        >>> metrics = MetricCollection([Accuracy(),\n        ...                             Precision(num_classes=3, average='macro'),\n        ...                             Recall(num_classes=3, average='macro')])\n        >>> metrics(preds, target)\n        {'Accuracy': tensor(0.1250), 'Precision': tensor(0.0667), 'Recall': tensor(0.1111)}\n\n    Example (input as dict):\n        >>> metrics = MetricCollection({'micro_recall': Recall(num_classes=3, average='micro'),\n        ...                             'macro_recall': Recall(num_classes=3, average='macro')})\n        >>> same_metric = metrics.clone()\n        >>> pprint(metrics(preds, target))\n        {'macro_recall': tensor(0.1111), 'micro_recall': tensor(0.1250)}\n        >>> pprint(same_metric(preds, target))\n        {'macro_recall': tensor(0.1111), 'micro_recall': tensor(0.1250)}\n        >>> metrics.persistent()\n\n    \"\"\"\n\n    def __init__(\n        self,\n        metrics: Union[List[Metric], Tuple[Metric], Dict[str, Metric]],\n        prefix: Optional[str] = None,\n    ):\n        super().__init__()\n        if isinstance(metrics, dict):\n            # Check all values are metrics\n            for name, metric in metrics.items():\n                if not isinstance(metric, Metric):\n                    raise ValueError(\n                        f\"Value {metric} belonging to key {name}\"\n                        \" is not an instance of `pl.metrics.Metric`\"\n                    )\n                self[name] = metric\n        elif isinstance(metrics, (tuple, list)):\n            for metric in metrics:\n                if not isinstance(metric, Metric):\n                    raise ValueError(\n                        f\"Input {metric} to `MetricCollection` is not a instance\"\n                        \" of `pl.metrics.Metric`\"\n                    )\n                name = metric.__class__.__name__\n                if name in self:\n                    raise ValueError(f\"Encountered two metrics both named {name}\")\n                self[name] = metric\n        else:\n            raise ValueError(\"Unknown input to MetricCollection.\")\n\n        self.prefix = self._check_prefix_arg(prefix)\n\n    def forward(self, *args, **kwargs) -> Dict[str, Any]:  # pylint: disable=E0202\n        \"\"\"\n        Iteratively call forward for each metric. Positional arguments (args) will\n        be passed to every metric in the collection, while keyword arguments (kwargs)\n        will be filtered based on the signature of the individual metric.\n        \"\"\"\n        return {self._set_prefix(k): m(*args, **m._filter_kwargs(**kwargs)) for k, m in self.items()}\n\n    def update(self, *args, **kwargs):  # pylint: disable=E0202\n        \"\"\"\n        Iteratively call update for each metric. Positional arguments (args) will\n        be passed to every metric in the collection, while keyword arguments (kwargs)\n        will be filtered based on the signature of the individual metric.\n        \"\"\"\n        for _, m in self.items():\n            m_kwargs = m._filter_kwargs(**kwargs)\n            m.update(*args, **m_kwargs)\n\n    def compute(self) -> Dict[str, Any]:\n        return {self._set_prefix(k): m.compute() for k, m in self.items()}\n\n    def reset(self) -> None:\n        \"\"\" Iteratively call reset for each metric \"\"\"\n        for _, m in self.items():\n            m.reset()\n\n    def clone(self, prefix: Optional[str] = None) -> 'MetricCollection':\n        \"\"\" Make a copy of the metric collection\n        Args:\n            prefix: a string to append in front of the metric keys\n        \"\"\"\n        mc = deepcopy(self)\n        mc.prefix = self._check_prefix_arg(prefix)\n        return mc\n\n    def persistent(self, mode: bool = True) -> None:\n        \"\"\"Method for post-init to change if metric states should be saved to\n        its state_dict\n        \"\"\"\n        for _, m in self.items():\n            m.persistent(mode)\n\n    def _set_prefix(self, k: str) -> str:\n        return k if self.prefix is None else self.prefix + k\n\n    @staticmethod\n    def _check_prefix_arg(prefix: str) -> Optional[str]:\n        if prefix is not None:\n            if isinstance(prefix, str):\n                return prefix\n            else:\n                raise ValueError('Expected input `prefix` to be a string')\n        return None\n", "path": "torchmetrics/collections.py"}], "after_files": [{"content": "# Copyright The PyTorch Lightning team.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nfrom copy import deepcopy\nfrom typing import Any, Dict, List, Optional, Tuple, Union\n\nfrom torch import nn\n\nfrom torchmetrics.metric import Metric\n\n\nclass MetricCollection(nn.ModuleDict):\n    \"\"\"\n    MetricCollection class can be used to chain metrics that have the same\n    call pattern into one single class.\n\n    Args:\n        metrics: One of the following\n\n            * list or tuple: if metrics are passed in as a list, will use the\n              metrics class name as key for output dict. Therefore, two metrics\n              of the same class cannot be chained this way.\n\n            * dict: if metrics are passed in as a dict, will use each key in the\n              dict as key for output dict. Use this format if you want to chain\n              together multiple of the same metric with different parameters.\n              Note that the keys in the output dict will be sorted alphabetically.\n\n        prefix: a string to append in front of the keys of the output dict\n\n    Raises:\n        ValueError:\n            If one of the elements of ``metrics`` is not an instance of ``pl.metrics.Metric``.\n        ValueError:\n            If two elements in ``metrics`` have the same ``name``.\n        ValueError:\n            If ``metrics`` is not a ``list``, ``tuple`` or a ``dict``.\n\n    Example (input as list):\n        >>> import torch\n        >>> from pprint import pprint\n        >>> from torchmetrics import MetricCollection, Accuracy, Precision, Recall\n        >>> target = torch.tensor([0, 2, 0, 2, 0, 1, 0, 2])\n        >>> preds = torch.tensor([2, 1, 2, 0, 1, 2, 2, 2])\n        >>> metrics = MetricCollection([Accuracy(),\n        ...                             Precision(num_classes=3, average='macro'),\n        ...                             Recall(num_classes=3, average='macro')])\n        >>> metrics(preds, target)\n        {'Accuracy': tensor(0.1250), 'Precision': tensor(0.0667), 'Recall': tensor(0.1111)}\n\n    Example (input as dict):\n        >>> metrics = MetricCollection({'micro_recall': Recall(num_classes=3, average='micro'),\n        ...                             'macro_recall': Recall(num_classes=3, average='macro')})\n        >>> same_metric = metrics.clone()\n        >>> pprint(metrics(preds, target))\n        {'macro_recall': tensor(0.1111), 'micro_recall': tensor(0.1250)}\n        >>> pprint(same_metric(preds, target))\n        {'macro_recall': tensor(0.1111), 'micro_recall': tensor(0.1250)}\n        >>> metrics.persistent()\n\n    \"\"\"\n\n    def __init__(\n        self,\n        metrics: Union[List[Metric], Tuple[Metric], Dict[str, Metric]],\n        prefix: Optional[str] = None,\n    ):\n        super().__init__()\n        if isinstance(metrics, dict):\n            # Check all values are metrics\n            # Make sure that metrics are added in deterministic order\n            for name in sorted(metrics.keys()):\n                metric = metrics[name]\n                if not isinstance(metric, Metric):\n                    raise ValueError(\n                        f\"Value {metric} belonging to key {name}\"\n                        \" is not an instance of `pl.metrics.Metric`\"\n                    )\n                self[name] = metric\n        elif isinstance(metrics, (tuple, list)):\n            for metric in metrics:\n                if not isinstance(metric, Metric):\n                    raise ValueError(\n                        f\"Input {metric} to `MetricCollection` is not a instance\"\n                        \" of `pl.metrics.Metric`\"\n                    )\n                name = metric.__class__.__name__\n                if name in self:\n                    raise ValueError(f\"Encountered two metrics both named {name}\")\n                self[name] = metric\n        else:\n            raise ValueError(\"Unknown input to MetricCollection.\")\n\n        self.prefix = self._check_prefix_arg(prefix)\n\n    def forward(self, *args, **kwargs) -> Dict[str, Any]:  # pylint: disable=E0202\n        \"\"\"\n        Iteratively call forward for each metric. Positional arguments (args) will\n        be passed to every metric in the collection, while keyword arguments (kwargs)\n        will be filtered based on the signature of the individual metric.\n        \"\"\"\n        return {self._set_prefix(k): m(*args, **m._filter_kwargs(**kwargs)) for k, m in self.items()}\n\n    def update(self, *args, **kwargs):  # pylint: disable=E0202\n        \"\"\"\n        Iteratively call update for each metric. Positional arguments (args) will\n        be passed to every metric in the collection, while keyword arguments (kwargs)\n        will be filtered based on the signature of the individual metric.\n        \"\"\"\n        for _, m in self.items():\n            m_kwargs = m._filter_kwargs(**kwargs)\n            m.update(*args, **m_kwargs)\n\n    def compute(self) -> Dict[str, Any]:\n        return {self._set_prefix(k): m.compute() for k, m in self.items()}\n\n    def reset(self) -> None:\n        \"\"\" Iteratively call reset for each metric \"\"\"\n        for _, m in self.items():\n            m.reset()\n\n    def clone(self, prefix: Optional[str] = None) -> 'MetricCollection':\n        \"\"\" Make a copy of the metric collection\n        Args:\n            prefix: a string to append in front of the metric keys\n        \"\"\"\n        mc = deepcopy(self)\n        mc.prefix = self._check_prefix_arg(prefix)\n        return mc\n\n    def persistent(self, mode: bool = True) -> None:\n        \"\"\"Method for post-init to change if metric states should be saved to\n        its state_dict\n        \"\"\"\n        for _, m in self.items():\n            m.persistent(mode)\n\n    def _set_prefix(self, k: str) -> str:\n        return k if self.prefix is None else self.prefix + k\n\n    @staticmethod\n    def _check_prefix_arg(prefix: str) -> Optional[str]:\n        if prefix is not None:\n            if isinstance(prefix, str):\n                return prefix\n            else:\n                raise ValueError('Expected input `prefix` to be a string')\n        return None\n", "path": "torchmetrics/collections.py"}]}