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

Calculate the L2 Condition Number of an SPD Matrix (Rayleigh Quotient)

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

Routine Name: rayleigh_cond

Author: Christian Bolander

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

$ gfortran -c rayleigh_cond.f90

and can be added to a program using

$ gfortran program.f90 rayleigh_cond.o

Description/Purpose: This subroutine uses the Rayleigh Quotient method to find the l2 condition number of a symmetric positive definite matrix. Since the nature of finding an eigenvector that corresponds to the largest and smallest eigenvalue is rather difficult, a brute force approach is applied. A large eigenvector is first run through the Rayleigh Quotient algorithm to generate (hopefully) the largest eigenvalue. Then, random vectors are run through for a set number of iterations to try and find all possible eigenvalues in the system.

Input:

A : REAL - a coefficient matrix of size n x n

n : INTEGER - the rank of A and the length of the eigenvectors

tol : REAL - the exit tolerance for the algorithm

maxiter : INTEGER - the maximum number of iterations before exit

Output:

cond : REAL - the final approximation of the condition number of the system

Usage/Example:

This routine can be implemented in a program as follows

INTEGER :: n, maxiter, i,j
REAL*8, ALLOCATABLE :: A(:, :)
REAL*8 :: tol, cond

n = 3

tol = 10.D-16
maxiter = 10000

ALLOCATE(A(n, n))
A = RESHAPE((/ 3.D0, 3.D0, 1.D0, &
			& 3.D0, 4.D0, 4.D0, &
			& 1.D0, 4.D0, 12.D0/), (/n, n/), ORDER=(/2, 1/))
CALL rayleigh_cond(A, n, tol, maxiter, cond)
WRITE(*,*) cond

The outputs from the above code:

 Maximum Eigenvalue:   14.062770861175807     
 Minimum Eigenvalue:  0.11804443416433479     
   119.13116413094421 

Additionally, using this routine on an 8 x 8 Hilbert matrix can be done with the following code:

INTEGER :: n, maxiter, i, printit, j
REAL*8, ALLOCATABLE :: A(:, :)
REAL*8 :: tol, cond

n = 8

tol = 10.D-16
maxiter = 10000
ALLOCATE(A(n, n))
DO i = 1, n
	DO j = 1, n
		A(i, j) = 1.0D0/(REAL(i) + REAL(j) - 1.0D0)
	END DO
END DO
CALL rayleigh_cond(A, n, tol, maxiter, cond)
WRITE(*,*) cond

and the output is

 Maximum Eigenvalue:   1.6959389969219500     
 Minimum Eigenvalue:   1.1115388618738692E-010
   15257577176.050144

which matches precisely with the second eigenvalue presented in this paper.

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

SUBROUTINE rayleigh_cond(A, n, tol, maxiter, cond)
	IMPLICIT NONE
	
	! Uses the Rayleigh Quotient iterative method to approximate the
	! condition number of a given symmetric positive definite matrix.
	
	! Takes as an input the matrix `A` of rank `n` that contains the
	! system to be analyzed, a tolerance, `tol`, for exiting the
	! iterative solver as well as a maximum number of iterations,
	! `maxiter`. The output of the subroutine is the approximation of 
	! the condition number.
	INTEGER, INTENT(IN) :: n, maxiter
	REAL*8, INTENT(IN) :: A(n, n), tol
	REAL*8, INTENT(OUT) :: cond
	
	INTEGER :: i
	REAL*8 :: lam_max, lam_min, lam_i, v0(n), v_i(n)
	
	! Runs through the Rayleigh Quotient algorithm once with a large
	! eigenvector to try and find the maximum off the bat.
	v0 = 10.D0
	CALL rayleigh_quotient(A, n, v0, tol, maxiter, 0, v_i, lam_max)
	
	! Superior to randomly assigning these values, as we start with
	! eigenvalues that we know exist in this system.
	lam_min = lam_max
	
	! Brute force approach that checks many random eigenvectors to see
	! if we can find the maximum and minimum.
	DO i = 1, INT(100*n)
		CALL rand_mat(n, 1, v0)
		
		! Useful to find extremely small eigenvalues
		IF (i < INT(50*n)) THEN
			v0 = v0/10.D0
		END IF
		
		! Stores maximum and minimum values found.
		CALL rayleigh_quotient(A, n, v0, tol, maxiter, 0, v_i, lam_i)
		IF (lam_i > lam_max) THEN
			lam_max = lam_i
		ELSE IF (lam_i < lam_min) THEN
			lam_min = lam_i
		END IF
	END DO
	
	! Finds the condition number and reports the eigenvalues found.
	WRITE(*,*) "Maximum Eigenvalue:", lam_max
	WRITE(*,*) "Minimum Eigenvalue:", lam_min
	cond = lam_max/lam_min
END SUBROUTINE