Coverage for src / cvx / linalg / valid.py: 100%
8 statements
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-19 05:40 +0000
« prev ^ index » next coverage.py v7.14.0, created at 2026-05-19 05:40 +0000
1"""Matrix validation utilities for handling non-finite values.
3This module provides functions for validating and cleaning matrices that may
4contain non-finite values (NaN or infinity). This is particularly useful when
5working with financial data where missing values are common.
7Example:
8 Extract the valid submatrix from a covariance matrix with missing data:
10 >>> import numpy as np
11 >>> from cvx.linalg import valid
12 >>> # Create a covariance matrix with some NaN values on diagonal
13 >>> cov = np.array([[np.nan, 0.5, 0.2],
14 ... [0.5, 2.0, 0.3],
15 ... [0.2, 0.3, np.nan]])
16 >>> # Get valid indicator and submatrix
17 >>> v, submatrix = valid(cov)
18 >>> v # Second row/column is valid
19 array([False, True, False])
20 >>> submatrix
21 array([[2.]])
23"""
25from __future__ import annotations
27import numpy as np
29from .exceptions import NonSquareMatrixError
32def valid(matrix: np.ndarray) -> tuple[np.ndarray, np.ndarray]:
33 """Extract the valid subset of a matrix by removing rows/columns with non-finite values.
35 This function identifies rows and columns in a square matrix that contain
36 non-finite values (NaN or infinity) on the diagonal and removes them,
37 returning both the indicator vector and the resulting valid submatrix.
39 This is useful when working with covariance matrices where some assets
40 may have missing or invalid data.
42 Args:
43 matrix: A square n x n matrix to be validated. Typically a covariance
44 or correlation matrix.
46 Returns:
47 A tuple containing:
48 - v: Boolean vector of shape (n,) indicating which rows/columns are
49 valid (True for valid, False for invalid).
50 - submatrix: The valid submatrix with invalid rows/columns removed.
51 Shape is (k, k) where k is the number of True values in v.
53 Raises:
54 NonSquareMatrixError: If the input matrix is not square (n x n).
56 Example:
57 Basic usage with a covariance matrix:
59 >>> import numpy as np
60 >>> from cvx.linalg import valid
61 >>> # Create a 3x3 matrix with one invalid entry
62 >>> cov = np.array([[1.0, 0.5, 0.2],
63 ... [0.5, np.nan, 0.3],
64 ... [0.2, 0.3, 1.0]])
65 >>> v, submatrix = valid(cov)
66 >>> v
67 array([ True, False, True])
68 >>> submatrix
69 array([[1. , 0.2],
70 [0.2, 1. ]])
72 Handling a fully valid matrix:
74 >>> cov = np.array([[1.0, 0.5], [0.5, 1.0]])
75 >>> v, submatrix = valid(cov)
76 >>> v
77 array([ True, True])
78 >>> np.allclose(submatrix, cov)
79 True
81 Handling infinity values:
83 >>> cov = np.array([[1.0, 0.5, 0.2],
84 ... [0.5, np.inf, 0.3],
85 ... [0.2, 0.3, 1.0]])
86 >>> v, submatrix = valid(cov)
87 >>> v
88 array([ True, False, True])
89 >>> submatrix
90 array([[1. , 0.2],
91 [0.2, 1. ]])
93 Multiple invalid entries:
95 >>> cov = np.array([[np.nan, 0.1, 0.2, 0.3],
96 ... [0.1, 2.0, 0.4, 0.5],
97 ... [0.2, 0.4, np.nan, 0.6],
98 ... [0.3, 0.5, 0.6, 3.0]])
99 >>> v, submatrix = valid(cov)
100 >>> v
101 array([False, True, False, True])
102 >>> submatrix.shape
103 (2, 2)
104 >>> submatrix
105 array([[2. , 0.5],
106 [0.5, 3. ]])
108 Non-square matrix raises NonSquareMatrixError:
110 >>> try:
111 ... valid(np.array([[1, 2, 3], [4, 5, 6]]))
112 ... except NonSquareMatrixError:
113 ... print("Caught NonSquareMatrixError for non-square matrix")
114 Caught NonSquareMatrixError for non-square matrix
116 Note:
117 The function checks only the diagonal elements for validity. It assumes
118 that if the diagonal is finite, the entire row/column is valid. This is
119 a common assumption for covariance matrices.
121 """
122 if matrix.shape[0] != matrix.shape[1]:
123 raise NonSquareMatrixError(matrix.shape[0], matrix.shape[1])
125 v = np.isfinite(np.diag(matrix))
126 return v, matrix[:, v][v]