def is_binclf_curves(
    binclf_curves: Tensor,
    valid_thresholds: Tensor | None,
) -> None:
    """Validate binary classification curves tensor.

    Args:
        binclf_curves: Tensor of shape (N, T, 2, 2) containing confusion matrices
            for N images and T thresholds.
        valid_thresholds: Optional tensor of T threshold values.

    Raises:
        ValueError: If tensor has wrong shape or invalid values.
        TypeError: If tensor has wrong dtype.
        RuntimeError: If number of thresholds doesn't match.

    Example:
        >>> curves = torch.zeros(10, 5, 2, 2, dtype=torch.int64)
        >>> is_binclf_curves(curves, None)  # No error
        >>> bad_curves = torch.zeros(10, 5, 3, 2, dtype=torch.int64)
        >>> is_binclf_curves(bad_curves, None)  # Raises ValueError
    """
    if binclf_curves.ndim != 4:
        msg = f"Expected binclf curves to be 4D, but got {binclf_curves.ndim}D"
        raise ValueError(msg)

    if binclf_curves.shape[-2:] != (2, 2):
        msg = f"Expected binclf curves to have shape (..., 2, 2), but got {binclf_curves.shape}"
        raise ValueError(msg)

    if binclf_curves.dtype != torch.int64:
        msg = f"Expected binclf curves to have dtype int64, but got {binclf_curves.dtype}."
        raise TypeError(msg)

    if (binclf_curves < 0).any():
        msg = "Expected binclf curves to have non-negative values, but got negative values."
        raise ValueError(msg)

    neg = binclf_curves[:, :, 0, :].sum(dim=-1)  # (num_images, num_thresholds)

    if (neg != neg[:, :1]).any():
        msg = "Expected binclf curves to have the same number of negatives per image for every thresh."
        raise ValueError(msg)

    pos = binclf_curves[:, :, 1, :].sum(dim=-1)  # (num_images, num_thresholds)

    if (pos != pos[:, :1]).any():
        msg = "Expected binclf curves to have the same number of positives per image for every thresh."
        raise ValueError(msg)

    if valid_thresholds is None:
        return

    if binclf_curves.shape[1] != valid_thresholds.shape[0]:
        msg = (
            "Expected the binclf curves to have as many confusion matrices as "
            f"the thresholds sequence, but got {binclf_curves.shape[1]} and "
            f"{valid_thresholds.shape[0]}"
        )
        raise RuntimeError(msg)
Validate binary classification curves tensor.

    Args:
        binclf_curves: Tensor of shape (N, T, 2, 2) containing confusion matrices
            for N images and T thresholds.
        valid_thresholds: Optional tensor of T threshold values.

    Raises:
        ValueError: If tensor has wrong shape or invalid values.
        TypeError: If tensor has wrong dtype.
        RuntimeError: If number of thresholds doesn't match.

    Example:
        >>> curves = torch.zeros(10, 5, 2, 2, dtype=torch.int64)
        >>> is_binclf_curves(curves, None)  # No error
        >>> bad_curves = torch.zeros(10, 5, 3, 2, dtype=torch.int64)
        >>> is_binclf_curves(bad_curves, None)  # Raises ValueError
    
src/anomalib/metrics/pimo/_validate.py