/src/igraph/src/properties/trees.c
Line | Count | Source |
1 | | /* |
2 | | igraph library. |
3 | | Copyright (C) 2005-2021 The igraph development team |
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, write to the Free Software |
17 | | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA |
18 | | 02110-1301 USA |
19 | | |
20 | | */ |
21 | | |
22 | | #include "igraph_structural.h" |
23 | | |
24 | | #include "igraph_bitset.h" |
25 | | #include "igraph_constructors.h" |
26 | | #include "igraph_cycles.h" |
27 | | #include "igraph_dqueue.h" |
28 | | #include "igraph_interface.h" |
29 | | #include "igraph_stack.h" |
30 | | |
31 | | /** |
32 | | * \function igraph_unfold_tree |
33 | | * \brief Unfolding a graph into a tree, by possibly multiplicating its vertices. |
34 | | * |
35 | | * A graph is converted into a tree (or forest, if it is unconnected), |
36 | | * by performing a breadth-first search on it, and replicating |
37 | | * vertices that were found a second, third, etc. time. |
38 | | * |
39 | | * \param graph The input graph, it can be either directed or |
40 | | * undirected. |
41 | | * \param tree Pointer to an uninitialized graph object, the result is |
42 | | * stored here. |
43 | | * \param mode For directed graphs; whether to follow paths along edge |
44 | | * directions (\c IGRAPH_OUT), or the opposite (\c IGRAPH_IN), or |
45 | | * ignore edge directions completely (\c IGRAPH_ALL). It is ignored |
46 | | * for undirected graphs. |
47 | | * \param roots A numeric vector giving the root vertex, or vertices |
48 | | * (if the graph is not connected), to start from. |
49 | | * \param vertex_index Pointer to an initialized vector, or a null |
50 | | * pointer. If not a null pointer, then a mapping from the vertices |
51 | | * in the new graph to the ones in the original is created here. |
52 | | * \return Error code. |
53 | | * |
54 | | * Time complexity: O(n+m), linear in the number vertices and edges. |
55 | | * |
56 | | */ |
57 | | igraph_error_t igraph_unfold_tree(const igraph_t *graph, igraph_t *tree, |
58 | | igraph_neimode_t mode, const igraph_vector_int_t *roots, |
59 | 0 | igraph_vector_int_t *vertex_index) { |
60 | |
|
61 | 0 | igraph_int_t no_of_nodes = igraph_vcount(graph); |
62 | 0 | igraph_int_t no_of_edges = igraph_ecount(graph); |
63 | 0 | igraph_int_t no_of_roots = igraph_vector_int_size(roots); |
64 | 0 | igraph_int_t tree_vertex_count = no_of_nodes; |
65 | |
|
66 | 0 | igraph_vector_int_t edges; |
67 | 0 | igraph_bitset_t seen_vertices; |
68 | 0 | igraph_bitset_t seen_edges; |
69 | |
|
70 | 0 | igraph_dqueue_int_t Q; |
71 | 0 | igraph_vector_int_t neis; |
72 | |
|
73 | 0 | igraph_int_t v_ptr = no_of_nodes; |
74 | |
|
75 | 0 | if (! igraph_vector_int_isininterval(roots, 0, no_of_nodes-1)) { |
76 | 0 | IGRAPH_ERROR("All roots should be vertices of the graph.", IGRAPH_EINVVID); |
77 | 0 | } |
78 | | |
79 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); |
80 | 0 | IGRAPH_CHECK(igraph_vector_int_reserve(&edges, no_of_edges * 2)); |
81 | 0 | IGRAPH_DQUEUE_INT_INIT_FINALLY(&Q, 100); |
82 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); |
83 | 0 | IGRAPH_BITSET_INIT_FINALLY(&seen_vertices, no_of_nodes); |
84 | 0 | IGRAPH_BITSET_INIT_FINALLY(&seen_edges, no_of_edges); |
85 | | |
86 | 0 | if (vertex_index) { |
87 | 0 | IGRAPH_CHECK(igraph_vector_int_range(vertex_index, 0, no_of_nodes)); |
88 | 0 | } |
89 | | |
90 | 0 | for (igraph_int_t r = 0; r < no_of_roots; r++) { |
91 | |
|
92 | 0 | igraph_int_t root = VECTOR(*roots)[r]; |
93 | 0 | IGRAPH_BIT_SET(seen_vertices, root); |
94 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&Q, root)); |
95 | | |
96 | 0 | while (!igraph_dqueue_int_empty(&Q)) { |
97 | 0 | igraph_int_t actnode = igraph_dqueue_int_pop(&Q); |
98 | |
|
99 | 0 | IGRAPH_CHECK(igraph_incident(graph, &neis, actnode, mode, IGRAPH_LOOPS)); |
100 | | |
101 | 0 | igraph_int_t n = igraph_vector_int_size(&neis); |
102 | 0 | for (igraph_int_t i = 0; i < n; i++) { |
103 | |
|
104 | 0 | igraph_int_t edge = VECTOR(neis)[i]; |
105 | 0 | igraph_int_t from = IGRAPH_FROM(graph, edge); |
106 | 0 | igraph_int_t to = IGRAPH_TO(graph, edge); |
107 | 0 | igraph_int_t nei = IGRAPH_OTHER(graph, edge, actnode); |
108 | |
|
109 | 0 | if (! IGRAPH_BIT_TEST(seen_edges, edge)) { |
110 | |
|
111 | 0 | IGRAPH_BIT_SET(seen_edges, edge); |
112 | |
|
113 | 0 | if (! IGRAPH_BIT_TEST(seen_vertices, nei)) { |
114 | |
|
115 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); |
116 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); |
117 | | |
118 | 0 | IGRAPH_BIT_SET(seen_vertices, nei); |
119 | 0 | IGRAPH_CHECK(igraph_dqueue_int_push(&Q, nei)); |
120 | |
|
121 | 0 | } else { |
122 | |
|
123 | 0 | tree_vertex_count++; |
124 | 0 | if (vertex_index) { |
125 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(vertex_index, nei)); |
126 | 0 | } |
127 | | |
128 | 0 | if (from == nei) { |
129 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v_ptr++)); |
130 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, to)); |
131 | 0 | } else { |
132 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, from)); |
133 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v_ptr++)); |
134 | 0 | } |
135 | 0 | } |
136 | 0 | } |
137 | |
|
138 | 0 | } /* for i<n */ |
139 | |
|
140 | 0 | } /* ! igraph_dqueue_int_empty(&Q) */ |
141 | |
|
142 | 0 | } /* r < igraph_vector_int_size(roots) */ |
143 | | |
144 | 0 | igraph_bitset_destroy(&seen_edges); |
145 | 0 | igraph_bitset_destroy(&seen_vertices); |
146 | 0 | igraph_vector_int_destroy(&neis); |
147 | 0 | igraph_dqueue_int_destroy(&Q); |
148 | 0 | IGRAPH_FINALLY_CLEAN(4); |
149 | |
|
150 | 0 | IGRAPH_CHECK(igraph_create(tree, &edges, tree_vertex_count, igraph_is_directed(graph))); |
151 | 0 | igraph_vector_int_destroy(&edges); |
152 | 0 | IGRAPH_FINALLY_CLEAN(1); |
153 | |
|
154 | 0 | return IGRAPH_SUCCESS; |
155 | 0 | } |
156 | | |
157 | | /* igraph_is_tree -- check if a graph is a tree */ |
158 | | |
159 | | /* count the number of vertices reachable from the root */ |
160 | 93 | static igraph_error_t igraph_i_is_tree_visitor(const igraph_t *graph, igraph_int_t root, igraph_neimode_t mode, igraph_int_t *visited_count) { |
161 | 93 | igraph_stack_int_t stack; |
162 | 93 | igraph_bitset_t visited; |
163 | 93 | igraph_vector_int_t neighbors; |
164 | 93 | igraph_int_t i; |
165 | | |
166 | 93 | IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbors, 0); |
167 | | |
168 | 93 | IGRAPH_BITSET_INIT_FINALLY(&visited, igraph_vcount(graph)); |
169 | | |
170 | 93 | IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); |
171 | 93 | IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); |
172 | | |
173 | 93 | *visited_count = 0; |
174 | | |
175 | | /* push the root into the stack */ |
176 | 93 | IGRAPH_CHECK(igraph_stack_int_push(&stack, root)); |
177 | | |
178 | 4.06k | while (! igraph_stack_int_empty(&stack)) { |
179 | 3.97k | igraph_int_t u; |
180 | 3.97k | igraph_int_t ncount; |
181 | | |
182 | | /* take a vertex from the stack, mark it as visited */ |
183 | 3.97k | u = igraph_stack_int_pop(&stack); |
184 | 3.97k | if (IGRAPH_LIKELY(! IGRAPH_BIT_TEST(visited, u))) { |
185 | 2.89k | IGRAPH_BIT_SET(visited, u); |
186 | 2.89k | *visited_count += 1; |
187 | 2.89k | } |
188 | | |
189 | | /* register all its yet-unvisited neighbours for future processing */ |
190 | 3.97k | IGRAPH_CHECK(igraph_neighbors(graph, &neighbors, u, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); |
191 | 3.97k | ncount = igraph_vector_int_size(&neighbors); |
192 | 66.5k | for (i = 0; i < ncount; ++i) { |
193 | 62.5k | igraph_int_t v = VECTOR(neighbors)[i]; |
194 | 62.5k | if (! IGRAPH_BIT_TEST(visited, v)) { |
195 | 3.88k | IGRAPH_CHECK(igraph_stack_int_push(&stack, v)); |
196 | 3.88k | } |
197 | 62.5k | } |
198 | 3.97k | } |
199 | | |
200 | 93 | igraph_vector_int_destroy(&neighbors); |
201 | 93 | igraph_stack_int_destroy(&stack); |
202 | 93 | igraph_bitset_destroy(&visited); |
203 | 93 | IGRAPH_FINALLY_CLEAN(3); |
204 | | |
205 | 93 | return IGRAPH_SUCCESS; |
206 | 93 | } |
207 | | |
208 | | |
209 | | /** |
210 | | * \ingroup structural |
211 | | * \function igraph_is_tree |
212 | | * \brief Decides whether the graph is a tree. |
213 | | * |
214 | | * An undirected graph is a tree if it is connected and has no cycles. |
215 | | * |
216 | | * </para><para> |
217 | | * In the directed case, an additional requirement is that all edges |
218 | | * are oriented away from a root (out-tree or arborescence) or all edges |
219 | | * are oriented towards a root (in-tree or anti-arborescence). |
220 | | * This test can be controlled using the \p mode parameter. |
221 | | * |
222 | | * </para><para> |
223 | | * By convention, the null graph (i.e. the graph with no vertices) is considered |
224 | | * not to be connected, and therefore not a tree. |
225 | | * |
226 | | * \param graph The graph object to analyze. |
227 | | * \param res Pointer to a Boolean variable, the result will be stored |
228 | | * here. |
229 | | * \param root If not \c NULL, the root node will be stored here. When \p mode |
230 | | * is \c IGRAPH_ALL or the graph is undirected, any vertex can be the root |
231 | | * and \p root is set to 0 (the first vertex). When \p mode is \c IGRAPH_OUT |
232 | | * or \c IGRAPH_IN, the root is set to the vertex with zero in- or out-degree, |
233 | | * respectively. |
234 | | * \param mode For a directed graph this specifies whether to test for an |
235 | | * out-tree, an in-tree or ignore edge directions. The respective |
236 | | * possible values are: |
237 | | * \c IGRAPH_OUT, \c IGRAPH_IN, \c IGRAPH_ALL. This argument is |
238 | | * ignored for undirected graphs. |
239 | | * \return Error code: |
240 | | * \c IGRAPH_EINVAL: invalid mode argument. |
241 | | * |
242 | | * Time complexity: At most O(|V|+|E|), the |
243 | | * number of vertices plus the number of edges in the graph. |
244 | | * |
245 | | * \sa \ref igraph_is_forest() to check if all components are trees, |
246 | | * which is equivalent to the graph lacking undirected cycles; |
247 | | * \ref igraph_is_connected(), \ref igraph_is_acyclic() |
248 | | * |
249 | | * \example examples/simple/igraph_kary_tree.c |
250 | | */ |
251 | 612 | igraph_error_t igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_int_t *root, igraph_neimode_t mode) { |
252 | 612 | igraph_bool_t is_tree = false; |
253 | 612 | igraph_bool_t treat_as_undirected = !igraph_is_directed(graph) || mode == IGRAPH_ALL; |
254 | 612 | igraph_int_t iroot = 0; |
255 | 612 | igraph_int_t visited_count; |
256 | 612 | igraph_int_t vcount, ecount; |
257 | | |
258 | 612 | vcount = igraph_vcount(graph); |
259 | 612 | ecount = igraph_ecount(graph); |
260 | | |
261 | 612 | if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED)) { |
262 | 0 | igraph_bool_t weakly_connected = igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED); |
263 | |
|
264 | 0 | if (weakly_connected) { |
265 | | /* For undirected graphs and for directed graphs with mode == IGRAPH_ALL, |
266 | | * we can return early if we know from the cache that the graph is weakly |
267 | | * connected and is a forest. We can do this even if the user wants the |
268 | | * root vertex because we always return zero as the root vertex for |
269 | | * undirected graphs */ |
270 | 0 | if (treat_as_undirected && |
271 | 0 | igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST) && |
272 | 0 | igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST) |
273 | 0 | ) { |
274 | 0 | is_tree = true; |
275 | 0 | iroot = 0; |
276 | 0 | goto success; |
277 | 0 | } |
278 | 0 | } else /* ! weakly_connected */ { |
279 | | /* If the graph is not weakly connected, then it is neither an undirected |
280 | | * not a directed tree. There is no root, so we can return early. */ |
281 | 0 | is_tree = false; |
282 | 0 | goto success; |
283 | 0 | } |
284 | 0 | } |
285 | | |
286 | | /* A tree must have precisely vcount-1 edges. */ |
287 | | /* By convention, the zero-vertex graph will not be considered a tree. */ |
288 | 612 | if (ecount != vcount - 1) { |
289 | 518 | is_tree = false; |
290 | 518 | goto success; |
291 | 518 | } |
292 | | |
293 | | /* The single-vertex graph is a tree, provided it has no edges (checked in the previous if (..)) */ |
294 | 94 | if (vcount == 1) { |
295 | 1 | is_tree = true; |
296 | 1 | iroot = 0; |
297 | 1 | goto success; |
298 | 1 | } |
299 | | |
300 | | /* For higher vertex counts we cannot short-circuit due to the possibility |
301 | | * of loops or multi-edges even when the edge count is correct. */ |
302 | | |
303 | | /* Ignore mode for undirected graphs. */ |
304 | 93 | if (! igraph_is_directed(graph)) { |
305 | 93 | mode = IGRAPH_ALL; |
306 | 93 | } |
307 | | |
308 | | /* The main algorithm: |
309 | | * We find a root and check that all other vertices are reachable from it. |
310 | | * We have already checked the number of edges, so with the additional |
311 | | * reachability condition we can verify if the graph is a tree. |
312 | | * |
313 | | * For directed graphs, the root is the node with no incoming/outgoing |
314 | | * connections, depending on 'mode'. For undirected, it is arbitrary, so |
315 | | * we choose 0. |
316 | | */ |
317 | | |
318 | 93 | is_tree = true; /* assume success */ |
319 | | |
320 | 93 | switch (mode) { |
321 | 93 | case IGRAPH_ALL: |
322 | 93 | iroot = 0; |
323 | 93 | break; |
324 | | |
325 | 0 | case IGRAPH_IN: |
326 | 0 | case IGRAPH_OUT: { |
327 | 0 | igraph_vector_int_t degree; |
328 | 0 | igraph_int_t i; |
329 | |
|
330 | 0 | IGRAPH_CHECK(igraph_vector_int_init(°ree, 0)); |
331 | 0 | IGRAPH_FINALLY(igraph_vector_int_destroy, °ree); |
332 | |
|
333 | 0 | IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_REVERSE_MODE(mode), IGRAPH_LOOPS)); |
334 | | |
335 | 0 | for (i = 0; i < vcount; ++i) { |
336 | 0 | if (VECTOR(degree)[i] == 0) { |
337 | 0 | break; |
338 | 0 | } |
339 | 0 | if (VECTOR(degree)[i] > 1) { |
340 | | /* In an out-tree, all vertices have in-degree 1, except for the root, |
341 | | * which has in-degree 0. Thus, if we encounter a larger in-degree, |
342 | | * the graph cannot be an out-tree. |
343 | | * We could perform this check for all degrees, but that would not |
344 | | * improve performance when the graph is indeed a tree, persumably |
345 | | * the most common case. Thus we only check until finding the root. |
346 | | */ |
347 | 0 | is_tree = false; |
348 | 0 | break; |
349 | 0 | } |
350 | 0 | } |
351 | | |
352 | | /* If no suitable root is found, the graph is not a tree. */ |
353 | 0 | if (is_tree && i == vcount) { |
354 | 0 | is_tree = false; |
355 | 0 | } else { |
356 | 0 | iroot = i; |
357 | 0 | } |
358 | |
|
359 | 0 | igraph_vector_int_destroy(°ree); |
360 | 0 | IGRAPH_FINALLY_CLEAN(1); |
361 | 0 | } |
362 | | |
363 | 0 | break; |
364 | 0 | default: |
365 | 0 | IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVMODE); |
366 | 93 | } |
367 | | |
368 | | /* if no suitable root was found, skip visiting vertices */ |
369 | 93 | if (is_tree) { |
370 | 93 | IGRAPH_CHECK(igraph_i_is_tree_visitor(graph, iroot, mode, &visited_count)); |
371 | 93 | is_tree = visited_count == vcount; |
372 | 93 | } |
373 | | |
374 | 612 | success: |
375 | 612 | if (res) { |
376 | 612 | *res = is_tree; |
377 | 612 | } |
378 | | |
379 | 612 | if (root) { |
380 | 0 | *root = iroot; |
381 | 0 | } |
382 | | |
383 | 612 | if (is_tree) { |
384 | | /* A graph that is a directed tree is also an undirected tree. |
385 | | * An undirected tree is weakly connected and is a forest, |
386 | | * so we can cache this. */ |
387 | 19 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, true); |
388 | 19 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_WEAKLY_CONNECTED, true); |
389 | 19 | } |
390 | | |
391 | 612 | return IGRAPH_SUCCESS; |
392 | 93 | } |
393 | | |
394 | | /* igraph_is_forest() -- check if a graph is a forest */ |
395 | | |
396 | | /* Verify that the graph has no cycles and count the number of reachable vertices. |
397 | | * This function performs a DFS starting from 'root'. |
398 | | * If it finds a cycle, it sets *res to false, otherwise it does not change it. |
399 | | * *visited_count will be incremented by the number of vertices reachable from 'root', |
400 | | * including 'root' itself. |
401 | | */ |
402 | | static igraph_error_t igraph_i_is_forest_visitor( |
403 | | const igraph_t *graph, igraph_int_t root, igraph_neimode_t mode, |
404 | | igraph_bitset_t *visited, igraph_stack_int_t *stack, igraph_vector_int_t *neis, |
405 | | igraph_int_t *visited_count, igraph_bool_t *res) |
406 | 32.9k | { |
407 | 32.9k | igraph_int_t i; |
408 | | |
409 | 32.9k | igraph_stack_int_clear(stack); |
410 | | |
411 | | /* push the root onto the stack */ |
412 | 32.9k | IGRAPH_CHECK(igraph_stack_int_push(stack, root)); |
413 | | |
414 | 73.4k | while (! igraph_stack_int_empty(stack)) { |
415 | 40.6k | igraph_int_t u; |
416 | 40.6k | igraph_int_t ncount; |
417 | | |
418 | | /* Take a vertex from stack and check if it is already visited. |
419 | | * If yes, then we found a cycle: the graph is not a forest. |
420 | | * Otherwise mark it as visited and continue. |
421 | | */ |
422 | 40.6k | u = igraph_stack_int_pop(stack); |
423 | 40.6k | if (IGRAPH_LIKELY(! IGRAPH_BIT_TEST(*visited, u))) { |
424 | 40.5k | IGRAPH_BIT_SET(*visited, u); |
425 | 40.5k | *visited_count += 1; |
426 | 40.5k | } |
427 | 157 | else { |
428 | 157 | *res = false; |
429 | 157 | break; |
430 | 157 | } |
431 | | |
432 | | /* Vertex discovery: Register all its neighbours for future processing */ |
433 | 40.5k | IGRAPH_CHECK(igraph_neighbors(graph, neis, u, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE)); |
434 | 40.5k | ncount = igraph_vector_int_size(neis); |
435 | | |
436 | 58.6k | for (i = 0; i < ncount; ++i) { |
437 | 18.3k | igraph_int_t v = VECTOR(*neis)[i]; |
438 | | |
439 | 18.3k | if (mode == IGRAPH_ALL) { |
440 | | /* In the undirected case, we avoid returning to the predecessor |
441 | | * vertex of 'v' in the DFS tree by skipping visited vertices. |
442 | | * |
443 | | * Note that in order to succcessfully detect a cycle, a vertex |
444 | | * within that cycle must end up on the stack more than once. |
445 | | * Does skipping visited vertices preclude this sometimes? |
446 | | * No, because any visited vertex can only be accessed through |
447 | | * an already discovered vertex (i.e. one that has already been |
448 | | * pushed onto the stack). |
449 | | */ |
450 | 18.3k | if (IGRAPH_LIKELY(! IGRAPH_BIT_TEST(*visited, v))) { |
451 | 9.60k | IGRAPH_CHECK(igraph_stack_int_push(stack, v)); |
452 | 9.60k | } |
453 | | /* To check for a self-loop in undirected graph */ |
454 | 8.78k | else if (v == u) { |
455 | 243 | *res = false; |
456 | 243 | break; |
457 | 243 | } |
458 | 18.3k | } |
459 | 0 | else { |
460 | 0 | IGRAPH_CHECK(igraph_stack_int_push(stack, v)); |
461 | 0 | } |
462 | 18.3k | } |
463 | 40.5k | } |
464 | | |
465 | 32.9k | return IGRAPH_SUCCESS; |
466 | 32.9k | } |
467 | | |
468 | | static igraph_error_t igraph_i_is_forest( |
469 | | const igraph_t *graph, igraph_bool_t *res, |
470 | | igraph_vector_int_t *roots, igraph_neimode_t mode |
471 | | ); |
472 | | |
473 | | /** |
474 | | * \ingroup structural |
475 | | * \function igraph_is_forest |
476 | | * \brief Decides whether the graph is a forest. |
477 | | * |
478 | | * An undirected graph is a forest if it has no cycles. Equivalently, |
479 | | * a graph is a forest if all connected components are trees. |
480 | | * |
481 | | * </para><para> |
482 | | * In the directed case, an additional requirement is that edges in each |
483 | | * tree are oriented away from the root (out-trees or arborescences) or all edges |
484 | | * are oriented towards the root (in-trees or anti-arborescences). |
485 | | * This test can be controlled using the \p mode parameter. |
486 | | * |
487 | | * </para><para> |
488 | | * By convention, the null graph (i.e. the graph with no vertices) is considered |
489 | | * to be a forest. |
490 | | * |
491 | | * </para><para> |
492 | | * The \p res return value of this function is cached in the graph itself if |
493 | | * \p mode is set to \c IGRAPH_ALL or if the graph is undirected. Calling the |
494 | | * function multiple times with no modifications to the graph in between |
495 | | * will return a cached value in O(1) time if the roots are not requested. |
496 | | * |
497 | | * \param graph The graph object to analyze. |
498 | | * \param res Pointer to a Boolean variable. If not \c NULL, then the result will |
499 | | * be stored here. |
500 | | * \param roots If not \c NULL, the root nodes will be stored here. When \p mode |
501 | | * is \c IGRAPH_ALL or the graph is undirected, any one vertex from each |
502 | | * component can be the root. When \p mode is \c IGRAPH_OUT |
503 | | * or \c IGRAPH_IN, all the vertices with zero in- or out-degree, |
504 | | * respectively are considered as root nodes. |
505 | | * \param mode For a directed graph this specifies whether to test for an |
506 | | * out-forest, an in-forest or ignore edge directions. The respective |
507 | | * possible values are: |
508 | | * \c IGRAPH_OUT, \c IGRAPH_IN, \c IGRAPH_ALL. This argument is |
509 | | * ignored for undirected graphs. |
510 | | * \return Error code: |
511 | | * \c IGRAPH_EINVMODE: invalid mode argument. |
512 | | * |
513 | | * Time complexity: At most O(|V|+|E|), the |
514 | | * number of vertices plus the number of edges in the graph. |
515 | | * |
516 | | * \sa \ref igraph_is_tree() to check if a graph is a tree, i.e. a forest with |
517 | | * a single component; \ref igraph_is_acyclic() to check if a graph lacks |
518 | | * (undirected or directed) cycles. |
519 | | */ |
520 | | igraph_error_t igraph_is_forest(const igraph_t *graph, igraph_bool_t *res, |
521 | 612 | igraph_vector_int_t *roots, igraph_neimode_t mode) { |
522 | | |
523 | 612 | const igraph_bool_t treat_as_undirected = !igraph_is_directed(graph) || mode == IGRAPH_ALL; |
524 | | |
525 | 612 | if (!roots && !res) { |
526 | 0 | return IGRAPH_SUCCESS; |
527 | 0 | } |
528 | | |
529 | | /* Note on cache use: |
530 | | * |
531 | | * The IGRAPH_PROP_IS_FOREST cached property is equivalent to this function's |
532 | | * result ONLY in the undirected case. Keep in mind that a graph that is not |
533 | | * a directed forest may still be an undirected forest, i.e. may still be free |
534 | | * of undirected cycles. Example: 1->2<-3->4. |
535 | | */ |
536 | | |
537 | 612 | if (igraph_i_property_cache_has(graph, IGRAPH_PROP_IS_FOREST)) { |
538 | 0 | const igraph_bool_t no_undirected_cycles = igraph_i_property_cache_get_bool(graph, IGRAPH_PROP_IS_FOREST); |
539 | 0 | if (treat_as_undirected && res && ! roots) { |
540 | | /* If the graph is treated as undirected and no roots are requested, |
541 | | * we can directly use the cached IGRAPH_PROP_IS_FOREST value. */ |
542 | 0 | *res = no_undirected_cycles; |
543 | 0 | return IGRAPH_SUCCESS; |
544 | 0 | } else { |
545 | | /* Otherwise we can use negative cached values (i.e. "false"): |
546 | | * - A graph with undirected cycles cannot be a directed forest. |
547 | | * - If the graph is not a forest, we don't need to look for roots. |
548 | | */ |
549 | 0 | if (! no_undirected_cycles) { |
550 | 0 | if (res) { *res = false; } |
551 | 0 | if (roots) { igraph_vector_int_clear(roots); } |
552 | 0 | return IGRAPH_SUCCESS; |
553 | 0 | } |
554 | 0 | } |
555 | 0 | } |
556 | | |
557 | 612 | IGRAPH_CHECK(igraph_i_is_forest(graph, res, roots, mode)); |
558 | | |
559 | | /* At this point we know whether the graph is an (undirected or directed) forest |
560 | | * as we have at least one of 'res' or 'roots'. The case when both are NULL was |
561 | | * caught above. */ |
562 | 612 | igraph_bool_t is_forest; |
563 | 612 | if (res != NULL) { |
564 | 612 | is_forest = *res; |
565 | 612 | } else /* roots != NULL */ { |
566 | 0 | is_forest = igraph_vcount(graph) == 0 || !igraph_vector_int_empty(roots); |
567 | 0 | } |
568 | 612 | if (is_forest) { |
569 | | /* If the graph is a directed forest, then it has no undirected cycles. |
570 | | * We can enter positive results in the cache unconditionally. */ |
571 | 226 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, true); |
572 | 386 | } else if (treat_as_undirected) { |
573 | | /* However, if the graph is not a directed forest, it might still be |
574 | | * an undirected forest. We can only enter negative results in the cache |
575 | | * when edge directions were ignored, but NOT in the directed case. */ |
576 | 386 | igraph_i_property_cache_set_bool_checked(graph, IGRAPH_PROP_IS_FOREST, false); |
577 | 386 | } |
578 | | |
579 | 612 | return IGRAPH_SUCCESS; |
580 | 612 | } |
581 | | |
582 | | static igraph_error_t igraph_i_is_forest( |
583 | | const igraph_t *graph, igraph_bool_t *res, |
584 | | igraph_vector_int_t *roots, igraph_neimode_t mode |
585 | 612 | ) { |
586 | 612 | igraph_bitset_t visited; |
587 | 612 | igraph_vector_int_t neis; |
588 | 612 | igraph_stack_int_t stack; |
589 | 612 | igraph_int_t visited_count = 0; |
590 | 612 | igraph_int_t vcount, ecount; |
591 | 612 | igraph_int_t v; |
592 | 612 | igraph_bool_t result; |
593 | | |
594 | 612 | vcount = igraph_vcount(graph); |
595 | 612 | ecount = igraph_ecount(graph); |
596 | | |
597 | 612 | if (roots) { |
598 | 0 | igraph_vector_int_clear(roots); |
599 | 0 | } |
600 | | |
601 | | /* Any graph with 0 edges is a forest. */ |
602 | 612 | if (ecount == 0) { |
603 | 37 | if (res) { |
604 | 37 | *res = true; |
605 | 37 | } |
606 | 37 | if (roots) { |
607 | 0 | for (v = 0; v < vcount; v++) { |
608 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); |
609 | 0 | } |
610 | 0 | } |
611 | 37 | return IGRAPH_SUCCESS; |
612 | 37 | } |
613 | | |
614 | | /* A forest can have at most vcount-1 edges. */ |
615 | 575 | if (ecount > vcount - 1) { |
616 | 93 | if (res) { |
617 | 93 | *res = false; |
618 | 93 | } |
619 | 93 | return IGRAPH_SUCCESS; |
620 | 93 | } |
621 | | |
622 | | /* Ignore mode for undirected graphs. */ |
623 | 482 | if (! igraph_is_directed(graph)) { |
624 | 482 | mode = IGRAPH_ALL; |
625 | 482 | } |
626 | | |
627 | 482 | result = true; /* assume success */ |
628 | | |
629 | 482 | IGRAPH_BITSET_INIT_FINALLY(&visited, vcount); |
630 | | |
631 | 482 | IGRAPH_CHECK(igraph_stack_int_init(&stack, 0)); |
632 | 482 | IGRAPH_FINALLY(igraph_stack_int_destroy, &stack); |
633 | | |
634 | 482 | IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0); |
635 | | |
636 | | /* The main algorithm: |
637 | | * |
638 | | * Undirected Graph:- We add each unvisited vertex to the roots vector, and |
639 | | * mark all other vertices that are reachable from it as visited. |
640 | | * |
641 | | * Directed Graph:- For each tree, the root is the node with no |
642 | | * incoming/outgoing connections, depending on 'mode'. We add each vertex |
643 | | * with zero degree to the roots vector and mark all other vertices that are |
644 | | * reachable from it as visited. |
645 | | * |
646 | | * If all the vertices are visited exactly once, then the graph is a forest. |
647 | | */ |
648 | | |
649 | 482 | switch (mode) { |
650 | 482 | case IGRAPH_ALL: |
651 | 482 | { |
652 | 37.8k | for (v = 0; v < vcount; ++v) { |
653 | 37.6k | if (!result) { |
654 | 281 | break; |
655 | 281 | } |
656 | 37.3k | if (! IGRAPH_BIT_TEST(visited, v)) { |
657 | 32.9k | if (roots) { |
658 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); |
659 | 0 | } |
660 | 32.9k | IGRAPH_CHECK(igraph_i_is_forest_visitor( |
661 | 32.9k | graph, v, mode, |
662 | 32.9k | &visited, &stack, &neis, |
663 | 32.9k | &visited_count, &result)); |
664 | 32.9k | } |
665 | 37.3k | } |
666 | 482 | break; |
667 | 482 | } |
668 | | |
669 | 482 | case IGRAPH_IN: |
670 | 0 | case IGRAPH_OUT: |
671 | 0 | { |
672 | 0 | igraph_vector_int_t degree; |
673 | |
|
674 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(°ree, 0); |
675 | 0 | IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), |
676 | 0 | IGRAPH_REVERSE_MODE(mode), IGRAPH_LOOPS)); |
677 | | |
678 | 0 | for (v = 0; v < vcount; ++v) { |
679 | | /* In an out-tree, roots have in-degree 0, |
680 | | * and all other vertices have in-degree 1. */ |
681 | 0 | if (VECTOR(degree)[v] > 1 || !result) { |
682 | 0 | result = false; |
683 | 0 | break; |
684 | 0 | } |
685 | 0 | if (VECTOR(degree)[v] == 0) { |
686 | 0 | if (roots) { |
687 | 0 | IGRAPH_CHECK(igraph_vector_int_push_back(roots, v)); |
688 | 0 | } |
689 | 0 | IGRAPH_CHECK(igraph_i_is_forest_visitor( |
690 | 0 | graph, v, mode, |
691 | 0 | &visited, &stack, &neis, |
692 | 0 | &visited_count, &result)); |
693 | 0 | } |
694 | 0 | } |
695 | | |
696 | 0 | igraph_vector_int_destroy(°ree); |
697 | 0 | IGRAPH_FINALLY_CLEAN(1); |
698 | 0 | break; |
699 | 0 | } |
700 | | |
701 | 0 | default: |
702 | 0 | IGRAPH_ERROR("Invalid mode.", IGRAPH_EINVMODE); |
703 | 482 | } |
704 | | |
705 | 482 | if (result) { |
706 | | /* In a forest, all vertices are reachable from the roots. */ |
707 | 189 | result = (visited_count == vcount); |
708 | 189 | } |
709 | | |
710 | 482 | if (res) { |
711 | 482 | *res = result; |
712 | 482 | } |
713 | | |
714 | | /* If the graph is not a forest then the root vector will be empty. */ |
715 | 482 | if (!result && roots) { |
716 | 0 | igraph_vector_int_clear(roots); |
717 | 0 | } |
718 | | |
719 | 482 | igraph_vector_int_destroy(&neis); |
720 | 482 | igraph_stack_int_destroy(&stack); |
721 | 482 | igraph_bitset_destroy(&visited); |
722 | 482 | IGRAPH_FINALLY_CLEAN(3); |
723 | | |
724 | 482 | return IGRAPH_SUCCESS; |
725 | 482 | } |
726 | | |
727 | | /** |
728 | | * \ingroup structural |
729 | | * \function igraph_is_acyclic |
730 | | * \brief Checks whether a graph is acyclic or not. |
731 | | * |
732 | | * This function checks whether a graph has any cycles. Edge directions are |
733 | | * considered, i.e. in directed graphs, only directed cycles are searched for. |
734 | | * |
735 | | * </para><para> |
736 | | * The result of this function is cached in the graph itself; calling |
737 | | * the function multiple times with no modifications to the graph in between |
738 | | * will return a cached value in O(1) time. |
739 | | * |
740 | | * \param graph The input graph. |
741 | | * \param res Pointer to a boolean constant, the result |
742 | | is stored here. |
743 | | * \return Error code. |
744 | | * |
745 | | * \sa \ref igraph_find_cycle() to find a cycle that demonstrates |
746 | | * that the graph is not acyclic; \ref igraph_is_forest() to look |
747 | | * for undirected cycles even in directed graphs; \ref igraph_is_dag() |
748 | | * to test specifically for directed acyclic graphs. |
749 | | * |
750 | | * Time complexity: O(|V|+|E|), where |V| and |E| are the number of |
751 | | * vertices and edges in the original input graph. |
752 | | */ |
753 | 612 | igraph_error_t igraph_is_acyclic(const igraph_t *graph, igraph_bool_t *res) { |
754 | 612 | if (igraph_is_directed(graph)) { |
755 | | /* igraph_is_dag is cached */ |
756 | 0 | return igraph_is_dag(graph, res); |
757 | 612 | } else { |
758 | | /* igraph_is_forest is cached if mode == IGRAPH_ALL and we don't need |
759 | | * the roots */ |
760 | | return igraph_is_forest(graph, res, NULL, IGRAPH_ALL); |
761 | 612 | } |
762 | 612 | } |