Coverage Report

Created: 2025-11-14 06:33

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/igraph/src/properties/degrees.c
Line
Count
Source
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_int_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_int_t *res, igraph_vs_t vids,
58
    igraph_neimode_t mode, igraph_loops_t loops
59
0
) {
60
61
0
    igraph_vector_int_t tmp;
62
63
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&tmp, 0);
64
65
0
    IGRAPH_CHECK(igraph_degree(graph, &tmp, vids, mode, loops));
66
0
    if (igraph_vector_int_size(&tmp) == 0) {
67
0
        *res = 0;
68
0
    } else {
69
0
        *res = igraph_vector_int_max(&tmp);
70
0
    }
71
72
0
    igraph_vector_int_destroy(&tmp);
73
0
    IGRAPH_FINALLY_CLEAN(1);
74
75
0
    return IGRAPH_SUCCESS;
76
0
}
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
0
        const igraph_vector_t *weights) {
85
86
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
87
0
    igraph_vector_int_t neis, edge_neis;
88
0
    igraph_int_t no_vids;
89
0
    igraph_vit_t vit;
90
0
    igraph_vector_t my_knn_v, *my_knn = knn;
91
0
    igraph_vector_t strength;
92
0
    igraph_vector_int_t deg;
93
0
    igraph_int_t maxdeg;
94
0
    igraph_vector_t deghist;
95
96
0
    if (igraph_vector_size(weights) != igraph_ecount(graph)) {
97
0
        IGRAPH_ERROR("Invalid weight vector size.", IGRAPH_EINVAL);
98
0
    }
99
100
0
    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
101
0
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
102
0
    no_vids = IGRAPH_VIT_SIZE(vit);
103
104
0
    if (!knn) {
105
0
        IGRAPH_VECTOR_INIT_FINALLY(&my_knn_v, no_vids);
106
0
        my_knn = &my_knn_v;
107
0
    } else {
108
0
        IGRAPH_CHECK(igraph_vector_resize(knn, no_vids));
109
0
    }
110
111
    /* Get degree of neighbours */
112
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&deg, no_of_nodes);
113
0
    IGRAPH_CHECK(igraph_degree(graph, &deg, igraph_vss_all(),
114
0
                               neighbor_degree_mode, IGRAPH_LOOPS));
115
0
    IGRAPH_VECTOR_INIT_FINALLY(&strength, no_of_nodes);
116
117
    /* Get strength of all nodes */
118
0
    IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(),
119
0
                                 mode, IGRAPH_LOOPS, weights));
120
121
    /* Get maximum degree for initialization */
122
0
    IGRAPH_CHECK(igraph_maxdegree(graph, &maxdeg, igraph_vss_all(),
123
0
                                  mode, IGRAPH_LOOPS));
124
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, maxdeg);
125
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&edge_neis, maxdeg);
126
0
    igraph_vector_int_clear(&neis);
127
0
    igraph_vector_int_clear(&edge_neis);
128
129
0
    if (knnk) {
130
0
        IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg));
131
0
        igraph_vector_null(knnk);
132
0
        IGRAPH_VECTOR_INIT_FINALLY(&deghist, maxdeg);
133
0
    }
