Coverage for src/cvx/linalg/solve/inv.py: 100%

21 statements  

« prev     ^ index     » next       coverage.py v7.15.0, created at 2026-07-03 18:56 +0000

1"""Matrix inversion helpers that ignore rows and columns with non-finite diagonals.""" 

2 

3from __future__ import annotations 

4 

5import numpy as np 

6 

7from ..core.exceptions import ( 

8 DEFAULT_COND_THRESHOLD, 

9 NonSquareMatrixError, 

10 SingularMatrixError, 

11) 

12from ..core.exceptions import ( 

13 check_and_warn_condition as _check_and_warn_condition, 

14) 

15from ..core.types import Matrix 

16from ..core.valid import valid 

17 

18 

19def inv( 

20 matrix: Matrix, 

21 cond_threshold: float = DEFAULT_COND_THRESHOLD, 

22) -> Matrix: 

23 """Invert a matrix restricted to the valid submatrix. 

24 

25 Rows and columns with non-finite diagonal entries are excluded from the 

26 inversion; the corresponding rows and columns in the result are set to NaN. 

27 When the condition number of the valid sub-matrix exceeds *cond_threshold*, 

28 an ``IllConditionedMatrixWarning`` is emitted. 

29 

30 Args: 

31 matrix: Square matrix to invert. 

32 cond_threshold: Condition-number threshold above which a warning is 

33 emitted. Defaults to ``1e12``. 

34 

35 Returns: 

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

37 mapped to invalid entries are returned as ``NaN``. 

38 

39 Raises: 

40 NonSquareMatrixError: If the matrix is not square. 

41 SingularMatrixError: If the valid sub-matrix is singular. 

42 

43 Example: 

44 >>> import numpy as np 

45 >>> from cvx.linalg import inv 

46 >>> np.allclose(inv(np.eye(2)), np.eye(2)) 

47 True 

48 

49 NaN-masked entries are skipped: 

50 

51 >>> matrix = np.array([[4.0, 0.0], [0.0, np.nan]]) 

52 >>> result = inv(matrix) 

53 >>> float(result[0, 0]) 

54 0.25 

55 >>> bool(np.isnan(result[0, 1]) and np.isnan(result[1, 0]) and np.isnan(result[1, 1])) 

56 True 

57 """ 

58 if matrix.shape[0] != matrix.shape[1]: 

59 raise NonSquareMatrixError(matrix.shape[0], matrix.shape[1]) 

60 

61 n = matrix.shape[0] 

62 result = np.full((n, n), np.nan) 

63 mask, submatrix = valid(matrix) 

64 

65 if mask.any(): 

66 _check_and_warn_condition(submatrix, cond_threshold) 

67 try: 

68 sub_inv = np.linalg.inv(submatrix) 

69 except np.linalg.LinAlgError as exc: 

70 raise SingularMatrixError(str(exc)) from exc 

71 

72 idx = np.where(mask)[0] 

73 result[np.ix_(idx, idx)] = sub_inv 

74 

75 return result