1 /* -*- mode: C -*- */
2 /* vim:set ts=4 sw=4 sts=4 et: */
3 /*
4 IGraph library.
5 Copyright (C) 2003-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_games.h"
25
26 #include "igraph_conversion.h"
27 #include "igraph_constructors.h"
28 #include "igraph_interface.h"
29 #include "igraph_random.h"
30
31 /**
32 * \function igraph_correlated_game
33 * \brief Generates a random graph correlated to an existing graph.
34 *
35 * Sample a new graph by perturbing the adjacency matrix of a
36 * given graph and shuffling its vertices.
37 *
38 * \param old_graph The original graph.
39 * \param new_graph The new graph will be stored here.
40 * \param corr A scalar in the unit interval, the target Pearson
41 * correlation between the adjacency matrices of the original the
42 * generated graph (the adjacency matrix being used as a vector).
43 * \param p A numeric scalar, the probability of an edge between two
44 * vertices, it must in the open (0,1) interval.
45 * \param permutation A permutation to apply to the vertices of the
46 * generated graph. It can also be a null pointer, in which case
47 * the vertices will not be permuted.
48 * \return Error code
49 *
50 * \sa \ref igraph_correlated_pair_game() for generating a pair
51 * of correlated random graphs in one go.
52 */
igraph_correlated_game(const igraph_t * old_graph,igraph_t * new_graph,igraph_real_t corr,igraph_real_t p,const igraph_vector_t * permutation)53 int igraph_correlated_game(const igraph_t *old_graph, igraph_t *new_graph,
54 igraph_real_t corr, igraph_real_t p,
55 const igraph_vector_t *permutation) {
56
57 int no_of_nodes = igraph_vcount(old_graph);
58 int no_of_edges = igraph_ecount(old_graph);
59 igraph_bool_t directed = igraph_is_directed(old_graph);
60 igraph_real_t no_of_all = directed ? no_of_nodes * (no_of_nodes - 1) :
61 no_of_nodes * (no_of_nodes - 1) / 2;
62 igraph_real_t no_of_missing = no_of_all - no_of_edges;
63 igraph_real_t q = p + corr * (1 - p);
64 igraph_real_t p_del = 1 - q;
65 igraph_real_t p_add = ((1 - q) * (p / (1 - p)));
66 igraph_vector_t add, delete, edges, newedges;
67 igraph_real_t last;
68 int p_e = 0, p_a = 0, p_d = 0, no_add, no_del;
69 igraph_real_t inf = IGRAPH_INFINITY;
70 igraph_real_t next_e, next_a, next_d;
71 int i;
72
73 if (corr < -1 || corr > 1) {
74 IGRAPH_ERROR("Correlation must be in [-1,1] in correlated "
75 "Erdos-Renyi game", IGRAPH_EINVAL);
76 }
77 if (p <= 0 || p >= 1) {
78 IGRAPH_ERROR("Edge probability must be in (0,1) in correlated "
79 "Erdos-Renyi game", IGRAPH_EINVAL);
80 }
81 if (permutation) {
82 if (igraph_vector_size(permutation) != no_of_nodes) {
83 IGRAPH_ERROR("Invalid permutation length in correlated Erdos-Renyi game",
84 IGRAPH_EINVAL);
85 }
86 }
87
88 /* Special cases */
89
90 if (corr == 0) {
91 return igraph_erdos_renyi_game(new_graph, IGRAPH_ERDOS_RENYI_GNP,
92 no_of_nodes, p, directed,
93 IGRAPH_NO_LOOPS);
94 }
95 if (corr == 1) {
96 /* We don't copy, because we don't need the attributes.... */
97 IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
98 IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0));
99 if (permutation) {
100 int newec = igraph_vector_size(&edges);
101 for (i = 0; i < newec; i++) {
102 int tmp = VECTOR(edges)[i];
103 VECTOR(edges)[i] = VECTOR(*permutation)[tmp];
104 }
105 }
106 IGRAPH_CHECK(igraph_create(new_graph, &edges, no_of_nodes, directed));
107 igraph_vector_destroy(&edges);
108 IGRAPH_FINALLY_CLEAN(1);
109 return 0;
110 }
111
112 IGRAPH_VECTOR_INIT_FINALLY(&newedges, 0);
113 IGRAPH_VECTOR_INIT_FINALLY(&add, 0);
114 IGRAPH_VECTOR_INIT_FINALLY(&delete, 0);
115 IGRAPH_VECTOR_INIT_FINALLY(&edges, no_of_edges * 2);
116
117 IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0));
118
119 RNG_BEGIN();
120
121 if (p_del > 0) {
122 last = RNG_GEOM(p_del);
123 while (last < no_of_edges) {
124 IGRAPH_CHECK(igraph_vector_push_back(&delete, last));
125 last += RNG_GEOM(p_del);
126 last += 1;
127 }
128 }
129 no_del = igraph_vector_size(&delete);
130
131 if (p_add > 0) {
132 last = RNG_GEOM(p_add);
133 while (last < no_of_missing) {
134 IGRAPH_CHECK(igraph_vector_push_back(&add, last));
135 last += RNG_GEOM(p_add);
136 last += 1;
137 }
138 }
139 no_add = igraph_vector_size(&add);
140
141 RNG_END();
142
143 IGRAPH_CHECK(igraph_get_edgelist(old_graph, &edges, /* bycol= */ 0));
144
145 /* Now we are merging the original edges, the edges that are removed,
146 and the new edges. We have the following pointers:
147 - p_a: the next edge to add
148 - p_d: the next edge to delete
149 - p_e: the next original edge
150 - next_e: the code of the next edge in 'edges'
151 - next_a: the code of the next edge to add
152 - next_d: the code of the next edge to delete */
153
154 #define D_CODE(f,t) (((t)==no_of_nodes-1 ? f : t) * no_of_nodes + (f))
155 #define U_CODE(f,t) ((t) * ((t)-1) / 2 + (f))
156 #define CODE(f,t) (directed ? D_CODE(f,t) : U_CODE(f,t))
157 #define CODEE() (CODE(VECTOR(edges)[2*p_e], VECTOR(edges)[2*p_e+1]))
158
159 /* First we (re)code the edges to delete */
160
161 for (i = 0; i < no_del; i++) {
162 int td = VECTOR(delete)[i];
163 int from = VECTOR(edges)[2 * td];
164 int to = VECTOR(edges)[2 * td + 1];
165 VECTOR(delete)[i] = CODE(from, to);
166 }
167
168 IGRAPH_CHECK(igraph_vector_reserve(&newedges,
169 (no_of_edges - no_del + no_add) * 2));
170
171 /* Now we can do the merge. Additional edges are tricky, because
172 the code must be shifted by the edges in the original graph. */
173
174 #define UPD_E() \
175 { if (p_e < no_of_edges) { next_e=CODEE(); } else { next_e = inf; } }
176 #define UPD_A() \
177 { if (p_a < no_add) { \
178 next_a = VECTOR(add)[p_a] + p_e; } else { next_a = inf; } }
179 #define UPD_D() \
180 { if (p_d < no_del) { \
181 next_d = VECTOR(delete)[p_d]; } else { next_d = inf; } }
182
183 UPD_E(); UPD_A(); UPD_D();
184
185 while (next_e != inf || next_a != inf || next_d != inf) {
186 if (next_e <= next_a && next_e < next_d) {
187
188 /* keep an edge */
189 IGRAPH_CHECK(igraph_vector_push_back(&newedges, VECTOR(edges)[2 * p_e]));
190 IGRAPH_CHECK(igraph_vector_push_back(&newedges, VECTOR(edges)[2 * p_e + 1]));
191 p_e ++; UPD_E(); UPD_A()
192
193 } else if (next_e <= next_a && next_e == next_d) {
194
195 /* delete an edge */
196 p_e ++; UPD_E(); UPD_A();
197 p_d++; UPD_D();
198
199 } else {
200
201 /* add an edge */
202 int to, from;
203 if (directed) {
204 to = (int) floor(next_a / no_of_nodes);
205 from = (int) (next_a - ((igraph_real_t)to) * no_of_nodes);
206 if (from == to) {
207 to = no_of_nodes - 1;
208 }
209 } else {
210 to = (int) floor((sqrt(8 * next_a + 1) + 1) / 2);
211 from = (int) (next_a - (((igraph_real_t)to) * (to - 1)) / 2);
212 }
213 IGRAPH_CHECK(igraph_vector_push_back(&newedges, from));
214 IGRAPH_CHECK(igraph_vector_push_back(&newedges, to));
215 p_a++; UPD_A();
216
217 }
218 }
219
220 igraph_vector_destroy(&edges);
221 igraph_vector_destroy(&add);
222 igraph_vector_destroy(&delete);
223 IGRAPH_FINALLY_CLEAN(3);
224
225 if (permutation) {
226 int newec = igraph_vector_size(&newedges);
227 for (i = 0; i < newec; i++) {
228 int tmp = VECTOR(newedges)[i];
229 VECTOR(newedges)[i] = VECTOR(*permutation)[tmp];
230 }
231 }
232
233 IGRAPH_CHECK(igraph_create(new_graph, &newedges, no_of_nodes, directed));
234
235 igraph_vector_destroy(&newedges);
236 IGRAPH_FINALLY_CLEAN(1);
237
238 return 0;
239 }
240
241 #undef D_CODE
242 #undef U_CODE
243 #undef CODE
244 #undef CODEE
245 #undef UPD_E
246 #undef UPD_A
247 #undef UPD_D
248
249 /**
250 * \function igraph_correlated_pair_game
251 * \brief Generates pairs of correlated random graphs.
252 *
253 * Sample two random graphs, with given correlation.
254 *
255 * \param graph1 The first graph will be stored here.
256 * \param graph2 The second graph will be stored here.
257 * \param n The number of vertices in both graphs.
258 * \param corr A scalar in the unit interval, the target Pearson
259 * correlation between the adjacency matrices of the original the
260 * generated graph (the adjacency matrix being used as a vector).
261 * \param p A numeric scalar, the probability of an edge between two
262 * vertices, it must in the open (0,1) interval.
263 * \param directed Whether to generate directed graphs.
264 * \param permutation A permutation to apply to the vertices of the
265 * second graph. It can also be a null pointer, in which case
266 * the vertices will not be permuted.
267 * \return Error code
268 *
269 * \sa \ref igraph_correlated_game() for generating a correlated pair
270 * to a given graph.
271 */
igraph_correlated_pair_game(igraph_t * graph1,igraph_t * graph2,igraph_integer_t n,igraph_real_t corr,igraph_real_t p,igraph_bool_t directed,const igraph_vector_t * permutation)272 int igraph_correlated_pair_game(igraph_t *graph1, igraph_t *graph2,
273 igraph_integer_t n, igraph_real_t corr, igraph_real_t p,
274 igraph_bool_t directed,
275 const igraph_vector_t *permutation) {
276
277 IGRAPH_CHECK(igraph_erdos_renyi_game(graph1, IGRAPH_ERDOS_RENYI_GNP, n, p,
278 directed, IGRAPH_NO_LOOPS));
279 IGRAPH_CHECK(igraph_correlated_game(graph1, graph2, corr, p, permutation));
280 return 0;
281 }
282