Coverage Report

Created: 2025-10-12 06:18

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