Skip to content

Covariance regularization

Covariance regularization utilities are imported from gmm_divergence.covariance.

Covariance regularization strategies for covariance matrices are techniques used to ensure that the covariance matrices of the components in a GMM (or single Gaussians) are well-conditioned and do not lead to numerical instability during estimation. These strategies can help prevent issues such as singular covariance matrices, which can arise when the data is not sufficiently diverse or when there are too few data points relative to the number of parameters being estimated.

gmm_divergence.covariance

Public covariance regularization API.

Functions

regularize_covariance

regularize_covariance(
    covariance: ArrayLike,
    /,
    *,
    method: CovarianceRegularizer = "diagonal_loading",
    batched: Literal[False] = False,
    copy: bool = True,
) -> Covariance
regularize_covariance(
    covariance: ArrayLike,
    /,
    *,
    method: CovarianceRegularizer = "diagonal_loading",
    batched: Literal[True],
    copy: bool = True,
) -> Covariances
regularize_covariance(
    covariance: ArrayLike,
    /,
    *,
    method: CovarianceRegularizer = "diagonal_loading",
    batched: None = None,
    copy: bool = True,
) -> Covariance | Covariances
regularize_covariance(
    covariance: ArrayLike,
    /,
    *,
    method: CovarianceRegularizer = "diagonal_loading",
    batched: bool | None = None,
    copy: bool = True,
) -> Covariance | Covariances

Regularize a covariance matrix or a batch of covariance matrices.

The covariances are validated and regularized according to the specified method. The input can be either a single covariance matrix with shape (d, d)` or a batch of covariance matrices with shape `(n, d, d). If the batch flag is not explicitly provided, the shape of the input is used to infer whether it is batched or not.

Parameters:

  • covariance (array - like) –

    A covariance matrix with shape (d, d) or a batch with shape (n, d, d).

  • method (str or covariance regularizer configuration, default: "diagonal_loading" ) –

    Regularization method to apply. Passing a string uses the method's default configuration. DiagonalLoading and LowRank also accept epsilon heuristic strategy objects through their eps field.

  • batched (bool or None, default: None ) –

    Whether to interpret the input as batched. If None, the shape is inferred from the array rank.

  • copy (bool, default: True ) –

    Whether to regularize a copy of the input.

Returns:

  • Covariance or batch of covariances

    Regularized covariance matrix or matrices.

Source code in src/gmm_divergence/covariance/_api.py
def regularize_covariance(
    covariance: npt.ArrayLike,
    /,
    *,
    method: CovarianceRegularizer = "diagonal_loading",
    batched: bool | None = None,
    copy: bool = True,
) -> Covariance | Covariances:
    """Regularize a covariance matrix or a batch of covariance matrices.

    The covariances are validated and regularized according to the specified
    method. The input can be either a single covariance matrix with shape ``(d,
    d)` or a batch of covariance matrices with shape `(n, d, d)``. If the
    batch flag is not explicitly provided, the shape of the input is used to infer whether it is
    batched or not.

    Parameters
    ----------
    covariance : array-like
        A covariance matrix with shape `(d, d)` or a batch with shape
        `(n, d, d)`.
    method : str or covariance regularizer configuration, default="diagonal_loading"
        Regularization method to apply. Passing a string uses the method's
        default configuration. `DiagonalLoading` and `LowRank` also accept
        epsilon heuristic strategy objects through their `eps` field.
    batched : bool or None, default=None
        Whether to interpret the input as batched. If `None`, the shape is
        inferred from the array rank.
    copy : bool, default=True
        Whether to regularize a copy of the input.

    Returns
    -------
    Covariance or batch of covariances
        Regularized covariance matrix or matrices.
    """
    spec, options = _REGISTRY.resolve(method)

    match spec.name:
        case "diagonal_loading":
            options = cast_options(options, DiagonalLoading)
            return diagonal_loading(covariance, eps=options.eps, batched=batched, copy=copy)
        case "linear_shrinkage":
            options = cast_options(options, LinearShrinkage)
            return linear_shrinkage(covariance, alpha=options.alpha, batched=batched, copy=copy)
        case "diagonal_shrinkage":
            options = cast_options(options, DiagonalShrinkage)
            return diagonal_shrinkage(covariance, alpha=options.alpha, batched=batched, copy=copy)
        case "eigenvalue_clipping":
            options = cast_options(options, EigenvalueClipping)
            return eigenvalue_clipping(
                covariance, min_eigenvalue=options.min_eigenvalue, batched=batched, copy=copy
            )
        case "lowrank":
            options = cast_options(options, LowRank)
            return lowrank(
                covariance, rank=options.rank, eps=options.eps, batched=batched, copy=copy
            )
        case _:
            msg = "Unhandled covariance regularizer registry entry."
            raise AssertionError(msg)

