1 /* -*- mode: C -*- */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /*
4 IGraph library.
5 Copyright (C) 2005-2021 The igraph development team
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301 USA
21
22 */
23
24 #include "igraph_structural.h"
25
26 #include "igraph_interface.h"
27
28 /**
29 * \function igraph_maxdegree
30 * \brief The maximum degree in a graph (or set of vertices).
31 *
32 * </para><para>
33 * The largest in-, out- or total degree of the specified vertices is
34 * calculated. If the graph has no vertices, or \p vids is empty,
35 * 0 is returned, as this is the smallest possible value for degrees.
36 *
37 * \param graph The input graph.
38 * \param res Pointer to an integer (\c igraph_integer_t), the result
39 * will be stored here.
40 * \param vids Vector giving the vertex IDs for which the maximum degree will
41 * be calculated.
42 * \param mode Defines the type of the degree.
43 * \c IGRAPH_OUT, out-degree,
44 * \c IGRAPH_IN, in-degree,
45 * \c IGRAPH_ALL, total degree (sum of the
46 * in- and out-degree).
47 * This parameter is ignored for undirected graphs.
48 * \param loops Boolean, gives whether the self-loops should be
49 * counted.
50 * \return Error code:
51 * \c IGRAPH_EINVVID: invalid vertex id.
52 * \c IGRAPH_EINVMODE: invalid mode argument.
53 *
54 * Time complexity: O(v) if loops is TRUE, and O(v*d) otherwise. v is the number
55 * of vertices for which the degree will be calculated, and d is their
56 * (average) degree.
57 */
igraph_maxdegree(const igraph_t * graph,igraph_integer_t * res,igraph_vs_t vids,igraph_neimode_t mode,igraph_bool_t loops)58 int igraph_maxdegree(const igraph_t *graph, igraph_integer_t *res,
59 igraph_vs_t vids, igraph_neimode_t mode,
60 igraph_bool_t loops) {
61
62 igraph_vector_t tmp;
63
64 IGRAPH_VECTOR_INIT_FINALLY(&tmp, 0);
65
66 IGRAPH_CHECK(igraph_degree(graph, &tmp, vids, mode, loops));
67 if (igraph_vector_size(&tmp) == 0) {
68 *res = 0;
69 } else {
70 *res = (igraph_integer_t) igraph_vector_max(&tmp);
71 }
72
73 igraph_vector_destroy(&tmp);
74 IGRAPH_FINALLY_CLEAN(1);
75
76 return IGRAPH_SUCCESS;
77 }
78
igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t * graph,igraph_vs_t vids,igraph_neimode_t mode,igraph_neimode_t neighbor_degree_mode,igraph_vector_t * knn,igraph_vector_t * knnk,const igraph_vector_t * weights)79 static int igraph_i_avg_nearest_neighbor_degree_weighted(const igraph_t *graph,
80 igraph_vs_t vids,
81 igraph_neimode_t mode,
82 igraph_neimode_t neighbor_degree_mode,
83 igraph_vector_t *knn,
84 igraph_vector_t *knnk,
85 const igraph_vector_t *weights) {
86
87 long int no_of_nodes = igraph_vcount(graph);
88 igraph_vector_t neis, edge_neis;
89 long int i, j, no_vids;
90 igraph_vit_t vit;
91 igraph_vector_t my_knn_v, *my_knn = knn;
92 igraph_vector_t strength, deg;
93 igraph_integer_t maxdeg;
94 igraph_vector_t deghist;
95 igraph_real_t mynan = IGRAPH_NAN;
96
97 if (igraph_vector_size(weights) != igraph_ecount(graph)) {
98 IGRAPH_ERROR("Invalid weight vector size", IGRAPH_EINVAL);
99 }
100
101 IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
102 IGRAPH_FINALLY(igraph_vit_destroy, &vit);
103 no_vids = IGRAPH_VIT_SIZE(vit);
104
105 if (!knn) {
106 IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids);
107 my_knn = &my_knn_v;
108 } else {
109 IGRAPH_CHECK(igraph_vector_resize(knn, no_vids));
110 }
111
112 /* Get degree of neighbours */
113 IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes);
114 IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(),
115 neighbor_degree_mode, IGRAPH_LOOPS));
116 IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes);
117
118 /* Get strength of all nodes */
119 IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(),
120 mode, IGRAPH_LOOPS, weights));
121
122 /* Get maximum degree for initialization */
123 IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(),
124 mode, IGRAPH_LOOPS));
125 IGRAPH_VECTOR_INIT_FINALLY(&neis, (long int)maxdeg);
126 IGRAPH_VECTOR_INIT_FINALLY(&edge_neis, (long int)maxdeg);
127 igraph_vector_resize(&neis, 0);
128 igraph_vector_resize(&edge_neis, 0);
129
130 if (knnk) {
131 IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg));
132 igraph_vector_null(knnk);
133 IGRAPH_VECTOR_INIT_FINALLY(°hist, (long int)maxdeg);
134 }
135
136 for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
137 igraph_real_t sum = 0.0;
138 long int v = IGRAPH_VIT_GET(vit);
139 long int nv;
140 igraph_real_t str = VECTOR(strength)[v];
141 /* Get neighbours and incident edges */
142 IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, mode));
143 IGRAPH_CHECK(igraph_incident(graph, &edge_neis, (igraph_integer_t) v, mode));
144 nv = igraph_vector_size(&neis);
145 for (j = 0; j < nv; j++) {
146 long int nei = (long int) VECTOR(neis)[j];
147 long int e = (long int) VECTOR(edge_neis)[j];
148 double w = VECTOR(*weights)[e];
149 sum += w * VECTOR(deg)[nei];
150 }
151 if (str != 0.0) {
152 VECTOR(*my_knn)[i] = sum / str;
153 } else {
154 VECTOR(*my_knn)[i] = mynan;
155 }
156 if (knnk && nv > 0) {
157 VECTOR(*knnk)[nv - 1] += VECTOR(*my_knn)[i];
158 VECTOR(deghist)[nv - 1] += 1;
159 }
160 }
161
162 igraph_vector_destroy(&edge_neis);
163 igraph_vector_destroy(&neis);
164 IGRAPH_FINALLY_CLEAN(2);
165
166 if (knnk) {
167 for (i = 0; i < maxdeg; i++) {
168 igraph_real_t dh = VECTOR(deghist)[i];
169 if (dh != 0) {
170 VECTOR(*knnk)[i] /= dh;
171 } else {
172 VECTOR(*knnk)[i] = mynan;
173 }
174 }
175
176 igraph_vector_destroy(°hist);
177 IGRAPH_FINALLY_CLEAN(1);
178 }
179
180 igraph_vector_destroy(&strength);
181 igraph_vector_destroy(°);
182 IGRAPH_FINALLY_CLEAN(2);
183
184 if (!knn) {
185 igraph_vector_destroy(&my_knn_v);
186 IGRAPH_FINALLY_CLEAN(1);
187 }
188
189 igraph_vit_destroy(&vit);
190 IGRAPH_FINALLY_CLEAN(1);
191
192 return 0;
193 }
194
195 /**
196 * \function igraph_avg_nearest_neighbor_degree
197 * Average neighbor degree.
198 *
199 * Calculates the average degree of the neighbors for each vertex (\p knn), and
200 * optionally, the same quantity as a function of the vertex degree (\p knnk).
201 *
202 * </para><para>
203 * For isolated vertices \p knn is set to NaN.
204 * The same is done in \p knnk for vertex degrees that
205 * don't appear in the graph.
206 *
207 * </para><para>
208 * The weighted version computes a weighted average of the neighbor degrees as
209 *
210 * <code>k_nn_u = 1/s_u sum_v w_uv k_v</code>,
211 *
212 * where <code>s_u = sum_v w_uv</code> is the sum of the incident edge weights
213 * of vertex \c u, i.e. its strength.
214 * The sum runs over the neighbors \c v of vertex \c u
215 * as indicated by \p mode. <code>w_uv</code> denotes the weighted adjacency matrix
216 * and <code>k_v</code> is the neighbors' degree, specified by \p neighbor_degree_mode.
217 *
218 * </para><para>
219 * Reference:
220 * A. Barrat, M. Barthélemy, R. Pastor-Satorras, and A. Vespignani,
221 * The architecture of complex weighted networks,
222 * Proc. Natl. Acad. Sci. USA 101, 3747 (2004).
223 * https://dx.doi.org/10.1073/pnas.0400087101
224 *
225 * \param graph The input graph. It may be directed.
226 * \param vids The vertices for which the calculation is performed.
227 * \param mode The type of neighbors to consider in directed graphs.
228 * \c IGRAPH_OUT considers out-neighbors, \c IGRAPH_IN in-neighbors
229 * and \c IGRAPH_ALL ignores edge directions.
230 * \param neighbor_degree_mode The type of degree to average in directed graphs.
231 * \c IGRAPH_OUT averages out-degrees, \c IGRAPH_IN averages in-degrees
232 * and \c IGRAPH_ALL ignores edge directions for the degree calculation.
233 * \param vids The vertices for which the calculation is performed.
234 * \param knn Pointer to an initialized vector, the result will be
235 * stored here. It will be resized as needed. Supply a \c NULL pointer
236 * here, if you only want to calculate \c knnk.
237 * \param knnk Pointer to an initialized vector, the average
238 * neighbor degree as a function of the vertex degree is stored
239 * here. The first (zeroth) element is for degree one vertices,
240 * etc. Supply a \c NULL pointer here if you don't want to calculate
241 * this.
242 * \param weights Optional edge weights. Supply a null pointer here
243 * for the non-weighted version.
244 *
245 * \return Error code.
246 *
247 * Time complexity: O(|V|+|E|), linear in the number of vertices and
248 * edges.
249 *
250 * \example examples/simple/igraph_knn.c
251 */
igraph_avg_nearest_neighbor_degree(const igraph_t * graph,igraph_vs_t vids,igraph_neimode_t mode,igraph_neimode_t neighbor_degree_mode,igraph_vector_t * knn,igraph_vector_t * knnk,const igraph_vector_t * weights)252 int igraph_avg_nearest_neighbor_degree(const igraph_t *graph,
253 igraph_vs_t vids,
254 igraph_neimode_t mode,
255 igraph_neimode_t neighbor_degree_mode,
256 igraph_vector_t *knn,
257 igraph_vector_t *knnk,
258 const igraph_vector_t *weights) {
259
260 long int no_of_nodes = igraph_vcount(graph);
261 igraph_vector_t neis;
262 long int i, j, no_vids;
263 igraph_vit_t vit;
264 igraph_vector_t my_knn_v, *my_knn = knn;
265 igraph_vector_t deg;
266 igraph_integer_t maxdeg;
267 igraph_vector_t deghist;
268 igraph_real_t mynan = IGRAPH_NAN;
269 igraph_bool_t simple;
270
271 IGRAPH_CHECK(igraph_is_simple(graph, &simple));
272 if (!simple) {
273 IGRAPH_ERROR("Average nearest neighbor degree works only with "
274 "simple graphs", IGRAPH_EINVAL);
275 }
276
277 if (weights) {
278 return igraph_i_avg_nearest_neighbor_degree_weighted(graph, vids,
279 mode, neighbor_degree_mode, knn, knnk, weights);
280 }
281
282 IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
283 IGRAPH_FINALLY(igraph_vit_destroy, &vit);
284 no_vids = IGRAPH_VIT_SIZE(vit);
285
286 if (!knn) {
287 IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids);
288 my_knn = &my_knn_v;
289 } else {
290 IGRAPH_CHECK(igraph_vector_resize(knn, no_vids));
291 }
292
293 IGRAPH_VECTOR_INIT_FINALLY(°, no_of_nodes);
294 IGRAPH_CHECK(igraph_degree(graph, °, igraph_vss_all(),
295 neighbor_degree_mode, IGRAPH_LOOPS));
296 IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(), mode, IGRAPH_LOOPS));
297 IGRAPH_VECTOR_INIT_FINALLY(&neis, maxdeg);
298 igraph_vector_resize(&neis, 0);
299
300 if (knnk) {
301 IGRAPH_CHECK(igraph_vector_resize(knnk, (long int)maxdeg));
302 igraph_vector_null(knnk);
303 IGRAPH_VECTOR_INIT_FINALLY(°hist, (long int)maxdeg);
304 }
305
306 for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
307 igraph_real_t sum = 0.0;
308 long int v = IGRAPH_VIT_GET(vit);
309 long int nv;
310 IGRAPH_CHECK(igraph_neighbors(graph, &neis, (igraph_integer_t) v, mode));
311 nv = igraph_vector_size(&neis);
312 for (j = 0; j < nv; j++) {
313 long int nei = (long int) VECTOR(neis)[j];
314 sum += VECTOR(deg)[nei];
315 }
316 if (nv != 0) {
317 VECTOR(*my_knn)[i] = sum / nv;
318 } else {
319 VECTOR(*my_knn)[i] = mynan;
320 }
321 if (knnk && nv > 0) {
322 VECTOR(*knnk)[nv - 1] += VECTOR(*my_knn)[i];
323 VECTOR(deghist)[nv - 1] += 1;
324 }
325 }
326
327 if (knnk) {
328 for (i = 0; i < maxdeg; i++) {
329 long int dh = (long int) VECTOR(deghist)[i];
330 if (dh != 0) {
331 VECTOR(*knnk)[i] /= dh;
332 } else {
333 VECTOR(*knnk)[i] = mynan;
334 }
335 }
336 igraph_vector_destroy(°hist);
337 IGRAPH_FINALLY_CLEAN(1);
338 }
339
340 igraph_vector_destroy(&neis);
341 igraph_vector_destroy(°);
342 igraph_vit_destroy(&vit);
343 IGRAPH_FINALLY_CLEAN(3);
344
345 if (!knn) {
346 igraph_vector_destroy(&my_knn_v);
347 IGRAPH_FINALLY_CLEAN(1);
348 }
349
350 return 0;
351 }
352
353 /**
354 * \function igraph_strength
355 * Strength of the vertices, weighted vertex degree in other words.
356 *
357 * In a weighted network the strength of a vertex is the sum of the
358 * weights of all incident edges. In a non-weighted network this is
359 * exactly the vertex degree.
360 * \param graph The input graph.
361 * \param res Pointer to an initialized vector, the result is stored
362 * here. It will be resized as needed.
363 * \param vids The vertices for which the calculation is performed.
364 * \param mode Gives whether to count only outgoing (\c IGRAPH_OUT),
365 * incoming (\c IGRAPH_IN) edges or both (\c IGRAPH_ALL).
366 * \param loops A logical scalar, whether to count loop edges as well.
367 * \param weights A vector giving the edge weights. If this is a NULL
368 * pointer, then \ref igraph_degree() is called to perform the
369 * calculation.
370 * \return Error code.
371 *
372 * Time complexity: O(|V|+|E|), linear in the number vertices and
373 * edges.
374 *
375 * \sa \ref igraph_degree() for the traditional, non-weighted version.
376 */
igraph_strength(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids,igraph_neimode_t mode,igraph_bool_t loops,const igraph_vector_t * weights)377 int igraph_strength(const igraph_t *graph, igraph_vector_t *res,
378 const igraph_vs_t vids, igraph_neimode_t mode,
379 igraph_bool_t loops, const igraph_vector_t *weights) {
380
381 long int no_of_nodes = igraph_vcount(graph);
382 igraph_vit_t vit;
383 long int no_vids;
384 igraph_vector_t neis;
385 long int i;
386
387 if (!weights) {
388 return igraph_degree(graph, res, vids, mode, loops);
389 }
390
391 if (igraph_vector_size(weights) != igraph_ecount(graph)) {
392 IGRAPH_ERROR("Invalid weight vector length", IGRAPH_EINVAL);
393 }
394
395 IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
396 IGRAPH_FINALLY(igraph_vit_destroy, &vit);
397 no_vids = IGRAPH_VIT_SIZE(vit);
398
399 IGRAPH_VECTOR_INIT_FINALLY(&neis, 0);
400 IGRAPH_CHECK(igraph_vector_reserve(&neis, no_of_nodes));
401 IGRAPH_CHECK(igraph_vector_resize(res, no_vids));
402 igraph_vector_null(res);
403
404 if (loops) {
405 for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
406 long int vid = IGRAPH_VIT_GET(vit);
407 long int j, n;
408 IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) vid, mode));
409 n = igraph_vector_size(&neis);
410 for (j = 0; j < n; j++) {
411 long int edge = (long int) VECTOR(neis)[j];
412 VECTOR(*res)[i] += VECTOR(*weights)[edge];
413 }
414 }
415 } else {
416 for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
417 long int vid = IGRAPH_VIT_GET(vit);
418 long int j, n;
419 IGRAPH_CHECK(igraph_incident(graph, &neis, (igraph_integer_t) vid, mode));
420 n = igraph_vector_size(&neis);
421 for (j = 0; j < n; j++) {
422 long int edge = (long int) VECTOR(neis)[j];
423 long int from = IGRAPH_FROM(graph, edge);
424 long int to = IGRAPH_TO(graph, edge);
425 if (from != to) {
426 VECTOR(*res)[i] += VECTOR(*weights)[edge];
427 }
428 }
429 }
430 }
431
432 igraph_vit_destroy(&vit);
433 igraph_vector_destroy(&neis);
434 IGRAPH_FINALLY_CLEAN(2);
435
436 return 0;
437 }
438
439
440 /**
441 * \function igraph_sort_vertex_ids_by_degree
442 * \brief Calculate a list of vertex ids sorted by degree of the corresponding vertex.
443 *
444 * The list of vertex ids is returned in a vector that is sorted
445 * in ascending or descending order of vertex degree.
446 *
447 * \param graph The input graph.
448 * \param outvids Pointer to an initialized vector that will be
449 * resized and will contain the ordered vertex ids.
450 * \param vids Input vertex selector of vertex ids to include in
451 * calculation.
452 * \param mode Defines the type of the degree.
453 * \c IGRAPH_OUT, out-degree,
454 * \c IGRAPH_IN, in-degree,
455 * \c IGRAPH_ALL, total degree (sum of the
456 * in- and out-degree).
457 * This parameter is ignored for undirected graphs.
458 * \param loops Boolean, gives whether the self-loops should be
459 * counted.
460 * \param order Specifies whether the ordering should be ascending
461 * (\c IGRAPH_ASCENDING) or descending (\c IGRAPH_DESCENDING).
462 * \param only_indices If true, then return a sorted list of indices
463 * into a vector corresponding to \c vids, rather than a list
464 * of vertex ids. This parameter is ignored if \c vids is set
465 * to all vertices via igraph_vs_all() or igraph_vss_all(),
466 * because in this case the indices and vertex ids are the
467 * same.
468 * \return Error code:
469 * \c IGRAPH_EINVVID: invalid vertex id.
470 * \c IGRAPH_EINVMODE: invalid mode argument.
471 *
472 */
igraph_sort_vertex_ids_by_degree(const igraph_t * graph,igraph_vector_t * outvids,igraph_vs_t vids,igraph_neimode_t mode,igraph_bool_t loops,igraph_order_t order,igraph_bool_t only_indices)473 int igraph_sort_vertex_ids_by_degree(const igraph_t *graph,
474 igraph_vector_t *outvids,
475 igraph_vs_t vids,
476 igraph_neimode_t mode,
477 igraph_bool_t loops,
478 igraph_order_t order,
479 igraph_bool_t only_indices) {
480 long int i;
481 igraph_vector_t degrees, vs_vec;
482 IGRAPH_VECTOR_INIT_FINALLY(°rees, 0);
483 IGRAPH_CHECK(igraph_degree(graph, °rees, vids, mode, loops));
484 IGRAPH_CHECK((int) igraph_vector_qsort_ind(°rees, outvids,
485 order == IGRAPH_DESCENDING));
486 if (only_indices || igraph_vs_is_all(&vids) ) {
487 igraph_vector_destroy(°rees);
488 IGRAPH_FINALLY_CLEAN(1);
489 } else {
490 IGRAPH_VECTOR_INIT_FINALLY(&vs_vec, 0);
491 IGRAPH_CHECK(igraph_vs_as_vector(graph, vids, &vs_vec));
492 for (i = 0; i < igraph_vector_size(outvids); i++) {
493 VECTOR(*outvids)[i] = VECTOR(vs_vec)[(long int)VECTOR(*outvids)[i]];
494 }
495 igraph_vector_destroy(&vs_vec);
496 igraph_vector_destroy(°rees);
497 IGRAPH_FINALLY_CLEAN(2);
498 }
499 return 0;
500 }
501