File size: 13,711 Bytes
7e1c831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ee1fb5
7e1c831
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
/* Exact and noisy runs of Lev(N_host) and of its r-fold amplification, written
 * independently of the artifact. A row of the r-fold map reads every copy of its
 * inputs, so all r copies of row i share the pre-activation
 *     z = sum_j w_ij cnt_j + r b_i + (r - 1) / 2,   cnt_j = copies of input j at 1,
 * and copy c outputs H(z) unless its noise crosses the comparator at -1/2, which
 * under centred Gaussian noise of deviation s happens with probability
 * Q(|z + 1/2| / s). Each copy of each row draws that event independently.
 *
 *   nsim FILE exact INST T HSTEPS HISTOUT        exact orbit: histogram of z, K*, A*, output
 *   nsim FILE curve INST s COPIES T SEED R       first step at which a copy of the state deviates
 *   nsim FILE run   s R SEED                     one noisy run of Omega* to its end
 *   nsim FILE fail  s COPIES T SEED R            first wrong majority-read state, per copy
 * INST 0 is the self-describing instance (m_P, r_H), 1 the self-reproducing one.
 */
#include <math.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <omp.h>

static int n, L, M, nnz;
static int *width, *rowoff;          /* rowoff[l]: first row of layer l (1-based layers) */
static int *bias, *fanin, *estart, *esrc, *ew;
static int maxw;
static int PCB, HALT, MEMB;
static int C_WR, C_EOT, C_RW, C_RD, R_IN, R_OUT, HALT_PC;
static unsigned char *blob[5];
static int bloblen[5];

static void rd(void *p, size_t sz, size_t k, FILE *f) { if (fread(p, sz, k, f) != k) { fprintf(stderr, "short\n"); exit(1); } }

static void load(const char *path) {
    FILE *f = fopen(path, "rb");
    int h[4]; rd(h, 4, 4, f); n = h[0]; L = h[1]; M = h[2]; nnz = h[3];
    width = malloc(sizeof(int) * (L + 1)); rd(width, 4, L + 1, f);
    rowoff = malloc(sizeof(int) * (L + 2));
    rowoff[1] = 0; for (int l = 1; l <= L; l++) rowoff[l + 1] = rowoff[l] + width[l];
    maxw = 0; for (int l = 0; l <= L; l++) if (width[l] > maxw) maxw = width[l];
    bias = malloc(sizeof(int) * M); fanin = malloc(sizeof(int) * M); estart = malloc(sizeof(int) * (M + 1));
    for (int i = 0; i < M; i++) { int bf[2]; rd(bf, 4, 2, f); bias[i] = bf[0]; fanin[i] = bf[1]; }
    estart[0] = 0; for (int i = 0; i < M; i++) estart[i + 1] = estart[i] + fanin[i];
    esrc = malloc(sizeof(int) * nnz); ew = malloc(sizeof(int) * nnz);
    for (int e = 0; e < nnz; e++) { int sw[2]; rd(sw, 4, 2, f); esrc[e] = sw[0]; ew[e] = sw[1]; }
    int lay[3]; rd(lay, 4, 3, f); PCB = lay[0]; HALT = lay[1]; MEMB = lay[2];
    int io[7]; rd(io, 4, 7, f);
    C_WR = io[0]; C_EOT = io[1]; C_RW = io[2]; C_RD = io[3]; R_IN = io[4]; R_OUT = io[5]; HALT_PC = io[6];
    for (int k = 0; k < 5; k++) { rd(&bloblen[k], 4, 1, f); blob[k] = malloc(bloblen[k]); rd(blob[k], 1, bloblen[k], f); }
    fclose(f);
}

/* ---- random numbers: xoshiro256** seeded by splitmix64 ---- */
typedef struct { uint64_t s[4]; } rng_t;
static uint64_t splitmix(uint64_t *x) { uint64_t z = (*x += 0x9e3779b97f4a7c15ULL); z = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9ULL; z = (z ^ (z >> 27)) * 0x94d049bb133111ebULL; return z ^ (z >> 31); }
static void rng_seed(rng_t *r, uint64_t seed) { for (int i = 0; i < 4; i++) r->s[i] = splitmix(&seed); }
static inline uint64_t rotl(uint64_t x, int k) { return (x << k) | (x >> (64 - k)); }
static inline uint64_t rng_next(rng_t *r) {
    uint64_t *s = r->s, res = rotl(s[1] * 5, 7) * 9, t = s[1] << 17;
    s[2] ^= s[0]; s[3] ^= s[1]; s[1] ^= s[2]; s[0] ^= s[3]; s[2] ^= t; s[3] = rotl(s[3], 45); return res;
}

