Coverage Report

Created: 2025-08-29 06:51

/src/igraph/src/properties/degrees.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
   IGraph library.
3
   Copyright (C) 2005-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_structural.h"
20
21
#include "igraph_interface.h"
22
23
/**
24
 * \function igraph_maxdegree
25
 * \brief The maximum degree in a graph (or set of vertices).
26
 *
27
 * The largest in-, out- or total degree of the specified vertices is
28
 * calculated. If the graph has no vertices, or \p vids is empty,
29
 * 0 is returned, as this is the smallest possible value for degrees.
30
 *
31
 * \param graph The input graph.
32
 * \param res Pointer to an integer (\c igraph_integer_t), the result
33
 *        will be stored here.
34
 * \param vids Vector giving the vertex IDs for which the maximum degree will
35
 *        be calculated.
36
 * \param mode Defines the type of the degree.
37
 *        \c IGRAPH_OUT, out-degree,
38
 *        \c IGRAPH_IN, in-degree,
39
 *        \c IGRAPH_ALL, total degree (sum of the
40
 *        in- and out-degree).
41
 *        This parameter is ignored for undirected graphs.
42
 * \param loops Specifies how to treat loop edges when calculating the
43
 *        degree. \c IGRAPH_NO_LOOPS ignores loop edges; \c IGRAPH_LOOPS_ONCE
44
 *        counts each loop edge only once; \c IGRAPH_LOOPS_TWICE counts each
45
 *        loop edge twice in undirected graphs and once in directed graphs.
46
 * \return Error code:
47
 *         \c IGRAPH_EINVVID: invalid vertex ID.
48
 *         \c IGRAPH_EINVMODE: invalid mode argument.
49
 *
50
 * Time complexity: O(v) if \p loops is \c true, and O(v*d) otherwise. v is the number
51
 * of vertices for which the degree will be calculated, and d is their
52
 * (average) degree.
53
 *
54
 * \sa \ref igraph_degree() to retrieve the degrees for several vertices.
55
 */
56
igraph_error_t igraph_maxdegree(
57
    const igraph_t *graph, igraph_integer_t *res, igraph_vs_t vids,
58
    igraph_neimode_t mode, igraph_loops_t loops
59
29.8k
) {
60
61
29.8k
    igraph_vector_int_t tmp;
62
63
29.8k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
64
65
29.8k
    IGRAPH_CHECK(igraph_degree(graph, &tmp, vids, mode, loops));
66
29.8k
    if (igraph_vector_int_size(&tmp) == 0) {
67
1.81k
        *res = 0;
68
28.0k
    } else {
69
28.0k
        *res = igraph_vector_int_max(&tmp);
70
28.0k
    }
71
72
29.8k
    igraph_vector_int_destroy(&tmp);
73
29.8k
    IGRAPH_FINALLY_CLEAN(1);
74
75
29.8k
    return IGRAPH_SUCCESS;
76
29.8k
}
77
78
static igraph_error_t avg_nearest_neighbor_degree_weighted(const igraph_t *graph,
79
        igraph_vs_t vids,
80
        igraph_neimode_t mode,
81
        igraph_neimode_t neighbor_degree_mode,
82
        igraph_vector_t *knn,
83
        igraph_vector_t *knnk,
84
3.21k
        const igraph_vector_t *weights) {
85
86
3.21k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
87
3.21k
    igraph_vector_int_t neis, edge_neis;
88
3.21k
    igraph_integer_t no_vids;
89
3.21k
    igraph_vit_t vit;
90
3.21k
    igraph_vector_t my_knn_v, *my_knn = knn;
91
3.21k
    igraph_vector_t strength;
92
3.21k
    igraph_vector_int_t deg;
93
3.21k
    igraph_integer_t maxdeg;
94
3.21k
    igraph_vector_t deghist;
95
96
3.21k
    if (igraph_vector_size(weights) != igraph_ecount(graph)) {
97
0
        IGRAPH_ERROR("Invalid weight vector size.", IGRAPH_EINVAL);
98
0
    }
99
100
3.21k
    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
101
3.21k
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
102
3.21k
    no_vids = IGRAPH_VIT_SIZE(vit);
103
104
3.21k
    if (!knn) {
105
0
        IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids);
106
0
        my_knn = &my_knn_v;
107
3.21k
    } else {
108
3.21k
        IGRAPH_CHECK(igraph_vector_resize(knn, no_vids));
109
3.21k
    }
110
111
    /* Get degree of neighbours */
