CharlesCNorton commited on
Commit
8f34e5f
·
1 Parent(s): 4dbae82

demos: standalone per-machine programs that put each machine to work

Browse files

A new demos/ folder, one script per machine class, each self-contained and
cross-checked against an independent reference:

- neural_computer8: self-modifying Sieve of Eratosthenes (all 54 primes < 256),
Euclid's GCD via DIV/MUL/SUB, Collatz step counts.
- neural_rv32: pi by Machin's formula to nine digits; ternary neural nets run as
NEUR software (one learned by gradient descent, one XOR compiled by hand).
- neural_matrix8: 65,536 CPUs stepped in lockstep as one batched matrix product.
- neural_attractor: factoring semiprimes by running a multiplier backward, and
6-/8-queens by relaxing toward zero unsatisfied clauses.
- neural_subleq8io: the universal constructor fabricates a sibling byte-for-byte
and boots it.
- neural_reflect: self-modifying SUBLEQ on a stored machine on the interpreter.
- neural_reversible: bijective mixing, exact reversal, and the counterfactual.
- neural_ca: a Loschmidt echo and a reversible-automaton block cipher.
- neural_tile: self-assembly grows Pascal mod 2, verified against Lucas' theorem.

demos/README.md ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # demos
2
+
3
+ Standalone programs that put each machine in the family to work on a named
4
+ task. Every script is self-contained (`python demos/<name>.py`), loads the
5
+ shipped weights, and cross-checks its result against an independent reference.
6
+
7
+ | Demo | Machine | What it does |
8
+ |------|---------|--------------|
9
+ | `neural_computer8_self_modifying_sieve.py` | `neural_computer8` | Sieve of Eratosthenes; with no indexed addressing, the program rewrites the address bytes of its own LOAD/STORE. Finds all 54 primes < 256. |
10
+ | `neural_computer8_euclid_gcd.py` | `neural_computer8` | Euclid's GCD, with `a mod b` built from the DIV/MUL/SUB opcodes and an XOR register swap. |
11
+ | `neural_computer8_collatz.py` | `neural_computer8` | Collatz step counts; exact for every seed whose trajectory stays inside the 8-bit range. |
12
+ | `neural_rv32_machin_pi.py` | `neural_rv32` | pi to nine digits by Machin's 1706 arctangent formula, printed to the console. |
13
+ | `neural_rv32_neural_nets_via_neur.py` | `neural_rv32` | Ternary neural nets run as NEUR software: one learned by gradient descent, one (XOR) compiled by construction. |
14
+ | `neural_matrix8_gpu_cpu_fleet.py` | `neural_matrix8` | 65,536 CPUs stepped in lockstep on the GPU as one batched matrix product. |
15
+ | `neural_attractor_factoring.py` | `neural_attractor` | Factors semiprimes by relaxing an 8x8 multiplier's energy backward; also divides. |
16
+ | `neural_attractor_nqueens.py` | `neural_attractor` | Solves 6- and 8-queens by relaxing toward zero unsatisfied clauses. |
17
+ | `neural_subleq8io_universal_constructor.py` | `neural_subleq8io` | The universal constructor fabricates a sibling machine byte-for-byte, which then boots. |
18
+ | `neural_reflect_self_modifying_stack.py` | `neural_reflect` | Self-modifying SUBLEQ code, on a stored machine, on the fixed interpreter: three levels in one tensor. |
19
+ | `neural_reversible_counterfactual.py` | `neural_reversible` | Bijective mixing, exact reverse recovery, and the counterfactual when a digest bit is flipped. |
20
+ | `neural_ca_loschmidt_echo.py` | `neural_ca` | A ~2,000-particle gas mixed 500 steps and un-mixed exactly; one flipped cell corrupts half the past. |
21
+ | `neural_ca_reversible_cipher.py` | `neural_ca` | The reversible automaton as a block cipher: exact decryption with the right key, noise with a wrong one. |
22
+ | `neural_tile_pascal_lucas.py` | `neural_tile` | Self-assembly grows Pascal's triangle mod 2; every cell verified against Lucas' theorem. |
demos/neural_attractor_factoring.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_attractor -- factoring semiprimes by running a multiplier backward.
2
+
3
+ An 8x8 array multiplier compiles to a quadratic energy that is 0 exactly on its
4
+ truth table. Clamp the 16 product bits and relax the 16 input bits and the
5
+ network settles into a factorization: multiplication run in reverse. Clamp one
6
+ input as well and the same circuit performs division.
7
+
8
+ python demos/neural_attractor_factoring.py
9
+ """
10
+ import os, sys, time
11
+ HERE = os.path.dirname(os.path.abspath(__file__))
12
+ REPO = os.path.dirname(HERE)
13
+ sys.path.insert(0, os.path.join(REPO, "src"))
14
+ from attractor import multiplier
15
+
16
+
17
+ if __name__ == "__main__":
18
+ c, io = multiplier(8)
19
+ print("neural_attractor: factoring by energy relaxation")
20
+ print("=" * 56)
21
+ print(f"8x8 array multiplier as an energy landscape: {len(c.gates)} gate "
22
+ f"gadgets, {c.n} wires, 16 free input bits\n")
23
+
24
+ ladder = [(3599, 59, 61), (9797, 97, 101), (32399, 179, 181), (57599, 239, 241)]
25
+ for N, pa, pb in ladder:
26
+ target = {io["prod"][k]: (N >> k) & 1 for k in range(16)}
27
+ t0, got = time.perf_counter(), None
28
+ for attempt in range(1, 8):
29
+ s = c.solve(io["xs"] + io["ys"], {io["zero"]: 0}, target,
30
+ sweeps=2500, restarts=12, seed=N * 7 + attempt)
31
+ if s is not None:
32
+ x = sum(s[io["xs"][k]] << k for k in range(8))
33
+ y = sum(s[io["ys"][k]] << k for k in range(8))
34
+ if x * y == N and x > 1 and y > 1:
35
+ got = (x, y, attempt); break
36
+ dt = time.perf_counter() - t0
37
+ if got:
38
+ x, y, att = got
39
+ tag = "the prime pair" if {x, y} == {pa, pb} else "valid factorization"
40
+ print(f" {N:5d} = {x:3d} x {y:3d} ({dt:4.1f}s, attempt {att}) [{tag}]")
41
+ else:
42
+ print(f" {N:5d}: ground state not reached in budget ({dt:.1f}s)")
43
+
44
+ # division: clamp one input and the product, relax the other input
45
+ N, xa = 57599, 241
46
+ fixed = {io["zero"]: 0}
47
+ for k in range(8):
48
+ fixed[io["xs"][k]] = (xa >> k) & 1
49
+ target = {io["prod"][k]: (N >> k) & 1 for k in range(16)}
50
+ t0 = time.perf_counter()
51
+ s = c.solve(io["ys"], fixed, target, sweeps=800, restarts=10, seed=7)
52
+ if s is not None:
53
+ y = sum(s[io["ys"][k]] << k for k in range(8))
54
+ print(f"\n division mode: clamp x=241 and the product -> y = {y} "
55
+ f"({time.perf_counter()-t0:.1f}s; {xa} x {y} = {xa * y})")
demos/neural_attractor_nqueens.py ADDED
@@ -0,0 +1,90 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_attractor -- the N-queens problem solved by energy relaxation.
2
+
3
+ The attractor computer has no clock and no program counter: a circuit compiles
4
+ to a quadratic energy whose global minimum is its consistent assignment, and
5
+ "running" it is rolling downhill. Here every N-queens constraint (one queen per
6
+ row, no two sharing a column or diagonal) becomes a clause wire that must be 1;
7
+ the annealer relaxes the board variables toward zero unsatisfied clauses. This
8
+ is the min-conflicts / MAX-SAT objective, expressed entirely in the family's
9
+ Heaviside threshold gadgets.
10
+
11
+ python demos/neural_attractor_nqueens.py
12
+ """
13
+ import os, sys, time
14
+ HERE = os.path.dirname(os.path.abspath(__file__))
15
+ REPO = os.path.dirname(HERE)
16
+ sys.path.insert(0, os.path.join(REPO, "src"))
17
+ from attractor import Circuit
18
+
19
+
20
+ def queens_circuit(N):
21
+ """Build the N-queens CNF as a threshold circuit; return every clause wire
22
+ so the solver can drive them all to 1 (a graded, per-clause objective)."""
23
+ c = Circuit()
24
+ v = {(r, col): c.wire() for r in range(N) for col in range(N)}
25
+ clauses = []
26
+
27
+ def OR_lits(lits):
28
+ acc = lits[0]
29
+ for w in lits[1:]:
30
+ acc = c.OR(acc, w)
31
+ return acc
32
+
33
+ for r in range(N): # each row: >= 1 queen
34
+ clauses.append(OR_lits([v[(r, col)] for col in range(N)]))
35
+ for c1 in range(N): # each row: <= 1 queen
36
+ for c2 in range(c1 + 1, N):
37
+ clauses.append(OR_lits([c.NOT(v[(r, c1)]), c.NOT(v[(r, c2)])]))
38
+ for col in range(N): # each column: <= 1
39
+ for r1 in range(N):
40
+ for r2 in range(r1 + 1, N):
41
+ clauses.append(OR_lits([c.NOT(v[(r1, col)]), c.NOT(v[(r2, col)])]))
42
+ cells = [(r, col) for r in range(N) for col in range(N)]
43
+ for i in range(len(cells)): # each diagonal: <= 1
44
+ for j in range(i + 1, len(cells)):
45
+ (r1, c1), (r2, c2) = cells[i], cells[j]
46
+ if r1 - c1 == r2 - c2 or r1 + c1 == r2 + c2:
47
+ clauses.append(OR_lits([c.NOT(v[(r1, c1)]), c.NOT(v[(r2, c2)])]))
48
+ return c, v, clauses
49
+
50
+
51
+ def valid(board, N):
52
+ qs = [rc for rc, b in board.items() if b]
53
+ if len(qs) != N:
54
+ return False
55
+ for i in range(len(qs)):
56
+ for j in range(i + 1, len(qs)):
57
+ (r1, c1), (r2, c2) = qs[i], qs[j]
58
+ if r1 == r2 or c1 == c2 or abs(r1 - r2) == abs(c1 - c2):
59
+ return False
60
+ return True
61
+
62
+
63
+ def solve_board(N, seeds=8):
64
+ c, v, clauses = queens_circuit(N)
65
+ free = list(v.values())
66
+ target = {w: 1 for w in clauses}
67
+ t0 = time.perf_counter()
68
+ for seed in range(seeds):
69
+ s = c.solve(free, {}, target, sweeps=6000, restarts=6, seed=seed)
70
+ if s is not None:
71
+ board = {rc: s[w] for rc, w in v.items()}
72
+ if valid(board, N):
73
+ return board, len(clauses), c.n, time.perf_counter() - t0
74
+ return None, len(clauses), c.n, time.perf_counter() - t0
75
+
76
+
77
+ if __name__ == "__main__":
78
+ print("neural_attractor: N-queens as energy relaxation")
79
+ print("=" * 56)
80
+ for N in (6, 8):
81
+ board, nclauses, nwires, dt = solve_board(N)
82
+ status = "SOLVED" if board else "no solution reached in budget"
83
+ print(f"\n{N}-queens: {nclauses} clause gadgets over {nwires} wires -> "
84
+ f"{status} ({dt:.1f}s)")
85
+ if board:
86
+ for r in range(N):
87
+ print(" " + " ".join("Q" if board[(r, col)] else "." for col in range(N)))
88
+ print(f" verified: {N} queens, no shared row/column/diagonal")
89
+ print("\nNo search loop was written: the constraints are an energy surface and")
90
+ print("the solution is its floor. The queens fall into place.")
demos/neural_ca_loschmidt_echo.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_ca -- a Loschmidt echo in a reversible cellular automaton.
2
+
3
+ The Margolus block rule is a bijection, so a gas of ~2,000 particles mixed for
4
+ 500 steps can be un-mixed by iterating the same rule backward: the initial
5
+ configuration returns cell-for-cell (particle number conserved throughout). Yet
6
+ flipping a single cell of the mixed state before reversing corrupts roughly half
7
+ the reconstructed past -- exact reversibility and sensitive dependence in the
8
+ same automaton.
9
+
10
+ python demos/neural_ca_loschmidt_echo.py
11
+ """
12
+ import os, sys, time, random, statistics
13
+ HERE = os.path.dirname(os.path.abspath(__file__))
14
+ REPO = os.path.dirname(HERE)
15
+ sys.path.insert(0, os.path.join(REPO, "src"))
16
+ import ca
17
+
18
+
19
+ def coarse(g, H, W, k=8):
20
+ out = []
21
+ for by in range(0, H, k):
22
+ for bx in range(0, W, k):
23
+ out.append(sum(g[y][x] for y in range(by, by + k) for x in range(bx, bx + k)))
24
+ return out
25
+
26
+
27
+ if __name__ == "__main__":
28
+ H = W = 64
29
+ rng = random.Random(2026)
30
+ grid = [[1 if rng.random() < 0.5 else 0 for _ in range(W)] for _ in range(H)]
31
+ n0 = sum(map(sum, grid))
32
+ STEPS = 500
33
+
34
+ print("neural_ca: Loschmidt echo (mix, then run time backward)")
35
+ print("=" * 56)
36
+ t0 = time.perf_counter()
37
+ fwd = ca.run(grid, STEPS, 0)
38
+ n1 = sum(map(sum, fwd))
39
+ back = ca.run_back(fwd, STEPS, 0)
40
+ echo = back == grid
41
+ dt = time.perf_counter() - t0
42
+
43
+ print(f"particles: {n0} at t=0, {n1} at t={STEPS} "
44
+ f"({'conserved' if n0 == n1 else 'NOT CONSERVED'})")
45
+ print(f"coarse 8x8 occupancy stdev: t=0 {statistics.pstdev(coarse(grid, H, W)):.2f} "
46
+ f"-> t={STEPS} {statistics.pstdev(coarse(fwd, H, W)):.2f} (mixed)")
47
+ print(f"{STEPS} steps forward + {STEPS} reversed in {dt:.1f}s: "
48
+ f"t=0 recovered {'EXACTLY' if echo else 'FAILED'}")
49
+
50
+ flip = [row[:] for row in fwd]
51
+ flip[0][0] ^= 1
52
+ back2 = ca.run_back(flip, STEPS, 0)
53
+ ham = sum(back2[y][x] != grid[y][x] for y in range(H) for x in range(W))
54
+ print(f"butterfly: flip ONE cell at t={STEPS}, reverse again -> reconstructed "
55
+ f"past wrong in {ham}/{H * W} cells")
demos/neural_ca_reversible_cipher.py ADDED
@@ -0,0 +1,84 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_ca -- a reversible cellular automaton as a perfect cipher.
2
+
3
+ The Margolus block rule is a bijection of the lattice, so iterating it forward
4
+ diffuses a bitmap into noise and iterating the exact same rule backward restores
5
+ it bit-for-bit. That makes the automaton a block cipher whose key is the pair
6
+ (number of steps, starting partition phase): the right key inverts the diffusion
7
+ perfectly, and a key off by a single step returns noise. No information is ever
8
+ destroyed, so decryption is exact rather than approximate.
9
+
10
+ python demos/neural_ca_reversible_cipher.py
11
+ """
12
+ import os, sys
13
+ HERE = os.path.dirname(os.path.abspath(__file__))
14
+ REPO = os.path.dirname(HERE)
15
+ sys.path.insert(0, os.path.join(REPO, "src"))
16
+ import ca
17
+
18
+ # a recognizable plaintext bitmap (a heart), padded into a larger lattice
19
+ ART = [
20
+ " ### ### ",
21
+ " ##### ##### ",
22
+ "#############",
23
+ "#############",
24
+ "#############",
25
+ " ########### ",
26
+ " ######### ",
27
+ " ####### ",
28
+ " ##### ",
29
+ " ### ",
30
+ " # ",
31
+ ]
32
+ PAD_X, PAD_Y = 6, 4
33
+
34
+
35
+ def make_grid():
36
+ # the Margolus partition tiles 2x2 blocks toroidally, so H and W must be even
37
+ h = len(ART) + 2 * PAD_Y
38
+ w = len(ART[0]) + 2 * PAD_X
39
+ h += h & 1
40
+ w += w & 1
41
+ g = [[0] * w for _ in range(h)]
42
+ for y, row in enumerate(ART):
43
+ for x, ch in enumerate(row):
44
+ if ch == "#":
45
+ g[y + PAD_Y][x + PAD_X] = 1
46
+ return g
47
+
48
+
49
+ def render(g):
50
+ return "\n".join("".join("#" if c else "." for c in row) for row in g)
51
+
52
+
53
+ def hamming(a, b):
54
+ return sum(a[y][x] != b[y][x] for y in range(len(a)) for x in range(len(a[0])))
55
+
56
+
57
+ if __name__ == "__main__":
58
+ plain = make_grid()
59
+ KEY_STEPS, KEY_PHASE = 200, 0
60
+
61
+ cipher = ca.run(plain, KEY_STEPS, KEY_PHASE)
62
+ recovered = ca.run_back(cipher, KEY_STEPS, KEY_PHASE)
63
+ wrong = ca.run_back(cipher, KEY_STEPS - 1, KEY_PHASE) # key off by one step
64
+
65
+ n = sum(map(sum, plain))
66
+ print("neural_ca: reversible-automaton cipher")
67
+ print("=" * 46)
68
+ print(f"key = ({KEY_STEPS} steps, phase {KEY_PHASE}); {n} set bits (conserved "
69
+ f"throughout: {sum(map(sum, cipher)) == n})\n")
70
+
71
+ print("PLAINTEXT:")
72
+ print(render(plain))
73
+ print(f"\nCIPHERTEXT after {KEY_STEPS} forward steps (diffused to noise):")
74
+ print(render(cipher))
75
+ print(f"\nDECRYPTED with the correct key:")
76
+ print(render(recovered))
77
+ print(f" exact recovery: {recovered == plain} (Hamming distance 0)")
78
+ print(f"\nDECRYPTED with a wrong key (off by one step):")
79
+ print(render(wrong))
80
+ print(f" still scrambled: {hamming(wrong, plain)} of {len(plain)*len(plain[0])} "
81
+ f"cells differ from the plaintext")
82
+ print("\nEvery step is a bijection, so the ciphertext holds exactly the")
83
+ print("information of the plaintext -- no more, no less. Only the exact")
84
+ print("reverse trajectory recovers it.")
demos/neural_computer8_collatz.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_computer8 -- the Collatz map iterated on the threshold CPU.
2
+
3
+ n -> n/2 if even, 3n+1 if odd, counting steps to 1. The parity test is a
4
+ bitwise AND with 1 that leaves the flags untouched, so the loop branches on the
5
+ comparison that precedes it. The register file is architecturally 8-bit, so the
6
+ machine computes the Collatz trajectory modulo 256; for seeds whose true
7
+ trajectory never exceeds 255, that is the exact Collatz step count, checked here
8
+ against a native reference.
9
+
10
+ python demos/neural_computer8_collatz.py
11
+ """
12
+ import os, sys, time
13
+ HERE = os.path.dirname(os.path.abspath(__file__))
14
+ REPO = os.path.dirname(HERE)
15
+ sys.path.insert(0, os.path.join(REPO, "src"))
16
+ sys.path.insert(0, os.path.join(REPO, "tools"))
17
+ from safetensors import safe_open
18
+ from cpu_programs import Asm
19
+ from eval_all import GenericThresholdCPU, get_manifest
20
+
21
+
22
+ def build(n0):
23
+ a = Asm(1024)
24
+ a.load(0, "N"); a.load(1, "ONE"); a.xor(2, 2) # R0=n, R1=1, R2=steps
25
+ a.label("loop")
26
+ a.cmp(0, 1); a.jz("done") # n == 1 ?
27
+ a.xor(3, 3); a.add(3, 0); a.and_(3, 1) # R3 = n & 1 (flags preserved)
28
+ a.cmp(3, 1); a.jz("odd")
29
+ a.shr(0); a.jmp("step") # even: n >>= 1
30
+ a.label("odd")
31
+ a.xor(3, 3); a.add(3, 0); a.shl(3) # R3 = 2n
32
+ a.add(0, 3); a.add(0, 1) # n = 3n + 1
33
+ a.label("step")
34
+ a.add(2, 1); a.jmp("loop") # steps++
35
+ a.label("done")
36
+ a.store(2, "RES"); a.halt()
37
+ a.org(0x300); a.label("N"); a.db(n0)
38
+ a.label("ONE"); a.db(1); a.label("RES"); a.db(0)
39
+ return a, a.assemble()
40
+
41
+
42
+ def true_collatz(n):
43
+ peak, steps = n, 0
44
+ while n != 1:
45
+ n = n // 2 if n % 2 == 0 else 3 * n + 1
46
+ peak = max(peak, n); steps += 1
47
+ return steps, peak
48
+
49
+
50
+ if __name__ == "__main__":
51
+ tens = {}
52
+ with safe_open(os.path.join(REPO, "variants", "neural_computer8_small.safetensors"),
53
+ framework="pt") as f:
54
+ for name in f.keys():
55
+ tens[name] = f.get_tensor(name).float()
56
+ cpu = GenericThresholdCPU(tens)
57
+ addr = get_manifest(tens)["addr_bits"]
58
+
59
+ print("neural_computer8: Collatz step counts through the gates")
60
+ print("=" * 56)
61
+ print(" seed CPU steps true steps peak in 8-bit range?")
62
+ for n0 in (6, 7, 9, 18, 25, 45):
63
+ a, mem = build(n0)
64
+ state = {"pc": 0, "regs": [0] * 4, "flags": [0] * 4, "mem": list(mem),
65
+ "halted": False, "sp": (1 << addr) - 1}
66
+ t0 = time.perf_counter()
67
+ final, cycles = cpu.run(state, max_cycles=3000)
68
+ steps = final["mem"][a.labels["RES"]]
69
+ tsteps, peak = true_collatz(n0)
70
+ inrange = peak <= 255
71
+ match = "MATCH" if (inrange and steps == tsteps) else \
72
+ ("mod-256" if not inrange else "MISMATCH")
73
+ print(f" {n0:4d} {steps:9d} {tsteps:10d} {peak:4d} "
74
+ f"{'yes' if inrange else 'no (wraps)':10s} [{match}]")
75
+ print("\n For every seed whose trajectory stays under 256 the threshold CPU")
76
+ print(" reproduces the exact Collatz step count.")
demos/neural_computer8_euclid_gcd.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_computer8 -- Euclid's algorithm on the threshold CPU.
2
+
3
+ The greatest common divisor by repeated remainder, where the remainder itself
4
+ is synthesized from the machine's own opcodes: a mod b = a - (a / b) * b, using
5
+ the DIV, MUL and SUB gates, and the register swap is three XORs (no temporary).
6
+ Every step runs through threshold neurons.
7
+
8
+ python demos/neural_computer8_euclid_gcd.py
9
+ """
10
+ import os, sys, time, math
11
+ HERE = os.path.dirname(os.path.abspath(__file__))
12
+ REPO = os.path.dirname(HERE)
13
+ sys.path.insert(0, os.path.join(REPO, "src"))
14
+ sys.path.insert(0, os.path.join(REPO, "tools"))
15
+ from safetensors import safe_open
16
+ from cpu_programs import Asm
17
+ from eval_all import GenericThresholdCPU, get_manifest
18
+
19
+
20
+ def build(a0, b0):
21
+ g = Asm(1024)
22
+ g.load(0, "A") # R0 = a
23
+ g.load(1, "B") # R1 = b
24
+ g.xor(3, 3) # R3 = 0
25
+ g.label("loop")
26
+ g.cmp(1, 3); g.jz("done") # while b != 0
27
+ g.xor(2, 2); g.add(2, 0) # R2 = a
28
+ g._alu(0x8, 2, 1) # R2 = a / b (DIV opcode)
29
+ g.mul(2, 1) # R2 = (a/b)*b
30
+ g.sub(0, 2) # R0 = a mod b
31
+ g.xor(0, 1); g.xor(1, 0); g.xor(0, 1) # swap -> (b, a mod b)
32
+ g.jmp("loop")
33
+ g.label("done")
34
+ g.store(0, "RES"); g.halt()
35
+ g.org(0x300)
36
+ g.label("A"); g.db(a0)
37
+ g.label("B"); g.db(b0)
38
+ g.label("RES"); g.db(0)
39
+ return g, g.assemble()
40
+
41
+
42
+ if __name__ == "__main__":
43
+ tens = {}
44
+ with safe_open(os.path.join(REPO, "variants", "neural_computer8_small.safetensors"),
45
+ framework="pt") as f:
46
+ for name in f.keys():
47
+ tens[name] = f.get_tensor(name).float()
48
+ cpu = GenericThresholdCPU(tens)
49
+ addr = get_manifest(tens)["addr_bits"]
50
+
51
+ print("neural_computer8: Euclid's GCD through the gates")
52
+ print("=" * 56)
53
+ for a0, b0 in [(84, 36), (120, 90), (221, 34), (255, 128)]:
54
+ g, mem = build(a0, b0)
55
+ state = {"pc": 0, "regs": [0] * 4, "flags": [0] * 4, "mem": list(mem),
56
+ "halted": False, "sp": (1 << addr) - 1}
57
+ t0 = time.perf_counter()
58
+ final, cycles = cpu.run(state, max_cycles=400)
59
+ res = final["mem"][g.labels["RES"]]
60
+ ok = res == math.gcd(a0, b0)
61
+ print(f" gcd({a0:3d}, {b0:3d}) = {res:3d} ({cycles:3d} cycles, "
62
+ f"{time.perf_counter()-t0:.1f}s) "
63
+ f"[{'MATCH' if ok else 'MISMATCH vs ' + str(math.gcd(a0, b0))}]")
demos/neural_computer8_self_modifying_sieve.py ADDED
@@ -0,0 +1,76 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_computer8 -- the Sieve of Eratosthenes with self-modifying code.
2
+
3
+ The threshold CPU's ISA has no indexed addressing, so the program does exactly
4
+ what memory-constrained 1970s code did: it rewrites the address bytes of its own
5
+ LOAD and STORE instructions before each access, walking a pointer across the
6
+ flags array. Every gate that fetches, decodes, marks, and patches is a threshold
7
+ neuron. It halts with all 54 primes below 256 marked.
8
+
9
+ python demos/neural_computer8_self_modifying_sieve.py
10
+ """
11
+ import os, sys, time
12
+ HERE = os.path.dirname(os.path.abspath(__file__))
13
+ REPO = os.path.dirname(HERE)
14
+ sys.path.insert(0, os.path.join(REPO, "src"))
15
+ sys.path.insert(0, os.path.join(REPO, "tools"))
16
+ from safetensors import safe_open
17
+ from cpu_programs import Asm, _enc
18
+ from eval_all import GenericThresholdCPU, get_manifest
19
+
20
+ FLAGS = 0x300 # flags[0..255] at 0x300..0x3FF
21
+
22
+
23
+ def build():
24
+ g = Asm(1024)
25
+ g.load(3, "ONE") # R3 = 1 (mark value / increment)
26
+ g.load(0, "TWO") # R0 = p = 2
27
+ g.label("outer")
28
+ g.store(0, "Rlo") # patch probe address low byte <- p
29
+ g.dw(_enc(0xA, 2, 0)) # probe: R2 = flags[p] (self-modified)
30
+ g.db(FLAGS >> 8); g.label("Rlo"); g.db(0x00)
31
+ g.cmp(2, 3); g.jz("next_p") # flags[p] == 1 -> composite, skip
32
+ g.xor(1, 1); g.add(1, 0); g.add(1, 0) # m = 2p
33
+ g.label("inner")
34
+ g.store(1, "Wlo") # patch mark address low byte <- m
35
+ g.dw(_enc(0xB, 0, 3)) # mark: flags[m] = R3 = 1 (self-modified)
36
+ g.db(FLAGS >> 8); g.label("Wlo"); g.db(0x00)
37
+ g.add(1, 0) # m += p (carry iff we passed 255)
38
+ g.jnc("inner")
39
+ g.label("next_p")
40
+ g.add(0, 3) # p += 1
41
+ g.load(2, "SIXTEEN")
42
+ g.cmp(0, 2)
43
+ g.jnz("outer") # loop until p == 16 (16^2 > 255)
44
+ g.halt()
45
+ g.org(0x200)
46
+ g.label("ONE"); g.db(1)
47
+ g.label("TWO"); g.db(2)
48
+ g.label("SIXTEEN"); g.db(16)
49
+ return g, g.assemble()
50
+
51
+
52
+ if __name__ == "__main__":
53
+ g, mem = build()
54
+ tens = {}
55
+ with safe_open(os.path.join(REPO, "variants", "neural_computer8_small.safetensors"),
56
+ framework="pt") as f:
57
+ for name in f.keys():
58
+ tens[name] = f.get_tensor(name).float()
59
+ cpu = GenericThresholdCPU(tens)
60
+ state = {"pc": 0, "regs": [0] * 4, "flags": [0] * 4, "mem": list(mem),
61
+ "halted": False, "sp": (1 << get_manifest(tens)["addr_bits"]) - 1}
62
+ t0 = time.perf_counter()
63
+ final, cycles = cpu.run(state, max_cycles=4000)
64
+ dt = time.perf_counter() - t0
65
+
66
+ got = [n for n in range(2, 256) if final["mem"][FLAGS + n] == 0]
67
+ ref = [n for n in range(2, 256) if all(n % d for d in range(2, int(n**0.5) + 1))]
68
+ print("neural_computer8: self-modifying Sieve of Eratosthenes")
69
+ print("=" * 56)
70
+ print(f"halted={final['halted']} {cycles} cycles through the gates ({dt:.0f}s)")
71
+ print(f"primes < 256 found: {len(got)} native sieve: {len(ref)} "
72
+ f"{'EXACT MATCH' if got == ref else 'MISMATCH'}")
73
+ print(" " + " ".join(map(str, got)))
74
+ print(f"self-modified operand bytes at halt: probe={final['mem'][g.labels['Rlo']]}, "
75
+ f"mark={final['mem'][g.labels['Wlo']]} (the program rewrote its own "
76
+ f"instruction stream as it ran)")
demos/neural_matrix8_gpu_cpu_fleet.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_matrix8 -- a fleet of processors as one batched matrix product.
2
+
3
+ The whole CPU is compiled to 108 ternary weight matrices with a Heaviside step
4
+ between them, so one clock cycle is one matrix-vector product plus a threshold.
5
+ Add a batch dimension and N processors are the same 108 matmuls: here 65,536
6
+ independent CPUs step in lockstep on the GPU. A 256-wide correctness fleet
7
+ confirms every CPU halts exactly at cycle 2n+1 for its own countdown input.
8
+
9
+ python demos/neural_matrix8_gpu_cpu_fleet.py
10
+ """
11
+ import os, sys, time
12
+ HERE = os.path.dirname(os.path.abspath(__file__))
13
+ REPO = os.path.dirname(HERE)
14
+ sys.path.insert(0, os.path.join(REPO, "src"))
15
+ import torch
16
+ from matrix8 import MatrixMachine, state_to_vec, _mk_state, _instr
17
+
18
+
19
+ def prog_bytes():
20
+ return (_instr(0x1, 0, 1) # SUB R0, R1
21
+ + _instr(0xD, 0, 0, 1) + [0, 0] # JNZ 0x0000 (address word)
22
+ + _instr(0xF) # HALT
23
+ + [0] * 8)
24
+
25
+
26
+ if __name__ == "__main__":
27
+ dev = "cuda" if torch.cuda.is_available() else "cpu"
28
+ mm = MatrixMachine.from_file(device=dev)
29
+ n_neurons = sum(int(b.numel()) for b in mm.B)
30
+ name = torch.cuda.get_device_name(0) if dev == "cuda" else "cpu"
31
+ print("neural_matrix8: 65,536 CPUs as one batched matrix product")
32
+ print("=" * 56)
33
+ print(f"loaded {len(mm.W)} ternary matrices, {n_neurons:,} threshold neurons "
34
+ f"per transition, device={dev} ({name})")
35
+ mem = prog_bytes()
36
+
37
+ # correctness fleet: one CPU per 8-bit input, all 256 at once
38
+ V = torch.stack([state_to_vec(_mk_state(mem=mem, regs=(x, 1, 0, 0)))
39
+ for x in range(256)]).to(dev)
40
+ first_halt = torch.full((256,), -1, dtype=torch.long)
41
+ step = 0
42
+ while step < 600:
43
+ halted = V[:, MatrixMachine.HALT_IDX] > 0.5
44
+ first_halt[(first_halt < 0) & halted.cpu()] = step
45
+ if bool(halted.all()):
46
+ break
47
+ V = mm.step(V); step += 1
48
+ expect = torch.tensor([2 * (x if x else 256) + 1 for x in range(256)])
49
+ ok_halt = bool((first_halt == expect).all())
50
+ ok_r0 = bool((V[:, 4:12].cpu().long().sum(1) == 0).all())
51
+ print(f"\ncorrectness fleet of 256: every CPU halts at cycle 2n+1 for its own "
52
+ f"input: {'EXACT' if ok_halt else 'MISMATCH'}; all results R0==0: {ok_r0}")
53
+
54
+ # throughput fleet: 65,536 CPUs
55
+ B = 65536
56
+ V = torch.stack([state_to_vec(_mk_state(mem=mem, regs=(x & 0xFF, 1, 0, 0)))
57
+ for x in range(256)]).repeat(256, 1).to(dev)
58
+ if dev == "cuda":
59
+ torch.cuda.synchronize()
60
+ t0, steps = time.perf_counter(), 0
61
+ while steps < 520:
62
+ V = mm.step(V); steps += 1
63
+ if steps % 64 == 0 and bool((V[:, MatrixMachine.HALT_IDX] > 0.5).all()):
64
+ break
65
+ if dev == "cuda":
66
+ torch.cuda.synchronize()
67
+ dt = time.perf_counter() - t0
68
+ instr = B * steps
69
+ print(f"\nthroughput fleet of {B:,}: {steps} transitions in {dt:.1f}s")
70
+ print(f" {instr / dt / 1e6:,.1f} million instructions/s aggregate "
71
+ f"({instr:,} instructions retired)")
72
+ print(f" {instr * n_neurons / dt / 1e9:,.1f} billion threshold-neuron "
73
+ f"evaluations/s")
demos/neural_reflect_self_modifying_stack.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_reflect -- three levels of machine in one tensor.
2
+
3
+ Level 0 is U, a fixed ternary threshold interpreter (~24k gates). Level 1 is a
4
+ complete SUBLEQ computer stored as data inside U's writable state. Level 2 is a
5
+ SUBLEQ program that rewrites its own operand bytes as it runs: a zeroing
6
+ instruction that walks itself across memory, erasing a block. The interpreter,
7
+ the machine it runs, and the program that machine executes are all the same
8
+ tensor; the final memory is checked bit-for-bit against a native SUBLEQ emulator.
9
+
10
+ python demos/neural_reflect_self_modifying_stack.py
11
+ """
12
+ import os, sys, time
13
+ HERE = os.path.dirname(os.path.abspath(__file__))
14
+ REPO = os.path.dirname(HERE)
15
+ sys.path.insert(0, os.path.join(REPO, "src"))
16
+ import torch
17
+ from reflect import (Cfg, build_subleq_machine, build_net, Leveled,
18
+ encode_netlist, state_to_vec, subleq32_step, MEM, MEMB)
19
+
20
+
21
+ if __name__ == "__main__":
22
+ dev = "cuda" if torch.cuda.is_available() else "cpu"
23
+ cfg = Cfg(A=14, G=296, banks=1, self_mod=False)
24
+ prog, lay = build_subleq_machine(cfg)
25
+ inet, ii, io = build_net(cfg)
26
+ print("neural_reflect: self-modifying code on a stored machine on U")
27
+ print("=" * 60)
28
+ print(f"level 0: interpreter U = {len(inet.gates):,} threshold gates (fixed)")
29
+ print(f"level 1: a SUBLEQ machine = {len(prog)} gates, stored as writable data")
30
+ lev = Leveled(inet, ii, io, device=dev)
31
+ nl = encode_netlist(cfg, prog)
32
+ sig0 = [0] * cfg.S
33
+ sig0[cfg.NET0:cfg.NET0 + len(nl)] = nl
34
+ base = state_to_vec(cfg, {"sig": sig0, "gp": 0, "halt": 0}).to(dev)
35
+ PC = lay["PC"]
36
+
37
+ # level 2: SUBLEQ (A B C: M[B]-=M[A]; if <=0 goto C). The first instruction
38
+ # zeroes a target cell, then two instructions increment its own A and B
39
+ # operand bytes, so the zeroing walks across a data block.
40
+ pm = [0] * 32
41
+ pm[0:3] = [24, 24, 3] # zero M[<target>]; these operands get rewritten
42
+ pm[3:6] = [19, 0, 6] # M[0] += 1 (patch own A-field: cell 0)
43
+ pm[6:9] = [19, 1, 9] # M[1] += 1 (patch own B-field: cell 1)
44
+ pm[9:12] = [20, 21, 15] # count -= 1; if <= 0 branch to epilogue
45
+ pm[12:15] = [18, 18, 0] # 0 -> unconditional jump to loop head
46
+ pm[15:18] = [18, 18, 31] # epilogue: branch to 31 = HALT
47
+ pm[18], pm[19], pm[20], pm[21] = 0, 255, 1, 4
48
+ pm[24:28] = [7, 99, 123, 200] # the block the program will erase
49
+ print(f"level 2: a program whose instruction 0 starts as [24,24,3] and "
50
+ f"rewrites its own operands")
51
+
52
+ ref_mem, ref_pc = list(pm), 0
53
+ while ref_pc != 31:
54
+ ref_mem, ref_pc = subleq32_step(ref_mem, ref_pc)
55
+
56
+ V = base.unsqueeze(0).clone()
57
+ for i in range(32):
58
+ for b in range(8):
59
+ V[:, MEM + b * MEMB + i] = float((pm[i] >> b) & 1)
60
+
61
+ def get_mem(Vc):
62
+ return sum(Vc[:, MEM + b * MEMB: MEM + b * MEMB + 32].long() << b
63
+ for b in range(8))[0].tolist()
64
+
65
+ t0, walk, steps, halted = time.perf_counter(), [], 0, False
66
+ for _ in range(26):
67
+ m_now = get_mem(V.cpu())
68
+ walk.append((m_now[0], m_now[1]))
69
+ for _ in range(cfg.G):
70
+ V = lev.step(V)
71
+ steps += 1
72
+ if int(V[0, cfg.S + cfg.GPW]) == 1:
73
+ halted = True
74
+ break
75
+ dt = time.perf_counter() - t0
76
+ got = get_mem(V.cpu())
77
+
78
+ mut = " -> ".join(f"[{a},{b}]" for a, b in
79
+ [walk[0]] + [w for i, w in enumerate(walk[1:], 1) if w != walk[i - 1]])
80
+ print(f"\nran {steps} hosted instructions in {dt:.0f}s "
81
+ f"({steps * cfg.G} interpreter recurrences); halted={halted}")
82
+ print(f" instruction 0's operand cells as it ran: {mut}")
83
+ print(f" target block M[24..27]: {pm[24:28]} -> {got[24:28]}")
84
+ print(f" final memory == native SUBLEQ reference: "
85
+ f"{'EXACT' if got == ref_mem else 'MISMATCH'}")
86
+ print("\n The program, the machine it runs on, and the machine that runs")
87
+ print(" that machine are all the same tensor.")
demos/neural_reversible_counterfactual.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_reversible -- computation with no erasure, and the counterfactual.
2
+
3
+ Every instruction of this register machine is a bijection, so the same weights
4
+ run backward invert the forward computation: a mixing program hashes a seed into
5
+ a digest, and stepping in reverse reconstructs the seed exactly, with no bit ever
6
+ erased (Landauer floor: zero). Because history is only consistent with the truth,
7
+ flipping a single bit of the digest and running backward yields a "past" that is
8
+ wrong in many bits: information is conserved, so a corrupted future refuses to
9
+ invert to the real input.
10
+
11
+ python demos/neural_reversible_counterfactual.py
12
+ """
13
+ import os, sys
14
+ HERE = os.path.dirname(os.path.abspath(__file__))
15
+ REPO = os.path.dirname(HERE)
16
+ sys.path.insert(0, os.path.join(REPO, "src"))
17
+ from reversible_cpu import RCPU, _clone
18
+
19
+
20
+ if __name__ == "__main__":
21
+ # cross-coupled mixing: R0 feeds R3 and R3 feeds R0, so the avalanche spreads
22
+ prog = []
23
+ for _ in range(8):
24
+ prog += [("ADD", 0, 3), ("TOFF", 3, 0, 2), ("ROL", 0, 1), ("XOR", 2, 3)]
25
+ cpu = RCPU(prog)
26
+ seed = cpu.new_state(regs=[7, 13, 201, 0])
27
+
28
+ print("neural_reversible: no-erasure computing and the counterfactual")
29
+ print("=" * 60)
30
+ s = _clone(seed)
31
+ cpu.run(s, len(prog))
32
+ print(f"seed registers {seed['R']} --{len(prog)} bijective steps--> "
33
+ f"digest {s['R']}")
34
+
35
+ b = _clone(s)
36
+ cpu.run_back(b, len(prog))
37
+ print(f"run backward: seed recovered "
38
+ f"{'EXACTLY' if b['R'] == seed['R'] and b['PC'] == seed['PC'] else 'FAILED'} "
39
+ f"(no bit was ever erased; Landauer floor 0)")
40
+
41
+ c = _clone(s)
42
+ c["R"][3] ^= 4 # corrupt one bit of the digest
43
+ cpu.run_back(c, len(prog))
44
+ diff = sum(bin(x ^ y).count("1") for x, y in zip(c["R"], seed["R"]))
45
+ print(f"counterfactual: flip ONE digest bit, run history backward -> the "
46
+ f"reconstructed 'seed' is wrong in {diff}/32 register bits")
47
+ print(f" corrupted 'seed' = {c['R']} true seed = {seed['R']}")
48
+ print("\n History inverts only when it is the true history. A tampered")
49
+ print(" future does not lead back to the real past.")
demos/neural_rv32_machin_pi.py ADDED
@@ -0,0 +1,85 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_rv32 -- pi by Machin's formula, computed on the RISC-V threshold CPU.
2
+
3
+ pi = 16*arctan(1/5) - 4*arctan(1/239) (John Machin, 1706), evaluated as an
4
+ integer arctangent series at scale 1e8 with rounded divisions. Every add,
5
+ multiply and divide runs through the ternary threshold datapath; 239^2 is
6
+ computed on-machine, and the result is formatted to the memory-mapped console.
7
+
8
+ python demos/neural_rv32_machin_pi.py
9
+ """
10
+ import os, sys, time
11
+ HERE = os.path.dirname(os.path.abspath(__file__))
12
+ REPO = os.path.dirname(HERE)
13
+ sys.path.insert(0, os.path.join(REPO, "src"))
14
+ import torch
15
+ from machines import Asm, Rv32ThresholdCPU, rv_state
16
+ from eval_all import int_to_bits
17
+
18
+
19
+ def native():
20
+ """Integer mirror of the exact recurrence the machine will run."""
21
+ accA, t, sign, d, dsA = 0, 1600000000 // 5, 1, 1, []
22
+ while t > 0:
23
+ accA += sign * ((t + d // 2) // d); dsA.append(d)
24
+ sign, d, t = -sign, d + 2, (t + 12) // 25
25
+ accB, t, q, dsB, sign, d = 0, (400000000 + 119) // 239, 239 * 239, [], 1, 1
26
+ while t > 0:
27
+ accB += sign * ((t + d // 2) // d); dsB.append(d)
28
+ sign, d, t = -sign, d + 2, (t + q // 2) // q
29
+ return accA - accB, dsA, dsB
30
+
31
+
32
+ def build(dsA, dsB):
33
+ a = Asm()
34
+ a.lui(4, 0x5F5E1) # x4 = 1,600,000,000
35
+ a.addi(5, 0, 25); a.addi(6, 0, 10); a.addi(8, 0, 239); a.addi(14, 0, 5)
36
+ a.div(11, 4, 14) # t = 16e8/5
37
+ a.addi(10, 0, 0) # accA
38
+ for k, d in enumerate(dsA):
39
+ a.addi(12, 0, d); a.addi(13, 11, d // 2); a.div(13, 13, 12)
40
+ (a.add if k % 2 == 0 else a.sub)(10, 10, 13)
41
+ if k + 1 < len(dsA):
42
+ a.addi(13, 11, 12); a.div(11, 13, 5)
43
+ a.lui(7, 0x17D78); a.ori(7, 7, 0x400) # x7 = 400,000,000
44
+ a.addi(13, 7, 119); a.div(11, 13, 8)
45
+ a.mul(9, 8, 8) # 239^2 computed on-machine
46
+ a.srli(14, 9, 1)
47
+ a.addi(15, 0, 0) # accB
48
+ for k, d in enumerate(dsB):
49
+ a.addi(12, 0, d); a.addi(13, 11, d // 2); a.div(13, 13, 12)
50
+ (a.add if k % 2 == 0 else a.sub)(15, 15, 13)
51
+ if k + 1 < len(dsB):
52
+ a.add(13, 11, 14); a.div(11, 13, 9)
53
+ a.sub(10, 10, 15) # x10 = pi * 1e8
54
+ a.addi(20, 0, 0x7F8); a.slli(20, 20, 5) # console 0xFF00
55
+ a.lui(21, 0x5F5E); a.ori(21, 21, 0x100) # P = 1e8
56
+ a.div(22, 10, 21); a.rem(10, 10, 21)
57
+ a.addi(22, 22, 48); a.sb(22, 20, 0) # integer digit
58
+ a.addi(22, 0, 46); a.sb(22, 20, 0) # '.'
59
+ a.div(21, 21, 6); a.addi(23, 0, 8)
60
+ a.label("digits")
61
+ a.div(22, 10, 21); a.rem(10, 10, 21)
62
+ a.addi(22, 22, 48); a.sb(22, 20, 0)
63
+ a.div(21, 21, 6); a.addi(23, 23, -1); a.bne(23, 0, "digits")
64
+ a.ecall()
65
+ return a.assemble()
66
+
67
+
68
+ if __name__ == "__main__":
69
+ pi9, dsA, dsB = native()
70
+ mem = build(dsA, dsB)
71
+ print("neural_rv32: Machin's pi on the threshold datapath")
72
+ print("=" * 56)
73
+ print("loading neural_rv32 (8.77M ternary parameters)...")
74
+ cpu = Rv32ThresholdCPU()
75
+ ts = rv_state(mem)
76
+ ts["_mem_bits"] = torch.tensor([int_to_bits(b, 8) for b in ts["mem"]], dtype=torch.float32)
77
+ t0, cyc = time.perf_counter(), 0
78
+ while not ts["halted"] and cyc < 400:
79
+ ts = cpu.step(ts); cyc += 1
80
+ want = f"{pi9 // 10**8}.{pi9 % 10**8:08d}"
81
+ print(f"console output : {ts['console']!r} ({cyc} cycles, "
82
+ f"{time.perf_counter()-t0:.0f}s, dual-issue pairs {cpu.pairs_issued})")
83
+ print(f"integer mirror : {want!r} (machine {'EXACT' if ts['console']==want else 'MISMATCH'})")
84
+ print(f"true pi : 3.14159265(35...) -> all printed digits "
85
+ f"{'correct' if ts['console']=='3.14159265' else 'NOT all correct'}")
demos/neural_rv32_neural_nets_via_neur.py ADDED
@@ -0,0 +1,184 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_rv32 -- neural networks running as software on the neural-net CPU.
2
+
3
+ neural_rv32's NEUR opcode computes exactly one ternary threshold neuron:
4
+ rd = H( popcount(rs1 & pos) - popcount(rs1 & neg) + sext5(bias) )
5
+ with weights in {-1,0,+1} and an integer bias. So a program of NEUR
6
+ instructions IS a ternary neural network -- and the CPU executing it is itself
7
+ a threshold network. One net here is LEARNED by gradient descent (a nonlinear
8
+ popcount band) and one is COMPILED by construction (8-bit parity, the function
9
+ that ended the single perceptron); both are then deployed as NEUR code and run
10
+ on the threshold datapath, with the CPU output checked against the reference net.
11
+
12
+ python demos/neural_rv32_neural_nets_via_neur.py
13
+ """
14
+ import os, sys, time
15
+ HERE = os.path.dirname(os.path.abspath(__file__))
16
+ REPO = os.path.dirname(HERE)
17
+ sys.path.insert(0, os.path.join(REPO, "src"))
18
+ os.chdir(REPO)
19
+ import torch
20
+ from machines import Asm, Rv32ThresholdCPU, rv_state
21
+ from eval_all import int_to_bits
22
+
23
+ torch.manual_seed(0)
24
+
25
+
26
+ def ternary(w): # {-1,0,+1} forward, identity backward (STE)
27
+ wq = torch.where(w > 0.5, 1.0, torch.where(w < -0.5, -1.0, 0.0))
28
+ return w + (wq - w).detach()
29
+
30
+ def ibias(b, lo=-16, hi=15):
31
+ bq = b.round().clamp(lo, hi)
32
+ return b + (bq - b).detach()
33
+
34
+ # ---- discrete NEUR reference (exactly what the hardware computes) ----------
35
+ def neuron(x, pos, neg, bias):
36
+ return 1 if (bin(x & pos).count("1") - bin(x & neg).count("1") + bias) >= 0 else 0
37
+
38
+ def discrete_net(x, hidden, out):
39
+ h = 0
40
+ for j, (p, n, b) in enumerate(hidden):
41
+ h |= neuron(x, p, n, b) << j
42
+ p, n, b = out
43
+ return neuron(h, p, n, b)
44
+
45
+ # ---- train a 2-layer ternary MLP; hidden step trained with an annealed -----
46
+ # sigmoid so real gradients flow, then hardened to Heaviside at extraction
47
+ def train(target_fn, H, epochs=5000, seed=0, nbits=8):
48
+ torch.manual_seed(seed)
49
+ N = 1 << nbits
50
+ xs = torch.arange(N)
51
+ X = torch.stack([((xs >> i) & 1).float() for i in range(nbits)], 1)
52
+ Y = torch.tensor([float(target_fn(int(x))) for x in xs])
53
+ Wh = torch.nn.Parameter(torch.randn(H, nbits) * 1.5)
54
+ bh = torch.nn.Parameter(torch.randn(H) * 1.5)
55
+ Wo = torch.nn.Parameter(torch.randn(H) * 1.5)
56
+ bo = torch.nn.Parameter(torch.zeros(1))
57
+ opt = torch.optim.Adam([Wh, bh, Wo, bo], lr=0.03)
58
+ npos = float(Y.sum())
59
+ bce = torch.nn.BCEWithLogitsLoss(pos_weight=torch.tensor((N - npos) / max(npos, 1.0)))
60
+ for ep in range(epochs):
61
+ opt.zero_grad()
62
+ Tt = 1.0 + 11.0 * ep / epochs
63
+ preh = X @ ternary(Wh).T + ibias(bh)
64
+ h = torch.sigmoid(Tt * preh)
65
+ preo = h @ ternary(Wo) + ibias(bo)
66
+ loss = bce(4.0 * preo, Y)
67
+ loss.backward(); opt.step()
68
+ Whq = torch.where(Wh > 0.5, 1, torch.where(Wh < -0.5, -1, 0)).tolist()
69
+ Woq = torch.where(Wo > 0.5, 1, torch.where(Wo < -0.5, -1, 0)).tolist()
70
+ bhq = bh.round().clamp(-16, 15).long().tolist()
71
+ boq = int(bo.round().clamp(-16, 15).item())
72
+ def pack(wrow):
73
+ return (sum(1 << i for i, w in enumerate(wrow) if w > 0),
74
+ sum(1 << i for i, w in enumerate(wrow) if w < 0))
75
+ hidden = [(*pack(Whq[j]), bhq[j]) for j in range(H)]
76
+ out = (*pack(Woq), boq)
77
+ acc = sum(discrete_net(int(x), hidden, out) == int(Y[i]) for i, x in enumerate(xs)) / N
78
+ return hidden, out, acc
79
+
80
+ # ---- emit the network as NEUR code and run it on neural_rv32 ---------------
81
+ def li(a, reg, v):
82
+ v &= 0xFFFFFFFF
83
+ hi = (v + 0x800) >> 12
84
+ lo = v - (hi << 12)
85
+ a.lui(reg, hi & 0xFFFFF)
86
+ if lo: a.addi(reg, reg, lo)
87
+
88
+ def build_program(xval, hidden, out):
89
+ a = Asm()
90
+ li(a, 1, xval) # x1 = input byte
91
+ li(a, 2, 0) # x2 = packed hidden
92
+ for j, (p, n, b) in enumerate(hidden):
93
+ li(a, 4, p | (n << 8) | ((b & 0x1F) << 16)) # weight word
94
+ a.neur(3, 1, 4) # x3 = hidden neuron j
95
+ if j: a.slli(2, 2, 1)
96
+ a.or_(2, 2, 3) # pack MSB-first (neuron 0 -> bit H-1)
97
+ p, n, b = out
98
+ H = len(hidden)
99
+ def remap(mask): # match the MSB-first hidden packing
100
+ r = 0
101
+ for j in range(H):
102
+ if (mask >> j) & 1: r |= 1 << (H - 1 - j)
103
+ return r
104
+ li(a, 4, remap(p) | (remap(n) << 8) | ((b & 0x1F) << 16))
105
+ a.neur(5, 2, 4) # x5 = output
106
+ a.ecall()
107
+ return a.assemble()
108
+
109
+ def run_cpu(cpu, xval, hidden, out):
110
+ mem = build_program(xval, hidden, out)
111
+ ts = rv_state(mem)
112
+ ts["_mem_bits"] = torch.tensor([int_to_bits(b, 8) for b in ts["mem"]], dtype=torch.float32)
113
+ cyc = 0
114
+ while not ts["halted"] and cyc < 200:
115
+ ts = cpu.step(ts); cyc += 1
116
+ return ts["regs"][5], cyc
117
+
118
+
119
+ def parity(x, nbits=8): return bin(x & ((1 << nbits) - 1)).count("1") & 1
120
+ def band(x): return int(3 <= bin(x).count("1") <= 5) # non-monotone in popcount
121
+
122
+ def learn(fn, H, seeds, epochs):
123
+ best, t0, used = None, time.perf_counter(), 0
124
+ for seed in range(seeds):
125
+ used = seed
126
+ hidden, out, acc = train(fn, H=H, seed=seed, epochs=epochs)
127
+ if best is None or acc > best[2]:
128
+ best = (hidden, out, acc)
129
+ if acc == 1.0:
130
+ break
131
+ hidden, out, acc = best
132
+ print(f" trained 8->{H}->1 ternary MLP by annealed-sigmoid SGD: {acc*100:.1f}% "
133
+ f"exact over all 256 inputs ({time.perf_counter()-t0:.0f}s, seed {used}); "
134
+ f"weights all in {{-1,0,+1}}")
135
+ return hidden, out, acc
136
+
137
+ def deploy_check(cpu, fn, hidden, out, sample):
138
+ t0, cpu_vs_net, cpu_vs_true, rows = time.perf_counter(), 0, 0, []
139
+ for x in sample:
140
+ y_cpu, _ = run_cpu(cpu, x, hidden, out)
141
+ cpu_vs_net += (y_cpu == discrete_net(x, hidden, out))
142
+ cpu_vs_true += (y_cpu == fn(x))
143
+ rows.append((x, y_cpu, fn(x)))
144
+ print(f" NEUR execution on neural_rv32: CPU == reference net on "
145
+ f"{cpu_vs_net}/{len(sample)} (deployment exact); CPU == truth on "
146
+ f"{cpu_vs_true}/{len(sample)} ({time.perf_counter()-t0:.0f}s)")
147
+ return rows
148
+
149
+ def compile_parity(nbits=8): # exact thermometer construction
150
+ mask = (1 << nbits) - 1
151
+ hidden = [(mask, 0, -(k + 1)) for k in range(nbits)] # h_k = [popcount>=k+1]
152
+ pos = sum(1 << j for j in range(nbits) if j % 2 == 0) # odd thresholds
153
+ neg = sum(1 << j for j in range(nbits) if j % 2 == 1) # even thresholds
154
+ return hidden, (pos, neg, -1)
155
+
156
+
157
+ if __name__ == "__main__":
158
+ print("=" * 72)
159
+ print(" Neural networks running as software on the CPU that IS a neural net")
160
+ print("=" * 72)
161
+ cpu = Rv32ThresholdCPU()
162
+
163
+ print("\n[1] LEARNED by gradient descent -- popcount BAND: fire iff 3<=popcount<=5")
164
+ print(" (non-monotone, so no single threshold can do it; genuinely nonlinear)")
165
+ hidden, out, acc = learn(band, H=8, seeds=12, epochs=5000)
166
+ deploy_check(cpu, band, hidden, out,
167
+ [0, 1, 3, 7, 15, 31, 63, 127, 255, 0b10110, 0b1111000, 0b11011011])
168
+ line = "".join(f"{k}:{discrete_net((1 << k) - 1, hidden, out)} " for k in range(9))
169
+ print(f" net output vs popcount k: {line.strip()} (true band: k in 3,4,5)")
170
+
171
+ print("\n[2] COMPILED by construction -- 8-bit PARITY (XOR), the non-separable")
172
+ print(" function Minsky & Papert used to end the single perceptron")
173
+ hidden, out = compile_parity(8)
174
+ acc = sum(discrete_net(x, hidden, out) == parity(x) for x in range(256)) / 256
175
+ print(f" thermometer net (8 hidden 'popcount>=k' + alternating output), all "
176
+ f"weights in {{-1,0,+1}}: {acc*100:.0f}% exact over all 256 bytes")
177
+ rows = deploy_check(cpu, parity, hidden, out,
178
+ [0, 1, 3, 7, 15, 31, 85, 127, 128, 170, 200, 254, 255])
179
+ for x, yc, yt in rows:
180
+ print(f" parity({x:3d}) = {yc} (true {yt}) [{'ok' if yc==yt else 'x'}]")
181
+
182
+ print("\n One network learned by SGD, one compiled by hand, both executed")
183
+ print(" instruction by instruction on a CPU whose every gate is a threshold")
184
+ print(" neuron. The machine running the models belongs to the models' own class.")
demos/neural_subleq8io_universal_constructor.py ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_subleq8io -- von Neumann's universal constructor, to the byte.
2
+
3
+ The one-instruction machine (194 gates) runs a 21-instruction SUBLEQ program
4
+ that reads a description of any machine in the family off its tape and emits
5
+ that machine's safetensors file one byte at a time. Here it fabricates
6
+ neural_tile.safetensors on the shipped gate netlist, byte-for-byte sha-identical
7
+ to the target, and the fabricated file is then booted: tiles reconstructed from
8
+ its own bytes regrow the binary counter. A computer made of threshold gates
9
+ prints a working computer, and the product runs.
10
+
11
+ python demos/neural_subleq8io_universal_constructor.py
12
+ """
13
+ import os, sys, time, json, hashlib
14
+ HERE = os.path.dirname(os.path.abspath(__file__))
15
+ REPO = os.path.dirname(HERE)
16
+ sys.path.insert(0, os.path.join(REPO, "src"))
17
+ os.chdir(REPO)
18
+ from constructor8 import describe, ref_construct, GateHost
19
+
20
+
21
+ if __name__ == "__main__":
22
+ target_path = os.path.join(REPO, "variants", "neural_tile.safetensors")
23
+ data = open(target_path, "rb").read()
24
+ tape = describe(data)
25
+ print("neural_subleq8io: the universal constructor fabricates a sibling")
26
+ print("=" * 60)
27
+ print(f"recipe compiled: {len(data)} B machine -> {len(tape)} B tape")
28
+
29
+ out_ref, n_ref = ref_construct(tape)
30
+ assert out_ref == data
31
+ print(f"reference: constructor needs {n_ref:,} instructions")
32
+
33
+ host = GateHost()
34
+ t0 = time.perf_counter()
35
+ out, n, _ = host.run(tape, max_steps=n_ref + 100)
36
+ dt = time.perf_counter() - t0
37
+ sha_out = hashlib.sha256(out).hexdigest()[:16]
38
+ sha_tgt = hashlib.sha256(data).hexdigest()[:16]
39
+ print(f"gate netlist: {n:,} instructions in {dt:.0f}s ({n / dt:.0f} instr/s), "
40
+ f"{len(out)} bytes emitted")
41
+ print(f" sha256 fabricated={sha_out} shipped={sha_tgt} "
42
+ f"{'BYTE-IDENTICAL' if out == data else 'MISMATCH'}; "
43
+ f"instruction count == reference: {n == n_ref}")
44
+
45
+ # boot the fabricated machine: regrow its counter from its own bytes
46
+ fab = os.path.join(os.environ.get("TEMP", "."), "fabricated_neural_tile.safetensors")
47
+ open(fab, "wb").write(out)
48
+ from safetensors.torch import load_file
49
+ from safetensors import safe_open
50
+ import tile as T
51
+
52
+ t = load_file(fab)
53
+ with safe_open(fab, framework="pt") as f:
54
+ m = f.metadata()
55
+ gl = json.loads(m["glues"])
56
+ strg = {gl[i]: int(v) for i, v in enumerate(t["glue_strength"].tolist())}
57
+ tiles = []
58
+ for row, name in zip(t["tile_glues"].tolist(), json.loads(m["tile_names"])):
59
+ sides = [gl[i] if i >= 0 else "" for i in row]
60
+ tiles.append(T.Tile(N=sides[0], E=sides[1], S=sides[2], W=sides[3], name=name))
61
+ NB = 8
62
+ rows = (1 << NB) - 1
63
+ A, det = T.grow(tiles, T.counter_seed(NB), int(m["tau"]), strg, (0, 0, NB, rows))
64
+ bad = filled = 0
65
+ for y in range(1, rows + 1):
66
+ cells = [A.get((x, y)) for x in range(NB)]
67
+ if any(cc is None for cc in cells):
68
+ continue
69
+ filled += 1
70
+ v = sum((1 if cc.N == "b1" else 0) << (NB - 1 - x) for x, cc in enumerate(cells))
71
+ bad += (v != y)
72
+ print(f"boot: the fabricated file grew its counter, {filled} rows, "
73
+ f"row y encodes y: {'EXACT' if bad == 0 and filled == rows else f'{bad} bad'}")
demos/neural_tile_pascal_lucas.py ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """neural_tile -- self-assembly grows Pascal's triangle mod 2 (Lucas' theorem).
2
+
3
+ The program is a set of square tiles whose binding rule is a threshold gate: a
4
+ tile attaches at a site when the summed strength of its matching glues meets the
5
+ temperature. With the XOR rule tile set, the crystal that grows is exactly the
6
+ Sierpinski triangle -- cell (x,y) is filled iff the binomial C(x+y, x) is odd,
7
+ which by Lucas' theorem is iff x AND y == 0. Every filled cell is verified
8
+ against that arithmetic. Computation as crystal growth.
9
+
10
+ python demos/neural_tile_pascal_lucas.py
11
+ """
12
+ import os, sys, time
13
+ HERE = os.path.dirname(os.path.abspath(__file__))
14
+ REPO = os.path.dirname(HERE)
15
+ sys.path.insert(0, os.path.join(REPO, "src"))
16
+ import tile as T
17
+
18
+
19
+ if __name__ == "__main__":
20
+ N = 100
21
+ print("neural_tile: Pascal mod 2 by threshold-gated self-assembly")
22
+ print("=" * 60)
23
+ t0 = time.perf_counter()
24
+ ts = T.rule2_tileset(lambda w, s: w ^ s)
25
+ seed = T._row_col_seed([1] * (N + 1), [1] * (N + 1))
26
+ A, det = T.grow(ts, seed, tau=2, strength={}, bounds=(0, 0, N, N), max_tiles=200000)
27
+ dt = time.perf_counter() - t0
28
+
29
+ interior = [(x, y) for x in range(1, N + 1) for y in range(1, N + 1)]
30
+ grown = sum(1 for p in interior if p in A)
31
+ bad = 0
32
+ for (x, y) in interior:
33
+ v = 1 if A[(x, y)].N == "v1" else 0
34
+ lucas = 1 if (x & y) == 0 else 0 # C(x+y,x) odd <=> no carry in x+y
35
+ bad += (v != lucas)
36
+ print(f"grew {grown} rule tiles in {dt:.1f}s (directed={det}, "
37
+ f"{2 * N} anti-diagonals deep)")
38
+ print(f"tile(x,y) == [C(x+y,x) is odd] for all {len(interior)} cells: "
39
+ f"{'EXACT' if bad == 0 else f'{bad} MISMATCHES'}")
40
+ print("\ncorner of the assembly (30x30, '#' = odd binomial):")
41
+ for y in range(30, 0, -1):
42
+ print(" " + "".join("#" if A[(x, y)].N == "v1" else "." for x in range(1, 31)))