将Rcpp数值向量转换为boost:ublas:vector

时间:2016-06-21 03:41:44

标签: c++ r boost rcpp

我尝试从rtype转换为ublas对象boost

使用我在Rcpp dev list regarding ublas中找到的一些代码,我能够返回包裹为ublas的{​​{1}}向量。

e.g。

rtype

为了模仿行为导入行为,我目前使用相应长度的// Converts from ublas to rtype template <typename T> Rcpp::Vector< Rcpp::traits::r_sexptype_traits<T>::rtype > ublas2rcpp( const boost::numeric::ublas::vector<T>& x ){ return Rcpp::Vector< Rcpp::traits::r_sexptype_traits<T>::rtype >( x.begin(), x.end() ) ; } 向量的循环,只需将ublas中的所有内容分配给它。

从循环切换会改善性能吗?

rtype

1 个答案:

答案 0 :(得分:4)

首先,抱歉延迟。希望我所做的事能弥补它。

话虽如此,让我们这样做!

简介

问题有两个:

  1. 从R转换为C ++(Rcpp::as<T>(obj)
  2. 从C ++转换为R(Rcpp::wrap(obj)
  3. 幸运的是,有一个名为Extending Rcpp的精彩Rcpp插图可以解决自定义对象。可悲的是,与其他文档相比,小插图的清晰度还有很多不足之处。 (我可能会尝试做一个改善它的公关。)

    所以,我将尝试通过一些评论带你完成这些步骤。请注意,使用的方法是通过模板和部分专业化,最终会产生一些不错的自动化。

    的说明

    第1阶段 - 前向声明

    在第一阶段,我们必须在参与Rcpp.h之前声明我们对我们希望使用的功能的意图。为此,我们将加载不同的头文件并向Rcpp::traits命名空间添加一些定义。

    主要是,当我们开始编写文件时,我们必须加载的第一个标头是RcppCommon.h不是通常的Rcpp.h !!如果我们在Rcpp.h电话会议之前未提出前瞻性声明,我们将无法正确注册我们的扩展。

    然后,我们必须为sourceCpp()提供不同的插件标记,以便在编译代码期间设置适当的标志。在插件之后,我们将包含我们想要使用的实际boost标头。最后,我们必须在Rcpp::as<T>(obj)命名空间中添加两个特殊的Rcpp函数声明Rcpp::wrap(obj)Rcpp::traits。要启用多种类型,我们必须创建Exporter类,而不是直接调用template <> ClassName as( SEXP )

    #include <RcppCommon.h>
    
    // Flags for C++ compiler
    
    // [[Rcpp::depends(BH)]]
    // [[Rcpp::plugins("cpp11")]]
    
    // Third party library includes that provide the template class of ublas
    #include <boost/numeric/ublas/matrix_sparse.hpp>
    #include <boost/numeric/ublas/matrix.hpp>
    
    // Provide Forward Declarations
    namespace Rcpp {
    
       namespace traits{
    
       // Setup non-intrusive extension via template specialization for
       // 'ublas' class boost::numeric::ublas
    
       // Support for wrap
       template <typename T> SEXP wrap(const boost::numeric::ublas::vector<T> & obj);
    
       // Support for as<T>
       template <typename T> class Exporter< boost::numeric::ublas::vector<T> >;
    
      }
    }
    

    第2阶段 - 包括Rcpp.h

    有一个阶段只是为了声明导入顺序似乎是无聊的,但如果在前向声明之前包含Rcpp.h,则Rcpp::traits不会更新,我们就会进入深渊。

    因此:

    // >> Place <Rcpp.h> AFTER the forward declaration!!!! <<
    
    #include <Rcpp.h>
    
    
    // >> Place Definitions of Forward Declarations AFTER <Rcpp.h>!!!! <<
    

    第3阶段 - 实施扩展

    现在,我们必须实际执行前向声明。特别是,由于as<>是直截了当的,wrap()是唯一有问题的实现。

    wrap()

    要实现wrap(),我们必须在Rcpp中称为Rcpp::traits::r_sexptype_traits<T>::rtype的内置类型转换索引。由此,我们可以获得包含int的{​​{1}},然后构建RTYPE。对于矩阵的构造,同样的想法也适用。

    Rcpp::Vector

    对于as(),我们需要考虑要传入的模板。此外,我们在as<>()类定义的正下方设置了typedef,以便轻松定义{{1}要在Exporter方法中使用的对象。除此之外,我们使用相同的技巧来从C ++ OUT类型到get()类型来回移动。

    为了完成T或从R到C ++的直接端口,我不得不做一些脏事:我复制了矢量内容。管理此输出的代码在R类的as<>内给出。您可能希望花一些时间考虑使用指针更改分配。我对get()不太熟悉,所以我没有看到解决指针传递的简单方法。

    Exporter

    第4阶段 - 测试

    好的,让我们看看我们的工作是否得到了回报(剧透确实如此!剧透)。要检查,我们应该看两个不同的领域:

    1. 功能内的跟踪诊断和;
    2. 自动化测试。
    3. 下面给出了两者。请注意,我已选择将ublas设置缩短为:

      // Define template specializations for as<> and wrap
      
      namespace Rcpp {
      
      namespace traits{
      
      // Defined wrap case
      template <typename T> SEXP wrap(const boost::numeric::ublas::vector<T> & obj){
        const int RTYPE = Rcpp::traits::r_sexptype_traits<T>::rtype ;
      
        return Rcpp::Vector< RTYPE >(obj.begin(), obj.end());
      };
      
      
      // Defined as< > case
      template<typename T>
      class Exporter< boost::numeric::ublas::vector<T> > {
        typedef typename boost::numeric::ublas::vector<T> OUT ;
      
        // Convert the type to a valid rtype. 
        const static int RTYPE = Rcpp::traits::r_sexptype_traits< T >::rtype ;
        Rcpp::Vector<RTYPE> vec;
      
      public:
        Exporter(SEXP x) : vec(x) {
          if (TYPEOF(x) != RTYPE)
            throw std::invalid_argument("Wrong R type for mapped 1D array");
        }
        OUT get() {
      
          // Need to figure out a way to perhaps do a pointer pass?
          OUT x(vec.size());
      
          std::copy(vec.begin(), vec.end(), x.begin()); // have to copy data
      
          return x;
        }
      } ;
      
      
      }
      }
      

      跟踪诊断

      ublas

      测试电话:

      // Here we define a shortcut to the boost ublas class to enable multiple ublas types via a template.
      // ublas::vector<T> => ublas::vector<double>, ... , ublas::vector<int>
      namespace ublas = ::boost::numeric::ublas;
      

      结果:

      // [[Rcpp::export]]
      void containment_test(Rcpp::NumericVector x1) {
      
        Rcpp::Rcout << "Converting from Rcpp::NumericVector to ublas::vector<double>" << std::endl;
      
        ublas::vector<double> x = Rcpp::as< ublas::vector<double> >(x1); // initialize the vector to all zero
      
        Rcpp::Rcout << "Running output test with ublas::vector<double>" << std::endl;
      
        for (unsigned i = 0; i < x.size (); ++ i)
          Rcpp::Rcout  << x(i) << std::endl;
      
        Rcpp::Rcout << "Converting from ublas::vector<double> to Rcpp::NumericVector" << std::endl;
      
        Rcpp::NumericVector test = Rcpp::wrap(x);
      
        Rcpp::Rcout << "Running output test with Rcpp::NumericVector" << std::endl;
      
        for (unsigned i = 0; i < test.size (); ++ i)
          Rcpp::Rcout  << test(i) << std::endl;
      
      }
      

      此测试按预期执行。进入下一次测试!

      自动化测试

      containment_test(c(1,2,3,4))
      

      测试电话:

      Converting from Rcpp::NumericVector to ublas::vector<double>
      Running output test with ublas::vector<double>
      1
      2
      3
      4
      Converting from ublas::vector<double> to Rcpp::NumericVector
      Running output test with Rcpp::NumericVector
      1
      2
      3
      4
      

      结果:

      // [[Rcpp::export]]
      ublas::vector<double> automagic_ublas_rcpp(ublas::vector<double> x1) {
        return x1;
      }
      

      成功!

      阶段5 - 一起用于 Cntrl + C Cntrl + V

      以下是舞台给出的上述代码块的组合。如果您将其复制并粘贴到automagic_ublas_rcpp(c(1,2,3.2,1.2)) 文件中,那么的所有内容都可以正常工作。如果没有,请告诉我。

      [1] 1.0 2.0 3.2 1.2
      

      结束备注

      哇...这太过分了。希望上面提供了足够的理由,因为我相信您可能希望将.cpp向量扩展到// -------------- Stage 1: Forward Declarations with `RcppCommon.h` #include <RcppCommon.h> // Flags for C++ compiler // [[Rcpp::depends(BH)]] // [[Rcpp::plugins("cpp11")]] // Third party library includes that provide the template class of ublas #include <boost/numeric/ublas/matrix_sparse.hpp> #include <boost/numeric/ublas/matrix.hpp> // Here we use ublas_vec to enable multiple ublas types via a template. // ublas::vector<T> => ublas::vector<double>, ... , ublas::vector<int> namespace ublas = ::boost::numeric::ublas; // Provide Forward Declarations namespace Rcpp { namespace traits{ // Setup non-intrusive extension via template specialization for // 'ublas' class boost::numeric::ublas // Support for wrap template <typename T> SEXP wrap(const boost::numeric::ublas::vector<T> & obj); // Support for as<T> template <typename T> class Exporter< boost::numeric::ublas::vector<T> >; } } // -------------- Stage 2: Including Rcpp.h // >> Place <Rcpp.h> AFTER the forward declaration!!!! << #include <Rcpp.h> // >> Place Definitions of Forward Declarations AFTER <Rcpp.h>!!!! << // -------------- Stage 3: Implementation of Declarations // Define template specializations for as<> and wrap namespace Rcpp { namespace traits{ // Defined wrap case template <typename T> SEXP wrap(const boost::numeric::ublas::vector<T> & obj){ const int RTYPE = Rcpp::traits::r_sexptype_traits<T>::rtype ; return Rcpp::Vector< RTYPE >(obj.begin(), obj.end()); }; // Defined as< > case template<typename T> class Exporter< boost::numeric::ublas::vector<T> > { typedef typename boost::numeric::ublas::vector<T> OUT ; // Convert the type to a valid rtype. const static int RTYPE = ::Rcpp::traits::r_sexptype_traits< T >::rtype ; Rcpp::Vector<RTYPE> vec; public: Exporter(SEXP x) : vec(x) { if (TYPEOF(x) != RTYPE) throw std::invalid_argument("Wrong R type for mapped 1D array"); } OUT get() { // Need to figure out a way to perhaps do a pointer pass? OUT x(vec.size()); std::copy(vec.begin(), vec.end(), x.begin()); // have to copy data return x; } } ; } } // -------------- Stage 4: Tests // [[Rcpp::export]] ublas::vector<double> automagic_ublas_rcpp(ublas::vector<double> x1) { return x1; } // [[Rcpp::export]] void containment_test(Rcpp::NumericVector x1) { Rcpp::Rcout << "Converting from Rcpp::NumericVector to ublas::vector<double>" << std::endl; ublas::vector<double> x = Rcpp::as< ublas::vector<double> >(x1); // initialize the vector to all zero Rcpp::Rcout << "Running output test with ublas::vector<double>" << std::endl; for (unsigned i = 0; i < x.size (); ++ i) Rcpp::Rcout << x(i) << std::endl; Rcpp::Rcout << "Converting from ublas::vector<double> to Rcpp::NumericVector" << std::endl; Rcpp::NumericVector test = Rcpp::wrap(x); Rcpp::Rcout << "Running output test with Rcpp::NumericVector" << std::endl; for (unsigned i = 0; i < test.size (); ++ i) Rcpp::Rcout << test(i) << std::endl; } 等等。此外,等待应该是值得的,因为你现在拥有1D的自动转换魔法,因此无需在ublas::matrix语句中调用Rcpp。实际上,您可以简化将函数的返回类型指定为ublas2rcpp()

      在其他新闻中,我认为可能阻止您在上面创建备用模板函数(例如return())可能无法推断出C ++ ublas::vector<double>键入{{1}是的。就个人而言,在Rcpp GitHub Repo中进行挖掘之后,我不确定是否存在这样的转换索引,或者是否因为rcpp2ublas用法而应该使用它。深入挖掘转换是另一天的冒险。