/src/igraph/src/connectivity/components.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | IGraph library. |
3 | | Copyright (C) 2003-2012 Gabor Csardi <csardi.gabor@gmail.com> |
4 | | 334 Harvard street, Cambridge, MA 02139 USA |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 2 of the License, or |
9 | | (at your option) any later version. |
10 | | |
11 | | This program is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with this program; if not, write to the Free Software |
18 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
19 | | 02110-1301 USA |
20 | | |
21 | | */ |
22 | | |
23 | | #include "igraph_components.h" |
24 | | |
25 | | #include "igraph_adjlist.h" |
26 | | #include "igraph_bitset.h" |
27 | | #include "igraph_dqueue.h" |
28 | | #include "igraph_interface.h" |
29 | | #include "igraph_progress.h" |
30 | | #include "igraph_stack.h" |
31 | | #include "igraph_structural.h" |
32 | | #include "igraph_vector.h" |
33 | | |
34 | | #include "core/interruption.h" |
35 | | #include "operators/subgraph.h" |
36 | | |
37 | | static igraph_error_t igraph_i_connected_components_weak( |
38 | | const igraph_t *graph, igraph_vector_int_t *membership, |
39 | | igraph_vector_int_t *csize, igraph_integer_t *no |
40 | | ); |
41 | | static igraph_error_t igraph_i_connected_components_strong( |
42 | | const igraph_t *graph, igraph_vector_int_t *membership, |
43 | | igraph_vector_int_t *csize, igraph_integer_t *no |
44 | | ); |
45 | | |
46 | | /** |
47 | | * \ingroup structural |
48 | | * \function igraph_connected_components |
49 | | * \brief Calculates the (weakly or strongly) connected components in a graph. |
50 | | * |
51 | | * When computing strongly connected components, the components will be |
52 | | * indexed in topological order. In other words, vertex \c v is reachable |
53 | | * from vertex \c u precisely when <code>membership[u] <= membership[v]</code>. |
54 | | * |
55 | | * \param graph The graph object to analyze. |
56 | | * \param membership For every vertex the ID of its component is given. |
57 | | * The vector has to be preinitialized and will be resized as needed. |
58 | | * Alternatively this argument can be \c NULL, in which case it is ignored. |
59 | | * \param csize For every component it gives its size, the order being defined |
60 | | * by the component IDs. The vector must be preinitialized and will be |
61 | | * resized as needed. Alternatively this argument can be \c NULL, in which |
62 | | * case it is ignored. |
63 | | * \param no Pointer to an integer, if not \c NULL then the number of |
64 | | * components will be stored here. |
65 | | * \param mode For directed graph this specifies whether to calculate |
66 | | * weakly or strongly connected components. Possible values: |
67 | | * \clist |
68 | | * \cli IGRAPH_WEAK |
69 | | * Compute weakly connected components, i.e. ignore edge directions. |
70 | | * \cli IGRAPH_STRONG |
71 | | * Compute strongly connnected components, i.e. consider edge directions. |
72 | | * \endclist |
73 | | * This parameter is ignored for undirected graphs. |
74 | | * \return Error code. |
75 | | * |
76 | | * Time complexity: O(|V|+|E|), where |V| and |E| are the number of vertices |
77 | | * and edges in the graph. |
78 | | * |
79 | | * \example examples/simple/igraph_contract_vertices.c |
80 | | */ |
81 | | |
82 | | igraph_error_t igraph_connected_components( |
83 | | const igraph_t *graph, igraph_vector_int_t *membership, |
84 | | igraph_vector_int_t *csize, igraph_integer_t *no, igraph_connectedness_t mode |
85 | 4.19k | ) { |
86 | 4.19k | if (mode == IGRAPH_WEAK || !igraph_is_directed(graph)) { |
87 | 4.19k | return igraph_i_connected_components_weak(graph, membership, csize, no); |
88 | 4.19k | } else if (mode == IGRAPH_STRONG) { |
89 | 0 | return igraph_i_connected_components_strong(graph, membership, csize, no); |
90 | 0 | } |
91 | | |
92 | 0 | IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); |
93 | 0 | } |
94 | | |
95 | | static igraph_error_t igraph_i_connected_components_weak( |
96 | | const igraph_t *graph, igraph_vector_int_t *membership, |
97 | | igraph_vector_int_t *csize, igraph_integer_t *no |
98 | 4.19k | ) { |
99 | | |
100 | 4.19k | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
101 | 4.19k | igraph_integer_t no_of_components; |
102 | 4.19k | igraph_bitset_t already_added; |
103 | 4.19k | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
104 | 4.19k | igraph_vector_int_t neis = IGRAPH_VECTOR_NULL; |
105 | | |
106 | | /* Memory for result, csize is dynamically allocated */ |
107 | 4.19k | if (membership) { |
108 | 1.67k | IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); |
109 | 1.67k | } |
110 | 4.19k | if (csize) { |
111 | 4.19k | igraph_vector_int_clear(csize); |
112 | 4.19k | } |
113 | | |
114 | | /* Try to make use of cached information. */ |
115 | 4.19k | if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) && |
116 | 4.19k | igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED)) { |
117 | | /* If we know that the graph is weakly connected from the cache, |
118 | | * we can return the result right away. We keep in mind that |
119 | | * the null graph is considered disconnected, therefore any connected |
120 | | * graph has precisely one component. */ |
121 | 351 | if (membership) { |
122 | | /* All vertices are members of the same component, |
123 | | * component number 0. */ |
124 | 137 | igraph_vector_int_null(membership); |
125 | 137 | } |
126 | 351 | if (csize) { |
127 | | /* The size of the single component is the same as the vertex count. */ |
128 | 351 | IGRAPH_CHECK(igraph_vector_int_push_back(csize, no_of_nodes)); |
129 | 351 | } |
130 | 351 | if (no) { |
131 | | /* There is one component. */ |
132 | 137 | *no = 1; |
133 | 137 | } |
134 | 351 | return IGRAPH_SUCCESS; |
135 | 351 | } |
136 | | |
137 | 3.84k | IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); |
138 | 3.84k | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, no_of_nodes > 100000 ? 10000 : no_of_nodes / 10); |
139 | 3.84k | IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); |
140 | | |
141 | | /* The algorithm */ |
142 | | |
143 | 3.84k | no_of_components = 0; |
144 | 686k | for (igraph_integer_t first_node = 0; first_node < no_of_nodes; ++first_node) { |
145 | 682k | igraph_integer_t act_component_size; |
146 | | |
147 | 682k | if (IGRAPH_BIT_TEST(already_added, first_node)) { |
148 | 74.0k | continue; |
149 | 74.0k | } |
150 | 608k | IGRAPH_ALLOW_INTERRUPTION(); |
151 | | |
152 | 608k | IGRAPH_BIT_SET(already_added, first_node); |
153 | 608k | act_component_size = 1; |
154 | 608k | if (membership) { |
155 | 247k | VECTOR(*membership)[first_node] = no_of_components; |
156 | 247k | } |
157 | 608k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, first_node)); |
158 | | |
159 | 1.29M | while ( !igraph_dqueue_int_empty(&q) ) { |
160 | 682k | igraph_integer_t act_node = igraph_dqueue_int_pop(&q); |
161 | 682k | IGRAPH_CHECK(igraph_neighbors( |
162 | 682k | graph, &neis, act_node, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE |
163 | 682k | )); |
164 | 682k | igraph_integer_t nei_count = igraph_vector_int_size(&neis); |
165 | 881k | for (igraph_integer_t i = 0; i < nei_count; i++) { |
166 | 198k | igraph_integer_t neighbor = VECTOR(neis)[i]; |
167 | 198k | if (IGRAPH_BIT_TEST(already_added, neighbor)) { |
168 | 124k | continue; |
169 | 124k | } |
170 | 74.0k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
171 | 74.0k | IGRAPH_BIT_SET(already_added, neighbor); |
172 | 74.0k | act_component_size++; |
173 | 74.0k | if (membership) { |
174 | 33.7k | VECTOR(*membership)[neighbor] = no_of_components; |
175 | 33.7k | } |
176 | 74.0k | } |
177 | 682k | } |
178 | | |
179 | 608k | no_of_components++; |
180 | 608k | if (csize) { |
181 | 608k | IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size)); |
182 | 608k | } |
183 | 608k | } |
184 | | |
185 | | /* Cleaning up */ |
186 | | |
187 | 3.84k | if (no) { |
188 | 1.53k | *no = no_of_components; |
189 | 1.53k | } |
190 | | |
191 | | /* Clean up */ |
192 | 3.84k | igraph_bitset_destroy(&already_added); |
193 | 3.84k | igraph_dqueue_int_destroy(&q); |
194 | 3.84k | igraph_vector_int_destroy(&neis); |
195 | 3.84k | IGRAPH_FINALLY_CLEAN(3); |
196 | | |
197 | | /* Update cache */ |
198 | 3.84k | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, no_of_components == 1); |
199 | | |
200 | 3.84k | return IGRAPH_SUCCESS; |
201 | 3.84k | } |
202 | | |
203 | | static igraph_error_t igraph_i_connected_components_strong( |
204 | | const igraph_t *graph, igraph_vector_int_t *membership, |
205 | | igraph_vector_int_t *csize, igraph_integer_t *no |
206 | 0 | ) { |
207 | 0 | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
208 | 0 | igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL; |
209 | 0 | igraph_integer_t num_seen; |
210 | 0 | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
211 | 0 | igraph_integer_t no_of_components = 0; |
212 | 0 | igraph_vector_int_t out = IGRAPH_VECTOR_NULL; |
213 | 0 | igraph_adjlist_t adjlist; |
214 | | |
215 | | /* Memory for result, csize is dynamically allocated */ |
216 | 0 | if (membership) { |
217 | 0 | IGRAPH_CHECK(igraph_vector_int_resize(membership, no_of_nodes)); |
218 | 0 | } |
219 | 0 | if (csize) { |
220 | 0 | igraph_vector_int_clear(csize); |
221 | 0 | } |
222 | | |
223 | | /* Try to make use of cached information. */ |
224 | 0 | if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED) && |
225 | 0 | igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED)) { |
226 | | /* If we know that the graph is strongly connected from the cache, |
227 | | * we can return the result right away. We keep in mind that |
228 | | * the null graph is considered disconnected, therefore any connected |
229 | | * graph has precisely one component. */ |
230 | 0 | if (membership) { |
231 | | /* All vertices are members of the same component, |
232 | | * component number 0. */ |
233 | 0 | igraph_vector_int_null(membership); |
234 | 0 | } |
235 | 0 | if (csize) { |
236 | | /* The size of the single component is the same as the vertex count. */ |
237 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(csize, no_of_nodes)); |
238 | 0 | } |
239 | 0 | if (no) { |
240 | | /* There is one component. */ |
241 | 0 | *no = 1; |
242 | 0 | } |
243 | 0 | return IGRAPH_SUCCESS; |
244 | 0 | } |
245 | | |
246 | | /* The result */ |
247 | | |
248 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes); |
249 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0); |
250 | 0 | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
251 | | |
252 | 0 | IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes)); |
253 | | |
254 | 0 | IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); |
255 | 0 | IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); |
256 | |
|
257 | 0 | num_seen = 0; |
258 | 0 | for (igraph_integer_t i = 0; i < no_of_nodes; i++) { |
259 | 0 | const igraph_vector_int_t *tmp; |
260 | |
|
261 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
262 | | |
263 | 0 | tmp = igraph_adjlist_get(&adjlist, i); |
264 | 0 | if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) { |
265 | 0 | continue; |
266 | 0 | } |
267 | | |
268 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); |
269 | 0 | while (!igraph_dqueue_int_empty(&q)) { |
270 | 0 | igraph_integer_t act_node = igraph_dqueue_int_back(&q); |
271 | 0 | tmp = igraph_adjlist_get(&adjlist, act_node); |
272 | 0 | if (VECTOR(next_nei)[act_node] == 0) { |
273 | | /* this is the first time we've met this vertex */ |
274 | 0 | VECTOR(next_nei)[act_node]++; |
275 | 0 | } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) { |
276 | | /* we've already met this vertex but it has more children */ |
277 | 0 | igraph_integer_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1]; |
278 | 0 | if (VECTOR(next_nei)[neighbor] == 0) { |
279 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
280 | 0 | } |
281 | 0 | VECTOR(next_nei)[act_node]++; |
282 | 0 | } else { |
283 | | /* we've met this vertex and it has no more children */ |
284 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node)); |
285 | 0 | igraph_dqueue_int_pop_back(&q); |
286 | 0 | num_seen++; |
287 | |
|
288 | 0 | if (num_seen % 10000 == 0) { |
289 | | /* time to report progress and allow the user to interrupt */ |
290 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", |
291 | 0 | num_seen * 50.0 / no_of_nodes, NULL); |
292 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
293 | 0 | } |
294 | 0 | } |
295 | 0 | } /* while q */ |
296 | 0 | } /* for */ |
297 | | |
298 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL); |
299 | | |
300 | 0 | igraph_adjlist_destroy(&adjlist); |
301 | 0 | IGRAPH_FINALLY_CLEAN(1); |
302 | |
|
303 | 0 | IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); |
304 | 0 | IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); |
305 | | |
306 | | /* OK, we've the 'out' values for the nodes, let's use them in |
307 | | decreasing order with the help of a heap */ |
308 | |
|
309 | 0 | igraph_vector_int_null(&next_nei); /* mark already added vertices */ |
310 | 0 | num_seen = 0; |
311 | |
|
312 | 0 | while (!igraph_vector_int_empty(&out)) { |
313 | 0 | igraph_integer_t act_component_size; |
314 | 0 | igraph_integer_t grandfather = igraph_vector_int_pop_back(&out); |
315 | |
|
316 | 0 | if (VECTOR(next_nei)[grandfather] != 0) { |
317 | 0 | continue; |
318 | 0 | } |
319 | 0 | VECTOR(next_nei)[grandfather] = 1; |
320 | 0 | act_component_size = 1; |
321 | 0 | if (membership) { |
322 | 0 | VECTOR(*membership)[grandfather] = no_of_components; |
323 | 0 | } |
324 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather)); |
325 | | |
326 | 0 | num_seen++; |
327 | 0 | if (num_seen % 10000 == 0) { |
328 | | /* time to report progress and allow the user to interrupt */ |
329 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", |
330 | 0 | 50.0 + num_seen * 50.0 / no_of_nodes, NULL); |
331 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
332 | 0 | } |
333 | | |
334 | 0 | while (!igraph_dqueue_int_empty(&q)) { |
335 | 0 | igraph_integer_t act_node = igraph_dqueue_int_pop_back(&q); |
336 | 0 | const igraph_vector_int_t *tmp = igraph_adjlist_get(&adjlist, act_node); |
337 | 0 | const igraph_integer_t n = igraph_vector_int_size(tmp); |
338 | 0 | for (igraph_integer_t i = 0; i < n; i++) { |
339 | 0 | igraph_integer_t neighbor = VECTOR(*tmp)[i]; |
340 | 0 | if (VECTOR(next_nei)[neighbor] != 0) { |
341 | 0 | continue; |
342 | 0 | } |
343 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
344 | 0 | VECTOR(next_nei)[neighbor] = 1; |
345 | 0 | act_component_size++; |
346 | 0 | if (membership) { |
347 | 0 | VECTOR(*membership)[neighbor] = no_of_components; |
348 | 0 | } |
349 | |
|
350 | 0 | num_seen++; |
351 | 0 | if (num_seen % 10000 == 0) { |
352 | | /* time to report progress and allow the user to interrupt */ |
353 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", |
354 | 0 | 50.0 + num_seen * 50.0 / no_of_nodes, NULL); |
355 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
356 | 0 | } |
357 | 0 | } |
358 | 0 | } |
359 | | |
360 | 0 | no_of_components++; |
361 | 0 | if (csize) { |
362 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(csize, act_component_size)); |
363 | 0 | } |
364 | 0 | } |
365 | | |
366 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); |
367 | | |
368 | 0 | if (no) { |
369 | 0 | *no = no_of_components; |
370 | 0 | } |
371 | | |
372 | | /* Clean up */ |
373 | 0 | igraph_adjlist_destroy(&adjlist); |
374 | 0 | igraph_vector_int_destroy(&out); |
375 | 0 | igraph_dqueue_int_destroy(&q); |
376 | 0 | igraph_vector_int_destroy(&next_nei); |
377 | 0 | IGRAPH_FINALLY_CLEAN(4); |
378 | | |
379 | | /* Update cache */ |
380 | 0 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, no_of_components == 1); |
381 | 0 | if (no_of_components == 1) { |
382 | 0 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true); |
383 | 0 | } |
384 | |
|
385 | 0 | return IGRAPH_SUCCESS; |
386 | 0 | } |
387 | | |
388 | | static igraph_error_t igraph_i_is_connected_weak(const igraph_t *graph, igraph_bool_t *res); |
389 | | |
390 | | /** |
391 | | * \ingroup structural |
392 | | * \function igraph_is_connected |
393 | | * \brief Decides whether the graph is (weakly or strongly) connected. |
394 | | * |
395 | | * A graph is considered connected when any of its vertices is reachable |
396 | | * from any other. A directed graph with this property is called |
397 | | * \em strongly connected. A directed graph that would be connected when |
398 | | * ignoring the directions of its edges is called \em weakly connected. |
399 | | * |
400 | | * </para><para> |
401 | | * A graph with zero vertices (i.e. the null graph) is \em not connected by |
402 | | * definition. This behaviour changed in igraph 0.9; earlier versions assumed |
403 | | * that the null graph is connected. See the following issue on Github for the |
404 | | * argument that led us to change the definition: |
405 | | * https://github.com/igraph/igraph/issues/1539 |
406 | | * |
407 | | * </para><para> |
408 | | * The return value of this function is cached in the graph itself, separately |
409 | | * for weak and strong connectivity. Calling the function multiple times with |
410 | | * no modifications to the graph in between will return a cached value in O(1) |
411 | | * time. |
412 | | * |
413 | | * \param graph The graph object to analyze. |
414 | | * \param res Pointer to a Boolean variable, the result will be stored |
415 | | * here. |
416 | | * \param mode For a directed graph this specifies whether to calculate |
417 | | * weak or strong connectedness. Possible values: |
418 | | * \c IGRAPH_WEAK, |
419 | | * \c IGRAPH_STRONG. This argument is |
420 | | * ignored for undirected graphs. |
421 | | * \return Error code: |
422 | | * \c IGRAPH_EINVAL: invalid mode argument. |
423 | | * |
424 | | * \sa \ref igraph_connected_components() to find the connected components, |
425 | | * \ref igraph_is_biconnected() to check if the graph is 2-vertex-connected. |
426 | | * |
427 | | * Time complexity: O(|V|+|E|), the |
428 | | * number of vertices |
429 | | * plus the number of edges in the graph. |
430 | | */ |
431 | | |
432 | | igraph_error_t igraph_is_connected(const igraph_t *graph, igraph_bool_t *res, |
433 | 1.37k | igraph_connectedness_t mode) { |
434 | | |
435 | 1.37k | igraph_cached_property_t prop; |
436 | 1.37k | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
437 | 1.37k | igraph_integer_t no; |
438 | | |
439 | 1.37k | if (!igraph_is_directed(graph)) { |
440 | 1.37k | mode = IGRAPH_WEAK; |
441 | 1.37k | } |
442 | | |
443 | 1.37k | switch (mode) { |
444 | 1.37k | case IGRAPH_WEAK: |
445 | 1.37k | prop = IGRAPH_PROP_IS_WEAKLY_CONNECTED; |
446 | 1.37k | break; |
447 | | |
448 | 0 | case IGRAPH_STRONG: |
449 | 0 | prop = IGRAPH_PROP_IS_STRONGLY_CONNECTED; |
450 | 0 | break; |
451 | | |
452 | 0 | default: |
453 | 0 | IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); |
454 | 1.37k | } |
455 | | |
456 | 1.37k | IGRAPH_RETURN_IF_CACHED_BOOL(graph, prop, res); |
457 | | |
458 | 1.37k | if (no_of_nodes == 0) { |
459 | | /* Changed in igraph 0.9; see https://github.com/igraph/igraph/issues/1539 |
460 | | * for the reasoning behind the change */ |
461 | 0 | *res = false; |
462 | 1.37k | } else if (no_of_nodes == 1) { |
463 | 0 | *res = true; |
464 | 1.37k | } else if (mode == IGRAPH_WEAK) { |
465 | 1.37k | IGRAPH_CHECK(igraph_i_is_connected_weak(graph, res)); |
466 | 1.37k | } else { /* mode == IGRAPH_STRONG */ |
467 | | /* A strongly connected graph has at least as many edges as vertices, |
468 | | * except for the singleton graph, which is handled above. */ |
469 | 0 | if (igraph_ecount(graph) < no_of_nodes) { |
470 | 0 | *res = false; |
471 | 0 | } else { |
472 | 0 | IGRAPH_CHECK(igraph_i_connected_components_strong(graph, NULL, NULL, &no)); |
473 | 0 | *res = (no == 1); |
474 | 0 | } |
475 | 0 | } |
476 | | |
477 | | /* Cache updates are done in igraph_i_connected_components_strong() and |
478 | | * igraph_i_is_connected_weak() because those might be called from other |
479 | | * places and we want to make use of the caching if so */ |
480 | | |
481 | 1.37k | return IGRAPH_SUCCESS; |
482 | 1.37k | } |
483 | | |
484 | 1.37k | static igraph_error_t igraph_i_is_connected_weak(const igraph_t *graph, igraph_bool_t *res) { |
485 | 1.37k | const igraph_integer_t no_of_nodes = igraph_vcount(graph); |
486 | 1.37k | const igraph_integer_t no_of_edges = igraph_ecount(graph); |
487 | 1.37k | igraph_integer_t added_count; |
488 | 1.37k | igraph_bitset_t already_added; |
489 | 1.37k | igraph_vector_int_t neis; |
490 | 1.37k | igraph_dqueue_int_t q; |
491 | | |
492 | | /* By convention, the null graph is not considered connected. |
493 | | * See https://github.com/igraph/igraph/issues/1538 */ |
494 | 1.37k | if (no_of_nodes == 0) { |
495 | 0 | *res = false; |
496 | 0 | goto exit; |
497 | 0 | } |
498 | | |
499 | | /* A connected graph has at least |V| - 1 edges. */ |
500 | 1.37k | if (no_of_edges < no_of_nodes - 1) { |
501 | 1.16k | *res = false; |
502 | 1.16k | goto exit; |
503 | 1.16k | } |
504 | | |
505 | 206 | IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); |
506 | 206 | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 10); |
507 | 206 | IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); |
508 | | |
509 | | /* Try to find at least two components */ |
510 | 206 | IGRAPH_BIT_SET(already_added, 0); |
511 | 206 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, 0)); |
512 | | |
513 | 206 | added_count = 1; |
514 | 2.49k | while (! igraph_dqueue_int_empty(&q)) { |
515 | 2.39k | IGRAPH_ALLOW_INTERRUPTION(); |
516 | | |
517 | 2.39k | const igraph_integer_t actnode = igraph_dqueue_int_pop(&q); |
518 | | |
519 | 2.39k | IGRAPH_CHECK(igraph_neighbors( |
520 | 2.39k | graph, &neis, actnode, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE |
521 | 2.39k | )); |
522 | 2.39k | const igraph_integer_t nei_count = igraph_vector_int_size(&neis); |
523 | | |
524 | 9.89k | for (igraph_integer_t i = 0; i < nei_count; i++) { |
525 | 7.60k | const igraph_integer_t neighbor = VECTOR(neis)[i]; |
526 | 7.60k | if (IGRAPH_BIT_TEST(already_added, neighbor)) { |
527 | 5.05k | continue; |
528 | 5.05k | } |
529 | | |
530 | 2.54k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
531 | 2.54k | added_count++; |
532 | 2.54k | IGRAPH_BIT_SET(already_added, neighbor); |
533 | | |
534 | 2.54k | if (added_count == no_of_nodes) { |
535 | | /* We have already reached all nodes: the graph is connected. |
536 | | * We can stop the traversal now. */ |
537 | 99 | goto done; |
538 | 99 | } |
539 | 2.54k | } |
540 | 2.39k | } |
541 | | |
542 | 206 | done: |
543 | | /* Connected? */ |
544 | 206 | *res = (added_count == no_of_nodes); |
545 | | |
546 | 206 | igraph_bitset_destroy(&already_added); |
547 | 206 | igraph_dqueue_int_destroy(&q); |
548 | 206 | igraph_vector_int_destroy(&neis); |
549 | 206 | IGRAPH_FINALLY_CLEAN(3); |
550 | | |
551 | 1.37k | exit: |
552 | 1.37k | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, *res); |
553 | 1.37k | if (igraph_is_directed(graph) && !(*res)) { |
554 | | /* If the graph is not weakly connected, it is not strongly connected |
555 | | * either so we can also cache that */ |
556 | 0 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_STRONGLY_CONNECTED, *res); |
557 | 0 | } |
558 | | |
559 | 1.37k | return IGRAPH_SUCCESS; |
560 | 206 | } |
561 | | |
562 | | static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, |
563 | | igraph_graph_list_t *components, |
564 | | igraph_integer_t maxcompno, igraph_integer_t minelements); |
565 | | |
566 | | static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, |
567 | | igraph_graph_list_t *components, |
568 | | igraph_integer_t maxcompno, igraph_integer_t minelements); |
569 | | |
570 | | /** |
571 | | * \function igraph_decompose |
572 | | * \brief Decomposes a graph into connected components. |
573 | | * |
574 | | * Creates a separate graph for each component of a graph. Note that the |
575 | | * vertex IDs in the new graphs will be different than in the original |
576 | | * graph, except when there is only a single component in the original graph. |
577 | | * |
578 | | * \param graph The original graph. |
579 | | * \param components This list of graphs will contain the individual components. |
580 | | * It should be initialized before calling this function and will be resized |
581 | | * to hold the graphs. |
582 | | * \param mode Either \c IGRAPH_WEAK or \c IGRAPH_STRONG for weakly |
583 | | * and strongly connected components respectively. |
584 | | * \param maxcompno The maximum number of components to return. The |
585 | | * first \p maxcompno components will be returned (which hold at |
586 | | * least \p minelements vertices, see the next parameter), the |
587 | | * others will be ignored. Supply <code>-1</code> here if you don't |
588 | | * want to limit the number of components. |
589 | | * \param minelements The minimum number of vertices a component |
590 | | * should contain in order to place it in the \p components |
591 | | * vector. For example, supplying 2 here ignores isolated vertices. |
592 | | * \return Error code, \c IGRAPH_ENOMEM if there is not enough memory |
593 | | * to perform the operation. |
594 | | * |
595 | | * Added in version 0.2.</para><para> |
596 | | * |
597 | | * Time complexity: O(|V|+|E|), the number of vertices plus the number |
598 | | * of edges. |
599 | | * |
600 | | * \example examples/simple/igraph_decompose.c |
601 | | */ |
602 | | |
603 | | igraph_error_t igraph_decompose(const igraph_t *graph, igraph_graph_list_t *components, |
604 | | igraph_connectedness_t mode, |
605 | 1.67k | igraph_integer_t maxcompno, igraph_integer_t minelements) { |
606 | 1.67k | if (!igraph_is_directed(graph)) { |
607 | 1.67k | mode = IGRAPH_WEAK; |
608 | 1.67k | } |
609 | | |
610 | 1.67k | switch (mode) { |
611 | 1.67k | case IGRAPH_WEAK: |
612 | 1.67k | return igraph_i_decompose_weak(graph, components, maxcompno, minelements); |
613 | 0 | case IGRAPH_STRONG: |
614 | 0 | return igraph_i_decompose_strong(graph, components, maxcompno, minelements); |
615 | 0 | default: |
616 | 0 | IGRAPH_ERROR("Invalid connectedness mode.", IGRAPH_EINVAL); |
617 | 1.67k | } |
618 | 1.67k | } |
619 | | |
620 | | static igraph_error_t igraph_i_decompose_weak(const igraph_t *graph, |
621 | | igraph_graph_list_t *components, |
622 | 1.67k | igraph_integer_t maxcompno, igraph_integer_t minelements) { |
623 | | |
624 | 1.67k | igraph_integer_t actstart; |
625 | 1.67k | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
626 | 1.67k | igraph_integer_t resco = 0; /* number of graphs created so far */ |
627 | 1.67k | igraph_bitset_t already_added; |
628 | 1.67k | igraph_dqueue_int_t q; |
629 | 1.67k | igraph_vector_int_t verts; |
630 | 1.67k | igraph_vector_int_t neis; |
631 | 1.67k | igraph_vector_int_t vids_old2new; |
632 | 1.67k | igraph_integer_t i; |
633 | 1.67k | igraph_t newg; |
634 | | |
635 | | |
636 | 1.67k | if (maxcompno < 0) { |
637 | 1.67k | maxcompno = IGRAPH_INTEGER_MAX; |
638 | 1.67k | } |
639 | | |
640 | 1.67k | igraph_graph_list_clear(components); |
641 | | |
642 | | /* already_added keeps track of what nodes made it into a graph already */ |
643 | 1.67k | IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); |
644 | 1.67k | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
645 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); |
646 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); |
647 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); |
648 | 1.67k | igraph_vector_int_fill(&vids_old2new, -1); |
649 | | |
650 | | /* vids_old2new would have been created internally in igraph_induced_subgraph(), |
651 | | but it is slow if the graph is large and consists of many small components, |
652 | | so we create it once here and then re-use it */ |
653 | | |
654 | | /* add a node and its neighbors at once, recursively |
655 | | then switch to next node that has not been added already */ |
656 | 286k | for (actstart = 0; resco < maxcompno && actstart < no_of_nodes; actstart++) { |
657 | | |
658 | 284k | if (IGRAPH_BIT_TEST(already_added, actstart)) { |
659 | 36.9k | continue; |
660 | 36.9k | } |
661 | 247k | IGRAPH_ALLOW_INTERRUPTION(); |
662 | | |
663 | 247k | igraph_vector_int_clear(&verts); |
664 | | |
665 | | /* add the node itself */ |
666 | 247k | IGRAPH_BIT_SET(already_added, actstart); |
667 | 247k | IGRAPH_CHECK(igraph_vector_int_push_back(&verts, actstart)); |
668 | 247k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, actstart)); |
669 | | |
670 | | /* add the neighbors, recursively */ |
671 | 532k | while (!igraph_dqueue_int_empty(&q) ) { |
672 | | /* pop from the queue of this component */ |
673 | 284k | igraph_integer_t actvert = igraph_dqueue_int_pop(&q); |
674 | 284k | IGRAPH_CHECK(igraph_neighbors( |
675 | 284k | graph, &neis, actvert, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE |
676 | 284k | )); |
677 | 284k | igraph_integer_t nei_count = igraph_vector_int_size(&neis); |
678 | | /* iterate over the neighbors */ |
679 | 380k | for (i = 0; i < nei_count; i++) { |
680 | 96.1k | igraph_integer_t neighbor = VECTOR(neis)[i]; |
681 | 96.1k | if (IGRAPH_BIT_TEST(already_added, neighbor)) { |
682 | 59.2k | continue; |
683 | 59.2k | } |
684 | | /* add neighbor */ |
685 | 36.9k | IGRAPH_BIT_SET(already_added, neighbor); |
686 | | |
687 | | /* recursion: append neighbor to the queues */ |
688 | 36.9k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
689 | 36.9k | IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor)); |
690 | 36.9k | } |
691 | 284k | } |
692 | | |
693 | | /* ok, we have a component */ |
694 | 247k | if (igraph_vector_int_size(&verts) < minelements) { |
695 | 246k | continue; |
696 | 246k | } |
697 | | |
698 | 1.71k | IGRAPH_CHECK(igraph_i_induced_subgraph_map( |
699 | 1.71k | graph, &newg, igraph_vss_vector(&verts), |
700 | 1.71k | IGRAPH_SUBGRAPH_AUTO, &vids_old2new, |
701 | 1.71k | /* invmap = */ NULL, /* map_is_prepared = */ true |
702 | 1.71k | )); |
703 | 1.71k | IGRAPH_FINALLY(igraph_destroy, &newg); |
704 | 1.71k | IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg)); |
705 | 1.71k | IGRAPH_FINALLY_CLEAN(1); /* ownership of newg now taken by 'components' */ |
706 | 1.71k | resco++; |
707 | | |
708 | | /* vids_old2new does not have to be cleaned up here; since we are doing |
709 | | * weak decomposition, each vertex will appear in only one of the |
710 | | * connected components so we won't ever touch an item in vids_old2new |
711 | | * if it was already set to a non-zero value in a previous component */ |
712 | | |
713 | 1.71k | } /* for actstart++ */ |
714 | | |
715 | 1.67k | igraph_vector_int_destroy(&vids_old2new); |
716 | 1.67k | igraph_vector_int_destroy(&neis); |
717 | 1.67k | igraph_vector_int_destroy(&verts); |
718 | 1.67k | igraph_dqueue_int_destroy(&q); |
719 | 1.67k | igraph_bitset_destroy(&already_added); |
720 | 1.67k | IGRAPH_FINALLY_CLEAN(5); |
721 | | |
722 | 1.67k | return IGRAPH_SUCCESS; |
723 | 1.67k | } |
724 | | |
725 | | static igraph_error_t igraph_i_decompose_strong(const igraph_t *graph, |
726 | | igraph_graph_list_t *components, |
727 | 0 | igraph_integer_t maxcompno, igraph_integer_t minelements) { |
728 | | |
729 | |
|
730 | 0 | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
731 | | |
732 | | /* this is a heap used twice for checking what nodes have |
733 | | * been counted already */ |
734 | 0 | igraph_vector_int_t next_nei = IGRAPH_VECTOR_NULL; |
735 | |
|
736 | 0 | igraph_integer_t i, n, num_seen; |
737 | 0 | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
738 | |
|
739 | 0 | igraph_integer_t no_of_components = 0; |
740 | |
|
741 | 0 | igraph_vector_int_t out = IGRAPH_VECTOR_NULL; |
742 | 0 | const igraph_vector_int_t* tmp; |
743 | |
|
744 | 0 | igraph_adjlist_t adjlist; |
745 | 0 | igraph_vector_int_t verts; |
746 | 0 | igraph_vector_int_t vids_old2new; |
747 | 0 | igraph_t newg; |
748 | |
|
749 | 0 | if (maxcompno < 0) { |
750 | 0 | maxcompno = IGRAPH_INTEGER_MAX; |
751 | 0 | } |
752 | |
|
753 | 0 | igraph_graph_list_clear(components); |
754 | | |
755 | | /* The result */ |
756 | |
|
757 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&vids_old2new, no_of_nodes); |
758 | 0 | igraph_vector_int_fill(&vids_old2new, -1); |
759 | |
|
760 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&verts, 0); |
761 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&next_nei, no_of_nodes); |
762 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&out, 0); |
763 | 0 | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
764 | | |
765 | 0 | IGRAPH_CHECK(igraph_vector_int_reserve(&out, no_of_nodes)); |
766 | | |
767 | 0 | igraph_vector_int_null(&out); |
768 | |
|
769 | 0 | IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_OUT, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); |
770 | 0 | IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); |
771 | | |
772 | | /* vids_old2new would have been created internally in igraph_induced_subgraph(), |
773 | | but it is slow if the graph is large and consists of many small components, |
774 | | so we create it once here and then re-use it */ |
775 | | |
776 | | /* number of components seen */ |
777 | 0 | num_seen = 0; |
778 | | /* populate the 'out' vector by browsing a node and following up |
779 | | all its neighbors recursively, then switching to the next |
780 | | unassigned node */ |
781 | 0 | for (i = 0; i < no_of_nodes; i++) { |
782 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
783 | | |
784 | | /* get all the 'out' neighbors of this node |
785 | | * NOTE: next_nei is initialized [0, 0, ...] */ |
786 | 0 | tmp = igraph_adjlist_get(&adjlist, i); |
787 | 0 | if (VECTOR(next_nei)[i] > igraph_vector_int_size(tmp)) { |
788 | 0 | continue; |
789 | 0 | } |
790 | | |
791 | | /* add this node to the queue for this component */ |
792 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, i)); |
793 | | |
794 | | /* consume the tree from this node ("root") recursively |
795 | | * until there is no more */ |
796 | 0 | while (!igraph_dqueue_int_empty(&q)) { |
797 | | /* this looks up but does NOT consume the queue */ |
798 | 0 | igraph_integer_t act_node = igraph_dqueue_int_back(&q); |
799 | | |
800 | | /* get all neighbors of this node */ |
801 | 0 | tmp = igraph_adjlist_get(&adjlist, act_node); |
802 | 0 | if (VECTOR(next_nei)[act_node] == 0) { |
803 | | /* this is the first time we've met this vertex, |
804 | | * because next_nei is initialized [0, 0, ...] */ |
805 | 0 | VECTOR(next_nei)[act_node]++; |
806 | | /* back to the queue, same vertex is up again */ |
807 | |
|
808 | 0 | } else if (VECTOR(next_nei)[act_node] <= igraph_vector_int_size(tmp)) { |
809 | | /* we've already met this vertex but it has more children */ |
810 | 0 | igraph_integer_t neighbor = VECTOR(*tmp)[VECTOR(next_nei)[act_node] - 1]; |
811 | 0 | if (VECTOR(next_nei)[neighbor] == 0) { |
812 | | /* add the root of the other children to the queue */ |
813 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
814 | 0 | } |
815 | 0 | VECTOR(next_nei)[act_node]++; |
816 | 0 | } else { |
817 | | /* we've met this vertex and it has no more children */ |
818 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&out, act_node)); |
819 | | /* this consumes the queue, since there's nowhere to go */ |
820 | 0 | igraph_dqueue_int_pop_back(&q); |
821 | 0 | num_seen++; |
822 | |
|
823 | 0 | if (num_seen % 10000 == 0) { |
824 | | /* time to report progress and allow the user to interrupt */ |
825 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", |
826 | 0 | num_seen * 50.0 / no_of_nodes, NULL); |
827 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
828 | 0 | } |
829 | 0 | } |
830 | 0 | } /* while q */ |
831 | 0 | } /* for */ |
832 | | |
833 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", 50.0, NULL); |
834 | | |
835 | 0 | igraph_adjlist_destroy(&adjlist); |
836 | 0 | IGRAPH_FINALLY_CLEAN(1); |
837 | |
|
838 | 0 | IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, IGRAPH_IN, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); |
839 | 0 | IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); |
840 | | |
841 | | /* OK, we've the 'out' values for the nodes, let's use them in |
842 | | * decreasing order with the help of the next_nei heap */ |
843 | |
|
844 | 0 | igraph_vector_int_null(&next_nei); /* mark already added vertices */ |
845 | | |
846 | | /* number of components built */ |
847 | 0 | num_seen = 0; |
848 | 0 | while (!igraph_vector_int_empty(&out) && no_of_components < maxcompno) { |
849 | | /* consume the vector from the last element */ |
850 | 0 | igraph_integer_t grandfather = igraph_vector_int_pop_back(&out); |
851 | | |
852 | | /* been here, done that |
853 | | * NOTE: next_nei is initialized as [0, 0, ...] */ |
854 | 0 | if (VECTOR(next_nei)[grandfather] != 0) { |
855 | 0 | continue; |
856 | 0 | } |
857 | | |
858 | | /* collect all the members of this component */ |
859 | 0 | igraph_vector_int_clear(&verts); |
860 | | |
861 | | /* this node is gone for any future components */ |
862 | 0 | VECTOR(next_nei)[grandfather] = 1; |
863 | | |
864 | | /* add to component */ |
865 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&verts, grandfather)); |
866 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, grandfather)); |
867 | | |
868 | 0 | num_seen++; |
869 | 0 | if (num_seen % 10000 == 0) { |
870 | | /* time to report progress and allow the user to interrupt */ |
871 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", |
872 | 0 | 50.0 + num_seen * 50.0 / no_of_nodes, NULL); |
873 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
874 | 0 | } |
875 | | |
876 | 0 | while (!igraph_dqueue_int_empty(&q)) { |
877 | | /* consume the queue from this node */ |
878 | 0 | igraph_integer_t act_node = igraph_dqueue_int_pop_back(&q); |
879 | 0 | tmp = igraph_adjlist_get(&adjlist, act_node); |
880 | 0 | n = igraph_vector_int_size(tmp); |
881 | 0 | for (i = 0; i < n; i++) { |
882 | 0 | igraph_integer_t neighbor = VECTOR(*tmp)[i]; |
883 | 0 | if (VECTOR(next_nei)[neighbor] != 0) { |
884 | 0 | continue; |
885 | 0 | } |
886 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
887 | 0 | VECTOR(next_nei)[neighbor] = 1; |
888 | | |
889 | | /* add to component */ |
890 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&verts, neighbor)); |
891 | | |
892 | 0 | num_seen++; |
893 | 0 | if (num_seen % 10000 == 0) { |
894 | | /* time to report progress and allow the user to interrupt */ |
895 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", |
896 | 0 | 50.0 + num_seen * 50.0 / no_of_nodes, NULL); |
897 | 0 | IGRAPH_ALLOW_INTERRUPTION(); |
898 | 0 | } |
899 | 0 | } |
900 | 0 | } |
901 | | |
902 | | /* ok, we have a component */ |
903 | 0 | if (igraph_vector_int_size(&verts) < minelements) { |
904 | 0 | continue; |
905 | 0 | } |
906 | | |
907 | 0 | IGRAPH_CHECK(igraph_i_induced_subgraph_map( |
908 | 0 | graph, &newg, igraph_vss_vector(&verts), |
909 | 0 | IGRAPH_SUBGRAPH_AUTO, &vids_old2new, |
910 | 0 | /* invmap = */ 0, /* map_is_prepared = */ 1 |
911 | 0 | )); |
912 | 0 | IGRAPH_FINALLY(igraph_destroy, &newg); |
913 | 0 | IGRAPH_CHECK(igraph_graph_list_push_back(components, &newg)); |
914 | 0 | IGRAPH_FINALLY_CLEAN(1); /* ownership of newg now taken by 'components' */ |
915 | | |
916 | | /* vids_old2new has to be cleaned up here because a vertex may appear |
917 | | * in multiple strongly connected components. Simply calling |
918 | | * igraph_vector_int_fill() would be an O(n) operation where n is the number |
919 | | * of vertices in the large graph so we cannot do that; we have to |
920 | | * iterate over 'verts' instead */ |
921 | 0 | n = igraph_vector_int_size(&verts); |
922 | 0 | for (i = 0; i < n; i++) { |
923 | 0 | VECTOR(vids_old2new)[VECTOR(verts)[i]] = 0; |
924 | 0 | } |
925 | |
|
926 | 0 | no_of_components++; |
927 | 0 | } |
928 | | |
929 | 0 | IGRAPH_PROGRESS("Strongly connected components: ", 100.0, NULL); |
930 | | |
931 | | /* Clean up, return */ |
932 | | |
933 | 0 | igraph_vector_int_destroy(&vids_old2new); |
934 | 0 | igraph_vector_int_destroy(&verts); |
935 | 0 | igraph_adjlist_destroy(&adjlist); |
936 | 0 | igraph_vector_int_destroy(&out); |
937 | 0 | igraph_dqueue_int_destroy(&q); |
938 | 0 | igraph_vector_int_destroy(&next_nei); |
939 | 0 | IGRAPH_FINALLY_CLEAN(6); |
940 | |
|
941 | 0 | return IGRAPH_SUCCESS; |
942 | |
|
943 | 0 | } |
944 | | |
945 | | /** |
946 | | * \function igraph_articulation_points |
947 | | * \brief Finds the articulation points in a graph. |
948 | | * |
949 | | * A vertex is an articulation point if its removal increases |
950 | | * the number of (weakly) connected components in the graph. |
951 | | * |
952 | | * </para><para> |
953 | | * Note that a graph without any articulation points is not necessarily |
954 | | * biconnected. Counterexamples are the two-vertex complete graph as well |
955 | | * as empty graphs. Use \ref igraph_is_biconnected() to check whether |
956 | | * a graph is biconnected. |
957 | | * |
958 | | * \param graph The input graph. It will be treated as undirected. |
959 | | * \param res Pointer to an initialized vector, the articulation points will |
960 | | * be stored here. |
961 | | * \return Error code. |
962 | | * |
963 | | * Time complexity: O(|V|+|E|), linear in the number of vertices and edges. |
964 | | * |
965 | | * \sa \ref igraph_biconnected_components(), \ref igraph_is_bipartite(), |
966 | | * \ref igraph_connected_components(), \ref igraph_bridges() |
967 | | */ |
968 | | |
969 | 0 | igraph_error_t igraph_articulation_points(const igraph_t *graph, igraph_vector_int_t *res) { |
970 | |
|
971 | 0 | return igraph_biconnected_components(graph, NULL, NULL, NULL, NULL, res); |
972 | 0 | } |
973 | | |
974 | | /** |
975 | | * \function igraph_biconnected_components |
976 | | * \brief Calculates biconnected components. |
977 | | * |
978 | | * A graph is biconnected if the removal of any single vertex (and |
979 | | * its incident edges) does not disconnect it. |
980 | | * |
981 | | * </para><para> |
982 | | * A biconnected component of a graph is a maximal biconnected |
983 | | * subgraph of it. The biconnected components of a graph can be given |
984 | | * by a partition of its edges: every edge is a member of exactly |
985 | | * one biconnected component. Note that this is not true for |
986 | | * vertices: the same vertex can be part of many biconnected |
987 | | * components, while isolated vertices are part of none at all. |
988 | | * |
989 | | * </para><para> |
990 | | * Note that some authors do not consider the graph consisting of |
991 | | * two connected vertices as biconnected, however, igraph does. |
992 | | * |
993 | | * </para><para> |
994 | | * igraph does not consider components containing a single vertex only as |
995 | | * being biconnected. Isolated vertices will not be part of any of the |
996 | | * biconnected components. This means that checking whether there is a single |
997 | | * biconnected component is not sufficient for determining if a graph is |
998 | | * biconnected. Use \ref igraph_is_biconnected() for this purpose. |
999 | | * |
1000 | | * \param graph The input graph. It will be treated as undirected. |
1001 | | * \param no If not a \c NULL pointer, the number of biconnected components will |
1002 | | * be stored here. |
1003 | | * \param tree_edges If not a \c NULL pointer, then the found components |
1004 | | * are stored here, in a list of vectors. Every vector in the list |
1005 | | * is a biconnected component, represented by its edges. More precisely, |
1006 | | * a spanning tree of the biconnected component is returned. |
1007 | | * \param component_edges If not a \c NULL pointer, then the edges of the |
1008 | | * biconnected components are stored here, in the same form as for |
1009 | | * \c tree_edges. |
1010 | | * \param components If not a \c NULL pointer, then the vertices of the |
1011 | | * biconnected components are stored here, in the same format as |
1012 | | * for the previous two arguments. |
1013 | | * \param articulation_points If not a NULL pointer, then the |
1014 | | * articulation points of the graph are stored in this vector. |
1015 | | * A vertex is an articulation point if its removal increases the |
1016 | | * number of (weakly) connected components in the graph. |
1017 | | * \return Error code. |
1018 | | * |
1019 | | * Time complexity: O(|V|+|E|), linear in the number of vertices and |
1020 | | * edges, but only if you do not calculate \p components and |
1021 | | * \p component_edges. If you calculate \p components, then it is |
1022 | | * quadratic in the number of vertices. If you calculate |
1023 | | * \p component_edges as well, then it is cubic in the number of |
1024 | | * vertices. |
1025 | | * |
1026 | | * \sa \ref igraph_articulation_points(), \ref igraph_is_biconnected(), |
1027 | | * \ref igraph_connected_components(). |
1028 | | * |
1029 | | * \example examples/simple/igraph_biconnected_components.c |
1030 | | */ |
1031 | | |
1032 | | igraph_error_t igraph_biconnected_components(const igraph_t *graph, |
1033 | | igraph_integer_t *no, |
1034 | | igraph_vector_int_list_t *tree_edges, |
1035 | | igraph_vector_int_list_t *component_edges, |
1036 | | igraph_vector_int_list_t *components, |
1037 | 1.67k | igraph_vector_int_t *articulation_points) { |
1038 | | |
1039 | 1.67k | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
1040 | 1.67k | igraph_vector_int_t nextptr; |
1041 | 1.67k | igraph_vector_int_t num, low; |
1042 | 1.67k | igraph_bitset_t found; |
1043 | 1.67k | igraph_vector_int_t *adjedges; |
1044 | 1.67k | igraph_stack_int_t path; |
1045 | 1.67k | igraph_stack_int_t edgestack; |
1046 | 1.67k | igraph_inclist_t inclist; |
1047 | 1.67k | igraph_integer_t counter, rootdfs = 0; |
1048 | 1.67k | igraph_vector_int_t vertex_added; |
1049 | 1.67k | igraph_integer_t comps = 0; |
1050 | 1.67k | igraph_vector_int_list_t *mycomponents = components, vcomponents; |
1051 | | |
1052 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes); |
1053 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes); |
1054 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); |
1055 | 1.67k | IGRAPH_BITSET_INIT_FINALLY(&found, no_of_nodes); |
1056 | | |
1057 | 1.67k | IGRAPH_STACK_INT_INIT_FINALLY(&path, 100); |
1058 | 1.67k | IGRAPH_STACK_INT_INIT_FINALLY(&edgestack, 100); |
1059 | | |
1060 | 1.67k | IGRAPH_CHECK(igraph_inclist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); |
1061 | 1.67k | IGRAPH_FINALLY(igraph_inclist_destroy, &inclist); |
1062 | | |
1063 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&vertex_added, no_of_nodes); |
1064 | | |
1065 | 1.67k | if (no) { |
1066 | 1.67k | *no = 0; |
1067 | 1.67k | } |
1068 | 1.67k | if (tree_edges) { |
1069 | 0 | igraph_vector_int_list_clear(tree_edges); |
1070 | 0 | } |
1071 | 1.67k | if (components) { |
1072 | 1.67k | igraph_vector_int_list_clear(components); |
1073 | 1.67k | } |
1074 | 1.67k | if (component_edges) { |
1075 | 1.67k | igraph_vector_int_list_clear(component_edges); |
1076 | 1.67k | } |
1077 | 1.67k | if (articulation_points) { |
1078 | 1.67k | igraph_vector_int_clear(articulation_points); |
1079 | 1.67k | } |
1080 | 1.67k | if (component_edges && !components) { |
1081 | 0 | mycomponents = &vcomponents; |
1082 | 0 | IGRAPH_VECTOR_INT_LIST_INIT_FINALLY(mycomponents, 0); |
1083 | 0 | } |
1084 | | |
1085 | 286k | for (igraph_integer_t i = 0; i < no_of_nodes; i++) { |
1086 | | |
1087 | 284k | if (VECTOR(low)[i] != 0) { |
1088 | 36.9k | continue; /* already visited */ |
1089 | 36.9k | } |
1090 | | |
1091 | 247k | IGRAPH_ALLOW_INTERRUPTION(); |
1092 | | |
1093 | 247k | IGRAPH_CHECK(igraph_stack_int_push(&path, i)); |
1094 | 247k | counter = 1; |
1095 | 247k | rootdfs = 0; |
1096 | 247k | VECTOR(low)[i] = VECTOR(num)[i] = counter++; |
1097 | 653k | while (!igraph_stack_int_empty(&path)) { |
1098 | 405k | igraph_integer_t n; |
1099 | 405k | igraph_integer_t act = igraph_stack_int_top(&path); |
1100 | 405k | igraph_integer_t actnext = VECTOR(nextptr)[act]; |
1101 | | |
1102 | 405k | adjedges = igraph_inclist_get(&inclist, act); |
1103 | 405k | n = igraph_vector_int_size(adjedges); |
1104 | 405k | if (actnext < n) { |
1105 | | /* Step down (maybe) */ |
1106 | 121k | igraph_integer_t edge = VECTOR(*adjedges)[actnext]; |
1107 | 121k | igraph_integer_t nei = IGRAPH_OTHER(graph, edge, act); |
1108 | 121k | if (VECTOR(low)[nei] == 0) { |
1109 | 36.9k | if (act == i) { |
1110 | 9.12k | rootdfs++; |
1111 | 9.12k | } |
1112 | 36.9k | IGRAPH_CHECK(igraph_stack_int_push(&edgestack, edge)); |
1113 | 36.9k | IGRAPH_CHECK(igraph_stack_int_push(&path, nei)); |
1114 | 36.9k | VECTOR(low)[nei] = VECTOR(num)[nei] = counter++; |
1115 | 84.1k | } else { |
1116 | | /* Update low value if needed */ |
1117 | 84.1k | if (VECTOR(num)[nei] < VECTOR(low)[act]) { |
1118 | 35.6k | VECTOR(low)[act] = VECTOR(num)[nei]; |
1119 | 35.6k | } |
1120 | 84.1k | } |
1121 | 121k | VECTOR(nextptr)[act] += 1; |
1122 | 284k | } else { |
1123 | | /* Step up */ |
1124 | 284k | igraph_stack_int_pop(&path); |
1125 | 284k | if (!igraph_stack_int_empty(&path)) { |
1126 | 36.9k | igraph_integer_t prev = igraph_stack_int_top(&path); |
1127 | | /* Update LOW value if needed */ |
1128 | 36.9k | if (VECTOR(low)[act] < VECTOR(low)[prev]) { |
1129 | 4.13k | VECTOR(low)[prev] = VECTOR(low)[act]; |
1130 | 4.13k | } |
1131 | | /* Check for articulation point */ |
1132 | 36.9k | if (VECTOR(low)[act] >= VECTOR(num)[prev]) { |
1133 | 31.0k | if (articulation_points && !IGRAPH_BIT_TEST(found, prev) |
1134 | 31.0k | && prev != i /* the root */) { |
1135 | 9.22k | IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, prev)); |
1136 | 9.22k | IGRAPH_BIT_SET(found, prev); |
1137 | 9.22k | } |
1138 | 31.0k | if (no) { |
1139 | 31.0k | *no += 1; |
1140 | 31.0k | } |
1141 | | |
1142 | | /*------------------------------------*/ |
1143 | | /* Record the biconnected component just found */ |
1144 | 31.0k | if (tree_edges || mycomponents) { |
1145 | 31.0k | igraph_vector_int_t *v, *v2; |
1146 | 31.0k | comps++; |
1147 | 31.0k | if (tree_edges) { |
1148 | 0 | IGRAPH_CHECK(igraph_vector_int_list_push_back_new(tree_edges, &v)); |
1149 | 0 | } |
1150 | 31.0k | if (mycomponents) { |
1151 | 31.0k | IGRAPH_CHECK(igraph_vector_int_list_push_back_new(mycomponents, &v2)); |
1152 | 31.0k | } |
1153 | | |
1154 | 36.9k | while (!igraph_stack_int_empty(&edgestack)) { |
1155 | 36.9k | igraph_integer_t e = igraph_stack_int_pop(&edgestack); |
1156 | 36.9k | igraph_integer_t from = IGRAPH_FROM(graph, e); |
1157 | 36.9k | igraph_integer_t to = IGRAPH_TO(graph, e); |
1158 | 36.9k | if (tree_edges) { |
1159 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(v, e)); |
1160 | 0 | } |
1161 | 36.9k | if (mycomponents) { |
1162 | 36.9k | if (VECTOR(vertex_added)[from] != comps) { |
1163 | 33.9k | VECTOR(vertex_added)[from] = comps; |
1164 | 33.9k | IGRAPH_CHECK(igraph_vector_int_push_back(v2, from)); |
1165 | 33.9k | } |
1166 | 36.9k | if (VECTOR(vertex_added)[to] != comps) { |
1167 | 34.0k | VECTOR(vertex_added)[to] = comps; |
1168 | 34.0k | IGRAPH_CHECK(igraph_vector_int_push_back(v2, to)); |
1169 | 34.0k | } |
1170 | 36.9k | } |
1171 | 36.9k | if (from == prev || to == prev) { |
1172 | 31.0k | break; |
1173 | 31.0k | } |
1174 | 36.9k | } |
1175 | | |
1176 | 31.0k | if (component_edges) { |
1177 | 31.0k | igraph_vector_int_t *nodes = igraph_vector_int_list_get_ptr(mycomponents, comps - 1); |
1178 | 31.0k | igraph_integer_t ii, no_vert = igraph_vector_int_size(nodes); |
1179 | 31.0k | igraph_vector_int_t *vv; |
1180 | | |
1181 | 31.0k | IGRAPH_CHECK(igraph_vector_int_list_push_back_new(component_edges, &vv)); |
1182 | 99.0k | for (ii = 0; ii < no_vert; ii++) { |
1183 | 68.0k | igraph_integer_t vert = VECTOR(*nodes)[ii]; |
1184 | 68.0k | igraph_vector_int_t *edges = igraph_inclist_get(&inclist, |
1185 | 68.0k | vert); |
1186 | 68.0k | igraph_integer_t j, nn = igraph_vector_int_size(edges); |
1187 | 1.26M | for (j = 0; j < nn; j++) { |
1188 | 1.19M | igraph_integer_t e = VECTOR(*edges)[j]; |
1189 | 1.19M | igraph_integer_t nei = IGRAPH_OTHER(graph, e, vert); |
1190 | 1.19M | if (VECTOR(vertex_added)[nei] == comps && nei < vert) { |
1191 | 48.0k | IGRAPH_CHECK(igraph_vector_int_push_back(vv, e)); |
1192 | 48.0k | } |
1193 | 1.19M | } |
1194 | 68.0k | } |
1195 | 31.0k | } |
1196 | 31.0k | } /* record component if requested */ |
1197 | | /*------------------------------------*/ |
1198 | | |
1199 | 31.0k | } |
1200 | 36.9k | } /* !igraph_stack_int_empty(&path) */ |
1201 | 284k | } |
1202 | | |
1203 | 405k | } /* !igraph_stack_int_empty(&path) */ |
1204 | | |
1205 | 247k | if (articulation_points && rootdfs >= 2) { |
1206 | 1.02k | IGRAPH_CHECK(igraph_vector_int_push_back(articulation_points, i)); |
1207 | 1.02k | } |
1208 | | |
1209 | 247k | } /* i < no_of_nodes */ |
1210 | | |
1211 | 1.67k | if (mycomponents != components) { |
1212 | 0 | igraph_vector_int_list_destroy(mycomponents); |
1213 | 0 | IGRAPH_FINALLY_CLEAN(1); |
1214 | 0 | } |
1215 | | |
1216 | 1.67k | igraph_vector_int_destroy(&vertex_added); |
1217 | 1.67k | igraph_inclist_destroy(&inclist); |
1218 | 1.67k | igraph_stack_int_destroy(&edgestack); |
1219 | 1.67k | igraph_stack_int_destroy(&path); |
1220 | 1.67k | igraph_bitset_destroy(&found); |
1221 | 1.67k | igraph_vector_int_destroy(&low); |
1222 | 1.67k | igraph_vector_int_destroy(&num); |
1223 | 1.67k | igraph_vector_int_destroy(&nextptr); |
1224 | 1.67k | IGRAPH_FINALLY_CLEAN(8); |
1225 | | |
1226 | 1.67k | return IGRAPH_SUCCESS; |
1227 | 1.67k | } |
1228 | | |
1229 | | /** |
1230 | | * \function igraph_is_biconnected |
1231 | | * \brief Checks whether a graph is biconnected. |
1232 | | * |
1233 | | * A graph is biconnected if the removal of any single vertex (and |
1234 | | * its incident edges) does not disconnect it. |
1235 | | * |
1236 | | * </para><para> |
1237 | | * igraph does not consider single-vertex graphs biconnected. |
1238 | | * |
1239 | | * </para><para> |
1240 | | * Note that some authors do not consider the graph consisting of |
1241 | | * two connected vertices as biconnected, however, igraph does. |
1242 | | * |
1243 | | * \param graph The input graph. It will be treated as undirected. |
1244 | | * \param result If not a \c NULL pointer, the result will be returned here. |
1245 | | * \return Error code. |
1246 | | * |
1247 | | * Time complexity: O(|V|+|E|), linear in the number of vertices and edges. |
1248 | | * |
1249 | | * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components(). |
1250 | | * |
1251 | | * \example examples/simple/igraph_is_biconnected.c |
1252 | | */ |
1253 | | |
1254 | 0 | igraph_error_t igraph_is_biconnected(const igraph_t *graph, igraph_bool_t *res) { |
1255 | |
|
1256 | 0 | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
1257 | 0 | igraph_vector_int_t nextptr; |
1258 | 0 | igraph_vector_int_t num, low; |
1259 | 0 | igraph_stack_int_t path; |
1260 | 0 | igraph_lazy_adjlist_t inclist; |
1261 | 0 | igraph_bool_t is_biconnected = true; |
1262 | |
|
1263 | 0 | if (no_of_nodes == 0 || no_of_nodes == 1) { |
1264 | | /* The null graph is not connected, hence it is not biconnected either. |
1265 | | * The singleton graph is not biconnected. */ |
1266 | 0 | is_biconnected = false; |
1267 | 0 | goto exit2; |
1268 | 0 | } |
1269 | | |
1270 | | /* no_of_nodes == 2 is special: if the two nodes are connected, then the |
1271 | | * graph is both biconnected _and_ acyclic, unlike no_of_nodes >= 3, where |
1272 | | * the graph is not acyclic if it is biconnected. */ |
1273 | | |
1274 | | /* We do not touch the cache for graphs with less than three nodes because |
1275 | | * of all the edge cases. */ |
1276 | 0 | if (no_of_nodes >= 3 && ( |
1277 | 0 | (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED) && |
1278 | 0 | !igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED)) || |
1279 | 0 | (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) && |
1280 | 0 | igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST)) |
1281 | 0 | )) { |
1282 | 0 | is_biconnected = false; |
1283 | 0 | goto exit2; |
1284 | 0 | } |
1285 | | |
1286 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&nextptr, no_of_nodes); |
1287 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&num, no_of_nodes); |
1288 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); |
1289 | | |
1290 | 0 | IGRAPH_STACK_INT_INIT_FINALLY(&path, 100); |
1291 | | |
1292 | 0 | IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &inclist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); |
1293 | 0 | IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &inclist); |
1294 | |
|
1295 | 0 | const igraph_integer_t root = 0; /* start DFS from vertex 0 */ |
1296 | 0 | igraph_integer_t counter = 1; |
1297 | 0 | igraph_integer_t rootdfs = 0; |
1298 | 0 | IGRAPH_CHECK(igraph_stack_int_push(&path, root)); |
1299 | 0 | VECTOR(low)[root] = VECTOR(num)[root] = counter++; |
1300 | 0 | while (!igraph_stack_int_empty(&path)) { |
1301 | 0 | igraph_integer_t act = igraph_stack_int_top(&path); |
1302 | 0 | igraph_integer_t actnext = VECTOR(nextptr)[act]; |
1303 | |
|
1304 | 0 | const igraph_vector_int_t *neis = igraph_lazy_adjlist_get(&inclist, act); |
1305 | 0 | const igraph_integer_t n = igraph_vector_int_size(neis); |
1306 | 0 | if (actnext < n) { |
1307 | | /* Step down (maybe) */ |
1308 | 0 | igraph_integer_t nei = VECTOR(*neis)[actnext]; |
1309 | 0 | if (VECTOR(low)[nei] == 0) { |
1310 | 0 | if (act == root) { |
1311 | 0 | rootdfs++; |
1312 | 0 | } |
1313 | 0 | IGRAPH_CHECK(igraph_stack_int_push(&path, nei)); |
1314 | 0 | VECTOR(low)[nei] = VECTOR(num)[nei] = counter++; |
1315 | 0 | } else { |
1316 | | /* Update low value if needed */ |
1317 | 0 | if (VECTOR(num)[nei] < VECTOR(low)[act]) { |
1318 | 0 | VECTOR(low)[act] = VECTOR(num)[nei]; |
1319 | 0 | } |
1320 | 0 | } |
1321 | 0 | VECTOR(nextptr)[act] += 1; |
1322 | 0 | } else { |
1323 | | /* Step up */ |
1324 | 0 | igraph_stack_int_pop(&path); |
1325 | 0 | if (!igraph_stack_int_empty(&path)) { |
1326 | 0 | igraph_integer_t prev = igraph_stack_int_top(&path); |
1327 | | /* Update LOW value if needed */ |
1328 | 0 | if (VECTOR(low)[act] < VECTOR(low)[prev]) { |
1329 | 0 | VECTOR(low)[prev] = VECTOR(low)[act]; |
1330 | 0 | } |
1331 | | /* Check for articulation point */ |
1332 | 0 | if (VECTOR(low)[act] >= VECTOR(num)[prev]) { |
1333 | 0 | if (prev != root /* the root */) { |
1334 | | /* Found an articulation point, the graph is not biconnected */ |
1335 | 0 | is_biconnected = false; |
1336 | 0 | goto exit; |
1337 | 0 | } |
1338 | 0 | } |
1339 | 0 | } /* !igraph_stack_int_empty(&path) */ |
1340 | 0 | } |
1341 | |
|
1342 | 0 | } /* !igraph_stack_int_empty(&path) */ |
1343 | | |
1344 | | /* The root is an articulation point, the graph is not biconnected */ |
1345 | 0 | if (rootdfs >= 2) { |
1346 | 0 | is_biconnected = false; |
1347 | 0 | goto exit; |
1348 | 0 | } |
1349 | | |
1350 | | /* We did not reach all vertices, the graph is not connected */ |
1351 | 0 | if (counter <= no_of_nodes) { |
1352 | 0 | is_biconnected = false; |
1353 | 0 | goto exit; |
1354 | 0 | } |
1355 | | |
1356 | 0 | exit: |
1357 | |
|
1358 | 0 | igraph_lazy_adjlist_destroy(&inclist); |
1359 | 0 | igraph_stack_int_destroy(&path); |
1360 | 0 | igraph_vector_int_destroy(&low); |
1361 | 0 | igraph_vector_int_destroy(&num); |
1362 | 0 | igraph_vector_int_destroy(&nextptr); |
1363 | 0 | IGRAPH_FINALLY_CLEAN(5); |
1364 | |
|
1365 | 0 | exit2: |
1366 | |
|
1367 | 0 | if (res) { |
1368 | 0 | *res = is_biconnected; |
1369 | 0 | } |
1370 | | |
1371 | | /* We do not touch the cache for graphs with less than three nodes because |
1372 | | * of all the edge cases. */ |
1373 | 0 | if (is_biconnected && no_of_nodes >= 3) { |
1374 | 0 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true); |
1375 | 0 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, false); |
1376 | 0 | } |
1377 | |
|
1378 | 0 | return IGRAPH_SUCCESS; |
1379 | 0 | } |
1380 | | |
1381 | | |
1382 | | /** |
1383 | | * \function igraph_bridges |
1384 | | * \brief Finds all bridges in a graph. |
1385 | | * |
1386 | | * An edge is a bridge if its removal increases the number of (weakly) |
1387 | | * connected components in the graph. |
1388 | | * |
1389 | | * \param graph The input graph. It will be treated as undirected. |
1390 | | * \param res Pointer to an initialized vector, the |
1391 | | * bridges will be stored here as edge indices. |
1392 | | * \return Error code. |
1393 | | * |
1394 | | * Time complexity: O(|V|+|E|), linear in the number of vertices and edges. |
1395 | | * |
1396 | | * \sa \ref igraph_articulation_points(), \ref igraph_biconnected_components(), |
1397 | | * \ref igraph_connected_components() |
1398 | | */ |
1399 | | |
1400 | 1.67k | igraph_error_t igraph_bridges(const igraph_t *graph, igraph_vector_int_t *bridges) { |
1401 | | |
1402 | | /* The algorithm is based on https://www.geeksforgeeks.org/bridge-in-a-graph/ |
1403 | | but instead of keeping track of the parent of each vertex in the DFS tree |
1404 | | we keep track of its incoming edge. This is necessary to support multigraphs. |
1405 | | Additionally, we use explicit stacks instead of recursion to avoid |
1406 | | stack overflow. */ |
1407 | | |
1408 | 1.67k | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
1409 | 1.67k | igraph_inclist_t il; |
1410 | 1.67k | igraph_bitset_t visited; |
1411 | 1.67k | igraph_vector_int_t vis; /* vis[u] time when vertex u was first visited */ |
1412 | 1.67k | igraph_vector_int_t low; /* low[u] is the lowest visit time of vertices reachable from u */ |
1413 | 1.67k | igraph_vector_int_t incoming_edge; |
1414 | 1.67k | igraph_stack_int_t su, si; |
1415 | 1.67k | igraph_integer_t time; |
1416 | | |
1417 | 1.67k | IGRAPH_CHECK(igraph_inclist_init(graph, &il, IGRAPH_ALL, IGRAPH_LOOPS_TWICE)); |
1418 | 1.67k | IGRAPH_FINALLY(igraph_inclist_destroy, &il); |
1419 | | |
1420 | | |
1421 | 1.67k | IGRAPH_BITSET_INIT_FINALLY(&visited, no_of_nodes); |
1422 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&vis, no_of_nodes); |
1423 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&low, no_of_nodes); |
1424 | | |
1425 | 1.67k | IGRAPH_VECTOR_INT_INIT_FINALLY(&incoming_edge, no_of_nodes); |
1426 | 1.67k | igraph_vector_int_fill(&incoming_edge, -1); |
1427 | | |
1428 | 1.67k | IGRAPH_STACK_INT_INIT_FINALLY(&su, 0); |
1429 | 1.67k | IGRAPH_STACK_INT_INIT_FINALLY(&si, 0); |
1430 | | |
1431 | 1.67k | igraph_vector_int_clear(bridges); |
1432 | | |
1433 | 1.67k | time = 0; |
1434 | 286k | for (igraph_integer_t start = 0; start < no_of_nodes; ++start) { |
1435 | 284k | if (! IGRAPH_BIT_TEST(visited, start)) { |
1436 | | /* Perform a DFS from 'start'. |
1437 | | * The top of the su stack is u, the vertex currently being visited. |
1438 | | * The top of the si stack is i, the index of u's neighbour that will |
1439 | | * be processed next. */ |
1440 | | |
1441 | 247k | IGRAPH_CHECK(igraph_stack_int_push(&su, start)); |
1442 | 247k | IGRAPH_CHECK(igraph_stack_int_push(&si, 0)); |
1443 | | |
1444 | 653k | while (! igraph_stack_int_empty(&su)) { |
1445 | 405k | igraph_integer_t u = igraph_stack_int_pop(&su); |
1446 | 405k | igraph_integer_t i = igraph_stack_int_pop(&si); |
1447 | | |
1448 | 405k | if (i == 0) { |
1449 | | /* We are at the first step of visiting vertex u. */ |
1450 | | |
1451 | 284k | IGRAPH_BIT_SET(visited, u); |
1452 | | |
1453 | 284k | time += 1; |
1454 | | |
1455 | 284k | VECTOR(vis)[u] = time; |
1456 | 284k | VECTOR(low)[u] = time; |
1457 | 284k | } |
1458 | | |
1459 | 405k | igraph_vector_int_t *incedges = igraph_inclist_get(&il, u); |
1460 | | |
1461 | 405k | if (i < igraph_vector_int_size(incedges)) { |
1462 | 121k | IGRAPH_CHECK(igraph_stack_int_push(&su, u)); |
1463 | 121k | IGRAPH_CHECK(igraph_stack_int_push(&si, i+1)); |
1464 | | |
1465 | 121k | igraph_integer_t edge = VECTOR(*incedges)[i]; |
1466 | 121k | igraph_integer_t v = IGRAPH_OTHER(graph, edge, u); |
1467 | | |
1468 | 121k | if (! IGRAPH_BIT_TEST(visited, v)) { |
1469 | 36.9k | VECTOR(incoming_edge)[v] = edge; |
1470 | | |
1471 | 36.9k | IGRAPH_CHECK(igraph_stack_int_push(&su, v)); |
1472 | 36.9k | IGRAPH_CHECK(igraph_stack_int_push(&si, 0)); |
1473 | 84.1k | } else if (edge != VECTOR(incoming_edge)[u]) { |
1474 | 47.2k | VECTOR(low)[u] = VECTOR(low)[u] < VECTOR(vis)[v] ? VECTOR(low)[u] : VECTOR(vis)[v]; |
1475 | 47.2k | } |
1476 | 284k | } else { |
1477 | | /* We are done visiting vertex u, so it won't be put back on the stack. |
1478 | | * We are ready to update the 'low' value of its parent w, and decide |
1479 | | * whether its incoming edge is a bridge. */ |
1480 | | |
1481 | 284k | igraph_integer_t edge = VECTOR(incoming_edge)[u]; |
1482 | 284k | if (edge >= 0) { |
1483 | 36.9k | igraph_integer_t w = IGRAPH_OTHER(graph, edge, u); /* parent of u in DFS tree */ |
1484 | 36.9k | VECTOR(low)[w] = VECTOR(low)[w] < VECTOR(low)[u] ? VECTOR(low)[w] : VECTOR(low)[u]; |
1485 | 36.9k | if (VECTOR(low)[u] > VECTOR(vis)[w]) { |
1486 | 29.7k | IGRAPH_CHECK(igraph_vector_int_push_back(bridges, edge)); |
1487 | 29.7k | } |
1488 | 36.9k | } |
1489 | 284k | } |
1490 | 405k | } |
1491 | 247k | } |
1492 | 284k | } |
1493 | | |
1494 | 1.67k | igraph_stack_int_destroy(&si); |
1495 | 1.67k | igraph_stack_int_destroy(&su); |
1496 | 1.67k | igraph_vector_int_destroy(&incoming_edge); |
1497 | 1.67k | igraph_vector_int_destroy(&low); |
1498 | 1.67k | igraph_vector_int_destroy(&vis); |
1499 | 1.67k | igraph_bitset_destroy(&visited); |
1500 | 1.67k | igraph_inclist_destroy(&il); |
1501 | 1.67k | IGRAPH_FINALLY_CLEAN(7); |
1502 | | |
1503 | 1.67k | return IGRAPH_SUCCESS; |
1504 | 1.67k | } |
1505 | | |
1506 | | /** |
1507 | | * \ingroup structural |
1508 | | * \function igraph_subcomponent |
1509 | | * \brief The vertices reachable from a given vertex. |
1510 | | * |
1511 | | * This function returns the set of vertices reachable from a specified |
1512 | | * vertex. In undirected graphs, this is simple the set of vertices within |
1513 | | * the same component. |
1514 | | * |
1515 | | * \param graph The graph object. |
1516 | | * \param res The result, vector with the IDs of the vertices reachable |
1517 | | * from \p vertex. |
1518 | | * \param vertex The id of the vertex of which the component is |
1519 | | * searched. |
1520 | | * \param mode Type of the component for directed graphs, possible |
1521 | | * values: |
1522 | | * \clist |
1523 | | * \cli IGRAPH_OUT |
1524 | | * the set of vertices reachable \em from the |
1525 | | * \p vertex, |
1526 | | * \cli IGRAPH_IN |
1527 | | * the set of vertices from which the |
1528 | | * \p vertex is reachable. |
1529 | | * \cli IGRAPH_ALL |
1530 | | * the graph is considered as an |
1531 | | * undirected graph. Note that this is \em not the same |
1532 | | * as the union of the previous two. |
1533 | | * \endclist |
1534 | | * \return Error code: |
1535 | | * \clist |
1536 | | * \cli IGRAPH_ENOMEM |
1537 | | * not enough memory for temporary data. |
1538 | | * \cli IGRAPH_EINVVID |
1539 | | * \p vertex is an invalid vertex ID |
1540 | | * \cli IGRAPH_EINVMODE |
1541 | | * invalid mode argument passed. |
1542 | | * \endclist |
1543 | | * |
1544 | | * Time complexity: O(|V|+|E|), |
1545 | | * |V| and |
1546 | | * |E| are the number of vertices and |
1547 | | * edges in the graph. |
1548 | | * |
1549 | | * \sa \ref igraph_induced_subgraph() if you want a graph object consisting only |
1550 | | * a given set of vertices and the edges between them; |
1551 | | * \ref igraph_reachability() to efficiently compute the reachable set from \em all |
1552 | | * vertices; \ref igraph_neighborhood() to find vertices within a given distance. |
1553 | | */ |
1554 | | igraph_error_t igraph_subcomponent( |
1555 | | const igraph_t *graph, igraph_vector_int_t *res, igraph_integer_t vertex, |
1556 | | igraph_neimode_t mode |
1557 | 1.66k | ) { |
1558 | | |
1559 | 1.66k | igraph_integer_t no_of_nodes = igraph_vcount(graph); |
1560 | 1.66k | igraph_dqueue_int_t q = IGRAPH_DQUEUE_NULL; |
1561 | 1.66k | igraph_bitset_t already_added; |
1562 | 1.66k | igraph_integer_t i, vsize; |
1563 | 1.66k | igraph_vector_int_t tmp = IGRAPH_VECTOR_NULL; |
1564 | | |
1565 | 1.66k | if (vertex < 0 || vertex >= no_of_nodes) { |
1566 | 0 | IGRAPH_ERROR("Vertex id out of range.", IGRAPH_EINVVID); |
1567 | 0 | } |
1568 | 1.66k | if (mode != IGRAPH_OUT && mode != IGRAPH_IN && |
1569 | 1.66k | mode != IGRAPH_ALL) { |
1570 | 0 | IGRAPH_ERROR("Invalid mode argument.", IGRAPH_EINVMODE); |
1571 | 0 | } |
1572 | | |
1573 | 1.66k | igraph_vector_int_clear(res); |
1574 | | |
1575 | 1.66k | IGRAPH_BITSET_INIT_FINALLY(&already_added, no_of_nodes); |
1576 | 1.66k | IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0); |
1577 | 1.66k | IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100); |
1578 | | |
1579 | 1.66k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, vertex)); |
1580 | 1.66k | IGRAPH_CHECK(igraph_vector_int_push_back(res, vertex)); |
1581 | 1.66k | IGRAPH_BIT_SET(already_added, vertex); |
1582 | | |
1583 | 26.9k | while (!igraph_dqueue_int_empty(&q)) { |
1584 | 25.2k | igraph_integer_t actnode = igraph_dqueue_int_pop(&q); |
1585 | | |
1586 | 25.2k | IGRAPH_ALLOW_INTERRUPTION(); |
1587 | | |
1588 | 25.2k | IGRAPH_CHECK(igraph_neighbors(graph, &tmp, actnode, mode, IGRAPH_NO_LOOPS, IGRAPH_MULTIPLE)); |
1589 | 25.2k | vsize = igraph_vector_int_size(&tmp); |
1590 | 87.4k | for (i = 0; i < vsize; i++) { |
1591 | 62.1k | igraph_integer_t neighbor = VECTOR(tmp)[i]; |
1592 | | |
1593 | 62.1k | if (IGRAPH_BIT_TEST(already_added, neighbor)) { |
1594 | 38.5k | continue; |
1595 | 38.5k | } |
1596 | 23.5k | IGRAPH_BIT_SET(already_added, neighbor); |
1597 | 23.5k | IGRAPH_CHECK(igraph_vector_int_push_back(res, neighbor)); |
1598 | 23.5k | IGRAPH_CHECK(igraph_dqueue_int_push(&q, neighbor)); |
1599 | 23.5k | } |
1600 | 25.2k | } |
1601 | | |
1602 | 1.66k | igraph_dqueue_int_destroy(&q); |
1603 | 1.66k | igraph_vector_int_destroy(&tmp); |
1604 | 1.66k | igraph_bitset_destroy(&already_added); |
1605 | 1.66k | IGRAPH_FINALLY_CLEAN(3); |
1606 | | |
1607 | 1.66k | return IGRAPH_SUCCESS; |
1608 | 1.66k | } |