#include "run_sp.h" #include "dgraph.h" #include "bheap.h" // # nocov start template void inst_graph (std::shared_ptr g, size_t nedges, const std::map & vert_map, const std::vector & from, const std::vector & to, const std::vector & dist, const std::vector & wt) { for (size_t i = 0; i < nedges; ++i) { size_t fromi = vert_map.at(from [i]); size_t toi = vert_map.at(to [i]); g->addNewEdge (fromi, toi, dist [i], wt [i], i); } } // # nocov end // RcppParallel jobs can be chunked to a specified "grain size"; see // https://rcppcore.github.io/RcppParallel/#grain_size // This function determines chunk size such that there are at least 100 chunks // for a given `nfrom`. size_t run_sp::get_chunk_size (const size_t nfrom) { size_t chunk_size; if (nfrom > 1000) chunk_size = 100; else if (nfrom > 100) chunk_size = 10; else chunk_size = 1; return chunk_size; } std::shared_ptr run_sp::getHeapImpl(const std::string& heap_type) { return std::make_shared >(); } struct OneDist : public RcppParallel::Worker { RcppParallel::RVector dp_fromi; const std::vector toi; const size_t nverts; const std::vector vx; const std::vector vy; const std::shared_ptr g; RcppParallel::RMatrix dout; // constructor OneDist ( const RcppParallel::RVector fromi, const std::vector toi_in, const size_t nverts_in, const std::vector vx_in, const std::vector vy_in, const std::shared_ptr g_in, RcppParallel::RMatrix dout_in) : dp_fromi (fromi), toi (toi_in), nverts (nverts_in), vx (vx_in), vy (vy_in), g (g_in), dout (dout_in) { } // Parallel function operator void operator() (std::size_t begin, std::size_t end) { const std::string heap_type = "BHeap"; std::shared_ptr pathfinder = std::make_shared (nverts, *run_sp::getHeapImpl (heap_type), g); std::vector w (nverts); std::vector d (nverts); std::vector prev (nverts); std::vector heuristic (nverts, 0.0); for (std::size_t i = begin; i < end; i++) { size_t from_i = static_cast (dp_fromi [i]); for (size_t j = 0; j < nverts; j++) { const double dx = vx [j] - vx [from_i], dy = vy [j] - vy [from_i]; heuristic [j] = sqrt (dx * dx + dy * dy); } pathfinder->AStar (d, w, prev, heuristic, from_i, toi); for (size_t j = 0; j < toi.size (); j++) { if (w [toi [j]] < INFINITE_DOUBLE) { dout (i, j) = d [toi [j]]; } } } } }; struct OneWeightedDist : public RcppParallel::Worker { RcppParallel::RVector dp_fromi; const std::vector toi; const std::vector wti; const size_t nverts; const double k; const double dlim; const std::vector vx; const std::vector vy; const std::shared_ptr g; RcppParallel::RMatrix dout; // constructor OneWeightedDist ( const RcppParallel::RVector fromi, const std::vector toi_in, const std::vector wti_in, const size_t nverts_in, const double k_in, const double dlim_in, const std::vector vx_in, const std::vector vy_in, const std::shared_ptr g_in, RcppParallel::RMatrix dout_in) : dp_fromi (fromi), toi (toi_in), wti (wti_in), nverts (nverts_in), k (k_in), dlim (dlim_in), vx (vx_in), vy (vy_in), g (g_in), dout (dout_in) { } // Parallel function operator void operator() (std::size_t begin, std::size_t end) { const std::string heap_type = "BHeap"; std::shared_ptr pathfinder = std::make_shared (nverts, *run_sp::getHeapImpl (heap_type), g); std::vector w (nverts); std::vector d (nverts); std::vector prev (nverts); std::vector heuristic (nverts, 0.0); for (std::size_t i = begin; i < end; i++) { size_t from_i = static_cast (dp_fromi [i]); for (size_t j = 0; j < nverts; j++) { const double dx = vx [j] - vx [from_i], dy = vy [j] - vy [from_i]; heuristic [j] = sqrt (dx * dx + dy * dy); } pathfinder->DijkstraLimit (d, w, prev, from_i, dlim); for (size_t j = 0; j < toi.size (); j++) { if (d [toi [j]] < INFINITE_DOUBLE) { dout (i, 0) += exp (-d [toi [j]] / k); dout (i, 1) += dout (i, 0) * wti [j]; } } } } }; struct OneDistNTargets : public RcppParallel::Worker { RcppParallel::RVector dp_fromi; const std::vector toi; const size_t nverts; const size_t n_targets; const std::vector vx; const std::vector vy; const std::shared_ptr g; RcppParallel::RMatrix dout; // constructor OneDistNTargets ( const RcppParallel::RVector fromi, const std::vector toi_in, const size_t nverts_in, const size_t n_targets_in, const std::vector vx_in, const std::vector vy_in, const std::shared_ptr g_in, RcppParallel::RMatrix dout_in) : dp_fromi (fromi), toi (toi_in), nverts (nverts_in), n_targets (n_targets_in), vx (vx_in), vy (vy_in), g (g_in), dout (dout_in) { } // Parallel function operator void operator() (std::size_t begin, std::size_t end) { const std::string heap_type = "BHeap"; std::shared_ptr pathfinder = std::make_shared (nverts, *run_sp::getHeapImpl (heap_type), g); std::vector w (nverts); std::vector d (nverts); std::vector prev (nverts); std::vector heuristic (nverts, 0.0); for (std::size_t i = begin; i < end; i++) { std::fill (d.begin (), d.end (), INFINITE_DOUBLE); std::fill (w.begin (), w.end (), INFINITE_DOUBLE); std::fill (heuristic.begin (), heuristic.end (), 0.0); std::fill (prev.begin (), prev.end (), INFINITE_INT); size_t from_i = static_cast (dp_fromi [i]); for (size_t j = 0; j < nverts; j++) { const double dx = vx [j] - vx [from_i], dy = vy [j] - vy [from_i]; heuristic [j] = sqrt (dx * dx + dy * dy); } pathfinder->DijkstraNTargets (d, w, prev, from_i, toi, n_targets); // The "NTargets" scanner only accumulates distances up to the // closest "n_targets" values, but still includes the full "toi" // values, most of which are NA. The loop to find the closest ones // therefore has to examine the whole "toi" vector and use an index. size_t count = 0; for (size_t j = 0; j < toi.size (); j++) { if (d [toi [j]] < INFINITE_DOUBLE) { dout (i, count) = d [toi [j]]; dout (i, n_targets + count) = static_cast (toi [j]); count++; } if (count >= n_targets) { break; } } } } }; struct SaveOneDist : public RcppParallel::Worker { RcppParallel::RVector dp_fromi; const std::vector from_names; const std::vector toi; const size_t nverts; const std::vector vx; const std::vector vy; const std::string path; const std::shared_ptr g; // constructor SaveOneDist ( const RcppParallel::RVector fromi, const std::vector from_names_in, const std::vector toi_in, const size_t nverts_in, const std::vector vx_in, const std::vector vy_in, const std::string path_in, const std::shared_ptr g_in) : dp_fromi (fromi), from_names (from_names_in), toi (toi_in), nverts (nverts_in), vx (vx_in), vy (vy_in), path (path_in), g (g_in) { } // Parallel function operator void operator() (std::size_t begin, std::size_t end) { const std::string heap_type = "BHeap"; std::shared_ptr pathfinder = std::make_shared (nverts, *run_sp::getHeapImpl (heap_type), g); std::vector w (nverts); std::vector d (nverts); std::vector prev (nverts); std::vector heuristic (nverts, 0.0); for (std::size_t i = begin; i < end; i++) { size_t from_i = static_cast (dp_fromi [i]); for (size_t j = 0; j < nverts; j++) { const double dx = vx [j] - vx [from_i], dy = vy [j] - vy [from_i]; heuristic [j] = sqrt (dx * dx + dy * dy); } pathfinder->AStar (d, w, prev, heuristic, from_i, toi); const std::string fname = path + "m4ra_from_" + from_names [i]; std::ofstream out_file; out_file.open (fname.c_str (), std::ofstream::out); for (size_t j = 0; j < toi.size (); j++) { double dtemp = -1.0; if (w [toi [j]] < INFINITE_DOUBLE) { dtemp = d [toi [j]]; } out_file << std::setprecision (10) << dtemp << std::endl; } out_file.close (); } } }; size_t run_sp::make_vert_map (const Rcpp::DataFrame &vert_map_in, const std::vector &vert_map_id, const std::vector &vert_map_n, std::map &vert_map) { for (size_t i = 0; i < static_cast (vert_map_in.nrow ()); ++i) { vert_map.emplace (vert_map_id [i], vert_map_n [i]); } size_t nverts = static_cast (vert_map.size ()); return (nverts); } //' rcpp_weighted_dists //' //' @noRd // [[Rcpp::export]] Rcpp::NumericMatrix rcpp_weighted_dists (const Rcpp::DataFrame graph, const Rcpp::DataFrame vert_map_in, Rcpp::IntegerVector fromi, Rcpp::IntegerVector toi_in, Rcpp::NumericVector weights, const double dlim, const double k) { std::vector toi = Rcpp::as > ( toi_in); size_t nfrom = static_cast (fromi.size ()); const std::vector from = graph [".vx0"]; const std::vector to = graph [".vx1"]; const std::vector dist = graph ["d"]; const std::vector wt = graph ["d_weighted"]; const size_t nedges = static_cast (graph.nrow ()); std::map vert_map; std::vector vert_map_id = vert_map_in ["vert"]; std::vector vert_map_n = vert_map_in ["id"]; const size_t nverts = run_sp::make_vert_map (vert_map_in, vert_map_id, vert_map_n, vert_map); std::vector vx (nverts), vy (nverts), wts; vx = Rcpp::as > (vert_map_in ["x"]); vy = Rcpp::as > (vert_map_in ["y"]); wts = Rcpp::as > (weights); std::shared_ptr g = std::make_shared (nverts); inst_graph (g, nedges, vert_map, from, to, dist, wt); Rcpp::NumericMatrix dout (static_cast (nfrom), 2); // Create parallel worker OneWeightedDist one_dist (RcppParallel::RVector (fromi), toi, wts, nverts, k, dlim, vx, vy, g, RcppParallel::RMatrix (dout)); size_t chunk_size = run_sp::get_chunk_size (nfrom); RcppParallel::parallelFor (0, nfrom, one_dist, chunk_size); return (dout); } //' rcpp_get_sp_dists_par //' //' @noRd // [[Rcpp::export]] Rcpp::NumericMatrix rcpp_get_sp_dists_par (const Rcpp::DataFrame graph, const Rcpp::DataFrame vert_map_in, Rcpp::IntegerVector fromi, Rcpp::IntegerVector toi_in) { std::vector toi = Rcpp::as > ( toi_in); size_t nfrom = static_cast (fromi.size ()); size_t nto = static_cast (toi.size ()); const std::vector from = graph [".vx0"]; const std::vector to = graph [".vx1"]; const std::vector dist = graph ["d"]; const std::vector wt = graph ["d_weighted"]; const size_t nedges = static_cast (graph.nrow ()); std::map vert_map; std::vector vert_map_id = vert_map_in ["vert"]; std::vector vert_map_n = vert_map_in ["id"]; const size_t nverts = run_sp::make_vert_map (vert_map_in, vert_map_id, vert_map_n, vert_map); std::vector vx (nverts), vy (nverts); vx = Rcpp::as > (vert_map_in ["x"]); vy = Rcpp::as > (vert_map_in ["y"]); std::shared_ptr g = std::make_shared (nverts); inst_graph (g, nedges, vert_map, from, to, dist, wt); Rcpp::NumericVector na_vec = Rcpp::NumericVector (nfrom * nto, Rcpp::NumericVector::get_na ()); Rcpp::NumericMatrix dout (static_cast (nfrom), static_cast (nto), na_vec.begin ()); // Create parallel worker OneDist one_dist (RcppParallel::RVector (fromi), toi, nverts, vx, vy, g, RcppParallel::RMatrix (dout)); size_t chunk_size = run_sp::get_chunk_size (nfrom); RcppParallel::parallelFor (0, nfrom, one_dist, chunk_size); return (dout); } //' rcpp_dists_to_n_targets //' //' @noRd // [[Rcpp::export]] Rcpp::NumericMatrix rcpp_dists_to_n_targets (const Rcpp::DataFrame graph, const Rcpp::DataFrame vert_map_in, Rcpp::IntegerVector fromi, Rcpp::IntegerVector toi_in, const int n_targets) { std::vector toi = Rcpp::as > ( toi_in); size_t nfrom = static_cast (fromi.size ()); const std::vector from = graph [".vx0"]; const std::vector to = graph [".vx1"]; const std::vector dist = graph ["d"]; const std::vector wt = graph ["d_weighted"]; const size_t nedges = static_cast (graph.nrow ()); std::map vert_map; std::vector vert_map_id = vert_map_in ["vert"]; std::vector vert_map_n = vert_map_in ["id"]; const size_t nverts = run_sp::make_vert_map (vert_map_in, vert_map_id, vert_map_n, vert_map); std::vector vx (nverts), vy (nverts); vx = Rcpp::as > (vert_map_in ["x"]); vy = Rcpp::as > (vert_map_in ["y"]); std::shared_ptr g = std::make_shared (nverts); inst_graph (g, nedges, vert_map, from, to, dist, wt); Rcpp::NumericVector na_vec = Rcpp::NumericVector (nfrom * 2 * static_cast (n_targets), Rcpp::NumericVector::get_na ()); Rcpp::NumericMatrix dout (static_cast (nfrom), static_cast (2 * n_targets), na_vec.begin ()); // Create parallel worker OneDistNTargets one_dist_n_targets (RcppParallel::RVector (fromi), toi, nverts, static_cast (n_targets), vx, vy, g, RcppParallel::RMatrix (dout)); const size_t chunk_size = run_sp::get_chunk_size (nfrom); RcppParallel::parallelFor (0, nfrom, one_dist_n_targets, chunk_size); return (dout); } //' rcpp_save_sp_dists_par //' //' This function doesn't return anything useful. The R function which calls it //' ultimately returns the paths to all files created here, but that is not //' really possible with Rcpp, because of difficulties getting CharacterVectors //' to play nicely with RcppParallel::RVector. The file name matching is easily //' done on the R side anyway. //' //' @noRd // [[Rcpp::export]] bool rcpp_save_sp_dists_par (const Rcpp::DataFrame graph, const Rcpp::DataFrame vert_map_in, Rcpp::IntegerVector from_index, Rcpp::CharacterVector from_names_in, Rcpp::IntegerVector toi_in, const std::string path) { std::vector toi = Rcpp::as > ( toi_in); std::vector from_names = Rcpp::as > (from_names_in); size_t nfrom = static_cast (from_index.size ()); const std::vector from = graph [".vx0"]; const std::vector to = graph [".vx1"]; const std::vector dist = graph ["d"]; const std::vector wt = graph ["d_weighted"]; const size_t nedges = static_cast (graph.nrow ()); std::map vert_map; std::vector vert_map_id = vert_map_in ["vert"]; std::vector vert_map_n = vert_map_in ["id"]; const size_t nverts = run_sp::make_vert_map (vert_map_in, vert_map_id, vert_map_n, vert_map); std::vector vx (nverts), vy (nverts); vx = Rcpp::as > (vert_map_in ["x"]); vy = Rcpp::as > (vert_map_in ["y"]); std::shared_ptr g = std::make_shared (nverts); inst_graph (g, nedges, vert_map, from, to, dist, wt); // Create parallel worker SaveOneDist one_dist (RcppParallel::RVector (from_index), from_names, toi, nverts, vx, vy, path, g); size_t chunk_size = run_sp::get_chunk_size (nfrom); RcppParallel::parallelFor (0, nfrom, one_dist, chunk_size); return (true); }