Search code examples
c++eigenrcpparmadillo

Converting Eigen::MatrixXd to arma::mat and make a copy on a new object


I have a function within which I want to convert an Eigen::MatrixXd object to arma::mat.

I am aware of this question but I can't seem to be able to correct the behavior that I'm getting. Calling matrixxd_to_armamat from R would cause no issues, the problem is when I have this conversion in another function in C. This is a little confusing and I would like to understand what's going on.

#include <RcppArmadillo.h>
#include <RcppEigen.h>

// [[Rcpp::depends(RcppEigen)]]
// [[Rcpp::depends(RcppArmadillo)]]

using namespace std;

arma::mat matrixxd_to_armamat(Eigen::MatrixXd eigen_A) {
  arma::mat arma_B = arma::mat(eigen_A.data(), eigen_A.rows(), eigen_A.cols(),
                               false, false);
  return arma_B;
}

arma::mat matrixxd_to_armamat2(Eigen::MatrixXd& eigen_A) {
  arma::mat arma_B = arma::mat(eigen_A.data(), eigen_A.rows(), eigen_A.cols(),
                               false, false);
  return arma_B;
}



//[[Rcpp::export]]
arma::mat tester(){
  Eigen::MatrixXd A_eigen(2,2);
  A_eigen(0,0) = 1.0;
  A_eigen(1,0) = 2.0;
  A_eigen(0,1) = -1.0;
  A_eigen(1,1) = -2.0;

  Rcpp::Rcout << A_eigen << endl;

  arma::mat A_arma  = matrixxd_to_armamat(A_eigen);
  arma::mat A_arma2 = matrixxd_to_armamat2(A_eigen);

  Rcpp::Rcout << A_arma << endl;
  Rcpp::Rcout << A_arma2 << endl;

  return A_arma2;
}
/* In R
> tester()
 1 -1
 2 -2
  4.6503e-310  -1.0000e+00
  4.9407e-324   7.2661e-43

   1.0000  -1.0000
   2.0000  -2.0000

              [,1] [,2]
[1,] 4.650273e-310   -1
[2,]  2.000000e+00   -2
*/


Solution

  • So, after running this, I can only produce a problem with A_arma object creation. The object here is getting an odd value indicating that the memory mapping was bad. My thought on this is because it is being copied into the function instead of a reference update. The original answer showed the manipulation under a scoped function that allowed the reference and memory re-use.

    In particular, from the armadillo docs on advanced ctors:

    Create a matrix using data from writable auxiliary (external) memory, where ptr_aux_mem is a pointer to the memory. By default the matrix allocates its own memory and copies data from the auxiliary memory (for safety). However, if copy_aux_mem is set to false, the matrix will instead directly use the auxiliary memory (ie. no copying); this is faster, but can be dangerous unless you know what you are doing!

    The latter part is my emphasis.

    So, here under a pass-by-copy paradigm, the object needs to be fully copied vs. a reference update if writing a common cast function.

    arma::mat matrixxd_to_armamat(Eigen::MatrixXd eigen_A) {
        arma::mat arma_B = arma::mat(eigen_A.data(), eigen_A.rows(), eigen_A.cols(),
                                     true,   // changed from false to true.
                                     false); 
        return arma_B;
    }
    

    Now, if you are okay with linking back to the original Eigen object through a reference, then this should work:

    arma::mat matrixxd_to_armamat2(Eigen::MatrixXd& eigen_A) {
        arma::mat arma_B = arma::mat(eigen_A.data(), eigen_A.rows(), eigen_A.cols(),
                                     false, false);
        return arma_B;
    }
    

    Running both yields:

    tester()
    # 1 -1
    # 2 -2
    #   1.0000  -1.0000
    #   2.0000  -2.0000
    #
    #   1.0000  -1.0000
    #   2.0000  -2.0000
    #
    #     [,1] [,2]
    #[1,]    1   -1
    #[2,]    2   -2