112
3.21k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&deg, no_of_nodes);
113
3.21k
    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(),
114
3.21k
                               neighbor_degree_mode, IGRAPH_LOOPS));
115
3.21k
    IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes);
116
117
    /* Get strength of all nodes */
118
3.21k
    IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(),
119
3.21k
                                 mode, IGRAPH_LOOPS, weights));
120
121
    /* Get maximum degree for initialization */
122
3.21k
    IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(),
123
3.21k
                                  mode, IGRAPH_LOOPS));
124
3.21k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg);
125
3.21k
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_neis, maxdeg);
126
3.21k
    igraph_vector_int_clear(&neis);
127
3.21k
    igraph_vector_int_clear(&edge_neis);
128
129
3.21k
    if (knnk) {
130
3.21k
        IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg));
131
3.21k
        igraph_vector_null(knnk);
132
3.21k
        IGRAPH_VECTOR_INIT_FINALLY(&deghist, maxdeg);
133
3.21k
    }
134
135
609k
    for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
136
606k
        igraph_real_t sum = 0.0;
137
606k
        igraph_integer_t v = IGRAPH_VIT_GET(vit);
138
606k
        igraph_integer_t nv;
139
606k
        igraph_real_t str = VECTOR(strength)[v];
140
        /* Get neighbours and incident edges */
141
606k
        IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE));
142
606k
        IGRAPH_CHECK(igraph_incident(graph, &edge_neis, v, mode, IGRAPH_LOOPS));
143
606k
        nv = igraph_vector_int_size(&neis);
144
763k
        for (igraph_integer_t j = 0; j < nv; j++) {
145
157k
            igraph_integer_t nei = VECTOR(neis)[j];
146
157k
            igraph_integer_t e = VECTOR(edge_neis)[j];
147
157k
            igraph_real_t w = VECTOR(*weights)[e];
148
157k
            sum += w * VECTOR(deg)[nei];
149
157k
        }
150
606k
        if (str != 0.0) {
151
82.6k
            VECTOR(*my_knn)[i] = sum / str;
152
523k
        } else {
153
523k
            VECTOR(*my_knn)[i] = IGRAPH_NAN;
154
523k
        }
155
606k
        if (knnk && nv > 0) {
156
82.6k
            VECTOR(*knnk)[nv - 1] += sum;
157
82.6k
            VECTOR(deghist)[nv - 1] += str;
158
82.6k
        }
159
606k
    }
160
161
3.21k
    igraph_vector_int_destroy(&edge_neis);
162
3.21k
    igraph_vector_int_destroy(&neis);
163
3.21k
    IGRAPH_FINALLY_CLEAN(2);
164
165
3.21k
    if (knnk) {
166
37.4k
        for (igraph_integer_t i = 0; i < maxdeg; i++) {
167
34.1k
            igraph_real_t dh = VECTOR(deghist)[i];
168
34.1k
            if (dh != 0) {
169
8.15k
                VECTOR(*knnk)[i] /= dh;
170
26.0k
            } else {
171
26.0k
                VECTOR(*knnk)[i] = IGRAPH_NAN;
172
26.0k
            }
173
34.1k
        }
174
175
3.21k
        igraph_vector_destroy(&deghist);
176
3.21k
        IGRAPH_FINALLY_CLEAN(1);
177
3.21k
    }
178
179
3.21k
    igraph_vector_destroy(&strength);
180
3.21k
    igraph_vector_int_destroy(&deg);
181
3.21k
    IGRAPH_FINALLY_CLEAN(2);
182
183
3.21k
    if (!knn) {
184
0
        igraph_vector_destroy(&my_knn_v);
185
0
        IGRAPH_FINALLY_CLEAN(1);
186
0
    }
187
188
3.21k
    igraph_vit_destroy(&vit);
189
3.21k
    IGRAPH_FINALLY_CLEAN(1);
190
191
3.21k
    return IGRAPH_SUCCESS;
