Distances on Directed Graphs in R
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}