estimate_epsilon

estimate_epsilon(
    covariance: ArrayLike,
    /,
    *,
    method: EpsilonMethod = "relative_trace",
    batched: Literal[False] = False,
) -> float
estimate_epsilon(
    covariance: ArrayLike,
    /,
    *,
    method: EpsilonMethod = "relative_trace",
    batched: Literal[True],
) -> FloatArray
estimate_epsilon(
    covariance: ArrayLike,
    /,
    *,
    method: EpsilonMethod = "relative_trace",
    batched: None = None,
) -> float | FloatArray
estimate_epsilon(
    covariance: ArrayLike,
    /,
    *,
    method: EpsilonMethod = "relative_trace",
    batched: bool | None = None,
) -> float | FloatArray

Estimate a diagonal-loading epsilon from covariance scale or spectrum.

Parameters:

  • covariance (array - like) –

    Covariance matrix with shape (d, d) or batch of matrices with shape (n, d, d).

  • method (str or epsilon heuristic configuration, default: "relative_trace" ) –

    Heuristic used to estimate the epsilon value.

  • batched (bool or None, default: None ) –

    Whether to interpret the input as batched. If None, the shape is inferred from the input rank.

Returns:

  • float or array

    Estimated epsilon value(s). If the input is a single covariance, a float is returned. If the input is a batch of covariances, an array of shape (n,) is returned with one epsilon per covariance.

Source code in src/gmm_divergence/covariance/_epsilon.py
def estimate_epsilon(
    covariance: npt.ArrayLike,
    /,
    *,
    method: EpsilonMethod = "relative_trace",
    batched: bool | None = None,
) -> float | FloatArray:
    """Estimate a diagonal-loading epsilon from covariance scale or spectrum.

    Parameters
    ----------
    covariance : array-like
        Covariance matrix with shape `(d, d)` or batch of matrices with shape
        `(n, d, d)`.
    method : str or epsilon heuristic configuration, default="relative_trace"
        Heuristic used to estimate the epsilon value.
    batched : bool or None, default=None
        Whether to interpret the input as batched. If `None`, the shape is
        inferred from the input rank.

    Returns
    -------
    float or array
        Estimated epsilon value(s). If the input is a single covariance, a float
        is returned. If the input is a batch of covariances, an array of shape
        `(n,)` is returned with one epsilon per covariance.
    """
    covariance_arr: FloatArray = np.asarray(covariance, dtype=np.float64)
    shape_kind = check_covariance_shape(covariance_arr, batched=batched)
    spec, options = _EPSILON_REGISTRY.resolve(method)

    match spec.name:
        case "relative_trace":
            options = cast_options(options, RelativeToTrace)
            return _relative_trace(covariance_arr, c=options.c, batched=shape_kind)
        case "target_condition_number":
            options = cast_options(options, TargetConditionNumber)
            return _target_condition_number(covariance_arr, kappa=options.kappa, batched=shape_kind)
        case "residual_variance":
            options = cast_options(options, ResidualVariance)
            return _residual_variance(
                covariance_arr, c=options.c, rank=options.r, batched=shape_kind
            )
        case _:
            msg = "Unhandled covariance epsilon heuristic registry entry."
            raise AssertionError(msg)

Regularizers

DiagonalLoading dataclass

DiagonalLoading(eps: EpsilonSpec = 1e-06)

Diagonal loading regularization configuration.

Applies the regularizer

\[ \Sigma_{\mathrm{reg}} = \Sigma + \varepsilon I, \]

where \(\Sigma\) is the input covariance, \(I\) is the identity matrix, and \(\varepsilon \ge 0\) is the diagonal loading amount.

eps class-attribute instance-attribute

eps: EpsilonSpec = 1e-06

Diagonal loading amount or epsilon heuristic.

LinearShrinkage dataclass

LinearShrinkage(alpha: float = 0.01)

