Safetensors
Trellis
English
vllm
glm_moe_dsa
siq
exl3
frankenmodel
Mixture of Experts
pruning
sparkinfer
torture-test
modelopt
Instructions to use malaiwah/GLM-5.2-Legume with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Trellis
How to use malaiwah/GLM-5.2-Legume with Trellis:
# No code snippets available yet for this library. # To use this model, check the repository files and the library's documentation. # Want to help? PRs adding snippets are welcome at: # https://github.com/huggingface/huggingface.js
- Notebooks
- Google Colab
- Kaggle
| #!/usr/bin/env python3 | |
| """ | |
| encode_tr3_v31.py — GLM-5.2 NVFP4 -> EXL3-trellis-3.0 hybrid production encoder. | |
| v2: CALIBRATED LDLQ (owner mandate — the v1 identity-Hessian shortcut is gone). | |
| v3: CROSS-SLICE LOCKSTEP LDLQ — same math, same bits, ~2-3x the speed. | |
| v3.1: v3 + CROSS-SLICE LOCKSTEP g_scale GOLDEN-SECTION SEARCH — same bits. | |
| v2's LDLQ main pass launches ext.quantize_tiles on one 16-row block of ONE | |
| slice at a time (n=512 slices -> 32 tiles/call) against a kernel wave of | |
| 2*SM tile-slots (296 on B200), idling ~89% of the wave. v3 co-processes | |
| many independent slice-walks in LOCKSTEP: at each LDLQ step it gathers the | |
| current 16-row block from every active walk into ONE quantize_tiles call, | |
| scatters the results back, and runs each walk's error-feedback GEMMs on | |
| that walk's own tensors. Per-slice operand values and op order are exactly | |
| v2's, and each tile's trellis DP is independent of every other tile in the | |
| batch (quantize.cu: tile_idx = blockIdx.x; all pointers offset purely by | |
| tile_idx; per-tile scratch slots), so v3 output is BIT-IDENTICAL to v2 — | |
| asserted once per worker (lockstep_self_check) and proven on real slices by | |
| --oracle (hard deployment gate). | |
| New CLI (encode CLI otherwise unchanged): --lockstep N (default 'auto' = | |
| 12 = all 3*TP slice-walks of one expert; N>12 pools ceil(N/12) experts). | |
| Validation extras: --oracle / --oracle-experts / --oracle-log, --bench / | |
| --bench-experts (both read-only wrt --work). Everything else — prologue | |
| math (finalize_capture_H, regularize, g_scale_gss per slice, seeds, su/sv | |
| draws), resumability, bitmap selection, bit-exactness asserts, logging, | |
| assemble/upload — is v2 verbatim. Done-JSON gains a "lockstep" field. | |
| v3.1 extends the same lockstep idea to the remaining serial hot spot, | |
| regularize()'s per-slice g_scale golden-section search: the 12 slice-walks' | |
| searches are independent, so at each golden-section step ALL active | |
| searches' (sampled tiles x candidate scale) evaluations are gathered into | |
| ONE quantize_tiles call, the per-search MSEs scattered back, and each | |
| search's bracket advanced independently (searches whose bracket closes drop | |
| out of the pool; survivors keep pooling). Tile sampling, candidate scales, | |
| iteration sequence, bracket arithmetic and tie behavior are VERBATIM | |
| v3 (= v2 = reference), so output stays bit-identical — asserted once per | |
| worker (pooled-vs-sequential g_scale equality on the first expert pool, | |
| g_scale_gss_lockstep self_check) and proven on real slices by --oracle | |
| (v3.1 vs v3 vs v2 byte equality, hard deployment gate). CLI unchanged; | |
| done-JSON additionally gains "gss_lockstep": true. | |
| Builds the KLC hybrid checkpoint from lukealonso/GLM-5.2-NVFP4 (ModelOpt-style | |
| NVFP4, 87 shards): | |
| * per MoE layer (3..77): encode ALL 256 routed experts with EXL3 trellis at | |
| exactly 3.0 bpw via the exllamav3 v0.0.43 CALIBRATED path (finalize_capture_H | |
| -> block LDL -> ldlq blocked quantization with error feedback), TP4 per-slice | |
| (gate/up N-sliced 4x[512,6144], down K-sliced 4x[6144,512], each slice its | |
| own Hadamard/su/sv/g_scale/trellis encode); | |
| * Hessians come from the OWNER'S calibration corpus via capture_hessians.py | |
| (Pass A) — stored MoE-input activations x + top-8 routing ids per layer: | |
| - gate/up slice (k=6144): H_e = X_e^T X_e over the tokens ROUTED to | |
| expert e. N-slicing does not change the input side, so all 8 gate/up | |
| slices of an expert share ONE 6144x6144 H (and, per reference | |
| semantics, one su drawn at first finalize; sv per slice). | |
| - down slice (k=512): I_e = silu(X_e Wg^T) * (X_e Wu^T) computed offline | |
| from the stored routed x with the DEQUANTIZED gate/up weights | |
| (GLM-5.2 config: hidden_act=silu, no swiglu clamp), H_down_e = I_e^T I_e | |
| [2048x2048]; K-slice r uses the [512r,512r+512) DIAGONAL block — | |
| slice-local LDLQ, consistent with slice-before-encode (each rank's | |
| down slice is an independent linear with input I_e[:,512r:512r+512), | |
| so its H is exactly that diagonal block; cross-slice H terms have no | |
| consumer because slices are encoded and Hadamard-transformed | |
| independently). | |
| - cold experts: if an expert saw < --min-routed (1024) routed tokens, | |
| its gate/up H falls back to the LAYER-level H (all tokens) and its | |
| down H to I over a seeded subsample of all tokens through that | |
| expert's own gate/up — recorded in the done-JSON, never a failure. | |
| * keep the 64 experts with the HIGHEST trellis round-trip error as NVFP4 | |
| (copied byte-exact from the source; their trellis encodes are discarded); | |
| RT-error is now measured under the calibrated encode; | |
| * carry every non-(routed-expert) tensor byte-exact (verified by sha256); | |
| * assemble a complete checkpoint dir + index + tier_bitmap + MANIFEST; | |
| * upload to a private HF repo (token from HF_TOKEN env, never logged). | |
| BIT-EXACTNESS GUARANTEES (owner mandate — these are correctness checks, not | |
| quality gates; there is NO quality evaluation anywhere in this driver): | |
| (a) every carried tensor: sha256(source payload bytes) is asserted equal to | |
| sha256(bytes re-read from the written output shard); | |
| (b) every trellis tensor: pack->unpack index equality (torch.equal) AND | |
| ext.reconstruct(packed) is asserted bit-equal to the encoder's own | |
| reconstructed values (the values the home exl3 kernel will decode); | |
| (c) --assemble writes MANIFEST.sha256 over every output file and audits the | |
| index (every weight_map entry -> existing shard containing that tensor, | |
| and every shard tensor -> weight_map). | |
| Per-expert RT-error is computed ONLY to choose the 64 NVFP4 keeps per layer. | |
| It never gates, never aborts. | |
| CALIBRATION IS MANDATORY: --encode refuses to start unless the capture dir | |
| (Pass A, capture_hessians.py) is complete for every requested layer. There is | |
| no identity-Hessian escape hatch. The reference q_fallback branch (count==0 / | |
| degenerate diagonal) is vendored for faithfulness but cannot trigger under the | |
| >= min-routed floor; if it ever does, it is recorded loudly in the done-JSON. | |
| DEPENDENCIES on the box (see RUNBOOK_TR3.md): | |
| torch==2.11.0+cu130, numpy, safetensors (unused at runtime but harmless), | |
| huggingface_hub, and the exllamav3 v0.0.43 cu132/torch2.11/cp312 wheel | |
| installed with --no-deps. Only the compiled extension `exllamav3_ext` is | |
| imported; the pure-Python quantization orchestration below is vendored | |
| VERBATIM from exllamav3 v0.0.43 (MIT, (c) turboderp), file | |
| exllamav3/modules/quant/exl3_lib/quantize.py, so the heavy deps of the | |
| exllamav3 python package (flash_attn, xformers, formatron, fla) are never | |
| imported. | |
| OUTPUT EXL3 TENSOR SCHEMA (native exllamav3 Linear storage; loadable by | |
| exllamav3.modules.quant.exl3.LinearEXL3 unmodified): | |
| model.layers.{L}.mlp.experts.{E}.{proj}.rank{r}.trellis int16 [K/16, N/16, 48] | |
| model.layers.{L}.mlp.experts.{E}.{proj}.rank{r}.suh float16 [K] | |
| model.layers.{L}.mlp.experts.{E}.{proj}.rank{r}.svh float16 [N] | |
| model.layers.{L}.mlp.experts.{E}.{proj}.rank{r}.mcg int32 scalar (0xCBAC1FED) | |
| where for proj in {gate_proj, up_proj}: K=6144 (model hidden), N=512 | |
| (=2048/4, rank r holds output rows [512r, 512r+512) of the full [2048,6144] | |
| weight); for down_proj: K=512 (rank r holds input cols [512r, 512r+512) of | |
| the full [6144,2048] weight), N=6144. K/N are exl3 conventions | |
| (in_features/out_features); trellis last dim = 16*bits = 48 at 3.0 bpw. | |
| Usage (all commands also in RUNBOOK_TR3.md; capture_hessians.py --capture must | |
| have completed first): | |
| python3 encode_tr3.py --smoke # kernel + calibrated-path check (synthetic H) | |
| python3 encode_tr3.py --encode [--layers 3-77] # multi-GPU calibrated encode (resumable) | |
| python3 encode_tr3.py --assemble # build final checkpoint dir | |
| python3 encode_tr3.py --upload [--repo NAME] # HF upload (HF_TOKEN env) | |
| """ | |
| from __future__ import annotations | |
| import argparse | |
| import hashlib | |
| import json | |
| import math | |
| import os | |
| import struct | |
| import subprocess | |
| import sys | |
| import time | |
| import traceback | |
| from functools import lru_cache | |
| # ---------------------------------------------------------------------------- | |
| # defaults / constants | |
| # ---------------------------------------------------------------------------- | |
| DEF_SRC = "/root/models/luke-nvfp4" | |
| DEF_WORK = "/root/tr3_work" | |
| DEF_OUT = "/root/out/GLM-5.2-NVFP4-TR3-Hybrid" | |
| DEF_REPO = "brandonmusic/GLM-5.2-NVFP4-TR3-Hybrid" | |
| DEF_CAPTURE = "/root/tr3_capture" # Pass A output (capture_hessians.py) | |
| BITS = 3 # owner-pinned: EXACTLY 3.0 bpw | |
| KEEP_NVFP4 = 64 # experts kept NVFP4 per MoE layer | |
| TP = 4 # TP4 per-slice encode, decided before encode | |
| SEED_BASE = 20260711 | |
| SIGMA_REG = 0.025 # exllamav3 v0.0.43 default Hessian damping | |
| MIN_ROUTED = 1024 # per-expert routed-token floor -> layer-H fallback | |
| FALLBACK_ROWS = 131072 # token subsample for cold-expert down-proj I | |
| HCHUNK = 65536 # row chunk for H accumulation matmuls | |
| FIRST_MOE_LAYER = 3 # config.first_k_dense_replace | |
| NUM_LAYERS = 78 # config.num_hidden_layers (layer 78 = MTP, carried) | |
| NUM_EXPERTS = 256 # config.n_routed_experts | |
| HIDDEN = 6144 # config.hidden_size | |
| MOE_INTER = 2048 # config.moe_intermediate_size | |
| SLICE = MOE_INTER // TP # 512 | |
| PROJS = ("gate_proj", "up_proj", "down_proj") | |
| # exllamav3 v0.0.43 constants (exl3_lib/quantize.py) | |
| HAD_K, HAD_N = 128, 128 | |
| CODEBOOK_SCALE = 1.24371088 | |
| MCG_MULT = 0xCBAC1FED # default codebook ("mcg") — matches convert_model.py default | |
| ST_DTYPE_SIZE = { | |
| "BOOL": 1, "U8": 1, "I8": 1, "F8_E4M3": 1, "F8_E5M2": 1, | |
| "I16": 2, "U16": 2, "F16": 2, "BF16": 2, | |
| "I32": 4, "U32": 4, "F32": 4, | |
| "I64": 8, "U64": 8, "F64": 8, | |
| } | |
| CHUNK = 64 << 20 # 64 MiB io chunk | |
| def log(msg: str, logfile: str | None = None): | |
| line = f"{time.strftime('%Y-%m-%d %H:%M:%S')} | {msg}" | |
| print(line, flush=True) | |
| if logfile: | |
| with open(logfile, "a") as f: | |
| f.write(line + "\n") | |
| # ---------------------------------------------------------------------------- | |
| # raw safetensors IO (byte-exact, dtype-agnostic) | |
| # ---------------------------------------------------------------------------- | |
| class STReader: | |
| """Minimal safetensors reader working on raw byte ranges.""" | |
| def __init__(self, path: str): | |
| self.path = path | |
| with open(path, "rb") as f: | |
| hlen = struct.unpack("<Q", f.read(8))[0] | |
| header = json.loads(f.read(hlen)) | |
| self.data_start = 8 + hlen | |
| self.tensors = {} | |
| for name, info in header.items(): | |
| if name == "__metadata__": | |
| continue | |
| s, e = info["data_offsets"] | |
| self.tensors[name] = (info["dtype"], tuple(info["shape"]), | |
| self.data_start + s, self.data_start + e) | |
| def nbytes(self, name: str) -> int: | |
| _, _, s, e = self.tensors[name] | |
| return e - s | |
| def read_bytes(self, name: str) -> bytes: | |
| _, _, s, e = self.tensors[name] | |
| with open(self.path, "rb") as f: | |
| f.seek(s) | |
| return f.read(e - s) | |
| def sha256_stream(self, name: str) -> str: | |
| _, _, s, e = self.tensors[name] | |
| h = hashlib.sha256() | |
| with open(self.path, "rb") as f: | |
| f.seek(s) | |
| left = e - s | |
| while left: | |
| b = f.read(min(CHUNK, left)) | |
| if not b: | |
| raise IOError(f"short read on {self.path}:{name}") | |
| h.update(b) | |
| left -= len(b) | |
| return h.hexdigest() | |
| def st_file_complete(path: str) -> bool: | |
| """A shard is complete iff its header parses and the file covers all data. | |
| (hf downloads rename atomically, so existence normally suffices; this | |
| also guards against partial copies.)""" | |
| try: | |
| size = os.path.getsize(path) | |
| if size < 16: | |
| return False | |
| with open(path, "rb") as f: | |
| hlen = struct.unpack("<Q", f.read(8))[0] | |
| if hlen > 100 << 20: | |
| return False | |
| header = json.loads(f.read(hlen)) | |
| end = max((v["data_offsets"][1] for k, v in header.items() | |
| if k != "__metadata__"), default=0) | |
| return size >= 8 + hlen + end | |
| except Exception: | |
| return False | |
| def wait_for_shard(path: str, logfile: str | None = None): | |
| waited = False | |
| while not (os.path.exists(path) and st_file_complete(path)): | |
| if not waited: | |
| log(f"waiting for shard {os.path.basename(path)} (download in progress?)", logfile) | |
| waited = True | |
| time.sleep(30) | |
| if waited: | |
| log(f"shard ready: {os.path.basename(path)}", logfile) | |
| def write_safetensors(path: str, entries: list, metadata: dict | None = None): | |
| """entries: list of (name, dtype_str, shape:tuple, payload) where payload is | |
| bytes or a zero-arg callable returning bytes. Streams to disk; returns | |
| (per_tensor_payload_sha256: dict, file_sha256: str).""" | |
| header = {} | |
| if metadata: | |
| header["__metadata__"] = metadata | |
| off = 0 | |
| sizes = [] | |
| for name, dt, shape, _ in entries: | |
| n = ST_DTYPE_SIZE[dt] | |
| for d in shape: | |
| n *= d | |
| header[name] = {"dtype": dt, "shape": list(shape), "data_offsets": [off, off + n]} | |
| sizes.append(n) | |
| off += n | |
| assert len(header) == len(entries) + (1 if metadata else 0), "duplicate tensor name in shard" | |
| hjson = json.dumps(header, separators=(",", ":")).encode() | |
| pad = (8 - (len(hjson) % 8)) % 8 # 8-byte align like the reference impl | |
| hjson += b" " * pad | |
| tensor_sha = {} | |
| fh = hashlib.sha256() | |
| tmp = path + ".tmp" | |
| with open(tmp, "wb") as f: | |
| head = struct.pack("<Q", len(hjson)) + hjson | |
| f.write(head) | |
| fh.update(head) | |
| for (name, dt, shape, payload), n in zip(entries, sizes): | |
| b = payload() if callable(payload) else payload | |
| if len(b) != n: | |
| raise ValueError(f"{name}: payload {len(b)} bytes != header {n}") | |
| f.write(b) | |
| fh.update(b) | |
| tensor_sha[name] = hashlib.sha256(b).hexdigest() | |
| os.replace(tmp, path) | |
| return tensor_sha, fh.hexdigest() | |
| def sha256_file(path: str) -> str: | |
| h = hashlib.sha256() | |
| with open(path, "rb") as f: | |
| while True: | |
| b = f.read(CHUNK) | |
| if not b: | |
| break | |
| h.update(b) | |
| return h.hexdigest() | |
| def sha256_range(path: str, start: int, end: int) -> str: | |
| h = hashlib.sha256() | |
| with open(path, "rb") as f: | |
| f.seek(start) | |
| left = end - start | |
| while left: | |
| b = f.read(min(CHUNK, left)) | |
| if not b: | |
| raise IOError(f"short read {path} [{start},{end})") | |
| h.update(b) | |
| left -= len(b) | |
| return h.hexdigest() | |
| # ---------------------------------------------------------------------------- | |
| # source model map | |
| # ---------------------------------------------------------------------------- | |
| class SourceModel: | |
| def __init__(self, src: str): | |
| self.src = src | |
| with open(os.path.join(src, "config.json")) as f: | |
| self.config = json.load(f) | |
| c = self.config | |
| assert c["num_hidden_layers"] == NUM_LAYERS, c["num_hidden_layers"] | |
| assert c["first_k_dense_replace"] == FIRST_MOE_LAYER | |
| assert c["n_routed_experts"] == NUM_EXPERTS | |
| assert c["hidden_size"] == HIDDEN | |
| assert c["moe_intermediate_size"] == MOE_INTER | |
| with open(os.path.join(src, "model.safetensors.index.json")) as f: | |
| self.index = json.load(f) | |
| self.weight_map = self.index["weight_map"] | |
| self._readers = {} | |
| def shard_path(self, tensor_name: str) -> str: | |
| return os.path.join(self.src, self.weight_map[tensor_name]) | |
| def reader_for(self, tensor_name: str, logfile=None) -> STReader: | |
| p = self.shard_path(tensor_name) | |
| if p not in self._readers: | |
| wait_for_shard(p, logfile) | |
| self._readers[p] = STReader(p) | |
| return self._readers[p] | |
| def moe_layers(self): | |
| return list(range(FIRST_MOE_LAYER, NUM_LAYERS)) # 3..77 (78 = MTP, carried) | |
| def expert_key(L, E, proj, suffix): | |
| return f"model.layers.{L}.mlp.experts.{E}.{proj}.{suffix}" | |
| # ---------------------------------------------------------------------------- | |
| # VENDORED from exllamav3 v0.0.43 (MIT (c) turboderp-org), quantize.py — | |
| # verbatim math, single-device, ProgressBar/stream scaffolding removed. | |
| # Verified byte-identical between tag v0.0.43 and master @ 2026-07-10. | |
| # ---------------------------------------------------------------------------- | |
| def _lazy_torch(): | |
| import torch # noqa | |
| import exllamav3_ext as ext # noqa (torch must be imported first) | |
| return torch, ext | |
| def tensor_core_perm(device): | |
| torch, _ = _lazy_torch() | |
| perm_a = [0] * 256 | |
| for t in range(32): | |
| r0 = (t % 4) * 2 | |
| r1 = r0 + 1 | |
| r2 = r0 + 8 | |
| r3 = r0 + 9 | |
| c0 = t // 4 | |
| c1 = c0 + 8 | |
| perm_a[t * 8 + 0] = r0 * 16 + c0 | |
| perm_a[t * 8 + 1] = r1 * 16 + c0 | |
| perm_a[t * 8 + 2] = r2 * 16 + c0 | |
| perm_a[t * 8 + 3] = r3 * 16 + c0 | |
| perm_a[t * 8 + 4] = r0 * 16 + c1 | |
| perm_a[t * 8 + 5] = r1 * 16 + c1 | |
| perm_a[t * 8 + 6] = r2 * 16 + c1 | |
| perm_a[t * 8 + 7] = r3 * 16 + c1 | |
| return torch.tensor(perm_a, dtype=torch.int, device=device) | |
| def tensor_core_perm_i(device): | |
| torch, _ = _lazy_torch() | |
| return torch.argsort(tensor_core_perm(device)) | |
| def _hadamard_f16(n: int): | |
| """Sylvester construction, identical to exllamav3 util/hadamard.py for | |
| power-of-two sizes (base case = hadamard_1.txt = [[1]], built in fp16).""" | |
| torch, _ = _lazy_torch() | |
| assert n & (n - 1) == 0 and n >= 1 | |
| h = torch.ones((1, 1), dtype=torch.float16) | |
| while h.shape[0] < n: | |
| d = h.shape[0] | |
| s = torch.empty((d * 2, d * 2), dtype=h.dtype) | |
| s[:d, :d] = h | |
| s[:d, d:] = h | |
| s[d:, :d] = h | |
| s[d:, d:] = -h | |
| h = s | |
| return h | |
| def get_hadamard_dt(n, device, dtype, scale=1.0): | |
| had = _hadamard_f16(n).to(device=device, dtype=dtype, copy=True) | |
| had *= scale | |
| return had | |
| def preapply_had_l(x, had_dim): | |
| torch, _ = _lazy_torch() | |
| k, n = x.shape | |
| x_dtype = x.dtype | |
| x = x.to(torch.float) | |
| had = get_hadamard_dt(had_dim, x.device, x.dtype, 1 / math.sqrt(had_dim)) | |
| x = (had @ x.view(-1, had_dim, n)).view(k, n) | |
| return x.to(x_dtype) | |
| def preapply_had_r(x, had_dim): | |
| torch, _ = _lazy_torch() | |
| k, n = x.shape | |
| x_dtype = x.dtype | |
| x = x.to(torch.float) | |
| had = get_hadamard_dt(had_dim, x.device, x.dtype, 1 / math.sqrt(had_dim)) | |
| x = (x.view(k, -1, had_dim) @ had).view(k, n) | |
| return x.to(x_dtype) | |
| def blockwise_preapply_had_l_(x, had_dim): | |
| torch, _ = _lazy_torch() | |
| k, n = x.shape | |
| assert k % had_dim == 0 and x.dtype == torch.float | |
| had = get_hadamard_dt(had_dim, x.device, x.dtype, 1 / math.sqrt(had_dim)) | |
| for i in range(k // had_dim): | |
| s = i * had_dim | |
| x[s:s + had_dim, :] = had @ x[s:s + had_dim, :].view(had_dim, n) | |
| def blockwise_preapply_had_r_(x, had_dim): | |
| torch, _ = _lazy_torch() | |
| k, n = x.shape | |
| assert n % had_dim == 0 and x.dtype == torch.float | |
| had = get_hadamard_dt(had_dim, x.device, x.dtype, 1 / math.sqrt(had_dim)) | |
| for i in range(n // had_dim): | |
| s = i * had_dim | |
| x[:, s:s + had_dim] = x[:, s:s + had_dim] @ had | |
| def block_rms(x, dim, keepdim=False, blocksize=32): | |
| torch, _ = _lazy_torch() | |
| n = x.size(dim) | |
| sq = None | |
| for block in torch.split(x, blocksize, dim=dim): | |
| bsq = block.square().sum(dim=dim, keepdim=keepdim) | |
| sq = bsq if sq is None else sq + bsq | |
| return (sq / n).sqrt() | |
| def _quant_temp_buffers(K: int): | |
| """Same layout as exl3_lib get_temp_buffers, but sized 2*SM-count so the | |
| kernel's internal wave (MIN(temp.size(0), 2*num_sms)) is fully utilized.""" | |
| torch, _ = _lazy_torch() | |
| sms = torch.cuda.get_device_properties(0).multi_processor_count | |
| b = 2 * sms | |
| temp_costs = torch.zeros((b, 2, 65536 >> K), dtype=torch.half, device="cuda:0") | |
| temp_edges = torch.zeros((b, 256, 65536 >> K), dtype=torch.short, device="cuda:0") | |
| return temp_costs, temp_edges | |
| def quantize_tiles_batched(tiles, quant_args): | |
| """ext.quantize_tiles over an arbitrary batch of 256-element tiles. | |
| The kernel loops internally in waves, so one call suffices.""" | |
| torch, ext = _lazy_torch() | |
| tiles = tiles.contiguous() | |
| assert tiles.shape[1] == 256 and tiles.dtype == torch.float | |
| K = quant_args["K"] | |
| q_tiles = torch.zeros_like(tiles) | |
| q_idx = torch.zeros_like(tiles, dtype=torch.short) | |
| temp_costs, temp_edges = _quant_temp_buffers(K) | |
| ext.quantize_tiles(tiles, q_tiles, q_idx, temp_costs, temp_edges, | |
| K, "mcg" in quant_args, "mul1" in quant_args) | |
| return q_tiles, q_idx | |
| def g_scale_gss(weight_r, quant_args, width=3): | |
| """Verbatim golden-section global-scale search (quantize.py g_scale_gss), | |
| minus streams/ProgressBar; identical sampling and iteration math.""" | |
| torch, _ = _lazy_torch() | |
| tiles = [] | |
| tiles_k = weight_r.shape[0] // 16 | |
| tiles_n = weight_r.shape[1] // 16 | |
| perm = tensor_core_perm(weight_r.device) | |
| for i in range(max(tiles_k, tiles_n)): | |
| for w in range(width): | |
| k = (i % tiles_k) * 16 | |
| n = ((i + w) % tiles_n) * 16 | |
| tile = weight_r[k:k + 16, n:n + 16].clone().view(256) | |
| tiles.append(tile[perm]) | |
| tiles = torch.stack(tiles) | |
| def test_scale(scale): | |
| quant_w, _ = quantize_tiles_batched(tiles * scale, quant_args) | |
| return ((quant_w / scale - tiles) ** 2).mean() | |
| phi = (1 + math.sqrt(5)) / 2 | |
| resphi = 2 - phi | |
| a, b = 0.1, 1.9 | |
| tol = 0.01 | |
| x1 = a + resphi * (b - a) | |
| x2 = b - resphi * (b - a) | |
| f1 = test_scale(x1) | |
| f2 = test_scale(x2) | |
| while abs(b - a) > tol: | |
| if f1 < f2: | |
| b, x2, f2 = x2, x1, f1 | |
| x1 = a + resphi * (b - a) | |
| f1 = test_scale(x1) | |
| else: | |
| a, x1, f1 = x1, x2, f2 | |
| x2 = b - resphi * (b - a) | |
| f2 = test_scale(x2) | |
| return (a + b) / 2, (f1 + f2) / 2 | |
| # ---------------------------------------------------------------------------- | |
| # v3.1 CROSS-SLICE LOCKSTEP g_scale GOLDEN-SECTION SEARCH | |
| # | |
| # v3 pools the LDLQ main pass; the remaining serial hot spot is regularize()'s | |
| # g_scale_gss: 13 quantize_tiles evaluations (2 bracket-init + 11 iterations | |
| # for the fixed [0.1,1.9]/tol=0.01 bracket) on a ~1,152-tile sampled batch, | |
| # PER SLICE, each eval with its own launch + device-sync round trip. The 12 | |
| # slice-walks' searches are independent, so v3.1 runs them in LOCKSTEP: at | |
| # each golden-section step it gathers every active search's (tiles x candidate | |
| # scale) evaluation into ONE quantize_tiles call, scatters the per-search MSEs | |
| # back, and advances each search's bracket independently. | |
| # | |
| # Bit-exactness argument (same shape as the v3 lockstep-LDLQ argument above): | |
| # * each search's sampled tile batch is built by the VERBATIM v3 loop; | |
| # * each search's bracket walk is the VERBATIM v3 iteration sequence — | |
| # _gss_bracket_gen() is a line-for-line copy of g_scale_gss()'s arithmetic | |
| # with test_scale(x) replaced by `yield x` (x1, x2, then exactly one new | |
| # candidate per iteration, evaluated even on the final iteration, exactly | |
| # like the reference); | |
| # * the pooled evaluation computes each search's input as its own verbatim | |
| # `tiles * scale` (torch.cat copies bits; quantize_tiles is per-tile | |
| # independent — see the v3 block comment) and each search's MSE with the | |
| # verbatim test_scale expression on that search's own contiguous rows of | |
| # the batched output (row-slices of a contiguous 2D tensor are contiguous | |
| # and 1024B-aligned, so the elementwise/mean kernels see byte-identical | |
| # standalone-shaped inputs); | |
| # * f-value comparisons move from 0-dim fp32 CUDA tensors to python floats: | |
| # fp32->fp64 conversion is exact and order-preserving, so every branch | |
| # (ties and NaN included) is decided identically; bracket arithmetic is | |
| # pure python fp64 in both versions. | |
| # Searches whose bracket closes drop out of the pool; survivors keep pooling. | |
| # (In practice all brackets close after the same 11 iterations — both golden- | |
| # section branches shrink |b-a| by the identical factor phi-1 — but nothing | |
| # below assumes that.) Proven once per worker by the pooled-vs-sequential | |
| # self_check on the first expert pool, and on real slices by --oracle | |
| # (v3.1 == v3 == v2, hard deployment gate). | |
| # ---------------------------------------------------------------------------- | |
| def _gss_bracket_gen(): | |
| """Verbatim g_scale_gss() bracket walk as a coroutine: yields candidate | |
| scales in the reference evaluation order, receives each candidate's MSE, | |
| and returns ((a+b)/2, (f1+f2)/2) exactly like the reference.""" | |
| phi = (1 + math.sqrt(5)) / 2 | |
| resphi = 2 - phi | |
| a, b = 0.1, 1.9 | |
| tol = 0.01 | |
| x1 = a + resphi * (b - a) | |
| x2 = b - resphi * (b - a) | |
| f1 = yield x1 | |
| f2 = yield x2 | |
| while abs(b - a) > tol: | |
| if f1 < f2: | |
| b, x2, f2 = x2, x1, f1 | |
| x1 = a + resphi * (b - a) | |
| f1 = yield x1 | |
| else: | |
| a, x1, f1 = x1, x2, f2 | |
| x2 = b - resphi * (b - a) | |
| f2 = yield x2 | |
| return (a + b) / 2, (f1 + f2) / 2 | |
| _self_checked_gss = False | |
| def g_scale_gss_lockstep(pre_ctxs, logfile=None, self_check=False, width=3): | |
| """Pooled golden-section g_scale search over many slices' regularized | |
| weights (see block comment above). Sets ctx["g_scale"] on every entry, | |
| bit-identical to sequential g_scale_gss(ctx["weight_r"], ...). One-time | |
| self_check (first pool of each worker): re-runs the UNTOUCHED v3 | |
| sequential g_scale_gss per slice and asserts exact g_scale equality | |
| before any result is used.""" | |
| global _self_checked_gss | |
| torch, _ = _lazy_torch() | |
| if not pre_ctxs: | |
| return | |
| qa = pre_ctxs[0]["quant_args"] | |
| for c in pre_ctxs: | |
| cq = c["quant_args"] | |
| assert cq["K"] == qa["K"] and ("mcg" in cq) == ("mcg" in qa) \ | |
| and ("mul1" in cq) == ("mul1" in qa), \ | |
| "gss lockstep pool requires uniform quantizer args" | |
| searches = [] | |
| for c in pre_ctxs: | |
| weight_r = c["weight_r"] | |
| # --- sampled tile batch: VERBATIM v3 g_scale_gss construction -------- | |
| tiles = [] | |
| tiles_k = weight_r.shape[0] // 16 | |
| tiles_n = weight_r.shape[1] // 16 | |
| perm = tensor_core_perm(weight_r.device) | |
| for i in range(max(tiles_k, tiles_n)): | |
| for w in range(width): | |
| k = (i % tiles_k) * 16 | |
| n = ((i + w) % tiles_n) * 16 | |
| tile = weight_r[k:k + 16, n:n + 16].clone().view(256) | |
| tiles.append(tile[perm]) | |
| tiles = torch.stack(tiles) | |
| gen = _gss_bracket_gen() | |
| searches.append({"ctx": c, "tiles": tiles, "gen": gen, | |
| "scale": gen.send(None)}) # primes to x1 (cannot stop here) | |
| active = list(searches) | |
| while active: | |
| # one pooled evaluation wave: every active search contributes its own | |
| # verbatim test_scale input (tiles * candidate_scale) | |
| parts = [s["tiles"] * s["scale"] for s in active] | |
| batch = torch.cat(parts, dim=0) if len(parts) > 1 else parts[0] | |
| q_w, _ = quantize_tiles_batched(batch, qa) | |
| off = 0 | |
| mses = [] | |
| for s, p in zip(active, parts): | |
| m = p.shape[0] | |
| # verbatim test_scale MSE on this search's own rows | |
| mses.append(((q_w[off:off + m] / s["scale"] - s["tiles"]) ** 2).mean()) | |
| off += m | |
| assert off == batch.shape[0] | |
| vals = torch.stack(mses).cpu().tolist() # ONE device sync per wave | |
| nxt = [] | |
| for s, v in zip(active, vals): | |
| try: | |
| s["scale"] = s["gen"].send(v) | |
| nxt.append(s) # bracket still open — keep pooling | |
| except StopIteration as e: # bracket closed — drop out of pool | |
| s["ctx"]["g_scale"] = e.value[0] | |
| active = nxt | |
| if self_check and not _self_checked_gss: | |
| for s in searches: | |
| g_seq, _ = g_scale_gss(s["ctx"]["weight_r"], s["ctx"]["quant_args"]) | |
| assert g_seq == s["ctx"]["g_scale"], \ | |
| f"pooled GSS g_scale {s['ctx']['g_scale']!r} != sequential {g_seq!r}" | |
| _self_checked_gss = True | |
| log(f"self-check PASSED: pooled GSS ({len(searches)} searches) == sequential " | |
| f"g_scale_gss, exact g_scale equality per slice", logfile) | |
| for s in searches: | |
| s["tiles"] = None | |
| s["gen"] = None | |
| def regularize(weight, su, sv, quant_args, H_diag=None, q_fallback=False): | |
| """Verbatim quantize.py regularize() (v0.0.43, full calibrated version: | |
| H_diag skew decides out-channel scaling), ProgressBar/verbose/save-image | |
| scaffolding removed. Mutates/returns weight.""" | |
| torch, _ = _lazy_torch() | |
| force_out_scales = quant_args["apply_out_scales"] | |
| # From experiments, it seems the deciding factor in when scaling output | |
| # channels is beneficial is when the input to the linear layer is very | |
| # irregular. Cutoff: 15% of the RMS sum on 2% of the channels. | |
| if not q_fallback and H_diag is not None: | |
| diag = H_diag.sqrt() | |
| diag, _ = torch.sort(diag, descending=True) | |
| cutoff = diag.shape[0] // 50 | |
| skew_factor = diag[:cutoff].sum() / diag.sum() | |
| if force_out_scales is None: | |
| apply_out_scales = skew_factor.item() < 0.15 | |
| else: | |
| apply_out_scales = force_out_scales | |
| else: | |
| apply_out_scales = True if force_out_scales is None else force_out_scales | |
| if q_fallback: | |
| apply_out_scales = force_out_scales | |
| out_channel_scales = block_rms(weight, dim=0, keepdim=True) | |
| mean = out_channel_scales.mean().item() | |
| if mean > 1e-30: | |
| out_channel_scales /= mean | |
| quant_args["zeros"] = False | |
| else: | |
| quant_args["zeros"] = True | |
| if force_out_scales is not None: | |
| apply_out_scales = True | |
| zero_out_scales = out_channel_scales.abs() < 1e-30 | |
| if apply_out_scales: | |
| out_channel_scales[zero_out_scales] = 0.1 | |
| sv = (sv * out_channel_scales + 1e-10).float() | |
| weight /= sv | |
| sv[zero_out_scales] = 0.0 | |
| blockwise_preapply_had_r_(weight, HAD_N) | |
| in_channel_scales = block_rms(weight, dim=1, keepdim=True) | |
| in_channel_scales[in_channel_scales.abs() < 1e-30] = 0.1 | |
| su = (su * in_channel_scales / (-CODEBOOK_SCALE) + 1e-10).float() | |
| weight /= su | |
| blockwise_preapply_had_l_(weight, HAD_K) | |
| g_scale, _ = g_scale_gss(weight, quant_args) | |
| weight *= g_scale | |
| su /= g_scale | |
| return apply_out_scales, weight, g_scale, su, sv | |
| def regularize_pre(weight, su, sv, quant_args, H_diag=None, q_fallback=False): | |
| """v3.1: VERBATIM regularize() head — everything up to (excluding) the | |
| g_scale_gss call, so the search itself can run pooled across slices | |
| (g_scale_gss_lockstep) followed by regularize_post(). regularize() above | |
| stays byte-untouched (v2 verbatim; used by encode_slice, i.e. --smoke and | |
| the --oracle reference passes, and by the v3-path encode_slice_prologue).""" | |
| torch, _ = _lazy_torch() | |
| force_out_scales = quant_args["apply_out_scales"] | |
| # From experiments, it seems the deciding factor in when scaling output | |
| # channels is beneficial is when the input to the linear layer is very | |
| # irregular. Cutoff: 15% of the RMS sum on 2% of the channels. | |
| if not q_fallback and H_diag is not None: | |
| diag = H_diag.sqrt() | |
| diag, _ = torch.sort(diag, descending=True) | |
| cutoff = diag.shape[0] // 50 | |
| skew_factor = diag[:cutoff].sum() / diag.sum() | |
| if force_out_scales is None: | |
| apply_out_scales = skew_factor.item() < 0.15 | |
| else: | |
| apply_out_scales = force_out_scales | |
| else: | |
| apply_out_scales = True if force_out_scales is None else force_out_scales | |
| if q_fallback: | |
| apply_out_scales = force_out_scales | |
| out_channel_scales = block_rms(weight, dim=0, keepdim=True) | |
| mean = out_channel_scales.mean().item() | |
| if mean > 1e-30: | |
| out_channel_scales /= mean | |
| quant_args["zeros"] = False | |
| else: | |
| quant_args["zeros"] = True | |
| if force_out_scales is not None: | |
| apply_out_scales = True | |
| zero_out_scales = out_channel_scales.abs() < 1e-30 | |
| if apply_out_scales: | |
| out_channel_scales[zero_out_scales] = 0.1 | |
| sv = (sv * out_channel_scales + 1e-10).float() | |
| weight /= sv | |
| sv[zero_out_scales] = 0.0 | |
| blockwise_preapply_had_r_(weight, HAD_N) | |
| in_channel_scales = block_rms(weight, dim=1, keepdim=True) | |
| in_channel_scales[in_channel_scales.abs() < 1e-30] = 0.1 | |
| su = (su * in_channel_scales / (-CODEBOOK_SCALE) + 1e-10).float() | |
| weight /= su | |
| blockwise_preapply_had_l_(weight, HAD_K) | |
| return apply_out_scales, weight, su, sv | |
| def regularize_post(weight, su, g_scale): | |
| """v3.1: VERBATIM regularize() tail (g_scale application).""" | |
| weight *= g_scale | |
| su /= g_scale | |
| return weight, su | |
| def get_quant_stream(device): | |
| torch, _ = _lazy_torch() | |
| return torch.cuda.Stream(device=device) | |
| def block_ldl(H, b, quant_args, verbose=False): | |
| """Verbatim quantize.py block_ldl() (v0.0.43): Cholesky with diagonal-damping | |
| retries, blocked column normalization, identity diagonal blocks. Returns | |
| (L, H_cpu); H's GPU storage is reused for L (reference memory trick).""" | |
| torch, _ = _lazy_torch() | |
| n, _ = H.shape | |
| assert (n % b == 0) | |
| m = n // b | |
| num_cholesky_retries = 0 | |
| retry_cpu = False | |
| while True: | |
| try: | |
| L = torch.linalg.cholesky(H) | |
| H_cpu = H.cpu() | |
| H.copy_(L) | |
| L = H | |
| H = H_cpu | |
| break | |
| except torch._C._LinAlgError as e: | |
| num_cholesky_retries += 1 | |
| if num_cholesky_retries > 10: | |
| print(" ## Cholesky decomp. failed, number of retries exceeded") | |
| raise e | |
| print(f" !! Cholesky decomp. failed, increasing diagonal damping, " | |
| f"attempt {num_cholesky_retries}/10") | |
| H.diagonal().add_(2.0 * quant_args.get("sigma_reg", 0.025) * H.diagonal().mean()) | |
| continue | |
| except Exception as e: | |
| if e.__class__.__name__ == "OutOfMemoryError" or "CUDA out of memory" in str(e) \ | |
| or "HIP out of memory" in str(e): | |
| retry_cpu = True | |
| break | |
| else: | |
| raise e | |
| if retry_cpu: | |
| print(f" !! Out of memory on {str(H.device)}, trying CPU fallback") | |
| torch.cuda.empty_cache() | |
| H_cpu = H.cpu() | |
| L_cpu = torch.linalg.cholesky(H_cpu) | |
| H.copy_(L_cpu) | |
| del L_cpu | |
| L = H | |
| H = H_cpu | |
| DL = torch.diagonal(L.reshape(m, b, m, b), dim1=0, dim2=2).permute(2, 0, 1) | |
| DL = torch.linalg.inv(DL) | |
| L = L.view(n, m, b) | |
| for i in range(m): | |
| L[:, i, :] = L[:, i, :] @ DL[i, :, :] | |
| L = L.reshape(n, n).contiguous() | |
| L_block = L.view(m, b, m, b).permute(0, 2, 1, 3) | |
| dr = torch.arange(m) | |
| L_block[dr, dr] = torch.stack([torch.eye(b, device=L.device, dtype=H.dtype)] * m) | |
| return L, H | |
| def ldlq(weight, L, quant_args): | |
| """Verbatim quantize.py ldlq() (v0.0.43): blocked (buf_size_k=128) LDLQ with | |
| error feedback — 16-row blocks quantized bottom-up, compensation from the | |
| LDL factor, cross-span error carried in prod_cache. ProgressBar removed; | |
| quantize_tiles_multigpu -> quantize_tiles_batched (single device; asserted | |
| bit-identical to the reference kernel wrapper by the worker self-checks).""" | |
| torch, _ = _lazy_torch() | |
| devices = quant_args["devices"] | |
| for device in devices: | |
| torch.cuda.synchronize(device) | |
| main_stream = get_quant_stream(devices[0]) | |
| with torch.cuda.stream(main_stream): | |
| device = L.device | |
| assert device == torch.device(devices[0]) | |
| buffer_device = weight.device | |
| size_k, size_n = weight.shape # Row-major | |
| assert size_k % 16 == 0 | |
| assert size_n % 128 == 0 | |
| tiles_k = size_k // 16 | |
| tiles_n = size_n // 16 | |
| buf_size_k = max(quant_args.get("buf_size_k", 128), 16) | |
| assert buf_size_k % 16 == 0 | |
| assert size_n % buf_size_k == 0 | |
| prod_cache = torch.zeros((size_k, size_n), dtype=torch.float, device=device) | |
| weight_q = torch.zeros((size_k, size_n), dtype=torch.float, device=buffer_device) | |
| encoded = torch.zeros((tiles_k, tiles_n, 256), dtype=torch.short, device=buffer_device) | |
| for j in range(size_k, 0, -buf_size_k): | |
| i = j - buf_size_k | |
| b_weight = weight[i:j].to(device) | |
| b_weight_q = weight_q[i:j] if device == buffer_device else \ | |
| torch.zeros_like(weight_q[i:j], device=device) | |
| b_encoded = encoded[i // 16: j // 16] if device == buffer_device else \ | |
| torch.zeros_like(encoded[i // 16: j // 16], device=device) | |
| b_prod_cache = prod_cache[i:j] | |
| b_L = L[i:j] | |
| for bj in range(buf_size_k, 0, -16): | |
| bi = bj - 16 | |
| # Error so far for the current span | |
| bb_err = b_weight[bj:] - b_weight_q[bj:] | |
| # Corresponding slice of LDL decomposition of H | |
| bb_L = b_L[bj:, i + bi:i + bj] | |
| # Input tiles for quantization | |
| compensation_term = b_prod_cache[bi:bj] | |
| compensation_term.addmm_(bb_L.T, bb_err, alpha=1.0, beta=1.0) | |
| rows = b_weight[bi:bj] + compensation_term | |
| tiles = rows.reshape(16, tiles_n, 16).permute(1, 0, 2).reshape(tiles_n, 256) | |
| tiles = tiles[:, tensor_core_perm(device)] | |
| quant_w, quant_i = quantize_tiles_batched(tiles, quant_args) | |
| quant_w = quant_w[:, tensor_core_perm_i(device)] | |
| quant_w = quant_w.reshape(tiles_n, 16, 16).permute(1, 0, 2).reshape(16, size_n) | |
| b_weight_q[bi:bj] = quant_w | |
| b_encoded[bi // 16: bj // 16] = quant_i.unsqueeze(0) | |
| if device != buffer_device: | |
| weight_q[i:j] = b_weight_q.to(buffer_device) | |
| encoded[i // 16: j // 16] = b_encoded.to(buffer_device) | |
| # Cache error term for the rest of the matrix | |
| b_err = b_weight - b_weight_q | |
| prod_cache.addmm_(b_L.T, b_err, alpha=1.0, beta=1.0) | |
| for device in devices: | |
| torch.cuda.synchronize(device) | |
| return weight_q, encoded | |
| # ---------------------------------------------------------------------------- | |
| # v3 CROSS-SLICE LOCKSTEP LDLQ | |
| # | |
| # quantize_tiles_kernel (exllamav3 v0.0.43 quantize.cu) assigns one thread | |
| # block per tile (tile_idx = blockIdx.x); the input/output/index/temp pointers | |
| # are all offset purely by tile_idx (temp_edges is a per-tile-slot scratch; | |
| # for K>=2 the DP costs live in the block's own shared memory), so a tile's | |
| # result is a pure function of its own 256 values — batch composition cannot | |
| # change per-tile results. The host wrapper loops internally in waves of | |
| # MIN(temp_costs.size(0), 2*num_sms) blocks, and _quant_temp_buffers already | |
| # allocates exactly 2*SM slots, so one call handles any batch size with full | |
| # waves and correct per-slot scratch (no temp resize needed — the underfill | |
| # was purely caller-side: 32 tiles/call vs 296 slots). | |
| # | |
| # LDLQWalk steps ONE slice's ldlq() 16-row block at a time — prepare() and | |
| # commit() are the v2 ldlq() inner-loop math, verbatim, on the walk's own | |
| # tensors — and lockstep_ldlq() co-processes many independent walks, gathering | |
| # every active walk's current tiles into ONE quantize_tiles call per step. | |
| # Each walk's error feedback uses only that walk's own outputs, and per-walk | |
| # op order/operands are exactly v2's, so lockstep(K walks) is bit-identical | |
| # to sequential per-slice ldlq() — asserted once per worker by | |
| # lockstep_self_check() and proven on real slices by --oracle. | |
| # ---------------------------------------------------------------------------- | |
| class LDLQWalk: | |
| """One slice's ldlq() as a stepped state machine (verbatim v2 math).""" | |
| __slots__ = ("ctx", "weight", "L", "weight_q", "encoded", "prod_cache", | |
| "size_k", "size_n", "tiles_k", "tiles_n", "buf", "j", "bj", | |
| "perm", "perm_i", "done") | |
| def __init__(self, ctx): | |
| torch, _ = _lazy_torch() | |
| weight, L, quant_args = ctx["weight_r"], ctx["L"], ctx["quant_args"] | |
| device = L.device | |
| assert device == torch.device(quant_args["devices"][0]) | |
| assert weight.device == device, "lockstep requires weight and L on one device" | |
| size_k, size_n = weight.shape # Row-major | |
| assert size_k % 16 == 0 | |
| assert size_n % 128 == 0 | |
| buf = max(quant_args.get("buf_size_k", 128), 16) | |
| assert buf % 16 == 0 | |
| assert size_n % buf == 0 and size_k % buf == 0 | |
| self.ctx = ctx | |
| self.weight = weight # read-only here (as in v2 ldlq) | |
| self.L = L # read-only here (diag pre-zeroed) | |
| self.size_k, self.size_n = size_k, size_n | |
| self.tiles_k, self.tiles_n = size_k // 16, size_n // 16 | |
| self.buf = buf | |
| self.prod_cache = torch.zeros((size_k, size_n), dtype=torch.float, device=device) | |
| self.weight_q = torch.zeros((size_k, size_n), dtype=torch.float, device=device) | |
| self.encoded = torch.zeros((self.tiles_k, self.tiles_n, 256), | |
| dtype=torch.short, device=device) | |
| self.perm = tensor_core_perm(device) | |
| self.perm_i = tensor_core_perm_i(device) | |
| self.j = size_k # span end (spans walk bottom-up) | |
| self.bj = buf # in-span block end | |
| self.done = False | |
| def prepare(self): | |
| """Verbatim v2 ldlq() inner-iteration head for block [bi,bj) of span | |
| [i,j): in-place compensation addmm_ + tile gather/permute. Returns | |
| this walk's [tiles_n, 256] fp32 tile rows for the batched call.""" | |
| j = self.j | |
| i = j - self.buf | |
| bj = self.bj | |
| bi = bj - 16 | |
| b_weight = self.weight[i:j] | |
| b_weight_q = self.weight_q[i:j] | |
| b_prod_cache = self.prod_cache[i:j] | |
| b_L = self.L[i:j] | |
| # Error so far for the current span | |
| bb_err = b_weight[bj:] - b_weight_q[bj:] | |
| # Corresponding slice of LDL decomposition of H | |
| bb_L = b_L[bj:, i + bi:i + bj] | |
| # Input tiles for quantization | |
| compensation_term = b_prod_cache[bi:bj] | |
| compensation_term.addmm_(bb_L.T, bb_err, alpha=1.0, beta=1.0) | |
| rows = b_weight[bi:bj] + compensation_term | |
| tiles = rows.reshape(16, self.tiles_n, 16).permute(1, 0, 2).reshape(self.tiles_n, 256) | |
| tiles = tiles[:, self.perm] | |
| return tiles | |
| def commit(self, quant_w, quant_i): | |
| """Verbatim v2 ldlq() inner-iteration tail: un-permute, store, and at | |
| span end run the cross-span error-feedback addmm_ and advance.""" | |
| j = self.j | |
| i = j - self.buf | |
| bj = self.bj | |
| bi = bj - 16 | |
| quant_w = quant_w[:, self.perm_i] | |
| quant_w = quant_w.reshape(self.tiles_n, 16, 16).permute(1, 0, 2).reshape(16, self.size_n) | |
| self.weight_q[i + bi:i + bj] = quant_w | |
| self.encoded[(i + bi) // 16:(i + bj) // 16] = quant_i.unsqueeze(0) | |
| self.bj -= 16 | |
| if self.bj == 0: | |
| # Cache error term for the rest of the matrix | |
| b_err = self.weight[i:j] - self.weight_q[i:j] | |
| self.prod_cache.addmm_(self.L[i:j].T, b_err, alpha=1.0, beta=1.0) | |
| self.j -= self.buf | |
| self.bj = self.buf | |
| if self.j == 0: | |
| self.done = True | |
| self.ctx["wq_reg"] = self.weight_q | |
| self.ctx["encoded"] = self.encoded | |
| return self.done | |
| def lockstep_ldlq(walks, n_max, logfile=None): | |
| """Advance up to n_max LDLQ walks in lockstep with ONE quantize_tiles call | |
| per step (see block comment above for the bit-exactness argument). Same | |
| stream/sync discipline as v2 ldlq(): sync all devices, run every op on the | |
| cached quant stream, sync again before returning.""" | |
| torch, _ = _lazy_torch() | |
| if not walks: | |
| return | |
| qa = walks[0].ctx["quant_args"] | |
| for w in walks: | |
| wq = w.ctx["quant_args"] | |
| assert wq["K"] == qa["K"] and ("mcg" in wq) == ("mcg" in qa) \ | |
| and ("mul1" in wq) == ("mul1" in qa) and wq["devices"] == qa["devices"], \ | |
| "lockstep pool requires uniform quantizer args" | |
| devices = qa["devices"] | |
| for device in devices: | |
| torch.cuda.synchronize(device) | |
| main_stream = get_quant_stream(devices[0]) | |
| with torch.cuda.stream(main_stream): | |
| pending = list(walks) | |
| active = [] | |
| while pending or active: | |
| while pending and len(active) < n_max: | |
| active.append(pending.pop(0)) | |
| parts = [w.prepare() for w in active] | |
| tiles = torch.cat(parts, dim=0) if len(parts) > 1 else parts[0] | |
| q_w, q_i = quantize_tiles_batched(tiles, qa) | |
| off = 0 | |
| nxt = [] | |
| for w, p in zip(active, parts): | |
| m = p.shape[0] | |
| w.commit(q_w[off:off + m], q_i[off:off + m]) | |
| off += m | |
| if not w.done: | |
| nxt.append(w) | |
| assert off == tiles.shape[0] | |
| active = nxt | |
| for device in devices: | |
| torch.cuda.synchronize(device) | |
| def resolve_lockstep(val) -> int: | |
| """--lockstep: 'auto' -> 3*TP = 12, i.e. all slice-walks of one expert | |
| co-stepped (8 gate/up walks x 32 tiles + 4 down walks x 384 tiles; the | |
| steady-state gate/up phase gathers 256 tiles vs the 296-slot wave). | |
| Integer N > 12 pools ceil(N/12) experts' walks together.""" | |
| if isinstance(val, int): | |
| n = val | |
| elif str(val).strip().lower() == "auto": | |
| return 3 * TP | |
| else: | |
| n = int(val) | |
| assert n >= 1, f"--lockstep must be >= 1 or 'auto' (got {val!r})" | |
| return n | |
| import threading | |
| finalize_capture_H_mutex = threading.Lock() | |
| def finalize_capture_H(H_data, quant_args, verbose=False): | |
| """Verbatim quantize.py finalize_capture_H() (v0.0.43): mean over captured | |
| rows, sigma_reg diagonal damping, su sign draw (shared by all linears using | |
| this H_data), su/128-Hadamard transform of H, block LDL with zeroed | |
| diagonal. Returns (q_fallback, H, L, su, H_diag).""" | |
| torch, _ = _lazy_torch() | |
| with finalize_capture_H_mutex: | |
| if H_data["H"].is_meta: | |
| H_data["L"] = None | |
| H_data["finalized"] = True | |
| H_data["diag"] = None | |
| H_data["q_fallback"] = True | |
| H = H_data["H"] | |
| k = H.shape[0] | |
| su = (torch.randn(k, device=H_data["device"]).sign() + 1e-5).sign().to(torch.float).unsqueeze(1) | |
| H_data["su"] = su | |
| return True, None, None, su, None | |
| if "H_swap_device" in H_data: | |
| H_data["H"] = H_data["H"].to(H_data["H_swap_device"]) | |
| del H_data["H_swap_device"] | |
| H = H_data["H"] | |
| if H_data["finalized"]: | |
| return H_data["q_fallback"], H, H_data["L"], H_data["su"], H_data["diag"] | |
| # Mean of samples summed up during forward pass; uncalibrated fallback | |
| # if no input activations or diagonal too small | |
| count = H_data["count"] | |
| if count == 0: | |
| q_fallback = True | |
| diag_mean = 0.0 | |
| else: | |
| H /= count | |
| diag_mean = torch.diag(H).mean() | |
| q_fallback = diag_mean.item() < 1e-20 | |
| # Regularize diagonal | |
| H.diagonal().add_(quant_args.get("sigma_reg", 0.025) * diag_mean) | |
| diag = H.diagonal().clone() | |
| # Random sign flips for input channel, fixed for the first linear | |
| # layer to quantize with this H | |
| k = H.shape[0] | |
| su = (torch.randn(k, device=H.device).sign() + 1e-5).sign().to(torch.float).unsqueeze(1) | |
| H_data["su"] = su | |
| # Input had | |
| H *= su.T | |
| blockwise_preapply_had_r_(H, HAD_K) | |
| H *= su | |
| blockwise_preapply_had_l_(H, HAD_K) | |
| # Get block LDL decomposition of H, zero diagonal | |
| if q_fallback: | |
| L = None | |
| else: | |
| L, H = block_ldl(H, 16, quant_args, verbose) | |
| dr = torch.arange(k) | |
| L[dr, dr] = 0 | |
| H_data["L"] = L | |
| H = H.cpu() | |
| H_data["H"] = H.cpu() | |
| H_data["finalized"] = True | |
| H_data["diag"] = diag | |
| H_data["q_fallback"] = q_fallback | |
| return q_fallback, H, L, su, diag | |
| def block_trace(A, B, block_size=1024): | |
| """Verbatim inner helper of quantize_exl3() metrics: trace(A^T B A) summed | |
| in blocks (A: [k, n], B: [k, k]).""" | |
| torch, _ = _lazy_torch() | |
| total = 0.0 | |
| for j_start in range(0, B.shape[1], block_size): | |
| j_end = min(j_start + block_size, B.shape[1]) | |
| B_block = B[:, j_start:j_end] | |
| A_j_block = A[j_start:j_end, :] | |
| partial = torch.einsum("ik,ij,jk->", A, B_block, A_j_block) | |
| total += partial.item() | |
| return total | |
| def pack_trellis(encoded, quant_args): | |
| torch, ext = _lazy_torch() | |
| K = quant_args["K"] | |
| shape = encoded.shape | |
| assert len(shape) == 3 and shape[2] == 256 and encoded.dtype == torch.int16 | |
| packed = torch.zeros((shape[0], shape[1], 256 * K // 16), | |
| dtype=torch.int16, device=encoded.device) | |
| ext.pack_trellis(packed, encoded.contiguous(), K) | |
| return packed | |
| # ---------------------------------------------------------------------------- | |
| # trellis quantization of one (k, n) tensor — LDLQ-calibrated (fallback path | |
| # retained verbatim for the reference q_fallback branch + self-checks) | |
| # ---------------------------------------------------------------------------- | |
| def trellis_quant_all_tiles(weight_r, quant_args): | |
| """Identity-Hessian quantization of the full tensor in one batch. | |
| With H=I there is no LDLQ error feedback, so every 16-row block is | |
| independent — bit-identical to the reference fallback_quant() loop | |
| (asserted by self_check_reference once per worker).""" | |
| torch, _ = _lazy_torch() | |
| k, n = weight_r.shape | |
| tk, tn = k // 16, n // 16 | |
| perm = tensor_core_perm(weight_r.device) | |
| perm_i = tensor_core_perm_i(weight_r.device) | |
| tiles = (weight_r.view(tk, 16, tn, 16).permute(0, 2, 1, 3) | |
| .reshape(tk * tn, 256))[:, perm].contiguous() | |
| q_tiles, q_idx = quantize_tiles_batched(tiles, quant_args) | |
| wq = (q_tiles[:, perm_i].view(tk, tn, 16, 16).permute(0, 2, 1, 3) | |
| .reshape(k, n).contiguous()) | |
| encoded = q_idx.view(tk, tn, 256).contiguous() | |
| return wq, encoded | |
| def reference_fallback_quant(weight_r, quant_args): | |
| """Verbatim inner loop of quantize.py fallback_quant() (streams removed): | |
| used once per worker to assert trellis_quant_all_tiles is bit-identical.""" | |
| torch, _ = _lazy_torch() | |
| device = weight_r.device | |
| size_k, size_n = weight_r.shape | |
| tiles_k, tiles_n = size_k // 16, size_n // 16 | |
| buf_size_k = max(quant_args.get("buf_size_k", 128), 16) | |
| assert buf_size_k % 16 == 0 and size_n % buf_size_k == 0 | |
| perm = tensor_core_perm(device) | |
| perm_i = tensor_core_perm_i(device) | |
| weight_q = torch.zeros((size_k, size_n), dtype=torch.float, device=device) | |
| encoded = torch.zeros((tiles_k, tiles_n, 256), dtype=torch.short, device=device) | |
| for j in range(size_k, 0, -buf_size_k): | |
| i = j - buf_size_k | |
| b_weight = weight_r[i:j] | |
| for bj in range(buf_size_k, 0, -16): | |
| bi = bj - 16 | |
| rows = b_weight[bi:bj] | |
| tiles = rows.reshape(16, tiles_n, 16).permute(1, 0, 2).reshape(tiles_n, 256) | |
| tiles = tiles[:, perm] | |
| quant_w, quant_i = quantize_tiles_batched(tiles, quant_args) | |
| quant_w = quant_w[:, perm_i] | |
| quant_w = quant_w.reshape(tiles_n, 16, 16).permute(1, 0, 2).reshape(16, size_n) | |
| weight_q[i + bi:i + bj] = quant_w | |
| encoded[(i + bi) // 16:(i + bj) // 16] = quant_i.unsqueeze(0) | |
| return weight_q, encoded | |
| _self_checked = False | |
| _self_checked_ldlq0 = False | |
| _self_checked_lockstep = False | |
| def encode_slice(w_slice, H_data, seed, out_scales, logfile=None, self_check=False): | |
| """Encode one (k, n) float32 CUDA tensor (exl3 convention: k=in_features, | |
| n=out_features) at BITS bpw with the CALIBRATED LDLQ path, mirroring | |
| exllamav3 v0.0.43 quantize_exl3() step for step. | |
| H_data: dict as built by make_H_data() — H = SUM of x x^T over calibration | |
| rows (fp32 [k,k] CUDA), count = rows. May be SHARED between slices with | |
| identical inputs (all 8 gate/up slices of an expert): the first encode | |
| finalizes it (draws su, transforms H, computes the block LDL); subsequent | |
| encodes reuse the cached factorization and su, drawing only their own sv — | |
| exactly the reference behavior for linears sharing a qmap. | |
| Returns (out: dict of cpu tensors {suh, svh, trellis}, stats dict). | |
| Raises AssertionError on any exactness failure (exact-or-bug).""" | |
| global _self_checked, _self_checked_ldlq0 | |
| torch, ext = _lazy_torch() | |
| k, n = w_slice.shape | |
| assert w_slice.dtype == torch.float32 and w_slice.is_cuda | |
| assert k % HAD_K == 0 and n % HAD_N == 0 and n % 128 == 0 | |
| assert H_data["H"].is_meta or tuple(H_data["H"].shape) == (k, k), \ | |
| f"H shape {tuple(H_data['H'].shape)} != ({k},{k})" | |
| device = w_slice.device | |
| quant_args = {"K": BITS, "devices": [0], "apply_out_scales": out_scales, | |
| "mcg": True, "sigma_reg": SIGMA_REG} | |
| # --- reference order: seed -> finalize (su on first use) -> sv ---------- | |
| torch.manual_seed(seed) | |
| q_fallback, H, L, su, H_diag = finalize_capture_H(H_data, quant_args, verbose=False) | |
| if H is not None and not H.is_cuda: | |
| H = H.to(device) # working copy for the proxy metric | |
| if L is not None and not L.is_cuda: | |
| L = L.to(device) | |
| if su.device != device: | |
| su = su.to(device) | |
| if H_diag is not None and H_diag.device != device: | |
| H_diag = H_diag.to(device) | |
| sv = (torch.randn(n, device=device).sign() + 1e-5).sign().to(torch.float).unsqueeze(0) | |
| # reference keeps the stored L on CPU between linears sharing this H_data | |
| if H_data.get("L") is not None and H_data["L"].is_cuda: | |
| H_data["L"] = H_data["L"].cpu() | |
| w_orig = w_slice | |
| apply_out_scales, weight_r, g_scale, su, sv = regularize( | |
| w_slice.clone(), su, sv, quant_args, H_diag=H_diag, q_fallback=q_fallback) | |
| if not q_fallback: | |
| # one-time wiring proof: ldlq with L=0 (no feedback) must equal the | |
| # independent batched tile quant (which itself is asserted equal to the | |
| # reference fallback loop below) | |
| if self_check and not _self_checked_ldlq0: | |
| wq_a, enc_a = ldlq(weight_r, torch.zeros_like(L), quant_args) | |
| wq_b, enc_b = trellis_quant_all_tiles(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_b), "ldlq(L=0) vs batched: encoded indices differ" | |
| assert torch.equal(wq_a, wq_b), "ldlq(L=0) vs batched: reconstructed values differ" | |
| wq_c, enc_c = reference_fallback_quant(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_c) and torch.equal(wq_a, wq_c), \ | |
| "batched vs reference fallback loop differ" | |
| _self_checked_ldlq0 = True | |
| _self_checked = True | |
| log("self-check PASSED: ldlq(L=0) == batched tile quant == reference fallback loop", logfile) | |
| wq_reg, encoded = ldlq(weight_r, L, quant_args) | |
| else: | |
| # reference q_fallback branch (uncalibrated) — cannot trigger under the | |
| # min-routed floor; kept verbatim, recorded loudly by the caller | |
| if self_check and not _self_checked: | |
| wq_a, enc_a = trellis_quant_all_tiles(weight_r, quant_args) | |
| wq_b, enc_b = reference_fallback_quant(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_b), "batched vs reference: encoded indices differ" | |
| assert torch.equal(wq_a, wq_b), "batched vs reference: reconstructed values differ" | |
| _self_checked = True | |
| log("self-check PASSED: batched tile quant is bit-identical to reference fallback_quant", logfile) | |
| wq_reg, encoded = wq_a, enc_a | |
| else: | |
| wq_reg, encoded = trellis_quant_all_tiles(weight_r, quant_args) | |
| # --- reference proxy error: trace(E H E^T) / trace(W H W^T) ------------- | |
| if not q_fallback: | |
| E = weight_r - wq_reg | |
| num = block_trace(E, H) | |
| den = block_trace(weight_r, H) | |
| proxy_err = num / max(den, 1e-8) | |
| del E | |
| else: | |
| proxy_err = (weight_r - wq_reg).square().mean().item() | |
| # --- mandatory encode->decode round-trip exactness (owner mandate (b)) --- | |
| packed = pack_trellis(encoded, quant_args) | |
| unpacked = torch.zeros_like(encoded) | |
| ext.unpack_trellis(unpacked, packed, BITS) | |
| assert torch.equal(unpacked, encoded), "pack/unpack round-trip mismatch (format bug)" | |
| w_dec = torch.empty((k, n), dtype=torch.half, device=packed.device) | |
| ext.reconstruct(w_dec, packed, BITS, True, False) # mcg codebook | |
| assert torch.equal(w_dec, wq_reg.half()), \ | |
| "ext.reconstruct != encoder values (codebook/packing bug)" | |
| expected_bytes = k * n * BITS // 8 | |
| assert packed.numel() * 2 == expected_bytes, "trellis size != exact 3.0 bpw" | |
| # relative round-trip error in weight domain (selection metric ONLY) | |
| wq = preapply_had_l(wq_reg, HAD_K) | |
| wq *= su | |
| wq = preapply_had_r(wq, HAD_N) | |
| wq *= sv | |
| sse = (wq - w_orig).double().pow(2).sum().item() | |
| ss = w_orig.double().pow(2).sum().item() | |
| out = { | |
| "suh": su.flatten().contiguous().to(dtype=torch.half).cpu(), | |
| "svh": sv.flatten().contiguous().to(dtype=torch.half).cpu(), | |
| "trellis": packed.cpu(), | |
| } | |
| stats = {"sse": sse, "ss": ss, "nmse": sse / max(ss, 1e-30), "g_scale": g_scale, | |
| "proxy_err": proxy_err, "q_fallback": bool(q_fallback), | |
| "apply_out_scales": bool(apply_out_scales) if apply_out_scales is not None else None} | |
| return out, stats | |
| # ---------------------------------------------------------------------------- | |
| # v3: encode_slice split into prologue / (lockstep LDLQ) / epilogue. | |
| # encode_slice() above is kept VERBATIM v2 (used by --smoke, --oracle and the | |
| # one-time self-checks); the split below is the same code with the ldlq() call | |
| # lifted out so many slices' main passes can run in lockstep. RNG contract is | |
| # identical to v2: everything a slice draws (su on first finalize of a shared | |
| # H, sv always) happens in its prologue under torch.manual_seed(seed) with | |
| # seeds derived from (L, E, proj, r); neither the LDLQ walk nor the epilogue | |
| # consumes RNG — so results are independent of walk scheduling order. | |
| # ---------------------------------------------------------------------------- | |
| def encode_slice_prologue(w_slice, H_data, seed, out_scales, logfile=None, self_check=False): | |
| """First half of encode_slice() (v2-verbatim math): seed -> finalize -> | |
| sv draw -> regularize (incl. per-slice g_scale_gss, UNCHANGED per owner | |
| mandate). Returns a ctx dict; when ctx["wq_reg"] is None the slice still | |
| needs its LDLQ main pass (run it as an LDLQWalk inside lockstep_ldlq), | |
| otherwise it was completed inline (reference q_fallback branch).""" | |
| global _self_checked, _self_checked_ldlq0 | |
| torch, ext = _lazy_torch() | |
| k, n = w_slice.shape | |
| assert w_slice.dtype == torch.float32 and w_slice.is_cuda | |
| assert k % HAD_K == 0 and n % HAD_N == 0 and n % 128 == 0 | |
| assert H_data["H"].is_meta or tuple(H_data["H"].shape) == (k, k), \ | |
| f"H shape {tuple(H_data['H'].shape)} != ({k},{k})" | |
| device = w_slice.device | |
| quant_args = {"K": BITS, "devices": [0], "apply_out_scales": out_scales, | |
| "mcg": True, "sigma_reg": SIGMA_REG} | |
| # --- reference order: seed -> finalize (su on first use) -> sv ---------- | |
| torch.manual_seed(seed) | |
| q_fallback, H, L, su, H_diag = finalize_capture_H(H_data, quant_args, verbose=False) | |
| if H is not None and not H.is_cuda: | |
| H = H.to(device) # working copy for the proxy metric | |
| if L is not None and not L.is_cuda: | |
| L = L.to(device) | |
| if su.device != device: | |
| su = su.to(device) | |
| if H_diag is not None and H_diag.device != device: | |
| H_diag = H_diag.to(device) | |
| sv = (torch.randn(n, device=device).sign() + 1e-5).sign().to(torch.float).unsqueeze(0) | |
| # reference keeps the stored L on CPU between linears sharing this H_data | |
| if H_data.get("L") is not None and H_data["L"].is_cuda: | |
| H_data["L"] = H_data["L"].cpu() | |
| w_orig = w_slice | |
| apply_out_scales, weight_r, g_scale, su, sv = regularize( | |
| w_slice.clone(), su, sv, quant_args, H_diag=H_diag, q_fallback=q_fallback) | |
| wq_reg, encoded = None, None | |
| if not q_fallback: | |
| # one-time wiring proof (v2): ldlq with L=0 (no feedback) must equal the | |
| # independent batched tile quant and the reference fallback loop | |
| if self_check and not _self_checked_ldlq0: | |
| wq_a, enc_a = ldlq(weight_r, torch.zeros_like(L), quant_args) | |
| wq_b, enc_b = trellis_quant_all_tiles(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_b), "ldlq(L=0) vs batched: encoded indices differ" | |
| assert torch.equal(wq_a, wq_b), "ldlq(L=0) vs batched: reconstructed values differ" | |
| wq_c, enc_c = reference_fallback_quant(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_c) and torch.equal(wq_a, wq_c), \ | |
| "batched vs reference fallback loop differ" | |
| _self_checked_ldlq0 = True | |
| _self_checked = True | |
| log("self-check PASSED: ldlq(L=0) == batched tile quant == reference fallback loop", logfile) | |
| # calibrated main pass deferred to the lockstep pool (LDLQWalk) | |
| else: | |
| # reference q_fallback branch (uncalibrated) — cannot trigger under the | |
| # min-routed floor; kept verbatim, recorded loudly by the caller | |
| if self_check and not _self_checked: | |
| wq_a, enc_a = trellis_quant_all_tiles(weight_r, quant_args) | |
| wq_b, enc_b = reference_fallback_quant(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_b), "batched vs reference: encoded indices differ" | |
| assert torch.equal(wq_a, wq_b), "batched vs reference: reconstructed values differ" | |
| _self_checked = True | |
| log("self-check PASSED: batched tile quant is bit-identical to reference fallback_quant", logfile) | |
| wq_reg, encoded = wq_a, enc_a | |
| else: | |
| wq_reg, encoded = trellis_quant_all_tiles(weight_r, quant_args) | |
| return {"k": k, "n": n, "device": device, "quant_args": quant_args, | |
| "q_fallback": q_fallback, "H": H, "L": L, "H_diag": H_diag, | |
| "su": su, "sv": sv, "g_scale": g_scale, | |
| "apply_out_scales": apply_out_scales, "w_orig": w_orig, | |
| "weight_r": weight_r, "wq_reg": wq_reg, "encoded": encoded} | |
| def encode_slice_epilogue(ctx): | |
| """Second half of encode_slice() — verbatim v2 math from the proxy metric | |
| onward (block_trace, pack/unpack/reconstruct exactness asserts, exact-bpw | |
| check, weight-domain RT error, output tensors).""" | |
| torch, ext = _lazy_torch() | |
| k, n = ctx["k"], ctx["n"] | |
| weight_r, wq_reg, encoded = ctx["weight_r"], ctx["wq_reg"], ctx["encoded"] | |
| assert wq_reg is not None and encoded is not None, \ | |
| "epilogue called before the slice's LDLQ walk completed" | |
| H, su, sv = ctx["H"], ctx["su"], ctx["sv"] | |
| q_fallback, quant_args = ctx["q_fallback"], ctx["quant_args"] | |
| w_orig, g_scale = ctx["w_orig"], ctx["g_scale"] | |
| apply_out_scales = ctx["apply_out_scales"] | |
| # --- reference proxy error: trace(E H E^T) / trace(W H W^T) ------------- | |
| if not q_fallback: | |
| E = weight_r - wq_reg | |
| num = block_trace(E, H) | |
| den = block_trace(weight_r, H) | |
| proxy_err = num / max(den, 1e-8) | |
| del E | |
| else: | |
| proxy_err = (weight_r - wq_reg).square().mean().item() | |
| # --- mandatory encode->decode round-trip exactness (owner mandate (b)) --- | |
| packed = pack_trellis(encoded, quant_args) | |
| unpacked = torch.zeros_like(encoded) | |
| ext.unpack_trellis(unpacked, packed, BITS) | |
| assert torch.equal(unpacked, encoded), "pack/unpack round-trip mismatch (format bug)" | |
| w_dec = torch.empty((k, n), dtype=torch.half, device=packed.device) | |
| ext.reconstruct(w_dec, packed, BITS, True, False) # mcg codebook | |
| assert torch.equal(w_dec, wq_reg.half()), \ | |
| "ext.reconstruct != encoder values (codebook/packing bug)" | |
| expected_bytes = k * n * BITS // 8 | |
| assert packed.numel() * 2 == expected_bytes, "trellis size != exact 3.0 bpw" | |
| # relative round-trip error in weight domain (selection metric ONLY) | |
| wq = preapply_had_l(wq_reg, HAD_K) | |
| wq *= su | |
| wq = preapply_had_r(wq, HAD_N) | |
| wq *= sv | |
| sse = (wq - w_orig).double().pow(2).sum().item() | |
| ss = w_orig.double().pow(2).sum().item() | |
| out = { | |
| "suh": su.flatten().contiguous().to(dtype=torch.half).cpu(), | |
| "svh": sv.flatten().contiguous().to(dtype=torch.half).cpu(), | |
| "trellis": packed.cpu(), | |
| } | |
| stats = {"sse": sse, "ss": ss, "nmse": sse / max(ss, 1e-30), "g_scale": g_scale, | |
| "proxy_err": proxy_err, "q_fallback": bool(q_fallback), | |
| "apply_out_scales": bool(apply_out_scales) if apply_out_scales is not None else None} | |
| return out, stats | |
| def encode_slice_prologue_pre(w_slice, H_data, seed, out_scales, logfile=None): | |
| """v3.1: first part of encode_slice_prologue() (v2-verbatim math): seed -> | |
| finalize (su on first use of a shared H) -> sv draw -> regularize_pre | |
| (out-scale decision, su/sv scaling, Hadamard transforms; g_scale NOT yet | |
| applied). The per-slice g_scale search then runs POOLED across slices | |
| (g_scale_gss_lockstep) before encode_slice_prologue_post(). RNG contract | |
| unchanged from v2/v3: every draw happens HERE under torch.manual_seed(seed) | |
| with seeds derived from (L, E, proj, r); the pooled search and _post | |
| consume no RNG, so results are independent of pool scheduling.""" | |
| torch, ext = _lazy_torch() | |
| k, n = w_slice.shape | |
| assert w_slice.dtype == torch.float32 and w_slice.is_cuda | |
| assert k % HAD_K == 0 and n % HAD_N == 0 and n % 128 == 0 | |
| assert H_data["H"].is_meta or tuple(H_data["H"].shape) == (k, k), \ | |
| f"H shape {tuple(H_data['H'].shape)} != ({k},{k})" | |
| device = w_slice.device | |
| quant_args = {"K": BITS, "devices": [0], "apply_out_scales": out_scales, | |
| "mcg": True, "sigma_reg": SIGMA_REG} | |
| # --- reference order: seed -> finalize (su on first use) -> sv ---------- | |
| torch.manual_seed(seed) | |
| q_fallback, H, L, su, H_diag = finalize_capture_H(H_data, quant_args, verbose=False) | |
| if H is not None and not H.is_cuda: | |
| H = H.to(device) # working copy for the proxy metric | |
| if L is not None and not L.is_cuda: | |
| L = L.to(device) | |
| if su.device != device: | |
| su = su.to(device) | |
| if H_diag is not None and H_diag.device != device: | |
| H_diag = H_diag.to(device) | |
| sv = (torch.randn(n, device=device).sign() + 1e-5).sign().to(torch.float).unsqueeze(0) | |
| # reference keeps the stored L on CPU between linears sharing this H_data | |
| if H_data.get("L") is not None and H_data["L"].is_cuda: | |
| H_data["L"] = H_data["L"].cpu() | |
| w_orig = w_slice | |
| apply_out_scales, weight_r, su, sv = regularize_pre( | |
| w_slice.clone(), su, sv, quant_args, H_diag=H_diag, q_fallback=q_fallback) | |
| return {"k": k, "n": n, "device": device, "quant_args": quant_args, | |
| "q_fallback": q_fallback, "H": H, "L": L, "H_diag": H_diag, | |
| "su": su, "sv": sv, "g_scale": None, | |
| "apply_out_scales": apply_out_scales, "w_orig": w_orig, | |
| "weight_r": weight_r, "wq_reg": None, "encoded": None} | |
| def encode_slice_prologue_post(ctx, logfile=None, self_check=False): | |
| """v3.1: rest of encode_slice_prologue() after the pooled g_scale search: | |
| apply g_scale (verbatim regularize tail), then the verbatim self-check / | |
| q_fallback branch. Returns the same ctx shape v3's prologue returned; | |
| when ctx["wq_reg"] is None the slice still needs its LDLQ main pass.""" | |
| global _self_checked, _self_checked_ldlq0 | |
| torch, ext = _lazy_torch() | |
| assert ctx["g_scale"] is not None, \ | |
| "prologue_post called before the pooled g_scale search set ctx['g_scale']" | |
| weight_r, su = regularize_post(ctx["weight_r"], ctx["su"], ctx["g_scale"]) | |
| ctx["weight_r"], ctx["su"] = weight_r, su | |
| L, q_fallback, quant_args = ctx["L"], ctx["q_fallback"], ctx["quant_args"] | |
| wq_reg, encoded = None, None | |
| if not q_fallback: | |
| # one-time wiring proof (v2): ldlq with L=0 (no feedback) must equal the | |
| # independent batched tile quant and the reference fallback loop | |
| if self_check and not _self_checked_ldlq0: | |
| wq_a, enc_a = ldlq(weight_r, torch.zeros_like(L), quant_args) | |
| wq_b, enc_b = trellis_quant_all_tiles(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_b), "ldlq(L=0) vs batched: encoded indices differ" | |
| assert torch.equal(wq_a, wq_b), "ldlq(L=0) vs batched: reconstructed values differ" | |
| wq_c, enc_c = reference_fallback_quant(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_c) and torch.equal(wq_a, wq_c), \ | |
| "batched vs reference fallback loop differ" | |
| _self_checked_ldlq0 = True | |
| _self_checked = True | |
| log("self-check PASSED: ldlq(L=0) == batched tile quant == reference fallback loop", logfile) | |
| # calibrated main pass deferred to the lockstep pool (LDLQWalk) | |
| else: | |
| # reference q_fallback branch (uncalibrated) — cannot trigger under the | |
| # min-routed floor; kept verbatim, recorded loudly by the caller | |
| if self_check and not _self_checked: | |
| wq_a, enc_a = trellis_quant_all_tiles(weight_r, quant_args) | |
| wq_b, enc_b = reference_fallback_quant(weight_r, quant_args) | |
| assert torch.equal(enc_a, enc_b), "batched vs reference: encoded indices differ" | |
| assert torch.equal(wq_a, wq_b), "batched vs reference: reconstructed values differ" | |
| _self_checked = True | |
| log("self-check PASSED: batched tile quant is bit-identical to reference fallback_quant", logfile) | |
| wq_reg, encoded = wq_a, enc_a | |
| else: | |
| wq_reg, encoded = trellis_quant_all_tiles(weight_r, quant_args) | |
| ctx["wq_reg"], ctx["encoded"] = wq_reg, encoded | |
| return ctx | |
| def lockstep_self_check(walk_ctxs, logfile=None): | |
| """One-time per worker (mirrors the v2 self-check philosophy): re-run the | |
| UNTOUCHED v2 ldlq() on every walk's (weight_r, L) — both read-only to the | |
| walk — and assert the lockstep pool produced bit-identical encodings.""" | |
| global _self_checked_lockstep | |
| torch, _ = _lazy_torch() | |
| for ctx in walk_ctxs: | |
| wq2, enc2 = ldlq(ctx["weight_r"], ctx["L"], ctx["quant_args"]) | |
| assert torch.equal(enc2, ctx["encoded"]), \ | |
| "lockstep vs sequential ldlq: encoded indices differ" | |
| assert torch.equal(wq2, ctx["wq_reg"]), \ | |
| "lockstep vs sequential ldlq: reconstructed values differ" | |
| _self_checked_lockstep = True | |
| log(f"self-check PASSED: lockstep pool ({len(walk_ctxs)} walks) == sequential " | |
| f"v2 ldlq(), bit-identical", logfile) | |
| def slice_seed(L, E, proj_idx, r): | |
| return (SEED_BASE * 1000003 + L * 65536 + E * 16 + proj_idx * 4 + r) & 0x7FFFFFFF | |
| # ---------------------------------------------------------------------------- | |
| # calibration artifacts (Pass A, capture_hessians.py) -> per-expert Hessians | |
| # ---------------------------------------------------------------------------- | |
| def make_H_data(H_sum, count, device): | |
| """Mimics exllamav3 Linear.init_H_data(True) + capture_H accumulation: | |
| H = SUM over calibration rows of x x^T (fp32 CUDA [k,k]), count = rows. | |
| finalize_capture_H() divides by count and mutates H in place, so callers | |
| must pass an OWNED tensor (clone shared/layer-level Hessians).""" | |
| return {"H": H_sum, "first_key": "slice", "count": count, | |
| "finalized": False, "num_total": count, "inf_nan": None, | |
| "device": device} | |
| class LayerCalib: | |
| """Loads one layer's capture artifacts (x.bin bf16 [N,6144], ids.bin uint8 | |
| [N,8]) into RAM and serves per-expert routed-token views + fallbacks.""" | |
| def __init__(self, capture_dir, L, logfile=None): | |
| torch, _ = _lazy_torch() | |
| import numpy as np | |
| d = os.path.join(capture_dir, f"layer_{L:03d}") | |
| man_path = os.path.join(d, "layer_manifest.json") | |
| assert os.path.exists(man_path), \ | |
| f"layer {L}: capture manifest missing ({man_path}) — run capture_hessians.py --capture first" | |
| with open(man_path) as f: | |
| self.manifest = json.load(f) | |
| self.L = L | |
| self.tokens = self.manifest["tokens"] | |
| assert self.manifest["hidden"] == HIDDEN and self.manifest["x_dtype"] == "bfloat16" | |
| xp = os.path.join(d, "x.bin") | |
| ip = os.path.join(d, "ids.bin") | |
| assert os.path.getsize(xp) == self.tokens * HIDDEN * 2, f"{xp}: size mismatch vs manifest" | |
| assert os.path.getsize(ip) == self.tokens * 8, f"{ip}: size mismatch vs manifest" | |
| t0 = time.time() | |
| x_np = np.fromfile(xp, dtype=np.int16).reshape(self.tokens, HIDDEN) | |
| self.x = torch.from_numpy(x_np).view(torch.bfloat16) # CPU RAM, ~12.3 GB | |
| ids = np.fromfile(ip, dtype=np.uint8).reshape(self.tokens, 8) | |
| # routed-token index: token lists per expert via stable flat argsort | |
| flat = ids.reshape(-1) | |
| order = np.argsort(flat, kind="stable") | |
| counts = np.bincount(flat, minlength=NUM_EXPERTS) | |
| self._starts = np.concatenate([[0], np.cumsum(counts)]) | |
| self._token_of = (order // 8).astype(np.int64) | |
| self.routed_counts = counts.tolist() | |
| self._H_layer_cpu = None # lazy (sum, count) for cold-expert fallback | |
| self._fallback_rows = None | |
| if logfile is not None: | |
| log(f"layer {L}: calib loaded — {self.tokens} tokens, routed min/max=" | |
| f"{counts.min()}/{counts.max()}, cold(<{MIN_ROUTED})=" | |
| f"{(counts < MIN_ROUTED).sum()} ({time.time()-t0:.1f}s)", logfile) | |
| def expert_rows(self, E): | |
| torch, _ = _lazy_torch() | |
| return torch.from_numpy(self._token_of[self._starts[E]:self._starts[E + 1]].copy()) | |
| def gather_chunks(self, rows, device, chunk=HCHUNK): | |
| """Yield fp32 CUDA row blocks of x[rows].""" | |
| torch, _ = _lazy_torch() | |
| for s in range(0, rows.numel(), chunk): | |
| sel = rows[s:s + chunk] | |
| yield torch.index_select(self.x, 0, sel).to(device).float() | |
| def all_rows(self): | |
| torch, _ = _lazy_torch() | |
| return torch.arange(self.tokens, dtype=torch.int64) | |
| def fallback_rows(self): | |
| """Deterministic token subsample for cold-expert down-proj Hessians.""" | |
| torch, _ = _lazy_torch() | |
| if self._fallback_rows is None: | |
| g = torch.Generator().manual_seed(SEED_BASE ^ (self.L * 2654435761 & 0x7FFFFFFF)) | |
| n = min(self.tokens, FALLBACK_ROWS) | |
| self._fallback_rows = torch.randperm(self.tokens, generator=g)[:n].sort().values | |
| return self._fallback_rows | |
| def layer_H(self, device): | |
| """Layer-level H (ALL tokens): SUM x x^T fp32 [6144,6144] + count. | |
| Cached on CPU; returns a fresh owned CUDA clone per call (finalize | |
| mutates its input).""" | |
| torch, _ = _lazy_torch() | |
| if self._H_layer_cpu is None: | |
| H = torch.zeros(HIDDEN, HIDDEN, dtype=torch.float32, device=device) | |
| for xc in self.gather_chunks(self.all_rows(), device): | |
| H.addmm_(xc.T, xc) | |
| assert torch.isfinite(H).all(), f"layer {self.L}: non-finite layer H" | |
| self._H_layer_cpu = H.cpu() | |
| del H | |
| return self._H_layer_cpu.to(device), self.tokens | |
| def build_expert_hessians(calib: "LayerCalib", E, Wg, Wu, device, min_routed): | |
| """Per-expert Hessian data for one MoE expert. | |
| Returns (hd_gateup, hd_down[4], meta): | |
| hd_gateup: ONE shared H_data (H = X_e^T X_e, [6144,6144]) for all 8 | |
| gate/up slices (identical inputs -> shared H/su, reference | |
| semantics). Cold expert -> layer-level H over all tokens. | |
| hd_down: 4 per-slice H_datas; slice r holds the [512r,512r+512) | |
| diagonal block of H_down_e = I_e^T I_e, where | |
| I_e = silu(X_e Wg^T) * (X_e Wu^T) (fp32, dequantized bf16 | |
| gate/up weights, GLM-5.2 swiglu: silu, no clamp). Cold | |
| expert -> I over the seeded layer token subsample. | |
| """ | |
| torch, _ = _lazy_torch() | |
| import torch.nn.functional as Fn | |
| rows = calib.expert_rows(E) | |
| n_e = int(rows.numel()) | |
| fallback = n_e < min_routed | |
| WgT = Wg.float().T.contiguous() # [6144, 2048] | |
| WuT = Wu.float().T.contiguous() | |
| H2048 = torch.zeros(MOE_INTER, MOE_INTER, dtype=torch.float32, device=device) | |
| if fallback: | |
| H6144, cnt_gu = calib.layer_H(device) | |
| i_rows = calib.fallback_rows() | |
| else: | |
| H6144 = torch.zeros(HIDDEN, HIDDEN, dtype=torch.float32, device=device) | |
| cnt_gu = n_e | |
| i_rows = rows | |
| cnt_down = int(i_rows.numel()) | |
| for xc in calib.gather_chunks(i_rows, device): | |
| if not fallback: | |
| H6144.addmm_(xc.T, xc) | |
| inter = Fn.silu(xc @ WgT) * (xc @ WuT) # [m, 2048] fp32 | |
| H2048.addmm_(inter.T, inter) | |
| del inter, xc | |
| del WgT, WuT | |
| assert torch.isfinite(H6144).all() and torch.isfinite(H2048).all(), \ | |
| f"expert {E}: non-finite Hessian accumulation" | |
| hd_gateup = make_H_data(H6144, cnt_gu, device) | |
| hd_down = [make_H_data(H2048[r * SLICE:(r + 1) * SLICE, | |
| r * SLICE:(r + 1) * SLICE].clone(), cnt_down, device) | |
| for r in range(TP)] | |
| del H2048 | |
| meta = {"routed": n_e, "h_fallback": fallback, "count_gu": cnt_gu, | |
| "count_down": cnt_down} | |
| return hd_gateup, hd_down, meta | |
| def free_H_data(*hds): | |
| for hd in hds: | |
| for k in ("H", "L", "su", "diag"): | |
| hd.pop(k, None) | |
| # ---------------------------------------------------------------------------- | |
| # NVFP4 dequantization (ModelOpt convention, verified against Luke's shards + | |
| # vLLM modelopt.py: weight_scale_2 = amax/(448*6), dequant = fp4 * fp8scale * ws2, | |
| # packed uint8 = two e2m1 nibbles, LOW nibble first) | |
| # ---------------------------------------------------------------------------- | |
| def _fp4_lut(device): | |
| torch, _ = _lazy_torch() | |
| v = [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0] | |
| return torch.tensor(v + [-x for x in v], dtype=torch.float32, device=device) | |
| def dequant_nvfp4(packed_u8, scale_fp8_u8, ws2: float, device): | |
| """packed_u8: torch.uint8 [N, K//2] (cpu); scale u8 view of fp8e4m3 | |
| [N, K//16]; ws2 float. Returns bf16 [N, K] on device.""" | |
| torch, _ = _lazy_torch() | |
| p = packed_u8.to(device) | |
| N, Kh = p.shape | |
| K = Kh * 2 | |
| lo = (p & 0xF).long() | |
| hi = (p >> 4).long() | |
| idx = torch.stack((lo, hi), dim=-1).view(N, K) # low nibble = element 2i | |
| w = _fp4_lut(device)[idx] # f32 [N, K] | |
| sc = scale_fp8_u8.to(device).view(torch.float8_e4m3fn).float() * ws2 | |
| w = (w.view(N, K // 16, 16) * sc.unsqueeze(-1)).view(N, K) | |
| # convention guard: catches multiply-vs-divide errors immediately | |
| rms = w.square().mean().sqrt().item() | |
| assert 1e-6 < rms < 1.0, f"NVFP4 dequant RMS implausible ({rms}) — scale convention bug" | |
| return w.to(torch.bfloat16) | |
| # ---------------------------------------------------------------------------- | |
| # per-layer encode | |
| # ---------------------------------------------------------------------------- | |
| def load_expert_bf16(src: SourceModel, L, E, proj, device, logfile): | |
| torch, _ = _lazy_torch() | |
| import numpy as np | |
| kw = expert_key(L, E, proj, "weight") | |
| ks = expert_key(L, E, proj, "weight_scale") | |
| k2 = expert_key(L, E, proj, "weight_scale_2") | |
| rd_w = src.reader_for(kw, logfile) | |
| rd_s = src.reader_for(ks, logfile) | |
| rd_2 = src.reader_for(k2, logfile) | |
| dt, shape, _, _ = rd_w.tensors[kw] | |
| assert dt == "U8", (kw, dt) | |
| exp_shape = (MOE_INTER, HIDDEN // 2) if proj != "down_proj" else (HIDDEN, MOE_INTER // 2) | |
| assert tuple(shape) == exp_shape, (kw, shape) | |
| dts, sshape, _, _ = rd_s.tensors[ks] | |
| assert dts == "F8_E4M3" and tuple(sshape) == (exp_shape[0], exp_shape[1] // 8), (ks, dts, sshape) | |
| packed = torch.from_numpy(np.frombuffer(rd_w.read_bytes(kw), dtype=np.uint8).copy()).view(*shape) | |
| scale = torch.from_numpy(np.frombuffer(rd_s.read_bytes(ks), dtype=np.uint8).copy()).view(*sshape) | |
| ws2 = struct.unpack("<f", rd_2.read_bytes(k2))[0] | |
| return dequant_nvfp4(packed, scale, ws2, device) | |
| def expert_slices(W_bf16, proj): | |
| """Yield (rank, slice_f32 (k,n) exl3-convention). gate/up: [2048,6144] | |
| N-sliced by output rows; down: [6144,2048] K-sliced by input cols.""" | |
| torch, _ = _lazy_torch() | |
| if proj != "down_proj": | |
| assert W_bf16.shape == (MOE_INTER, HIDDEN) | |
| for r in range(TP): | |
| sl = W_bf16[r * SLICE:(r + 1) * SLICE, :] # [512, 6144] (out,in) | |
| yield r, sl.transpose(0, 1).contiguous().float() # (k=6144, n=512) | |
| else: | |
| assert W_bf16.shape == (HIDDEN, MOE_INTER) | |
| for r in range(TP): | |
| sl = W_bf16[:, r * SLICE:(r + 1) * SLICE] # [6144, 512] (out,in-slice) | |
| yield r, sl.transpose(0, 1).contiguous().float() # (k=512, n=6144) | |
| def layer_paths(work, L): | |
| return (os.path.join(work, f"tr3-layer-{L:03d}.safetensors"), | |
| os.path.join(work, f"layer-{L:03d}.done.json")) | |
| def layer_done(work, L): | |
| st_path, done_path = layer_paths(work, L) | |
| if not (os.path.exists(st_path) and os.path.exists(done_path)): | |
| return False | |
| try: | |
| with open(done_path) as f: | |
| d = json.load(f) | |
| return d.get("file_sha256") == sha256_file(st_path) | |
| except Exception: | |
| return False | |
| def process_layer(src: SourceModel, work: str, L: int, out_scales, capture_dir: str, | |
| min_routed: int, logfile: str, lockstep_n: int = None): | |
| torch, _ = _lazy_torch() | |
| t0 = time.time() | |
| device = "cuda:0" | |
| st_path, done_path = layer_paths(work, L) | |
| if lockstep_n is None: | |
| lockstep_n = 3 * TP | |
| calib = LayerCalib(capture_dir, L, logfile) | |
| calib_tokens = calib.tokens | |
| calib_sha_x = calib.manifest.get("sha256_x") | |
| stash = {} # (E, proj, r) -> tensors dict | |
| err_num = [0.0] * NUM_EXPERTS | |
| err_den = [0.0] * NUM_EXPERTS | |
| slice_nmse = {} | |
| slice_proxy = {} | |
| routed_count = [0] * NUM_EXPERTS | |
| h_fallback_experts = [] | |
| q_fallback_slices = [] | |
| out_scales_on = 0 | |
| group_size = max(1, (lockstep_n + 3 * TP - 1) // (3 * TP)) # experts per pool | |
| log(f"layer {L}: lockstep={lockstep_n} walks in flight " | |
| f"({group_size} expert(s) per pool), gss_lockstep=on", logfile) | |
| for E0 in range(0, NUM_EXPERTS, group_size): | |
| Es = list(range(E0, min(E0 + group_size, NUM_EXPERTS))) | |
| te = time.time() | |
| group_hd = [] | |
| pre_list = [] # [(E, proj, r, ctx)] in the v2 slice order | |
| walk_ctxs = [] | |
| for E in Es: | |
| W = {proj: load_expert_bf16(src, L, E, proj, device, logfile) for proj in PROJS} | |
| hd_gu, hd_down, hmeta = build_expert_hessians( | |
| calib, E, W["gate_proj"], W["up_proj"], device, min_routed) | |
| routed_count[E] = hmeta["routed"] | |
| if hmeta["h_fallback"]: | |
| h_fallback_experts.append(E) | |
| # fixed slice order: gate r0..3 (finalizes the shared 6144-H, drawing | |
| # su under gate.rank0's seed), up r0..3 (reuse), down r0..3 (own H); | |
| # _pre prologues stay strictly sequential per slice (seed/su/sv/ | |
| # regularize_pre = v2 verbatim); the per-slice g_scale searches then | |
| # run POOLED (v3.1) and the LDLQ main passes run POOLED (v3) | |
| for pi, proj in enumerate(PROJS): | |
| for r, sl in expert_slices(W[proj], proj): | |
| hd = hd_gu if proj != "down_proj" else hd_down[r] | |
| ctx = encode_slice_prologue_pre( | |
| sl, hd, slice_seed(L, E, pi, r), out_scales, logfile=logfile) | |
| pre_list.append((E, proj, r, ctx)) | |
| del W | |
| group_hd.append((hd_gu, hd_down)) | |
| # v3.1 pooled golden-section g_scale search across the group's slices | |
| # (one-time pooled-vs-sequential equality assert on the first pool) | |
| g_scale_gss_lockstep([c for _, _, _, c in pre_list], logfile, self_check=True) | |
| ordered = [] # [(E, proj, r, ctx)] in the v2 slice order | |
| for E, proj, r, ctx in pre_list: | |
| ctx = encode_slice_prologue_post(ctx, logfile=logfile, self_check=True) | |
| ordered.append((E, proj, r, ctx)) | |
| if ctx["wq_reg"] is None: | |
| walk_ctxs.append(ctx) | |
| del pre_list | |
| # lockstep LDLQ main pass over every pending walk in the group | |
| walks = [LDLQWalk(c) for c in walk_ctxs] | |
| lockstep_ldlq(walks, lockstep_n, logfile) | |
| if walks and not _self_checked_lockstep: | |
| lockstep_self_check(walk_ctxs, logfile) | |
| del walks | |
| for E, proj, r, ctx in ordered: | |
| out, stats = encode_slice_epilogue(ctx) | |
| stash[(E, proj, r)] = out | |
| err_num[E] += stats["sse"] | |
| err_den[E] += stats["ss"] | |
| key = f"{E}.{proj}.rank{r}" | |
| slice_nmse[key] = stats["nmse"] | |
| slice_proxy[key] = stats["proxy_err"] | |
| if stats["q_fallback"]: | |
| q_fallback_slices.append(key) # should stay empty (loud record) | |
| if stats["apply_out_scales"]: | |
| out_scales_on += 1 | |
| ctx.clear() | |
| for hd_gu, hd_down in group_hd: | |
| free_H_data(hd_gu, *hd_down) | |
| del ordered, walk_ctxs, group_hd | |
| dt_e = (time.time() - te) / len(Es) | |
| for E in Es: | |
| if E % 16 == 15: | |
| rel = err_num[E] / max(err_den[E], 1e-30) | |
| log(f"layer {L} expert {E:3d} done ({dt_e:.1f}s/expert, " | |
| f"routed {routed_count[E]}, last nmse {rel:.3e})", logfile) | |
| del calib | |
| if q_fallback_slices: | |
| log(f"layer {L} WARNING: {len(q_fallback_slices)} slices hit the reference " | |
| f"q_fallback (uncalibrated) branch: {q_fallback_slices[:4]}...", logfile) | |
| rel_err = [err_num[E] / max(err_den[E], 1e-30) for E in range(NUM_EXPERTS)] | |
| order = sorted(range(NUM_EXPERTS), key=lambda E: rel_err[E], reverse=True) | |
| keep = sorted(order[:KEEP_NVFP4]) # highest trellis RT-error stay NVFP4 | |
| tail = [E for E in range(NUM_EXPERTS) if E not in set(keep)] | |
| assert len(tail) == NUM_EXPERTS - KEEP_NVFP4 | |
| entries = [] | |
| mcg_bytes = struct.pack("<I", MCG_MULT) # stored as I32 scalar (uint32 bit pattern) | |
| for E in tail: | |
| for proj in PROJS: | |
| for r in range(TP): | |
| t = stash[(E, proj, r)] | |
| base = f"model.layers.{L}.mlp.experts.{E}.{proj}.rank{r}" | |
| tr = t["trellis"] | |
| entries.append((f"{base}.suh", "F16", tuple(t["suh"].shape), | |
| t["suh"].numpy().tobytes())) | |
| entries.append((f"{base}.svh", "F16", tuple(t["svh"].shape), | |
| t["svh"].numpy().tobytes())) | |
| entries.append((f"{base}.trellis", "I16", tuple(tr.shape), | |
| tr.numpy().tobytes())) | |
| entries.append((f"{base}.mcg", "I32", (), mcg_bytes)) | |
| _, file_sha = write_safetensors(st_path, entries, metadata={"format": "pt"}) | |
| done = { | |
| "layer": L, | |
| "bits": BITS, | |
| "codebook": "mcg", | |
| "hessian": "ldlq-calibrated", | |
| "sigma_reg": SIGMA_REG, | |
| "min_routed": min_routed, | |
| "capture": { | |
| "dir": capture_dir, | |
| "tokens": calib_tokens, | |
| "sha256_x": calib_sha_x, | |
| }, | |
| "expert_routed_count": routed_count, | |
| "experts_layer_h_fallback": h_fallback_experts, | |
| "q_fallback_slices": q_fallback_slices, | |
| "slices_with_out_scales": out_scales_on, | |
| "su_scheme": "gate/up slices share one H+su per expert (su drawn under " | |
| "gate.rank0 seed at first finalize, reference semantics); " | |
| "down slices each own H+su under their slice seed; sv per slice", | |
| "tp": TP, | |
| "keep_nvfp4": keep, | |
| "tail_tr3": tail, | |
| "expert_rel_rt_mse": rel_err, | |
| "slice_nmse": slice_nmse, | |
| "slice_proxy_err": slice_proxy, | |
| "seed_base": SEED_BASE, | |
| "lockstep": lockstep_n, | |
| "gss_lockstep": True, | |
| "exllamav3": "0.0.43", | |
| "file": os.path.basename(st_path), | |
| "file_sha256": file_sha, | |
| "encode_seconds": round(time.time() - t0, 1), | |
| "finished": time.strftime("%Y-%m-%dT%H:%M:%S"), | |
| } | |
| tmp = done_path + ".tmp" | |
| with open(tmp, "w") as f: | |
| json.dump(done, f, indent=1) | |
| os.replace(tmp, done_path) | |
| log(f"layer {L} COMPLETE in {done['encode_seconds']}s; keep_nvfp4={keep[:8]}...", logfile) | |
| # ---------------------------------------------------------------------------- | |
| # worker + orchestrator | |
| # ---------------------------------------------------------------------------- | |
| def parse_layers(spec: str): | |
| out = [] | |
| for part in spec.split(","): | |
| part = part.strip() | |
| if "-" in part: | |
| a, b = part.split("-") | |
| out += list(range(int(a), int(b) + 1)) | |
| elif part: | |
| out.append(int(part)) | |
| for L in out: | |
| assert FIRST_MOE_LAYER <= L < NUM_LAYERS, f"layer {L} is not a main MoE layer" | |
| return sorted(set(out)) | |
| def check_capture(capture_dir: str, layers) -> None: | |
| """CALIBRATION IS MANDATORY: refuse to encode without a complete capture.""" | |
| missing = [] | |
| for L in layers: | |
| d = os.path.join(capture_dir, f"layer_{L:03d}") | |
| if not (os.path.exists(os.path.join(d, "layer_manifest.json")) | |
| and os.path.exists(os.path.join(d, "x.bin")) | |
| and os.path.exists(os.path.join(d, "ids.bin"))): | |
| missing.append(L) | |
| if missing: | |
| raise SystemExit( | |
| f"capture incomplete in {capture_dir}: layers {missing[:8]}" | |
| f"{'...' if len(missing) > 8 else ''} — run capture_hessians.py --capture " | |
| f"first (owner mandate: no identity-Hessian encode)") | |
| def worker_main(args): | |
| logfile = os.path.join(args.work, "logs", f"worker{args.worker_rank}.log") | |
| os.makedirs(os.path.dirname(logfile), exist_ok=True) | |
| torch, ext = _lazy_torch() | |
| lockstep_n = resolve_lockstep(args.lockstep) | |
| log(f"worker {args.worker_rank} up on {torch.cuda.get_device_name(0)} " | |
| f"(CUDA_VISIBLE_DEVICES={os.environ.get('CUDA_VISIBLE_DEVICES')}, " | |
| f"lockstep={lockstep_n})", logfile) | |
| src = SourceModel(args.src) | |
| layers = parse_layers(args.layers) | |
| mine = layers[args.worker_rank::args.workers] | |
| check_capture(args.capture_dir, mine) | |
| out_scales = {"auto": None, "always": True, "never": False}[args.out_scales] | |
| for L in mine: | |
| if layer_done(args.work, L): | |
| log(f"layer {L} already done, skipping", logfile) | |
| continue | |
| process_layer(src, args.work, L, out_scales, args.capture_dir, | |
| args.min_routed, logfile, lockstep_n) | |
| log(f"worker {args.worker_rank} finished {len(mine)} layers", logfile) | |
| def orchestrate_encode(args): | |
| os.makedirs(os.path.join(args.work, "logs"), exist_ok=True) | |
| logfile = os.path.join(args.work, "logs", "driver.log") | |
| layers = parse_layers(args.layers) | |
| check_capture(args.capture_dir, layers) | |
| todo = [L for L in layers if not layer_done(args.work, L)] | |
| log(f"encode: {len(layers)} layers requested, {len(todo)} to do, " | |
| f"{args.workers} workers, bits={BITS}, keep={KEEP_NVFP4}, tp={TP}, " | |
| f"hessian=ldlq-calibrated (capture {args.capture_dir}, " | |
| f"min_routed={args.min_routed}), lockstep={resolve_lockstep(args.lockstep)}", logfile) | |
| if not todo: | |
| log("nothing to do", logfile) | |
| return | |
| procs = [] | |
| for r in range(args.workers): | |
| env = dict(os.environ) | |
| env["CUDA_VISIBLE_DEVICES"] = str(r % args.gpus) | |
| cmd = [sys.executable, os.path.abspath(__file__), | |
| "--worker-rank", str(r), | |
| "--workers", str(args.workers), | |
| "--src", args.src, "--work", args.work, | |
| "--layers", args.layers, "--out-scales", args.out_scales, | |
| "--capture-dir", args.capture_dir, | |
| "--min-routed", str(args.min_routed), | |
| "--lockstep", str(args.lockstep)] | |
| p = subprocess.Popen(cmd, env=env) | |
| procs.append(p) | |
| log(f"spawned worker {r} pid {p.pid} on GPU {r}", logfile) | |
| rc = 0 | |
| try: | |
| for r, p in enumerate(procs): | |
| code = p.wait() | |
| log(f"worker {r} exited with code {code}", logfile) | |
| rc |= code | |
| except KeyboardInterrupt: | |
| for p in procs: | |
| p.terminate() | |
| raise | |
| done = [L for L in layers if layer_done(args.work, L)] | |
| log(f"encode pass done: {len(done)}/{len(layers)} layers complete", logfile) | |
| sys.exit(1 if rc else 0) | |
| # ---------------------------------------------------------------------------- | |
| # assemble | |
| # ---------------------------------------------------------------------------- | |
| def aux_files(src_dir: str): | |
| """Every non-weight file in the source repo is carried byte-exact — | |
| everything except the shards, the index, and config.json (rewritten | |
| with the hybrid addendum).""" | |
| skip = {"model.safetensors.index.json", "config.json", "MANIFEST.sha256"} | |
| out = [] | |
| for fn in sorted(os.listdir(src_dir)): | |
| p = os.path.join(src_dir, fn) | |
| if not os.path.isfile(p) or fn in skip or fn.endswith(".safetensors"): | |
| continue | |
| out.append(fn) | |
| return out | |
| def assemble(args): | |
| os.makedirs(args.out, exist_ok=True) | |
| logfile = os.path.join(args.work, "logs", "assemble.log") | |
| os.makedirs(os.path.dirname(logfile), exist_ok=True) | |
| src = SourceModel(args.src) | |
| layers = src.moe_layers() | |
| dones = {} | |
| for L in layers: | |
| st_path, done_path = layer_paths(args.work, L) | |
| assert layer_done(args.work, L), f"layer {L} not encoded/verified; run --encode first" | |
| with open(done_path) as f: | |
| dones[L] = json.load(f) | |
| log(f"assemble: all {len(layers)} MoE layers present", logfile) | |
| # ---- shard plan: one output shard per layer + embed + head ------------- | |
| all_src = sorted(src.weight_map.keys()) | |
| dropped = set() | |
| for L in layers: | |
| for E in dones[L]["tail_tr3"]: | |
| for proj in PROJS: | |
| for sfx in ("weight", "weight_scale", "weight_scale_2"): | |
| dropped.add(expert_key(L, E, proj, sfx)) | |
| def carried_for_prefix(pfx): | |
| return [k for k in all_src if k.startswith(pfx) and k not in dropped] | |
| shard_jobs = [] # (out_name, [("src", key) | ("tr3", L, key)]) | |
| shard_jobs.append(("model-embed.safetensors", | |
| [("src", "model.embed_tokens.weight")])) | |
| head = [k for k in ("lm_head.weight", "model.norm.weight") if k in src.weight_map] | |
| shard_jobs.append(("model-head.safetensors", [("src", k) for k in head])) | |
| for L in range(NUM_LAYERS + 1): # 0..78 incl. MTP layer 78 | |
| items = [("src", k) for k in carried_for_prefix(f"model.layers.{L}.")] | |
| if L in dones: | |
| st_path, _ = layer_paths(args.work, L) | |
| rd = STReader(st_path) | |
| items += [("tr3", L, k) for k in sorted(rd.tensors.keys())] | |
| shard_jobs.append((f"model-layer-{L:03d}.safetensors", items)) | |
| accounted = set() | |
| for _, items in shard_jobs: | |
| for it in items: | |
| if it[0] == "src": | |
| accounted.add(it[1]) | |
| missing = [k for k in all_src if k not in dropped and k not in accounted] | |
| assert not missing, f"unaccounted source tensors: {missing[:5]} (+{len(missing)-5 if len(missing)>5 else 0})" | |
| # ---- write shards with per-tensor byte-exact verification -------------- | |
| weight_map = {} | |
| total_size = 0 | |
| from multiprocessing import Pool | |
| job_args = [(args.src, args.work, args.out, name, items) for name, items in shard_jobs] | |
| with Pool(processes=min(args.io_workers, len(job_args))) as pool: | |
| results = pool.map(_assemble_shard, job_args) | |
| for (name, _), (wm_part, nbytes) in zip(shard_jobs, results): | |
| weight_map.update({k: name for k in wm_part}) | |
| total_size += nbytes | |
| log(f"assemble: wrote {len(shard_jobs)} shards, {total_size/2**30:.1f} GiB tensor payload, " | |
| f"all carried tensors verified byte-exact", logfile) | |
| # ---- config.json with hybrid addendum (quantization_config untouched) -- | |
| hset = {d.get("hessian") for d in dones.values()} | |
| assert hset == {"ldlq-calibrated"}, f"mixed/legacy hessian modes in done JSONs: {hset}" | |
| cap_infos = {json.dumps(d["capture"], sort_keys=True) for d in dones.values()} | |
| assert len(cap_infos) == 1, "layers were encoded against different captures" | |
| cap = dones[layers[0]]["capture"] | |
| calibration = { | |
| "capture_dir": cap["dir"], | |
| "tokens_per_layer": cap["tokens"], | |
| "min_routed_floor": dones[layers[0]]["min_routed"], | |
| "sigma_reg": dones[layers[0]]["sigma_reg"], | |
| "layer_h_fallback_experts_total": sum(len(d["experts_layer_h_fallback"]) | |
| for d in dones.values()), | |
| "q_fallback_slices_total": sum(len(d["q_fallback_slices"]) | |
| for d in dones.values()), | |
| } | |
| cap_manifest = os.path.join(cap["dir"], "capture_manifest.json") | |
| if os.path.exists(cap_manifest): | |
| with open(cap_manifest) as f: | |
| cm = json.load(f) | |
| calibration.update({ | |
| "corpus_path": cm.get("corpus_path"), | |
| "corpus_sha256": cm.get("corpus_sha256"), | |
| "corpus_seed": cm.get("seed"), | |
| "num_samples": cm.get("num_samples"), | |
| "total_tokens": cm.get("total_tokens"), | |
| }) | |
| import shutil | |
| shutil.copyfile(cap_manifest, os.path.join(args.out, "calibration_manifest.json")) | |
| log("copied calibration_manifest.json into checkpoint", logfile) | |
| cfg = dict(src.config) | |
| cfg["hybrid_tr3_tail"] = { | |
| "producer": "encode_tr3.py", | |
| "source_repo": "lukealonso/GLM-5.2-NVFP4", | |
| "format": "exl3-trellis", | |
| "bits": float(BITS), | |
| "codebook": "mcg", | |
| "mcg_multiplier": MCG_MULT, | |
| "hessian": "ldlq-calibrated", | |
| "calibration": calibration, | |
| "exllamav3_version": "0.0.43", | |
| "moe_layers": [layers[0], layers[-1]], | |
| "experts_per_layer": NUM_EXPERTS, | |
| "nvfp4_keep_per_layer": KEEP_NVFP4, | |
| "tr3_tail_per_layer": NUM_EXPERTS - KEEP_NVFP4, | |
| "tp": TP, | |
| "slicing": { | |
| "gate_proj": "N-sliced: rank r = output rows [512r, 512r+512) of [2048,6144]; " | |
| "H = expert-routed X_e^T X_e [6144,6144], shared with up_proj", | |
| "up_proj": "N-sliced: rank r = output rows [512r, 512r+512) of [2048,6144]; " | |
| "H shared with gate_proj (same input)", | |
| "down_proj": "K-sliced: rank r = input cols [512r, 512r+512) of [6144,2048]; " | |
| "H = [512r,512r+512) diagonal block of I_e^T I_e " | |
| "(I_e = silu(X_e Wg^T) * (X_e Wu^T), dequantized gate/up) — " | |
| "slice-local LDLQ", | |
| }, | |
| "tensor_schema": "model.layers.{L}.mlp.experts.{E}.{proj}.rank{r}.{trellis|suh|svh|mcg}", | |
| "selection": "per layer, 64 experts with highest pooled relative trellis RT-MSE " | |
| "(measured under the calibrated LDLQ encode) kept NVFP4", | |
| "tier_bitmap": "tier_bitmap.json", | |
| "seed_base": SEED_BASE, | |
| } | |
| with open(os.path.join(args.out, "config.json"), "w") as f: | |
| json.dump(cfg, f, indent=2) | |
| # ---- index + audit ------------------------------------------------------ | |
| index = {"metadata": {"total_size": total_size}, "weight_map": weight_map} | |
| with open(os.path.join(args.out, "model.safetensors.index.json"), "w") as f: | |
| json.dump(index, f, indent=1, sort_keys=True) | |
| audit_index(args.out, logfile) | |
| # ---- tier bitmap --------------------------------------------------------- | |
| tb = {str(L): {"keep_nvfp4": dones[L]["keep_nvfp4"], | |
| "expert_rel_rt_mse": dones[L]["expert_rel_rt_mse"]} for L in layers} | |
| with open(os.path.join(args.out, "tier_bitmap.json"), "w") as f: | |
| json.dump(tb, f) | |
| # ---- aux files byte-exact ------------------------------------------------ | |
| for fn in aux_files(args.src): | |
| sp = os.path.join(args.src, fn) | |
| dp = os.path.join(args.out, fn) | |
| with open(sp, "rb") as fi, open(dp, "wb") as fo: | |
| while True: | |
| b = fi.read(CHUNK) | |
| if not b: | |
| break | |
| fo.write(b) | |
| assert sha256_file(sp) == sha256_file(dp), f"aux copy mismatch: {fn}" | |
| log(f"copied {fn} (byte-exact verified)", logfile) | |
| # ---- manifest ------------------------------------------------------------- | |
| names = sorted(fn for fn in os.listdir(args.out) | |
| if os.path.isfile(os.path.join(args.out, fn)) and fn != "MANIFEST.sha256") | |
| with open(os.path.join(args.out, "MANIFEST.sha256"), "w") as f: | |
| for fn in names: | |
| f.write(f"{sha256_file(os.path.join(args.out, fn))} {fn}\n") | |
| log(f"assemble COMPLETE -> {args.out} ({len(names)} files + MANIFEST.sha256)", logfile) | |
| def _assemble_shard(job): | |
| """Pool worker: write one output shard, then re-read it and assert every | |
| tensor's payload sha256 equals the source payload sha256.""" | |
| src_dir, work, out, name, items = job | |
| src = SourceModel(src_dir) | |
| readers = {} | |
| def rd(path): | |
| if path not in readers: | |
| readers[path] = STReader(path) | |
| return readers[path] | |
| entries = [] | |
| src_sha = {} | |
| for it in items: | |
| if it[0] == "src": | |
| key = it[1] | |
| r = rd(os.path.join(src_dir, src.weight_map[key])) | |
| dt, shape, _, _ = r.tensors[key] | |
| entries.append((key, dt, shape, (lambda r=r, key=key: r.read_bytes(key)))) | |
| else: | |
| _, L, key = it | |
| st_path, _ = layer_paths(work, L) | |
| r = rd(st_path) | |
| dt, shape, _, _ = r.tensors[key] | |
| entries.append((key, dt, shape, (lambda r=r, key=key: r.read_bytes(key)))) | |
| out_path = os.path.join(out, name) | |
| written_sha, _ = write_safetensors(out_path, entries, metadata={"format": "pt"}) | |
| # source-side hashes (streamed straight from source files) | |
| for it in items: | |
| if it[0] == "src": | |
| key = it[1] | |
| src_sha[key] = rd(os.path.join(src_dir, src.weight_map[key])).sha256_stream(key) | |
| else: | |
| _, L, key = it | |
| st_path, _ = layer_paths(work, L) | |
| src_sha[key] = rd(st_path).sha256_stream(key) | |
| # re-read the written file and verify byte-exactness per tensor | |
| check = STReader(out_path) | |
| nbytes = 0 | |
| for key, sha in src_sha.items(): | |
| dt, shape, s, e = check.tensors[key] | |
| got = sha256_range(out_path, s, e) | |
| assert got == sha, f"BYTE-EXACT VERIFY FAILED: {name}:{key}" | |
| assert written_sha[key] == sha, f"write-path hash mismatch: {name}:{key}" | |
| nbytes += e - s | |
| return list(src_sha.keys()), nbytes | |
| def audit_index(out: str, logfile): | |
| with open(os.path.join(out, "model.safetensors.index.json")) as f: | |
| index = json.load(f) | |
| wm = index["weight_map"] | |
| by_file = {} | |
| for k, fn in wm.items(): | |
| by_file.setdefault(fn, set()).add(k) | |
| for fn, keys in by_file.items(): | |
| p = os.path.join(out, fn) | |
| assert os.path.exists(p), f"index maps to missing shard {fn}" | |
| rd = STReader(p) | |
| in_file = set(rd.tensors.keys()) | |
| assert keys == in_file, (f"index/shard mismatch in {fn}: " | |
| f"missing={list(keys-in_file)[:3]} extra={list(in_file-keys)[:3]}") | |
| log(f"index audit OK: {len(wm)} tensors across {len(by_file)} shards, bijective", logfile) | |
| # ---------------------------------------------------------------------------- | |
| # upload | |
| # ---------------------------------------------------------------------------- | |
| def upload(args): | |
| token = os.environ.get("HF_TOKEN") | |
| assert token, "HF_TOKEN env var not set (never pass it on the command line)" | |
| from huggingface_hub import HfApi | |
| api = HfApi(token=token) | |
| api.create_repo(args.repo, repo_type="model", private=True, exist_ok=True) | |
| for attempt in range(1, 6): | |
| try: | |
| api.upload_large_folder(repo_id=args.repo, folder_path=args.out, | |
| repo_type="model") | |
| print(f"upload complete: https://huggingface.co/{args.repo} (private)") | |
| return | |
| except Exception as e: | |
| print(f"upload attempt {attempt}/5 failed: {type(e).__name__}: {e}") | |
| time.sleep(min(60 * attempt, 300)) | |
| raise SystemExit("upload failed after 5 attempts") | |
| # ---------------------------------------------------------------------------- | |
| # smoke test (random tensors + synthetic-activation Hessians, no source model | |
| # or capture needed — exercises finalize_capture_H/block_ldl/ldlq end to end | |
| # plus both bit-exactness self-checks) | |
| # ---------------------------------------------------------------------------- | |
| def smoke(args): | |
| torch, ext = _lazy_torch() | |
| log(f"smoke: torch {torch.__version__}, gpu {torch.cuda.get_device_name(0)}") | |
| torch.manual_seed(99) | |
| for (k, n) in ((HIDDEN, SLICE), (SLICE, HIDDEN)): | |
| w = torch.randn(k, n, device="cuda:0", dtype=torch.float32) * 0.02 | |
| # synthetic calibration activations (mildly anisotropic, like real x) | |
| X = torch.randn(8192, k, device="cuda:0", dtype=torch.float32) | |
| X *= (1.0 + torch.rand(k, device="cuda:0")).unsqueeze(0) | |
| H_data = make_H_data(X.T @ X, X.shape[0], "cuda:0") | |
| del X | |
| t0 = time.time() | |
| out, stats = encode_slice(w, H_data, 1234, None, self_check=True) | |
| dt = time.time() - t0 | |
| assert not stats["q_fallback"], "smoke unexpectedly hit the uncalibrated branch" | |
| assert out["trellis"].numel() * 2 == k * n * BITS // 8 | |
| free_H_data(H_data) | |
| log(f"smoke ({k}x{n}): OK (ldlq), nmse={stats['nmse']:.4e}, " | |
| f"proxy_err={stats['proxy_err']:.4e}, g_scale={stats['g_scale']:.3f}, " | |
| f"{dt:.2f}s (round-trip exactness asserted)") | |
| # shared-H reuse path (gate/up semantics): second encode on a finalized H | |
| X = torch.randn(4096, HIDDEN, device="cuda:0", dtype=torch.float32) | |
| hd = make_H_data(X.T @ X, X.shape[0], "cuda:0") | |
| del X | |
| w1 = torch.randn(HIDDEN, SLICE, device="cuda:0", dtype=torch.float32) * 0.02 | |
| w2 = torch.randn(HIDDEN, SLICE, device="cuda:0", dtype=torch.float32) * 0.02 | |
| _, s1 = encode_slice(w1, hd, 555, None) | |
| assert hd["finalized"] | |
| _, s2 = encode_slice(w2, hd, 556, None) | |
| free_H_data(hd) | |
| log(f"smoke shared-H reuse: OK (nmse {s1['nmse']:.3e} / {s2['nmse']:.3e})") | |
| # --- v3: lockstep-vs-sequential byte-equality on synthetic H ------------- | |
| # (a) mixed geometries co-stepped (384-step gate/up-like walk + 32-step | |
| # down-like walk, uneven completion); (b) shared-H pair (su drawn once | |
| # under the first slice's seed, reference shared-qmap semantics). | |
| torch.manual_seed(101) | |
| cases = [] # (w, H_sum clone A, clone B, seed) | |
| for i, (k, n) in enumerate(((HIDDEN, SLICE), (SLICE, HIDDEN))): | |
| w = torch.randn(k, n, device="cuda:0", dtype=torch.float32) * 0.02 | |
| X = torch.randn(8192, k, device="cuda:0", dtype=torch.float32) | |
| X *= (1.0 + torch.rand(k, device="cuda:0")).unsqueeze(0) | |
| Hs = X.T @ X | |
| del X | |
| cases.append((w, Hs, 4321 + i)) | |
| ws = torch.randn(HIDDEN, SLICE, device="cuda:0", dtype=torch.float32) * 0.02 | |
| ws2 = torch.randn(HIDDEN, SLICE, device="cuda:0", dtype=torch.float32) * 0.02 | |
| Xs = torch.randn(4096, HIDDEN, device="cuda:0", dtype=torch.float32) | |
| Hshared = Xs.T @ Xs | |
| del Xs | |
| # sequential pass (verbatim v2 encode_slice) | |
| hds = [make_H_data(Hs.clone(), 8192, "cuda:0") for _, Hs, _ in cases] | |
| hd_sh = make_H_data(Hshared.clone(), 4096, "cuda:0") | |
| outs_a = [encode_slice(w, hd, seed, None)[0] | |
| for (w, _, seed), hd in zip(cases, hds)] | |
| outs_a.append(encode_slice(ws, hd_sh, 777, None)[0]) | |
| outs_a.append(encode_slice(ws2, hd_sh, 778, None)[0]) | |
| for hd in hds: | |
| free_H_data(hd) | |
| free_H_data(hd_sh) | |
| # lockstep pass (v3 pipeline) on identical inputs | |
| hds = [make_H_data(Hs.clone(), 8192, "cuda:0") for _, Hs, _ in cases] | |
| hd_sh = make_H_data(Hshared.clone(), 4096, "cuda:0") | |
| ctxs = [encode_slice_prologue(w, hd, seed, None) | |
| for (w, _, seed), hd in zip(cases, hds)] | |
| ctxs.append(encode_slice_prologue(ws, hd_sh, 777, None)) | |
| ctxs.append(encode_slice_prologue(ws2, hd_sh, 778, None)) | |
| walks = [LDLQWalk(c) for c in ctxs if c["wq_reg"] is None] | |
| lockstep_ldlq(walks, 12) | |
| outs_b = [encode_slice_epilogue(c)[0] for c in ctxs] | |
| for hd in hds: | |
| free_H_data(hd) | |
| free_H_data(hd_sh) | |
| names = ["6144x512", "512x6144", "sharedH-1", "sharedH-2"] | |
| for name, oa, ob in zip(names, outs_a, outs_b): | |
| for tname in ("trellis", "suh", "svh"): | |
| assert oa[tname].numpy().tobytes() == ob[tname].numpy().tobytes(), \ | |
| f"smoke lockstep mismatch: {name} {tname}" | |
| log(f"smoke lockstep: OK — {len(names)} walks (mixed geometry + shared-H) " | |
| f"lockstep == sequential encode_slice, byte-identical") | |
| # --- v3.1: pooled-GSS pipeline byte-equality on the same inputs ---------- | |
| # (pre-prologues -> g_scale_gss_lockstep [with the one-time pooled-vs- | |
| # sequential g_scale self-check] -> post-prologues -> lockstep LDLQ -> | |
| # epilogues) vs the sequential v2 encode_slice outputs above. | |
| hds = [make_H_data(Hs.clone(), 8192, "cuda:0") for _, Hs, _ in cases] | |
| hd_sh = make_H_data(Hshared.clone(), 4096, "cuda:0") | |
| pres = [encode_slice_prologue_pre(w, hd, seed, None) | |
| for (w, _, seed), hd in zip(cases, hds)] | |
| pres.append(encode_slice_prologue_pre(ws, hd_sh, 777, None)) | |
| pres.append(encode_slice_prologue_pre(ws2, hd_sh, 778, None)) | |
| g_scale_gss_lockstep(pres, None, self_check=True) | |
| ctxs = [encode_slice_prologue_post(c) for c in pres] | |
| walks = [LDLQWalk(c) for c in ctxs if c["wq_reg"] is None] | |
| lockstep_ldlq(walks, 12) | |
| outs_c = [encode_slice_epilogue(c)[0] for c in ctxs] | |
| for hd in hds: | |
| free_H_data(hd) | |
| free_H_data(hd_sh) | |
| for name, oa, oc in zip(names, outs_a, outs_c): | |
| for tname in ("trellis", "suh", "svh"): | |
| assert oa[tname].numpy().tobytes() == oc[tname].numpy().tobytes(), \ | |
| f"smoke v3.1 pooled-GSS mismatch: {name} {tname}" | |
| log(f"smoke gss lockstep: OK — {len(names)} walks (pooled GSS pipeline) == " | |
| f"sequential encode_slice, byte-identical") | |
| log("smoke PASSED") | |
| # ---------------------------------------------------------------------------- | |
| # v3 oracle (HARD deployment gate) + bench — both read-only wrt --work | |
| # ---------------------------------------------------------------------------- | |
| def oracle(args): | |
| """HARD GATE: byte-equality of the deployed v2 sequential encode_slice() | |
| vs the v3 lockstep-LDLQ pipeline vs the v3.1 pooled-GSS pipeline on REAL | |
| slices — dequantized source experts + real captured Hessians of ONE layer | |
| (pick a layer the running encode has not reached), spanning both | |
| geometries and the shared-H gate/up semantics. The expert's raw Hessian | |
| sums are built once and CLONED per pass so all three passes see | |
| bit-identical inputs. PASS requires v3.1 == v3 == v2 on trellis+suh+svh | |
| bytes AND exact g_scale equality. Writes nothing except --oracle-log.""" | |
| torch, _ = _lazy_torch() | |
| olog = args.oracle_log | |
| layers = parse_layers(args.layers) | |
| assert len(layers) == 1, "--oracle expects exactly one layer, e.g. --layers 72" | |
| L = layers[0] | |
| check_capture(args.capture_dir, [L]) | |
| lockstep_n = resolve_lockstep(args.lockstep) | |
| out_scales = {"auto": None, "always": True, "never": False}[args.out_scales] | |
| experts = [int(x) for x in args.oracle_experts.split(",")] | |
| src = SourceModel(args.src) | |
| log(f"ORACLE start (v3.1): layer {L}, experts {experts}, lockstep={lockstep_n}, " | |
| f"gpu {torch.cuda.get_device_name(0)}, torch {torch.__version__}", olog) | |
| calib = LayerCalib(args.capture_dir, L, olog) | |
| device = "cuda:0" | |
| total = passed = 0 | |
| t_v2 = t_v3 = t_v31 = 0.0 | |
| for E in experts: | |
| W = {proj: load_expert_bf16(src, L, E, proj, device, olog) for proj in PROJS} | |
| hd0_gu, hd0_down, hmeta = build_expert_hessians( | |
| calib, E, W["gate_proj"], W["up_proj"], device, args.min_routed) | |
| log(f"ORACLE E{E}: routed={hmeta['routed']} h_fallback={hmeta['h_fallback']} " | |
| f"(raw H sums built once, cloned per pass)", olog) | |
| snap_gu = (hd0_gu["H"].clone(), hd0_gu["count"]) | |
| snap_down = [(hd["H"].clone(), hd["count"]) for hd in hd0_down] | |
| free_H_data(hd0_gu, *hd0_down) | |
| slices = [] | |
| for pi, proj in enumerate(PROJS): | |
| for r, sl in expert_slices(W[proj], proj): | |
| slices.append((pi, proj, r, sl)) | |
| del W | |
| # ---- pass 1: v2 sequential encode_slice (the deployed math) -------- | |
| hd_gu = make_H_data(snap_gu[0].clone(), snap_gu[1], device) | |
| hd_down = [make_H_data(h.clone(), c, device) for h, c in snap_down] | |
| v2_out = {} | |
| torch.cuda.synchronize() | |
| t1 = time.time() | |
| for pi, proj, r, sl in slices: | |
| hd = hd_gu if proj != "down_proj" else hd_down[r] | |
| v2_out[(proj, r)] = encode_slice(sl, hd, slice_seed(L, E, pi, r), | |
| out_scales, logfile=None, self_check=False) | |
| torch.cuda.synchronize() | |
| t_v2 += time.time() - t1 | |
| free_H_data(hd_gu, *hd_down) | |
| # ---- pass 2: v3 lockstep-LDLQ pipeline (the RUNNING encode's path) --- | |
| hd_gu = make_H_data(snap_gu[0].clone(), snap_gu[1], device) | |
| hd_down = [make_H_data(h.clone(), c, device) for h, c in snap_down] | |
| torch.cuda.synchronize() | |
| t1 = time.time() | |
| ctxs = [] | |
| for pi, proj, r, sl in slices: | |
| hd = hd_gu if proj != "down_proj" else hd_down[r] | |
| ctxs.append((proj, r, encode_slice_prologue( | |
| sl, hd, slice_seed(L, E, pi, r), out_scales, | |
| logfile=None, self_check=False))) | |
| walks = [LDLQWalk(c) for _, _, c in ctxs if c["wq_reg"] is None] | |
| lockstep_ldlq(walks, lockstep_n, None) | |
| v3_out = {} | |
| for proj, r, ctx in ctxs: | |
| v3_out[(proj, r)] = encode_slice_epilogue(ctx) | |
| torch.cuda.synchronize() | |
| t_v3 += time.time() - t1 | |
| free_H_data(hd_gu, *hd_down) | |
| del walks, ctxs | |
| # ---- pass 3: v3.1 pooled-GSS pipeline on identical inputs ----------- | |
| hd_gu = make_H_data(snap_gu[0].clone(), snap_gu[1], device) | |
| hd_down = [make_H_data(h.clone(), c, device) for h, c in snap_down] | |
| torch.cuda.synchronize() | |
| t1 = time.time() | |
| pres = [] | |
| for pi, proj, r, sl in slices: | |
| hd = hd_gu if proj != "down_proj" else hd_down[r] | |
| pres.append((proj, r, encode_slice_prologue_pre( | |
| sl, hd, slice_seed(L, E, pi, r), out_scales, logfile=None))) | |
| g_scale_gss_lockstep([c for _, _, c in pres], None, self_check=False) | |
| ctxs31 = [(proj, r, encode_slice_prologue_post(c)) for proj, r, c in pres] | |
| walks = [LDLQWalk(c) for _, _, c in ctxs31 if c["wq_reg"] is None] | |
| lockstep_ldlq(walks, lockstep_n, None) | |
| v31_out = {} | |
| for proj, r, ctx in ctxs31: | |
| v31_out[(proj, r)] = encode_slice_epilogue(ctx) | |
| torch.cuda.synchronize() | |
| t_v31 += time.time() - t1 | |
| free_H_data(hd_gu, *hd_down) | |
| del walks, ctxs31, pres | |
| # ---- byte comparison: v3.1 vs v3 vs v2 ------------------------------- | |
| for pi, proj, r, sl in slices: | |
| (o2, s2) = v2_out[(proj, r)] | |
| (o3, s3) = v3_out[(proj, r)] | |
| (o31, s31) = v31_out[(proj, r)] | |
| eq_31_2 = all(o31[t].numpy().tobytes() == o2[t].numpy().tobytes() | |
| for t in ("trellis", "suh", "svh")) | |
| eq_31_3 = all(o31[t].numpy().tobytes() == o3[t].numpy().tobytes() | |
| for t in ("trellis", "suh", "svh")) | |
| g_eq = (s31["g_scale"] == s3["g_scale"] == s2["g_scale"]) | |
| ok = eq_31_2 and eq_31_3 and g_eq | |
| sha = hashlib.sha256(o31["trellis"].numpy().tobytes()).hexdigest()[:16] | |
| total += 1 | |
| passed += int(ok) | |
| log(f"ORACLE layer {L} E{E} {proj}.rank{r} k={sl.shape[0]} n={sl.shape[1]}: " | |
| f"{'BYTE-IDENTICAL v3.1==v3==v2' if ok else '*** MISMATCH ***'} " | |
| f"[trellis sha256[:16]={sha}] " | |
| f"g v2={s2['g_scale']:.6f} v3={s3['g_scale']:.6f} v3.1={s31['g_scale']:.6f} " | |
| f"g-exact={g_eq} nmse v2={s2['nmse']:.9e} v3.1={s31['nmse']:.9e} " | |
| f"proxy v2={s2['proxy_err']:.9e} v3.1={s31['proxy_err']:.9e}", olog) | |
| del slices, v2_out, v3_out, v31_out, snap_gu, snap_down | |
| torch.cuda.empty_cache() | |
| verdict = "PASS" if passed == total else "FAIL" | |
| log(f"ORACLE {verdict}: {passed}/{total} slices byte-identical v3.1==v3==v2 " | |
| f"(trellis+suh+svh, g_scale exact-equal), both geometries + shared-H " | |
| f"semantics; identical inputs took v2 sequential {t_v2:.1f}s vs v3 " | |
| f"lockstep {t_v3:.1f}s vs v3.1 pooled-GSS {t_v31:.1f}s (contended timings)", olog) | |
| if passed != total: | |
| sys.exit(2) | |
| def bench(args): | |
| """Short v3 throughput probe (no --work writes): run --bench-experts | |
| experts of one layer through the FULL v3 per-expert path (shard load + | |
| H build + 12 prologues + lockstep LDLQ + epilogues incl. every exactness | |
| assert) and report s/expert. Safe to run beside a live encode; timings | |
| are then worst-case contended.""" | |
| torch, _ = _lazy_torch() | |
| layers = parse_layers(args.layers) | |
| assert len(layers) == 1, "--bench expects exactly one layer, e.g. --layers 72" | |
| L = layers[0] | |
| check_capture(args.capture_dir, [L]) | |
| lockstep_n = resolve_lockstep(args.lockstep) | |
| out_scales = {"auto": None, "always": True, "never": False}[args.out_scales] | |
| src = SourceModel(args.src) | |
| log(f"BENCH start (v3.1 pooled GSS): layer {L}, {args.bench_experts} experts, " | |
| f"lockstep={lockstep_n}, gpu {torch.cuda.get_device_name(0)}") | |
| calib = LayerCalib(args.capture_dir, L, None) | |
| device = "cuda:0" | |
| group_size = max(1, (lockstep_n + 3 * TP - 1) // (3 * TP)) | |
| times = [] | |
| E0 = 0 | |
| while E0 < args.bench_experts: | |
| Es = list(range(E0, min(E0 + group_size, args.bench_experts))) | |
| torch.cuda.synchronize() | |
| te = time.time() | |
| pre_list, walk_ctxs, group_hd = [], [], [] | |
| for E in Es: | |
| W = {proj: load_expert_bf16(src, L, E, proj, device, None) for proj in PROJS} | |
| hd_gu, hd_down, _hm = build_expert_hessians( | |
| calib, E, W["gate_proj"], W["up_proj"], device, args.min_routed) | |
| for pi, proj in enumerate(PROJS): | |
| for r, sl in expert_slices(W[proj], proj): | |
| hd = hd_gu if proj != "down_proj" else hd_down[r] | |
| pre_list.append(encode_slice_prologue_pre( | |
| sl, hd, slice_seed(L, E, pi, r), out_scales)) | |
| del W | |
| group_hd.append((hd_gu, hd_down)) | |
| tg = time.time() | |
| g_scale_gss_lockstep(pre_list) | |
| torch.cuda.synchronize() | |
| tg = time.time() - tg | |
| ordered = [] | |
| for ctx in pre_list: | |
| ctx = encode_slice_prologue_post(ctx) | |
| ordered.append(ctx) | |
| if ctx["wq_reg"] is None: | |
| walk_ctxs.append(ctx) | |
| walks = [LDLQWalk(c) for c in walk_ctxs] | |
| tq = time.time() | |
| lockstep_ldlq(walks, lockstep_n) | |
| tq = time.time() - tq | |
| for ctx in ordered: | |
| encode_slice_epilogue(ctx) | |
| ctx.clear() | |
| for hd_gu, hd_down in group_hd: | |
| free_H_data(hd_gu, *hd_down) | |
| torch.cuda.synchronize() | |
| dt = (time.time() - te) / len(Es) | |
| times.append(dt) | |
| log(f"BENCH experts {Es[0]}-{Es[-1]}: {dt:.2f}s/expert " | |
| f"(lockstep main pass {tq/len(Es):.2f}s/expert, " | |
| f"pooled gss {tg/len(Es):.2f}s/expert)") | |
| E0 += group_size | |
| times_sorted = sorted(times) | |
| mean = sum(times) / len(times) | |
| log(f"BENCH RESULT (v3.1 pooled GSS): layer {L}, {len(times)} pool(s), " | |
| f"lockstep={lockstep_n} — mean {mean:.2f}s/expert, best {times_sorted[0]:.2f}, " | |
| f"worst {times_sorted[-1]:.2f} " | |
| f"(v3 baselines: 19.92s bench under 3-contention, ~5.1s solo in enc3)") | |
| log(f"BENCH projection at --workers 4 (1/GPU): {mean:.1f}s/expert -> " | |
| f"{mean*256/60:.1f} min/layer/worker -> {mean*256*75/4/3600:.1f} h for 75 layers") | |
| # ---------------------------------------------------------------------------- | |
| # main | |
| # ---------------------------------------------------------------------------- | |
| def main(): | |
| ap = argparse.ArgumentParser(description="GLM-5.2 NVFP4 -> EXL3-tr3 hybrid encoder") | |
| ap.add_argument("--encode", action="store_true", help="run multi-GPU encode (default action)") | |
| ap.add_argument("--assemble", action="store_true", help="assemble final checkpoint dir") | |
| ap.add_argument("--upload", action="store_true", help="upload --out to private HF repo") | |
| ap.add_argument("--smoke", action="store_true", help="kernel/roundtrip smoke test on random data") | |
| ap.add_argument("--src", default=DEF_SRC) | |
| ap.add_argument("--work", default=DEF_WORK) | |
| ap.add_argument("--out", default=DEF_OUT) | |
| ap.add_argument("--repo", default=DEF_REPO) | |
| ap.add_argument("--layers", default=f"{FIRST_MOE_LAYER}-{NUM_LAYERS-1}", | |
| help="e.g. '3-77' or '3,5,10-12'") | |
| ap.add_argument("--workers", type=int, default=4) | |
| ap.add_argument("--gpus", type=int, default=4, | |
| help="physical GPU count; workers are assigned round-robin (worker r -> GPU r%%gpus)") | |
| ap.add_argument("--io-workers", type=int, default=12) | |
| ap.add_argument("--capture-dir", default=DEF_CAPTURE, | |
| help="Pass A output (capture_hessians.py) — REQUIRED for --encode") | |
| ap.add_argument("--min-routed", type=int, default=MIN_ROUTED, | |
| help="per-expert routed-token floor; below it the expert uses the " | |
| "layer-level H (recorded in done JSON, never a failure)") | |
| ap.add_argument("--out-scales", choices=["auto", "always", "never"], default="auto", | |
| help="exl3 out-channel scales; 'auto' matches convert_model.py default " | |
| "(calibrated: per-slice H-diag skew decision, reference math)") | |
| ap.add_argument("--lockstep", default="auto", | |
| help="v3: max LDLQ slice-walks co-stepped per quantize_tiles call; " | |
| "'auto' = 12 (all walks of one expert); N>12 pools " | |
| "ceil(N/12) experts per lockstep group") | |
| ap.add_argument("--oracle", action="store_true", | |
| help="HARD GATE: byte-equality of v2 sequential vs v3 lockstep vs " | |
| "v3.1 pooled-GSS on real slices of ONE --layers layer; " | |
| "logs to --oracle-log") | |
| ap.add_argument("--oracle-experts", default="0,1", | |
| help="comma-separated expert ids for --oracle (12 slices each)") | |
| ap.add_argument("--oracle-log", default="/root/tr3_v31_oracle.log") | |
| ap.add_argument("--bench", action="store_true", | |
| help="v3 throughput probe on ONE --layers layer (no --work writes)") | |
| ap.add_argument("--bench-experts", type=int, default=8) | |
| ap.add_argument("--worker-rank", type=int, default=None, help=argparse.SUPPRESS) | |
| args = ap.parse_args() | |
| try: | |
| if args.worker_rank is not None: | |
| worker_main(args) | |
| elif args.smoke: | |
| smoke(args) | |
| elif args.oracle: | |
| oracle(args) | |
| elif args.bench: | |
| bench(args) | |
| elif args.assemble: | |
| assemble(args) | |
| elif args.upload: | |
| upload(args) | |
| else: | |
| orchestrate_encode(args) | |
| except Exception: | |
| traceback.print_exc() | |
| sys.exit(1) | |
| if __name__ == "__main__": | |
| main() | |