New clustering metrics

### Describe the workflow you want to enable

Scikit-learn defines three popular metrics for evaluating clustering performance when there are no ground-truth cluster labels: [sklearn.metrics.silhouette_score](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.silhouette_score.html), [sklearn.metrics.calinski_harabasz_score](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.calinski_harabasz_score.html) and [sklearn.metrics.davies_bouldin_score](https://scikit-learn.org/stable/modules/generated/sklearn.metrics.davies_bouldin_score.html). But there are lots of others, and it's previously been [discussed](https://github.com/scikit-learn/scikit-learn/discussions/21164) whether to integrate more into scikit-learn.

### Describe your proposed solution

I've implemented four relatively popular ones, using the same interface and code style as in https://github.com/scikit-learn/scikit-learn/blob/main/sklearn/metrics/cluster/_unsupervised.py. Would there be interest in integrating these into scikit-learn?

```python
import numpy as np
from itertools import combinations
from sklearn.utils import check_X_y
from sklearn.metrics.cluster._unsupervised import check_number_of_labels
from sklearn.preprocessing import LabelEncoder


def log_ss_ratio(X, labels):
    X, labels = check_X_y(X, labels)
    le = LabelEncoder()
    labels = le.fit_transform(labels)

    n_samples, _ = X.shape
    n_labels = len(le.classes_)

    check_number_of_labels(n_labels, n_samples)

    extra_disp, intra_disp = 0.0, 0.0
    mean = X.mean(axis=0)
    for k in range(n_labels):
        cluster_k = X[labels == k]
        mean_k = cluster_k.mean(axis=0)
        extra_disp += len(cluster_k) * ((mean_k - mean) ** 2).sum()
        intra_disp += ((cluster_k - mean_k) ** 2).sum()

    return np.log(extra_disp / intra_disp)


def ball_hall(X, labels):
    X, labels = check_X_y(X, labels)
    le = LabelEncoder()
    labels = le.fit_transform(labels)

    n_samples, _ = X.shape
    n_labels = len(le.classes_)

    check_number_of_labels(n_labels, n_samples)

    sum_mean_dispersions = 0

    for k in range(n_labels):
        cluster_k = X[labels == k]
        mean_k = cluster_k.mean(axis=0)
        intra_disp_cluster = ((cluster_k - mean_k) ** 2).sum()
        sum_mean_dispersions += intra_disp_cluster / len(cluster_k)

    return sum_mean_dispersions / n_labels


def banfeld_raftery(X, labels):
    X, labels = check_X_y(X, labels)
    le = LabelEncoder()
    labels = le.fit_transform(labels)

    n_samples, _ = X.shape
    n_labels = len(le.classes_)

    check_number_of_labels(n_labels, n_samples)

    br_index = 0
    for k in range(n_labels):
        cluster_k = X[labels == k]
        mean_k = cluster_k.mean(axis=0)
        intra_disp_cluster = ((cluster_k - mean_k) ** 2).sum()
        br_index += len(cluster_k) * \
                    np.log(intra_disp_cluster / len(cluster_k))

    return br_index


def ray_turi(X, labels):
    X, labels = check_X_y(X, labels)
    le = LabelEncoder()
    labels = le.fit_transform(labels)

    n_samples, _ = X.shape
    n_labels = len(le.classes_)

    check_number_of_labels(n_labels, n_samples)

    intra_disp = 0.0

    cluster_means = []
    for k in range(n_labels):
        cluster_k = X[labels == k]
        mean_k = cluster_k.mean(axis=0)
        intra_disp += ((cluster_k - mean_k) ** 2).sum()
        cluster_means.append(mean_k)

    min_cluster_mean_diff = min(
        ((mean_i - mean_j) ** 2).sum()
        for mean_i, mean_j in combinations(cluster_means, 2))

    return intra_disp / (min_cluster_mean_diff * n_samples)
```

### Describe alternatives you've considered, if relevant

_No response_

### Additional context

_No response_

COMMENT:
I'v arrived here from #28243 where I laid out the case for including DBCV by explaining how it fills a gap presented by the 3 current metrics which were also mentioned above

> "I would love to see more "unsupervised" (without the need in labels) clustering metrics, since current ones are not very flexible (they rely to heavily on SSq or L2 distance)."

> "Sadly all metrics described in OP rely on L2 distance from the "center". I was thinking about something like this"

That's exactly the problem I experienced when tasked with clustering non-spherical data in a practical setting based on the "conventional" metrics.
@glevv Just like you correctly demonstrated with your example, DBCV avoids the typical shortcoming as it does not rely on centroids and therefore does not make an implicit spherical assumption. Instead, it works based on MSTs, much like HDBSCAN does (my issue linked above goes into more detail on this as well).

My PR #28244 aims to include the implementation of DBCV that's shipped with https://github.com/scikit-learn-contrib/hdbscan, instead of being based on https://github.com/FelSiq/DBCV
> the HDBSCAN DBCV version also contains an optional performance improvement (can toggle via kwarg) based on [this](https://github.com/luis261/clustering-validity-comparison) benchmark of mine, which outperforms the default DBCV version (only demonstrated for low-dimensional data) by [avoiding excessive inflation of core distances](https://github.com/scikit-learn-contrib/hdbscan/pull/552)

> "I would like to work on adding the DBCV score. Tell me if anyone is already on it to not duplicate our work"

@Nylio-prog I was not aware of your PR before ramping up myself, sorry about that

Regardless of which implementation gets merged (if any), I believe my benchmark and the argument laid out in #28243 can provide helpful context to aid in the decision making process when it comes to adding this feature or not.
My argument for including DBCV is backed by an illustrative example as well as an experiment with a more systematic, empirical methodology according to [this paper](https://www.semanticscholar.org/paper/Relative-clustering-validity-criteria:-A-overview-Vendramin-Campello/ad93d3b55fb94827c4df45e9fc67c55a7d90d00b)

Labels: New Feature, Needs Decision - Include Feature