Linear shrinkage toward an isotropic covariance target.

Applies the shrinkage

\[ \Sigma_{\mathrm{reg}} = (1-\alpha)\,\Sigma + \alpha\,\tau I, \qquad \tau = \frac{\mathrm{tr}(\Sigma)}{d}, \]

where \(d\) is the dimensionality and \(\tau I\) is an isotropic covariance with the same average marginal variance as \(\Sigma\).

alpha class-attribute instance-attribute

alpha: float = 0.01

Interpolation weight between the covariance and isotropic target.

DiagonalShrinkage dataclass

DiagonalShrinkage(alpha: float = 0.01)

Shrinkage toward the covariance diagonal.

Applies the shrinkage

\[ \Sigma_{\mathrm{reg}} = (1-\alpha)\,\Sigma + \alpha\,\mathrm{diag}(\Sigma), \]

where \(\mathrm{diag}(\Sigma)\) is the diagonal matrix containing the marginal variances of \(\Sigma\).

alpha class-attribute instance-attribute

alpha: float = 0.01

Interpolation weight between the covariance and diagonal target.

EigenvalueClipping dataclass

EigenvalueClipping(min_eigenvalue: float = 1e-06)

Eigenvalue-clipping regularization configuration.

Given an eigendecomposition

\[ \Sigma = Q \, \mathrm{diag}(\lambda_1, \ldots, \lambda_d) \, Q^T, \]

this method returns

\[ \Sigma_{\mathrm{reg}} = Q \, \mathrm{diag}(\max(\lambda_i, \lambda_{\min})) \, Q^T, \]

where \(\lambda_{\min} > 0\) is the eigenvalue floor.

min_eigenvalue class-attribute instance-attribute

min_eigenvalue: float = 1e-06

Smallest allowed eigenvalue after clipping.

LowRank dataclass

LowRank(rank: int = 1, eps: EpsilonSpec = 1e-06)

Low-rank covariance approximation with diagonal loading.

For an eigendecomposition

\[ \Sigma = U \, \mathrm{diag}(\lambda_1, \ldots, \lambda_d) \, U^T, \]

the approximation keeps the leading rank eigenpairs and returns

\[ \Sigma_{\mathrm{reg}} = U_r \, \Lambda_r \, U_r^T + \varepsilon I, \]

where \(U_r\) contains the leading eigenvectors, \(\Lambda_r\) the corresponding eigenvalues, and \(\varepsilon \ge 0\) is the diagonal loading applied after truncation.

eps class-attribute instance-attribute

eps: EpsilonSpec = 1e-06

Diagonal loading amount or epsilon heuristic.

rank class-attribute instance-attribute

rank: int = 1

Target rank for the approximation.

Epsilon Heuristics

RelativeToTrace dataclass

RelativeToTrace(c: float = 1e-06)

Scale epsilon with the covariance trace.

Sets

\[ \varepsilon = c\,\frac{\mathrm{tr}(\Sigma)}{d}, \]

where \(d\) is the covariance dimension.

c class-attribute instance-attribute

c: float = 1e-06

Multiplier \(c\) in \(\varepsilon = c\,\mathrm{tr}(\Sigma)/d\).

TargetConditionNumber dataclass

TargetConditionNumber(kappa: float = 100000000.0)

Choose epsilon to enforce a target condition number.

For

\[ \Sigma_{\mathrm{reg}} = \Sigma + \varepsilon I, \]

picks the smallest \(\varepsilon \ge 0\) such that \(\kappa(\Sigma_{\mathrm{reg}}) \le \text{kappa}\).

kappa class-attribute instance-attribute

kappa: float = 100000000.0

Target upper bound \(\kappa(\Sigma + \varepsilon I)\).

ResidualVariance dataclass

ResidualVariance(c: float = 1.0, r: int | None = None)

Scale epsilon from discarded low-rank variance.

With target rank \(r\), sets

\[ \varepsilon = c\,\frac{1}{d-r}\sum_{i=1}^{d-r}\lambda_i^{\mathrm{disc}}, \]

where \(\lambda_i^{\mathrm{disc}}\) are the discarded eigenvalues.

c class-attribute instance-attribute

c: float = 1.0

Multiplier \(c\) on the mean discarded eigenvalue.

r class-attribute instance-attribute

r: int | None = None

Target rank \(r\) used to define the discarded spectrum.