CharlesCNorton
neural_attractor: an energy-based threshold computer where computation is relaxation to a ground state and the program is the coupling matrix. No program counter, no clock, no forward-only execution: clamp any subset of wires and relax. AND/OR/NOT energy gadgets (each zero iff the gate relation holds) make it universal by construction; forward evaluation is exact, and clamping outputs runs circuits backward (an 8x8 multiplier compiled to couplings factors 35=5x7, 143=11x13) or solves SAT. Module, tests, artifact builder, and the shipped coupling matrix.
7ed141b | """Compile a circuit into the attractor computer's coupling matrix and ship it | |
| as variants/neural_attractor.safetensors, a peer artifact to the other machines: | |
| here the weights are the couplings Q (with linear terms L), and running is | |
| relaxation. Round-trips the file and checks forward evaluation and a backward | |
| inversion (factoring).""" | |
| from __future__ import annotations | |
| import os | |
| import random | |
| import sys | |
| import torch | |
| from safetensors.torch import save_file, load_file | |
| from safetensors import safe_open | |
| ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) | |
| sys.path.insert(0, os.path.join(ROOT, "src")) | |
| from attractor import multiplier, to_tensors, from_tensors | |
| OUT = os.path.join(ROOT, "variants", "neural_attractor.safetensors") | |
| BITS = 8 | |
| def main() -> int: | |
| c, io = multiplier(BITS) | |
| tensors, meta = to_tensors(c, io) | |
| save_file(tensors, OUT, metadata=meta) | |
| size = os.path.getsize(OUT) | |
| print(f"Built {os.path.relpath(OUT, ROOT)}: {BITS}x{BITS} multiplier as couplings") | |
| print(f" wires={c.n} Q entries={len(c.Q)} L entries={len(c.L)} size={size/1024:.1f} KB") | |
| # round-trip | |
| t = load_file(OUT) | |
| with safe_open(OUT, framework="pt") as f: | |
| m = f.metadata() | |
| c2, io2 = from_tensors(t, m) | |
| xs, ys, zero, prod = io2["xs"], io2["ys"], io2["zero"], io2["prod"] | |
| rng = random.Random(0) | |
| bad = 0 | |
| for _ in range(200): | |
| a, b = rng.randint(0, 255), rng.randint(0, 255) | |
| clamp = {zero: 0} | |
| for k in range(BITS): | |
| clamp[xs[k]] = (a >> k) & 1 | |
| clamp[ys[k]] = (b >> k) & 1 | |
| s = c2.forward_eval(clamp) | |
| got = sum(s[w] << k for k, w in enumerate(prod)) | |
| if got != a * b or c2.energy(s) != 0: | |
| bad += 1 | |
| print(f" round-trip forward multiply (200 cases): {'OK' if bad == 0 else f'FAIL({bad})'}") | |
| # backward: factor a small product through the loaded couplings | |
| N = 35 | |
| target = {prod[k]: (N >> k) & 1 for k in range(2 * BITS)} | |
| s = c2.solve(xs + ys, {zero: 0}, target, sweeps=2500, restarts=120, seed=N) | |
| if s is not None: | |
| fa = sum(s[xs[k]] << k for k in range(BITS)) | |
| fb = sum(s[ys[k]] << k for k in range(BITS)) | |
| print(f" round-trip backward factor {N}: {fa} x {fb} {'OK' if fa * fb == N else 'WRONG'}") | |
| ok = fa * fb == N | |
| else: | |
| print(f" round-trip backward factor {N}: not found") | |
| ok = False | |
| return 0 if (bad == 0 and ok) else 1 | |
| if __name__ == "__main__": | |
| sys.exit(main()) | |