1 /*
2 *
3 * gengraph - generation of random simple connected graphs with prescribed
4 * degree sequence
5 *
6 * Copyright (C) 2006 Fabien Viger
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 */
21 #include "gengraph_definitions.h"
22 #include "gengraph_random.h"
23 #include "gengraph_powerlaw.h"
24 #include "gengraph_degree_sequence.h"
25 #include "gengraph_hash.h"
26
27 #include "igraph_statusbar.h"
28
29 #include <cstdio>
30 #include <cstdlib>
31 #include <cmath>
32 #include <cassert>
33 #include <vector>
34 #include <stdexcept>
35
36 // using namespace __gnu_cxx;
37 using namespace std;
38
39 namespace gengraph {
40
41 // shuffle an int[] randomly
42 void random_permute(int *a, int n);
43
44 // sort an array of positive integers in time & place O(n + max)
45 void cumul_sort(int *q, int n);
46
47
detach()48 void degree_sequence::detach() {
49 deg = NULL;
50 }
51
~degree_sequence()52 degree_sequence::~degree_sequence() {
53 if (deg != NULL) {
54 delete[] deg;
55 }
56 deg = NULL;
57 }
58
make_even(int mini,int maxi)59 void degree_sequence::make_even(int mini, int maxi) {
60 if (total % 2 == 0) {
61 return;
62 }
63 if (maxi < 0) {
64 maxi = 0x7FFFFFFF;
65 }
66 int i;
67 for (i = 0; i < n; i++) {
68 if (deg[i] > mini) {
69 deg[i]--;
70 total--;
71 break;
72 } else if (deg[i] < maxi) {
73 deg[i]++;
74 total++;
75 break;
76 }
77 }
78 if (i == n) {
79 IGRAPH_WARNING("Warning: degree_sequence::make_even() forced one "
80 "degree to go over degmax");
81 deg[0]++;
82 total++;
83 }
84 }
85
shuffle()86 void degree_sequence::shuffle() {
87 random_permute(deg, n);
88 }
89
sort()90 void degree_sequence::sort() {
91 cumul_sort(deg, n);
92 }
93
compute_total()94 void degree_sequence::compute_total() {
95 total = 0;
96 for (int i = 0; i < n; i++) {
97 total += deg[i];
98 }
99 }
100
101 degree_sequence::
degree_sequence(int n0,int * degs)102 degree_sequence(int n0, int *degs) {
103 deg = degs;
104 n = n0;
105 compute_total();
106 }
107
108 degree_sequence::
degree_sequence(const igraph_vector_t * out_seq)109 degree_sequence(const igraph_vector_t *out_seq) {
110 n = igraph_vector_size(out_seq);
111 deg = new int[n];
112 for (long int i = 0; i < n; i++) {
113 deg[i] = VECTOR(*out_seq)[i];
114 }
115 compute_total();
116 }
117
118 #ifndef FBUFF_SIZE
119 #define FBUFF_SIZE 999
120 #endif //FBUFF_SIZE
121
122 // degree_sequence::degree_sequence(FILE *f, bool DISTRIB) {
123 // n = 0;
124 // total = 0;
125 // char *buff = new char[FBUFF_SIZE];
126 // char *c;
127 // vector<int> degree;
128 // if(!DISTRIB) {
129 // // Input is a 'raw' degree sequence d0 d1 d2 d3 ...
130 // while(fgets(buff, FBUFF_SIZE, f)) {
131 // int d = strtol(buff, &c, 10);
132 // if(c == buff) continue;
133 // degree.push_back(d);
134 // total += d;
135 // }
136 // n = int(degree.size());
137 // deg = new int[n];
138 // int *yo = deg;
139 // vector<int>::iterator end = degree.end();
140 // for(vector<int>::iterator it=degree.begin(); it!=end; *(yo++) = *(it++));
141 // }
142 // else {
143 // // Input is a degree distribution : d0 #(degree=d0), d1 #(degree=d1), ...
144 // vector<int> n_with_degree;
145 // int line = 0;
146 // int syntax = 0;
147 // int ignored = 0;
148 // int first_syntax = 0;
149 // int first_ignored = 0;
150 // while(fgets(buff, FBUFF_SIZE, f)) {
151 // line++;
152 // int d = strtol(buff, &c, 10);
153 // if(c == buff) { ignored++; first_ignored = line; continue; }
154 // char *cc;
155 // int i = strtol(c, &cc, 10);
156 // if(cc == c) { syntax++; first_syntax = line; continue; }
157 // n += i;
158 // total += i*d;
159 // degree.push_back(d);
160 // n_with_degree.push_back(i);
161 // if( cc != c) { syntax++; first_syntax = line; }
162 // }
163 // if(VERBOSE()) {
164 // if(ignored > 0) fprintf(stderr,"Ignored %d lines (first was line #%d)\n", ignored, first_ignored);
165 // if(syntax > 0) fprintf(stderr,"Found %d probable syntax errors (first was line #%d)\n", syntax, first_syntax);
166 // }
167 // deg = new int[n];
168 // int *yo = deg;
169 // vector<int>::iterator it_n = n_with_degree.begin();
170 // for(vector<int>::iterator it = degree.begin(); it != degree.end(); it++)
171 // for(int k = *(it_n++); k--; *yo++ = *it);
172 // }
173 // if(VERBOSE()) {
174 // if(total % 2 != 0) fprintf(stderr,"Warning: degree sequence is odd\n");
175 // fprintf(stderr,"Degree sequence created. N=%d, 2M=%d\n", n, total);
176 // }
177 // }
178
179 // n vertices, exponent, min degree, max degree, average degree (optional, default is -1)
180 degree_sequence::
degree_sequence(int _n,double exp,int degmin,int degmax,double z)181 degree_sequence(int _n, double exp, int degmin, int degmax, double z) {
182
183 n = _n;
184 if (exp == 0.0) {
185 // Binomial distribution
186 if (z < 0) {
187 throw std::invalid_argument(
188 "Fatal error in degree_sequence constructor: "
189 "positive average degree must be specified.");
190 }
191 if (degmax < 0) {
192 degmax = n - 1;
193 }
194 total = int(floor(double(n) * z + 0.5));
195 deg = new int[n];
196 KW_RNG::RNG myrand;
197 double p = (z - double(degmin)) / double(n);
198 total = 0;
199 for (int i = 0; i < n; i++) {
200 do {
201 deg[i] = 1 + myrand.binomial(p, n);
202 } while (deg[i] > degmax);
203 total += deg[i];
204 }
205 } else {
206 // Power-law distribution
207 igraph_status("Creating powerlaw sampler...", 0);
208 powerlaw pw(exp, degmin, degmax);
209 if (z == -1.0) {
210 pw.init();
211 igraph_statusf("done. Mean=%f\n", 0, pw.mean());
212 } else {
213 double offset = pw.init_to_mean(z);
214 igraph_statusf("done. Offset=%f, Mean=%f\n", 0, offset, pw.mean());
215 }
216
217 deg = new int[n];
218 total = 0;
219 int i;
220
221 igraph_statusf("Sampling %d random numbers...", 0, n);
222 for (i = 0; i < n; i++) {
223 deg[i] = pw.sample();
224 total += deg[i];
225 }
226
227 igraph_status("done\nSimple statistics on degrees...", 0);
228 int wanted_total = int(floor(z * n + 0.5));
229 sort();
230 igraph_statusf("done : Max=%d, Total=%d.\n", 0, deg[0], total);
231 if (z != -1.0) {
232 igraph_statusf("Adjusting total to %d...", 0, wanted_total);
233 int iterations = 0;
234
235 while (total != wanted_total) {
236 sort();
237 for (i = 0; i < n && total > wanted_total; i++) {
238 total -= deg[i];
239 if (total + degmin <= wanted_total) {
240 deg[i] = wanted_total - total;
241 } else {
242 deg[i] = pw.sample();
243 }
244 total += deg[i];
245 }
246 iterations += i;
247 for (i = n - 1; i > 0 && total < wanted_total; i--) {
248 total -= deg[i];
249 if (total + (deg[0] >> 1) >= wanted_total) {
250 deg[i] = wanted_total - total;
251 } else {
252 deg[i] = pw.sample();
253 }
254 total += deg[i];
255 }
256 iterations += n - 1 - i;
257 }
258 igraph_statusf("done(%d iterations).", 0, iterations);
259 igraph_statusf(" Now, degmax = %d\n", 0, dmax());
260 }
261
262 shuffle();
263 }
264 }
265
266 // void degree_sequence::print() {
267 // for(int i=0; i<n; i++) printf("%d\n",deg[i]);
268 // }
269
270 // void degree_sequence::print_cumul() {
271 // if(n==0) return;
272 // int dmax = deg[0];
273 // int dmin = deg[0];
274 // int i;
275 // for(i=1; i<n; i++) if(dmax<deg[i]) dmax=deg[i];
276 // for(i=1; i<n; i++) if(dmin>deg[i]) dmin=deg[i];
277 // int *dd = new int[dmax-dmin+1];
278 // for(i=dmin; i<=dmax; i++) dd[i-dmin]=0;
279 // if(VERBOSE()) fprintf(stderr,"Computing cumulative distribution...");
280 // for(i=0; i<n; i++) dd[deg[i]-dmin]++;
281 // if(VERBOSE()) fprintf(stderr,"done\n");
282 // for(i=dmin; i<=dmax; i++) if(dd[i-dmin]>0) printf("%d %d\n",i,dd[i-dmin]);
283 // delete[] dd;
284 // }
285
havelhakimi()286 bool degree_sequence::havelhakimi() {
287
288 int i;
289 int dm = dmax() + 1;
290 // Sort vertices using basket-sort, in descending degrees
291 int *nb = new int[dm];
292 int *sorted = new int[n];
293 // init basket
294 for (i = 0; i < dm; i++) {
295 nb[i] = 0;
296 }
297 // count basket
298 for (i = 0; i < n; i++) {
299 nb[deg[i]]++;
300 }
301 // cumul
302 int c = 0;
303 for (i = dm - 1; i >= 0; i--) {
304 int t = nb[i];
305 nb[i] = c;
306 c += t;
307 }
308 // sort
309 for (i = 0; i < n; i++) {
310 sorted[nb[deg[i]]++] = i;
311 }
312
313 // Binding process starts
314 int first = 0; // vertex with biggest residual degree
315 int d = dm - 1; // maximum residual degree available
316
317 for (c = total / 2; c > 0; ) {
318 // We design by 'v' the vertex of highest degree (indexed by first)
319 // look for current degree of v
320 while (nb[d] <= first) {
321 d--;
322 }
323 // store it in dv
324 int dv = d;
325 // bind it !
326 c -= dv;
327 int dc = d; // residual degree of vertices we bind to
328 int fc = ++first; // position of the first vertex with degree dc
329
330 while (dv > 0 && dc > 0) {
331 int lc = nb[dc];
332 if (lc != fc) {
333 while (dv > 0 && lc > fc) {
334 // binds v with sorted[--lc]
335 dv--;
336 lc--;
337 }
338 fc = nb[dc];
339 nb[dc] = lc;
340 }
341 dc--;
342 }
343 if (dv != 0) { // We couldn't bind entirely v
344 delete[] nb;
345 delete[] sorted;
346 return false;
347 }
348 }
349 delete[] nb;
350 delete[] sorted;
351 return true;
352 }
353
354 //*************************
355 // Subroutines definitions
356 //*************************
357
int_adjust(double x)358 inline int int_adjust(double x) {
359 return (int(floor(x + random_float())));
360 }
361
random_permute(int * a,int n)362 void random_permute(int *a, int n) {
363 int j, tmp;
364 for (int i = 0; i < n - 1; i++) {
365 j = i + my_random() % (n - i);
366 tmp = a[i];
367 a[i] = a[j];
368 a[j] = tmp;
369 }
370 }
371
cumul_sort(int * q,int n)372 void cumul_sort(int *q, int n) {
373 // looks for the maximum q[i] and minimum
374 if (n == 0) {
375 return;
376 }
377 int qmax = q[0];
378 int qmin = q[0];
379 int i;
380 for (i = 0; i < n; i++) if (q[i] > qmax) {
381 qmax = q[i];
382 }
383 for (i = 0; i < n; i++) if (q[i] < qmin) {
384 qmin = q[i];
385 }
386
387 // counts #q[i] with given q
388 int *nb = new int[qmax - qmin + 1];
389 for (int *onk = nb + (qmax - qmin + 1); onk != nb; * (--onk) = 0) { }
390 for (i = 0; i < n; i++) {
391 nb[q[i] - qmin]++;
392 }
393
394 // counts cumulative distribution
395 for (i = qmax - qmin; i > 0; i--) {
396 nb[i - 1] += nb[i];
397 }
398
399 // sort by q[i]
400 int last_q;
401 int tmp;
402 int modifier = qmax - qmin + 1;
403 for (int current = 0; current < n; current++) {
404 tmp = q[current];
405 if (tmp >= qmin && tmp <= qmax) {
406 last_q = qmin;
407 do {
408 q[current] = last_q + modifier;
409 last_q = tmp;
410 current = --nb[last_q - qmin];
411 } while ((tmp = q[current]) >= qmin && tmp <= qmax);
412 q[current] = last_q + modifier;
413 }
414 }
415 delete[] nb;
416 for (i = 0; i < n; i++) {
417 q[i] = q[i] - modifier;
418 }
419 }
420
421 } // namespace gengraph
422