Reputation: 87
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
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