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

Solve a Square Linear System Using Gauss-Seidel

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

Routine Name: gaussseidel_solve

Author: Christian Bolander

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

$ gfortran -c gaussseidel_solve.f90

and can be added to a program using

$ gfortran program.f90 gaussseidel_solve.o

Description/Purpose: This routine uses the Gauss-Seidel method to iteratively solve the system of equations given by:

by using the following algorithm

Input:

A : REAL - the input coefficient matrix of size n x n

n : INTEGER - the size of A and the length of b and x

b : REAL - the right hand size vector of length n

tol : REAL - the tolerance between the iterations

maxiter : INTEGER - a limit on the maximum number of iterations to be performed by the Jacobi iteration

printit : INTEGER - if equal to 1, the algorithm prints the final convergence error and iteration count

Output:

x : REAL - the solution to the system of equations given by Ax = b

Usage/Example:

This routine can be implemented in a program as follows

INTEGER :: n, maxiter, i, printit
REAL*8, ALLOCATABLE :: A(:, :), b(:), x(:), Ab(:, :)
REAL*8 :: tol

n = 3
maxiter = 1000
tol = 1e-16
printit = 0
ALLOCATE(A(n, n), b(n), x(n), Ab(n, n + 1))
A = RESHAPE((/10.D0, 1.D0, 3.D0, &
& 1.D0, 10.D0, 0.D0, &
& 3.D0, 2.D0, 10.D0/), (/n, n/), ORDER=(/2, 1/))
b = (/2.D0, 4.D0, 1.D0/)
x = 0.D0
CALL gaussseidel_solve(A, n, b, tol, maxiter, x)
WRITE(*,*) x

The outputs from the above code:

  0.16997792494481237       0.38300220750551878       -2.7593818984547436E-002

To verify the routine, it can be compared to the Gaussian Elimination subroutine as follows

	IMPLICIT NONE

	INTEGER :: n, maxiter, i, printit
	REAL*8, ALLOCATABLE :: A(:, :), b(:), x(:), Ab(:, :)
	REAL*8 :: tol

	n = 3
	maxiter = 1000
	tol = 1e-16
	printit = 0
	ALLOCATE(A(n, n), b(n), x(n), Ab(n, n + 1))
	A = RESHAPE((/10.D0, 1.D0, 3.D0, &
				& 1.D0, 10.D0, 0.D0, &
				& 3.D0, 2.D0, 10.D0/), (/n, n/), ORDER=(/2, 1/))
	b = (/2.D0, 4.D0, 1.D0/)
	Ab(:n, :n) = A
	Ab(:, n + 1) = b
	x = 0.D0
	CALL gaussseidel_solve(A, n, b, tol, maxiter, x, printit)
	WRITE(*,*) "Gauss-Seidel"
	WRITE(*,*) x
	x = 0.D0
	CALL direct_ge_bsin(Ab, n, n + 1, x)
	WRITE(*,*) "Gauss-Elimination"
	WRITE(*,*) x

with the output

 Gauss-Seidel
  0.16997792494481237       0.38300220750551878       -2.7593818984547436E-002
 Gauss-Elimination
  0.16997792494481237       0.38300220750551872       -2.7593818984547460E-002

In addition, with a larger, diagonally dominant matrix (n = 1000), the difference between the Gauss-Seidel method and the Gaussian-Elimination routine can be seen below as the l2 norm of the absolute error between the two returned vector:

4.1975798679441814E-017

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

SUBROUTINE gaussseidel_solve(A, n, b, tol, maxiter, x0, printit)
	IMPLICIT NONE
	
	! Takes as an input the square, coefficient matrix `A` of size `n`
	! as well as the right-hand side vector `b`. In addition, a
	! tolerance argument `tol` is used to determine sufficient
	! convergence and a `maxiter` argument specifies the maximum number
	! of iterations before the solver exits. An initial guess for the
	! solution, `x0`, is given as an input. It stores the iterations of
	! the Gauss-Seidel approximation to the solution x and is output as
	! soon as the solver exits. 
	! An input that tells the algorithm to print the final convergence
	! error and iteration count is also an input.
	INTEGER, INTENT(IN) :: n, maxiter, printit
	REAL*8, INTENT(IN) :: A(n, n), b(n), tol
	REAL*8, INTENT(INOUT) :: x0(n)
	
	INTEGER :: i, j, k
	REAL*8 :: ax_sum, x1(n), error
	
	x1 = x0
	k = 0
	error = 10.D0*tol
	
	! Starts the iterative Gauss-Seidel solver.
	DO WHILE (error > tol .AND. k < maxiter)
		
		! Finds the next iteration of the vector x.
		DO i = 1, n
			ax_sum = b(i)
			
			! Sets the Gauss-Seidel routine apart from the Jacobi in
			! that it uses any updated values previously calculated in 
			! the iterations to give a more 'accurate' approximation.
			DO j = 1, i - 1
				ax_sum = ax_sum - A(i, j)*x1(j)
			END DO
			DO j = i + 1, n
				ax_sum = ax_sum - A(i, j)*x0(j)
			END DO
		
			x1(i) = ax_sum/A(i, i)
			
			! The x vector approximation is updated, an error is
			! calculated, and the x0 vector is reset to the approximation
			! given in this iteration.
		END DO
		k = k + 1
		CALL abs_err_vecl2(x0, x1, n, error)
		x0 = x1
	END DO
	IF (printit == 1) THEN
		WRITE(*,*) error, k
	END IF
END SUBROUTINE