#!/usr/bin/env python3 """Offline GPU training/export only. Deployed inference and bridges are Go.""" import argparse import copy import hashlib import json import math import random import resource import struct import sys import time from pathlib import Path import numpy as np import torch from torch import nn LABELS = ["add", "subtract", "multiply", "less_than", "less_equal", "equal", "and", "or"] FEATURES, HIDDEN, LIMIT = 256, 48, 512 FROZEN_DATASET_SHA = "af0a637320a8256cd6ebcdb3486a6885f2766441c90f0cb154c11599f3c8ca3d" def require(condition, message): if not condition: raise ValueError(message) def unique_pairs(pairs): result = {} for key, value in pairs: require(key not in result, "duplicate JSON key") result[key] = value return result def validate_frozen_dataset(path): root = Path(__file__).resolve().parents[1] raw = path.read_bytes() manifest_raw = path.with_name("manifest.json").read_bytes() manifest = json.loads(manifest_raw, object_pairs_hook=unique_pairs) require(sha(raw) == FROZEN_DATASET_SHA == manifest.get("dataset_sha256"), "dataset differs from frozen pilot") require(manifest.get("model_contract_sha256") == sha((root / "model-contract.json").read_bytes()), "model contract pin mismatch") require(manifest.get("generator_source_sha256") == sha((root / "cmd/dataset/main.go").read_bytes()), "generator source pin mismatch") lines = raw.decode("utf-8").splitlines(keepends=True) rows = [json.loads(line, object_pairs_hook=unique_pairs) for line in lines] fields = {"id", "template_id", "configuration_id", "language", "split", "text", "label"} require(all(isinstance(r, dict) and set(r) == fields for r in rows), "dataset row shape mismatch") require(all(isinstance(r["text"], str) and 1 <= len(r["text"].encode("utf-8")) <= LIMIT for r in rows), "input byte limit") require(all(r["label"] in LABELS and r["split"] in ("train", "calibration", "test") and r["language"] in ("en", "ko") for r in rows), "dataset enum mismatch") require(len({r["id"] for r in rows}) == len(rows) == 2048, "duplicate IDs or wrong row count") require(len({r["text"] for r in rows}) == len(rows), "duplicate texts") require(manifest.get("rows") == {"total": 2048, "train": 1536, "calibration": 256, "test": 256}, "manifest count mismatch") split_rows = {s: [r for r in rows if r["split"] == s] for s in ("train", "calibration", "test")} for split, expected in (("train", 1536), ("calibration", 256), ("test", 256)): require(len(split_rows[split]) == expected, "split count mismatch") split_raw = "".join(line for line, row in zip(lines, rows) if row["split"] == split).encode("utf-8") require(sha(split_raw) == manifest.get("split_sha256", {}).get(split), "split hash mismatch") for a, b in (("train", "calibration"), ("train", "test"), ("calibration", "test")): require(not {r["template_id"] for r in split_rows[a]} & {r["template_id"] for r in split_rows[b]}, "template split leakage") return raw, rows, {"dataset_manifest_sha256": sha(manifest_raw), "model_contract_sha256": manifest["model_contract_sha256"], "generator_source_sha256": manifest["generator_source_sha256"]} def sha(raw): return hashlib.sha256(raw).hexdigest() def save_json(path, value): path.parent.mkdir(parents=True, exist_ok=True) path.write_text(json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True, allow_nan=False) + "\n") def features(text): encoded = text.encode("utf-8") if not 1 <= len(encoded) <= LIMIT: raise ValueError("input must contain 1..512 UTF-8 bytes; inputs are not truncated") raw = bytes(c + 32 if 65 <= c <= 90 else c for c in encoded) counts = np.zeros(FEATURES, dtype=np.float32) for n in (2, 3): for start in range(max(0, len(raw) - n + 1)): value = 2166136261 for c in raw[start:start + n]: value = ((value ^ c) * 16777619) & 0xffffffff counts[value % FEATURES] += 1 norm = math.sqrt(sum(float(c) ** 2 for c in counts)) if norm: counts *= np.float32(1 / norm) return counts def ternary(weight): scale = weight.detach().abs().mean().clamp_min(1e-8) codes = torch.round(weight / scale).clamp(-1, 1) quantized = codes * scale return weight + (quantized - weight).detach() class Model(nn.Module): def __init__(self, qat=False): super().__init__() self.first = nn.Linear(FEATURES, HIDDEN) self.last = nn.Linear(HIDDEN, len(LABELS)) self.qat = qat def forward(self, x): w1 = ternary(self.first.weight) if self.qat else self.first.weight w2 = ternary(self.last.weight) if self.qat else self.last.weight x = torch.relu(nn.functional.linear(x, w1, self.first.bias)) return nn.functional.linear(x, w2, self.last.bias) def probabilities(logits, temperature): z = logits.astype(np.float64) / temperature z -= z.max(axis=1, keepdims=True) p = np.exp(z) return p / p.sum(axis=1, keepdims=True) def scores(logits, labels, temperature): p = probabilities(logits, temperature) choices = p.argmax(axis=1) correct = choices == labels onehot = np.eye(len(LABELS))[labels] confidence = p.max(axis=1) ece, bins = 0.0, [] for i in range(10): mask = (confidence >= i / 10) & (confidence < (i + 1) / 10 if i < 9 else confidence <= 1) n = int(mask.sum()) accuracy = float(correct[mask].mean()) if n else None mean = float(confidence[mask].mean()) if n else None if n: ece += n / len(labels) * abs(accuracy - mean) bins.append({"lower": i / 10, "upper": (i + 1) / 10, "count": n, "accuracy": accuracy, "mean_confidence": mean}) return {"planned": len(labels), "observed": len(labels), "correct": int(correct.sum()), "accuracy": float(correct.mean()), "nll": float(-np.log(p[np.arange(len(labels)), labels].clip(1e-12)).mean()), "brier_multiclass_sum": float(((p - onehot) ** 2).sum(axis=1).mean()), "ece_equal_width_10": ece, "calibration_bins": bins} def calibrate(logits, labels): grid = np.geomspace(0.15, 5.0, 80) temperature = min(grid, key=lambda t: scores(logits, labels, float(t))["nll"]) p = probabilities(logits, float(temperature)) correct, confidence = p.argmax(axis=1) == labels, p.max(axis=1) candidates = [] for threshold in np.linspace(0.125, 0.99, 90): accepted = confidence >= threshold count = int(accepted.sum()) if count >= 32 and float(correct[accepted].mean()) >= 0.95: candidates.append((count, -float(threshold), float(threshold))) threshold = max(candidates)[2] if candidates else 1.0 return float(temperature), threshold def export_model(model, variant, directory, temperature, threshold): directory.mkdir(parents=True, exist_ok=False) blob, layout = bytearray(), [] for name, tensor, rows, cols in [ ("w1", model.first.weight, HIDDEN, FEATURES), ("b1", model.first.bias, 1, HIDDEN), ("w2", model.last.weight, len(LABELS), HIDDEN), ("b2", model.last.bias, 1, len(LABELS)), ]: array = tensor.detach().cpu().numpy().astype(np.float32).reshape(-1) offset, scale = len(blob), 1.0 if variant != "fp32" and name.startswith("w"): scale = max(float(np.abs(array).mean()), 1e-8) codes = np.rint(array / scale).clip(-1, 1).astype(np.int8) packed = bytearray() for start in range(0, len(codes), 5): group = [int(x) + 1 for x in codes[start:start + 5]] group += [1] * (5 - len(group)) packed.append(sum(code * (3 ** index) for index, code in enumerate(group))) blob.extend(packed) encoding = "ternary_base3_5" else: blob.extend(array.astype("