134
135
0
    for (igraph_int_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
136
0
        igraph_real_t sum = 0.0;
137
0
        igraph_int_t v = IGRAPH_VIT_GET(vit);
138
0
        igraph_int_t nv;
139
0
        igraph_real_t str = VECTOR(strength)[v];
140
        /* Get neighbours and incident edges */
141
0
        IGRAPH_CHECK(igraph_neighbors(graph, &neis, v, mode, IGRAPH_LOOPS, IGRAPH_MULTIPLE));
142
0
        IGRAPH_CHECK(igraph_incident(graph, &edge_neis, v, mode, IGRAPH_LOOPS));
143
0
        nv = igraph_vector_int_size(&neis);
144
0
        for (igraph_int_t j = 0; j < nv; j++) {
145
0
            igraph_int_t nei = VECTOR(neis)[j];
146
0
            igraph_int_t e = VECTOR(edge_neis)[j];
147
0
            igraph_real_t w = VECTOR(*weights)[e];
148
0
            sum += w * VECTOR(deg)[nei];
149
0
        }
150
0
        if (str != 0.0) {
151
0
            VECTOR(*my_knn)[i] = sum / str;
152
0
        } else {
153
0
            VECTOR(*my_knn)[i] = IGRAPH_NAN;
154
0
        }
155
0
        if (knnk && nv > 0) {
156
0
            VECTOR(*knnk)[nv - 1] += sum;
157
0
            VECTOR(deghist)[nv - 1] += str;
158
0
        }
159
0
    }
160
161
0
    igraph_vector_int_destroy(&edge_neis);
162
0
    igraph_vector_int_destroy(&neis);
163
0
    IGRAPH_FINALLY_CLEAN(2);
164
165
0
    if (knnk) {
166
0
        for (igraph_int_t i = 0; i < maxdeg; i++) {
167
0
            igraph_real_t dh = VECTOR(deghist)[i];
168
0
            if (dh != 0) {
169
0
                VECTOR(*knnk)[i] /= dh;
170
0
            } else {
171
0
                VECTOR(*knnk)[i] = IGRAPH_NAN;
172
0
            }
173
0
        }
174
175
0
        igraph_vector_destroy(&deghist);
176
0
        IGRAPH_FINALLY_CLEAN(1);
177
0
    }
178
179
0
    igraph_vector_destroy(&strength);
180
0
    igraph_vector_int_destroy(&deg);
181
0
    IGRAPH_FINALLY_CLEAN(2);
182
183
0
    if (!knn) {
184
0
        igraph_vector_destroy(&my_knn_v);
185
0
        IGRAPH_FINALLY_CLEAN(1);
186
0
    }
187
188
0
    igraph_vit_destroy(&vit);
189
0
    IGRAPH_FINALLY_CLEAN(1);
190
191
0
    return IGRAPH_SUCCESS;
