Coverage for src/cvx/linalg/decomposition/cholesky.py: 100%
23 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 18:56 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-03 18:56 +0000
1"""Cholesky decomposition utilities for covariance matrices."""
3from __future__ import annotations
5import warnings
6from typing import cast
8import numpy as np
9from numpy.linalg import cholesky as _cholesky
11from ..core.types import Matrix, Vector
14def cholesky(cov: Matrix, rhs: Vector | Matrix | None = None) -> Vector | Matrix:
15 """Compute the upper triangular Cholesky factor of a covariance matrix.
17 Returns the upper triangular factor R such that R.T @ R = cov.
19 Args:
20 cov: A positive definite covariance matrix of shape (n, n).
21 rhs: Deprecated. When provided the system ``cov @ x = rhs`` is solved
22 and *x* is returned; use :func:`cholesky_solve` instead. This
23 parameter will be removed in 1.0.
25 Returns:
26 The upper triangular Cholesky factor R when *rhs* is ``None``, or the
27 solution x to ``cov @ x = rhs`` otherwise (deprecated).
29 Raises:
30 np.linalg.LinAlgError: When *rhs* is ``None`` and *cov* is not
31 positive-definite, or when *rhs* is given and both Cholesky and
32 LU-based solves fail.
34 Warns:
35 DeprecationWarning: When *rhs* is given.
37 Example:
38 >>> import numpy as np
39 >>> from cvx.linalg import cholesky
40 >>> cov = np.array([[4.0, 2.0], [2.0, 5.0]])
41 >>> R = cholesky(cov)
42 >>> np.allclose(R.T @ R, cov)
43 True
44 """
45 if rhs is None:
46 return cast("Matrix", _cholesky(cov).transpose())
47 warnings.warn(
48 "Passing 'rhs' to cholesky() is deprecated and will be removed in 1.0; use cholesky_solve(cov, rhs) instead.",
49 DeprecationWarning,
50 stacklevel=2,
51 )
52 return cholesky_solve(cov, rhs)
55def cholesky_solve(cov: Matrix, rhs: Vector | Matrix) -> Vector | Matrix:
56 """Solve ``cov @ x = rhs`` using the Cholesky decomposition.
58 The Cholesky factorisation is attempted first for numerical stability;
59 when *cov* is not positive-definite the solve falls back to LU
60 decomposition.
62 Args:
63 cov: A positive definite covariance matrix of shape (n, n).
64 rhs: Right-hand side vector of length n or matrix of shape (n, k).
66 Returns:
67 The solution x to ``cov @ x = rhs`` with the same shape as *rhs*.
69 Raises:
70 np.linalg.LinAlgError: When both the Cholesky and LU-based solves fail.
72 Example:
73 >>> import numpy as np
74 >>> from cvx.linalg import cholesky_solve
75 >>> cholesky_solve(np.eye(2), np.array([1.0, 2.0])).tolist()
76 [1.0, 2.0]
77 >>> cholesky_solve(np.array([[4.0, 0.0], [0.0, 9.0]]), np.array([8.0, 27.0])).tolist()
78 [2.0, 3.0]
79 """
80 try:
81 upper = _cholesky(cov).transpose()
82 return cast("Vector | Matrix", np.linalg.solve(upper, np.linalg.solve(upper.T, rhs)))
83 except np.linalg.LinAlgError:
84 return cast("Vector | Matrix", np.linalg.solve(cov, rhs))
87def is_positive_definite(matrix: Matrix) -> bool:
88 """Return True if *matrix* is symmetric positive-definite, False otherwise.
90 The check is performed via an attempted Cholesky decomposition — the most
91 numerically reliable way to test positive-definiteness for symmetric matrices.
93 This function is side-effect-free: it raises no exceptions and emits no
94 warnings. It is suitable for use as a guard before passing a matrix to a
95 linear solver.
97 Args:
98 matrix: Square matrix to test.
100 Returns:
101 ``True`` if the matrix is positive-definite, ``False`` otherwise.
103 Example:
104 >>> import numpy as np
105 >>> from cvx.linalg import is_positive_definite
106 >>> is_positive_definite(np.eye(3))
107 True
108 >>> is_positive_definite(np.array([[1.0, 2.0], [2.0, 1.0]]))
109 False
110 >>> is_positive_definite(np.array([[1.0, 0.5], [0.5, 1.0]]))
111 True
112 """
113 try:
114 cholesky(matrix)
115 except np.linalg.LinAlgError:
116 return False
117 else:
118 return True