CharlesCNorton commited on
Commit ·
cbc9ef0
1
Parent(s): ac103bc
neural_reversible: ship the reversible arithmetic core as variants/neural_reversible.safetensors (8-bit in-place adder as a reversible gate sequence plus the Heaviside weights realizing each gate); builder round-trips it and confirms the loaded circuit is a bijection computing b<-a+b
Browse files- README.md +6 -0
- tools/build_reversible.py +101 -0
- variants/neural_reversible.safetensors +3 -0
README.md
CHANGED
|
@@ -636,9 +636,15 @@ turning that into measured energy below the Landauer bound requires adiabatic
|
|
| 636 |
drive of the crossbar, which is the physical frontier. Logical reversibility is
|
| 637 |
proven here.
|
| 638 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 639 |
```bash
|
| 640 |
python src/reversible.py # reversible gates, Cuccaro ALU, Bennett construction
|
| 641 |
python src/reversible_cpu.py # bijective transition and backward execution
|
|
|
|
| 642 |
```
|
| 643 |
|
| 644 |
---
|
|
|
|
| 636 |
drive of the crossbar, which is the physical frontier. Logical reversibility is
|
| 637 |
proven here.
|
| 638 |
|
| 639 |
+
The arithmetic core ships as `variants/neural_reversible.safetensors`, an 8-bit
|
| 640 |
+
in-place adder (`b <- a+b`) stored as its reversible gate sequence together with
|
| 641 |
+
the Heaviside AND/XOR weights that realize each gate, so the file holds both the
|
| 642 |
+
reversible program and the threshold substrate it runs on.
|
| 643 |
+
|
| 644 |
```bash
|
| 645 |
python src/reversible.py # reversible gates, Cuccaro ALU, Bennett construction
|
| 646 |
python src/reversible_cpu.py # bijective transition and backward execution
|
| 647 |
+
python tools/build_reversible.py # ship + round-trip variants/neural_reversible.safetensors
|
| 648 |
```
|
| 649 |
|
| 650 |
---
|
tools/build_reversible.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Ship the reversible machine's arithmetic core as a threshold-gate artifact,
|
| 2 |
+
variants/neural_reversible.safetensors. The circuit is an in-place 8-bit adder
|
| 3 |
+
(b <- a+b) expressed as a sequence of reversible gates (CNOT, Toffoli); each gate
|
| 4 |
+
is realized by the Heaviside AND/XOR weights stored alongside, so the file holds
|
| 5 |
+
both the reversible program (the gate list) and the threshold substrate it runs
|
| 6 |
+
on. Round-trips the file and confirms the loaded circuit is a bijection that
|
| 7 |
+
computes b <- a+b with the addend and carry restored."""
|
| 8 |
+
from __future__ import annotations
|
| 9 |
+
import os
|
| 10 |
+
import random
|
| 11 |
+
import sys
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
from safetensors.torch import save_file, load_file
|
| 15 |
+
from safetensors import safe_open
|
| 16 |
+
|
| 17 |
+
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
| 18 |
+
sys.path.insert(0, os.path.join(ROOT, "src"))
|
| 19 |
+
import reversible as rv
|
| 20 |
+
|
| 21 |
+
OUT = os.path.join(ROOT, "variants", "neural_reversible.safetensors")
|
| 22 |
+
WIDTH = 8
|
| 23 |
+
|
| 24 |
+
# The two reversible primitives, keyed to their gate functions.
|
| 25 |
+
_CODE = {rv.CNOT: 0, rv.TOFF: 1}
|
| 26 |
+
_FN = {0: rv.CNOT, 1: rv.TOFF}
|
| 27 |
+
|
| 28 |
+
# Heaviside threshold gates that implement the target updates: a CNOT target is
|
| 29 |
+
# XOR(t,c); a Toffoli target is XOR(t, AND(a,b)). AND/OR/NAND are single gates.
|
| 30 |
+
_SUBSTRATE = {
|
| 31 |
+
"and_w": torch.tensor([1, 1]), "and_b": torch.tensor(-2),
|
| 32 |
+
"or_w": torch.tensor([1, 1]), "or_b": torch.tensor(-1),
|
| 33 |
+
"nand_w": torch.tensor([-1, -1]), "nand_b": torch.tensor(1),
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def encode(ops):
|
| 38 |
+
codes, args = [], []
|
| 39 |
+
for gate, *a in ops:
|
| 40 |
+
codes.append(_CODE[gate])
|
| 41 |
+
args.append((a + [-1, -1, -1])[:3])
|
| 42 |
+
return torch.tensor(codes, dtype=torch.long), torch.tensor(args, dtype=torch.long)
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def main() -> int:
|
| 46 |
+
a_bits = list(range(WIDTH))
|
| 47 |
+
b_bits = list(range(WIDTH, 2 * WIDTH))
|
| 48 |
+
carry = 2 * WIDTH
|
| 49 |
+
n = 2 * WIDTH + 1
|
| 50 |
+
ops = rv._adder_ops(a_bits, b_bits, carry)
|
| 51 |
+
codes, args = encode(ops)
|
| 52 |
+
|
| 53 |
+
tensors = {"gate_code": codes, "gate_args": args, **_SUBSTRATE}
|
| 54 |
+
import json
|
| 55 |
+
meta = {"width": str(WIDTH), "n_wires": str(n),
|
| 56 |
+
"a_bits": json.dumps(a_bits), "b_bits": json.dumps(b_bits),
|
| 57 |
+
"carry": str(carry), "circuit": "in-place reversible adder b<-a+b"}
|
| 58 |
+
save_file(tensors, OUT, metadata=meta)
|
| 59 |
+
print(f"Built {os.path.relpath(OUT, ROOT)}: reversible {WIDTH}-bit adder")
|
| 60 |
+
print(f" gates={len(ops)} wires={n} size={os.path.getsize(OUT)} bytes")
|
| 61 |
+
|
| 62 |
+
# round-trip: reconstruct the op list from the file and run it
|
| 63 |
+
t = load_file(OUT)
|
| 64 |
+
with safe_open(OUT, framework="pt") as f:
|
| 65 |
+
m = f.metadata()
|
| 66 |
+
W = int(m["width"]); ab = json.loads(m["a_bits"]); bb = json.loads(m["b_bits"])
|
| 67 |
+
cy = int(m["carry"]); nn = int(m["n_wires"])
|
| 68 |
+
loaded = []
|
| 69 |
+
for code, a in zip(t["gate_code"].tolist(), t["gate_args"].tolist()):
|
| 70 |
+
loaded.append((_FN[code], *[x for x in a if x >= 0]))
|
| 71 |
+
|
| 72 |
+
def run(reg):
|
| 73 |
+
for gate, *a in loaded:
|
| 74 |
+
gate(reg, *a)
|
| 75 |
+
|
| 76 |
+
mask = (1 << W) - 1
|
| 77 |
+
bad = 0
|
| 78 |
+
rng = random.Random(0)
|
| 79 |
+
for _ in range(400):
|
| 80 |
+
a = rng.randint(0, mask); b = rng.randint(0, mask)
|
| 81 |
+
reg = [0] * nn
|
| 82 |
+
for k in range(W):
|
| 83 |
+
reg[ab[k]] = (a >> k) & 1
|
| 84 |
+
reg[bb[k]] = (b >> k) & 1
|
| 85 |
+
run(reg)
|
| 86 |
+
got_b = sum(reg[bb[k]] << k for k in range(W))
|
| 87 |
+
got_a = sum(reg[ab[k]] << k for k in range(W))
|
| 88 |
+
if got_b != ((a + b) & mask) or got_a != a or reg[cy] != 0:
|
| 89 |
+
bad += 1
|
| 90 |
+
print(f" round-trip b<-a+b, addend & carry restored (400 cases): "
|
| 91 |
+
f"{'OK' if bad == 0 else f'FAIL({bad})'}")
|
| 92 |
+
# bijection is exhaustive-checkable at width 4 on the same construction
|
| 93 |
+
a4 = list(range(4)); b4 = list(range(4, 8)); c4 = 8
|
| 94 |
+
ops4 = rv._adder_ops(a4, b4, c4)
|
| 95 |
+
perm = rv.is_permutation(lambda r: rv._apply(r, ops4), 9)
|
| 96 |
+
print(f" loaded circuit is a bijection (4-bit): {'OK' if perm else 'FAIL'}")
|
| 97 |
+
return 0 if (bad == 0 and perm) else 1
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
if __name__ == "__main__":
|
| 101 |
+
sys.exit(main())
|
variants/neural_reversible.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:9ee9363e618d246cc2009082e61ccc48fc237917d75231bb417e9175238073f1
|
| 3 |
+
size 2304
|