1 /* -*- mode: C -*-  */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /*
4    IGraph library.
5    Copyright (C) 2005-2021 The igraph development team
6 
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11 
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16 
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20    02110-1301 USA
21 
22 */
23 
24 #include "igraph_structural.h"
25 
26 #include "igraph_adjlist.h"
27 #include "igraph_constructors.h"
28 #include "igraph_dqueue.h"
29 #include "igraph_interface.h"
30 #include "igraph_stack.h"
31 
32 /**
33  * \function igraph_unfold_tree
34  * Unfolding a graph into a tree, by possibly multiplicating its vertices.
35  *
36  * A graph is converted into a tree (or forest, if it is unconnected),
37  * by performing a breadth-first search on it, and replicating
38  * vertices that were found a second, third, etc. time.
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  */
igraph_unfold_tree(const igraph_t * graph,igraph_t * tree,igraph_neimode_t mode,const igraph_vector_t * roots,igraph_vector_t * vertex_index)57 int igraph_unfold_tree(const igraph_t *graph, igraph_t *tree,
58                        igraph_neimode_t mode, const igraph_vector_t *roots,
59                        igraph_vector_t *vertex_index) {
60 
61     long int no_of_nodes = igraph_vcount(graph);
62     long int no_of_edges = igraph_ecount(graph);
63     long int no_of_roots = igraph_vector_size(roots);
64     long int tree_vertex_count = no_of_nodes;
65 
66     igraph_vector_t edges;
67     igraph_vector_bool_t seen_vertices;
68     igraph_vector_bool_t seen_edges;
69 
70     igraph_dqueue_t Q;
71     igraph_vector_t neis;
72 
73     long int i, n, r, v_ptr = no_of_nodes;
74 
75     /* TODO: handle not-connected graphs, multiple root vertices */
76 
77     IGRAPH_VECTOR_INIT_FINALLY(&edges, 0);
78     igraph_vector_reserve(&edges, no_of_edges * 2);
79     IGRAPH_DQUEUE_INIT_FINALLY(&Q, 100);
80     IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
81     IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen_vertices, no_of_nodes);
82     IGRAPH_VECTOR_BOOL_INIT_FINALLY(&seen_edges, no_of_edges);
83 
84     if (vertex_index) {
85         IGRAPH_CHECK(igraph_vector_resize(vertex_index, no_of_nodes));
86         for (i = 0; i < no_of_nodes; i++) {
87             VECTOR(*vertex_index)[i] = i;
88         }
89     }
90 
91     for (r = 0; r < no_of_roots; r++) {
92 
93         long int root = (long int) VECTOR(*roots)[r];
94         VECTOR(seen_vertices)[root] = 1;
95         igraph_dqueue_push(&Q, root);
96 
97         while (!igraph_dqueue_empty(&Q)) {
98             long int actnode = (long int) igraph_dqueue_pop(&Q);
99 
100             IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) actnode, mode));
101             n = igraph_vector_size(&neis);
102             for (i = 0; i < n; i++) {
103 
104                 long int edge = (long int) VECTOR(neis)[i];
105                 long int from = IGRAPH_FROM(graph, edge);
106                 long int to = IGRAPH_TO(graph, edge);
107                 long int nei = IGRAPH_OTHER(graph, edge, actnode);
108 
109                 if (! VECTOR(seen_edges)[edge]) {
110 
111                     VECTOR(seen_edges)[edge] = 1;
112 
113                     if (! VECTOR(seen_vertices)[nei]) {
114 
115                         igraph_vector_push_back(&edges, from);
116                         igraph_vector_push_back(&edges, to);
117 
118                         VECTOR(seen_vertices)[nei] = 1;
119                         IGRAPH_CHECK(igraph_dqueue_push(&Q, nei));
120 
121                     } else {
122 
123                         tree_vertex_count++;
124                         if (vertex_index) {
125                             IGRAPH_CHECK(igraph_vector_push_back(vertex_index, nei));
126                         }
127 
128                         if (from == nei) {
129                             igraph_vector_push_back(&edges, v_ptr++);
130                             igraph_vector_push_back(&edges, to);
131                         } else {
132                             igraph_vector_push_back(&edges, from);
133                             igraph_vector_push_back(&edges, v_ptr++);
134                         }
135                     }
136                 }
137 
138             } /* for i<n */
139 
140         } /* ! igraph_dqueue_empty(&Q) */
141 
142     } /* r < igraph_vector_size(roots) */
143 
144     igraph_vector_bool_destroy(&seen_edges);
145     igraph_vector_bool_destroy(&seen_vertices);
146     igraph_vector_destroy(&neis);
147     igraph_dqueue_destroy(&Q);
148     IGRAPH_FINALLY_CLEAN(4);
149 
150     IGRAPH_CHECK(igraph_create(tree, &edges, tree_vertex_count, igraph_is_directed(graph)));
151     igraph_vector_destroy(&edges);
152     IGRAPH_FINALLY_CLEAN(1);
153 
154     return 0;
155 }
156 
157 /* igraph_is_tree -- check if a graph is a tree */
158 
159 /* count the number of vertices reachable from the root */
igraph_i_is_tree_visitor(igraph_integer_t root,const igraph_adjlist_t * al,igraph_integer_t * visited_count)160 static int igraph_i_is_tree_visitor(igraph_integer_t root, const igraph_adjlist_t *al, igraph_integer_t *visited_count) {
161     igraph_stack_int_t stack;
162     igraph_vector_bool_t visited;
163     long i;
164 
165     IGRAPH_CHECK(igraph_vector_bool_init(&visited, igraph_adjlist_size(al)));
166     IGRAPH_FINALLY(igraph_vector_bool_destroy, &visited);
167 
168     IGRAPH_CHECK(igraph_stack_int_init(&stack, 0));
169     IGRAPH_FINALLY(igraph_stack_int_destroy, &stack);
170 
171     *visited_count = 0;
172 
173     /* push the root into the stack */
174     IGRAPH_CHECK(igraph_stack_int_push(&stack, root));
175 
176     while (! igraph_stack_int_empty(&stack)) {
177         igraph_integer_t u;
178         igraph_vector_int_t *neighbors;
179         long ncount;
180 
181         /* take a vertex from the stack, mark it as visited */
182         u = igraph_stack_int_pop(&stack);
183         if (IGRAPH_LIKELY(! VECTOR(visited)[u])) {
184             VECTOR(visited)[u] = 1;
185             *visited_count += 1;
186         }
187 
188         /* register all its yet-unvisited neighbours for future processing */
189         neighbors = igraph_adjlist_get(al, u);
190         ncount = igraph_vector_int_size(neighbors);
191         for (i = 0; i < ncount; ++i) {
192             igraph_integer_t v = VECTOR(*neighbors)[i];
193             if (! VECTOR(visited)[v]) {
194                 IGRAPH_CHECK(igraph_stack_int_push(&stack, v));
195             }
196         }
197     }
198 
199     igraph_stack_int_destroy(&stack);
200     igraph_vector_bool_destroy(&visited);
201     IGRAPH_FINALLY_CLEAN(2);
202 
203     return IGRAPH_SUCCESS;
204 }
205 
206 
207 /**
208  * \ingroup structural
209  * \function igraph_is_tree
210  * \brief Decides whether the graph is a tree.
211  *
212  * An undirected graph is a tree if it is connected and has no cycles.
213  * </para><para>
214  *
215  * In the directed case, a possible additional requirement is that all
216  * edges are oriented away from a root (out-tree or arborescence) or all edges
217  * are oriented towards a root (in-tree or anti-arborescence).
218  * This test can be controlled using the \p mode parameter.
219  * </para><para>
220  *
221  * By convention, the null graph (i.e. the graph with no vertices) is considered not to be a tree.
222  *
223  * \param graph The graph object to analyze.
224  * \param res Pointer to a logical variable, the result will be stored
225  *        here.
226  * \param root If not \c NULL, the root node will be stored here. When \p mode
227  *        is \c IGRAPH_ALL or the graph is undirected, any vertex can be the root
228  *        and \p root is set to 0 (the first vertex). When \p mode is \c IGRAPH_OUT
229  *        or \c IGRAPH_IN, the root is set to the vertex with zero in- or out-degree,
230  *        respectively.
231  * \param mode For a directed graph this specifies whether to test for an
232  *        out-tree, an in-tree or ignore edge directions. The respective
233  *        possible values are:
234  *        \c IGRAPH_OUT, \c IGRAPH_IN, \c IGRAPH_ALL. This argument is
235  *        ignored for undirected graphs.
236  * \return Error code:
237  *        \c IGRAPH_EINVAL: invalid mode argument.
238  *
239  * Time complexity: At most O(|V|+|E|), the
240  * number of vertices plus the number of edges in the graph.
241  *
242  * \sa igraph_is_weakly_connected()
243  *
244  * \example examples/simple/igraph_tree.c
245  */
igraph_is_tree(const igraph_t * graph,igraph_bool_t * res,igraph_integer_t * root,igraph_neimode_t mode)246 int igraph_is_tree(const igraph_t *graph, igraph_bool_t *res, igraph_integer_t *root, igraph_neimode_t mode) {
247     igraph_adjlist_t al;
248     igraph_integer_t iroot = 0;
249     igraph_integer_t visited_count;
250     igraph_integer_t vcount, ecount;
251 
252     vcount = igraph_vcount(graph);
253     ecount = igraph_ecount(graph);
254 
255     /* A tree must have precisely vcount-1 edges. */
256     /* By convention, the zero-vertex graph will not be considered a tree. */
257     if (ecount != vcount - 1) {
258         *res = 0;
259         return IGRAPH_SUCCESS;
260     }
261 
262     /* The single-vertex graph is a tree, provided it has no edges (checked in the previous if (..)) */
263     if (vcount == 1) {
264         *res = 1;
265         if (root) {
266             *root = 0;
267         }
268         return IGRAPH_SUCCESS;
269     }
270 
271     /* For higher vertex counts we cannot short-circuit due to the possibility
272      * of loops or multi-edges even when the edge count is correct. */
273 
274     /* Ignore mode for undirected graphs. */
275     if (! igraph_is_directed(graph)) {
276         mode = IGRAPH_ALL;
277     }
278 
279     IGRAPH_CHECK(igraph_adjlist_init(graph, &al, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE));
280     IGRAPH_FINALLY(igraph_adjlist_destroy, &al);
281 
282     /* The main algorithm:
283      * We find a root and check that all other vertices are reachable from it.
284      * We have already checked the number of edges, so with the additional
285      * reachability condition we can verify if the graph is a tree.
286      *
287      * For directed graphs, the root is the node with no incoming/outgoing
288      * connections, depending on 'mode'. For undirected, it is arbitrary, so
289      * we choose 0.
290      */
291 
292     *res = 1; /* assume success */
293 
294     switch (mode) {
295     case IGRAPH_ALL:
296         iroot = 0;
297         break;
298 
299     case IGRAPH_IN:
300     case IGRAPH_OUT: {
301         igraph_vector_t degree;
302         igraph_integer_t i;
303 
304         IGRAPH_CHECK(igraph_vector_init(&degree, 0));
305         IGRAPH_FINALLY(igraph_vector_destroy, &degree);
306 
307         IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), mode == IGRAPH_IN ? IGRAPH_OUT : IGRAPH_IN, /* loops = */ 1));
308 
309         for (i = 0; i < vcount; ++i)
310             if (VECTOR(degree)[i] == 0) {
311                 break;
312             }
313 
314         /* if no suitable root is found, the graph is not a tree */
315         if (i == vcount) {
316             *res = 0;
317         } else {
318             iroot = i;
319         }
320 
321         igraph_vector_destroy(&degree);
322         IGRAPH_FINALLY_CLEAN(1);
323     }
324 
325     break;
326     default:
327         IGRAPH_ERROR("Invalid mode", IGRAPH_EINVMODE);
328     }
329 
330     /* if no suitable root was found, skip visiting vertices */
331     if (*res) {
332         IGRAPH_CHECK(igraph_i_is_tree_visitor(iroot, &al, &visited_count));
333         *res = visited_count == vcount;
334     }
335 
336     if (root) {
337         *root = iroot;
338     }
339 
340     igraph_adjlist_destroy(&al);
341     IGRAPH_FINALLY_CLEAN(1);
342 
343     return IGRAPH_SUCCESS;
344 }
345