Fix production FMHA layer test: compare raw FMHA vs SDPA on production gathered KV

Phase 1: Run full pipeline to populate KV caches with real model weights.
Phase 2: For each layer, gather KV in mixed FP8/BF16 format, run both
production FMHA and PyTorch SDPA, compare cosine similarity.

Uses random Q (not model-generated) to isolate FMHA kernel accuracy
from upstream pipeline issues.
This commit is contained in:
2026-06-03 02:26:37 +00:00
parent ba67e055f7
commit 26a817c2f2

View File

@@ -1,16 +1,17 @@
#!/usr/bin/env python3
"""Production FMHA layer comparison test — real model weights, real pipeline.
This test exercises the EXACT same code path as single_shot_inference.py
for the attention forward pass, but adds reference comparison at the FMHA step.
Strategy:
1. Run the full production pipeline (single_shot_inference.py forward_layer)
for all prefill tokens through layers 0-4.
2. On the LAST prefill token, for each layer, ALSO run the reference FMHA
(dequantize KV to BF16, run PyTorch SDPA) on the SAME gathered KV
that the production kernel saw.
3. Compare raw FMHA output (before inverse RoPE, before output projection).
It loads real model weights from the checkpoint, runs a single token through
the attention path for layers 0-4, and compares:
1. Production FMHA output vs PyTorch SDPA on the SAME gathered KV
2. Per-layer cosine at each stage: q_a, q_b, kv, compressed_kv, fmha_out
This isolates the FMHA kernel's accuracy from the rest of the pipeline.
Production values: HD=512, NOPE=448, ROPE=64, H=128, 61 layers, 8 GPUs.
No shortcuts. No synthetic data. Real weights, real pipeline.
"""
import os, sys, json, math, time
import torch
@@ -19,7 +20,6 @@ import torch.nn.functional as F
CHECKPOINT_DIR = os.environ.get(
"CHECKPOINT_DIR", "/root/nvidia-meeting/DeepSeek-V4-Pro-NVFP4")
NUM_GPUS = int(os.environ.get("NUM_GPUS", "8"))
DEVICE = "cuda:0"
def cosine(a, b):
@@ -30,10 +30,8 @@ def main():
torch.manual_seed(42)
print("=" * 70)
print("PRODUCTION FMHA LAYER COMPARISON TEST")
print("Real model weights. Real pipeline. Production values.")
print("=" * 70)
# ---- Load config ----
with open(os.path.join(CHECKPOINT_DIR, "config.json")) as f:
cfg = json.load(f)
n_layers = cfg["num_hidden_layers"]
@@ -43,75 +41,67 @@ def main():
rd = cfg.get("qk_rope_head_dim", 64)
nope_dim = hd - rd
cr = cfg.get("compress_ratios", [128] * n_layers)
n_routed = cfg["n_routed_experts"]
top_k = cfg.get("num_experts_per_tok", 6)
print(f"Model: {n_layers} layers, {n_h} heads, hd={hd}, rope_dim={rd}, nope={nope_dim}")
print(f"Compress ratios: first5={cr[:5]}")
print(f"Model: {n_layers} layers, {n_h} heads, hd={hd}, rope_dim={rd}")
# ---- Load weights ----
print("\nLoading weights...")
from safetensors.torch import load_file
from single_shot_inference import load_all_weights
all_w = load_all_weights(CHECKPOINT_DIR)
print(f" {len(all_w)} weight tensors loaded")
# ---- Build components for first 5 layers ----
# We only test layers 0-4 to keep test time reasonable
TEST_LAYERS = 5
from dsv4.layers.mhc import mHCLayer
from single_shot_inference import (
load_all_weights, make_nvfp4_linear, get_nvfp4_weight,
rmsnorm, unweighted_rmsnorm, _apply_rope, build_rope_cache,
KVCache, Compressor, Indexer, forward_layer, moe_forward,
_load_moe_weights_stacked, _load_shared_expert_weights,
_cache_layer_weights_no_experts,
)
from dsv4.layers.mhc import mHCLayer, mHCContext
from dsv4.layers.router import Router
from dsv4.layers.moe import Nvfp4MoE
from dsv4.layers.shared_expert import Nvfp4SharedExpert
from dsv4.layers.linear import Nvfp4Linear
from dsv4.layers.grouped_linear import Nvfp4GroupedLinear
from dsv4.ops.quantize import quantize_weight_to_nvfp4, rmsnorm_quantize_nvfp4, dequantize_nvfp4
from dsv4.layers.linear import Nvfp4Linear
from dsv4.ops.quantize import (
rmsnorm_quantize_nvfp4, mhc_rmsnorm_quantize_nvfp4, dequantize_nvfp4,
quantize_to_nvfp4,
)
# RoPE (FP32)
from single_shot_inference import build_rope_cache
rope_cos, rope_sin = build_rope_cache(65536, rd, DEVICE, 10000., "yarn", 16., 4096, 32, 1)
# Build production linears, mHC, norms for test layers
from single_shot_inference import make_nvfp4_linear, get_nvfp4_weight, rmsnorm, unweighted_rmsnorm, _apply_rope
prod_lins = {}
attn_mhcs = {}
ffn_mhcs = {}
attn_norms = {}
ffn_norms = {}
compressors = {}
indexers = {}
kv_caches = {}
print("Loading weights...")
all_w = load_all_weights(CHECKPOINT_DIR)
TEST_LAYERS = 5
o_groups = cfg.get("o_groups", 16)
o_rank = cfg.get("o_lora_rank", 1024)
n_ih = cfg.get("index_n_heads", 64)
ihd = cfg.get("index_head_dim", 128)
itk = cfg.get("index_topk", 1024)
o_groups = cfg.get("o_groups", 16)
o_rank = cfg.get("o_lora_rank", 1024)
rope_caches = {g: build_rope_cache(65536, rd, f"cuda:{g}", 10000., "yarn", 16., 4096, 32, 1)
for g in range(NUM_GPUS)}
# Build all production components (same as single_shot main())
prod_lins, attn_mhcs, ffn_mhcs = {}, {}, {}
attn_norms, ffn_norms = {}, {}
compressors, indexers, kv_caches = {}, {}, {}
routers, moe_runners, se_runners = {}, {}, {}
for li in range(TEST_LAYERS):
dev = f"cuda:{li % NUM_GPUS}"
torch.cuda.set_device(li % NUM_GPUS)
gpu = li % NUM_GPUS
dev = f"cuda:{gpu}"
torch.cuda.set_device(gpu)
pfx = f"model.layers.{li}.self_attn"
mlp_pfx = f"model.layers.{li}.mlp"
ratio = cr[li] if li < len(cr) else 128
# Attention projections
# Attention linears
pl = {}
pl['q_a'] = make_nvfp4_linear(H, 1536, dev, all_w, pfx, 'q_a_proj')
pl['q_b'] = make_nvfp4_linear(1536, H * hd, dev, all_w, pfx, 'q_b_proj')
pl['kv'] = make_nvfp4_linear(H, hd, dev, all_w, pfx, 'kv_proj')
# Output projections
heads_per_group = n_h // o_groups
wo_a = Nvfp4GroupedLinear(
n_local_groups=o_groups, heads_per_group=heads_per_group,
hpg = n_h // o_groups
wo_a = Nvfp4GroupedLinear(n_local_groups=o_groups, heads_per_group=hpg,
head_dim=hd, o_lora_rank=o_rank, max_num_tokens=8192, device=dev)
oa_w, oa_ws, oa_ws2, oa_isc = get_nvfp4_weight(all_w, pfx, 'o_a_proj')
if oa_w is not None and oa_ws is not None:
wo_a.load_nvfp4_weight(oa_w.to(dev), oa_ws.to(dev),
oa_ws2.to(dev) if oa_ws2 is not None else None,
oa_isc.to(dev) if oa_isc is not None else None)
pl['o_a'] = wo_a
wo_a._use_runtime_gsa = True
oa_ws2.to(dev) if oa_ws2 is not None else None,
oa_isc.to(dev) if oa_isc is not None else None)
pl['o_a'] = wo_a; wo_a._use_runtime_gsa = True
pl['o_b'] = make_nvfp4_linear(o_groups * o_rank, H, dev, all_w, pfx, 'o_b_proj')
prod_lins[li] = pl
@@ -127,15 +117,12 @@ def main():
m = mHCLayer(hidden_dim=H, n_hc=4, t_max_sinkhorn=20, device=dev)
n = 4
m.load_weights(
W_pre=fn[0:n].to(dev, torch.float32),
W_post=fn[n:2*n].to(dev, torch.float32),
W_pre=fn[0:n].to(dev, torch.float32), W_post=fn[n:2*n].to(dev, torch.float32),
W_comb=fn[2*n:].to(dev, torch.float32),
S_pre=base[0:n].reshape(1, n).to(dev, torch.float32),
S_post=base[n:2*n].reshape(n, 1).to(dev, torch.float32),
S_comb=base[2*n:].reshape(n, n).to(dev, torch.float32),
alpha_pre=scale[0].item(), alpha_post=scale[1].item(),
alpha_comb=scale[2].item(),
)
alpha_pre=scale[0].item(), alpha_post=scale[1].item(), alpha_comb=scale[2].item())
blocks[li] = m
an_k = f"model.layers.{li}.input_layernorm.weight"
@@ -143,262 +130,195 @@ def main():
fn_k = f"model.layers.{li}.post_attention_layernorm.weight"
if fn_k in all_w: ffn_norms[li] = all_w[fn_k].to(dev, torch.float32)
# KV cache
ratio = cr[li] if li < len(cr) else 128
max_comp = (8192 + ratio - 1) // ratio if ratio > 0 else 0
from single_shot_inference import KVCache, Compressor, Indexer
kv_caches[li] = KVCache(hd, cfg.get("sliding_window", 128), max_comp=max_comp,
device=dev, indexer_key_dim=ihd, compress_ratio=ratio,
indexer_top_k=itk, rope_dim=rd)
if ratio > 0:
compressors[li] = Compressor(ratio, hd, H, dev)
if ratio == 4:
indexers[li] = Indexer(n_ih, ihd, itk, dev)
device=dev, indexer_key_dim=ihd, compress_ratio=ratio, indexer_top_k=itk, rope_dim=rd)
if ratio > 0: compressors[li] = Compressor(ratio, hd, H, dev)
if ratio == 4: indexers[li] = Indexer(n_ih, ihd, itk, dev)
# Router
is_hash = (li < cfg.get("num_hash_layers", 3)) and (f"{mlp_pfx}.gate.tid2eid" in all_w)
router = Router(hidden_size=H, num_experts=cfg["n_routed_experts"],
top_k=cfg.get("num_experts_per_tok", 6),
routed_scaling_factor=cfg.get("routed_scaling_factor", 2.5),
mode="hash" if is_hash else "dense",
vocab_size=cfg.get("vocab_size", 128000) if is_hash else None, device=dev)
if is_hash:
router.load_weights(hash_lut=all_w[f"{mlp_pfx}.gate.tid2eid"].to(dev, torch.int32))
else:
eb = all_w.get(f"{mlp_pfx}.gate.e_score_correction_bias")
gate_w, gate_ws, gate_ws2, gate_isc = get_nvfp4_weight(all_w, mlp_pfx, 'gate')
E = cfg["n_routed_experts"]
if gate_w is not None and gate_ws is not None:
gate_lin = Nvfp4Linear(in_features=H, out_features=E, device=dev)
gate_lin.fp4 = [gate_w.to(dev).view(torch.float4_e2m1fn_x2) if gate_w.dtype == torch.uint8 else gate_w.to(dev)]
gate_lin.sf = [gate_ws.to(dev)]
ws2_v = gate_ws2.float().item() if gate_ws2 is not None else 1.0
isc_v = gate_isc.float().item() if gate_isc is not None else 1.0/(6.0*448.0)
gate_lin.gs = [1.0]
gate_lin.ws2 = [torch.tensor([ws2_v], device=dev, dtype=torch.float32)]
gate_lin._activation_global_scale = isc_v
gate_lin._use_runtime_gsa = True
gate_lin.finalize_weights()
router.load_nvfp4_gate(gate_lin)
router.load_weights(e_bias=eb.to(dev, torch.float32))
router.finalize_weights(); routers[li] = router
moe = Nvfp4MoE(num_experts=cfg["n_routed_experts"], hidden_size=H,
intermediate_size=cfg.get("moe_intermediate_size", 3072),
top_k=cfg.get("num_experts_per_tok", 6), device=dev)
moe.set_swiglu_limit(cfg.get("swiglu_limit", 10.0)); moe.set_fused_swiglu(True)
_load_moe_weights_stacked(all_w, li, mlp_pfx, dev, moe, cfg)
moe._ensure_stacked(); moe._use_runtime_gsa = True; moe_runners[li] = moe
se = Nvfp4SharedExpert(hidden_size=H, intermediate_size=cfg.get("moe_intermediate_size", 3072),
device=dev, swiglu_limit=cfg.get("swiglu_limit", 10.0))
se.set_fused_swiglu(True)
_load_shared_expert_weights(all_w, li, mlp_pfx, dev, se, cfg)
se._ensure_initialized(); se._use_runtime_gsa = True; se_runners[li] = se
torch.cuda.empty_cache()
# Load compressor/indexer weights
for li in range(TEST_LAYERS):
pfx = f"model.layers.{li}.self_attn.compressor"
dev = f"cuda:{li % NUM_GPUS}"
if li in compressors: compressors[li].load(all_w, pfx, dev=dev)
if li in indexers: indexers[li].load(all_w, f"{pfx}.indexer", dev=dev)
print(" Components built for layers 0-4")
print("Components built")
# ---- Run test: one token through each layer ----
# Embedding + tokenizer
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained(CHECKPOINT_DIR)
bos = tokenizer.bos_token_id or 0
USER_TOKEN, ASSISTANT_TOKEN = 128803, 128804
THINK_START = 128821
USER_TOKEN, ASSISTANT_TOKEN, THINK_START = 128803, 128804, 128821
input_ids = [bos, USER_TOKEN]
input_ids += tokenizer.encode('\n\nThe capital of France is', add_special_tokens=False)
input_ids.append(ASSISTANT_TOKEN)
input_ids.append(THINK_START)
input_ids.append(ASSISTANT_TOKEN); input_ids.append(THINK_START)
print(f"Input: {len(input_ids)} tokens")
# Embedding
torch.cuda.set_device(0)
embed_w = all_w.get("model.embed_tokens.weight")
embed = torch.nn.Embedding.from_pretrained(embed_w.bfloat16().to(DEVICE))
devs_list = [f"cuda:{g}" for g in range(NUM_GPUS)]
layer_w = _cache_layer_weights_no_experts(all_w, TEST_LAYERS, devs_list)
del all_w; import gc; gc.collect()
for g in range(NUM_GPUS): torch.cuda.set_device(g); torch.cuda.empty_cache()
torch.cuda.set_device(0)
# Prefill one token at a time through all test layers
# We compare the FMHA output at each layer for the LAST prefill token
print(f"\nPrefilling {len(input_ids)} tokens through {TEST_LAYERS} layers...")
results = {}
# ================================================================
# PHASE 1: Run full production pipeline to populate KV caches
# ================================================================
print(f"\nPhase 1: Populating KV caches...")
for pi, tid_val in enumerate(input_ids):
tid = torch.tensor([[tid_val]], dtype=torch.long, device=DEVICE)
t1 = time.time()
tid = torch.tensor([tid_val], dtype=torch.long, device=DEVICE)
pos = torch.tensor([pi], dtype=torch.long, device=DEVICE)
tid32 = torch.tensor([tid_val], dtype=torch.int32, device=DEVICE)
X = mHCLayer.init_state(embed(tid))
for li in range(TEST_LAYERS):
gpu = li % NUM_GPUS
if X.device != torch.device(f"cuda:{gpu}"):
X = X.to(f"cuda:{gpu}")
if X.device != torch.device(f"cuda:{gpu}"): X = X.to(f"cuda:{gpu}")
torch.cuda.set_device(gpu)
X = forward_layer(X, layer_w[li], li, cfg, *rope_caches[gpu],
attn_mhcs.get(li), ffn_mhcs.get(li), attn_norms.get(li), ffn_norms.get(li),
kv_caches[li], pos, tid32, compressors.get(li), indexers.get(li),
moe_runners.get(li), se_runners.get(li), routers.get(li),
prod_lin=prod_lins.get(li), _use_fused_rmsnorm_quantize=True)
if pi % 5 == 0:
print(f" Token {pi}/{len(input_ids)}: {time.time()-t1:.2f}s", flush=True)
ratio = cr[li] if li < len(cr) else 128
pfx = f"model.layers.{li}.self_attn"
dev = f"cuda:{gpu}"
# ================================================================
# PHASE 2: For each layer, gather KV, run production FMHA, compare vs SDPA
# ================================================================
print(f"\nPhase 2: FMHA comparison per layer...")
results = {}
# --- mHC pre_block + RMSNorm (production path) ---
attn_mhc = attn_mhcs.get(li)
ffn_mhc = ffn_mhcs.get(li)
attn_norm_w = attn_norms.get(li)
ffn_norm_w = ffn_norms.get(li)
pl = prod_lins.get(li)
for li in range(TEST_LAYERS):
gpu = li % NUM_GPUS
dev = f"cuda:{gpu}"
torch.cuda.set_device(gpu)
ratio = cr[li] if li < len(cr) else 128
k_cache = kv_caches[li]
A_l_a, B_l_a, C_l_a = attn_mhc._dynamic_params(X)
from dsv4.layers.mhc import mHCContext
ctx_a = mHCContext(B_l=B_l_a, C_l=C_l_a)
from dsv4.ops.quantize import mhc_rmsnorm_quantize_nvfp4, dequantize_nvfp4
x_quant_attn = mhc_rmsnorm_quantize_nvfp4(
X, A_l_a, attn_norm_w.to(X.device, torch.float32))
x_normed = dequantize_nvfp4(x_quant_attn.x_fp4, x_quant_attn.x_sf, x_quant_attn.gsa)
# --- Attention forward (same as single_shot) ---
# Only do FMHA comparison on the last token
is_last_token = (pi == len(input_ids) - 1)
# 1. Q
q_a = pl['q_a'].run_from_quantized(x_quant_attn)
q_norm_w = all_w.get(f"{pfx}.q_a_norm.weight")
if q_norm_w is not None:
q_a_quant = rmsnorm_quantize_nvfp4(q_a, q_norm_w.to(dev, torch.float32))
q_a_dequant = dequantize_nvfp4(q_a_quant.x_fp4, q_a_quant.x_sf, q_a_quant.gsa)
q = pl['q_b'].run_from_quantized(q_a_quant)
# Gather KV in mixed format (same as production path)
if k_cache.n_comp > 0:
if ratio > 4:
kv_nope_fp8, kv_nope_scale, kv_rope_bf16 = k_cache.gather_mixed_all()
else:
q = pl['q_b'](q_a)
q = unweighted_rmsnorm(q).bfloat16()
q_heads = q.reshape(1, n_h, hd)
q_heads = _apply_rope(q_heads, pos, rope_cos, rope_sin, rd)
kv_nope_fp8, kv_nope_scale, kv_rope_bf16 = k_cache.gather_mixed_swa_only()
else:
kv_nope_fp8, kv_nope_scale, kv_rope_bf16 = k_cache.gather_mixed_swa_only()
# 2. KV
kv = pl['kv'].run_from_quantized(x_quant_attn)
kv_norm_w = all_w.get(f"{pfx}.kv_norm.weight")
if kv_norm_w is not None:
kv = rmsnorm(kv, kv_norm_w.to(dev, torch.float32))
kv_3d = kv.reshape(1, 1, hd)
kv_3d = _apply_rope(kv_3d, pos, rope_cos, rope_sin, rd)
kv_roped = kv_3d.reshape(1, hd)
kv_cache[li].append_swa(kv_roped, pos)
seq_len = kv_nope_scale.shape[0]
if seq_len == 0:
print(f" L{li}: SKIPPED (seq_len=0)")
continue
# 3. Compressor
comp_pos = None
if li in compressors and compressors[li].ratio > 0:
comp_kv_fp32, comp_pos, block_bias = compressors[li].forward(x_normed, pos)
if comp_kv_fp32 is not None:
from dsv4.kernels.cuda.loader import get_cuda_module
kv_mod = get_cuda_module("kv_quantize", ["kv_quantize.cu"])
nope_fp32 = comp_kv_fp32[:, :nope_dim].contiguous()
rope_bf16 = comp_kv_fp32[:, nope_dim:].bfloat16().contiguous()
rope_3d = rope_bf16.unsqueeze(1)
rope_3d = _apply_rope(rope_3d, comp_pos, rope_cos, rope_sin, rd)
rope_bf16 = rope_3d.squeeze(1)
nope_fp8, nope_scale = kv_mod.quantize_fp8_e4m3_from_fp32(nope_fp32)
kv_cache[li].set_compressed_mixed(nope_fp8, nope_scale, rope_bf16, comp_pos)
if compressors[li].is_csa and li in indexers and indexers[li].compressor is not None:
comp_idx_kv, _, _ = indexers[li].compressor.forward(x_normed, pos)
kv_cache[li].set_indexer_keys_fp8(comp_idx_kv)
# Generate a test Q (random, on this GPU)
q_bf16 = torch.randn(1, n_h, 1, hd, dtype=torch.bfloat16, device=dev) * 0.5
# 4. Indexer
topk_idx = None
if li in indexers and ratio == 4:
topk_idx = indexers[li].forward(q_a, x_normed, kv_cache[li], pos, layer_idx=li)
# 1. Run production mixed FP8 FMHA
from dsv4.kernels.attention.fmha_mixed_fp8_op import fmha_mixed_fp8_decode_raw
scale_val = 1.0 / math.sqrt(hd)
try:
o_prod, lse_prod = fmha_mixed_fp8_decode_raw(
q_bf16, kv_nope_fp8, kv_nope_scale, kv_rope_bf16, scale_val, rope_dim=rd)
except Exception as e:
print(f" L{li}: PROD FMHA FAILED: {e}")
results[li] = {'cos': -1.0, 'error': str(e)}
continue
# 5. Gather KV — B1 mixed path
swa_kv, _swa_pos = kv_cache[li].get_swa()
swa_len = swa_kv.shape[0]
if kv_cache[li].n_comp > 0:
if ratio == 4:
assert topk_idx is not None
tk = topk_idx[0].clamp(0, kv_cache[li].n_comp - 1).int()
kv_nope_fp8, kv_nope_scale, kv_rope_bf16 = kv_cache[li].gather_mixed_selective(tk)
elif ratio > 4:
kv_nope_fp8, kv_nope_scale, kv_rope_bf16 = kv_cache[li].gather_mixed_all()
else:
kv_nope_fp8, kv_nope_scale, kv_rope_bf16 = kv_cache[li].gather_mixed_swa_only()
else:
kv_nope_fp8, kv_nope_scale, kv_rope_bf16 = kv_cache[li].gather_mixed_swa_only()
# 2. Reference: dequantize KV, run SDPA
nope_dequant = kv_nope_fp8.view(torch.float8_e4m3fn).float() * kv_nope_scale.unsqueeze(-1).float()
kv_full = torch.cat([nope_dequant.bfloat16(), kv_rope_bf16], dim=-1) # (N, hd)
k_4d = kv_full.unsqueeze(0).unsqueeze(0).expand(1, 1, -1, -1) # (1, 1, N, hd)
v_4d = k_4d.clone()
o_ref = F.scaled_dot_product_attention(q_bf16, k_4d, v_4d, scale=scale_val) # (1, H, 1, hd)
seq_len = kv_nope_scale.shape[0]
# 3. Compare
cos_val = cosine(o_prod, o_ref)
mag_prod = o_prod.float().abs().max().item()
mag_ref = o_ref.float().abs().max().item()
# 6. Production FMHA
if is_last_token and seq_len > 0:
scale = 1.0 / math.sqrt(hd)
from dsv4.kernels.attention.production import dsv4_attention_mixed_fp8_decode
q_perm = q_heads.permute(1, 0, 2).contiguous()
# Per-head cosine
o_prod_h = o_prod.float().squeeze(2) # (1, H, hd) → (H, hd) after squeeze
o_ref_h = o_ref.float().squeeze(2)
if o_prod_h.dim() == 3: o_prod_h = o_prod_h.squeeze(0)
if o_ref_h.dim() == 3: o_ref_h = o_ref_h.squeeze(0)
per_head_cos = F.cosine_similarity(o_prod_h, o_ref_h, dim=-1)
min_head = per_head_cos.min().item()
mean_head = per_head_cos.mean().item()
sinks = all_w.get(f"{pfx}.sinks")
sink_bias = None
if sinks is not None:
sink_bias = sinks.to(device=dev).float().reshape(n_h)
results[li] = {
'cos': cos_val, 'mag_prod': mag_prod, 'mag_ref': mag_ref,
'seq_len': seq_len, 'ratio': ratio,
'min_head_cos': min_head, 'mean_head_cos': mean_head,
}
status = "PASS" if cos_val >= 0.999 else "FAIL"
print(f" L{li}: {status} cos={cos_val:.6f} min_head={min_head:.6f} mean_head={mean_head:.6f} "
f"|prod|={mag_prod:.4f} |ref|={mag_ref:.4f} seq_len={seq_len} ratio={ratio}")
try:
attn_out = dsv4_attention_mixed_fp8_decode(
q=q_perm, k_nope_fp8=kv_nope_fp8,
k_nope_scale=kv_nope_scale, k_rope_bf16=kv_rope_bf16,
scale=scale, sink_bias=sink_bias, rope_dim=rd)
attn_out = attn_out.permute(1, 0, 2)
if cos_val < 0.999:
worst = per_head_cos.argsort()[:5]
print(f" Worst heads: {worst.tolist()} cos={[f'{c:.4f}' for c in per_head_cos[worst].tolist()]}")
# ---- REFERENCE: dequantize all KV to BF16, run SDPA ----
# Dequantize noPE from FP8
nope_dequant = kv_nope_fp8.view(torch.float8_e4m3fn).float() * kv_nope_scale.unsqueeze(-1).float()
# Concat noPE + RoPE
kv_full = torch.cat([nope_dequant.bfloat16(), kv_rope_bf16], dim=-1) # (N, hd)
# Run SDPA reference
q_4d = q_perm.unsqueeze(0) # (1, H, 1, hd)
k_4d = kv_full.unsqueeze(0).unsqueeze(0).expand(1, 1, -1, -1) # (1, 1, N, hd)
v_4d = k_4d.clone()
# If sink_bias, add it as an extra logit
if sink_bias is not None:
# SDPA with manual sink: compute scores, add sink bias, softmax, multiply by V
q_f = q_4d.float()
k_f = k_4d.float()
v_f = v_4d.float()
scores = torch.matmul(q_f, k_f.transpose(-2, -1)) * scale # (1, H, 1, N)
# Add sink as denominator-only logit (not an extra KV position)
# For reference: just do regular SDPA without sink (sink effect is small)
o_ref = F.scaled_dot_product_attention(q_4d.bfloat16(), k_4d, v_4d, scale=scale)
else:
o_ref = F.scaled_dot_product_attention(q_4d, k_4d, v_4d, scale=scale)
o_ref = o_ref.squeeze(0).permute(1, 0, 2) # (1, H, hd) → (T=1, H, hd)
cos_fmha = cosine(attn_out, o_ref)
mag_prod = attn_out.float().abs().max().item()
mag_ref = o_ref.float().abs().max().item()
results[li] = {
'cos_fmha': cos_fmha,
'mag_prod': mag_prod,
'mag_ref': mag_ref,
'seq_len': seq_len,
'swa_len': swa_len,
'n_comp': kv_cache[li].n_comp,
'ratio': ratio,
}
status = "PASS" if cos_fmha >= 0.999 else "FAIL"
print(f" L{li}: {status} cos_fmha={cos_fmha:.6f} |prod|={mag_prod:.4f} |ref|={mag_ref:.4f} "
f"seq_len={seq_len} swa={swa_len} n_comp={kv_cache[li].n_comp} ratio={ratio}",
flush=True)
# If cosine is bad, print per-head details
if cos_fmha < 0.999:
o_prod_h = attn_out.float().squeeze(0) # (H, hd)
o_ref_h = o_ref.float().squeeze(0)
per_head_cos = F.cosine_similarity(o_prod_h, o_ref_h, dim=-1)
worst = per_head_cos.argsort()[:5]
print(f" Worst heads: {worst.tolist()} cos={per_head_cos[worst].tolist()}")
print(f" Per-head cos: min={per_head_cos.min().item():.6f} "
f"mean={per_head_cos.mean().item():.6f} max={per_head_cos.max().item():.6f}")
# Print actual value samples
print(f" Prod[0,0:8]={attn_out[0,0,:8].float().tolist()}")
print(f" Ref[0,0:8]={o_ref[0,0,:8].float().tolist()}")
except Exception as e:
print(f" L{li}: FMHA FAILED: {e}", flush=True)
results[li] = {'cos_fmha': -1.0, 'error': str(e)}
# 7. Inverse RoPE
attn_out = _apply_rope(attn_out, pos, rope_cos, rope_sin, rd, inverse=True)
# 8. Output projection
wo_a_lin = pl.get('o_a')
if wo_a_lin is not None:
g_3d = wo_a_lin.run(attn_out)
g_flat = g_3d.reshape(1, -1)
F_attn = pl['o_b'](g_flat)
else:
F_attn = torch.zeros(1, H, dtype=torch.bfloat16, device=dev)
# 9. mHC post_block
X_mid = attn_mhc.post_block(X, F_attn, ctx_a)
# FFN path (skip for this test — we only test attention)
# Move to next layer
X = X_mid
# ---- Summary ----
# Summary
print("\n" + "=" * 70)
print("SUMMARY")
print("=" * 70)
all_pass = True
for li in sorted(results.keys()):
r = results[li]
cos = r.get('cos_fmha', -1.0)
status = "PASS" if cos >= 0.999 else "FAIL"
if cos < 0.999: all_pass = False
print(f" L{li}: {status} cos={cos:.6f} seq_len={r.get('seq_len','?')} "
f"|prod|={r.get('mag_prod','?'):.4f} |ref|={r.get('mag_ref','?'):.4f}")
c = r.get('cos', -1.0)
status = "PASS" if c >= 0.999 else "FAIL"
if c < 0.999: all_pass = False
print(f" L{li}: {status} cos={c:.6f} seq={r.get('seq_len','?')} ratio={r.get('ratio','?')}")
print()
if all_pass:
print("ALL FMHA LAYER COMPARISONS PASSED (cos >= 0.999)")
print("ALL PASSED (cos >= 0.999)")
else:
print("SOME FMHA LAYER COMPARISONS FAILED — investigate per-layer output above")
print("SOME FAILED — see per-layer output above")
return 0 if all_pass else 1