#!/usr/bin/env python3 """Test fused mHC pre_block + RMSNorm + NVFP4 quantize kernel. Validates the fused kernel against the reference (unfused) path: Reference: mHC.pre_block(X_l) → rmsnorm(x_in, weight) → quantize_nvfp4_gpu_fused(x_normed) Fused: mhc_rmsnorm_quantize_nvfp4(X_l, A_l, weight) → QuantizedActivation Tests at production shapes: (1, 4, 7168) for decode, (8, 4, 7168) for prefill. """ import torch import math def rmsnorm_ref(x, weight, eps=1e-6): """PyTorch reference RMSNorm.""" xf = x.float() rms = xf.pow(2).mean(dim=-1, keepdim=True).add(eps).rsqrt() return xf * rms * weight.float() def main(): from dsv4.ops.quantize import quantize_nvfp4_gpu_fused, mhc_rmsnorm_quantize_nvfp4, dequantize_nvfp4 device = "cuda" torch.manual_seed(42) DIVISOR = 6.0 * 448.0 EPS = 1e-6 N_HC = 4 HIDDEN = 7168 test_configs = [ (1, N_HC, HIDDEN, "decode T=1"), (8, N_HC, HIDDEN, "prefill T=8"), (128, N_HC, HIDDEN, "prefill T=128"), ] all_pass = True for M, n_hc, N, label in test_configs: print(f"\n=== Test: {label} (M={M}, n_hc={n_hc}, N={N}) ===") # Create X_l and A_l (mHC pre_block inputs) X_l = torch.randn(M, n_hc, N, dtype=torch.bfloat16, device=device) A_l = torch.softmax(torch.randn(M, n_hc, dtype=torch.bfloat16, device=device), dim=-1) norm_weight = torch.randn(N, dtype=torch.float32, device=device) * 0.1 + 1.0 # Reference path: unfused x_in = torch.bmm(A_l.unsqueeze(1).float(), X_l.float()).squeeze(1) # (M, N) FP32 x_normed = rmsnorm_ref(x_in.bfloat16(), norm_weight, EPS) x_fp4_ref, x_sf_ref, gsa_ref = quantize_nvfp4_gpu_fused(x_normed.bfloat16(), DIVISOR) # Fused path quant = mhc_rmsnorm_quantize_nvfp4(X_l, A_l, norm_weight, EPS, DIVISOR) # Validate shapes assert quant.x_fp4.shape == (M, N // 2), f"fp4 shape: {quant.x_fp4.shape}" assert quant.x_sf.shape == (M, N // 16), f"sf shape: {quant.x_sf.shape}" assert quant.gsa.shape == (M,), f"gsa shape: {quant.gsa.shape}" # Dequantize and compare dq_fused = dequantize_nvfp4(quant.x_fp4, quant.x_sf, quant.gsa) dq_ref = dequantize_nvfp4(x_fp4_ref, x_sf_ref, gsa_ref) # Cosine similarity cos = torch.nn.functional.cosine_similarity( dq_fused.float().flatten().unsqueeze(0), dq_ref.float().flatten().unsqueeze(0) ).item() print(f" Dequant cosine (fused vs unfused): {cos:.6f}") # Against true mHC + RMSNorm output cos_vs_ref = torch.nn.functional.cosine_similarity( dq_fused.float().flatten().unsqueeze(0), x_normed.flatten().unsqueeze(0) ).item() print(f" vs true mHC+RMSNorm: {cos_vs_ref:.6f}") if cos < 0.995: print(f" FAIL: dequant cosine too low ({cos:.6f})") all_pass = False if cos_vs_ref < 0.990: print(f" FAIL: vs true mHC+RMSNorm cosine too low ({cos_vs_ref:.6f})") all_pass = False # gsa per-row validation max_gsa_diff = 0.0 for row in range(min(M, 4)): x_in_row = (A_l[row].unsqueeze(0).float() @ X_l[row].float()).squeeze(0) # (N,) x_normed_row = rmsnorm_ref(x_in_row.unsqueeze(0).bfloat16(), norm_weight, EPS) ref_amax = x_normed_row.abs().max().item() ref_gsa = max(ref_amax, 1e-8) / DIVISOR diff = abs(quant.gsa[row].item() - ref_gsa) max_gsa_diff = max(max_gsa_diff, diff) print(f" gsa per-row diff: {max_gsa_diff:.2e}") if max_gsa_diff > 0.5: print(f" FAIL: gsa diff too large") all_pass = False print(f"\n{'='*60}") print(f"{'ALL TESTS PASSED' if all_pass else 'SOME TESTS FAILED'}") return 0 if all_pass else 1 if __name__ == "__main__": exit(main())