Coverage Report

Created: 2026-01-10 06:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/centrality/coreness.c
Line
Count
Source
1
/*
2
   igraph library.
3
   Copyright (C) 2006-2023  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_community.h"
20
21
#include "igraph_memory.h"
22
#include "igraph_interface.h"
23
#include "igraph_iterators.h"
24
25
/**
26
 * \function igraph_coreness
27
 * \brief The coreness of the vertices in a graph.
28
 *
29
 * The k-core of a graph is a maximal subgraph in which each vertex
30
 * has at least degree k. (Degree here means the degree in the
31
 * subgraph of course.). The coreness of a vertex is the highest order
32
 * of a k-core containing the vertex.
33
 *
34
 * </para><para>
35
 * This function implements the algorithm presented in Vladimir
36
 * Batagelj, Matjaz Zaversnik: An O(m) Algorithm for Cores
37
 * Decomposition of Networks.
38
 * https://arxiv.org/abs/cs/0310049
39
 *
40
 * \param graph The input graph.
41
 * \param cores Pointer to an initialized vector, the result of the
42
 *        computation will be stored here. It will be resized as
43
 *        needed. For each vertex it contains the highest order of a
44
 *        core containing the vertex.
45
 * \param mode For directed graph it specifies whether to calculate
46
 *        in-cores, out-cores or the undirected version. It is ignored
47
 *        for undirected graphs. Possible values: \c IGRAPH_ALL
48
 *        undirected version, \c IGRAPH_IN in-cores, \c IGRAPH_OUT
49
 *        out-cores.
50
 * \return Error code.
51
 *
52
 * Time complexity: O(|E|), the number of edges.
53
 */
54
55
igraph_error_t igraph_coreness(const igraph_t *graph,
56
3.54k
        igraph_vector_int_t *cores, igraph_neimode_t mode) {
57
58
3.54k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
59
3.54k
    igraph_int_t *bin, *vert, *pos;
60
3.54k
    igraph_int_t maxdeg;
61
3.54k
    igraph_vector_int_t neis;
62
3.54k
    igraph_neimode_t omode;
63
64
3.54k
    if (mode != IGRAPH_ALL && mode != IGRAPH_OUT && mode != IGRAPH_IN) {
65
0
        IGRAPH_ERROR("Invalid mode in k-cores.", IGRAPH_EINVMODE);
66
0
    }
67
3.54k
    if (!igraph_is_directed(graph)) {
68
1.81k
        mode = IGRAPH_ALL;
69
1.81k
    }
70
3.54k
    omode = IGRAPH_REVERSE_MODE(mode);
71
72
    /* catch null graph */
73
3.54k
    if (no_of_nodes == 0) {
74
2
        igraph_vector_int_clear(cores);
75
2
        return IGRAPH_SUCCESS;
76
2
    }
77
78
3.54k
    vert = IGRAPH_CALLOC(no_of_nodes, igraph_int_t);
79
3.54k
    IGRAPH_CHECK_OOM(vert, "Insufficient memory for k-cores.");
80
3.54k
    IGRAPH_FINALLY(igraph_free, vert);
81
82
3.54k
    pos = IGRAPH_CALLOC(no_of_nodes, igraph_int_t);
83
3.54k
    IGRAPH_CHECK_OOM(pos, "Insufficient memory for k-cores.");
84
3.54k
    IGRAPH_FINALLY(igraph_free, pos);
85
86
    /* maximum degree + degree of vertices */
87
3.54k
    IGRAPH_CHECK(igraph_degree(graph, cores, igraph_vss_all(), mode, IGRAPH_LOOPS));
88
89
    /* null graph was already handled earlier, 'cores' is not empty */
90
3.54k
    maxdeg = igraph_vector_int_max(cores);
91
92
3.54k
    bin = IGRAPH_CALLOC(maxdeg + 1, igraph_int_t);
93
3.54k
    IGRAPH_CHECK_OOM(bin, "Insufficient memory for k-cores.");
94
3.54k
    IGRAPH_FINALLY(igraph_free, bin);
95
96
    /* degree histogram */
97
617k
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
98
613k
        bin[ VECTOR(*cores)[i] ] += 1;
99
613k
    }
100
101
    /* start pointers */
102
81.6k
    for (igraph_int_t d = 0, start = 0; d <= maxdeg; d++) {
103
78.1k
        igraph_int_t k = bin[d];
104
78.1k
        bin[d] = start;
105
78.1k
        start += k;
106
78.1k
    }
107
108
    /* sort in vert (and corrupt bin) */
109
617k
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
110
613k
        pos[i] = bin[VECTOR(*cores)[i]];
111
613k
        vert[pos[i]] = i;
112
613k
        bin[VECTOR(*cores)[i]] += 1;
113
613k
    }
114
115
    /* correct bin */
116
78.1k
    for (igraph_int_t d = maxdeg; d > 0; d--) {
117
74.6k
        bin[d] = bin[d - 1];
118
74.6k
    }
119
3.54k
    bin[0] = 0;
120
121
    /* this is the main algorithm */
122
3.54k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg);
123
617k
    for (igraph_int_t i = 0; i < no_of_nodes; i++) {
124
613k
        igraph_int_t v = vert[i];
125
613k
        IGRAPH_CHECK(igraph_neighbors(
126
613k
            graph, &neis, v, omode, IGRAPH_LOOPS, IGRAPH_MULTIPLE
127
613k
        ));
128
613k
        igraph_int_t nei_count = igraph_vector_int_size(&neis);
129
820k
        for (igraph_int_t j = 0; j < nei_count; j++) {
130
206k
            igraph_int_t u = VECTOR(neis)[j];
131
206k
            if (VECTOR(*cores)[u] > VECTOR(*cores)[v]) {
132
65.3k
                igraph_int_t du = VECTOR(*cores)[u];
133
65.3k
                igraph_int_t pu = pos[u];
134
65.3k
                igraph_int_t pw = bin[du];
135
65.3k
                igraph_int_t w = vert[pw];
136
65.3k
                if (u != w) {
137
22.4k
                    pos[u] = pw;
138
22.4k
                    pos[w] = pu;
139
22.4k
                    vert[pu] = w;
140
22.4k
                    vert[pw] = u;
141
22.4k
                }
142
65.3k
                bin[du] += 1;
143
65.3k
                VECTOR(*cores)[u] -= 1;
144
65.3k
            }
145
206k
        }
146
613k
    }
147
148
3.54k
    igraph_vector_int_destroy(&neis);
149
3.54k
    IGRAPH_FINALLY_CLEAN(1);
150
151
3.54k
    igraph_free(bin);
152
3.54k
    igraph_free(pos);
153
3.54k
    igraph_free(vert);
154
3.54k
    IGRAPH_FINALLY_CLEAN(3);
155
156
3.54k
    return IGRAPH_SUCCESS;
157
3.54k
}