简体   繁体   English

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

[英]Convert Rcpp Numeric Vector into boost:ublas:vector

I'm trying to convert from an rtype object to ublas from boost . 我正在尝试从boostrtype对象转换为ublas

Using some code I found from the Rcpp dev list regarding ublas , I'm able to return the ublas vector wrapped as an rtype . 使用从Rcpp开发人员列表中找到的有关ublas的一些代码,我可以返回包装为rtypeublas向量。

eg 例如

// 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()
  ) ;
}

To mimick the behavior import behavior, I currently use a loop over ublas vector of the corresponding length and just assign everything from the rtype to it. 为了模仿行为导入行为,我目前在对应长度的ublas向量上使用循环,并从rtype分配所有内容。

Would switching from a loop improve performance? 从循环切换会提高性能吗?

// My attempt to convert from rtype to ublas

// so R can find the libraries
//[[Rcpp::depends(BH)]]
//[[Rcpp::plugins("cpp11")]]

#include <Rcpp.h>


#include <cstdlib>
#include <iostream>
#include <fstream>


#include <boost/numeric/odeint.hpp>
#include <boost/numeric/ublas/matrix.hpp>
#include <boost/numeric/ublas/io.hpp>

using namespace std;
using namespace Rcpp;
using namespace boost::numeric::ublas;

typedef boost::numeric::ublas::vector< double > vector_type;
typedef boost::numeric::ublas::matrix< double > matrix_type;

template <typename T>    /// this need to be fixed up, hopefully working for now though
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()
  ) ;
}

// [[Rcpp::export]]
NumericVector main(NumericVector x1)
{


  int L =x1.length();


  vector_type x(L , 0 ); // initialize the vector to all zero

  for(int i=0;i<L;i++)
  {

    x(i) =  x1(i);

  }


return(ublas2rcpp(x));   

}

First off, sorry for the delay. 首先,对于延迟,我们深表歉意。 Hopefully, what I've done makes up for it. 希望我所做的一切能够弥补这一点。

With that being said, let's do this! 话虽这么说,让我们做到这一点!

Introduction 介绍

The issue is two fold that is being attempted: 问题有两种:

  1. Converting from R to C++ ( Rcpp::as<T>(obj) ) 从R转换为C ++( Rcpp::as<T>(obj)
  2. Converting from C++ to R ( Rcpp::wrap(obj) ) 从C ++转换为R( Rcpp::wrap(obj)

Luckily, there is a wonderful Rcpp vignette called Extending Rcpp that addresses custom objects. 幸运的是,有一个名为Extending Rcpp的 Rcpp小插图可以解决自定义对象。 Sadly, the clarity of the vignette leaves a lot to be desired when compared to the other documentation. 可悲的是,与其他文档相比,小插图的清晰度还有很多不足之处。 (I may try to do a PR that improves it.) (我可能会尝试做一个改善它的PR。)

So, I'm going to try to take you through the steps with a bit of commentary. 因此,我将尝试通过一些评论向您介绍这些步骤。 Please note that the approach used is via Templates and partial specialization and will result in some nice automagic at the end. 请注意,所使用的方法是通过模板和部分专业化 ,最终会带来一些不错的自动效果。

Explanation of 的解释

Stage 1 - Forward Declarations 第1阶段-前期声明

In the first stage, we must declare our intent to the features we wish to use prior to engaging Rcpp.h . 在第一步中,我们必须在使用Rcpp.h之前声明我们希望使用的功能。 To do so, we will load a different header file and add some definitions to the Rcpp::traits namespace. 为此,我们将加载一个不同的头文件,并向Rcpp::traits命名空间添加一些定义。

Principally, when we start writing the file, the first header that we must load is RcppCommon.h and not the usual Rcpp.h !! 原则上,当我们开始写入文件时,必须加载的第一个标头是RcppCommon.h不是通常的Rcpp.h If we do not place the forward declaration prior to the Rcpp.h call, we will be unable to appropriately register our extension. 如果不将前向声明放在Rcpp.h调用之前,则将无法正确注册我们的扩展名。

Then, we must provide add in the different plugin markup for sourceCpp() to set the appropriate flags during the compilation of the code. 然后,我们必须为sourceCpp()提供其他插件标记中的sourceCpp()以在代码编译期间设置适当的标志。 After the plugins, we will include the actual boost headers that we want to use. 在插件之后,我们将包括我们要使用的实际boost头。 Lastly, we must add two special Rcpp function declaration, Rcpp::as<T>(obj) and Rcpp::wrap(obj) , within Rcpp::traits namespace. 最后,我们必须在Rcpp::traits命名空间中添加两个特殊的Rcpp函数声明: Rcpp::as<T>(obj)Rcpp::wrap(obj) To enable multiple types, we must create an Exporter class instead of a more direct call to template <> ClassName as( SEXP ) . 要启用多种类型,我们必须创建一个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> >;

  }
}

Stage 2 - Include the Rcpp.h 第2阶段-包含Rcpp.h

It might seem frivolous to have a stage just to declare import order, but if Rcpp.h is included before the forward declaration then Rcpp::traits is not updated and we enter the abyss. 仅具有一个阶段来声明导入顺序似乎是轻浮的,但是如果Rcpp.h被包括在正向声明之前,则Rcpp::traits不会被更新,我们进入了深渊。

