prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  Self-Implemented Metrics Behaved Weirdly in DDP Mode
  ## 🐛 Bug
  
  Self-implemented nDCG@5 is larger than nDCG@1 in DDP mode. What I was trying to do here is to record the sum of sample-wise scores **_s_** and the number of samples **_n_**. At the end of batch/epoch, I can obtain the mean of nDCG@k by dividing **_s_** by **_n_**.
  
  My implementation looks very similar to the example on [IMPLEMENTING A METRIC](https://torchmetrics.readthedocs.io/en/stable/pages/implement.html). However, It failed in distributed settings.
  
  `Epoch 8:  60%|██████    | 1541/2555 [04:58<03:16,  5.17it/s, loss=0.00354, v_num=213259, train_ndcg@1=0.656, train_ndcg@3=0.675, train_ndcg@5=0.698,...`
  
  ### To Reproduce
  
  <details>
    <summary>Self-implemented Metric</summary>
  
  ```python
  from torchmetrics.functional.retrieval import retrieval_normalized_dcg
  from torchmetrics import Metric
  import torch
  from torch import Tensor
  
  class NDCG(Metric):
      full_state_update = False
  
      def __init__(self, top_k: int):
          super().__init__()
          self.top_k = top_k
          self.add_state("ndcg", default=torch.tensor(0, dtype=torch.float), dist_reduce_fx="sum")
          self.add_state("n", default=torch.tensor(0), dist_reduce_fx="sum")
  
      def update(self, preds: Tensor, target: Tensor):
          # preds and target are 2-D tensors.
          assert preds.shape == target.shape
  
          self.ndcg += torch.stack([retrieval_normalized_dcg(p, t, k=self.top_k) for p, t in zip(preds, target)]).sum()
          self.n += len(preds)
  
      def compute(self):
          return self.ndcg / self.n
  ```
  
  </details>
  
  ### Expected behavior
  
  The documentation of self-implemented metric looked too simple and probably missed some details of distributed metrics.
  
  I noticed that you used list and in `compute()` you call mean() to get the average score of a given batch. Can you please explain the difference between these two implementations?
  
  ### Environment
  - TorchMetrics version (and how you installed TM, e.g. conda, pip, build from source):
  0.10.3 (conda-forge)
  - Python version:
  3.10.12
  PyTorch version (e.g., 1.0):
  1.13.1
  - Any other relevant information such as OS (e.g., Linux):
  pytorch-lightning: 1.9.4
  Linux: 5.15.0-78-generic
  
  ### Additional context
  
  <!-- Add any other context about the problem here. -->
  
  
  --- END ISSUE ---
  
  Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
  --- BEGIN FILES ---
  Path: `src/torchmetrics/functional/retrieval/ndcg.py`
  Content:
  ```
  1 # Copyright The Lightning team.
  2 #
  3 # Licensed under the Apache License, Version 2.0 (the "License");
  4 # you may not use this file except in compliance with the License.
  5 # You may obtain a copy of the License at
  6 #
  7 #     http://www.apache.org/licenses/LICENSE-2.0
  8 #
  9 # Unless required by applicable law or agreed to in writing, software
  10 # distributed under the License is distributed on an "AS IS" BASIS,
  11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12 # See the License for the specific language governing permissions and
  13 # limitations under the License.
  14 from typing import Optional
  15 
  16 import torch
  17 from torch import Tensor
  18 
  19 from torchmetrics.utilities.checks import _check_retrieval_functional_inputs
  20 
  21 
  22 def _dcg(target: Tensor) -> Tensor:
  23     """Compute Discounted Cumulative Gain for input tensor."""
  24     denom = torch.log2(torch.arange(target.shape[-1], device=target.device) + 2.0)
  25     return (target / denom).sum(dim=-1)
  26 
  27 
  28 def retrieval_normalized_dcg(preds: Tensor, target: Tensor, top_k: Optional[int] = None) -> Tensor:
  29     """Compute `Normalized Discounted Cumulative Gain`_ (for information retrieval).
  30 
  31     ``preds`` and ``target`` should be of the same shape and live on the same device.
  32     ``target`` must be either `bool` or `integers` and ``preds`` must be ``float``,
  33     otherwise an error is raised.
  34 
  35     Args:
  36         preds: estimated probabilities of each document to be relevant.
  37         target: ground truth about each document relevance.
  38         top_k: consider only the top k elements (default: ``None``, which considers them all)
  39 
  40     Return:
  41         A single-value tensor with the nDCG of the predictions ``preds`` w.r.t. the labels ``target``.
  42 
  43     Raises:
  44         ValueError:
  45             If ``top_k`` parameter is not `None` or an integer larger than 0
  46 
  47     Example:
  48         >>> from torchmetrics.functional.retrieval import retrieval_normalized_dcg
  49         >>> preds = torch.tensor([.1, .2, .3, 4, 70])
  50         >>> target = torch.tensor([10, 0, 0, 1, 5])
  51         >>> retrieval_normalized_dcg(preds, target)
  52         tensor(0.6957)
  53 
  54     """
  55     preds, target = _check_retrieval_functional_inputs(preds, target, allow_non_binary_target=True)
  56 
  57     top_k = preds.shape[-1] if top_k is None else top_k
  58 
  59     if not (isinstance(top_k, int) and top_k > 0):
  60         raise ValueError("`top_k` has to be a positive integer or None")
  61 
  62     sorted_target = target[torch.argsort(preds, dim=-1, descending=True)][:top_k]
  63     ideal_target = torch.sort(target, descending=True)[0][:top_k]
  64 
  65     ideal_dcg = _dcg(ideal_target)
  66     target_dcg = _dcg(sorted_target)
  67 
  68     # filter undefined scores
  69     all_irrelevant = ideal_dcg == 0
  70     target_dcg[all_irrelevant] = 0
  71     target_dcg[~all_irrelevant] /= ideal_dcg[~all_irrelevant]
  72 
  73     return target_dcg.mean()
  74
  ```
  --- 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()
  
  ```
  
golden_diff: diff --git a/src/torchmetrics/functional/retrieval/ndcg.py b/src/torchmetrics/functional/retrieval/ndcg.py
  --- a/src/torchmetrics/functional/retrieval/ndcg.py
  +++ b/src/torchmetrics/functional/retrieval/ndcg.py
  @@ -19,10 +19,53 @@
   from torchmetrics.utilities.checks import _check_retrieval_functional_inputs
   
   
  -def _dcg(target: Tensor) -> Tensor:
  -    """Compute Discounted Cumulative Gain for input tensor."""
  -    denom = torch.log2(torch.arange(target.shape[-1], device=target.device) + 2.0)
  -    return (target / denom).sum(dim=-1)
  +def _tie_average_dcg(target: Tensor, preds: Tensor, discount_cumsum: Tensor) -> Tensor:
  +    """Translated version of sklearns `_tie_average_dcg` function.
  +
  +    Args:
  +        target: ground truth about each document relevance.
  +        preds: estimated probabilities of each document to be relevant.
  +        discount_cumsum: cumulative sum of the discount.
  +
  +    Returns:
  +        The cumulative gain of the tied elements.
  +
  +    """
  +    _, inv, counts = torch.unique(-preds, return_inverse=True, return_counts=True)
  +    ranked = torch.zeros_like(counts, dtype=torch.float32)
  +    ranked.scatter_add_(0, inv, target.to(dtype=ranked.dtype))
  +    ranked = ranked / counts
  +    groups = counts.cumsum(dim=0) - 1
  +    discount_sums = torch.zeros_like(counts, dtype=torch.float32)
  +    discount_sums[0] = discount_cumsum[groups[0]]
  +    discount_sums[1:] = discount_cumsum[groups].diff()
  +    return (ranked * discount_sums).sum()
  +
  +
  +def _dcg_sample_scores(target: Tensor, preds: Tensor, top_k: int, ignore_ties: bool) -> Tensor:
  +    """Translated version of sklearns `_dcg_sample_scores` function.
  +
  +    Args:
  +        target: ground truth about each document relevance.
  +        preds: estimated probabilities of each document to be relevant.
  +        top_k: consider only the top k elements
  +        ignore_ties: If True, ties are ignored. If False, ties are averaged.
  +
  +    Returns:
  +        The cumulative gain
  +
  +    """
  +    discount = 1.0 / (torch.log2(torch.arange(target.shape[-1], device=target.device) + 2.0))
  +    discount[top_k:] = 0.0
  +
  +    if ignore_ties:
  +        ranking = preds.argsort(descending=True)
  +        ranked = target[ranking]
  +        cumulative_gain = (discount * ranked).sum()
  +    else:
  +        discount_cumsum = discount.cumsum(dim=-1)
  +        cumulative_gain = _tie_average_dcg(target, preds, discount_cumsum)
  +    return cumulative_gain
   
   
   def retrieval_normalized_dcg(preds: Tensor, target: Tensor, top_k: Optional[int] = None) -> Tensor:
  @@ -59,15 +102,12 @@
       if not (isinstance(top_k, int) and top_k > 0):
           raise ValueError("`top_k` has to be a positive integer or None")
   
  -    sorted_target = target[torch.argsort(preds, dim=-1, descending=True)][:top_k]
  -    ideal_target = torch.sort(target, descending=True)[0][:top_k]
  -
  -    ideal_dcg = _dcg(ideal_target)
  -    target_dcg = _dcg(sorted_target)
  +    gain = _dcg_sample_scores(target, preds, top_k, ignore_ties=False)
  +    normalized_gain = _dcg_sample_scores(target, target, top_k, ignore_ties=True)
   
       # filter undefined scores
  -    all_irrelevant = ideal_dcg == 0
  -    target_dcg[all_irrelevant] = 0
  -    target_dcg[~all_irrelevant] /= ideal_dcg[~all_irrelevant]
  +    all_irrelevant = normalized_gain == 0
  +    gain[all_irrelevant] = 0
  +    gain[~all_irrelevant] /= normalized_gain[~all_irrelevant]
   
  -    return target_dcg.mean()
  +    return gain.mean()
  
verification_info: {"golden_diff": "diff --git a/src/torchmetrics/functional/retrieval/ndcg.py b/src/torchmetrics/functional/retrieval/ndcg.py\n--- a/src/torchmetrics/functional/retrieval/ndcg.py\n+++ b/src/torchmetrics/functional/retrieval/ndcg.py\n@@ -19,10 +19,53 @@\n from torchmetrics.utilities.checks import _check_retrieval_functional_inputs\n \n \n-def _dcg(target: Tensor) -> Tensor:\n-    \"\"\"Compute Discounted Cumulative Gain for input tensor.\"\"\"\n-    denom = torch.log2(torch.arange(target.shape[-1], device=target.device) + 2.0)\n-    return (target / denom).sum(dim=-1)\n+def _tie_average_dcg(target: Tensor, preds: Tensor, discount_cumsum: Tensor) -> Tensor:\n+    \"\"\"Translated version of sklearns `_tie_average_dcg` function.\n+\n+    Args:\n+        target: ground truth about each document relevance.\n+        preds: estimated probabilities of each document to be relevant.\n+        discount_cumsum: cumulative sum of the discount.\n+\n+    Returns:\n+        The cumulative gain of the tied elements.\n+\n+    \"\"\"\n+    _, inv, counts = torch.unique(-preds, return_inverse=True, return_counts=True)\n+    ranked = torch.zeros_like(counts, dtype=torch.float32)\n+    ranked.scatter_add_(0, inv, target.to(dtype=ranked.dtype))\n+    ranked = ranked / counts\n+    groups = counts.cumsum(dim=0) - 1\n+    discount_sums = torch.zeros_like(counts, dtype=torch.float32)\n+    discount_sums[0] = discount_cumsum[groups[0]]\n+    discount_sums[1:] = discount_cumsum[groups].diff()\n+    return (ranked * discount_sums).sum()\n+\n+\n+def _dcg_sample_scores(target: Tensor, preds: Tensor, top_k: int, ignore_ties: bool) -> Tensor:\n+    \"\"\"Translated version of sklearns `_dcg_sample_scores` function.\n+\n+    Args:\n+        target: ground truth about each document relevance.\n+        preds: estimated probabilities of each document to be relevant.\n+        top_k: consider only the top k elements\n+        ignore_ties: If True, ties are ignored. If False, ties are averaged.\n+\n+    Returns:\n+        The cumulative gain\n+\n+    \"\"\"\n+    discount = 1.0 / (torch.log2(torch.arange(target.shape[-1], device=target.device) + 2.0))\n+    discount[top_k:] = 0.0\n+\n+    if ignore_ties:\n+        ranking = preds.argsort(descending=True)\n+        ranked = target[ranking]\n+        cumulative_gain = (discount * ranked).sum()\n+    else:\n+        discount_cumsum = discount.cumsum(dim=-1)\n+        cumulative_gain = _tie_average_dcg(target, preds, discount_cumsum)\n+    return cumulative_gain\n \n \n def retrieval_normalized_dcg(preds: Tensor, target: Tensor, top_k: Optional[int] = None) -> Tensor:\n@@ -59,15 +102,12 @@\n     if not (isinstance(top_k, int) and top_k > 0):\n         raise ValueError(\"`top_k` has to be a positive integer or None\")\n \n-    sorted_target = target[torch.argsort(preds, dim=-1, descending=True)][:top_k]\n-    ideal_target = torch.sort(target, descending=True)[0][:top_k]\n-\n-    ideal_dcg = _dcg(ideal_target)\n-    target_dcg = _dcg(sorted_target)\n+    gain = _dcg_sample_scores(target, preds, top_k, ignore_ties=False)\n+    normalized_gain = _dcg_sample_scores(target, target, top_k, ignore_ties=True)\n \n     # filter undefined scores\n-    all_irrelevant = ideal_dcg == 0\n-    target_dcg[all_irrelevant] = 0\n-    target_dcg[~all_irrelevant] /= ideal_dcg[~all_irrelevant]\n+    all_irrelevant = normalized_gain == 0\n+    gain[all_irrelevant] = 0\n+    gain[~all_irrelevant] /= normalized_gain[~all_irrelevant]\n \n-    return target_dcg.mean()\n+    return gain.mean()\n", "issue": "Self-Implemented Metrics Behaved Weirdly in DDP Mode\n## \ud83d\udc1b Bug\r\n\r\nSelf-implemented nDCG@5 is larger than nDCG@1 in DDP mode. What I was trying to do here is to record the sum of sample-wise scores **_s_** and the number of samples **_n_**. At the end of batch/epoch, I can obtain the mean of nDCG@k by dividing **_s_** by **_n_**.\r\n\r\nMy implementation looks very similar to the example on [IMPLEMENTING A METRIC](https://torchmetrics.readthedocs.io/en/stable/pages/implement.html). However, It failed in distributed settings.\r\n\r\n`Epoch 8:  60%|\u2588\u2588\u2588\u2588\u2588\u2588    | 1541/2555 [04:58<03:16,  5.17it/s, loss=0.00354, v_num=213259, train_ndcg@1=0.656, train_ndcg@3=0.675, train_ndcg@5=0.698,...`\r\n\r\n### To Reproduce\r\n\r\n<details>\r\n  <summary>Self-implemented Metric</summary>\r\n\r\n```python\r\nfrom torchmetrics.functional.retrieval import retrieval_normalized_dcg\r\nfrom torchmetrics import Metric\r\nimport torch\r\nfrom torch import Tensor\r\n\r\nclass NDCG(Metric):\r\n    full_state_update = False\r\n\r\n    def __init__(self, top_k: int):\r\n        super().__init__()\r\n        self.top_k = top_k\r\n        self.add_state(\"ndcg\", default=torch.tensor(0, dtype=torch.float), dist_reduce_fx=\"sum\")\r\n        self.add_state(\"n\", default=torch.tensor(0), dist_reduce_fx=\"sum\")\r\n\r\n    def update(self, preds: Tensor, target: Tensor):\r\n        # preds and target are 2-D tensors.\r\n        assert preds.shape == target.shape\r\n\r\n        self.ndcg += torch.stack([retrieval_normalized_dcg(p, t, k=self.top_k) for p, t in zip(preds, target)]).sum()\r\n        self.n += len(preds)\r\n\r\n    def compute(self):\r\n        return self.ndcg / self.n\r\n```\r\n\r\n</details>\r\n\r\n### Expected behavior\r\n\r\nThe documentation of self-implemented metric looked too simple and probably missed some details of distributed metrics.\r\n\r\nI noticed that you used list and in `compute()` you call mean() to get the average score of a given batch. Can you please explain the difference between these two implementations?\r\n\r\n### Environment\r\n- TorchMetrics version (and how you installed TM, e.g. conda, pip, build from source):\r\n0.10.3 (conda-forge)\r\n- Python version:\r\n3.10.12\r\nPyTorch version (e.g., 1.0):\r\n1.13.1\r\n- Any other relevant information such as OS (e.g., Linux):\r\npytorch-lightning: 1.9.4\r\nLinux: 5.15.0-78-generic\r\n\r\n### Additional context\r\n\r\n<!-- Add any other context about the problem here. -->\r\n\n", "before_files": [{"content": "# Copyright The Lightning team.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Optional\n\nimport torch\nfrom torch import Tensor\n\nfrom torchmetrics.utilities.checks import _check_retrieval_functional_inputs\n\n\ndef _dcg(target: Tensor) -> Tensor:\n    \"\"\"Compute Discounted Cumulative Gain for input tensor.\"\"\"\n    denom = torch.log2(torch.arange(target.shape[-1], device=target.device) + 2.0)\n    return (target / denom).sum(dim=-1)\n\n\ndef retrieval_normalized_dcg(preds: Tensor, target: Tensor, top_k: Optional[int] = None) -> Tensor:\n    \"\"\"Compute `Normalized Discounted Cumulative Gain`_ (for information retrieval).\n\n    ``preds`` and ``target`` should be of the same shape and live on the same device.\n    ``target`` must be either `bool` or `integers` and ``preds`` must be ``float``,\n    otherwise an error is raised.\n\n    Args:\n        preds: estimated probabilities of each document to be relevant.\n        target: ground truth about each document relevance.\n        top_k: consider only the top k elements (default: ``None``, which considers them all)\n\n    Return:\n        A single-value tensor with the nDCG of the predictions ``preds`` w.r.t. the labels ``target``.\n\n    Raises:\n        ValueError:\n            If ``top_k`` parameter is not `None` or an integer larger than 0\n\n    Example:\n        >>> from torchmetrics.functional.retrieval import retrieval_normalized_dcg\n        >>> preds = torch.tensor([.1, .2, .3, 4, 70])\n        >>> target = torch.tensor([10, 0, 0, 1, 5])\n        >>> retrieval_normalized_dcg(preds, target)\n        tensor(0.6957)\n\n    \"\"\"\n    preds, target = _check_retrieval_functional_inputs(preds, target, allow_non_binary_target=True)\n\n    top_k = preds.shape[-1] if top_k is None else top_k\n\n    if not (isinstance(top_k, int) and top_k > 0):\n        raise ValueError(\"`top_k` has to be a positive integer or None\")\n\n    sorted_target = target[torch.argsort(preds, dim=-1, descending=True)][:top_k]\n    ideal_target = torch.sort(target, descending=True)[0][:top_k]\n\n    ideal_dcg = _dcg(ideal_target)\n    target_dcg = _dcg(sorted_target)\n\n    # filter undefined scores\n    all_irrelevant = ideal_dcg == 0\n    target_dcg[all_irrelevant] = 0\n    target_dcg[~all_irrelevant] /= ideal_dcg[~all_irrelevant]\n\n    return target_dcg.mean()\n", "path": "src/torchmetrics/functional/retrieval/ndcg.py"}], "after_files": [{"content": "# Copyright The Lightning team.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\nfrom typing import Optional\n\nimport torch\nfrom torch import Tensor\n\nfrom torchmetrics.utilities.checks import _check_retrieval_functional_inputs\n\n\ndef _tie_average_dcg(target: Tensor, preds: Tensor, discount_cumsum: Tensor) -> Tensor:\n    \"\"\"Translated version of sklearns `_tie_average_dcg` function.\n\n    Args:\n        target: ground truth about each document relevance.\n        preds: estimated probabilities of each document to be relevant.\n        discount_cumsum: cumulative sum of the discount.\n\n    Returns:\n        The cumulative gain of the tied elements.\n\n    \"\"\"\n    _, inv, counts = torch.unique(-preds, return_inverse=True, return_counts=True)\n    ranked = torch.zeros_like(counts, dtype=torch.float32)\n    ranked.scatter_add_(0, inv, target.to(dtype=ranked.dtype))\n    ranked = ranked / counts\n    groups = counts.cumsum(dim=0) - 1\n    discount_sums = torch.zeros_like(counts, dtype=torch.float32)\n    discount_sums[0] = discount_cumsum[groups[0]]\n    discount_sums[1:] = discount_cumsum[groups].diff()\n    return (ranked * discount_sums).sum()\n\n\ndef _dcg_sample_scores(target: Tensor, preds: Tensor, top_k: int, ignore_ties: bool) -> Tensor:\n    \"\"\"Translated version of sklearns `_dcg_sample_scores` function.\n\n    Args:\n        target: ground truth about each document relevance.\n        preds: estimated probabilities of each document to be relevant.\n        top_k: consider only the top k elements\n        ignore_ties: If True, ties are ignored. If False, ties are averaged.\n\n    Returns:\n        The cumulative gain\n\n    \"\"\"\n    discount = 1.0 / (torch.log2(torch.arange(target.shape[-1], device=target.device) + 2.0))\n    discount[top_k:] = 0.0\n\n    if ignore_ties:\n        ranking = preds.argsort(descending=True)\n        ranked = target[ranking]\n        cumulative_gain = (discount * ranked).sum()\n    else:\n        discount_cumsum = discount.cumsum(dim=-1)\n        cumulative_gain = _tie_average_dcg(target, preds, discount_cumsum)\n    return cumulative_gain\n\n\ndef retrieval_normalized_dcg(preds: Tensor, target: Tensor, top_k: Optional[int] = None) -> Tensor:\n    \"\"\"Compute `Normalized Discounted Cumulative Gain`_ (for information retrieval).\n\n    ``preds`` and ``target`` should be of the same shape and live on the same device.\n    ``target`` must be either `bool` or `integers` and ``preds`` must be ``float``,\n    otherwise an error is raised.\n\n    Args:\n        preds: estimated probabilities of each document to be relevant.\n        target: ground truth about each document relevance.\n        top_k: consider only the top k elements (default: ``None``, which considers them all)\n\n    Return:\n        A single-value tensor with the nDCG of the predictions ``preds`` w.r.t. the labels ``target``.\n\n    Raises:\n        ValueError:\n            If ``top_k`` parameter is not `None` or an integer larger than 0\n\n    Example:\n        >>> from torchmetrics.functional.retrieval import retrieval_normalized_dcg\n        >>> preds = torch.tensor([.1, .2, .3, 4, 70])\n        >>> target = torch.tensor([10, 0, 0, 1, 5])\n        >>> retrieval_normalized_dcg(preds, target)\n        tensor(0.6957)\n\n    \"\"\"\n    preds, target = _check_retrieval_functional_inputs(preds, target, allow_non_binary_target=True)\n\n    top_k = preds.shape[-1] if top_k is None else top_k\n\n    if not (isinstance(top_k, int) and top_k > 0):\n        raise ValueError(\"`top_k` has to be a positive integer or None\")\n\n    gain = _dcg_sample_scores(target, preds, top_k, ignore_ties=False)\n    normalized_gain = _dcg_sample_scores(target, target, top_k, ignore_ties=True)\n\n    # filter undefined scores\n    all_irrelevant = normalized_gain == 0\n    gain[all_irrelevant] = 0\n    gain[~all_irrelevant] /= normalized_gain[~all_irrelevant]\n\n    return gain.mean()\n", "path": "src/torchmetrics/functional/retrieval/ndcg.py"}]}