Compute Covariance#

covariance #

FUNCTION DESCRIPTION
covariance_diag

Compute diagonal covariance matrices for each component.

covariance_spherical

Compute spherical covariance matrices for each component.

covariance_full

Compute full covariance matrices for each component.

covariance_tied

Compute tied covariance matrix shared across all components.

log_prob_gaussian_diag

Compute log probabilities for Gaussian mixture with diagonal covariances.

log_prob_gaussian_spherical

Compute log probabilities for Gaussian mixture with spherical covariances.

log_prob_gaussian_full

Compute log probabilities for Gaussian mixture with full covariances.

log_prob_gaussian_tied

Compute log probabilities for Gaussian mixture with tied covariance.

Functions#

covariance_diag(X: torch.Tensor, resp: torch.Tensor, weights: torch.Tensor, means: torch.Tensor, eps: float = torch.finfo(torch.float32).eps) -> torch.Tensor #

Compute diagonal covariance matrices for each component.

PARAMETER DESCRIPTION
X

Data tensor of shape (n_samples, n_features).

TYPE: Tensor

resp

Responsibilities of shape (n_samples, n_components).

TYPE: Tensor

weights

Sample weights of shape (n_samples,).

TYPE: Tensor

means

Component means of shape (n_components, n_features).

TYPE: Tensor

eps

Regularization term for numerical stability.

TYPE: float, by default torch.finfo(torch.float32).eps DEFAULT: eps

RETURNS DESCRIPTION
Tensor

Diagonal covariances of shape (n_components, n_features).

Source code in spectre/compute/covariance.py
def covariance_diag(
    X: torch.Tensor,
    resp: torch.Tensor,
    weights: torch.Tensor,
    means: torch.Tensor,
    eps: float = torch.finfo(torch.float32).eps,
) -> torch.Tensor:
    """
    Compute diagonal covariance matrices for each component.

    Parameters
    ----------
    X : torch.Tensor
        Data tensor of shape (n_samples, n_features).
    resp : torch.Tensor
        Responsibilities of shape (n_samples, n_components).
    weights : torch.Tensor
        Sample weights of shape (n_samples,).
    means : torch.Tensor
        Component means of shape (n_components, n_features).
    eps : float, by default torch.finfo(torch.float32).eps
        Regularization term for numerical stability.

    Returns
    -------
    torch.Tensor
        Diagonal covariances of shape (n_components, n_features).
    """
    resp_weighted = resp * weights.unsqueeze(1)
    nk = resp_weighted.sum(dim=0).clamp(min=eps)
    diag_cov = (resp_weighted.T @ (X * X)) / nk.unsqueeze(1) - means**2 + eps
    return diag_cov

covariance_spherical(X: torch.Tensor, resp: torch.Tensor, weights: torch.Tensor, means: torch.Tensor, eps: float = torch.finfo(torch.float32).eps) -> torch.Tensor #

Compute spherical covariance matrices for each component.

PARAMETER DESCRIPTION
X

Data tensor of shape (n_samples, n_features).

TYPE: Tensor

resp

Responsibilities of shape (n_samples, n_components).

TYPE: Tensor

weights

Sample weights of shape (n_samples,).

TYPE: Tensor

means

Component means of shape (n_components, n_features).

TYPE: Tensor

eps

Regularization term for numerical stability.

TYPE: float, by default torch.finfo(torch.float32).eps DEFAULT: eps

RETURNS DESCRIPTION
Tensor

Spherical covariances of shape (n_components, 1).

Source code in spectre/compute/covariance.py
def covariance_spherical(
    X: torch.Tensor,
    resp: torch.Tensor,
    weights: torch.Tensor,
    means: torch.Tensor,
    eps: float = torch.finfo(torch.float32).eps,
) -> torch.Tensor:
    """
    Compute spherical covariance matrices for each component.

    Parameters
    ----------
    X : torch.Tensor
        Data tensor of shape (n_samples, n_features).
    resp : torch.Tensor
        Responsibilities of shape (n_samples, n_components).
    weights : torch.Tensor
        Sample weights of shape (n_samples,).
    means : torch.Tensor
        Component means of shape (n_components, n_features).
    eps : float, by default torch.finfo(torch.float32).eps
        Regularization term for numerical stability.

    Returns
    -------
    torch.Tensor
        Spherical covariances of shape (n_components, 1).
    """
    resp_weighted = resp * weights.unsqueeze(1)
    nk = resp_weighted.sum(dim=0).clamp(min=eps)
    sph_cov = ((resp_weighted.T @ (X * X)) / nk.unsqueeze(1) - means * means).mean(
        dim=1, keepdim=True
    ) + eps
    return sph_cov