/* flip thresholds: a copy at distance k + 1/2 from the comparator flips when a draw is below thr[k] */
#define KMAX 4096
static uint64_t thr[KMAX];
static void set_noise(double s) {
    for (int k = 0; k < KMAX; k++) {
        double q = 0.5 * erfc((k + 0.5) / s / sqrt(2.0));
        double v = q * 18446744073709551616.0;
        thr[k] = v >= 18446744073709551615.0 ? UINT64_MAX : (uint64_t)v;
    }
}

/* ---- the device of Definition 5.1 on copies of the state ---- */
typedef struct { int head; long nout; } devst;

static int get_byte(const unsigned char *x, int r, int j) {     /* majority of the r copies */
    int v = 0;
    for (int k = 0; k < 8; k++) {
        int c = 0; for (int q = 0; q < r; q++) c += x[q * n + MEMB + 8 * j + k];
        v = (v << 1) | (2 * c > r);
    }
    return v;
}
static void put_byte(unsigned char *x, int r, int j, int v) {
    for (int k = 0; k < 8; k++) { int b = (v >> (7 - k)) & 1; for (int q = 0; q < r; q++) x[q * n + MEMB + 8 * j + k] = b; }
}
/* returns the byte emitted, or -1 */
static int device(unsigned char *x, int r, devst *d, const unsigned char *tape, int tlen) {
    int emit = -1;
    if (get_byte(x, r, C_WR) == 1) { emit = get_byte(x, r, R_OUT); put_byte(x, r, R_OUT, 0); d->nout++; }
    if (get_byte(x, r, C_RD) == 1) {
        if (d->head < tlen) { put_byte(x, r, R_IN, tape[d->head]); put_byte(x, r, C_EOT, 0); d->head++; }
        else put_byte(x, r, C_EOT, 1);
    }
    if (get_byte(x, r, C_RW) == 1) d->head = 0;
    put_byte(x, r, C_RD, 0); put_byte(x, r, C_RW, 0); put_byte(x, r, C_WR, 0);
    return emit;
}
static int halted(const unsigned char *x, int r) {
    int c = 0; for (int q = 0; q < r; q++) c += x[q * n + HALT]; return 2 * c > r;
}

/* ---- one step of the r-fold map on x (r copies of n bits) into y ---- */
typedef struct { unsigned char *a, *b; } buf_t;
static buf_t newbuf(int r) { buf_t B; B.a = malloc((size_t)r * maxw); B.b = malloc((size_t)r * maxw); return B; }

/* noisy: rng may be NULL for the exact map. hist (exact, r = 1): counts of k per step. stats: K*, A* */
static void step(const unsigned char *x, unsigned char *y, int r, rng_t *g, buf_t *B,
                 long *hist, int *kstar, int *astar) {
    unsigned char *in = B->a, *out = B->b;
    for (int q = 0; q < r; q++) memcpy(in + (size_t)q * maxw, x + (size_t)q * n, n);
    int half = (r - 1) / 2;
    for (int l = 1; l <= L; l++) {
        int W = width[l], Wp = width[l - 1];
        int active = 0;
        if (astar) for (int j = 0; j < Wp; j++) active += in[j];
        for (int i = 0; i < W; i++) {
            int row = rowoff[l] + i, z = r * bias[row] + half, k1 = 0;
            for (int e = estart[row]; e < estart[row + 1]; e++) {
                int c = 0; for (int q = 0; q < r; q++) c += in[(size_t)q * maxw + esrc[e]];
                z += ew[e] * c; k1 += c;
            }
            int v = z >= 0, kk = z >= 0 ? z : -z - 1;
            if (hist) hist[kk < KMAX ? kk : KMAX - 1]++;
            if (kstar && k1 > *kstar) *kstar = k1;
            if (astar && active - k1 > *astar) *astar = active - k1;
            for (int q = 0; q < r; q++) {
                int o = v;
                if (g && kk < KMAX && thr[kk] && rng_next(g) < thr[kk]) o ^= 1;
                out[(size_t)q * maxw + i] = o;
            }
        }
        unsigned char *t = in; in = out; out = t;
    }
    for (int q = 0; q < r; q++) memcpy(y + (size_t)q * n, in + (size_t)q * maxw, n);
}

