1 /* -*- mode: C -*- */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /*
4 IGraph library.
5 Copyright (C) 2005-2020 The igraph development team <igraph@igraph.org>
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21 #include "igraph_paths.h"
22
23 #include "igraph_adjlist.h"
24 #include "igraph_interface.h"
25 #include "igraph_dqueue.h"
26 #include "igraph_memory.h"
27 #include "igraph_progress.h"
28
29 #include "core/indheap.h"
30 #include "core/interruption.h"
31
32 #include <string.h>
33
34 /*****************************************************/
35 /***** Average path length and global efficiency *****/
36 /*****************************************************/
37
38 /* Computes the average of pairwise distances (used for igraph_average_path_length),
39 * or of inverse pairwise distances (used for igraph_global_efficiency), in an unweighted graph. */
igraph_i_average_path_length_unweighted(const igraph_t * graph,igraph_real_t * res,igraph_real_t * unconnected_pairs,const igraph_bool_t directed,const igraph_bool_t invert,const igraph_bool_t unconn)40 static int igraph_i_average_path_length_unweighted(
41 const igraph_t *graph,
42 igraph_real_t *res,
43 igraph_real_t *unconnected_pairs, /* if not NULL, will be set to the no. of non-connected ordered vertex pairs */
44 const igraph_bool_t directed,
45 const igraph_bool_t invert, /* average inverse distances instead of distances */
46 const igraph_bool_t unconn /* average over connected pairs instead of all pairs */)
47 {
48 long int no_of_nodes = igraph_vcount(graph);
49 long int source, j, n;
50 long int *already_added;
51 igraph_real_t no_of_pairs = no_of_nodes > 0 ? no_of_nodes * (no_of_nodes - 1.0) : 0.0; /* no. of ordered vertex pairs */
52 igraph_real_t no_of_conn_pairs = 0.0; /* no. of ordered pairs between which there is a path */
53
54 igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
55 igraph_vector_int_t *neis;
56 igraph_adjlist_t allneis;
57
58 *res = 0;
59 already_added = IGRAPH_CALLOC(no_of_nodes, long int);
60 if (already_added == 0) {
61 IGRAPH_ERROR("Average path length calculation failed", IGRAPH_ENOMEM);
62 }
63 IGRAPH_FINALLY(igraph_free, already_added);
64 IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
65
66 IGRAPH_CHECK(igraph_adjlist_init(
67 graph, &allneis,
68 directed ? IGRAPH_OUT : IGRAPH_ALL,
69 IGRAPH_LOOPS, IGRAPH_MULTIPLE
70 ));
71 IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
72
73 for (source = 0; source < no_of_nodes; source++) {
74 IGRAPH_CHECK(igraph_dqueue_push(&q, source));
75 IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
76 already_added[source] = source + 1;
77
78 IGRAPH_ALLOW_INTERRUPTION();
79
80 while (!igraph_dqueue_empty(&q)) {
81 long int actnode = (long int) igraph_dqueue_pop(&q);
82 long int actdist = (long int) igraph_dqueue_pop(&q);
83
84 neis = igraph_adjlist_get(&allneis, actnode);
85 n = igraph_vector_int_size(neis);
86 for (j = 0; j < n; j++) {
87 long int neighbor = (long int) VECTOR(*neis)[j];
88 if (already_added[neighbor] == source + 1) {
89 continue;
90 }
91 already_added[neighbor] = source + 1;
92 if (invert) {
93 *res += 1.0/(actdist + 1.0);
94 } else {
95 *res += actdist + 1.0;
96 }
97 no_of_conn_pairs += 1;
98 IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
99 IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
100 }
101 } /* while !igraph_dqueue_empty */
102 } /* for source < no_of_nodes */
103
104
105 if (no_of_pairs == 0) {
106 *res = IGRAPH_NAN; /* can't average zero items */
107 } else {
108 if (unconn) { /* average over connected pairs */
109 if (no_of_conn_pairs == 0) {
110 *res = IGRAPH_NAN; /* can't average zero items */
111 } else {
112 *res /= no_of_conn_pairs;
113 }
114 } else { /* average over all pairs */
115 /* no_of_conn_pairs < no_of_pairs implies that the graph is disconnected */
116 if (no_of_conn_pairs < no_of_pairs && ! invert) {
117 /* When invert=false, assume the distance between non-connected pairs to be infinity */
118 *res = IGRAPH_INFINITY;
119 } else {
120 /* When invert=true, assume the inverse distance between non-connected pairs
121 * to be zero. Therefore, no special treatment is needed for disconnected graphs. */
122 *res /= no_of_pairs;
123 }
124 }
125 }
126
127 if (unconnected_pairs)
128 *unconnected_pairs = no_of_pairs - no_of_conn_pairs;
129
130 /* clean */
131 IGRAPH_FREE(already_added);
132 igraph_dqueue_destroy(&q);
133 igraph_adjlist_destroy(&allneis);
134 IGRAPH_FINALLY_CLEAN(3);
135
136 return IGRAPH_SUCCESS;
137 }
138
139
140 /* Computes the average of pairwise distances (used for igraph_average_path_length_dijkstra),
141 * or of inverse pairwise distances (used for igraph_global_efficiency), in an unweighted graph.
142 * Uses Dijkstra's algorithm, therefore all weights must be non-negative.
143 */
igraph_i_average_path_length_dijkstra(const igraph_t * graph,igraph_real_t * res,igraph_real_t * unconnected_pairs,const igraph_vector_t * weights,const igraph_bool_t directed,const igraph_bool_t invert,const igraph_bool_t unconn)144 static int igraph_i_average_path_length_dijkstra(
145 const igraph_t *graph,
146 igraph_real_t *res,
147 igraph_real_t *unconnected_pairs,
148 const igraph_vector_t *weights,
149 const igraph_bool_t directed,
150 const igraph_bool_t invert, /* average inverse distances instead of distances */
151 const igraph_bool_t unconn /* average over connected pairs instead of all pairs */)
152 {
153
154 /* Implementation details. This is the basic Dijkstra algorithm,
155 with a binary heap. The heap is indexed, i.e. it stores not only
156 the distances, but also which vertex they belong to.
157
158 From now on we use a 2-way heap, so the distances can be queried
159 directly from the heap.
160
161 Dirty tricks:
162 - the opposite of the distance is stored in the heap, as it is a
163 maximum heap and we need a minimum heap.
164 - we don't use IGRAPH_INFINITY in the res matrix during the
165 computation, as IGRAPH_FINITE() might involve a function call
166 and we want to spare that. -1 will denote infinity instead.
167 */
168
169 long int no_of_nodes = igraph_vcount(graph);
170 long int no_of_edges = igraph_ecount(graph);
171 igraph_2wheap_t Q;
172 igraph_lazy_inclist_t inclist;
173 long int source, j;
174 igraph_real_t no_of_pairs;
175 igraph_real_t no_of_conn_pairs = 0.0; /* no. of ordered pairs between which there is a path */
176
177 if (!weights) {
178 return igraph_i_average_path_length_unweighted(graph, res, unconnected_pairs, directed, invert, unconn);
179 }
180
181 if (igraph_vector_size(weights) != no_of_edges) {
182 IGRAPH_ERRORF("Weight vector length (%ld) does not match the number of edges (%ld).",
183 IGRAPH_EINVAL, igraph_vector_size(weights), no_of_edges);
184 }
185 if (no_of_edges > 0) {
186 igraph_real_t min = igraph_vector_min(weights);
187 if (min < 0) {
188 IGRAPH_ERRORF("Weight vector must be non-negative, got %g.", IGRAPH_EINVAL, min);
189 }
190 else if (igraph_is_nan(min)) {
191 IGRAPH_ERROR("Weight vector must not contain NaN values.", IGRAPH_EINVAL);
192 }
193 }
194
195 /* Avoid returning a negative zero, which would be printed as -0 in tests. */
196 if (no_of_nodes > 0) {
197 no_of_pairs = no_of_nodes * (no_of_nodes - 1.0);
198 } else {
199 no_of_pairs = 0;
200 }
201
202 IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
203 IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
204 IGRAPH_CHECK(igraph_lazy_inclist_init(
205 graph, &inclist, directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS
206 ));
207 IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
208
209 *res = 0.0;
210
211 for (source = 0; source < no_of_nodes; ++source) {
212
213 IGRAPH_ALLOW_INTERRUPTION();
214
215 igraph_2wheap_clear(&Q);
216 igraph_2wheap_push_with_index(&Q, source, -1.0);
217
218 while (!igraph_2wheap_empty(&Q)) {
219 long int minnei = igraph_2wheap_max_index(&Q);
220 igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q);
221 igraph_vector_int_t *neis;
222 long int nlen;
223
224 if (minnei != source) {
225 if (invert) {
226 *res += 1.0/(mindist - 1.0);
227 } else {
228 *res += mindist - 1.0;
229 }
230 no_of_conn_pairs += 1;
231 }
232
233 /* Now check all neighbors of 'minnei' for a shorter path */
234 neis = igraph_lazy_inclist_get(&inclist, (igraph_integer_t) minnei);
235 nlen = igraph_vector_int_size(neis);
236 for (j = 0; j < nlen; j++) {
237 long int edge = (long int) VECTOR(*neis)[j];
238 long int tto = IGRAPH_OTHER(graph, edge, minnei);
239 igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
240 igraph_bool_t active = igraph_2wheap_has_active(&Q, tto);
241 igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto);
242 igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0;
243 if (!has) {
244 /* This is the first non-infinite distance */
245 IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist));
246 } else if (altdist < curdist) {
247 /* This is a shorter path */
248 IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist));
249 }
250 }
251 } /* !igraph_2wheap_empty(&Q) */
252 } /* for source < no_of_nodes */
253
254 if (no_of_pairs == 0) {
255 *res = IGRAPH_NAN; /* can't average zero items */
256 } else {
257 if (unconn) { /* average over connected pairs */
258 if (no_of_conn_pairs == 0) {
259 *res = IGRAPH_NAN; /* can't average zero items */
260 } else {
261 *res /= no_of_conn_pairs;
262 }
263 } else { /* average over all pairs */
264 /* no_of_conn_pairs < no_of_pairs implies that the graph is disconnected */
265 if (no_of_conn_pairs < no_of_pairs && ! invert) {
266 *res = IGRAPH_INFINITY;
267 } else {
268 *res /= no_of_pairs;
269 }
270 }
271 }
272
273 if (unconnected_pairs)
274 *unconnected_pairs = no_of_pairs - no_of_conn_pairs;
275
276 igraph_lazy_inclist_destroy(&inclist);
277 igraph_2wheap_destroy(&Q);
278 IGRAPH_FINALLY_CLEAN(2);
279
280 return IGRAPH_SUCCESS;
281 }
282
283
284 /**
285 * \ingroup structural
286 * \function igraph_average_path_length
287 * \brief Calculates the average unweighted shortest path length between all vertex pairs.
288 *
289 * </para><para>
290 * If no vertex pairs can be included in the calculation, for example because the graph
291 * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c TRUE,
292 * NaN is returned.
293 *
294 * \param graph The graph object.
295 * \param res Pointer to a real number, this will contain the result.
296 * \param unconn_pairs Pointer to a real number. If not a null pointer, the number of
297 * ordered vertex pairs where the second vertex is unreachable from the first one
298 * will be stored here.
299 * \param directed Boolean, whether to consider directed
300 * paths. Ignored for undirected graphs.
301 * \param unconn What to do if the graph is not connected. If
302 * \c TRUE, only those vertex pairs will be included in the calculation
303 * between which there is a path. If \c FALSE, \c IGRAPH_INFINITY is returned
304 * for disconnected graphs.
305 * \return Error code:
306 * \c IGRAPH_ENOMEM, not enough memory for data structures
307 *
308 * Time complexity: O(|V| |E|), the number of vertices times the number of edges.
309 *
310 * \sa \ref igraph_average_path_length_dijkstra() for the weighted version.
311 *
312 * \example examples/simple/igraph_average_path_length.c
313 */
314
igraph_average_path_length(const igraph_t * graph,igraph_real_t * res,igraph_real_t * unconn_pairs,igraph_bool_t directed,igraph_bool_t unconn)315 int igraph_average_path_length(const igraph_t *graph,
316 igraph_real_t *res, igraph_real_t *unconn_pairs,
317 igraph_bool_t directed, igraph_bool_t unconn)
318 {
319 return igraph_i_average_path_length_unweighted(graph, res, unconn_pairs, directed, /* invert= */ 0, unconn);
320 }
321
322
323 /**
324 * \ingroup structural
325 * \function igraph_average_path_length_dijkstra
326 * \brief Calculates the average weighted shortest path length between all vertex pairs.
327 *
328 * </para><para>
329 * If no vertex pairs can be included in the calculation, for example because the graph
330 * has fewer than two vertices, or if the graph has no edges and \c unconn is set to \c TRUE,
331 * NaN is returned.
332 *
333 * </para><para>
334 * All distinct ordered vertex pairs are taken into account.
335 *
336 * \param graph The graph object.
337 * \param res Pointer to a real number, this will contain the result.
338 * \param unconn_pairs Pointer to a real number. If not a null pointer, the number of
339 * ordered vertex pairs where the second vertex is unreachable from the first one
340 * will be stored here.
341 * \param weights The edge weights. All edge weights must be
342 * non-negative for Dijkstra's algorithm to work. Additionally, no
343 * edge weight may be NaN. If either case does not hold, an error
344 * is returned. If this is a null pointer, then the unweighted
345 * version, \ref igraph_average_path_length() is called.
346 * \param directed Boolean, whether to consider directed paths.
347 * Ignored for undirected graphs.
348 * \param unconn If \c TRUE, only those pairs are considered for the calculation
349 * between which there is a path. If \c FALSE, \c IGRAPH_INFINITY is returned
350 * for disconnected graphs.
351 * \return Error code:
352 * \clist
353 * \cli IGRAPH_ENOMEM
354 * not enough memory for data structures
355 * \cli IGRAPH_EINVAL
356 * invalid weight vector
357 * \endclist
358 *
359 * Time complexity: O(|V| |E| log|E| + |V|), where |V| is the number of
360 * vertices and |E| is the number of edges.
361 *
362 * \sa \ref igraph_average_path_length() for a slightly faster unweighted version.
363 *
364 * \example examples/simple/igraph_grg_game.c
365 */
366
igraph_average_path_length_dijkstra(const igraph_t * graph,igraph_real_t * res,igraph_real_t * unconn_pairs,const igraph_vector_t * weights,igraph_bool_t directed,igraph_bool_t unconn)367 int igraph_average_path_length_dijkstra(const igraph_t *graph,
368 igraph_real_t *res, igraph_real_t *unconn_pairs,
369 const igraph_vector_t *weights,
370 igraph_bool_t directed, igraph_bool_t unconn)
371 {
372 return igraph_i_average_path_length_dijkstra(graph, res, unconn_pairs, weights, directed, /* invert= */ 0, unconn);
373 }
374
375
376 /**
377 * \ingroup structural
378 * \function igraph_global_efficiency
379 * \brief Calculates the global efficiency of a network.
380 *
381 * </para><para>
382 * The global efficiency of a network is defined as the average of inverse distances
383 * between all pairs of vertices: <code>E_g = 1/(N*(N-1)) sum_{i!=j} 1/d_ij</code>,
384 * where N is the number of vertices.
385 * The inverse distance between pairs that are not reachable from each other is considered
386 * to be zero. For graphs with fewer than 2 vertices, NaN is returned.
387 *
388 * </para><para>
389 * Reference:
390 * V. Latora and M. Marchiori,
391 * Efficient Behavior of Small-World Networks,
392 * Phys. Rev. Lett. 87, 198701 (2001).
393 * https://dx.doi.org/10.1103/PhysRevLett.87.198701
394 *
395 * \param graph The graph object.
396 * \param res Pointer to a real number, this will contain the result.
397 * \param weights The edge weights. All edge weights must be
398 * non-negative for Dijkstra's algorithm to work. Additionally, no
399 * edge weight may be NaN. If either case does not hold, an error
400 * is returned. If this is a null pointer, then the unweighted
401 * version, \ref igraph_average_path_length() is used in calculating
402 * the global efficiency.
403 * \param directed Boolean, whether to consider directed paths.
404 * Ignored for undirected graphs.
405 * \return Error code:
406 * \clist
407 * \cli IGRAPH_ENOMEM
408 * not enough memory for data structures
409 * \cli IGRAPH_EINVAL
410 * invalid weight vector
411 * \endclist
412 *
413 * Time complexity: O(|V| |E| log|E| + |V|) for weighted graphs and
414 * O(|V| |E|) for unweighted ones. |V| denotes the number of
415 * vertices and |E| denotes the number of edges.
416 *
417 */
418
igraph_global_efficiency(const igraph_t * graph,igraph_real_t * res,const igraph_vector_t * weights,igraph_bool_t directed)419 int igraph_global_efficiency(const igraph_t *graph, igraph_real_t *res,
420 const igraph_vector_t *weights,
421 igraph_bool_t directed)
422 {
423 return igraph_i_average_path_length_dijkstra(graph, res, NULL, weights, directed, /* invert= */ 1, /* unconn= */ 0);
424 }
425
426
427 /****************************/
428 /***** Local efficiency *****/
429 /****************************/
430
igraph_i_local_efficiency_unweighted(const igraph_t * graph,const igraph_adjlist_t * adjlist,igraph_dqueue_t * q,long int * already_counted,igraph_vector_t * vertex_neis,igraph_vector_char_t * nei_mask,igraph_real_t * res,igraph_integer_t vertex,igraph_neimode_t mode)431 static int igraph_i_local_efficiency_unweighted(
432 const igraph_t *graph,
433 const igraph_adjlist_t *adjlist,
434 igraph_dqueue_t *q,
435 long int *already_counted,
436 igraph_vector_t *vertex_neis,
437 igraph_vector_char_t *nei_mask,
438 igraph_real_t *res,
439 igraph_integer_t vertex,
440 igraph_neimode_t mode)
441 {
442
443 long int no_of_nodes = igraph_vcount(graph);
444 long int vertex_neis_size;
445 long int neighbor_count; /* unlike 'vertex_neis_size', 'neighbor_count' does not count self-loops and multi-edges */
446 long int i, j;
447
448 igraph_dqueue_clear(q);
449 memset(already_counted, 0, no_of_nodes * sizeof(long int));
450
451 IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode));
452 vertex_neis_size = igraph_vector_size(vertex_neis);
453
454 igraph_vector_char_fill(nei_mask, 0);
455 neighbor_count = 0;
456 for (i=0; i < vertex_neis_size; ++i) {
457 long int v = VECTOR(*vertex_neis)[i];
458 if (v != vertex && ! VECTOR(*nei_mask)[v]) {
459 VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */
460 neighbor_count++;
461 }
462 }
463
464 *res = 0.0;
465
466 /* when the neighbor count is smaller than 2, we return 0.0 */
467 if (neighbor_count < 2) {
468 return IGRAPH_SUCCESS;
469 }
470
471 for (i=0; i < vertex_neis_size; ++i) {
472 long int source = VECTOR(*vertex_neis)[i];
473 long int reached = 0;
474
475 IGRAPH_ALLOW_INTERRUPTION();
476
477 if (source == vertex)
478 continue;
479
480 if (VECTOR(*nei_mask)[source] == 2)
481 continue;
482 VECTOR(*nei_mask)[source] = 2; /* mark neighbour as already processed */
483
484 IGRAPH_CHECK(igraph_dqueue_push(q, source));
485 IGRAPH_CHECK(igraph_dqueue_push(q, 0));
486 already_counted[source] = source + 1;
487
488 while (!igraph_dqueue_empty(q)) {
489 igraph_vector_int_t *act_neis;
490 long int act_neis_size;
491 long int act = (long int) igraph_dqueue_pop(q);
492 long int actdist = (long int) igraph_dqueue_pop(q);
493
494 if (act != source && VECTOR(*nei_mask)[act]) {
495 *res += 1.0 / actdist;
496 reached++;
497 if (reached == neighbor_count) {
498 igraph_dqueue_clear(q);
499 break;
500 }
501 }
502
503 act_neis = igraph_adjlist_get(adjlist, act);
504 act_neis_size = igraph_vector_int_size(act_neis);
505 for (j = 0; j < act_neis_size; j++) {
506 long int neighbor = (long int) VECTOR(*act_neis)[j];
507
508 if (neighbor == vertex || already_counted[neighbor] == i + 1)
509 continue;
510
511 already_counted[neighbor] = i + 1;
512 IGRAPH_CHECK(igraph_dqueue_push(q, neighbor));
513 IGRAPH_CHECK(igraph_dqueue_push(q, actdist + 1));
514 }
515 }
516 }
517
518 *res /= neighbor_count * (neighbor_count - 1.0);
519
520 return IGRAPH_SUCCESS;
521 }
522
igraph_i_local_efficiency_dijkstra(const igraph_t * graph,igraph_lazy_inclist_t * inclist,igraph_2wheap_t * Q,igraph_vector_t * vertex_neis,igraph_vector_char_t * nei_mask,igraph_real_t * res,igraph_integer_t vertex,igraph_neimode_t mode,const igraph_vector_t * weights)523 static int igraph_i_local_efficiency_dijkstra(
524 const igraph_t *graph,
525 igraph_lazy_inclist_t *inclist,
526 igraph_2wheap_t *Q,
527 igraph_vector_t *vertex_neis,
528 igraph_vector_char_t *nei_mask, /* true if the corresponding node is a neighbour of 'vertex' */
529 igraph_real_t *res,
530 igraph_integer_t vertex,
531 igraph_neimode_t mode,
532 const igraph_vector_t *weights)
533 {
534
535 /* Implementation details. This is the basic Dijkstra algorithm,
536 with a binary heap. The heap is indexed, i.e. it stores not only
537 the distances, but also which vertex they belong to.
538
539 From now on we use a 2-way heap, so the distances can be queried
540 directly from the heap.
541
542 Dirty tricks:
543 - the opposite of the distance is stored in the heap, as it is a
544 maximum heap and we need a minimum heap.
545 - we don't use IGRAPH_INFINITY in the res matrix during the
546 computation, as IGRAPH_FINITE() might involve a function call
547 and we want to spare that. -1 will denote infinity instead.
548 */
549
550 long int i, j;
551 long int vertex_neis_size;
552 long int neighbor_count; /* unlike 'inc_edges_size', 'neighbor_count' does not count self-loops or multi-edges */
553
554 IGRAPH_CHECK(igraph_neighbors(graph, vertex_neis, vertex, mode));
555 vertex_neis_size = igraph_vector_size(vertex_neis);
556
557 igraph_vector_char_fill(nei_mask, 0);
558 neighbor_count = 0;
559 for (i=0; i < vertex_neis_size; ++i) {
560 long int v = VECTOR(*vertex_neis)[i];
561 if (v != vertex && ! VECTOR(*nei_mask)[v]) {
562 VECTOR(*nei_mask)[v] = 1; /* mark as unprocessed neighbour */
563 neighbor_count++;
564 }
565 }
566
567 *res = 0.0;
568
569 /* when the neighbor count is smaller than 2, we return 0.0 */
570 if (neighbor_count < 2) {
571 return IGRAPH_SUCCESS;
572 }
573
574 for (i=0; i < vertex_neis_size; ++i) {
575 long int source = VECTOR(*vertex_neis)[i];
576 long int reached = 0;
577
578 IGRAPH_ALLOW_INTERRUPTION();
579
580 if (source == vertex)
581 continue;
582
583 /* avoid processing a neighbour twice in multigraphs */
584 if (VECTOR(*nei_mask)[source] == 2)
585 continue;
586 VECTOR(*nei_mask)[source] = 2; /* mark as already processed */
587
588 igraph_2wheap_clear(Q);
589 igraph_2wheap_push_with_index(Q, source, -1.0);
590
591 while (!igraph_2wheap_empty(Q)) {
592 long int minnei = igraph_2wheap_max_index(Q);
593 igraph_real_t mindist = -igraph_2wheap_deactivate_max(Q);
594 igraph_vector_int_t *neis;
595 long int nlen;
596
597 if (minnei != source && VECTOR(*nei_mask)[minnei]) {
598 *res += 1.0/(mindist - 1.0);
599 reached++;
600 if (reached == neighbor_count) {
601 igraph_2wheap_clear(Q);
602 break;
603 }
604 }
605
606 /* Now check all neighbors of 'minnei' for a shorter path */
607 neis = igraph_lazy_inclist_get(inclist, (igraph_integer_t) minnei);
608 nlen = igraph_vector_int_size(neis);
609 for (j = 0; j < nlen; j++) {
610 igraph_real_t altdist, curdist;
611 igraph_bool_t active, has;
612 long int edge = (long int) VECTOR(*neis)[j];
613 long int tto = IGRAPH_OTHER(graph, edge, minnei);
614
615 if (tto == vertex)
616 continue;
617
618 altdist = mindist + VECTOR(*weights)[edge];
619 active = igraph_2wheap_has_active(Q, tto);
620 has = igraph_2wheap_has_elem(Q, tto);
621 curdist = active ? -igraph_2wheap_get(Q, tto) : 0.0;
622 if (!has) {
623 /* This is the first non-infinite distance */
624 IGRAPH_CHECK(igraph_2wheap_push_with_index(Q, tto, -altdist));
625 } else if (altdist < curdist) {
626 /* This is a shorter path */
627 IGRAPH_CHECK(igraph_2wheap_modify(Q, tto, -altdist));
628 }
629 }
630
631 } /* !igraph_2wheap_empty(&Q) */
632
633 }
634
635 *res /= neighbor_count * (neighbor_count - 1.0);
636
637 return IGRAPH_SUCCESS;
638 }
639
640
641 /**
642 * \ingroup structural
643 * \function igraph_local_efficiency
644 * \brief Calculates the local efficiency around each vertex in a network.
645 *
646 * </para><para>
647 * The local efficiency of a network around a vertex is defined as follows:
648 * We remove the vertex and compute the distances (shortest path lengths) between
649 * its neighbours through the rest of the network. The local efficiency around the
650 * removed vertex is the average of the inverse of these distances.
651 *
652 * </para><para>
653 * The inverse distance between two vertices which are not reachable from each other
654 * is considered to be zero. The local efficiency around a vertex with fewer than two
655 * neighbours is taken to be zero by convention.
656 *
657 * </para><para>
658 * Reference:
659 * I. Vragović, E. Louis, and A. Díaz-Guilera,
660 * Efficiency of informational transfer in regular and complex networks,
661 * Phys. Rev. E 71, 1 (2005).
662 * http://dx.doi.org/10.1103/PhysRevE.71.036122
663 *
664 * \param graph The graph object.
665 * \param res Pointer to an initialized vector, this will contain the result.
666 * \param vids The vertices around which the local efficiency will be calculated.
667 * \param weights The edge weights. All edge weights must be
668 * non-negative. Additionally, no edge weight may be NaN. If either
669 * case does not hold, an error is returned. If this is a null
670 * pointer, then the unweighted version,
671 * \ref igraph_average_path_length() is called.
672 * \param directed Boolean, whether to consider directed paths.
673 * Ignored for undirected graphs.
674 * \param mode How to determine the local neighborhood of each vertex
675 * in directed graphs. Ignored in undirected graphs.
676 * \clist
677 * \cli IGRAPH_ALL
678 * take both in- and out-neighbours;
679 * this is a reasonable default for high-level interfaces.
680 * \cli IGRAPH_OUT
681 * take only out-neighbours
682 * \cli IGRAPH_IN
683 * take only in-neighbours
684 * \endclist
685 * \return Error code:
686 * \clist
687 * \cli IGRAPH_ENOMEM
688 * not enough memory for data structures
689 * \cli IGRAPH_EINVAL
690 * invalid weight vector
691 * \endclist
692 *
693 * Time complexity: O(|E|^2 log|E|) for weighted graphs and
694 * O(|E|^2) for unweighted ones. |E| denotes the number of edges.
695 *
696 * \sa \ref igraph_average_local_efficiency()
697 *
698 */
699
igraph_local_efficiency(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids,const igraph_vector_t * weights,igraph_bool_t directed,igraph_neimode_t mode)700 int igraph_local_efficiency(const igraph_t *graph, igraph_vector_t *res,
701 const igraph_vs_t vids,
702 const igraph_vector_t *weights,
703 igraph_bool_t directed, igraph_neimode_t mode)
704 {
705 long int no_of_nodes = igraph_vcount(graph);
706 long int no_of_edges = igraph_ecount(graph);
707 long int nodes_to_calc; /* no. of vertices includes in computation */
708 igraph_vit_t vit;
709 igraph_vector_t vertex_neis;
710 igraph_vector_char_t nei_mask;
711 long int i;
712
713 /* 'nei_mask' is a vector indexed by vertices. The meaning of its values is as follows:
714 * 0: not a neighbour of 'vertex'
715 * 1: a not-yet-processed neighbour of 'vertex'
716 * 2: an already processed neighbour of 'vertex'
717 *
718 * Marking neighbours of already processed is necessary to avoid processing them more
719 * than once in multigraphs.
720 */
721 IGRAPH_CHECK(igraph_vector_char_init(&nei_mask, no_of_nodes));
722 IGRAPH_FINALLY(igraph_vector_char_destroy, &nei_mask);
723 IGRAPH_VECTOR_INIT_FINALLY(&vertex_neis, 0);
724
725 IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
726 IGRAPH_FINALLY(igraph_vit_destroy, &vit);
727
728 nodes_to_calc = IGRAPH_VIT_SIZE(vit);
729
730 IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
731
732 if (! weights) /* unweighted case */
733 {
734 long int *already_counted;
735 igraph_adjlist_t adjlist;
736 igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
737
738 already_counted = IGRAPH_CALLOC(no_of_nodes, long int);
739 if (already_counted == 0) {
740 IGRAPH_ERROR("Local efficiency calculation failed", IGRAPH_ENOMEM);
741 }
742 IGRAPH_FINALLY(igraph_free, already_counted);
743
744 IGRAPH_CHECK(igraph_adjlist_init(
745 graph, &adjlist,
746 directed ? IGRAPH_OUT : IGRAPH_ALL,
747 IGRAPH_LOOPS, IGRAPH_MULTIPLE
748 ));
749 IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist);
750
751 IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
752
753 for (IGRAPH_VIT_RESET(vit), i=0;
754 ! IGRAPH_VIT_END(vit);
755 IGRAPH_VIT_NEXT(vit), i++)
756 {
757 IGRAPH_CHECK(igraph_i_local_efficiency_unweighted(
758 graph, &adjlist,
759 &q, already_counted, &vertex_neis, &nei_mask,
760 &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode));
761 }
762
763 igraph_dqueue_destroy(&q);
764 igraph_adjlist_destroy(&adjlist);
765 IGRAPH_FREE(already_counted);
766 IGRAPH_FINALLY_CLEAN(3);
767 }
768 else /* weighted case */
769 {
770 igraph_lazy_inclist_t inclist;
771 igraph_2wheap_t Q;
772
773 if (igraph_vector_size(weights) != no_of_edges) {
774 IGRAPH_ERROR("Weight vector length does not match the number of edges", IGRAPH_EINVAL);
775 }
776 if (no_of_edges > 0) {
777 igraph_real_t min = igraph_vector_min(weights);
778 if (min < 0) {
779 IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL);
780 }
781 else if (igraph_is_nan(min)) {
782 IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL);
783 }
784 }
785
786 IGRAPH_CHECK(igraph_lazy_inclist_init(
787 graph, &inclist, directed ? IGRAPH_OUT : IGRAPH_ALL, IGRAPH_LOOPS
788 ));
789 IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &inclist);
790 IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
791 IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
792
793 for (IGRAPH_VIT_RESET(vit), i=0;
794 ! IGRAPH_VIT_END(vit);
795 IGRAPH_VIT_NEXT(vit), i++)
796 {
797 IGRAPH_CHECK(igraph_i_local_efficiency_dijkstra(
798 graph, &inclist,
799 &Q, &vertex_neis, &nei_mask,
800 &(VECTOR(*res)[i]), IGRAPH_VIT_GET(vit), mode, weights));
801 }
802
803 igraph_2wheap_destroy(&Q);
804 igraph_lazy_inclist_destroy(&inclist);
805 IGRAPH_FINALLY_CLEAN(2);
806 }
807
808 igraph_vit_destroy(&vit);
809 igraph_vector_destroy(&vertex_neis);
810 igraph_vector_char_destroy(&nei_mask);
811 IGRAPH_FINALLY_CLEAN(3);
812
813 return IGRAPH_SUCCESS;
814 }
815
816
817 /**
818 * \ingroup structural
819 * \function igraph_average_local_efficiency
820 * \brief Calculates the average local efficiency in a network.
821 *
822 * For the null graph, zero is returned by convention.
823 *
824 * \param graph The graph object.
825 * \param res Pointer to a real number, this will contain the result.
826 * \param weights The edge weights. They must be all non-negative.
827 * If a null pointer is given, all weights are assumed to be 1.
828 * \param directed Boolean, whether to consider directed paths.
829 * Ignored for undirected graphs.
830 * \param mode How to determine the local neighborhood of each vertex
831 * in directed graphs. Ignored in undirected graphs.
832 * \clist
833 * \cli IGRAPH_ALL
834 * take both in- and out-neighbours;
835 * this is a reasonable default for high-level interfaces.
836 * \cli IGRAPH_OUT
837 * take only out-neighbours
838 * \cli IGRAPH_IN
839 * take only in-neighbours
840 * \endclist
841 * \return Error code:
842 * \clist
843 * \cli IGRAPH_ENOMEM
844 * not enough memory for data structures
845 * \cli IGRAPH_EINVAL
846 * invalid weight vector
847 * \endclist
848 *
849 * Time complexity: O(|E|^2 log|E|) for weighted graphs and
850 * O(|E|^2) for unweighted ones. |E| denotes the number of edges.
851 *
852 * \sa \ref igraph_local_efficiency()
853 *
854 */
855
igraph_average_local_efficiency(const igraph_t * graph,igraph_real_t * res,const igraph_vector_t * weights,igraph_bool_t directed,igraph_neimode_t mode)856 int igraph_average_local_efficiency(const igraph_t *graph, igraph_real_t *res,
857 const igraph_vector_t *weights,
858 igraph_bool_t directed, igraph_neimode_t mode)
859 {
860 long int no_of_nodes = igraph_vcount(graph);
861 igraph_vector_t local_eff;
862
863 /* If there are fewer than 3 vertices, no vertex has more than one neighbour, thus all
864 local efficiencies are zero. For the null graph, we return zero by convention. */
865 if (no_of_nodes < 3) {
866 *res = 0;
867 return IGRAPH_SUCCESS;
868 }
869
870 IGRAPH_VECTOR_INIT_FINALLY(&local_eff, no_of_nodes);
871
872 IGRAPH_CHECK(igraph_local_efficiency(graph, &local_eff, igraph_vss_all(), weights, directed, mode));
873
874 *res = igraph_vector_sum(&local_eff);
875 *res /= no_of_nodes;
876
877 igraph_vector_destroy(&local_eff);
878 IGRAPH_FINALLY_CLEAN(1);
879
880 return IGRAPH_SUCCESS;
881 }
882
883
884 /***************************/
885 /***** Graph diameter ******/
886 /***************************/
887
888 /**
889 * \ingroup structural
890 * \function igraph_diameter
891 * \brief Calculates the diameter of a graph (longest geodesic).
892 *
893 * The diameter of a graph is the length of the longest shortest path it has.
894 * This function computes both the diameter, as well as the corresponding path.
895 * The diameter of the null graph is considered be infinity by convention.
896 *
897 * If the graph has no vertices, \c IGRAPH_NAN is returned.
898 *
899 * \param graph The graph object.
900 * \param pres Pointer to a real number, if not \c NULL then it will contain
901 * the diameter (the actual distance).
902 * \param pfrom Pointer to an integer, if not \c NULL it will be set to the
903 * source vertex of the diameter path. If the graph has no diameter path,
904 * it will be set to -1.
905 * \param pto Pointer to an integer, if not \c NULL it will be set to the
906 * target vertex of the diameter path. If the graph has no diameter path,
907 * it will be set to -1.
908 * \param path Pointer to an initialized vector. If not \c NULL the actual
909 * longest geodesic path will be stored here. The vector will be
910 * resized as needed.
911 * \param directed Boolean, whether to consider directed
912 * paths. Ignored for undirected graphs.
913 * \param unconn What to do if the graph is not connected. If
914 * \c TRUE the longest geodesic within a component
915 * will be returned, otherwise \c IGRAPH_INFINITY is returned.
916 * \return Error code:
917 * \c IGRAPH_ENOMEM, not enough memory for
918 * temporary data.
919 *
920 * Time complexity: O(|V||E|), the
921 * number of vertices times the number of edges.
922 *
923 * \sa \ref igraph_diameter_dijkstra()
924 *
925 * \example examples/simple/igraph_diameter.c
926 */
927
igraph_diameter(const igraph_t * graph,igraph_real_t * pres,igraph_integer_t * pfrom,igraph_integer_t * pto,igraph_vector_t * path,igraph_bool_t directed,igraph_bool_t unconn)928 int igraph_diameter(const igraph_t *graph, igraph_real_t *pres,
929 igraph_integer_t *pfrom, igraph_integer_t *pto,
930 igraph_vector_t *path,
931 igraph_bool_t directed, igraph_bool_t unconn) {
932
933 long int no_of_nodes = igraph_vcount(graph);
934 long int i, j, n;
935 long int *already_added;
936 long int nodes_reached;
937 long int from = 0, to = 0;
938 igraph_real_t res = 0;
939
940 igraph_dqueue_t q = IGRAPH_DQUEUE_NULL;
941 igraph_vector_int_t *neis;
942 igraph_neimode_t dirmode;
943 igraph_adjlist_t allneis;
944
945 /* See https://github.com/igraph/igraph/issues/1538#issuecomment-724071857
946 * for why we return NaN for the null graph. */
947 if (no_of_nodes == 0) {
948 if (pres) {
949 *pres = IGRAPH_NAN;
950 }
951 if (path) {
952 igraph_vector_clear(path);
953 }
954 if (pfrom) {
955 *pfrom = -1;
956 }
957 if (pto) {
958 *pto = -1;
959 }
960 return IGRAPH_SUCCESS;
961 }
962
963 if (directed) {
964 dirmode = IGRAPH_OUT;
965 } else {
966 dirmode = IGRAPH_ALL;
967 }
968 already_added = IGRAPH_CALLOC(no_of_nodes, long int);
969 if (already_added == 0) {
970 IGRAPH_ERROR("diameter failed", IGRAPH_ENOMEM);
971 }
972 IGRAPH_FINALLY(igraph_free, already_added);
973 IGRAPH_DQUEUE_INIT_FINALLY(&q, 100);
974
975 IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, dirmode, IGRAPH_LOOPS, IGRAPH_MULTIPLE));
976 IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
977
978 for (i = 0; i < no_of_nodes; i++) {
979 nodes_reached = 1;
980 IGRAPH_CHECK(igraph_dqueue_push(&q, i));
981 IGRAPH_CHECK(igraph_dqueue_push(&q, 0));
982 already_added[i] = i + 1;
983
984 IGRAPH_PROGRESS("Diameter: ", 100.0 * i / no_of_nodes, NULL);
985
986 IGRAPH_ALLOW_INTERRUPTION();
987
988 while (!igraph_dqueue_empty(&q)) {
989 long int actnode = (long int) igraph_dqueue_pop(&q);
990 long int actdist = (long int) igraph_dqueue_pop(&q);
991 if (actdist > res) {
992 res = actdist;
993 from = i;
994 to = actnode;
995 }
996
997 neis = igraph_adjlist_get(&allneis, actnode);
998 n = igraph_vector_int_size(neis);
999 for (j = 0; j < n; j++) {
1000 long int neighbor = (long int) VECTOR(*neis)[j];
1001 if (already_added[neighbor] == i + 1) {
1002 continue;
1003 }
1004 already_added[neighbor] = i + 1;
1005 nodes_reached++;
1006 IGRAPH_CHECK(igraph_dqueue_push(&q, neighbor));
1007 IGRAPH_CHECK(igraph_dqueue_push(&q, actdist + 1));
1008 }
1009 } /* while !igraph_dqueue_empty */
1010
1011 /* not connected, return IGRAPH_INFINITY */
1012 if (nodes_reached != no_of_nodes && !unconn) {
1013 res = IGRAPH_INFINITY;
1014 from = -1;
1015 to = -1;
1016 break;
1017 }
1018 } /* for i<no_of_nodes */
1019
1020 IGRAPH_PROGRESS("Diameter: ", 100.0, NULL);
1021
1022 /* return the requested info */
1023 if (pres != 0) {
1024 *pres = res;
1025 }
1026 if (pfrom != 0) {
1027 *pfrom = (igraph_integer_t) from;
1028 }
1029 if (pto != 0) {
1030 *pto = (igraph_integer_t) to;
1031 }
1032 if (path != 0) {
1033 if (! igraph_finite(res)) {
1034 igraph_vector_clear(path);
1035 } else {
1036 igraph_vector_ptr_t tmpptr;
1037 igraph_vector_ptr_init(&tmpptr, 1);
1038 IGRAPH_FINALLY(igraph_vector_ptr_destroy, &tmpptr);
1039 VECTOR(tmpptr)[0] = path;
1040 IGRAPH_CHECK(igraph_get_shortest_paths(graph, &tmpptr, 0,
1041 (igraph_integer_t) from,
1042 igraph_vss_1((igraph_integer_t)to),
1043 dirmode, 0, 0));
1044 igraph_vector_ptr_destroy(&tmpptr);
1045 IGRAPH_FINALLY_CLEAN(1);
1046 }
1047 }
1048
1049 /* clean */
1050 IGRAPH_FREE(already_added);
1051 igraph_dqueue_destroy(&q);
1052 igraph_adjlist_destroy(&allneis);
1053 IGRAPH_FINALLY_CLEAN(3);
1054
1055 return 0;
1056 }
1057
1058 /**
1059 * \function igraph_diameter_dijkstra
1060 * \brief Calculates the weighted diameter of a graph using Dijkstra's algorithm.
1061 *
1062 * This function computes the weighted diameter of a graph.
1063 *
1064 * If the graph has no vertices, \c IGRAPH_NAN is returned.
1065 *
1066 * \param graph The input graph, can be directed or undirected.
1067 * \param pres Pointer to a real number, if not \c NULL then it will contain
1068 * the diameter (the actual distance).
1069 * \param pfrom Pointer to an integer, if not \c NULL it will be set to the
1070 * source vertex of the diameter path. If the graph has no diameter path,
1071 * it will be set to -1.
1072 * \param pto Pointer to an integer, if not \c NULL it will be set to the
1073 * target vertex of the diameter path. If the graph has no diameter path,
1074 * it will be set to -1.
1075 * \param path Pointer to an initialized vector. If not \c NULL the actual
1076 * longest geodesic path will be stored here. The vector will be
1077 * resized as needed.
1078 * \param directed Boolean, whether to consider directed
1079 * paths. Ignored for undirected graphs.
1080 * \param unconn What to do if the graph is not connected. If
1081 * \c TRUE the longest geodesic within a component
1082 * will be returned, otherwise \c IGRAPH_INFINITY is
1083 * returned.
1084 * \return Error code.
1085 *
1086 * Time complexity: O(|V||E|*log|E|), |V| is the number of vertices,
1087 * |E| is the number of edges.
1088 *
1089 * \sa \ref igraph_diameter()
1090 */
1091
1092
igraph_diameter_dijkstra(const igraph_t * graph,const igraph_vector_t * weights,igraph_real_t * pres,igraph_integer_t * pfrom,igraph_integer_t * pto,igraph_vector_t * path,igraph_bool_t directed,igraph_bool_t unconn)1093 int igraph_diameter_dijkstra(const igraph_t *graph,
1094 const igraph_vector_t *weights,
1095 igraph_real_t *pres,
1096 igraph_integer_t *pfrom,
1097 igraph_integer_t *pto,
1098 igraph_vector_t *path,
1099 igraph_bool_t directed,
1100 igraph_bool_t unconn) {
1101
1102 /* Implementation details. This is the basic Dijkstra algorithm,
1103 with a binary heap. The heap is indexed, i.e. it stores not only
1104 the distances, but also which vertex they belong to.
1105
1106 From now on we use a 2-way heap, so the distances can be queried
1107 directly from the heap.
1108
1109 Dirty tricks:
1110 - the opposite of the distance is stored in the heap, as it is a
1111 maximum heap and we need a minimum heap.
1112 - we don't use IGRAPH_INFINITY during the computation, as IGRAPH_FINITE()
1113 might involve a function call and we want to spare that. -1 will denote
1114 infinity instead.
1115 */
1116
1117 long int no_of_nodes = igraph_vcount(graph);
1118 long int no_of_edges = igraph_ecount(graph);
1119
1120 igraph_2wheap_t Q;
1121 igraph_inclist_t inclist;
1122 long int source, j;
1123 igraph_neimode_t dirmode = directed ? IGRAPH_OUT : IGRAPH_ALL;
1124
1125 long int from = -1, to = -1;
1126 igraph_real_t res = 0;
1127 long int nodes_reached = 0;
1128
1129 /* See https://github.com/igraph/igraph/issues/1538#issuecomment-724071857
1130 * for why we return NaN for the null graph. */
1131 if (no_of_nodes == 0) {
1132 if (pres) {
1133 *pres = IGRAPH_NAN;
1134 }
1135 if (path) {
1136 igraph_vector_clear(path);
1137 }
1138 if (pfrom) {
1139 *pfrom = -1;
1140 }
1141 if (pto) {
1142 *pto = -1;
1143 }
1144 return IGRAPH_SUCCESS;
1145 }
1146
1147 if (!weights) {
1148 igraph_real_t diameter;
1149 IGRAPH_CHECK(igraph_diameter(graph, &diameter, pfrom, pto, path, directed, unconn));
1150 if (pres) {
1151 *pres = diameter;
1152 }
1153 return IGRAPH_SUCCESS;
1154 }
1155
1156 if (weights && igraph_vector_size(weights) != no_of_edges) {
1157 IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
1158 }
1159
1160 if (no_of_edges > 0) {
1161 igraph_real_t min = igraph_vector_min(weights);
1162 if (min < 0) {
1163 IGRAPH_ERROR("Weight vector must be non-negative", IGRAPH_EINVAL);
1164 }
1165 else if (igraph_is_nan(min)) {
1166 IGRAPH_ERROR("Weight vector must not contain NaN values", IGRAPH_EINVAL);
1167 }
1168 }
1169
1170 IGRAPH_CHECK(igraph_2wheap_init(&Q, no_of_nodes));
1171 IGRAPH_FINALLY(igraph_2wheap_destroy, &Q);
1172 IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, dirmode, IGRAPH_LOOPS));
1173 IGRAPH_FINALLY(igraph_inclist_destroy, &inclist);
1174
1175 for (source = 0; source < no_of_nodes; source++) {
1176
1177 IGRAPH_PROGRESS("Weighted diameter: ", source * 100.0 / no_of_nodes, NULL);
1178 IGRAPH_ALLOW_INTERRUPTION();
1179
1180 igraph_2wheap_clear(&Q);
1181 igraph_2wheap_push_with_index(&Q, source, -1.0);
1182
1183 nodes_reached = 0.0;
1184
1185 while (!igraph_2wheap_empty(&Q)) {
1186 long int minnei = igraph_2wheap_max_index(&Q);
1187 igraph_real_t mindist = -igraph_2wheap_deactivate_max(&Q);
1188 igraph_vector_int_t *neis;
1189 long int nlen;
1190
1191 if (mindist > res) {
1192 res = mindist; from = source; to = minnei;
1193 }
1194 nodes_reached++;
1195
1196 /* Now check all neighbors of 'minnei' for a shorter path */
1197 neis = igraph_inclist_get(&inclist, minnei);
1198 nlen = igraph_vector_int_size(neis);
1199 for (j = 0; j < nlen; j++) {
1200 long int edge = (long int) VECTOR(*neis)[j];
1201 long int tto = IGRAPH_OTHER(graph, edge, minnei);
1202 igraph_real_t altdist = mindist + VECTOR(*weights)[edge];
1203 igraph_bool_t active = igraph_2wheap_has_active(&Q, tto);
1204 igraph_bool_t has = igraph_2wheap_has_elem(&Q, tto);
1205 igraph_real_t curdist = active ? -igraph_2wheap_get(&Q, tto) : 0.0;
1206
1207 if (!has) {
1208 /* First finite distance */
1209 IGRAPH_CHECK(igraph_2wheap_push_with_index(&Q, tto, -altdist));
1210 } else if (altdist < curdist) {
1211 /* A shorter path */
1212 IGRAPH_CHECK(igraph_2wheap_modify(&Q, tto, -altdist));
1213 }
1214 }
1215
1216 } /* !igraph_2wheap_empty(&Q) */
1217
1218 /* not connected, return infinity */
1219 if (nodes_reached != no_of_nodes && !unconn) {
1220 res = IGRAPH_INFINITY;
1221 from = to = -1;
1222 break;
1223 }
1224
1225 } /* source < no_of_nodes */
1226
1227 /* Compensate for the +1 that we have added to distances */
1228 res -= 1;
1229
1230 igraph_inclist_destroy(&inclist);
1231 igraph_2wheap_destroy(&Q);
1232 IGRAPH_FINALLY_CLEAN(2);
1233
1234 IGRAPH_PROGRESS("Weighted diameter: ", 100.0, NULL);
1235
1236 if (pres) {
1237 *pres = res;
1238 }
1239 if (pfrom) {
1240 *pfrom = (igraph_integer_t) from;
1241 }
1242 if (pto) {
1243 *pto = (igraph_integer_t) to;
1244 }
1245 if (path) {
1246 if (!igraph_finite(res)) {
1247 igraph_vector_clear(path);
1248 } else {
1249 igraph_vector_ptr_t tmpptr;
1250 igraph_vector_ptr_init(&tmpptr, 1);
1251 IGRAPH_FINALLY(igraph_vector_ptr_destroy, &tmpptr);
1252 VECTOR(tmpptr)[0] = path;
1253 IGRAPH_CHECK(igraph_get_shortest_paths_dijkstra(graph,
1254 /*vertices=*/ &tmpptr, /*edges=*/ 0,
1255 (igraph_integer_t) from,
1256 igraph_vss_1((igraph_integer_t) to),
1257 weights, dirmode, /*predecessors=*/ 0,
1258 /*inbound_edges=*/ 0));
1259 igraph_vector_ptr_destroy(&tmpptr);
1260 IGRAPH_FINALLY_CLEAN(1);
1261 }
1262 }
1263
1264 return 0;
1265 }
1266