def class_scores(
    image_embeddings: torch.Tensor,
    text_embeddings: torch.Tensor,
    temperature: float = 1.0,
    target_class: int | None = None,
) -> torch.Tensor:
    """Compute class scores between image embeddings and text embeddings.

    Computes similarity scores between image and text embeddings by first calculating
    cosine similarity and then applying temperature scaling and softmax. This follows
    Equation (1) in the WinCLIP paper.

    Each text embedding represents a prompt for a specific class. The similarity matrix
    is used to compute confidence scores for each class.

    Args:
        image_embeddings (torch.Tensor): Image embeddings with shape ``(N, D)`` or
            ``(B, N, D)``, where:
            - ``B`` is optional batch dimension
            - ``N`` is number of image embeddings
            - ``D`` is embedding dimension
        text_embeddings (torch.Tensor): Text embeddings with shape ``(M, D)`` or
            ``(B, M, D)``, where:
            - ``B`` is optional batch dimension
            - ``M`` is number of text embeddings
            - ``D`` is embedding dimension (must match image embeddings)
        temperature (float, optional): Temperature scaling parameter. Higher values
            make distribution more uniform, lower values make it more peaked.
            Defaults to ``1.0``.
        target_class (int | None, optional): Index of target class. If provided,
            returns scores only for that class. Defaults to ``None``.

    Returns:
        torch.Tensor: Class similarity scores. Shape depends on inputs and
            ``target_class``:
            - If no target class: ``(N, M)`` or ``(B, N, M)``
            - If target class specified: ``(N,)`` or ``(B, N)``

    Examples:
        Basic usage with 2D inputs:

        >>> image_embeddings = torch.tensor([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
        >>> text_embeddings = torch.tensor([[0.0, 1.0, 0.0], [1.0, 1.0, 0.0]])
        >>> class_scores(image_embeddings, text_embeddings)
        tensor([[0.3302, 0.6698],
                [0.5727, 0.4273]])

        With different sized inputs:

        >>> image_embeddings = torch.randn(100, 128)  # 100 vectors
        >>> text_embeddings = torch.randn(200, 128)  # 200 class prompts
        >>> class_scores(image_embeddings, text_embeddings).shape
        torch.Size([100, 200])

        With batched 3D inputs:

        >>> image_embeddings = torch.randn(10, 100, 128)  # 10 batches
        >>> text_embeddings = torch.randn(10, 200, 128)  # 10 batches
        >>> class_scores(image_embeddings, text_embeddings).shape
        torch.Size([10, 100, 200])

        With target class specified:

        >>> scores = class_scores(image_embeddings, text_embeddings, target_class=0)
        >>> scores.shape
        torch.Size([10, 100])
    """
    scores = (cosine_similarity(image_embeddings, text_embeddings) / temperature).softmax(dim=-1)
    if target_class is not None:
        return scores[..., target_class]
    return scores
Compute class scores between image embeddings and text embeddings.

    Computes similarity scores between image and text embeddings by first calculating
    cosine similarity and then applying temperature scaling and softmax. This follows
    Equation (1) in the WinCLIP paper.

    Each text embedding represents a prompt for a specific class. The similarity matrix
    is used to compute confidence scores for each class.

    Args:
        image_embeddings (torch.Tensor): Image embeddings with shape ``(N, D)`` or
            ``(B, N, D)``, where:
            - ``B`` is optional batch dimension
            - ``N`` is number of image embeddings
            - ``D`` is embedding dimension
        text_embeddings (torch.Tensor): Text embeddings with shape ``(M, D)`` or
            ``(B, M, D)``, where:
            - ``B`` is optional batch dimension
            - ``M`` is number of text embeddings
            - ``D`` is embedding dimension (must match image embeddings)
        temperature (float, optional): Temperature scaling parameter. Higher values
            make distribution more uniform, lower values make it more peaked.
            Defaults to ``1.0``.
        target_class (int | None, optional): Index of target class. If provided,
            returns scores only for that class. Defaults to ``None``.

    Returns:
        torch.Tensor: Class similarity scores. Shape depends on inputs and
            ``target_class``:
            - If no target class: ``(N, M)`` or ``(B, N, M)``
            - If target class specified: ``(N,)`` or ``(B, N)``

    Examples:
        Basic usage with 2D inputs:

        >>> image_embeddings = torch.tensor([[1.0, 0.0, 0.0], [0.0, 1.0, 0.0]])
        >>> text_embeddings = torch.tensor([[0.0, 1.0, 0.0], [1.0, 1.0, 0.0]])
        >>> class_scores(image_embeddings, text_embeddings)
        tensor([[0.3302, 0.6698],
                [0.5727, 0.4273]])

        With different sized inputs:

        >>> image_embeddings = torch.randn(100, 128)  # 100 vectors
        >>> text_embeddings = torch.randn(200, 128)  # 200 class prompts
        >>> class_scores(image_embeddings, text_embeddings).shape
        torch.Size([100, 200])

        With batched 3D inputs:

        >>> image_embeddings = torch.randn(10, 100, 128)  # 10 batches
        >>> text_embeddings = torch.randn(10, 200, 128)  # 10 batches
        >>> class_scores(image_embeddings, text_embeddings).shape
        torch.Size([10, 100, 200])

        With target class specified:

        >>> scores = class_scores(image_embeddings, text_embeddings, target_class=0)
        >>> scores.shape
        torch.Size([10, 100])
    
src/anomalib/models/image/winclip/utils.py