Шаблонная функция для разреженных и плотных матриц в RcppArmadillo

Я пытаюсь определить шаблонную функцию, которая может обрабатывать входные данные как с разреженной, так и с плотной матрицей, используя RcppArmadillo. У меня есть очень простой случай отправки плотной или разреженной матрицы в C ++ и обратно в R, чтобы он работал следующим образом:

library(inline); library(Rcpp); library(RcppArmadillo)

sourceCpp(code =    "
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>
using namespace Rcpp ;
using namespace arma ;

template <typename T> T importexport_template(const T X) {
    T ret = X ;
    return ret ;
};

//[[Rcpp::export]]
SEXP importexport(SEXP X) {
    return wrap( importexport_template(X) ) ;
}")

library(Matrix)
X <- diag(3)
X_sp <- as(X, "dgCMatrix")

importexport(X)
##     [,1] [,2] [,3]
##[1,]    1    0    0
##[2,]    0    1    0
##[3,]    0    0    1
importexport(X_sp)
##3 x 3 sparse Matrix of class "dgCMatrix"
##          
##[1,] 1 . .
##[2,] . 1 .
##[3,] . . 1

и я интерпретирую это как означающее, что шаблон в основном работает (т. е. плотная R-матрица превращается в arma::mat, а разреженная R-матрица превращается в arma::sp_mat-объект неявными вызовами Rcpp::as, а соответствующие подразумеваемые Rcpp:wraps затем сделайте то же самое и верните плотный вместо плотного и разреженный на разреженный).

Фактическая функция, которую я пытаюсь написать, конечно же, требует нескольких аргументов, и вот где я терплю неудачу - выполняю что-то вроде:

sourceCpp(code ="
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

using namespace Rcpp ;
using namespace arma ;

template <typename T> T scalarmult_template(const T X, double scale) {
    T ret = X * scale;
    return ret;
};

//[[Rcpp::export]]
SEXP scalarmult(SEXP X, double scale) {
    return wrap(scalarmult_template(X, scale) ) ;
}")

терпит неудачу, потому что компилятор не знает, как разрешить * во время компиляции для SEXPREC* const. Думаю, мне нужно что-то вроде оператора-переключателя в этом фрагменте Rcpp Gallery для правильной отправки конкретным функциям шаблона, но я не знаю, как написать это для типов, которые кажутся более сложными, чем INTSXP и т. д.

Думаю, я знаю, как получить доступ к типу, который мне понадобится для такого оператора switch, например:

sourceCpp(code ="
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

using namespace Rcpp ;
using namespace arma ;

//[[Rcpp::export]]
SEXP printtype(SEXP Xr) {
    Rcpp::Rcout << TYPEOF(Xr) << std::endl ;
    return R_NilValue;
}")
printtype(X)
##14
##NULL
printtype(X_sp)
##25
##NULL

но я не понимаю, что дальше делать. Как будет выглядеть версия scalarmult_template, которая работает с разреженными и плотными матрицами?


person fabians    schedule 19.03.2014    source источник
comment
Это сложнее, потому что вы хотите отправлять как на S4 (т. Е. С TYPEOF, возвращающим 25), так и на примитивные типы. Я бы посоветовал обрабатывать отправку на уровне R, а затем упростить код C ++. В противном случае вам понадобится что-то вроде if (Rf_isS4(Xr) && Rf_inherits(Xr, "<MatrixClass>")) {...}   -  person Kevin Ushey    schedule 19.03.2014
comment
@KevinUshey: спасибо! Я добавляю ответ на свой вопрос на основе вашего предложения.   -  person fabians    schedule 20.03.2014


Ответы (1)


Отвечая на свой вопрос, основанный на комментарии @ KevinUshey. Я делаю матричное умножение для 3 случаев: плотно-плотный, разреженно-плотный и "indMatrix" -плотный:

library(inline)
library(Rcpp)
library(RcppArmadillo)
library(Matrix)
library(rbenchmark)

sourceCpp(code="
// [[Rcpp::depends(RcppArmadillo)]]
#include <RcppArmadillo.h>

using namespace Rcpp ;
using namespace arma ;

arma::mat matmult_sp(const arma::sp_mat X, const arma::mat Y){
    arma::mat ret = X * Y;
    return ret;
};
arma::mat matmult_dense(const arma::mat X, const arma::mat Y){
    arma::mat ret = X * Y;
    return ret;
};
arma::mat matmult_ind(const SEXP Xr, const arma::mat Y){
    // pre-multplication with index matrix is a permutation of Y's rows: 
    S4 X(Xr);
    arma::uvec perm =  X.slot("perm");
    arma::mat ret = Y.rows(perm - 1);
    return ret;
};

//[[Rcpp::export]]
arma::mat matmult_cpp(SEXP Xr, const arma::mat Y) {
    if (Rf_isS4(Xr)) {
        if(Rf_inherits(Xr, "dgCMatrix")) {
            return matmult_sp(as<arma::sp_mat>(Xr), Y) ;
        } ;
        if(Rf_inherits(Xr, "indMatrix")) {
            return matmult_ind(Xr, Y) ; 
        } ;
        stop("unknown class of Xr") ;
    } else {
        return matmult_dense(as<arma::mat>(Xr), Y) ;
    } 
}")

n <- 10000
d <- 20
p <- 30  

X <- matrix(rnorm(n*d), n, d)
X_sp <- as(diag(n)[,1:d], "dgCMatrix")
X_ind <- as(sample(1:d, n, rep=TRUE), "indMatrix")
Y <- matrix(1:(d*p), d, p)

matmult_cpp(as(X_ind, "ngTMatrix"), Y)
## Error: unknown class of Xr

all.equal(X%*%Y, matmult_cpp(X, Y))
## [1] TRUE

all.equal(as.vector(X_sp%*%Y), 
          as.vector(matmult_cpp(X_sp, Y)))
## [1] TRUE

all.equal(X_ind%*%Y, matmult_cpp(X_ind, Y))
## [1] TRUE

РЕДАКТИРОВАТЬ: это было преобразовано в сообщение галереи Rcpp.

person fabians    schedule 20.03.2014
comment
Это может быть настолько хорошо, насколько это возможно. SEXP - это тип объединения, о котором вы будете знать только во время времени выполнения, что в значительной степени исключает решение на основе чистых шаблонов, пытающееся справиться с этим во время компиляции. - person Dirk Eddelbuettel; 20.03.2014