/src/igraph/src/paths/shortest_paths.c
Line | Count | Source |
1 | | /* |
2 | | igraph library. |
3 | | Copyright (C) 2005-2020 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_interface.h" |
23 | | #include "igraph_dqueue.h" |
24 | | #include "igraph_memory.h" |
25 | | #include "igraph_progress.h" |
26 | | |
27 | | #include "core/indheap.h" |
28 | | #include "core/interruption.h" |
29 | | |
30 | | #include <string.h> |
31 | | |
32 | | /*****************************************************/ |
33 | | /***** Average path length and global efficiency *****/ |
34 | | /*****************************************************/ |
35 | | |
36 | | /* Computes the average of pairwise distances (used for igraph_average_path_length), |
37 | | * or of inverse pairwise distances (used for igraph_global_efficiency), in an unweighted graph. */ |
38 | | static igraph_error_t igraph_i_average_path_length_unweighted( |
39 | | const igraph_t *graph, |
40 | | igraph_real_t *res, |
41 | | igraph_real_t *unconnected_pairs, /* if not NULL, will be set to the no. of non-connected ordered vertex pairs */ |
42 | | const igraph_bool_t directed, |
43 | | const igraph_bool_t invert, /* average inverse distances instead of distances */ |
44 | | const igraph_bool_t unconn /* average over connected pairs instead of all pairs */) |
45 | 1.25k | { |
46 | 1.25k | const igraph_int_t no_of_nodes = igraph_vcount(graph); |
47 | | |
48 | | /* The number of ordered vertex pairs. |
49 | | * While the conditional below is not mathematically necessary, it prevents |
50 | | * returning -0.0 where 0.0 would be expected. */ |
51 | 1.25k | const igraph_real_t no_of_pairs = no_of_nodes > 0 ? no_of_nodes * (no_of_nodes - 1.0) : 0.0; |
52 | | |
53 | 1.25k | igraph_int_t n; |
54 | 1.25k | igraph_int_t *already_added; |
55 | | |
56 | 1.25k | igraph_real_t no_of_conn_pairs = 0.0; /* no. of ordered pairs between which there is a path */ |
57 | | |
58 | 1.25k | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
59 | 1.25k | igraph_vector_int_t *neis; |
60 | 1.25k | igraph_adjlist_t allneis; |
61 | | |
62 | 1.25k | *res = 0; |
63 | 1.25k | already_added = IGRAPH_CALLOC(no_of_nodes, igraph_int_t); |
64 | 1.25k | IGRAPH_CHECK_OOM(already_added, "Insufficient memory for average path length."); |
65 | 1.25k | IGRAPH_FINALLY(igraph_free, already_added); |
66 | | |
67 | 1.25k | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
68 | | |
69 | 1.25k | IGRAPH_CHECK(igraph_adjlist_init( |
70 | 1.25k | graph, &allneis, |
71 | 1.25k | directed ? IGRAPH_OUT : IGRAPH_ALL, |
72 | 1.25k | IGRAPH_LOOPS, IGRAPH_MULTIPLE |
73 | 1.25k | )); |
74 | 1.25k | IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); |
75 | | |
76 | 56.3k | for (igraph_int_t source = 0; source < no_of_nodes; source++) { |
77 | 55.0k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, source)); |
78 | 55.0k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); |
79 | 55.0k | already_added[source] = source + 1; |
80 | | |
81 | 55.0k | IGRAPH_ALLOW_INTERRUPTION(); |
82 | | |
83 | 348k | while (!igraph_dqueue_int_empty(&q)) { |
84 | 292k | igraph_int_t actnode = igraph_dqueue_int_pop(&q); |
85 | 292k | igraph_int_t actdist = igraph_dqueue_int_pop(&q); |
86 | | |
87 | 292k | neis = igraph_adjlist_get(&allneis, actnode); |
88 | 292k | n = igraph_vector_int_size(neis); |
89 | 941k | for (igraph_int_t j = 0; j < n; j++) { |
90 | 648k | igraph_int_t neighbor = VECTOR(*neis)[j]; |
91 | 648k | if (already_added[neighbor] == source + 1) { |
92 | 410k | continue; |
93 | 410k | } |
94 | 237k | already_added[neighbor] = source + 1; |
95 | 237k | if (invert) { |
96 | 237k | *res += 1.0/(actdist + 1.0); |
97 | 237k | } else { |
98 | 0 | *res += actdist + 1.0; |
99 | 0 | } |
100 | 237k | no_of_conn_pairs += 1; |
101 | 237k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
102 | 237k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); |
103 | 237k | } |
104 | 292k | } /* while !igraph_dqueue_int_empty */ |
105 | 55.0k | } /* for source < no_of_nodes */ |
106 | | |
107 | | |
108 | 1.25k | if (no_of_pairs == 0) { |
109 | 9 | *res = IGRAPH_NAN; /* can't average zero items */ |
110 | 1.24k | } else { |
111 | 1.24k | if (unconn) { /* average over connected pairs */ |
112 | 0 | if (no_of_conn_pairs == 0) { |
113 | 0 | *res = IGRAPH_NAN; /* can't average zero items */ |
114 | 0 | } else { |
115 | 0 | *res /= no_of_conn_pairs; |
116 | 0 | } |
117 | 1.24k | } else { /* average over all pairs */ |
118 | | /* no_of_conn_pairs < no_of_pairs implies that the graph is disconnected */ |
119 | 1.24k | if (no_of_conn_pairs < no_of_pairs && ! invert) { |
120 | | /* When invert=false, assume the distance between non-connected pairs to be infinity */ |
121 | 0 | *res = IGRAPH_INFINITY; |
122 | 1.24k | } else { |
123 | | /* When invert=true, assume the inverse distance between non-connected pairs |
124 | | * to be zero. Therefore, no special treatment is needed for disconnected graphs. */ |
125 | 1.24k | *res /= no_of_pairs; |
126 | 1.24k | } |
127 | 1.24k | } |
128 | 1.24k | } |
129 | | |
130 | 1.25k | if (unconnected_pairs) |
131 | 0 | *unconnected_pairs = no_of_pairs - no_of_conn_pairs; |
132 | | |
133 | | /* clean */ |
134 | 1.25k | IGRAPH_FREE(already_added); |
135 | 1.25k | igraph_dqueue_int_destroy(&q); |
136 | 1.25k | igraph_adjlist_destroy(&allneis); |
137 | 1.25k | IGRAPH_FINALLY_CLEAN(3); |
138 | | |
139 | 1.25k | return IGRAPH_SUCCESS; |
140 | 1.25k | } |
141 | | |
142 | | |
143 | | /* Computes the average of pairwise distances (used for igraph_average_path_length), |
144 | | * or of inverse pairwise distances (used for igraph_global_efficiency), in a weighted graph. |
145 | | * Uses Dijkstra's algorithm, therefore all weights must be non-negative. |
146 | | */ |
147 | | static igraph_error_t igraph_i_average_path_length_dijkstra( |
148 | | const igraph_t *graph, |
149 | | const igraph_vector_t *weights, |
150 | | igraph_real_t *res, |
151 | | igraph_real_t *unconnected_pairs, |
152 | | const igraph_bool_t directed, |
153 | | const igraph_bool_t invert, /* average inverse distances instead of distances */ |
154 | | const igraph_bool_t unconn /* average over connected pairs instead of all pairs */) |
155 | 3.78k | { |
156 | | |
157 | | /* Implementation details. This is the basic Dijkstra algorithm, |
158 | | with a binary heap. The heap is indexed, i.e. it stores not only |
159 | | the distances, but also which vertex they belong to. |
160 | | |
161 | | From now on we use a 2-way heap, so the distances can be queried |
162 | | directly from the heap. |
163 | | |
164 | | Dirty tricks: |
165 | | - the opposite of the distance is stored in the heap, as it is a |
166 | | maximum heap and we need a minimum heap. |
167 | | - we don't use IGRAPH_INFINITY in the res matrix during the |
168 | | computation, as isfinite() might involve a function call |
169 | | and we want to spare that. -1 will denote infinity instead. |
170 | | */ |
171 | | |
172 | 3.78k | igraph_int_t no_of_nodes = igraph_vcount(graph); |
173 | 3.78k | igraph_int_t no_of_edges = igraph_ecount(graph); |
174 | 3.78k | igraph_2wheap_t Q; |
175 | 3.78k | igraph_lazy_inclist_t inclist; |
176 | 3.78k | igraph_real_t no_of_pairs; |
177 | 3.78k | igraph_real_t no_of_conn_pairs = 0.0; /* no. of ordered pairs between which there is a path */ |
178 | | |
179 | 3.78k | IGRAPH_ASSERT(weights != 0); |
180 | | |
181 | 3.78k | if (igraph_vector_size(weights) != no_of_edges) { |
182 | 0 | IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match the number of edges (%" IGRAPH_PRId ").", |
183 | 0 | IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges); |
184 | 0 | } |
185 | 3.78k | if (no_of_edges > 0) { |
186 | 3.58k | igraph_real_t min = igraph_vector_min(weights); |
187 | 3.58k | if (min < 0) { |
188 | 0 | IGRAPH_ERRORF("Weight vector must be non-negative, got %g.", IGRAPH_EINVAL, min); |
189 | 0 | } |
190 | 3.58k | else if (isnan(min)) { |
191 | 0 | IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); |
192 | 0 | } |
193 | 3.58k | } |
194 | | |
195 | | /* Avoid returning a negative zero, which would be printed as -0 in tests. */ |
196 | 3.78k | if (no_of_nodes > 0) { |
197 | 3.77k | no_of_pairs = no_of_nodes * (no_of_nodes - 1.0); |
198 | 3.77k | } else { |
199 | 3 | no_of_pairs = 0; |
200 | 3 | } |
201 | | |
202 | 3.78k | IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); |
203 | 3.78k | IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); |
204 | 3.78k | IGRAPH_CHECK(igraph_lazy_inclist_init( |
205 | 3.78k | graph, &inclist, directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS |
206 | 3.78k | )); |
207 | 3.78k | IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); |
208 | | |
209 | 3.78k | *res = 0.0; |
210 | | |
211 | 616k | for (igraph_int_t source = 0; source < no_of_nodes; ++source) { |
212 | | |
213 | 613k | IGRAPH_ALLOW_INTERRUPTION(); |
214 | | |
215 | 613k | igraph_2wheap_clear(&Q); |
216 | 613k | igraph_2wheap_push_with_index(&Q, source, -1.0); |
217 | | |
218 | 4.45M | while (!igraph_2wheap_empty(&Q)) { |
219 | 3.83M | igraph_int_t minnei = igraph_2wheap_max_index(&Q); |
220 | 3.83M | igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); |
221 | 3.83M | igraph_vector_int_t *neis; |
222 | 3.83M | igraph_int_t nlen; |
223 | | |
224 | 3.83M | if (minnei != source) { |
225 | 3.22M | if (invert) { |
226 | 50.4k | *res += 1.0/(mindist - 1.0); |
227 | 3.17M | } else { |
228 | 3.17M | *res += mindist - 1.0; |
229 | 3.17M | } |
230 | 3.22M | no_of_conn_pairs += 1; |
231 | 3.22M | } |
232 | | |
233 | | /* Now check all neighbors of 'minnei' for a shorter path */ |
234 | 3.83M | neis = igraph_lazy_inclist_get(&inclist, minnei); |
235 | 3.83M | IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); |
236 | 3.83M | nlen = igraph_vector_int_size(neis); |
237 | 10.8M | for (igraph_int_t j = 0; j < nlen; j++) { |
238 | 7.02M | igraph_int_t edge = VECTOR(*neis)[j]; |
239 | 7.02M | igraph_int_t tto = IGRAPH_OTHER(graph, edge, minnei); |
240 | 7.02M | igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; |
241 | 7.02M | igraph_bool_t active = igraph_2wheap_has_active(&Q, tto); |
242 | 7.02M | igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto); |
243 | 7.02M | igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0; |
244 | 7.02M | if (altdist == IGRAPH_INFINITY) { |
245 | | /* Ignore edges with positive infinite weight */ |
246 | 7.02M | } else if (!has) { |
247 | | /* This is the first non-infinite distance */ |
248 | 3.22M | IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); |
249 | 3.80M | } else if (altdist < curdist) { |
250 | | /* This is a shorter path */ |
251 | 127k | igraph_2wheap_modify(&Q, tto, -altdist); |
252 | 127k | } |
253 | 7.02M | } |
254 | 3.83M | } /* !igraph_2wheap_empty(&Q) */ |
255 | 613k | } /* for source < no_of_nodes */ |
256 | | |
257 | 3.78k | if (no_of_pairs == 0) { |
258 | 28 | *res = IGRAPH_NAN; /* can't average zero items */ |
259 | 3.75k | } else { |
260 | 3.75k | if (unconn) { /* average over connected pairs */ |
261 | 3.03k | if (no_of_conn_pairs == 0) { |
262 | 196 | *res = IGRAPH_NAN; /* can't average zero items */ |
263 | 2.83k | } else { |
264 | 2.83k | *res /= no_of_conn_pairs; |
265 | 2.83k | } |
266 | 3.03k | } else { /* average over all pairs */ |
267 | | /* no_of_conn_pairs < no_of_pairs implies that the graph is disconnected */ |
268 | 720 | if (no_of_conn_pairs < no_of_pairs && ! invert) { |
269 | 0 | *res = IGRAPH_INFINITY; |
270 | 720 | } else { |
271 | 720 | *res /= no_of_pairs; |
272 | 720 | } |
273 | 720 | } |
274 | 3.75k | } |
275 | | |
276 | 3.78k | if (unconnected_pairs) { |
277 | 3.05k | *unconnected_pairs = no_of_pairs - no_of_conn_pairs; |
278 | 3.05k | } |
279 | | |
280 | 3.78k | igraph_lazy_inclist_destroy(&inclist); |
281 | 3.78k | igraph_2wheap_destroy(&Q); |
282 | 3.78k | IGRAPH_FINALLY_CLEAN(2); |
283 | | |
284 | 3.78k | return IGRAPH_SUCCESS; |
285 | 3.78k | } |
286 | | |
287 | | |
288 | | /** |
289 | | * \ingroup structural |
290 | | * \function igraph_average_path_length |
291 | | * \brief The average shortest path length between all vertex pairs. |
292 | | * |
293 | | * If no vertex pairs can be included in the calculation, for example because the graph |
294 | | * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c true, |
295 | | * NaN is returned. |
296 | | * |
297 | | * </para><para> |
298 | | * All distinct ordered vertex pairs are taken into account. |
299 | | * |
300 | | * \param graph The graph object. |
301 | | * \param weights The edge weights. All edge weights must be |
302 | | * non-negative for Dijkstra's algorithm to work. Additionally, no |
303 | | * edge weight may be NaN. If either case does not hold, an error |
304 | | * is returned. Passa a null pointer here if the graph is unweighted. |
305 | | * Edges with positive infinite weight are ignored. |
306 | | * \param res Pointer to a real number, this will contain the result. |
307 | | * \param unconn_pairs Pointer to a real number. If not a null pointer, the number of |
308 | | * ordered vertex pairs where the second vertex is unreachable from the first one |
309 | | * will be stored here. |
310 | | * \param directed Boolean, whether to consider directed paths. |
311 | | * Ignored for undirected graphs. |
312 | | * \param unconn If \c true, only those pairs are considered for the calculation |
313 | | * between which there is a path. If \c false, \c IGRAPH_INFINITY is returned |
314 | | * for disconnected graphs. |
315 | | * \return Error code: |
316 | | * \clist |
317 | | * \cli IGRAPH_ENOMEM |
318 | | * not enough memory for data structures |
319 | | * \cli IGRAPH_EINVAL |
320 | | * invalid weight vector |
321 | | * \endclist |
322 | | * |
323 | | * Time complexity: O(|V| |E| log|E| + |V|), where |V| is the number of |
324 | | * vertices and |E| is the number of edges. |
325 | | * |
326 | | * \example examples/simple/igraph_grg_game.c |
327 | | */ |
328 | | |
329 | | igraph_error_t igraph_average_path_length( |
330 | | const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *res, |
331 | | igraph_real_t *unconn_pairs, igraph_bool_t directed, igraph_bool_t unconn |
332 | 3.05k | ) { |
333 | 3.05k | if (weights) { |
334 | 3.05k | return igraph_i_average_path_length_dijkstra( |
335 | 3.05k | graph, weights, res, unconn_pairs, directed, /* invert= */ false, unconn |
336 | 3.05k | ); |
337 | 3.05k | } else { |
338 | 0 | return igraph_i_average_path_length_unweighted( |
339 | 0 | graph, res, unconn_pairs, directed, /* invert = */ false, unconn |
340 | 0 | ); |
341 | 0 | } |
342 | 3.05k | } |
343 | | |
344 | | |
345 | | /** |
346 | | * \ingroup structural |
347 | | * \function igraph_global_efficiency |
348 | | * \brief Calculates the global efficiency of a network. |
349 | | * |
350 | | * The global efficiency of a network is defined as the average of inverse |
351 | | * distances between all pairs of vertices: |
352 | | * <code>E_g = 1/(N*(N-1)) sum_{i!=j} 1/d_ij</code>, |
353 | | * where \c N is the number of vertices. The inverse distance between pairs |
354 | | * that are not reachable from each other is considered to be zero. For graphs |
355 | | * with fewer than 2 vertices, NaN is returned. |
356 | | * |
357 | | * </para><para> |
358 | | * Reference: |
359 | | * |
360 | | * </para><para> |
361 | | * V. Latora and M. Marchiori, |
362 | | * Efficient Behavior of Small-World Networks, |
363 | | * Phys. Rev. Lett. 87, 198701 (2001). |
364 | | * https://dx.doi.org/10.1103/PhysRevLett.87.198701 |
365 | | * |
366 | | * \param graph The graph object. |
367 | | * \param weights The edge weights. All edge weights must be |
368 | | * non-negative for Dijkstra's algorithm to work. Additionally, no |
369 | | * edge weight may be NaN. If either case does not hold, an error |
370 | | * is returned. If this is a null pointer, then the unweighted |
371 | | * version, \ref igraph_average_path_length() is used in calculating |
372 | | * the global efficiency. Edges with positive infinite weights are |
373 | | * ignored. |
374 | | * \param res Pointer to a real number, this will contain the result. |
375 | | * \param directed Boolean, whether to consider directed paths. |
376 | | * Ignored for undirected graphs. |
377 | | * \return Error code: |
378 | | * \clist |
379 | | * \cli IGRAPH_ENOMEM |
380 | | * not enough memory for data structures |
381 | | * \cli IGRAPH_EINVAL |
382 | | * invalid weight vector |
383 | | * \endclist |
384 | | * |
385 | | * Time complexity: O(|V| |E| log|E| + |V|) for weighted graphs and |
386 | | * O(|V| |E|) for unweighted ones. |V| denotes the number of |
387 | | * vertices and |E| denotes the number of edges. |
388 | | * |
389 | | * \sa \ref igraph_local_efficiency(), \ref igraph_average_local_efficiency() |
390 | | */ |
391 | | |
392 | | igraph_error_t igraph_global_efficiency( |
393 | | const igraph_t *graph, const igraph_vector_t *weights, |
394 | | igraph_real_t *res, igraph_bool_t directed |
395 | 1.98k | ) { |
396 | 1.98k | if (weights) { |
397 | 728 | return igraph_i_average_path_length_dijkstra( |
398 | 728 | graph, weights, res, NULL, directed, /* invert= */ true, /* unconn= */ false |
399 | 728 | ); |
400 | 1.25k | } else { |
401 | 1.25k | return igraph_i_average_path_length_unweighted( |
402 | 1.25k | graph, res, NULL, directed, /* invert= */ true, /* unconn= */ false |
403 | 1.25k | ); |
404 | 1.25k | } |
405 | 1.98k | } |
406 | | |
407 | | |
408 | | /****************************/ |
409 | | /***** Local efficiency *****/ |
410 | | /****************************/ |
411 | | |
412 | | static igraph_error_t igraph_i_local_efficiency_unweighted( |
413 | | const igraph_t *graph, |
414 | | const igraph_adjlist_t *adjlist, |
415 | | igraph_dqueue_int_t *q, |
416 | | igraph_int_t *already_counted, |
417 | | igraph_vector_int_t *vertex_neis, |
418 | | igraph_vector_char_t *nei_mask, |
419 | | igraph_real_t *res, |
420 | | igraph_int_t vertex, |
421 | | igraph_neimode_t mode) |
422 | 55.0k | { |
423 | | |
424 | 55.0k | igraph_int_t no_of_nodes = igraph_vcount(graph); |
425 | 55.0k | igraph_int_t vertex_neis_size; |
426 | 55.0k | igraph_int_t neighbor_count; /* unlike 'vertex_neis_size', 'neighbor_count' does not count self-loops and multi-edges */ |
427 | | |
428 | 55.0k | igraph_dqueue_int_clear(q); |
429 | | |
430 | | /* already_counted[i] is 0 iff vertex i was not reached so far, otherwise |
431 | | * it is the index of the source vertex in vertex_neis that it was reached |
432 | | * from, plus 1 */ |
433 | 55.0k | memset(already_counted, 0, no_of_nodes * sizeof(already_counted[0])); |
434 | | |
435 | 55.0k | IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); |
436 | 55.0k | vertex_neis_size = igraph_vector_int_size(vertex_neis); |
437 | | |
438 | 55.0k | igraph_vector_char_null(nei_mask); |
439 | 55.0k | neighbor_count = 0; |
440 | 95.1k | for (igraph_int_t i=0; i < vertex_neis_size; ++i) { |
441 | 40.0k | igraph_int_t v = VECTOR(*vertex_neis)[i]; |
442 | 40.0k | if (v != vertex && ! VECTOR(*nei_mask)[v]) { |
443 | 29.6k | VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */ |
444 | 29.6k | neighbor_count++; |
445 | 29.6k | } |
446 | 40.0k | } |
447 | | |
448 | 55.0k | *res = 0.0; |
449 | | |
450 | | /* when the neighbor count is smaller than 2, we return 0.0 */ |
451 | 55.0k | if (neighbor_count < 2) { |
452 | 49.1k | return IGRAPH_SUCCESS; |
453 | 49.1k | } |
454 | | |
455 | 33.1k | for (igraph_int_t i=0; i < vertex_neis_size; ++i) { |
456 | 27.3k | igraph_int_t source = VECTOR(*vertex_neis)[i]; |
457 | 27.3k | igraph_int_t reached = 0; |
458 | | |
459 | 27.3k | IGRAPH_ALLOW_INTERRUPTION(); |
460 | | |
461 | 27.3k | if (source == vertex) |
462 | 1.01k | continue; |
463 | | |
464 | 26.3k | if (VECTOR(*nei_mask)[source] == 2) |
465 | 3.66k | continue; |
466 | | |
467 | 22.6k | VECTOR(*nei_mask)[source] = 2; /* mark neighbour as already processed */ |
468 | | |
469 | 22.6k | IGRAPH_CHECK(igraph_dqueue_int_push(q, source)); |
470 | 22.6k | IGRAPH_CHECK(igraph_dqueue_int_push(q, 0)); |
471 | 22.6k | already_counted[source] = i + 1; |
472 | | |
473 | 461k | while (!igraph_dqueue_int_empty(q)) { |
474 | 439k | igraph_vector_int_t *act_neis; |
475 | 439k | igraph_int_t act_neis_size; |
476 | 439k | igraph_int_t act = igraph_dqueue_int_pop(q); |
477 | 439k | igraph_int_t actdist = igraph_dqueue_int_pop(q); |
478 | | |
479 | 439k | if (act != source && VECTOR(*nei_mask)[act]) { |
480 | 62.6k | *res += 1.0 / actdist; |
481 | 62.6k | reached++; |
482 | 62.6k | if (reached == neighbor_count) { |
483 | 0 | igraph_dqueue_int_clear(q); |
484 | 0 | break; |
485 | 0 | } |
486 | 62.6k | } |
487 | | |
488 | 439k | act_neis = igraph_adjlist_get(adjlist, act); |
489 | 439k | act_neis_size = igraph_vector_int_size(act_neis); |
490 | 1.83M | for (igraph_int_t j = 0; j < act_neis_size; j++) { |
491 | 1.39M | igraph_int_t neighbor = VECTOR(*act_neis)[j]; |
492 | | |
493 | 1.39M | if (neighbor == vertex || already_counted[neighbor] == i + 1) |
494 | 977k | continue; |
495 | | |
496 | 416k | already_counted[neighbor] = i + 1; |
497 | 416k | IGRAPH_CHECK(igraph_dqueue_int_push(q, neighbor)); |
498 | 416k | IGRAPH_CHECK(igraph_dqueue_int_push(q, actdist + 1)); |
499 | 416k | } |
500 | 439k | } |
501 | 22.6k | } |
502 | | |
503 | 5.87k | *res /= neighbor_count * (neighbor_count - 1.0); |
504 | | |
505 | 5.87k | return IGRAPH_SUCCESS; |
506 | 5.87k | } |
507 | | |
508 | | static igraph_error_t igraph_i_local_efficiency_dijkstra( |
509 | | const igraph_t *graph, |
510 | | igraph_lazy_inclist_t *inclist, |
511 | | igraph_2wheap_t *Q, |
512 | | igraph_vector_int_t *vertex_neis, |
513 | | igraph_vector_char_t *nei_mask, /* true if the corresponding node is a neighbour of 'vertex' */ |
514 | | igraph_real_t *res, |
515 | | igraph_int_t vertex, |
516 | | igraph_neimode_t mode, |
517 | | const igraph_vector_t *weights) |
518 | 31.1k | { |
519 | | |
520 | | /* Implementation details. This is the basic Dijkstra algorithm, |
521 | | with a binary heap. The heap is indexed, i.e. it stores not only |
522 | | the distances, but also which vertex they belong to. |
523 | | |
524 | | From now on we use a 2-way heap, so the distances can be queried |
525 | | directly from the heap. |
526 | | |
527 | | Dirty tricks: |
528 | | - the opposite of the distance is stored in the heap, as it is a |
529 | | maximum heap and we need a minimum heap. |
530 | | - we don't use IGRAPH_INFINITY in the res matrix during the |
531 | | computation, as isfinite() might involve a function call |
532 | | and we want to spare that. -1 will denote infinity instead. |
533 | | */ |
534 | | |
535 | 31.1k | igraph_int_t vertex_neis_size; |
536 | 31.1k | igraph_int_t neighbor_count; /* unlike 'inc_edges_size', 'neighbor_count' does not count self-loops or multi-edges */ |
537 | | |
538 | 31.1k | IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); |
539 | 31.1k | vertex_neis_size = igraph_vector_int_size(vertex_neis); |
540 | | |
541 | 31.1k | igraph_vector_char_null(nei_mask); |
542 | 31.1k | neighbor_count = 0; |
543 | 48.2k | for (igraph_int_t i=0; i < vertex_neis_size; ++i) { |
544 | 17.0k | igraph_int_t v = VECTOR(*vertex_neis)[i]; |
545 | 17.0k | if (v != vertex && ! VECTOR(*nei_mask)[v]) { |
546 | 8.49k | VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */ |
547 | 8.49k | neighbor_count++; |
548 | 8.49k | } |
549 | 17.0k | } |
550 | | |
551 | 31.1k | *res = 0.0; |
552 | | |
553 | | /* when the neighbor count is smaller than 2, we return 0.0 */ |
554 | 31.1k | if (neighbor_count < 2) { |
555 | 29.6k | return IGRAPH_SUCCESS; |
556 | 29.6k | } |
557 | | |
558 | 10.2k | for (igraph_int_t i=0; i < vertex_neis_size; ++i) { |
559 | 8.75k | igraph_int_t source = VECTOR(*vertex_neis)[i]; |
560 | 8.75k | igraph_int_t reached = 0; |
561 | | |
562 | 8.75k | IGRAPH_ALLOW_INTERRUPTION(); |
563 | | |
564 | 8.75k | if (source == vertex) |
565 | 1.07k | continue; |
566 | | |
567 | | /* avoid processing a neighbour twice in multigraphs */ |
568 | 7.68k | if (VECTOR(*nei_mask)[source] == 2) |
569 | 2.21k | continue; |
570 | 5.46k | VECTOR(*nei_mask)[source] = 2; /* mark as already processed */ |
571 | | |
572 | 5.46k | igraph_2wheap_clear(Q); |
573 | 5.46k | igraph_2wheap_push_with_index(Q, source, -1.0); |
574 | | |
575 | 80.8k | while (!igraph_2wheap_empty(Q)) { |
576 | 75.4k | igraph_int_t minnei = igraph_2wheap_max_index(Q); |
577 | 75.4k | igraph_real_t mindist = -igraph_2wheap_deactivate_max(Q); |
578 | 75.4k | igraph_vector_int_t *neis; |
579 | 75.4k | igraph_int_t nlen; |
580 | | |
581 | 75.4k | if (minnei != source && VECTOR(*nei_mask)[minnei]) { |
582 | 8.47k | *res += 1.0/(mindist - 1.0); |
583 | 8.47k | reached++; |
584 | 8.47k | if (reached == neighbor_count) { |
585 | 0 | igraph_2wheap_clear(Q); |
586 | 0 | break; |
587 | 0 | } |
588 | 8.47k | } |
589 | | |
590 | | /* Now check all neighbors of 'minnei' for a shorter path */ |
591 | 75.4k | neis = igraph_lazy_inclist_get(inclist, minnei); |
592 | 75.4k | IGRAPH_CHECK_OOM(neis, "Failed to query incident edges."); |
593 | 75.4k | nlen = igraph_vector_int_size(neis); |
594 | 285k | for (igraph_int_t j = 0; j < nlen; j++) { |
595 | 210k | igraph_real_t altdist, curdist; |
596 | 210k | igraph_bool_t active, has; |
597 | 210k | igraph_int_t edge = VECTOR(*neis)[j]; |
598 | 210k | igraph_int_t tto = IGRAPH_OTHER(graph, edge, minnei); |
599 | | |
600 | 210k | if (tto == vertex) { |
601 | 8.02k | continue; |
602 | 8.02k | } |
603 | | |
604 | 202k | altdist = mindist + VECTOR(*weights)[edge]; |
605 | 202k | active = igraph_2wheap_has_active(Q, tto); |
606 | 202k | has = igraph_2wheap_has_elem(Q, tto); |
607 | 202k | curdist = active ? -igraph_2wheap_get(Q, tto) : 0.0; |
608 | 202k | if (!has) { |
609 | | /* This is the first non-infinite distance */ |
610 | 69.9k | IGRAPH_CHECK(igraph_2wheap_push_with_index(Q, tto, -altdist)); |
611 | 132k | } else if (altdist < curdist) { |
612 | | /* This is a shorter path */ |
613 | 10.6k | igraph_2wheap_modify(Q, tto, -altdist); |
614 | 10.6k | } |
615 | 202k | } |
616 | | |
617 | 75.4k | } /* !igraph_2wheap_empty(&Q) */ |
618 | | |
619 | 5.46k | } |
620 | | |
621 | 1.53k | *res /= neighbor_count * (neighbor_count - 1.0); |
622 | | |
623 | 1.53k | return IGRAPH_SUCCESS; |
624 | 1.53k | } |
625 | | |
626 | | |
627 | | /** |
628 | | * \ingroup structural |
629 | | * \function igraph_local_efficiency |
630 | | * \brief Calculates the local efficiency around each vertex in a network. |
631 | | * |
632 | | * The local efficiency of a network around a vertex is defined as follows: |
633 | | * We remove the vertex and compute the distances (shortest path lengths) between |
634 | | * its neighbours through the rest of the network. The local efficiency around the |
635 | | * removed vertex is the average of the inverse of these distances. |
636 | | * |
637 | | * </para><para> |
638 | | * The inverse distance between two vertices which are not reachable from each other |
639 | | * is considered to be zero. The local efficiency around a vertex with fewer than two |
640 | | * neighbours is taken to be zero by convention. |
641 | | * |
642 | | * </para><para> |
643 | | * Reference: |
644 | | * |
645 | | * </para><para> |
646 | | * I. Vragović, E. Louis, and A. DÃaz-Guilera, |
647 | | * Efficiency of informational transfer in regular and complex networks, |
648 | | * Phys. Rev. E 71, 1 (2005). |
649 | | * http://dx.doi.org/10.1103/PhysRevE.71.036122 |
650 | | * |
651 | | * \param graph The graph object. |
652 | | * \param weights The edge weights. All edge weights must be |
653 | | * non-negative. Additionally, no edge weight may be NaN. If either |
654 | | * case does not hold, an error is returned. If this is a null |
655 | | * pointer, then the unweighted version, |
656 | | * \ref igraph_average_path_length() is called. Edges with positive |
657 | | * infinite weights are ignored. |
658 | | * \param res Pointer to an initialized vector, this will contain the result. |
659 | | * \param vids The vertices around which the local efficiency will be calculated. |
660 | | * \param directed Boolean, whether to consider directed paths. |
661 | | * Ignored for undirected graphs. |
662 | | * \param mode How to determine the local neighborhood of each vertex |
663 | | * in directed graphs. Ignored in undirected graphs. |
664 | | * \clist |
665 | | * \cli IGRAPH_ALL |
666 | | * take both in- and out-neighbours; |
667 | | * this is a reasonable default for high-level interfaces. |
668 | | * \cli IGRAPH_OUT |
669 | | * take only out-neighbours |
670 | | * \cli IGRAPH_IN |
671 | | * take only in-neighbours |
672 | | * \endclist |
673 | | * \return Error code: |
674 | | * \clist |
675 | | * \cli IGRAPH_ENOMEM |
676 | | * not enough memory for data structures |
677 | | * \cli IGRAPH_EINVAL |
678 | | * invalid weight vector |
679 | | * \endclist |
680 | | * |
681 | | * Time complexity: O(|E|^2 log|E|) for weighted graphs and |
682 | | * O(|E|^2) for unweighted ones. |E| denotes the number of edges. |
683 | | * |
684 | | * \sa \ref igraph_average_local_efficiency(), \ref igraph_global_efficiency() |
685 | | * |
686 | | */ |
687 | | |
688 | | igraph_error_t igraph_local_efficiency( |
689 | | const igraph_t *graph, const igraph_vector_t *weights, igraph_vector_t *res, |
690 | | const igraph_vs_t vids, igraph_bool_t directed, igraph_neimode_t mode |
691 | 1.98k | ) { |
692 | 1.98k | igraph_int_t no_of_nodes = igraph_vcount(graph); |
693 | 1.98k | igraph_int_t no_of_edges = igraph_ecount(graph); |
694 | 1.98k | igraph_int_t nodes_to_calc; /* no. of vertices includes in computation */ |
695 | 1.98k | igraph_vit_t vit; |
696 | 1.98k | igraph_vector_int_t vertex_neis; |
697 | 1.98k | igraph_vector_char_t nei_mask; |
698 | 1.98k | igraph_int_t i; |
699 | | |
700 | | /* 'nei_mask' is a vector indexed by vertices. The meaning of its values is as follows: |
701 | | * 0: not a neighbour of 'vertex' |
702 | | * 1: a not-yet-processed neighbour of 'vertex' |
703 | | * 2: an already processed neighbour of 'vertex' |
704 | | * |
705 | | * Marking neighbours of already processed is necessary to avoid processing them more |
706 | | * than once in multigraphs. |
707 | | */ |
708 | 1.98k | IGRAPH_CHECK(igraph_vector_char_init(&nei_mask, no_of_nodes)); |
709 | 1.98k | IGRAPH_FINALLY(igraph_vector_char_destroy, &nei_mask); |
710 | 1.98k | IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_neis, 0); |
711 | | |
712 | 1.98k | IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); |
713 | 1.98k | IGRAPH_FINALLY(igraph_vit_destroy, &vit); |
714 | | |
715 | 1.98k | nodes_to_calc = IGRAPH_VIT_SIZE(vit); |
716 | | |
717 | 1.98k | IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); |
718 | | |
719 | 1.98k | if (! weights) /* unweighted case */ |
720 | 1.25k | { |
721 | 1.25k | igraph_int_t *already_counted; |
722 | 1.25k | igraph_adjlist_t adjlist; |
723 | 1.25k | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
724 | | |
725 | 1.25k | already_counted = IGRAPH_CALLOC(no_of_nodes, igraph_int_t); |
726 | 1.25k | IGRAPH_CHECK_OOM(already_counted, "Insufficient memory for local efficiency calculation."); |
727 | 1.25k | IGRAPH_FINALLY(igraph_free, already_counted); |
728 | | |
729 | 1.25k | IGRAPH_CHECK(igraph_adjlist_init( |
730 | 1.25k | graph, &adjlist, |
731 | 1.25k | directed ? IGRAPH_OUT : IGRAPH_ALL, |
732 | 1.25k | IGRAPH_LOOPS, IGRAPH_MULTIPLE |
733 | 1.25k | )); |
734 | 1.25k | IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); |
735 | | |
736 | 1.25k | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
737 | | |
738 | 1.25k | for (IGRAPH_VIT_RESET(vit), i=0; |
739 | 56.3k | ! IGRAPH_VIT_END(vit); |
740 | 55.0k | IGRAPH_VIT_NEXT(vit), i++) |
741 | 55.0k | { |
742 | 55.0k | IGRAPH_CHECK(igraph_i_local_efficiency_unweighted( |
743 | 55.0k | graph, &adjlist, |
744 | 55.0k | &q, already_counted, &vertex_neis, &nei_mask, |
745 | 55.0k | &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode)); |
746 | 55.0k | } |
747 | | |
748 | 1.25k | igraph_dqueue_int_destroy(&q); |
749 | 1.25k | igraph_adjlist_destroy(&adjlist); |
750 | 1.25k | IGRAPH_FREE(already_counted); |
751 | 1.25k | IGRAPH_FINALLY_CLEAN(3); |
752 | 1.25k | } |
753 | 728 | else /* weighted case */ |
754 | 728 | { |
755 | 728 | igraph_lazy_inclist_t inclist; |
756 | 728 | igraph_2wheap_t Q; |
757 | | |
758 | 728 | if (igraph_vector_size(weights) != no_of_edges) { |
759 | 0 | IGRAPH_ERROR("Weight vector length does not match the number of edges.", IGRAPH_EINVAL); |
760 | 0 | } |
761 | 728 | if (no_of_edges > 0) { |
762 | 689 | igraph_real_t min = igraph_vector_min(weights); |
763 | 689 | if (min < 0) { |
764 | 0 | IGRAPH_ERRORF("Weights must not be negative, got %g.", IGRAPH_EINVAL, min); |
765 | 0 | } |
766 | 689 | else if (isnan(min)) { |
767 | 0 | IGRAPH_ERROR("Weights must not contain NaN values.", IGRAPH_EINVAL); |
768 | 0 | } |
769 | 689 | } |
770 | | |
771 | 728 | IGRAPH_CHECK(igraph_lazy_inclist_init( |
772 | 728 | graph, &inclist, directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS |
773 | 728 | )); |
774 | 728 | IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist); |
775 | 728 | IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); |
776 | 728 | IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); |
777 | | |
778 | 728 | for (IGRAPH_VIT_RESET(vit), i=0; |
779 | 31.8k | ! IGRAPH_VIT_END(vit); |
780 | 31.1k | IGRAPH_VIT_NEXT(vit), i++) |
781 | 31.1k | { |
782 | 31.1k | IGRAPH_CHECK(igraph_i_local_efficiency_dijkstra( |
783 | 31.1k | graph, &inclist, |
784 | 31.1k | &Q, &vertex_neis, &nei_mask, |
785 | 31.1k | &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode, weights)); |
786 | 31.1k | } |
787 | | |
788 | 728 | igraph_2wheap_destroy(&Q); |
789 | 728 | igraph_lazy_inclist_destroy(&inclist); |
790 | 728 | IGRAPH_FINALLY_CLEAN(2); |
791 | 728 | } |
792 | | |
793 | 1.98k | igraph_vit_destroy(&vit); |
794 | 1.98k | igraph_vector_int_destroy(&vertex_neis); |
795 | 1.98k | igraph_vector_char_destroy(&nei_mask); |
796 | 1.98k | IGRAPH_FINALLY_CLEAN(3); |
797 | | |
798 | 1.98k | return IGRAPH_SUCCESS; |
799 | 1.98k | } |
800 | | |
801 | | |
802 | | /** |
803 | | * \ingroup structural |
804 | | * \function igraph_average_local_efficiency |
805 | | * \brief Calculates the average local efficiency in a network. |
806 | | * |
807 | | * For the null graph, zero is returned by convention. |
808 | | * |
809 | | * \param graph The graph object. |
810 | | * \param weights The edge weights. They must be all non-negative. |
811 | | * If a null pointer is given, all weights are assumed to be 1. Edges |
812 | | * with positive infinite weight are ignored. |
813 | | * \param res Pointer to a real number, this will contain the result. |
814 | | * \param directed Boolean, whether to consider directed paths. |
815 | | * Ignored for undirected graphs. |
816 | | * \param mode How to determine the local neighborhood of each vertex |
817 | | * in directed graphs. Ignored in undirected graphs. |
818 | | * \clist |
819 | | * \cli IGRAPH_ALL |
820 | | * take both in- and out-neighbours; |
821 | | * this is a reasonable default for high-level interfaces. |
822 | | * \cli IGRAPH_OUT |
823 | | * take only out-neighbours |
824 | | * \cli IGRAPH_IN |
825 | | * take only in-neighbours |
826 | | * \endclist |
827 | | * \return Error code: |
828 | | * \clist |
829 | | * \cli IGRAPH_ENOMEM |
830 | | * not enough memory for data structures |
831 | | * \cli IGRAPH_EINVAL |
832 | | * invalid weight vector |
833 | | * \endclist |
834 | | * |
835 | | * Time complexity: O(|E|^2 log|E|) for weighted graphs and |
836 | | * O(|E|^2) for unweighted ones. |E| denotes the number of edges. |
837 | | * |
838 | | * \sa \ref igraph_local_efficiency() |
839 | | * |
840 | | */ |
841 | | |
842 | | igraph_error_t igraph_average_local_efficiency( |
843 | | const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *res, |
844 | | igraph_bool_t directed, igraph_neimode_t mode |
845 | 0 | ) { |
846 | 0 | igraph_int_t no_of_nodes = igraph_vcount(graph); |
847 | 0 | igraph_vector_t local_eff; |
848 | | |
849 | | /* If there are fewer than 3 vertices, no vertex has more than one neighbour, thus all |
850 | | local efficiencies are zero. For the null graph, we return zero by convention. */ |
851 | 0 | if (no_of_nodes < 3) { |
852 | 0 | *res = 0; |
853 | 0 | return IGRAPH_SUCCESS; |
854 | 0 | } |
855 | | |
856 | 0 | IGRAPH_VECTOR_INIT_FINALLY(&local_eff, no_of_nodes); |
857 | | |
858 | 0 | IGRAPH_CHECK(igraph_local_efficiency(graph, weights, &local_eff, igraph_vss_all(),directed, mode)); |
859 | | |
860 | 0 | *res = igraph_vector_sum(&local_eff); |
861 | 0 | *res /= no_of_nodes; |
862 | |
|
863 | 0 | igraph_vector_destroy(&local_eff); |
864 | 0 | IGRAPH_FINALLY_CLEAN(1); |
865 | |
|
866 | 0 | return IGRAPH_SUCCESS; |
867 | 0 | } |
868 | | |
869 | | |
870 | | /***************************/ |
871 | | /***** Graph diameter ******/ |
872 | | /***************************/ |
873 | | |
874 | | /** |
875 | | * \ingroup structural |
876 | | * \function igraph_diameter |
877 | | * \brief Calculates the diameter of a graph (longest geodesic). |
878 | | * |
879 | | * The diameter of a graph is the length of the longest shortest path it has, |
880 | | * i.e. the maximum eccentricity of the graph's vertices. |
881 | | * This function computes both the diameter, as well as a corresponding path. |
882 | | * The diameter of the null graph is considered be infinity by convention. |
883 | | * |
884 | | * If the graph has no vertices, \c IGRAPH_NAN is returned. |
885 | | * |
886 | | * \param graph The graph object. |
887 | | * \param res Pointer to a real number, if not \c NULL then it will contain |
888 | | * the diameter (the actual distance). |
889 | | * \param from Pointer to an integer, if not \c NULL it will be set to the |
890 | | * source vertex of the diameter path. If the graph has no diameter path, |
891 | | * it will be set to -1. |
892 | | * \param to Pointer to an integer, if not \c NULL it will be set to the |
893 | | * target vertex of the diameter path. If the graph has no diameter path, |
894 | | * it will be set to -1. |
895 | | * \param vertex_path Pointer to an initialized vector. If not \c NULL the actual |
896 | | * longest geodesic path in terms of vertices will be stored here. The vector will be |
897 | | * resized as needed. |
898 | | * \param edge_path Pointer to an initialized vector. If not \c NULL the actual |
899 | | * longest geodesic path in terms of edges will be stored here. The vector will be |
900 | | * resized as needed. |
901 | | * \param directed Boolean, whether to consider directed |
902 | | * paths. Ignored for undirected graphs. |
903 | | * \param unconn What to do if the graph is not connected. If |
904 | | * \c true the longest geodesic within a component |
905 | | * will be returned, otherwise \c IGRAPH_INFINITY is returned. |
906 | | * \return Error code: |
907 | | * \c IGRAPH_ENOMEM, not enough memory for |
908 | | * temporary data. |
909 | | * |
910 | | * Time complexity: O(|V||E|), the |
911 | | * number of vertices times the number of edges. |
912 | | * |
913 | | * \sa \ref igraph_radius() for the minimum eccentricity. |
914 | | * |
915 | | * \example examples/simple/igraph_diameter.c |
916 | | */ |
917 | | |
918 | | static igraph_error_t igraph_i_diameter_unweighted( |
919 | | const igraph_t *graph, igraph_real_t *res, igraph_int_t *from, |
920 | | igraph_int_t *to, igraph_vector_int_t *vertex_path, |
921 | | igraph_vector_int_t *edge_path, igraph_bool_t directed, igraph_bool_t unconn |
922 | 0 | ) { |
923 | |
|
924 | 0 | const igraph_int_t no_of_nodes = igraph_vcount(graph); |
925 | 0 | igraph_int_t n; |
926 | 0 | igraph_int_t *already_added; |
927 | 0 | igraph_int_t nodes_reached; |
928 | | /* from/to are initialized to 0 because in a singleton graph, or in an edgeless graph |
929 | | * with unconn = true, the diameter path will be considered to consist of vertex 0 only. */ |
930 | 0 | igraph_int_t ifrom = 0, ito = 0; |
931 | 0 | igraph_real_t ires = 0; |
932 | |
|
933 | 0 | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
934 | 0 | igraph_vector_int_t *neis; |
935 | 0 | igraph_neimode_t dirmode; |
936 | 0 | igraph_adjlist_t allneis; |
937 | | |
938 | | /* See https://github.com/igraph/igraph/issues/1538#issuecomment-724071857 |
939 | | * for why we return NaN for the null graph. */ |
940 | 0 | if (no_of_nodes == 0) { |
941 | 0 | if (res) { |
942 | 0 | *res = IGRAPH_NAN; |
943 | 0 | } |
944 | 0 | if (vertex_path) { |
945 | 0 | igraph_vector_int_clear(vertex_path); |
946 | 0 | } |
947 | 0 | if (edge_path) { |
948 | 0 | igraph_vector_int_clear(edge_path); |
949 | 0 | } |
950 | 0 | if (from) { |
951 | 0 | *from = -1; |
952 | 0 | } |
953 | 0 | if (to) { |
954 | 0 | *to = -1; |
955 | 0 | } |
956 | 0 | return IGRAPH_SUCCESS; |
957 | 0 | } |
958 | | |
959 | 0 | if (directed) { |
960 | 0 | dirmode = IGRAPH_OUT; |
961 | 0 | } else { |
962 | 0 | dirmode = IGRAPH_ALL; |
963 | 0 | } |
964 | 0 | already_added = IGRAPH_CALLOC(no_of_nodes, igraph_int_t); |
965 | 0 | IGRAPH_CHECK_OOM(already_added, "Insufficient memory for diameter calculation."); |
966 | 0 | IGRAPH_FINALLY(igraph_free, already_added); |
967 | |
|
968 | 0 | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
969 | | |
970 | 0 | IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); |
971 | 0 | IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis); |
972 | |
|
973 | 0 | for (igraph_int_t i = 0; i < no_of_nodes; i++) { |
974 | 0 | nodes_reached = 1; |
975 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); |
976 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); |
977 | 0 | already_added[i] = i + 1; |
978 | |
|
979 | 0 | IGRAPH_PROGRESS("Diameter: ", 100.0 * i / no_of_nodes, NULL); |
980 | | |
981 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
982 | | |
983 | 0 | while (!igraph_dqueue_int_empty(&q)) { |
984 | 0 | igraph_int_t actnode = igraph_dqueue_int_pop(&q); |
985 | 0 | igraph_int_t actdist = igraph_dqueue_int_pop(&q); |
986 | 0 | if (actdist > ires) { |
987 | 0 | ires = actdist; |
988 | 0 | ifrom = i; |
989 | 0 | ito = actnode; |
990 | 0 | } |
991 | |
|
992 | 0 | neis = igraph_adjlist_get(&allneis, actnode); |
993 | 0 | n = igraph_vector_int_size(neis); |
994 | 0 | for (igraph_int_t j = 0; j < n; j++) { |
995 | 0 | igraph_int_t neighbor = VECTOR(*neis)[j]; |
996 | 0 | if (already_added[neighbor] == i + 1) { |
997 | 0 | continue; |
998 | 0 | } |
999 | 0 | already_added[neighbor] = i + 1; |
1000 | 0 | nodes_reached++; |
1001 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
1002 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, actdist + 1)); |
1003 | 0 | } |
1004 | 0 | } /* while !igraph_dqueue_int_empty */ |
1005 | | |
1006 | | /* not connected, return IGRAPH_INFINITY */ |
1007 | 0 | if (nodes_reached != no_of_nodes && !unconn) { |
1008 | 0 | ires = IGRAPH_INFINITY; |
1009 | 0 | ifrom = -1; |
1010 | 0 | ito = -1; |
1011 | 0 | break; |
1012 | 0 | } |
1013 | 0 | } /* for i<no_of_nodes */ |
1014 | | |
1015 | 0 | IGRAPH_PROGRESS("Diameter: ", 100.0, NULL); |
1016 | | |
1017 | | /* return the requested info */ |
1018 | 0 | if (res != 0) { |
1019 | 0 | *res = ires; |
1020 | 0 | } |
1021 | 0 | if (from != 0) { |
1022 | 0 | *from = ifrom; |
1023 | 0 | } |
1024 | 0 | if (to != 0) { |
1025 | 0 | *to = ito; |
1026 | 0 | } |
1027 | 0 | if ((vertex_path) || (edge_path)) { |
1028 | 0 | if (! isfinite(ires)) { |
1029 | 0 | if (vertex_path) { |
1030 | 0 | igraph_vector_int_clear(vertex_path); |
1031 | 0 | } |
1032 | 0 | if (edge_path){ |
1033 | 0 | igraph_vector_int_clear(edge_path); |
1034 | 0 | } |
1035 | 0 | } else { |
1036 | 0 | IGRAPH_CHECK(igraph_get_shortest_path(graph, NULL, vertex_path, edge_path, |
1037 | 0 | ifrom, ito, dirmode)); |
1038 | 0 | } |
1039 | 0 | } |
1040 | | |
1041 | | /* clean */ |
1042 | 0 | IGRAPH_FREE(already_added); |
1043 | 0 | igraph_dqueue_int_destroy(&q); |
1044 | 0 | igraph_adjlist_destroy(&allneis); |
1045 | 0 | IGRAPH_FINALLY_CLEAN(3); |
1046 | |
|
1047 | 0 | return IGRAPH_SUCCESS; |
1048 | 0 | } |
1049 | | |
1050 | | static igraph_error_t igraph_i_diameter_dijkstra( |
1051 | | const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *res, |
1052 | | igraph_int_t *from, igraph_int_t *to, igraph_vector_int_t *vertex_path, |
1053 | | igraph_vector_int_t *edge_path, igraph_bool_t directed, igraph_bool_t unconn |
1054 | 3.05k | ) { |
1055 | | /* Implementation details. This is the basic Dijkstra algorithm, |
1056 | | with a binary heap. The heap is indexed, i.e. it stores not only |
1057 | | the distances, but also which vertex they belong to. |
1058 | | |
1059 | | From now on we use a 2-way heap, so the distances can be queried |
1060 | | directly from the heap. |
1061 | | |
1062 | | Dirty tricks: |
1063 | | - the opposite of the distance is stored in the heap, as it is a |
1064 | | maximum heap and we need a minimum heap. |
1065 | | - we don't use IGRAPH_INFINITY during the computation, as isfinite() |
1066 | | might involve a function call and we want to spare that. -1 will denote |
1067 | | infinity instead. |
1068 | | */ |
1069 | | |
1070 | 3.05k | const igraph_int_t no_of_nodes = igraph_vcount(graph); |
1071 | 3.05k | const igraph_int_t no_of_edges = igraph_ecount(graph); |
1072 | | |
1073 | 3.05k | igraph_2wheap_t Q; |
1074 | 3.05k | igraph_inclist_t inclist; |
1075 | 3.05k | igraph_neimode_t dirmode = directed ? IGRAPH_OUT : IGRAPH_ALL; |
1076 | | |
1077 | | /* from/to are initialized to 0 because in a singleton graph, or in an edgeless graph |
1078 | | * with unconn = true, the diameter path will be considered to consist of vertex 0 only. */ |
1079 | 3.05k | igraph_int_t ifrom = 0, ito = 0; |
1080 | 3.05k | igraph_real_t ires = 0; |
1081 | 3.05k | igraph_int_t nodes_reached = 0; |
1082 | | |
1083 | | /* See https://github.com/igraph/igraph/issues/1538#issuecomment-724071857 |
1084 | | * for why we return NaN for the null graph. */ |
1085 | 3.05k | if (no_of_nodes == 0) { |
1086 | 2 | if (res) { |
1087 | 2 | *res = IGRAPH_NAN; |
1088 | 2 | } |
1089 | 2 | if (vertex_path) { |
1090 | 0 | igraph_vector_int_clear(vertex_path); |
1091 | 0 | } |
1092 | 2 | if (edge_path) { |
1093 | 0 | igraph_vector_int_clear(edge_path); |
1094 | 0 | } |
1095 | 2 | if (from) { |
1096 | 0 | *from = -1; |
1097 | 0 | } |
1098 | 2 | if (to) { |
1099 | 0 | *to = -1; |
1100 | 0 | } |
1101 | 2 | return IGRAPH_SUCCESS; |
1102 | 2 | } |
1103 | | |
1104 | 3.05k | IGRAPH_ASSERT(weights != 0); |
1105 | | |
1106 | 3.05k | if (igraph_vector_size(weights) != no_of_edges) { |
1107 | 0 | IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") not equal to number of edges (%" IGRAPH_PRId ").", |
1108 | 0 | IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges); |
1109 | 0 | } |
1110 | | |
1111 | 3.05k | if (no_of_edges > 0) { |
1112 | 2.89k | igraph_real_t min = igraph_vector_min(weights); |
1113 | 2.89k | if (min < 0) { |
1114 | 0 | IGRAPH_ERRORF("Weight vector must be non-negative, got %g.", IGRAPH_EINVAL, min); |
1115 | 0 | } |
1116 | 2.89k | else if (isnan(min)) { |
1117 | 0 | IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL); |
1118 | 0 | } |
1119 | 2.89k | } |
1120 | | |
1121 | 3.05k | IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes)); |
1122 | 3.05k | IGRAPH_FINALLY(igraph_2wheap_destroy, &Q); |
1123 | 3.05k | IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, dirmode, IGRAPH_LOOPS)); |
1124 | 3.05k | IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); |
1125 | | |
1126 | 294k | for (igraph_int_t source = 0; source < no_of_nodes; source++) { |
1127 | | |
1128 | 292k | IGRAPH_PROGRESS("Weighted diameter: ", source * 100.0 / no_of_nodes, NULL); |
1129 | 292k | IGRAPH_ALLOW_INTERRUPTION(); |
1130 | | |
1131 | 292k | igraph_2wheap_clear(&Q); |
1132 | 292k | igraph_2wheap_push_with_index(&Q, source, -1.0); |
1133 | | |
1134 | 292k | nodes_reached = 0.0; |
1135 | | |
1136 | 3.20M | while (!igraph_2wheap_empty(&Q)) { |
1137 | 2.91M | igraph_int_t minnei = igraph_2wheap_max_index(&Q); |
1138 | 2.91M | igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q); |
1139 | 2.91M | igraph_vector_int_t *neis; |
1140 | 2.91M | igraph_int_t nlen; |
1141 | | |
1142 | 2.91M | if (mindist > ires) { |
1143 | 36.5k | ires = mindist; ifrom = source; ito = minnei; |
1144 | 36.5k | } |
1145 | 2.91M | nodes_reached++; |
1146 | | |
1147 | | /* Now check all neighbors of 'minnei' for a shorter path */ |
1148 | 2.91M | neis = igraph_inclist_get(&inclist, minnei); |
1149 | 2.91M | nlen = igraph_vector_int_size(neis); |
1150 | 9.01M | for (igraph_int_t j = 0; j < nlen; j++) { |
1151 | 6.10M | igraph_int_t edge = VECTOR(*neis)[j]; |
1152 | 6.10M | igraph_int_t tto = IGRAPH_OTHER(graph, edge, minnei); |
1153 | 6.10M | igraph_real_t altdist = mindist + VECTOR(*weights)[edge]; |
1154 | 6.10M | igraph_bool_t active = igraph_2wheap_has_active(&Q, tto); |
1155 | 6.10M | igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto); |
1156 | 6.10M | igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0; |
1157 | | |
1158 | 6.10M | if (!has) { |
1159 | | /* First finite distance */ |
1160 | 2.62M | IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist)); |
1161 | 3.48M | } else if (altdist < curdist) { |
1162 | | /* A shorter path */ |
1163 | 104k | igraph_2wheap_modify(&Q, tto, -altdist); |
1164 | 104k | } |
1165 | 6.10M | } |
1166 | | |
1167 | 2.91M | } /* !igraph_2wheap_empty(&Q) */ |
1168 | | |
1169 | | /* not connected, return infinity */ |
1170 | 292k | if (nodes_reached != no_of_nodes && !unconn) { |
1171 | 1.48k | ires = IGRAPH_INFINITY; |
1172 | 1.48k | ifrom = ito = -1; |
1173 | 1.48k | break; |
1174 | 1.48k | } |
1175 | | |
1176 | 292k | } /* source < no_of_nodes */ |
1177 | | |
1178 | | /* Compensate for the +1 that we have added to distances */ |
1179 | 3.05k | ires -= 1; |
1180 | | |
1181 | 3.05k | igraph_inclist_destroy(&inclist); |
1182 | 3.05k | igraph_2wheap_destroy(&Q); |
1183 | 3.05k | IGRAPH_FINALLY_CLEAN(2); |
1184 | | |
1185 | 3.05k | IGRAPH_PROGRESS("Weighted diameter: ", 100.0, NULL); |
1186 | | |
1187 | 3.05k | if (res) { |
1188 | 3.05k | *res = ires; |
1189 | 3.05k | } |
1190 | 3.05k | if (from) { |
1191 | 0 | *from = ifrom; |
1192 | 0 | } |
1193 | 3.05k | if (to) { |
1194 | 0 | *to = ito; |
1195 | 0 | } |
1196 | 3.05k | if ((vertex_path) || (edge_path)) { |
1197 | 0 | if (!isfinite(ires)) { |
1198 | 0 | if (vertex_path){ |
1199 | 0 | igraph_vector_int_clear(vertex_path); |
1200 | 0 | } |
1201 | 0 | if (edge_path) { |
1202 | 0 | igraph_vector_int_clear(edge_path); |
1203 | 0 | } |
1204 | 0 | } else { |
1205 | 0 | IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, |
1206 | 0 | /*vertices=*/ vertex_path, /*edges=*/ edge_path, |
1207 | 0 | ifrom, ito, |
1208 | 0 | weights, dirmode)); |
1209 | 0 | } |
1210 | 0 | } |
1211 | | |
1212 | 3.05k | return IGRAPH_SUCCESS; |
1213 | 3.05k | } |
1214 | | |
1215 | | /** |
1216 | | * \function igraph_diameter |
1217 | | * \brief Calculates the weighted diameter of a graph using Dijkstra's algorithm. |
1218 | | * |
1219 | | * This function computes the weighted diameter of a graph, defined as the longest |
1220 | | * weighted shortest path, or the maximum weighted eccentricity of the graph's |
1221 | | * vertices. A corresponding shortest path, as well as its endpoints, |
1222 | | * can also be optionally computed. |
1223 | | * |
1224 | | * </para><para> |
1225 | | * If the graph has no vertices, \c IGRAPH_NAN is returned. |
1226 | | * |
1227 | | * \param graph The input graph, can be directed or undirected. |
1228 | | * \param weights The edge weights of the graph. Can be \c NULL for an |
1229 | | * unweighted graph. Edges with positive infinite weight are ignored. |
1230 | | * \param res Pointer to a real number, if not \c NULL then it will contain |
1231 | | * the diameter (the actual distance). |
1232 | | * \param from Pointer to an integer, if not \c NULL it will be set to the |
1233 | | * source vertex of the diameter path. If the graph has no diameter path, |
1234 | | * it will be set to -1. |
1235 | | * \param to Pointer to an integer, if not \c NULL it will be set to the |
1236 | | * target vertex of the diameter path. If the graph has no diameter path, |
1237 | | * it will be set to -1. |
1238 | | * \param vertex_path Pointer to an initialized vector. If not \c NULL the actual |
1239 | | * longest geodesic path in terms of vertices will be stored here. The vector will be |
1240 | | * resized as needed. |
1241 | | * \param edge_path Pointer to an initialized vector. If not \c NULL the actual |
1242 | | * longest geodesic path in terms of edges will be stored here. The vector will be |
1243 | | * resized as needed. |
1244 | | * \param directed Boolean, whether to consider directed |
1245 | | * paths. Ignored for undirected graphs. |
1246 | | * \param unconn What to do if the graph is not connected. If |
1247 | | * \c true the longest geodesic within a component |
1248 | | * will be returned, otherwise \c IGRAPH_INFINITY is |
1249 | | * returned. |
1250 | | * \return Error code. |
1251 | | * |
1252 | | * Time complexity: O(|V||E|*log|E|), |V| is the number of vertices, |
1253 | | * |E| is the number of edges. |
1254 | | * |
1255 | | * \sa \ref igraph_radius() for the minimum eccentricity. |
1256 | | * |
1257 | | * \example examples/simple/igraph_diameter.c |
1258 | | */ |
1259 | | igraph_error_t igraph_diameter( |
1260 | | const igraph_t *graph, const igraph_vector_t *weights, igraph_real_t *res, |
1261 | | igraph_int_t *from, igraph_int_t *to, igraph_vector_int_t *vertex_path, |
1262 | | igraph_vector_int_t *edge_path, igraph_bool_t directed, igraph_bool_t unconn |
1263 | 3.05k | ) { |
1264 | 3.05k | if (weights) { |
1265 | 3.05k | return igraph_i_diameter_dijkstra( |
1266 | 3.05k | graph, weights, res, from, to, vertex_path, edge_path, |
1267 | 3.05k | directed, unconn |
1268 | 3.05k | ); |
1269 | 3.05k | } else { |
1270 | 0 | return igraph_i_diameter_unweighted( |
1271 | 0 | graph, res, from, to, vertex_path, edge_path, |
1272 | 0 | directed, unconn |
1273 | 0 | ); |
1274 | 0 | } |
1275 | 3.05k | } |
1276 | | |
1277 | | /** |
1278 | | * Temporarily removes all edges incident on the vertex with the given ID from |
1279 | | * the graph by setting the weights of these edges to infinity. |
1280 | | * |
1281 | | * \param graph the graph |
1282 | | * \param weights the weights of the edges of the graph |
1283 | | * \param vid the ID of the vertex to remove |
1284 | | * \param edges_removed vector that records the IDs of the edges that were |
1285 | | * "removed" (i.e. their weights were set to infinity) |
1286 | | * \param eids temporary vector that is used to retrieve the IDs of the |
1287 | | * incident edges, to make this function free of memory allocations |
1288 | | */ |
1289 | | static igraph_error_t igraph_i_semidelete_vertex( |
1290 | | const igraph_t *graph, igraph_vector_t *weights, |
1291 | | igraph_int_t vid, igraph_vector_int_t *edges_removed, |
1292 | | igraph_vector_int_t *eids |
1293 | 334k | ) { |
1294 | 334k | IGRAPH_CHECK(igraph_incident(graph, eids, vid, IGRAPH_ALL, IGRAPH_LOOPS)); |
1295 | | |
1296 | 334k | const igraph_int_t n = igraph_vector_int_size(eids); |
1297 | 1.16M | for (igraph_int_t j = 0; j < n; j++) { |
1298 | 833k | const igraph_int_t eid = VECTOR(*eids)[j]; |
1299 | 833k | IGRAPH_CHECK(igraph_vector_int_push_back(edges_removed, eid)); |
1300 | 833k | VECTOR(*weights)[eid] = IGRAPH_INFINITY; |
1301 | 833k | } |
1302 | | |
1303 | 334k | return IGRAPH_SUCCESS; |
1304 | 334k | } |
1305 | | |
1306 | | static igraph_bool_t igraph_i_has_edge_with_infinite_weight( |
1307 | | const igraph_vector_int_t* path, const igraph_vector_t* weights |
1308 | 17.6k | ) { |
1309 | 17.6k | const igraph_int_t n = weights ? igraph_vector_int_size(path) : 0; |
1310 | 94.6k | for (igraph_int_t i = 0; i < n; i++) { |
1311 | 89.3k | const igraph_int_t edge = VECTOR(*path)[i]; |
1312 | 89.3k | if (!isfinite(VECTOR(*weights)[edge])) { |
1313 | 12.3k | return true; |
1314 | 12.3k | } |
1315 | 89.3k | } |
1316 | | |
1317 | 5.31k | return false; |
1318 | 17.6k | } |
1319 | | |
1320 | | static igraph_real_t igraph_i_get_total_weight_of_path( |
1321 | | igraph_vector_int_t* path, const igraph_vector_t* weights |
1322 | 7.60k | ) { |
1323 | 7.60k | const igraph_int_t n = igraph_vector_int_size(path); |
1324 | 7.60k | igraph_real_t result; |
1325 | | |
1326 | 7.60k | if (weights) { |
1327 | 7.60k | result = 0; |
1328 | 326k | for (igraph_int_t i = 0; i < n; i++) { |
1329 | 318k | igraph_int_t edge = VECTOR(*path)[i]; |
1330 | 318k | result += VECTOR(*weights)[edge]; |
1331 | 318k | } |
1332 | 7.60k | } else { |
1333 | 0 | result = n; |
1334 | 0 | } |
1335 | | |
1336 | 7.60k | return result; |
1337 | 7.60k | } |
1338 | | |
1339 | | /** |
1340 | | * \function igraph_get_k_shortest_paths |
1341 | | * \brief k shortest paths between two vertices. |
1342 | | * |
1343 | | * This function returns the \p k shortest paths between two vertices, in order of |
1344 | | * increasing lengths. |
1345 | | * |
1346 | | * </para><para> |
1347 | | * Reference: |
1348 | | * |
1349 | | * </para><para> |
1350 | | * Yen, Jin Y.: |
1351 | | * An algorithm for finding shortest routes from all source nodes to a given |
1352 | | * destination in general networks. |
1353 | | * Quarterly of Applied Mathematics. 27 (4): 526–530. (1970) |
1354 | | * https://doi.org/10.1090/qam/253822 |
1355 | | * |
1356 | | * \param graph The graph object. |
1357 | | * \param weights The edge weights of the graph. Can be \c NULL for an |
1358 | | * unweighted graph. Infinite weights will be treated as missing |
1359 | | * edges. |
1360 | | * \param vertex_paths Pointer to an initialized list of integer vectors, the result |
1361 | | * will be stored here in \ref igraph_vector_int_t objects. Each vector |
1362 | | * object contains the vertex IDs along the <code>k</code>th shortest path |
1363 | | * between \p from and \p to, where \c k is the vector list index. May |
1364 | | * be \c NULL if the vertex paths are not needed. |
1365 | | * \param edge_paths Pointer to an initialized list of integer vectors, the result |
1366 | | * will be stored here in \ref igraph_vector_int_t objects. Each vector |
1367 | | * object contains the edge IDs along the <code>k</code>th shortest path |
1368 | | * between \p from and \p to, where \c k is the vector list index. May be |
1369 | | * \c NULL if the edge paths are not needed. |
1370 | | * \param k The number of paths. |
1371 | | * \param from The ID of the vertex from which the paths are calculated. |
1372 | | * \param to The ID of the vertex to which the paths are calculated. |
1373 | | * \param mode The type of paths to be used for the |
1374 | | * calculation in directed graphs. Possible values: |
1375 | | * \clist |
1376 | | * \cli IGRAPH_OUT |
1377 | | * The outgoing paths of \p from are calculated. |
1378 | | * \cli IGRAPH_IN |
1379 | | * The incoming paths of \p from are calculated. |
1380 | | * \cli IGRAPH_ALL |
1381 | | * The directed graph is considered as an |
1382 | | * undirected one for the computation. |
1383 | | * \endclist |
1384 | | * \return Error code: |
1385 | | * \clist |
1386 | | * \cli IGRAPH_ENOMEM |
1387 | | * Not enough memory for temporary data. |
1388 | | * \cli IGRAPH_EINVVID |
1389 | | * \p from or \p to is an invalid vertex id. |
1390 | | * \cli IGRAPH_EINVMODE |
1391 | | * Invalid mode argument. |
1392 | | * \cli IGRAPH_EINVAL |
1393 | | * Invalid argument. |
1394 | | * \endclist |
1395 | | * |
1396 | | * Time complexity: k |V| (|V| log|V| + |E|), where |V| is the number of vertices, |
1397 | | * and |E| is the number of edges. |
1398 | | * |
1399 | | * \sa \ref igraph_get_all_simple_paths(), \ref igraph_get_shortest_paths(), |
1400 | | * \ref igraph_get_shortest_paths_dijkstra() |
1401 | | */ |
1402 | | igraph_error_t igraph_get_k_shortest_paths( |
1403 | | const igraph_t *graph, const igraph_vector_t *weights, |
1404 | | igraph_vector_int_list_t *vertex_paths, |
1405 | | igraph_vector_int_list_t *edge_paths, |
1406 | | igraph_int_t k, igraph_int_t from, igraph_int_t to, |
1407 | | igraph_neimode_t mode |
1408 | 1.51k | ) { |
1409 | 1.51k | igraph_vector_int_list_t paths_pot; /* potential shortest paths */ |
1410 | 1.51k | igraph_int_t vertex_spur; |
1411 | 1.51k | igraph_vector_int_t path_spur, path_root, path_total, path_shortest; |
1412 | 1.51k | igraph_int_t nr_edges_root, i_path_current, i_path, edge_path_root, vertex_root_del; |
1413 | 1.51k | igraph_int_t n; |
1414 | 1.51k | igraph_vector_t current_weights; |
1415 | 1.51k | igraph_vector_int_t edges_removed; |
1416 | 1.51k | const igraph_int_t no_of_edges = igraph_ecount(graph); |
1417 | 1.51k | igraph_bool_t infinite_path, already_in_potential_paths; |
1418 | 1.51k | igraph_vector_int_t *path_0; |
1419 | 1.51k | igraph_vector_int_t eids; |
1420 | 1.51k | igraph_real_t path_weight, shortest_path_weight; |
1421 | 1.51k | igraph_int_t edge_paths_owned = 0; |
1422 | | |
1423 | 1.51k | if (!igraph_is_directed(graph) && (mode == IGRAPH_IN || mode == IGRAPH_OUT)) { |
1424 | 0 | mode = IGRAPH_ALL; |
1425 | 0 | } |
1426 | | |
1427 | 1.51k | if (vertex_paths) { |
1428 | 1.51k | igraph_vector_int_list_clear(vertex_paths); |
1429 | 1.51k | } |
1430 | | |
1431 | 1.51k | if (!edge_paths) { |
1432 | | /* We will need our own instance */ |
1433 | 1.51k | edge_paths = IGRAPH_CALLOC(1, igraph_vector_int_list_t); |
1434 | 1.51k | IGRAPH_CHECK_OOM(edge_paths, "Cannot allocate vector for storing edge paths."); |
1435 | 1.51k | IGRAPH_FINALLY(igraph_free, edge_paths); |
1436 | 1.51k | edge_paths_owned = 1; |
1437 | | |
1438 | 1.51k | IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(edge_paths, 0); |
1439 | 1.51k | edge_paths_owned = 2; |
1440 | 1.51k | } |
1441 | | |
1442 | 1.51k | igraph_vector_int_list_clear(edge_paths); |
1443 | | |
1444 | 1.51k | if (k == 0) { |
1445 | 0 | goto cleanup; |
1446 | 0 | } |
1447 | | |
1448 | 1.51k | IGRAPH_CHECK(igraph_vector_int_list_resize(edge_paths, 1)); |
1449 | 1.51k | path_0 = igraph_vector_int_list_get_ptr(edge_paths, 0); |
1450 | | |
1451 | 1.51k | IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, |
1452 | 1.51k | NULL, |
1453 | 1.51k | path_0, |
1454 | 1.51k | from, |
1455 | 1.51k | to, |
1456 | 1.51k | weights, |
1457 | 1.51k | mode)); |
1458 | | |
1459 | | /* Check if there's a path. */ |
1460 | 1.51k | infinite_path = igraph_i_has_edge_with_infinite_weight(path_0, weights); |
1461 | 1.51k | if (infinite_path || (from != to && igraph_vector_int_size(path_0) == 0)) { |
1462 | | /* No path found. */ |
1463 | 975 | igraph_vector_int_list_clear(edge_paths); |
1464 | 975 | goto cleanup; |
1465 | 975 | } |
1466 | | |
1467 | 542 | IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(&paths_pot, 0); |
1468 | 542 | IGRAPH_VECTOR_INT_INIT_FINALLY(&path_spur, 0); |
1469 | 542 | IGRAPH_VECTOR_INT_INIT_FINALLY(&path_root, 0); |
1470 | 542 | IGRAPH_VECTOR_INT_INIT_FINALLY(&path_total, 0); |
1471 | 542 | IGRAPH_VECTOR_INT_INIT_FINALLY(&edges_removed, 0); |
1472 | 542 | IGRAPH_VECTOR_INT_INIT_FINALLY(&eids, 0); |
1473 | 542 | IGRAPH_VECTOR_INIT_FINALLY(¤t_weights, no_of_edges); |
1474 | | |
1475 | | /* If weights are NULL we use a uniform weight vector where each edge has |
1476 | | * a weight of 1. Later on, we replace the weights of removed edges with |
1477 | | * infinities. Note that we work on a copy of the weight vector so the |
1478 | | * original vector remains intact. |
1479 | | */ |
1480 | 542 | if (weights) { |
1481 | 542 | igraph_vector_update(¤t_weights, weights); /* reserved */ |
1482 | 542 | } else { |
1483 | 0 | igraph_vector_fill(¤t_weights, 1); |
1484 | 0 | } |
1485 | | |
1486 | 1.46k | for (i_path_current = 1; i_path_current < k; i_path_current++) { |
1487 | 1.32k | igraph_vector_int_t *path_previous = igraph_vector_int_list_tail_ptr(edge_paths); |
1488 | 1.32k | igraph_int_t path_previous_length = igraph_vector_int_size(path_previous); |
1489 | 17.4k | for (nr_edges_root = 0; nr_edges_root < path_previous_length; nr_edges_root++) { |
1490 | | /* Determine spur node. */ |
1491 | 16.1k | if (mode == IGRAPH_OUT) { |
1492 | 0 | vertex_spur = IGRAPH_FROM(graph, VECTOR(*path_previous)[nr_edges_root]); |
1493 | 16.1k | } else if (mode == IGRAPH_IN) { |
1494 | 16.1k | vertex_spur = IGRAPH_TO(graph, VECTOR(*path_previous)[nr_edges_root]); |
1495 | 16.1k | } else { |
1496 | 0 | igraph_int_t eid = VECTOR(*path_previous)[nr_edges_root]; |
1497 | 0 | igraph_int_t vertex_spur_1 = IGRAPH_FROM(graph, eid); |
1498 | 0 | igraph_int_t vertex_spur_2 = IGRAPH_TO(graph, eid); |
1499 | 0 | igraph_int_t vertex_spur_3; |
1500 | 0 | igraph_int_t vertex_spur_4; |
1501 | 0 | if (nr_edges_root < path_previous_length-1) { |
1502 | 0 | igraph_int_t eid_next = VECTOR(*path_previous)[nr_edges_root + 1]; |
1503 | 0 | vertex_spur_3 = IGRAPH_FROM(graph, eid_next); |
1504 | 0 | vertex_spur_4 = IGRAPH_TO(graph, eid_next); |
1505 | 0 | } else { |
1506 | 0 | vertex_spur_3 = vertex_spur_4 = to; |
1507 | 0 | } |
1508 | 0 | if (vertex_spur_1 == vertex_spur_3 || vertex_spur_1 == vertex_spur_4) { |
1509 | 0 | vertex_spur = vertex_spur_2; |
1510 | 0 | } else { |
1511 | 0 | vertex_spur = vertex_spur_1; |
1512 | 0 | } |
1513 | 0 | } |
1514 | | |
1515 | | /* Determine root path. */ |
1516 | 16.1k | IGRAPH_CHECK(igraph_vector_int_resize(&path_root, nr_edges_root)); |
1517 | 350k | for (igraph_int_t i = 0; i < nr_edges_root; i++) { |
1518 | 334k | VECTOR(path_root)[i] = VECTOR(*path_previous)[i]; |
1519 | 334k | } |
1520 | | |
1521 | | /* Remove edges that are part of the previous shortest paths which share the same root path. */ |
1522 | 55.2k | for (i_path = 0; i_path < i_path_current; i_path++) { |
1523 | 39.1k | igraph_vector_int_t *path_check = igraph_vector_int_list_get_ptr(edge_paths, i_path); |
1524 | 39.1k | igraph_bool_t equal = true; |
1525 | 582k | for (igraph_int_t i = 0; i < nr_edges_root; i++) { |
1526 | 558k | if (VECTOR(path_root)[i] != VECTOR(*path_check)[i]) { |
1527 | 14.8k | equal = false; |
1528 | 14.8k | break; |
1529 | 14.8k | } |
1530 | 558k | } |
1531 | 39.1k | if (equal) { |
1532 | 24.2k | IGRAPH_CHECK(igraph_vector_int_push_back(&edges_removed, VECTOR(*path_check)[nr_edges_root])); |
1533 | 24.2k | VECTOR(current_weights)[VECTOR(*path_check)[nr_edges_root]] = IGRAPH_INFINITY; |
1534 | 24.2k | } |
1535 | 39.1k | } |
1536 | | |
1537 | | /* pseudocode: for each node rootPathNode in rootPath except spurNode: |
1538 | | * remove rootPathNode from Graph; |
1539 | | */ |
1540 | 350k | for (edge_path_root = 0; edge_path_root < nr_edges_root; edge_path_root++) { |
1541 | 334k | if (mode == IGRAPH_OUT) { |
1542 | 0 | vertex_root_del = IGRAPH_FROM(graph, VECTOR(path_root)[edge_path_root]); |
1543 | 334k | } else if (mode == IGRAPH_IN) { |
1544 | 334k | vertex_root_del = IGRAPH_TO(graph, VECTOR(path_root)[edge_path_root]); |
1545 | 334k | } else { |
1546 | 0 | igraph_int_t eid = VECTOR(*path_previous)[edge_path_root]; |
1547 | 0 | igraph_int_t eid_next = VECTOR(*path_previous)[edge_path_root + 1]; |
1548 | 0 | igraph_int_t vertex_root_del_1 = IGRAPH_FROM(graph, eid); |
1549 | 0 | igraph_int_t vertex_root_del_2 = IGRAPH_TO(graph, eid); |
1550 | 0 | igraph_int_t vertex_root_del_3 = IGRAPH_FROM(graph, eid_next); |
1551 | 0 | igraph_int_t vertex_root_del_4 = IGRAPH_TO(graph, eid_next); |
1552 | 0 | if (vertex_root_del_1 == vertex_root_del_3 || vertex_root_del_1 == vertex_root_del_4) { |
1553 | 0 | vertex_root_del = vertex_root_del_2; |
1554 | 0 | } else { |
1555 | 0 | vertex_root_del = vertex_root_del_1; |
1556 | 0 | } |
1557 | 0 | } |
1558 | | /* Remove vertex by setting incident edges to infinity */ |
1559 | 334k | IGRAPH_CHECK(igraph_i_semidelete_vertex( |
1560 | 334k | graph, ¤t_weights, vertex_root_del, &edges_removed, |
1561 | 334k | &eids |
1562 | 334k | )); |
1563 | 334k | } |
1564 | | |
1565 | | /* Determine spur path */ |
1566 | 16.1k | IGRAPH_CHECK(igraph_get_shortest_path_dijkstra(graph, |
1567 | 16.1k | NULL, |
1568 | 16.1k | &path_spur, |
1569 | 16.1k | vertex_spur, |
1570 | 16.1k | to, |
1571 | 16.1k | ¤t_weights, |
1572 | 16.1k | mode)); |
1573 | 16.1k | infinite_path = igraph_i_has_edge_with_infinite_weight(&path_spur, ¤t_weights); |
1574 | | |
1575 | | /* Add total (root + spur) path to potential paths if it's not in there yet. */ |
1576 | 16.1k | if (!infinite_path) { |
1577 | 3.79k | IGRAPH_CHECK(igraph_vector_int_update(&path_total, &path_root)); |
1578 | 3.79k | IGRAPH_CHECK(igraph_vector_int_append(&path_total, &path_spur)); |
1579 | | |
1580 | 3.79k | already_in_potential_paths = false; |
1581 | 3.79k | n = igraph_vector_int_list_size(&paths_pot); |
1582 | 216k | for (igraph_int_t i = 0; i < n; i++) { |
1583 | 213k | if (igraph_vector_int_all_e(&path_total, igraph_vector_int_list_get_ptr(&paths_pot, i))) { |
1584 | 467 | already_in_potential_paths = true; |
1585 | 467 | break; |
1586 | 467 | } |
1587 | 213k | } |
1588 | | |
1589 | 3.79k | if (!already_in_potential_paths) { |
1590 | 3.32k | IGRAPH_CHECK(igraph_vector_int_list_push_back_copy(&paths_pot, &path_total)); |
1591 | 3.32k | } |
1592 | 3.79k | } |
1593 | | |
1594 | | /* Cleanup */ |
1595 | 16.1k | n = igraph_vector_int_size(&edges_removed); |
1596 | 874k | for (igraph_int_t i = 0; i < n; i++) { |
1597 | 858k | VECTOR(current_weights)[VECTOR(edges_removed)[i]] = |
1598 | 858k | weights ? VECTOR(*weights)[VECTOR(edges_removed)[i]] : 1; |
1599 | 858k | } |
1600 | 16.1k | igraph_vector_int_clear(&edges_removed); |
1601 | 16.1k | } |
1602 | | |
1603 | | /* Add shortest potential path to shortest paths */ |
1604 | 1.32k | n = igraph_vector_int_list_size(&paths_pot); |
1605 | 1.32k | if (n == 0) { |
1606 | 405 | break; |
1607 | 405 | } |
1608 | | |
1609 | 920 | shortest_path_weight = igraph_i_get_total_weight_of_path( |
1610 | 920 | igraph_vector_int_list_get_ptr(&paths_pot, 0), weights |
1611 | 920 | ); |
1612 | 920 | i_path = 0; |
1613 | 7.60k | for (igraph_int_t i = 1; i < n; i++) { |
1614 | 6.68k | path_weight = igraph_i_get_total_weight_of_path( |
1615 | 6.68k | igraph_vector_int_list_get_ptr(&paths_pot, i), weights |
1616 | 6.68k | ); |
1617 | 6.68k | if (path_weight < shortest_path_weight) { |
1618 | 406 | i_path = i; |
1619 | 406 | shortest_path_weight = path_weight; |
1620 | 406 | } |
1621 | 6.68k | } |
1622 | | |
1623 | 920 | IGRAPH_CHECK(igraph_vector_int_list_remove_fast(&paths_pot, i_path, &path_shortest)); |
1624 | 920 | IGRAPH_CHECK(igraph_vector_int_list_push_back(edge_paths, &path_shortest)); |
1625 | 920 | } |
1626 | | |
1627 | 542 | igraph_vector_destroy(¤t_weights); |
1628 | 542 | igraph_vector_int_destroy(&eids); |
1629 | 542 | igraph_vector_int_destroy(&edges_removed); |
1630 | 542 | igraph_vector_int_destroy(&path_total); |
1631 | 542 | igraph_vector_int_destroy(&path_root); |
1632 | 542 | igraph_vector_int_destroy(&path_spur); |
1633 | 542 | igraph_vector_int_list_destroy(&paths_pot); |
1634 | 542 | IGRAPH_FINALLY_CLEAN(7); |
1635 | | |
1636 | 542 | if (vertex_paths) { |
1637 | 542 | const igraph_int_t no_of_edge_paths = igraph_vector_int_list_size(edge_paths); |
1638 | | |
1639 | 542 | IGRAPH_CHECK(igraph_vector_int_list_resize(vertex_paths, no_of_edge_paths)); |
1640 | 2.00k | for (igraph_int_t i = 0; i < no_of_edge_paths; i++) { |
1641 | 1.46k | igraph_vector_int_t* edge_path = igraph_vector_int_list_get_ptr(edge_paths, i); |
1642 | 1.46k | igraph_vector_int_t* vertex_path = igraph_vector_int_list_get_ptr(vertex_paths, i); |
1643 | 1.46k | IGRAPH_CHECK(igraph_vertex_path_from_edge_path(graph, from, edge_path, vertex_path, mode)); |
1644 | 1.46k | } |
1645 | 542 | } |
1646 | | |
1647 | 1.51k | cleanup: |
1648 | 1.51k | if (edge_paths_owned >= 2) { |
1649 | 1.51k | igraph_vector_int_list_destroy(edge_paths); |
1650 | 1.51k | IGRAPH_FINALLY_CLEAN(1); |
1651 | 1.51k | } |
1652 | 1.51k | if (edge_paths_owned >= 1) { |
1653 | 1.51k | igraph_free(edge_paths); |
1654 | 1.51k | IGRAPH_FINALLY_CLEAN(1); |
1655 | 1.51k | } |
1656 | | |
1657 | 1.51k | return IGRAPH_SUCCESS; |
1658 | 542 | } |