Coverage Report

Created: 2025-11-21 06:19

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