static unsigned char *initial(int inst, int r) {
    const unsigned char *m = blob[inst == 0 ? 0 : 2];
    unsigned char *x = calloc((size_t)r * n, 1);
    for (int j = 0; j < 256; j++) put_byte(x, r, j, m[j]);
    return x;
}

int main(int argc, char **argv) {
    load(argv[1]);
    const char *mode = argv[2];
    if (!strcmp(mode, "exact")) {
        int inst = atoi(argv[3]); long T = atol(argv[4]); long H = atol(argv[5]);
        FILE *hf = fopen(argv[6], "wb");
        const unsigned char *tape = blob[inst == 0 ? 1 : 3]; int tlen = bloblen[inst == 0 ? 1 : 3];
        const unsigned char *expect = inst == 0 ? NULL : blob[4];
        unsigned char *x = initial(inst, 1), *y = malloc(n);
        buf_t B = newbuf(1); devst d = {0, 0};
        long *hist = calloc(KMAX, sizeof(long));
        int kstar = 0, astar = 0, kstar_h = 0, astar_h = 0; long t; long wrong = 0; long out = 0;
        unsigned char *outbuf = malloc(1 << 20);
        for (t = 0; t < T; t++) {
            memset(hist, 0, KMAX * sizeof(long));
            step(x, y, 1, NULL, &B, hist, &kstar, &astar);
            if (t < H) { fwrite(hist, sizeof(long), 64, hf); kstar_h = kstar; astar_h = astar; }
            int e = device(y, 1, &d, tape, tlen);
            if (e >= 0) { if (expect && (out >= bloblen[4] || e != expect[out])) wrong++; if (out < (1 << 20)) outbuf[out] = e; out++; }
            memcpy(x, y, n);
            if (halted(x, 1)) { t++; break; }
        }
        fclose(hf);
        printf("{\"steps\": %ld, \"halted\": %d, \"bytes\": %ld, \"wrong_bytes\": %ld, \"Kstar_first\": %d, \"Astar_first\": %d, \"Kstar\": %d, \"Astar\": %d, \"head\": %d}\n",
               t, halted(x, 1), out, wrong, kstar_h, astar_h, kstar, astar, d.head);
        return 0;
    }
    if (!strcmp(mode, "curve")) {
        int inst = atoi(argv[3]); double s = atof(argv[4]); int C = atoi(argv[5]); long T = atol(argv[6]);
        uint64_t seed = strtoull(argv[7], NULL, 10); int r = atoi(argv[8]);
        set_noise(s);
        const unsigned char *tape = blob[inst == 0 ? 1 : 3]; int tlen = bloblen[inst == 0 ? 1 : 3];
        /* the exact orbit and the device state along it */
        unsigned char *orb = malloc((size_t)(T + 1) * r * n); int *heads = malloc(sizeof(int) * (T + 1));
        unsigned char *x0 = initial(inst, r); memcpy(orb, x0, (size_t)r * n);
        buf_t B = newbuf(r); devst d = {0, 0}; heads[0] = 0;
        for (long t = 0; t < T; t++) {
            step(orb + (size_t)t * r * n, orb + (size_t)(t + 1) * r * n, r, NULL, &B, NULL, NULL, NULL);
            device(orb + (size_t)(t + 1) * r * n, r, &d, tape, tlen); heads[t + 1] = d.head;
        }
        long *first = calloc(C, sizeof(long));
        #pragma omp parallel
        {
            buf_t Bt = newbuf(r); unsigned char *y = malloc((size_t)r * n);
            #pragma omp for schedule(dynamic, 1)
            for (int c = 0; c < C; c++) {
                rng_t g; rng_seed(&g, seed * 1000003ULL + c);
                for (long t = 0; t < T; t++) {
                    step(orb + (size_t)t * r * n, y, r, &g, &Bt, NULL, NULL, NULL);
                    devst dc = {heads[t], 0};
                    device(y, r, &dc, tape, tlen);
                    if (memcmp(y, orb + (size_t)(t + 1) * r * n, (size_t)r * n)) { first[c] = t + 1; break; }
                }
            }
        }
        printf("[");
        for (int c = 0; c < C; c++) printf("%ld%s", first[c], c + 1 < C ? ", " : "");
        printf("]\n");
        return 0;
    }
    if (!strcmp(mode, "run")) {
        double s = atof(argv[3]); int r = atoi(argv[4]); uint64_t seed = strtoull(argv[5], NULL, 10);
        set_noise(s);
        const unsigned char *tape = blob[3]; int tlen = bloblen[3]; const unsigned char *expect = blob[4];
        unsigned char *x = initial(1, r), *y = malloc((size_t)r * n);
        buf_t B = newbuf(r); devst d = {0, 0}; rng_t g; rng_seed(&g, seed);
        long t, out = 0; const char *why = "steps exhausted";
        for (t = 0; t < 4000000; t++) {
            step(x, y, r, &g, &B, NULL, NULL, NULL);
            int e = device(y, r, &d, tape, tlen);
            memcpy(x, y, (size_t)r * n);
            if (e >= 0) {
                if (out >= bloblen[4] || e != expect[out]) { why = "a wrong byte"; t++; break; }
                out++;
            }
            if (halted(x, r)) { why = out == bloblen[4] ? "the whole instance" : "the halt bit set"; t++; break; }
        }
        printf("{\"s\": %g, \"r\": %d, \"seed\": %llu, \"steps\": %ld, \"bytes\": %ld, \"outcome\": \"%s\"}\n",
               s, r, (unsigned long long)seed, t, out, why);
        return 0;
    }
    if (!strcmp(mode, "fail")) {
        double s = atof(argv[3]); int C = atoi(argv[4]); long T = atol(argv[5]);
        uint64_t seed = strtoull(argv[6], NULL, 10); int r = atoi(argv[7]);
        set_noise(s);
        const unsigned char *tape = blob[1]; int tlen = bloblen[1];
        /* the exact orbit, one copy suffices: majority reads are compared with it */
        unsigned char *orb = malloc((size_t)(T + 1) * n); int *heads = malloc(sizeof(int) * (T + 1));
        unsigned char *x0 = initial(0, 1); memcpy(orb, x0, n);
        buf_t B = newbuf(1); devst d = {0, 0}; heads[0] = 0;
        for (long t = 0; t < T; t++) {
            step(orb + (size_t)t * n, orb + (size_t)(t + 1) * n, 1, NULL, &B, NULL, NULL, NULL);
            device(orb + (size_t)(t + 1) * n, 1, &d, tape, tlen); heads[t + 1] = d.head;
        }
        long *first = calloc(C, sizeof(long));
        #pragma omp parallel
        {
            buf_t Bt = newbuf(r); unsigned char *x = malloc((size_t)r * n), *y = malloc((size_t)r * n);
            #pragma omp for schedule(dynamic, 1)
            for (int c = 0; c < C; c++) {
                rng_t g; rng_seed(&g, seed * 1000003ULL + c);
                unsigned char *xi = initial(0, r); memcpy(x, xi, (size_t)r * n); free(xi);
                for (long t = 0; t < T; t++) {
                    step(x, y, r, &g, &Bt, NULL, NULL, NULL);
                    devst dc = {heads[t], 0};
                    device(y, r, &dc, tape, tlen);
                    int bad = 0;
                    for (int i = 0; i < n && !bad; i++) {
                        int cnt = 0; for (int q = 0; q < r; q++) cnt += y[(size_t)q * n + i];
                        bad = (2 * cnt > r) != orb[(size_t)(t + 1) * n + i];
                    }
                    if (bad) { first[c] = t + 1; break; }
                    memcpy(x, y, (size_t)r * n);
                }
            }
        }
        printf("[");
        for (int c = 0; c < C; c++) printf("%ld%s", first[c], c + 1 < C ? ", " : "");
        printf("]\n");
        return 0;
    }
    fprintf(stderr, "unknown mode\n");
    return 1;
}