/src/igraph/src/paths/unweighted.c
Line | Count | Source |
1 | | /* |
2 | | igraph library. |
3 | | Copyright (C) 2005-2025 The igraph development team <igraph@igraph.org> |
4 | | |
5 | | This program is free software; you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation; either version 2 of the License, or |
8 | | (at your option) any later version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this program. If not, see <https://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include "igraph_paths.h" |
20 | | |
21 | | #include "igraph_adjlist.h" |
22 | | #include "igraph_dqueue.h" |
23 | | #include "igraph_interface.h" |
24 | | #include "igraph_memory.h" |
25 | | |
26 | | #include "core/interruption.h" |
27 | | #include "paths/paths_internal.h" |
28 | | |
29 | | /** |
30 | | * \ingroup structural |
31 | | * \function igraph_distances_cutoff |
32 | | * \brief Length of the shortest paths between vertices, with cutoff. |
33 | | * |
34 | | * This function is similar to \ref igraph_distances(), but |
35 | | * paths longer than \p cutoff will not be considered. |
36 | | * |
37 | | * \param graph The graph object. |
38 | | * \param weights Optional edge weights. If \c NULL, the graph is considered |
39 | | * unweighted, i.e. all edge weights are equal to 1. |
40 | | * \param res The result of the calculation, a matrix. A pointer to an |
41 | | * initialized matrix, to be more precise. The matrix will be |
42 | | * resized if needed. It will have the same |
43 | | * number of rows as the length of the \p from |
44 | | * argument, and its number of columns is the number of |
45 | | * vertices in the \p to argument. One row of the matrix shows the |
46 | | * distances from/to a given vertex to the ones in \p to. |
47 | | * For the unreachable vertices \c IGRAPH_INFINITY is returned. |
48 | | * \param from The source vertices._d |
49 | | * \param to The target vertices. It is not allowed to include a |
50 | | * vertex twice or more. |
51 | | * \param mode The type of shortest paths to be used for the |
52 | | * calculation in directed graphs. Possible values: |
53 | | * \clist |
54 | | * \cli IGRAPH_OUT |
55 | | * the lengths of the outgoing paths are calculated. |
56 | | * \cli IGRAPH_IN |
57 | | * the lengths of the incoming paths are calculated. |
58 | | * \cli IGRAPH_ALL |
59 | | * the directed graph is considered as an undirected one for |
60 | | * the computation. |
61 | | * \endclist |
62 | | * \param cutoff The maximal length of paths that will be considered. |
63 | | * When the distance of two vertices is greater than this value, |
64 | | * it will be returned as \c IGRAPH_INFINITY. Negative cutoffs and |
65 | | * \ref IGRAPH_UNLIMITED are treated as infinity. |
66 | | * \return Error code: |
67 | | * \clist |
68 | | * \cli IGRAPH_ENOMEM |
69 | | * not enough memory for temporary |
70 | | * data. |
71 | | * \cli IGRAPH_EINVVID |
72 | | * invalid vertex ID passed. |
73 | | * \cli IGRAPH_EINVMODE |
74 | | * invalid mode argument. |
75 | | * \endclist |
76 | | * |
77 | | * Time complexity: O(s |E| + |V|), where s is the number of source vertices to use, |
78 | | * and |V| and |E| are the number of vertices and edges in the graph. |
79 | | * |
80 | | * \sa \ref igraph_distances_dijkstra_cutoff() for the weighted version with non-negative |
81 | | * weights. |
82 | | * |
83 | | * \example examples/simple/distances.c |
84 | | */ |
85 | | igraph_error_t igraph_distances_cutoff( |
86 | | const igraph_t *graph, |
87 | | const igraph_vector_t *weights, |
88 | | igraph_matrix_t *res, |
89 | | const igraph_vs_t from, const igraph_vs_t to, |
90 | | igraph_neimode_t mode, |
91 | 0 | igraph_real_t cutoff) { |
92 | |
|
93 | 0 | if (weights == NULL) { |
94 | | /* Unweighted distances */ |
95 | 0 | return igraph_i_distances_unweighted_cutoff(graph, res, from, to, mode, cutoff); |
96 | 0 | } else { |
97 | | /* Dijkstra's algorithm; will return an error if there are negative weights */ |
98 | 0 | return igraph_distances_dijkstra_cutoff(graph, res, from, to, weights, mode, cutoff); |
99 | 0 | } |
100 | 0 | } |
101 | | |
102 | | igraph_error_t igraph_i_distances_unweighted_cutoff( |
103 | | const igraph_t *graph, |
104 | | igraph_matrix_t *res, |
105 | | const igraph_vs_t from, const igraph_vs_t to, |
106 | | igraph_neimode_t mode, |
107 | 0 | igraph_real_t cutoff) { |
108 | |
|
109 | 0 | igraph_int_t no_of_nodes = igraph_vcount(graph); |
110 | 0 | igraph_int_t no_of_from, no_of_to; |
111 | 0 | igraph_int_t *already_counted; |
112 | 0 | igraph_adjlist_t adjlist; |
113 | 0 | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
114 | 0 | igraph_vector_int_t *neis; |
115 | 0 | igraph_bool_t all_to; |
116 | |
|
117 | 0 | igraph_int_t i, j; |
118 | 0 | igraph_vit_t fromvit, tovit; |
119 | 0 | igraph_vector_int_t indexv; |
120 | |
|
121 | 0 | if (mode != IGRAPH_OUT && mode != IGRAPH_IN && |
122 | 0 | mode != IGRAPH_ALL) { |
123 | 0 | IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE); |
124 | 0 | } |
125 | | |
126 | 0 | IGRAPH_CHECK(igraph_vit_create(graph, from, &fromvit)); |
127 | 0 | IGRAPH_FINALLY(igraph_vit_destroy, &fromvit); |
128 | 0 | no_of_from = IGRAPH_VIT_SIZE(fromvit); |
129 | |
|
130 | 0 | IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); |
131 | 0 | IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); |
132 | |
|
133 | 0 | already_counted = IGRAPH_CALLOC(no_of_nodes, igraph_int_t); |
134 | 0 | IGRAPH_CHECK_OOM(already_counted, "Insufficient memory for graph distance calculation."); |
135 | 0 | IGRAPH_FINALLY(igraph_free, already_counted); |
136 | |
|
137 | 0 | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
138 | | |
139 | 0 | all_to = igraph_vs_is_all(&to); |
140 | 0 | if (all_to) { |
141 | 0 | no_of_to = no_of_nodes; |
142 | 0 | } else { |
143 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&indexv, no_of_nodes); |
144 | 0 | IGRAPH_CHECK(igraph_vit_create(graph, to, &tovit)); |
145 | 0 | IGRAPH_FINALLY(igraph_vit_destroy, &tovit); |
146 | 0 | no_of_to = IGRAPH_VIT_SIZE(tovit); |
147 | 0 | for (i = 0; !IGRAPH_VIT_END(tovit); IGRAPH_VIT_NEXT(tovit)) { |
148 | 0 | igraph_int_t v = IGRAPH_VIT_GET(tovit); |
149 | 0 | if (VECTOR(indexv)[v]) { |
150 | 0 | IGRAPH_ERROR("Target vertex list must not have any duplicates.", |
151 | 0 | IGRAPH_EINVAL); |
152 | 0 | } |
153 | 0 | VECTOR(indexv)[v] = ++i; |
154 | 0 | } |
155 | 0 | } |
156 | | |
157 | 0 | IGRAPH_CHECK(igraph_matrix_resize(res, no_of_from, no_of_to)); |
158 | 0 | igraph_matrix_fill(res, IGRAPH_INFINITY); |
159 | |
|
160 | 0 | for (IGRAPH_VIT_RESET(fromvit), i = 0; |
161 | 0 | !IGRAPH_VIT_END(fromvit); |
162 | 0 | IGRAPH_VIT_NEXT(fromvit), i++) { |
163 | 0 | igraph_int_t reached = 0; |
164 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, IGRAPH_VIT_GET(fromvit))); |
165 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); |
166 | 0 | already_counted[ IGRAPH_VIT_GET(fromvit) ] = i + 1; |
167 | |
|
168 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
169 | | |
170 | 0 | while (!igraph_dqueue_int_empty(&q)) { |
171 | 0 | igraph_int_t act = igraph_dqueue_int_pop(&q); |
172 | 0 | igraph_int_t actdist = igraph_dqueue_int_pop(&q); |
173 | |
|
174 | 0 | if (cutoff >= 0 && actdist > cutoff) { |
175 | 0 | continue; |
176 | 0 | } |
177 | | |
178 | 0 | if (all_to) { |
179 | 0 | MATRIX(*res, i, act) = actdist; |
180 | 0 | } else { |
181 | 0 | if (VECTOR(indexv)[act]) { |
182 | 0 | MATRIX(*res, i, VECTOR(indexv)[act] - 1) = actdist; |
183 | 0 | reached++; |
184 | 0 | if (reached == no_of_to) { |
185 | 0 | igraph_dqueue_int_clear(&q); |
186 | 0 | break; |
187 | 0 | } |
188 | 0 | } |
189 | 0 | } |
190 | | |
191 | 0 | neis = igraph_adjlist_get(&adjlist, act); |
192 | 0 | igraph_int_t nei_count = igraph_vector_int_size(neis); |
193 | 0 | for (j = 0; j < nei_count; j++) { |
194 | 0 | igraph_int_t neighbor = VECTOR(*neis)[j]; |
195 | 0 | if (already_counted[neighbor] == i + 1) { |
196 | 0 | continue; |
197 | 0 | } |
198 | 0 | already_counted[neighbor] = i + 1; |
199 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
200 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); |
201 | 0 | } |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | /* Clean */ |
206 | 0 | if (!all_to) { |
207 | 0 | igraph_vit_destroy(&tovit); |
208 | 0 | igraph_vector_int_destroy(&indexv); |
209 | 0 | IGRAPH_FINALLY_CLEAN(2); |
210 | 0 | } |
211 | |
|
212 | 0 | IGRAPH_FREE(already_counted); |
213 | 0 | igraph_dqueue_int_destroy(&q); |
214 | 0 | igraph_vit_destroy(&fromvit); |
215 | 0 | igraph_adjlist_destroy(&adjlist); |
216 | 0 | IGRAPH_FINALLY_CLEAN(4); |
217 | |
|
218 | 0 | return IGRAPH_SUCCESS; |
219 | 0 | } |
220 | | |
221 | | /** |
222 | | * \ingroup structural |
223 | | * \function igraph_distances |
224 | | * \brief Length of the shortest paths between vertices. |
225 | | * |
226 | | * \param graph The graph object. |
227 | | * \param weights Optional edge weights. If \c NULL, the graph is considered |
228 | | * unweighted, i.e. all edge weights are 1. |
229 | | * \param res The result of the calculation, a matrix. A pointer to an |
230 | | * initialized matrix, to be more precise. The matrix will be |
231 | | * resized if needed. It will have the same |
232 | | * number of rows as the length of the \p from |
233 | | * argument, and its number of columns is the number of |
234 | | * vertices in the \p to argument. One row of the matrix shows the |
235 | | * distances from/to a given vertex to the ones in \p to. |
236 | | * For the unreachable vertices \c IGRAPH_INFINITY is returned. |
237 | | * \param from The source vertices. |
238 | | * \param to The target vertices. It is not allowed to include a |
239 | | * vertex twice or more. |
240 | | * \param mode The type of shortest paths to be used for the |
241 | | * calculation in directed graphs. Possible values: |
242 | | * \clist |
243 | | * \cli IGRAPH_OUT |
244 | | * the lengths of the outgoing paths are calculated. |
245 | | * \cli IGRAPH_IN |
246 | | * the lengths of the incoming paths are calculated. |
247 | | * \cli IGRAPH_ALL |
248 | | * the directed graph is considered as an undirected one for |
249 | | * the computation. |
250 | | * \endclist |
251 | | * \return Error code: |
252 | | * \clist |
253 | | * \cli IGRAPH_ENOMEM |
254 | | * not enough memory for temporary |
255 | | * data. |
256 | | * \cli IGRAPH_EINVVID |
257 | | * invalid vertex ID passed. |
258 | | * \cli IGRAPH_EINVMODE |
259 | | * invalid mode argument. |
260 | | * \endclist |
261 | | * |
262 | | * Time complexity: O(n(|V|+|E|)), |
263 | | * n is the number of vertices to calculate, |
264 | | * |V| and |E| are the number of vertices and edges in the graph. |
265 | | * |
266 | | * \sa \ref igraph_get_shortest_paths() to get the paths themselves, |
267 | | * \ref igraph_distances_dijkstra() for the weighted version with non-negative |
268 | | * weights, \ref igraph_distances_bellman_ford() if you also have negative |
269 | | * weights. |
270 | | * |
271 | | * \example examples/simple/distances.c |
272 | | */ |
273 | | igraph_error_t igraph_distances( |
274 | | const igraph_t *graph, |
275 | | const igraph_vector_t *weights, |
276 | | igraph_matrix_t *res, |
277 | | const igraph_vs_t from, const igraph_vs_t to, |
278 | 0 | igraph_neimode_t mode) { |
279 | |
|
280 | 0 | igraph_real_t vcount_real = igraph_vcount(graph); |
281 | 0 | igraph_real_t ecount_real = igraph_ecount(graph); |
282 | 0 | igraph_real_t ecount_threshold; |
283 | 0 | igraph_int_t from_size; |
284 | 0 | igraph_bool_t negative_weights = false; |
285 | |
|
286 | 0 | IGRAPH_CHECK(igraph_i_validate_distance_weights(graph, weights, &negative_weights)); |
287 | | |
288 | 0 | if (!igraph_is_directed(graph)) { |
289 | 0 | mode = IGRAPH_ALL; |
290 | 0 | } |
291 | | |
292 | | /* Edge count threshold for using Floyd-Warshall for all-to-all case. |
293 | | * Based on experiments, this is faster than Dijkstra for densities |
294 | | * above 0.1, provided that the graph has more than about 50 vertices. |
295 | | * See also https://github.com/igraph/igraph/issues/2822 */ |
296 | 0 | ecount_threshold = vcount_real * vcount_real * 0.1; |
297 | 0 | if (ecount_threshold < 250) ecount_threshold = 250; |
298 | |
|
299 | 0 | if (!weights) { |
300 | | /* Unweighted case */ |
301 | 0 | return igraph_i_distances_unweighted_cutoff(graph, res, from, to, mode, -1); |
302 | 0 | } else if (igraph_vs_is_all(&from) && ecount_real > ecount_threshold) { |
303 | | /* All-to-all distances with dense graph */ |
304 | 0 | return igraph_distances_floyd_warshall(graph, res, from, to, weights, mode, IGRAPH_FLOYD_WARSHALL_AUTOMATIC); |
305 | 0 | } else if (!negative_weights) { |
306 | | /* Non-negative weights: use Dijkstra's algorithm. */ |
307 | 0 | return igraph_i_distances_dijkstra_cutoff(graph, res, from, to, weights, mode, -1); |
308 | 0 | } else { |
309 | | /* Negative weights: use Bellman-Ford or Johnson's algorithm. |
310 | | * |
311 | | * In the undirected case we always use Bellman-Ford. Normally, a negative weight |
312 | | * undirected edge triggers an error as it is effectively a negative cycle. |
313 | | * However, with Bellman-Ford the negative edge might be avoided if it is |
314 | | * not reachable from the 'from' vertices. In cotrast, Johnson will always raise |
315 | | * an error. |
316 | | */ |
317 | 0 | if (mode != IGRAPH_ALL) { |
318 | 0 | IGRAPH_CHECK(igraph_vs_size(graph, &from, &from_size)); |
319 | 0 | if (from_size > 100) { |
320 | 0 | return igraph_i_distances_johnson(graph, res, from, to, weights, mode); |
321 | 0 | } |
322 | 0 | } |
323 | 0 | return igraph_i_distances_bellman_ford(graph, res, from, to, weights, mode); |
324 | 0 | } |
325 | 0 | } |
326 | | |
327 | | /** |
328 | | * \ingroup structural |
329 | | * \function igraph_get_shortest_paths |
330 | | * \brief Shortest paths from a vertex. |
331 | | * |
332 | | * Finds unweighted shortest paths from a single source vertex to the specified |
333 | | * sets of target vertices. If there is more than one geodesic between two vertices, |
334 | | * this function gives only one of them. Use \ref igraph_get_all_shortest_paths() |
335 | | * to find \em all shortest paths. |
336 | | * |
337 | | * \param graph The graph object. |
338 | | * \param weights Optional edge weights. If \c NULL, the graph is considered |
339 | | * unweighted, i.e. all edge weights are equal to 1. |
340 | | * \param vertices The result, the IDs of the vertices along the paths. |
341 | | * This is a list of integer vectors where each element is an |
342 | | * \ref igraph_vector_int_t object. The list will be resized as needed. |
343 | | * Supply a null pointer here if you don't need these vectors. |
344 | | * \param edges The result, the IDs of the edges along the paths. |
345 | | * This is a list of integer vectors where each element is an |
346 | | * \ref igraph_vector_int_t object. The list will be resized as needed. |
347 | | * Supply a null pointer here if you don't need these vectors. |
348 | | * \param from The ID of the vertex from/to which the geodesics are |
349 | | * calculated. |
350 | | * \param to Vertex sequence with the IDs of the vertices to/from which the |
351 | | * shortest paths will be calculated. A vertex might be given multiple |
352 | | * times. |
353 | | * \param mode The type of shortest paths to be used for the |
354 | | * calculation in directed graphs. Possible values: |
355 | | * \clist |
356 | | * \cli IGRAPH_OUT |
357 | | * the outgoing paths are calculated. |
358 | | * \cli IGRAPH_IN |
359 | | * the incoming paths are calculated. |
360 | | * \cli IGRAPH_ALL |
361 | | * the directed graph is considered as an |
362 | | * undirected one for the computation. |
363 | | * \endclist |
364 | | * \param parents A pointer to an initialized igraph vector or \c NULL. |
365 | | * If not \c NULL, a vector containing the parent of each vertex in |
366 | | * the single source shortest path tree is returned here. The |
367 | | * parent of vertex \c i in the tree is the vertex from which vertex \c i |
368 | | * was reached. The parent of the start vertex (in the \p from |
369 | | * argument) is -1. If the parent is -2, it means |
370 | | * that the given vertex was not reached from the source during the |
371 | | * search. Note that the search terminates if all the vertices in |
372 | | * \p to are reached. |
373 | | * \param inbound_edges A pointer to an initialized igraph vector or \c NULL. |
374 | | * If not \c NULL, a vector containing the inbound edge of each vertex in |
375 | | * the single source shortest path tree is returned here. The |
376 | | * inbound edge of vertex \c i in the tree is the edge via which vertex \c i |
377 | | * was reached. The start vertex and vertices that were not reached |
378 | | * during the search will have -1 in the corresponding entry of the |
379 | | * vector. Note that the search terminates if all the vertices in |
380 | | * \p to are reached. |
381 | | * |
382 | | * \return Error code: |
383 | | * \clist |
384 | | * \cli IGRAPH_ENOMEM |
385 | | * not enough memory for temporary data. |
386 | | * \cli IGRAPH_EINVVID |
387 | | * \p from is invalid vertex ID |
388 | | * \cli IGRAPH_EINVMODE |
389 | | * invalid mode argument. |
390 | | * \endclist |
391 | | * |
392 | | * Time complexity: O(|V|+|E|), |
393 | | * |V| is the number of vertices, |
394 | | * |E| the number of edges in the |
395 | | * graph. |
396 | | * |
397 | | * \sa \ref igraph_distances() if you only need the path lengths but |
398 | | * not the paths themselves; \ref igraph_get_shortest_paths_dijkstra() |
399 | | * for the weighted version; \ref igraph_get_all_shortest_paths() to |
400 | | * return all shortest paths between (source, target) pairs. |
401 | | * |
402 | | * \example examples/simple/igraph_get_shortest_paths.c |
403 | | */ |
404 | | igraph_error_t igraph_get_shortest_paths( |
405 | | const igraph_t *graph, |
406 | | const igraph_vector_t *weights, |
407 | | igraph_vector_int_list_t *vertices, |
408 | | igraph_vector_int_list_t *edges, |
409 | | igraph_int_t from, const igraph_vs_t to, |
410 | | igraph_neimode_t mode, |
411 | | igraph_vector_int_t *parents, |
412 | 0 | igraph_vector_int_t *inbound_edges) { |
413 | |
|
414 | 0 | igraph_bool_t negative_weights; |
415 | 0 | IGRAPH_CHECK(igraph_i_validate_distance_weights(graph, weights, &negative_weights)); |
416 | | |
417 | 0 | if (weights == NULL) { |
418 | 0 | return igraph_i_get_shortest_paths_unweighted(graph, vertices, edges, from, to, mode, parents, inbound_edges); |
419 | 0 | } else if (!negative_weights) { |
420 | | /* Dijkstra's algorithm */ |
421 | 0 | return igraph_i_get_shortest_paths_dijkstra(graph, vertices, edges, from, to, weights, mode, parents, inbound_edges); |
422 | 0 | } else { |
423 | | /* Negative weights; will use Bellman-Ford algorithm */ |
424 | 0 | return igraph_i_get_shortest_paths_bellman_ford(graph, vertices, edges, from, to, weights, mode, parents, inbound_edges); |
425 | 0 | } |
426 | 0 | } |
427 | | |
428 | | igraph_error_t igraph_i_get_shortest_paths_unweighted( |
429 | | const igraph_t *graph, |
430 | | igraph_vector_int_list_t *vertices, |
431 | | igraph_vector_int_list_t *edges, |
432 | | igraph_int_t from, igraph_vs_t to, |
433 | | igraph_neimode_t mode, |
434 | | igraph_vector_int_t *parents, |
435 | 0 | igraph_vector_int_t *inbound_edges) { |
436 | | |
437 | | /* TODO: use inclist_t if to is long (longer than 1?) */ |
438 | |
|
439 | 0 | igraph_int_t no_of_nodes = igraph_vcount(graph); |
440 | 0 | igraph_int_t *parent_eids; |
441 | |
|
442 | 0 | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
443 | |
|
444 | 0 | igraph_int_t i, j, vsize; |
445 | 0 | igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; |
446 | |
|
447 | 0 | igraph_vit_t vit; |
448 | |
|
449 | 0 | igraph_int_t to_reach; |
450 | 0 | igraph_int_t reached = 0; |
451 | |
|
452 | 0 | if (from < 0 || from >= no_of_nodes) { |
453 | 0 | IGRAPH_ERROR("Index of source vertex is out of range.", IGRAPH_EINVVID); |
454 | 0 | } |
455 | 0 | if (mode != IGRAPH_OUT && mode != IGRAPH_IN && |
456 | 0 | mode != IGRAPH_ALL) { |
457 | 0 | IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE); |
458 | 0 | } |
459 | | |
460 | 0 | IGRAPH_CHECK(igraph_vit_create(graph, to, &vit)); |
461 | 0 | IGRAPH_FINALLY(igraph_vit_destroy, &vit); |
462 | |
|
463 | 0 | if (vertices) { |
464 | 0 | IGRAPH_CHECK(igraph_vector_int_list_resize(vertices, IGRAPH_VIT_SIZE(vit))); |
465 | 0 | } |
466 | 0 | if (edges) { |
467 | 0 | IGRAPH_CHECK(igraph_vector_int_list_resize(edges, IGRAPH_VIT_SIZE(vit))); |
468 | 0 | } |
469 | | |
470 | 0 | parent_eids = IGRAPH_CALLOC(no_of_nodes, igraph_int_t); |
471 | 0 | IGRAPH_CHECK_OOM(parent_eids, "Insufficient memory for shortest path calculation."); |
472 | 0 | IGRAPH_FINALLY(igraph_free, parent_eids); |
473 | |
|
474 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); |
475 | 0 | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
476 | | |
477 | | /* Mark the vertices we need to reach */ |
478 | 0 | to_reach = IGRAPH_VIT_SIZE(vit); |
479 | 0 | for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) { |
480 | 0 | if (parent_eids[ IGRAPH_VIT_GET(vit) ] == 0) { |
481 | 0 | parent_eids[ IGRAPH_VIT_GET(vit) ] = -1; |
482 | 0 | } else { |
483 | 0 | to_reach--; /* this node was given multiple times */ |
484 | 0 | } |
485 | 0 | } |
486 | | |
487 | | /* Meaning of parent_eids[i]: |
488 | | * |
489 | | * - If parent_eids[i] < 0, it means that vertex i has to be reached and has not |
490 | | * been reached yet. |
491 | | * |
492 | | * - If parent_eids[i] = 0, it means that vertex i does not have to be reached and |
493 | | * it has not been reached yet. |
494 | | * |
495 | | * - If parent_eids[i] = 1, it means that vertex i is the start vertex. |
496 | | * |
497 | | * - Otherwise, parent_eids[i] is the ID of the edge from which vertex i was |
498 | | * reached plus 2. |
499 | | */ |
500 | |
|
501 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, from + 1)); |
502 | 0 | if (parent_eids[ from ] < 0) { |
503 | 0 | reached++; |
504 | 0 | } |
505 | 0 | parent_eids[ from ] = 1; |
506 | |
|
507 | 0 | while (!igraph_dqueue_int_empty(&q) && reached < to_reach) { |
508 | 0 | igraph_int_t act = igraph_dqueue_int_pop(&q) - 1; |
509 | |
|
510 | 0 | IGRAPH_CHECK(igraph_incident(graph, &tmp, act, mode, IGRAPH_LOOPS)); |
511 | 0 | vsize = igraph_vector_int_size(&tmp); |
512 | 0 | for (j = 0; j < vsize; j++) { |
513 | 0 | igraph_int_t edge = VECTOR(tmp)[j]; |
514 | 0 | igraph_int_t neighbor = IGRAPH_OTHER(graph, edge, act); |
515 | 0 | if (parent_eids[neighbor] > 0) { |
516 | 0 | continue; |
517 | 0 | } else if (parent_eids[neighbor] < 0) { |
518 | 0 | reached++; |
519 | 0 | } |
520 | 0 | parent_eids[neighbor] = edge + 2; |
521 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor + 1)); |
522 | 0 | } |
523 | 0 | } |
524 | | |
525 | 0 | if (reached < to_reach) { |
526 | 0 | IGRAPH_WARNING("Couldn't reach some vertices"); |
527 | 0 | } |
528 | | |
529 | | /* Create `parents' if needed */ |
530 | 0 | if (parents) { |
531 | 0 | IGRAPH_CHECK(igraph_vector_int_resize(parents, no_of_nodes)); |
532 | | |
533 | 0 | for (i = 0; i < no_of_nodes; i++) { |
534 | 0 | if (parent_eids[i] <= 0) { |
535 | | /* i was not reached */ |
536 | 0 | VECTOR(*parents)[i] = -2; |
537 | 0 | } else if (parent_eids[i] == 1) { |
538 | | /* i is the start vertex */ |
539 | 0 | VECTOR(*parents)[i] = -1; |
540 | 0 | } else { |
541 | | /* i was reached via the edge with ID = parent_eids[i] - 2 */ |
542 | 0 | VECTOR(*parents)[i] = IGRAPH_OTHER(graph, parent_eids[i] - 2, i); |
543 | 0 | } |
544 | 0 | } |
545 | 0 | } |
546 | | |
547 | | /* Create `inbound_edges' if needed */ |
548 | 0 | if (inbound_edges) { |
549 | 0 | IGRAPH_CHECK(igraph_vector_int_resize(inbound_edges, no_of_nodes)); |
550 | | |
551 | 0 | for (i = 0; i < no_of_nodes; i++) { |
552 | 0 | if (parent_eids[i] <= 1) { |
553 | | /* i was not reached or i is the start vertex */ |
554 | 0 | VECTOR(*inbound_edges)[i] = -1; |
555 | 0 | } else { |
556 | | /* i was reached via the edge with ID = parent_eids[i] - 2 */ |
557 | 0 | VECTOR(*inbound_edges)[i] = parent_eids[i] - 2; |
558 | 0 | } |
559 | 0 | } |
560 | 0 | } |
561 | | |
562 | | /* Create `vertices' and `edges' if needed */ |
563 | 0 | if (vertices || edges) { |
564 | 0 | for (IGRAPH_VIT_RESET(vit), j = 0; |
565 | 0 | !IGRAPH_VIT_END(vit); |
566 | 0 | IGRAPH_VIT_NEXT(vit), j++) { |
567 | 0 | igraph_int_t node = IGRAPH_VIT_GET(vit); |
568 | 0 | igraph_vector_int_t *vvec = 0, *evec = 0; |
569 | 0 | if (vertices) { |
570 | 0 | vvec = igraph_vector_int_list_get_ptr(vertices, j); |
571 | 0 | igraph_vector_int_clear(vvec); |
572 | 0 | } |
573 | 0 | if (edges) { |
574 | 0 | evec = igraph_vector_int_list_get_ptr(edges, j); |
575 | 0 | igraph_vector_int_clear(evec); |
576 | 0 | } |
577 | |
|
578 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
579 | | |
580 | 0 | if (parent_eids[node] > 0) { |
581 | 0 | igraph_int_t act = node; |
582 | 0 | igraph_int_t size = 0; |
583 | 0 | igraph_int_t edge; |
584 | 0 | while (parent_eids[act] > 1) { |
585 | 0 | size++; |
586 | 0 | edge = parent_eids[act] - 2; |
587 | 0 | act = IGRAPH_OTHER(graph, edge, act); |
588 | 0 | } |
589 | 0 | if (vvec) { |
590 | 0 | IGRAPH_CHECK(igraph_vector_int_resize(vvec, size + 1)); |
591 | 0 | VECTOR(*vvec)[size] = node; |
592 | 0 | } |
593 | 0 | if (evec) { |
594 | 0 | IGRAPH_CHECK(igraph_vector_int_resize(evec, size)); |
595 | 0 | } |
596 | 0 | act = node; |
597 | 0 | while (parent_eids[act] > 1) { |
598 | 0 | size--; |
599 | 0 | edge = parent_eids[act] - 2; |
600 | 0 | act = IGRAPH_OTHER(graph, edge, act); |
601 | 0 | if (vvec) { |
602 | 0 | VECTOR(*vvec)[size] = act; |
603 | 0 | } |
604 | 0 | if (evec) { |
605 | 0 | VECTOR(*evec)[size] = edge; |
606 | 0 | } |
607 | 0 | } |
608 | 0 | } |
609 | 0 | } |
610 | 0 | } |
611 | | |
612 | | /* Clean */ |
613 | 0 | IGRAPH_FREE(parent_eids); |
614 | 0 | igraph_dqueue_int_destroy(&q); |
615 | 0 | igraph_vector_int_destroy(&tmp); |
616 | 0 | igraph_vit_destroy(&vit); |
617 | 0 | IGRAPH_FINALLY_CLEAN(4); |
618 | |
|
619 | 0 | return IGRAPH_SUCCESS; |
620 | 0 | } |
621 | | |
622 | | /** |
623 | | * \function igraph_get_shortest_path |
624 | | * \brief Shortest path from one vertex to another one. |
625 | | * |
626 | | * Calculates and returns a single unweighted shortest path from a |
627 | | * given vertex to another one. If there is more than one shortest |
628 | | * path between the two vertices, then an arbitrary one is returned. |
629 | | * |
630 | | * </para><para> |
631 | | * This function is a wrapper to \ref igraph_get_shortest_paths() |
632 | | * for the special case when only one target vertex is considered. |
633 | | * |
634 | | * \param graph The input graph, it can be directed or |
635 | | * undirected. Directed paths are considered in directed |
636 | | * graphs. |
637 | | * \param weights Optional edge weights. If \c NULL, the graph is considered |
638 | | * unweighted, i.e. all edge weights are equal to 1. |
639 | | * \param vertices Pointer to an initialized vector or a null |
640 | | * pointer. If not a null pointer, then the vertex IDs along |
641 | | * the path are stored here, including the source and target |
642 | | * vertices. |
643 | | * \param edges Pointer to an initialized vector or a null |
644 | | * pointer. If not a null pointer, then the edge IDs along the |
645 | | * path are stored here. |
646 | | * \param from The ID of the source vertex. |
647 | | * \param to The ID of the target vertex. |
648 | | * \param mode A constant specifying how edge directions are |
649 | | * considered in directed graphs. Valid modes are: |
650 | | * \c IGRAPH_OUT, follows edge directions; |
651 | | * \c IGRAPH_IN, follows the opposite directions; and |
652 | | * \c IGRAPH_ALL, ignores edge directions. This argument is |
653 | | * ignored for undirected graphs. |
654 | | * \return Error code. |
655 | | * |
656 | | * Time complexity: O(|V|+|E|), linear in the number of vertices and |
657 | | * edges in the graph. |
658 | | * |
659 | | * \sa \ref igraph_get_shortest_paths() for the version with more target |
660 | | * vertices. |
661 | | */ |
662 | | igraph_error_t igraph_get_shortest_path( |
663 | | const igraph_t *graph, |
664 | | const igraph_vector_t *weights, |
665 | | igraph_vector_int_t *vertices, |
666 | | igraph_vector_int_t *edges, |
667 | | igraph_int_t from, igraph_int_t to, |
668 | 0 | igraph_neimode_t mode) { |
669 | |
|
670 | 0 | igraph_vector_int_list_t vertices2, *vp = &vertices2; |
671 | 0 | igraph_vector_int_list_t edges2, *ep = &edges2; |
672 | |
|
673 | 0 | if (vertices) { |
674 | 0 | IGRAPH_CHECK(igraph_vector_int_list_init(&vertices2, 1)); |
675 | 0 | IGRAPH_FINALLY(igraph_vector_int_list_destroy, &vertices2); |
676 | 0 | } else { |
677 | 0 | vp = NULL; |
678 | 0 | } |
679 | 0 | if (edges) { |
680 | 0 | IGRAPH_CHECK(igraph_vector_int_list_init(&edges2, 1)); |
681 | 0 | IGRAPH_FINALLY(igraph_vector_int_list_destroy, &edges2); |
682 | 0 | } else { |
683 | 0 | ep = NULL; |
684 | 0 | } |
685 | | |
686 | 0 | IGRAPH_CHECK(igraph_get_shortest_paths(graph, weights, vp, ep, from, |
687 | 0 | igraph_vss_1(to), mode, NULL, NULL)); |
688 | | |
689 | | /* We use the constant time vector_swap() instead of the linear-time vector_update() to move the |
690 | | result to the output parameter. */ |
691 | 0 | if (edges) { |
692 | 0 | igraph_vector_int_swap(edges, igraph_vector_int_list_get_ptr(&edges2, 0)); |
693 | 0 | igraph_vector_int_list_destroy(&edges2); |
694 | 0 | IGRAPH_FINALLY_CLEAN(1); |
695 | 0 | } |
696 | 0 | if (vertices) { |
697 | 0 | igraph_vector_int_swap(vertices, igraph_vector_int_list_get_ptr(&vertices2, 0)); |
698 | 0 | igraph_vector_int_list_destroy(&vertices2); |
699 | 0 | IGRAPH_FINALLY_CLEAN(1); |
700 | 0 | } |
701 | |
|
702 | 0 | return IGRAPH_SUCCESS; |
703 | 0 | } |