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

Approximate the L2 Condition Number of an SPD Matrix (Power Method + Inverse Iteration)

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

Routine Name: K2_cond

Author: Christian Bolander

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

$ gfortran -c K2_cond.f90

and can be added to a program using

$ gfortran program.f90 K2_cond.o

Description/Purpose: This subroutine uses the power method and inverse iteration method to calculate the highest and lowest eigenvalue of a symmetric positive definite matrix. It then returns an approximation of the l2 condition number of the matrix by taking the absolute value of the highest over the lowest eigenvalue, i.e.

Input:

A : REAL - a symmetric positive definite coefficient matrix of size n x n

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

v0 : REAL - the initial guess of the eigenvetor

tol : REAL - the exit tolerance for the algorithm

maxiter : INTEGER - the maximum number of iterations before exit

printit : INTEGER - flag for printing final convergence information

Output:

cond : REAL - the approximation of the l2 condition number

Usage/Example:

This routine can be implemented in a program as follows

INTEGER :: n, maxiter, i, printit
REAL*8, ALLOCATABLE :: A(:, :), v0(:), v(:)
REAL*8 :: tol, lam

n = 3
ALLOCATE(A(n, n), v0(n), v(n))
lam = 0.D0
tol = 10.D-16
maxiter = 10000
printit = 1
v0 = 1.D0
A = RESHAPE((/3.D0, 0.D0, 0.D0, &
			& 0.D0, 6.D0, 0.D0, &
			& 0.D0, 0.D0, 2.D0/), (/n, n/), ORDER=(/2, 1/))
CALL K2_cond(A, n, v0, tol, maxiter, printit, cond)
WRITE(*,*) cond

The outputs from the above code:

   ! Power Method
   0.0000000000000000               28  ! Convergence error and number of iterations at exit
   6.0000000000000000     ! Eigenvalue found
   ! Inverse Iteration
   0.0000000000000000               19  ! Convergence error and number of iterations at exit
   2.0000000000000000     ! Eigenvalue found
   3.0000000000000000 ! Condition number

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

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

n = 20
ALLOCATE(A(n, n), v0(n), v(n))
lam = 0.D0
tol = 10.D-16
maxiter = 10000
printit = 1
v0 = 1.D0
DO i = 1, n
	DO j = 1, n
		A(i, j) = 1.0D0/(REAL(i) + REAL(j) - 1.0D0)
	END DO
END DO
CALL K2_cond(A, n, v0, tol, maxiter, printit, cond)
WRITE(*,*) cond

and the output is

   0.0000000000000000               15
   1.1175345637892222E-017           1
   1.7065554679049347E+017           ! Awesome....

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

SUBROUTINE K2_cond(A, n, v0, tol, maxiter, printit, cond)
	IMPLICIT NONE
	
	! Finds an approximation of the l2 condition number using the `power
	! method` and `inverse iteration` subroutines.
	
	! Takes as an input the matrix `A` of rank `n` that contains the
	! system to be analyzed, an initial guess for the eigenvector, `v0`,
	! a tolerance, `tol`, for exiting the iterative solver as well as a
	! maximum number of iterations, `maxiter`. Finally, a flag to print
	! the final number of iterations and convergence error, `printit` is
	! an input. The output of the subroutine is an approximation of the
	! l2 condition number. ***Note that `A` needs to be a symmetric
	! positive definite matrix.
	INTEGER, INTENT(IN) :: n, maxiter, printit
	REAL*8, INTENT(IN) :: A(n, n), v0(n), tol
	REAL*8, INTENT(OUT) :: cond
	
	REAL*8 :: vhigh(n), vlow(n), lam_1, lam_n, alpha
	
	! Find the lowest eigenvalue using the inverse iteration method.
	alpha = 0.D0
	
	! Calculates the largest eigenvalue.
	CALL power_method(A, n, v0, tol, maxiter, printit, vhigh, lam_1)
	WRITE(*,*) lam_1
	
	! Calculates the smallest eigenvalue.
	CALL inverse_iteration(A, n, v0, alpha, tol, maxiter, printit, vlow, lam_n)
	WRITE(*,*) lam_n
	
	! Estimates the l2 condition number.
	cond = lam_1/lam_n
END SUBROUTINE