Coverage Report

Created: 2026-07-22 06:57

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/operators/subgraph.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2006-2024  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_operators.h"
20
21
#include "igraph_bitset.h"
22
#include "igraph_constructors.h"
23
#include "igraph_interface.h"
24
25
#include "core/interruption.h"
26
#include "core/set.h"
27
#include "graph/attributes.h"
28
#include "operators/subgraph.h"
29
30
/**
31
 * Subgraph creation, old version: it copies the graph and then deletes
32
 * unneeded vertices.
33
 */
34
static igraph_error_t igraph_i_induced_subgraph_copy_and_delete(
35
        const igraph_t *graph, igraph_t *res, const igraph_vs_t vids,
36
0
        igraph_vector_int_t *map, igraph_vector_int_t *invmap) {
37
38
0
    const igraph_int_t no_of_nodes = igraph_vcount(graph);
39
0
    igraph_int_t no_of_new_nodes_estimate;
40
0
    igraph_vector_int_t delete;
41
0
    igraph_bitset_t remain;
42
0
    igraph_vit_t vit;
43
44
0
    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
45
0
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
46
47
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&delete, 0);
48
0
    IGRAPH_BITSET_INIT_FINALLY(&remain, no_of_nodes);
49
50
    /* Calculate how many nodes there will be in the new graph. The result is
51
     * a lower bound only as 'vit' may contain the same vertex more than once. */
52
0
    no_of_new_nodes_estimate = no_of_nodes - IGRAPH_VIT_SIZE(vit);
53
0
    if (no_of_new_nodes_estimate < 0) {
54
0
        no_of_new_nodes_estimate = 0;
55
0
    }
56
57
0
    IGRAPH_CHECK(igraph_vector_int_reserve(&delete, no_of_new_nodes_estimate));
58
59
0
    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
60
0
        IGRAPH_BIT_SET(remain, IGRAPH_VIT_GET(vit));
61
0
    }
62
63
0
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
64
0
        IGRAPH_ALLOW_INTERRUPTION();
65
66
0
        if (! IGRAPH_BIT_TEST(remain, i)) {
67
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i));
68
0
        }
69
0
    }
70
71
0
    igraph_bitset_destroy(&remain);
72
0
    IGRAPH_FINALLY_CLEAN(1);
73
74
0
    IGRAPH_CHECK(igraph_copy(res, graph));
75
0
    IGRAPH_FINALLY(igraph_destroy, res);
76
0
    IGRAPH_CHECK(igraph_delete_vertices_map(res, igraph_vss_vector(&delete),
77
0
                                            map, invmap));
78
79
0
    igraph_vector_int_destroy(&delete);
80
0
    igraph_vit_destroy(&vit);
81
0
    IGRAPH_FINALLY_CLEAN(3);
82
83
0
    return IGRAPH_SUCCESS;
84
0
}
85
86
/**
87
 * Subgraph creation, new version: creates the new graph instead of
88
 * copying the old one.
89
 *
90
 * map_is_prepared is an indicator that the caller has already prepared the
91
 * 'map' vector and that this function should not resize or clear it. This
92
 * is used to spare an O(n) operation (where n is the number of vertices in
93
 * the _original_ graph) in cases when induced_subgraph() is repeatedly
94
 * called on the same graph; one example is igraph_decompose().
95
 */
96
static igraph_error_t igraph_i_induced_subgraph_create_from_scratch(
97
        const igraph_t *graph, igraph_t *res, const igraph_vs_t vids,
98
        igraph_vector_int_t *map, igraph_vector_int_t *invmap,
99
0
        igraph_bool_t map_is_prepared) {
100
101
0
    const igraph_bool_t directed = igraph_is_directed(graph);
102
0
    const igraph_int_t no_of_nodes = igraph_vcount(graph);
103
0
    igraph_int_t no_of_new_nodes = 0;
104
0
    igraph_int_t n;
105
0
    igraph_int_t to;
106
0
    igraph_int_t eid;
107
0
    igraph_vector_int_t vids_old2new, vids_new2old;
108
0
    igraph_vector_int_t eids_new2old;
109
0
    igraph_vector_int_t vids_vec;
110
0
    igraph_vector_int_t nei_edges;
111
0
    igraph_vector_int_t new_edges;
112
0
    igraph_vit_t vit;
113
0
    igraph_vector_int_t *my_vids_old2new = &vids_old2new,
114
0
                        *my_vids_new2old = &vids_new2old;
115
116
    /* The order of initialization is important here, they will be destroyed in the
117
     * opposite order */
118
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&eids_new2old, 0);
119
0
    if (invmap) {
120
0
        my_vids_new2old = invmap;
121
0
        igraph_vector_int_clear(my_vids_new2old);
122
0
    } else {
123
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_new2old, 0);
124
0
    }
