!>
!> @file bchfac.f90
!>
!> @brief
!>
!> @copyright
!> Copyright (©) 2021 EPFL (Ecole Polytechnique Fédérale de Lausanne)
!> SPC (Swiss Plasma Center)
!>
!> SPClibs is free software: you can redistribute it and/or modify it under
!> the terms of the GNU Lesser General Public License as published by the Free
!> Software Foundation, either version 3 of the License, or (at your option)
!> any later version.
!>
!> SPClibs is distributed in the hope that it will be useful, but WITHOUT ANY
!> WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
!> FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
!>
!> You should have received a copy of the GNU Lesser General Public License
!> along with this program. If not, see .
!>
!> @authors
!> (in alphabetical order)
!> @author Trach-Minh Tran
!>
subroutine bchfac ( w, nbands, nrow, diag )
!*************************************************************************
!
!! BCHFAC constructs a Cholesky factorization of a matrix.
!
! Discussion:
!
! The factorization has the form
!
! C = L * D * L'
!
! with L unit lower triangular and D diagonal, for a given matrix C of
! order NROW, where C is symmetric positive semidefinite and banded,
! having NBANDS diagonals at and below the main diagonal.
!
! Gauss elimination is used, adapted to the symmetry and bandedness of C.
!
! Near-zero pivots are handled in a special way. The diagonal
! element C(N,N)=W(1,N) is saved initially in DIAG(N), all N.
!
! At the N-th elimination step, the current pivot element, W(1,N),
! is compared with its original value, DIAG(N). If, as the result
! of prior elimination steps, this element has been reduced by about
! a word length, (i.e., if W(1,N)+DIAG(N) <= DIAG(N)), then the pivot
! is declared to be zero, and the entire N-th row is declared to
! be linearly dependent on the preceding rows. This has the effect
! of producing X(N) = 0 when solving C*X = B for X, regardless of B.
!
! Justification for this is as follows. In contemplated applications
! of this program, the given equations are the normal equations for
! some least-squares approximation problem, DIAG(N) = C(N,N) gives
! the norm-square of the N-th basis function, and, at this point,
! W(1,N) contains the norm-square of the error in the least-squares
! approximation to the N-th basis function by linear combinations
! of the first N-1.
!
! Having W(1,N)+DIAG(N) <= DIAG(N) signifies that the N-th function
! is linearly dependent to machine accuracy on the first N-1
! functions, therefore can safely be left out from the basis of
! approximating functions.
!
! The solution of a linear system C*X=B is effected by the
! succession of the following two calls:
!
! CALL BCHFAC(W,NBANDS,NROW,DIAG)
!
! CALL BCHSLV(W,NBANDS,NROW,B,X)
!
! Reference:
!
! Carl DeBoor,
! A Practical Guide to Splines,
! Springer Verlag.
!
! Parameters:
!
! Input/output, real ( kind = 8 ) W(NBANDS,NROW).
!
! On input, W contains the NBANDS diagonals in its rows,
! with the main diagonal in row 1. Precisely, W(I,J)
! contains C(I+J-1,J), I=1,...,NBANDS, J=1,...,NROW.
!
! For example, the interesting entries of a seven diagonal
! symmetric matrix C of order 9 would be stored in W as
!
! 11 22 33 44 55 66 77 88 99
! 21 32 43 54 65 76 87 98 *
! 31 42 53 64 75 86 97 * *
! 41 52 63 74 85 96 * * *
!
! Entries of the array not associated with an
! entry of C are never referenced.
!
! On output, W contains the Cholesky factorization
! C = L*D*L-transp, with W(1,I) containing 1/D(I,I) and W(I,J)
! containing L(I-1+J,J), I=2,...,NBANDS.
!
! Input, integer NBANDS, indicates the bandwidth of the
! matrix C, i.e., C(I,J) = 0 for NBANDS < ABS(I-J).
!
! Input, integer NROW, is the order of the matrix C.
!
! Work array, real ( kind = 8 ) DIAG(NROW).
!
implicit none
integer nbands
integer nrow
real ( kind = 8 ) diag(nrow)
integer i
integer imax
integer j
integer jmax
integer n
real ( kind = 8 ) ratio
real ( kind = 8 ) w(nbands,nrow)
if ( nrow <= 1 ) then
if ( 0.0D+00 < w(1,1) ) then
w(1,1) = 1.0D+00 / w(1,1)
end if
return
end if
!
! Store the diagonal.
!
diag(1:nrow) = w(1,1:nrow)
!
! Factorization.
!
do n = 1, nrow
if ( w(1,n) + diag(n) <= diag(n) ) then
w(1:nbands,n) = 0.0D+00
else
w(1,n) = 1.0D+00 / w(1,n)
imax = min ( nbands-1, nrow-n )
jmax = imax
do i = 1, imax
ratio = w(i+1,n) * w(1,n)
do j = 1, jmax
w(j,n+i) = w(j,n+i) - w(j+i,n) * ratio
end do
jmax = jmax-1
w(i+1,n) = ratio
end do
end if
end do
return
end