Inferrator
Inferrator

Reputation: 87

More efficient way to access lower triangular elements using Armadillo

Is there a more efficient way to create a vector containing the lower triangular elements of a matrix? For certain algorithms it is useful to just have these elements in a vector.

However, the code below obviously creates copies, when I'd prefer pointers. This is probably not possible given the non-contigous nature of the element positions.

One alternative I thought about is to create an index matrix via find(trimatu(inmat)!=0) or so, but I can't imagine that to be more efficient. Finding exact zeroes in a matrix of doubles is usually not very fast, and also there may be actual 0s in within the triangular I am trying to extract. Something along those lines has been discussed here (C++ Armadillo Access Triangular Matrix Elements), however, the question is 5 years old and armadillo has improved a lot since then.

vec trimat2vec(mat const& inmat){

  int K = inmat.n_rows;
  int p = K*(K-1)/2;
  vec out(p);

  int counter=0;
  for(int i=0; i<K; i++){
    for(int j=1; j<K; j++){
      if(i<j){
        out(counter)=inmat(j,i);
        counter+=1;
      }
    }
  }
  return(out);
}

Upvotes: 0

Views: 244

Answers (1)

Inferrator
Inferrator

Reputation: 87

Ok, with the new version Armadillo 9.870, there is now a better way to do this. The speed improvement is small, especially on small matrices, but it appears to be consistent. More importantly, the code is much shorter.

I call the new function trimat2vec_new.

vec trimat2vec_new(mat const& inmat){
  return(inmat.elem(trimatl_ind(size(inmat),-1)));
}

Simple benchmark in R:

a=matrix(1:900,30,30)

all(trimat2vec_new(a) == trimat2vec(a))

library(microbenchmark)
microbenchmark(trimat2vec_new(a),
               trimat2vec(a),
               times = 1000)

yields:

Unit: microseconds
              expr   min     lq     mean median    uq    max neval cld
 trimat2vec_new(a) 3.026 3.4060 3.633960  3.557 3.727 20.549  1000  a 
     trimat2vec(a) 3.116 3.6515 3.955116  3.787 3.958 42.981  1000   b

I especially like the convenience of the new trimatl_ind function. And the small speed improvement.

Upvotes: 1

Related Questions