192
3.21k
}
193
194
/**
195
 * \function igraph_avg_nearest_neighbor_degree
196
 * \brief Average neighbor degree.
197
 *
198
 * Calculates the average degree of the neighbors for each vertex (\p knn), and
199
 * optionally, the same quantity as a function of the vertex degree (\p knnk).
200
 *
201
 * </para><para>
202
 * For isolated vertices \p knn is set to NaN. The same is done in \p knnk for
203
 * vertex degrees that don't appear in the graph.
204
 *
205
 * </para><para>
206
 * The weighted version computes a weighted average of the neighbor degrees as
207
 *
208
 * </para><para>
209
 * <code>k_nn_u = 1/s_u sum_v w_uv k_v</code>,
210
 *
211
 * </para><para>
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
 * This is equation (6) in the reference below.
218
 *
219
 * </para><para>
220
 * When only the <code>k_nn(k)</code> degree correlation function is needed,
221
 * \ref igraph_degree_correlation_vector() can be used as well. This function provides
222
 * more flexible control over how degree at each end of directed edges are computed.
223
 *
224
 * </para><para>
225
 * Reference:
226
 *
227
 * </para><para>
228
 * A. Barrat, M. Barthélemy, R. Pastor-Satorras, and A. Vespignani,
229
 * The architecture of complex weighted networks,
230
 * Proc. Natl. Acad. Sci. USA 101, 3747 (2004).
231
 * https://dx.doi.org/10.1073/pnas.0400087101
232
 *
233
 * \param graph The input graph. It may be directed.
234
 * \param vids The vertices for which the calculation is performed.
235
 * \param mode The type of neighbors to consider in directed graphs.
236
 *   \c IGRAPH_OUT considers out-neighbors, \c IGRAPH_IN in-neighbors
237
 *   and \c IGRAPH_ALL ignores edge directions.
238
 * \param neighbor_degree_mode The type of degree to average in directed graphs.
239
 *   \c IGRAPH_OUT averages out-degrees, \c IGRAPH_IN averages in-degrees
240
 *   and \c IGRAPH_ALL ignores edge directions for the degree calculation.
241
 * \param knn Pointer to an initialized vector, the result will be
242
 *   stored here. It will be resized as needed. Supply a \c NULL pointer
243
 *   here if you only want to calculate \c knnk.
244
 * \param knnk Pointer to an initialized vector, the average
245
 *   neighbor degree as a function of the vertex degree is stored
246
 *   here. This is sometimes referred to as the <code>k_nn(k)</code>
247
 *   degree correlation function. The first (zeroth) element is for degree
248
 *   one vertices, etc. The calculation is done based only on the vertices
249
 *   \p vids. Supply a \c NULL pointer here if you don't want to calculate this.
250
 * \param weights Optional edge weights. Supply a null pointer here
251
 *   for the non-weighted version.
252
 *
253
 * \return Error code.
254
 *
255
 * \sa \ref igraph_degree_correlation_vector() for computing only the degree correlation function,
256
 * with more flexible control over degree computations.
257
 *
258
 * Time complexity: O(|V|+|E|), linear in the number of vertices and
259
 * edges.
260
 *
261
 * \example examples/simple/igraph_avg_nearest_neighbor_degree.c
262
 */
263
igraph_error_t igraph_avg_nearest_neighbor_degree(const igraph_t *graph,
264
                                       igraph_vs_t vids,
265
                                       igraph_neimode_t mode,
266
                                       igraph_neimode_t neighbor_degree_mode,
267
                                       igraph_vector_t *knn,
268
                                       igraph_vector_t *knnk,
269
3.21k
                                       const igraph_vector_t *weights) {
270
271
3.21k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
272
3.21k
    igraph_vector_int_t neis;
273
3.21k
    igraph_integer_t no_vids;
274
3.21k
    igraph_vit_t vit;
275
3.21k
    igraph_vector_t my_knn_v, *my_knn = knn;
276
3.21k
    igraph_vector_int_t deg;
277
3.21k
    igraph_integer_t maxdeg;
278
3.21k
    igraph_vector_int_t deghist;
279
280
3.21k
    if (weights) {
281
3.21k
        return avg_nearest_neighbor_degree_weighted(graph, vids,
282
3.21k
                mode, neighbor_degree_mode, knn, knnk, weights);
283
3.21k
    }
284
285
0
    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
286
0
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
287
0
    no_vids = IGRAPH_VIT_SIZE(vit);
288
289
0
    if (!knn) {
290
0
        IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids);
291
0
        my_knn = &my_knn_v;
292
0
    } else {
293
0
        IGRAPH_CHECK(igraph_vector_resize(knn, no_vids));
294
0
    }
295
296
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&deg, no_of_nodes);
297
0
    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(),
298
0
                               neighbor_degree_mode, IGRAPH_LOOPS));
299
0
    IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(), mode, IGRAPH_LOOPS));
300
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg);
301
0
    igraph_vector_int_clear(&neis);
302
303
0
    if (knnk) {
304
0
        IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg));