192
0
}
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
0
                                       const igraph_vector_t *weights) {
270
271
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
272
0
    igraph_vector_int_t neis;
273
0
    igraph_int_t no_vids;
274
0
    igraph_vit_t vit;
275
0
    igraph_vector_t my_knn_v, *my_knn = knn;
276
0
    igraph_vector_int_t deg;
277
0
    igraph_int_t maxdeg;
278
0
    igraph_vector_int_t deghist;
279
280
0
    if (weights) {
281
0
        return avg_nearest_neighbor_degree_weighted(graph, vids,
282
0
                mode, neighbor_degree_mode, knn, knnk, weights);
283
0
    }
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_int_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
310
0
        igraph_real_t sum = 0.0;
311
0
        igraph_int_t v = IGRAPH_VIT_GET(vit);
312
0
        igraph_int_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_int_t j = 0; j < nv; j++) {
316
0
            igraph_int_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_int_t i = 0; i < maxdeg; i++) {
332
0
            igraph_int_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
 * Computes the degree correlation function <code>k_nn(k)</code>, defined as the
361
 * mean degree of the targets of directed edges whose source has degree \c k.
362
 * The averaging is done over all directed edges. The \p from_mode and \p to_mode
363
 * parameters control how the source and target vertex degrees are computed.
364
 * This way the out-in, out-out, in-in and in-out degree correlation functions
365
 * can all be computed.
366
 *
367
 * </para><para>
368
 * In undirected graphs, edges are treated as if they were a pair of reciprocal directed
369
 * ones.
370
 *
371
 * </para><para>
372
 * If P_ij is the joint degree distribution of the graph, computable with
373
 * \ref igraph_joint_degree_distribution(), then
374
 * <code>k_nn(k) = (sum_j j P_kj) / (sum_j P_kj)</code>.
375
 *
376
 * </para><para>
377
 * The function \ref igraph_avg_nearest_neighbor_degree(), whose main purpose is to
378
 * calculate the average neighbor degree for each vertex separately, can also compute
379
 * <code>k_nn(k)</code>. It differs from this function in that it can take a subset
380
 * of vertices to base the calculation on, but it does not allow the same fine-grained
381
 * control over how degrees are computed.
382
 *
383
 * </para><para>
384
 * References:
385
 *
386
 * </para><para>
387
 * R. Pastor-Satorras, A. Vazquez, A. Vespignani:
388
 * Dynamical and Correlation Properties of the Internet,
389
 * Phys. Rev. Lett., vol. 87, pp. 258701 (2001).
390
 * https://doi.org/10.1103/PhysRevLett.87.258701
391
 *
392
 * </para><para>
393
 * A. Vazquez, R. Pastor-Satorras, A. Vespignani:
394
 * Large-scale topological and dynamical properties of the Internet,
395
 * Phys. Rev. E, vol. 65, pp. 066130 (2002).
396
 * https://doi.org/10.1103/PhysRevE.65.066130
397
 *
398
 * </para><para>
399
 * A. Barrat, M. Barthélemy, R. Pastor-Satorras, and A. Vespignani,
400
 * The architecture of complex weighted networks,
401
 * Proc. Natl. Acad. Sci. USA 101, 3747 (2004).
402
 * https://dx.doi.org/10.1073/pnas.0400087101
403
 *
404
 * \param graph The input graph.
405
 * \param weights An optional weight vector. If not \c NULL, weighted averages will be computed.
406
 * \param knnk An initialized vector, the result will be written here.
407
 *    <code>knnk[d]</code> will contain the mean degree of vertices connected to
408
 *    by vertices of degree \c d. Note that in contrast to
409
 *    \ref igraph_avg_nearest_neighbor_degree(), <code>d=0</code> is also
410
 *    included.
411
 * \param from_mode How to compute the degree of sources? Can be \c IGRAPH_OUT
412
 *    for out-degree, \c IGRAPH_IN for in-degree, or \c IGRAPH_ALL for total degree.
413
 *    Ignored in undirected graphs.
414
 * \param to_mode How to compute the degree of sources? Can be \c IGRAPH_OUT
415
 *    for out-degree, \c IGRAPH_IN for in-degree, or \c IGRAPH_ALL for total degree.
416
 *    Ignored in undirected graphs.
417
 * \param directed_neighbors Whether to consider <code>u -> v</code> connections
418
 *    to be directed. Undirected connections are treated as reciprocal directed ones,
419
 *    i.e. both <code>u -> v</code> and <code>v -> u</code> will be considered.
420
 *    Ignored in undirected graphs.
421
 * \return Error code.
422
 *
423
 * \sa \ref igraph_avg_nearest_neighbor_degree() for computing the average neighbour
424
 * degree of a set of vertices, \ref igraph_joint_degree_distribution() to get the
425
 * complete joint degree distribution, and \ref igraph_assortativity_degree()
426
 * to compute the degree assortativity.
427
 *
428
 * Time complexity: O(|E| + |V|)
429
 */
430
igraph_error_t igraph_degree_correlation_vector(
431
        const igraph_t *graph, const igraph_vector_t *weights,
432
        igraph_vector_t *knnk,
433
        igraph_neimode_t from_mode, igraph_neimode_t to_mode,
434
0
        igraph_bool_t directed_neighbors) {
435
436
0
    igraph_int_t no_of_nodes = igraph_vcount(graph);
437
0
    igraph_int_t no_of_edges = igraph_ecount(graph);
438
0
    igraph_int_t maxdeg;
439
0
    igraph_vector_t weight_sums;
440
0
    igraph_vector_int_t *deg_from, *deg_to, deg_out, deg_in, deg_all;
441
442
0
    if (weights && igraph_vector_size(weights) != no_of_edges) {
443
0
        IGRAPH_ERRORF("Weight vector length (%" IGRAPH_PRId ") does not match number of edges (%" IGRAPH_PRId ").",
444
0
                      IGRAPH_EINVAL,
445
0
                      igraph_vector_size(weights), no_of_edges);
446
0
    }
447
448
0
    if (! igraph_is_directed(graph)) {
449
0
        from_mode = to_mode = IGRAPH_ALL;
450
0
        directed_neighbors = false;
451
0
    }
452
453
0
    igraph_bool_t have_out = from_mode == IGRAPH_OUT || to_mode == IGRAPH_OUT;
454
0
    igraph_bool_t have_in  = from_mode == IGRAPH_IN  || to_mode == IGRAPH_IN;
455
0
    igraph_bool_t have_all = from_mode == IGRAPH_ALL || to_mode == IGRAPH_ALL;
456
457
0
    if (have_out) {
458
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&deg_out, no_of_nodes);
459
0
        IGRAPH_CHECK(igraph_degree(graph, &deg_out, igraph_vss_all(), IGRAPH_OUT, /* loops */ true));
460
0
    }
461
462
0
    if (have_in) {
463
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&deg_in, no_of_nodes);
464
0
        IGRAPH_CHECK(igraph_degree(graph, &deg_in, igraph_vss_all(), IGRAPH_IN, /* loops */ true));
465
0
    }
