prompt: We are currently solving the following issue within our repository. Here is the issue text:
  --- BEGIN ISSUE ---
  `MeanAveragePrecision` update method does not accept `torchvision.datapoints.BoundingBox` arguments
  ## 🐛 Bug
  
  When the `preds` or `target` arguments to `MeanAveragePrecision.update` method are not of the type `list[dict[str,Tensor]]` a ValueError is raised, but this precludes dictionary values of type `torchvision.datapoints.BoundingBox` which inherit from Tensor.  
  
  The ValueError is raised in the function `torchmetrics.detection.helpers._input_validator`.  I think this could be fixed by changing all the lines like 
  
  `if any(type(target[ivn]) is not Tensor for target in targets):`
  
  to be something like
  
  `if not all(isinstance(target[ivn], Tensor) for target in targets):`
  
  One could also validate that the `BoundingBox.format` is the same as `MeanAveragePrecision.box_format` whenever a BoundingBox class is passed to the update method.
  
  ### To Reproduce
  
  ```python
  from torch import tensor
  from torchmetrics.detection import MeanAveragePrecision
  from torchvision.datapoints import BoundingBox, BoundingBoxFormat
  
  preds = [
      dict(
          boxes=tensor([[1.0, 2.0, 1.0, 2.0]]),
          scores=tensor([0.1]),
          labels=tensor([0]),
      )
  ]
  
  bounding_box = BoundingBox(
      data=tensor([[1.0, 2.0, 1.0, 2.0]]),
      format=BoundingBoxFormat.XYXY,
      spatial_size=(100,100)
  )
  target = [
      dict(
          boxes=bounding_box,
          labels=tensor([0]),
      )
  ]
  
  metric = MeanAveragePrecision(iou_type='bbox', box_format='xyxy')
  metric.update(preds, target)
  ```
  
  ### Expected behavior
  
  `MeanAveragePrecision.update` should accept `torchvision.datapoints.BoundingBox` arguments.
  
  ### Environment
  python 3.11.4
  pytorch 2.0.1
  torchmetrics 1.2.0
  torchvision 0.15.2
  
  
  
  --- 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/detection/helpers.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 from typing import Dict, Literal, Sequence, Tuple, Union
  15 
  16 from torch import Tensor
  17 
  18 
  19 def _input_validator(
  20     preds: Sequence[Dict[str, Tensor]],
  21     targets: Sequence[Dict[str, Tensor]],
  22     iou_type: Union[Literal["bbox", "segm"], Tuple[Literal["bbox", "segm"]]] = "bbox",
  23     ignore_score: bool = False,
  24 ) -> None:
  25     """Ensure the correct input format of `preds` and `targets`."""
  26     if isinstance(iou_type, str):
  27         iou_type = (iou_type,)
  28 
  29     name_map = {"bbox": "boxes", "segm": "masks"}
  30     if any(tp not in name_map for tp in iou_type):
  31         raise Exception(f"IOU type {iou_type} is not supported")
  32     item_val_name = [name_map[tp] for tp in iou_type]
  33 
  34     if not isinstance(preds, Sequence):
  35         raise ValueError(f"Expected argument `preds` to be of type Sequence, but got {preds}")
  36     if not isinstance(targets, Sequence):
  37         raise ValueError(f"Expected argument `target` to be of type Sequence, but got {targets}")
  38     if len(preds) != len(targets):
  39         raise ValueError(
  40             f"Expected argument `preds` and `target` to have the same length, but got {len(preds)} and {len(targets)}"
  41         )
  42 
  43     for k in [*item_val_name, "labels"] + (["scores"] if not ignore_score else []):
  44         if any(k not in p for p in preds):
  45             raise ValueError(f"Expected all dicts in `preds` to contain the `{k}` key")
  46 
  47     for k in [*item_val_name, "labels"]:
  48         if any(k not in p for p in targets):
  49             raise ValueError(f"Expected all dicts in `target` to contain the `{k}` key")
  50 
  51     for ivn in item_val_name:
  52         if any(type(pred[ivn]) is not Tensor for pred in preds):
  53             raise ValueError(f"Expected all {ivn} in `preds` to be of type Tensor")
  54     if not ignore_score and any(type(pred["scores"]) is not Tensor for pred in preds):
  55         raise ValueError("Expected all scores in `preds` to be of type Tensor")
  56     if any(type(pred["labels"]) is not Tensor for pred in preds):
  57         raise ValueError("Expected all labels in `preds` to be of type Tensor")
  58     for ivn in item_val_name:
  59         if any(type(target[ivn]) is not Tensor for target in targets):
  60             raise ValueError(f"Expected all {ivn} in `target` to be of type Tensor")
  61     if any(type(target["labels"]) is not Tensor for target in targets):
  62         raise ValueError("Expected all labels in `target` to be of type Tensor")
  63 
  64     for i, item in enumerate(targets):
  65         for ivn in item_val_name:
  66             if item[ivn].size(0) != item["labels"].size(0):
  67                 raise ValueError(
  68                     f"Input '{ivn}' and labels of sample {i} in targets have a"
  69                     f" different length (expected {item[ivn].size(0)} labels, got {item['labels'].size(0)})"
  70                 )
  71     if ignore_score:
  72         return
  73     for i, item in enumerate(preds):
  74         for ivn in item_val_name:
  75             if not (item[ivn].size(0) == item["labels"].size(0) == item["scores"].size(0)):
  76                 raise ValueError(
  77                     f"Input '{ivn}', labels and scores of sample {i} in predictions have a"
  78                     f" different length (expected {item[ivn].size(0)} labels and scores,"
  79                     f" got {item['labels'].size(0)} labels and {item['scores'].size(0)})"
  80                 )
  81 
  82 
  83 def _fix_empty_tensors(boxes: Tensor) -> Tensor:
  84     """Empty tensors can cause problems in DDP mode, this methods corrects them."""
  85     if boxes.numel() == 0 and boxes.ndim == 1:
  86         return boxes.unsqueeze(0)
  87     return boxes
  88 
  89 
  90 def _validate_iou_type_arg(iou_type: Union[Literal["bbox", "segm"], Tuple[str]] = "bbox") -> Tuple[str]:
  91     allowed_iou_types = ("segm", "bbox")
  92     if isinstance(iou_type, str):
  93         iou_type = (iou_type,)
  94     if any(tp not in allowed_iou_types for tp in iou_type):
  95         raise ValueError(
  96             f"Expected argument `iou_type` to be one of {allowed_iou_types} or a list of, but got {iou_type}"
  97         )
  98     return iou_type
  99
  ```
  --- 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/detection/helpers.py b/src/torchmetrics/detection/helpers.py
  --- a/src/torchmetrics/detection/helpers.py
  +++ b/src/torchmetrics/detection/helpers.py
  @@ -49,16 +49,16 @@
               raise ValueError(f"Expected all dicts in `target` to contain the `{k}` key")
   
       for ivn in item_val_name:
  -        if any(type(pred[ivn]) is not Tensor for pred in preds):
  +        if not all(isinstance(pred[ivn], Tensor) for pred in preds):
               raise ValueError(f"Expected all {ivn} in `preds` to be of type Tensor")
  -    if not ignore_score and any(type(pred["scores"]) is not Tensor for pred in preds):
  +    if not ignore_score and not all(isinstance(pred["scores"], Tensor) for pred in preds):
           raise ValueError("Expected all scores in `preds` to be of type Tensor")
  -    if any(type(pred["labels"]) is not Tensor for pred in preds):
  +    if not all(isinstance(pred["labels"], Tensor) for pred in preds):
           raise ValueError("Expected all labels in `preds` to be of type Tensor")
       for ivn in item_val_name:
  -        if any(type(target[ivn]) is not Tensor for target in targets):
  +        if not all(isinstance(target[ivn], Tensor) for target in targets):
               raise ValueError(f"Expected all {ivn} in `target` to be of type Tensor")
  -    if any(type(target["labels"]) is not Tensor for target in targets):
  +    if not all(isinstance(target["labels"], Tensor) for target in targets):
           raise ValueError("Expected all labels in `target` to be of type Tensor")
   
       for i, item in enumerate(targets):
  @@ -88,6 +88,7 @@
   
   
   def _validate_iou_type_arg(iou_type: Union[Literal["bbox", "segm"], Tuple[str]] = "bbox") -> Tuple[str]:
  +    """Validate that iou type argument is correct."""
       allowed_iou_types = ("segm", "bbox")
       if isinstance(iou_type, str):
           iou_type = (iou_type,)
  
verification_info: {"golden_diff": "diff --git a/src/torchmetrics/detection/helpers.py b/src/torchmetrics/detection/helpers.py\n--- a/src/torchmetrics/detection/helpers.py\n+++ b/src/torchmetrics/detection/helpers.py\n@@ -49,16 +49,16 @@\n             raise ValueError(f\"Expected all dicts in `target` to contain the `{k}` key\")\n \n     for ivn in item_val_name:\n-        if any(type(pred[ivn]) is not Tensor for pred in preds):\n+        if not all(isinstance(pred[ivn], Tensor) for pred in preds):\n             raise ValueError(f\"Expected all {ivn} in `preds` to be of type Tensor\")\n-    if not ignore_score and any(type(pred[\"scores\"]) is not Tensor for pred in preds):\n+    if not ignore_score and not all(isinstance(pred[\"scores\"], Tensor) for pred in preds):\n         raise ValueError(\"Expected all scores in `preds` to be of type Tensor\")\n-    if any(type(pred[\"labels\"]) is not Tensor for pred in preds):\n+    if not all(isinstance(pred[\"labels\"], Tensor) for pred in preds):\n         raise ValueError(\"Expected all labels in `preds` to be of type Tensor\")\n     for ivn in item_val_name:\n-        if any(type(target[ivn]) is not Tensor for target in targets):\n+        if not all(isinstance(target[ivn], Tensor) for target in targets):\n             raise ValueError(f\"Expected all {ivn} in `target` to be of type Tensor\")\n-    if any(type(target[\"labels\"]) is not Tensor for target in targets):\n+    if not all(isinstance(target[\"labels\"], Tensor) for target in targets):\n         raise ValueError(\"Expected all labels in `target` to be of type Tensor\")\n \n     for i, item in enumerate(targets):\n@@ -88,6 +88,7 @@\n \n \n def _validate_iou_type_arg(iou_type: Union[Literal[\"bbox\", \"segm\"], Tuple[str]] = \"bbox\") -> Tuple[str]:\n+    \"\"\"Validate that iou type argument is correct.\"\"\"\n     allowed_iou_types = (\"segm\", \"bbox\")\n     if isinstance(iou_type, str):\n         iou_type = (iou_type,)\n", "issue": "`MeanAveragePrecision` update method does not accept `torchvision.datapoints.BoundingBox` arguments\n## \ud83d\udc1b Bug\r\n\r\nWhen the `preds` or `target` arguments to `MeanAveragePrecision.update` method are not of the type `list[dict[str,Tensor]]` a ValueError is raised, but this precludes dictionary values of type `torchvision.datapoints.BoundingBox` which inherit from Tensor.  \r\n\r\nThe ValueError is raised in the function `torchmetrics.detection.helpers._input_validator`.  I think this could be fixed by changing all the lines like \r\n\r\n`if any(type(target[ivn]) is not Tensor for target in targets):`\r\n\r\nto be something like\r\n\r\n`if not all(isinstance(target[ivn], Tensor) for target in targets):`\r\n\r\nOne could also validate that the `BoundingBox.format` is the same as `MeanAveragePrecision.box_format` whenever a BoundingBox class is passed to the update method.\r\n\r\n### To Reproduce\r\n\r\n```python\r\nfrom torch import tensor\r\nfrom torchmetrics.detection import MeanAveragePrecision\r\nfrom torchvision.datapoints import BoundingBox, BoundingBoxFormat\r\n\r\npreds = [\r\n    dict(\r\n        boxes=tensor([[1.0, 2.0, 1.0, 2.0]]),\r\n        scores=tensor([0.1]),\r\n        labels=tensor([0]),\r\n    )\r\n]\r\n\r\nbounding_box = BoundingBox(\r\n    data=tensor([[1.0, 2.0, 1.0, 2.0]]),\r\n    format=BoundingBoxFormat.XYXY,\r\n    spatial_size=(100,100)\r\n)\r\ntarget = [\r\n    dict(\r\n        boxes=bounding_box,\r\n        labels=tensor([0]),\r\n    )\r\n]\r\n\r\nmetric = MeanAveragePrecision(iou_type='bbox', box_format='xyxy')\r\nmetric.update(preds, target)\r\n```\r\n\r\n### Expected behavior\r\n\r\n`MeanAveragePrecision.update` should accept `torchvision.datapoints.BoundingBox` arguments.\r\n\r\n### Environment\r\npython 3.11.4\r\npytorch 2.0.1\r\ntorchmetrics 1.2.0\r\ntorchvision 0.15.2\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.\nfrom typing import Dict, Literal, Sequence, Tuple, Union\n\nfrom torch import Tensor\n\n\ndef _input_validator(\n    preds: Sequence[Dict[str, Tensor]],\n    targets: Sequence[Dict[str, Tensor]],\n    iou_type: Union[Literal[\"bbox\", \"segm\"], Tuple[Literal[\"bbox\", \"segm\"]]] = \"bbox\",\n    ignore_score: bool = False,\n) -> None:\n    \"\"\"Ensure the correct input format of `preds` and `targets`.\"\"\"\n    if isinstance(iou_type, str):\n        iou_type = (iou_type,)\n\n    name_map = {\"bbox\": \"boxes\", \"segm\": \"masks\"}\n    if any(tp not in name_map for tp in iou_type):\n        raise Exception(f\"IOU type {iou_type} is not supported\")\n    item_val_name = [name_map[tp] for tp in iou_type]\n\n    if not isinstance(preds, Sequence):\n        raise ValueError(f\"Expected argument `preds` to be of type Sequence, but got {preds}\")\n    if not isinstance(targets, Sequence):\n        raise ValueError(f\"Expected argument `target` to be of type Sequence, but got {targets}\")\n    if len(preds) != len(targets):\n        raise ValueError(\n            f\"Expected argument `preds` and `target` to have the same length, but got {len(preds)} and {len(targets)}\"\n        )\n\n    for k in [*item_val_name, \"labels\"] + ([\"scores\"] if not ignore_score else []):\n        if any(k not in p for p in preds):\n            raise ValueError(f\"Expected all dicts in `preds` to contain the `{k}` key\")\n\n    for k in [*item_val_name, \"labels\"]:\n        if any(k not in p for p in targets):\n            raise ValueError(f\"Expected all dicts in `target` to contain the `{k}` key\")\n\n    for ivn in item_val_name:\n        if any(type(pred[ivn]) is not Tensor for pred in preds):\n            raise ValueError(f\"Expected all {ivn} in `preds` to be of type Tensor\")\n    if not ignore_score and any(type(pred[\"scores\"]) is not Tensor for pred in preds):\n        raise ValueError(\"Expected all scores in `preds` to be of type Tensor\")\n    if any(type(pred[\"labels\"]) is not Tensor for pred in preds):\n        raise ValueError(\"Expected all labels in `preds` to be of type Tensor\")\n    for ivn in item_val_name:\n        if any(type(target[ivn]) is not Tensor for target in targets):\n            raise ValueError(f\"Expected all {ivn} in `target` to be of type Tensor\")\n    if any(type(target[\"labels\"]) is not Tensor for target in targets):\n        raise ValueError(\"Expected all labels in `target` to be of type Tensor\")\n\n    for i, item in enumerate(targets):\n        for ivn in item_val_name:\n            if item[ivn].size(0) != item[\"labels\"].size(0):\n                raise ValueError(\n                    f\"Input '{ivn}' and labels of sample {i} in targets have a\"\n                    f\" different length (expected {item[ivn].size(0)} labels, got {item['labels'].size(0)})\"\n                )\n    if ignore_score:\n        return\n    for i, item in enumerate(preds):\n        for ivn in item_val_name:\n            if not (item[ivn].size(0) == item[\"labels\"].size(0) == item[\"scores\"].size(0)):\n                raise ValueError(\n                    f\"Input '{ivn}', labels and scores of sample {i} in predictions have a\"\n                    f\" different length (expected {item[ivn].size(0)} labels and scores,\"\n                    f\" got {item['labels'].size(0)} labels and {item['scores'].size(0)})\"\n                )\n\n\ndef _fix_empty_tensors(boxes: Tensor) -> Tensor:\n    \"\"\"Empty tensors can cause problems in DDP mode, this methods corrects them.\"\"\"\n    if boxes.numel() == 0 and boxes.ndim == 1:\n        return boxes.unsqueeze(0)\n    return boxes\n\n\ndef _validate_iou_type_arg(iou_type: Union[Literal[\"bbox\", \"segm\"], Tuple[str]] = \"bbox\") -> Tuple[str]:\n    allowed_iou_types = (\"segm\", \"bbox\")\n    if isinstance(iou_type, str):\n        iou_type = (iou_type,)\n    if any(tp not in allowed_iou_types for tp in iou_type):\n        raise ValueError(\n            f\"Expected argument `iou_type` to be one of {allowed_iou_types} or a list of, but got {iou_type}\"\n        )\n    return iou_type\n", "path": "src/torchmetrics/detection/helpers.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.\nfrom typing import Dict, Literal, Sequence, Tuple, Union\n\nfrom torch import Tensor\n\n\ndef _input_validator(\n    preds: Sequence[Dict[str, Tensor]],\n    targets: Sequence[Dict[str, Tensor]],\n    iou_type: Union[Literal[\"bbox\", \"segm\"], Tuple[Literal[\"bbox\", \"segm\"]]] = \"bbox\",\n    ignore_score: bool = False,\n) -> None:\n    \"\"\"Ensure the correct input format of `preds` and `targets`.\"\"\"\n    if isinstance(iou_type, str):\n        iou_type = (iou_type,)\n\n    name_map = {\"bbox\": \"boxes\", \"segm\": \"masks\"}\n    if any(tp not in name_map for tp in iou_type):\n        raise Exception(f\"IOU type {iou_type} is not supported\")\n    item_val_name = [name_map[tp] for tp in iou_type]\n\n    if not isinstance(preds, Sequence):\n        raise ValueError(f\"Expected argument `preds` to be of type Sequence, but got {preds}\")\n    if not isinstance(targets, Sequence):\n        raise ValueError(f\"Expected argument `target` to be of type Sequence, but got {targets}\")\n    if len(preds) != len(targets):\n        raise ValueError(\n            f\"Expected argument `preds` and `target` to have the same length, but got {len(preds)} and {len(targets)}\"\n        )\n\n    for k in [*item_val_name, \"labels\"] + ([\"scores\"] if not ignore_score else []):\n        if any(k not in p for p in preds):\n            raise ValueError(f\"Expected all dicts in `preds` to contain the `{k}` key\")\n\n    for k in [*item_val_name, \"labels\"]:\n        if any(k not in p for p in targets):\n            raise ValueError(f\"Expected all dicts in `target` to contain the `{k}` key\")\n\n    for ivn in item_val_name:\n        if not all(isinstance(pred[ivn], Tensor) for pred in preds):\n            raise ValueError(f\"Expected all {ivn} in `preds` to be of type Tensor\")\n    if not ignore_score and not all(isinstance(pred[\"scores\"], Tensor) for pred in preds):\n        raise ValueError(\"Expected all scores in `preds` to be of type Tensor\")\n    if not all(isinstance(pred[\"labels\"], Tensor) for pred in preds):\n        raise ValueError(\"Expected all labels in `preds` to be of type Tensor\")\n    for ivn in item_val_name:\n        if not all(isinstance(target[ivn], Tensor) for target in targets):\n            raise ValueError(f\"Expected all {ivn} in `target` to be of type Tensor\")\n    if not all(isinstance(target[\"labels\"], Tensor) for target in targets):\n        raise ValueError(\"Expected all labels in `target` to be of type Tensor\")\n\n    for i, item in enumerate(targets):\n        for ivn in item_val_name:\n            if item[ivn].size(0) != item[\"labels\"].size(0):\n                raise ValueError(\n                    f\"Input '{ivn}' and labels of sample {i} in targets have a\"\n                    f\" different length (expected {item[ivn].size(0)} labels, got {item['labels'].size(0)})\"\n                )\n    if ignore_score:\n        return\n    for i, item in enumerate(preds):\n        for ivn in item_val_name:\n            if not (item[ivn].size(0) == item[\"labels\"].size(0) == item[\"scores\"].size(0)):\n                raise ValueError(\n                    f\"Input '{ivn}', labels and scores of sample {i} in predictions have a\"\n                    f\" different length (expected {item[ivn].size(0)} labels and scores,\"\n                    f\" got {item['labels'].size(0)} labels and {item['scores'].size(0)})\"\n                )\n\n\ndef _fix_empty_tensors(boxes: Tensor) -> Tensor:\n    \"\"\"Empty tensors can cause problems in DDP mode, this methods corrects them.\"\"\"\n    if boxes.numel() == 0 and boxes.ndim == 1:\n        return boxes.unsqueeze(0)\n    return boxes\n\n\ndef _validate_iou_type_arg(iou_type: Union[Literal[\"bbox\", \"segm\"], Tuple[str]] = \"bbox\") -> Tuple[str]:\n    \"\"\"Validate that iou type argument is correct.\"\"\"\n    allowed_iou_types = (\"segm\", \"bbox\")\n    if isinstance(iou_type, str):\n        iou_type = (iou_type,)\n    if any(tp not in allowed_iou_types for tp in iou_type):\n        raise ValueError(\n            f\"Expected argument `iou_type` to be one of {allowed_iou_types} or a list of, but got {iou_type}\"\n        )\n    return iou_type\n", "path": "src/torchmetrics/detection/helpers.py"}]}