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(°ree, 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(°ree, &order, maxdegree + 1);
188 igraph_vector_destroy(°ree);
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(°ree, no_of_nodes);
581
582 IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL,
583 IGRAPH_LOOPS));
584 maxdegree = (long int) igraph_vector_max(°ree) + 1;
585 IGRAPH_CHECK(igraph_vector_order1(°ree, &order, maxdegree));
586
587 igraph_vector_destroy(°ree);
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(°ree, no_of_nodes);
759
760 IGRAPH_CHECK(igraph_degree(graph, °ree, igraph_vss_all(), IGRAPH_ALL,
761 IGRAPH_LOOPS));
762 maxdegree = (long int) igraph_vector_max(°ree) + 1;
763 IGRAPH_CHECK(igraph_vector_order1(°ree, &order, maxdegree));
764
765 IGRAPH_CHECK(igraph_strength(graph, °ree, 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(°ree);
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