Viewing contents of file '../idllib/idl_5.2/lib/gs_iter.pro'
;$Id: gs_iter.pro,v 1.9.6.1 1999/01/16 16:40:26 scottm Exp $
;
; Copyright (c) 1994-1999, Research Systems, Inc.  All rights reserved.
;       Unauthorized reproduction prohibited.
;+
; NAME:
;       GS_ITER
;
; PURPOSE:
;       This function solves an n by n linear system of equations
;       using Gauss-Seidel iteration.
;
; CATEGORY:
;       Linear Algebra.
;
; CALLING SEQUENCE:
;       Result = GS_ITER(A, B)
;
; INPUTS:
;       A:      An N by N array of type: int, float, or double.
;
;       B:      An N-element vector of type: int, float, or double.
;
; KEYWORD PARAMETERS:
;       CHECK:    An integer value of 0 or 1 that denies or requests
;                 checking A for a diagonally dominant form.
;                 CHECK = 0 (the default) results in no checking.
;                 CHECK = 1  Checks A and reports if it does not
;                            meet the required condition. This is
;                            just a warning. The algorithm will
;                            proceed on the chance it may converge.
;
;       LAMBDA:   A scalar value in the range: [0.0, 2.0]
;                 This value determines the amount of 'RELAXATION'.
;                 Relaxation is a weighting technique that is used
;                 to enhance convergence.
;                 1) LAMBDA = 1.0 (the default) no weighting.
;                 2) A value in the range  0.0 <= LAMBDA < 1.0  improves
;                    convergence in oscillatory and non-convergent systems.
;                 3) A value in the range  1.0 < LAMBDA <= 2.0  improves
;                    convergence in systems known to converge.
;
;       MAX_ITER: The maximum number of iterations allowable for the
;                 algorithm to converge to the solution. The default 
;                 is 30 iterations.
;         
;       X_0:      An N-element vector that provides the algorithm's 
;                 starting point. The default is [1.0, 1.0, ... , 1.0].       
;
;       TOL:      The relative error tolerance between current and past
;                 iterates calculated as:  ABS( (current-past)/current )
;                 The default is 1.0e-4.
;
; SIDE EFFECTS:
;       Upon output A and B are divided by the diagonal elements of A.
;       Integer inputs are converted to floats.
;       Note: These SIDE EFFECTS have been removed for IDL v5.0.
;
; RESTRICTIONS:
;       The equations must be entered in a DIAGONALLY DOMINANT form
;       to guarantee convergence.
;       A system is diagonally dominant if it satisfies the condition:
;                   abs(A(row,row)) > SUM(abs(A(row,col)))
;       where SUM runs col=1,N excluding col = row and A is in row major.
;       This restriction on A is known as a sufficient condition. That is,
;       it is sometimes possible for the algorithm to converge without the
;       condition being satisfied. But, convergence is guaranteed if the
;       condition is satisfied.
;
; EXAMPLE:
;       Define an array (a) in a non-diagonally dominant form.
;         a = [[ 1.0,  7.0, -4.0], $
;              [ 4.0, -4.0,  9.0], $
;              [12.0, -1.0,  3.0]]
;       And right-side vector (b).
;         b = [12.0, 2.0, -9.0]
;       Compute the solution of the system, ax = b.
;         result = gs_iter(a, b)
;       Note: This example fails to converge, because A is not in
;             diagonally dominant form.
;
;       Reorder the array given above into diagonally dominant form.
;         a = [[12.0, -1.0,  3.0], $
;              [ 1.0,  7.0, -4.0], $
;              [ 4.0, -4.0,  9.0]]
;       Make corresponding changes in the ordering of b.
;         b = [-9.0, 12.0, 2.0]
;       Compute the solution of the system, ax = b.
;         result = gs_iter(a, b)
;
; PROCEDURE:
;       GS_ITER.PRO implements the Gauss-Seidel iterative method with
;       over- and under- relaxation to enhance convergence.
;     
; REFERENCE:
;       ADVANCED ENGINEERING MATHEMATICS (seventh edition)
;       Erwin Kreyszig
;       ISBN 0-471-55380-8
;
; MODIFICATION HISTORY:
;       Written by:  GGS, RSI, April 1993
;       Modified:    GGS, RSI, February 1994
;                    1) Format keyword is no longer supported. The matrix
;                       should be supplied in a row major format. 
;                    2) The input/output parameter X has been removed. The 
;                       algorithm's initial starting point is an n-element
;                       vector of 1s. The keyword X_0 has been added to 
;                       override the default.
;                    3) GS_ITER is now called as a function, x = gs_iter( ). 
;       Modified:    GGS, RSI, April 1996
;                    The input arguments are no longer overwritten.
;                    Added DOUBLE keyword. Modified keyword checking and use 
;                    of double precision.
;       Modified:    S. Lett, RSI, March 1998
;                    Modified stopping criteria.  Tol is used as an absolute
;                    tolerance when the iterates are very near zero.
;-