466
467
0
    if (have_all) {
468
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&deg_all, no_of_nodes);
469
0
        IGRAPH_CHECK(igraph_degree(graph, &deg_all, igraph_vss_all(), IGRAPH_ALL, /* loops */ true));
470
0
    }
471
472
0
    switch (from_mode) {
473
0
    case IGRAPH_OUT: deg_from = &deg_out; break;
474
0
    case IGRAPH_IN:  deg_from = &deg_in;  break;
475
0
    case IGRAPH_ALL: deg_from = &deg_all; break;
476
0
    default:
477
0
        IGRAPH_ERROR("Invalid 'from' mode.", IGRAPH_EINVMODE);
478
0
    }
479
480
0
    switch (to_mode) {
481
0
    case IGRAPH_OUT: deg_to = &deg_out; break;
482
0
    case IGRAPH_IN:  deg_to = &deg_in;  break;
483
0
    case IGRAPH_ALL: deg_to = &deg_all; break;
484
0
    default:
485
0
        IGRAPH_ERROR("Invalid 'to' mode.", IGRAPH_EINVMODE);
486
0
    }
487
488
0
    maxdeg = no_of_edges > 0 ? igraph_vector_int_max(deg_from) : 0;
489
490
0
    IGRAPH_VECTOR_INIT_FINALLY(&weight_sums, maxdeg+1);
491
492
0
    IGRAPH_CHECK(igraph_vector_resize(knnk, maxdeg+1));
493
0
    igraph_vector_null(knnk);
494
495
0
    for (igraph_int_t eid=0; eid < no_of_edges; eid++) {
496
0
        igraph_int_t from = IGRAPH_FROM(graph, eid);
497
0
        igraph_int_t to   = IGRAPH_TO(graph, eid);
498
0
        igraph_int_t fromdeg = VECTOR(*deg_from)[from];
499
0
        igraph_int_t todeg   = VECTOR(*deg_to)[to];
500
0
        igraph_real_t w = weights ? VECTOR(*weights)[eid] : 1;
501
502
0
        VECTOR(weight_sums)[fromdeg] += w;
503
0
        VECTOR(*knnk)[fromdeg] += w * todeg;
504
505
        /* Treat undirected edges as reciprocal directed ones */
506
0
        if (! directed_neighbors) {
507
0
            VECTOR(weight_sums)[todeg] += w;
508
0
            VECTOR(*knnk)[todeg] += w * fromdeg;
509
0
        }
510
0
    }
