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 #include "graph/attributes.h"
32 
igraph_i_rewire_edges_no_multiple(igraph_t * graph,igraph_real_t prob,igraph_bool_t loops,igraph_vector_t * edges)33 static int igraph_i_rewire_edges_no_multiple(igraph_t *graph, igraph_real_t prob,
34                                              igraph_bool_t loops,
35                                              igraph_vector_t *edges) {
36 
37     int no_verts = igraph_vcount(graph);
38     int no_edges = igraph_ecount(graph);
39     igraph_vector_t eorder, tmp;
40     igraph_vector_int_t first, next, prev, marked;
41     int i, to_rewire, last_other = -1;
42 
43     /* Create our special graph representation */
44 
45 # define ADD_STUB(vertex, stub) do {                \
46         if (VECTOR(first)[(vertex)]) {              \
47             VECTOR(prev)[(int) VECTOR(first)[(vertex)]-1]=(stub)+1;   \
48         }                               \
49         VECTOR(next)[(stub)]=VECTOR(first)[(vertex)];       \
50         VECTOR(prev)[(stub)]=0;                 \
51         VECTOR(first)[(vertex)]=(stub)+1;               \
52     } while (0)
53 
54 # define DEL_STUB(vertex, stub) do {                    \
55         if (VECTOR(next)[(stub)]) {                     \
56             VECTOR(prev)[VECTOR(next)[(stub)]-1]=VECTOR(prev)[(stub)];    \
57         }                                   \
58         if (VECTOR(prev)[(stub)]) {                     \
59             VECTOR(next)[VECTOR(prev)[(stub)]-1]=VECTOR(next)[(stub)];    \
60         } else {                                \
61             VECTOR(first)[(vertex)]=VECTOR(next)[(stub)];         \
62         }                                   \
63     } while (0)
64 
65 # define MARK_NEIGHBORS(vertex) do {                \
66         int xxx_ =VECTOR(first)[(vertex)];              \
67         while (xxx_) {                      \
68             int o= (int) VECTOR(*edges)[xxx_ % 2 ? xxx_ : xxx_-2];    \
69             VECTOR(marked)[o]=other+1;                \
70             xxx_=VECTOR(next)[xxx_-1];                \
71         }                               \
72     } while (0)
73 
74     IGRAPH_CHECK(igraph_vector_int_init(&first, no_verts));
75     IGRAPH_FINALLY(igraph_vector_int_destroy, &first);
76     IGRAPH_CHECK(igraph_vector_int_init(&next, no_edges * 2));
77     IGRAPH_FINALLY(igraph_vector_int_destroy, &next);
78     IGRAPH_CHECK(igraph_vector_int_init(&prev, no_edges * 2));
79     IGRAPH_FINALLY(igraph_vector_int_destroy, &prev);
80     IGRAPH_CHECK(igraph_get_edgelist(graph, edges, /*bycol=*/ 0));
81     IGRAPH_VECTOR_INIT_FINALLY(&eorder, no_edges);
82     IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_edges);
83     for (i = 0; i < no_edges; i++) {
84         int idx1 = 2 * i, idx2 = idx1 + 1,
85             from = (int) VECTOR(*edges)[idx1], to = (int) VECTOR(*edges)[idx2];
86         VECTOR(tmp)[i] = from;
87         ADD_STUB(from, idx1);
88         ADD_STUB(to, idx2);
89     }
90     IGRAPH_CHECK(igraph_vector_order1(&tmp, &eorder, no_verts));
91     igraph_vector_destroy(&tmp);
92     IGRAPH_FINALLY_CLEAN(1);
93 
94     IGRAPH_CHECK(igraph_vector_int_init(&marked, no_verts));
95     IGRAPH_FINALLY(igraph_vector_int_destroy, &marked);
96 
97     /* Rewire the stubs, part I */
98 
99     to_rewire = (int) RNG_GEOM(prob);
100     while (to_rewire < no_edges) {
101         int stub = (int) (2 * VECTOR(eorder)[to_rewire] + 1);
102         int v = (int) VECTOR(*edges)[stub];
103         int ostub = stub - 1;
104         int other = (int) VECTOR(*edges)[ostub];
105         int pot;
106         if (last_other != other) {
107             MARK_NEIGHBORS(other);
108         }
109         /* Do the rewiring */
110         do {
111             if (loops) {
112                 pot = (int) RNG_INTEGER(0, no_verts - 1);
113             } else {
114                 pot = (int) RNG_INTEGER(0, no_verts - 2);
115                 pot = pot != other ? pot : no_verts - 1;
116             }
117         } while (VECTOR(marked)[pot] == other + 1 && pot != v);
118 
119         if (pot != v) {
120             DEL_STUB(v, stub);
121             ADD_STUB(pot, stub);
122             VECTOR(marked)[v] = 0;
123             VECTOR(marked)[pot] = other + 1;
124             VECTOR(*edges)[stub] = pot;
125         }
126 
127         to_rewire += RNG_GEOM(prob) + 1;
128         last_other = other;
129     }
130 
131     /* Create the new index, from the potentially rewired stubs */
132 
133     IGRAPH_VECTOR_INIT_FINALLY(&tmp, no_edges);
134     for (i = 0; i < no_edges; i++) {
135         VECTOR(tmp)[i] = VECTOR(*edges)[2 * i + 1];
136     }
137     IGRAPH_CHECK(igraph_vector_order1(&tmp, &eorder, no_verts));
138     igraph_vector_destroy(&tmp);
139     IGRAPH_FINALLY_CLEAN(1);
140 
141     /* Rewire the stubs, part II */
142 
143     igraph_vector_int_null(&marked);
144     last_other = -1;
145 
146     to_rewire = (int) RNG_GEOM(prob);
147     while (to_rewire < no_edges) {
148         int stub = (int) (2 * VECTOR(eorder)[to_rewire]);
149         int v = (int) VECTOR(*edges)[stub];
150         int ostub = stub + 1;
151         int other = (int) VECTOR(*edges)[ostub];
152         int pot;
153         if (last_other != other) {
154             MARK_NEIGHBORS(other);
155         }
156         /* Do the rewiring */
157         do {
158             if (loops) {
159                 pot = (int) RNG_INTEGER(0, no_verts - 1);
160             } else {
161                 pot = (int) RNG_INTEGER(0, no_verts - 2);
162                 pot = pot != other ? pot : no_verts - 1;
163             }
164         } while (VECTOR(marked)[pot] == other + 1 && pot != v);
165         if (pot != v) {
166             DEL_STUB(v, stub);
167             ADD_STUB(pot, stub);
168             VECTOR(marked)[v] = 0;
169             VECTOR(marked)[pot] = other + 1;
170             VECTOR(*edges)[stub] = pot;
171         }
172 
173         to_rewire += RNG_GEOM(prob) + 1;
174         last_other = other;
175     }
176 
177     igraph_vector_int_destroy(&marked);
178     igraph_vector_int_destroy(&prev);
179     igraph_vector_int_destroy(&next);
180     igraph_vector_int_destroy(&first);
181     igraph_vector_destroy(&eorder);
182     IGRAPH_FINALLY_CLEAN(5);
183 
184     return 0;
185 }
186 
187 #undef ADD_STUB
188 #undef DEL_STUB
189 #undef MARK_NEIGHBORS
190 
191 /**
192  * \function igraph_rewire_edges
193  * \brief Rewires the edges of a graph with constant probability.
194  *
195  * This function rewires the edges of a graph with a constant
196  * probability. More precisely each end point of each edge is rewired
197  * to a uniformly randomly chosen vertex with constant probability \p
198  * prob.
199  *
200  * </para><para> Note that this function modifies the input \p graph,
201  * call \ref igraph_copy() if you want to keep it.
202  *
203  * \param graph The input graph, this will be rewired, it can be
204  *    directed or undirected.
205  * \param prob The rewiring probability a constant between zero and
206  *    one (inclusive).
207  * \param loops Boolean, whether loop edges are allowed in the new
208  *    graph, or not.
209  * \param multiple Boolean, whether multiple edges are allowed in the
210  *    new graph.
211  * \return Error code.
212  *
213  * \sa \ref igraph_watts_strogatz_game() uses this function for the
214  * rewiring.
215  *
216  * Time complexity: O(|V|+|E|).
217  */
igraph_rewire_edges(igraph_t * graph,igraph_real_t prob,igraph_bool_t loops,igraph_bool_t multiple)218 int igraph_rewire_edges(igraph_t *graph, igraph_real_t prob,
219                         igraph_bool_t loops, igraph_bool_t multiple) {
220 
221     igraph_t newgraph;
222     long int no_of_edges = igraph_ecount(graph);
223     long int no_of_nodes = igraph_vcount(graph);
224     long int endpoints = no_of_edges * 2;
225     long int to_rewire;
226     igraph_vector_t edges;
227 
228     if (prob < 0 || prob > 1) {
229         IGRAPH_ERROR("Rewiring probability should be between zero and one",
230                      IGRAPH_EINVAL);
231     }
232 
233     if (prob == 0) {
234         /* This is easy, just leave things as they are */
235         return IGRAPH_SUCCESS;
236     }
237 
238     IGRAPH_VECTOR_INIT_FINALLY(&edges, endpoints);
239 
240     RNG_BEGIN();
241 
242     if (prob != 0 && no_of_edges > 0) {
243         if (multiple) {
244             /* If multiple edges are allowed, then there is an easy and fast
245             method. Each endpoint of an edge is rewired with probability p,
246              so the "skips" between the really rewired endpoints follow a
247              geometric distribution. */
248             IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
249             to_rewire = (long int) RNG_GEOM(prob);
250             while (to_rewire < endpoints) {
251                 if (loops) {
252                     VECTOR(edges)[to_rewire] = RNG_INTEGER(0, no_of_nodes - 1);
253                 } else {
254                     long int opos = to_rewire % 2 ? to_rewire - 1 : to_rewire + 1;
255                     long int nei = (long int) VECTOR(edges)[opos];
256                     long int r = RNG_INTEGER(0, no_of_nodes - 2);
257                     VECTOR(edges)[ to_rewire ] = (r != nei ? r : no_of_nodes - 1);
258                 }
259                 to_rewire += RNG_GEOM(prob) + 1;
260             }
261 
262         } else {
263             IGRAPH_CHECK(igraph_i_rewire_edges_no_multiple(graph, prob, loops,
264                          &edges));
265         }
266     }
267 
268     RNG_END();
269 
270     IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes,
271                                igraph_is_directed(graph)));
272     igraph_vector_destroy(&edges);
273     IGRAPH_FINALLY_CLEAN(1);
274 
275     IGRAPH_FINALLY(igraph_destroy, &newgraph);
276     IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
277     IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1);
278     IGRAPH_FINALLY_CLEAN(1);
279     igraph_destroy(graph);
280     *graph = newgraph;
281 
282     return 0;
283 }
284 
285 /**
286  * \function igraph_rewire_directed_edges
287  * \brief Rewires the chosen endpoint of directed edges.
288  *
289  * This function rewires either the start or end of directed edges in a graph
290  * with a constant probability. Correspondingly, either the in-degree sequence
291  * or the out-degree sequence of the graph will be preserved.
292  *
293  * </para><para> Note that this function modifies the input \p graph,
294  * call \ref igraph_copy() if you want to keep it.
295  *
296  * </para><para> This function can produce multiple edges between two vertices.
297  *
298  * \param graph The input graph, this will be rewired, it can be
299  *    directed or undirected. If it is undirected or \p mode is set to
300  *    IGRAPH_ALL, \ref igraph_rewire_edges() will be called.
301  * \param prob The rewiring probability, a constant between zero and
302  *    one (inclusive).
303  * \param loops Boolean, whether loop edges are allowed in the new
304  *    graph, or not.
305  * \param mode The endpoints of directed edges to rewire. It is ignored for
306  *    undirected graphs. Possible values:
307  *        \clist
308  *        \cli IGRAPH_OUT
309  *          rewire the end of each directed edge
310  *        \cli IGRAPH_IN
311  *          rewire the start of each directed edge
312  *        \cli IGRAPH_ALL
313  *          rewire both endpoints of each edge
314  *        \endclist
315  * \return Error code.
316  *
317  * \sa \ref igraph_rewire_edges(), \ref igraph_rewire()
318  *
319  * Time complexity: O(|E|).
320  */
igraph_rewire_directed_edges(igraph_t * graph,igraph_real_t prob,igraph_bool_t loops,igraph_neimode_t mode)321 int igraph_rewire_directed_edges(igraph_t *graph, igraph_real_t prob,
322                                  igraph_bool_t loops, igraph_neimode_t mode) {
323 
324     if (prob < 0 || prob > 1) {
325         IGRAPH_ERROR("Rewiring probability should be between zero and one",
326                      IGRAPH_EINVAL);
327     }
328 
329     if (mode != IGRAPH_OUT && mode != IGRAPH_IN &&
330         mode != IGRAPH_ALL) {
331         IGRAPH_ERROR("Invalid mode argument", IGRAPH_EINVMODE);
332     }
333 
334     if (prob == 0) {
335         return IGRAPH_SUCCESS;
336     }
337 
338     if (igraph_is_directed(graph) && mode != IGRAPH_ALL) {
339         igraph_t newgraph;
340         long int no_of_edges = igraph_ecount(graph);
341         long int no_of_nodes = igraph_vcount(graph);
342         long int to_rewire;
343         long int offset = 0;
344         igraph_vector_t edges;
345 
346         IGRAPH_VECTOR_INIT_FINALLY(&edges, 2 * no_of_edges);
347 
348         switch (mode) {
349         case IGRAPH_IN:
350             offset = 0;
351             break;
352         case IGRAPH_OUT:
353             offset = 1;
354             break;
355         case IGRAPH_ALL:
356             break; /* suppress compiler warning */
357         }
358 
359         IGRAPH_CHECK(igraph_get_edgelist(graph, &edges, 0));
360 
361         RNG_BEGIN();
362 
363         to_rewire = RNG_GEOM(prob);
364         while (to_rewire < no_of_edges) {
365             if (loops) {
366                 VECTOR(edges)[2 * to_rewire + offset] = RNG_INTEGER(0, no_of_nodes - 1);
367             } else {
368                 long int nei = (long int) VECTOR(edges)[2 * to_rewire + (1 - offset)];
369                 long int r = RNG_INTEGER(0, no_of_nodes - 2);
370                 VECTOR(edges)[2 * to_rewire + offset] = (r != nei ? r : no_of_nodes - 1);
371             }
372             to_rewire += RNG_GEOM(prob) + 1;
373         }
374 
375         RNG_END();
376 
377         IGRAPH_CHECK(igraph_create(&newgraph, &edges, (igraph_integer_t) no_of_nodes,
378                                    igraph_is_directed(graph)));
379         igraph_vector_destroy(&edges);
380         IGRAPH_FINALLY_CLEAN(1);
381 
382         IGRAPH_FINALLY(igraph_destroy, &newgraph);
383         IGRAPH_I_ATTRIBUTE_DESTROY(&newgraph);
384         IGRAPH_I_ATTRIBUTE_COPY(&newgraph, graph, 1, 1, 1);
385         IGRAPH_FINALLY_CLEAN(1);
386         igraph_destroy(graph);
387         *graph = newgraph;
388 
389     } else {
390         IGRAPH_CHECK(igraph_rewire_edges(graph, prob, loops, /* multiple = */ 1));
391     }
392 
393     return 0;
394 }
395