/machines/1RB1RF_1LC0LE_1RD0LB_1RA0RC_1RE1LB_0RD---/check_proof.py · raw
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 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | #!/usr/bin/env python3 """check_proof.py: simulation evidence for the DELTA proof of twin #25 (1RB1RF_1LC0LE_1RD0LB_1RA0RC_1RE1LB_0RD---). The parent is kill #3, 1RB0LD_1RC0RA_1RD1RF_1LA0LE_1RE1LD_0RB--- (KILLED_NONHALT). This script imports the parent's check_proof.py read-only, for its abstract map (the rules of the parent's proof.md section 2), its INV+(e) checker and its cycle-pair checker. Everything that depends on the machine or on the start is re-run here on the twin: the twin's own transition table is simulated directly, and the twin's own orbit is checked. Nothing here is a proof. Every claim it checks is a hypothesis until Rocq accepts it. Sections (each prints PASS/FAIL lines; the SUMMARY is the conjunction of all of them): S the relation: sigma = (A C)(B D) is the only state renaming, with or without mirroring, that takes the parent's table to the twin's, and sigma(A) = C; so the twin's blank-tape run is the parent's run from 0^inf C> 0^inf, relabelled (checked step by step), not the parent's own run (which has 11 C> at step 2) A every macro rule of the parent's proof.md section 2, from random instances, simulated on the TWIN's table (canonical head state C = sigma(A)), against the parent's abstract map C the twin's initial segment: blank tape -> K(2; (11,13,0); -) at step 1,088 D the twin's direct simulation from blank vs the abstract map, transition by transition; the step of overflow 6 and the base E-start K(2; (95,68,0),(34,33,1),(10,15,1),(1,26,1); -) B the base: INV+(e) clause by clause (parent's inv_violations), and the Rocq J_base clauses (PC E1 of the parent's .v, transcribed) at the twin's base G the twin's orbit from its base: parent's section G2 (every lemma of proof.md 3.8 at every cycle pair) and section F (every clause of INV at every cycle), to L* > lmax; the parent's rocq/jcheck.py invariant J, macro step by macro step, for a number of re-pairings X the coupling: twin and parent abstract orbits, compared at every macro step (L <= lx) and at every re-pairing (L <= lmax): same rule sequence, and exactly one block differs, by +2, and it is the far block Usage: python3 -B check_proof.py [--quick] [--seed N] [--steps N] [--lmax N] [--lx N] [--jcyc N] """ import os, sys, random, argparse, itertools, importlib.util from collections import Counter sys.dont_write_bytecode = True TM = '1RB1RF_1LC0LE_1RD0LB_1RA0RC_1RE1LB_0RD---' PARENT = '1RB0LD_1RC0RA_1RD1RF_1LA0LE_1RE1LD_0RB---' HERE = os.path.dirname(os.path.abspath(__file__)) PDIR = os.path.join(os.path.dirname(HERE), PARENT) def load(name, path): spec = importlib.util.spec_from_file_location(name, path) mod = importlib.util.module_from_spec(spec); spec.loader.exec_module(mod) return mod cp = load('parent_check_proof', os.path.join(PDIR, 'check_proof.py')) # parent's checks (read-only) assert cp.TM == PARENT ST = 'ABCDEF' SIGMA = {'A': 'C', 'B': 'D', 'C': 'A', 'D': 'B', 'E': 'E', 'F': 'F'} # parent state -> twin state TAB = cp.parse_tm(TM) # (state index, read) -> (write, move, next) | None CANON = ST.index(SIGMA['A']) # the twin's K head state: C INIT_STEP = 1088 INIT_K = (2, [(11, 13, 0)], None) BASE_K = (2, [(95, 68, 0), (34, 33, 1), (10, 15, 1), (1, 26, 1)], None) BASE_STEP = 79114 # checked in section D (parent: 78,710) PARENT_BASE_K = (2, [(95, 68, 0), (34, 33, 1), (10, 15, 1), (1, 24, 1)], None) def letters(s): t = {} for i, blk in enumerate(s.split('_')): for r in (0, 1): x = blk[3*r:3*r+3] t[(ST[i], r)] = None if x == '---' else (int(x[0]), x[1], x[2]) return t # ---------------------------------------------------------------- S: the relation def image(tp, sig, mirror): out = {} for (q, r), v in tp.items(): out[(sig[q], r)] = None if v is None else (v[0], {'L': 'R', 'R': 'L'}[v[1]] if mirror else v[1], sig[v[2]]) return out def lockstep(n): """Twin from 0^inf A> 0^inf vs parent from 0^inf C> 0^inf: same tape, same position, twin state = sigma(parent state).""" tp, tt = cp.TAB, TAB sg = [ST.index(SIGMA[c]) for c in ST] size = 2*n + 16 a = bytearray(size); b = bytearray(size); pa = pb = n + 8 sa, sb = 0, ST.index('C') # twin starts in A, parent in C for step in range(n): if sa != sg[sb] or pa != pb or a[pa] != b[pb]: return step ta, tb = tt[(sa, a[pa])], tp[(sb, b[pb])] if (ta is None) != (tb is None): return step if ta is None: return None a[pa] = ta[0]; pa += ta[1]; sa = ta[2] b[pb] = tb[0]; pb += tb[1]; sb = tb[2] return None if a == b else -1 def section_S(n): print('== S. relation to the parent: renaming / mirror search, start state, lockstep') tp, tt = letters(PARENT), letters(TM) sols = [(dict(zip(ST, perm)), mir) for perm in itertools.permutations(ST) for mir in (False, True) if image(tp, dict(zip(ST, perm)), mir) == tt] uniq = len(sols) == 1 and sols[0] == (SIGMA, False) print(' maps phi with twin = phi(parent) among 720 renamings x {plain, mirror}: %s' % [(''.join(s[c] for c in ST), 'mirror' if m else 'plain') for s, m in sols]) print(' unique, no mirror, sigma = (A C)(B D) (A->C B->D C->A D->B, E, F fixed) ->', 'PASS' if uniq else 'FAIL') inv = all(SIGMA[SIGMA[c]] == c for c in ST) print(' sigma is an involution (sigma^-1 = sigma) ->', 'PASS' if inv else 'FAIL') st = SIGMA['A'] print(' twin start state A = sigma(%s): the twin from blank is sigma(parent from 0^inf C> 0^inf)' % [c for c in ST if SIGMA[c] == 'A'][0]) r = lockstep(n) okl = r is None print(' lockstep %d steps, twin from blank vs relabelled parent from 0^inf C> 0^inf: %s -> %s' % ( n, 'identical at every step' if okl else 'differ at step %s' % r, 'PASS' if okl else 'FAIL')) # the parent's own run: A0 = 1RB, B0 = 1RC, so step 2 is 0^inf 1 1 C> 0^inf (not 0^inf C> 0^inf) t1, t2 = cp.TAB[(0, 0)], cp.TAB[(1, 0)] own = t1 == (1, 1, 1) and t2 == (1, 1, 2) print(' parent from its own blank start: A0=1RB, B0=1RC, so step 2 is 0^inf 1 1 C> 0^inf; the twin lacks that 11 ->', 'PASS' if own else 'FAIL') return uniq and inv and okl and own and st == 'C' # ---------------------------------------------------------------- A: the parent's rules on the twin's table def run_macro(h, P, T, cap=None): """Parent's run_macro, on the twin's table, with the twin's canonical head state C (= sigma(A)).""" w = cp.build(h, P, T); n = len(w) if cap is None: cap = 400*n*n + 100000 pad = 4*n + 64 tape = bytearray(pad) + bytearray(int(c) for c in w) + bytearray(pad) pos = pad - 1; st = CANON; lo = pos; steps = 0 while steps < cap: tr = TAB[(st, tape[pos])] if tr is None: return ('HALT', steps, st) tape[pos] = tr[0]; pos += tr[1]; st = tr[2]; steps += 1 if pos < 0 or pos >= len(tape): ext = bytearray(len(tape)) if pos < 0: tape = ext + tape; pos += len(ext); lo += len(ext) else: tape = tape + ext if pos < lo: lo = pos if st == CANON and pos <= lo + 1 and tape[pos] == 0 and not any(tape[lo:pos]): r = cp.parse(''.join('1' if b else '0' for b in tape[pos+1:])) if r is not None and r[0] != '?': return (r, steps) return ('CAP', steps) def section_A(n, rng): print('== A. the parent\'s macro rules (proof.md section 2 of the parent) on the TWIN\'s table, head state C') allok = True; tot = 0 for rule in cp.RULES: ok = 0; fails = [] for i in range(n): h, P, T = cp.gen(rule, rng, i) exp = cp.astep(h, P, T) assert exp[3] == rule, (rule, exp[3], h, P, T) got = run_macro(h, P, T) good = got[0] not in ('HALT', 'CAP') and got[0] == (exp[0], exp[1], exp[2]) and got[1] >= 1 ok += good if not good: fails.append((h, P, T, str(got)[:120])) tot += n; allok &= (ok == n) print(' %-14s %3d/%d %s' % (rule, ok, n, 'PASS' if ok == n else 'FAIL ' + str(fails[:2]))) # the halting cell, relabelled: parent halts on F1, entered only from C1 (=1RF), entered only from B0; # twin: F1 is ---, F is entered only from A1 (=1RF), A is entered only from D0 (=1RA) entF = sorted(ST[q] + str(r) for (q, r), v in TAB.items() if v and v[2] == 5) entA = sorted(ST[q] + str(r) for (q, r), v in TAB.items() if v and v[2] == 0) okh = TAB[(5, 1)] is None and entF == ['A1'] and entA == ['D0'] print(' halting transition: F1 undefined; F entered only from %s, A only from %s; so the twin halts exactly at D> 0 1 1 ->' % (entF, entA), 'PASS' if okh else 'FAIL') print(' A total: %s (%d instances)' % ('PASS' if allok and okh else 'FAIL', tot)) return allok and okh # ---------------------------------------------------------------- C, D: the twin's initial segment, direct cross-check def stream_canonical(max_steps): """Direct simulation of the twin from the blank tape; yields (step, K) at every canonical record config (state C on a new leftmost cell, tape to its right parses as some K).""" size = 1 << 16 tape = bytearray(size); pos = size//2; st = 0; lo = pos; hi = pos; steps = 0 while steps < max_steps: tr = TAB[(st, tape[pos])] if tr is None: yield (steps, 'HALT'); return tape[pos] = tr[0]; pos += tr[1]; st = tr[2]; steps += 1 if pos < 0 or pos >= len(tape): ext = bytearray(len(tape)) if pos < 0: tape = ext + tape; pos += len(ext); lo += len(ext); hi += len(ext) else: tape = tape + ext if pos > hi: hi = pos if pos < lo: lo = pos if st == CANON: r = cp.parse(tape[pos+1:hi+1].translate(cp.BITS).decode()) if r is not None and r[0] != '?': yield (steps, r) def section_CD(max_steps): print('== C. the twin\'s initial segment') seq = list(stream_canonical(max_steps)) halted = any(k == 'HALT' for _, k in seq) pre = [(s, k) for s, k in seq if s < INIT_STEP] first = [x for x in seq if x[0] == INIT_STEP] okC = bool(first) and first[0][1] == INIT_K and not halted print(' canonical configs before step %d: %s' % (INIT_STEP, pre)) if len(pre) >= 3: try: nx = cp.astep(*pre[1][1]) print(' step %d is in D: %s predicts %s, observed %s at step %d (%s); that successor has L = 0, outside D' % (pre[1][0], nx[3], nx[:3], pre[2][1], pre[2][0], 'match' if nx[:3] == pre[2][1] else 'MISMATCH')) except cp.Outside as e: print(' step %d: outside (%s)' % (pre[1][0], e)) print(' step %d: %s -> %s' % (INIT_STEP, first[0][1] if first else None, 'PASS' if okC else 'FAIL')) print('== D. the twin\'s direct simulation from blank (%d steps) vs the parent\'s abstract map' % max_steps) cnt = Counter(); bad = 0; rep = 0; base = None; reps = [] post = [x for x in seq if x[0] >= INIT_STEP and x[1] != 'HALT'] for (s0, k0), (s1, k1) in zip(post, post[1:]): try: h, P, T, name = cp.astep(*k0) if (h, P, T) == (k1[0], k1[1], k1[2]): cnt[name] += 1 if name in ('Ov', 'X1'): rep += 1; reps.append(s1) if rep == 6: base = (s1, k1) else: bad += 1; print(' MISMATCH at step', s0, k0, '->', k1, 'predicted', (h, P, T)) except cp.Outside as e: bad += 1; print(' OUTSIDE at step', s0, e) okD = bad == 0 and len(post) > 1 and not halted print(' %d consecutive canonical transitions, %s; last canonical step %d -> %s' % ( sum(cnt.values()), dict(cnt), post[-1][0], 'PASS' if okD else 'FAIL')) print(' re-pairings (Ov/X1) at steps:', reps[:12], '...' if len(reps) > 12 else '') okB = base is not None and base[0] == BASE_STEP and base[1] == BASE_K print(' base E-start (after re-pairing 6): step %s, %s -> %s' % (base[0] if base else None, base[1] if base else None, 'PASS' if okB else 'FAIL')) return okC, okD and okB # ---------------------------------------------------------------- B: INV+(e) and the Rocq J clauses at the base def dv(P): v = 0 for (_, _, d) in reversed(P): v = d + 2*v return v def PC_E1(h, i, p, q, n, tv): """PC E1 false h i p q n tv of the parent's .v (Definitions PN, PI, PC), transcribed literally.""" w = 2**n pn = (q >= tv + w + 1 and p + tv + w >= 3) if n == 0 else \ (q >= tv + 3*w + 1 and (p + tv) % 2 == 1 and p + tv + w >= 3*w) if i == 1: pi = h % 2 == 0 and 2 <= n and p + tv + w + 3 >= 11*w and h + 4*(tv + w) + 6 >= 12*w elif i == 2: pi = p + tv + w + 1 >= 7*w else: pi = True return 1 <= p and 1 <= q and pn and pi def J_base(K): """J (mkst h P TN E1 false): P <> [], TC E1 false TN (T = TN, c4 = false), AllI (PCx E1 false h) 1 P.""" h, P, T = K if not P or T is not None: return False, [] res = [PC_E1(h, i+1, p, q, len(P)-1-i, dv(P[i:])) for i, (p, q, d) in enumerate(P)] return all(res), res def section_B(): print('== B. the base E-start: INV+(e) (parent proof.md 3.8.1) and the Rocq J_base clauses') h, P, T = BASE_K v = cp.inv_violations(*BASE_K) Ls = cp.Lstar(P, T) print(' %s: L* = %d; (b) q_1+1=%d >= %d, q_2=%d >= %d, q_3=%d >= %d; (c) p+d = %d, %d, %d odd; far pair (p,q) = (%d,%d), q >= 6;' ' (e1) p_1+1=%d >= %d, (e2) p_2=%d >= %d' % ( BASE_K, Ls, P[0][1]+1, 5*2**(Ls-1), P[1][1], 5*2**(Ls-2), P[2][1], 5*2**(Ls-3), P[0][0]+P[0][2], P[1][0]+P[1][2], P[2][0]+P[2][2], P[3][0], P[3][1], P[0][0]+1, 2**(Ls+2), P[1][0], 2**Ls)) okI = not v print(' INV+(e) violations:', v or 'none', '->', 'PASS' if okI else 'FAIL') okJ, res = J_base(BASE_K) okJp, resp = J_base(PARENT_BASE_K) print(' Rocq J (mkst 2 base TN E1 false), PC E1 per pair:', res, '-> twin', 'PASS' if okJ else 'FAIL', '| parent base (sanity):', 'PASS' if okJp else 'FAIL') # the only difference from the parent's base is the far q (24 -> 26); in J it enters only PN E1 at n = 0 diff = [(j+1, a, b) for j, (a, b) in enumerate(zip(P, PARENT_BASE_K[1])) if a != b] okd = diff == [(4, (1, 26, 1), (1, 24, 1))] print(' difference from the parent\'s base: %s (far pair only; J uses it only in q >= tv + 2^0 + 1 = 3) ->' % diff, 'PASS' if okd else 'FAIL') return okI and okJ and okJp and okd # ---------------------------------------------------------------- G: the twin's orbit through the parent's checks def section_G(lmax, jcyc): print('== G. the twin\'s orbit from its base through the parent\'s checks (G2, F) and the parent\'s jcheck J') saved = (cp.INIT_K, cp.BASE_K) cp.INIT_K, cp.BASE_K = INIT_K, BASE_K # the parent's orbit_e/class_invariants read these globals try: r2 = cp.orbit_e(lmax) rF = cp.class_invariants(lmax) finally: cp.INIT_K, cp.BASE_K = saved jc = load('parent_jcheck', os.path.join(PDIR, 'rocq', 'jcheck.py')) Pb = [list(x) for x in BASE_K[1]] res = jc.run(BASE_K[0], Pb, ('N',), ('E1', False), jcyc) okj = res[0] == 'OK' print(' jcheck J (parent rocq/jcheck.py) at every macro step from the twin\'s base, %d re-pairings: %s %s rules %s -> %s' % (jcyc, res[0], res[1] or '', res[3], 'PASS' if okj else 'FAIL')) ok = r2 and rF and okj print(' G total:', 'PASS' if ok else 'FAIL') return ok # ---------------------------------------------------------------- X: the coupling with the parent's orbit def block_diff(ka, kb): """Twin K kb vs parent K ka. Returns None if h, L, digits or tail type differ; else the list of (kind, index, delta) over all exponents (p_j, q_j, tail parameter) that differ.""" (ha, Pa, Ta), (hb, Pb, Tb) = ka, kb if ha != hb or len(Pa) != len(Pb) or [x[2] for x in Pa] != [x[2] for x in Pb]: return None if (Ta is None) != (Tb is None) or (Ta is not None and Ta[0] != Tb[0]): return None d = [] for j, (x, y) in enumerate(zip(Pa, Pb)): if x[0] != y[0]: d.append(('p', j+1, y[0]-x[0])) if x[1] != y[1]: d.append(('q', j+1, y[1]-x[1])) if Ta is not None and Ta[1] != Tb[1]: d.append((Ta[0], 0, Tb[1]-Ta[1])) return d def far_kind(d, K): """Name the +2 block relative to the far end: the last pair's p or q, or the tail parameter.""" kind, j, _ = d if j == 0: return kind return kind + ('_L' if j == len(K[1]) else '_L-1' if j == len(K[1]) - 1 else '_interior') def section_X(lx, lmax): print('== X. the coupling: twin vs parent abstract orbits') # X1: every macro step (plain astep, no batching), from the first K at which both orbits have the same h a, b = cp.INIT_K, INIT_K n = 0; kinds = Counter(); okx = True while len(a[1]) <= lx: d = block_diff(a, b) if d is None or len(d) != 1 or d[0][2] != 2: okx = False; print(' X1 FAIL at macro step %d: parent %s, twin %s' % (n, a, b)); break kinds[far_kind(d[0], a)] += 1 ha, Pa, Ta, na = cp.astep(*a); hb, Pb, Tb, nb = cp.astep(*b) if na != nb: okx = False; print(' X1 FAIL: rule %s vs %s at macro step %d' % (na, nb, n)); break a, b = (ha, Pa, Ta), (hb, Pb, Tb); n += 1 far = all(not k.endswith('_interior') and not k.endswith('_L-1') for k in kinds) print(' X1: %d macro steps from K0 (parent K(2;(11,11,0);-), twin K(2;(11,13,0);-)) to L > %d: same rule at every step,' ' exactly one exponent differs, by +2: %s; where: %s -> %s' % (n, lx, okx, dict(kinds), 'PASS' if okx and far else 'FAIL')) # X2: at every re-pairing (batched map), to L > lmax def repairs(K): h, P, T = K; P = list(P); names = []; m = 0 while len(P) <= lmax: h, P, T, name, k, mm = cp.mstep(h, P, T) m += mm if name != 'bulk': names.append(name) if name in ('Ov', 'X1'): yield (h, list(P), T), tuple(names), m; names = []; m = 0 cnt = 0; kinds = Counter(); ok2 = True; events = Counter() for (ka, na, ma), (kb, nb, mb) in zip(repairs(cp.INIT_K), repairs(INIT_K)): cnt += 1 d = block_diff(ka, kb) if d is None or len(d) != 1 or d[0][2] != 2 or ma != mb or [x for x in na if x != 'Dec'] != [x for x in nb if x != 'Dec']: ok2 = False; print(' X2 FAIL at re-pairing %d: parent %s, twin %s' % (cnt, str(ka)[:80], str(kb)[:80])); break kinds[(far_kind(d[0], ka), na[-1])] += 1 for x in na: if x not in ('Dec', 'End', 'Ov'): events[x] += 1 far = all('_interior' not in k[0] and '_L-1' not in k[0] for k in kinds) print(' X2: %d re-pairings to L > %d: same macro steps (all rules but Dec, and the same number of decrements) in every' ' cycle, exactly one exponent differs, by +2, always the far block: %s' % (cnt, lmax, dict(kinds))) print(' rare rules seen in both orbits: %s -> %s' % (dict(events), 'PASS' if ok2 and far else 'FAIL')) return okx and far and ok2 if __name__ == '__main__': ap = argparse.ArgumentParser() ap.add_argument('--quick', action='store_true') ap.add_argument('--seed', type=int, default=20261010) ap.add_argument('--steps', type=int, default=None) ap.add_argument('--lmax', type=int, default=None) ap.add_argument('--lx', type=int, default=None) ap.add_argument('--jcyc', type=int, default=None) a = ap.parse_args() rng = random.Random(a.seed) n = 12 if a.quick else 45 steps = a.steps or (2*10**6 if a.quick else 3*10**7) lmax = a.lmax or (60 if a.quick else 300) lx = a.lx or (12 if a.quick else 16) jcyc = a.jcyc or (26 if a.quick else 34) r = {'S relation': section_S(2*10**5 if a.quick else 2*10**6), 'A rules on twin': section_A(n, rng)} okC, okD = section_CD(steps) r['C init'] = okC; r['D cross-check+base'] = okD r['B base INV+(e), J'] = section_B() r['G twin orbit'] = section_G(lmax, jcyc) r['X coupling'] = section_X(lx, lmax) print('== SUMMARY:', ', '.join('%s %s' % (k, 'PASS' if v else 'FAIL') for k, v in r.items()), '->', 'PASS' if all(r.values()) else 'FAIL', '| Simulation checks the claims of proof.md; the proof is the argument in proof.md (and the parent\'s).') sys.exit(0 if all(r.values()) else 1) |