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