305
0
        igraph_vector_null(knnk);
306
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&deghist, maxdeg);
307
0
    }
308
309
0
    for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
310
0
        igraph_real_t sum = 0.0;
311
0
        igraph_integer_t v = IGRAPH_VIT_GET(vit);
312
0
        igraph_integer_t nv;
313
0
        IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE));
314
0
        nv = igraph_vector_int_size(&neis);
315
0
        for (igraph_integer_t j = 0; j < nv; j++) {
316
0
            igraph_integer_t nei = VECTOR(neis)[j];
317
0
            sum += VECTOR(deg)[nei];
318
0
        }
319
0
        if (nv != 0) {
320
0
            VECTOR(*my_knn)[i] = sum / nv;
321
0
        } else {
322
0
            VECTOR(*my_knn)[i] = IGRAPH_NAN;
323
0
        }
324
0
        if (knnk && nv > 0) {
325
0
            VECTOR(*knnk)[nv - 1] += VECTOR(*my_knn)[i];
326
0
            VECTOR(deghist)[nv - 1] += 1;
327
0
        }
328
0
    }
329
330
0
    if (knnk) {
331
0
        for (igraph_integer_t i = 0; i < maxdeg; i++) {
332
0
            igraph_integer_t dh = VECTOR(deghist)[i];
333
0
            if (dh != 0) {
334
0
                VECTOR(*knnk)[i] /= dh;
335
0
            } else {
336
0
                VECTOR(*knnk)[i] = IGRAPH_NAN;
337
0
            }
338
0
        }
339
0
        igraph_vector_int_destroy(&deghist);
340
0
        IGRAPH_FINALLY_CLEAN(1);
341
0
    }
342
343
0
    igraph_vector_int_destroy(&neis);
344
0
    igraph_vector_int_destroy(&deg);
345
0
    igraph_vit_destroy(&vit);
346
0
    IGRAPH_FINALLY_CLEAN(3);
347
348
0
    if (!knn) {
349
0
        igraph_vector_destroy(&my_knn_v);
350
0
        IGRAPH_FINALLY_CLEAN(1);
351
0
    }
352
353
0
    return IGRAPH_SUCCESS;
354
0
}
355
356
/**
357
 * \function igraph_degree_correlation_vector
358
 * \brief Degree correlation function.
359
 *
360
 * \experimental
361
 *
362
 * Computes the degree correlation function <code>k_nn(k)</code>, defined as the
363
 * mean degree of the targets of directed edges whose source has degree \c k.
364
 * The averaging is done over all directed edges. The \p from_mode and \p to_mode
365
 * parameters control how the source and target vertex degrees are computed.
366
 * This way the out-in, out-out, in-in and in-out degree correlation functions
367
 * can all be computed.
368
 *
369
 * </para><para>
370
 * In undirected graphs, edges are treated as if they were a pair of reciprocal directed
371
 * ones.
372
 *
373
 * </para><para>
374
 * If P_ij is the joint degree distribution of the graph, computable with
375
 * \ref igraph_joint_degree_distribution(), then
376
 * <code>k_nn(k) = (sum_j j P_kj) / (sum_j P_kj)</code>.
377
 *
378
 * </para><para>
379
 * The function \ref igraph_avg_nearest_neighbor_degree(), whose main purpose is to
380
 * calculate the average neighbor degree for each vertex separately, can also compute
381
 * <code>k_nn(k)</code>. It differs from this function in that it can take a subset
382
 * of vertices to base the calculation on, but it does not allow the same fine-grained
383
 * control over how degrees are computed.
384
 *
385
 * </para><para>
386
 * References:
387
 *
388
 * </para><para>
389
 * R. Pastor-Satorras, A. Vazquez, A. Vespignani:
390
 * Dynamical and Correlation Properties of the Internet,
391
 * Phys. Rev. Lett., vol. 87, pp. 258701 (2001).
392
 * https://doi.org/10.1103/PhysRevLett.87.258701
393
 *
394
 * </para><para>
395
 * A. Vazquez, R. Pastor-Satorras, A. Vespignani:
396
 * Large-scale topological and dynamical properties of the Internet,
397
 * Phys. Rev. E, vol. 65, pp. 066130 (2002).
398
 * https://doi.org/10.1103/PhysRevE.65.066130
399
 *
400
 * </para><para>
401
 * A. Barrat, M. Barthélemy, R. Pastor-Satorras, and A. Vespignani,
402
 * The architecture of complex weighted networks,
403
 * Proc. Natl. Acad. Sci. USA 101, 3747 (2004).
404
 * https://dx.doi.org/10.1073/pnas.0400087101
405
 *
406
 * \param graph The input graph.
407
 * \param weights An optional weight vector. If not \c NULL, weighted averages will be computed.
408
 * \param knnk An initialized vector, the result will be written here.
409
 *    <code>knnk[d]</code> will contain the mean degree of vertices connected to
410
 *    by vertices of degree \c d. Note that in contrast to
411
 *    \ref igraph_avg_nearest_neighbor_degree(), <code>d=0</code> is also
412
 *    included.
413
 * \param from_mode How to compute the degree of sources? Can be \c IGRAPH_OUT
414
 *    for out-degree, \c IGRAPH_IN for in-degree, or \c IGRAPH_ALL for total degree.
415
 *    Ignored in undirected graphs.
416
 * \param to_mode How to compute the degree of sources? Can be \c IGRAPH_OUT
417
 *    for out-degree, \c IGRAPH_IN for in-degree, or \c IGRAPH_ALL for total degree.
418
 *    Ignored in undirected graphs.
419
 * \param directed_neighbors Whether to consider <code>u -> v</code> connections
420
 *    to be directed. Undirected connections are treated as reciprocal directed ones,
421
 *    i.e. both <code>u -> v</code> and <code>v -> u</code> will be considered.
422
 *    Ignored in undirected graphs.
423
 * \return Error code.
424
 *
425
 * \sa \ref igraph_avg_nearest_neighbor_degree() for computing the average neighbour
426
 * degree of a set of vertices, \ref igraph_joint_degree_distribution() to get the
427
 * complete joint degree distribution, and \ref igraph_assortativity_degree()
428
 * to compute the degree assortativity.
429
 *
430
 * Time complexity: O(|E| + |V|)
431
 */
