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