/src/igraph/src/properties/complete.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 it under |
6 | | the terms of the GNU General Public License as published by the Free Software |
7 | | Foundation; either version 2 of the License, or (at your option) any later |
8 | | version. |
9 | | |
10 | | This program is distributed in the hope that it will be useful, but WITHOUT |
11 | | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
12 | | FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. |
13 | | |
14 | | You should have received a copy of the GNU General Public License along with |
15 | | this program. If not, see <https://www.gnu.org/licenses/>. |
16 | | */ |
17 | | |
18 | | |
19 | | #include "igraph_interface.h" |
20 | | #include "igraph_structural.h" |
21 | | |
22 | | #include "core/interruption.h" |
23 | | |
24 | | /** |
25 | | * \ingroup structural |
26 | | * \function igraph_is_complete |
27 | | * \brief Decides whether the graph is complete. |
28 | | * |
29 | | * A graph is considered complete if all pairs of different vertices are |
30 | | * adjacent. |
31 | | * |
32 | | * </para><para> |
33 | | * The null graph and the singleton graph are considered complete. |
34 | | * |
35 | | * \param graph The graph object to analyze. |
36 | | * \param res Pointer to a Boolean variable, the result will be stored here. |
37 | | * |
38 | | * \return Error code. |
39 | | * |
40 | | * Time complexity: O(|V| + |E|) at worst. |
41 | | */ |
42 | | |
43 | 12.4k | igraph_error_t igraph_is_complete(const igraph_t *graph, igraph_bool_t *res) { |
44 | | |
45 | 12.4k | const igraph_int_t vcount = igraph_vcount(graph); |
46 | 12.4k | const igraph_int_t ecount = igraph_ecount(graph); |
47 | 12.4k | igraph_int_t complete_ecount; |
48 | 12.4k | igraph_bool_t simple, directed = igraph_is_directed(graph); |
49 | 12.4k | igraph_vector_int_t neighbours; |
50 | 12.4k | int iter = 0; |
51 | | |
52 | | /* If the graph is the null graph or the singleton graph, return early */ |
53 | 12.4k | if (vcount == 0 || vcount == 1) { |
54 | 24 | *res = true; |
55 | 24 | return IGRAPH_SUCCESS; |
56 | 24 | } |
57 | | |
58 | | /* Compute the amount of edges a complete graph of vcount vertices would |
59 | | have */ |
60 | | |
61 | | /* Depends on whether the graph is directed */ |
62 | | |
63 | | /* We have to take care of integer overflowing */ |
64 | | |
65 | | #if IGRAPH_INTEGER_SIZE == 32 |
66 | | if (directed) { |
67 | | /* Highest x s.t. x² - x < 2^31 - 1 */ |
68 | | if (vcount > 46341) { |
69 | | *res = false; |
70 | | return IGRAPH_SUCCESS; |
71 | | } else { |
72 | | complete_ecount = vcount * (vcount - 1); |
73 | | } |
74 | | } else { |
75 | | /* Highest x s.t. (x² - x) / 2 < 2^31 - 1 */ |
76 | | if (vcount > 65536) { |
77 | | *res = false; |
78 | | return IGRAPH_SUCCESS; |
79 | | } else { |
80 | | complete_ecount = vcount % 2 == 0 ? |
81 | | (vcount / 2) * (vcount - 1) : |
82 | | vcount * ((vcount - 1) / 2); |
83 | | } |
84 | | } |
85 | | #elif IGRAPH_INTEGER_SIZE == 64 |
86 | 12.4k | if (directed) { |
87 | | /* Highest x s.t. x² - x < 2^63 - 1 */ |
88 | 0 | if (vcount > 3037000500) { |
89 | 0 | *res = false; |
90 | 0 | return IGRAPH_SUCCESS; |
91 | 0 | } else { |
92 | 0 | complete_ecount = vcount * (vcount - 1); |
93 | 0 | } |
94 | 12.4k | } else { |
95 | | /* Highest x s.t. (x² - x) / 2 < 2^63 - 1 */ |
96 | 12.4k | if (vcount > 4294967296) { |
97 | 0 | *res = false; |
98 | 0 | return IGRAPH_SUCCESS; |
99 | 12.4k | } else { |
100 | 12.4k | complete_ecount = vcount % 2 == 0 ? |
101 | 6.11k | (vcount / 2) * (vcount - 1) : |
102 | 12.4k | vcount * ((vcount - 1) / 2); |
103 | 12.4k | } |
104 | 12.4k | } |
105 | | #else |
106 | | /* If values other than 32 or 64 become allowed, |
107 | | * this code will need to be updated. */ |
108 | | # error "Unexpected IGRAPH_INTEGER_SIZE value." |
109 | | #endif |
110 | | |
111 | | /* If the amount of edges is strictly lower than what it should be for a |
112 | | complete graph, return early */ |
113 | | |
114 | 12.4k | if (ecount < complete_ecount) { |
115 | 10.4k | *res = false; |
116 | 10.4k | return IGRAPH_SUCCESS; |
117 | 10.4k | } |
118 | | |
119 | | /* If the graph is simple, compare and conclude */ |
120 | 1.95k | IGRAPH_CHECK(igraph_is_simple(graph, &simple, IGRAPH_DIRECTED)); |
121 | | |
122 | 1.95k | if (simple) { |
123 | 1.85k | *res = (ecount == complete_ecount); |
124 | 1.85k | return IGRAPH_SUCCESS; |
125 | 1.85k | } |
126 | | |
127 | | /* Allocate memory for vector of size v */ |
128 | 92 | IGRAPH_VECTOR_INT_INIT_FINALLY(&neighbours, vcount); |
129 | | |
130 | 205 | for (igraph_int_t i = 0; i < vcount; ++i) { |
131 | 183 | IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); |
132 | | |
133 | 183 | IGRAPH_CHECK(igraph_neighbors( |
134 | 183 | graph, &neighbours, i, IGRAPH_OUT, IGRAPH_NO_LOOPS, |
135 | 183 | IGRAPH_NO_MULTIPLE |
136 | 183 | )); |
137 | | |
138 | 183 | if ((igraph_vector_int_size(&neighbours) < vcount - 1)) { |
139 | 70 | *res = false; |
140 | 70 | goto cleanup; |
141 | 70 | } |
142 | 183 | } |
143 | | |
144 | | /* If we arrive here, we have found no neighbour vector of size strictly |
145 | | less than vcount - 1. The graph is therefore complete */ |
146 | | |
147 | 22 | *res = true; |
148 | | |
149 | 92 | cleanup: |
150 | | |
151 | 92 | igraph_vector_int_destroy(&neighbours); |
152 | 92 | IGRAPH_FINALLY_CLEAN(1); |
153 | | |
154 | 92 | return IGRAPH_SUCCESS; |
155 | 22 | } |
156 | | |
157 | | |
158 | | /* Test for cliques or independent sets, depending on whether independent_set == true. */ |
159 | | static igraph_error_t is_clique(const igraph_t *graph, igraph_vs_t candidate, |
160 | | igraph_bool_t directed, igraph_bool_t *res, |
161 | 0 | igraph_bool_t independent_set) { |
162 | 0 | igraph_vector_int_t vids; |
163 | 0 | igraph_int_t n; /* clique size */ |
164 | 0 | igraph_bool_t result = true; /* be optimistic */ |
165 | 0 | int iter = 0; |
166 | | |
167 | | /* The following implementation is optimized for testing for small cliques |
168 | | * in large graphs. */ |
169 | |
|
170 | 0 | IGRAPH_VECTOR_INT_INIT_FINALLY(&vids, 0); |
171 | 0 | IGRAPH_CHECK(igraph_vs_as_vector(graph, candidate, &vids)); |
172 | | |
173 | 0 | n = igraph_vector_int_size(&vids); |
174 | |
|
175 | 0 | for (igraph_int_t i = 0; i < n; i++) { |
176 | 0 | igraph_int_t u = VECTOR(vids)[i]; |
177 | 0 | for (igraph_int_t j = directed ? 0 : i+1; j < n; j++) { |
178 | 0 | igraph_int_t v = VECTOR(vids)[j]; |
179 | | /* Compare u and v for equality instead of i and j in case |
180 | | * the vertex list contained duplicates. */ |
181 | 0 | if (u != v) { |
182 | 0 | igraph_int_t eid; |
183 | 0 | IGRAPH_CHECK(igraph_get_eid(graph, &eid, u, v, directed, false)); |
184 | 0 | if (independent_set) { |
185 | 0 | if (eid != -1) { |
186 | 0 | result = false; |
187 | 0 | goto done; |
188 | 0 | } |
189 | 0 | } else { |
190 | 0 | if (eid == -1) { |
191 | 0 | result = false; |
192 | 0 | goto done; |
193 | 0 | } |
194 | 0 | } |
195 | 0 | } |
196 | 0 | } |
197 | 0 | IGRAPH_ALLOW_INTERRUPTION_LIMITED(iter, 1 << 8); |
198 | 0 | } |
199 | | |
200 | 0 | done: |
201 | |
|
202 | 0 | *res = result; |
203 | |
|
204 | 0 | igraph_vector_int_destroy(&vids); |
205 | 0 | IGRAPH_FINALLY_CLEAN(1); |
206 | |
|
207 | 0 | return IGRAPH_SUCCESS; |
208 | 0 | } |
209 | | |
210 | | /** |
211 | | * \ingroup structural |
212 | | * \function igraph_is_clique |
213 | | * \brief Does a set of vertices form a clique? |
214 | | * |
215 | | * Tests if all pairs within a set of vertices are adjacent, i.e. whether they |
216 | | * form a clique. An empty set and singleton set are considered to be a clique. |
217 | | * |
218 | | * \param graph The input graph. |
219 | | * \param candidate The vertex set to test for being a clique. |
220 | | * \param directed Whether to take edge directions into account in directed graphs. |
221 | | * \param res The result will be stored here. |
222 | | * \return Error code. |
223 | | * |
224 | | * \sa \ref igraph_is_complete() to test if a graph is complete; |
225 | | * \ref igraph_is_independent_vertex_set() to test for independent vertex sets; |
226 | | * \ref igraph_cliques(), \ref igraph_maximal_cliques() and |
227 | | * \ref igraph_largest_cliques() to find cliques. |
228 | | * |
229 | | * Time complexity: O(n^2 log(d)) where n is the number of vertices in the |
230 | | * candidate set and d is the typical vertex degree. |
231 | | */ |
232 | | igraph_error_t igraph_is_clique(const igraph_t *graph, igraph_vs_t candidate, |
233 | 0 | igraph_bool_t directed, igraph_bool_t *res) { |
234 | |
|
235 | 0 | if (! igraph_is_directed(graph)) { |
236 | 0 | directed = false; |
237 | 0 | } |
238 | |
|
239 | 0 | if (igraph_is_directed(graph) == directed && igraph_vs_is_all(&candidate)) { |
240 | 0 | return igraph_is_complete(graph, res); |
241 | 0 | } |
242 | | |
243 | 0 | return is_clique(graph, candidate, directed, res, /* independent_set */ false); |
244 | 0 | } |
245 | | |
246 | | /** |
247 | | * \ingroup structural |
248 | | * \function igraph_is_independent_vertex_set |
249 | | * \brief Does a set of vertices form an independent set? |
250 | | * |
251 | | * Tests if no pairs within a set of vertices are adjacenct, i.e. whether they |
252 | | * form an independent set. An empty set and singleton set are both considered |
253 | | * to be an independent set. |
254 | | * |
255 | | * \param graph The input graph. |
256 | | * \param candidate The vertex set to test for being an independent set. |
257 | | * \param res The result will be stored here. |
258 | | * \return Error code. |
259 | | * |
260 | | * \sa \ref igraph_is_clique() to test for cliques; \ref igraph_independent_vertex_sets(), |
261 | | * \ref igraph_maximal_independent_vertex_sets() and |
262 | | * \ref igraph_largest_independent_vertex_sets() to find independent vertex sets. |
263 | | * |
264 | | * Time complexity: O(n^2 log(d)) where n is the number of vertices in the |
265 | | * candidate set and d is the typical vertex degree. |
266 | | */ |
267 | | igraph_error_t igraph_is_independent_vertex_set(const igraph_t *graph, igraph_vs_t candidate, |
268 | 0 | igraph_bool_t *res) { |
269 | | |
270 | | /* Note: igraph_count_loops() already makes use of the cache. */ |
271 | 0 | if (igraph_vs_is_all(&candidate)) { |
272 | 0 | igraph_int_t loop_count; |
273 | 0 | igraph_count_loops(graph, &loop_count); |
274 | 0 | *res = (igraph_ecount(graph) - loop_count) == 0; |
275 | 0 | return IGRAPH_SUCCESS; |
276 | 0 | } |
277 | | |
278 | 0 | return is_clique(graph, candidate, /* directed */ false, res, /* independent_set */ true); |
279 | 0 | } |