Skip to content

API Reference

NaN-aware linear algebra utilities for risk models. All public symbols are importable directly from the top-level package:

from cvx.linalg import cholesky, cholesky_solve, eigh, eigvalsh, eigvals, qr, svd, pca
from cvx.linalg import solve, lstsq, inv
from cvx.linalg import norm, a_norm, inv_a_norm, cond, det
from cvx.linalg import rand_cov, valid, is_positive_definite
from cvx.linalg.covariance.ewm_cov import ewm_covariance  # requires the 'ewm' extra (polars)

Decompositions

cvx.linalg.decomposition.cholesky.cholesky(cov, rhs=None)

Compute the upper triangular Cholesky factor of a covariance matrix.

Returns the upper triangular factor R such that R.T @ R = cov.

Parameters:

Name Type Description Default
cov Matrix

A positive definite covariance matrix of shape (n, n).

required
rhs Vector | Matrix | None

Deprecated. When provided the system cov @ x = rhs is solved and x is returned; use :func:cholesky_solve instead. This parameter will be removed in 1.0.

None

Returns:

Type Description
Vector | Matrix

The upper triangular Cholesky factor R when rhs is None, or the

Vector | Matrix

solution x to cov @ x = rhs otherwise (deprecated).

Raises:

Type Description
LinAlgError

When rhs is None and cov is not positive-definite, or when rhs is given and both Cholesky and LU-based solves fail.

Warns:

Type Description
DeprecationWarning

When rhs is given.

Example

import numpy as np from cvx.linalg import cholesky cov = np.array([[4.0, 2.0], [2.0, 5.0]]) R = cholesky(cov) np.allclose(R.T @ R, cov) True

Source code in src/cvx/linalg/decomposition/cholesky.py
def cholesky(cov: Matrix, rhs: Vector | Matrix | None = None) -> Vector | Matrix:
    """Compute the upper triangular Cholesky factor of a covariance matrix.

    Returns the upper triangular factor R such that R.T @ R = cov.

    Args:
        cov: A positive definite covariance matrix of shape (n, n).
        rhs: Deprecated. When provided the system ``cov @ x = rhs`` is solved
            and *x* is returned; use :func:`cholesky_solve` instead. This
            parameter will be removed in 1.0.

    Returns:
        The upper triangular Cholesky factor R when *rhs* is ``None``, or the
        solution x to ``cov @ x = rhs`` otherwise (deprecated).

    Raises:
        np.linalg.LinAlgError: When *rhs* is ``None`` and *cov* is not
            positive-definite, or when *rhs* is given and both Cholesky and
            LU-based solves fail.

    Warns:
        DeprecationWarning: When *rhs* is given.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import cholesky
        >>> cov = np.array([[4.0, 2.0], [2.0, 5.0]])
        >>> R = cholesky(cov)
        >>> np.allclose(R.T @ R, cov)
        True
    """
    if rhs is None:
        return cast("Matrix", _cholesky(cov).transpose())
    warnings.warn(
        "Passing 'rhs' to cholesky() is deprecated and will be removed in 1.0; use cholesky_solve(cov, rhs) instead.",
        DeprecationWarning,
        stacklevel=2,
    )
    return cholesky_solve(cov, rhs)

cvx.linalg.decomposition.cholesky.cholesky_solve(cov, rhs)

Solve cov @ x = rhs using the Cholesky decomposition.

The Cholesky factorisation is attempted first for numerical stability; when cov is not positive-definite the solve falls back to LU decomposition.

Parameters:

Name Type Description Default
cov Matrix

A positive definite covariance matrix of shape (n, n).

required
rhs Vector | Matrix

Right-hand side vector of length n or matrix of shape (n, k).

required

Returns:

Type Description
Vector | Matrix

The solution x to cov @ x = rhs with the same shape as rhs.

Raises:

Type Description
LinAlgError

When both the Cholesky and LU-based solves fail.

Example

import numpy as np from cvx.linalg import cholesky_solve cholesky_solve(np.eye(2), np.array([1.0, 2.0])).tolist() [1.0, 2.0] cholesky_solve(np.array([[4.0, 0.0], [0.0, 9.0]]), np.array([8.0, 27.0])).tolist() [2.0, 3.0]

Source code in src/cvx/linalg/decomposition/cholesky.py
def cholesky_solve(cov: Matrix, rhs: Vector | Matrix) -> Vector | Matrix:
    """Solve ``cov @ x = rhs`` using the Cholesky decomposition.

    The Cholesky factorisation is attempted first for numerical stability;
    when *cov* is not positive-definite the solve falls back to LU
    decomposition.

    Args:
        cov: A positive definite covariance matrix of shape (n, n).
        rhs: Right-hand side vector of length n or matrix of shape (n, k).

    Returns:
        The solution x to ``cov @ x = rhs`` with the same shape as *rhs*.

    Raises:
        np.linalg.LinAlgError: When both the Cholesky and LU-based solves fail.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import cholesky_solve
        >>> cholesky_solve(np.eye(2), np.array([1.0, 2.0])).tolist()
        [1.0, 2.0]
        >>> cholesky_solve(np.array([[4.0, 0.0], [0.0, 9.0]]), np.array([8.0, 27.0])).tolist()
        [2.0, 3.0]
    """
    try:
        upper = _cholesky(cov).transpose()
        return cast("Vector | Matrix", np.linalg.solve(upper, np.linalg.solve(upper.T, rhs)))
    except np.linalg.LinAlgError:
        return cast("Vector | Matrix", np.linalg.solve(cov, rhs))

cvx.linalg.decomposition.cholesky.is_positive_definite(matrix)

Return True if matrix is symmetric positive-definite, False otherwise.

The check is performed via an attempted Cholesky decomposition — the most numerically reliable way to test positive-definiteness for symmetric matrices.

This function is side-effect-free: it raises no exceptions and emits no warnings. It is suitable for use as a guard before passing a matrix to a linear solver.

Parameters:

Name Type Description Default
matrix Matrix

Square matrix to test.

required

Returns:

Type Description
bool

True if the matrix is positive-definite, False otherwise.

Example

import numpy as np from cvx.linalg import is_positive_definite is_positive_definite(np.eye(3)) True is_positive_definite(np.array([[1.0, 2.0], [2.0, 1.0]])) False is_positive_definite(np.array([[1.0, 0.5], [0.5, 1.0]])) True

Source code in src/cvx/linalg/decomposition/cholesky.py
def is_positive_definite(matrix: Matrix) -> bool:
    """Return True if *matrix* is symmetric positive-definite, False otherwise.

    The check is performed via an attempted Cholesky decomposition — the most
    numerically reliable way to test positive-definiteness for symmetric matrices.

    This function is side-effect-free: it raises no exceptions and emits no
    warnings.  It is suitable for use as a guard before passing a matrix to a
    linear solver.

    Args:
        matrix: Square matrix to test.

    Returns:
        ``True`` if the matrix is positive-definite, ``False`` otherwise.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import is_positive_definite
        >>> is_positive_definite(np.eye(3))
        True
        >>> is_positive_definite(np.array([[1.0, 2.0], [2.0, 1.0]]))
        False
        >>> is_positive_definite(np.array([[1.0, 0.5], [0.5, 1.0]]))
        True
    """
    try:
        cholesky(matrix)
    except np.linalg.LinAlgError:
        return False
    else:
        return True

cvx.linalg.decomposition.eigh.eigh(matrix)

Compute eigendecomposition of a real symmetric or Hermitian matrix.

Rows and columns with non-finite diagonal entries are excluded before decomposition.

Parameters:

Name Type Description Default
matrix Matrix

Square symmetric/Hermitian input matrix.

required

Returns:

Type Description
Vector

A tuple (eigenvalues, eigenvectors) as returned by np.linalg.eigh

Matrix

on the valid submatrix. Eigenvalues are sorted in ascending order.

Source code in src/cvx/linalg/decomposition/eigh.py
def eigh(matrix: Matrix) -> tuple[Vector, Matrix]:
    """Compute eigendecomposition of a real symmetric or Hermitian matrix.

    Rows and columns with non-finite diagonal entries are excluded before
    decomposition.

    Args:
        matrix: Square symmetric/Hermitian input matrix.

    Returns:
        A tuple ``(eigenvalues, eigenvectors)`` as returned by ``np.linalg.eigh``
        on the valid submatrix. Eigenvalues are sorted in ascending order.
    """
    _, submatrix = valid(matrix)
    return np.linalg.eigh(submatrix)

cvx.linalg.decomposition.eigh.eigvalsh(matrix)

Return eigenvalues of a real symmetric or Hermitian matrix.

Rows and columns with non-finite diagonal entries are excluded before decomposition.

Parameters:

Name Type Description Default
matrix Matrix

Square symmetric/Hermitian input matrix.

required

Returns:

Type Description
Vector

Eigenvalues of the valid submatrix in ascending order.

Source code in src/cvx/linalg/decomposition/eigh.py
def eigvalsh(matrix: Matrix) -> Vector:
    """Return eigenvalues of a real symmetric or Hermitian matrix.

    Rows and columns with non-finite diagonal entries are excluded before
    decomposition.

    Args:
        matrix: Square symmetric/Hermitian input matrix.

    Returns:
        Eigenvalues of the valid submatrix in ascending order.
    """
    eigenvalues, _ = eigh(matrix)
    return eigenvalues

cvx.linalg.decomposition.eigvals.eigvals(matrix)

Return the eigenvalues of a square matrix.

This routine supports general (non-symmetric) square matrices and may return complex eigenvalues.

Parameters:

Name Type Description Default
matrix Matrix

Square input matrix.

required

Returns:

Type Description
Vector | NDArray[complex128]

Eigenvalues of matrix as returned by numpy.linalg.eigvals.

Raises:

Type Description
NotAMatrixError

If matrix is not two-dimensional.

NonSquareMatrixError

If matrix is not square.

Source code in src/cvx/linalg/decomposition/eigvals.py
def eigvals(matrix: Matrix) -> Vector | npt.NDArray[np.complex128]:
    """Return the eigenvalues of a square matrix.

    This routine supports general (non-symmetric) square matrices and may
    return complex eigenvalues.

    Args:
        matrix: Square input matrix.

    Returns:
        Eigenvalues of ``matrix`` as returned by ``numpy.linalg.eigvals``.

    Raises:
        NotAMatrixError: If *matrix* is not two-dimensional.
        NonSquareMatrixError: If *matrix* is not square.
    """
    if matrix.ndim != 2:
        raise NotAMatrixError(matrix.ndim, func="eigvals")

    if matrix.shape[0] != matrix.shape[1]:
        raise NonSquareMatrixError(matrix.shape[0], matrix.shape[1])

    return cast("Vector | npt.NDArray[np.complex128]", np.linalg.eigvals(matrix))

cvx.linalg.decomposition.qr.qr(matrix)

Compute the reduced QR decomposition of a 2-D matrix.

Parameters:

Name Type Description Default
matrix Matrix

