Repository: scikit-learn/scikit-learn
File: sklearn/metrics/_classification.py (Python)

Code before:
                "`cohen_kappa_score` is otherwise agnostic to the order of `y1` and "
                "`y2`)."
            )
            raise ValueError(msg) from e
        raise

    xp, _, device_ = get_namespace_and_device(y1, y2)
    n_classes = confusion.shape[0]
    # array_api_strict only supports floating point dtypes for __truediv__
    # which is used below to compute `expected` as well as `k`. Therefore
    # we use the maximum floating point dtype available for relevant arrays
    # to avoid running into this problem.
    max_float_dtype = _max_precision_float_dtype(xp, device=device_)
    confusion = xp.astype(confusion, max_float_dtype, copy=False)
    sum0 = xp.sum(confusion, axis=0)
    sum1 = xp.sum(confusion, axis=1)
    # expected = xp.linalg.outer(sum0, sum1) / xp.sum(sum0)

    numerator = xp.linalg.outer(sum0, sum1)
    denominator = xp.sum(sum0)
    msg_zero_division = (
        "`y2` contains no labels that are presented in both `y1` and `labels`."
        "`cohen_kappa_score` is undefined and set to the value defined by "
        "the `replace_undefined_by` param, which defaults to `np.nan`."
    )
    if np.isclose(_convert_to_numpy(denominator, xp), 0):
        warnings.warn(msg_zero_division, UndefinedMetricWarning, stacklevel=2)
        return replace_undefined_by

    expected = numerator / denominator

    if weights is None:
        w_mat = xp.ones([n_classes, n_classes], dtype=max_float_dtype, device=device_)
        _fill_diagonal(w_mat, 0, xp=xp)
    else:  # "linear" or "quadratic"
        w_mat = xp.zeros([n_classes, n_classes], dtype=max_float_dtype, device=device_)
        w_mat += xp.arange(n_classes)
        if weights == "linear":
            w_mat = xp.abs(w_mat - w_mat.T)
        else:
            w_mat = (w_mat - w_mat.T) ** 2

    numerator = xp.sum(w_mat * confusion)
    denominator = xp.sum(w_mat * expected)
    msg_zero_division = (
        "`y1`, `y2` and `labels` have only one label in common. "
        "`cohen_kappa_score` is undefined and set to the value defined by the "
        "`replace_undefined_by` param, which defaults to `np.nan`."
    )
    if np.isclose(_convert_to_numpy(denominator, xp), 0):
        warnings.warn(msg_zero_division, UndefinedMetricWarning, stacklevel=2)

Review comment (suggestion):
(This is solving a merge conflict after array api was added to cohen_kappa_score:)

Converting `denominator` to numpy here (also below in li. 1046) (without replacement) because we only deal with an integer and don't need GPU support.

Instead using `if xpx.isclose(denominator, 0):` raises `ValueError: Arrays from two different devices (array_api_strict.Device('device1') and array_api_strict.Device('CPU_DEVICE')) can not be combined.` for `array_api_strict` which I don't recall what this means, but it seems it is not so important to find out or fix that, since converting to numpy looks like the better alternative to me.

Code after:
    # we use the maximum floating point dtype available for relevant arrays
    # to avoid running into this problem.
    max_float_dtype = _max_precision_float_dtype(xp, device=device_)
    confusion = xp.astype(confusion, max_float_dtype, copy=False)
    sum0 = xp.sum(confusion, axis=0)
    sum1 = xp.sum(confusion, axis=1)

    numerator = xp.linalg.outer(sum0, sum1)
    denominator = xp.sum(sum0)
    msg_zero_division = (
        "`y2` contains no labels that are present in both `y1` and `labels`."
        "`cohen_kappa_score` is undefined and set to the value defined by "
        f"the `replace_undefined_by` param, which is set to {replace_undefined_by}."
    )
    # exact equality is safe here, since denominator is a sum of positive terms:
    if denominator == 0:
        warnings.warn(msg_zero_division, UndefinedMetricWarning, stacklevel=2)
        return replace_undefined_by

    expected = numerator / denominator

    if weights is None:
        w_mat = xp.ones([n_classes, n_classes], dtype=max_float_dtype, device=device_)
        _fill_diagonal(w_mat, 0, xp=xp)
    else:  # "linear" or "quadratic"
        w_mat = xp.zeros([n_classes, n_classes], dtype=max_float_dtype, device=device_)
        w_mat += xp.arange(n_classes)
        if weights == "linear":
            w_mat = xp.abs(w_mat - w_mat.T)
        else:
            w_mat = (w_mat - w_mat.T) ** 2

    numerator = xp.sum(w_mat * confusion)
    denominator = xp.sum(w_mat * expected)
    msg_zero_division = (
        "`y1`, `y2` and `labels` have only one label in common. "
        "`cohen_kappa_score` is undefined and set to the value defined by the "
        f"the `replace_undefined_by` param, which is set to {replace_undefined_by}."
    )
    # exact equality is safe here, since denominator is a sum of positive terms:
    if denominator == 0:
        warnings.warn(msg_zero_division, UndefinedMetricWarning, stacklevel=2)
        return replace_undefined_by

    k = numerator / denominator
    return float(1 - k)


@validate_params(
    {
        "y_true": ["array-like", "sparse matrix"],