/* Independent exhaustive zero-forcing search for P(n,k) by FIRST-FORCE * NORMALISATION (Stage 5 robustness check; written independently of * code/brute_force.c: different reduction principle, different closure * algorithm, different enumeration scheme). * * Principle (Lemma 3.5 argument, results/derivations.md): if S is a * zero-forcing set with |S| = s < 2n, some first force x -> y exists, so * x in S, y not in S, and the two other neighbours of x are in S. The * directed edges of P(n,k) fall into four orbits under the dihedral group * D_n (which acts by automorphisms), with representatives * u0 -> v0, u0 -> u1, v0 -> u0, v0 -> v_k. * Hence a zero-forcing s-set exists iff for one of the four representatives * (x,y) some s-set S with {x} u (N(x) \ {y}) subset S, y not in S, forces. * We therefore enumerate all (s-3)-subsets T of V \ (N[x]) and test * S = {x} u (N(x)\{y}) u T. Total work 4 * C(2n-4, s-3). * * Closure: counter/worklist algorithm (each black vertex keeps the number * of its white neighbours; a black vertex with count 1 is pushed and * performs its force), NOT the pass-based bitmask loop of brute_force.c. * * usage: ff_brute n k s [threads] * output: one line per representative and a total line; a witness is * printed for the first forcing set found (if any). */ #include #include #include #include #include static int n, k, N, s; static int nbr[128][3]; /* neighbour lists */ static int rep_x[4], rep_y[4]; static void build_graph(void) { N = 2 * n; for (int i = 0; i < n; i++) { int u = i, v = n + i; nbr[u][0] = (i + n - 1) % n; nbr[u][1] = (i + 1) % n; nbr[u][2] = v; nbr[v][0] = n + (i + n - k) % n; nbr[v][1] = n + (i + k) % n; nbr[v][2] = u; } /* sanity: simple cubic graph, symmetric adjacency */ for (int w = 0; w < N; w++) { for (int a = 0; a < 3; a++) { int t = nbr[w][a]; if (t == w) { fprintf(stderr, "loop at %d\n", w); exit(3); } for (int b = 0; b < a; b++) if (nbr[w][b] == t) { fprintf(stderr, "multi-edge at %d\n", w); exit(3); } int back = 0; for (int b = 0; b < 3; b++) if (nbr[t][b] == w) back = 1; if (!back) { fprintf(stderr, "asymmetric adjacency %d %d\n", w, t); exit(3); } } } rep_x[0] = 0; rep_y[0] = n; /* u0 -> v0 */ rep_x[1] = 0; rep_y[1] = 1; /* u0 -> u1 */ rep_x[2] = n; rep_y[2] = 0; /* v0 -> u0 */ rep_x[3] = n; rep_y[3] = n + k; /* v0 -> v_k */ } /* returns 1 iff the closure of the set given by black[] is V */ static int forces_all(unsigned char *black) { int cnt[128], queue[128], qh = 0, qt = 0, nblack = 0; for (int w = 0; w < N; w++) if (black[w]) nblack++; for (int w = 0; w < N; w++) { if (!black[w]) continue; int c = 0; for (int a = 0; a < 3; a++) if (!black[nbr[w][a]]) c++; cnt[w] = c; if (c == 1) queue[qt++] = w; } while (qh < qt) { int b = queue[qh++]; if (cnt[b] != 1) continue; /* stale entry */ int y = -1; for (int a = 0; a < 3; a++) if (!black[nbr[b][a]]) { y = nbr[b][a]; break; } if (y < 0) continue; black[y] = 1; nblack++; if (nblack == N) return 1; /* y became black: update counters of y's black neighbours, and y's own counter */ int cy = 0; for (int a = 0; a < 3; a++) { int t = nbr[y][a]; if (black[t]) { cnt[t]--; if (cnt[t] == 1) queue[qt++] = t; } else cy++; } cnt[y] = cy; if (cy == 1) queue[qt++] = y; } return nblack == N; } /* work items: (representative r, first element index) */ static int nthreads = 1; static volatile long next_item = 0; static pthread_mutex_t mu = PTHREAD_MUTEX_INITIALIZER; static unsigned long long tot_enum[4], tot_forcing[4]; static int witness_found = 0; static unsigned char witness[128]; typedef struct { int id; } targ; static void *worker(void *arg) { (void)arg; int free_list[128], nfree; int idx[128]; unsigned char base[128], black[128]; unsigned long long enumerated = 0, forcing = 0; for (;;) { pthread_mutex_lock(&mu); long item = next_item++; pthread_mutex_unlock(&mu); int r = (int)(item / N), first = (int)(item % N); if (r >= 4) break; int x = rep_x[r], y = rep_y[r]; memset(base, 0, N); base[x] = 1; for (int a = 0; a < 3; a++) if (nbr[x][a] != y) base[nbr[x][a]] = 1; nfree = 0; for (int w = 0; w < N; w++) if (!base[w] && w != y) free_list[nfree++] = w; int t = s - 3; if (t < 0) continue; /* enumerate t-subsets of free_list whose smallest index is `first` */ if (first >= nfree) continue; if (t == 0) { if (first != 0) continue; memcpy(black, base, N); enumerated++; if (forces_all(black)) { forcing++; pthread_mutex_lock(&mu); if (!witness_found) { witness_found = 1; memcpy(witness, base, N); } pthread_mutex_unlock(&mu); } continue; } if (nfree - first < t) continue; idx[0] = first; for (int i = 1; i < t; i++) idx[i] = first + i; for (;;) { memcpy(black, base, N); for (int i = 0; i < t; i++) black[free_list[idx[i]]] = 1; enumerated++; if (forces_all(black)) { forcing++; pthread_mutex_lock(&mu); if (!witness_found) { witness_found = 1; memcpy(witness, base, N); for (int i = 0; i < t; i++) witness[free_list[idx[i]]] = 1; } pthread_mutex_unlock(&mu); } /* next combination with idx[0] fixed */ int i = t - 1; while (i >= 1 && idx[i] == nfree - t + i) i--; if (i < 1) break; idx[i]++; for (int j = i + 1; j < t; j++) idx[j] = idx[j - 1] + 1; } pthread_mutex_lock(&mu); tot_enum[r] += enumerated; tot_forcing[r] += forcing; pthread_mutex_unlock(&mu); enumerated = forcing = 0; } return NULL; } int main(int argc, char **argv) { if (argc < 4) { fprintf(stderr, "usage: ff_brute n k s [threads]\n"); return 2; } n = atoi(argv[1]); k = atoi(argv[2]); s = atoi(argv[3]); nthreads = argc > 4 ? atoi(argv[4]) : 1; if (n > 64 || n < 2 * k + 1) { fprintf(stderr, "need 2k+1 <= n <= 64\n"); return 2; } build_graph(); pthread_t th[64]; for (int i = 0; i < nthreads; i++) pthread_create(&th[i], NULL, worker, NULL); for (int i = 0; i < nthreads; i++) pthread_join(th[i], NULL); const char *names[4] = {"u0->v0", "u0->u1", "v0->u0", "v0->vk"}; unsigned long long E = 0, F = 0; for (int r = 0; r < 4; r++) { printf("n=%d k=%d s=%d rep=%s enumerated=%llu forcing=%llu\n", n, k, s, names[r], tot_enum[r], tot_forcing[r]); E += tot_enum[r]; F += tot_forcing[r]; } printf("n=%d k=%d s=%d TOTAL enumerated=%llu forcing=%llu", n, k, s, E, F); if (witness_found) { printf(" witness="); int first = 1; for (int w = 0; w < N; w++) if (witness[w]) { printf("%s%c%d", first ? "" : ",", w < n ? 'u' : 'v', w < n ? w : w - n); first = 0; } } printf("\n"); return 0; }