432
igraph_error_t igraph_degree_correlation_vector(
433
        const igraph_t *graph, const igraph_vector_t *weights,
434
        igraph_vector_t *knnk,
435
        igraph_neimode_t from_mode, igraph_neimode_t to_mode,
436
3.21k
        igraph_bool_t directed_neighbors) {
437
438
3.21k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
439
3.21k
    igraph_integer_t no_of_edges = igraph_ecount(graph);
440
3.21k
    igraph_integer_t maxdeg;
441
3.21k
    igraph_vector_t weight_sums;
442
3.21k
    igraph_vector_int_t *deg_from, *deg_to, deg_out, deg_in, deg_all;
443
444
3.21k
    if (weights && igraph_vector_size(weights) != no_of_edges) {
445
0
        IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").",
446
0
                      IGRAPH_EINVAL,
447
0
                      igraph_vector_size(weights), no_of_edges);
448
0
    }
449
450
3.21k
    if (! igraph_is_directed(graph)) {
451
1.60k
        from_mode = to_mode = IGRAPH_ALL;
452
1.60k
        directed_neighbors = false;
453
1.60k
    }
454
455
3.21k
    igraph_bool_t have_out = from_mode == IGRAPH_OUT || to_mode == IGRAPH_OUT;
456
3.21k
    igraph_bool_t have_in  = from_mode == IGRAPH_IN  || to_mode == IGRAPH_IN;
457
3.21k
    igraph_bool_t have_all = from_mode == IGRAPH_ALL || to_mode == IGRAPH_ALL;
458
459
3.21k
    if (have_out) {
460
1.60k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&deg_out, no_of_nodes);
461
1.60k
        IGRAPH_CHECK(igraph_degree(graph, &deg_out, igraph_vss_all(), IGRAPH_OUT, /* loops */ true));
462
1.60k
    }
463
464
3.21k
    if (have_in) {
465
1.60k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&deg_in, no_of_nodes);
466
1.60k
        IGRAPH_CHECK(igraph_degree(graph, &deg_in, igraph_vss_all(), IGRAPH_IN, /* loops */ true));
467
1.60k
    }
468
469
3.21k
    if (have_all) {
470
1.60k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&deg_all, no_of_nodes);
471
1.60k
        IGRAPH_CHECK(igraph_degree(graph, &deg_all, igraph_vss_all(), IGRAPH_ALL, /* loops */ true));
472
1.60k
    }
