Coverage for src / cvx / linalg / cholesky.py: 100%
17 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"""Cholesky decomposition utilities for covariance matrices."""
3from __future__ import annotations
5import numpy as np
6from numpy.linalg import cholesky as _cholesky
9def cholesky(cov: np.ndarray, rhs: np.ndarray | None = None) -> np.ndarray:
10 """Compute the upper triangular Cholesky factor, or solve a linear system.
12 When called without *rhs*, returns the upper triangular factor R such that
13 R.T @ R = cov. When *rhs* is given, solves ``cov @ x = rhs`` using the
14 Cholesky decomposition for numerical stability, falling back to LU
15 decomposition if *cov* is not positive-definite.
17 Args:
18 cov: A positive definite covariance matrix of shape (n, n).
19 rhs: Optional right-hand side vector or matrix. When provided the
20 system ``cov @ x = rhs`` is solved and *x* is returned.
22 Returns:
23 The upper triangular Cholesky factor R when *rhs* is ``None``, or the
24 solution x to ``cov @ x = rhs`` otherwise.
26 Raises:
27 np.linalg.LinAlgError: When *rhs* is ``None`` and *cov* is not
28 positive-definite, or when *rhs* is given and both Cholesky and
29 LU-based solves fail.
31 Example:
32 Decomposition (no rhs):
34 >>> import numpy as np
35 >>> from cvx.linalg import cholesky
36 >>> cov = np.array([[4.0, 2.0], [2.0, 5.0]])
37 >>> R = cholesky(cov)
38 >>> np.allclose(R.T @ R, cov)
39 True
41 Solve (with rhs):
43 >>> cholesky(np.eye(2), np.array([1.0, 2.0])).tolist()
44 [1.0, 2.0]
45 >>> cholesky(np.array([[4.0, 0.0], [0.0, 9.0]]), np.array([8.0, 27.0])).tolist()
46 [2.0, 3.0]
47 """
48 if rhs is None:
49 return _cholesky(cov).transpose()
50 try:
51 upper = _cholesky(cov).transpose()
52 return np.linalg.solve(upper, np.linalg.solve(upper.T, rhs))
53 except np.linalg.LinAlgError:
54 return np.linalg.solve(cov, rhs)
57def is_positive_definite(matrix: np.ndarray) -> bool:
58 """Return True if *matrix* is symmetric positive-definite, False otherwise.
60 The check is performed via an attempted Cholesky decomposition — the most
61 numerically reliable way to test positive-definiteness for symmetric matrices.
63 This function is side-effect-free: it raises no exceptions and emits no
64 warnings. It is suitable for use as a guard before passing a matrix to a
65 linear solver.
67 Args:
68 matrix: Square matrix to test.
70 Returns:
71 ``True`` if the matrix is positive-definite, ``False`` otherwise.
73 Example:
74 >>> import numpy as np
75 >>> from cvx.linalg import is_positive_definite
76 >>> is_positive_definite(np.eye(3))
77 True
78 >>> is_positive_definite(np.array([[1.0, 2.0], [2.0, 1.0]]))
79 False
80 >>> is_positive_definite(np.array([[1.0, 0.5], [0.5, 1.0]]))
81 True
82 """
83 try:
84 cholesky(matrix)
85 except np.linalg.LinAlgError:
86 return False
87 else:
88 return True