125
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&new_edges, 0);
126
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&nei_edges, 0);
127
0
    if (map) {
128
0
        my_vids_old2new = map;
129
0
        if (!map_is_prepared) {
130
0
            IGRAPH_CHECK(igraph_vector_int_resize(map, no_of_nodes));
131
0
            igraph_vector_int_fill(map, -1);
132
0
        }
133
0
    } else {
134
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes);
135
0
        igraph_vector_int_fill(&vids_old2new, -1);
136
0
    }
137
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_vec, 0);
138
139
0
    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
140
0
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
141
142
    /* Calculate the mapping from the old node IDs to the new ones. The other
143
     * igraph_simplify implementation in igraph_i_simplify_copy_and_delete
144
     * ensures that the order of vertex IDs is kept during remapping (i.e.
145
     * if the old ID of vertex A is less than the old ID of vertex B, then
146
     * the same will also be true for the new IDs). To ensure compatibility
147
     * with the other implementation, we have to fetch the vertex IDs into
148
     * a vector first and then sort it.
149
     */
150
0
    IGRAPH_CHECK(igraph_vit_as_vector(&vit, &vids_vec));
151
0
    igraph_vit_destroy(&vit);
152
0
    IGRAPH_FINALLY_CLEAN(1);
153
154
0
    igraph_vector_int_sort(&vids_vec);
155
0
    n = igraph_vector_int_size(&vids_vec);
156
0
    for (igraph_int_t i = 0; i < n; i++) {
157
0
        igraph_int_t vid = VECTOR(vids_vec)[i];
158
159
        /* Cater for duplicate vertex IDs in the input vertex selector; we use
160
         * the first occurrence of each vertex ID and ignore the rest */
161
0
        if (VECTOR(*my_vids_old2new)[vid] < 0) {
162
0
            IGRAPH_CHECK(igraph_vector_int_push_back(my_vids_new2old, vid));
163
0
            VECTOR(*my_vids_old2new)[vid] = no_of_new_nodes;
164
0
            no_of_new_nodes++;
165
0
        }
166
0
    }
167
0
    igraph_vector_int_destroy(&vids_vec);
168
0
    IGRAPH_FINALLY_CLEAN(1);
169
170
    /* Create the new edge list */
171
0
    for (igraph_int_t i = 0; i < no_of_new_nodes; i++) {
172
0
        igraph_int_t old_vid = VECTOR(*my_vids_new2old)[i];
173
0
        igraph_int_t new_vid = i;
174
0
        igraph_bool_t skip_loop_edge;
175
176
0
        IGRAPH_CHECK(igraph_incident(graph, &nei_edges, old_vid, IGRAPH_OUT, IGRAPH_LOOPS));
177
0
        n = igraph_vector_int_size(&nei_edges);
178
179
0
        if (directed) {
180
            /* directed graph; this is easier */
181
0
            for (igraph_int_t j = 0; j < n; j++) {
182
0
                eid = VECTOR(nei_edges)[j];
183
184
0
                to = VECTOR(*my_vids_old2new)[ IGRAPH_TO(graph, eid) ];
185
0
                if (to < 0) {
186
0
                    continue;
187
0
                }
188
189
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, new_vid));
190
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, to));
191
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&eids_new2old, eid));
192
0
            }