473
474
3.21k
    switch (from_mode) {
475
1.60k
    case IGRAPH_OUT: deg_from = &deg_out; break;
476
0
    case IGRAPH_IN:  deg_from = &deg_in;  break;
477
1.60k
    case IGRAPH_ALL: deg_from = &deg_all; break;
478
0
    default:
479
0
        IGRAPH_ERROR("Invalid 'from' mode.", IGRAPH_EINVMODE);
480
3.21k
    }
481
482
3.21k
    switch (to_mode) {
483
0
    case IGRAPH_OUT: deg_to = &deg_out; break;
484
1.60k
    case IGRAPH_IN:  deg_to = &deg_in;  break;
485
1.60k
    case IGRAPH_ALL: deg_to = &deg_all; break;
486
0
    default:
487
0
        IGRAPH_ERROR("Invalid 'to' mode.", IGRAPH_EINVMODE);
488
3.21k
    }
489
490
3.21k
    maxdeg = no_of_edges > 0 ? igraph_vector_int_max(deg_from) : 0;
491
492
3.21k
    IGRAPH_VECTOR_INIT_FINALLY(&weight_sums, maxdeg+1);
493
494
3.21k
    IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg+1));
495
3.21k
    igraph_vector_null(knnk);
496
497
112k
    for (igraph_integer_t eid=0; eid < no_of_edges; eid++) {
498
109k
        igraph_integer_t from = IGRAPH_FROM(graph, eid);
499
109k
        igraph_integer_t to   = IGRAPH_TO(graph, eid);
500
109k
        igraph_integer_t fromdeg = VECTOR(*deg_from)[from];
501
109k
        igraph_integer_t todeg   = VECTOR(*deg_to)[to];
502
109k
        igraph_real_t w = weights ? VECTOR(*weights)[eid] : 1;
503
504
109k
        VECTOR(weight_sums)[fromdeg] += w;
505
109k
        VECTOR(*knnk)[fromdeg] += w * todeg;
506
507
        /* Treat undirected edges as reciprocal directed ones */
508
109k
        if (! directed_neighbors) {
509
48.3k
            VECTOR(weight_sums)[todeg] += w;
510
48.3k
            VECTOR(*knnk)[todeg] += w * fromdeg;
511
48.3k
        }
512
109k
    }
513
514
3.21k
    IGRAPH_CHECK(igraph_vector_div(knnk, &weight_sums));
515
516
3.21k
    igraph_vector_destroy(&weight_sums);
517
3.21k
    IGRAPH_FINALLY_CLEAN(1);
518
519
    /* In reverse order of initialization: */
520
521
3.21k
    if (have_all) {
522
1.60k
        igraph_vector_int_destroy(&deg_all);
523
1.60k
        IGRAPH_FINALLY_CLEAN(1);
524
1.60k
    }
525
526
3.21k
    if (have_in) {
527
1.60k
        igraph_vector_int_destroy(&deg_in);
528
1.60k
        IGRAPH_FINALLY_CLEAN(1);
529
1.60k
    }
530
531
3.21k
    if (have_out) {
532
1.60k
        igraph_vector_int_destroy(&deg_out);
533
1.60k
        IGRAPH_FINALLY_CLEAN(1);
534
1.60k
    }
535
536
3.21k
    return IGRAPH_SUCCESS;
537
3.21k
}
538
539
static igraph_error_t strength_all(
540
        const igraph_t *graph, igraph_vector_t *res,
541
        igraph_neimode_t mode, igraph_bool_t loops,
542
27.0k
        const igraph_vector_t *weights) {
543
544
    // When calculating strength for all vertices, iterating over edges is faster
545
27.0k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
546
27.0k
    igraph_integer_t no_of_edges = igraph_ecount(graph);
547
548
27.0k
    IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
549
27.0k
    igraph_vector_null(res);
550
551
27.0k
    if (!igraph_is_directed(graph)) {
552
5.54k
        mode = IGRAPH_ALL;
553
5.54k
    }
554
555
27.0k
    if (loops) {
556
20.0k
        if (mode & IGRAPH_OUT) {
557
540k
            for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) {
558
523k
                VECTOR(*res)[IGRAPH_FROM(graph, edge)] += VECTOR(*weights)[edge];
559
523k
            }
560
16.7k
        }
561
20.0k
        if (mode & IGRAPH_IN) {
562
255k
            for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) {
563
246k
                VECTOR(*res)[IGRAPH_TO(graph, edge)] += VECTOR(*weights)[edge];
564
246k
            }
565
8.76k
        }
