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 +3 -6
- paper/runs/paper_organism.json +0 -93
- paper/runs/paper_settle.json +0 -12
- paper/runs/paper_total.json +0 -13
- src/check_dependence.py +0 -121
- src/check_settle.py +0 -108
- src/check_total.py +0 -156
- src/organism.py +0 -318
- src/paper_verify.py +4 -59
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,
|
| 1031 |
-
|
| 1032 |
-
|
| 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
|
| 235 |
-
hypothesis of the size proposition."""
|
| 236 |
print("[5] Interpreter U")
|
| 237 |
import torch
|
| 238 |
-
from reflect import (Cfg, build_net, Leveled,
|
| 239 |
-
|
| 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 |
# ---------------------------------------------------------------------------
|