193
0
        } else {
194
            /* undirected graph. We need to be careful with loop edges as each
195
             * loop edge will appear twice. We use a boolean flag to skip every
196
             * second loop edge */
197
0
            skip_loop_edge = false;
198
0
            for (igraph_int_t j = 0; j < n; j++) {
199
0
                eid = VECTOR(nei_edges)[j];
200
201
0
                if (IGRAPH_FROM(graph, eid) != old_vid) {
202
                    /* avoid processing edges twice */
203
0
                    continue;
204
0
                }
205
206
0
                to = VECTOR(*my_vids_old2new)[ IGRAPH_TO(graph, eid) ];
207
0
                if (to < 0) {
208
0
                    continue;
209
0
                }
210
211
0
                if (new_vid == to) {
212
                    /* this is a loop edge; check whether we need to skip it */
213
0
                    skip_loop_edge = !skip_loop_edge;
214
0
                    if (skip_loop_edge) {
215
0
                        continue;
216
0
                    }
217
0
                }
218
219
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, new_vid));
220
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&new_edges, to));
221
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&eids_new2old, eid));
222
0
            }
223
0
        }
224
0
    }
225
226
    /* Get rid of some vectors that are not needed anymore */
227
0
    if (!map) {
228
0
        igraph_vector_int_destroy(&vids_old2new);
229
0
        IGRAPH_FINALLY_CLEAN(1);
230
0
    }
231
0
    igraph_vector_int_destroy(&nei_edges);
232
0
    IGRAPH_FINALLY_CLEAN(1);
233
234
    /* Create the new graph */
235
0
    IGRAPH_CHECK(igraph_create(res, &new_edges, no_of_new_nodes, directed));
236
237
    /* Now we can also get rid of the new_edges vector */
238
0
    igraph_vector_int_destroy(&new_edges);
239
0
    IGRAPH_FINALLY_CLEAN(1);
240
241
    /* Make sure that the newly created graph is destroyed if something happens from
242
     * now on */
243
0
    IGRAPH_FINALLY(igraph_destroy, res);
244
245
    /* Copy the graph attributes */
246
0
    IGRAPH_CHECK(igraph_i_attribute_copy(res, graph, true, /* vertex= */ false, /* edge= */ false));
247
248
    /* Copy the vertex attributes */
249
0
    IGRAPH_CHECK(igraph_i_attribute_permute_vertices(graph, res, my_vids_new2old));
250
251
    /* Copy the edge attributes */
252
0
    IGRAPH_CHECK(igraph_i_attribute_permute_edges(graph, res, &eids_new2old));
253
254
    /* Get rid of the remaining stuff */
255
0
    if (!invmap) {
256
0
        igraph_vector_int_destroy(my_vids_new2old);
257
0
        IGRAPH_FINALLY_CLEAN(1);
258
0
    }
259
0
    igraph_vector_int_destroy(&eids_new2old);
260
0
    IGRAPH_FINALLY_CLEAN(2);   /* 1 + 1 since we don't need to destroy res */
261
262
0
    return IGRAPH_SUCCESS;
