Repository: Lightning-AI/torchmetrics

Issue:
Add an option to switch distributions order in the KLDivergence.
## 🚀 Feature

<!-- A clear and concise description of the feature proposal -->

I want to add an option to switch the order of distributions in the KLDivergence.

Ex.

```py
kl_divergence(P, Q, order="pq") := P log (P / Q)  # default
kl_divergence(P, Q, order="qp") := Q log (Q / P)
```
### Motivation

<!-- Please outline the motivation for the proposal. Is your feature request related to a problem? e.g., I'm always frustrated when [...]. If this is related to another GitHub issue, please link here too -->


The current kl_divergence is defined as follows:

```py
kl_divergence(P, Q) := P log (P / Q)
```

This is inconsistent with other metrics regarding the order of arguments.

The documentation says that "P usually represents a distribution over data."
In other words, the P represents the target.

However, the first argument of the current kl_divergence is P (= target) and is inconsistent with other metrics expecting prediction as the first argument.

For example, the PyTorch KLDivLoss is consistent with the conventional interpretation, but the argument order is opposite of the tochmetrics version.

```py
# pytorch KLDivLoss
kl_divergence(y_pred, y_true) = y_true log (y_true / y_pred)

# https://pytorch.org/docs/stable/generated/torch.nn.KLDivLoss.html#torch.nn.KLDivLoss
```

This is confusing. 

There is a practical issue that makes it difficult to use KLDivergence in the MetricCollection because MetricCollection expects all metrics to accept arguments in the same order: (preds, target).

### Pitch

<!-- A clear and concise description of what you want to happen. -->

I want to enable to accept the argument (predictions, targets) in KLDivergence and use it in MetricCollection

Ex.

```py
metrics = MetricCollection([
    KLDivergence(order="qp"),
    MeanAbsoluteError(),
    ...
])
```

### Alternatives
<!-- A clear and concise description of any alternative solutions or features you've considered, if any. -->

Redefine to switch the argument meanings:

```
kl_divergence(Q, P) = P log (P / Q)
```

But this breaks backward compatibility.

### Additional context

<!-- Add any other context or screenshots about the feature request here. -->

no



Discussion:
Hi! thanks for your contribution!, great first issue!
@Borda opinions here? I think the better option is actually to switch the arguments for the metric to be consistent with the rest of torchmetrics and rename to `preds`, `target`. We then need to deprecate correctly.

Resolving patch:
diff --git a/src/torchmetrics/functional/regression/kl_divergence.py b/src/torchmetrics/functional/regression/kl_divergence.py
--- a/src/torchmetrics/functional/regression/kl_divergence.py
+++ b/src/torchmetrics/functional/regression/kl_divergence.py
@@ -20,6 +20,7 @@
 
 from torchmetrics.utilities.checks import _check_same_shape
 from torchmetrics.utilities.compute import _safe_xlogy
+from torchmetrics.utilities.prints import rank_zero_warn
 
 
 def _kld_update(p: Tensor, q: Tensor, log_prob: bool) -> Tuple[Tensor, int]:
