Repository: Lightning-AI/torchmetrics

Issue:
BinaryMatthewsCorrCoef  implementation for extreme cases is wrong
The formula which was suggested in the [paper](https://bmcgenomics.biomedcentral.com/articles/10.1186/s12864-019-6413-7) might be wrong (#1812)

### Number of false negatives / MCC (torchmetrics)
```python
n = 1000
mcc_scores = []
targets = torch.Tensor([1] * n)
for i in range(n+1):
    preds = torch.Tensor([1] * (n - i) + [0] * i)
    mcc_scores.append(mcc_torch(preds, targets))
plt.ylim(-1, 1)
plt.plot(range(n+1), mcc_scores)
```
![image](https://github.com/Lightning-AI/torchmetrics/assets/68476180/90d81d60-7042-413f-848f-f74af8a2501e)
As we can see, even for the worst case, the metric is not reaching to -1

### Number of false positives / MCC (torchmetrics)
```python
n = 1000
mcc_scores = []
targets = torch.Tensor([0] * n)
for i in range(n+1):
    preds = torch.Tensor([0] * (n - i) + [1] * i)
    mcc_scores.append(mcc_torch(preds, targets))
plt.ylim(-1, 1)
plt.plot(range(n+1), mcc_scores)
```
![4a3f95bc-dccb-43ff-8689-cc7119d294f3](https://github.com/Lightning-AI/torchmetrics/assets/68476180/4762efb2-0053-488d-b89f-f275ca961303)
MCC metric should be same even if we switch the positive and negative case. But as we can clearly see above, If the input contains all `True negatives`, the MCC is -1.

Kindly revert to the previous version.



Discussion:

Resolving patch:
diff --git a/src/torchmetrics/functional/classification/matthews_corrcoef.py b/src/torchmetrics/functional/classification/matthews_corrcoef.py
--- a/src/torchmetrics/functional/classification/matthews_corrcoef.py
+++ b/src/torchmetrics/functional/classification/matthews_corrcoef.py
@@ -44,10 +44,10 @@ def _matthews_corrcoef_reduce(confmat: Tensor) -> Tensor:
 
     if confmat.numel() == 4:  # binary case
         tn, fp, fn, tp = confmat.reshape(-1)
-        if tp != 0 and tn == 0 and fp == 0 and fn == 0:
+        if tp + tn != 0 and fp + fn == 0:
             return torch.tensor(1.0, dtype=confmat.dtype, device=confmat.device)
 
-        if tp == 0 and tn != 0 and fp == 0 and fn == 0:
+        if tp + tn == 0 and fp + fn != 0:
             return torch.tensor(-1.0, dtype=confmat.dtype, device=confmat.device)
 
     tk = confmat.sum(dim=-1).float()
@@ -71,7 +71,7 @@ def _matthews_corrcoef_reduce(confmat: Tensor) -> Tensor:
 
         eps = torch.tensor(torch.finfo(torch.float32).eps, dtype=torch.float32, device=confmat.device)
         numerator = torch.sqrt(eps) * (a - b)
-        denom = torch.sqrt(2 * (a + b) * (a + eps) * (b + eps))
+        denom = (tp + fp + eps) * (tp + fn + eps) * (tn + fp + eps) * (tn + fn + eps)
     elif denom == 0:
         return torch.tensor(0, dtype=confmat.dtype, device=confmat.device)
     return numerator / torch.sqrt(denom)


Test patch:
diff --git a/tests/unittests/classification/test_matthews_corrcoef.py b/tests/unittests/classification/test_matthews_corrcoef.py
--- a/tests/unittests/classification/test_matthews_corrcoef.py
+++ b/tests/unittests/classification/test_matthews_corrcoef.py
@@ -321,7 +321,7 @@ def test_zero_case_in_multiclass():
 @pytest.mark.parametrize(
     ("metric_fn", "preds", "target", "expected"),
     [
-        (binary_matthews_corrcoef, torch.zeros(10), torch.zeros(10), -1.0),
+        (binary_matthews_corrcoef, torch.zeros(10), torch.zeros(10), 1.0),
         (binary_matthews_corrcoef, torch.ones(10), torch.ones(10), 1.0),
         (
             binary_matthews_corrcoef,
@@ -329,11 +329,13 @@ def test_zero_case_in_multiclass():
             torch.tensor([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]),
             0.0,
         ),
+        (binary_matthews_corrcoef, torch.zeros(10), torch.ones(10), -1.0),
+        (binary_matthews_corrcoef, torch.ones(10), torch.zeros(10), -1.0),
         (
             partial(multilabel_matthews_corrcoef, num_labels=NUM_CLASSES),
             torch.zeros(10, NUM_CLASSES).long(),
             torch.zeros(10, NUM_CLASSES).long(),
-            -1.0,
+            1.0,
         ),
         (
             partial(multilabel_matthews_corrcoef, num_labels=NUM_CLASSES),
@@ -341,6 +343,18 @@ def test_zero_case_in_multiclass():
             torch.ones(10, NUM_CLASSES).long(),
             1.0,
         ),
+        (
+            partial(multilabel_matthews_corrcoef, num_labels=NUM_CLASSES),
+            torch.zeros(10, NUM_CLASSES).long(),
+            torch.ones(10, NUM_CLASSES).long(),
+            -1.0,
+        ),
+        (
+            partial(multilabel_matthews_corrcoef, num_labels=NUM_CLASSES),
+            torch.ones(10, NUM_CLASSES).long(),
+            torch.zeros(10, NUM_CLASSES).long(),
+            -1.0,
+        ),
     ],
 )
 def test_corner_cases(metric_fn, preds, target, expected):