263
0
}
264
265
/**
266
 * \ingroup structural
267
 * \function igraph_induced_subgraph
268
 * \brief Creates a subgraph induced by the specified vertices.
269
 *
270
 * This function collects the specified vertices and all edges between
271
 * them to a new graph. As vertex IDs are always contiguos integers starting
272
 * at zero, the IDs in the created subgraph will be different from the IDs in
273
 * the original graph. To get the mappings between them, use
274
 * \ref igraph_induced_subgraph_map()
275
 *
276
 * \param graph The graph object.
277
 * \param res The subgraph, another graph object will be stored here,
278
 *        do \em not initialize this object before calling this
279
 *        function, and call \ref igraph_destroy() on it if you don't need
280
 *        it any more.
281
 * \param vids A vertex selector describing which vertices to keep. A vertex
282
 *        may appear more than once in the selector, but it will be considered
283
 *        only once (i.e. it is not possible to duplicate a vertex by adding
284
 *        its ID more than once to the selector). The order in which the
285
 *        vertices appear in the vertex selector is ignored; the returned
286
 *        subgraph will always contain the vertices of the original graph in
287
 *        increasing order of vertex IDs.
288
 * \param impl This parameter selects which implementation should we
289
 *        use when constructing the new graph. Basically there are two
290
 *        possibilities: \c IGRAPH_SUBGRAPH_COPY_AND_DELETE copies the
291
 *        existing graph and deletes the vertices that are not needed
292
 *        in the new graph, while \c IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH
293
 *        constructs the new graph from scratch without copying the old
294
 *        one. The latter is more efficient if you are extracting a
295
 *        relatively small subpart of a very large graph, while the
296
 *        former is better if you want to extract a subgraph whose size
297
 *        is comparable to the size of the whole graph. There is a third
298
 *        possibility: \c IGRAPH_SUBGRAPH_AUTO will select one of the
299
 *        two methods automatically based on the ratio of the number
300
 *        of vertices in the new and the old graph.
301
 *
302
 * \return Error code:
303
 *         \c IGRAPH_ENOMEM, not enough memory for
304
 *         temporary data.
305
 *         \c IGRAPH_EINVVID, invalid vertex ID in
306
 *         \p vids.
307
 *
308
 * Time complexity: O(|V|+|E|),
309
 * |V| and
310
 * |E| are the number of vertices and
311
 * edges in the original graph.
312
 *
313
 * \sa \ref igraph_induced_subgraph_map() to also retrieve the vertex ID
314
 * mapping between the graph and the extracted subgraph;
315
 * \ref igraph_delete_vertices() to delete the specified set of
316
 * vertices from a graph, the opposite of this function.
317
 */
