Distances on Directed Graphs in R
at main 330 lines 9.6 kB view raw
1#include "pathfinders.h" 2#include "heaps/heap.h" 3 4#include <algorithm> // std::fill 5 6// Modified from code by Shane Saunders 7 8// @param n Number of vertices in graph 9// @param heapD The type of heap used 10// @param g A DGraph object 11// @param twoheap If `TRUE`, uses a bi-directional search. 12PF::PathFinder::PathFinder(size_t n, 13 const HeapDesc& heapD, 14 std::shared_ptr<const DGraph> g) 15{ 16 m_heap = heapD.newInstance (n); 17 m_closed = new bool [n]; 18 m_open = new bool [n]; 19 init (g); 20} 21 22PF::PathFinder::~PathFinder() { 23 delete [] m_open; 24 delete [] m_closed; 25 delete m_heap; 26} 27 28void PF::PathFinder::init(std::shared_ptr<const DGraph> g) { 29 m_graph = g; 30} 31 32void PF::PathFinder::init_arrays ( 33 std::vector <double>& d, 34 std::vector <double>& w, 35 std::vector <long int>& prev, 36 bool *m_open_vec, 37 bool *m_closed_vec, 38 const size_t v, 39 const size_t n) 40{ 41 std::fill (w.begin (), w.end (), INFINITE_DOUBLE); 42 std::fill (d.begin (), d.end (), INFINITE_DOUBLE); 43 std::fill (prev.begin (), prev.end (), INFINITE_INT); 44 w [v] = 0.0; 45 d [v] = 0.0; 46 prev [v] = -1; 47 48 std::fill (m_open_vec, m_open_vec + n, false); 49 std::fill (m_closed_vec, m_closed_vec + n, false); 50 m_open_vec [v] = true; 51} 52 53void PF::PathFinder::scan_edges (const DGraphEdge *edge, 54 std::vector<double>& d, 55 std::vector<double>& w, 56 std::vector<long int>& prev, 57 bool *m_open_vec, 58 const bool *m_closed_vec, 59 const size_t &v0) 60{ 61 while (edge) { 62 size_t et = edge->target; 63 if (!m_closed_vec [et]) 64 { 65 double wt = w [v0] + edge->wt; 66 if (wt < w [et]) { 67 d [et] = d [v0] + edge->dist; 68 w [et] = wt; 69 prev [et] = static_cast <int> (v0); 70 71 if (m_open_vec [et]) { 72 m_heap->decreaseKey(et, wt); 73 } 74 else { 75 m_heap->insert (et, wt); 76 m_open_vec [et] = true; 77 } 78 } else 79 m_closed [et] = true; 80 } 81 edge = edge->nextOut; 82 } 83} 84 85void PF::PathFinder::scan_edges_heur (const DGraphEdge *edge, 86 std::vector<double>& d, 87 std::vector<double>& w, 88 std::vector<long int>& prev, 89 bool *m_open_vec, 90 const bool *m_closed_vec, 91 const size_t &v0, 92 const std::vector<double> &heur) // heuristic for A* 93{ 94 while (edge) { 95 size_t et = edge->target; 96 if (!m_closed_vec [et]) 97 { 98 double wt = w [v0] + edge->wt; 99 if (wt < w [et]) { 100 d [et] = d [v0] + edge->dist; 101 w [et] = wt; 102 prev [et] = static_cast <int> (v0); 103 104 if (m_open_vec [et]) { 105 m_heap->decreaseKey(et, wt + heur [et] - heur [v0]); 106 } 107 else { 108 m_heap->insert (et, wt + heur [et] - heur [v0]); 109 m_open_vec [et] = true; 110 } 111 } else 112 m_closed [et] = true; 113 } 114 edge = edge->nextOut; 115 } 116} 117 118/********************************************************************** 119 ************************ PATH ALGORITHMS ************************* 120 **********************************************************************/ 121 122void PF::PathFinder::Dijkstra ( 123 std::vector<double>& d, 124 std::vector<double>& w, 125 std::vector<long int>& prev, 126 const size_t v0, 127 const std::vector <size_t> &to_index) 128{ 129 const DGraphEdge *edge; 130 131 const size_t n = m_graph->nVertices(); 132 const std::vector<DGraphVertex>& vertices = m_graph->vertices(); 133 134 PF::PathFinder::init_arrays (d, w, prev, m_open, m_closed, v0, n); 135 m_heap->insert (v0, 0.0); 136 137 size_t n_reached = 0; 138 const size_t n_targets = to_index.size (); 139 bool *is_target = new bool [n]; 140 std::fill (is_target, is_target + n, false); 141 for (auto t: to_index) 142 is_target [t] = true; 143 144 while (m_heap->nItems() > 0) { 145 size_t v = m_heap->deleteMin(); 146 147 m_closed [v] = true; 148 m_open [v] = false; 149 150 edge = vertices [v].outHead; 151 scan_edges (edge, d, w, prev, m_open, m_closed, v); 152 153 if (is_target [v]) 154 n_reached++; 155 if (n_reached == n_targets) 156 break; 157 } // end while nItems > 0 158 delete [] is_target; 159} 160 161// Modified Dijkstra that stops as soon as first target is reached. Used to find 162// minimal distance to nearest one of set of possible targets. 163void PF::PathFinder::DijkstraNearest ( 164 std::vector<double>& d, 165 std::vector<double>& w, 166 std::vector<long int>& prev, 167 const size_t v0, 168 const std::vector <size_t> &to_index) 169{ 170 const DGraphEdge *edge; 171 172 const size_t n = m_graph->nVertices(); 173 const std::vector<DGraphVertex>& vertices = m_graph->vertices(); 174 175 PF::PathFinder::init_arrays (d, w, prev, m_open, m_closed, v0, n); 176 m_heap->insert (v0, 0.0); 177 178 bool *is_target = new bool [n]; 179 std::fill (is_target, is_target + n, false); 180 for (auto t: to_index) 181 is_target [t] = true; 182 183 while (m_heap->nItems() > 0) { 184 size_t v = m_heap->deleteMin(); 185 186 m_closed [v] = true; 187 m_open [v] = false; 188 189 edge = vertices [v].outHead; 190 scan_edges (edge, d, w, prev, m_open, m_closed, v); 191 192 if (is_target [v]) 193 break; 194 } // end while nItems > 0 195 delete [] is_target; 196} 197 198// Modified pathfinder only out to specified distance limit. Done as separate 199// routine to avoid costs of the `if` clause in general non-limited case. 200void PF::PathFinder::DijkstraLimit ( 201 std::vector<double>& d, 202 std::vector<double>& w, 203 std::vector<long int>& prev, 204 const size_t v0, 205 const double &dlim) 206{ 207 const DGraphEdge *edge; 208 209 const size_t n = m_graph->nVertices(); 210 const std::vector<DGraphVertex>& vertices = m_graph->vertices(); 211 212 PF::PathFinder::init_arrays (d, w, prev, m_open, m_closed, v0, n); 213 m_heap->insert (v0, 0.0); 214 215 while (m_heap->nItems() > 0) { 216 size_t v = m_heap->deleteMin(); 217 218 m_closed [v] = true; 219 m_open [v] = false; 220 221 // explore the OUT set of v only if distances are < threshold 222 bool explore = false; 223 edge = vertices [v].outHead; 224 while (edge) { 225 if ((d [v] + edge->dist) <= dlim) 226 { 227 explore = true; 228 break; 229 } 230 edge = edge->nextOut; 231 } 232 233 if (explore) 234 { 235 edge = vertices [v].outHead; 236 scan_edges (edge, d, w, prev, m_open, m_closed, v); 237 } 238 } // end while nItems > 0 239} 240 241void PF::PathFinder::AStar (std::vector<double>& d, 242 std::vector<double>& w, 243 std::vector<long int>& prev, 244 const std::vector<double>& heur, 245 const size_t v0, 246 const std::vector <size_t> &to_index) 247{ 248 const DGraphEdge *edge; 249 250 const size_t n = m_graph->nVertices(); 251 const std::vector<DGraphVertex>& vertices = m_graph->vertices(); 252 253 PF::PathFinder::init_arrays (d, w, prev, m_open, m_closed, v0, n); 254 m_heap->insert (v0, heur [v0]); 255 256 size_t n_reached = 0; 257 const size_t n_targets = to_index.size (); 258 bool *is_target = new bool [n]; 259 std::fill (is_target, is_target + n, false); 260 for (auto t: to_index) 261 is_target [t] = true; 262 263 while (m_heap->nItems() > 0) { 264 size_t v = m_heap->deleteMin(); 265 266 m_closed [v] = true; 267 m_open [v] = false; 268 269 edge = vertices [v].outHead; 270 scan_edges_heur (edge, d, w, prev, m_open, m_closed, v, heur); 271 272 if (is_target [v]) 273 n_reached++; 274 if (n_reached == n_targets) 275 break; 276 } // end while m_heap->nItems 277 delete [] is_target; 278} 279 280// Dijkstra with C++ std::set, modified from the above to use EdgeSet edge_set 281// instead of m_heap, and so all routines hard-coded here. 282void PF::PathFinder::Dijkstra_set (std::vector<double>& d, 283 std::vector<double>& w, 284 std::vector<long int>& prev, 285 size_t v0) 286{ 287 const DGraphEdge *edge; 288 289 const size_t n = m_graph->nVertices(); 290 const std::vector<DGraphVertex>& vertices = m_graph->vertices(); 291 292 PF::PathFinder::init_arrays (d, w, prev, m_open, m_closed, v0, n); 293 m_heap->insert(v0, 0.0); 294 295 edge_set.insert (DijkstraEdge (0.0, v0)); 296 297 while (edge_set.size () > 0) { 298 EdgeSet::iterator ei = edge_set.begin(); 299 size_t v = ei->geti(); 300 edge_set.erase (ei); 301 302 m_closed [v] = true; 303 m_open [v] = false; 304 305 edge = vertices [v].outHead; 306 while (edge) { 307 size_t et = edge->target; 308 309 if (!m_closed [et]) { 310 double wt = w [v] + edge->wt; 311 if (wt < w [et]) { 312 d [et] = d [v] + edge->dist; 313 double wold = w [et]; 314 w [et] = wt; 315 prev [et] = static_cast <int> (v); 316 if (m_open [et]) 317 { 318 DijkstraEdge de (wold, et); 319 if (edge_set.find (de) != edge_set.end ()) 320 edge_set.erase (edge_set.find (de)); 321 } else 322 m_open [et] = true; 323 edge_set.insert (DijkstraEdge (w [et], et)); 324 } 325 } 326 327 edge = edge->nextOut; 328 } // end while edge 329 } // end while edge_set.size 330}