7.3. Cholesky factorization#
Definition: The Transpose of a Matrix
Transpose of a matrix is formed by turning rows into columns and vice versa. For a matrix \(A\), we denote the transpose of \(A\) by \(A^T\).
Definition: Symmetric and Skew Symmetric Matrices
An \(n\times n\) matrix \(A\) is
Symmetric if \(A = A^T\).
Skew-symmetric if \(A = -A^T\).
Definition: Positive Definite
An \(n\times n\) complex matrix \(A\) is positive definite if
for all nonzero vectors x in \(\mathbb{R}^n\).
Theorem
Assume that \(A\) is a real and symmetric positive definite matrix. Then, a unique real lower triangular matrix B with positive diagonal entries exists such that
where \(B^T\) denotes the conjugate transpose of \(B\).
Furthermore, We have,
Observe that \(L\) is lower triangular and \(U\) upper triangular. Thus,
Solving this system:
Column 1. Set \(j = 1\) and for \(1\leq i\leq n\), we have,
Column j. For a fixed \(j\) and for \(1\leq i\leq n\), we have,
Thus,
See [Allaire, Kaber, Trabelsi, and Allaire, 2008, Khoury and Harder, 2016] for the full derivation of this algorithm.
import numpy as np
def myCholesky(A):
'''
Assuming that the matrix A is symmetric and positive definite,
this function calculates a lower triangular matrix G with positive
diagonal entries, such that A = BB^T
Parameters
----------
A : numpy array
DESCRIPTION. Matrix A
Returns
-------
B : numpy array
DESCRIPTION. Matrix B from A = BB*
'''
n = A.shape[1]
B = np.zeros([n, n], dtype=float);
B[0,0] = np.sqrt(A[0,0]);
B[1:n, 0]= A[1:n,0] / B[0,0];
for j in range(1, n):
B[j,j] = np.sqrt(A[j,j]- np.dot(B[j, 0:j], B[j, 0:j]))
for i in range(j+1, n):
B[i,j]=(A[i,j] - np.dot(B[i, 0:j],B[j, 0:j]))/B[j,j]
return B
function [B] = myCholesky(A)
%{
Assuming that the matrix A is symmetric and positive definite,
this function calculates a lower triangular matrix G with positive
diagonal entries, such that A = BB^T
Parameters
----------
A : numpy array
DESCRIPTION. Matrix A
Returns
-------
B : numpy array
DESCRIPTION. Matrix B from A = BB*
%}
B = zeros(n,n);
B(1,1)=sqrt(A(1,1));
B(2:n,1)=A(2:n,1)/B(1,1);
for j=2:n
B(j,j)=sqrt(A(j,j)-dot(B(j,1:j-1),B(j,1:j-1)));
for i=j+1:n
B(i,j)=(A(i,j)-dot(B(i,1:j-1),B(j,1:j-1)))/B(j,j);
end
end
Example: Apply Cholesky decomposition on the following matrix and identify \(B\).
Solution: We have,
import numpy as np
from hd_Matrix_Decomposition import myCholesky
from IPython.display import display, Latex
from sympy import init_session, init_printing, Matrix
A = np.array([ [7, 3, -1, 2], [3, 8, 1, -4], [-1, 1, 4, -1], [2, -4, -1, 6] ])
B = myCholesky(A)
display(Latex(r'A ='), Matrix(A))
display(Latex(r'B ='), Matrix(np.round(B, 2)))
display(Latex(r'B B^T ='), Matrix(np.round(np.matmul(B,B.T), 2)))
hd.matrix_decomp_fig(mats = [B, B.T, np.round(np.matmul(B,B.T), 2)], labels = ['$A$', '$B$', '$B B^T$'])
Note that we could get a similar results using function, numpy.linalg.cholesky
.
import scipy.linalg as linalg
B = np.linalg.cholesky(A)
display(Latex(r'B ='), Matrix(np.round(B, 2)))
7.3.1. Solving Linear systems using Cholesky factorization#
We can solve the linear system \(Ax=b\) for \(x\) using Cholesky decomposition. To demonstrate this, we use the following example,
Example: Solve the following linear system using Cholesky decomposition.
Solution:
Let \(A=\left[\begin{array}{cccc} 7 & 3 & -1 & 2\\ 3 & 8 & 1 & -4\\ -1 & 1 & 4 & -1\\ 2 & -4 & -1 & 6 \end{array}\right]\) and \(b=\left[\begin{array}{c} 18\\ 6\\ 9\\ 15 \end{array}\right]\). Then, this linear system can be also expressed as follows,
We have,
A = np.array([ [7, 3, -1, 2], [3, 8, 1, -4], [-1, 1, 4, -1], [2, -4, -1, 6] ])
b = np.array([[18],[ 6],[ 9],[15]])
B = myCholesky(A)
display(Latex(r'B ='), Matrix(np.round(B, 2)))
hd.matrix_decomp_fig(mats = [A, B, B.T], labels = ['$A$', '$B$', '$B^T$'])
Now we can solve the following linear systems instead
# solving y
y = np.linalg.solve(B, b)
display(Latex(r'y ='), Matrix(np.round(y, 2)))
# solving x
x = np.linalg.solve(B.T, y)
display(Latex(r'x ='), Matrix(np.round(x, 2)))
Let’s now solve the linear system directly and compare the results.
x_new = linalg.solve(A, b)
display(Latex(r'x ='), Matrix(np.round(x_new, 2)))