phanerozoic commited on
Commit
fb69925
·
1 Parent(s): 7e1c831

Remove the scripts and records of the results cut from the paper: the organism on the record interpreter, tapes that are not recipes, order-independent settling and the dependence hypothesis

Browse files
README.md CHANGED
@@ -997,8 +997,6 @@ src/ the library (run scripts as `python src/<nam
997
  ├── paper_stats.py unit counts, levelization sizes, effective fan-in
998
  ├── paper_verify.py codec, framing, serialization, datapath, interpreter
999
  ├── paper_runs.py self-description, three generations, lockstep
1000
- ├── organism.py an organism on neural_reflect: decodes a description into
1001
- │ the idle bank, copies it and hands over
1002
  ├── host_family.py the host family H_A: a constant-depth SUBLEQ step for
1003
  │ every word width, and its serialization in build order
1004
  ├── netlist_eval.c an evaluator of the host family, the amplified host and the
@@ -1027,10 +1025,9 @@ src/ the library (run scripts as `python src/<nam
1027
  │ against the bounds, weight error with zero-entry leakage, the
1028
  │ active-input counts that certify an orbit
1029
  ├── neuromorphic.py the datapath on the Loihi and Akida neuron models
1030
- └── check_*.py codec boundaries and optimality, step counts, tapes that are
1031
- not recipes, ternary depth, environment cost, device, host,
1032
- host family, levelization, framing, settling, dependence,
1033
- serialization, all 29 machines
1034
  paper/ the manuscript, and the record of every run it reports
1035
  rocq/ Rocq proofs: the netlist of the two-head machine computes its
1036
  step, the organism builds every image from any description
 
997
  ├── paper_stats.py unit counts, levelization sizes, effective fan-in
998
  ├── paper_verify.py codec, framing, serialization, datapath, interpreter
999
  ├── paper_runs.py self-description, three generations, lockstep
 
 
1000
  ├── host_family.py the host family H_A: a constant-depth SUBLEQ step for
1001
  │ every word width, and its serialization in build order
1002
  ├── netlist_eval.c an evaluator of the host family, the amplified host and the
 
1025
  │ against the bounds, weight error with zero-entry leakage, the
1026
  │ active-input counts that certify an orbit
1027
  ├── neuromorphic.py the datapath on the Loihi and Akida neuron models
1028
+ └── check_*.py codec boundaries and optimality, step counts, ternary depth,
1029
+ environment cost, device, host, host family, levelization,
1030
+ framing, serialization, all 29 machines
 
1031
  paper/ the manuscript, and the record of every run it reports
1032
  rocq/ Rocq proofs: the netlist of the two-head machine computes its
1033
  step, the organism builds every image from any description
paper/runs/paper_organism.json DELETED
@@ -1,93 +0,0 @@
1
- {
2
- "A": 13,
3
- "S": 8192,
4
- "G": 33,
5
- "b": 50,
6
- "N": 1650,
7
- "records": 33,
8
- "passes_per_generation": 1651,
9
- "steps_per_generation": 54483,
10
- "D0": 51,
11
- "D1": 1701,
12
- "NET0": 4892,
13
- "NET1": 6542,
14
- "reference": {
15
- "generations": [
16
- {
17
- "bank": 1,
18
- "bank_ok": true,
19
- "ptr": 0,
20
- "gp": 0,
21
- "Q0": true,
22
- "Q1": true,
23
- "D0": true,
24
- "D1": true,
25
- "halt": 0
26
- },
27
- {
28
- "bank": 0,
29
- "bank_ok": true,
30
- "ptr": 0,
31
- "gp": 0,
32
- "Q0": true,
33
- "Q1": true,
34
- "D0": true,
35
- "D1": true,
36
- "halt": 0
37
- },
38
- {
39
- "bank": 1,
40
- "bank_ok": true,
41
- "ptr": 0,
42
- "gp": 0,
43
- "Q0": true,
44
- "Q1": true,
45
- "D0": true,
46
- "D1": true,
47
- "halt": 0
48
- },
49
- {
50
- "bank": 0,
51
- "bank_ok": true,
52
- "ptr": 0,
53
- "gp": 0,
54
- "Q0": true,
55
- "Q1": true,
56
- "D0": true,
57
- "D1": true,
58
- "halt": 0
59
- }
60
- ],
61
- "periodic": true,
62
- "ok": true
63
- },
64
- "universal": {
65
- "adder_records": 9,
66
- "inputs": 8,
67
- "failures": 0,
68
- "ok": true
69
- },
70
- "net_units": 90862,
71
- "net_levels": 59,
72
- "netlist": {
73
- "lockstep_steps": 4224,
74
- "lockstep_ok": true,
75
- "generations": [
76
- {
77
- "bank": 1,
78
- "bank_ok": true,
79
- "ptr": 0,
80
- "gp": 0,
81
- "Q0": true,
82
- "Q1": true,
83
- "D0": true,
84
- "D1": true,
85
- "halt": 0,
86
- "equals_reference": true
87
- }
88
- ],
89
- "ok": true
90
- },
91
- "seconds": 647.9628848040011,
92
- "ok": true
93
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
paper/runs/paper_settle.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "records": 9,
3
- "depth": 4,
4
- "G": 11,
5
- "orders": 362880,
6
- "inputs": 8,
7
- "states": 2903040,
8
- "wrong_after_d": 0,
9
- "wrong_after_d_minus_1": 134784,
10
- "ok": true,
11
- "seconds": 215.41703450400382
12
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
paper/runs/paper_total.json DELETED
@@ -1,13 +0,0 @@
1
- {
2
- "P_cases": 825,
3
- "P_halting": 383,
4
- "P_divergent": 442,
5
- "P_failures": 0,
6
- "Pe_failures": 0,
7
- "Pe_bound_slack": 1.007727975270479,
8
- "recipe_agreement": 600,
9
- "recipe_disagreements": 0,
10
- "Pe_selfdescription_steps": 794994,
11
- "Pe_selfdescription_exact": true,
12
- "Pe_instructions": 22
13
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/check_dependence.py DELETED
@@ -1,121 +0,0 @@
1
- """Witness search for the dependence hypothesis of the size proposition.
2
-
3
- For every coordinate q of the record region, exhibit a state x and a data-region
4
- coordinate s with U(x)_s != U(x xor e_q)_s. Random states almost never witness
5
- this because a random output address lands in the 106-bit data region with
6
- probability about one in ten; the search below forces the record under
7
- evaluation to write into the data region and randomizes everything else.
8
- """
9
- import os, random, sys
10
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
11
- import torch
12
- from reflect import (Cfg, build_net, Leveled, encode_gate, pad, state_to_vec,
13
- vec_to_state, slot_off, field_off)
14
-
15
-
16
- def targeted(cfg, U, rng, q, g, r, D0, D1, budget=4000):
17
- """Second pass for address bits, index flags and weight codes: make the
18
- field under test decide the output, and place both the read and the write
19
- where they can be observed. Free coordinates are the data region and the
20
- inactive record region, neither of which the evaluated record occupies."""
21
- A, SLOT = cfg.A, cfg.SLOT
22
- free = list(range(D0, D1)) + list(range(cfg.NET1, cfg.S))
23
- freeset = set(free)
24
- Q0 = cfg.NET0
25
- base = Q0 + g * cfg.R
26
- for _ in range(budget):
27
- slot = 0 if r < SLOT else (1 if r < 2 * SLOT else None)
28
- # the tested slot decides the output: out = x at that slot's address
29
- if slot is None:
30
- w = (1, 1)
31
- bias = rng.randrange(-(1 << (cfg.BB - 1)), 1 << (cfg.BB - 1))
32
- else:
33
- w = (1, 0) if slot == 0 else (0, 1)
34
- bias = -1
35
- a = [rng.choice(free), rng.choice(free)]
36
- i = [rng.randint(0, 1), rng.randint(0, 1)]
37
- ao = rng.randrange(D0, D1)
38
- io = 0
39
- ptr = rng.randrange(0, 1 << A)
40
- rec = encode_gate(cfg, pad(cfg, [(a[0], w[0], i[0]), (a[1], w[1], i[1])]),
41
- bias, (ao, io))
42
- sig = [0] * cfg.S
43
- for d in free:
44
- sig[d] = rng.randint(0, 1)
45
- for k in range(A):
46
- sig[cfg.PTR_BASE + k] = (ptr >> (A - 1 - k)) & 1
47
- sig[base:base + cfg.R] = rec
48
- s0, s1 = list(sig), list(sig)
49
- s1[q] ^= 1
50
- x = U(s0, g)
51
- y = U(s1, g)
52
- if any(x[d] != y[d] for d in range(D0, D1)):
53
- return budget
54
- return 0
55
-
56
-
57
- def main() -> int:
58
- cfg = Cfg()
59
- net, inputs, outputs = build_net(cfg)
60
- lev = Leveled(net, inputs, outputs, device="cpu")
61
-
62
- def U(sig, gp):
63
- v = state_to_vec(cfg, {"sig": sig, "gp": gp, "halt": 0}).unsqueeze(0)
64
- return vec_to_state(cfg, lev.step(v[:, :len(inputs)])[0])["sig"]
65
-
66
- D0, D1 = cfg.WORK_BASE, cfg.NET0 # data region [D0, D1)
67
- rng = random.Random(20260910)
68
- Q0 = cfg.NET0
69
- Qbits = list(range(Q0, Q0 + cfg.G * cfg.R))
70
- witnessed, tries_used = [], []
71
- for q in Qbits:
72
- g, r = divmod(q - Q0, cfg.R)
73
- found = 0
74
- for attempt in range(160):
75
- # a record that writes into the data region, everything else random
76
- a0 = rng.randrange(D0, D1 - 1)
77
- a1 = rng.randrange(D0, D1 - 1)
78
- ao = rng.randrange(D0, D1 - 1)
79
- w0 = rng.choice((-1, 0, 1))
80
- w1 = rng.choice((-1, 0, 1))
81
- bias = rng.randrange(-(1 << (cfg.BB - 1)), 1 << (cfg.BB - 1))
82
- i0, i1, io = rng.randint(0, 1), rng.randint(0, 1), rng.randint(0, 1)
83
- rec = encode_gate(cfg, pad(cfg, [(a0, w0, i0), (a1, w1, i1)]),
84
- bias, (ao, io))
85
- sig = [0] * cfg.S
86
- for d in range(D0, D1):
87
- sig[d] = rng.randint(0, 1)
88
- ptr = rng.choice((0, 1, 2, 3))
89
- for k in range(cfg.A):
90
- sig[cfg.PTR_BASE + k] = (ptr >> (cfg.A - 1 - k)) & 1
91
- base = cfg.bank_base(0) + g * cfg.R
92
- sig[base:base + cfg.R] = rec
93
- s0 = list(sig)
94
- s1 = list(sig)
95
- s1[q] ^= 1
96
- a = U(s0, g)
97
- b = U(s1, g)
98
- if any(a[d] != b[d] for d in range(D0, D1)):
99
- found = attempt + 1
100
- break
101
- if not found:
102
- found = targeted(cfg, U, rng, q, g, r, D0, D1)
103
- if found:
104
- witnessed.append(q)
105
- tries_used.append(found)
106
- ok = len(witnessed) == len(Qbits)
107
- print(f"record-region bits: {len(Qbits)}")
108
- print(f"bits with an exhibited witness: {len(witnessed)}"
109
- f"{' (hypothesis holds for every bit)' if ok else ' MISSING: '
110
- + str([q - Q0 for q in Qbits if q not in witnessed][:20])}")
111
- if tries_used:
112
- print(f"attempts needed: max {max(tries_used)}, mean "
113
- f"{sum(tries_used)/len(tries_used):.1f}")
114
- print("every data coordinate of U is a multiplexer output depending on the "
115
- "write-select line and the previous value, hence on at least two "
116
- "coordinates")
117
- return 0 if ok else 1
118
-
119
-
120
- if __name__ == "__main__":
121
- sys.exit(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/check_settle.py DELETED
@@ -1,108 +0,0 @@
1
- """Order-independent settling on the interpreter itself.
2
-
3
- The full adder of Lemma 3.2, with carry-in a primary input, is compiled to
4
- stored records and laid out in every one of the 9! orders of its records. For
5
- every order and every one of the eight inputs the interpreter's netlist is run
6
- for d passes, d the depth of the adder, and the outputs are compared with
7
- a + b + c_in, as Lemma 7.5 asserts. The same states are also read after d - 1
8
- passes, where some order must still be wrong, since a pass that evaluates the
9
- records against the order of the netlist advances the correct front by one
10
- level only.
11
-
12
- python src/check_settle.py [--device cuda] [--batch 16384]
13
- """
14
- import argparse
15
- import itertools
16
- import json
17
- import os
18
- import sys
19
- import time
20
-
21
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
22
- import torch
23
- from matrix8 import Net
24
- from reflect import Cfg, SparseLeveled, build_net, compile_to_reflect, encode_netlist, topo
25
-
26
- REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
27
-
28
-
29
- def runs_path(name: str) -> str:
30
- d = os.path.join(REPO, "paper", "runs")
31
- os.makedirs(d, exist_ok=True)
32
- return os.path.join(d, name)
33
-
34
-
35
- def main() -> int:
36
- ap = argparse.ArgumentParser()
37
- ap.add_argument("--device", default="cuda" if torch.cuda.is_available() else "cpu")
38
- ap.add_argument("--batch", type=int, default=16384)
39
- args = ap.parse_args()
40
- t_start = time.perf_counter()
41
-
42
- cfg = Cfg()
43
- net = Net()
44
- s_out, c_out = net.FA("fa", "a", "b", "cin")
45
- depth = {}
46
- for g in topo(net):
47
- depth[g] = 1 + max((depth.get(s, 0) for s, _ in net.gates[g][0]), default=0)
48
- d = max(depth.values())
49
- ia = {"a": cfg.WORK_BASE, "b": cfg.WORK_BASE + 1, "cin": cfg.WORK_BASE + 2}
50
- rg, addr, _ = compile_to_reflect(cfg, net, ia, [s_out, c_out], cfg.WORK_BASE + 3)
51
- n = len(rg)
52
- print(f" full adder of Lemma 3.2: {n} records of depth {d}; U has G = {cfg.G}")
53
-
54
- unet, uin, uout = build_net(cfg)
55
- lev = SparseLeveled(unet, uin, uout, device=args.device)
56
- width = cfg.STATE_BITS
57
-
58
- # the record bits of each record, and of the no-op that fills the region
59
- enc = torch.tensor([encode_netlist(cfg, [r])[:cfg.R] for r in rg], dtype=torch.float32)
60
- pad = torch.tensor(encode_netlist(cfg, [])[:cfg.R * (cfg.G - n)], dtype=torch.float32)
61
- perms = torch.tensor(list(itertools.permutations(range(n))), dtype=torch.long)
62
- P = perms.shape[0]
63
- inputs = torch.tensor([[(v >> 0) & 1, (v >> 1) & 1, (v >> 2) & 1] for v in range(8)],
64
- dtype=torch.float32)
65
- want = inputs.sum(1)
66
- print(f" {P:,} record orders x 8 inputs = {8 * P:,} states, {d} passes of "
67
- f"{cfg.G} records each")
68
-
69
- bad_d = bad_early = 0
70
- done = 0
71
- t0 = time.perf_counter()
72
- orders_per_batch = max(1, args.batch // 8)
73
- for lo in range(0, P, orders_per_batch):
74
- pb = perms[lo:lo + orders_per_batch]
75
- B = pb.shape[0] * 8
76
- region = enc[pb].reshape(pb.shape[0], n * cfg.R)
77
- region = torch.cat([region, pad.expand(pb.shape[0], -1)], dim=1)
78
- X = torch.zeros(B, width)
79
- X[:, cfg.NET0:cfg.NET0 + cfg.G * cfg.R] = region.repeat_interleave(8, dim=0)
80
- iv = inputs.repeat(pb.shape[0], 1)
81
- X[:, ia["a"]], X[:, ia["b"]], X[:, ia["cin"]] = iv[:, 0], iv[:, 1], iv[:, 2]
82
- X = X.to(args.device)
83
- wv = want.repeat(pb.shape[0]).to(args.device)
84
- for p in range(d):
85
- for _ in range(cfg.G):
86
- X = lev.step(X)
87
- if p == d - 2:
88
- got = X[:, addr[s_out]] + 2 * X[:, addr[c_out]]
89
- bad_early += int((got != wv).sum())
90
- got = X[:, addr[s_out]] + 2 * X[:, addr[c_out]]
91
- bad_d += int((got != wv).sum())
92
- done += B
93
- if lo // orders_per_batch % 8 == 0:
94
- rate = done / (time.perf_counter() - t0)
95
- print(f" {done:,}/{8 * P:,} states ({rate:,.0f} states/s)", flush=True)
96
- ok = bad_d == 0 and bad_early > 0
97
- print(f" after {d} passes: {'every order and every input correct' if bad_d == 0 else f'{bad_d} WRONG'}; "
98
- f"after {d - 1} passes: {bad_early:,} states still wrong, so {d} passes are needed")
99
- json.dump({"records": n, "depth": d, "G": cfg.G, "orders": P, "inputs": 8,
100
- "states": 8 * P, "wrong_after_d": bad_d, "wrong_after_d_minus_1": bad_early,
101
- "ok": ok, "seconds": time.perf_counter() - t_start},
102
- open(runs_path("paper_settle.json"), "w"), indent=1)
103
- print("SETTLE:", "PASS" if ok else "FAIL")
104
- return 0 if ok else 1
105
-
106
-
107
- if __name__ == "__main__":
108
- sys.exit(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/check_total.py DELETED
@@ -1,156 +0,0 @@
1
- """The constructor on tapes that are not recipes.
2
-
3
- Two statements are checked. First, the decoding loop of P halts on an arbitrary
4
- tape exactly when a tag byte in {0, 128} is reached, counting the stale input
5
- register as the tag once the tape is exhausted, and its output on every such
6
- tape is the extension of delta computed by selfrep.decode_any. Second, the
7
- program P_e obtained by inserting one end-of-tape guard after the tag read
8
- halts on every tape, agrees with P on recipes, and runs in at most
9
- 643 |tau| + 9 steps.
10
- """
11
- import json
12
- import os
13
- import random
14
- import sys
15
-
16
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
17
- from selfrep import (HOST_PATH, M_STAR, P, P_TOTAL, decode, decode_any, describe,
18
- describe_literal, memory_image, run_reference, tau_star)
19
-
20
- REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
21
- M_E = memory_image(P_TOTAL)
22
- M_PP = memory_image(P)
23
-
24
-
25
- def runs_path(name: str) -> str:
26
- d = os.path.join(REPO, "paper", "runs")
27
- os.makedirs(d, exist_ok=True)
28
- return os.path.join(d, name)
29
-
30
-
31
- def budget(tape: bytes) -> int:
32
- """The bound proved for P_e: at most |tau| passes that consume a tape byte,
33
- each of at most 5*127 + 8 steps, and one final pass of at most nine."""
34
- return 643 * len(tape) + 9
35
-
36
-
37
- def tapes():
38
- rng = random.Random(777)
39
- out = [(b"", "empty")]
40
- for t in (0, 1, 2, 127, 128, 129, 200, 255):
41
- out.append((bytes([t]), f"a single byte {t}"))
42
- out.append((bytes([t, 0x41]), f"byte {t} then a payload byte"))
43
- out.append((b"\x05\x01", "a literal token truncated after one payload byte"))
44
- out.append((b"\x7f", "a literal tag with no payload at all"))
45
- out.append((b"\xff", "a repeat tag with no payload"))
46
- out.append((b"\x80\x41\x41", "the reserved tag 128"))
47
- sigma = open(HOST_PATH, "rb").read()
48
- r = describe(sigma)
49
- out.append((r, "a real recipe"))
50
- out.append((r[:-1], "that recipe with its end token removed"))
51
- out.append((r[:5000], "a truncated recipe"))
52
- out.append((tau_star(sigma, M_STAR), "tau*"))
53
- for _ in range(600):
54
- n = rng.randrange(0, 60)
55
- alpha = rng.choice([2, 3, 130, 256])
56
- out.append((bytes(rng.randrange(alpha) for _ in range(n)), "random"))
57
- for _ in range(200):
58
- f = bytes(rng.randrange(rng.choice([2, 256])) for _ in range(rng.randrange(0, 80)))
59
- enc = rng.choice((describe, describe_literal))
60
- r = enc(f)
61
- k = rng.randrange(0, len(r) + 1)
62
- out.append((r[:k], "a prefix of a recipe"))
63
- return out
64
-
65
-
66
- def main() -> int:
67
- out = {}
68
- ok = True
69
- cases = tapes()
70
-
71
- # P: the halting characterization, and the output when it halts.
72
- diverge = halt = 0
73
- bad = []
74
- for tape, label in cases:
75
- want, halts = decode_any(tape, guard=False)
76
- cap = budget(tape) * 4
77
- try:
78
- got, steps = run_reference(M_PP, tape, max_steps=cap)
79
- except Exception as exc: # pragma: no cover
80
- bad.append((label, repr(exc)))
81
- continue
82
- ran_out = steps >= cap
83
- if halts:
84
- halt += 1
85
- if ran_out or got != want:
86
- bad.append((label, "halting case wrong"))
87
- else:
88
- diverge += 1
89
- if not ran_out or not got.startswith(want):
90
- bad.append((label, "divergent case wrong"))
91
- print(f" P on {len(cases)} tapes: {halt} halt, {diverge} do not; the "
92
- f"characterization and the emitted bytes agree in every case: "
93
- f"{'yes' if not bad else f'NO {bad[:3]}'}")
94
- ok &= not bad
95
- out["P_cases"] = len(cases)
96
- out["P_halting"] = halt
97
- out["P_divergent"] = diverge
98
- out["P_failures"] = len(bad)
99
-
100
- # P_e: total, and within the proved bound.
101
- ebad = []
102
- worst = 0.0
103
- for tape, label in cases:
104
- want, halts = decode_any(tape, guard=True)
105
- assert halts
106
- cap = budget(tape)
107
- got, steps = run_reference(M_E, tape, max_steps=cap + 1)
108
- if steps > cap or got != want:
109
- ebad.append((label, steps, cap))
110
- worst = max(worst, steps / cap)
111
- print(f" P_e on the same {len(cases)} tapes: halts on every one, output "
112
- f"equals the extended decoding, and the step bound 643|tau| + 9 "
113
- f"holds: {'yes' if not ebad else f'NO {ebad[:3]}'}")
114
- print(f" the worst case observed reaches {worst * 100:.0f} per cent of the "
115
- f"bound")
116
- ok &= not ebad
117
- out["Pe_failures"] = len(ebad)
118
- out["Pe_bound_slack"] = 1 / worst if worst else None
119
-
120
- # P_e agrees with P on recipes.
121
- rng = random.Random(31337)
122
- agree = 0
123
- abad = 0
124
- for _ in range(300):
125
- f = bytes(rng.randrange(rng.choice([2, 5, 256]))
126
- for _ in range(rng.randrange(0, 400)))
127
- for enc in (describe, describe_literal):
128
- r = enc(f)
129
- a, _ = run_reference(M_PP, r)
130
- b, _ = run_reference(M_E, r)
131
- agree += 1
132
- if not (a == b == f):
133
- abad += 1
134
- print(f" P and P_e emit the same bytes on {agree} recipes, equal to the "
135
- f"described string: {'yes' if abad == 0 else f'NO ({abad})'}")
136
- ok &= abad == 0
137
- out["recipe_agreement"] = agree
138
- out["recipe_disagreements"] = abad
139
-
140
- # the self-description still goes through, on the total program
141
- sigma = open(HOST_PATH, "rb").read()
142
- o, n = run_reference(M_E, describe(sigma))
143
- same = o == sigma
144
- print(f" P_e emits sigma(N_host) from its recipe in {n:,} steps: "
145
- f"{'exact' if same else 'FAILED'}")
146
- ok &= same
147
- out["Pe_selfdescription_steps"] = n
148
- out["Pe_selfdescription_exact"] = same
149
- out["Pe_instructions"] = len(P_TOTAL)
150
-
151
- json.dump(out, open(runs_path("paper_total.json"), "w"), indent=1)
152
- return 0 if ok else 1
153
-
154
-
155
- if __name__ == "__main__":
156
- sys.exit(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/organism.py DELETED
@@ -1,318 +0,0 @@
1
- """A von Neumann organism inside the interpreter.
2
-
3
- The organism is a stored netlist R held in the active record region together
4
- with a description held in a data region of the same size. Each pass of the
5
- interpreter handles one bit position p, the value of the pointer: the
6
- description bit of the active copy is copied into the description region of
7
- the other copy, and its negation is written into the inactive record region.
8
- When the pointer reaches the size N of a record region the organism clears the
9
- pointer and flips the bank bit, and the interpreter continues from the netlist
10
- that was just written.
11
-
12
- With R's own description, every generation constructs R from the description
13
- and copies the description, bit for bit, and hands over to the result, which
14
- does the same. With the description of any other netlist X of the same size the
15
- first generation constructs X and hands over to it, so the constructor is
16
- universal over stored netlists. Construction, copying and instantiation are all
17
- carried out by the dynamics of the interpreter: the pointer clause is part of
18
- its netlist and the output device is not used.
19
-
20
- records role
21
- t_k conjunction of the pointer bits 2k, 2k+1 with the bits of N
22
- tree e = [PTR = N]
23
- reset PTR_j <- PTR_j and not e, for every bit j of N that is 1
24
- transfer D_other[p], D_active[p] <- D_active[p];
25
- Q_other[p] <- not D_active[p], Q_active[p] <- Q_active[p]
26
- advance PTR_ADV <- not e
27
- flip BANK <- BANK xor e
28
-
29
- python src/organism.py [--generations N] [--net-generations N] [--device D]
30
- """
31
- from __future__ import annotations
32
-
33
- import argparse
34
- import json
35
- import os
36
- import random
37
- import sys
38
- import time
39
-
40
- sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
41
- from reflect import Cfg, Leveled, Runner, build_net, encode_netlist, ref_step, to_bits
42
-
43
- REPO = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
44
- A_ORG = 13
45
-
46
-
47
- def runs_path(name: str) -> str:
48
- d = os.path.join(REPO, "paper", "runs")
49
- os.makedirs(d, exist_ok=True)
50
- return os.path.join(d, name)
51
-
52
-
53
- def records_needed(A: int, N: int) -> int:
54
- """Pairs, tree, reset, transfer, advance and flip."""
55
- pairs = -(-A // 2)
56
- return pairs + (pairs - 1) + bin(N).count("1") + 10 + 1 + 3
57
-
58
-
59
- def organism_cfg(A: int = A_ORG) -> Cfg:
60
- """The two-bank member whose record region holds exactly the organism."""
61
- R = 3 * A + 11
62
- for G in range(8, 1 << 12):
63
- if records_needed(A, G * R) == G:
64
- return Cfg(A=A, G=G, banks=2, self_mod=True)
65
- raise ValueError("no record count is a fixed point")
66
-
67
-
68
- class Layout:
69
- """Scratch coordinates, then the two description regions, below NET0."""
70
-
71
- def __init__(self, cfg: Cfg):
72
- self.N = cfg.G * cfg.R
73
- self.scratch = cfg.WORK_BASE
74
- self.n_scratch = 32
75
- self.D0 = self.scratch + self.n_scratch
76
- self.D1 = self.D0 + self.N
77
- self.free = self.D1 + self.N
78
- assert self.free <= cfg.NET0, "the description regions overlap the records"
79
-
80
-
81
- def organism_program(cfg: Cfg, L: Layout):
82
- """The G records of the organism, in evaluation order."""
83
- w = [L.scratch]
84
-
85
- def fresh():
86
- s = w[0]
87
- w[0] += 1
88
- assert w[0] <= L.scratch + L.n_scratch
89
- return s
90
-
91
- bits = to_bits(L.N, cfg.A) # coordinate PTR_BASE + i carries bits[i]
92
- prog = []
93
- leaves = []
94
- for k in range(0, cfg.A, 2):
95
- slots, bias = [], 0
96
- for j in (k, k + 1):
97
- if j < cfg.A:
98
- if bits[j]:
99
- slots.append((cfg.PTR_BASE + j, 1, 0))
100
- bias -= 1
101
- else:
102
- slots.append((cfg.PTR_BASE + j, -1, 0))
103
- t = fresh()
104
- prog.append((slots, bias, (t, 0)))
105
- leaves.append(t)
106
- e = fresh()
107
- level = leaves
108
- while len(level) > 1:
109
- nxt = []
110
- for i in range(0, len(level) - 1, 2):
111
- dst = e if len(level) == 2 else fresh()
112
- prog.append(([(level[i], 1, 0), (level[i + 1], 1, 0)], -2, (dst, 0)))
113
- nxt.append(dst)
114
- if len(level) % 2:
115
- nxt.append(level[-1])
116
- level = nxt
117
- assert level == [e]
118
- for i in range(cfg.A): # PTR = N -> PTR = 0
119
- if bits[i]:
120
- prog.append(([(cfg.PTR_BASE + i, 1, 0), (e, -1, 0)], -1,
121
- (cfg.PTR_BASE + i, 0)))
122
- k = cfg.BANK_SIG
123
- c, d, u, v, u2, v2 = (fresh() for _ in range(6))
124
- prog += [
125
- ([(L.D0, 1, 1), (k, -1, 0)], -1, (c, 0)), # D0[p] and not k
126
- ([(L.D1, 1, 1), (k, 1, 0)], -2, (d, 0)), # D1[p] and k
127
- ([(c, 1, 0), (d, 1, 0)], -1, (L.D1, 1)), # D1[p] <- D_k[p]
128
- ([(c, 1, 0), (d, 1, 0)], -1, (L.D0, 1)), # D0[p] <- D_k[p]
129
- ([(L.D0, -1, 1), (k, -1, 0)], 0, (u, 0)), # not D_k[p] and not k
130
- ([(cfg.NET1, 1, 1), (k, 1, 0)], -2, (v, 0)), # Q1[p] and k
131
- ([(u, 1, 0), (v, 1, 0)], -1, (cfg.NET1, 1)), # Q1[p] <- k ? Q1[p] : not D_k[p]
132
- ([(L.D0, -1, 1), (k, 1, 0)], -1, (u2, 0)), # not D_k[p] and k
133
- ([(cfg.NET0, 1, 1), (k, -1, 0)], -1, (v2, 0)), # Q0[p] and not k
134
- ([(u2, 1, 0), (v2, 1, 0)], -1, (cfg.NET0, 1)), # Q0[p] <- k ? not D_k[p] : Q0[p]
135
- ]
136
- prog.append(([(e, -1, 0)], 0, (cfg.PTR_ADV, 0))) # advance unless PTR = N
137
- to, tn = fresh(), fresh()
138
- prog += [([(k, 1, 0), (e, 1, 0)], -1, (to, 0)),
139
- ([(k, -1, 0), (e, -1, 0)], 1, (tn, 0)),
140
- ([(to, 1, 0), (tn, 1, 0)], -2, (k, 0))] # k <- k xor e
141
- assert len(prog) == cfg.G, (len(prog), cfg.G)
142
- return prog
143
-
144
-
145
- def negate(bits):
146
- return [1 - b for b in bits]
147
-
148
-
149
- def initial_state(cfg: Cfg, L: Layout, organism, description, seed: int):
150
- """Bank 0 holds the organism and D0 the description; PTR, PTR_ADV, BANK,
151
- HALT, the record counter and the halt bit are 0; every other coordinate is
152
- random, the inactive record region and D1 included."""
153
- rng = random.Random(seed)
154
- sig = [rng.randint(0, 1) for _ in range(cfg.S)]
155
- for c in list(range(cfg.PTR_BASE, cfg.PTR_BASE + cfg.A)) + [
156
- cfg.PTR_ADV, cfg.BANK_SIG, cfg.HALT_SIG, cfg.OUT_STROBE]:
157
- sig[c] = 0
158
- sig[cfg.NET0:cfg.NET0 + L.N] = organism
159
- sig[L.D0:L.D0 + L.N] = description
160
- return {"sig": sig, "gp": 0, "halt": 0}
161
-
162
-
163
- def run_generation(cfg, L, step, st):
164
- """N + 1 passes: N bit positions, then the hand-over pass."""
165
- for _ in range((L.N + 1) * cfg.G):
166
- st = step(st)
167
- return st
168
-
169
-
170
- def regions(cfg, L, sig):
171
- return {"Q0": sig[cfg.NET0:cfg.NET0 + L.N], "Q1": sig[cfg.NET1:cfg.NET1 + L.N],
172
- "D0": sig[L.D0:L.D0 + L.N], "D1": sig[L.D1:L.D1 + L.N]}
173
-
174
-
175
- def check_generation(cfg, L, st, k, organism):
176
- """After generation k the bank bit is k mod 2, the pointer and the record
177
- counter are 0, and both copies hold the organism and its description."""
178
- sig = st["sig"]
179
- r = regions(cfg, L, sig)
180
- ptr = int("".join(map(str, sig[cfg.PTR_BASE:cfg.PTR_BASE + cfg.A])), 2)
181
- desc = negate(organism)
182
- return {"bank": sig[cfg.BANK_SIG], "bank_ok": sig[cfg.BANK_SIG] == k % 2,
183
- "ptr": ptr, "gp": st["gp"],
184
- "Q0": r["Q0"] == organism, "Q1": r["Q1"] == organism,
185
- "D0": r["D0"] == desc, "D1": r["D1"] == desc,
186
- "halt": st["halt"]}
187
-
188
-
189
- def adder_program(cfg, L):
190
- """The full adder of Lemma 3.2 on inputs held in the free region, padded
191
- with no-op records to G records: a netlist other than the organism."""
192
- from matrix8 import Net
193
- from reflect import compile_to_reflect
194
- net = Net()
195
- s, co = net.FA("fa", "a", "b", "cin")
196
- ia = {"a": L.free, "b": L.free + 1, "cin": L.free + 2}
197
- rg, addr, _ = compile_to_reflect(cfg, net, ia, [s, co], L.free + 3)
198
- noop = ([], -1, (cfg.TRASH, 0))
199
- prog = rg + [noop] * (cfg.G - len(rg))
200
- return prog, ia, addr[s], addr[co], len(rg)
201
-
202
-
203
- def main() -> int:
204
- ap = argparse.ArgumentParser()
205
- ap.add_argument("--generations", type=int, default=4)
206
- ap.add_argument("--net-generations", type=int, default=2)
207
- ap.add_argument("--device", default="cuda")
208
- args = ap.parse_args()
209
- t_start = time.perf_counter()
210
-
211
- cfg = organism_cfg()
212
- L = Layout(cfg)
213
- prog = organism_program(cfg, L)
214
- organism = encode_netlist(cfg, prog)
215
- desc = negate(organism)
216
- print(f" member: A = {cfg.A}, S = {cfg.S}, G = {cfg.G}, b = {cfg.R}, "
217
- f"two banks, span {cfg.span}; N = {L.N} bits per record region")
218
- print(f" organism: {len(prog)} records; description regions at {L.D0} and "
219
- f"{L.D1}; one generation is N + 1 = {L.N + 1} passes, "
220
- f"{(L.N + 1) * cfg.G:,} steps")
221
- out = {"A": cfg.A, "S": cfg.S, "G": cfg.G, "b": cfg.R, "N": L.N,
222
- "records": len(prog), "passes_per_generation": L.N + 1,
223
- "steps_per_generation": (L.N + 1) * cfg.G, "D0": L.D0, "D1": L.D1,
224
- "NET0": cfg.NET0, "NET1": cfg.NET1}
225
-
226
- # --- self-reproduction on the reference of Definition 7.1 --------------
227
- ref = lambda s: ref_step(cfg, s)
228
- st = initial_state(cfg, L, organism, desc, seed=1)
229
- gens, states = [], []
230
- t0 = time.perf_counter()
231
- for k in range(1, args.generations + 1):
232
- st = run_generation(cfg, L, ref, st)
233
- g = check_generation(cfg, L, st, k, organism)
234
- gens.append(g)
235
- states.append(list(st["sig"]))
236
- print(f" reference, generation {k}: bank {g['bank']}, PTR {g['ptr']}, "
237
- f"Q0 {'=' if g['Q0'] else '!='} R, Q1 {'=' if g['Q1'] else '!='} R, "
238
- f"D0 {'=' if g['D0'] else '!='} d(R), D1 {'=' if g['D1'] else '!='} d(R)",
239
- flush=True)
240
- periodic = all(states[i] == states[i + 2] for i in range(len(states) - 2))
241
- ok_ref = all(g["bank_ok"] and g["ptr"] == 0 and g["gp"] == 0 and g["Q0"] and g["Q1"]
242
- and g["D0"] and g["D1"] and g["halt"] == 0 for g in gens) and periodic
243
- print(f" reference: {args.generations} generations exact; the state after "
244
- f"generation k equals the state after generation k + 2: "
245
- f"{'yes' if periodic else 'NO'} ({time.perf_counter() - t0:.0f} s)", flush=True)
246
- out["reference"] = {"generations": gens, "periodic": periodic, "ok": ok_ref}
247
-
248
- # --- universal construction: the full adder, on every input -------------
249
- aprog, ia, s_addr, c_addr, n_adder = adder_program(cfg, L)
250
- X = encode_netlist(cfg, aprog)
251
- ubad = 0
252
- for v in range(8):
253
- a, b, cin = v & 1, (v >> 1) & 1, (v >> 2) & 1
254
- st = initial_state(cfg, L, organism, negate(X), seed=100 + v)
255
- st["sig"][ia["a"]], st["sig"][ia["b"]], st["sig"][ia["cin"]] = a, b, cin
256
- st = run_generation(cfg, L, ref, st)
257
- built = st["sig"][cfg.NET1:cfg.NET1 + L.N] == X and st["sig"][cfg.BANK_SIG] == 1
258
- for _ in range(4 * cfg.G): # the adder's depth is four
259
- st = ref(st)
260
- total = st["sig"][s_addr] + 2 * st["sig"][c_addr]
261
- if not built or total != a + b + cin:
262
- ubad += 1
263
- print(f" universal construction: the description of a {n_adder}-record full "
264
- f"adder yields the adder in bank 1, which then computes a + b + c_in on "
265
- f"all 8 inputs: {'yes' if ubad == 0 else f'NO ({ubad} failures)'}", flush=True)
266
- out["universal"] = {"adder_records": n_adder, "inputs": 8, "failures": ubad,
267
- "ok": ubad == 0}
268
-
269
- # --- the netlist of the member, generation by generation ----------------
270
- t0 = time.perf_counter()
271
- net, inputs, outputs = build_net(cfg)
272
- lev = Leveled(net, inputs, outputs, device=args.device)
273
- R = Runner(cfg, lev)
274
- print(f" netlist N_U+: {len(net.gates):,} units in {len(lev.plan)} levels "
275
- f"({time.perf_counter() - t0:.0f} s to build)", flush=True)
276
- out["net_units"] = len(net.gates)
277
- out["net_levels"] = len(lev.plan)
278
- st_n = initial_state(cfg, L, organism, desc, seed=1)
279
- st_r = initial_state(cfg, L, organism, desc, seed=1)
280
- lock = 2 * cfg.G * 64
281
- diverged = None
282
- for n in range(lock):
283
- st_n = R.step(st_n)
284
- st_r = ref(st_r)
285
- if st_n["sig"] != st_r["sig"] or st_n["gp"] != st_r["gp"]:
286
- diverged = n
287
- break
288
- print(f" netlist and reference agree on the full state over the first {lock:,} "
289
- f"steps: {'yes' if diverged is None else f'NO at step {diverged}'}", flush=True)
290
- st_n = initial_state(cfg, L, organism, desc, seed=1)
291
- net_gens = []
292
- t0 = time.perf_counter()
293
- for k in range(1, args.net_generations + 1):
294
- st_n = run_generation(cfg, L, R.step, st_n)
295
- g = check_generation(cfg, L, st_n, k, organism)
296
- g["equals_reference"] = st_n["sig"] == states[k - 1]
297
- net_gens.append(g)
298
- print(f" netlist, generation {k}: bank {g['bank']}, Q0 {'=' if g['Q0'] else '!='} R, "
299
- f"Q1 {'=' if g['Q1'] else '!='} R, descriptions "
300
- f"{'exact' if g['D0'] and g['D1'] else 'WRONG'}, full state equals the "
301
- f"reference's: {'yes' if g['equals_reference'] else 'NO'} "
302
- f"({time.perf_counter() - t0:.0f} s)", flush=True)
303
- ok_net = diverged is None and all(
304
- g["bank_ok"] and g["Q0"] and g["Q1"] and g["D0"] and g["D1"] and g["equals_reference"]
305
- for g in net_gens)
306
- out["netlist"] = {"lockstep_steps": lock, "lockstep_ok": diverged is None,
307
- "generations": net_gens, "ok": ok_net}
308
-
309
- ok = ok_ref and ubad == 0 and ok_net
310
- out["seconds"] = time.perf_counter() - t_start
311
- out["ok"] = ok
312
- json.dump(out, open(runs_path("paper_organism.json"), "w"), indent=1)
313
- print("ORGANISM:", "PASS" if ok else "FAIL")
314
- return 0 if ok else 1
315
-
316
-
317
- if __name__ == "__main__":
318
- sys.exit(main())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/paper_verify.py CHANGED
@@ -231,12 +231,11 @@ def check_host_datapath():
231
 
232
  # ---------------------------------------------------------------------------
233
  def check_interpreter():
234
- """One-record semantics exhaustively over record fields, and the dependence
235
- hypothesis of the size proposition."""
236
  print("[5] Interpreter U")
237
  import torch
238
- from reflect import (Cfg, build_net, Leveled, encode_gate, encode_netlist,
239
- pad, ref_step, state_to_vec, vec_to_state)
240
  cfg = Cfg()
241
  net, inputs, outputs = build_net(cfg)
242
  lev = Leveled(net, inputs, outputs, device="cpu")
@@ -277,64 +276,10 @@ def check_interpreter():
277
  print(f" record counter advances mod G on all {cfg.G} values: "
278
  f"{'exact' if cbad == 0 else 'FAILED'}")
279
 
280
- # dependence hypothesis of the size proposition: for every record-region
281
- # coordinate q, exhibit a state x and a data coordinate s with
282
- # U(x)_s != U(x xor e_q)_s. A random state rarely witnesses this, because a
283
- # random output address lands in the 106-bit data region about one time in
284
- # ten; the targeted pass of check_dependence forces the record under test to
285
- # write into the data region and makes the tested field decisive.
286
- import random
287
- from check_dependence import targeted
288
-
289
- def U1(sig, gp):
290
- return U(sig, gp)["sig"]
291
-
292
- rng = random.Random(20260910)
293
- D0, D1 = cfg.WORK_BASE, cfg.NET0
294
- Q0 = cfg.NET0
295
- Qbits = list(range(Q0, Q0 + cfg.G * cfg.R))
296
- influenced = set()
297
- for q in Qbits:
298
- g, r = divmod(q - Q0, cfg.R)
299
- found = 0
300
- for attempt in range(160):
301
- slots = [(rng.randrange(D0, D1 - 1), rng.choice((-1, 0, 1)),
302
- rng.randint(0, 1)) for _ in range(2)]
303
- bias = rng.randrange(-(1 << (cfg.BB - 1)), 1 << (cfg.BB - 1))
304
- out = (rng.randrange(D0, D1 - 1), rng.randint(0, 1))
305
- rec = encode_gate(cfg, pad(cfg, slots), bias, out)
306
- sig = [0] * cfg.S
307
- for d in range(D0, D1):
308
- sig[d] = rng.randint(0, 1)
309
- ptr = rng.choice((0, 1, 2, 3))
310
- for k in range(cfg.A):
311
- sig[cfg.PTR_BASE + k] = (ptr >> (cfg.A - 1 - k)) & 1
312
- base = cfg.bank_base(0) + g * cfg.R
313
- sig[base:base + cfg.R] = rec
314
- s0, s1 = list(sig), list(sig)
315
- s1[q] ^= 1
316
- a0, a1 = U1(s0, g), U1(s1, g)
317
- if any(a0[d] != a1[d] for d in range(D0, D1)):
318
- found = attempt + 1
319
- break
320
- if not found:
321
- found = targeted(cfg, U1, rng, q, g, r, D0, D1)
322
- if found:
323
- influenced.add(q)
324
- print(f" every one of the {len(Qbits)} record-region bits influences some "
325
- f"data-region bit of U(x): {len(influenced)}/{len(Qbits)}"
326
- f"{' (hypothesis holds)' if len(influenced) == len(Qbits) else ' FAILED'}")
327
  R["interpreter"] = {
328
  "onerecord_cases": total, "onerecord_failures": bad,
329
- "counter_failures": cbad,
330
- "Q_bits": len(Qbits), "influencing": len(influenced),
331
- "S": cfg.S, "G": cfg.G, "R": cfg.R, "b": cfg.R,
332
- "Q_size": cfg.G * cfg.R, "records_max": (cfg.G * cfg.R) // cfg.R,
333
- "units_lower_bound": (cfg.G * cfg.R + 1) // 2,
334
  }
335
- print(f" |Q| = {cfg.G*cfg.R}, b = {cfg.R}: at most {(cfg.G*cfg.R)//cfg.R} "
336
- f"records stored; any fan-in-2 netlist realising U has at least "
337
- f"{(cfg.G*cfg.R+1)//2} units")
338
 
339
 
340
  # ---------------------------------------------------------------------------
 
231
 
232
  # ---------------------------------------------------------------------------
233
  def check_interpreter():
234
+ """One-record semantics exhaustively over record fields, and the record counter."""
 
235
  print("[5] Interpreter U")
236
  import torch
237
+ from reflect import (Cfg, build_net, Leveled, encode_netlist, state_to_vec,
238
+ vec_to_state)
239
  cfg = Cfg()
240
  net, inputs, outputs = build_net(cfg)
241
  lev = Leveled(net, inputs, outputs, device="cpu")
 
276
  print(f" record counter advances mod G on all {cfg.G} values: "
277
  f"{'exact' if cbad == 0 else 'FAILED'}")
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  R["interpreter"] = {
280
  "onerecord_cases": total, "onerecord_failures": bad,
281
+ "counter_failures": cbad, "S": cfg.S, "G": cfg.G, "b": cfg.R,
 
 
 
 
282
  }
 
 
 
283
 
284
 
285
  # ---------------------------------------------------------------------------