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