318
igraph_error_t igraph_induced_subgraph(const igraph_t *graph, igraph_t *res,
319
                                       const igraph_vs_t vids,
320
0
                                       igraph_subgraph_implementation_t impl) {
321
0
    return igraph_induced_subgraph_map(graph, res, vids, impl,
322
0
                                       /* map= */ NULL, /* invmap= */ NULL);
323
0
}
324
325
static igraph_error_t igraph_i_induced_subgraph_suggest_implementation(
326
        const igraph_t *graph, const igraph_vs_t vids,
327
0
        igraph_subgraph_implementation_t *result) {
328
0
    double ratio;
329
0
    igraph_int_t num_vs;
330
331
0
    if (igraph_vs_is_all(&vids)) {
332
0
        ratio = 1.0;
333
0
    } else {
334
0
        IGRAPH_CHECK(igraph_vs_size(graph, &vids, &num_vs));
335
0
        ratio = (igraph_real_t) num_vs / igraph_vcount(graph);
336
0
    }
337
338
    /* The threshold of 0.5 is justified by the benchmarking done in
339
     * https://github.com/igraph/igraph/pull/2708
340
     * Small improvements may be possible by using better heuristics. */
341
0
    if (ratio > 0.5) {
342
0
        *result = IGRAPH_SUBGRAPH_COPY_AND_DELETE;
343
0
    } else {
344
0
        *result = IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH;
345
0
    }
346
347
0
    return IGRAPH_SUCCESS;
348
0
}
349
350
igraph_error_t igraph_i_induced_subgraph_map(const igraph_t *graph, igraph_t *res,
351
                                  const igraph_vs_t vids,
352
                                  igraph_subgraph_implementation_t impl,
353
                                  igraph_vector_int_t *map,
354
                                  igraph_vector_int_t *invmap,
355
0
                                  igraph_bool_t map_is_prepared) {
356
357
0
    if (impl == IGRAPH_SUBGRAPH_AUTO) {
358
0
        IGRAPH_CHECK(igraph_i_induced_subgraph_suggest_implementation(graph, vids, &impl));
359
0
    }
360
361
0
    switch (impl) {
362
0
    case IGRAPH_SUBGRAPH_COPY_AND_DELETE:
363
0
        return igraph_i_induced_subgraph_copy_and_delete(graph, res, vids, map, invmap);
364
365
0
    case IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH:
366
0
        return igraph_i_induced_subgraph_create_from_scratch(graph, res, vids, map,
367
0
                invmap, /* map_is_prepared = */ map_is_prepared);
368
369
0
    default:
370
0
        IGRAPH_ERROR("unknown subgraph implementation type", IGRAPH_EINVAL);
371
0
    }
372
0
}
373
374
/**
375
 * \ingroup structural
376
 * \function igraph_induced_subgraph_map
377
 * \brief Creates an induced subraph and returns the mapping from the original.
378
 *
379
 * This function collects the specified vertices and all edges between
380
 * them to a new graph. As vertex IDs are always contiguos integers starting
381
 * at zero, the IDs in the created subgraph will be different from the IDs in
382
 * the original graph. The mapping between the vertex IDs in the graph and the
383
 * extracted subgraphs are returned in \p map and \p invmap.
384
 *
385
 * \param graph The graph object.
386
 * \param res The subgraph, another graph object will be stored here,
387
 *        do \em not initialize this object before calling this
388
 *        function, and call \ref igraph_destroy() on it if you don't need
389
 *        it any more.
390
 * \param vids A vertex selector describing which vertices to keep.
391
 * \param impl This parameter selects which implementation should be
392
 *        used when constructing the new graph. Basically there are two
393
 *        possibilities: \c IGRAPH_SUBGRAPH_COPY_AND_DELETE copies the
394
 *        existing graph and deletes the vertices that are not needed
395
 *        in the new graph, while \c IGRAPH_SUBGRAPH_CREATE_FROM_SCRATCH
396
 *        constructs the new graph from scratch without copying the old
397
 *        one. The latter is more efficient if you are extracting a
398
 *        relatively small subpart of a very large graph, while the
399
 *        former is better if you want to extract a subgraph whose size
400
 *        is comparable to the size of the whole graph. There is a third
401
 *        possibility: \c IGRAPH_SUBGRAPH_AUTO will select one of the
402
 *        two methods automatically based on the ratio of the number
403
 *        of vertices in the new and the old graph.
404
 * \param map Returns a map of the vertices in \p graph to the vertices
405
 *        in \p res. -1 indicates a vertex is not mapped. A value of \c i at
406
 *        position \c j indicates the vertex \c j in \p graph is mapped
407
 *        to vertex i in \p res.
408
 * \param invmap Returns a map of the vertices in \p res to the vertices
409
 *        in \p graph. A value of \c i at position \c j indicates that
410
 *        vertex \c i in \p graph is mapped to vertex \c j in \p res.
411
 *
412
 * \return Error code:
413
 *         \c IGRAPH_ENOMEM, not enough memory for
414
 *         temporary data.
415
 *         \c IGRAPH_EINVVID, invalid vertex ID in
416
 *         \p vids.
417
 *
418
 * Time complexity: O(|V|+|E|),
419
 * |V| and
420
 * |E| are the number of vertices and
421
 * edges in the original graph.
422
 *
423
 * \sa \ref igraph_delete_vertices() to delete the specified set of
424
 * vertices from a graph, the opposite of this function.
425
 */
426
igraph_error_t igraph_induced_subgraph_map(const igraph_t *graph, igraph_t *res,
427
                                           const igraph_vs_t vids,
428
                                           igraph_subgraph_implementation_t impl,
429
                                           igraph_vector_int_t *map,
430
0
                                           igraph_vector_int_t *invmap) {
431
0
    return igraph_i_induced_subgraph_map(graph, res, vids, impl,
432
0
                                         map, invmap,
433
0
                                         /* map_is_prepared = */ false);
434
0
}
435
436
/**
437
 * \function igraph_induced_subgraph_edges
438
 * \brief The edges contained within an induced sugraph.
439
 *
440
 * This function finds the IDs of those edges which connect vertices from
441
 * a given list, passed in the \p vids parameter.
442
 *
443
 * \param graph The graph.
444
 * \param vids A vertex selector specifying the vertices that make up the subgraph.
445
 * \param edges Integer vector. The IDs of edges within the subgraph induces by
446
 *    \p vids will be stored here.
447
 * \return Error code.
448
 *
449
 * Time complexity: O(mv log(nv)) where nv is the number of vertices in \p vids
450
 * and mv is the sum of degrees of vertices in \p vids.
451
 */