566
20.0k
    } else {
567
7.07k
        if (mode & IGRAPH_OUT) {
568
141k
            for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) {
569
135k
                igraph_integer_t from = IGRAPH_FROM(graph, edge);
570
135k
                if (from != IGRAPH_TO(graph, edge)) {
571
124k
                   VECTOR(*res)[from] += VECTOR(*weights)[edge];
572
124k
                }
573
135k
            }
574
5.47k
        }
575
7.07k
        if (mode & IGRAPH_IN) {
576
141k
            for (igraph_integer_t edge = 0; edge < no_of_edges; ++edge) {
577
135k
                igraph_integer_t to = IGRAPH_TO(graph, edge);
578
135k
                if (IGRAPH_FROM(graph, edge) != to) {
579
124k
                    VECTOR(*res)[to] += VECTOR(*weights)[edge];
580
124k
                }
581
135k
            }
582
5.47k
        }
583
7.07k
    }
584
585
27.0k
    return IGRAPH_SUCCESS;
586
27.0k
}
587
588
/**
589
 * \function igraph_strength
590
 * \brief Strength of the vertices, also called weighted vertex degree.
591
 *
592
 * In a weighted network the strength of a vertex is the sum of the
593
 * weights of all incident edges. In a non-weighted network this is
594
 * exactly the vertex degree.
595
 *
596
 * \param graph The input graph.
597
 * \param res Pointer to an initialized vector, the result is stored
598
 *   here. It will be resized as needed.
599
 * \param vids The vertices for which the calculation is performed.
600
 * \param mode Gives whether to count only outgoing (\c IGRAPH_OUT),
601
 *   incoming (\c IGRAPH_IN) edges or both (\c IGRAPH_ALL).
602
 *   This parameter is ignored for undirected graphs.
603
 * \param loops Constant of type \ref igraph_loops_t. Specifies how to treat
604
 *   loop edges when calculating the strength. \c IGRAPH_NO_LOOPS ignores loop
605
 *   edges; \c IGRAPH_LOOPS_ONCE counts each loop edge only once;
606
 *   \c IGRAPH_LOOPS_TWICE counts each loop edge twice in undirected graphs and
607
 *   once in directed graphs.
608
 * \param weights A vector giving the edge weights. If this is a \c NULL
609
 *   pointer, then \ref igraph_degree() is called to perform the
610
 *   calculation.
611
 * \return Error code.
612
 *
613
 * Time complexity: O(|V|+|E|), linear in the number vertices and
614
 * edges.
615
 *
616
 * \sa \ref igraph_degree() for the traditional, non-weighted version.
617
 */
618
igraph_error_t igraph_strength(
619
    const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids,
620
    igraph_neimode_t mode, igraph_loops_t loops, const igraph_vector_t *weights
621
43.2k
) {
622
623
43.2k
    igraph_integer_t no_of_nodes = igraph_vcount(graph);
624
43.2k
    igraph_vit_t vit;
625
43.2k
    igraph_integer_t no_vids;
626
43.2k
    igraph_vector_int_t degrees;
627
43.2k
    igraph_vector_int_t neis;
628
629
43.2k
    if (! weights) {
630
16.1k
        IGRAPH_VECTOR_INT_INIT_FINALLY(&degrees, no_of_nodes);
631
16.1k
        IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
632
16.1k
        IGRAPH_CHECK(igraph_degree(graph, &degrees, vids, mode, loops));
633
1.44M
        for (igraph_integer_t i = 0; i < no_of_nodes; i++) {
634
1.42M
            VECTOR(*res)[i] = VECTOR(degrees)[i];
635
1.42M
        }
636
16.1k
        igraph_vector_int_destroy(&degrees);
637
16.1k
        IGRAPH_FINALLY_CLEAN(1);
638
16.1k
        return IGRAPH_SUCCESS;
639
16.1k
    }
640
641
27.0k
    if (igraph_vector_size(weights) != igraph_ecount(graph)) {
642
0
        IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL);
643
0
    }
644
645
27.0k
    if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) {
646
0
        IGRAPH_ERROR("Invalid mode for vertex strength calculation.", IGRAPH_EINVMODE);
647
0
    }
648
649
27.0k
    if (igraph_vs_is_all(&vids)) {
650
27.0k
        return strength_all(graph, res, mode, loops, weights);
651
27.0k
    }
652
653
0
    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
654
0
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
655
0
    no_vids = IGRAPH_VIT_SIZE(vit);
656
657
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
658
0
    IGRAPH_CHECK(igraph_vector_int_reserve(&neis, no_of_nodes));
659
0
    IGRAPH_CHECK(igraph_vector_resize(res, no_vids));