FUNCTION GS_ITER, A, B, Check = Check, Lambda = Lambda, Max_Iter = Max_Iter, $
                        X_0 = X_0, Tol = Tol, Double = Double

  ON_ERROR, 2  ;Return to caller if error occurs.

  TypeA = SIZE(A)
  TypeB = SIZE(B)

  if TypeA[TypeA[0]+1] lt 2 or TypeA[TypeA[0]+1] gt 5 then $
    MESSAGE, "Input array (A) must be integer, float, or double."

  if TypeB[TypeB[0]+1] lt 2 or TypeB[TypeB[0]+1] gt 5 then $
    MESSAGE, "Input vector (B) must be integer, float, or double."

  if TypeA[TypeA[0]-1] ne TypeA[TypeA[0]] then $
    MESSAGE, "Input array must be square."

  ;If the DOUBLE keyword is not set then the internal precision and
  ;result are determined by the type of input.
  if N_ELEMENTS(Double) eq 0 then $
    Double = (TypeA[TypeA[0]+1] eq 5 or TypeB[TypeB[0]+1] eq 5)

  ;Set default values for keyword parameters
  if KEYWORD_SET(Lambda)   eq 0 then Lambda = 1.0
  if KEYWORD_SET(Max_Iter) eq 0 then Max_Iter = 30
  if KEYWORD_SET(X_0)      eq 0 then X_0 = REPLICATE(1.0, TypeA[1]) 
  if KEYWORD_SET(Tol)      eq 0 then Tol = 1.0e-4

  ;Diagonal elements of input matrix.
  Diag = A[LINDGEN(TypeA[1]) * (TypeA[1]+1)]

  if KEYWORD_SET(Check) ne 0 then begin
    Sum = TOTAL(ABS(A), 1, Double = Double) - ABS(diag)
    caution = WHERE(Sum ge ABS(Diag), Count)
    if Count ne 0 then begin
      PRINT, "Input matrix is not in Diagonally Dominant form." & $
      PRINT, "Algorithm may not converge."
    endif
  endif

  ;Precondition inputs.
  ;Divide the rows of A and B by the diagonal elements of A.
  if Double eq 0 then begin
    AA = A / (REPLICATE(1.0, TypeA[1]) # Diag)
    BB = B / (Diag + 0.0)
    X_0 = FLOAT(X_0)
  endif else begin
    AA = A / (REPLICATE(1.0d, TypeA[1]) # Diag)
    BB = B / (Diag + 0d)
    X_0 = DOUBLE(X_0)
  endelse

  Cond = 0
  Iter = 0
 
  epsilon = (machar( Double = double )).eps

  ;Begin the computational loop and continue WHILE
  ;the number of iterations is less than max_iter
  ;AND the relative error between iterations is
  ;greater than tol.
  while(Iter lt Max_Iter and Cond eq 0) do begin
    Cond = 1
    Iter = Iter + 1
    ;Formulate x_0 as the row vectors of A.
    for k = 0, TypeA[1]-1 do begin
        xLast = X_0[k]
        X_0[k] = Lambda * (BB[k] - (TOTAL(X_0*AA[*,k],1, Double = Double)) + $
                           (AA[k,k] * X_0[k])) + (1.0 - lambda) * xLast
        if Cond eq 1 then begin
            Error = ABS(X_0[k] - xLast) 
            IF (X_0[k] GT epsilon/tol) AND (Error GT Tol*abs( X_0[k] )) THEN $
              Cond = 0
        endif
    ENDFOR
  ENDWHILE
  if Iter ge Max_Iter and Cond eq 0 then $
    MESSAGE, "Algorithm failed to converge within given parameters."

  RETURN, X_0

END