| """ |
| Step 1 keystone: verified GF(2^8) multiply -- the arithmetic core of Reed-Solomon |
| erasure coding (Step 2). |
| |
| A direct 16-bit -> 8-bit neural multiply is hard to drive to N/N. So we decompose |
| it the way real RS implementations do -- via log/exp tables: |
| |
| a * b = EXP[ (LOG[a] + LOG[b]) mod 255 ] (and 0 if a==0 or b==0) |
| |
| LOG and EXP are 256-entry lookups -> trivially N/N verifiable neural units. We |
| train both, compose them into a full multiply, and verify the COMPOSED multiply |
| against the golden field over ALL 65,536 (a,b) pairs. |
| |
| Field: GF(2^8), reduction polynomial 0x11D, generator 2 (standard for RS). |
| """ |
| from __future__ import annotations |
| import torch |
| from .common import mlp, train, verify, pm, bits_of, int_of, run8 |
|
|
| POLY = 0x11D |
|
|
|
|
| def build_tables(): |
| exp = [0] * 512 |
| log = [0] * 256 |
| x = 1 |
| for i in range(255): |
| exp[i] = x |
| log[x] = i |
| x <<= 1 |
| if x & 0x100: |
| x ^= POLY |
| for i in range(255, 512): |
| exp[i] = exp[i - 255] |
| return exp, log |
|
|
|
|
| EXP, LOG = build_tables() |
|
|
|
|
| def gf_mul(a: int, b: int) -> int: |
| if a == 0 or b == 0: |
| return 0 |
| return EXP[LOG[a] + LOG[b]] |
|
|
|
|
| |
| def log_domain(): |
| xs = [pm(bits_of(a, 8)) for a in range(1, 256)] |
| ys = [[float(b) for b in bits_of(LOG[a], 8)] for a in range(1, 256)] |
| return torch.stack(xs), torch.tensor(ys) |
|
|
|
|
| def exp_domain(): |
| xs = [pm(bits_of(i, 8)) for i in range(255)] |
| ys = [[float(b) for b in bits_of(EXP[i], 8)] for i in range(255)] |
| return torch.stack(xs), torch.tensor(ys) |
|
|
|
|
| def gf_mul_neural(a: int, b: int, net_log, net_exp) -> int: |
| if a == 0 or b == 0: |
| return 0 |
| s = (run8(net_log, a) + run8(net_log, b)) % 255 |
| return run8(net_exp, s) |
|
|
|
|
| def train_units(): |
| print(" training LOG (8->8):") |
| net_log = train(mlp(8, 8, 256, 2), *log_domain(), steps=8000, tag="log") |
| print(" training EXP (8->8):") |
| net_exp = train(mlp(8, 8, 256, 2), *exp_domain(), steps=8000, tag="exp") |
| return net_log, net_exp |
|
|
|
|
| def verify_mul(net_log, net_exp) -> tuple[int, int]: |
| ok = 0 |
| for a in range(256): |
| for b in range(256): |
| ok += (gf_mul_neural(a, b, net_log, net_exp) == gf_mul(a, b)) |
| return ok, 256 * 256 |
|
|