Coverage Report

Created: 2026-01-13 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/properties/girth.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_adjlist.h"
25
#include "igraph_components.h"
26
#include "igraph_dqueue.h"
27
#include "igraph_interface.h"
28
29
#include "core/interruption.h"
30
31
/**
32
 * \function igraph_girth
33
 * \brief The girth of a graph is the length of the shortest cycle in it.
34
 *
35
 * The current implementation works for undirected graphs only,
36
 * directed graphs are treated as undirected graphs. Self-loops and
37
 * multiple edges are ignored, i.e. cycles of length 1 or 2 are
38
 * not considered.
39
 *
40
 * </para><para>
41
 * For graphs that contain no cycles, and only for such graphs,
42
 * infinity is returned.
43
 *
44
 * </para><para>
45
 * The first implementation of this function was done by Keith Briggs,
46
 * thanks Keith.
47
 *
48
 * </para><para>
49
 * Reference:
50
 *
51
 * </para><para>
52
 * Alon Itai and Michael Rodeh:
53
 * Finding a minimum circuit in a graph
54
 * \emb Proceedings of the ninth annual ACM symposium on Theory of
55
 * computing \eme, 1-10, 1977.
56
 * https://doi.org/10.1145/800105.803390
57
 *
58
 * \param graph The input graph. Edge directions will be ignored.
59
 * \param girth Pointer to an \c igraph_real_t, if not \c NULL then the result
60
 *     will be stored here.
61
 * \param cycle Pointer to an initialized vector, the vertex IDs in
62
 *     the shortest cycle of length at least 3 will be stored here.
63
 *     If \c NULL then it is ignored.
64
 * \return Error code.
65
 *
66
 * Time complexity: O((|V|+|E|)^2), |V| is the number of vertices, |E|
67
 * is the number of edges in the general case. If the graph has no
68
 * cycles at all then the function needs O(|V|+|E|) time to realize
69
 * this and then it stops.
70
 *
71
 * \example examples/simple/igraph_girth.c
72
 */
73
igraph_error_t igraph_girth(const igraph_t *graph,
74
                            igraph_real_t *girth,
75
1.88k
                            igraph_vector_int_t *cycle) {
76
77
1.88k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
78
1.88k
    igraph_dqueue_int_t q;
79
1.88k
    igraph_lazy_adjlist_t adjlist;
80
1.88k
    igraph_int_t mincirc = IGRAPH_INTEGER_MAX, minvertex = 0;
81
1.88k
    igraph_int_t node;
82
1.88k
    igraph_bool_t triangle = false;
83
1.88k
    igraph_vector_int_t *neis;
84
1.88k
    igraph_vector_int_t level;
85
1.88k
    igraph_int_t stoplevel = no_of_nodes + 1;
86
1.88k
    igraph_bool_t anycircle = false;
87
1.88k
    igraph_int_t t1 = 0, t2 = 0;
88
89
1.88k
    IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE));
90
1.88k
    IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
91
1.88k
    IGRAPH_DQUEUE_INT_INIT_FINALLY(&q, 100);
92
1.88k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&level, no_of_nodes);
93
94
283k
    for (node = 0; !triangle && node < no_of_nodes; node++) {
95
96
        /* Are there circles in this graph at all? */
97
282k
        if (node == 1 && anycircle == 0) {
98
1.53k
            igraph_bool_t conn;
99
1.53k
            IGRAPH_CHECK(igraph_is_connected(graph, &conn, IGRAPH_WEAK));
100
1.53k
            if (conn) {
101
                /* No, there are none */
102
105
                break;
103
105
            }
104
1.53k
        }
105
106
282k
        anycircle = 0;
107
282k
        igraph_dqueue_int_clear(&q);
108
282k
        igraph_vector_int_null(&level);
109
282k
        IGRAPH_CHECK(igraph_dqueue_int_push(&q, node));
110
282k
        VECTOR(level)[node] = 1;
111
112
282k
        IGRAPH_ALLOW_INTERRUPTION();
113
114
2.23M
        while (!igraph_dqueue_int_empty(&q)) {
115
1.96M
            igraph_int_t actnode = igraph_dqueue_int_pop(&q);
116
1.96M
            igraph_int_t actlevel = VECTOR(level)[actnode];
117
1.96M
            igraph_int_t i, n;
118
119
1.96M
            if (actlevel >= stoplevel) {
120
8.75k
                break;
121
8.75k
            }
122
123
1.95M
            neis = igraph_lazy_adjlist_get(&adjlist, actnode);
124
1.95M
            IGRAPH_CHECK_OOM(neis, "Failed to query neighbors.");
125
126
1.95M
            n = igraph_vector_int_size(neis);
127
5.44M
            for (i = 0; i < n; i++) {
128
3.49M
                igraph_int_t nei = VECTOR(*neis)[i];
129
3.49M
                igraph_int_t neilevel = VECTOR(level)[nei];
130
3.49M
                if (neilevel != 0) {
131
1.68M
                    if (neilevel == actlevel - 1) {
132
1.67M
                        continue;
133
1.67M
                    } else {
134
                        /* found circle */
135
6.40k
                        stoplevel = neilevel;
136
6.40k
                        anycircle = 1;
137
6.40k
                        if (actlevel < mincirc) {
138
                            /* Is it a minimum circle? */
139
6.40k
                            mincirc = actlevel + neilevel - 1;
140
6.40k
                            minvertex = node;
141
6.40k
                            t1 = actnode; t2 = nei;
142
6.40k
                            if (neilevel == 2) {
143
                                /* Is it a triangle? */
144
285
                                triangle = 1;
145
285
                            }
146
6.40k
                        }
147
6.40k
                        if (neilevel == actlevel) {
148
485
                            break;
149
485
                        }
150
6.40k
                    }
151
1.81M
                } else {
152
1.81M
                    IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei));
153
1.81M
                    VECTOR(level)[nei] = actlevel + 1;
154
1.81M
                }
155
3.49M
            }
