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