[英]Convert Rcpp Numeric Vector into boost:ublas:vector
I'm trying to convert from an rtype
object to ublas
from boost
. 我正在尝试从
boost
将rtype
对象转换为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的一些代码,我可以返回包装为
rtype
的ublas
向量。
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! 话虽这么说,让我们做到这一点!
The issue is two fold that is being attempted: 问题有两种:
Rcpp::as<T>(obj)
) Rcpp::as<T>(obj)
) Rcpp::wrap(obj)
) 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.
请注意,所使用的方法是通过模板和部分专业化 ,最终会带来一些不错的自动效果。
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> >;
}
}
Rcpp.h
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>!!!! <<
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
. 由此,我们可以获得包含
RTYPE
的int
,然后构造一个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;
}
} ;
}
}
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:
要进行检查,我们应该研究两个不同的领域:
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;
// [[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!
进入下一个测试!
// [[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! 成功!
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;
}
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.