156
157
1.95M
        } /* while q !empty */
158
282k
    } /* node */
159
160
1.88k
    if (girth) {
161
1.88k
        if (mincirc == IGRAPH_INTEGER_MAX) {
162
1.40k
            *girth = IGRAPH_INFINITY;
163
1.40k
        } else {
164
476
            *girth = mincirc;
165
476
        }
166
1.88k
    }
167
168
1.88k
    if (mincirc == IGRAPH_INTEGER_MAX) {
169
1.40k
        mincirc = 0;
170
1.40k
    }
171
172
    /* Store the actual circle, if needed */
173
1.88k
    if (cycle) {
174
1.88k
        IGRAPH_CHECK(igraph_vector_int_resize(cycle, mincirc));
175
1.88k
        if (mincirc != 0) {
176
476
            igraph_int_t i, n, idx = 0;
177
476
            igraph_dqueue_int_clear(&q);
178
476
            igraph_vector_int_null(&level); /* used for parent pointers */
179
25.7k
#define PARENT(x) (VECTOR(level)[(x)])
180
476
            IGRAPH_CHECK(igraph_dqueue_int_push(&q, minvertex));
181
476
            PARENT(minvertex) = minvertex;
182
3.73k
            while (PARENT(t1) == 0 || PARENT(t2) == 0) {
183
3.25k
                igraph_int_t actnode = igraph_dqueue_int_pop(&q);
184
3.25k
                neis = igraph_lazy_adjlist_get(&adjlist, actnode);
185
3.25k
                IGRAPH_CHECK_OOM(neis, "Failed to query neighbors.");
186
3.25k
                n = igraph_vector_int_size(neis);
187
13.5k
                for (i = 0; i < n; i++) {
188
10.3k
                    igraph_int_t nei = VECTOR(*neis)[i];
189
10.3k
                    if (PARENT(nei) == 0) {
190
7.39k
                        PARENT(nei) = actnode + 1;
191
7.39k
                        IGRAPH_CHECK(igraph_dqueue_int_push(&q, nei));
192
7.39k
                    }
193
10.3k
                }
194
3.25k
            }  /* while q !empty */
195
            /* Ok, now use PARENT to create the path */
196
1.75k
            while (t1 != minvertex) {
197
1.28k
                VECTOR(*cycle)[idx++] = t1;
198
1.28k
                t1 = PARENT(t1) - 1;
199
1.28k
            }
200
476
            VECTOR(*cycle)[idx] = minvertex;
201
476
            idx = mincirc - 1;
202
1.89k
            while (t2 != minvertex) {
203
1.41k
                VECTOR(*cycle)[idx--] = t2;
204
1.41k
                t2 = PARENT(t2) - 1;
205
1.41k
            }
206
476
        } /* anycircle */
207
1.88k
    } /* circle */
208
1.88k
#undef PARENT
209
210
1.88k
    igraph_vector_int_destroy(&level);
211
1.88k
    igraph_dqueue_int_destroy(&q);
212
1.88k
    igraph_lazy_adjlist_destroy(&adjlist);
213
1.88k
    IGRAPH_FINALLY_CLEAN(3);
214
215
1.88k
    return IGRAPH_SUCCESS;
216
1.88k
}