module inv_harville !© karin meyer
integer, save :: nrczhz, maxrec
integer, dimension(:),allocatable, save :: ifirst, ivcol, inext
real(8), dimension(:),allocatable, save :: dia, zhz
integer, dimension(:), allocatable, save :: ivec
real(8), dimension(:), allocatable, save :: xvec
contains
! ==========================================================
subroutine harville( nrow, ngibbs, nburn, iseed1, iseed2 )
! ==========================================================
! Purpose : Obtain inverse of large, sparse matrix (symmetric & positive
! definite) using Gibbs sampling
! Assumes non-zero elements of the matrix are stored
! in linked list format, which has been sorted
! dia: diagonal elements
! zhz: off-triagonal elements (lower triangle only)
! ifirst: position of first element in a row
! inext: position of next off-diagonal element
! ivcol: column no. for off-diagonal elements
! ** Version to give diagonal elements of inverse only **
! Reference: Harville, D.A. (1999) "Use of the Gibbs sampler to
! invert large, possibly sparse, positive definite matrices"
! Linear Algebra and its Applications 289 : 203-224
! Arguments:
! nrow - no. of rows in matrix
! ngibbs - no. of Gibbs samples to be drawn
! nburn - no. of samples to be discarded in "burn-in" phase
! is1, is2 - seed values to intialize random no. generator
!------------------------------------------------------------- km 11/2002 --------
implicit none
integer, intent(in) :: nrow, ngibbs, nburn, iseed1, iseed2
real(8), dimension(nrow) :: xvec, dd, dinv, adj, sdev
real(8) :: aa, xbar, xx
integer :: irow, igibbs, ij, is1, is2
! function to generate random normal deviate from the ranlibf library
! (replace with similiar function as desired)
real, external :: snorm
! initialize random number generator from ranlibf library (replace if desired)
call setall( iseed1, iseed2 )
! initialize
dinv = 0.d0; xvec = 0.d0
do irow = 1, nrow
if( dia(irow) <= 0 ) then
write(*,*)'Error in subroutine HARVILLE: zero/negative diagonal element'
stop 'Programmed stop'
end if
dd(irow) = 1.d0/dia(irow); sdev(irow) = sqrt( dd(irow) )
end do
! start of gibbs sampler
do igibbs = 1, ngibbs
! ... adjustments for previous samples (upper triangle of matrix)
adj = 0.d0
do irow = 1, nrow
do ij = ifirst(irow)+1, ifirst(irow+1)
adj(ivcol(ij)) = adj(ivcol(ij)) + zhz(ij)*xvec(irow)
end do
end do
do irow = 1, nrow
! ... adjustments for samples from this iterate (lower triangle)
aa = 0.d0
do ij = ifirst(irow)+1, ifirst(irow+1)
aa = aa + zhz(ij)*xvec(ivcol(ij))
end do
! ... conditional mean
xbar = -( aa + adj(irow) ) * dd(irow)
! ... new sample
xvec(irow) = xbar + sdev(irow) * snorm()
! ... accumulate
if( igibbs > nburn) dinv(irow) = dinv(irow) + xbar * xbar
end do ! irow
end do ! igibbs
! inverse
xx = 1.d0 / dble( ngibbs - nburn )
do irow = 1, nrow
dia(irow) = dd(irow) + dinv(irow) * xx
end do
return
end subroutine harville
! ==============================
subroutine lnkrow ( irow, nn )
! ==============================
! routine to add elements to a row in a sparse matrix held in linked list
! arguments:
! irow - row no.
! nn - no. of elements to be added
! information passed in module harville:
! ivec(nn) - vector of column no.s (must be in ascending order)
! xvec(nn) - vector of elements to be added
!------------------------------------------------------ km ------------
implicit none
integer, intent(in) :: irow, nn
logical, intent(in) :: opt
integer :: kk, ipre, ipcol, iplace, icol, i
if( nn < 1 ) return
! check that column no.s are in ascending sequence
ipcol = 0
do kk = 1, nn
icol = ivec(kk)
if( icol <= ipcol ) then
write(*,*)'subroutine "lnkrow" : col. no.s out of order !'
write(*,*)' row no. =',irow
do i = 1, nn
write(*,*)' column ',i,ivec(i),xvec(i)
end do
stop 'Programmed stop'
end if
ipcol = icol
end do
ipre = 0; iplace = ifirst(irow)
do kk = 1, nn
icol = ivec(kk)
! go to next element in this row
10 if( iplace > 0 .and. ivcol(iplace) < icol )then
ipre = iplace; iplace = inext(iplace); go to 10
! element found
else if( iplace > 0 .and. icol == ivcol(iplace) )then
zhz(iplace) = zhz(iplace) + xvec(kk)
ipre = iplace; iplace = inext(iplace)
! element (irow,icol) has not been accessed before
else
nrczhz = nrczhz + 1
! check that array size is sufficient
if( nrczhz > maxrec ) then
write(*,*)'Subroutine "LNKROW" : DIMENSIONS EXCEEDED !!'
write(*,*)'max. no. of elements =', maxrec
write(*,*)'row no. =', irow
write(*,*)'no. elements in row =', nn
write(*,*)'column no. =', icol
write(*,*)'element in row no. =', kk
stop 'reset parameter "maxrec" '
end if
zhz(nrczhz) = xvec(kk); ivcol(nrczhz) = icol
if( iplace == 0 .and. ifirst(irow) == 0 )then
! first element in this row
ifirst(irow) = nrczhz; inext(nrczhz) = 0
else if( iplace == 0 .and. ifirst(irow) /= 0 ) then
! all previous elements in the row have lower column no.
inext(ipre) = nrczhz; inext(nrczhz)=0
else if(iplace /= 0 .and. ipre == 0 ) then
! fit element in "right" place, swapping indicators
inext(nrczhz) = ifirst(irow); ifirst(irow) = nrczhz
else
inext(nrczhz) = inext(ipre); inext(ipre) = nrczhz
end if
ipre = nrczhz; iplace = inext(nrczhz)
end if
end do
return
end subroutine lnkrow
! ===========================
subroutine lnksrt( neqns )
! ===========================
! routine to sort a sparse matrix held in linked list
! arguments:
! newns - no. of rows
!------------------------------------------------------ km -----------
implicit none
integer, intent(in) :: neqns
integer :: newplc, irow, iplace, jplace, nrow, icol
real(8) :: zave
newplc = 0; nrow = 0
do irow = 1, neqns
iplace = ifirst(irow); ifirst(irow) = nrow
do while( iplace > 0 )
newplc = newplc + 1
do while( iplace < newplc .and. iplace > 0 )
iplace = inext(iplace)
end do
jplace = inext(iplace)
if( iplace /= newplc ) then
zave = zhz(newplc)
zhz(newplc) = zhz(iplace)
zhz(iplace) = zave
inext(iplace) = inext(newplc)
inext(newplc) = iplace
icol = ivcol(iplace)
ivcol(iplace) = ivcol(newplc)
ivcol(newplc) = icol
end if
nrow = nrow+1; iplace = jplace
end do
end do ! irow
ifirst(neqns+1) = nrczhz
if( nrow /= nrczhz ) then
write(*,*)'Subroutine LNKSRT: Error in sorting',nrow,nrczhz,neqns
stop 'Programmed stop'
end if
return
end subroutine lnksrt
end module inv_harville