Coverage Report

Created: 2025-10-28 07:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}