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 |
None
|
Returns:
| Type | Description |
|---|---|
Vector | Matrix
|
The upper triangular Cholesky factor R when rhs is |
Vector | Matrix
|
solution x to |
Raises:
| Type | Description |
|---|---|
LinAlgError
|
When rhs is |
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
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 |
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
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
|
|
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
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 |
Matrix
|
on the valid submatrix. Eigenvalues are sorted in ascending order. |
Source code in src/cvx/linalg/decomposition/eigh.py
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
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 |
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
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 |
required |
Returns:
| Type | Description |
|---|---|
tuple[Matrix, Matrix]
|
A tuple |
Raises:
| Type | Description |
|---|---|
NotAMatrixError
|
If |
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
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 |
required |
Returns:
| Type | Description |
|---|---|
tuple[Matrix, Matrix, Matrix]
|
Tuple |
Source code in src/cvx/linalg/decomposition/svd.py
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 |
required |
k
|
int
|
Number of leading singular triplets to keep. Must be between 1 and
|
required |
Returns:
| Type | Description |
|---|---|
Matrix
|
Tuple |
Vector
|
such that |
Matrix
|
of matrix. Singular values are in descending order. |
Raises:
| Type | Description |
|---|---|
InvalidComponentsError
|
If k is smaller than 1 or larger than
|
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
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 |
10
|
Raises:
| Type | Description |
|---|---|
InvalidComponentsError
|
If n_components is smaller than 1 or larger
than |
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
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:matvecandndrive the iteration, so non x nmatrix is formed); or - a callable
v -> A @ vtogether 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: |
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 |
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-09
|
seed
|
int | None
|
Seed for the random starting vector, for reproducibility. If
|
None
|
Returns:
| Type | Description |
|---|---|
float
|
Tuple |
Vector
|
dominant eigenvalue estimate (a |
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
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 |
required |
rhs
|
Vector | Matrix
|
Right-hand side vector of length |
required |
cond_threshold
|
float
|
Condition-number threshold above which a warning is
emitted. Defaults to |
DEFAULT_COND_THRESHOLD
|
Returns:
| Type | Description |
|---|---|
Vector | Matrix
|
A solution array with the same shape as |
Vector | Matrix
|
invalid rows or columns are returned as |
Raises:
| Type | Description |
|---|---|
NonSquareMatrixError
|
If the matrix is not square. |
DimensionMismatchError
|
If the leading dimension of |
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
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 |
required |
rhs
|
Vector
|
Right-hand side vector of length |
required |
cond_threshold
|
float
|
Condition-number threshold above which a warning is
emitted. Defaults to |
DEFAULT_COND_THRESHOLD
|
Returns:
| Type | Description |
|---|---|
Vector
|
A four-tuple |
Vector
|
func: |
int
|
|
Vector
|
|
tuple[Vector, Vector, int, Vector]
|
|
tuple[Vector, Vector, int, Vector]
|
|
Raises:
| Type | Description |
|---|---|
DimensionMismatchError
|
If |
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
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 |
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 |
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
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 |
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
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 |
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
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 |
DEFAULT_COND_THRESHOLD
|
Returns:
| Type | Description |
|---|---|
float
|
The Euclidean norm of the finite vector entries, or |
float
|
|
float
|
diagonal entries are not finite. Returns |
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
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: |
None
|
Returns:
| Type | Description |
|---|---|
float
|
The condition number as a |
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
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 |
DEFAULT_COND_THRESHOLD
|
Returns:
| Type | Description |
|---|---|
float
|
The determinant of the valid sub-matrix, or |
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
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
|
30
|
is_halflife
|
bool
|
When |
False
|
warmup
|
int
|
Minimum number of common observations required before
a pair's cell is non-NaN. Defaults to |
0
|
Returns:
| Type | Description |
|---|---|
dict[Hashable, Matrix]
|
Dictionary keyed by index value (date or integer) mapping to |
dict[Hashable, Matrix]
|
a square symmetric |
dict[Hashable, Matrix]
|
where |
dict[Hashable, Matrix]
|
matches assets. Unavailable cells are |
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
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
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 |
required |
min_var
|
float
|
Threshold below which a variance is treated as zero;
the corresponding row and column are filled with |
1e-14
|
Returns:
| Type | Description |
|---|---|
Matrix
|
Symmetrised correlation matrix of shape |
Matrix
|
entries in |
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
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
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 | |
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
__init__(detail='')
¶
Initialize with an optional extra detail string.
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
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
__init__(vector_size, matrix_size)
¶
Initialize with the offending vector and matrix sizes.
Source code in src/cvx/linalg/core/exceptions.py
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
__init__(rows, cols)
¶
Initialize with the offending matrix shape.
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
__init__(ndim, func='eigvals')
¶
Initialize with the actual number of dimensions and the rejecting function.
Source code in src/cvx/linalg/core/exceptions.py
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
__init__(warmup=None)
¶
Initialize with the offending warmup value.
Source code in src/cvx/linalg/core/exceptions.py
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
__init__(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
__init__(n_components, max_components)
¶
Initialize with the requested and maximum number of components.
Source code in src/cvx/linalg/core/exceptions.py
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
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: |
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
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.