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(&deg, no_of_nodes);
114     IGRAPH_CHECK(igraph_degree(graph, &deg, 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(&deghist, (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(&deghist);
177         IGRAPH_FINALLY_CLEAN(1);
178     }
179 
180     igraph_vector_destroy(&strength);
181     igraph_vector_destroy(&deg);
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(&deg, no_of_nodes);
294     IGRAPH_CHECK(igraph_degree(graph, &deg, 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(&deghist, (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(&deghist);
337         IGRAPH_FINALLY_CLEAN(1);
338     }
339 
340     igraph_vector_destroy(&neis);
341     igraph_vector_destroy(&deg);
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(&degrees, 0);
483     IGRAPH_CHECK(igraph_degree(graph, &degrees, vids, mode, loops));
484     IGRAPH_CHECK((int) igraph_vector_qsort_ind(&degrees, outvids,
485                  order == IGRAPH_DESCENDING));
486     if (only_indices || igraph_vs_is_all(&vids) ) {
487         igraph_vector_destroy(&degrees);
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(&degrees);
497         IGRAPH_FINALLY_CLEAN(2);
498     }
499     return 0;
500 }
501