根据 Rcpp.h 为自定义 类 扩展 Rcpp::as

Extending Rcpp::as for custom classes depending on Rcpp.h

我正在研究同时使用 Rcpp::IntegerVector(row/column 指针)和模板化 std::vector<T> 的 Rcpp 稀疏矩阵 class。基本原理是,通过简单地将它们保留为指向 R 对象的指针,可以避免在极大的稀疏矩阵中深度复制整数指针向量 (@i@p) 的开销,并且始终如一地显示微基准这种方法几乎只需要转换为 Eigen::SparseMatrix<T>arma::SpMat<T> 的一半时间,同时使用更少的内存。

基本Rcpp稀疏矩阵class

namespace SpRcpp {

    template<typename T>
    class SpMatrix {

    public:
        Rcpp::IntegerVector i, p;
        std::vector<T> x;
        unsigned int n_row, n_col;

        // constructor for the class from an Rcpp::S4 object
        SpMatrix(Rcpp::S4& mat) {
            Rcpp::IntegerVector dims = mat.slot("Dim");
            n_row = (unsigned int)dims[0];
            i = mat.slot("i");
            p = mat.slot("p");
            x = Rcpp::as<std::vector<T>>(mat.slot("x"));
        };
        // other constructors, class methods, iterators, etc.
    };
}

用法示例:

//[[Rcpp::export]]
std::vector<float> SpRcpp_SpMatrix(Rcpp::S4& mat) {
    SpRcpp::SpMatrix<float> A(mat);
    return A.x;
}

这有效!

但是,我想让 Rcpp 隐式转换 S4 dgCMatrix 对象,例如,转换为 SpRcpp::SpMatrix 对象以启用如下功能:

隐式 Rcpp 包装

//[[Rcpp::export]]
std::vector<float> SpRcpp_SpMatrix2(SpRcpp::SpMatrix<float>& mat) {
    return mat.x;
}

这就是使用 Rcpp::as 的情况。

我试过以下方法:

namespace Rcpp {
    namespace traits {
        template <typename T>
        class Exporter< SpRcpp::SpMatrix<T> > {
        public:
            Exporter(SEXP x) { Rcpp::S4 mat = x; }
            SpRcpp::SpMatrix<T> get() {
                return SpRcpp::SpMatrix<T>(mat);
            }
        private: Rcpp::S4 mat;
        };
    }
}

这可以编译,而且我知道 SpRcpp::SpMatrix<T>(Rcpp::S4& x) 构造函数可以工作,但是当我尝试将 dgCMatrix 提供给 SpRcpp_SpMatrix() 时,我得到错误:

Error in SpRcpp_SpMatrix2(A) : Not an S4 object.

我想这是因为我在所有 class 声明之前声明了以下内容:

#include <RcppCommon.h>
#include <Rcpp.h>

根据文档 here, the RcppGallery example, and the RcppArmadillo 实现,#include <Rcpp.h> 不能在 Rcpp::asRcpp::wrap 函数之前,但在我的情况下我不能这样做,因为我的 class定义需要Rcpp.h.

问题:SpMatrix class 取决于 Rcpp.h?

创建一个Rcpp SparseMatrix其实很简单class!我想多了。

#include <rcpp.h>

// Rcpp for sparse matrices (spRcpp)
namespace Rcpp {
    class SparseMatrix {
    public:
        Rcpp::IntegerVector i, p;
        Rcpp::NumericVector x;
        int n_rows, n_cols;

        // constructor
        SparseMatrix(Rcpp::S4 mat) {
            Rcpp::IntegerVector dim = mat.slot("Dim");
            i = mat.slot("i");
            p = mat.slot("p");
            x = mat.slot("x");
            n_rows = (int)dim[0];
            n_cols = (int)dim[1];
        };
    };
}

namespace Rcpp {
    template <> Rcpp::SparseMatrix as(SEXP mat) {
        return Rcpp::SparseMatrix(mat);
    }
}

//[[Rcpp::export]]
Rcpp::NumericVector toRcppSparseMatrix(Rcpp::SparseMatrix& A) {
    return A.x;
}

给定 Matrix::dgCMatrixmat,在 1-2 微秒内调用 toRcppSparseMatrix(mat) returns 非零值以获得 2500 万个值。这与 RcppArmadillo 或 RcppEigen 稀疏矩阵转换形成对比,后者对于同一矩阵大约需要 250 毫秒,并在内存中运行深度复制。

正如 Dirk 所建议的,使用 RcppArmadillo ivecdvec 非常有效,但仍然会创建浅拷贝,这会导致大约 100 毫秒的运行时间并消耗一些内存。

显然,上述方法仅限于 double 类型,因此如果没有深拷贝,则无法进行 float 操作。