Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/emit-bitexact-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ on:
- "tools/verify_multitarget.py"
- "tools/verify_trainer_c.py"
- "tools/fuzz_trainer.py"
- "tools/verify_igla_race.py"
- "specs/igla/race/ternary_mac.t27"
- "specs/ternary/gft_smul.t27"
- "specs/ternary/gft_sadd.t27"
- ".github/workflows/emit-bitexact-gate.yml"
Expand Down Expand Up @@ -56,3 +58,6 @@ jobs:

- name: Differential-fuzz the trainer (random topologies, edge inputs)
run: python3 tools/fuzz_trainer.py 40

- name: Cross-check IGLA RACE ternary MAC (C + Rust == model)
run: python3 tools/verify_igla_race.py
9 changes: 8 additions & 1 deletion docs/NOW.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
# NOW — feat: differential fuzzer for the trainer (random topologies + edge inputs) (2026-08-07)
# NOW — feat: cross-target bit-exact for IGLA RACE ternary MAC + gen-backend findings (2026-08-07)

Last updated: 2026-08-07

## feat: apply the multi-target harness to IGLA RACE ternary_mac -- bridge + findings (Refs #1764)

- Applied the GF-T trainer's multi-target discipline to IGLA RACE. `tools/verify_igla_race.py` emits `ternary_mul`/`ternary_mac` (the multiplier-free R-SI-1 MAC primitives, {-1,0,+1} weights) from `specs/igla/race/ternary_mac.t27` to C and Rust via t27c and cross-checks against an independent reference over 800 vectors incl. edges (a=-128 sign-flip wrap, invalid codes>2 decode to 0, i32-accumulator edges): **C == Rust == reference BIT-EXACT** -- the RACE ternary MAC core is now multi-target-verified, closing the "RACE specs sealed/simulated but not cross-checked" gap. Verified the guard catches a corrupted sign-flip (161 mismatches)
- **FINDING (gen-backend gaps on this IGLA spec):** the FULL spec does NOT emit compilable C or Rust -- gen-c chokes on slice `.len()` (in `ternary_dot`) and emits test functions twice (redefinition); gen-rust emits `serde::Serialize/Deserialize` derives (serde not available) + a non-Copy struct moved twice. The tool reports this as a NOTE and verifies the core arithmetic by extracting decode/mul/mac (which don't use slices). So RACE's "spec -> any target" is only true for the arithmetic core, not the whole spec
- **FINDING (provenance):** `.trinity/seals/math_math-igla-primitives.json` exists but its source `specs/math/igla_primitives.t27` is ABSENT from the tree (imported by `coder/arch.t27`, `coder/training.t27`) -- a live seal over a missing spec
- Wired into emit-bitexact-gate (SKIPs if cc/rustc/t27c absent). Realizes the IGLA<->GF-T-trainer bridge from the prior research cycle. Tool+CI only; Refs #1764

## feat: fuzz C-trainer == GF-T model over random topologies with edge values (Refs #1764)

- The whole-trainer cross-target proof (verify_trainer_c) checked a few CHOSEN nets on a fixed 80-step sequence. `tools/fuzz_trainer.py` widens it to a RANDOMIZED space: random topology (1-3 inputs, 1-3 hidden layers of width 1-5, 1-3 outputs) x random training inputs with EDGE VALUES injected (offset-saturation-large 1e5, tiny 1e-9, exact 0/+-1/+-2/+-0.5), cross-checking the C trainer against the Python GF-T model per step
Expand Down
206 changes: 206 additions & 0 deletions tools/verify_igla_race.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
#!/usr/bin/env python3
"""Cross-target bit-exactness for IGLA RACE's ternary MAC core.

The RACE ternary accelerator specs (specs/igla/race/ternary_mac.t27) are sealed and
simulated but were never cross-checked across t27's backends. This applies the same
multi-target discipline the GF-T trainer uses: it emits `ternary_mul` / `ternary_mac`
(the multiplier-free R-SI-1 MAC primitives, {-1,0,+1} weights) to C and Rust via
t27c, compiles both, and cross-checks against an independent Python reference over
random + edge inputs -- a=-128 sign-flip wrap, invalid codes (>2 decode to 0), and
i32-accumulator edges. Closes the "RACE specs not multi-target-verified" gap.

Self-contained + CI-friendly: SKIPs (exit 0) if t27c / cc / rustc is missing; a real
divergence exits 1. Run: python3 tools/verify_igla_race.py
"""
import os, re, sys, shutil, subprocess, tempfile, random

ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
SPEC = "specs/igla/race/ternary_mac.t27"
N = 800


def skip(msg):
print(f"SKIP verify_igla_race: {msg}"); sys.exit(0)


def find_t27c():
for p in ("target/debug/t27c", "target/release/t27c"):
c = os.path.join(ROOT, p)
if os.path.exists(c):
return c
return shutil.which("t27c")


def i8(x): return ((x + 128) & 0xFF) - 128
def i32(x): return ((x + (1 << 31)) & 0xFFFFFFFF) - (1 << 31)


def ref_mul(a, code):
a = i8(a); d = 1 if code == 1 else (-1 if code == 2 else 0)
if d == 0: return 0
return i8(a if d == 1 else -a)


def ref_mac(acc, a, code): return i32(i32(acc) + ref_mul(a, code))


def gen_vectors():
r = random.Random(7777)
A = [-128, -1, 0, 1, 127]; C = [0, 1, 2, 3, 255]; ACC = [0, (1 << 31) - 1, -(1 << 31), 1000000]
v = []
for a in A:
for c in C:
for ac in ACC:
v.append((a, c, ac))
while len(v) < N:
v.append((r.randint(-128, 127), r.choice([0, 1, 2, 3, r.randint(0, 255)]),
r.randint(-(1 << 31), (1 << 31) - 1)))
return v[:N]


def _brace_block(src, start):
"""Return src[start .. matching close brace], brace-matched from the first {."""
b = src.find("{", start)
if b < 0:
return None
depth, j = 0, b
while j < len(src):
if src[j] == "{": depth += 1
elif src[j] == "}":
depth -= 1
if depth == 0:
return src[start:j + 1]
j += 1
return None


def _extract_def(src, sig):
"""Extract the DEFINITION (sig followed by a `{...}` body), skipping any
prototype (`sig;`). Returns the block or None."""
for m in re.finditer(re.escape(sig), src):
rest = src[m.end():]
nxt = rest.lstrip()
if nxt[:1] == "{":
return _brace_block(src, m.start())
return None


def full_spec_compiles(t27c, wd):
"""Diagnostic: does the WHOLE spec emit compilable C and Rust? Returns (c_ok,
rust_ok, note). This surfaces gen-backend gaps in the IGLA spec (slice .len(),
duplicate test emission, serde deps, non-Copy struct)."""
c = subprocess.run([t27c, "gen-c", SPEC], capture_output=True, text=True, cwd=ROOT).stdout
open(os.path.join(wd, "full.c"), "w").write('#define assert_eq(x,y) ((void)0)\n' + c + "\nint main(){return 0;}\n")
c_ok = subprocess.run(["cc", "-c", "-o", os.path.join(wd, "f.o"), os.path.join(wd, "full.c")],
cwd=wd, capture_output=True, text=True).returncode == 0
r = subprocess.run([t27c, "gen-rust", SPEC], capture_output=True, text=True, cwd=ROOT).stdout
open(os.path.join(wd, "full.rs"), "w").write(r + "\nfn main(){}\n")
r_ok = subprocess.run(["rustc", "-A", "warnings", "--emit=metadata", "-o", os.path.join(wd, "f.rmeta"),
os.path.join(wd, "full.rs")], cwd=wd, capture_output=True, text=True).returncode == 0
return c_ok, r_ok


def _core_c(t27c):
src = subprocess.run([t27c, "gen-c", SPEC], capture_output=True, text=True, cwd=ROOT).stdout
st = re.search(r"typedef struct\s*\{[^}]*\}\s*TernaryWeight\s*;", src)
defs = [_extract_def(src, s) for s in (
"int8_t ternary_decode(TernaryWeight w)",
"int8_t ternary_mul(int8_t a, TernaryWeight w)",
"int32_t ternary_mac(int32_t acc, int8_t a, TernaryWeight w)")]
if not st or any(d is None for d in defs):
return None
return "#include <stdint.h>\n#include <stdio.h>\n" + st.group(0) + "\n" + "\n".join(defs)


def _core_rust(t27c):
src = subprocess.run([t27c, "gen-rust", SPEC], capture_output=True, text=True, cwd=ROOT).stdout
ms = re.search(r"pub struct TernaryWeight\b", src)
if not ms:
return None
st = _brace_block(src, ms.start()) # "pub struct TernaryWeight { pub code: u8, }"
blocks = []
for name in ("ternary_decode", "ternary_mul", "ternary_mac"):
m = re.search(r"pub fn " + name + r"\b", src)
if not m:
return None
blk = _brace_block(src, m.start()) # Rust has definitions only, no prototypes
if blk is None:
return None
blocks.append(blk)
if st is None:
return None
return "#[derive(Clone, Copy)]\n" + st + "\n" + "\n".join(blocks)


def run_c(t27c, vecs, wd):
core = _core_c(t27c)
if core is None:
return None
A = ",".join(str(a) for a, _, _ in vecs); C = ",".join(str(c) for _, c, _ in vecs)
AC = ",".join(str(ac) for _, _, ac in vecs)
src = (core + f'\nint main(){{int8_t A[]={{{A}}}; uint8_t C[]={{{C}}}; int32_t AC[]={{{AC}}}; int n={len(vecs)};'
f'for(int i=0;i<n;i++){{TernaryWeight w; w.code=C[i];'
f'printf("%d %d\\n",(int)ternary_mul(A[i],w),(int)ternary_mac(AC[i],A[i],w));}}return 0;}}')
open(os.path.join(wd, "m.c"), "w").write(src)
if subprocess.run(["cc", "-O2", "-o", os.path.join(wd, "cb"), os.path.join(wd, "m.c")],
cwd=wd, capture_output=True, text=True).returncode != 0:
return None
out = subprocess.run([os.path.join(wd, "cb")], capture_output=True, text=True).stdout
return [tuple(map(int, ln.split())) for ln in out.strip().splitlines()]


def run_rust(t27c, vecs, wd):
core = _core_rust(t27c)
if core is None:
return None
A = ",".join(str(a) for a, _, _ in vecs); C = ",".join(str(c) for _, c, _ in vecs)
AC = ",".join(str(ac) for _, _, ac in vecs)
src = core + (f'\nfn main(){{let a:[i8;{len(vecs)}]=[{A}]; let c:[u8;{len(vecs)}]=[{C}]; '
f'let ac:[i32;{len(vecs)}]=[{AC}]; for i in 0..a.len(){{let w=TernaryWeight{{code:c[i]}}; '
f'println!("{{}} {{}}", ternary_mul(a[i],w) as i32, ternary_mac(ac[i],a[i],w));}}}}\n')
rs = os.path.join(wd, "m.rs"); open(rs, "w").write(src)
if subprocess.run(["rustc", "-A", "warnings", "-O", "-o", os.path.join(wd, "rb"), rs],
cwd=wd, capture_output=True, text=True).returncode != 0:
return None
out = subprocess.run([os.path.join(wd, "rb")], capture_output=True, text=True).stdout
return [tuple(map(int, ln.split())) for ln in out.strip().splitlines()]


def main():
t27c = find_t27c()
if not t27c:
skip("t27c not found")
if not os.path.exists(os.path.join(ROOT, SPEC)):
skip("ternary_mac.t27 not found")
if not shutil.which("cc"):
skip("no C compiler")
if not shutil.which("rustc"):
skip("rustc not on PATH")
vecs = gen_vectors()
ref = [(ref_mul(a, c), ref_mac(ac, a, c)) for a, c, ac in vecs]
ok = True
with tempfile.TemporaryDirectory() as wd:
# diagnostic: does the WHOLE spec emit compilable C/Rust? (surfaces gen gaps)
c_ok, r_ok = full_spec_compiles(t27c, wd)
print(f"NOTE full-spec gen-c compiles: {c_ok} | gen-rust compiles: {r_ok} "
f"(if False, the emitter has gaps on this IGLA spec -- slice .len(), "
f"duplicate test emission, serde deps, non-Copy struct; core arithmetic checked below)")
for tgt, runner in (("C", run_c), ("Rust", run_rust)):
got = runner(t27c, vecs, wd)
if got is None:
print(f"FAIL: {tgt} backend failed to build/run"); ok = False; continue
if len(got) != len(ref):
print(f"FAIL: {tgt} produced {len(got)} of {len(ref)}"); ok = False; continue
mism = [(i, r, g) for i, (r, g) in enumerate(zip(ref, got)) if r != g]
if mism:
i, r, g = mism[0]
print(f"FAIL: {tgt} != ref in {len(mism)}/{len(ref)}; first vec {vecs[i]} ref={r} {tgt}={g}")
ok = False
else:
print(f"OK ternary_mul/mac: {tgt} == reference BIT-EXACT over {len(ref)} vectors (edges incl.)")
print("IGLA RACE ternary MAC BIT-EXACT ACROSS TARGETS (C + Rust + model)" if ok else "IGLA RACE CROSS-TARGET MISMATCH")
sys.exit(0 if ok else 1)


if __name__ == "__main__":
main()
Loading