我正在尝试使用Rcpp和RcppParallel构建一个实现Dijkstra算法的R包。您可以看到我的作品here。现在,我想添加一个新函数,然后出现一个奇怪的行为。当我通过sourceCpp函数编译该函数并尝试使用时,它运行良好,但是当我使用该函数重建软件包时,调用该函数时R几乎立即崩溃。通过Rstudio构建程序包时,我没有任何错误(清理并重新构建)。
该算法是双向Dijkstra,因此它使用两个图(正向和反向)和来自STL的两个优先级队列。图只是对向量的向量。
这是代码(对不起,但我不知道如何简化):
// [[Rcpp::depends(RcppParallel)]]
// [[Rcpp::plugins(cpp11)]]
#include <queue>
#include <vector>
#include <limits>
#include <functional>
#include <RcppParallel.h>
#include <Rcpp.h>
using namespace RcppParallel;
struct Pardijkstra : public Worker
{
//input
const std::vector<std::vector<std::pair<int, double> > > m_graph;
const std::vector<std::vector<std::pair<int, double> > > m_graphr;
RVector<int> m_dep;
RVector<int> m_arr;
const int m_nbnodes;
//output
RcppParallel::RVector<double> m_result;
//constructor
Pardijkstra(const std::vector<std::vector<std::pair<int, double> > > &graph,
const std::vector<std::vector<std::pair<int, double> > > &graphr,
Rcpp::IntegerVector & dep,
Rcpp::IntegerVector & arr,
const int nbnodes,
Rcpp::NumericVector result) : m_graph(graph),m_graphr(graphr),m_dep(dep), m_arr(arr),m_nbnodes(nbnodes),m_result(result)
{
}
//overload () operator
void operator()(std::size_t begin, std::size_t end){
struct comp{
//Custom comparator included in priority queues
bool operator()(const std::pair<int, double> &a, const std::pair<int, double> &b){
return a.second > b.second;
}
};
//
for (std::size_t k=begin; k!=end;k++){
//Here is the algorithm (bidirectional search)
int StartNode=m_dep[k];
int EndNode=m_arr[k];
std::vector<double> Distances(m_nbnodes, std::numeric_limits<double>::max());
std::vector<double> Distances2(m_nbnodes, std::numeric_limits<double>::max());
//Distances are stored here
Distances[StartNode] = 0.0;
Distances2[EndNode] = 0.0;
std::vector <int> Visited(m_nbnodes,0);
std::priority_queue<std::pair<int, double>, std::vector<std::pair<int, double> >, comp > Q;
std::priority_queue<std::pair<int, double>, std::vector<std::pair<int, double> >, comp > Qr;
Q.push(std::make_pair(StartNode, 0.0));
Qr.push(std::make_pair(EndNode, 0.0));
Visited[StartNode]+=1;
Visited[EndNode]+=1;
double mu=std::numeric_limits<double>::max();
while (!Q.empty() && !Qr.empty()) {
//Stopping criterion
if (Q.top().second+Qr.top().second >= mu){
break;
}
//Forward search
if (!Q.empty()){
int v=Q.top().first;
int w=Q.top().second;
Q.pop();
if (w <= Distances[v]) {
for (int i=0; i< m_graph[v].size(); i++){
int v2 = m_graph[v][i].first;
double w2 = m_graph[v][i].second;
if (Distances[v] + w2 < Distances[v2]) {
Distances[v2] = Distances[v] + w2;
Q.push(std::make_pair(v2, Distances[v2]));
Visited[v2]+=1;
}
}
}
if ((Visited[v]>1) && (Distances[v]+Distances2[v]) < mu){
mu=Distances[v]+Distances2[v];
}
}
//Backward search
if (!Qr.empty()){
int vv=Qr.top().first;
int ww=Qr.top().second;
Qr.pop();
Visited[vv]+=1;
if (ww <= Distances2[vv]) {
for (int i=0; i< m_graphr[vv].size(); i++){
int vv2 = m_graphr[vv][i].first;
double ww2 = m_graphr[vv][i].second;
if (Distances2[vv] + ww2 < Distances2[vv2]) {
Distances2[vv2] = Distances2[vv] + ww2;
Qr.push(std::make_pair(vv2, Distances2[vv2]));
Visited[vv2]+=1;
}
}
}
if ((Visited[vv]> 1) && (Distances[vv]+Distances2[vv]) < mu){
mu=Distances[vv]+Distances2[vv];
}
}
}
if (mu >= std::numeric_limits<double>::max()){
m_result[k] = Rcpp::NumericVector::get_na();
}
else {
m_result[k]=mu;
}
}
}
};
//Fonction exported in R
// [[Rcpp::export]]
Rcpp::NumericVector Bidir_par(Rcpp::IntegerVector dep, Rcpp::IntegerVector arr,Rcpp::IntegerVector gfrom,Rcpp::IntegerVector gto,Rcpp::NumericVector gw,int NbNodes){
//dep : origin nodes
//arr: destination nodes
//gfrom: first nodes of graphs edges
//gto: last nodes of graphs edges
//gw: weights of graphs edges
//NbNodes: number of nodes in the graph
Rcpp::NumericVector result(dep.size());
//Construction of the two graphs
int NbEdges=gfrom.size();
std::vector<std::vector<std::pair<int, double> > > G(NbNodes);
std::vector<std::vector<std::pair<int, double> > > Gr(NbNodes);
for (int i = 0; i != NbEdges; ++i) {
G[gfrom[i]].push_back(std::make_pair(gto[i], gw[i]));
Gr[gto[i]].push_back(std::make_pair(gfrom[i], gw[i]));
}
Pardijkstra Dijfunc(G,Gr,dep,arr,NbNodes,result);
parallelFor(0,dep.length(),Dijfunc);
return Rcpp::wrap(result);
}
我知道输入应该是RVector或RMatrix,但是不幸的是,有向图不能以这种形式简化而不会降低效率。 std :: vector在内存中是连续的吗?
我注意到,如果删除一条if语句(例如所有向后搜索),它将起作用。
如果您想查看软件包中实现的其他功能,可以检查我的github存储库。所有其他功能都很好。
这是Makevars文件:
CXX_STD = CXX11
PKG_LIBS += $(shell ${R_HOME}/bin/Rscript -e "RcppParallel::RcppParallelLibs()")
Makevars.win文件:
CXX_STD = CXX11
PKG_CXXFLAGS += -DRCPP_PARALLEL_USE_TBB=1
PKG_LIBS += $(shell "${R_HOME}/bin${R_ARCH_BIN}/Rscript.exe" -e "RcppParallel::RcppParallelLibs()")
和说明文件:
Imports: Rcpp (>= 1.0.1), RcppParallel (>= 4.4.2)
LinkingTo: Rcpp, RcppParallel
SystemRequirements: GNU make
那我在做什么错了?
为什么该函数可与sourceCpp函数一起使用,而不与软件包一起使用?
编辑:根据@thc,我的实现不是线程安全的,是否有办法以安全的方式重写此算法?在这里知道优先级队列是必须的。
感谢您的帮助!