covariance_full(X: torch.Tensor, resp: torch.Tensor, weights: torch.Tensor, means: torch.Tensor, eps: float = torch.finfo(torch.float32).eps) -> torch.Tensor #

Compute full covariance matrices for each component.

PARAMETER DESCRIPTION
X

Data tensor of shape (n_samples, n_features).

TYPE: Tensor

resp

Responsibilities of shape (n_samples, n_components).

TYPE: Tensor

weights

Sample weights of shape (n_samples,).

TYPE: Tensor

means

Component means of shape (n_components, n_features).

TYPE: Tensor

eps

Regularization term for numerical stability.

TYPE: float, by default torch.finfo(torch.float32).eps DEFAULT: eps

RETURNS DESCRIPTION
Tensor

Full covariances of shape (n_components, n_features, n_features).

Source code in spectre/compute/covariance.py
def covariance_full(
    X: torch.Tensor,
    resp: torch.Tensor,
    weights: torch.Tensor,
    means: torch.Tensor,
    eps: float = torch.finfo(torch.float32).eps,
) -> torch.Tensor:
    """
    Compute full covariance matrices for each component.

    Parameters
    ----------
    X : torch.Tensor
        Data tensor of shape (n_samples, n_features).
    resp : torch.Tensor
        Responsibilities of shape (n_samples, n_components).
    weights : torch.Tensor
        Sample weights of shape (n_samples,).
    means : torch.Tensor
        Component means of shape (n_components, n_features).
    eps : float, by default torch.finfo(torch.float32).eps
        Regularization term for numerical stability.

    Returns
    -------
    torch.Tensor
        Full covariances of shape (n_components, n_features, n_features).
    """
    resp_weighted = resp * weights.unsqueeze(1)
    nk = resp_weighted.sum(dim=0).clamp(min=eps)

    diff = X.unsqueeze(1) - means.unsqueeze(0)
    weighted_diff = diff * resp_weighted.unsqueeze(2).sqrt()
    weighted_diff_T = weighted_diff.permute(1, 0, 2)

    full_cov = torch.bmm(weighted_diff_T.transpose(-2, -1), weighted_diff_T) / nk.view(
        -1, 1, 1
    )

    n_features = X.shape[1]
    eye = torch.eye(n_features, device=X.device).unsqueeze(0)
    full_cov += eps * eye

    return full_cov

covariance_tied(X: torch.Tensor, resp: torch.Tensor, weights: torch.Tensor, means: torch.Tensor, eps: float = torch.finfo(torch.float32).eps) -> torch.Tensor #

Compute tied covariance matrix shared across all components.

PARAMETER DESCRIPTION
X

Data tensor of shape (n_samples, n_features).

TYPE: Tensor

resp

Responsibilities of shape (n_samples, n_components).

TYPE: Tensor

weights

Sample weights of shape (n_samples,).

TYPE: Tensor

means

Component means of shape (n_components, n_features).

TYPE: Tensor

eps

Regularization term for numerical stability.

TYPE: float, by default torch.finfo(torch.float32).eps DEFAULT: eps

RETURNS DESCRIPTION
Tensor

Tied covariance of shape (n_features, n_features).

