• No results found

Some Details of Gauss Elimination

Array sections. Fortran 90 array section notation can simplify some steps in this program. The first of the inner loops may be replaced by a single array assignment:

A(I,I+1:N+1)=A(I,I+1:N+1)/A(I,I)

Similar notation may be employed for subtracting multiples of a given row of this matrix from each of the rows below it. The doubly nested loop becomes an array section operation, with subscript ranges in both dimensions, to reduce all elements in the block A(I+1:N,I+1: N+1).

doI=1,N

A(I,I+1:N+1)=A(I,I+1:N+1)/A(I,I) A(I+1:N,I+1: N+1)=A(I+1:N,I+1: N+1)&

-matmul(A(I+1:N,I:I),A(I:I,I+1: N+1)) enddo

The call to matmul forms an outer product of parts of column i and row i of A. The arguments are ex-pressed as array sections of rank 2, with section subscript range I:I in one of the dimensions of each argument. It is a Fortran requirement that at least one of the arguments of matmul must have rank 2.

The remaining outer loop (which cannot be converted to array section syntax) applies the reduction step n times, once for each row, each time generating (“virtual”) zeros below the diagonal in the corre-sponding column. When row n is reached, there are no rows following it; the subscript range (I + 1 : N) is empty and no actual reduction occurs at this stage.

1.2 ARRAY TECHNIQUES

9 See W. H. Press et al, Numerical Recipes in Fortran 90 (Cambridge, UK: Cambridge Univ Press, 1996), p. 1014;

and M. J. Maron and R. J. Lopez, Numerical Analysis, 3rd ed. (Belmont, Calif.: Wadsworth, 1991), algorithm 4.4B, p. 220. Example 1 in this section is based on Examples 5.15, 5.28, and 8.5 in essential Fortran, by Loren P.

Meissner (Unicomp, 1997).

Choosing the pivotal row. We have seen how to use the ith row to generate zeros below the diagonal in the ith column. A complication arises in case the diagonal element Aii happens to be zero. In this case, it is necessary to select another row below the ith where the element in column i is not zero, and to ex-change that row with row i before proceeding with the reduction. (If all elements in column i below the diagonal are zeros, the system does not have a unique solution.) The element that is moved to the diago-nal position is called the pivot element. Some pivot choices are known to result in smaller numerical error than others. The method described here, called scaled partial pivoting, consists of choosing the larg-est element in column i, in absolute value, relative to a scale factor si which is the largest coefficient in row i before reduction begins.

The intrinsic function maxloc may be employed to find the pivot element. The Fortran 90 version of this intrinsic function returns a vector of length one; a Fortran 95 variant is simpler because it returns a scalar when the optional dim argument is present.

Loc=maxloc(abs(A(I:N,I)/S(I:N)),dim=1) !Fortran95 The argument of maxloc is a section, with lower bound i, from the vector of absolute values of element-wise quotients |Aji / sj |, j = i, n. The function result is an integer value that locates the largest element relative to the beginning of the specified array section; this value must be adjusted to the top of the actual column. Within A itself, the column subscript value of the largest element is Loc+I-1 . Indirect row interchange. It is not necessary to actually interchange the rows of the matrix A. Instead, elements of a permutation vector p (of integer type) are interchanged, and references to rows of A apply elements of this vector as subscripts:

doI=1,N

A(P(I),I+1:N+1)=A(P(I),I+1:N+1)/A(P(I),I) A(P(I+1:N),I+1:N+1)=A(P(I+1:N),I+1:N+1)&

-matmul(A(P(I+1:N),I:I),A(P(I,I),I+1: N+1)) enddo

Interchange can be performed in three steps:

Temp=P(Loc+I-1) P(Loc+I-1)=P(I) P(I)=Temp

The permutation vector is initialized with the sequence of integers, 1, 2, . . . , n. An array constructor is suitable for this initialization:

P=(/(K,K=1,N)/)

Back substitution. After reduction has been completed, back substitution takes place. In our Fortran program, elements of the solution x will replace those of b in row n + 1 of A; we continue to apply the permutation vector p to row indices. Equation n involves only the nth variable, whose coefficient in the reduced system is 1. Hence, the value of this variable is the nth right side element in the reduced system, and is located at An,n+1. Each of the other variables in turn (working backward from n – 1 to 1) is com-puted as

xi = bi – ∑ k = i + 1n Aik xk

The sum is of course a scalar product, and can be expressed with the intrinsic function dot_product: A(P(J),N+1)=A(P(J),N+1)&

-dot_product(A(P(J),J+1:N),A(P(J+1:N),N+1))

The first argument of dot_product is row pj of A; the second argument is column n + 1 of A. The permutation vector p is applied as a row subscript to this column vector.

23

Say It with Fortran

A Fortran implementation of the Gauss elimination algorithm described in the foregoing paragraphs, with scaled partial pivoting and back substitution, appears as follows:

subroutine Gauss( A, X, Flag )

A is an assumed-shape N by N + 1 matrix. At entry to the subroutine, the right side vector has been stored in column N + 1 of A; at exit, the solution vector has replaced it. The subroutine sets Flag to .false. if A is singular. A permutation vector is applied to all row subscripts to avoid physically interchanging rows. Local arrays have auto-matic shape. K is an array constructor implied do variable.

real, dimension(:, :), intent (in out) :: A real, dimension(:), intent (out) :: X logical, intent (out) :: Flag

real, dimension(size( A, dim = 1 )) :: S integer, dimension(size( A, dim = 1 )) :: P integer :: N, K, I, Loc, Temp

Start subroutine Gauss

N = size( A, dim = 1 )

The following steps initialize the permutation vector P to a sequence of consecutive integers, and store a copy of the largest element (in absolute value) in each row in the corresponding element of S.

P=(/(K,K=1,N)/)

S=maxval(abs(A),dim=2) Flag=.false.

if(any(S<=0))then

print*,"Oneoftherownormsiszero."

return endif

Block reduction: For each column, the largest element below the diagonal is located; the permutation vector is modified to record this location. Then the indicated row is divided by the diagonal element, and multiples are subtracted from each of the remaining rows.

do I = 1, N

Loc = maxloc( abs( A(I: N, I) / S(I: N) ), dim = 1 ) if (Loc <= 0) then

print *, " A is singular."

return else

Temp = P(Loc + I - 1) P(Loc + I - 1) = P(I) P(I) = Temp

end if

A(P(I), I + 1: N + 1) = A(P(I), I + 1: N + 1) / A(P(I), I) A(P(I + 1: N), I + 1: N + 1) = A(P(I + 1: N), I + 1: N + 1) &

- matmul( A(P(I + 1: N), I: I), A(P(I: I), I + 1: N + 1) ) end do

1.2 ARRAY TECHNIQUES

Back substitution: The reduced right side, now stored in row N + 1 of A, is replaced by the required solution.

do I = N, 1, -1

X(I) = A(P(I), N + 1) - dot_product( A(P(I), I + 1: N), X(I + 1: N) ) end do

Flag = .true.

return

end subroutine Gauss

Multiple right sides. The Gauss elimination program is easily modified to process multiple right sides with the same coefficient matrix. The right sides are stored as A(:, N + 1: N + M). Row operations are extended to column N + M. The scalar product in the back substitution step is changed to use the intrinsic function matmul, with the same first argument as before and the second argument changed to a matrix with column subscript N + 1: N + M.