1 /* -*- mode: C -*-  */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /*
4    IGraph library.
5    Copyright (C) 2005-2012  Gabor Csardi <csardi.gabor@gmail.com>
6    334 Harvard street, Cambridge, MA 02139 USA
7 
8    This program is free software; you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation; either version 2 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program; if not, write to the Free Software
20    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21    02110-1301 USA
22 
23 */
24 
25 #include "igraph_transitivity.h"
26 
27 #include "igraph_interface.h"
28 #include "igraph_adjlist.h"
29 #include "igraph_memory.h"
30 #include "igraph_motifs.h"
31 #include "igraph_structural.h"
32 
33 #include "core/interruption.h"
34 #include "properties/properties_internal.h"
35 
36 /**
37  * \function igraph_transitivity_avglocal_undirected
38  * \brief Average local transitivity (clustering coefficient).
39  *
40  * The transitivity measures the probability that two neighbors of a
41  * vertex are connected. In case of the average local transitivity,
42  * this probability is calculated for each vertex and then the average
43  * is taken. Vertices with less than two neighbors require special treatment,
44  * they will either be left out from the calculation or they will be considered
45  * as having zero transitivity, depending on the \c mode argument.
46  * Edge directions and edge multiplicities are ignored.
47  *
48  * </para><para>
49  * Note that this measure is different from the global transitivity measure
50  * (see \ref igraph_transitivity_undirected() ) as it simply takes the
51  * average local transitivity across the whole network.
52  *
53  * </para><para>
54  * Clustering coefficient is an alternative name for transitivity.
55  *
56  * </para><para>
57  * References:
58  *
59  * </para><para>
60  * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks.
61  * Nature 393(6684):440-442 (1998).
62  *
63  * \param graph The input graph. Edge directions and multiplicites are ignored.
64  * \param res Pointer to a real variable, the result will be stored here.
65  * \param mode Defines how to treat vertices with degree less than two.
66  *    \c IGRAPH_TRANSITIVITY_NAN leaves them out from averaging,
67  *    \c IGRAPH_TRANSITIVITY_ZERO includes them with zero transitivity.
68  *    The result will be \c NaN if the mode is \c IGRAPH_TRANSITIVITY_NAN
69  *    and there are no vertices with more than one neighbor.
70  *
71  * \return Error code.
72  *
73  * \sa \ref igraph_transitivity_undirected(), \ref
74  * igraph_transitivity_local_undirected().
75  *
76  * Time complexity: O(|V|*d^2), |V| is the number of vertices in the
77  * graph and d is the average degree.
78  */
79 
igraph_transitivity_avglocal_undirected(const igraph_t * graph,igraph_real_t * res,igraph_transitivity_mode_t mode)80 int igraph_transitivity_avglocal_undirected(const igraph_t *graph,
81         igraph_real_t *res,
82         igraph_transitivity_mode_t mode) {
83 
84     igraph_integer_t i, no_of_nodes = igraph_vcount(graph), nans = 0;
85     igraph_real_t sum = 0.0;
86     igraph_vector_t vec;
87 
88     if (no_of_nodes == 0) {
89         if (mode == IGRAPH_TRANSITIVITY_ZERO) {
90             *res = 0;
91         } else {
92             *res = IGRAPH_NAN;
93         }
94     } else {
95         IGRAPH_VECTOR_INIT_FINALLY(&vec, no_of_nodes);
96 
97         IGRAPH_CHECK(igraph_transitivity_local_undirected(graph, &vec, igraph_vss_all(), mode));
98 
99         for (i = 0, nans = 0; i < no_of_nodes; i++) {
100             if (!igraph_is_nan(VECTOR(vec)[i])) {
101                 sum += VECTOR(vec)[i];
102             } else {
103                 nans++;
104             }
105         }
106 
107         igraph_vector_destroy(&vec);
108         IGRAPH_FINALLY_CLEAN(1);
109 
110         *res = sum / (no_of_nodes - nans);
111     }
112 
113     return IGRAPH_SUCCESS;
114 }
115 
igraph_transitivity_local_undirected1(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids,igraph_transitivity_mode_t mode)116 int igraph_transitivity_local_undirected1(const igraph_t *graph,
117         igraph_vector_t *res,
118         const igraph_vs_t vids,
119         igraph_transitivity_mode_t mode) {
120 
121 #define TRANSIT
122 #include "properties/triangles_template1.h"
123 #undef TRANSIT
124 
125     return IGRAPH_SUCCESS;
126 }
127 
igraph_transitivity_local_undirected2(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids,igraph_transitivity_mode_t mode)128 int igraph_transitivity_local_undirected2(const igraph_t *graph,
129         igraph_vector_t *res,
130         const igraph_vs_t vids,
131         igraph_transitivity_mode_t mode) {
132 
133     long int no_of_nodes = igraph_vcount(graph);
134     igraph_vit_t vit;
135     long int nodes_to_calc, affected_nodes;
136     long int maxdegree = 0;
137     long int i, j, k, nn;
138     igraph_lazy_adjlist_t adjlist;
139     igraph_vector_t indexv, avids, rank, order, triangles, degree;
140     long int *neis;
141 
142     IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
143     IGRAPH_FINALLY(igraph_vit_destroy, &vit);
144     nodes_to_calc = IGRAPH_VIT_SIZE(vit);
145 
146     IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE));
147     IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist);
148 
149     IGRAPH_VECTOR_INIT_FINALLY(&indexv, no_of_nodes);
150     IGRAPH_VECTOR_INIT_FINALLY(&avids, 0);
151     IGRAPH_CHECK(igraph_vector_reserve(&avids, nodes_to_calc));
152     k = 0;
153     for (i = 0; i < nodes_to_calc; IGRAPH_VIT_NEXT(vit), i++) {
154         long int v = IGRAPH_VIT_GET(vit);
155         igraph_vector_int_t *neis2;
156         long int neilen;
157         if (VECTOR(indexv)[v] == 0) {
158             VECTOR(indexv)[v] = k + 1; k++;
159             IGRAPH_CHECK(igraph_vector_push_back(&avids, v));
160         }
161 
162         neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v);
163         neilen = igraph_vector_int_size(neis2);
164         for (j = 0; j < neilen; j++) {
165             long int nei = (long int) VECTOR(*neis2)[j];
166             if (VECTOR(indexv)[nei] == 0) {
167                 VECTOR(indexv)[nei] = k + 1; k++;
168                 IGRAPH_CHECK(igraph_vector_push_back(&avids, nei));
169             }
170         }
171     }
172 
173     /* Degree, ordering, ranking */
174     affected_nodes = igraph_vector_size(&avids);
175     IGRAPH_VECTOR_INIT_FINALLY(&order, 0);
176     IGRAPH_VECTOR_INIT_FINALLY(&degree, affected_nodes);
177     for (i = 0; i < affected_nodes; i++) {
178         long int v = (long int) VECTOR(avids)[i];
179         igraph_vector_int_t *neis2;
180         long int deg;
181         neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) v);
182         VECTOR(degree)[i] = deg = igraph_vector_int_size(neis2);
183         if (deg > maxdegree) {
184             maxdegree = deg;
185         }
186     }
187     igraph_vector_order1(&degree, &order, maxdegree + 1);
188     igraph_vector_destroy(&degree);
189     IGRAPH_FINALLY_CLEAN(1);
190     IGRAPH_VECTOR_INIT_FINALLY(&rank, affected_nodes);
191     for (i = 0; i < affected_nodes; i++) {
192         VECTOR(rank)[ (long int) VECTOR(order)[i] ] = affected_nodes - i - 1;
193     }
194 
195     neis = IGRAPH_CALLOC(no_of_nodes, long int);
196     if (neis == 0) {
197         IGRAPH_ERROR("Insufficient memory for local transitivity calculation.", IGRAPH_ENOMEM);
198     }
199     IGRAPH_FINALLY(igraph_free, neis);
200 
201     IGRAPH_VECTOR_INIT_FINALLY(&triangles, affected_nodes);
202     for (nn = affected_nodes - 1; nn >= 0; nn--) {
203         long int node = (long int) VECTOR(avids) [ (long int) VECTOR(order)[nn] ];
204         igraph_vector_int_t *neis1, *neis2;
205         long int neilen1, neilen2;
206         long int nodeindex = (long int) VECTOR(indexv)[node];
207         long int noderank = (long int) VECTOR(rank) [nodeindex - 1];
208 
209         /*     fprintf(stderr, "node %li (indexv %li, rank %li)\n", node, */
210         /*      (long int)VECTOR(indexv)[node]-1, noderank); */
211 
212         IGRAPH_ALLOW_INTERRUPTION();
213 
214         neis1 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node);
215         neilen1 = igraph_vector_int_size(neis1);
216         for (i = 0; i < neilen1; i++) {
217             long int nei = (long int) VECTOR(*neis1)[i];
218             neis[nei] = node + 1;
219         }
220         for (i = 0; i < neilen1; i++) {
221             long int nei = (long int) VECTOR(*neis1)[i];
222             long int neiindex = (long int) VECTOR(indexv)[nei];
223             long int neirank = (long int) VECTOR(rank)[neiindex - 1];
224 
225             /*       fprintf(stderr, "  nei %li (indexv %li, rank %li)\n", nei, */
226             /*        neiindex, neirank); */
227             if (neirank > noderank) {
228                 neis2 = igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) nei);
229                 neilen2 = igraph_vector_int_size(neis2);
230                 for (j = 0; j < neilen2; j++) {
231                     long int nei2 = (long int) VECTOR(*neis2)[j];
232                     long int nei2index = (long int) VECTOR(indexv)[nei2];
233                     long int nei2rank = (long int) VECTOR(rank)[nei2index - 1];
234                     /*    fprintf(stderr, "    triple %li %li %li\n", node, nei, nei2); */
235                     if (nei2rank < neirank) {
236                         continue;
237                     }
238                     if (neis[nei2] == node + 1) {
239                         /*      fprintf(stderr, "    triangle\n"); */
240                         VECTOR(triangles) [ nei2index - 1 ] += 1;
241                         VECTOR(triangles) [ neiindex - 1 ] += 1;
242                         VECTOR(triangles) [ nodeindex - 1 ] += 1;
243                     }
244                 }
245             }
246         }
247     }
248 
249     /* Ok, for all affected vertices the number of triangles were counted */
250 
251     IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
252     IGRAPH_VIT_RESET(vit);
253     for (i = 0; i < nodes_to_calc; i++, IGRAPH_VIT_NEXT(vit)) {
254         long int node = IGRAPH_VIT_GET(vit);
255         long int idx = (long int) VECTOR(indexv)[node] - 1;
256         igraph_vector_int_t *neis2 =
257             igraph_lazy_adjlist_get(&adjlist, (igraph_integer_t) node);
258         long int deg = igraph_vector_int_size(neis2);
259         if (mode == IGRAPH_TRANSITIVITY_ZERO && deg < 2) {
260             VECTOR(*res)[i] = 0.0;
261         } else {
262             VECTOR(*res)[i] = VECTOR(triangles)[idx] / deg / (deg - 1) * 2.0;
263         }
264         /*     fprintf(stderr, "%f %f\n", VECTOR(triangles)[idx], triples); */
265     }
266 
267     igraph_vector_destroy(&triangles);
268     igraph_free(neis);
269     igraph_vector_destroy(&rank);
270     igraph_vector_destroy(&order);
271     igraph_vector_destroy(&avids);
272     igraph_vector_destroy(&indexv);
273     igraph_lazy_adjlist_destroy(&adjlist);
274     igraph_vit_destroy(&vit);
275     IGRAPH_FINALLY_CLEAN(8);
276 
277     return 0;
278 }
279 
280 /* We don't use this, it is theoretically good, but practically not.
281  */
282 
283 /* int igraph_transitivity_local_undirected3(const igraph_t *graph, */
284 /*                    igraph_vector_t *res, */
285 /*                    const igraph_vs_t vids) { */
286 
287 /*   igraph_vit_t vit; */
288 /*   long int nodes_to_calc; */
289 /*   igraph_lazy_adjlist_t adjlist; */
290 /*   long int i, j; */
291 
292 /*   IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit)); */
293 /*   IGRAPH_FINALLY(igraph_vit_destroy, &vit); */
294 /*   nodes_to_calc=IGRAPH_VIT_SIZE(vit); */
295 
296 /*   IGRAPH_CHECK(igraph_lazy_adjlist_init(graph, &adjlist, IGRAPH_ALL, */
297 /*                    IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE)); */
298 /*   IGRAPH_FINALLY(igraph_lazy_adjlist_destroy, &adjlist); */
299 
300 /*   IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc)); */
301 /*   for (i=0, IGRAPH_VIT_RESET(vit); !IGRAPH_VIT_END(vit);  */
302 /*        i++, IGRAPH_VIT_NEXT(vit)) { */
303 /*     long int node=IGRAPH_VIT_GET(vit); */
304 /*     igraph_vector_t *neis=igraph_lazy_adjlist_get(&adjlist, node); */
305 /*     long int n1=igraph_vector_size(neis); */
306 /*     igraph_real_t triangles=0; */
307 /*     igraph_real_t triples=(double)n1*(n1-1); */
308 /*     IGRAPH_ALLOW_INTERRUPTION(); */
309 /*     for (j=0; j<n1; j++) { */
310 /*       long int node2=VECTOR(*neis)[j]; */
311 /*       igraph_vector_t *neis2=igraph_lazy_adjlist_get(&adjlist, node2); */
312 /*       long int n2=igraph_vector_size(neis2); */
313 /*       long int l1=0, l2=0; */
314 /*       while (l1 < n1 && l2 < n2) { */
315 /*  long int nei1=VECTOR(*neis)[l1]; */
316 /*  long int nei2=VECTOR(*neis2)[l2]; */
317 /*  if (nei1 < nei2) {  */
318 /*    l1++; */
319 /*  } else if (nei1 > nei2) { */
320 /*    l2++; */
321 /*  } else { */
322 /*    triangles+=1; */
323 /*    l1++; l2++; */
324 /*  } */
325 /*       } */
326 /*     } */
327 /*     /\* We're done with 'node' *\/ */
328 /*     VECTOR(*res)[i] = triangles / triples;   */
329 /*   } */
330 
331 /*   igraph_lazy_adjlist_destroy(&adjlist); */
332 /*   igraph_vit_destroy(&vit); */
333 /*   IGRAPH_FINALLY_CLEAN(2); */
334 
335 /*   return 0; */
336 /* } */
337 
338 /* This removes loop, multiple edges and edges that point
339      "backwards" according to the rank vector. */
340 /* Note: Also used in scan.c */
igraph_i_trans4_al_simplify(igraph_adjlist_t * al,const igraph_vector_int_t * rank)341 int igraph_i_trans4_al_simplify(igraph_adjlist_t *al,
342                                 const igraph_vector_int_t *rank) {
343     long int i;
344     long int n = al->length;
345     igraph_vector_int_t mark;
346     igraph_vector_int_init(&mark, n);
347     IGRAPH_FINALLY(igraph_vector_int_destroy, &mark);
348     for (i = 0; i < n; i++) {
349         igraph_vector_int_t *v = &al->adjs[i];
350         int j, l = igraph_vector_int_size(v);
351         int irank = VECTOR(*rank)[i];
352         VECTOR(mark)[i] = i + 1;
353         for (j = 0; j < l; /* nothing */) {
354             long int e = (long int) VECTOR(*v)[j];
355             if (VECTOR(*rank)[e] > irank && VECTOR(mark)[e] != i + 1) {
356                 VECTOR(mark)[e] = i + 1;
357                 j++;
358             } else {
359                 VECTOR(*v)[j] = igraph_vector_int_tail(v);
360                 igraph_vector_int_pop_back(v);
361                 l--;
362             }
363         }
364     }
365 
366     igraph_vector_int_destroy(&mark);
367     IGRAPH_FINALLY_CLEAN(1);
368     return 0;
369 
370 }
371 
igraph_transitivity_local_undirected4(const igraph_t * graph,igraph_vector_t * res,igraph_transitivity_mode_t mode)372 int igraph_transitivity_local_undirected4(const igraph_t *graph,
373         igraph_vector_t *res,
374         igraph_transitivity_mode_t mode) {
375 
376 #define TRANSIT 1
377 #include "properties/triangles_template.h"
378 #undef TRANSIT
379 
380     return 0;
381 }
382 
383 /**
384  * \function igraph_transitivity_local_undirected
385  * \brief Calculates the local transitivity (clustering coefficient) of a graph.
386  *
387  * The transitivity measures the probability that two neighbors of a
388  * vertex are connected. In case of the local transitivity, this
389  * probability is calculated separately for each vertex.
390  *
391  * </para><para>
392  * Note that this measure is different from the global transitivity measure
393  * (see \ref igraph_transitivity_undirected() ) as it calculates a transitivity
394  * value for each vertex individually.
395  *
396  * </para><para>
397  * Clustering coefficient is an alternative name for transitivity.
398  *
399  * </para><para>
400  * References:
401  *
402  * </para><para>
403  * D. J. Watts and S. Strogatz: Collective dynamics of small-world networks.
404  * Nature 393(6684):440-442 (1998).
405  *
406  * \param graph The input graph. Edge directions and multiplicities are ignored.
407  * \param res Pointer to an initialized vector, the result will be
408  *   stored here. It will be resized as needed.
409  * \param vids Vertex set, the vertices for which the local
410  *   transitivity will be calculated.
411  * \param mode Defines how to treat vertices with degree less than two.
412  *    \c IGRAPH_TRANSITIVITY_NAN returns \c NaN for these vertices,
413  *    \c IGRAPH_TRANSITIVITY_ZERO returns zero.
414  * \return Error code.
415  *
416  * \sa \ref igraph_transitivity_undirected(), \ref
417  * igraph_transitivity_avglocal_undirected().
418  *
419  * Time complexity: O(n*d^2), n is the number of vertices for which
420  * the transitivity is calculated, d is the average vertex degree.
421  */
422 
igraph_transitivity_local_undirected(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids,igraph_transitivity_mode_t mode)423 int igraph_transitivity_local_undirected(const igraph_t *graph,
424         igraph_vector_t *res,
425         const igraph_vs_t vids,
426         igraph_transitivity_mode_t mode) {
427 
428     if (igraph_vs_is_all(&vids)) {
429         return igraph_transitivity_local_undirected4(graph, res, mode);
430     } else {
431         igraph_vit_t vit;
432         long int size;
433         IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
434         IGRAPH_FINALLY(igraph_vit_destroy, &vit);
435         size = IGRAPH_VIT_SIZE(vit);
436         igraph_vit_destroy(&vit);
437         IGRAPH_FINALLY_CLEAN(1);
438         if (size < 100) {
439             return igraph_transitivity_local_undirected1(graph, res, vids, mode);
440         } else {
441             return igraph_transitivity_local_undirected2(graph, res, vids, mode);
442         }
443     }
444 }
445 
igraph_adjacent_triangles1(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids)446 static int igraph_adjacent_triangles1(const igraph_t *graph,
447                                       igraph_vector_t *res,
448                                       const igraph_vs_t vids) {
449 # include "properties/triangles_template1.h"
450     return 0;
451 }
452 
igraph_adjacent_triangles4(const igraph_t * graph,igraph_vector_t * res)453 static int igraph_adjacent_triangles4(const igraph_t *graph,
454                                       igraph_vector_t *res) {
455 # include "properties/triangles_template.h"
456     return 0;
457 }
458 
459 /**
460  * \function igraph_adjacent_triangles
461  * \brief Count the number of triangles a vertex is part of.
462  *
463  * \param graph The input graph. Edge directions and multiplicities are ignored.
464  * \param res Initiliazed vector, the results are stored here.
465  * \param vids The vertices to perform the calculation for.
466  * \return Error mode.
467  *
468  * \sa \ref igraph_list_triangles() to list them.
469  *
470  * Time complexity: O(d^2 n), d is the average vertex degree of the
471  * queried vertices, n is their number.
472  */
473 
igraph_adjacent_triangles(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids)474 int igraph_adjacent_triangles(const igraph_t *graph,
475                               igraph_vector_t *res,
476                               const igraph_vs_t vids) {
477     if (igraph_vs_is_all(&vids)) {
478         return igraph_adjacent_triangles4(graph, res);
479     } else {
480         return igraph_adjacent_triangles1(graph, res, vids);
481     }
482 }
483 
484 /**
485  * \function igraph_list_triangles
486  * \brief Find all triangles in a graph.
487  *
488  * \param graph The input graph, edge directions are ignored.
489  *        Multiple edges are ignored.
490  * \param res Pointer to an initialized integer vector, the result
491  *        is stored here, in a long list of triples of vertex ids.
492  *        Each triple is a triangle in the graph. Each triangle is
493  *        listed exactly once.
494  * \return Error code.
495  *
496  * \sa \ref igraph_transitivity_undirected() to count the triangles,
497  * \ref igraph_adjacent_triangles() to count the triangles a vertex
498  * participates in.
499  *
500  * Time complexity: O(d^2 n), d is the average degree, n is the number
501  * of vertices.
502  */
503 
igraph_list_triangles(const igraph_t * graph,igraph_vector_int_t * res)504 int igraph_list_triangles(const igraph_t *graph,
505                           igraph_vector_int_t *res) {
506 # define TRIANGLES
507 # include "properties/triangles_template.h"
508 # undef TRIANGLES
509     return IGRAPH_SUCCESS;
510 }
511 
512 /**
513  * \ingroup structural
514  * \function igraph_transitivity_undirected
515  * \brief Calculates the transitivity (clustering coefficient) of a graph.
516  *
517  * </para><para>
518  * The transitivity measures the probability that two neighbors of a
519  * vertex are connected. More precisely, this is the ratio of the
520  * triangles and connected triples in the graph, the result is a
521  * single real number. Directed graphs are considered as undirected ones
522  * and multi-edges are ignored.
523  *
524  * </para><para>
525  * Note that this measure is different from the local transitivity measure
526  * (see \ref igraph_transitivity_local_undirected() ) as it calculates a single
527  * value for the whole graph.
528  *
529  * </para><para>
530  * Clustering coefficient is an alternative name for transitivity.
531  *
532  * </para><para>
533  * References:
534  *
535  * </para><para>
536  * S. Wasserman and K. Faust: Social Network Analysis: Methods and
537  * Applications. Cambridge: Cambridge University Press, 1994.
538  *
539  * \param graph The graph object. Edge directions and multiplicites are ignored.
540  * \param res Pointer to a real variable, the result will be stored here.
541  * \param mode Defines how to treat graphs with no connected triples.
542  *   \c IGRAPH_TRANSITIVITY_NAN returns \c NaN in this case,
543  *   \c IGRAPH_TRANSITIVITY_ZERO returns zero.
544  * \return Error code:
545  *         \c IGRAPH_ENOMEM: not enough memory for
546  *         temporary data.
547  *
548  * \sa \ref igraph_transitivity_local_undirected(),
549  * \ref igraph_transitivity_avglocal_undirected().
550  *
551  * Time complexity: O(|V|*d^2), |V| is the number of vertices in
552  * the graph, d is the average node degree.
553  *
554  * \example examples/simple/igraph_transitivity.c
555  */
556 
igraph_transitivity_undirected(const igraph_t * graph,igraph_real_t * res,igraph_transitivity_mode_t mode)557 int igraph_transitivity_undirected(const igraph_t *graph,
558                                    igraph_real_t *res,
559                                    igraph_transitivity_mode_t mode) {
560 
561     long int no_of_nodes = igraph_vcount(graph);
562     igraph_real_t triples = 0, triangles = 0;
563     long int node, nn;
564     long int maxdegree;
565     long int *neis;
566     igraph_vector_t order;
567     igraph_vector_t rank;
568     igraph_vector_t degree;
569 
570     igraph_adjlist_t allneis;
571     igraph_vector_int_t *neis1, *neis2;
572     long int i, j, neilen1, neilen2;
573 
574     if (no_of_nodes == 0) {
575         *res = mode == IGRAPH_TRANSITIVITY_ZERO ? 0.0 : IGRAPH_NAN;
576         return IGRAPH_SUCCESS;
577     }
578 
579     IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
580     IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
581 
582     IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
583                                IGRAPH_LOOPS));
584     maxdegree = (long int) igraph_vector_max(&degree) + 1;
585     IGRAPH_CHECK(igraph_vector_order1(&degree, &order, maxdegree));
586 
587     igraph_vector_destroy(&degree);
588     IGRAPH_FINALLY_CLEAN(1);
589 
590     IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes);
591     for (i = 0; i < no_of_nodes; i++) {
592         VECTOR(rank)[ (long int) VECTOR(order)[i] ] = no_of_nodes - i - 1;
593     }
594 
595     IGRAPH_CHECK(igraph_adjlist_init(graph, &allneis, IGRAPH_ALL, IGRAPH_NO_LOOPS, IGRAPH_NO_MULTIPLE));
596     IGRAPH_FINALLY(igraph_adjlist_destroy, &allneis);
597 
598     neis = IGRAPH_CALLOC(no_of_nodes, long int);
599     if (! neis) {
600         IGRAPH_ERROR("Insufficient memory for undirected global transitivity.", IGRAPH_ENOMEM);
601     }
602     IGRAPH_FINALLY(igraph_free, neis);
603 
604     for (nn = no_of_nodes - 1; nn >= 0; nn--) {
605         node = (long int) VECTOR(order)[nn];
606 
607         IGRAPH_ALLOW_INTERRUPTION();
608 
609         neis1 = igraph_adjlist_get(&allneis, node);
610         neilen1 = igraph_vector_int_size(neis1);
611         triples += (double)neilen1 * (neilen1 - 1);
612         /* Mark the neighbors of 'node' */
613         for (i = 0; i < neilen1; i++) {
614             long int nei = (long int) VECTOR(*neis1)[i];
615             neis[nei] = node + 1;
616         }
617         for (i = 0; i < neilen1; i++) {
618             long int nei = (long int) VECTOR(*neis1)[i];
619             /* If 'nei' is not ready yet */
620             if (VECTOR(rank)[nei] > VECTOR(rank)[node]) {
621                 neis2 = igraph_adjlist_get(&allneis, nei);
622                 neilen2 = igraph_vector_int_size(neis2);
623                 for (j = 0; j < neilen2; j++) {
624                     long int nei2 = (long int) VECTOR(*neis2)[j];
625                     if (neis[nei2] == node + 1) {
626                         triangles += 1.0;
627                     }
628                 }
629             }
630         }
631     }
632 
633     IGRAPH_FREE(neis);
634     igraph_adjlist_destroy(&allneis);
635     igraph_vector_destroy(&rank);
636     igraph_vector_destroy(&order);
637     IGRAPH_FINALLY_CLEAN(4);
638 
639     if (triples == 0 && mode == IGRAPH_TRANSITIVITY_ZERO) {
640         *res = 0;
641     } else {
642         *res = triangles / triples * 2.0;
643     }
644 
645     return 0;
646 }
647 
igraph_i_transitivity_barrat1(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids,const igraph_vector_t * weights,igraph_transitivity_mode_t mode)648 static int igraph_i_transitivity_barrat1(const igraph_t *graph,
649                                          igraph_vector_t *res,
650                                          const igraph_vs_t vids,
651                                          const igraph_vector_t *weights,
652                                          igraph_transitivity_mode_t mode) {
653 
654     long int no_of_nodes = igraph_vcount(graph);
655     igraph_vit_t vit;
656     long int nodes_to_calc;
657     igraph_vector_int_t *adj1, *adj2;
658     igraph_vector_long_t neis;
659     igraph_vector_t actw;
660     igraph_lazy_inclist_t incident;
661     long int i;
662     igraph_vector_t strength;
663 
664     /* Precondition: weight vector is not null, its length equals the number of
665      * edges, and the graph has at least one vertex. The graph must not have
666      * multi-edges. These must be ensured by the caller.
667      */
668 
669     IGRAPH_CHECK(igraph_vit_create(graph, vids, &vit));
670     IGRAPH_FINALLY(igraph_vit_destroy, &vit);
671     nodes_to_calc = IGRAPH_VIT_SIZE(vit);
672 
673     IGRAPH_CHECK(igraph_vector_long_init(&neis, no_of_nodes));
674     IGRAPH_FINALLY(igraph_vector_long_destroy, &neis);
675 
676     IGRAPH_VECTOR_INIT_FINALLY(&actw, no_of_nodes);
677 
678     IGRAPH_VECTOR_INIT_FINALLY(&strength, 0);
679     IGRAPH_CHECK(igraph_strength(graph, &strength, igraph_vss_all(), IGRAPH_ALL,
680                                  IGRAPH_LOOPS, weights));
681 
682     IGRAPH_CHECK(igraph_lazy_inclist_init(graph, &incident, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
683     IGRAPH_FINALLY(igraph_lazy_inclist_destroy, &incident);
684 
685     IGRAPH_CHECK(igraph_vector_resize(res, nodes_to_calc));
686 
687     for (i = 0; !IGRAPH_VIT_END(vit); IGRAPH_VIT_NEXT(vit), i++) {
688         long int node = IGRAPH_VIT_GET(vit);
689         long int adjlen1, adjlen2, j, k;
690         igraph_real_t triples, triangles;
691 
692         IGRAPH_ALLOW_INTERRUPTION();
693 
694         adj1 = igraph_lazy_inclist_get(&incident, (igraph_integer_t) node);
695         adjlen1 = igraph_vector_int_size(adj1);
696         /* Mark the neighbors of the node */
697         for (j = 0; j < adjlen1; j++) {
698             long int edge = (long int) VECTOR(*adj1)[j];
699             long int nei = IGRAPH_OTHER(graph, edge, node);
700             VECTOR(neis)[nei] = i + 1;
701             VECTOR(actw)[nei] = VECTOR(*weights)[edge];
702         }
703         triples = VECTOR(strength)[node] * (adjlen1 - 1);
704         triangles = 0.0;
705 
706         for (j = 0; j < adjlen1; j++) {
707             long int edge1 = (long int) VECTOR(*adj1)[j];
708             igraph_real_t weight1 = VECTOR(*weights)[edge1];
709             long int v = IGRAPH_OTHER(graph, edge1, node);
710             adj2 = igraph_lazy_inclist_get(&incident, (igraph_integer_t) v);
711             adjlen2 = igraph_vector_int_size(adj2);
712             for (k = 0; k < adjlen2; k++) {
713                 long int edge2 = (long int) VECTOR(*adj2)[k];
714                 long int v2 = IGRAPH_OTHER(graph, edge2, v);
715                 if (VECTOR(neis)[v2] == i + 1) {
716                     triangles += (VECTOR(actw)[v2] + weight1) / 2.0;
717                 }
718             }
719         }
720         if (mode == IGRAPH_TRANSITIVITY_ZERO && triples == 0) {
721             VECTOR(*res)[i] = 0.0;
722         } else {
723             VECTOR(*res)[i] = triangles / triples;
724         }
725     }
726 
727     igraph_lazy_inclist_destroy(&incident);
728     igraph_vector_destroy(&strength);
729     igraph_vector_destroy(&actw);
730     igraph_vector_long_destroy(&neis);
731     igraph_vit_destroy(&vit);
732     IGRAPH_FINALLY_CLEAN(5);
733 
734     return IGRAPH_SUCCESS;
735 }
736 
igraph_i_transitivity_barrat4(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids,const igraph_vector_t * weights,igraph_transitivity_mode_t mode)737 static int igraph_i_transitivity_barrat4(const igraph_t *graph,
738                                          igraph_vector_t *res,
739                                          const igraph_vs_t vids,
740                                          const igraph_vector_t *weights,
741                                          igraph_transitivity_mode_t mode) {
742 
743     long int no_of_nodes = igraph_vcount(graph);
744     igraph_vector_t order, degree, rank;
745     long int maxdegree;
746     igraph_inclist_t incident;
747     igraph_vector_long_t neis;
748     igraph_vector_int_t *adj1, *adj2;
749     igraph_vector_t actw;
750     long int i, nn;
751 
752     /* Precondition: weight vector is not null, its length equals the number of
753      * edges, and the graph has at least one vertex. The graph must not have
754      * multi-edges. These must be ensured by the caller.
755      */
756 
757     IGRAPH_VECTOR_INIT_FINALLY(&order, no_of_nodes);
758     IGRAPH_VECTOR_INIT_FINALLY(&degree, no_of_nodes);
759 
760     IGRAPH_CHECK(igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
761                                IGRAPH_LOOPS));
762     maxdegree = (long int) igraph_vector_max(&degree) + 1;
763     IGRAPH_CHECK(igraph_vector_order1(&degree, &order, maxdegree));
764 
765     IGRAPH_CHECK(igraph_strength(graph, &degree, igraph_vss_all(), IGRAPH_ALL,
766                                  IGRAPH_LOOPS, weights));
767 
768     IGRAPH_VECTOR_INIT_FINALLY(&rank, no_of_nodes);
769     for (i = 0; i < no_of_nodes; i++) {
770         VECTOR(rank)[ (long int)VECTOR(order)[i] ] = no_of_nodes - i - 1;
771     }
772 
773     IGRAPH_CHECK(igraph_inclist_init(graph, &incident, IGRAPH_ALL, IGRAPH_LOOPS_TWICE));
774     IGRAPH_FINALLY(igraph_inclist_destroy, &incident);
775 
776     IGRAPH_CHECK(igraph_vector_long_init(&neis, no_of_nodes));
777     IGRAPH_FINALLY(igraph_vector_long_destroy, &neis);
778 
779     IGRAPH_VECTOR_INIT_FINALLY(&actw, no_of_nodes);
780 
781     IGRAPH_CHECK(igraph_vector_resize(res, no_of_nodes));
782     igraph_vector_null(res);
783 
784     for (nn = no_of_nodes - 1; nn >= 0; nn--) {
785         long int adjlen1, adjlen2;
786         igraph_real_t triples;
787         long int node = (long int) VECTOR(order)[nn];
788 
789         IGRAPH_ALLOW_INTERRUPTION();
790 
791         adj1 = igraph_inclist_get(&incident, node);
792         adjlen1 = igraph_vector_int_size(adj1);
793         triples = VECTOR(degree)[node] * (adjlen1 - 1) / 2.0;
794         /* Mark the neighbors of the node */
795         for (i = 0; i < adjlen1; i++) {
796             long int edge = (long int) VECTOR(*adj1)[i];
797             long int nei = IGRAPH_OTHER(graph, edge, node);
798             VECTOR(neis)[nei] = node + 1;
799             VECTOR(actw)[nei] = VECTOR(*weights)[edge];
800         }
801 
802         for (i = 0; i < adjlen1; i++) {
803             long int edge1 = (long int) VECTOR(*adj1)[i];
804             igraph_real_t weight1 = VECTOR(*weights)[edge1];
805             long int nei = IGRAPH_OTHER(graph, edge1, node);
806             long int j;
807             if (VECTOR(rank)[nei] > VECTOR(rank)[node]) {
808                 adj2 = igraph_inclist_get(&incident, nei);
809                 adjlen2 = igraph_vector_int_size(adj2);
810                 for (j = 0; j < adjlen2; j++) {
811                     long int edge2 = (long int) VECTOR(*adj2)[j];
812                     igraph_real_t weight2 = VECTOR(*weights)[edge2];
813                     long int nei2 = IGRAPH_OTHER(graph, edge2, nei);
814                     if (VECTOR(rank)[nei2] < VECTOR(rank)[nei]) {
815                         continue;
816                     }
817                     if (VECTOR(neis)[nei2] == node + 1) {
818                         VECTOR(*res)[nei2] += (VECTOR(actw)[nei2] + weight2) / 2.0;
819                         VECTOR(*res)[nei] += (weight1 + weight2) / 2.0;
820                         VECTOR(*res)[node] += (VECTOR(actw)[nei2] + weight1) / 2.0;
821                     }
822                 }
823             }
824         }
825 
826         if (mode == IGRAPH_TRANSITIVITY_ZERO && triples == 0) {
827             VECTOR(*res)[node] = 0.0;
828         } else {
829             VECTOR(*res)[node] /= triples;
830         }
831     }
832 
833     igraph_vector_destroy(&actw);
834     igraph_vector_long_destroy(&neis);
835     igraph_inclist_destroy(&incident);
836     igraph_vector_destroy(&rank);
837     igraph_vector_destroy(&degree);
838     igraph_vector_destroy(&order);
839     IGRAPH_FINALLY_CLEAN(6);
840 
841     return IGRAPH_SUCCESS;
842 }
843 
844 /**
845  * \function igraph_transitivity_barrat
846  * Weighted transitivity, as defined by A. Barrat.
847  *
848  * This is a local transitivity, i.e. a vertex-level index. For a
849  * given vertex \c i, from all triangles in which it participates we
850  * consider the weight of the edges incident on \c i. The transitivity
851  * is the sum of these weights divided by twice the strength of the
852  * vertex (see \ref igraph_strength()) and the degree of the vertex
853  * minus one. See   Alain Barrat, Marc Barthelemy, Romualdo
854  * Pastor-Satorras, Alessandro Vespignani: The architecture of complex
855  * weighted networks, Proc. Natl. Acad. Sci. USA 101, 3747 (2004) at
856  * http://arxiv.org/abs/cond-mat/0311416 for the exact formula.
857  *
858  * \param graph The input graph. Edge directions are ignored for
859  *   directed graphs. Note that the function does \em not work for
860  *   non-simple graphs.
861  * \param res Pointer to an initialized vector, the result will be
862  *   stored here. It will be resized as needed.
863  * \param vids The vertices for which the calculation is performed.
864  * \param weights Edge weights. If this is a null pointer, then a
865  *   warning is given and \ref igraph_transitivity_local_undirected()
866  *   is called.
867  * \param mode Defines how to treat vertices with zero strength.
868  *   \c IGRAPH_TRANSITIVITY_NAN says that the transitivity of these
869  *   vertices is \c NaN, \c IGRAPH_TRANSITIVITY_ZERO says it is zero.
870  *
871  * \return Error code.
872  *
873  * Time complexity: O(|V|*d^2), |V| is the number of vertices in
874  * the graph, d is the average node degree.
875  *
876  * \sa \ref igraph_transitivity_undirected(), \ref
877  * igraph_transitivity_local_undirected() and \ref
878  * igraph_transitivity_avglocal_undirected() for other kinds of
879  * (non-weighted) transitivity.
880  */
881 
igraph_transitivity_barrat(const igraph_t * graph,igraph_vector_t * res,const igraph_vs_t vids,const igraph_vector_t * weights,igraph_transitivity_mode_t mode)882 int igraph_transitivity_barrat(const igraph_t *graph,
883                                igraph_vector_t *res,
884                                const igraph_vs_t vids,
885                                const igraph_vector_t *weights,
886                                igraph_transitivity_mode_t mode) {
887     long int no_of_nodes = igraph_vcount(graph);
888     long int no_of_edges = igraph_ecount(graph);
889     igraph_bool_t has_multiple;
890 
891     /* Handle fallback to unweighted version and common cases */
892     if (!weights) {
893         if (no_of_edges != 0) {
894             IGRAPH_WARNING("No weights given for Barrat's transitivity, unweighted version is used.");
895         }
896         return igraph_transitivity_local_undirected(graph, res, vids, mode);
897     }
898 
899     if (igraph_vector_size(weights) != no_of_edges) {
900         IGRAPH_ERRORF("Edge weight vector length (%ld) not equal to "
901                       "number of edges (%ld).", IGRAPH_EINVAL,
902                       igraph_vector_size(weights), no_of_edges);
903     }
904 
905     if (no_of_nodes == 0) {
906         igraph_vector_clear(res);
907         return IGRAPH_SUCCESS;
908     }
909 
910     IGRAPH_CHECK(igraph_has_multiple(graph, &has_multiple));
911     if (has_multiple) {
912         IGRAPH_ERROR(
913             "Barrat's weighted transitivity measure works only if the graph "
914             "has no multiple edges.", IGRAPH_EINVAL
915         );
916     }
917 
918     /* Preconditions validated, now we can call the real implementation */
919 
920     if (igraph_vs_is_all(&vids)) {
921         return igraph_i_transitivity_barrat4(graph, res, vids, weights, mode);
922     } else {
923         return igraph_i_transitivity_barrat1(graph, res, vids, weights, mode);
924     }
925 }
926