/src/igraph/src/connectivity/reachability.c
Line | Count | Source |
1 | | /* |
2 | | igraph library. |
3 | | Copyright (C) 2024 The igraph development team <igraph@igraph.org> |
4 | | |
5 | | This program is free software; you can redistribute it and/or modify |
6 | | it under the terms of the GNU General Public License as published by |
7 | | the Free Software Foundation; either version 2 of the License, or |
8 | | (at your option) any later version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, |
11 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
12 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
13 | | GNU General Public License for more details. |
14 | | |
15 | | You should have received a copy of the GNU General Public License |
16 | | along with this program. If not, see <https://www.gnu.org/licenses/>. |
17 | | */ |
18 | | |
19 | | #include "igraph_reachability.h" |
20 | | |
21 | | #include "igraph_adjlist.h" |
22 | | #include "igraph_bitset_list.h" |
23 | | #include "igraph_components.h" |
24 | | #include "igraph_constructors.h" |
25 | | #include "igraph_interface.h" |
26 | | |
27 | | |
28 | | /** |
29 | | * \ingroup structural |
30 | | * \function igraph_reachability |
31 | | * \brief Calculates which vertices are reachable from each vertex in the graph. |
32 | | * |
33 | | * The resulting list will contain one bitset for each strongly connected component. |
34 | | * The bitset for component i will have its j-th bit set, if vertex j is reachable |
35 | | * from some vertex in component i in 0 or more steps. |
36 | | * In particular, a vertex is always reachable from itself. |
37 | | * |
38 | | * \param graph The graph object to analyze. |
39 | | * \param membership Pointer to an integer vector. For every vertex, |
40 | | * the ID of its component is given. The vector will be resized as needed. |
41 | | * This parameter must not be \c NULL. |
42 | | * \param csize Pointer to an integer vector or \c NULL. For every component, it |
43 | | * gives its size (vertex count), the order being defined by the component |
44 | | * IDs. The vector will be resized as needed. |
45 | | * \param no_of_components Pointer to an integer or \c NULL. The number of |
46 | | * components will be stored here. |
47 | | * \param reach A list of bitsets representing the result. It will be resized |
48 | | * as needed. <code>reach[membership[u]][v]</code> is set to \c true if |
49 | | * vertex \c v is reachable from vertex \c u. |
50 | | * \param mode In directed graphs, controls the treatment of edge directions. |
51 | | * Ignored in undirected graphs. With \c IGRAPH_OUT, reachability is computed |
52 | | * by traversing edges along their direction. With \c IGRAPH_IN, edges are |
53 | | * traversed opposite to their direction. With \c IGRAPH_ALL, edge directions |
54 | | * are ignored and the graph is treated as undirected. |
55 | | * \return Error code: |
56 | | * \c IGRAPH_ENOMEM if there is not enough memory |
57 | | * to perform the operation. |
58 | | * |
59 | | * \sa \ref igraph_connected_components() to find the connnected components |
60 | | * of a graph; \ref igraph_count_reachable() to count how many vertices |
61 | | * are reachable from each vertex; \ref igraph_subcomponent() to find |
62 | | * which vertices are rechable from a single vertex. |
63 | | * |
64 | | * Time complexity: O(|C||V|/w + |V| + |E|), where |
65 | | * |C| is the number of strongly connected components (at most |V|), |
66 | | * |V| is the number of vertices, and |
67 | | * |E| is the number of edges respectively, |
68 | | * and w is the bit width of \type igraph_int_t, typically the |
69 | | * word size of the machine (32 or 64). |
70 | | */ |
71 | | |
72 | | igraph_error_t igraph_reachability( |
73 | | const igraph_t *graph, |
74 | | igraph_vector_int_t *membership, |
75 | | igraph_vector_int_t *csize, |
76 | | igraph_int_t *no_of_components, |
77 | | igraph_bitset_list_t *reach, |
78 | 3.49k | igraph_neimode_t mode) { |
79 | | |
80 | 3.49k | const igraph_int_t no_of_nodes = igraph_vcount(graph); |
81 | 3.49k | igraph_int_t no_of_comps; |
82 | 3.49k | igraph_adjlist_t adjlist, dag; |
83 | | |
84 | 3.49k | if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) { |
85 | 0 | IGRAPH_ERROR("Invalid mode for reachability.", IGRAPH_EINVMODE); |
86 | 0 | } |
87 | | |
88 | 3.49k | if (! igraph_is_directed(graph)) { |
89 | 0 | mode = IGRAPH_ALL; |
90 | 0 | } |
91 | | |
92 | 3.49k | IGRAPH_CHECK(igraph_connected_components(graph, |
93 | 3.49k | membership, csize, &no_of_comps, |
94 | 3.49k | mode == IGRAPH_ALL ? IGRAPH_WEAK : IGRAPH_STRONG)); |
95 | | |
96 | 3.49k | if (no_of_components) { |
97 | 0 | *no_of_components = no_of_comps; |
98 | 0 | } |
99 | | |
100 | 3.49k | IGRAPH_CHECK(igraph_bitset_list_resize(reach, no_of_comps)); |
101 | | |
102 | 680k | for (igraph_int_t comp = 0; comp < no_of_comps; comp++) { |
103 | 676k | IGRAPH_CHECK(igraph_bitset_resize(igraph_bitset_list_get_ptr(reach, comp), no_of_nodes)); |
104 | 676k | } |
105 | 706k | for (igraph_int_t v = 0; v < no_of_nodes; v++) { |
106 | 702k | IGRAPH_BIT_SET(*igraph_bitset_list_get_ptr(reach, VECTOR(*membership)[v]), v); |
107 | 702k | } |
108 | | |
109 | 3.49k | if (mode == IGRAPH_ALL) { |
110 | 0 | return IGRAPH_SUCCESS; |
111 | 0 | } |
112 | | |
113 | 3.49k | IGRAPH_CHECK(igraph_adjlist_init(graph, &adjlist, mode, IGRAPH_LOOPS_ONCE, IGRAPH_MULTIPLE)); |
114 | 3.49k | IGRAPH_FINALLY(igraph_adjlist_destroy, &adjlist); |
115 | | |
116 | 3.49k | IGRAPH_CHECK(igraph_adjlist_init_empty(&dag, no_of_comps)); |
117 | 3.49k | IGRAPH_FINALLY(igraph_adjlist_destroy, &dag); |
118 | | |
119 | 706k | for (igraph_int_t v = 0; v < no_of_nodes; v++) { |
120 | 702k | const igraph_vector_int_t *neighbours = igraph_adjlist_get(&adjlist, v); |
121 | 702k | igraph_vector_int_t *dag_neighbours = igraph_adjlist_get(&dag, VECTOR(*membership)[v]); |
122 | 702k | const igraph_int_t n = igraph_vector_int_size(neighbours); |
123 | 827k | for (igraph_int_t i = 0; i < n; i++) { |
124 | 124k | igraph_int_t w = VECTOR(*neighbours)[i]; |
125 | 124k | if (VECTOR(*membership)[v] != VECTOR(*membership)[w]) { |
126 | 56.8k | IGRAPH_CHECK(igraph_vector_int_push_back(dag_neighbours, VECTOR(*membership)[w])); |
127 | 56.8k | } |
128 | 124k | } |
129 | 702k | } |
130 | | |
131 | | /* Iterate through strongly connected components in reverser topological order, |
132 | | * exploiting the fact that they are indexed in topological order. */ |
133 | 680k | for (igraph_int_t i = 0; i < no_of_comps; i++) { |
134 | 676k | const igraph_int_t comp = mode == IGRAPH_IN ? i : no_of_comps - i - 1; |
135 | 676k | const igraph_vector_int_t *dag_neighbours = igraph_adjlist_get(&dag, comp); |
136 | 676k | igraph_bitset_t *from_bitset = igraph_bitset_list_get_ptr(reach, comp); |
137 | 676k | const igraph_int_t n = igraph_vector_int_size(dag_neighbours); |
138 | 733k | for (igraph_int_t j = 0; j < n; j++) { |
139 | 56.8k | const igraph_bitset_t *to_bitset = igraph_bitset_list_get_ptr(reach, VECTOR(*dag_neighbours)[j]); |
140 | 56.8k | igraph_bitset_or(from_bitset, from_bitset, to_bitset); |
141 | 56.8k | } |
142 | 676k | } |
143 | | |
144 | 3.49k | igraph_adjlist_destroy(&adjlist); |
145 | 3.49k | igraph_adjlist_destroy(&dag); |
146 | 3.49k | IGRAPH_FINALLY_CLEAN(2); |
147 | | |
148 | 3.49k | return IGRAPH_SUCCESS; |
149 | 3.49k | } |
150 | | |
151 | | |
152 | | /** |
153 | | * \ingroup structural |
154 | | * \function igraph_count_reachable |
155 | | * \brief The number of vertices reachable from each vertex in the graph. |
156 | | * |
157 | | * \param graph The graph object to analyze. |
158 | | * \param counts Integer vector. <code>counts[v]</code> will store the number |
159 | | * of vertices reachable from vertex \c v, including \c v itself. |
160 | | * \param mode In directed graphs, controls the treatment of edge directions. |
161 | | * Ignored in undirected graphs. With \c IGRAPH_OUT, reachability is computed |
162 | | * by traversing edges along their direction. With \c IGRAPH_IN, edges are |
163 | | * traversed opposite to their direction. With \c IGRAPH_ALL, edge directions |
164 | | * are ignored and the graph is treated as undirected. |
165 | | * \return Error code: |
166 | | * \c IGRAPH_ENOMEM if there is not enough memory |
167 | | * to perform the operation. |
168 | | * |
169 | | * \sa \ref igraph_connected_components(), \ref igraph_transitive_closure() |
170 | | * |
171 | | * Time complexity: O(|C||V|/w + |V| + |E|), where |
172 | | * |C| is the number of strongly connected components (at most |V|), |
173 | | * |V| is the number of vertices, and |
174 | | * |E| is the number of edges respectively, |
175 | | * and w is the bit width of \type igraph_int_t, typically the |
176 | | * word size of the machine (32 or 64). |
177 | | */ |
178 | | |
179 | | igraph_error_t igraph_count_reachable(const igraph_t *graph, |
180 | | igraph_vector_int_t *counts, |
181 | 1.74k | igraph_neimode_t mode) { |
182 | | |
183 | 1.74k | igraph_vector_int_t membership; |
184 | 1.74k | igraph_int_t no_of_nodes = igraph_vcount(graph); |
185 | 1.74k | igraph_bitset_list_t reach; |
186 | | |
187 | 1.74k | IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); |
188 | 1.74k | IGRAPH_BITSET_LIST_INIT_FINALLY(&reach, 0); |
189 | | |
190 | 1.74k | IGRAPH_CHECK(igraph_reachability(graph, &membership, NULL, NULL, &reach, mode)); |
191 | | |
192 | 1.74k | IGRAPH_CHECK(igraph_vector_int_resize(counts, igraph_vcount(graph))); |
193 | 353k | for (igraph_int_t i = 0; i < no_of_nodes; i++) { |
194 | 351k | VECTOR(*counts)[i] = igraph_bitset_popcount(igraph_bitset_list_get_ptr(&reach, VECTOR(membership)[i])); |
195 | 351k | } |
196 | | |
197 | 1.74k | igraph_bitset_list_destroy(&reach); |
198 | 1.74k | igraph_vector_int_destroy(&membership); |
199 | 1.74k | IGRAPH_FINALLY_CLEAN(2); |
200 | | |
201 | 1.74k | return IGRAPH_SUCCESS; |
202 | 1.74k | } |
203 | | |
204 | | |
205 | | /** |
206 | | * \ingroup structural |
207 | | * \function igraph_transitive_closure |
208 | | * \brief Computes the transitive closure of a graph. |
209 | | * |
210 | | * The resulting graph will have an edge from vertex \c i to vertex \c j |
211 | | * if \c j is reachable from \c i. |
212 | | * |
213 | | * \param graph The graph object to analyze. |
214 | | * \param closure The resulting graph representing the transitive closure. |
215 | | * \return Error code: |
216 | | * \c IGRAPH_ENOMEM if there is not enough memory |
217 | | * to perform the operation. |
218 | | * |
219 | | * \sa \ref igraph_connected_components(), \ref igraph_count_reachable() |
220 | | * |
221 | | * Time complexity: O(|V|^2 + |E|), where |
222 | | * |V| is the number of vertices, and |
223 | | * |E| is the number of edges, respectively. |
224 | | */ |
225 | 1.74k | igraph_error_t igraph_transitive_closure(const igraph_t *graph, igraph_t *closure) { |
226 | 1.74k | const igraph_int_t no_of_nodes = igraph_vcount(graph); |
227 | 1.74k | const igraph_bool_t directed = igraph_is_directed(graph); |
228 | 1.74k | igraph_vector_int_t membership, edges; |
229 | 1.74k | igraph_bitset_list_t reach; |
230 | | |
231 | 1.74k | IGRAPH_VECTOR_INT_INIT_FINALLY(&membership, 0); |
232 | 1.74k | IGRAPH_BITSET_LIST_INIT_FINALLY(&reach, 0); |
233 | | |
234 | 1.74k | IGRAPH_CHECK(igraph_reachability(graph, &membership, NULL, NULL, &reach, IGRAPH_OUT)); |
235 | | |
236 | 1.74k | IGRAPH_VECTOR_INT_INIT_FINALLY(&edges, 0); |
237 | 353k | for (igraph_int_t u = 0; u < no_of_nodes; u++) { |
238 | 351k | const igraph_bitset_t *row = igraph_bitset_list_get_ptr(&reach, VECTOR(membership)[u]); |
239 | 84.1M | for (igraph_int_t v = directed ? 0 : u + 1; v < no_of_nodes; v++) { |
240 | 83.7M | if (u != v && IGRAPH_BIT_TEST(*row, v)) { |
241 | 1.07M | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, u)); |
242 | 1.07M | IGRAPH_CHECK(igraph_vector_int_push_back(&edges, v)); |
243 | 1.07M | } |
244 | 83.7M | } |
245 | 351k | } |
246 | | |
247 | 1.74k | igraph_bitset_list_destroy(&reach); |
248 | 1.74k | igraph_vector_int_destroy(&membership); |
249 | 1.74k | IGRAPH_FINALLY_CLEAN(2); |
250 | | |
251 | 1.74k | IGRAPH_CHECK(igraph_create(closure, &edges, no_of_nodes, directed)); |
252 | | |
253 | 1.74k | igraph_vector_int_destroy(&edges); |
254 | 1.74k | IGRAPH_FINALLY_CLEAN(1); |
255 | | |
256 | 1.74k | return IGRAPH_SUCCESS; |
257 | 1.74k | } |