Source code in spectre/compute/covariance.py
def covariance_tied(
    X: torch.Tensor,
    resp: torch.Tensor,
    weights: torch.Tensor,
    means: torch.Tensor,
    eps: float = torch.finfo(torch.float32).eps,
) -> torch.Tensor:
    """
    Compute tied covariance matrix shared across all components.

    Parameters
    ----------
    X : torch.Tensor
        Data tensor of shape (n_samples, n_features).
    resp : torch.Tensor
        Responsibilities of shape (n_samples, n_components).
    weights : torch.Tensor
        Sample weights of shape (n_samples,).
    means : torch.Tensor
        Component means of shape (n_components, n_features).
    eps : float, by default torch.finfo(torch.float32).eps
        Regularization term for numerical stability.

    Returns
    -------
    torch.Tensor
        Tied covariance of shape (n_features, n_features).
    """
    resp_weighted = resp * weights.unsqueeze(1)
    nk = resp_weighted.sum(dim=0).clamp(min=eps)

    diff = X.unsqueeze(1) - means.unsqueeze(0)
    weighted_diff = diff * resp_weighted.unsqueeze(2).sqrt()
    weighted_diff_flat = weighted_diff.reshape(-1, X.shape[1])

    tied_cov = weighted_diff_flat.T @ weighted_diff_flat
    tied_cov /= nk.sum()
    tied_cov += eps * torch.eye(X.shape[1], device=X.device)

    return tied_cov

log_prob_gaussian_diag(X: torch.Tensor, means: torch.Tensor, covariances: torch.Tensor, weights: torch.Tensor, eps: float = torch.finfo(torch.float32).eps) -> torch.Tensor #

Compute log probabilities for Gaussian mixture with diagonal covariances.

PARAMETER DESCRIPTION
X

Data tensor of shape (n_samples, n_features).

TYPE: Tensor

means

Component means of shape (n_components, n_features).

TYPE: Tensor

covariances

Diagonal covariances of shape (n_components, n_features).

TYPE: Tensor

weights

Mixing weights of shape (n_components,).

TYPE: Tensor

eps

Regularization term for numerical stability.

TYPE: float, by default torch.finfo(torch.float32).eps DEFAULT: eps

RETURNS DESCRIPTION
Tensor

Log probabilities of shape (n_samples, n_components).

Source code in spectre/compute/covariance.py
def log_prob_gaussian_diag(
    X: torch.Tensor,
    means: torch.Tensor,
    covariances: torch.Tensor,
    weights: torch.Tensor,
    eps: float = torch.finfo(torch.float32).eps,
) -> torch.Tensor:
    """
    Compute log probabilities for Gaussian mixture with diagonal covariances.

    Parameters
    ----------
    X : torch.Tensor
        Data tensor of shape (n_samples, n_features).
    means : torch.Tensor
        Component means of shape (n_components, n_features).
    covariances : torch.Tensor
        Diagonal covariances of shape (n_components, n_features).
    weights : torch.Tensor
        Mixing weights of shape (n_components,).
    eps : float, by default torch.finfo(torch.float32).eps
        Regularization term for numerical stability.

    Returns
    -------
    torch.Tensor
        Log probabilities of shape (n_samples, n_components).
    """
    n_features = X.shape[1]
    log_2pi = torch.tensor(2.0 * torch.pi, device=X.device).log()

    diff = X.unsqueeze(1) - means.unsqueeze(0)
    precision = 1.0 / covariances.clamp(min=eps)
    log_prob = -0.5 * (diff * diff * precision.unsqueeze(0)).sum(dim=2)

    log_det = torch.log(covariances.clamp(min=eps)).sum(dim=1)
    log_prob -= 0.5 * (n_features * log_2pi + log_det).unsqueeze(0)

    return log_prob + torch.log(weights.clamp(min=eps)).unsqueeze(0)

log_prob_gaussian_spherical(X: torch.Tensor, means: torch.Tensor, covariances: torch.Tensor, weights: torch.Tensor, eps: float = torch.finfo(torch.float32).eps) -> torch.Tensor #

Compute log probabilities for Gaussian mixture with spherical covariances.

PARAMETER DESCRIPTION
X

Data tensor of shape (n_samples, n_features).

TYPE: Tensor

means

Component means of shape (n_components, n_features).

TYPE: Tensor

covariances

Spherical covariances of shape (n_components, 1).

TYPE: Tensor

weights

Mixing weights of shape (n_components,).

TYPE: Tensor

eps

