R会话使用RcppParallel中止

时间:2019-06-13 18:04:18

标签: r algorithm rcpp rcppparallel

我正在尝试使用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,我的实现不是线程安全的,是否有办法以安全的方式重写此算法?在这里知道优先级队列是必须的。

感谢您的帮助!

0 个答案:

没有答案