511
512
0
    IGRAPH_CHECK(igraph_vector_div(knnk, &weight_sums));
513
514
0
    igraph_vector_destroy(&weight_sums);
515
0
    IGRAPH_FINALLY_CLEAN(1);
516
517
    /* In reverse order of initialization: */
518
519
0
    if (have_all) {
520
0
        igraph_vector_int_destroy(&deg_all);
521
0
        IGRAPH_FINALLY_CLEAN(1);
522
0
    }
523
524
0
    if (have_in) {
525
0
        igraph_vector_int_destroy(&deg_in);
526
0
        IGRAPH_FINALLY_CLEAN(1);
527
0
    }
528
529
0
    if (have_out) {
530
0
        igraph_vector_int_destroy(&deg_out);
531
0
        IGRAPH_FINALLY_CLEAN(1);
532
0
    }
533
534
0
    return IGRAPH_SUCCESS;
535
0
}
536
537
static igraph_error_t strength_all(
538
        const igraph_t *graph, igraph_vector_t *res,
539
        igraph_neimode_t mode, igraph_bool_t loops,
540
1.44k
        const igraph_vector_t *weights) {
541
542
    // When calculating strength for all vertices, iterating over edges is faster
543
1.44k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
544
1.44k
    igraph_int_t no_of_edges = igraph_ecount(graph);
545
546
1.44k
    IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
547
1.44k
    igraph_vector_null(res);
548
549
1.44k
    if (!igraph_is_directed(graph)) {
550
723
        mode = IGRAPH_ALL;
551
723
    }
552
553
1.44k
    if (loops) {
554
723
        if (mode & IGRAPH_OUT) {
555
9.46k
            for (igraph_int_t edge = 0; edge < no_of_edges; ++edge) {
556
8.74k
                VECTOR(*res)[IGRAPH_FROM(graph, edge)] += VECTOR(*weights)[edge];
557
8.74k
            }
558
723
        }
559
723
        if (mode & IGRAPH_IN) {
560
9.46k
            for (igraph_int_t edge = 0; edge < no_of_edges; ++edge) {
561
8.74k
                VECTOR(*res)[IGRAPH_TO(graph, edge)] += VECTOR(*weights)[edge];
562
8.74k
            }
563
723
        }
564
724
    } else {
565
724
        if (mode & IGRAPH_OUT) {
566
17.5k
            for (igraph_int_t edge = 0; edge < no_of_edges; ++edge) {
567
16.8k
                igraph_int_t from = IGRAPH_FROM(graph, edge);
568
16.8k
                if (from != IGRAPH_TO(graph, edge)) {
569
13.8k
                   VECTOR(*res)[from] += VECTOR(*weights)[edge];
570
13.8k
                }
571
16.8k
            }
572
724
        }
573
724
        if (mode & IGRAPH_IN) {
574
17.5k
            for (igraph_int_t edge = 0; edge < no_of_edges; ++edge) {
575
16.8k
                igraph_int_t to = IGRAPH_TO(graph, edge);
576
16.8k
                if (IGRAPH_FROM(graph, edge) != to) {
577
13.8k
                    VECTOR(*res)[to] += VECTOR(*weights)[edge];
578
13.8k
                }
579
16.8k
            }
580
724
        }
581
724
    }
582
583
1.44k
    return IGRAPH_SUCCESS;
584
1.44k
}
585
586
/**
587
 * \function igraph_strength
588
 * \brief Strength of the vertices, also called weighted vertex degree.
589
 *
590
 * In a weighted network the strength of a vertex is the sum of the
591
 * weights of all incident edges. In a non-weighted network this is
592
 * exactly the vertex degree.
593
 *
594
 * \param graph The input graph.
595
 * \param res Pointer to an initialized vector, the result is stored
596
 *   here. It will be resized as needed.
597
 * \param vids The vertices for which the calculation is performed.
598
 * \param mode Gives whether to count only outgoing (\c IGRAPH_OUT),
599
 *   incoming (\c IGRAPH_IN) edges or both (\c IGRAPH_ALL).
600
 *   This parameter is ignored for undirected graphs.
601
 * \param loops Constant of type \ref igraph_loops_t. Specifies how to treat
602
 *   loop edges when calculating the strength. \c IGRAPH_NO_LOOPS ignores loop
603
 *   edges; \c IGRAPH_LOOPS_ONCE counts each loop edge only once;
604
 *   \c IGRAPH_LOOPS_TWICE counts each loop edge twice in undirected graphs and
605
 *   once in directed graphs.
606
 * \param weights A vector giving the edge weights. If this is a \c NULL
607
 *   pointer, then \ref igraph_degree() is called to perform the
608
 *   calculation.
609
 * \return Error code.
610
 *
611
 * Time complexity: O(|V|+|E|), linear in the number vertices and
612
 * edges.
613
 *
614
 * \sa \ref igraph_degree() for the traditional, non-weighted version.
615
 */