660
0
    igraph_vector_null(res);
661
662
0
    for (igraph_integer_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
663
0
        IGRAPH_CHECK(igraph_incident(graph, &neis, IGRAPH_VIT_GET(vit), mode, loops));
664
0
        const igraph_integer_t n = igraph_vector_int_size(&neis);
665
0
        for (igraph_integer_t j = 0; j < n; j++) {
666
0
            igraph_integer_t edge = VECTOR(neis)[j];
667
0
            VECTOR(*res)[i] += VECTOR(*weights)[edge];
668
0
        }
669
0
    }
670
671
0
    igraph_vit_destroy(&vit);
672
0
    igraph_vector_int_destroy(&neis);
673
0
    IGRAPH_FINALLY_CLEAN(2);
674
675
0
    return IGRAPH_SUCCESS;
676
0
}
677
678
679
/**
680
 * \function igraph_sort_vertex_ids_by_degree
681
 * \brief Calculate a list of vertex IDs sorted by degree of the corresponding vertex.
682
 *
683
 * The list of vertex IDs is returned in a vector that is sorted
684
 * in ascending or descending order of vertex degree.
685
 *
686
 * \param graph The input graph.
687
 * \param outvids Pointer to an initialized vector that will be
688
 *        resized and will contain the ordered vertex IDs.
689
 * \param vids Input vertex selector of vertex IDs to include in
690
 *        calculation.
691
 * \param mode Defines the type of the degree.
692
 *        \c IGRAPH_OUT, out-degree,
693
 *        \c IGRAPH_IN, in-degree,
694
 *        \c IGRAPH_ALL, total degree (sum of the
695
 *        in- and out-degree).
696
 *        This parameter is ignored for undirected graphs.
697
 * \param loops Specifies how to treat loop edges when calculating the
698
 *        degrees. \c IGRAPH_NO_LOOPS ignores loop edges; \c IGRAPH_LOOPS_ONCE
699
 *        counts each loop edge only once; \c IGRAPH_LOOPS_TWICE counts each
700
 *        loop edge twice in undirected graphs and once in directed graphs.
701
 * \param order Specifies whether the ordering should be ascending
702
 *        (\c IGRAPH_ASCENDING) or descending (\c IGRAPH_DESCENDING).
703
 * \param only_indices If true, then return a sorted list of indices
704
 *        into a vector corresponding to \c vids, rather than a list
705
 *        of vertex IDs. This parameter is ignored if \c vids is set
706
 *        to all vertices via \ref igraph_vs_all() or \ref igraph_vss_all(),
707
 *        because in this case the indices and vertex IDs are the
708
 *        same.
709
 * \return Error code:
710
 *         \c IGRAPH_EINVVID: invalid vertex ID.
711
 *         \c IGRAPH_EINVMODE: invalid mode argument.
712
 *
713
 */
714
igraph_error_t igraph_sort_vertex_ids_by_degree(
715
    const igraph_t *graph, igraph_vector_int_t *outvids,
716
    igraph_vs_t vids, igraph_neimode_t mode, igraph_loops_t loops,
717
    igraph_order_t order, igraph_bool_t only_indices
718
0
) {
719
0
    igraph_integer_t i, n;
720
0
    igraph_vector_int_t degrees;
721
0
    igraph_vector_int_t vs_vec;
722
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&degrees, 0);
723
0
    IGRAPH_CHECK(igraph_degree(graph, &degrees, vids, mode, loops));
724
0
    IGRAPH_CHECK(igraph_vector_int_sort_ind(&degrees, outvids, order));
725
0
    if (only_indices || igraph_vs_is_all(&vids) ) {
726
0
        igraph_vector_int_destroy(&degrees);
727
0
        IGRAPH_FINALLY_CLEAN(1);
728
0
    } else {
729
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&vs_vec, 0);
730
0
        IGRAPH_CHECK(igraph_vs_as_vector(graph, vids, &vs_vec));
731
0
        n = igraph_vector_int_size(outvids);
732
0
        for (i = 0; i < n; i++) {
733
0
            VECTOR(*outvids)[i] = VECTOR(vs_vec)[VECTOR(*outvids)[i]];
734
0
        }
735
0
        igraph_vector_int_destroy(&vs_vec);
736
0
        igraph_vector_int_destroy(&degrees);
737
0
        IGRAPH_FINALLY_CLEAN(2);
738
0
    }
739
0
    return IGRAPH_SUCCESS;
740
0
}