452
0
igraph_error_t igraph_induced_subgraph_edges(const igraph_t *graph, igraph_vs_t vids, igraph_vector_int_t *edges) {
453
    /* TODO: When the size of \p vids is large, is it faster to use a boolean vector instead of a set
454
     * to test membership within \p vids? Benchmark to find out at what size it is worth switching
455
     * to the alternative implementation.
456
     */
457
0
    igraph_vit_t vit;
458
0
    igraph_set_t vids_set;
459
0
    igraph_vector_int_t incedges;
460
461
0
    if (igraph_vs_is_all(&vids)) {
462
0
        IGRAPH_CHECK(igraph_vector_int_range(edges, 0, igraph_ecount(graph)));
463
0
        return IGRAPH_SUCCESS;
464
0
    }
465
466
0
    igraph_vector_int_clear(edges);
467
468
0
    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
469
0
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
470
471
0
    IGRAPH_SET_INIT_FINALLY(&vids_set, IGRAPH_VIT_SIZE(vit));
472
0
    for (; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
473
0
        IGRAPH_CHECK(igraph_set_add(&vids_set, IGRAPH_VIT_GET(vit)));
474
0
    }
475
476
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&incedges, 0);
477
478
0
    for (IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit)) {
479
0
        igraph_int_t v = IGRAPH_VIT_GET(vit);
480
0
        IGRAPH_CHECK(igraph_incident(graph, &incedges, v, IGRAPH_ALL, IGRAPH_LOOPS_ONCE));
481
482
0
        igraph_int_t d = igraph_vector_int_size(&incedges);
483
0
        for (igraph_int_t i=0; i < d; i++) {
484
0
            igraph_int_t e = VECTOR(incedges)[i];
485
0
            igraph_int_t u = IGRAPH_OTHER(graph, e, v);
486
            /* The v <= u check avoids adding non-loop edges twice.
487
             * Loop edges only appear once due to the use of
488
             * IGRAPH_LOOPS_ONCE in igraph_incident() */
489
0
            if (v <= u && igraph_set_contains(&vids_set, u)) {
490
0
                IGRAPH_CHECK(igraph_vector_int_push_back(edges, e));
491
0
            }
492
0
        }
493
0
    }
494
495
0
    IGRAPH_FINALLY_CLEAN(3);
496
0
    igraph_vector_int_destroy(&incedges);
497
0
    igraph_set_destroy(&vids_set);
498
0
    igraph_vit_destroy(&vit);
499
500
0
    return IGRAPH_SUCCESS;
501
0
}
502
503
/**
504
 * \ingroup structural
505
 * \function igraph_subgraph_from_edges
506
 * \brief Creates a subgraph with the specified edges and their endpoints.
507
 *
508
 * </para><para>
509
 * This function collects the specified edges and their endpoints to a new
510
 * graph. As the edge IDs in a graph are always contiguous integers starting at
511
 * zero, the edge IDs in the extracted subgraph will be different from those
512
 * in the original graph. Vertex IDs will also be reassigned if
513
 * \p delete_vertices is set to \c true. Attributes are preserved.
514
 *
515
 * \param graph The graph object.
516
 * \param res The subgraph, another graph object will be stored here,
517
 *        do \em not initialize this object before calling this
518
 *        function, and call \ref igraph_destroy() on it if you don't need
519
 *        it any more.
520
 * \param eids An edge selector describing which edges to keep.
521
 * \param delete_vertices Whether to delete the vertices not incident on any
522
 *        of the specified edges as well. If \c false, the number of vertices
523
 *        in the result graph will always be equal to the number of vertices
524
 *        in the input graph.
525
 * \return Error code:
526
 *         \c IGRAPH_ENOMEM, not enough memory for temporary data.
527
 *         \c IGRAPH_EINVEID, invalid edge ID in \p eids.
528
 *
529
 * Time complexity: O(|V|+|E|), |V| and |E| are the number of vertices and
530
 * edges in the original graph.
531
 *
532
 * \sa \ref igraph_delete_edges() to delete the specified set of
533
 * edges from a graph, the opposite of this function.
534
 */