Regularization term for numerical stability.

TYPE: float, by default torch.finfo(torch.float32).eps DEFAULT: eps

RETURNS DESCRIPTION
Tensor

Log probabilities of shape (n_samples, n_components).

Source code in spectre/compute/covariance.py
def log_prob_gaussian_spherical(
    X: torch.Tensor,
    means: torch.Tensor,
    covariances: torch.Tensor,
    weights: torch.Tensor,
    eps: float = torch.finfo(torch.float32).eps,
) -> torch.Tensor:
    """
    Compute log probabilities for Gaussian mixture with spherical covariances.

    Parameters
    ----------
    X : torch.Tensor
        Data tensor of shape (n_samples, n_features).
    means : torch.Tensor
        Component means of shape (n_components, n_features).
    covariances : torch.Tensor
        Spherical covariances of shape (n_components, 1).
    weights : torch.Tensor
        Mixing weights of shape (n_components,).
    eps : float, by default torch.finfo(torch.float32).eps
        Regularization term for numerical stability.

    Returns
    -------
    torch.Tensor
        Log probabilities of shape (n_samples, n_components).
    """
    n_features = X.shape[1]
    log_2pi = torch.tensor(2.0 * torch.pi, device=X.device).log()

    diff = X.unsqueeze(1) - means.unsqueeze(0)
    precision = 1.0 / covariances.clamp(min=eps)
    squared_dist = (diff * diff).sum(dim=2)
    log_prob = -0.5 * squared_dist * precision.squeeze(1).unsqueeze(0)

    log_det = n_features * torch.log(covariances.clamp(min=eps)).squeeze(1)
    log_prob -= 0.5 * (n_features * log_2pi + log_det).unsqueeze(0)

    return log_prob + torch.log(weights.clamp(min=eps)).unsqueeze(0)

log_prob_gaussian_full(X: torch.Tensor, means: torch.Tensor, covariances: torch.Tensor, weights: torch.Tensor, eps: float = torch.finfo(torch.float32).eps) -> torch.Tensor #

Compute log probabilities for Gaussian mixture with full covariances.

PARAMETER DESCRIPTION
X

Data tensor of shape (n_samples, n_features).

TYPE: Tensor

means

Component means of shape (n_components, n_features).

TYPE: Tensor

covariances

Full covariances of shape (n_components, n_features, n_features).

TYPE: Tensor

weights

Mixing weights of shape (n_components,).

TYPE: Tensor

eps

Regularization term for numerical stability.

TYPE: float, by default torch.finfo(torch.float32).eps DEFAULT: eps

RETURNS DESCRIPTION
Tensor

Log probabilities of shape (n_samples, n_components).

Source code in spectre/compute/covariance.py
def log_prob_gaussian_full(
    X: torch.Tensor,
    means: torch.Tensor,
    covariances: torch.Tensor,
    weights: torch.Tensor,
    eps: float = torch.finfo(torch.float32).eps,
) -> torch.Tensor:
    """
    Compute log probabilities for Gaussian mixture with full covariances.

    Parameters
    ----------
    X : torch.Tensor
        Data tensor of shape (n_samples, n_features).
    means : torch.Tensor
        Component means of shape (n_components, n_features).
    covariances : torch.Tensor
        Full covariances of shape (n_components, n_features, n_features).
    weights : torch.Tensor
        Mixing weights of shape (n_components,).
    eps : float, by default torch.finfo(torch.float32).eps
        Regularization term for numerical stability.

    Returns
    -------
    torch.Tensor
        Log probabilities of shape (n_samples, n_components).
    """
    n_samples, n_features = X.shape
    log_2pi = torch.tensor(2.0 * torch.pi, device=X.device).log()

    try:
        diff = X.unsqueeze(1) - means.unsqueeze(0)
        diff_T = diff.permute(1, 0, 2)

        L = torch.linalg.cholesky(covariances)
        y = torch.linalg.solve_triangular(L, diff_T.transpose(-2, -1), upper=False)

        quad = (y * y).sum(dim=1).T
        log_det = 2 * torch.log(torch.diagonal(L, dim1=-2, dim2=-1)).sum(dim=-1)
        log_prob = -0.5 * (quad + log_det.unsqueeze(0) + n_features * log_2pi)

    except torch.linalg.LinAlgError:
        log_prob = torch.zeros(n_samples, means.shape[0], device=X.device)

        for k in range(means.shape[0]):
            diff = X - means[k]
            cov = covariances[k]
            cov_reg = cov + eps * torch.eye(n_features, device=X.device)

            try:
                L_k = torch.linalg.cholesky(cov_reg)
                y = torch.linalg.solve_triangular(L_k, diff.T, upper=False)
                quad = (y * y).sum(dim=0)
                log_det = 2 * torch.log(torch.diag(L_k)).sum()
                log_prob[:, k] = -0.5 * (quad + log_det + n_features * log_2pi)
            except torch.linalg.LinAlgError:
                precision = torch.linalg.inv(cov_reg)
                log_det = torch.logdet(cov_reg)
                quad = (diff @ precision * diff).sum(dim=1)
                log_prob[:, k] = -0.5 * (quad + log_det + n_features * log_2pi)

    return log_prob + torch.log(weights.clamp(min=eps)).unsqueeze(0)