Thus: 从而:

// >> Place <Rcpp.h> AFTER the forward declaration!!!! <<

#include <Rcpp.h>


// >> Place Definitions of Forward Declarations AFTER <Rcpp.h>!!!! <<

Stage 3 - Implementing the Extensions 阶段3-实施扩展

Now, we must actually implement the forward declarations. 现在,我们实际上必须实现前向声明。 In particular, the only implementation that will be slightly problematic is the as<> since the wrap() is straight forward. 特别是,由于wrap()是直接的,唯一会带来一些问题的实现是as<>

wrap()

To implement wrap() we must appeal to a built in type conversion index within Rcpp called Rcpp::traits::r_sexptype_traits<T>::rtype . 要实现wrap()我们必须吸引Rcpp中的内置类型转换索引,该索引称为Rcpp::traits::r_sexptype_traits<T>::rtype From this, we are able to obtain an int containing the RTYPE and then construct an Rcpp::Vector . 由此,我们可以获得包含RTYPEint ,然后构造一个Rcpp::Vector For the construction of a matrix, the same ideas hold true. 对于矩阵的构造,相同的想法仍然适用。

as()

For as<>() , we need to consider the template that will be passed in. Furthermore, we setup a typedef directly underneath the Exporter class definition to easily define an OUT object to be used within the get() method. 对于as<>() ,我们需要考虑将要传入的模板。此外,我们在Exporter类定义的正下方设置了一个typedef以轻松定义要在get()方法中使用的OUT对象。 Outside of that, we use the same trick to move back and forth from a C++ T type to an R type. 除此之外,我们使用相同的技巧将C ++ T类型转换为R类型。

In order to accomplish the as<> , or the direct port from R to C++, I had to do something dirty: I copied the vector contents . 为了完成as<>或从R到C ++的直接端口,我不得不做些肮脏的事情: 复制了vector内容 The code that governs this output is given within the get() of the Exporter class. Exporter类的get()中给出了控制此输出的代码。 You may wish to spend some time looking into changing the assignment using pointers perhaps. 您可能希望花一些时间来研究使用指针更改分配。 I'm not very well versed with ublas so I did not see an easy approach to resolve the pointer pass. 我对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;
  }
} ;


}
}

Stage 4 - Testing 阶段4-测试

Okay, let's see if what we worked on paid off ( spoiler It did! spoiler ). 好的,让我们看看我们所做的工作是否得到了回报( spoiler它做到了! spoiler )。 To check, we should look at two different areas: 要进行检查,我们应该研究两个不同的领域:

  1. Trace diagnostics within the function and; 跟踪功能内的诊断;以及
  2. An automagic test. 自动测试。

Both of which are given below. 两者都在下面给出。 Note that I've opted to shorten the ublas setup to just be: 请注意,我选择将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;

Trace Diagnostics 跟踪诊断

// [[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;

}

Test Call: 测试电话:

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

Results: 结果:

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

This test performed as expected. 该测试按预期执行。 Onto the next test! 进入下一个测试!

Automagic test 自动测试

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

Test Call: 测试电话:

automagic_ublas_rcpp(c(1,2,3.2,1.2))

Results: 结果:

[1] 1.0 2.0 3.2 1.2

Success! 成功!

Stage 5 - All together for the Cntrl + C and Cntrl + V 阶段5- Cntrl + CCntrl + V在一起

Here is the combination of the above code chunks given by stage. 这是阶段给出的上述代码块的组合。 If you copy and paste this into your .cpp file, then everything should work. 如果您复制并粘贴到您的这个.cpp文件,那么一切都应该工作。 If not, let me know. 如果没有,请告诉我。

// -------------- 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;

}

Closing Remarks 闭幕致辞

Whew... That was a lot. 哇...好多。 Hopefully, the above provided enough justification as I believe you may want to extend past a 1D vector to perhaps a ublas::matrix and so on. 希望上面提供了足够的理由,因为我相信您可能希望将一1D向量扩展到ublas::matrix等。 In addition, the wait should hopefully have been worth it as you now have the autoconvert magic of Rcpp so there is no need to call ublas2rcpp() within the return() statement. 此外,等待应该值得,因为您现在拥有Rcpp的自动转换功能,因此无需在return()语句中调用ublas2rcpp() In fact, you could simplify specify the return type of the function as ublas::vector<double> ! 实际上,您可以简化将函数的返回类型指定为ublas::vector<double>

In other news, I think what probably stopped you from being able to create the alternate template function above (eg rcpp2ublas ) was not being able to deduce what C++ T type the Rcpp::Vector was. 在其他消息中,我认为可能阻止您创建上述替代模板功能(例如rcpp2ublas )的原因是无法推断Rcpp::Vector是什么C ++ T类型。 Personally, after digging around in the Rcpp GitHub Repo , I'm not sure such a conversion index exists or if it does should be used due to the Shield usage. 就个人而言,在Rcpp GitHub Repo中进行挖掘之后,我不确定是否存在这样的转换索引,或者由于Shield使用,是否应该使用该转换索引。 Digging deeper into conversions is an adventure for another day. 深入研究转化是另一天的冒险。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM