Reputation: 616
Using RcppEigen
I want to extract only the diagonal of a sparse matrix as a sparse matrix. Seemed easy enough - below you find my attempts and none deliver my desired result. Mind you attempt 5 doesn't compile and doesn't work. Here are some resources I used; Rcpp Gallery, KDE Forum and in the same post KDE Forum (2), Eigen Sparse Tutorial and SO. Feel like I am close... maybe not... I will let the experts decide.
// [[Rcpp::depends(RcppEigen)]]
#include <RcppEigen.h>
#include <Eigen/SparseCore>
// [[Rcpp::export]]
Eigen::SparseMatrix<double> diag_mat1(Eigen::Map<Eigen::SparseMatrix<double> > &X){
// cannot access diagonal of mapped sparse matrix
const int n(X.rows());
Eigen::VectorXd dii(n);
for (int i = 0; i < n; ++i) {
dii[i] = X.coeff(i,i);
}
Eigen::SparseMatrix<double> ans(dii.asDiagonal());
return ans;
}
// [[Rcpp::export]]
Eigen::SparseMatrix<double> diag_mat2(Eigen::SparseMatrix<double> &X){
Eigen::SparseVector<double> dii(X.diagonal().sparseView());
Eigen::SparseMatrix<double> ans(dii);
return ans;
}
// [[Rcpp::export]]
Eigen::SparseMatrix<double> diag_mat3(Eigen::SparseMatrix<double> &X){
Eigen::VectorXd dii(X.diagonal());
Eigen::SparseMatrix<double> ans(dii.asDiagonal());
ans.pruned(); //hoping this helps
return ans;
}
// [[Rcpp::export]]
Eigen::SparseMatrix<double> diag_mat4(Eigen::SparseMatrix<double> &X){
Eigen::SparseMatrix<double> ans(X.diagonal().asDiagonal());
return ans;
}
// [[Rcpp::export]]
Eigen::SparseMatrix<double> diag_mat5(Eigen::SparseMatrix<double> &X){
struct keep_diag {
inline bool operator() (const int& row, const int& col, const double&) const
{ return row==col; }
};
Eigen::SparseMatrix<double> ans(X.prune(keep_diag()));
return ans;
}
/***R
library(Matrix)
set.seed(42)
nc <- nr <- 5
m <- rsparsematrix(nr, nc, nnz = 10)
diag_mat1(m)
diag_mat2(m)
diag_mat3(m)
diag_mat4(m)
*/
EDIT: Added the results that each attempt gives;
> diag_mat1(m)
5 x 5 sparse Matrix of class "dgCMatrix"
[1,] 0 . . . .
[2,] . -0.095 . . .
[3,] . . 0 . .
[4,] . . . 2 .
[5,] . . . . 1.5
> diag_mat2(m)
5 x 1 sparse Matrix of class "dgCMatrix"
[1,] .
[2,] -0.095
[3,] .
[4,] 2.000
[5,] 1.500
> diag_mat3(m)
5 x 5 sparse Matrix of class "dgCMatrix"
[1,] 0 . . . .
[2,] . -0.095 . . .
[3,] . . 0 . .
[4,] . . . 2 .
[5,] . . . . 1.5
> diag_mat4(m)
5 x 5 sparse Matrix of class "dgCMatrix"
[1,] 0 . . . .
[2,] . -0.095 . . .
[3,] . . 0 . .
[4,] . . . 2 .
[5,] . . . . 1.5
EDIT2: Added desired output;
5 x 5 sparse Matrix of class "dgCMatrix"
[1,] . . . . .
[2,] . -0.095 . . .
[3,] . . . . .
[4,] . . . 2 .
[5,] . . . . 1.5
Answer with inspiration thanks to Aleh;
Eigen::SparseMatrix<double> diag_mat6(Eigen::Map<Eigen::SparseMatrix<double> > &X){
const int n(X.rows());
Eigen::SparseMatrix<double> dii(n, n);
for (int i = 0; i < n; ++i) {
if (X.coeff(i,i) != 0.0 ) dii.insert(i, i) = X.coeff(i,i);
}
dii.makeCompressed();
return dii;
}
Upvotes: 2
Views: 755
Reputation: 11738
I prefer RcppArmadillo because it generally behaves more like R than RcppEigen does.
For your problem, with RcppArmadillo, you can do:
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
// [[Rcpp::export]]
arma::sp_mat extractDiag(const arma::sp_mat& x) {
int n = x.n_rows;
arma::sp_mat res(n, n);
for (int i = 0; i < n; i++)
res(i, i) = x(i, i);
return res;
}
As suggested by @mtall, you can simply use:
// [[Rcpp::export]]
arma::sp_mat extractDiag3(const arma::sp_mat& x) {
return arma::diagmat(x);
}
If you really want to do this in Eigen, from the documentation, I came up with:
// [[Rcpp::export]]
Eigen::SparseMatrix<double> extractDiag2(Eigen::Map<Eigen::SparseMatrix<double> > &X){
int n = X.rows();
Eigen::SparseMatrix<double> res(n, n);
double d;
typedef Eigen::Triplet<double> T;
std::vector<T> tripletList;
tripletList.reserve(n);
for (int i = 0; i < n; i++) {
d = X.coeff(i, i);
if (d != 0) tripletList.push_back(T(i, i, d));
}
res.setFromTriplets(tripletList.begin(), tripletList.end());
return res;
}
Upvotes: 3
Reputation: 826
I think you just need to skip zero elements across the diagonal:
for (int i = 0; i < n; ++i) {
if (X.coeff(i,i) != 0.0)
dii[i] = X.coeff(i,i);
}
}
Upvotes: 1