Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

push_back/append or appending a vector with a loop in C++ Armadillo

Tags:

c++

armadillo

I would like to create a vector (arma::uvec) of integers - I do not ex ante know the size of the vector. I could not find approptiate function in Armadillo documentation, but moreover I was not successfull with creating the vector by a loop. I think the issue is initializing the vector or in keeping track of its length.

 arma::uvec foo(arma::vec x){
 arma::uvec vect;
 int nn=x.size();
 vect(0)=1;
 int ind=0;
 for (int i=0; i<nn; i++){
     if ((x(i)>0)){
        ind=ind+1;
        vect(ind)=i;
     }
 }
   return vect;
}

The error message is: Error: Mat::operator(): index out of bounds.

I would not want to assign 1 to the first element of the vector, but could live with that if necessary.

PS: I would really like to know how to obtain the vector of unknown length by appending, so that I could use it even in more general cases.

like image 782
DatamineR Avatar asked Jan 31 '15 02:01

DatamineR


2 Answers

Repeatedly appending elements to a vector is a really bad idea from a performance point of view, as it can cause repeated memory reallocations and copies.

There are two main solutions to that.

  1. Set the size of the vector to the theoretical maximum length of your operation (nn in this case), and then use a loop to set some of the values in the vector. You will need to keep a separate counter for the number of set elements in the vector so far. After the loop, take a subvector of the vector, using the .head() function. The advantage here is that there will be only one copy.

  2. An alternative solution is to use two loops, to reduce memory usage. In the first loop work out the final length of the vector. Then set the size of the vector to the final length. In the second loop set the elements in the vector. Obviously using two loops is less efficient than one loop, but it's likely that this is still going to be much faster than appending.

If you still want to be a lazy coder and inefficiently append elements, use the .insert_rows() function.

As a sidenote, your foo(arma::vec x) is already making an unnecessary copy the input vector. Arguments in C++ are by default passed by value, which basically means C++ will make a copy of x before running your function. To avoid this unnecessary copy, change your function to foo(const arma::vec& x), which means take a constant reference to x. The & is critical here.

like image 180
mtall Avatar answered Nov 13 '22 20:11

mtall


In addition to mtall's answer, which i agree with, for a case in which performance wasn't needed i used this:

void uvec_push(arma::uvec & v, unsigned int value) {
    arma::uvec av(1);
    av.at(0) = value;
    v.insert_rows(v.n_rows, av.row(0));
}
like image 2
spacm Avatar answered Nov 13 '22 22:11

spacm