/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 | } |