View on GitHub

Bolander-Linear-Algebra-Library

Repository containing homework assignments and software for Math 5610: Computational Linear Algebra and Solution of Systems of Equations

Cholesky Factorization of a Symmetric, Positive Definite Matrix

This is a part of the student software manual project for Math 5610: Computational Linear Algebra and Solution of Systems of Equations.

Routine Name: cholesky_factor

Author: Christian Bolander

Language: Fortran. This code can be compiled using the GNU Fortran compiler by

$ gfortran -c cholesky_factor.f90

and can be added to a program using

$ gfortran program.f90 cholesky_factor.o

Description/Purpose: This routine takes a symmetric, positive definite matrix and finds its Cholesky factorization, G, such that

where G is a lower triangular matrix. The output of this method is that G is stored in the lower triangular part of the input A, like

The Cholesky factorization is useful in least squares problems as well as being a valuable method to determine if a matrix is symmetric and positive definite.

Input:

n : INTEGER - number of rows and columns in the matrix

A : REAL - a symmetric, positive definite array of size (n, n)

Output:

A : REAL - the Cholesky factorization of A as the lower triangular and diagonal with the upper triangular (without the diagonal) filled with the original elements of A.

Usage/Example:

This routine can be implemented in a program as follows

IMPLICIT NONE

INTEGER :: n, i, error
REAL*8, ALLOCATABLE :: mat(:, :)

n = 3

ALLOCATE(mat(n, n))

CALL spd_mat_gen(mat, n)

WRITE(*, *) "SPD MATRIX"
DO i = 1, n
	WRITE(*, *) mat(i, :)
END DO

CALL cholesky_factor(mat, n, error)

WRITE(*,*) "CHOLESKY FACTORIZATION"
DO i = 1, n
	WRITE(*, *) mat(i, :)
END DO

The outputs from the above code for several iterations:

n = 3
 SPD MATRIX
   1.4562893868127005       0.41766112582738740        1.1072287345244631     
  0.41766112582738740       0.18262626225367931       0.29113699430590895     
   1.1072287345244631       0.29113699430590895       0.86936702617046580     
 CHOLESKY FACTORIZATION
   1.2067681578549794       0.41766112582738740        1.1072287345244631     
  0.34609889489442336       0.25068270224835659       0.29113699430590895     
  0.91751570284432538      -0.10536896347408566       0.12817699770608676 

n = 4
 SPD MATRIX
  0.51158588923659165  0.83823970663441538  0.18292133423321394  0.50232653457365195     
  0.83823970663441538  1.4799031965642322   0.30653084541539288  0.83881474976684034     
0.18292133423321394 0.30653084541539288  8.4358977183772194E-002 0.13737083047203774     
  0.50232653457365195  0.83881474976684034  0.13737083047203774  0.60147574581917396     
 CHOLESKY FACTORIZATION
  0.71525232557230578  0.83823970663441538  0.18292133423321394  0.50232653457365195     
   1.1719496416368893  0.32624719773723548  0.30653084541539288  0.83881474976684034     
0.25574378117100183 2.0879911822754612E-002 0.13608131708065188  0.13737083047203774     
0.70230674772251556 4.8265881097165980E-002 -0.31780659536203587 7.0073893775801283E-002

An additional, large example that is verified with another solver can be seen here.

Implementation/Code: The code for cholesky_factor can be seen below.

SUBROUTINE cholesky_factor(A, n)
	IMPLICIT NONE
	
	! Takes as inputs a symmetric, positive definite matrix `A` of size
	! `n` and outputs a modified version of `A` with the Cholesky
	! factorization stored in the lower triangular and diagonal parts of
	! the matrix and the original elements in the upper triangular part.
	INTEGER, INTENT(IN) :: n
	REAL*8, INTENT(INOUT) :: A(1:n, 1:n)
	
	INTEGER :: i, j, k
	REAL*8 :: factor
	
	! The Cholesky factorization method first loops through all of the
	! diagonal elements and takes their square root. If this process fails
	DO k = 1, n - 1
		
		A(k, k) = SQRT(A(k, k))
		
		! Then loops through the lower triangular components to compute
		! the Cholesky decomposition.
		DO i = k + 1, n ! rows
			A(i, k) = A(i, k)/A(k, k)
		END DO
		DO j = k + 1, n ! rows
			DO i = j, n ! columns
				A(i, j) = A(i, j) - A(i, k)*A(j, k)
			END DO
		END DO
	END DO
	! The last diagonal value is simply factored to its square root.
	A(n, n) = SQRT(A(n, n))
	
END SUBROUTINE