Minor documentation edits.
[zddfun.git] / cycle_test.c
blob79959dd960a163337c6fbb422c299a0a95be6045
1 // Confirm that an 2x2 grid graph has 2 simple cycles.
2 // Confirm that an 3x3 grid graph has 14 simple cycles (easily checked by hand).
3 // Confirm that an 8x8 grid graph has 603841648932 simple cycles (see Knuth).
4 //
5 // Sample run below. Results not independently confirmed.
6 // n, cycles in nxn grid graph, average length, standard deviation
7 // 2, 2, 2.000000, 2.000000
8 // 3, 14, 5.714286, 2.249717
9 // 4, 214, 10.710280, 2.771717
10 // 5, 9350, 17.462246, 3.152186
11 // 6, 1222364, 25.768157, 3.724317
12 // 7, 487150372, 35.805114, 4.284285
13 // 8, 603841648932, 47.479949, 4.756736
14 // 9, 2318527339461266, 60.709770, 5.221515
15 // 10, 27359264067916806102, 75.502314, 5.707011
16 // 11, 988808811046283595068100, 91.876952, 6.201849
17 // 12, 109331355810135629946698361372, 109.838303, 6.697420
19 #include <stdint.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <string.h>
23 #include "memo.h"
24 #include "zdd.h"
25 #include <stdarg.h>
26 #include "io.h"
28 struct grid_graph_s {
29 int vcount;
30 int ecount;
31 int *vtab;
32 int *rtab, *ctab;
33 int *au, *av;
35 typedef struct grid_graph_s grid_graph_t[1];
36 typedef struct grid_graph_s *grid_graph_ptr;
38 void grid_graph_init(grid_graph_t gg, int n) {
39 // Label nodes and edges of grid graph. For example, when max = 3 we have
40 // 1 2 4
41 // 3 5 7
42 // 6 8 9
43 int *newintarray(int n) { return malloc(sizeof(int) * n); }
44 int *vtab = gg->vtab = newintarray(n * n);
45 int *rtab = gg->rtab = newintarray(n * n + 1);
46 int *ctab = gg->ctab = newintarray(n * n + 1);
47 gg->vcount = n;
48 int v = 1;
49 int i = 0, j = 0;
50 for(;;) {
51 rtab[v] = i;
52 ctab[v] = j;
53 vtab[i * n + j] = v++;
54 if (i == n - 1) {
55 if (j == n - 1) break;
56 i = j + 1;
57 j = n - 1;
58 } else if (!j) {
59 j = i + 1;
60 i = 0;
61 } else {
62 i++, j--;
66 gg->ecount = n * (n - 1) * 2;
67 // Arcs go from u to v.
68 int *au = gg->au = newintarray(gg->ecount + 1);
69 int *av = gg->av = newintarray(gg->ecount + 1);
70 i = 1;
71 for(v = 1; v <= n * n; v++) {
72 if (ctab[v] != n - 1) {
73 au[i] = v;
74 av[i] = vtab[rtab[v] * n + ctab[v] + 1];
75 i++;
77 if (rtab[v] != n - 1) {
78 au[i] = v;
79 av[i] = vtab[(rtab[v] + 1) * n + ctab[v]];
80 i++;
85 for(int i = 1; i <= zdd_vmax(); i++) {
86 printf("%d -> %d\n", au[i], av[i]);
88 for(int i = 0; i < max; i++) {
89 for(int j = 0; j < max; j++) {
90 printf(" %d", vtab[i][j]);
92 printf("\n");
94 printf("\n");
98 void grid_graph_clear(grid_graph_t gg) {
99 free(gg->vtab);
100 free(gg->rtab);
101 free(gg->ctab);
102 free(gg->au);
103 free(gg->av);
106 void compute_grid_graph(grid_graph_t gg) {
107 int *au = gg->au;
108 int *av = gg->av;
110 zdd_set_vmax(gg->ecount);
112 // Construct ZDD of all simple loops. See Knuth.
113 memo_t node_tab[zdd_vmax() + 1];
114 for(uint16_t v = 1; v <= zdd_vmax(); v++) memo_init(node_tab[v]);
116 uint32_t unique(uint16_t v, uint32_t lo, uint32_t hi) {
117 // Create or return existing node representing !v ? lo : hi.
118 uint32_t key[2] = { lo, hi };
119 memo_it it;
120 int just_created = memo_it_insert_u(&it, node_tab[v], (void *) key, 8);
121 if (just_created) {
122 uint32_t r;
123 memo_it_put(it, (void *) (r = zdd_abs_node(v, lo, hi)));
124 if (!(r << 15)) printf("node #%x\n", r);
125 return r;
127 return (uint32_t) memo_it_data(it);
130 int max = gg->vcount;
131 // By arc e, we have already considered all arcs with sources less than
132 // the current source, au[e], thus nothing we do from now on can affect their
133 // state. Also, av[e] is as least as large as all previous targets we
134 // have considered, so our choices so far cannot possibly influence targets
135 // strictly larger than the current target.
137 // Thus rather than consider our choice's effects on every vertex, we can
138 // restrict our focus to au[e], ..., av[e].
140 // We associate an int with each vertex in our state. -1 means we've already
141 // chosen two edges containing this vertex, so can choose no more. Otherwise
142 // we store the vertex representing the other end: if we haven't yet picked
143 // edges from this vertex, the vertex is its own other end.
145 // When picking an edge that closes a loop, we see if we have picked edges
146 // outside the loop; if so, the set of edges is not a simple loop.
148 // We keep our state in a char *, so this cannot work on 256x256 grids.
149 // Our crit-bit trie uses NULL-terminated strings as keys, so 0 cannot appear
150 // in our state. Thus, in the state:
151 // -1 means we've already picked two edges involving this vertex
152 // n means the other end is n + au[e] - 1
153 memo_t cache[zdd_vmax() + 1];
154 for(int i = 0; i <= zdd_vmax(); i++) memo_init(cache[i]);
155 uint32_t recurse(int e, char *state, int start, int count) {
156 char newstate[max + 1];
157 int newcount = 0;
158 memo_it it = NULL;
159 uint32_t memoize(uint32_t n) {
160 if (it) return (uint32_t) memo_it_put(it, (void *) n);
161 return n;
163 // state == NULL is a special case that we use during the first call.
164 if (!state) {
165 // I really should be using au[] and av[].
166 newstate[0] = 1;
167 newstate[1] = 2;
168 newcount = 2;
169 } else {
170 state[count] = 0;
171 int just_created = memo_it_insert(&it, cache[e], state);
172 if (!just_created) return (uint32_t) memo_it_data(it);
173 // Examine part of state that cannot be affected by future choices,
174 // including whether we include the current edge.
175 // Return false node if it's impossible to continue, otherwise
176 // remove them from the state and continue.
177 int j = au[e] - start;
178 if (j > count - 1) die("bad vertex or edge numbering");
179 for (int i = 0; i < j; i++) {
180 int otherend = state[i];
181 if (otherend != -1 && otherend - 1 != i) {
182 // Vertex start - 1 + i is dangling, and we can no longer connect it
183 // to our loop.
184 return memoize(0);
187 // Copy over the part of the state that is still relevant.
188 while(j < count) {
189 int n = state[j];
190 newstate[newcount++] = n < 0 ? -1 : n + start - au[e];
191 j++;
193 // Add vertices that now matter to our state.
194 j += start;
195 while(j <= av[e]) {
196 newstate[newcount++] = j++ - au[e] + 1;
198 // By now newcount == av[e] - au[e].
201 // If we've come to the last edge...
202 if (e == zdd_vmax()) {
203 if (newstate[0] == 1) {
204 // ...we have the empty graph:
205 return memoize(1);
206 } else {
207 // ...or must need the last edge to finish the
208 // loop; we want !V ? FALSE : TRUE (an elementary family)
209 return memoize(unique(e, 0, 1));
213 // Recurse the case where we don't pick the current edge.
214 uint32_t lo = recurse(e + 1, newstate, au[e], newcount);
216 // Before we recurse the other case, we must check a couple of things.
217 // Let's initially assume we are done if we pick the current edge!
218 uint32_t hi = 1;
219 // Examine the other ends of au[e] and av[e], conveniently located at
220 // the ends of newstate[].
221 int u = newstate[0];
222 int v = newstate[newcount - 1];
223 if (u == -1 || v == -1) {
224 // At least one of the endpoints of the current edge is already busy.
225 hi = 0;
226 } else if (u + au[e] - 1 == av[e]) {
227 // We have a closed a loop. We're good as long as nothing is dangling.
228 for (int i = 1; i < newcount - 1; i++) {
229 if (newstate[i] != -1 && newstate[i] != i + 1) {
230 // Dangling link starting at i + au[e] - 1.
231 hi = 0;
232 break;
235 } else {
236 // Recurse the case when we do pick the current edge. Modify the
237 // state accordingly for the call.
238 newstate[0] = -1;
239 newstate[newcount - 1] = -1;
240 newstate[v - 1] = u;
241 newstate[u - 1] = v;
242 hi = recurse(e + 1, newstate, au[e], newcount);
244 // Compress HI -> FALSE nodes.
245 if (!hi) return memoize(lo);
246 return memoize(unique(e, lo, hi));
248 zdd_push();
249 zdd_set_root(recurse(1, NULL, 0, 0));
250 for(int i = 0; i <= zdd_vmax(); i++) memo_clear(cache[i]);
251 for(uint16_t v = 1; v <= zdd_vmax(); v++) memo_clear(node_tab[v]);
254 int main() {
255 grid_graph_t gg;
257 void printloop(int *v, int vcount) {
258 int *rtab = gg->rtab;
259 int *ctab = gg->ctab;
260 int *au = gg->au;
261 int *av = gg->av;
262 int max = gg->vcount;
263 char pic[2 * max][2 * max];
264 for(int i = 0; i < max; i++) {
265 for(int j = 0; j < max; j++) {
266 pic[2 * i][2 * j] = '.';
267 pic[2 * i][2 * j + 1] = ' ';
268 pic[2 * i + 1][2 * j] = ' ';
269 pic[2 * i + 1][2 * j + 1] = ' ';
271 pic[2 * i][2 * max - 1] = '\0';
272 pic[2 * i + 1][2 * max - 1] = '\0';
275 for(int i = 0; i < vcount; i++) {
276 int e = v[i];
277 int r = rtab[au[e]] + rtab[av[e]];
278 int c = ctab[au[e]] + ctab[av[e]];
279 pic[r][c] = r & 1 ? '|' : '-';
282 /* Plain ASCII output:
283 for(int i = 0; i < 2 * max; i++) puts(pic[i]);
286 for(int i = 0; i < 2 * max - 1; i++) {
287 for(int j = 0; j < 2 * max; j++) {
288 int analyse() {
289 int n = 0;
290 int filled(int x, int y) {
291 if (x >= 0 && x < 2 * max - 1 && y >= 0 && y < 2 * max - 1 &&
292 pic[x][y] != ' ') return 1;
293 return 0;
295 if (filled(i - 1, j)) n += 1;
296 if (filled(i, j - 1)) n += 2;
297 if (filled(i + 1, j)) n += 4;
298 if (filled(i, j + 1)) n += 8;
299 return n;
301 switch(pic[i][j]) {
302 case '|':
303 printf("\u2502");
304 break;
305 case '-':
306 printf("\u2500");
307 break;
308 case '.':
309 switch(analyse()) {
310 case 0:
311 printf("\u00b7");
312 break;
313 case 3:
314 printf("\u2518");
315 break;
316 case 5:
317 printf("\u2502");
318 break;
319 case 6:
320 printf("\u2510");
321 break;
322 case 9:
323 printf("\u2514");
324 break;
325 case 10:
326 printf("\u2500");
327 break;
328 case 12:
329 printf("\u250c");
330 break;
331 default:
332 printf("*");
333 break;
335 break;
336 case '\0':
337 putchar('\n');
338 break;
339 default:
340 putchar(pic[i][j]);
341 break;
345 putchar('\n');
346 putchar('\n');
349 zdd_init();
350 for(int n = 2; n <= 3; n++) {
351 printf("All loops in %dx%d grid graph\n", n, n);
352 grid_graph_init(gg, n);
353 compute_grid_graph(gg);
354 zdd_forall(printloop);
355 zdd_pop();
356 grid_graph_clear(gg);
358 mpz_t z, z1, z2;
359 mpf_t f, f1, f2;
360 mpz_init(z);
361 mpz_init(z1);
362 mpz_init(z2);
363 mpf_init(f);
364 mpf_init(f1);
365 mpf_init(f2);
366 printf(" n, cycles in nxn grid graph, average length, standard deviation\n");
367 for(int n = 2; n <= 12; n++) {
368 grid_graph_init(gg, n);
369 compute_grid_graph(gg);
370 zdd_count_2(z, z1, z2);
371 mpf_set_z(f, z);
372 mpf_set_z(f1, z1);
373 mpf_set_z(f2, z2);
374 mpf_div(f2, f2, f);
375 mpf_div(f, f1, f);
376 mpf_mul(f1, f, f);
377 mpf_sub(f2, f2, f1);
378 mpf_sqrt(f2, f2);
379 gmp_printf("%2d, %Zd, %Ff, %Ff\n", n, z, f, f2);
380 zdd_forlargest(printloop);
381 switch(n) {
382 case 3:
383 EXPECT(!mpz_cmp_ui(z, 14));
384 break;
385 case 8:
387 mpz_t answer;
388 mpz_init(answer);
389 mpz_set_str(answer, "603841648932", 0);
390 EXPECT(!mpz_cmp(z, answer));
391 mpz_clear(answer);
392 break;
395 zdd_pop();
396 grid_graph_clear(gg);
397 fflush(stdout);
399 mpz_clear(z);
400 mpz_clear(z1);
401 mpz_clear(z2);
402 mpf_clear(f);
403 mpf_clear(f1);
404 mpf_clear(f2);