1 /* -*- mode: C -*-  */
2 /*
3    IGraph library.
4    Copyright (C) 2009-2012  Gabor Csardi <csardi.gabor@gmail.com>
5    334 Harvard street, Cambridge, MA 02139 USA
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_mixing.h"
25 
26 #include "igraph_interface.h"
27 
28 /**
29  * \function igraph_assortativity_nominal
30  * Assortativity of a graph based on vertex categories
31  *
32  * Assuming the vertices of the input graph belong to different
33  * categories, this function calculates the assortativity coefficient of
34  * the graph. The assortativity coefficient is between minus one and one
35  * and it is one if all connections stay within categories, it is
36  * minus one, if the network is perfectly disassortative. For a
37  * randomly connected network it is (asymptotically) zero.
38  *
39  * </para><para>See equation (2) in M. E. J. Newman: Mixing patterns
40  * in networks, Phys. Rev. E 67, 026126 (2003)
41  * (http://arxiv.org/abs/cond-mat/0209450) for the proper
42  * definition.
43  *
44  * \param graph The input graph, it can be directed or undirected.
45  * \param types Vector giving the vertex types. They are assumed to be
46  *    integer numbers, starting with zero.
47  * \param res Pointer to a real variable, the result is stored here.
48  * \param directed Boolean, it gives whether to consider edge
49  *    directions in a directed graph. It is ignored for undirected
50  *    graphs.
51  * \return Error code.
52  *
53  * Time complexity: O(|E|+t), |E| is the number of edges, t is the
54  * number of vertex types.
55  *
56  * \sa \ref igraph_assortativity if the vertex types are defines by
57  * numeric values (e.g. vertex degree), instead of categories.
58  *
59  * \example examples/simple/assortativity.c
60  */
61 
igraph_assortativity_nominal(const igraph_t * graph,const igraph_vector_t * types,igraph_real_t * res,igraph_bool_t directed)62 int igraph_assortativity_nominal(const igraph_t *graph,
63                                  const igraph_vector_t *types,
64                                  igraph_real_t *res,
65                                  igraph_bool_t directed) {
66 
67     long int no_of_nodes = igraph_vcount(graph);
68     long int no_of_edges = igraph_ecount(graph);
69     long int no_of_types;
70     igraph_vector_t ai, bi, eii;
71     long int e, i;
72     igraph_real_t sumaibi = 0.0, sumeii = 0.0;
73 
74     if (igraph_vector_size(types) != no_of_nodes) {
75         IGRAPH_ERROR("Invalid `types' vector length", IGRAPH_EINVAL);
76     }
77 
78     if (igraph_vector_min(types) < 0) {
79         IGRAPH_ERROR("Invalid `types' vector", IGRAPH_EINVAL);
80     }
81 
82     directed = directed && igraph_is_directed(graph);
83 
84     no_of_types = (long int) igraph_vector_max(types) + 1;
85     IGRAPH_VECTOR_INIT_FINALLY(&ai, no_of_types);
86     IGRAPH_VECTOR_INIT_FINALLY(&bi, no_of_types);
87     IGRAPH_VECTOR_INIT_FINALLY(&eii, no_of_types);
88 
89     for (e = 0; e < no_of_edges; e++) {
90         long int from = IGRAPH_FROM(graph, e);
91         long int to = IGRAPH_TO(graph, e);
92         long int from_type = (long int) VECTOR(*types)[from];
93         long int to_type = (long int) VECTOR(*types)[to];
94 
95         VECTOR(ai)[from_type] += 1;
96         VECTOR(bi)[to_type] += 1;
97         if (from_type == to_type) {
98             VECTOR(eii)[from_type] += 1;
99         }
100         if (!directed) {
101             if (from_type == to_type) {
102                 VECTOR(eii)[from_type] += 1;
103             }
104             VECTOR(ai)[to_type] += 1;
105             VECTOR(bi)[from_type] += 1;
106         }
107     }
108 
109     for (i = 0; i < no_of_types; i++) {
110         sumaibi += (VECTOR(ai)[i] / no_of_edges) * (VECTOR(bi)[i] / no_of_edges);
111         sumeii  += (VECTOR(eii)[i] / no_of_edges);
112     }
113 
114     if (!directed) {
115         sumaibi /= 4.0;
116         sumeii  /= 2.0;
117     }
118 
119     *res = (sumeii - sumaibi) / (1.0 - sumaibi);
120 
121     igraph_vector_destroy(&eii);
122     igraph_vector_destroy(&bi);
123     igraph_vector_destroy(&ai);
124     IGRAPH_FINALLY_CLEAN(3);
125 
126     return 0;
127 }
128 
129 /**
130  * \function igraph_assortativity
131  * Assortativity based on numeric properties of vertices
132  *
133  * This function calculates the assortativity coefficient of the input
134  * graph. This coefficient is basically the correlation between the
135  * actual connectivity patterns of the vertices and the pattern
136  * expected from the distribution of the vertex types.
137  *
138  * </para><para>See equation (21) in M. E. J. Newman: Mixing patterns
139  * in networks, Phys. Rev. E 67, 026126 (2003)
140  * (http://arxiv.org/abs/cond-mat/0209450) for the proper
141  * definition. The actual calculation is performed using equation (26)
142  * in the same paper for directed graphs, and equation (4) in
143  * M. E. J. Newman: Assortative mixing in networks,
144  * Phys. Rev. Lett. 89, 208701 (2002)
145  * (http://arxiv.org/abs/cond-mat/0205405/) for undirected graphs.
146  *
147  * \param graph The input graph, it can be directed or undirected.
148  * \param types1 The vertex values, these can be arbitrary numeric
149  *     values.
150  * \param types2 A second value vector to be using for the incoming
151  *     edges when calculating assortativity for a directed graph.
152  *     Supply a null pointer here if you want to use the same values
153  *     for outgoing and incoming edges. This argument is ignored
154  *     (with a warning) if it is not a null pointer and undirected
155  *     assortativity coefficient is being calculated.
156  * \param res Pointer to a real variable, the result is stored here.
157  * \param directed Boolean, whether to consider edge directions for
158  *     directed graphs. It is ignored for undirected graphs.
159  * \return Error code.
160  *
161  * Time complexity: O(|E|), linear in the number of edges of the
162  * graph.
163  *
164  * \sa \ref igraph_assortativity_nominal() if you have discrete vertex
165  * categories instead of numeric labels, and \ref
166  * igraph_assortativity_degree() for the special case of assortativity
167  * based on vertex degree.
168  *
169  * \example examples/simple/assortativity.c
170  */
171 
igraph_assortativity(const igraph_t * graph,const igraph_vector_t * types1,const igraph_vector_t * types2,igraph_real_t * res,igraph_bool_t directed)172 int igraph_assortativity(const igraph_t *graph,
173                          const igraph_vector_t *types1,
174                          const igraph_vector_t *types2,
175                          igraph_real_t *res,
176                          igraph_bool_t directed) {
177 
178     long int no_of_nodes = igraph_vcount(graph);
179     long int no_of_edges = igraph_ecount(graph);
180     long int e;
181 
182     directed = directed && igraph_is_directed(graph);
183 
184     if (!directed && types2) {
185         IGRAPH_WARNING("Only `types1' is used for undirected case");
186     }
187 
188     if (igraph_vector_size(types1) != no_of_nodes) {
189         IGRAPH_ERROR("Invalid `types1' vector length", IGRAPH_EINVAL);
190     }
191 
192     if (types2 && igraph_vector_size(types2) != no_of_nodes) {
193         IGRAPH_ERROR("Invalid `types2' vector length", IGRAPH_EINVAL);
194     }
195 
196     if (!directed) {
197         igraph_real_t num1 = 0.0, num2 = 0.0, den1 = 0.0;
198 
199         for (e = 0; e < no_of_edges; e++) {
200             long int from = IGRAPH_FROM(graph, e);
201             long int to = IGRAPH_TO(graph, e);
202             igraph_real_t from_type = VECTOR(*types1)[from];
203             igraph_real_t to_type = VECTOR(*types1)[to];
204 
205             num1 += from_type * to_type;
206             num2 += from_type + to_type;
207             den1 += from_type * from_type + to_type * to_type;
208         }
209 
210         num1 /= no_of_edges;
211         den1 /= no_of_edges * 2;
212         num2 /= no_of_edges * 2;
213         num2 = num2 * num2;
214 
215         *res = (num1 - num2) / (den1 - num2);
216 
217     } else {
218         igraph_real_t num1 = 0.0, num2 = 0.0, num3 = 0.0,
219                       den1 = 0.0, den2 = 0.0;
220         igraph_real_t num, den;
221 
222         if (!types2) {
223             types2 = types1;
224         }
225 
226         for (e = 0; e < no_of_edges; e++) {
227             long int from = IGRAPH_FROM(graph, e);
228             long int to = IGRAPH_TO(graph, e);
229             igraph_real_t from_type = VECTOR(*types1)[from];
230             igraph_real_t to_type = VECTOR(*types2)[to];
231 
232             num1 += from_type * to_type;
233             num2 += from_type;
234             num3 += to_type;
235             den1 += from_type * from_type;
236             den2 += to_type * to_type;
237         }
238 
239         num = num1 - num2 * num3 / no_of_edges;
240         den = sqrt(den1 - num2 * num2 / no_of_edges) *
241               sqrt(den2 - num3 * num3 / no_of_edges);
242 
243         *res = num / den;
244     }
245 
246     return 0;
247 }
248 
249 /**
250  * \function igraph_assortativity_degree
251  * Assortativity of a graph based on vertex degree
252  *
253  * Assortativity based on vertex degree, please see the discussion at
254  * the documentation of \ref igraph_assortativity() for details.
255  *
256  * \param graph The input graph, it can be directed or undirected.
257  * \param res Pointer to a real variable, the result is stored here.
258  * \param directed Boolean, whether to consider edge directions for
259  *     directed graphs. This argument is ignored for undirected
260  *     graphs. Supply 1 (=TRUE) here to do the natural thing, i.e. use
261  *     directed version of the measure for directed graphs and the
262  *     undirected version for undirected graphs.
263  * \return Error code.
264  *
265  * Time complexity: O(|E|+|V|), |E| is the number of edges, |V| is
266  * the number of vertices.
267  *
268  * \sa \ref igraph_assortativity() for the general function
269  * calculating assortativity for any kind of numeric vertex values.
270  *
271  * \example examples/simple/assortativity.c
272  */
273 
igraph_assortativity_degree(const igraph_t * graph,igraph_real_t * res,igraph_bool_t directed)274 int igraph_assortativity_degree(const igraph_t *graph,
275                                 igraph_real_t *res,
276                                 igraph_bool_t directed) {
277 
278     directed = directed && igraph_is_directed(graph);
279 
280     if (directed) {
281         igraph_vector_t indegree, outdegree;
282         igraph_vector_init(&indegree, 0);
283         igraph_vector_init(&outdegree, 0);
284         igraph_degree(graph, &indegree, igraph_vss_all(), IGRAPH_IN, /*loops=*/ 1);
285         igraph_degree(graph, &outdegree, igraph_vss_all(), IGRAPH_OUT, /*loops=*/ 1);
286         igraph_vector_add_constant(&indegree, -1);
287         igraph_vector_add_constant(&outdegree, -1);
288         igraph_assortativity(graph, &outdegree, &indegree, res, /*directed=*/ 1);
289         igraph_vector_destroy(&indegree);
290         igraph_vector_destroy(&outdegree);
291     } else {
292         igraph_vector_t degree;
293         igraph_vector_init(&degree, 0);
294         igraph_degree(graph, &degree, igraph_vss_all(), IGRAPH_ALL, /*loops=*/ 1);
295         igraph_vector_add_constant(&degree, -1);
296         igraph_assortativity(graph, &degree, 0, res, /*directed=*/ 0);
297         igraph_vector_destroy(&degree);
298     }
299 
300     return 0;
301 }
302