Input matrix with shape (m, n).

required

Returns:

Type Description
tuple[Matrix, Matrix]

A tuple (Q, R) matching np.linalg.qr(matrix, mode="reduced").

Raises:

Type Description
NotAMatrixError

If matrix is not two-dimensional.

Example

import numpy as np from cvx.linalg import qr q, r = qr(np.array([[1.0, 2.0], [3.0, 4.0]])) np.allclose(q @ r, np.array([[1.0, 2.0], [3.0, 4.0]])) True

Source code in src/cvx/linalg/decomposition/qr.py
def qr(matrix: Matrix) -> tuple[Matrix, Matrix]:
    """Compute the reduced QR decomposition of a 2-D matrix.

    Args:
        matrix: Input matrix with shape ``(m, n)``.

    Returns:
        A tuple ``(Q, R)`` matching ``np.linalg.qr(matrix, mode="reduced")``.

    Raises:
        NotAMatrixError: If ``matrix`` is not two-dimensional.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import qr
        >>> q, r = qr(np.array([[1.0, 2.0], [3.0, 4.0]]))
        >>> np.allclose(q @ r, np.array([[1.0, 2.0], [3.0, 4.0]]))
        True
    """
    if matrix.ndim != 2:
        raise NotAMatrixError(matrix.ndim, func="qr")

    return np.linalg.qr(matrix, mode="reduced")

cvx.linalg.decomposition.svd.svd(matrix)

Compute the compact singular value decomposition of a matrix.

This is a thin wrapper around numpy.linalg.svd with full_matrices=False.

Parameters:

Name Type Description Default
matrix Matrix

Input matrix of shape (m, n).

required

Returns:

Type Description
tuple[Matrix, Matrix, Matrix]

Tuple (u, s, vt) such that matrix == u @ np.diag(s) @ vt.

Source code in src/cvx/linalg/decomposition/svd.py
def svd(matrix: Matrix) -> tuple[Matrix, Matrix, Matrix]:
    """Compute the compact singular value decomposition of a matrix.

    This is a thin wrapper around ``numpy.linalg.svd`` with
    ``full_matrices=False``.

    Args:
        matrix: Input matrix of shape ``(m, n)``.

    Returns:
        Tuple ``(u, s, vt)`` such that ``matrix == u @ np.diag(s) @ vt``.
    """
    return np.linalg.svd(matrix, full_matrices=False)

cvx.linalg.decomposition.svd.svd_k(matrix, k)

Compute the truncated rank-k singular value decomposition.

Returns the k leading singular triplets — the best rank-k approximation of matrix in both the spectral and Frobenius norms (Eckart-Young). This is an exact truncation: the full compact SVD is computed and sliced, so the result is deterministic and matches :func:numpy.linalg.svd on the leading components. Like :func:svd, it is a raw decomposition and is not NaN-aware; clean non-finite entries first.

Parameters:

Name Type Description Default
matrix Matrix

Input matrix of shape (m, n).

required
k int

Number of leading singular triplets to keep. Must be between 1 and min(m, n).

required

Returns:

Type Description
Matrix

Tuple (u, s, vt) with shapes (m, k), (k,) and (k, n),

Vector

such that u @ np.diag(s) @ vt is the best rank-k approximation

Matrix

of matrix. Singular values are in descending order.

Raises:

Type Description
InvalidComponentsError

If k is smaller than 1 or larger than min(m, n).

Example

import numpy as np from cvx.linalg import svd_k matrix = np.diag([3.0, 2.0, 1.0]) @ np.ones((3, 4)) u, s, vt = svd_k(matrix, k=1) u.shape, s.shape, vt.shape ((3, 1), (1,), (1, 4))

The leading triplets agree with the full SVD:

u_full, s_full, vt_full = np.linalg.svd(matrix, full_matrices=False) bool(np.allclose(s, s_full[:1])) True

svd_k(matrix, min(m, n)) reconstructs the matrix exactly:

u, s, vt = svd_k(matrix, k=3) bool(np.allclose(u @ np.diag(s) @ vt, matrix)) True

Source code in src/cvx/linalg/decomposition/svd.py
def svd_k(matrix: Matrix, k: int) -> tuple[Matrix, Vector, Matrix]:
    """Compute the truncated rank-``k`` singular value decomposition.

    Returns the ``k`` leading singular triplets — the best rank-``k``
    approximation of *matrix* in both the spectral and Frobenius norms
    (Eckart-Young). This is an *exact* truncation: the full compact SVD is
    computed and sliced, so the result is deterministic and matches
    :func:`numpy.linalg.svd` on the leading components. Like :func:`svd`, it is
    a raw decomposition and is **not** NaN-aware; clean non-finite entries
    first.

    Args:
        matrix: Input matrix of shape ``(m, n)``.
        k: Number of leading singular triplets to keep. Must be between 1 and
            ``min(m, n)``.

    Returns:
        Tuple ``(u, s, vt)`` with shapes ``(m, k)``, ``(k,)`` and ``(k, n)``,
        such that ``u @ np.diag(s) @ vt`` is the best rank-``k`` approximation
        of *matrix*. Singular values are in descending order.

    Raises:
        InvalidComponentsError: If *k* is smaller than 1 or larger than
            ``min(m, n)``.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import svd_k
        >>> matrix = np.diag([3.0, 2.0, 1.0]) @ np.ones((3, 4))
        >>> u, s, vt = svd_k(matrix, k=1)
        >>> u.shape, s.shape, vt.shape
        ((3, 1), (1,), (1, 4))

        The leading triplets agree with the full SVD:

        >>> u_full, s_full, vt_full = np.linalg.svd(matrix, full_matrices=False)
        >>> bool(np.allclose(s, s_full[:1]))
        True

        ``svd_k(matrix, min(m, n))`` reconstructs the matrix exactly:

        >>> u, s, vt = svd_k(matrix, k=3)
        >>> bool(np.allclose(u @ np.diag(s) @ vt, matrix))
        True
    """
    max_components = min(matrix.shape)
    if not 1 <= k <= max_components:
        raise InvalidComponentsError(k, max_components)

    u, s, vt = np.linalg.svd(matrix, full_matrices=False)
    return u[:, :k], s[:k], vt[:k, :]

cvx.linalg.covariance.pca.pca(returns, n_components=10)

Compute the first n principal components for a return matrix using SVD.

Unlike most functions in this package, pca is not NaN-aware: returns must contain only finite values. Clean or impute missing data first.

Parameters:

Name Type Description Default
returns Matrix

Array of asset returns with shape (n_samples, n_assets). Must not contain NaN or infinite values.

required
n_components int

Number of principal components to extract. Must be between 1 and min(n_samples, n_assets). Defaults to 10.

10

Raises:

Type Description
InvalidComponentsError

If n_components is smaller than 1 or larger than min(n_samples, n_assets).

Returns:

Type Description
PCA

PCA named tuple containing: - explained_variance: Ratio of variance explained by each component - factors: Factor returns (scores) - exposure: Factor exposures (loadings) - cov: Factor covariance matrix - systematic: Returns explained by factors - idiosyncratic: Residual returns

Example

import numpy as np from cvx.linalg import pca np.random.seed(42) returns = np.random.randn(100, 10) result = pca(returns, n_components=3) bool(result.explained_variance[0] > result.explained_variance[1]) True factor_corr = np.corrcoef(result.factors.T) bool(np.allclose(factor_corr, np.eye(3), atol=0.1)) True VtV = result.exposure @ result.exposure.T bool(np.allclose(VtV, np.eye(3), atol=1e-10)) True all(result.explained_variance[i] >= result.explained_variance[i+1] ... for i in range(len(result.explained_variance)-1)) True reconstructed = result.factors @ result.exposure centered_systematic = result.systematic - returns.mean(axis=0) bool(np.allclose(reconstructed, centered_systematic, atol=1e-10)) True

Source code in src/cvx/linalg/covariance/pca.py
def pca(returns: Matrix, n_components: int = 10) -> PCA:
    """Compute the first n principal components for a return matrix using SVD.

    Unlike most functions in this package, ``pca`` is not NaN-aware: *returns*
    must contain only finite values. Clean or impute missing data first.

    Args:
        returns: Array of asset returns with shape (n_samples, n_assets).
            Must not contain NaN or infinite values.
        n_components: Number of principal components to extract. Must be
            between 1 and ``min(n_samples, n_assets)``. Defaults to 10.

    Raises:
        InvalidComponentsError: If *n_components* is smaller than 1 or larger
            than ``min(n_samples, n_assets)``.

    Returns:
        PCA named tuple containing:
            - explained_variance: Ratio of variance explained by each component
            - factors: Factor returns (scores)
            - exposure: Factor exposures (loadings)
            - cov: Factor covariance matrix
            - systematic: Returns explained by factors
            - idiosyncratic: Residual returns

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import pca
        >>> np.random.seed(42)
        >>> returns = np.random.randn(100, 10)
        >>> result = pca(returns, n_components=3)
        >>> bool(result.explained_variance[0] > result.explained_variance[1])
        True
        >>> factor_corr = np.corrcoef(result.factors.T)
        >>> bool(np.allclose(factor_corr, np.eye(3), atol=0.1))
        True
        >>> VtV = result.exposure @ result.exposure.T
        >>> bool(np.allclose(VtV, np.eye(3), atol=1e-10))
        True
        >>> all(result.explained_variance[i] >= result.explained_variance[i+1]
        ...     for i in range(len(result.explained_variance)-1))
        True
        >>> reconstructed = result.factors @ result.exposure
        >>> centered_systematic = result.systematic - returns.mean(axis=0)
        >>> bool(np.allclose(reconstructed, centered_systematic, atol=1e-10))
        True

    """
    max_components = min(returns.shape)
    if not 1 <= n_components <= max_components:
        raise InvalidComponentsError(n_components, max_components)

    x_mean = returns.mean(axis=0)
    x_centered = returns - x_mean

    u, s_full, vt = svd(x_centered)

    u = u[:, :n_components]
    s = s_full[:n_components]
    vt = vt[:n_components, :]

    factors: Matrix = u * s
    exposure: Matrix = vt
    explained_variance: Matrix = (s**2) / np.sum(s_full**2)
    cov: Matrix = np.atleast_2d(np.cov(factors.T))
    systematic: Matrix = factors @ vt + x_mean
    idiosyncratic: Matrix = x_centered - factors @ vt

    return PCA(
        explained_variance=explained_variance,
        factors=factors,
        exposure=exposure,
        cov=cov,
        systematic=systematic,
        idiosyncratic=idiosyncratic,
    )

cvx.linalg.decomposition.power_iteration.power_iteration(operator, *, n=None, n_iter=1000, tol=1e-09, seed=None)

Estimate the dominant eigenpair of a real symmetric operator.

Repeatedly applies operator to a random unit vector, renormalizing each step, until the Rayleigh-quotient eigenvalue estimate stops changing. The iterate converges to the eigenvector whose eigenvalue is largest in magnitude; the matching eigenvalue (returned with its sign) comes from the Rayleigh quotient v.T @ (operator @ v).