616
igraph_error_t igraph_strength(
617
    const igraph_t *graph, igraph_vector_t *res, const igraph_vs_t vids,
618
    igraph_neimode_t mode, igraph_loops_t loops, const igraph_vector_t *weights
619
1.44k
) {
620
621
1.44k
    igraph_int_t no_of_nodes = igraph_vcount(graph);
622
1.44k
    igraph_vit_t vit;
623
1.44k
    igraph_int_t no_vids;
624
1.44k
    igraph_vector_int_t degrees;
625
1.44k
    igraph_vector_int_t neis;
626
627
1.44k
    if (! weights) {
628
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&degrees, no_of_nodes);
629
0
        IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
630
0
        IGRAPH_CHECK(igraph_degree(graph, &degrees, vids, mode, loops));
631
0
        for (igraph_int_t i = 0; i < no_of_nodes; i++) {
632
0
            VECTOR(*res)[i] = VECTOR(degrees)[i];
633
0
        }
634
0
        igraph_vector_int_destroy(&degrees);
635
0
        IGRAPH_FINALLY_CLEAN(1);
636
0
        return IGRAPH_SUCCESS;
637
0
    }
638
639
1.44k
    if (igraph_vector_size(weights) != igraph_ecount(graph)) {
640
0
        IGRAPH_ERROR("Invalid weight vector length.", IGRAPH_EINVAL);
641
0
    }
642
643
1.44k
    if (mode != IGRAPH_OUT && mode != IGRAPH_IN && mode != IGRAPH_ALL) {
644
0
        IGRAPH_ERROR("Invalid mode for vertex strength calculation.", IGRAPH_EINVMODE);
645
0
    }
646
647
1.44k
    if (igraph_vs_is_all(&vids)) {
648
1.44k
        return strength_all(graph, res, mode, loops, weights);
649
1.44k
    }
650
651
0
    IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
652
0
    IGRAPH_FINALLY(igraph_vit_destroy, &vit);
653
0
    no_vids = IGRAPH_VIT_SIZE(vit);
654
655
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&neis, 0);
656
0
    IGRAPH_CHECK(igraph_vector_int_reserve(&neis, no_of_nodes));
657
0
    IGRAPH_CHECK(igraph_vector_resize(res, no_vids));
658
0
    igraph_vector_null(res);
659
660
0
    for (igraph_int_t i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
661
0
        IGRAPH_CHECK(igraph_incident(graph, &neis, IGRAPH_VIT_GET(vit), mode, loops));
662
0
        const igraph_int_t n = igraph_vector_int_size(&neis);
663
0
        for (igraph_int_t j = 0; j < n; j++) {
664
0
            igraph_int_t edge = VECTOR(neis)[j];
665
0
            VECTOR(*res)[i] += VECTOR(*weights)[edge];
666
0
        }
667
0
    }
668
669
0
    igraph_vit_destroy(&vit);
670
0
    igraph_vector_int_destroy(&neis);
