"""Full-model B200 reference KLD; explicitly distinct from native SM120 true-decode KLD.""" from __future__ import annotations import argparse import dataclasses import hashlib import json import os import re import sys import time import types from concurrent.futures import ThreadPoolExecutor from pathlib import Path import numpy as np import torch import torch.nn.functional as F from accelerate import dispatch_model, init_empty_weights from accelerate.utils import set_module_tensor_to_device from safetensors import safe_open from transformers import AutoConfig, Glm5NextForConditionalGeneration import encode_layer_v2 as encoder from glm53_nvfp4 import p8_coupled_scale as coupled from glm53_nvfp4.p8_layer_rate_damage import RankLayerStreams, decode_expert, state_codebook from kld_core import score_window def sha(path): h = hashlib.sha256() with Path(path).open("rb") as f: for part in iter(lambda: f.read(64 << 20), b""): h.update(part) return h.hexdigest() def qdq_rp2(values, global_scale, policy): if global_scale != 1.0 or policy != "amax": raise ValueError("Only the fixed RP2 amax carrier is supported") blocks = values.float().reshape(*values.shape[:-1], values.shape[-1] // 32, 32) def term(x): maximum = x.abs().amax(-1, keepdim=True) exponent = torch.ceil(torch.log2((maximum / 448).clamp_min(2.0 ** -127))).clamp(-127, 128) scale = torch.exp2(exponent) return (x / scale).clamp(-448, 448).to(torch.float8_e4m3fn).float() * scale hi = term(blocks) return (hi + term(blocks - hi)).reshape_as(values) def experts_forward(self, hidden_states, top_k_index, top_k_weights): final = torch.zeros_like(hidden_states, dtype=torch.float32) # Every expert shares this row-local input transform. Evaluate it once per layer; # selecting rows afterward preserves the reference's casts and arithmetic. fc1_input = coupled.coupled_input_carrier( hidden_states, self._cv2_scales.gate_up_suh, quantize=True) # These are exact decoded physical weights; scales and rotations stay in the forward path. for expert in torch.unique(top_k_index).tolist(): token_idx, route_idx = torch.where(top_k_index == expert) gate, up = self.gate_up_proj[expert].chunk(2, dim=0) down_input = coupled.coupled_middle_carrier( fc1_input[token_idx], gate, up, self._cv2_scales, expert=expert, intermediate_draw=coupled.COUPLED_SIGN_DRAW, quantize=True) down = F.linear(down_input.float(), self.down_proj[expert].float()).to(torch.float16).float() route = coupled.block_hadamard(down, block_size=coupled.HADAMARD_INPUT) route = route * self._cv2_scales.down_svh.float() result = coupled.block_hadamard(route, block_size=coupled.HADAMARD_RESIDUAL) final.index_add_(0, token_idx, result * top_k_weights[token_idx, route_idx, None].float()) return final.to(hidden_states.dtype) def tr3_experts_forward(self, hidden_states, top_k_index, top_k_weights): final = torch.zeros_like(hidden_states, dtype=torch.float32) for expert in torch.unique(top_k_index).tolist(): token_idx, route_idx = torch.where(top_k_index == expert) gate, up = self.gate_up_proj[expert].chunk(2, dim=0) result = coupled.source_expert_reference(hidden_states[token_idx], gate, up, self.down_proj[expert]) final.index_add_(0, token_idx, result * top_k_weights[token_idx, route_idx, None].float()) return final.to(hidden_states.dtype) def layer_device(layer): # Balance the 42 routed layers 11/11/10/10, leaving decoding/forward headroom. return 0 if layer < 14 else 1 if layer < 25 else 2 if layer < 35 else 3 @torch.inference_mode() def load_model(source, checkpoint, mode): config = AutoConfig.from_pretrained(source, local_files_only=True) config._attn_implementation = "sdpa" config.text_config._attn_implementation = "sdpa" old_dtype = torch.get_default_dtype() torch.set_default_dtype(torch.bfloat16) try: with init_empty_weights(): model = Glm5NextForConditionalGeneration(config) finally: torch.set_default_dtype(old_dtype) wm = json.loads((source / "model.safetensors.index.json").read_text())["weight_map"] # A root entry makes Accelerate move every descendant to GPU0 before the # per-layer hooks attach. Map complete sibling blocks explicitly instead. device_map = {"model.language_model.embed_tokens": 0, "model.language_model.norm": 3, "model.language_model.hc_head": 3, "lm_head": 3} for name, _ in model.model.named_children(): if name != "language_model": device_map[f"model.{name}"] = 0 for layer in range(45): device_map[f"model.language_model.layers.{layer}"] = layer_device(layer) def target_device(name): match = re.search(r"language_model\.layers\.(\d+)\.", name) if match: return layer_device(int(match.group(1))) return 3 if name.startswith(("lm_head.", "model.language_model.norm.", "model.language_model.hc_head.")) else 0 def source_names(name): # Exact GLM5Next renames/concatenation from Transformers 5.17 conversion_mapping.py. if ".self_attn.conv1d.weight" in name: return [name.replace("self_attn.conv1d.weight", f"self_attn.{axis}_conv1d.weight") for axis in ("q", "k", "v")] name = name.replace("self_attn.forget_gate.", "self_attn.") for hop in ("attn", "ffn"): for part in ("fn", "base", "scale"): name = name.replace(f"{hop}_hc.{part}", f"hc_{hop}_{part}") return [name] # Only non-routed tensors are read from BF16. Routed storage is filled directly from the checkpoint. missing = [] for name, tensor in list(model.named_parameters()) + list(model.named_buffers()): if ".mlp.experts." in name: continue names = source_names(name) if any(key not in wm for key in names): if tensor.is_meta: missing.append(name) continue values = [] for key in names: with safe_open(str(source / wm[key]), framework="pt", device="cpu") as f: values.append(f.get_tensor(key)) value = torch.cat(values, dim=0) if len(values) > 1 else values[0] if tuple(value.shape) != tuple(tensor.shape): raise ValueError(f"Non-expert shape mismatch: {name}: {value.shape} vs {tensor.shape}") set_module_tensor_to_device(model, name, target_device(name), value=value, dtype=value.dtype) if missing: raise ValueError(f"Missing source parameters: {missing}") tr3_map = None if mode == "tr3": tr3_map = json.loads((checkpoint / "model.safetensors.index.json").read_text())["weight_map"] sys.path.insert(0, str(Path(os.environ["GLM53_KQUANT_ROOT"]))) from kquant.exl3_reference import decode_exl3_weight from glm53_nvfp4 import trellis_nvfp4 as tn @torch.inference_mode() def load_layers(gpu): torch.cuda.set_device(gpu) device = torch.device(f"cuda:{gpu}") for layer in range(3, 45): if layer_device(layer) != gpu: continue start = time.time() prefix = f"model.language_model.layers.{layer}.mlp.experts" module = model.get_submodule(prefix) for name in ("gate_up_proj", "down_proj"): shape = getattr(module, name).shape value = torch.empty(shape, dtype=torch.float16 if mode == "tr3" else torch.bfloat16, device=device) set_module_tensor_to_device(model, f"{prefix}.{name}", device, value=value, dtype=value.dtype) if mode == "tr3": mcg = tn.procedural_state_values("mcg").float().to(device) def get(name): with safe_open(str(checkpoint / tr3_map[name]), framework="pt", device="cpu") as f: return f.get_tensor(name).to(device) for expert in range(288): for projection in ("gate", "up", "down"): base = f"model.language_model.layers.{layer}.mlp.experts.{expert}.{projection}_proj" trellis = get(base + ".trellis") bits = trellis.shape[-1] // 16 assert bits == 4 states = tn.reconstruct_trellis_states(tn.unpack_trellis_edges(trellis, bits), bits) weight = decode_exl3_weight(states, get(base + ".suh").float(), get(base + ".svh").float(), codebook_values=mcg, bits=bits).t() target = (module.down_proj[expert] if projection == "down" else module.gate_up_proj[expert, :2048] if projection == "gate" else module.gate_up_proj[expert, 2048:]) target.copy_(weight) module.forward = types.MethodType(tr3_experts_forward, module) torch.cuda.synchronize(device) print(json.dumps({"decoded_tr3_layer": layer, "gpu": gpu, "seconds": round(time.time() - start, 2)}), flush=True) continue streams = RankLayerStreams([checkpoint / "sidecars" / f"p8-layer-{layer:03d}-tp4-rank-{r}.safetensors" for r in range(4)]) lut = state_codebook(streams.bits, device) for expert in range(288): gate, up, down = decode_expert(streams.expert(expert), streams.bits, lut, device) module.gate_up_proj[expert, :2048].copy_(gate) module.gate_up_proj[expert, 2048:].copy_(up) module.down_proj[expert].copy_(down) scales = encoder.r27_scales(layer) module._cv2_scales = dataclasses.replace(scales, **{ field.name: getattr(scales, field.name).to(device) for field in dataclasses.fields(scales) if isinstance(getattr(scales, field.name), torch.Tensor)}) module.forward = types.MethodType(experts_forward, module) torch.cuda.synchronize(device) print(json.dumps({"decoded_layer": layer, "gpu": gpu, "seconds": round(time.time() - start, 2)}), flush=True) with ThreadPoolExecutor(4) as pool: list(pool.map(load_layers, range(4))) if any(p.is_meta for p in model.parameters()): raise RuntimeError("Unloaded model parameter") if mode == "p8": coupled._qdq_e4m3_k32 = qdq_rp2 model = dispatch_model(model, device_map=device_map, force_hooks=True) model.eval() return model @torch.inference_mode() def main(): ap = argparse.ArgumentParser() ap.add_argument("--checkpoint", type=Path, required=True) ap.add_argument("--out", type=Path, required=True) ap.add_argument("--mode", choices=("p8", "tr3"), default="p8") args = ap.parse_args() source = Path(os.environ["CV2_BF16"]) root = Path(os.environ["CV2_ROOT"]) teacher = Path(os.environ["CV2_DATA"]) / "kld-teacher" manifest = json.loads((root / "ops/verified-inputs.json").read_text()) args.out.mkdir(parents=True, exist_ok=True) torch.set_num_threads(16) torch.backends.cuda.matmul.allow_tf32 = False model = load_model(source, args.checkpoint, args.mode) rows = [] for window in manifest["windows"]: wid = window["id"] summary_path = args.out / f"{wid}.json" if summary_path.exists(): rows.append(json.loads(summary_path.read_text())) continue started = time.time() token_path = teacher / f"calibration/panel-v1/arrays/{wid}.tokens.npy" teacher_path = teacher / window["teacher_path"] assert sha(token_path) == window["input_sha256"], wid assert sha(teacher_path) == window["teacher_sha256"], wid tokens = np.load(token_path, allow_pickle=False).reshape(-1).astype(np.int64) assert tokens.shape == (2048,) assert hashlib.sha256(tokens.astype("