Restructure: cutedsl/ -> dsv4/ with proper layering

- Split bridge.py -> ops/quantize.py, ops/layouts.py, ops/gemm_runner.py
- Renamed classes: CuTeDSLNvfp4Linear -> Nvfp4Linear, etc.
- Moved kernel code to dsv4/kernels/ (gemm, attention, compressor, decode, cuda)
- Moved PyTorch bridges to dsv4/ops/
- Moved nn.Module layers to dsv4layers/
- Moved reference implementations to dsv4/reference/
- Moved vendored CUTLASS code to vendored/
- Archived ~190 debug tests to tests/archive/
- Kept ~15 canonical tests in tests/unit/
- Updated all import paths
- Added stubs for future components (model/, cache/, loader/)
- Updated pyproject.toml: dsv4-inference package name
This commit is contained in:
2026-05-21 17:30:44 +00:00
parent 99e143dd0e
commit 3fb3c925af
274 changed files with 715 additions and 556 deletions

View File

@@ -6,7 +6,7 @@ sys.path.insert(0, '/root/nvfp4-megamoe-kernel/cutedsl')
sys.path.insert(0, '/root/nvfp4-megamoe-kernel/vllm')
from cutedsl.reference.moe_pipeline import moe_pipeline
from vllm.nvfp4_cutedsl import CuTeDSLMoERunner
from vllm.nvfp4_cutedsl import Nvfp4MoE
torch.cuda.set_device(0)
@@ -33,7 +33,7 @@ ref_out = moe_pipeline(
print(f"Reference output: amax={ref_out.amax().item():.4f} mean={ref_out.mean().item():.4f}")
# Run runner with warmup gs
runner = CuTeDSLMoERunner(
runner = Nvfp4MoE(
num_experts=3, hidden_size=256, intermediate_size=512,
max_num_tokens=4, top_k=2, device='cuda'
)

View File

@@ -51,14 +51,14 @@ def dequant_nvfp4(packed_uint8, scale_e4m3, global_scale):
def test_projection(name, weight, weight_sf, weight_gs, hidden_states, in_features, out_features):
"""Test a single NVFP4 projection."""
sys.path.insert(0, "/root/nvfp4-megamoe-kernel")
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
# Convert weight to CuTeDSL format: (out, in_packed) uint8 → (in_packed, out) float4
fp4 = [weight.view(torch.float4_e2m1fn_x2).permute(1, 0).contiguous()]
sf = [weight_sf.permute(1, 0).contiguous()]
gs = [weight_gs]
runner = CuTeDSLNvfp4Linear(
runner = Nvfp4Linear(
in_features=in_features,
out_features=out_features,
max_num_tokens=8192,

View File

@@ -55,7 +55,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -66,7 +66,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r

View File

@@ -50,7 +50,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -61,7 +61,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -82,7 +82,7 @@ def build_cos_sin(max_pos=4096, rope_dim=ROPE):
def test_blackwell_attention(layer_id, compress_ratio):
"""Test the full blackwell attention pipeline for a specific layer."""
from cutedsl.blackwell_attention import (
from dsv4.reference.attention import (
apply_gptj_rope, apply_inv_gptj_rope,
blackwell_attention_forward,
kv_quantize_fp8, kv_dequantize_fp8,

View File

@@ -52,7 +52,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -63,7 +63,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -169,7 +169,7 @@ def main():
# For this test, we use kv_n directly as the KV for attention
# ── Step 6: FULL ATTENTION (PyTorch SDPA, works on Blackwell) ──
from cutedsl.csa_attention import full_attention_reference
from dsv4.reference.csa_attention import full_attention_reference
o_attn = full_attention_reference(q_rope, kv_n, scale=SCALE)
print(f" Attention output: amax={o_attn.amax():.4f} NaN={torch.isnan(o_attn).any()}")

View File

@@ -42,7 +42,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -53,7 +53,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r

View File

@@ -61,7 +61,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -72,7 +72,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r

View File

@@ -24,7 +24,7 @@ from vllm.model_executor.layers.csa_attention import (
apply_gptj_rope,
apply_inv_gptj_rope,
)
from cutedsl.native_swa_decode import native_swa_decode_attention
from dsv4.ops.decode_swa import native_swa_decode_attention
torch.manual_seed(42)
torch.cuda.set_device(0)

View File

@@ -43,7 +43,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -54,7 +54,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r

View File

@@ -45,7 +45,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -56,7 +56,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r

View File

@@ -57,7 +57,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -68,7 +68,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -195,7 +195,7 @@ def main():
# ── Shared expert ─────────────────────────────────────────────────
print("\n--- Shared Expert: CuTeDSL vs BF16 ---")
from cutedsl.shared_expert_pipeline import CuTeDSLSharedExpertRunner
from dsv4.layers.shared_expert import Nvfp4SharedExpert
sgw = G(f"{m}.shared_experts.gate_proj.weight"); sgsf = G(f"{m}.shared_experts.gate_proj.weight_scale")
sggs = G(f"{m}.shared_experts.gate_proj.weight_scale_2").item()
@@ -211,7 +211,7 @@ def main():
s32 = sgu_sf.float(); s32[:si] *= sggs/smgs; s32[si:] *= sugs/smgs
sgu_sf = s32.to(torch.float8_e4m3fn)
ser = CuTeDSLSharedExpertRunner(hidden_size=H, intermediate_size=si, max_num_tokens=8192,
ser = Nvfp4SharedExpert(hidden_size=H, intermediate_size=si, max_num_tokens=8192,
device=DEV, swiglu_limit=SL)
ser.l1_fp4 = [sgu_w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()]
ser.l1_sf = [sgu_sf.permute(1,0).contiguous()]; ser.l1_gs = [smgs]

View File

@@ -39,12 +39,12 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -102,7 +102,7 @@ def causal_prefill_attention(q, kv, scale):
def test_full_layer(layer_id, num_tokens=8, num_moe_experts=16):
"""Test a complete transformer layer with attention + MoE."""
from cutedsl.runner import CuTeDSLMoERunner
from dsv4.layers.moe import Nvfp4MoE
torch.cuda.set_device(0)
torch.manual_seed(42)
@@ -162,7 +162,7 @@ def test_full_layer(layer_id, num_tokens=8, num_moe_experts=16):
# Free per-expert lists
del gate_ws, gate_sfs, gate_gss, up_ws, up_sfs, up_gss, down_ws, down_sfs, down_gss
moe_runner = CuTeDSLMoERunner(
moe_runner = Nvfp4MoE(
num_experts=num_moe_experts,
hidden_size=H,
intermediate_size=INTERMEDIATE,

View File

@@ -56,7 +56,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -67,7 +67,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -124,7 +124,7 @@ def bf16_causal_attention(q, kv, scale):
def make_moe_runner(layer_id, wm, model_path):
"""Create CuTeDSL MoE runner for a layer."""
from cutedsl.runner import CuTeDSLMoERunner
from dsv4.layers.moe import Nvfp4MoE
p = f"model.layers.{layer_id}.mlp"
G = lambda k: P(k, wm, model_path).to(DEV)
@@ -147,7 +147,7 @@ def make_moe_runner(layer_id, wm, model_path):
l2_sf = w2_sf.to(torch.float8_e4m3fn).permute(1,0).contiguous() if w2_sf.dtype != torch.float8_e4m3fn else w2_sf.permute(1,0).contiguous()
intermediate_size = 3072 # per expert
runner = CuTeDSLMoERunner(
runner = Nvfp4MoE(
num_experts=NUM_EXPERTS,
hidden_size=H,
intermediate_size=intermediate_size,

View File

@@ -62,7 +62,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -73,7 +73,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -135,7 +135,9 @@ def kv_quantize_nvfp4(kv_bf16):
kv_bf16: (T, HD) BF16
Returns: (T, HD//2) fp4, (T, HD//16) sf, scalar gs
"""
from cutedsl.bridge import quantize_to_nvfp4
from dsv4.ops.quantize import (
quantize_to_nvfp4,
)
return quantize_to_nvfp4(kv_bf16)

View File

@@ -50,7 +50,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -61,7 +61,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -80,15 +80,17 @@ def main():
# ── INSPECT: How does CuTeDSL runner.run() use gs? ────────────────
print("\n--- INSPECTING CuTeDSL runner internals ---")
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from cutedsl.bridge import quantize_activation_nvfp4
from dsv4.layers.linear import Nvfp4Linear
from dsv4.ops.quantize import (
quantize_activation_nvfp4,
)
print("\n quantize_activation_nvfp4 signature:")
sig = inspect.signature(quantize_activation_nvfp4)
print(f" {sig}")
print("\n CuTeDSLNvfp4Linear._run_impl source (key lines):")
src = inspect.getsource(CuTeDSLNvfp4Linear._run_impl)
print("\n Nvfp4Linear._run_impl source (key lines):")
src = inspect.getsource(Nvfp4Linear._run_impl)
for i, line in enumerate(src.split('\n')):
stripped = line.strip()
if any(kw in stripped for kw in ['global_scale', '_activation', 'quantize', 'return', 'def ']):

View File

@@ -47,12 +47,12 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r

View File

@@ -2,7 +2,7 @@
"""
DeepSeek-V4 MoE Runner NaN Test
Tests the CuTeDSLMoERunner (grouped GEMM path) with real weights.
Tests the Nvfp4MoE (grouped GEMM path) with real weights.
The single-expert tests pass this test exercises the FULL MoE runner
with routing, padding, grouped GEMM, and combine.
@@ -39,7 +39,7 @@ def rms(x, w, eps=1e-6):
def pack_expert_weights(wm, G, layer_id=2, num_local_experts=16):
"""Pack per-expert weights into stacked format for CuTeDSLMoERunner.
"""Pack per-expert weights into stacked format for Nvfp4MoE.
Only loads the first num_local_experts to fit in memory.
"""
m = f"model.layers.{layer_id}.mlp"
@@ -77,7 +77,7 @@ def pack_expert_weights(wm, G, layer_id=2, num_local_experts=16):
# Actually w13 = stacked gate+up, w2 = down
# But our runner expects separate L1 (gate+up) and L2 (down)
# The w13 format is (E, 2*intermediate, hidden//2) with gate and up interleaved
# For CuTeDSLMoERunner, we stack gate and up side-by-side
# For Nvfp4MoE, we stack gate and up side-by-side
# Stack gate and up into w13 format: (E, 2*intermediate, hidden//2)
w13_w = torch.cat([torch.stack(gate_ws), torch.stack(up_ws)], dim=1) # (E, 6144, 3584)
@@ -92,8 +92,8 @@ def pack_expert_weights(wm, G, layer_id=2, num_local_experts=16):
def test_moe_runner(layer_id=2):
"""Test the CuTeDSLMoERunner with real weights."""
from cutedsl.runner import CuTeDSLMoERunner
"""Test the Nvfp4MoE with real weights."""
from dsv4.layers.moe import Nvfp4MoE
torch.cuda.set_device(0)
torch.manual_seed(42)
@@ -118,7 +118,7 @@ def test_moe_runner(layer_id=2):
intermediate_size = INTERMEDIATE # 3072
hidden_size = H # 7168
runner = CuTeDSLMoERunner(
runner = Nvfp4MoE(
num_experts=num_local_experts,
hidden_size=hidden_size,
intermediate_size=intermediate_size,
@@ -178,7 +178,7 @@ def test_moe_runner(layer_id=2):
def main():
print("=" * 70)
print(" DeepSeek-V4 MoE Runner NaN Test")
print(" Tests CuTeDSLMoERunner (grouped GEMM) with real weights")
print(" Tests Nvfp4MoE (grouped GEMM) with real weights")
print("=" * 70)
test_moe_runner(layer_id=2)

View File

@@ -58,8 +58,11 @@ def main():
topk_weights = torch.ones(NUM_TOKENS, TOP_K, dtype=torch.float32, device=DEVICE) / TOP_K
# Setup runner
from vllm.nvfp4_cutedsl import CuTeDSLMoERunner
from cutedsl.bridge import assemble_scales_3d_side, make_b_k_major
from vllm.nvfp4_cutedsl import Nvfp4MoE
from dsv4.ops.layouts import (
assemble_scales_3d_side,
make_b_k_major,
)
l1_fp4, l1_sf, l1_gs_list = [], [], []
l2_fp4, l2_sf, l2_gs_list = [], [], []
@@ -91,7 +94,7 @@ def main():
l2_sf.append(torch.ones(INTERMEDIATE_SIZE//16, HIDDEN_SIZE, dtype=torch.float8_e4m3fn, device=DEVICE))
l2_gs_list.append(1.0)
runner = CuTeDSLMoERunner(
runner = Nvfp4MoE(
num_experts=NUM_EXPERTS, hidden_size=HIDDEN_SIZE,
intermediate_size=INTERMEDIATE_SIZE, max_num_tokens=NUM_TOKENS,
top_k=TOP_K, device=DEVICE,

View File

@@ -48,7 +48,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -59,7 +59,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -108,8 +108,11 @@ def nvfp4_qk_attention(q, kv, scale):
This is a standard GEMM that CuTeDSL can handle.
We quantize Q as the "activation" and K^T as the "weight".
"""
from cutedsl.bridge import quantize_to_nvfp4, quantize_activation_nvfp4
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.ops.quantize import (
quantize_to_nvfp4,
quantize_activation_nvfp4,
)
from dsv4.layers.linear import Nvfp4Linear
T, NH, HD = q.shape
device = q.device
@@ -123,7 +126,7 @@ def nvfp4_qk_attention(q, kv, scale):
kv_T = kv.T.contiguous() # (HD, T)
w_fp4, w_sf, w_gs = quantize_to_nvfp4(kv_T) # (HD//2, T), (HD//16, T), scalar
# Use CuTeDSLNvfp4Linear runner for Q×K^T GEMM
# Use Nvfp4Linear runner for Q×K^T GEMM
# in_features=HD, out_features=T
# Q is "activation" side, K^T is "weight" side
M = T * NH
@@ -131,7 +134,7 @@ def nvfp4_qk_attention(q, kv, scale):
N = T
# Create runner for this specific (M, K, N) combination
runner = CuTeDSLNvfp4Linear(
runner = Nvfp4Linear(
in_features=K, out_features=N, max_num_tokens=M, device=str(device)
)

View File

@@ -64,7 +64,7 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
@@ -75,7 +75,7 @@ def make_runner(w, sf, gs_t, inf, outf, fused=False, lw=None):
s32[:sp] *= g1/gs; s32[sp:] *= g2/gs; s = s32.to(torch.float8_e4m3fn)
else:
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -163,7 +163,7 @@ class NVFP4Attention:
Returns:
(T, NH, HD) attention output
"""
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
T, NH, HD = q_bf16.shape
device = q_bf16.device
@@ -175,13 +175,13 @@ class NVFP4Attention:
# Q is "activation" (T*NH, HD), K^T is "weight" (T, HD)
# GEMM: (T*NH, HD) × (HD, T) → (T*NH, T)
#
# We use CuTeDSLNvfp4Linear with in_features=HD, out_features=T
# We use Nvfp4Linear with in_features=HD, out_features=T
# Q is the "hidden_states", K (kv) is the "weight" matrix
# Create or get cached runner
cache_key = (T, HD, NH)
if self._runner is None or getattr(self, '_cache_key', None) != cache_key:
runner = CuTeDSLNvfp4Linear(
runner = Nvfp4Linear(
in_features=HD,
out_features=T,
max_num_tokens=T * NH,
@@ -191,15 +191,17 @@ class NVFP4Attention:
# Set K as the weight: kv (T, HD) → treat as weight (N=T, K=HD)
# quantize_to_nvfp4 quantizes along last dim (D=HD) as activation
# For weight, we need (K, N) layout — but kv is (T, HD) = (N, K)
# CuTeDSLNvfp4Linear expects weight in (N, K//2) after permute
# Nvfp4Linear expects weight in (N, K//2) after permute
from cutedsl.bridge import quantize_to_nvfp4
from dsv4.ops.quantize import (
quantize_to_nvfp4,
)
# Quantize KV as a 2D tensor: (T, HD)
# quantize_to_nvfp4 works on last dim (D=HD), returns:
# (T, HD//2) fp4, (T, HD//16) sf, scalar gs
kv_fp4, kv_sf, kv_gs = quantize_to_nvfp4(kv_bf16)
# For CuTeDSLNvfp4Linear, weight is (N, K_packed) = (T, HD//2)
# For Nvfp4Linear, weight is (N, K_packed) = (T, HD//2)
# Our kv_fp4 is already (T, HD//2) — perfect!
# sf needs to be (N, K_sf) = (T, HD//16) — already correct

View File

@@ -163,7 +163,7 @@ def dequant_nvfp4(packed_uint8, scale_e4m3, global_scale):
def test_wo_b_nvfp4(z, wo_b_weight, wo_b_sf, wo_b_gs):
"""Test wo_b NVFP4 GEMM against BF16 reference."""
sys.path.insert(0, "/root/nvfp4-megamoe-kernel")
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
in_features = wo_b_weight.shape[1] * 2
out_features = wo_b_weight.shape[0]
@@ -173,7 +173,7 @@ def test_wo_b_nvfp4(z, wo_b_weight, wo_b_sf, wo_b_gs):
sf = [wo_b_sf.permute(1, 0).contiguous()]
gs = [wo_b_gs]
runner = CuTeDSLNvfp4Linear(
runner = Nvfp4Linear(
in_features=in_features,
out_features=out_features,
max_num_tokens=8192,

View File

@@ -91,8 +91,11 @@ def main():
print(f"BF16 ref: amax={ref_out.amax().item():.4f}")
# CuTeDSL runner
from vllm.nvfp4_cutedsl import CuTeDSLMoERunner
from cutedsl.bridge import assemble_scales_3d_side, make_b_k_major
from vllm.nvfp4_cutedsl import Nvfp4MoE
from dsv4.ops.layouts import (
assemble_scales_3d_side,
make_b_k_major,
)
l1_fp4, l1_sf, l1_gs = [], [], []
l2_fp4, l2_sf, l2_gs = [], [], []
@@ -125,7 +128,7 @@ def main():
l2_sf.append(torch.ones(INTERMEDIATE_SIZE//16, HIDDEN_SIZE, dtype=torch.float8_e4m3fn, device=DEVICE))
l2_gs.append(1.0)
runner = CuTeDSLMoERunner(
runner = Nvfp4MoE(
num_experts=NUM_EXPERTS, hidden_size=HIDDEN_SIZE,
intermediate_size=INTERMEDIATE_SIZE, max_num_tokens=MAX_NUM_TOKENS,
top_k=TOP_K, device=DEVICE,

View File

@@ -39,12 +39,12 @@ def rms(x, w, eps=1e-6):
return (w.float() * (x * torch.rsqrt(v+eps)).float()).to(x.dtype)
def make_runner(w, sf, gs_t, inf, outf):
from cutedsl.nvfp4_linear import CuTeDSLNvfp4Linear
from dsv4.layers.linear import Nvfp4Linear
fp4 = w.view(torch.float4_e2m1fn_x2).permute(1,0).contiguous()
s = sf.to(torch.float8_e4m3fn) if sf.dtype != torch.float8_e4m3fn else sf
s = s.permute(1,0).contiguous()
gs = gs_t.max().item() if gs_t.numel() > 1 else gs_t.item()
r = CuTeDSLNvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r = Nvfp4Linear(in_features=inf, out_features=outf, max_num_tokens=8192, device=str(w.device))
r.fp4 = [fp4]; r.sf = [s]; r.gs = [gs]
r.finalize_weights(); r._ensure_initialized()
return r
@@ -132,7 +132,7 @@ with torch.no_grad():
print(f" Output: amax={o_with_rope.amax():.4f} NaN={torch.isnan(o_with_rope).any()}")
# Test 3: Full pipeline
from cutedsl.csa_attention import apply_inv_gptj_rope
from dsv4.reference.csa_attention import apply_inv_gptj_rope
o_inv = apply_inv_gptj_rope(o_with_rope, positions, cos_sin, NOPE, ROPE)
o_grouped = o_inv.view(NT, OG, HPG * HD).permute(1, 0, 2)
woa_3d = woa.view(OG, OL, HPG * HD)

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env python3
"""
Test A: Compare moe_pipeline output vs CuTeDSLMoERunner output.
Test A: Compare moe_pipeline output vs Nvfp4MoE output.
Uses the same weights and inputs. If they differ, the runner is broken.
Runs on the B200 host (not inside Docker):
@@ -13,9 +13,17 @@ from safetensors import safe_open
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, REPO_ROOT)
from cutedsl.moe_pipeline import run_nvfp4_moe
from vllm.nvfp4_cutedsl import CuTeDSLMoERunner
from cutedsl.bridge import quantize_to_nvfp4, quantize_weight_to_nvfp4, make_b_k_major, assemble_scales_3d_side, compute_expert_offsets
from dsv4.reference.moe_pipeline import run_nvfp4_moe
from vllm.nvfp4_cutedsl import Nvfp4MoE
from dsv4.ops.quantize import (
quantize_to_nvfp4,
quantize_weight_to_nvfp4,
)
from dsv4.ops.layouts import (
make_b_k_major,
assemble_scales_3d_side,
compute_expert_offsets,
)
MODEL_DIR = "/root/nvidia-meeting/DeepSeek-V4-Pro-NVFP4"
DEVICE = "cuda"
@@ -126,9 +134,9 @@ def main():
)
print(f" Pipeline: amax={pipeline_out.abs().max():.4f}, mean={pipeline_out.float().mean():.6f}")
# ── Path 2: CuTeDSLMoERunner with checkpoint input_scale (what vLLM uses) ──
print("\n Running CuTeDSLMoERunner (checkpoint gs)...")
runner = CuTeDSLMoERunner(num_experts, hidden_size, intermediate_size, device=DEVICE)
# ── Path 2: Nvfp4MoE with checkpoint input_scale (what vLLM uses) ──
print("\n Running Nvfp4MoE (checkpoint gs)...")
runner = Nvfp4MoE(num_experts, hidden_size, intermediate_size, device=DEVICE)
runner.prepare_weights_direct(
[w.clone() for w in weights['l1_fp4']],
[w.clone() for w in weights['l1_sf']],
@@ -157,12 +165,12 @@ def main():
).item()
print(f" Cosine vs pipeline: {cos_ckpt:.6f}")
# ── Path 3: CuTeDSLMoERunner with dynamic gs ──
print("\n Running CuTeDSLMoERunner (dynamic gs)...")
# ── Path 3: Nvfp4MoE with dynamic gs ──
print("\n Running Nvfp4MoE (dynamic gs)...")
# We can't use quantize_to_nvfp4 in the runner (cudagraph), but we can
# compute the gs from the input and set it before calling run
x_igs = (hidden_states.abs().max().item()) / (6.0 * 448.0)
runner2 = CuTeDSLMoERunner(num_experts, hidden_size, intermediate_size, device=DEVICE)
runner2 = Nvfp4MoE(num_experts, hidden_size, intermediate_size, device=DEVICE)
runner2.prepare_weights_direct(
[w.clone() for w in weights['l1_fp4']],
[w.clone() for w in weights['l1_sf']],

View File

@@ -14,9 +14,14 @@ import os, sys, torch
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, REPO_ROOT)
from cutedsl.bridge import quantize_to_nvfp4, assemble_scales_2d_side
from cutedsl.kernel.moe.torch_scaled_grouped_mm import pad_and_swizzle_single, ceil_div
from vllm.nvfp4_cutedsl import CuTeDSLMoERunner
from dsv4.ops.quantize import (
quantize_to_nvfp4,
)
from dsv4.ops.layouts import (
assemble_scales_2d_side,
)
from dsv4.kernels.gemm.grouped import pad_and_swizzle_single, ceil_div
from vllm.nvfp4_cutedsl import Nvfp4MoE
def test_scale_assembly():
@@ -27,7 +32,7 @@ def test_scale_assembly():
intermediate_size = 3072
# Create a runner just to use its _assemble_scales_cudagraph_safe
runner = CuTeDSLMoERunner(num_experts, hidden_size, intermediate_size, device=DEVICE)
runner = Nvfp4MoE(num_experts, hidden_size, intermediate_size, device=DEVICE)
# Trigger _ensure_stacked and buffer allocation with dummy weights
def rand_fp4(*shape):
return torch.randint(0, 256, shape, dtype=torch.uint8, device=DEVICE).view(torch.float4_e2m1fn_x2)

View File

@@ -3,15 +3,20 @@
import os, sys, torch
REPO_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, REPO_ROOT)
from cutedsl.bridge import quantize_to_nvfp4, assemble_scales_2d_side
from cutedsl.kernel.moe.torch_scaled_grouped_mm import pad_and_swizzle_single, ceil_div
from vllm.nvfp4_cutedsl import CuTeDSLMoERunner
from dsv4.ops.quantize import (
quantize_to_nvfp4,
)
from dsv4.ops.layouts import (
assemble_scales_2d_side,
)
from dsv4.kernels.gemm.grouped import pad_and_swizzle_single, ceil_div
from vllm.nvfp4_cutedsl import Nvfp4MoE
DEVICE = "cuda"
num_experts = 3
hidden_size = 7168
runner = CuTeDSLMoERunner(num_experts, hidden_size, 3072, device=DEVICE)
runner = Nvfp4MoE(num_experts, hidden_size, 3072, device=DEVICE)
def rand_fp4(*shape):
return torch.randint(0, 256, shape, dtype=torch.uint8, device=DEVICE).view(torch.float4_e2m1fn_x2)
def rand_sf(*shape):

View File

@@ -1,6 +1,6 @@
"""Standalone test: Shared expert using CuTeDSL dedicated runner.
Tests the CuTeDSLSharedExpertRunner for the shared expert path.
Tests the Nvfp4SharedExpert for the shared expert path.
Compares against BF16 dequantized reference.
Usage: python3 test_shared_expert.py
@@ -55,7 +55,7 @@ def main():
torch.manual_seed(42)
sys.path.insert(0, "/root/nvfp4-megamoe-kernel")
from cutedsl.shared_expert_pipeline import CuTeDSLSharedExpertRunner
from dsv4.layers.shared_expert import Nvfp4SharedExpert
with open(os.path.join(MODEL_PATH, "model.safetensors.index.json")) as f:
wm = json.load(f)["weight_map"]
@@ -101,7 +101,7 @@ def main():
l2_sf = [down_sf.permute(1, 0).contiguous()]
# Create runner
runner = CuTeDSLSharedExpertRunner(
runner = Nvfp4SharedExpert(
hidden_size=HIDDEN_SIZE,
intermediate_size=INTERMEDIATE_SIZE,
max_num_tokens=8192,

View File

@@ -13,12 +13,16 @@ import torch
import sys
sys.path.insert(0, '/root/dsv4-nvfp4-workspace/kernel')
from cutedsl.bridge import (
from dsv4.ops.quantize import (
quantize_weight_to_nvfp4,
quantize_activation_nvfp4,
)
from dsv4.ops.layouts import (
make_b_k_major,
assemble_scales_2d_side,
assemble_scales_3d_side,
)
from dsv4.ops.gemm_runner import (
run_nvfp4_grouped_gemm,
warmup_compilation,
)

Some files were not shown because too many files have changed in this diff Show More