/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 | } |