log_prob_gaussian_tied(X: torch.Tensor, means: torch.Tensor, covariances: torch.Tensor, weights: torch.Tensor, eps: float = 1e-07) -> torch.Tensor #

Compute log probabilities for Gaussian mixture with tied covariance.

PARAMETER DESCRIPTION
X

Data tensor of shape (n_samples, n_features).

TYPE: Tensor

means

Component means of shape (n_components, n_features).

TYPE: Tensor

covariances

Tied covariance of shape (n_features, n_features).

TYPE: Tensor

weights

Mixing weights of shape (n_components,).

TYPE: Tensor

eps

Regularization term for numerical stability.

TYPE: float, by default 1e-7 DEFAULT: 1e-07

RETURNS DESCRIPTION
Tensor

Log probabilities of shape (n_samples, n_components).

Source code in spectre/compute/covariance.py
def log_prob_gaussian_tied(
    X: torch.Tensor,
    means: torch.Tensor,
    covariances: torch.Tensor,
    weights: torch.Tensor,
    eps: float = 1e-7,
) -> torch.Tensor:
    """
    Compute log probabilities for Gaussian mixture with tied covariance.

    Parameters
    ----------
    X : torch.Tensor
        Data tensor of shape (n_samples, n_features).
    means : torch.Tensor
        Component means of shape (n_components, n_features).
    covariances : torch.Tensor
        Tied covariance of shape (n_features, n_features).
    weights : torch.Tensor
        Mixing weights of shape (n_components,).
    eps : float, by default 1e-7
        Regularization term for numerical stability.

    Returns
    -------
    torch.Tensor
        Log probabilities of shape (n_samples, n_components).
    """
    n_samples, n_features = X.shape
    n_components = means.shape[0]
    log_2pi = torch.tensor(2.0 * torch.pi, device=X.device).log()

    try:
        diff = X.unsqueeze(1) - means.unsqueeze(0)
        diff_T = diff.permute(1, 0, 2)

        L = torch.linalg.cholesky(covariances)
        log_det = 2 * torch.log(torch.diag(L)).sum()
        L_expanded = L.unsqueeze(0).expand(n_components, -1, -1)
        y = torch.linalg.solve_triangular(
            L_expanded, diff_T.transpose(-2, -1), upper=False
        )

        quad = (y * y).sum(dim=1).T
        log_prob = -0.5 * (quad + log_det + n_features * log_2pi)

    except torch.linalg.LinAlgError:
        cov_reg = covariances + eps * torch.eye(n_features, device=X.device)
        precision = torch.linalg.inv(cov_reg)
        log_det = torch.logdet(cov_reg)

        diff = X.unsqueeze(1) - means.unsqueeze(0)
        quad_form = (diff @ precision * diff).sum(dim=2)
        log_prob = -0.5 * (quad_form + log_det + n_features * log_2pi)

    return log_prob + torch.log(weights.clamp(min=eps)).unsqueeze(0)