671
0
    IGRAPH_FINALLY_CLEAN(2);
672
673
0
    return IGRAPH_SUCCESS;
674
0
}
675
676
677
/**
678
 * \function igraph_sort_vertex_ids_by_degree
679
 * \brief Calculate a list of vertex IDs sorted by degree of the corresponding vertex.
680
 *
681
 * The list of vertex IDs is returned in a vector that is sorted
682
 * in ascending or descending order of vertex degree.
683
 *
684
 * \param graph The input graph.
685
 * \param outvids Pointer to an initialized vector that will be
686
 *        resized and will contain the ordered vertex IDs.
687
 * \param vids Input vertex selector of vertex IDs to include in
688
 *        calculation.
689
 * \param mode Defines the type of the degree.
690
 *        \c IGRAPH_OUT, out-degree,
691
 *        \c IGRAPH_IN, in-degree,
692
 *        \c IGRAPH_ALL, total degree (sum of the
693
 *        in- and out-degree).
694
 *        This parameter is ignored for undirected graphs.
695
 * \param loops Specifies how to treat loop edges when calculating the
696
 *        degrees. \c IGRAPH_NO_LOOPS ignores loop edges; \c IGRAPH_LOOPS_ONCE
697
 *        counts each loop edge only once; \c IGRAPH_LOOPS_TWICE counts each
698
 *        loop edge twice in undirected graphs and once in directed graphs.
699
 * \param order Specifies whether the ordering should be ascending
700
 *        (\c IGRAPH_ASCENDING) or descending (\c IGRAPH_DESCENDING).
701
 * \param only_indices If true, then return a sorted list of indices
702
 *        into a vector corresponding to \c vids, rather than a list
703
 *        of vertex IDs. This parameter is ignored if \c vids is set
704
 *        to all vertices via \ref igraph_vs_all() or \ref igraph_vss_all(),
705
 *        because in this case the indices and vertex IDs are the
706
 *        same.
707
 * \return Error code:
708
 *         \c IGRAPH_EINVVID: invalid vertex ID.
709
 *         \c IGRAPH_EINVMODE: invalid mode argument.
710
 *
711
 */
712
igraph_error_t igraph_sort_vertex_ids_by_degree(
713
    const igraph_t *graph, igraph_vector_int_t *outvids,
714
    igraph_vs_t vids, igraph_neimode_t mode, igraph_loops_t loops,
715
    igraph_order_t order, igraph_bool_t only_indices
716
0
) {
717
0
    igraph_int_t i, n;
718
0
    igraph_vector_int_t degrees;
719
0
    igraph_vector_int_t vs_vec;
720
0
    IGRAPH_VECTOR_INT_INIT_FINALLY(&degrees, 0);
721
0
    IGRAPH_CHECK(igraph_degree(graph, &degrees, vids, mode, loops));
722
0
    IGRAPH_CHECK(igraph_vector_int_sort_ind(&degrees, outvids, order));
723
0
    if (only_indices || igraph_vs_is_all(&vids) ) {
724
0
        igraph_vector_int_destroy(&degrees);
725
0
        IGRAPH_FINALLY_CLEAN(1);
726
0
    } else {
727
0
        IGRAPH_VECTOR_INT_INIT_FINALLY(&vs_vec, 0);
728
0
        IGRAPH_CHECK(igraph_vs_as_vector(graph, vids, &vs_vec));
729
0
        n = igraph_vector_int_size(outvids);
730
0
        for (i = 0; i < n; i++) {
731
0
            VECTOR(*outvids)[i] = VECTOR(vs_vec)[VECTOR(*outvids)[i]];
732
0
        }
733
0
        igraph_vector_int_destroy(&vs_vec);
734
0
        igraph_vector_int_destroy(&degrees);
735
0
        IGRAPH_FINALLY_CLEAN(2);
736
0
    }
737
0
    return IGRAPH_SUCCESS;
738
0
}