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).
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
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
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);
53 vtab
[i
* n
+ j
] = v
++;
55 if (j
== n
- 1) break;
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);
71 for(v
= 1; v
<= n
* n
; v
++) {
72 if (ctab
[v
] != n
- 1) {
74 av
[i
] = vtab
[rtab
[v
] * n
+ ctab
[v
] + 1];
77 if (rtab
[v
] != n
- 1) {
79 av
[i
] = vtab
[(rtab
[v
] + 1) * n
+ ctab
[v
]];
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]);
98 void grid_graph_clear(grid_graph_t gg
) {
106 void compute_grid_graph(grid_graph_t gg
) {
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
};
120 int just_created
= memo_it_insert_u(&it
, node_tab
[v
], (void *) key
, 8);
123 memo_it_put(it
, (void *) (r
= zdd_abs_node(v
, lo
, hi
)));
124 if (!(r
<< 15)) printf("node #%x\n", 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];
159 uint32_t memoize(uint32_t n
) {
160 if (it
) return (uint32_t) memo_it_put(it
, (void *) n
);
163 // state == NULL is a special case that we use during the first call.
165 // I really should be using au[] and av[].
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
187 // Copy over the part of the state that is still relevant.
190 newstate
[newcount
++] = n
< 0 ? -1 : n
+ start
- au
[e
];
193 // Add vertices that now matter to our state.
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:
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!
219 // Examine the other ends of au[e] and av[e], conveniently located at
220 // the ends of newstate[].
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.
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.
236 // Recurse the case when we do pick the current edge. Modify the
237 // state accordingly for the call.
239 newstate
[newcount
- 1] = -1;
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
));
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
]);
257 void printloop(int *v
, int vcount
) {
258 int *rtab
= gg
->rtab
;
259 int *ctab
= gg
->ctab
;
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
++) {
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
++) {
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;
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;
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
);
356 grid_graph_clear(gg
);
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
);
379 gmp_printf("%2d, %Zd, %Ff, %Ff\n", n
, z
, f
, f2
);
380 zdd_forlargest(printloop
);
383 EXPECT(!mpz_cmp_ui(z
, 14));
389 mpz_set_str(answer
, "603841648932", 0);
390 EXPECT(!mpz_cmp(z
, answer
));
396 grid_graph_clear(gg
);