operator may be given three ways, so the leading eigenvalue can be estimated matrix-free (e.g. the Lipschitz constant of a gradient step):

  • a dense symmetric (n, n) array;
  • any :class:~cvx.linalg.SymmetricOperator (its :meth:matvec and n drive the iteration, so no n x n matrix is formed); or
  • a callable v -> A @ v together with the dimension n.

Each iteration costs a single application, so this is far cheaper than a full :func:~cvx.linalg.eigh when only the leading eigenpair is needed and there is a clear spectral gap. Symmetry is assumed; only the result's interpretation as an eigenpair relies on it. Like :func:~cvx.linalg.svd, this is a raw primitive and is not NaN-aware.

Parameters:

Name Type Description Default
operator Matrix | SupportsMatvec | Callable[[Vector], Vector]

A symmetric matrix, a :class:~cvx.linalg.SymmetricOperator, or a callable applying it to a vector.

required
n int | None

Dimension of the operator. Required when operator is a bare callable; ignored otherwise (taken from the array shape or the operator's n).

None
n_iter int

Maximum number of iterations. Defaults to 1000.

1000
tol float

Convergence tolerance on the relative change of the eigenvalue estimate between iterations. Defaults to 1e-9.

1e-09
seed int | None

Seed for the random starting vector, for reproducibility. If None, uses the current NumPy random state.

None

Returns:

Type Description
float

Tuple (eigenvalue, eigenvector) where eigenvalue is the signed

Vector

dominant eigenvalue estimate (a float) and eigenvector is the

tuple[float, Vector]

corresponding unit-norm eigenvector. The eigenvector sign is arbitrary.

Raises:

Type Description
NotAMatrixError

If operator is an array that is not 2-D.

NonSquareMatrixError

If operator is an array that is not square.

ValueError

If operator is a bare callable and n is not given.

Example

import numpy as np from cvx.linalg import power_iteration matrix = np.diag([3.0, 2.0, 1.0]) eigenvalue, eigenvector = power_iteration(matrix, seed=0) bool(np.isclose(eigenvalue, 3.0)) True bool(np.isclose(abs(eigenvector[0]), 1.0)) True

It runs matrix-free on a :class:~cvx.linalg.SymmetricOperator, so the leading eigenvalue of M.T @ M needs no n x n matrix:

from cvx.linalg import GramOperator rng = np.random.default_rng(0) M = rng.standard_normal((20, 5)) lam, _ = power_iteration(GramOperator(M), seed=0) bool(np.isclose(lam, np.linalg.eigvalsh(M.T @ M)[-1])) True

Source code in src/cvx/linalg/decomposition/power_iteration.py
def power_iteration(
    operator: Matrix | SupportsMatvec | Callable[[Vector], Vector],
    *,
    n: int | None = None,
    n_iter: int = 1000,
    tol: float = 1e-9,
    seed: int | None = None,
) -> tuple[float, Vector]:
    """Estimate the dominant eigenpair of a real symmetric operator.

    Repeatedly applies *operator* to a random unit vector, renormalizing each
    step, until the Rayleigh-quotient eigenvalue estimate stops changing. The
    iterate converges to the eigenvector whose eigenvalue is largest in
    magnitude; the matching eigenvalue (returned with its sign) comes from the
    Rayleigh quotient ``v.T @ (operator @ v)``.

    *operator* may be given three ways, so the leading eigenvalue can be
    estimated **matrix-free** (e.g. the Lipschitz constant of a gradient step):

    * a dense symmetric ``(n, n)`` array;
    * any :class:`~cvx.linalg.SymmetricOperator` (its :meth:`matvec` and ``n``
      drive the iteration, so no ``n x n`` matrix is formed); or
    * a callable ``v -> A @ v`` together with the dimension *n*.

    Each iteration costs a single application, so this is far cheaper than a full
    :func:`~cvx.linalg.eigh` when only the leading eigenpair is needed and there
    is a clear spectral gap. Symmetry is assumed; only the result's
    interpretation as an eigenpair relies on it. Like :func:`~cvx.linalg.svd`,
    this is a raw primitive and is **not** NaN-aware.

    Args:
        operator: A symmetric matrix, a :class:`~cvx.linalg.SymmetricOperator`,
            or a callable applying it to a vector.
        n: Dimension of the operator. Required when *operator* is a bare callable;
            ignored otherwise (taken from the array shape or the operator's ``n``).
        n_iter: Maximum number of iterations. Defaults to 1000.
        tol: Convergence tolerance on the relative change of the eigenvalue
            estimate between iterations. Defaults to ``1e-9``.
        seed: Seed for the random starting vector, for reproducibility. If
            ``None``, uses the current NumPy random state.

    Returns:
        Tuple ``(eigenvalue, eigenvector)`` where *eigenvalue* is the signed
        dominant eigenvalue estimate (a ``float``) and *eigenvector* is the
        corresponding unit-norm eigenvector. The eigenvector sign is arbitrary.

    Raises:
        NotAMatrixError: If *operator* is an array that is not 2-D.
        NonSquareMatrixError: If *operator* is an array that is not square.
        ValueError: If *operator* is a bare callable and *n* is not given.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import power_iteration
        >>> matrix = np.diag([3.0, 2.0, 1.0])
        >>> eigenvalue, eigenvector = power_iteration(matrix, seed=0)
        >>> bool(np.isclose(eigenvalue, 3.0))
        True
        >>> bool(np.isclose(abs(eigenvector[0]), 1.0))
        True

        It runs matrix-free on a :class:`~cvx.linalg.SymmetricOperator`, so the
        leading eigenvalue of ``M.T @ M`` needs no ``n x n`` matrix:

        >>> from cvx.linalg import GramOperator
        >>> rng = np.random.default_rng(0)
        >>> M = rng.standard_normal((20, 5))
        >>> lam, _ = power_iteration(GramOperator(M), seed=0)
        >>> bool(np.isclose(lam, np.linalg.eigvalsh(M.T @ M)[-1]))
        True
    """
    apply, dim = _resolve(operator, n)

    rng = np.random.default_rng(seed)
    v = rng.standard_normal((dim,))
    v = v / np.linalg.norm(v)

    eigenvalue = float(v @ apply(v))
    for _ in range(n_iter):
        w = apply(v)
        norm = float(np.linalg.norm(w))
        if norm < 1e-15:
            # operator annihilates the iterate: the dominant eigenvalue is zero.
            return 0.0, v
        v = w / norm
        new_eigenvalue = float(v @ apply(v))
        if abs(new_eigenvalue - eigenvalue) <= tol * max(1.0, abs(new_eigenvalue)):
            return new_eigenvalue, v
        eigenvalue = new_eigenvalue

    return eigenvalue, v

Solvers

cvx.linalg.solve.solve.solve(matrix, rhs, cond_threshold=DEFAULT_COND_THRESHOLD)

Solve a linear system restricted to the valid submatrix.

Rows and columns with non-finite diagonal entries are excluded from the solve; the corresponding positions in the result are set to NaN. Cholesky decomposition is attempted first for numerical stability and falls back to LU decomposition for non-positive-definite matrices. When the condition number of the valid sub-matrix exceeds cond_threshold, an IllConditionedMatrixWarning is emitted.

Parameters:

Name Type Description Default
matrix Matrix

Square coefficient matrix of shape (n, n).

required
rhs Vector | Matrix

Right-hand side vector of length n or matrix of shape (n, k).

required
cond_threshold float

Condition-number threshold above which a warning is emitted. Defaults to 1e12.

DEFAULT_COND_THRESHOLD

Returns:

Type Description
Vector | Matrix

A solution array with the same shape as rhs. Entries mapped to

Vector | Matrix

invalid rows or columns are returned as NaN.

Raises:

Type Description
NonSquareMatrixError

If the matrix is not square.

DimensionMismatchError

If the leading dimension of rhs does not match the matrix dimension.

SingularMatrixError

If the valid sub-matrix is singular.

Example

import numpy as np from cvx.linalg import solve solve(np.eye(2), np.array([1.0, 2.0])).tolist() [1.0, 2.0]

NaN-masked entries are skipped:

matrix = np.array([[4.0, 0.0], [0.0, np.nan]]) solve(matrix, np.array([8.0, 1.0])).tolist() [2.0, nan]

Matrix right-hand sides are supported:

solve(np.eye(2), np.array([[1.0, 2.0], [3.0, 4.0]])).tolist() [[1.0, 2.0], [3.0, 4.0]]

Source code in src/cvx/linalg/solve/solve.py
def solve(
    matrix: Matrix,
    rhs: Vector | Matrix,
    cond_threshold: float = DEFAULT_COND_THRESHOLD,
) -> Vector | Matrix:
    """Solve a linear system restricted to the valid submatrix.

    Rows and columns with non-finite diagonal entries are excluded from the
    solve; the corresponding positions in the result are set to NaN.  Cholesky
    decomposition is attempted first for numerical stability and falls back to
    LU decomposition for non-positive-definite matrices.  When the condition
    number of the valid sub-matrix exceeds *cond_threshold*, an
    ``IllConditionedMatrixWarning`` is emitted.

    Args:
        matrix: Square coefficient matrix of shape ``(n, n)``.
        rhs: Right-hand side vector of length ``n`` or matrix of shape ``(n, k)``.
        cond_threshold: Condition-number threshold above which a warning is
            emitted. Defaults to ``1e12``.

    Returns:
        A solution array with the same shape as ``rhs``. Entries mapped to
        invalid rows or columns are returned as ``NaN``.

    Raises:
        NonSquareMatrixError: If the matrix is not square.
        DimensionMismatchError: If the leading dimension of ``rhs`` does not
            match the matrix dimension.
        SingularMatrixError: If the valid sub-matrix is singular.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import solve
        >>> solve(np.eye(2), np.array([1.0, 2.0])).tolist()
        [1.0, 2.0]

        NaN-masked entries are skipped:

        >>> matrix = np.array([[4.0, 0.0], [0.0, np.nan]])
        >>> solve(matrix, np.array([8.0, 1.0])).tolist()
        [2.0, nan]

        Matrix right-hand sides are supported:

        >>> solve(np.eye(2), np.array([[1.0, 2.0], [3.0, 4.0]])).tolist()
        [[1.0, 2.0], [3.0, 4.0]]
    """
    if matrix.shape[0] != matrix.shape[1]:
        raise NonSquareMatrixError(matrix.shape[0], matrix.shape[1])

    if rhs.shape[0] != matrix.shape[0]:
        raise DimensionMismatchError(rhs.shape[0], matrix.shape[0])

    solution = np.full(rhs.shape, np.nan)
    mask, submatrix = valid(matrix)

    if mask.any():
        _check_and_warn_condition(submatrix, cond_threshold)
        try:
            solution[mask] = _cholesky_solve(submatrix, rhs[mask])
        except np.linalg.LinAlgError as exc:
            raise SingularMatrixError(str(exc)) from exc

    return solution

cvx.linalg.solve.lstsq.lstsq(matrix, rhs, cond_threshold=DEFAULT_COND_THRESHOLD)

Solve an overdetermined or underdetermined system in the least-squares sense.

Rows where any entry in matrix or the corresponding entry in rhs is non-finite are excluded before solving. The returned solution vector always has length equal to the number of columns in matrix. When the effective condition number of the valid sub-matrix exceeds cond_threshold, an IllConditionedMatrixWarning is emitted.

Parameters:

Name Type Description Default
matrix Matrix

Coefficient matrix of shape (m, n).

required
rhs Vector

Right-hand side vector of length m.

required
cond_threshold float

Condition-number threshold above which a warning is emitted. Defaults to 1e12.

DEFAULT_COND_THRESHOLD

Returns:

Type Description
Vector

A four-tuple (x, residuals, rank, sv) matching the convention of

Vector

func:numpy.linalg.lstsq:

int
  • x — least-squares solution of shape (n,).
Vector
  • residuals — sum of squared residuals; empty when the solution is not unique or all rows are invalid.
tuple[Vector, Vector, int, Vector]
  • rank — effective rank of the valid sub-matrix.
tuple[Vector, Vector, int, Vector]
  • sv — singular values of the valid sub-matrix in descending order.

Raises:

Type Description
DimensionMismatchError

If rhs length does not match the number of rows in matrix.

Example

import numpy as np from cvx.linalg import lstsq A = np.array([[1.0, 1.0], [1.0, 2.0], [1.0, 3.0]]) b = np.array([6.0, 5.0, 7.0]) x, res, rank, sv = lstsq(A, b) int(rank) 2

NaN rows are silently dropped:

A_nan = np.array([[1.0, 1.0], [np.nan, 2.0], [1.0, 3.0]]) b_nan = np.array([6.0, 5.0, 7.0]) x2, _, rank2, _ = lstsq(A_nan, b_nan) int(rank2) 2

Source code in src/cvx/linalg/solve/lstsq.py
def lstsq(
    matrix: Matrix,
    rhs: Vector,
    cond_threshold: float = DEFAULT_COND_THRESHOLD,
) -> tuple[Vector, Vector, int, Vector]:
    """Solve an overdetermined or underdetermined system in the least-squares sense.

    Rows where any entry in *matrix* or the corresponding entry in *rhs* is
    non-finite are excluded before solving.  The returned solution vector
    always has length equal to the number of columns in *matrix*.  When the
    effective condition number of the valid sub-matrix exceeds
    *cond_threshold*, an ``IllConditionedMatrixWarning`` is emitted.

    Args:
        matrix: Coefficient matrix of shape ``(m, n)``.
        rhs: Right-hand side vector of length ``m``.
        cond_threshold: Condition-number threshold above which a warning is
            emitted. Defaults to ``1e12``.

    Returns:
        A four-tuple ``(x, residuals, rank, sv)`` matching the convention of
        :func:`numpy.linalg.lstsq`:

        - ``x`` — least-squares solution of shape ``(n,)``.
        - ``residuals`` — sum of squared residuals; empty when the solution is
          not unique or all rows are invalid.
        - ``rank`` — effective rank of the valid sub-matrix.
        - ``sv`` — singular values of the valid sub-matrix in descending order.

    Raises:
        DimensionMismatchError: If ``rhs`` length does not match the number of
            rows in *matrix*.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import lstsq
        >>> A = np.array([[1.0, 1.0], [1.0, 2.0], [1.0, 3.0]])
        >>> b = np.array([6.0, 5.0, 7.0])
        >>> x, res, rank, sv = lstsq(A, b)
        >>> int(rank)
        2

        NaN rows are silently dropped:

        >>> A_nan = np.array([[1.0, 1.0], [np.nan, 2.0], [1.0, 3.0]])
        >>> b_nan = np.array([6.0, 5.0, 7.0])
        >>> x2, _, rank2, _ = lstsq(A_nan, b_nan)
        >>> int(rank2)
        2
    """
    if rhs.shape[0] != matrix.shape[0]:
        raise DimensionMismatchError(rhs.shape[0], matrix.shape[0])

    n_cols = matrix.shape[1]

    # Filter rows that contain any non-finite value in matrix or rhs.
    row_mask = np.isfinite(matrix).all(axis=1) & np.isfinite(rhs)
    sub_matrix = matrix[row_mask]
    sub_rhs = rhs[row_mask]

    if sub_matrix.shape[0] == 0:
        return np.full(n_cols, np.nan), np.array([]), 0, np.array([])

    x, residuals, rank, sv = np.linalg.lstsq(sub_matrix, sub_rhs, rcond=None)

    _warn_ill_conditioned(_condition_number(sv), cond_threshold)

    return (
        x.astype(np.float64, copy=False),
        residuals.astype(np.float64, copy=False),
        int(rank),
        sv.astype(np.float64, copy=False),
    )

cvx.linalg.solve.inv.inv(matrix, cond_threshold=DEFAULT_COND_THRESHOLD)

Invert a matrix restricted to the valid submatrix.

Rows and columns with non-finite diagonal entries are excluded from the inversion; the corresponding rows and columns in the result are set to NaN. When the condition number of the valid sub-matrix exceeds cond_threshold, an IllConditionedMatrixWarning is emitted.

Parameters:

Name Type Description Default
matrix Matrix

Square matrix to invert.

required
cond_threshold float

Condition-number threshold above which a warning is emitted. Defaults to 1e12.

DEFAULT_COND_THRESHOLD

Returns:

Type Description
Matrix

An inverted matrix with the same shape as matrix. Rows and columns

Matrix

mapped to invalid entries are returned as NaN.

Raises:

Type Description
NonSquareMatrixError

If the matrix is not square.

SingularMatrixError

If the valid sub-matrix is singular.

Example

import numpy as np from cvx.linalg import inv np.allclose(inv(np.eye(2)), np.eye(2)) True

NaN-masked entries are skipped:

matrix = np.array([[4.0, 0.0], [0.0, np.nan]]) result = inv(matrix) float(result[0, 0]) 0.25 bool(np.isnan(result[0, 1]) and np.isnan(result[1, 0]) and np.isnan(result[1, 1])) True

Source code in src/cvx/linalg/solve/inv.py
def inv(
    matrix: Matrix,
    cond_threshold: float = DEFAULT_COND_THRESHOLD,
) -> Matrix:
    """Invert a matrix restricted to the valid submatrix.

    Rows and columns with non-finite diagonal entries are excluded from the
    inversion; the corresponding rows and columns in the result are set to NaN.
    When the condition number of the valid sub-matrix exceeds *cond_threshold*,
    an ``IllConditionedMatrixWarning`` is emitted.

    Args:
        matrix: Square matrix to invert.
        cond_threshold: Condition-number threshold above which a warning is
            emitted. Defaults to ``1e12``.

    Returns:
        An inverted matrix with the same shape as *matrix*. Rows and columns
        mapped to invalid entries are returned as ``NaN``.

    Raises:
        NonSquareMatrixError: If the matrix is not square.
        SingularMatrixError: If the valid sub-matrix is singular.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import inv
        >>> np.allclose(inv(np.eye(2)), np.eye(2))
        True

        NaN-masked entries are skipped:

        >>> matrix = np.array([[4.0, 0.0], [0.0, np.nan]])
        >>> result = inv(matrix)
        >>> float(result[0, 0])
        0.25
        >>> bool(np.isnan(result[0, 1]) and np.isnan(result[1, 0]) and np.isnan(result[1, 1]))
        True
    """
    if matrix.shape[0] != matrix.shape[1]:
        raise NonSquareMatrixError(matrix.shape[0], matrix.shape[1])

    n = matrix.shape[0]
    result = np.full((n, n), np.nan)
    mask, submatrix = valid(matrix)

    if mask.any():
        _check_and_warn_condition(submatrix, cond_threshold)
        try:
            sub_inv = np.linalg.inv(submatrix)
        except np.linalg.LinAlgError as exc:
            raise SingularMatrixError(str(exc)) from exc

        idx = np.where(mask)[0]
        result[np.ix_(idx, idx)] = sub_inv

    return result

Norms & Metrics

cvx.linalg.norm.norm.norm(x, ord=None)

Compute the norm of a vector or matrix, ignoring non-finite entries.

Non-finite entries (NaN, inf) are treated as zero before computing the norm, so they contribute nothing to the result. Supports all ord values accepted by np.linalg.norm.

Parameters:

Name Type Description Default
x Vector | Matrix

Input array (1-D vector or 2-D matrix).

required
ord int | float | Literal['fro', 'nuc'] | None

Order of the norm. See np.linalg.norm for valid values. Common choices: None (default 2-norm for vectors, Frobenius for matrices), 1, 2, np.inf, 'fro', 'nuc'.

None

Returns:

Type Description
float

The norm as a float.

Example

import numpy as np from cvx.linalg import norm norm(np.array([3.0, np.nan, 4.0])) 5.0 norm(np.array([[1.0, np.nan], [np.nan, 1.0]]), ord='fro') 1.4142135623730951

Source code in src/cvx/linalg/norm/norm.py
def norm(
    x: Vector | Matrix,
    ord: int | float | Literal["fro", "nuc"] | None = None,  # noqa: A002  # mirrors np.linalg.norm public API
) -> float:
    """Compute the norm of a vector or matrix, ignoring non-finite entries.

    Non-finite entries (NaN, inf) are treated as zero before computing the norm,
    so they contribute nothing to the result. Supports all ``ord`` values accepted
    by ``np.linalg.norm``.

    Args:
        x: Input array (1-D vector or 2-D matrix).
        ord: Order of the norm. See ``np.linalg.norm`` for valid values.
            Common choices: ``None`` (default 2-norm for vectors, Frobenius for
            matrices), ``1``, ``2``, ``np.inf``, ``'fro'``, ``'nuc'``.

    Returns:
        The norm as a float.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import norm
        >>> norm(np.array([3.0, np.nan, 4.0]))
        5.0
        >>> norm(np.array([[1.0, np.nan], [np.nan, 1.0]]), ord='fro')
        1.4142135623730951
    """
    return float(np.linalg.norm(np.where(np.isfinite(x), x, 0.0), ord=ord))

cvx.linalg.norm.norm.a_norm(vector, matrix=None)

Calculate the generalized norm of a vector with respect to a matrix.

Parameters:

Name Type Description Default
vector Vector

The input vector.

required
matrix Matrix | None

Optional square matrix defining the quadratic form.

None

Returns:

Type Description
float

The Euclidean norm of the finite vector entries, or sqrt(v.T @ A @ v)

float

after dropping rows and columns whose diagonal entries are not finite.

Raises:

Type Description
NonSquareMatrixError

If the matrix is not square.

DimensionMismatchError

If the vector length does not match the matrix dimension.

Example

import numpy as np from cvx.linalg import a_norm a_norm(np.array([3.0, 4.0])) 5.0

Source code in src/cvx/linalg/norm/norm.py
def a_norm(vector: Vector, matrix: Matrix | None = None) -> float:
    """Calculate the generalized norm of a vector with respect to a matrix.

    Args:
        vector: The input vector.
        matrix: Optional square matrix defining the quadratic form.

    Returns:
        The Euclidean norm of the finite vector entries, or ``sqrt(v.T @ A @ v)``
        after dropping rows and columns whose diagonal entries are not finite.

    Raises:
        NonSquareMatrixError: If the matrix is not square.
        DimensionMismatchError: If the vector length does not match the matrix dimension.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import a_norm
        >>> a_norm(np.array([3.0, 4.0]))
        5.0
    """
    if matrix is None:
        return float(np.linalg.norm(vector[np.isfinite(vector)], 2))

    _validate_square(vector, matrix)

    mask, submatrix = valid(matrix)
    if mask.any():
        filtered_vector = vector[mask]
        return float(np.sqrt(filtered_vector @ submatrix @ filtered_vector))

    return float("nan")

cvx.linalg.norm.norm.inv_a_norm(vector, matrix=None, cond_threshold=DEFAULT_COND_THRESHOLD)

Calculate the inverse A-norm of a vector using an optional matrix.

If matrix is None, returns the Euclidean norm of finite entries. Otherwise computes sqrt(v.T @ A^{-1} @ v) on the valid submatrix, attempting Cholesky decomposition first for numerical stability and falling back to LU decomposition for non-positive-definite matrices. When the condition number of the valid sub-matrix exceeds cond_threshold, an IllConditionedMatrixWarning is emitted.

Parameters:

Name Type Description Default
vector Vector

The input vector.

required
matrix Matrix | None

Optional square matrix defining the quadratic form.

None
cond_threshold float

Condition-number threshold above which a warning is emitted. Defaults to 1e12.

DEFAULT_COND_THRESHOLD

Returns:

Type Description
float

The Euclidean norm of the finite vector entries, or

float

sqrt(v.T @ A^{-1} @ v) after dropping rows and columns whose

float

diagonal entries are not finite. Returns nan when no valid entries

float

exist.

Raises:

Type Description
NonSquareMatrixError

If the matrix is not square.

DimensionMismatchError

If the vector length does not match the matrix dimension.

SingularMatrixError

If the valid sub-matrix is singular.

Example

import numpy as np from cvx.linalg import inv_a_norm inv_a_norm(np.array([3.0, 4.0])) 5.0

Source code in src/cvx/linalg/norm/norm.py
def inv_a_norm(
    vector: Vector,
    matrix: Matrix | None = None,
    cond_threshold: float = DEFAULT_COND_THRESHOLD,
) -> float:
    """Calculate the inverse A-norm of a vector using an optional matrix.

    If ``matrix`` is ``None``, returns the Euclidean norm of finite entries.
    Otherwise computes ``sqrt(v.T @ A^{-1} @ v)`` on the valid submatrix,
    attempting Cholesky decomposition first for numerical stability and
    falling back to LU decomposition for non-positive-definite matrices.
    When the condition number of the valid sub-matrix exceeds *cond_threshold*,
    an ``IllConditionedMatrixWarning`` is emitted.

    Args:
        vector: The input vector.
        matrix: Optional square matrix defining the quadratic form.
        cond_threshold: Condition-number threshold above which a warning is
            emitted. Defaults to ``1e12``.

    Returns:
        The Euclidean norm of the finite vector entries, or
        ``sqrt(v.T @ A^{-1} @ v)`` after dropping rows and columns whose
        diagonal entries are not finite. Returns ``nan`` when no valid entries
        exist.

    Raises:
        NonSquareMatrixError: If the matrix is not square.
        DimensionMismatchError: If the vector length does not match the matrix dimension.
        SingularMatrixError: If the valid sub-matrix is singular.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import inv_a_norm
        >>> inv_a_norm(np.array([3.0, 4.0]))
        5.0
    """
    if matrix is None:
        return float(np.linalg.norm(vector[np.isfinite(vector)], 2))

    _validate_square(vector, matrix)

    mask, submatrix = valid(matrix)
    if mask.any():
        _check_and_warn_condition(submatrix, cond_threshold)
        filtered_vector = vector[mask]
        try:
            solved = _cholesky_solve(submatrix, filtered_vector)
        except np.linalg.LinAlgError as exc:
            raise SingularMatrixError(str(exc)) from exc
        return float(np.sqrt(np.dot(filtered_vector, solved)))

    return float("nan")

cvx.linalg.core.exceptions.cond(matrix, p=None)

Return the condition number of a matrix.

Returns nan if the matrix contains any non-finite (NaN or inf) entries. Otherwise delegates to :func:numpy.linalg.cond.

Parameters:

Name Type Description Default
matrix Matrix

Input matrix.

required
p int | float | Literal['fro', 'nuc'] | None

Order of the norm used to compute the condition number. Accepts the same values as :func:numpy.linalg.cond (None, 1, -1, 2, -2, numpy.inf, -numpy.inf, 'fro'). Defaults to None which corresponds to the 2-norm (largest singular value divided by the smallest).

None

Returns:

Type Description
float

The condition number as a float, or nan when the matrix

float

contains non-finite entries.

Examples:

>>> import numpy as np
>>> cond(np.eye(3))
1.0
>>> import math
>>> math.isnan(cond(np.array([[float('nan'), 1.0], [1.0, 2.0]])))
True
>>> cond(np.diag([1.0, 1e10]), p=1)
10000000000.0
Source code in src/cvx/linalg/core/exceptions.py
def cond(matrix: Matrix, p: int | float | Literal["fro", "nuc"] | None = None) -> float:
    """Return the condition number of a matrix.

    Returns ``nan`` if the matrix contains any non-finite (NaN or inf) entries.
    Otherwise delegates to :func:`numpy.linalg.cond`.

    Args:
        matrix: Input matrix.
        p: Order of the norm used to compute the condition number.
            Accepts the same values as :func:`numpy.linalg.cond`
            (``None``, ``1``, ``-1``, ``2``, ``-2``, ``numpy.inf``,
            ``-numpy.inf``, ``'fro'``).  Defaults to ``None`` which
            corresponds to the 2-norm (largest singular value divided by
            the smallest).

    Returns:
        The condition number as a ``float``, or ``nan`` when the matrix
        contains non-finite entries.

    Examples:
        >>> import numpy as np
        >>> cond(np.eye(3))
        1.0
        >>> import math
        >>> math.isnan(cond(np.array([[float('nan'), 1.0], [1.0, 2.0]])))
        True
        >>> cond(np.diag([1.0, 1e10]), p=1)
        10000000000.0
    """
    if not np.all(np.isfinite(matrix)):
        return float("nan")
    return float(np.linalg.cond(matrix, p=p))

cvx.linalg.solve.det.det(matrix, cond_threshold=DEFAULT_COND_THRESHOLD)

Return the determinant of a square matrix.

Rows and columns with non-finite diagonal entries are excluded before the computation; when no valid rows or columns remain the function returns nan. When the condition number of the valid sub-matrix exceeds cond_threshold, an IllConditionedMatrixWarning is emitted.

Parameters:

Name Type Description Default
matrix Matrix

Square input matrix.

required
cond_threshold float

Condition-number threshold above which a warning is emitted. Defaults to 1e12.

DEFAULT_COND_THRESHOLD

Returns:

Type Description
float

The determinant of the valid sub-matrix, or nan when no valid

float

entries exist.

Raises:

Type Description
NonSquareMatrixError

If the matrix is not square.

Example

import numpy as np from cvx.linalg import det det(np.eye(3)) 1.0

NaN-masked entries are skipped:

matrix = np.array([[2.0, 0.0], [0.0, np.nan]]) det(matrix) 2.0

Source code in src/cvx/linalg/solve/det.py
def det(
    matrix: Matrix,
    cond_threshold: float = DEFAULT_COND_THRESHOLD,
) -> float:
    """Return the determinant of a square matrix.

    Rows and columns with non-finite diagonal entries are excluded before the
    computation; when no valid rows or columns remain the function returns
    ``nan``.  When the condition number of the valid sub-matrix exceeds
    *cond_threshold*, an ``IllConditionedMatrixWarning`` is emitted.

    Args:
        matrix: Square input matrix.
        cond_threshold: Condition-number threshold above which a warning is
            emitted. Defaults to ``1e12``.

    Returns:
        The determinant of the valid sub-matrix, or ``nan`` when no valid
        entries exist.

    Raises:
        NonSquareMatrixError: If the matrix is not square.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import det
        >>> det(np.eye(3))
        1.0

        NaN-masked entries are skipped:

        >>> matrix = np.array([[2.0, 0.0], [0.0, np.nan]])
        >>> det(matrix)
        2.0
    """
    if matrix.shape[0] != matrix.shape[1]:
        raise NonSquareMatrixError(matrix.shape[0], matrix.shape[1])

    mask, submatrix = valid(matrix)

    if not mask.any():
        return _SENTINEL

    _check_and_warn_condition(submatrix, cond_threshold)
    return float(np.linalg.det(submatrix))

Covariance

cvx.linalg.covariance.ewm_cov.ewm_covariance(data, assets, index_col, window=30, is_halflife=False, warmup=0)

Compute the exponentially weighted covariance matrix of returns.

EWM covariance uses the identity Cov(X, Y) = EWM(X*Y) - EWM(X)*EWM(Y) applied to the common non-null observations of each pair, which is equivalent to pandas.DataFrame.ewm(span).cov(bias=True).

Each date is included in the result as long as at least one matrix entry is non-NaN. Cells involving a late-starting asset are NaN until that asset has enough observations; the date is never dropped on account of a single asset being unavailable. Dates where every cell is NaN (before the warmup period is met for any asset) are omitted.

Parameters:

Name Type Description Default
data DataFrame

Polars DataFrame containing the index column and asset columns.

required
assets list[str]

Ordered list of asset column names.

required
index_col str

Name of the index (e.g. date) column in data.

required
window int

Span (default) or half-life (when is_halflife is True) of the exponential decay. Defaults to 30.

30
is_halflife bool

When True window is interpreted as the half-life; otherwise it is the EWMA span. Defaults to False.

False
warmup int

Minimum number of common observations required before a pair's cell is non-NaN. Defaults to 0 (cells are non-NaN from the first shared observation).

0

Returns:

Type Description
dict[Hashable, Matrix]

Dictionary keyed by index value (date or integer) mapping to

dict[Hashable, Matrix]

a square symmetric numpy.ndarray of shape (n, n)

dict[Hashable, Matrix]

where n is the number of assets. Row/column order

dict[Hashable, Matrix]

matches assets. Unavailable cells are NaN.

Raises:

Type Description
NonIntegerWarmupError

If warmup is not an integer (booleans included).

NegativeWarmupError

If warmup is negative.

Source code in src/cvx/linalg/covariance/ewm_cov.py
def ewm_covariance(
    data: pl.DataFrame,
    assets: list[str],
    index_col: str,
    window: int = 30,
    is_halflife: bool = False,
    warmup: int = 0,
) -> dict[Hashable, Matrix]:
    """Compute the exponentially weighted covariance matrix of returns.

    EWM covariance uses the identity
    ``Cov(X, Y) = EWM(X*Y) - EWM(X)*EWM(Y)`` applied to the
    *common non-null observations* of each pair, which is equivalent
    to ``pandas.DataFrame.ewm(span).cov(bias=True)``.

    Each date is included in the result as long as at least one
    matrix entry is non-NaN.  Cells involving a late-starting asset
    are ``NaN`` until that asset has enough observations; the date is
    never dropped on account of a single asset being unavailable.
    Dates where every cell is NaN (before the warmup period is met
    for any asset) are omitted.

    Args:
        data: Polars DataFrame containing the index column and asset columns.
        assets: Ordered list of asset column names.
        index_col: Name of the index (e.g. date) column in *data*.
        window: Span (default) or half-life (when *is_halflife* is
            ``True``) of the exponential decay.  Defaults to ``30``.
        is_halflife: When ``True`` *window* is interpreted as the
            half-life; otherwise it is the EWMA span.  Defaults to
            ``False``.
        warmup: Minimum number of common observations required before
            a pair's cell is non-NaN.  Defaults to ``0`` (cells are
            non-NaN from the first shared observation).

    Returns:
        Dictionary keyed by index value (date or integer) mapping to
        a square symmetric ``numpy.ndarray`` of shape ``(n, n)``
        where ``n`` is the number of assets.  Row/column order
        matches *assets*.  Unavailable cells are ``NaN``.

    Raises:
        NonIntegerWarmupError: If *warmup* is not an integer (booleans included).
        NegativeWarmupError: If *warmup* is negative.

    """
    _validate_warmup(warmup)

    n = len(assets)
    min_samples = 1 if warmup == 0 else warmup

    def _ewm(expr: pl.Expr) -> pl.Expr:
        """Apply EWM mean with the configured span or half-life."""
        if is_halflife:
            return expr.ewm_mean(half_life=window, min_samples=min_samples)
        return expr.ewm_mean(span=window, min_samples=min_samples)

    pair_df = data.with_columns(_pairwise_cov_exprs(assets, _ewm)).drop(assets)
    all_keys = pair_df[index_col].to_list()
    pair_arr = pair_df.drop(index_col).to_numpy()

    ii, jj = np.triu_indices(n)
    cube = np.full((len(all_keys), n, n), np.nan)
    cube[:, ii, jj] = pair_arr
    cube[:, jj, ii] = pair_arr

    has_data = ~np.all(np.isnan(cube), axis=(1, 2))
    return {k: cube[t] for t, k in enumerate(all_keys) if has_data[t]}

cvx.linalg.covariance.rand_cov.rand_cov(n, seed=None)

Construct a random positive semi-definite covariance matrix of size n x n.

The matrix is constructed as A^T @ A where A is a random n x n matrix with elements drawn from a standard normal distribution. This ensures the result is symmetric and positive semi-definite.

Parameters:

Name Type Description Default
n int

Size of the covariance matrix (n x n).

required
seed int | None

Random seed for reproducibility. If None, uses the current random state.

None

Returns:

Type Description
Matrix

A random positive semi-definite n x n covariance matrix.

Example

Generate a reproducible random covariance matrix:

import numpy as np from cvx.linalg import rand_cov cov1 = rand_cov(3, seed=42) cov2 = rand_cov(3, seed=42) np.allclose(cov1, cov2) True

Verify positive definiteness via Cholesky decomposition:

cov = rand_cov(5, seed=123)

If Cholesky succeeds without error, matrix is positive definite

L = np.linalg.cholesky(cov) bool(np.allclose(L @ L.T, cov)) True

Eigenvalue verification:

cov = rand_cov(3, seed=99) eigenvalues = np.linalg.eigvalsh(cov)

All eigenvalues should be positive for PD matrix

bool(np.all(eigenvalues > 0)) True

Different seeds produce different matrices:

cov1 = rand_cov(3, seed=1) cov2 = rand_cov(3, seed=2) bool(not np.allclose(cov1, cov2)) True

Without seed, consecutive calls may differ (random state):

These may or may not be equal depending on random state

cov_a = rand_cov(2, seed=None) cov_b = rand_cov(2, seed=None) cov_a.shape == cov_b.shape == (2, 2) True

Note

The generated matrix is guaranteed to be positive semi-definite because it is constructed as A^T @ A. In practice, it will typically be positive definite (all eigenvalues strictly positive) unless n is very large.

Source code in src/cvx/linalg/covariance/rand_cov.py
def rand_cov(n: int, seed: int | None = None) -> Matrix:
    """Construct a random positive semi-definite covariance matrix of size n x n.

    The matrix is constructed as A^T @ A where A is a random n x n matrix with
    elements drawn from a standard normal distribution. This ensures the result
    is symmetric and positive semi-definite.

    Args:
        n: Size of the covariance matrix (n x n).
        seed: Random seed for reproducibility. If None, uses the current
            random state.

    Returns:
        A random positive semi-definite n x n covariance matrix.

    Example:
        Generate a reproducible random covariance matrix:

        >>> import numpy as np
        >>> from cvx.linalg import rand_cov
        >>> cov1 = rand_cov(3, seed=42)
        >>> cov2 = rand_cov(3, seed=42)
        >>> np.allclose(cov1, cov2)
        True

        Verify positive definiteness via Cholesky decomposition:

        >>> cov = rand_cov(5, seed=123)
        >>> # If Cholesky succeeds without error, matrix is positive definite
        >>> L = np.linalg.cholesky(cov)
        >>> bool(np.allclose(L @ L.T, cov))
        True

        Eigenvalue verification:

        >>> cov = rand_cov(3, seed=99)
        >>> eigenvalues = np.linalg.eigvalsh(cov)
        >>> # All eigenvalues should be positive for PD matrix
        >>> bool(np.all(eigenvalues > 0))
        True

        Different seeds produce different matrices:

        >>> cov1 = rand_cov(3, seed=1)
        >>> cov2 = rand_cov(3, seed=2)
        >>> bool(not np.allclose(cov1, cov2))
        True

        Without seed, consecutive calls may differ (random state):

        >>> # These may or may not be equal depending on random state
        >>> cov_a = rand_cov(2, seed=None)
        >>> cov_b = rand_cov(2, seed=None)
        >>> cov_a.shape == cov_b.shape == (2, 2)
        True

    Note:
        The generated matrix is guaranteed to be positive semi-definite because
        it is constructed as A^T @ A. In practice, it will typically be positive
        definite (all eigenvalues strictly positive) unless n is very large.

    """
    rng = np.random.default_rng(seed)
    a = rng.standard_normal((n, n))
    return np.transpose(a) @ a

cvx.linalg.covariance.cov_to_corr.cov_to_corr(cov, min_var=1e-14)

Convert a covariance matrix to a correlation matrix.

Off-diagonal entries are symmetrised by averaging the upper and lower triangles, so floating-point asymmetry in cov does not propagate. Diagonal entries are set to 1.0 when the variance is above min_var and to nan otherwise. All entries are clipped to [-1, 1].

Parameters:

Name Type Description Default
cov Matrix

Square covariance matrix of shape (N, N).

required
min_var float

Threshold below which a variance is treated as zero; the corresponding row and column are filled with nan. Defaults to 1e-14.

1e-14

Returns:

Type Description
Matrix

Symmetrised correlation matrix of shape (N, N) with diagonal

Matrix

entries in {1.0, nan}.

Example

import numpy as np from cvx.linalg import cov_to_corr cov = np.array([[4.0, 2.0], [2.0, 9.0]]) corr = cov_to_corr(cov) np.allclose(np.diag(corr), [1.0, 1.0]) True float(round(corr[0, 1], 6)) 0.333333

Source code in src/cvx/linalg/covariance/cov_to_corr.py
def cov_to_corr(cov: Matrix, min_var: float = 1e-14) -> Matrix:
    """Convert a covariance matrix to a correlation matrix.

    Off-diagonal entries are symmetrised by averaging the upper and lower
    triangles, so floating-point asymmetry in *cov* does not propagate.
    Diagonal entries are set to ``1.0`` when the variance is above *min_var*
    and to ``nan`` otherwise.  All entries are clipped to ``[-1, 1]``.

    Args:
        cov: Square covariance matrix of shape ``(N, N)``.
        min_var: Threshold below which a variance is treated as zero;
            the corresponding row and column are filled with ``nan``.
            Defaults to ``1e-14``.

    Returns:
        Symmetrised correlation matrix of shape ``(N, N)`` with diagonal
        entries in ``{1.0, nan}``.

    Example:
        >>> import numpy as np
        >>> from cvx.linalg import cov_to_corr
        >>> cov = np.array([[4.0, 2.0], [2.0, 9.0]])
        >>> corr = cov_to_corr(cov)
        >>> np.allclose(np.diag(corr), [1.0, 1.0])
        True
        >>> float(round(corr[0, 1], 6))
        0.333333
    """
    var = np.diag(cov)
    denom = np.sqrt(np.outer(var, var))
    with np.errstate(divide="ignore", invalid="ignore"):
        corr = np.where(denom > min_var, cov / denom, np.nan)
    corr = np.clip(corr, -1.0, 1.0)
    n = len(var)
    idx = np.arange(n)
    corr[idx, idx] = np.where(var > min_var, 1.0, np.nan)
    tril_i, tril_j = np.tril_indices(n, k=-1)
    avg = 0.5 * (corr[tril_i, tril_j] + corr[tril_j, tril_i])
    corr[tril_i, tril_j] = avg
    corr[tril_j, tril_i] = avg
    return corr

Validation

cvx.linalg.core.valid.valid(matrix)

Extract the valid subset of a matrix by removing rows/columns with non-finite values.

This function identifies rows and columns in a square matrix that contain non-finite values (NaN or infinity) on the diagonal and removes them, returning both the indicator vector and the resulting valid submatrix.

This is useful when working with covariance matrices where some assets may have missing or invalid data.

Parameters:

Name Type Description Default
matrix Matrix

A square n x n matrix to be validated. Typically a covariance or correlation matrix.

required

Returns:

Type Description
tuple[NDArray[bool_], Matrix]

A tuple containing: - v: Boolean vector of shape (n,) indicating which rows/columns are valid (True for valid, False for invalid). - submatrix: The valid submatrix with invalid rows/columns removed. Shape is (k, k) where k is the number of True values in v.

Raises:

Type Description
NonSquareMatrixError

If the input matrix is not square (n x n).

Example

Basic usage with a covariance matrix:

import numpy as np from cvx.linalg import valid

Create a 3x3 matrix with one invalid entry

cov = np.array([[1.0, 0.5, 0.2], ... [0.5, np.nan, 0.3], ... [0.2, 0.3, 1.0]]) v, submatrix = valid(cov) v array([ True, False, True]) submatrix array([[1. , 0.2], [0.2, 1. ]])

Handling a fully valid matrix:

cov = np.array([[1.0, 0.5], [0.5, 1.0]]) v, submatrix = valid(cov) v array([ True, True]) np.allclose(submatrix, cov) True

Handling infinity values:

cov = np.array([[1.0, 0.5, 0.2], ... [0.5, np.inf, 0.3], ... [0.2, 0.3, 1.0]]) v, submatrix = valid(cov) v array([ True, False, True]) submatrix array([[1. , 0.2], [0.2, 1. ]])

Multiple invalid entries:

cov = np.array([[np.nan, 0.1, 0.2, 0.3], ... [0.1, 2.0, 0.4, 0.5], ... [0.2, 0.4, np.nan, 0.6], ... [0.3, 0.5, 0.6, 3.0]]) v, submatrix = valid(cov) v array([False, True, False, True]) submatrix.shape (2, 2) submatrix array([[2. , 0.5], [0.5, 3. ]])

Non-square matrix raises NonSquareMatrixError:

try: ... valid(np.array([[1, 2, 3], [4, 5, 6]])) ... except NonSquareMatrixError: ... print("Caught NonSquareMatrixError for non-square matrix") Caught NonSquareMatrixError for non-square matrix

Note

The function checks only the diagonal elements for validity. It assumes that if the diagonal is finite, the entire row/column is valid. This is a common assumption for covariance matrices.

Source code in src/cvx/linalg/core/valid.py
def valid(matrix: Matrix) -> tuple[npt.NDArray[np.bool_], Matrix]:
    """Extract the valid subset of a matrix by removing rows/columns with non-finite values.

    This function identifies rows and columns in a square matrix that contain
    non-finite values (NaN or infinity) on the diagonal and removes them,
    returning both the indicator vector and the resulting valid submatrix.

    This is useful when working with covariance matrices where some assets
    may have missing or invalid data.

    Args:
        matrix: A square n x n matrix to be validated. Typically a covariance
            or correlation matrix.

    Returns:
        A tuple containing:
            - v: Boolean vector of shape (n,) indicating which rows/columns are
              valid (True for valid, False for invalid).
            - submatrix: The valid submatrix with invalid rows/columns removed.
              Shape is (k, k) where k is the number of True values in v.

    Raises:
        NonSquareMatrixError: If the input matrix is not square (n x n).

    Example:
        Basic usage with a covariance matrix:

        >>> import numpy as np
        >>> from cvx.linalg import valid
        >>> # Create a 3x3 matrix with one invalid entry
        >>> cov = np.array([[1.0, 0.5, 0.2],
        ...                 [0.5, np.nan, 0.3],
        ...                 [0.2, 0.3, 1.0]])
        >>> v, submatrix = valid(cov)
        >>> v
        array([ True, False,  True])
        >>> submatrix
        array([[1. , 0.2],
               [0.2, 1. ]])

        Handling a fully valid matrix:

        >>> cov = np.array([[1.0, 0.5], [0.5, 1.0]])
        >>> v, submatrix = valid(cov)
        >>> v
        array([ True,  True])
        >>> np.allclose(submatrix, cov)
        True

        Handling infinity values:

        >>> cov = np.array([[1.0, 0.5, 0.2],
        ...                 [0.5, np.inf, 0.3],
        ...                 [0.2, 0.3, 1.0]])
        >>> v, submatrix = valid(cov)
        >>> v
        array([ True, False,  True])
        >>> submatrix
        array([[1. , 0.2],
               [0.2, 1. ]])

        Multiple invalid entries:

        >>> cov = np.array([[np.nan, 0.1, 0.2, 0.3],
        ...                 [0.1, 2.0, 0.4, 0.5],
        ...                 [0.2, 0.4, np.nan, 0.6],
        ...                 [0.3, 0.5, 0.6, 3.0]])
        >>> v, submatrix = valid(cov)
        >>> v
        array([False,  True, False,  True])
        >>> submatrix.shape
        (2, 2)
        >>> submatrix
        array([[2. , 0.5],
               [0.5, 3. ]])

        Non-square matrix raises NonSquareMatrixError:

        >>> try:
        ...     valid(np.array([[1, 2, 3], [4, 5, 6]]))
        ... except NonSquareMatrixError:
        ...     print("Caught NonSquareMatrixError for non-square matrix")
        Caught NonSquareMatrixError for non-square matrix

    Note:
        The function checks only the diagonal elements for validity. It assumes
        that if the diagonal is finite, the entire row/column is valid. This is
        a common assumption for covariance matrices.

    """
    if matrix.shape[0] != matrix.shape[1]:
        raise NonSquareMatrixError(matrix.shape[0], matrix.shape[1])

    v = np.isfinite(np.diag(matrix))
    return v, matrix[:, v][v]

Exceptions & Warnings

cvx.linalg.core.exceptions.SingularMatrixError

Bases: ValueError

Raised when a matrix is (numerically) singular and cannot be inverted.

Parameters:

Name Type Description Default
detail str

Optional extra detail string to append to the message.

''

Examples:

>>> raise SingularMatrixError()
Traceback (most recent call last):
    ...
cvx.linalg.core.exceptions.SingularMatrixError: Matrix is singular and cannot be solved.
Source code in src/cvx/linalg/core/exceptions.py
class SingularMatrixError(ValueError):
    """Raised when a matrix is (numerically) singular and cannot be inverted.

    Args:
        detail: Optional extra detail string to append to the message.

    Examples:
        >>> raise SingularMatrixError()
        Traceback (most recent call last):
            ...
        cvx.linalg.core.exceptions.SingularMatrixError: Matrix is singular and cannot be solved.
    """

    def __init__(self, detail: str = "") -> None:
        """Initialize with an optional extra detail string."""
        msg = "Matrix is singular and cannot be solved."
        if detail:
            msg = f"{msg} {detail}"
        super().__init__(msg)

__init__(detail='')

Initialize with an optional extra detail string.

Source code in src/cvx/linalg/core/exceptions.py
def __init__(self, detail: str = "") -> None:
    """Initialize with an optional extra detail string."""
    msg = "Matrix is singular and cannot be solved."
    if detail:
        msg = f"{msg} {detail}"
    super().__init__(msg)

cvx.linalg.core.exceptions.IllConditionedMatrixWarning

Bases: UserWarning

Emitted when a matrix condition number exceeds a configurable threshold.

Examples:

>>> import warnings
>>> with warnings.catch_warnings(record=True) as w:
...     warnings.simplefilter("always")
...     warnings.warn("condition number 1e13", IllConditionedMatrixWarning)
...     issubclass(w[-1].category, IllConditionedMatrixWarning)
True
Source code in src/cvx/linalg/core/exceptions.py
class IllConditionedMatrixWarning(UserWarning):
    """Emitted when a matrix condition number exceeds a configurable threshold.

    Examples:
        >>> import warnings
        >>> with warnings.catch_warnings(record=True) as w:
        ...     warnings.simplefilter("always")
        ...     warnings.warn("condition number 1e13", IllConditionedMatrixWarning)
        ...     issubclass(w[-1].category, IllConditionedMatrixWarning)
        True
    """

cvx.linalg.core.exceptions.DimensionMismatchError

Bases: ValueError

Raised when vector and matrix dimensions are incompatible.

Parameters:

Name Type Description Default
vector_size int

Length of the offending vector.

required
matrix_size int

Expected dimension inferred from the matrix.

required

Examples:

>>> raise DimensionMismatchError(3, 2)
Traceback (most recent call last):
    ...
cvx.linalg.core.exceptions.DimensionMismatchError: Vector length 3 does not match matrix dimension 2.
Source code in src/cvx/linalg/core/exceptions.py
class DimensionMismatchError(ValueError):
    """Raised when vector and matrix dimensions are incompatible.

    Args:
        vector_size: Length of the offending vector.
        matrix_size: Expected dimension inferred from the matrix.

    Examples:
        >>> raise DimensionMismatchError(3, 2)
        Traceback (most recent call last):
            ...
        cvx.linalg.core.exceptions.DimensionMismatchError: Vector length 3 does not match matrix dimension 2.
    """

    def __init__(self, vector_size: int, matrix_size: int) -> None:
        """Initialize with the offending vector and matrix sizes."""
        super().__init__(f"Vector length {vector_size} does not match matrix dimension {matrix_size}.")
        self.vector_size = vector_size
        self.matrix_size = matrix_size

__init__(vector_size, matrix_size)

Initialize with the offending vector and matrix sizes.

Source code in src/cvx/linalg/core/exceptions.py
def __init__(self, vector_size: int, matrix_size: int) -> None:
    """Initialize with the offending vector and matrix sizes."""
    super().__init__(f"Vector length {vector_size} does not match matrix dimension {matrix_size}.")
    self.vector_size = vector_size
    self.matrix_size = matrix_size

cvx.linalg.core.exceptions.NonSquareMatrixError

Bases: ValueError

Raised when a square matrix is required but the input is not square.

Parameters:

Name Type Description Default
rows int

Number of rows in the offending matrix.

required
cols int

Number of columns in the offending matrix.

required

Examples:

>>> raise NonSquareMatrixError(3, 2)
Traceback (most recent call last):
    ...
cvx.linalg.core.exceptions.NonSquareMatrixError: Matrix must be square, got shape (3, 2).
Source code in src/cvx/linalg/core/exceptions.py
class NonSquareMatrixError(ValueError):
    """Raised when a square matrix is required but the input is not square.

    Args:
        rows: Number of rows in the offending matrix.
        cols: Number of columns in the offending matrix.

    Examples:
        >>> raise NonSquareMatrixError(3, 2)
        Traceback (most recent call last):
            ...
        cvx.linalg.core.exceptions.NonSquareMatrixError: Matrix must be square, got shape (3, 2).
    """

    def __init__(self, rows: int, cols: int) -> None:
        """Initialize with the offending matrix shape."""
        super().__init__(f"Matrix must be square, got shape ({rows}, {cols}).")
        self.rows = rows
        self.cols = cols

__init__(rows, cols)

Initialize with the offending matrix shape.

Source code in src/cvx/linalg/core/exceptions.py
def __init__(self, rows: int, cols: int) -> None:
    """Initialize with the offending matrix shape."""
    super().__init__(f"Matrix must be square, got shape ({rows}, {cols}).")
    self.rows = rows
    self.cols = cols

cvx.linalg.core.exceptions.NotAMatrixError

Bases: TypeError

Raised when a 2-D matrix is required but the input has a different number of dimensions.

Parameters:

Name Type Description Default
ndim int

Actual number of dimensions of the offending array.

required
func str

Name of the function that rejected the input.

'eigvals'

Examples:

>>> raise NotAMatrixError(3)
Traceback (most recent call last):
    ...
cvx.linalg.core.exceptions.NotAMatrixError: eigvals() expected a 2-D matrix, got 3-D input.
>>> raise NotAMatrixError(3, func="qr")
Traceback (most recent call last):
    ...
cvx.linalg.core.exceptions.NotAMatrixError: qr() expected a 2-D matrix, got 3-D input.
Source code in src/cvx/linalg/core/exceptions.py
class NotAMatrixError(TypeError):
    """Raised when a 2-D matrix is required but the input has a different number of dimensions.

    Args:
        ndim: Actual number of dimensions of the offending array.
        func: Name of the function that rejected the input.

    Examples:
        >>> raise NotAMatrixError(3)
        Traceback (most recent call last):
            ...
        cvx.linalg.core.exceptions.NotAMatrixError: eigvals() expected a 2-D matrix, got 3-D input.
        >>> raise NotAMatrixError(3, func="qr")
        Traceback (most recent call last):
            ...
        cvx.linalg.core.exceptions.NotAMatrixError: qr() expected a 2-D matrix, got 3-D input.
    """

    def __init__(self, ndim: int, func: str = "eigvals") -> None:
        """Initialize with the actual number of dimensions and the rejecting function."""
        super().__init__(f"{func}() expected a 2-D matrix, got {ndim}-D input.")
        self.ndim = ndim
        self.func = func

__init__(ndim, func='eigvals')

Initialize with the actual number of dimensions and the rejecting function.

Source code in src/cvx/linalg/core/exceptions.py
def __init__(self, ndim: int, func: str = "eigvals") -> None:
    """Initialize with the actual number of dimensions and the rejecting function."""
    super().__init__(f"{func}() expected a 2-D matrix, got {ndim}-D input.")
    self.ndim = ndim
    self.func = func

cvx.linalg.core.exceptions.NegativeWarmupError

Bases: ValueError

Raised when a negative warmup period is requested.

Parameters:

Name Type Description Default
warmup int | None

The offending warmup value.

None

Examples:

>>> raise NegativeWarmupError(-3)
Traceback (most recent call last):
    ...
cvx.linalg.core.exceptions.NegativeWarmupError: warmup must be non-negative, got -3.
Source code in src/cvx/linalg/core/exceptions.py
class NegativeWarmupError(ValueError):
    """Raised when a negative warmup period is requested.

    Args:
        warmup: The offending warmup value.

    Examples:
        >>> raise NegativeWarmupError(-3)
        Traceback (most recent call last):
            ...
        cvx.linalg.core.exceptions.NegativeWarmupError: warmup must be non-negative, got -3.
    """

    def __init__(self, warmup: int | None = None) -> None:
        """Initialize with the offending warmup value."""
        msg = "warmup must be non-negative."
        if warmup is not None:
            msg = f"warmup must be non-negative, got {warmup}."
        super().__init__(msg)
        self.warmup = warmup

__init__(warmup=None)

Initialize with the offending warmup value.

Source code in src/cvx/linalg/core/exceptions.py
def __init__(self, warmup: int | None = None) -> None:
    """Initialize with the offending warmup value."""
    msg = "warmup must be non-negative."
    if warmup is not None:
        msg = f"warmup must be non-negative, got {warmup}."
    super().__init__(msg)
    self.warmup = warmup

cvx.linalg.core.exceptions.NonIntegerWarmupError

Bases: TypeError

Raised when warmup is not an integer (booleans are rejected as well).

Parameters:

Name Type Description Default
value object

The offending warmup value.

required

Examples:

>>> raise NonIntegerWarmupError(True)
Traceback (most recent call last):
    ...
cvx.linalg.core.exceptions.NonIntegerWarmupError: warmup must be an integer, got bool.
Source code in src/cvx/linalg/core/exceptions.py
class NonIntegerWarmupError(TypeError):
    """Raised when warmup is not an integer (booleans are rejected as well).

    Args:
        value: The offending warmup value.

    Examples:
        >>> raise NonIntegerWarmupError(True)
        Traceback (most recent call last):
            ...
        cvx.linalg.core.exceptions.NonIntegerWarmupError: warmup must be an integer, got bool.
    """

    def __init__(self, value: object) -> None:
        """Initialize with the offending warmup value."""
        super().__init__(f"warmup must be an integer, got {type(value).__name__}.")
        self.value = value

__init__(value)

Initialize with the offending warmup value.

Source code in src/cvx/linalg/core/exceptions.py
def __init__(self, value: object) -> None:
    """Initialize with the offending warmup value."""
    super().__init__(f"warmup must be an integer, got {type(value).__name__}.")
    self.value = value

cvx.linalg.core.exceptions.InvalidComponentsError

Bases: ValueError

Raised when the requested number of principal components is out of range.

Parameters:

Name Type Description Default
n_components int

The requested number of components.

required
max_components int

The largest number of components supported by the data.

required

Examples:

>>> raise InvalidComponentsError(10, 5)
Traceback (most recent call last):
    ...
cvx.linalg.core.exceptions.InvalidComponentsError: n_components must be between 1 and 5, got 10.
Source code in src/cvx/linalg/core/exceptions.py
class InvalidComponentsError(ValueError):
    """Raised when the requested number of principal components is out of range.

    Args:
        n_components: The requested number of components.
        max_components: The largest number of components supported by the data.

    Examples:
        >>> raise InvalidComponentsError(10, 5)
        Traceback (most recent call last):
            ...
        cvx.linalg.core.exceptions.InvalidComponentsError: n_components must be between 1 and 5, got 10.
    """

    def __init__(self, n_components: int, max_components: int) -> None:
        """Initialize with the requested and maximum number of components."""
        super().__init__(f"n_components must be between 1 and {max_components}, got {n_components}.")
        self.n_components = n_components
        self.max_components = max_components

__init__(n_components, max_components)

Initialize with the requested and maximum number of components.

Source code in src/cvx/linalg/core/exceptions.py
def __init__(self, n_components: int, max_components: int) -> None:
    """Initialize with the requested and maximum number of components."""
    super().__init__(f"n_components must be between 1 and {max_components}, got {n_components}.")
    self.n_components = n_components
    self.max_components = max_components

cvx.linalg.core.exceptions.check_and_warn_condition(matrix, threshold)

Emit IllConditionedMatrixWarning when the condition number exceeds threshold.

Parameters:

Name Type Description Default
matrix Matrix

Square matrix whose condition number is checked.

required
threshold float

Upper bound before a warning is issued.

required
Example

import numpy as np import warnings with warnings.catch_warnings(record=True) as w: ... warnings.simplefilter("always") ... check_and_warn_condition(np.eye(2), 0.5) ... len(w) 1

Source code in src/cvx/linalg/core/exceptions.py
def check_and_warn_condition(matrix: Matrix, threshold: float) -> None:
    """Emit IllConditionedMatrixWarning when the condition number exceeds threshold.

    Args:
        matrix: Square matrix whose condition number is checked.
        threshold: Upper bound before a warning is issued.

    Example:
        >>> import numpy as np
        >>> import warnings
        >>> with warnings.catch_warnings(record=True) as w:
        ...     warnings.simplefilter("always")
        ...     check_and_warn_condition(np.eye(2), 0.5)
        ...     len(w)
        1
    """
    warn_ill_conditioned(cond(matrix), threshold, stacklevel=4)

cvx.linalg.core.exceptions.warn_ill_conditioned(cond_value, threshold, stacklevel=3)

Emit IllConditionedMatrixWarning when cond_value exceeds threshold.

Parameters:

Name Type Description Default
cond_value float

Condition number to compare against the threshold.

required
threshold float

Upper bound before a warning is issued.

required
stacklevel int

Stack level passed to :func:warnings.warn so the warning points at the caller of the public API. Defaults to 3.

3
Example

import warnings with warnings.catch_warnings(record=True) as w: ... warnings.simplefilter("always") ... warn_ill_conditioned(2.0, 0.5) ... len(w) 1

Source code in src/cvx/linalg/core/exceptions.py
def warn_ill_conditioned(cond_value: float, threshold: float, stacklevel: int = 3) -> None:
    """Emit IllConditionedMatrixWarning when *cond_value* exceeds *threshold*.

    Args:
        cond_value: Condition number to compare against the threshold.
        threshold: Upper bound before a warning is issued.
        stacklevel: Stack level passed to :func:`warnings.warn` so the warning
            points at the caller of the public API. Defaults to ``3``.

    Example:
        >>> import warnings
        >>> with warnings.catch_warnings(record=True) as w:
        ...     warnings.simplefilter("always")
        ...     warn_ill_conditioned(2.0, 0.5)
        ...     len(w)
        1
    """
    if cond_value > threshold:
        warnings.warn(
            f"Matrix condition number {cond_value:.3e} exceeds threshold {threshold:.3e}; "
            "results may be numerically unreliable.",
            IllConditionedMatrixWarning,
            stacklevel=stacklevel,
        )

cvx.linalg.core.exceptions.DEFAULT_COND_THRESHOLD = 1000000000000.0 module-attribute

Default condition-number threshold above which an IllConditionedMatrixWarning is emitted.


Types

cvx.linalg.core.types.Matrix = npt.NDArray[np.float64] module-attribute

A 2-D float64 NumPy array.

cvx.linalg.core.types.Vector = npt.NDArray[np.float64] module-attribute

A 1-D float64 NumPy array.

cvx.linalg.core.types.SupportsMatvec

Bases: Protocol

Structural protocol for an operator applied matrix-free.

Anything exposing a dimension n and a matvec(x) -> A @ x satisfies it, so a routine can accept an operator by shape rather than by importing a concrete class. Every :class:~cvx.linalg.SymmetricOperator conforms; plain arrays and bare callables deliberately do not. It lives in :mod:~cvx.linalg.core.types (the lowest layer) so consumers such as :func:~cvx.linalg.power_iteration can isinstance-check against it without pulling in the operator backends.

Source code in src/cvx/linalg/core/types.py
@runtime_checkable
class SupportsMatvec(Protocol):
    """Structural protocol for an operator applied matrix-free.

    Anything exposing a dimension ``n`` and a ``matvec(x) -> A @ x`` satisfies it,
    so a routine can accept an operator by shape rather than by importing a concrete
    class. Every :class:`~cvx.linalg.SymmetricOperator` conforms; plain arrays and
    bare callables deliberately do not. It lives in :mod:`~cvx.linalg.core.types`
    (the lowest layer) so consumers such as
    :func:`~cvx.linalg.power_iteration` can ``isinstance``-check against it without
    pulling in the operator backends.
    """

    @property
    def n(self) -> int:
        """Dimension of the operator (it acts on vectors of length ``n``)."""
        ...

    def matvec(self, x: Vector | Matrix) -> Vector | Matrix:
        """Return ``A @ x``."""
        ...

n property

Dimension of the operator (it acts on vectors of length n).

matvec(x)

Return A @ x.

Source code in src/cvx/linalg/core/types.py
def matvec(self, x: Vector | Matrix) -> Vector | Matrix:
    """Return ``A @ x``."""
    ...