Repository: Lightning-AI/torchmetrics

Issue:
Sort currently does not support bool dtype on CUDA: Regression in version 0.7.3 +
## 🐛 Bug

When I try to compute MaP on the GPU with no predictions, I receive the error:`RuntimeError: Sort currently does not support bool dtype on CUDA.`

This occurs in `torchmetrics==0.7.3` and `torchmetrics==0.8.0`, the error does not occur in `0.7.2`.

### To Reproduce

Steps to reproduce the behavior...

```
Traceback (most recent call last):
  File "C:/Users/hughesc/OneDrive - Microsoft/Documents/Git/pytorch-accelerated/pytorch_accelerated/exp.py", line 24, in <module>
    print(map.compute())
  File "C:\Users\hughesc\Anaconda3\envs\accelerated-dev\lib\site-packages\torchmetrics\metric.py", line 440, in wrapped_func
    value = compute(*args, **kwargs)
  File "C:\Users\hughesc\Anaconda3\envs\accelerated-dev\lib\site-packages\torchmetrics\detection\mean_ap.py", line 765, in compute
    precisions, recalls = self._calculate(classes)
  File "C:\Users\hughesc\Anaconda3\envs\accelerated-dev\lib\site-packages\torchmetrics\detection\mean_ap.py", line 627, in _calculate
    recall, precision, scores = MeanAveragePrecision.__calculate_recall_precision_scores(
  File "C:\Users\hughesc\Anaconda3\envs\accelerated-dev\lib\site-packages\torchmetrics\detection\mean_ap.py", line 697, in __calculate_recall_precision_scores
    inds = torch.argsort(det_scores, descending=True)
RuntimeError: Sort currently does not support bool dtype on CUDA.
```

#### Code sample

```
import torch
from torchmetrics.detection import MeanAveragePrecision

if __name__ == '__main__':

    device = torch.device('cuda:0')

    preds = [
        dict( boxes=torch.tensor([], device=device),
        scores=torch.tensor([], device=device),
        labels=torch.tensor([], device=device),)
    ]

    targets = [
        dict(boxes=torch.tensor([[1., 2., 3., 4.]], device=device),
             scores=torch.tensor([0.8], device=device),
             labels=torch.tensor([1], device=device), )
    ]

    map = MeanAveragePrecision(class_metrics=True).to(device)

    map.update(preds, targets)

    print(map.compute())
```

### Expected behavior

I would like to be able to compute the metric on the GPU

### Environment

- OS (e.g., Linux): Linux & Windows
- Python & PyTorch Version (e.g., 1.0): Python 3.8 & 3.9, PyTorch 1.10
- How you installed PyTorch (`conda`, `pip`, build command if you used source): conda
- Any other relevant information:





Discussion:
Hi! thanks for your contribution!, great first issue!
Hi, @Chris-hughes10 - Great first issue! 🎉 I love the description and thanks for sharing the code for reproducing the bug.

I can confirm that this is a regression from release 0.7.2, IMO: an explicit cast from `torch.bool` to `torch.uint8` (on CUDA only) while applying `torch.argsort` should fix this. Looks like PyTorch doesn't support sorting for boolean dtypes on CUDA devices:

```cpp
// FIXME: remove this check once cub sort supports bool
TORCH_CHECK(self_dtype != ScalarType::Bool,
  "Sort currently does not support bool dtype on CUDA.");
```
https://github.com/pytorch/pytorch/blob/1a7e43be141ce01469d7605075cb1008bf19abd7/aten/src/ATen/native/cuda/Sort.cpp#L80

To maintainers: we don't test for boolean inputs (I only see floating point inputs) and this issue only comes when we pass empty preds.

@Chris-hughes10 : In case it's too important and cannot wait till Monday, please let me know. I can create a branch with the fix. I just want to wait for everyone's opinion on this, as they might have more context. :)

cc: @Borda @SkafteNicki @stancld for their inputs
@krshrimali mind sending PR with fix? 

Resolving patch:
diff --git a/torchmetrics/detection/mean_ap.py b/torchmetrics/detection/mean_ap.py
--- a/torchmetrics/detection/mean_ap.py
+++ b/torchmetrics/detection/mean_ap.py
@@ -694,7 +694,10 @@ def __calculate_recall_precision_scores(
 
         # different sorting method generates slightly different results.
         # mergesort is used to be consistent as Matlab implementation.
-        inds = torch.argsort(det_scores, descending=True)
+        # Sort in PyTorch does not support bool types on CUDA (yet, 1.11.0)
+        dtype = torch.uint8 if det_scores.is_cuda and det_scores.dtype is torch.bool else det_scores.dtype
+        # Explicitly cast to uint8 to avoid error for bool inputs on CUDA to argsort
+        inds = torch.argsort(det_scores.to(dtype), descending=True)
         det_scores_sorted = det_scores[inds]
 
         det_matches = torch.cat([e["dtMatches"][:, :max_det] for e in img_eval_cls_bbox], axis=1)[:, inds]


Test patch:
diff --git a/tests/detection/test_map.py b/tests/detection/test_map.py
--- a/tests/detection/test_map.py
+++ b/tests/detection/test_map.py
@@ -133,6 +133,25 @@
     ],
 )
 
+# Test empty preds case, to ensure bool inputs are properly casted to uint8
+# From https://github.com/PyTorchLightning/metrics/issues/981
+_inputs3 = Input(
+    preds=[
+        [
+            dict(boxes=torch.tensor([]), scores=torch.tensor([]), labels=torch.tensor([])),
+        ],
+    ],
+    target=[
+        [
+            dict(
+                boxes=torch.tensor([[1.0, 2.0, 3.0, 4.0]]),
+                scores=torch.tensor([0.8]),
+                labels=torch.tensor([1]),
+            ),
+        ],
+    ],
+)
+
 
 def _compare_fn(preds, target) -> dict:
     """Comparison function for map implementation.
@@ -283,7 +302,7 @@ def _move_to_gpu(input):
 
 @pytest.mark.skipif(_pytest_condition, reason="test requires that torchvision=>0.8.0 is installed")
 @pytest.mark.skipif(_gpu_test_condition, reason="test requires CUDA availability")
-@pytest.mark.parametrize("inputs", [_inputs, _inputs2])
+@pytest.mark.parametrize("inputs", [_inputs, _inputs2, _inputs3])
 def test_map_gpu(inputs):
     """Test predictions on single gpu."""
     metric = MeanAveragePrecision()