views:

467

answers:

2

I need to test if one variance matrix is diagonal. If not, I'll do Cholesky LDL decomposition. But I was wondering which would be most reliable and fastest way to test is matrix diagonal? I'm using Fortran.

First thing that come to my mind is to take sum of all elements of matrix, and substract diagonal elements from that sum. If the answer is 0, matrix is diagonal. Any better ideas?

In Fortran I'll write

!A is my matrix
k=0.0d0
do i in 1:n #n is the number of rows/colums
k = k + A(i,i)
end do

if(abs(sum(A)-k) < epsilon(k)*sum(A)) then
#do cholesky LDL, which I have to write myself, haven't found any subroutines for that  in Lapack or anywhere else
end if
+8  A: 

It would be much better to just traverse all the off-diagonal elements and test if they are near zero (comparing a floating-point number for inequality is prone to rounding errors and can lead to erroneous results).

First, once you find any violating element you can immediately stop traversing and this may allow for significant time decrease if violating matrices are typical.

Second, it would potentially allow for better loop unrolling by the compiler (Fortran compilers are known for good optimization strategies) and for faster on-chip execution due to less inter-instruction dependencies.

Add to this the fact that your suggested algorithm is prone to overflows and error accumulation and the "traverse-and-test" algorithm is not.

sharptooth
Thank you very much, I'll do it that way.
Jouni
+1. Plus, it's multi-processor-friendly!
Stobor
And A is symmetric so I need only to traverse half of the matrix... Thank you again, I'm not a "real" programmer, so couldn't say about multi-processor, inter-instruction dependencies etc. :D Just statistician. ;)
Jouni
"It would be much better to just traverse all the off-diagonal elements and test if they are zero." - change that to "if they are smaller than epsilon". Very rarely you get zero values, they're usually something like 0.0002332 and so on.
ldigas
Good point. I'm comparing the elements like this: if(abs(Ht(k,i)) > epsilon(0.0d0)*abs(Ht(k,i))) thenNot sure have I understood the epsilon-function correctly? Do I need that abs(Ht(k,i)) there? I have just read some examples, I don't really understand the real meaning of that multiplication.
Jouni
@Jouni - No. I didn't mean Epsilon as the smallest processor-dependent value (which is AFAIS what you're trying to do here). I ment it more something like IF(element_value.GT.greatest_element_value_in_matrix*eps) THEN where eps=10**-5. ... meaning if some element is smaller than what you find on the main diagonal by 5 orders of magnitude, it can be considered zero (and therefore shouldn't make the matrix any less diagonal - its just a remain of numeric process which diffs from zero a little)
ldigas
These comments should have some kind of formatting also, IMHO. Just for this kinda stuff.
ldigas
A: 

Search the matrix for non zero values

logical :: not_diag
integer :: i, j

not_diag = .false.

outer: do i = 2, size(A,1)
  do j = i, size(A, 2)
    if (A(i,j) > PRECISION) then
      not_diag = .true.
      exit outer
    end if
  end
end outer

if (not_diag) then
  ! DO LDL' decomposition
end if

To use the double precision LAPACK routines replace the first 's' with 'd'. So spotrf becomes dpotrf

http://www.netlib.org/lapack/double/

Ian
Yep, but there is no dsptrf.f, only ssptrf.f which does LDL' decomposition. dpotrf does LL' decomposition.
Jouni