Download daisychain/verified/common.py from DaisyChainAI/DaisyChain-Train: direct link, hf CLI and curl.
- Browser
- Download file 2.03 kB
-
https://huggingface.co/DaisyChainAI/DaisyChain-Train/resolve/d51e6088b103edb81044d3431bbbc00a6d70021f/daisychain/verified/common.py
- Command line
-
hf download hf://DaisyChainAI/DaisyChain-Train@d51e6088b103edb81044d3431bbbc00a6d70021f/daisychain/verified/common.py
-
curl -L -o common.py https://huggingface.co/DaisyChainAI/DaisyChain-Train/resolve/d51e6088b103edb81044d3431bbbc00a6d70021f/daisychain/verified/common.py
2.03 kB
| """Shared helpers for verified neural units. | |
| Same discipline as the neural-aarch64 / neural-photonic / neural-ddr units: | |
| a small MLP is trained until it is *bit-identical to a golden reference over its | |
| entire finite input domain* (N/N verification). | |
| """ | |
| from __future__ import annotations | |
| import torch | |
| import torch.nn as nn | |
| def bits_of(v: int, n: int) -> torch.Tensor: | |
| """LSB-first bit vector of length n.""" | |
| return torch.tensor([(v >> i) & 1 for i in range(n)], dtype=torch.float32) | |
| def int_of(bits: torch.Tensor) -> int: | |
| """Inverse of bits_of: LSB-first bit vector -> int.""" | |
| v = 0 | |
| for i, b in enumerate(bits.tolist()): | |
| if b >= 0.5: | |
| v |= (1 << i) | |
| return v | |
| def pm(bits: torch.Tensor) -> torch.Tensor: | |
| """Map {0,1} bits to {-1,+1} for a friendlier input scale.""" | |
| return bits * 2.0 - 1.0 | |
| def mlp(inp: int, out: int, h: int = 128, layers: int = 3) -> nn.Sequential: | |
| mods: list[nn.Module] = [nn.Linear(inp, h), nn.ReLU()] | |
| for _ in range(layers - 1): | |
| mods += [nn.Linear(h, h), nn.ReLU()] | |
| mods += [nn.Linear(h, out)] | |
| return nn.Sequential(*mods) | |
| def verify(net: nn.Module, X: torch.Tensor, Ybits: torch.Tensor) -> tuple[int, int]: | |
| """Return (n_correct, n_total) over the full enumerated domain.""" | |
| net.eval() | |
| pred = (net(X) > 0).float() | |
| ok = (pred == Ybits).all(dim=1).sum().item() | |
| return int(ok), X.shape[0] | |
| def train(net: nn.Module, X: torch.Tensor, Ybits: torch.Tensor, | |
| steps: int = 4000, lr: float = 2e-3, tag: str = "") -> nn.Module: | |
| opt = torch.optim.Adam(net.parameters(), lr=lr) | |
| lossf = nn.BCEWithLogitsLoss() | |
| net.train() | |
| for s in range(steps): | |
| opt.zero_grad() | |
| loss = lossf(net(X), Ybits) | |
| loss.backward() | |
| opt.step() | |
| if tag and (s % 1000 == 0 or s == steps - 1): | |
| ok, tot = verify(net, X, Ybits) | |
| net.train() | |
| print(f" [{tag}] step {s:5d} loss {loss.item():.5f} verify {ok}/{tot}") | |
| return net | |