@@ -91,6 +92,14 @@ def kl_divergence(
     over data and :math:`Q` is often a prior or approximation of :math:`P`. It should be noted that the KL divergence
     is a non-symmetrical metric i.e. :math:`D_{KL}(P||Q) \neq D_{KL}(Q||P)`.
 
+    .. warning::
+        The input order and naming in metric ``kl_divergence`` is set to be deprecated in v1.4 and changed in v1.5.
+        Input argument ``p`` will be renamed to ``target`` and will be moved to be the second argument of the metric.
+        Input argument ``q`` will be renamed to ``preds`` and will be moved to the first argument of the metric.
+        Thus, ``kl_divergence(p, q)`` will equal ``kl_divergence(target=q, preds=p)`` in the future to be consistent
+        with the rest of ``torchmetrics``. From v1.4 the two new arguments will be added as keyword arguments and
+        from v1.5 the two old arguments will be removed.
+
     Args:
         p: data distribution with shape ``[N, d]``
         q: prior or approximate distribution with shape ``[N, d]``
@@ -111,5 +120,14 @@ def kl_divergence(
         tensor(0.0853)
 
     """
+    rank_zero_warn(
+        "The input order and naming in metric `kl_divergence` is set to be deprecated in v1.4 and changed in v1.5."
+        "Input argument `p` will be renamed to `target` and will be moved to be the second argument of the metric."
+        "Input argument `q` will be renamed to `preds` and will be moved to the first argument of the metric."
+        "Thus, `kl_divergence(p, q)` will equal `kl_divergence(target=q, preds=p)` in the future to be consistent with"
+        " the rest of torchmetrics. From v1.4 the two new arguments will be added as keyword arguments and from v1.5"
+        " the two old arguments will be removed.",
+        DeprecationWarning,
+    )
     measures, total = _kld_update(p, q, log_prob)
     return _kld_compute(measures, total, reduction)
diff --git a/src/torchmetrics/regression/kl_divergence.py b/src/torchmetrics/regression/kl_divergence.py
--- a/src/torchmetrics/regression/kl_divergence.py
+++ b/src/torchmetrics/regression/kl_divergence.py
@@ -22,6 +22,7 @@
 from torchmetrics.utilities.data import dim_zero_cat
 from torchmetrics.utilities.imports import _MATPLOTLIB_AVAILABLE
 from torchmetrics.utilities.plot import _AX_TYPE, _PLOT_OUT_TYPE
+from torchmetrics.utilities.prints import rank_zero_warn
 
 if not _MATPLOTLIB_AVAILABLE:
     __doctest_skip__ = ["KLDivergence.plot"]
@@ -46,6 +47,14 @@ class KLDivergence(Metric):
 
     - ``kl_divergence`` (:class:`~torch.Tensor`): A tensor with the KL divergence
 
+    .. warning::
+        The input order and naming in metric ``KLDivergence`` is set to be deprecated in v1.4 and changed in v1.5.
+        Input argument ``p`` will be renamed to ``target`` and will be moved to be the second argument of the metric.
+        Input argument ``q`` will be renamed to ``preds`` and will be moved to the first argument of the metric.
+        Thus, ``KLDivergence(p, q)`` will equal ``KLDivergence(target=q, preds=p)`` in the future to be consistent
+        with the rest of ``torchmetrics``. From v1.4 the two new arguments will be added as keyword arguments and
+        from v1.5 the two old arguments will be removed.
+
     Args:
         log_prob: bool indicating if input is log-probabilities or probabilities. If given as probabilities,
             will normalize to make sure the distributes sum to 1.
@@ -92,6 +101,15 @@ def __init__(
         reduction: Literal["mean", "sum", "none", None] = "mean",
         **kwargs: Any,
     ) -> None:
+        rank_zero_warn(
+            "The input order and naming in metric `KLDivergence` is set to be deprecated in v1.4 and changed in v1.5."
+            "Input argument `p` will be renamed to `target` and will be moved to be the second argument of the metric."
+            "Input argument `q` will be renamed to `preds` and will be moved to the first argument of the metric."
+            "Thus, `KLDivergence(p, q)` will equal `KLDivergence(target=q, preds=p)` in the future to be consistent"
+            " with the rest of torchmetrics. From v1.4 the two new arguments will be added as keyword arguments and"
+            " from v1.5 the two old arguments will be removed.",
+            DeprecationWarning,
+        )
         super().__init__(**kwargs)
         if not isinstance(log_prob, bool):
             raise TypeError(f"Expected argument `log_prob` to be bool but got {log_prob}")


Test patch:
diff --git a/tests/unittests/test_deprecated.py b/tests/unittests/test_deprecated.py
new file mode 100644
--- /dev/null
+++ b/tests/unittests/test_deprecated.py
@@ -0,0 +1,16 @@
+import pytest
+import torch
+from torchmetrics.functional.regression import kl_divergence
+from torchmetrics.regression import KLDivergence
+
+
+def test_deprecated_kl_divergence_input_order():
+    """Ensure that the deprecated input order for kl_divergence raises a warning."""
+    preds = torch.randn(10, 2)
+    target = torch.randn(10, 2)
+
+    with pytest.deprecated_call(match="The input order and naming in metric `kl_divergence` is set to be deprecated.*"):
+        kl_divergence(preds, target)
+
+    with pytest.deprecated_call(match="The input order and naming in metric `KLDivergence` is set to be deprecated.*"):
+        KLDivergence()