535
536
igraph_error_t igraph_subgraph_from_edges(
537
    const igraph_t *graph, igraph_t *res, const igraph_es_t eids,
538
    igraph_bool_t delete_vertices
539
0
) {
540
541
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
542
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
543
0
    igraph_int_t no_of_edges_to_delete_estimate;
544
0
    igraph_vector_int_t delete = IGRAPH_VECTOR_NULL;
545
0
    igraph_bitset_t vremain, eremain;
546
0
    igraph_eit_t eit;
547
548
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&delete, 0);
549
0
    IGRAPH_BITSET_INIT_FINALLY(&vremain, no_of_nodes);
550
0
    IGRAPH_BITSET_INIT_FINALLY(&eremain, no_of_edges);
551
552
0
    IGRAPH_CHECK(igraph_eit_create(graph, eids, &eit));
553
0
    IGRAPH_FINALLY(igraph_eit_destroy, &eit);
554
555
    /* Calculate how many edges there will be in the new graph. The result is
556
     * a lower bound only as 'eit' may contain the same edge more than once. */
557
0
    no_of_edges_to_delete_estimate = no_of_edges - IGRAPH_EIT_SIZE(eit);
558
0
    if (no_of_edges_to_delete_estimate < 0) {
559
0
        no_of_edges_to_delete_estimate = 0;
560
0
    }
561
562
0
    IGRAPH_CHECK(igraph_vector_int_reserve(&delete, no_of_edges_to_delete_estimate));
563
564
    /* Collect the vertex and edge IDs that will remain */
565
0
    for (IGRAPH_EIT_RESET(eit); !IGRAPH_EIT_END(eit); IGRAPH_EIT_NEXT(eit)) {
566
0
        igraph_int_t eid = IGRAPH_EIT_GET(eit);
567
0
        igraph_int_t from = IGRAPH_FROM(graph, eid), to = IGRAPH_TO(graph, eid);
568
0
        IGRAPH_BIT_SET(eremain, eid);
569
0
        IGRAPH_BIT_SET(vremain, from);
570
0
        IGRAPH_BIT_SET(vremain, to);
571
0
    }
572
573
0
    igraph_eit_destroy(&eit);
574
0
    IGRAPH_FINALLY_CLEAN(1);
575
576
    /* Collect the edge IDs to be deleted */
577
0
    for (igraph_int_t i = 0; i < no_of_edges; i++) {
578
0
        IGRAPH_ALLOW_INTERRUPTION();
579
0
        if (! IGRAPH_BIT_TEST(eremain, i)) {
580
0
            IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i));
581
0
        }
582
0
    }
583
584
0
    igraph_bitset_destroy(&eremain);
585
0
    IGRAPH_FINALLY_CLEAN(1);
586
587
    /* Delete the unnecessary edges */
588
0
    IGRAPH_CHECK(igraph_copy(res, graph));
589
0
    IGRAPH_FINALLY(igraph_destroy, res);
590
0
    IGRAPH_CHECK(igraph_delete_edges(res, igraph_ess_vector(&delete)));
591
592
0
    if (delete_vertices) {
593
        /* Collect the vertex IDs to be deleted */
594
0
        igraph_vector_int_clear(&delete);
595
0
        for (igraph_int_t i = 0; i < no_of_nodes; i++) {
596
0
            IGRAPH_ALLOW_INTERRUPTION();
597
0
            if (! IGRAPH_BIT_TEST(vremain, i)) {
598
0
                IGRAPH_CHECK(igraph_vector_int_push_back(&delete, i));
599
0
            }
600
0
        }
601
0
    }
602
603
0
    igraph_bitset_destroy(&vremain);
604
0
    IGRAPH_FINALLY_CLEAN(1);
605
606
    /* Delete the unnecessary vertices */
607
0
    if (delete_vertices) {
608
0
        IGRAPH_CHECK(igraph_delete_vertices(res, igraph_vss_vector(&delete)));
609
0
    }
610
611
0
    igraph_vector_int_destroy(&delete);
612
0
    IGRAPH_FINALLY_CLEAN(2);
613
614
0
    return IGRAPH_SUCCESS;
615
0
}