kernels-bot commited on
Commit
2222481
·
verified ·
1 Parent(s): d6e2c02

Uploaded using `kernel-builder`.

Browse files
Files changed (28) hide show
  1. benchmarks/benchmark.py +161 -0
  2. build/torch-stable-abi29-cu128-x86_64-linux/__init__.py +24 -0
  3. build/torch-stable-abi29-cu128-x86_64-linux/_nvfp4_gemm_cuda_9c5a0e3.abi3.so +3 -0
  4. build/torch-stable-abi29-cu128-x86_64-linux/_ops.py +9 -0
  5. build/torch-stable-abi29-cu128-x86_64-linux/_pack.py +110 -0
  6. build/torch-stable-abi29-cu128-x86_64-linux/functional.py +75 -0
  7. build/torch-stable-abi29-cu128-x86_64-linux/layers.py +215 -0
  8. build/torch-stable-abi29-cu128-x86_64-linux/metadata.json +38 -0
  9. build/torch-stable-abi29-cu128-x86_64-linux/metadata.json.sigstore +1 -0
  10. build/torch-stable-abi29-cu128-x86_64-linux/nvfp4_gemm/__init__.py +26 -0
  11. build/torch-stable-abi29-cu130-x86_64-linux/__init__.py +24 -0
  12. build/torch-stable-abi29-cu130-x86_64-linux/_nvfp4_gemm_cuda_9c5a0e3.abi3.so +3 -0
  13. build/torch-stable-abi29-cu130-x86_64-linux/_ops.py +9 -0
  14. build/torch-stable-abi29-cu130-x86_64-linux/_pack.py +110 -0
  15. build/torch-stable-abi29-cu130-x86_64-linux/functional.py +75 -0
  16. build/torch-stable-abi29-cu130-x86_64-linux/layers.py +215 -0
  17. build/torch-stable-abi29-cu130-x86_64-linux/metadata.json +38 -0
  18. build/torch-stable-abi29-cu130-x86_64-linux/metadata.json.sigstore +1 -0
  19. build/torch-stable-abi29-cu130-x86_64-linux/nvfp4_gemm/__init__.py +26 -0
  20. build/torch-stable-abi29-cu132-x86_64-linux/__init__.py +24 -0
  21. build/torch-stable-abi29-cu132-x86_64-linux/_nvfp4_gemm_cuda_9c5a0e3.abi3.so +3 -0
  22. build/torch-stable-abi29-cu132-x86_64-linux/_ops.py +9 -0
  23. build/torch-stable-abi29-cu132-x86_64-linux/_pack.py +110 -0
  24. build/torch-stable-abi29-cu132-x86_64-linux/functional.py +75 -0
  25. build/torch-stable-abi29-cu132-x86_64-linux/layers.py +215 -0
  26. build/torch-stable-abi29-cu132-x86_64-linux/metadata.json +38 -0
  27. build/torch-stable-abi29-cu132-x86_64-linux/metadata.json.sigstore +1 -0
  28. build/torch-stable-abi29-cu132-x86_64-linux/nvfp4_gemm/__init__.py +26 -0
benchmarks/benchmark.py ADDED
@@ -0,0 +1,161 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ from kernels.benchmark import Benchmark
4
+
5
+ SHAPES = {
6
+ "qkv": (8704, 6656),
7
+ "o": (6656, 4096),
8
+ "mlp": (39936, 6656),
9
+ "down": (6656, 19968),
10
+ "lm_head": (202048, 6656),
11
+ }
12
+
13
+
14
+ def _dequant(pw):
15
+ grid = torch.tensor(
16
+ [0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0], device=pw.qweight.device
17
+ )
18
+ gs = pw.global_scale.float()
19
+ out = torch.empty(pw.n, pw.k, dtype=torch.float16, device=pw.qweight.device)
20
+ step = 16384
21
+ for i in range(0, pw.n, step):
22
+ qw = pw.qweight[i : i + step]
23
+ nib = torch.stack([qw & 0xF, qw >> 4], dim=-1)
24
+ nib = nib.reshape(qw.shape[0], pw.k).long()
25
+ sign = torch.where((nib & 0x8) != 0, -1.0, 1.0)
26
+ vals = grid[nib & 0x7] * sign
27
+ sf = pw.sf_rowmajor[i : i + step].view(torch.float8_e4m3fn).float()
28
+ scale = sf.repeat_interleave(16, dim=1)
29
+ out[i : i + step] = ((vals * scale) / gs).to(torch.float16)
30
+ return out
31
+
32
+
33
+ class NvFP4GemmBenchmark(Benchmark):
34
+ seed: int = 42
35
+
36
+ def _setup_shape(self, name, m):
37
+ n, k = SHAPES[name]
38
+ torch.manual_seed(self.seed)
39
+ w = torch.randn(n, k, dtype=torch.bfloat16) * 0.02
40
+ self.pw = self.kernel.pack(w.to(self.device))
41
+ self.x = torch.randn(m, k, dtype=torch.bfloat16, device=self.device) * 0.125
42
+ self.ref_w = _dequant(self.pw)
43
+ self.xs = self.x.to(torch.float16)
44
+
45
+ def setup_decode_qkv(self):
46
+ self._setup_shape("qkv", 1)
47
+
48
+ def benchmark_decode_qkv(self):
49
+ self.out = self.kernel.gemm(self.pw, self.x)
50
+
51
+ def verify_decode_qkv(self):
52
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
53
+
54
+ def setup_decode_o(self):
55
+ self._setup_shape("o", 1)
56
+
57
+ def benchmark_decode_o(self):
58
+ self.out = self.kernel.gemm(self.pw, self.x)
59
+
60
+ def verify_decode_o(self):
61
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
62
+
63
+ def setup_decode_mlp(self):
64
+ self._setup_shape("mlp", 1)
65
+
66
+ def benchmark_decode_mlp(self):
67
+ self.out = self.kernel.gemm(self.pw, self.x)
68
+
69
+ def verify_decode_mlp(self):
70
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
71
+
72
+ def setup_decode_down(self):
73
+ self._setup_shape("down", 1)
74
+
75
+ def benchmark_decode_down(self):
76
+ self.out = self.kernel.gemm(self.pw, self.x)
77
+
78
+ def verify_decode_down(self):
79
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
80
+
81
+ def setup_decode_lm_head(self):
82
+ self._setup_shape("lm_head", 1)
83
+
84
+ def benchmark_decode_lm_head(self):
85
+ self.out = self.kernel.gemm(self.pw, self.x)
86
+
87
+ def verify_decode_lm_head(self):
88
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
89
+
90
+ def setup_prefill_mlp(self):
91
+ self._setup_shape("mlp", 512)
92
+ self.x = self.x * 0.2
93
+ xq = self.kernel.quantize_reference(self.x.cpu()).to(self.device)
94
+ self.xs = xq.to(torch.float16)
95
+
96
+ def benchmark_prefill_mlp(self):
97
+ self.out = self.kernel.gemm(self.pw, self.x)
98
+
99
+ def verify_prefill_mlp(self):
100
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
101
+
102
+
103
+ class NvFP4RawGemvBenchmark(Benchmark):
104
+ seed: int = 42
105
+
106
+ def _setup_shape(self, name):
107
+ n, k = SHAPES[name]
108
+ torch.manual_seed(self.seed)
109
+ w = torch.randn(n, k, dtype=torch.bfloat16) * 0.02
110
+ pw = self.kernel.pack(w.to(self.device))
111
+ self.qw = pw.qweight
112
+ self.sf = pw.sf_rowmajor
113
+ self.alpha = (1.0 / pw.global_scale).float().reshape(1)
114
+ self.x = torch.randn(1, k, dtype=torch.bfloat16, device=self.device) * 0.125
115
+ self.ref_w = _dequant(pw)
116
+ self.xs = self.x.to(torch.float16)
117
+
118
+ def setup_gemv_qkv(self):
119
+ self._setup_shape("qkv")
120
+
121
+ def benchmark_gemv_qkv(self):
122
+ self.out = self.kernel._ops.ops.nvfp4_gemv(self.x, self.qw, self.sf, self.alpha)
123
+
124
+ def verify_gemv_qkv(self):
125
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
126
+
127
+ def setup_gemv_o(self):
128
+ self._setup_shape("o")
129
+
130
+ def benchmark_gemv_o(self):
131
+ self.out = self.kernel._ops.ops.nvfp4_gemv(self.x, self.qw, self.sf, self.alpha)
132
+
133
+ def verify_gemv_o(self):
134
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
135
+
136
+ def setup_gemv_mlp(self):
137
+ self._setup_shape("mlp")
138
+
139
+ def benchmark_gemv_mlp(self):
140
+ self.out = self.kernel._ops.ops.nvfp4_gemv(self.x, self.qw, self.sf, self.alpha)
141
+
142
+ def verify_gemv_mlp(self):
143
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
144
+
145
+ def setup_gemv_down(self):
146
+ self._setup_shape("down")
147
+
148
+ def benchmark_gemv_down(self):
149
+ self.out = self.kernel._ops.ops.nvfp4_gemv(self.x, self.qw, self.sf, self.alpha)
150
+
151
+ def verify_gemv_down(self):
152
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
153
+
154
+ def setup_gemv_lm_head(self):
155
+ self._setup_shape("lm_head")
156
+
157
+ def benchmark_gemv_lm_head(self):
158
+ self.out = self.kernel._ops.ops.nvfp4_gemv(self.x, self.qw, self.sf, self.alpha)
159
+
160
+ def verify_gemv_lm_head(self):
161
+ return (self.xs @ self.ref_w.T).to(torch.bfloat16)
build/torch-stable-abi29-cu128-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from . import layers
2
+ from .layers import fuse_decode_projections
3
+ from .functional import gemm, gemm_swiglu
4
+ from ._pack import (
5
+ PackedWeight,
6
+ global_scale_for,
7
+ pack,
8
+ pack_swiglu,
9
+ quantize_reference,
10
+ swizzled_sf_shape,
11
+ )
12
+
13
+ __all__ = [
14
+ "PackedWeight",
15
+ "pack",
16
+ "pack_swiglu",
17
+ "quantize_reference",
18
+ "global_scale_for",
19
+ "swizzled_sf_shape",
20
+ "gemm",
21
+ "gemm_swiglu",
22
+ "layers",
23
+ "fuse_decode_projections",
24
+ ]
build/torch-stable-abi29-cu128-x86_64-linux/_nvfp4_gemm_cuda_9c5a0e3.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a57bde35bea7918bb524003d916e0074a3b72f5ed76a48c3fcdfe397ae65a8ba
3
+ size 2987544
build/torch-stable-abi29-cu128-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _nvfp4_gemm_cuda_9c5a0e3
3
+ ops = torch.ops._nvfp4_gemm_cuda_9c5a0e3
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_nvfp4_gemm_cuda_9c5a0e3::{op_name}"
build/torch-stable-abi29-cu128-x86_64-linux/_pack.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+
3
+ import torch
4
+
5
+ from ._ops import ops
6
+
7
+ FLOAT8_E4M3_MAX = 448.0
8
+ FLOAT4_E2M1_MAX = 6.0
9
+ SF_VEC_SIZE = 16 # elements sharing one FP8 scale
10
+
11
+
12
+ def _round_up(x: int, m: int) -> int:
13
+ return (x + m - 1) // m * m
14
+
15
+
16
+ def swizzled_sf_shape(rows: int, cols: int) -> tuple[int, int]:
17
+ """Shape of the swizzled scale-factor tensor (dtype int32) produced by
18
+ `scaled_fp4_quant(..., is_sf_swizzled_layout=True)` for a [rows, cols]
19
+ input. Mirrors `computeSwizzledSFShape` in nvfp4_utils.cuh: 128-row tiles,
20
+ 4 FP8 scales packed per int32 column."""
21
+ return _round_up(rows, 128), _round_up(cols // SF_VEC_SIZE, 4) // 4
22
+
23
+
24
+ def global_scale_for(w: torch.Tensor) -> torch.Tensor:
25
+ """Per-tensor second-level scale: FP8_MAX * FP4_MAX / amax."""
26
+ amax = w.abs().amax().to(torch.float32)
27
+ return (FLOAT8_E4M3_MAX * FLOAT4_E2M1_MAX / amax.clamp(min=1e-12)).reshape(1)
28
+
29
+
30
+ @dataclass
31
+ class PackedWeight:
32
+ """NVFP4 representation of a bf16 matrix [n, k]."""
33
+
34
+ qweight: torch.Tensor # uint8 [n, k/2], two e2m1 values per byte
35
+ sf: torch.Tensor # int32, swizzled FP8-E4M3 block scales
36
+ global_scale: torch.Tensor # fp32 [1]
37
+ n: int
38
+ k: int
39
+ swiglu: bool = False
40
+ # uint8 [n, k/16] row-major FP8-E4M3 block scales, consumed by the W4A16
41
+ # GEMV decode path (None when only the swizzled layout is available).
42
+ sf_rowmajor: torch.Tensor | None = None
43
+
44
+ def to(self, device) -> "PackedWeight":
45
+ return PackedWeight(
46
+ qweight=self.qweight.to(device),
47
+ sf=self.sf.to(device),
48
+ global_scale=self.global_scale.to(device),
49
+ n=self.n,
50
+ k=self.k,
51
+ swiglu=self.swiglu,
52
+ sf_rowmajor=(
53
+ None if self.sf_rowmajor is None else self.sf_rowmajor.to(device)
54
+ ),
55
+ )
56
+
57
+
58
+ def pack(
59
+ w: torch.Tensor, device: str | torch.device = "cuda", swiglu: bool = False
60
+ ) -> PackedWeight:
61
+ """Quantize a bf16/fp16 matrix [n, k] to NVFP4 (requires sm100+)."""
62
+ if w.dim() != 2 or not w.is_floating_point():
63
+ raise ValueError("pack() expects a 2-D floating-point weight")
64
+ n, k = w.shape
65
+ if k % SF_VEC_SIZE != 0:
66
+ raise ValueError(f"in_features must be a multiple of {SF_VEC_SIZE}, got {k}")
67
+ wd = w.detach().to(device, torch.bfloat16).contiguous()
68
+ gs = global_scale_for(wd).to(device)
69
+ qweight, sf = ops.scaled_fp4_quant(wd, gs, True)
70
+ # Row-major scales for the W4A16 GEMV decode path; the qweight output is
71
+ # identical to the swizzled call's, so it is discarded.
72
+ _, sf_rowmajor = ops.scaled_fp4_quant(wd, gs, False)
73
+ return PackedWeight(
74
+ qweight=qweight,
75
+ sf=sf,
76
+ global_scale=gs,
77
+ n=n,
78
+ k=k,
79
+ swiglu=swiglu,
80
+ sf_rowmajor=sf_rowmajor,
81
+ )
82
+
83
+
84
+ def pack_swiglu(
85
+ w_gate: torch.Tensor, w_up: torch.Tensor, device: str | torch.device = "cuda"
86
+ ) -> PackedWeight:
87
+ """Pack gate and up projections [n, k] as one [2n, k] matrix; the GEMM
88
+ output has gate values in columns [0, n) and up in [n, 2n)."""
89
+ if w_gate.shape != w_up.shape:
90
+ raise ValueError("gate and up matrices must have the same shape")
91
+ return pack(torch.cat([w_gate, w_up], dim=0), device=device, swiglu=True)
92
+
93
+
94
+ def quantize_reference(w: torch.Tensor) -> torch.Tensor:
95
+ """Pure-torch NVFP4 emulation (no GPU kernel): quantize + dequantize to
96
+ the same numerics the kernel produces, for tests and quality analysis."""
97
+ orig_dtype = w.dtype
98
+ wf = w.float()
99
+ gs = global_scale_for(wf) # fp8_max*fp4_max/amax
100
+ n, k = wf.shape
101
+ g = wf.reshape(n, k // SF_VEC_SIZE, SF_VEC_SIZE)
102
+ # Block scale: amax/FP4_MAX * global_scale, stored in FP8-E4M3.
103
+ block_amax = g.abs().amax(-1, keepdim=True)
104
+ sf = (block_amax / FLOAT4_E2M1_MAX) * gs
105
+ sf = sf.to(torch.float8_e4m3fn).float().clamp(min=1e-12)
106
+ scaled = g * (gs / sf)
107
+ # e2m1 grid: {0, .5, 1, 1.5, 2, 3, 4, 6} with signs.
108
+ grid = torch.tensor([0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0])
109
+ q = grid[(scaled.abs().unsqueeze(-1) - grid).abs().argmin(-1)] * scaled.sign()
110
+ return (q * sf / gs).reshape(n, k).to(orig_dtype)
build/torch-stable-abi29-cu128-x86_64-linux/functional.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ from ._ops import add_op_namespace_prefix, ops
4
+ from ._pack import FLOAT4_E2M1_MAX, FLOAT8_E4M3_MAX, PackedWeight, swizzled_sf_shape
5
+
6
+
7
+ @torch.library.register_fake(add_op_namespace_prefix("scaled_fp4_quant"))
8
+ def _quant_fake(input, input_global_scale, is_sf_swizzled_layout):
9
+ n = input.size(-1)
10
+ m = input.numel() // n
11
+ out = input.new_empty((m, n // 2), dtype=torch.uint8)
12
+ if is_sf_swizzled_layout:
13
+ sf_m, sf_n = swizzled_sf_shape(m, n)
14
+ sf = input.new_empty((sf_m, sf_n), dtype=torch.int32)
15
+ else:
16
+ sf = input.new_empty((m, n // 16), dtype=torch.uint8)
17
+ return out, sf
18
+
19
+
20
+ @torch.library.register_fake(add_op_namespace_prefix("cutlass_scaled_fp4_mm"))
21
+ def _mm_fake(a, b, a_sf, b_sf, alpha):
22
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
23
+
24
+
25
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv"))
26
+ def _gemv_fake(a, b, b_sf, alpha):
27
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
28
+
29
+
30
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv_swiglu"))
31
+ def _gemv_swiglu_fake(a, b, b_sf, alpha):
32
+ return a.new_empty((a.size(0), b.size(0) // 2), dtype=torch.bfloat16)
33
+
34
+
35
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv_gated"))
36
+ def _gemv_gated_fake(a, g, b, b_sf, alpha):
37
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
38
+
39
+
40
+ def _as_fp8(sf: torch.Tensor) -> torch.Tensor:
41
+ return sf.view(torch.float8_e4m3fn) if sf.dtype == torch.int32 else sf
42
+
43
+
44
+ def gemm(pw: PackedWeight, x: torch.Tensor) -> torch.Tensor:
45
+ """y[..., n] = x[..., k] @ W.T with dynamic NVFP4 activation quantization
46
+ (W4A4 on the Blackwell block-scaled tensor cores)."""
47
+ if x.shape[-1] != pw.k:
48
+ raise ValueError(f"expected {pw.k} input features, got {x.shape[-1]}")
49
+ x2 = x.reshape(-1, pw.k)
50
+ if not x2.is_contiguous():
51
+ x2 = x2.contiguous()
52
+ # Decode path: W4A16 GEMV keeps activations in bf16 (no activation
53
+ # quantization). The kernel re-reads the weight per output row, so it is
54
+ # only worthwhile for tiny m; prefill stays on the W4A4 CUTLASS path.
55
+ if x2.size(0) <= 2 and getattr(pw, "sf_rowmajor", None) is not None:
56
+ alpha = (1.0 / pw.global_scale).to(torch.float32).reshape(1)
57
+ y = ops.nvfp4_gemv(x2.to(torch.bfloat16), pw.qweight, pw.sf_rowmajor, alpha)
58
+ return y.reshape(*x.shape[:-1], pw.n)
59
+ a_amax = x2.abs().amax().to(torch.float32)
60
+ a_gs = (FLOAT8_E4M3_MAX * FLOAT4_E2M1_MAX / a_amax.clamp(min=1e-12)).reshape(1)
61
+ aq, a_sf = ops.scaled_fp4_quant(x2.to(torch.bfloat16), a_gs, True)
62
+ alpha = (1.0 / (a_gs * pw.global_scale)).to(torch.float32)
63
+ # The quant op returns swizzled scales packed 4-per-int32; the GEMM takes
64
+ # the same bytes typed as fp8-e4m3.
65
+ y = ops.cutlass_scaled_fp4_mm(aq, pw.qweight, _as_fp8(a_sf), _as_fp8(pw.sf), alpha)
66
+ return y.reshape(*x.shape[:-1], pw.n)
67
+
68
+
69
+ def gemm_swiglu(pw: PackedWeight, x: torch.Tensor) -> torch.Tensor:
70
+ """silu(x @ W_gate.T) * (x @ W_up.T) for a SwiGLU-packed matrix."""
71
+ if not pw.swiglu:
72
+ raise ValueError("packed matrix was not packed with pack_swiglu")
73
+ y = gemm(pw, x)
74
+ half = pw.n // 2
75
+ return torch.nn.functional.silu(y[..., :half]) * y[..., half:]
build/torch-stable-abi29-cu128-x86_64-linux/layers.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ from .functional import gemm
5
+ from ._ops import ops
6
+ from ._pack import PackedWeight
7
+
8
+
9
+ class NVFP4Linear(nn.Module):
10
+ can_torch_compile = True
11
+ has_backward = False
12
+
13
+ def forward(self, x):
14
+ pw = PackedWeight(
15
+ qweight=self.weight,
16
+ sf=self.weight_sf,
17
+ global_scale=self.weight_global_scale,
18
+ n=self.out_features,
19
+ k=self.in_features,
20
+ sf_rowmajor=getattr(self, "weight_sf_rowmajor", None),
21
+ )
22
+ return gemm(pw, x)
23
+
24
+
25
+ def _ntokens(x):
26
+ return x.numel() // x.shape[-1]
27
+
28
+
29
+ def _fused_gemv(module, x):
30
+ return ops.nvfp4_gemv(
31
+ x.reshape(-1, x.shape[-1]).to(torch.bfloat16),
32
+ module.nvfp4_fused_qweight,
33
+ module.nvfp4_fused_sf,
34
+ module.nvfp4_fused_alpha,
35
+ )
36
+
37
+
38
+ class NVFP4FusedSwiGLUMLP(nn.Module):
39
+ can_torch_compile = True
40
+ has_backward = False
41
+
42
+ def forward(self, x):
43
+ if _ntokens(x) <= 2 and hasattr(self, "nvfp4_fused_qweight"):
44
+ x2 = x.reshape(-1, x.shape[-1]).to(torch.bfloat16)
45
+ h = ops.nvfp4_gemv_swiglu(
46
+ x2,
47
+ self.nvfp4_fused_qweight,
48
+ self.nvfp4_fused_sf,
49
+ self.nvfp4_fused_alpha,
50
+ )
51
+ return self.down_proj(h.reshape(*x.shape[:-1], -1))
52
+ return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
53
+
54
+
55
+ class NVFP4GatedAttention(nn.Module):
56
+ can_torch_compile = True
57
+ has_backward = False
58
+
59
+ def forward(
60
+ self,
61
+ hidden_states,
62
+ position_embeddings=None,
63
+ attention_mask=None,
64
+ past_key_values=None,
65
+ **kwargs,
66
+ ):
67
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
68
+
69
+ input_shape = hidden_states.shape[:-1]
70
+ hidden_shape = (*input_shape, -1, self.head_dim)
71
+ og = None
72
+ if _ntokens(hidden_states) <= 2 and hasattr(self, "nvfp4_fused_qweight"):
73
+ fused = _fused_gemv(self, hidden_states)
74
+ q, k, v, *rest = torch.split(fused, self.nvfp4_fused_splits, dim=-1)
75
+ og = rest[0] if rest else None
76
+ else:
77
+ q = self.q_proj(hidden_states)
78
+ k = self.k_proj(hidden_states)
79
+ v = self.v_proj(hidden_states)
80
+ query_states = q.reshape(hidden_shape).transpose(1, 2)
81
+ key_states = k.reshape(hidden_shape).transpose(1, 2)
82
+ value_states = v.reshape(hidden_shape).transpose(1, 2)
83
+
84
+ norm = getattr(self, "qk_norm", None)
85
+ if norm is not None:
86
+ query_states = norm(query_states) * getattr(self, "qk_scale_factor", 1.0)
87
+ key_states = norm(key_states)
88
+
89
+ if position_embeddings is not None:
90
+ cos, sin = position_embeddings
91
+ query_states, key_states = self.nvfp4_rope(
92
+ query_states, key_states, cos, sin
93
+ )
94
+
95
+ if past_key_values is not None:
96
+ key_states, value_states = past_key_values.update(
97
+ key_states, value_states, self.layer_idx
98
+ )
99
+
100
+ attention_interface = ALL_ATTENTION_FUNCTIONS.get_interface(
101
+ self.config._attn_implementation, self.nvfp4_eager
102
+ )
103
+ attn_output, attn_weights = attention_interface(
104
+ self,
105
+ query_states,
106
+ key_states,
107
+ value_states,
108
+ attention_mask,
109
+ dropout=self.attention_dropout if self.training else 0.0,
110
+ scaling=self.scaling,
111
+ sliding_window=getattr(self, "sliding_window", None),
112
+ **kwargs,
113
+ )
114
+
115
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
116
+ if og is None and getattr(self, "gate_proj", None) is not None:
117
+ og = self.gate_proj(hidden_states)
118
+ o = self.o_proj
119
+ if og is not None and _ntokens(attn_output) <= 2 and _has_nvfp4(o):
120
+ a2 = attn_output.reshape(-1, attn_output.shape[-1])
121
+ og2 = og.reshape(a2.shape).contiguous()
122
+ alpha = (1.0 / o.weight_global_scale.float()).reshape(1)
123
+ y = ops.nvfp4_gemv_gated(a2, og2, o.weight, o.weight_sf_rowmajor, alpha)
124
+ return y.reshape(*input_shape, -1), attn_weights
125
+ if og is not None:
126
+ attn_output = attn_output * torch.sigmoid(og.reshape(attn_output.shape))
127
+ return o(attn_output), attn_weights
128
+
129
+
130
+ def _has_nvfp4(m):
131
+ return m is not None and hasattr(m, "weight_sf_rowmajor")
132
+
133
+
134
+ def _is_silu(act):
135
+ return isinstance(act, nn.SiLU) or type(act).__name__ == "SiLUActivation"
136
+
137
+
138
+ def _attach_fused(module, mods):
139
+ # Keep a single packed allocation and expose row views back through the
140
+ # original projection modules. Previously fusion allocated concatenated
141
+ # weights while retaining an independent copy in every projection.
142
+ # Attention and SwiGLU use row-concatenated storage; the latter is handled
143
+ # by the GEMV kernel using the gate/up half offset.
144
+ fused_qweight = torch.cat([m.weight for m in mods], 0).contiguous()
145
+ fused_sf = torch.cat([m.weight_sf_rowmajor for m in mods], 0).contiguous()
146
+ module.nvfp4_fused_qweight = fused_qweight
147
+ module.nvfp4_fused_sf = fused_sf
148
+ module.nvfp4_fused_alpha = torch.cat(
149
+ [
150
+ (1.0 / m.weight_global_scale.float())
151
+ .reshape(1)
152
+ .expand(m.out_features)
153
+ .contiguous()
154
+ for m in mods
155
+ ]
156
+ )
157
+ module.nvfp4_fused_splits = [m.out_features for m in mods]
158
+
159
+ offset = 0
160
+ for m in mods:
161
+ end = offset + m.out_features
162
+ m.weight = fused_qweight[offset:end]
163
+ m.weight_sf_rowmajor = fused_sf[offset:end]
164
+ offset = end
165
+
166
+
167
+ def _capture_helpers(cls, rope_fn):
168
+ if getattr(cls, "nvfp4_eager", None) is not None:
169
+ return
170
+ fwd = cls.forward
171
+ names = ("apply_rotary_pos_emb_interleave", "apply_rotary_pos_emb")
172
+ rope = rope_fn or next(
173
+ (fwd.__globals__[n] for n in names if n in fwd.__code__.co_names), None
174
+ )
175
+ cls.nvfp4_rope = staticmethod(rope) if rope is not None else None
176
+ cls.nvfp4_eager = staticmethod(fwd.__globals__["eager_attention_forward"])
177
+
178
+
179
+ def _mark(cls, layer_name):
180
+ if getattr(cls, "kernel_layer_name", None) is not None:
181
+ return
182
+ try:
183
+ from kernels import use_kernel_forward_from_hub
184
+
185
+ use_kernel_forward_from_hub(layer_name)(cls)
186
+ except Exception:
187
+ cls.kernel_layer_name = layer_name
188
+
189
+
190
+ def fuse_decode_projections(model, rope_fn=None):
191
+ n = 0
192
+ for mod in model.modules():
193
+ qkv = [getattr(mod, p, None) for p in ("q_proj", "k_proj", "v_proj")]
194
+ if (
195
+ all(_has_nvfp4(p) for p in qkv)
196
+ and hasattr(mod, "o_proj")
197
+ and hasattr(mod, "layer_idx")
198
+ ):
199
+ gate = getattr(mod, "gate_proj", None)
200
+ _attach_fused(mod, qkv + ([gate] if _has_nvfp4(gate) else []))
201
+ _capture_helpers(type(mod), rope_fn)
202
+ _mark(type(mod), "NVFP4GatedAttention")
203
+ n += 1
204
+ continue
205
+ gu = [getattr(mod, p, None) for p in ("gate_proj", "up_proj")]
206
+ if (
207
+ all(_has_nvfp4(p) for p in gu)
208
+ and hasattr(mod, "down_proj")
209
+ and _is_silu(getattr(mod, "act_fn", None))
210
+ and gu[0].out_features == gu[1].out_features
211
+ ):
212
+ _attach_fused(mod, gu)
213
+ _mark(type(mod), "NVFP4SwiGLUMLP")
214
+ n += 1
215
+ return n
build/torch-stable-abi29-cu128-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "nvfp4-gemm",
3
+ "id": "_nvfp4_gemm_cuda_9c5a0e3",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "upstream": "git+https://github.com/vllm-project/vllm.git",
7
+ "python-depends": [],
8
+ "backend": {
9
+ "type": "cuda",
10
+ "archs": [
11
+ "10.0a",
12
+ "12.0a"
13
+ ]
14
+ },
15
+ "digest": {
16
+ "algorithm": "sha256",
17
+ "files": {
18
+ "__init__.py": "ARXik9k13ZmZ/1tDxHgH6bQyJ8Cn6wmCrz5WEM+DABI=",
19
+ "_nvfp4_gemm_cuda_9c5a0e3.abi3.so": "pXveNb6nkYu1JAA9kW4AdKO3L17XakjD/N/jl65lqLo=",
20
+ "_ops.py": "+jBXp3Uoatvj6U0lcsYwN/3KdSd8+Aknhm5T+xbff6w=",
21
+ "_pack.py": "jATF2LhpSLUEiPWSrhsCWeyxIyn80CSy2QJNkKMADlc=",
22
+ "functional.py": "ZTd0hcUOkDdU9ZvpQMPC81KBP3FUvkmfZu7d7mhZRss=",
23
+ "layers.py": "asplwNUY3+drs57APjw3J9kFLtswK9YI0SxRVHZj2Hg=",
24
+ "nvfp4_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
25
+ }
26
+ },
27
+ "provenance": {
28
+ "kernel-builder": {
29
+ "version": "0.17.0-dev0",
30
+ "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
31
+ "dirty": false
32
+ },
33
+ "kernel": {
34
+ "sha": "9c5a0e35e9733ee93d458d2d4d5814f67d209d29",
35
+ "dirty": false
36
+ }
37
+ }
38
+ }
build/torch-stable-abi29-cu128-x86_64-linux/metadata.json.sigstore ADDED
@@ -0,0 +1 @@
 
 
1
+ {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"certificate":{"rawBytes":"MIIHSjCCBtGgAwIBAgIUS9OSD73Crs3cQlv9jdxLWV7a4AEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwODA2MTQwMzQ2WhcNMjYwODA2MTQxMzQ2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE7gZqHDNOMOz4Q8FeyZVlihIXU9AvTzg6euBRbzyxDrLTmcVUvJu3N7RJKMEan5CwIH0OALdYikglKoQ2vkhd2KOCBfAwggXsMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU5z6L3FGXVChZAWCu0GEujVULvPswHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wawYDVR0RAQH/BGEwX4ZdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTATBgorBgEEAYO/MAEEBAVCdWlsZDArBgorBgEEAYO/MAEFBB1odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMG0GCisGAQQBg78wAQkEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTAbBgorBgEEAYO/MAELBA0MC3NlbGYtaG9zdGVkMEAGCisGAQQBg78wAQwEMgwwaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MDgGCisGAQQBg78wAQ0EKgwoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTAfBgorBgEEAYO/MAEOBBEMD3JlZnMvaGVhZHMvbWFpbjAaBgorBgEEAYO/MAEPBAwMCjEwNzE0NzU1MjkwLgYKKwYBBAGDvzABEAQgDB5odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2UwGAYKKwYBBAGDvzABEQQKDAgyNTcyMDc0MzBtBgorBgEEAYO/MAESBF8MXWh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS8uZ2l0aHViL3dvcmtmbG93cy9idWlsZC55YW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAETBCoMKDljNWEwZTM1ZTk3MzNlZTkzZDQ1OGQyZDRkNTgxNGY2N2QyMDlkMjkwIQYKKwYBBAGDvzABFAQTDBF3b3JrZmxvd19kaXNwYXRjaDBkBgorBgEEAYO/MAEVBFYMVGh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS9hY3Rpb25zL3J1bnMvMzExMDgyOTgwNDUvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzBGBgorBgEEAYO/MAEYBDgMNnJlcG86aHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHk6cmVmOnJlZnMvaGVhZHMvbWFpbjCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABn9djIroAAAQDAEcwRQIgEpfpnLDesOAQNDUp8m0yF2wvPAFoeOdx9ISDqtauwoMCIQDQatHRh59ieCHLBGQUKrZ+itQ4BYRDw8Iuc68iIN2yqTAKBggqhkjOPQQDAwNnADBkAjB9q9YrxbAcKxC6YxC3iIS5TTtGqrD/0p6yXCn1Rfbj8sVqp2y6XqzqCw27XDOr+/sCMBDJIfFTbRoyAo1CeXvtzjVsg1kyq/coxBG0OYe0MRhCQxi1GlXLNpTgUeljDF5PPA=="}, "tlogEntries":[{"logIndex":"2360822422", "logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion":{"kind":"hashedrekord", "version":"0.0.1"}, "integratedTime":"1786025026", "inclusionPromise":{"signedEntryTimestamp":"MEYCIQD4uRzsrIjcaXHD1riGjwx70gmfNhYNSSKad+gGnL48FwIhAPC/ipDCJKDDJ3faJqNNzxD9ojhpTY7ks80diyGkiA74"}, "inclusionProof":{"logIndex":"2238918160", "rootHash":"Inbkmbis8U2sMiEM6EmTLZwA5s7x9Sik3HRcVtFMZsM=", "treeSize":"2238918170", "hashes":["DLvWUnbgILnx2+gOnJhcAMtoy/dgm6AFYaJFtiwG8Zg=", "zrSnDQOKLAZKUSbtZvNdr806Ret7zVdbR/kbIw8+d6g=", "yIxZWohpF6TmhojqaHkM2V5308xv0dTGBpt62aZIndE=", "s22eUbTXqsYuVO1WL518IVbaqZCnT/gJ8+Cy7ialgsU=", "Boblyud220auq3IRgfn7lwa+B1whTYwPKuWoUUCiG5w=", "yOi1SZCAJyQeGFNfXEyJmZfgE/yHiViHJh7AkVStYx0=", "ZDDgBLJeZMIedbwsKMeIdE+rnhT6LFyxK01q4RQswB8=", "1QfEqQEF22LcM0N+z5i77k0Oy00Arz4yjX7D1LTJP34=", "tSch9ErXVKdLoCFPSZ8D8aLt8T+YHXh/0cV7rQ+HxWc=", "VN19Pd6HJSN2xUEEfXlAK+NBL7uztCXLEaUeuJxgAbY=", "2DzQwQqFGfdTX3BVYQkcDxiT/TBV7lnr+ya29yCNm+o=", "QVx27ou3XJyzOgzVsHRov+ie3M7wOfT492kpmkbAeBo=", "lLhES7her3Dzu/HSMpPVolTSfVQlKbD23EIsEwXq4V4=", "kVyfjHB6dSBvAqnJBVxvTpb/VPDK9zzq5/txahxnbhc=", "u23nWGlBSoQOMevXp72NpPaDjuN0qcB01i17Odcs3Wk=", "i5Zl8FZrDwxCDv2e2DNO2M8JvpR/c11ElvCZS53/teA=", "xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="], "checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n2238918170\nInbkmbis8U2sMiEM6EmTLZwA5s7x9Sik3HRcVtFMZsM=\n\n— rekor.sigstore.dev wNI9ajBEAiAN94fxCmq+jARLavCZelggw0xfIsLdrUI02+C1Z6/LsgIgMV4fswAMOmC7Iztunae8RAFJKZhsGEde4JBU7ziBs9s=\n"}}, "canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIyMWJlOWNlMzc3MWUzN2U3MDI3MTk1ZTRkMDMyNDc0MGY0ZWQyMTM0MTE1NzA0MzI0YThkNmM2OTliNDMwMTQyIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FVUNJUUNVeXhrdFFjQWJUZ1ovSjRsRnhXY2hLbHFaWU1kTUY1VUM3SHRQYmNSckRBSWdSdjlaOFZaVE9vTDQ0cldURW51c0h3cU9tWmdrSFg4b2lkUEFGd0Nub0NNPSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaFRha05EUW5SSFowRjNTVUpCWjBsVlV6bFBVMFEzTTBOeWN6TmpVV3gyT1dwa2VFeFhWamRoTkVGRmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDlFUVRKTlZGRjNUWHBSTWxkb1kwNU5hbGwzVDBSQk1rMVVVWGhOZWxFeVYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVUzWjFweFNFUk9UMDFQZWpSUk9FWmxlVnBXYkdsb1NWaFZPVUYyVkhwbk5tVjFRbElLWW5wNWVFUnlURlJ0WTFaVmRrcDFNMDQzVWtwTFRVVmhialZEZDBsSU1FOUJUR1JaYVd0bmJFdHZVVEoyYTJoa01rdFBRMEptUVhkbloxaHpUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlUxZWpaTUNqTkdSMWhXUTJoYVFWZERkVEJIUlhWcVZsVk1kbEJ6ZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJGM1dVUldVakJTUVZGSUwwSkhSWGRZTkZwa1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKb01Wb3laSEJpYldSdFdWZE9iQXBNTW5Sc1kyMDFiR0pJVFhSWk1qbDBZbGhXZFdGWVVqVk1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1rb3hZVmQ0YTB4dWJHaGlWM2hCQ21OdFZtMWplVGx2V2xkR2EyTjVPWFJaVjJ4MVRVUnJSME5wYzBkQlVWRkNaemM0ZDBGUlJVVkxNbWd3WkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpBS1lWYzVkV041Tlc1aFdGSnZaRmRLTVdNeVZubFpNamwxWkVkV2RXUkROV3BpTWpCM1NIZFpTMHQzV1VKQ1FVZEVkbnBCUWtGblVWSmtNamw1WVRKYWN3cGlNMlJtV2tkc2VtTkhSakJaTW1kM1RtZFpTMHQzV1VKQ1FVZEVkbnBCUWtGM1VXOVBWMDB4V1ZSQ2JFMTZWbXhQVkdONlRUSldiRTlVVG10T1JGVTBDbHBFU210T1IxRXhUMFJGTUZwcVdUTmFSRWwzVDFkUmVVOVVRVlJDWjI5eVFtZEZSVUZaVHk5TlFVVkZRa0ZXUTJSWGJITmFSRUZ5UW1kdmNrSm5SVVVLUVZsUEwwMUJSVVpDUWpGdlpGZGtibUZYTlc1YWJVWnFXbE01Y2xwWVNuVmFWM2g2VEZkT2RtSlhNVEZpYld3d1pWUkJaRUpuYjNKQ1owVkZRVmxQTHdwTlFVVkhRa0U1ZVZwWFducE1NbWhzV1ZkU2Vrd3lNV2hoVnpSM1QzZFpTMHQzV1VKQ1FVZEVkbnBCUWtOQlVYUkVRM1J2WkVoU2QyTjZiM1pNTTFKMkNtRXlWblZNYlVacVpFZHNkbUp1VFhWYU1td3dZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVY3dSME5wYzBkQlVWRkNaemM0ZDBGUmEwVUtXSGQ0WkdGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkRXd5YURGYU1tUndZbTFrYlZsWFRteE1NblJzWTIwMWJHSklUWFJaTWpsMFlsaFdkUXBoV0ZJMVRIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTmlNMlI2VERKS01XRlhlR3RNYm14b1lsZDRRV050Vm0xamVUbHZXbGRHYTJONU9YUlpWMngxQ2sxRVowZERhWE5IUVZGUlFtYzNPSGRCVVc5RlMyZDNiMDlYVFRGWlZFSnNUWHBXYkU5VVkzcE5NbFpzVDFST2EwNUVWVFJhUkVwclRrZFJNVTlFUlRBS1dtcFpNMXBFU1hkUFYxRjVUMVJCWWtKbmIzSkNaMFZGUVZsUEwwMUJSVXhDUVRCTlF6Tk9iR0pIV1hSaFJ6bDZaRWRXYTAxRlFVZERhWE5IUVZGUlFncG5OemgzUVZGM1JVMW5kM2RoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlUxRVowZERhWE5IUVZGUlFtYzNPSGRCVVRCRlMyZDNiMDlYVFRGWlZFSnNUWHBXYkU5VVkzcE5NbFpzVDFST2EwNUVWVFFLV2tSS2EwNUhVVEZQUkVVd1dtcFpNMXBFU1hkUFYxRjVUMVJCWmtKbmIzSkNaMFZGUVZsUEwwMUJSVTlDUWtWTlJETktiRnB1VFhaaFIxWm9Xa2hOZGdwaVYwWndZbXBCWVVKbmIzSkNaMFZGUVZsUEwwMUJSVkJDUVhkTlEycEZkMDU2UlRCT2VsVXhUV3ByZDB4bldVdExkMWxDUWtGSFJIWjZRVUpGUVZGbkNrUkNOVzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWM1IwRlpTMHQzV1VKQ1FVZEVkbnBCUWtWUlVVc0tSRUZuZVU1VVkzbE5SR013VFhwQ2RFSm5iM0pDWjBWRlFWbFBMMDFCUlZOQ1JqaE5XRmRvTUdSSVFucFBhVGgyV2pKc01HRklWbWxNYlU1MllsTTVid3BrVjJSdVlWYzFibHB0Um1wYVV6bHlXbGhLZFZwWGVIcE1WMDUyWWxjeE1XSnRiREJsVXpoMVdqSnNNR0ZJVm1sTU0yUjJZMjEwYldKSE9UTmplVGxwQ21SWGJITmFRelUxV1ZjeGMxRklTbXhhYmsxMllVZFdhRnBJVFhaaVYwWndZbXBCTkVKbmIzSkNaMFZGUVZsUEwwMUJSVlJDUTI5TlMwUnNhazVYUlhjS1dsUk5NVnBVYXpOTmVrNXNXbFJyZWxwRVVURlBSMUY1V2tSU2EwNVVaM2hPUjFreVRqSlJlVTFFYkd0TmFtdDNTVkZaUzB0M1dVSkNRVWRFZG5wQlFncEdRVkZVUkVKR00ySXpTbkphYlhoMlpERTVhMkZZVG5kWldGSnFZVVJDYTBKbmIzSkNaMFZGUVZsUEwwMUJSVlpDUmxsTlZrZG9NR1JJUW5wUGFUaDJDbG95YkRCaFNGWnBURzFPZG1KVE9XOWtWMlJ1WVZjMWJscHRSbXBhVXpseVdsaEtkVnBYZUhwTVYwNTJZbGN4TVdKdGJEQmxVemxvV1ROU2NHSXlOWG9LVEROS01XSnVUWFpOZWtWNFRVUm5lVTlVWjNkT1JGVjJXVmhTTUZwWE1YZGtTRTEyVFZSQlYwSm5iM0pDWjBWRlFWbFBMMDFCUlZkQ1FXZE5RbTVDTVFwWmJYaHdXWHBDUjBKbmIzSkNaMFZGUVZsUEwwMUJSVmxDUkdkTlRtNUtiR05IT0RaaFNGWnVXakpzZFZveVdtaFpNbFYyWVRKV2VXSnRWbk5qZVRGcUNtSXlNWFJrVnpWd1pFaHJObU50Vm0xUGJrcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2NHSnFRMEpwWjFsTFMzZFpRa0pCU0ZkbFVVbEZRV2RTT0VKSWIwRUtaVUZDTWtGT01EbE5SM0pIZUhoRmVWbDRhMlZJU214dVRuZExhVk5zTmpRemFubDBMelJsUzJOdlFYWkxaVFpQUVVGQlFtNDVaR3BKY205QlFVRlJSQXBCUldOM1VsRkpaMFZ3Wm5CdVRFUmxjMDlCVVU1RVZYQTRiVEI1UmpKM2RsQkJSbTlsVDJSNE9VbFRSSEYwWVhWM2IwMURTVkZFVVdGMFNGSm9OVGxwQ21WRFNFeENSMUZWUzNKYUsybDBVVFJDV1ZKRWR6aEpkV00yT0dsSlRqSjVjVlJCUzBKblozRm9hMnBQVUZGUlJFRjNUbTVCUkVKclFXcENPWEU1V1hJS2VHSkJZMHQ0UXpaWmVFTXphVWxUTlZSVWRFZHhja1F2TUhBMmVWaERiakZTWm1KcU9ITldjWEF5ZVRaWWNYcHhRM2N5TjFoRVQzSXJMM05EVFVKRVNncEpaa1pVWWxKdmVVRnZNVU5sV0haMGVtcFdjMmN4YTNseEwyTnZlRUpITUU5WlpUQk5VbWhEVVhocE1VZHNXRXhPY0ZSblZXVnNha1JHTlZCUVFUMDlDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifX19fQ=="}], "timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyDADAgEAMIICvwYJKoZIhvcNAQcCoIICsDCCAqwCAQMxDTALBglghkgBZQMEAgEwgbcGCyqGSIb3DQEJEAEEoIGnBIGkMIGhAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgw1pVzNBcFREuotVjBVq4ac24HV/5E7bLCQe5t5jIi0gCFEp1QvjmRM06C3vt6D/r2EYGK947GA8yMDI2MDgwNjE0MDM0NlowAwIBAaAypDAwLjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MRUwEwYDVQQDEwxzaWdzdG9yZS10c2GgADGCAdowggHWAgEBMFEwOTEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MSAwHgYDVQQDExdzaWdzdG9yZS10c2Etc2VsZnNpZ25lZAIUOhNULwyQYe68wUMvy4qOiyojiwwwCwYJYIZIAWUDBAIBoIH8MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwODA2MTQwMzQ2WjAvBgkqhkiG9w0BCQQxIgQg2a7o4bl9utb9zteMoi/0SUUnf8lryCUpvcOKki8hxlcwgY4GCyqGSIb3DQEJEAIvMX8wfTB7MHkEIIX5J7wHq2LKw7RDVsEO/IGyxog/2nq55thw2dE6zQW3MFUwPaQ7MDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAoGCCqGSM49BAMCBGYwZAIwILnROv5qsr9Nt/eVheYqzE1snFA0UyPg7whadKIheNnLy5UxYH/cif1PiRxaYjT1AjA3BTNDpZ4M2k5OpGmoGhYhVmz2t6tQD+ZLpf7+oMmrPVk2zY13GDeBvTcrPCkW2og="}]}}, "messageSignature":{"messageDigest":{"algorithm":"SHA2_256", "digest":"Ib6c43ceN+cCcZXk0DJHQPTtITQRVwQySo1saZtDAUI="}, "signature":"MEUCIQCUyxktQcAbTgZ/J4lFxWchKlqZYMdMF5UC7HtPbcRrDAIgRv9Z8VZTOoL44rWTEnusHwqOmZgkHX8oidPAFwCnoCM="}}
build/torch-stable-abi29-cu128-x86_64-linux/nvfp4_gemm/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch-stable-abi29-cu130-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from . import layers
2
+ from .layers import fuse_decode_projections
3
+ from .functional import gemm, gemm_swiglu
4
+ from ._pack import (
5
+ PackedWeight,
6
+ global_scale_for,
7
+ pack,
8
+ pack_swiglu,
9
+ quantize_reference,
10
+ swizzled_sf_shape,
11
+ )
12
+
13
+ __all__ = [
14
+ "PackedWeight",
15
+ "pack",
16
+ "pack_swiglu",
17
+ "quantize_reference",
18
+ "global_scale_for",
19
+ "swizzled_sf_shape",
20
+ "gemm",
21
+ "gemm_swiglu",
22
+ "layers",
23
+ "fuse_decode_projections",
24
+ ]
build/torch-stable-abi29-cu130-x86_64-linux/_nvfp4_gemm_cuda_9c5a0e3.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:677d4daa14831cb173a766cd1c00ad62e33acffd66504685d841679a53cbd315
3
+ size 3124488
build/torch-stable-abi29-cu130-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _nvfp4_gemm_cuda_9c5a0e3
3
+ ops = torch.ops._nvfp4_gemm_cuda_9c5a0e3
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_nvfp4_gemm_cuda_9c5a0e3::{op_name}"
build/torch-stable-abi29-cu130-x86_64-linux/_pack.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+
3
+ import torch
4
+
5
+ from ._ops import ops
6
+
7
+ FLOAT8_E4M3_MAX = 448.0
8
+ FLOAT4_E2M1_MAX = 6.0
9
+ SF_VEC_SIZE = 16 # elements sharing one FP8 scale
10
+
11
+
12
+ def _round_up(x: int, m: int) -> int:
13
+ return (x + m - 1) // m * m
14
+
15
+
16
+ def swizzled_sf_shape(rows: int, cols: int) -> tuple[int, int]:
17
+ """Shape of the swizzled scale-factor tensor (dtype int32) produced by
18
+ `scaled_fp4_quant(..., is_sf_swizzled_layout=True)` for a [rows, cols]
19
+ input. Mirrors `computeSwizzledSFShape` in nvfp4_utils.cuh: 128-row tiles,
20
+ 4 FP8 scales packed per int32 column."""
21
+ return _round_up(rows, 128), _round_up(cols // SF_VEC_SIZE, 4) // 4
22
+
23
+
24
+ def global_scale_for(w: torch.Tensor) -> torch.Tensor:
25
+ """Per-tensor second-level scale: FP8_MAX * FP4_MAX / amax."""
26
+ amax = w.abs().amax().to(torch.float32)
27
+ return (FLOAT8_E4M3_MAX * FLOAT4_E2M1_MAX / amax.clamp(min=1e-12)).reshape(1)
28
+
29
+
30
+ @dataclass
31
+ class PackedWeight:
32
+ """NVFP4 representation of a bf16 matrix [n, k]."""
33
+
34
+ qweight: torch.Tensor # uint8 [n, k/2], two e2m1 values per byte
35
+ sf: torch.Tensor # int32, swizzled FP8-E4M3 block scales
36
+ global_scale: torch.Tensor # fp32 [1]
37
+ n: int
38
+ k: int
39
+ swiglu: bool = False
40
+ # uint8 [n, k/16] row-major FP8-E4M3 block scales, consumed by the W4A16
41
+ # GEMV decode path (None when only the swizzled layout is available).
42
+ sf_rowmajor: torch.Tensor | None = None
43
+
44
+ def to(self, device) -> "PackedWeight":
45
+ return PackedWeight(
46
+ qweight=self.qweight.to(device),
47
+ sf=self.sf.to(device),
48
+ global_scale=self.global_scale.to(device),
49
+ n=self.n,
50
+ k=self.k,
51
+ swiglu=self.swiglu,
52
+ sf_rowmajor=(
53
+ None if self.sf_rowmajor is None else self.sf_rowmajor.to(device)
54
+ ),
55
+ )
56
+
57
+
58
+ def pack(
59
+ w: torch.Tensor, device: str | torch.device = "cuda", swiglu: bool = False
60
+ ) -> PackedWeight:
61
+ """Quantize a bf16/fp16 matrix [n, k] to NVFP4 (requires sm100+)."""
62
+ if w.dim() != 2 or not w.is_floating_point():
63
+ raise ValueError("pack() expects a 2-D floating-point weight")
64
+ n, k = w.shape
65
+ if k % SF_VEC_SIZE != 0:
66
+ raise ValueError(f"in_features must be a multiple of {SF_VEC_SIZE}, got {k}")
67
+ wd = w.detach().to(device, torch.bfloat16).contiguous()
68
+ gs = global_scale_for(wd).to(device)
69
+ qweight, sf = ops.scaled_fp4_quant(wd, gs, True)
70
+ # Row-major scales for the W4A16 GEMV decode path; the qweight output is
71
+ # identical to the swizzled call's, so it is discarded.
72
+ _, sf_rowmajor = ops.scaled_fp4_quant(wd, gs, False)
73
+ return PackedWeight(
74
+ qweight=qweight,
75
+ sf=sf,
76
+ global_scale=gs,
77
+ n=n,
78
+ k=k,
79
+ swiglu=swiglu,
80
+ sf_rowmajor=sf_rowmajor,
81
+ )
82
+
83
+
84
+ def pack_swiglu(
85
+ w_gate: torch.Tensor, w_up: torch.Tensor, device: str | torch.device = "cuda"
86
+ ) -> PackedWeight:
87
+ """Pack gate and up projections [n, k] as one [2n, k] matrix; the GEMM
88
+ output has gate values in columns [0, n) and up in [n, 2n)."""
89
+ if w_gate.shape != w_up.shape:
90
+ raise ValueError("gate and up matrices must have the same shape")
91
+ return pack(torch.cat([w_gate, w_up], dim=0), device=device, swiglu=True)
92
+
93
+
94
+ def quantize_reference(w: torch.Tensor) -> torch.Tensor:
95
+ """Pure-torch NVFP4 emulation (no GPU kernel): quantize + dequantize to
96
+ the same numerics the kernel produces, for tests and quality analysis."""
97
+ orig_dtype = w.dtype
98
+ wf = w.float()
99
+ gs = global_scale_for(wf) # fp8_max*fp4_max/amax
100
+ n, k = wf.shape
101
+ g = wf.reshape(n, k // SF_VEC_SIZE, SF_VEC_SIZE)
102
+ # Block scale: amax/FP4_MAX * global_scale, stored in FP8-E4M3.
103
+ block_amax = g.abs().amax(-1, keepdim=True)
104
+ sf = (block_amax / FLOAT4_E2M1_MAX) * gs
105
+ sf = sf.to(torch.float8_e4m3fn).float().clamp(min=1e-12)
106
+ scaled = g * (gs / sf)
107
+ # e2m1 grid: {0, .5, 1, 1.5, 2, 3, 4, 6} with signs.
108
+ grid = torch.tensor([0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0])
109
+ q = grid[(scaled.abs().unsqueeze(-1) - grid).abs().argmin(-1)] * scaled.sign()
110
+ return (q * sf / gs).reshape(n, k).to(orig_dtype)
build/torch-stable-abi29-cu130-x86_64-linux/functional.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ from ._ops import add_op_namespace_prefix, ops
4
+ from ._pack import FLOAT4_E2M1_MAX, FLOAT8_E4M3_MAX, PackedWeight, swizzled_sf_shape
5
+
6
+
7
+ @torch.library.register_fake(add_op_namespace_prefix("scaled_fp4_quant"))
8
+ def _quant_fake(input, input_global_scale, is_sf_swizzled_layout):
9
+ n = input.size(-1)
10
+ m = input.numel() // n
11
+ out = input.new_empty((m, n // 2), dtype=torch.uint8)
12
+ if is_sf_swizzled_layout:
13
+ sf_m, sf_n = swizzled_sf_shape(m, n)
14
+ sf = input.new_empty((sf_m, sf_n), dtype=torch.int32)
15
+ else:
16
+ sf = input.new_empty((m, n // 16), dtype=torch.uint8)
17
+ return out, sf
18
+
19
+
20
+ @torch.library.register_fake(add_op_namespace_prefix("cutlass_scaled_fp4_mm"))
21
+ def _mm_fake(a, b, a_sf, b_sf, alpha):
22
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
23
+
24
+
25
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv"))
26
+ def _gemv_fake(a, b, b_sf, alpha):
27
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
28
+
29
+
30
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv_swiglu"))
31
+ def _gemv_swiglu_fake(a, b, b_sf, alpha):
32
+ return a.new_empty((a.size(0), b.size(0) // 2), dtype=torch.bfloat16)
33
+
34
+
35
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv_gated"))
36
+ def _gemv_gated_fake(a, g, b, b_sf, alpha):
37
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
38
+
39
+
40
+ def _as_fp8(sf: torch.Tensor) -> torch.Tensor:
41
+ return sf.view(torch.float8_e4m3fn) if sf.dtype == torch.int32 else sf
42
+
43
+
44
+ def gemm(pw: PackedWeight, x: torch.Tensor) -> torch.Tensor:
45
+ """y[..., n] = x[..., k] @ W.T with dynamic NVFP4 activation quantization
46
+ (W4A4 on the Blackwell block-scaled tensor cores)."""
47
+ if x.shape[-1] != pw.k:
48
+ raise ValueError(f"expected {pw.k} input features, got {x.shape[-1]}")
49
+ x2 = x.reshape(-1, pw.k)
50
+ if not x2.is_contiguous():
51
+ x2 = x2.contiguous()
52
+ # Decode path: W4A16 GEMV keeps activations in bf16 (no activation
53
+ # quantization). The kernel re-reads the weight per output row, so it is
54
+ # only worthwhile for tiny m; prefill stays on the W4A4 CUTLASS path.
55
+ if x2.size(0) <= 2 and getattr(pw, "sf_rowmajor", None) is not None:
56
+ alpha = (1.0 / pw.global_scale).to(torch.float32).reshape(1)
57
+ y = ops.nvfp4_gemv(x2.to(torch.bfloat16), pw.qweight, pw.sf_rowmajor, alpha)
58
+ return y.reshape(*x.shape[:-1], pw.n)
59
+ a_amax = x2.abs().amax().to(torch.float32)
60
+ a_gs = (FLOAT8_E4M3_MAX * FLOAT4_E2M1_MAX / a_amax.clamp(min=1e-12)).reshape(1)
61
+ aq, a_sf = ops.scaled_fp4_quant(x2.to(torch.bfloat16), a_gs, True)
62
+ alpha = (1.0 / (a_gs * pw.global_scale)).to(torch.float32)
63
+ # The quant op returns swizzled scales packed 4-per-int32; the GEMM takes
64
+ # the same bytes typed as fp8-e4m3.
65
+ y = ops.cutlass_scaled_fp4_mm(aq, pw.qweight, _as_fp8(a_sf), _as_fp8(pw.sf), alpha)
66
+ return y.reshape(*x.shape[:-1], pw.n)
67
+
68
+
69
+ def gemm_swiglu(pw: PackedWeight, x: torch.Tensor) -> torch.Tensor:
70
+ """silu(x @ W_gate.T) * (x @ W_up.T) for a SwiGLU-packed matrix."""
71
+ if not pw.swiglu:
72
+ raise ValueError("packed matrix was not packed with pack_swiglu")
73
+ y = gemm(pw, x)
74
+ half = pw.n // 2
75
+ return torch.nn.functional.silu(y[..., :half]) * y[..., half:]
build/torch-stable-abi29-cu130-x86_64-linux/layers.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ from .functional import gemm
5
+ from ._ops import ops
6
+ from ._pack import PackedWeight
7
+
8
+
9
+ class NVFP4Linear(nn.Module):
10
+ can_torch_compile = True
11
+ has_backward = False
12
+
13
+ def forward(self, x):
14
+ pw = PackedWeight(
15
+ qweight=self.weight,
16
+ sf=self.weight_sf,
17
+ global_scale=self.weight_global_scale,
18
+ n=self.out_features,
19
+ k=self.in_features,
20
+ sf_rowmajor=getattr(self, "weight_sf_rowmajor", None),
21
+ )
22
+ return gemm(pw, x)
23
+
24
+
25
+ def _ntokens(x):
26
+ return x.numel() // x.shape[-1]
27
+
28
+
29
+ def _fused_gemv(module, x):
30
+ return ops.nvfp4_gemv(
31
+ x.reshape(-1, x.shape[-1]).to(torch.bfloat16),
32
+ module.nvfp4_fused_qweight,
33
+ module.nvfp4_fused_sf,
34
+ module.nvfp4_fused_alpha,
35
+ )
36
+
37
+
38
+ class NVFP4FusedSwiGLUMLP(nn.Module):
39
+ can_torch_compile = True
40
+ has_backward = False
41
+
42
+ def forward(self, x):
43
+ if _ntokens(x) <= 2 and hasattr(self, "nvfp4_fused_qweight"):
44
+ x2 = x.reshape(-1, x.shape[-1]).to(torch.bfloat16)
45
+ h = ops.nvfp4_gemv_swiglu(
46
+ x2,
47
+ self.nvfp4_fused_qweight,
48
+ self.nvfp4_fused_sf,
49
+ self.nvfp4_fused_alpha,
50
+ )
51
+ return self.down_proj(h.reshape(*x.shape[:-1], -1))
52
+ return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
53
+
54
+
55
+ class NVFP4GatedAttention(nn.Module):
56
+ can_torch_compile = True
57
+ has_backward = False
58
+
59
+ def forward(
60
+ self,
61
+ hidden_states,
62
+ position_embeddings=None,
63
+ attention_mask=None,
64
+ past_key_values=None,
65
+ **kwargs,
66
+ ):
67
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
68
+
69
+ input_shape = hidden_states.shape[:-1]
70
+ hidden_shape = (*input_shape, -1, self.head_dim)
71
+ og = None
72
+ if _ntokens(hidden_states) <= 2 and hasattr(self, "nvfp4_fused_qweight"):
73
+ fused = _fused_gemv(self, hidden_states)
74
+ q, k, v, *rest = torch.split(fused, self.nvfp4_fused_splits, dim=-1)
75
+ og = rest[0] if rest else None
76
+ else:
77
+ q = self.q_proj(hidden_states)
78
+ k = self.k_proj(hidden_states)
79
+ v = self.v_proj(hidden_states)
80
+ query_states = q.reshape(hidden_shape).transpose(1, 2)
81
+ key_states = k.reshape(hidden_shape).transpose(1, 2)
82
+ value_states = v.reshape(hidden_shape).transpose(1, 2)
83
+
84
+ norm = getattr(self, "qk_norm", None)
85
+ if norm is not None:
86
+ query_states = norm(query_states) * getattr(self, "qk_scale_factor", 1.0)
87
+ key_states = norm(key_states)
88
+
89
+ if position_embeddings is not None:
90
+ cos, sin = position_embeddings
91
+ query_states, key_states = self.nvfp4_rope(
92
+ query_states, key_states, cos, sin
93
+ )
94
+
95
+ if past_key_values is not None:
96
+ key_states, value_states = past_key_values.update(
97
+ key_states, value_states, self.layer_idx
98
+ )
99
+
100
+ attention_interface = ALL_ATTENTION_FUNCTIONS.get_interface(
101
+ self.config._attn_implementation, self.nvfp4_eager
102
+ )
103
+ attn_output, attn_weights = attention_interface(
104
+ self,
105
+ query_states,
106
+ key_states,
107
+ value_states,
108
+ attention_mask,
109
+ dropout=self.attention_dropout if self.training else 0.0,
110
+ scaling=self.scaling,
111
+ sliding_window=getattr(self, "sliding_window", None),
112
+ **kwargs,
113
+ )
114
+
115
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
116
+ if og is None and getattr(self, "gate_proj", None) is not None:
117
+ og = self.gate_proj(hidden_states)
118
+ o = self.o_proj
119
+ if og is not None and _ntokens(attn_output) <= 2 and _has_nvfp4(o):
120
+ a2 = attn_output.reshape(-1, attn_output.shape[-1])
121
+ og2 = og.reshape(a2.shape).contiguous()
122
+ alpha = (1.0 / o.weight_global_scale.float()).reshape(1)
123
+ y = ops.nvfp4_gemv_gated(a2, og2, o.weight, o.weight_sf_rowmajor, alpha)
124
+ return y.reshape(*input_shape, -1), attn_weights
125
+ if og is not None:
126
+ attn_output = attn_output * torch.sigmoid(og.reshape(attn_output.shape))
127
+ return o(attn_output), attn_weights
128
+
129
+
130
+ def _has_nvfp4(m):
131
+ return m is not None and hasattr(m, "weight_sf_rowmajor")
132
+
133
+
134
+ def _is_silu(act):
135
+ return isinstance(act, nn.SiLU) or type(act).__name__ == "SiLUActivation"
136
+
137
+
138
+ def _attach_fused(module, mods):
139
+ # Keep a single packed allocation and expose row views back through the
140
+ # original projection modules. Previously fusion allocated concatenated
141
+ # weights while retaining an independent copy in every projection.
142
+ # Attention and SwiGLU use row-concatenated storage; the latter is handled
143
+ # by the GEMV kernel using the gate/up half offset.
144
+ fused_qweight = torch.cat([m.weight for m in mods], 0).contiguous()
145
+ fused_sf = torch.cat([m.weight_sf_rowmajor for m in mods], 0).contiguous()
146
+ module.nvfp4_fused_qweight = fused_qweight
147
+ module.nvfp4_fused_sf = fused_sf
148
+ module.nvfp4_fused_alpha = torch.cat(
149
+ [
150
+ (1.0 / m.weight_global_scale.float())
151
+ .reshape(1)
152
+ .expand(m.out_features)
153
+ .contiguous()
154
+ for m in mods
155
+ ]
156
+ )
157
+ module.nvfp4_fused_splits = [m.out_features for m in mods]
158
+
159
+ offset = 0
160
+ for m in mods:
161
+ end = offset + m.out_features
162
+ m.weight = fused_qweight[offset:end]
163
+ m.weight_sf_rowmajor = fused_sf[offset:end]
164
+ offset = end
165
+
166
+
167
+ def _capture_helpers(cls, rope_fn):
168
+ if getattr(cls, "nvfp4_eager", None) is not None:
169
+ return
170
+ fwd = cls.forward
171
+ names = ("apply_rotary_pos_emb_interleave", "apply_rotary_pos_emb")
172
+ rope = rope_fn or next(
173
+ (fwd.__globals__[n] for n in names if n in fwd.__code__.co_names), None
174
+ )
175
+ cls.nvfp4_rope = staticmethod(rope) if rope is not None else None
176
+ cls.nvfp4_eager = staticmethod(fwd.__globals__["eager_attention_forward"])
177
+
178
+
179
+ def _mark(cls, layer_name):
180
+ if getattr(cls, "kernel_layer_name", None) is not None:
181
+ return
182
+ try:
183
+ from kernels import use_kernel_forward_from_hub
184
+
185
+ use_kernel_forward_from_hub(layer_name)(cls)
186
+ except Exception:
187
+ cls.kernel_layer_name = layer_name
188
+
189
+
190
+ def fuse_decode_projections(model, rope_fn=None):
191
+ n = 0
192
+ for mod in model.modules():
193
+ qkv = [getattr(mod, p, None) for p in ("q_proj", "k_proj", "v_proj")]
194
+ if (
195
+ all(_has_nvfp4(p) for p in qkv)
196
+ and hasattr(mod, "o_proj")
197
+ and hasattr(mod, "layer_idx")
198
+ ):
199
+ gate = getattr(mod, "gate_proj", None)
200
+ _attach_fused(mod, qkv + ([gate] if _has_nvfp4(gate) else []))
201
+ _capture_helpers(type(mod), rope_fn)
202
+ _mark(type(mod), "NVFP4GatedAttention")
203
+ n += 1
204
+ continue
205
+ gu = [getattr(mod, p, None) for p in ("gate_proj", "up_proj")]
206
+ if (
207
+ all(_has_nvfp4(p) for p in gu)
208
+ and hasattr(mod, "down_proj")
209
+ and _is_silu(getattr(mod, "act_fn", None))
210
+ and gu[0].out_features == gu[1].out_features
211
+ ):
212
+ _attach_fused(mod, gu)
213
+ _mark(type(mod), "NVFP4SwiGLUMLP")
214
+ n += 1
215
+ return n
build/torch-stable-abi29-cu130-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "nvfp4-gemm",
3
+ "id": "_nvfp4_gemm_cuda_9c5a0e3",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "upstream": "git+https://github.com/vllm-project/vllm.git",
7
+ "python-depends": [],
8
+ "backend": {
9
+ "type": "cuda",
10
+ "archs": [
11
+ "10.0a",
12
+ "12.0a"
13
+ ]
14
+ },
15
+ "digest": {
16
+ "algorithm": "sha256",
17
+ "files": {
18
+ "__init__.py": "ARXik9k13ZmZ/1tDxHgH6bQyJ8Cn6wmCrz5WEM+DABI=",
19
+ "_nvfp4_gemm_cuda_9c5a0e3.abi3.so": "Z31NqhSDHLFzp2bNHACtYuM6z/1mUEaF2EFnmlPL0xU=",
20
+ "_ops.py": "+jBXp3Uoatvj6U0lcsYwN/3KdSd8+Aknhm5T+xbff6w=",
21
+ "_pack.py": "jATF2LhpSLUEiPWSrhsCWeyxIyn80CSy2QJNkKMADlc=",
22
+ "functional.py": "ZTd0hcUOkDdU9ZvpQMPC81KBP3FUvkmfZu7d7mhZRss=",
23
+ "layers.py": "asplwNUY3+drs57APjw3J9kFLtswK9YI0SxRVHZj2Hg=",
24
+ "nvfp4_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
25
+ }
26
+ },
27
+ "provenance": {
28
+ "kernel-builder": {
29
+ "version": "0.17.0-dev0",
30
+ "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
31
+ "dirty": false
32
+ },
33
+ "kernel": {
34
+ "sha": "9c5a0e35e9733ee93d458d2d4d5814f67d209d29",
35
+ "dirty": false
36
+ }
37
+ }
38
+ }
build/torch-stable-abi29-cu130-x86_64-linux/metadata.json.sigstore ADDED
@@ -0,0 +1 @@
 
 
1
+ {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"certificate":{"rawBytes":"MIIHSzCCBtGgAwIBAgIUHAfPbhyAe3cC5esJqPyqePr8GwUwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwODA2MTQwMzQ3WhcNMjYwODA2MTQxMzQ3WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEWUCsho2YzsyZePsqOe+UxlvRqdrcSrEw5hWkuhJezhApQK2pVybhQB/CKQDwkqHePS1yPoZMs7arsRTrDvp71KOCBfAwggXsMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUMXuHbek4tvcWcO8sCL27wc77BhwwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wawYDVR0RAQH/BGEwX4ZdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTATBgorBgEEAYO/MAEEBAVCdWlsZDArBgorBgEEAYO/MAEFBB1odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMG0GCisGAQQBg78wAQkEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTAbBgorBgEEAYO/MAELBA0MC3NlbGYtaG9zdGVkMEAGCisGAQQBg78wAQwEMgwwaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MDgGCisGAQQBg78wAQ0EKgwoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTAfBgorBgEEAYO/MAEOBBEMD3JlZnMvaGVhZHMvbWFpbjAaBgorBgEEAYO/MAEPBAwMCjEwNzE0NzU1MjkwLgYKKwYBBAGDvzABEAQgDB5odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2UwGAYKKwYBBAGDvzABEQQKDAgyNTcyMDc0MzBtBgorBgEEAYO/MAESBF8MXWh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS8uZ2l0aHViL3dvcmtmbG93cy9idWlsZC55YW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAETBCoMKDljNWEwZTM1ZTk3MzNlZTkzZDQ1OGQyZDRkNTgxNGY2N2QyMDlkMjkwIQYKKwYBBAGDvzABFAQTDBF3b3JrZmxvd19kaXNwYXRjaDBkBgorBgEEAYO/MAEVBFYMVGh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS9hY3Rpb25zL3J1bnMvMzExMDgyOTgwNDUvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzBGBgorBgEEAYO/MAEYBDgMNnJlcG86aHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHk6cmVmOnJlZnMvaGVhZHMvbWFpbjCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABn9djJjQAAAQDAEcwRQIhAIIWCYxW+rI6aaMi9HgD2DleYR6J+F6YqKXUxUv4Z4GmAiAyg9XAQCiB5lJQkeWNWmg/JPDvfOUMGlJVQFdOpjX/bzAKBggqhkjOPQQDAwNoADBlAjAjA7B7LOxYtc7NF049oP6Qo975a2A5zVyU/BWuCOQov+xcw5zDliXhCWXOLNW2BnACMQD0H11DuJC9QXwQNmHwj+A29g/LndNPkjV9AIyBq6hwTfWIEED6AkpjmuOSkbTrm2A="}, "tlogEntries":[{"logIndex":"2360822606", "logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion":{"kind":"hashedrekord", "version":"0.0.1"}, "integratedTime":"1786025027", "inclusionPromise":{"signedEntryTimestamp":"MEQCIALdVIyCoXgz+SjRmxpJQDqgY8ecSWQymgLlX9xe2X61AiAUBt7xZWbAvhvQVPlt5yysMBXgs6LV+l2xYpfTXZ2TMA=="}, "inclusionProof":{"logIndex":"2238918344", "rootHash":"eN8oZuSEFGs70/BLfp19zZEf/Ef5N6oImTmtz+Jny7c=", "treeSize":"2238918358", "hashes":["OD35pd+GVFggfOcYGAvxIqbLmRi+yMYiisbcPtNJdJk=", "ohU4TYqXEjN0FOll1utEBukEQUc2a8glNqdj9lVxaqk=", "HErYWbjZJEkdOhwWC3iOORjGwhaO9z3YtuqIZsf55ek=", "qa31WzmDahZLK7TXE00fLX/3SyJLF2NCAmvEO941Ga4=", "NCmw55uH4clQcrqrPxLkqS6vGExt+NgyLt4oB68au6o=", "m06N3WcrfN7DqNJzhl3g+U0foWjztV58UZHFQYl3DGE=", "Fw0l2YFqeuLVAAuiRC2S6aMQd7o0x2qVKJK00oNkgcM=", "yOi1SZCAJyQeGFNfXEyJmZfgE/yHiViHJh7AkVStYx0=", "ZDDgBLJeZMIedbwsKMeIdE+rnhT6LFyxK01q4RQswB8=", "1QfEqQEF22LcM0N+z5i77k0Oy00Arz4yjX7D1LTJP34=", "tSch9ErXVKdLoCFPSZ8D8aLt8T+YHXh/0cV7rQ+HxWc=", "VN19Pd6HJSN2xUEEfXlAK+NBL7uztCXLEaUeuJxgAbY=", "2DzQwQqFGfdTX3BVYQkcDxiT/TBV7lnr+ya29yCNm+o=", "QVx27ou3XJyzOgzVsHRov+ie3M7wOfT492kpmkbAeBo=", "lLhES7her3Dzu/HSMpPVolTSfVQlKbD23EIsEwXq4V4=", "kVyfjHB6dSBvAqnJBVxvTpb/VPDK9zzq5/txahxnbhc=", "u23nWGlBSoQOMevXp72NpPaDjuN0qcB01i17Odcs3Wk=", "i5Zl8FZrDwxCDv2e2DNO2M8JvpR/c11ElvCZS53/teA=", "xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="], "checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n2238918358\neN8oZuSEFGs70/BLfp19zZEf/Ef5N6oImTmtz+Jny7c=\n\n— rekor.sigstore.dev wNI9ajBEAiBaLWisA7jij7yzI4AVOOST7ZFXt3aNtgN0/P2yM2mVmAIgAzBqsGjinhohmAEg/l+k00weJZykRapSeglzTmmpNBw=\n"}}, "canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiI5ZGIxY2IxOGEzZGIzMWU5N2E1MjUzMmYzZjcxMzBkNWQxMTYxYWIzNjM3NmZjZTUwNGVlN2YxMDYwZWQxOWM4In19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FUUNJSHQ1eC83V0FzZWwyaWpqSXl5SGwyclgvUyt3U09jUkVwYUgwalRjeUpmUkFpQjl4SzRERlB4SFdkMjJRZHkyV0o2a3g2Z0lGZ3BpZ2NGczBZbVpGS1MwZFE9PSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaFRla05EUW5SSFowRjNTVUpCWjBsVlNFRm1VR0pvZVVGbE0yTkROV1Z6U25GUWVYRmxVSEk0UjNkVmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDlFUVRKTlZGRjNUWHBSTTFkb1kwNU5hbGwzVDBSQk1rMVVVWGhOZWxFelYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZYVlVOemFHOHlXWHB6ZVZwbFVITnhUMlVyVlhoc2RsSnhaSEpqVTNKRmR6Vm9WMnNLZFdoS1pYcG9RWEJSU3pKd1ZubGlhRkZDTDBOTFVVUjNhM0ZJWlZCVE1YbFFiMXBOY3pkaGNuTlNWSEpFZG5BM01VdFBRMEptUVhkbloxaHpUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZOV0hWSUNtSmxhelIwZG1OWFkwODRjME5NTWpkM1l6YzNRbWgzZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJGM1dVUldVakJTUVZGSUwwSkhSWGRZTkZwa1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKb01Wb3laSEJpYldSdFdWZE9iQXBNTW5Sc1kyMDFiR0pJVFhSWk1qbDBZbGhXZFdGWVVqVk1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1rb3hZVmQ0YTB4dWJHaGlWM2hCQ21OdFZtMWplVGx2V2xkR2EyTjVPWFJaVjJ4MVRVUnJSME5wYzBkQlVWRkNaemM0ZDBGUlJVVkxNbWd3WkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpBS1lWYzVkV041Tlc1aFdGSnZaRmRLTVdNeVZubFpNamwxWkVkV2RXUkROV3BpTWpCM1NIZFpTMHQzV1VKQ1FVZEVkbnBCUWtGblVWSmtNamw1WVRKYWN3cGlNMlJtV2tkc2VtTkhSakJaTW1kM1RtZFpTMHQzV1VKQ1FVZEVkbnBCUWtGM1VXOVBWMDB4V1ZSQ2JFMTZWbXhQVkdONlRUSldiRTlVVG10T1JGVTBDbHBFU210T1IxRXhUMFJGTUZwcVdUTmFSRWwzVDFkUmVVOVVRVlJDWjI5eVFtZEZSVUZaVHk5TlFVVkZRa0ZXUTJSWGJITmFSRUZ5UW1kdmNrSm5SVVVLUVZsUEwwMUJSVVpDUWpGdlpGZGtibUZYTlc1YWJVWnFXbE01Y2xwWVNuVmFWM2g2VEZkT2RtSlhNVEZpYld3d1pWUkJaRUpuYjNKQ1owVkZRVmxQTHdwTlFVVkhRa0U1ZVZwWFducE1NbWhzV1ZkU2Vrd3lNV2hoVnpSM1QzZFpTMHQzV1VKQ1FVZEVkbnBCUWtOQlVYUkVRM1J2WkVoU2QyTjZiM1pNTTFKMkNtRXlWblZNYlVacVpFZHNkbUp1VFhWYU1td3dZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVY3dSME5wYzBkQlVWRkNaemM0ZDBGUmEwVUtXSGQ0WkdGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkRXd5YURGYU1tUndZbTFrYlZsWFRteE1NblJzWTIwMWJHSklUWFJaTWpsMFlsaFdkUXBoV0ZJMVRIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTmlNMlI2VERKS01XRlhlR3RNYm14b1lsZDRRV050Vm0xamVUbHZXbGRHYTJONU9YUlpWMngxQ2sxRVowZERhWE5IUVZGUlFtYzNPSGRCVVc5RlMyZDNiMDlYVFRGWlZFSnNUWHBXYkU5VVkzcE5NbFpzVDFST2EwNUVWVFJhUkVwclRrZFJNVTlFUlRBS1dtcFpNMXBFU1hkUFYxRjVUMVJCWWtKbmIzSkNaMFZGUVZsUEwwMUJSVXhDUVRCTlF6Tk9iR0pIV1hSaFJ6bDZaRWRXYTAxRlFVZERhWE5IUVZGUlFncG5OemgzUVZGM1JVMW5kM2RoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlUxRVowZERhWE5IUVZGUlFtYzNPSGRCVVRCRlMyZDNiMDlYVFRGWlZFSnNUWHBXYkU5VVkzcE5NbFpzVDFST2EwNUVWVFFLV2tSS2EwNUhVVEZQUkVVd1dtcFpNMXBFU1hkUFYxRjVUMVJCWmtKbmIzSkNaMFZGUVZsUEwwMUJSVTlDUWtWTlJETktiRnB1VFhaaFIxWm9Xa2hOZGdwaVYwWndZbXBCWVVKbmIzSkNaMFZGUVZsUEwwMUJSVkJDUVhkTlEycEZkMDU2UlRCT2VsVXhUV3ByZDB4bldVdExkMWxDUWtGSFJIWjZRVUpGUVZGbkNrUkNOVzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWM1IwRlpTMHQzV1VKQ1FVZEVkbnBCUWtWUlVVc0tSRUZuZVU1VVkzbE5SR013VFhwQ2RFSm5iM0pDWjBWRlFWbFBMMDFCUlZOQ1JqaE5XRmRvTUdSSVFucFBhVGgyV2pKc01HRklWbWxNYlU1MllsTTVid3BrVjJSdVlWYzFibHB0Um1wYVV6bHlXbGhLZFZwWGVIcE1WMDUyWWxjeE1XSnRiREJsVXpoMVdqSnNNR0ZJVm1sTU0yUjJZMjEwYldKSE9UTmplVGxwQ21SWGJITmFRelUxV1ZjeGMxRklTbXhhYmsxMllVZFdhRnBJVFhaaVYwWndZbXBCTkVKbmIzSkNaMFZGUVZsUEwwMUJSVlJDUTI5TlMwUnNhazVYUlhjS1dsUk5NVnBVYXpOTmVrNXNXbFJyZWxwRVVURlBSMUY1V2tSU2EwNVVaM2hPUjFreVRqSlJlVTFFYkd0TmFtdDNTVkZaUzB0M1dVSkNRVWRFZG5wQlFncEdRVkZVUkVKR00ySXpTbkphYlhoMlpERTVhMkZZVG5kWldGSnFZVVJDYTBKbmIzSkNaMFZGUVZsUEwwMUJSVlpDUmxsTlZrZG9NR1JJUW5wUGFUaDJDbG95YkRCaFNGWnBURzFPZG1KVE9XOWtWMlJ1WVZjMWJscHRSbXBhVXpseVdsaEtkVnBYZUhwTVYwNTJZbGN4TVdKdGJEQmxVemxvV1ROU2NHSXlOWG9LVEROS01XSnVUWFpOZWtWNFRVUm5lVTlVWjNkT1JGVjJXVmhTTUZwWE1YZGtTRTEyVFZSQlYwSm5iM0pDWjBWRlFWbFBMMDFCUlZkQ1FXZE5RbTVDTVFwWmJYaHdXWHBDUjBKbmIzSkNaMFZGUVZsUEwwMUJSVmxDUkdkTlRtNUtiR05IT0RaaFNGWnVXakpzZFZveVdtaFpNbFYyWVRKV2VXSnRWbk5qZVRGcUNtSXlNWFJrVnpWd1pFaHJObU50Vm0xUGJrcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2NHSnFRMEpwWjFsTFMzZFpRa0pCU0ZkbFVVbEZRV2RTT0VKSWIwRUtaVUZDTWtGT01EbE5SM0pIZUhoRmVWbDRhMlZJU214dVRuZExhVk5zTmpRemFubDBMelJsUzJOdlFYWkxaVFpQUVVGQlFtNDVaR3BLYWxGQlFVRlJSQXBCUldOM1VsRkphRUZKU1ZkRFdYaFhLM0pKTm1GaFRXazVTR2RFTWtSc1pWbFNOa29yUmpaWmNVdFlWWGhWZGpSYU5FZHRRV2xCZVdjNVdFRlJRMmxDQ2pWc1NsRnJaVmRPVjIxbkwwcFFSSFptVDFWTlIyeEtWbEZHWkU5d2FsZ3ZZbnBCUzBKblozRm9hMnBQVUZGUlJFRjNUbTlCUkVKc1FXcEJha0UzUWpjS1RFOTRXWFJqTjA1R01EUTViMUEyVVc4NU56VmhNa0UxZWxaNVZTOUNWM1ZEVDFGdmRpdDRZM2MxZWtSc2FWaG9RMWRZVDB4T1Z6SkNia0ZEVFZGRU1BcElNVEZFZFVwRE9WRllkMUZPYlVoM2FpdEJNamxuTDB4dVpFNVFhMnBXT1VGSmVVSnhObWgzVkdaWFNVVkZSRFpCYTNCcWJYVlBVMnRpVkhKdE1rRTlDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifX19fQ=="}], "timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyzADAgEAMIICwgYJKoZIhvcNAQcCoIICszCCAq8CAQMxDTALBglghkgBZQMEAgEwgbgGCyqGSIb3DQEJEAEEoIGoBIGlMIGiAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQghTN3Etc/xMW2ECEnnJ77fF4BFTlIInKJA+klU1KR364CFQDI1qpdKNJ+eB43rDLpCBw+sGN+HxgPMjAyNjA4MDYxNDAzNDdaMAMCAQGgMqQwMC4xFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEVMBMGA1UEAxMMc2lnc3RvcmUtdHNhoAAxggHcMIIB2AIBATBRMDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAsGCWCGSAFlAwQCAaCB/DAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8XDTI2MDgwNjE0MDM0N1owLwYJKoZIhvcNAQkEMSIEIMpfp637Nc+LAhkPFU6iox6d4QDZrv1y+2irknjjsgXOMIGOBgsqhkiG9w0BCRACLzF/MH0wezB5BCCF+Se8B6tiysO0Q1bBDvyBssaIP9p6uebYcNnROs0FtzBVMD2kOzA5MRUwEwYDVQQKEwxzaWdzdG9yZS5kZXYxIDAeBgNVBAMTF3NpZ3N0b3JlLXRzYS1zZWxmc2lnbmVkAhQ6E1QvDJBh7rzBQy/Lio6LKiOLDDAKBggqhkjOPQQDAgRoMGYCMQC542sKD2Td6CYDzDxI/dgV+8SVjUZVYPo4mJTdgP6Hd7R+QAV73XhuCT3ZHaX+dO8CMQDrfFq1+W81IP5G6WQG9G5xqXqyDbIUsSmlZJHMsPkiK4+y30rW0rYtVZk8XTacep4="}]}}, "messageSignature":{"messageDigest":{"algorithm":"SHA2_256", "digest":"nbHLGKPbMel6UlMvP3Ew1dEWGrNjdvzlBO5/EGDtGcg="}, "signature":"MEQCIHt5x/7WAsel2ijjIyyHl2rX/S+wSOcREpaH0jTcyJfRAiB9xK4DFPxHWd22Qdy2WJ6kx6gIFgpigcFs0YmZFKS0dQ=="}}
build/torch-stable-abi29-cu130-x86_64-linux/nvfp4_gemm/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))
build/torch-stable-abi29-cu132-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from . import layers
2
+ from .layers import fuse_decode_projections
3
+ from .functional import gemm, gemm_swiglu
4
+ from ._pack import (
5
+ PackedWeight,
6
+ global_scale_for,
7
+ pack,
8
+ pack_swiglu,
9
+ quantize_reference,
10
+ swizzled_sf_shape,
11
+ )
12
+
13
+ __all__ = [
14
+ "PackedWeight",
15
+ "pack",
16
+ "pack_swiglu",
17
+ "quantize_reference",
18
+ "global_scale_for",
19
+ "swizzled_sf_shape",
20
+ "gemm",
21
+ "gemm_swiglu",
22
+ "layers",
23
+ "fuse_decode_projections",
24
+ ]
build/torch-stable-abi29-cu132-x86_64-linux/_nvfp4_gemm_cuda_9c5a0e3.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d789bb047d295c5147d5865809881daf3aa4384e30addbc61d5b2f4e03b23c4e
3
+ size 3260632
build/torch-stable-abi29-cu132-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _nvfp4_gemm_cuda_9c5a0e3
3
+ ops = torch.ops._nvfp4_gemm_cuda_9c5a0e3
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_nvfp4_gemm_cuda_9c5a0e3::{op_name}"
build/torch-stable-abi29-cu132-x86_64-linux/_pack.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from dataclasses import dataclass
2
+
3
+ import torch
4
+
5
+ from ._ops import ops
6
+
7
+ FLOAT8_E4M3_MAX = 448.0
8
+ FLOAT4_E2M1_MAX = 6.0
9
+ SF_VEC_SIZE = 16 # elements sharing one FP8 scale
10
+
11
+
12
+ def _round_up(x: int, m: int) -> int:
13
+ return (x + m - 1) // m * m
14
+
15
+
16
+ def swizzled_sf_shape(rows: int, cols: int) -> tuple[int, int]:
17
+ """Shape of the swizzled scale-factor tensor (dtype int32) produced by
18
+ `scaled_fp4_quant(..., is_sf_swizzled_layout=True)` for a [rows, cols]
19
+ input. Mirrors `computeSwizzledSFShape` in nvfp4_utils.cuh: 128-row tiles,
20
+ 4 FP8 scales packed per int32 column."""
21
+ return _round_up(rows, 128), _round_up(cols // SF_VEC_SIZE, 4) // 4
22
+
23
+
24
+ def global_scale_for(w: torch.Tensor) -> torch.Tensor:
25
+ """Per-tensor second-level scale: FP8_MAX * FP4_MAX / amax."""
26
+ amax = w.abs().amax().to(torch.float32)
27
+ return (FLOAT8_E4M3_MAX * FLOAT4_E2M1_MAX / amax.clamp(min=1e-12)).reshape(1)
28
+
29
+
30
+ @dataclass
31
+ class PackedWeight:
32
+ """NVFP4 representation of a bf16 matrix [n, k]."""
33
+
34
+ qweight: torch.Tensor # uint8 [n, k/2], two e2m1 values per byte
35
+ sf: torch.Tensor # int32, swizzled FP8-E4M3 block scales
36
+ global_scale: torch.Tensor # fp32 [1]
37
+ n: int
38
+ k: int
39
+ swiglu: bool = False
40
+ # uint8 [n, k/16] row-major FP8-E4M3 block scales, consumed by the W4A16
41
+ # GEMV decode path (None when only the swizzled layout is available).
42
+ sf_rowmajor: torch.Tensor | None = None
43
+
44
+ def to(self, device) -> "PackedWeight":
45
+ return PackedWeight(
46
+ qweight=self.qweight.to(device),
47
+ sf=self.sf.to(device),
48
+ global_scale=self.global_scale.to(device),
49
+ n=self.n,
50
+ k=self.k,
51
+ swiglu=self.swiglu,
52
+ sf_rowmajor=(
53
+ None if self.sf_rowmajor is None else self.sf_rowmajor.to(device)
54
+ ),
55
+ )
56
+
57
+
58
+ def pack(
59
+ w: torch.Tensor, device: str | torch.device = "cuda", swiglu: bool = False
60
+ ) -> PackedWeight:
61
+ """Quantize a bf16/fp16 matrix [n, k] to NVFP4 (requires sm100+)."""
62
+ if w.dim() != 2 or not w.is_floating_point():
63
+ raise ValueError("pack() expects a 2-D floating-point weight")
64
+ n, k = w.shape
65
+ if k % SF_VEC_SIZE != 0:
66
+ raise ValueError(f"in_features must be a multiple of {SF_VEC_SIZE}, got {k}")
67
+ wd = w.detach().to(device, torch.bfloat16).contiguous()
68
+ gs = global_scale_for(wd).to(device)
69
+ qweight, sf = ops.scaled_fp4_quant(wd, gs, True)
70
+ # Row-major scales for the W4A16 GEMV decode path; the qweight output is
71
+ # identical to the swizzled call's, so it is discarded.
72
+ _, sf_rowmajor = ops.scaled_fp4_quant(wd, gs, False)
73
+ return PackedWeight(
74
+ qweight=qweight,
75
+ sf=sf,
76
+ global_scale=gs,
77
+ n=n,
78
+ k=k,
79
+ swiglu=swiglu,
80
+ sf_rowmajor=sf_rowmajor,
81
+ )
82
+
83
+
84
+ def pack_swiglu(
85
+ w_gate: torch.Tensor, w_up: torch.Tensor, device: str | torch.device = "cuda"
86
+ ) -> PackedWeight:
87
+ """Pack gate and up projections [n, k] as one [2n, k] matrix; the GEMM
88
+ output has gate values in columns [0, n) and up in [n, 2n)."""
89
+ if w_gate.shape != w_up.shape:
90
+ raise ValueError("gate and up matrices must have the same shape")
91
+ return pack(torch.cat([w_gate, w_up], dim=0), device=device, swiglu=True)
92
+
93
+
94
+ def quantize_reference(w: torch.Tensor) -> torch.Tensor:
95
+ """Pure-torch NVFP4 emulation (no GPU kernel): quantize + dequantize to
96
+ the same numerics the kernel produces, for tests and quality analysis."""
97
+ orig_dtype = w.dtype
98
+ wf = w.float()
99
+ gs = global_scale_for(wf) # fp8_max*fp4_max/amax
100
+ n, k = wf.shape
101
+ g = wf.reshape(n, k // SF_VEC_SIZE, SF_VEC_SIZE)
102
+ # Block scale: amax/FP4_MAX * global_scale, stored in FP8-E4M3.
103
+ block_amax = g.abs().amax(-1, keepdim=True)
104
+ sf = (block_amax / FLOAT4_E2M1_MAX) * gs
105
+ sf = sf.to(torch.float8_e4m3fn).float().clamp(min=1e-12)
106
+ scaled = g * (gs / sf)
107
+ # e2m1 grid: {0, .5, 1, 1.5, 2, 3, 4, 6} with signs.
108
+ grid = torch.tensor([0.0, 0.5, 1.0, 1.5, 2.0, 3.0, 4.0, 6.0])
109
+ q = grid[(scaled.abs().unsqueeze(-1) - grid).abs().argmin(-1)] * scaled.sign()
110
+ return (q * sf / gs).reshape(n, k).to(orig_dtype)
build/torch-stable-abi29-cu132-x86_64-linux/functional.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+
3
+ from ._ops import add_op_namespace_prefix, ops
4
+ from ._pack import FLOAT4_E2M1_MAX, FLOAT8_E4M3_MAX, PackedWeight, swizzled_sf_shape
5
+
6
+
7
+ @torch.library.register_fake(add_op_namespace_prefix("scaled_fp4_quant"))
8
+ def _quant_fake(input, input_global_scale, is_sf_swizzled_layout):
9
+ n = input.size(-1)
10
+ m = input.numel() // n
11
+ out = input.new_empty((m, n // 2), dtype=torch.uint8)
12
+ if is_sf_swizzled_layout:
13
+ sf_m, sf_n = swizzled_sf_shape(m, n)
14
+ sf = input.new_empty((sf_m, sf_n), dtype=torch.int32)
15
+ else:
16
+ sf = input.new_empty((m, n // 16), dtype=torch.uint8)
17
+ return out, sf
18
+
19
+
20
+ @torch.library.register_fake(add_op_namespace_prefix("cutlass_scaled_fp4_mm"))
21
+ def _mm_fake(a, b, a_sf, b_sf, alpha):
22
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
23
+
24
+
25
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv"))
26
+ def _gemv_fake(a, b, b_sf, alpha):
27
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
28
+
29
+
30
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv_swiglu"))
31
+ def _gemv_swiglu_fake(a, b, b_sf, alpha):
32
+ return a.new_empty((a.size(0), b.size(0) // 2), dtype=torch.bfloat16)
33
+
34
+
35
+ @torch.library.register_fake(add_op_namespace_prefix("nvfp4_gemv_gated"))
36
+ def _gemv_gated_fake(a, g, b, b_sf, alpha):
37
+ return a.new_empty((a.size(0), b.size(0)), dtype=torch.bfloat16)
38
+
39
+
40
+ def _as_fp8(sf: torch.Tensor) -> torch.Tensor:
41
+ return sf.view(torch.float8_e4m3fn) if sf.dtype == torch.int32 else sf
42
+
43
+
44
+ def gemm(pw: PackedWeight, x: torch.Tensor) -> torch.Tensor:
45
+ """y[..., n] = x[..., k] @ W.T with dynamic NVFP4 activation quantization
46
+ (W4A4 on the Blackwell block-scaled tensor cores)."""
47
+ if x.shape[-1] != pw.k:
48
+ raise ValueError(f"expected {pw.k} input features, got {x.shape[-1]}")
49
+ x2 = x.reshape(-1, pw.k)
50
+ if not x2.is_contiguous():
51
+ x2 = x2.contiguous()
52
+ # Decode path: W4A16 GEMV keeps activations in bf16 (no activation
53
+ # quantization). The kernel re-reads the weight per output row, so it is
54
+ # only worthwhile for tiny m; prefill stays on the W4A4 CUTLASS path.
55
+ if x2.size(0) <= 2 and getattr(pw, "sf_rowmajor", None) is not None:
56
+ alpha = (1.0 / pw.global_scale).to(torch.float32).reshape(1)
57
+ y = ops.nvfp4_gemv(x2.to(torch.bfloat16), pw.qweight, pw.sf_rowmajor, alpha)
58
+ return y.reshape(*x.shape[:-1], pw.n)
59
+ a_amax = x2.abs().amax().to(torch.float32)
60
+ a_gs = (FLOAT8_E4M3_MAX * FLOAT4_E2M1_MAX / a_amax.clamp(min=1e-12)).reshape(1)
61
+ aq, a_sf = ops.scaled_fp4_quant(x2.to(torch.bfloat16), a_gs, True)
62
+ alpha = (1.0 / (a_gs * pw.global_scale)).to(torch.float32)
63
+ # The quant op returns swizzled scales packed 4-per-int32; the GEMM takes
64
+ # the same bytes typed as fp8-e4m3.
65
+ y = ops.cutlass_scaled_fp4_mm(aq, pw.qweight, _as_fp8(a_sf), _as_fp8(pw.sf), alpha)
66
+ return y.reshape(*x.shape[:-1], pw.n)
67
+
68
+
69
+ def gemm_swiglu(pw: PackedWeight, x: torch.Tensor) -> torch.Tensor:
70
+ """silu(x @ W_gate.T) * (x @ W_up.T) for a SwiGLU-packed matrix."""
71
+ if not pw.swiglu:
72
+ raise ValueError("packed matrix was not packed with pack_swiglu")
73
+ y = gemm(pw, x)
74
+ half = pw.n // 2
75
+ return torch.nn.functional.silu(y[..., :half]) * y[..., half:]
build/torch-stable-abi29-cu132-x86_64-linux/layers.py ADDED
@@ -0,0 +1,215 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ import torch.nn as nn
3
+
4
+ from .functional import gemm
5
+ from ._ops import ops
6
+ from ._pack import PackedWeight
7
+
8
+
9
+ class NVFP4Linear(nn.Module):
10
+ can_torch_compile = True
11
+ has_backward = False
12
+
13
+ def forward(self, x):
14
+ pw = PackedWeight(
15
+ qweight=self.weight,
16
+ sf=self.weight_sf,
17
+ global_scale=self.weight_global_scale,
18
+ n=self.out_features,
19
+ k=self.in_features,
20
+ sf_rowmajor=getattr(self, "weight_sf_rowmajor", None),
21
+ )
22
+ return gemm(pw, x)
23
+
24
+
25
+ def _ntokens(x):
26
+ return x.numel() // x.shape[-1]
27
+
28
+
29
+ def _fused_gemv(module, x):
30
+ return ops.nvfp4_gemv(
31
+ x.reshape(-1, x.shape[-1]).to(torch.bfloat16),
32
+ module.nvfp4_fused_qweight,
33
+ module.nvfp4_fused_sf,
34
+ module.nvfp4_fused_alpha,
35
+ )
36
+
37
+
38
+ class NVFP4FusedSwiGLUMLP(nn.Module):
39
+ can_torch_compile = True
40
+ has_backward = False
41
+
42
+ def forward(self, x):
43
+ if _ntokens(x) <= 2 and hasattr(self, "nvfp4_fused_qweight"):
44
+ x2 = x.reshape(-1, x.shape[-1]).to(torch.bfloat16)
45
+ h = ops.nvfp4_gemv_swiglu(
46
+ x2,
47
+ self.nvfp4_fused_qweight,
48
+ self.nvfp4_fused_sf,
49
+ self.nvfp4_fused_alpha,
50
+ )
51
+ return self.down_proj(h.reshape(*x.shape[:-1], -1))
52
+ return self.down_proj(self.act_fn(self.gate_proj(x)) * self.up_proj(x))
53
+
54
+
55
+ class NVFP4GatedAttention(nn.Module):
56
+ can_torch_compile = True
57
+ has_backward = False
58
+
59
+ def forward(
60
+ self,
61
+ hidden_states,
62
+ position_embeddings=None,
63
+ attention_mask=None,
64
+ past_key_values=None,
65
+ **kwargs,
66
+ ):
67
+ from transformers.modeling_utils import ALL_ATTENTION_FUNCTIONS
68
+
69
+ input_shape = hidden_states.shape[:-1]
70
+ hidden_shape = (*input_shape, -1, self.head_dim)
71
+ og = None
72
+ if _ntokens(hidden_states) <= 2 and hasattr(self, "nvfp4_fused_qweight"):
73
+ fused = _fused_gemv(self, hidden_states)
74
+ q, k, v, *rest = torch.split(fused, self.nvfp4_fused_splits, dim=-1)
75
+ og = rest[0] if rest else None
76
+ else:
77
+ q = self.q_proj(hidden_states)
78
+ k = self.k_proj(hidden_states)
79
+ v = self.v_proj(hidden_states)
80
+ query_states = q.reshape(hidden_shape).transpose(1, 2)
81
+ key_states = k.reshape(hidden_shape).transpose(1, 2)
82
+ value_states = v.reshape(hidden_shape).transpose(1, 2)
83
+
84
+ norm = getattr(self, "qk_norm", None)
85
+ if norm is not None:
86
+ query_states = norm(query_states) * getattr(self, "qk_scale_factor", 1.0)
87
+ key_states = norm(key_states)
88
+
89
+ if position_embeddings is not None:
90
+ cos, sin = position_embeddings
91
+ query_states, key_states = self.nvfp4_rope(
92
+ query_states, key_states, cos, sin
93
+ )
94
+
95
+ if past_key_values is not None:
96
+ key_states, value_states = past_key_values.update(
97
+ key_states, value_states, self.layer_idx
98
+ )
99
+
100
+ attention_interface = ALL_ATTENTION_FUNCTIONS.get_interface(
101
+ self.config._attn_implementation, self.nvfp4_eager
102
+ )
103
+ attn_output, attn_weights = attention_interface(
104
+ self,
105
+ query_states,
106
+ key_states,
107
+ value_states,
108
+ attention_mask,
109
+ dropout=self.attention_dropout if self.training else 0.0,
110
+ scaling=self.scaling,
111
+ sliding_window=getattr(self, "sliding_window", None),
112
+ **kwargs,
113
+ )
114
+
115
+ attn_output = attn_output.reshape(*input_shape, -1).contiguous()
116
+ if og is None and getattr(self, "gate_proj", None) is not None:
117
+ og = self.gate_proj(hidden_states)
118
+ o = self.o_proj
119
+ if og is not None and _ntokens(attn_output) <= 2 and _has_nvfp4(o):
120
+ a2 = attn_output.reshape(-1, attn_output.shape[-1])
121
+ og2 = og.reshape(a2.shape).contiguous()
122
+ alpha = (1.0 / o.weight_global_scale.float()).reshape(1)
123
+ y = ops.nvfp4_gemv_gated(a2, og2, o.weight, o.weight_sf_rowmajor, alpha)
124
+ return y.reshape(*input_shape, -1), attn_weights
125
+ if og is not None:
126
+ attn_output = attn_output * torch.sigmoid(og.reshape(attn_output.shape))
127
+ return o(attn_output), attn_weights
128
+
129
+
130
+ def _has_nvfp4(m):
131
+ return m is not None and hasattr(m, "weight_sf_rowmajor")
132
+
133
+
134
+ def _is_silu(act):
135
+ return isinstance(act, nn.SiLU) or type(act).__name__ == "SiLUActivation"
136
+
137
+
138
+ def _attach_fused(module, mods):
139
+ # Keep a single packed allocation and expose row views back through the
140
+ # original projection modules. Previously fusion allocated concatenated
141
+ # weights while retaining an independent copy in every projection.
142
+ # Attention and SwiGLU use row-concatenated storage; the latter is handled
143
+ # by the GEMV kernel using the gate/up half offset.
144
+ fused_qweight = torch.cat([m.weight for m in mods], 0).contiguous()
145
+ fused_sf = torch.cat([m.weight_sf_rowmajor for m in mods], 0).contiguous()
146
+ module.nvfp4_fused_qweight = fused_qweight
147
+ module.nvfp4_fused_sf = fused_sf
148
+ module.nvfp4_fused_alpha = torch.cat(
149
+ [
150
+ (1.0 / m.weight_global_scale.float())
151
+ .reshape(1)
152
+ .expand(m.out_features)
153
+ .contiguous()
154
+ for m in mods
155
+ ]
156
+ )
157
+ module.nvfp4_fused_splits = [m.out_features for m in mods]
158
+
159
+ offset = 0
160
+ for m in mods:
161
+ end = offset + m.out_features
162
+ m.weight = fused_qweight[offset:end]
163
+ m.weight_sf_rowmajor = fused_sf[offset:end]
164
+ offset = end
165
+
166
+
167
+ def _capture_helpers(cls, rope_fn):
168
+ if getattr(cls, "nvfp4_eager", None) is not None:
169
+ return
170
+ fwd = cls.forward
171
+ names = ("apply_rotary_pos_emb_interleave", "apply_rotary_pos_emb")
172
+ rope = rope_fn or next(
173
+ (fwd.__globals__[n] for n in names if n in fwd.__code__.co_names), None
174
+ )
175
+ cls.nvfp4_rope = staticmethod(rope) if rope is not None else None
176
+ cls.nvfp4_eager = staticmethod(fwd.__globals__["eager_attention_forward"])
177
+
178
+
179
+ def _mark(cls, layer_name):
180
+ if getattr(cls, "kernel_layer_name", None) is not None:
181
+ return
182
+ try:
183
+ from kernels import use_kernel_forward_from_hub
184
+
185
+ use_kernel_forward_from_hub(layer_name)(cls)
186
+ except Exception:
187
+ cls.kernel_layer_name = layer_name
188
+
189
+
190
+ def fuse_decode_projections(model, rope_fn=None):
191
+ n = 0
192
+ for mod in model.modules():
193
+ qkv = [getattr(mod, p, None) for p in ("q_proj", "k_proj", "v_proj")]
194
+ if (
195
+ all(_has_nvfp4(p) for p in qkv)
196
+ and hasattr(mod, "o_proj")
197
+ and hasattr(mod, "layer_idx")
198
+ ):
199
+ gate = getattr(mod, "gate_proj", None)
200
+ _attach_fused(mod, qkv + ([gate] if _has_nvfp4(gate) else []))
201
+ _capture_helpers(type(mod), rope_fn)
202
+ _mark(type(mod), "NVFP4GatedAttention")
203
+ n += 1
204
+ continue
205
+ gu = [getattr(mod, p, None) for p in ("gate_proj", "up_proj")]
206
+ if (
207
+ all(_has_nvfp4(p) for p in gu)
208
+ and hasattr(mod, "down_proj")
209
+ and _is_silu(getattr(mod, "act_fn", None))
210
+ and gu[0].out_features == gu[1].out_features
211
+ ):
212
+ _attach_fused(mod, gu)
213
+ _mark(type(mod), "NVFP4SwiGLUMLP")
214
+ n += 1
215
+ return n
build/torch-stable-abi29-cu132-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "nvfp4-gemm",
3
+ "id": "_nvfp4_gemm_cuda_9c5a0e3",
4
+ "version": 1,
5
+ "license": "Apache-2.0",
6
+ "upstream": "git+https://github.com/vllm-project/vllm.git",
7
+ "python-depends": [],
8
+ "backend": {
9
+ "type": "cuda",
10
+ "archs": [
11
+ "10.0a",
12
+ "12.0a"
13
+ ]
14
+ },
15
+ "digest": {
16
+ "algorithm": "sha256",
17
+ "files": {
18
+ "__init__.py": "ARXik9k13ZmZ/1tDxHgH6bQyJ8Cn6wmCrz5WEM+DABI=",
19
+ "_nvfp4_gemm_cuda_9c5a0e3.abi3.so": "14m7BH0pXFFH1YZYCYgdrzqkOE4wrdvGHVsvTgOyPE4=",
20
+ "_ops.py": "+jBXp3Uoatvj6U0lcsYwN/3KdSd8+Aknhm5T+xbff6w=",
21
+ "_pack.py": "jATF2LhpSLUEiPWSrhsCWeyxIyn80CSy2QJNkKMADlc=",
22
+ "functional.py": "ZTd0hcUOkDdU9ZvpQMPC81KBP3FUvkmfZu7d7mhZRss=",
23
+ "layers.py": "asplwNUY3+drs57APjw3J9kFLtswK9YI0SxRVHZj2Hg=",
24
+ "nvfp4_gemm/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
25
+ }
26
+ },
27
+ "provenance": {
28
+ "kernel-builder": {
29
+ "version": "0.17.0-dev0",
30
+ "sha": "19aaa6421e674e9fecc352bbae6eab81d19a6bf4",
31
+ "dirty": false
32
+ },
33
+ "kernel": {
34
+ "sha": "9c5a0e35e9733ee93d458d2d4d5814f67d209d29",
35
+ "dirty": false
36
+ }
37
+ }
38
+ }
build/torch-stable-abi29-cu132-x86_64-linux/metadata.json.sigstore ADDED
@@ -0,0 +1 @@
 
 
1
+ {"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"certificate":{"rawBytes":"MIIHSzCCBtGgAwIBAgIUQP0jgzq6iUNWh+2Y8Rxg9VqrtCMwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjYwODA2MTQwMzQ3WhcNMjYwODA2MTQxMzQ3WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEbvXQv6+bD792eCLro0ZEBoUesPS3o66yHG0PtgJa1NhBoB4yV2geGnvcXQLZq7PWvs5pl1I6ycA6O+pv3a0jKaOCBfAwggXsMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUcuoodiclwg80Xgmz2zHcxEaSRcMwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wawYDVR0RAQH/BGEwX4ZdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDkGCisGAQQBg78wAQEEK2h0dHBzOi8vdG9rZW4uYWN0aW9ucy5naXRodWJ1c2VyY29udGVudC5jb20wHwYKKwYBBAGDvzABAgQRd29ya2Zsb3dfZGlzcGF0Y2gwNgYKKwYBBAGDvzABAwQoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTATBgorBgEEAYO/MAEEBAVCdWlsZDArBgorBgEEAYO/MAEFBB1odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eTAdBgorBgEEAYO/MAEGBA9yZWZzL2hlYWRzL21haW4wOwYKKwYBBAGDvzABCAQtDCtodHRwczovL3Rva2VuLmFjdGlvbnMuZ2l0aHVidXNlcmNvbnRlbnQuY29tMG0GCisGAQQBg78wAQkEXwxdaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5Ly5naXRodWIvd29ya2Zsb3dzL2J1aWxkLnlhbWxAcmVmcy9oZWFkcy9tYWluMDgGCisGAQQBg78wAQoEKgwoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTAbBgorBgEEAYO/MAELBA0MC3NlbGYtaG9zdGVkMEAGCisGAQQBg78wAQwEMgwwaHR0cHM6Ly9naXRodWIuY29tL2h1Z2dpbmdmYWNlL2tlcm5lbHMtY29tbXVuaXR5MDgGCisGAQQBg78wAQ0EKgwoOWM1YTBlMzVlOTczM2VlOTNkNDU4ZDJkNGQ1ODE0ZjY3ZDIwOWQyOTAfBgorBgEEAYO/MAEOBBEMD3JlZnMvaGVhZHMvbWFpbjAaBgorBgEEAYO/MAEPBAwMCjEwNzE0NzU1MjkwLgYKKwYBBAGDvzABEAQgDB5odHRwczovL2dpdGh1Yi5jb20vaHVnZ2luZ2ZhY2UwGAYKKwYBBAGDvzABEQQKDAgyNTcyMDc0MzBtBgorBgEEAYO/MAESBF8MXWh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS8uZ2l0aHViL3dvcmtmbG93cy9idWlsZC55YW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAETBCoMKDljNWEwZTM1ZTk3MzNlZTkzZDQ1OGQyZDRkNTgxNGY2N2QyMDlkMjkwIQYKKwYBBAGDvzABFAQTDBF3b3JrZmxvd19kaXNwYXRjaDBkBgorBgEEAYO/MAEVBFYMVGh0dHBzOi8vZ2l0aHViLmNvbS9odWdnaW5nZmFjZS9rZXJuZWxzLWNvbW11bml0eS9hY3Rpb25zL3J1bnMvMzExMDgyOTgwNDUvYXR0ZW1wdHMvMTAWBgorBgEEAYO/MAEWBAgMBnB1YmxpYzBGBgorBgEEAYO/MAEYBDgMNnJlcG86aHVnZ2luZ2ZhY2Uva2VybmVscy1jb21tdW5pdHk6cmVmOnJlZnMvaGVhZHMvbWFpbjCBigYKKwYBBAHWeQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAABn9djKbEAAAQDAEcwRQIhAOsiNNSfv3dX77qcN9iuNZj2oTpkyRL3wAyNHTGKxZe0AiBP1JX+m9rtjPbYEXSF8Cw+hk5BrZ5fxzNiZC3TI9FfgjAKBggqhkjOPQQDAwNoADBlAjEA/s95wwFRDQUOX06PKVEt45K6RnhS+AUm70/LIXqbqE+4kFjW5ruFyV+potMxLYKYAjBaoT33gaX/2cnP/Es5mBs7UBabUm/CfFYL+CBf0OzFKMXsYn3Gb+VRfAghuXxY+qI="}, "tlogEntries":[{"logIndex":"2360822794", "logId":{"keyId":"wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="}, "kindVersion":{"kind":"hashedrekord", "version":"0.0.1"}, "integratedTime":"1786025028", "inclusionPromise":{"signedEntryTimestamp":"MEQCIAJ+mz7Vh4YuC2Ju2kJBDk/nOMGzpY13mQRZEPtOVyB7AiBjx8QlP9qfjGWQWmDYA/QtrrVlnlcIWuGfMXeOK4yAyA=="}, "inclusionProof":{"logIndex":"2238918532", "rootHash":"rKIuTXmGd55gfj4rnFlrOuOrqWBpfOkbhZyhvBNAvj0=", "treeSize":"2238918554", "hashes":["l1vm1EL7VhYR/fL9IVTYJcPVc+efJzqct14GrWlyXLg=", "0F/8qEd4yf19WRRy8re+mSmkmNm9rrWnZOhzgKScN2w=", "Jz+PRA4FGGCnAdj3jIZjiHxchfAsf0y3ZBZdscAIVkw=", "DwgeNpdx/Xl0foZN932HJNaNZGxS/ApVyicyhFgh/3A=", "Khbu+Fuzutx3ADkt7AeAP4aiISNWAcz/kswjB45B21c=", "5WcoMXSpzvnq4KWL+iZGwWkjGDYd22yUN5/2n1rJbDw=", "yqmsZOsG1fWCdReSoyBV2DCsoA3XKvTEPnOnexLwYnI=", "yOi1SZCAJyQeGFNfXEyJmZfgE/yHiViHJh7AkVStYx0=", "ZDDgBLJeZMIedbwsKMeIdE+rnhT6LFyxK01q4RQswB8=", "1QfEqQEF22LcM0N+z5i77k0Oy00Arz4yjX7D1LTJP34=", "tSch9ErXVKdLoCFPSZ8D8aLt8T+YHXh/0cV7rQ+HxWc=", "VN19Pd6HJSN2xUEEfXlAK+NBL7uztCXLEaUeuJxgAbY=", "2DzQwQqFGfdTX3BVYQkcDxiT/TBV7lnr+ya29yCNm+o=", "QVx27ou3XJyzOgzVsHRov+ie3M7wOfT492kpmkbAeBo=", "lLhES7her3Dzu/HSMpPVolTSfVQlKbD23EIsEwXq4V4=", "kVyfjHB6dSBvAqnJBVxvTpb/VPDK9zzq5/txahxnbhc=", "u23nWGlBSoQOMevXp72NpPaDjuN0qcB01i17Odcs3Wk=", "i5Zl8FZrDwxCDv2e2DNO2M8JvpR/c11ElvCZS53/teA=", "xH/DCseLHr9eKoYT8qsORZK7zVdEGYWHuVtsVrD95wY="], "checkpoint":{"envelope":"rekor.sigstore.dev - 1193050959916656506\n2238918554\nrKIuTXmGd55gfj4rnFlrOuOrqWBpfOkbhZyhvBNAvj0=\n\n— rekor.sigstore.dev wNI9ajBFAiEAmIz2SLnVdzVNEMYAlAzbQ2Qqy+ULNVITLFWfsOxRQvYCIE9KfpPwYh1cVJZGK92tvhqJWkRphkx2o0JBP8+RXV8g\n"}}, "canonicalizedBody":"eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiaGFzaGVkcmVrb3JkIiwic3BlYyI6eyJkYXRhIjp7Imhhc2giOnsiYWxnb3JpdGhtIjoic2hhMjU2IiwidmFsdWUiOiIxNGUxZTJjODY3YzAyZDVlNGYyYTNjMmM2NjUyNzk3MTY1ZGJmMzlhODZmYTkwYzliNmQwOWZjYmQ1ZWRlZWVjIn19LCJzaWduYXR1cmUiOnsiY29udGVudCI6Ik1FWUNJUUMrSVJ0ZituN080NFVLMkJONlkybk4xN2hnaUNwUHpIRm9taklEL0p0K1NnSWhBT3plcTBFRk1WRGJEYisvVFViT3pUQWdRWFhCQS8vUURYS2tVbDVBeENDOSIsInB1YmxpY0tleSI6eyJjb250ZW50IjoiTFMwdExTMUNSVWRKVGlCRFJWSlVTVVpKUTBGVVJTMHRMUzB0Q2sxSlNVaFRla05EUW5SSFowRjNTVUpCWjBsVlVWQXdhbWQ2Y1RacFZVNVhhQ3N5V1RoU2VHYzVWbkZ5ZEVOTmQwTm5XVWxMYjFwSmVtb3dSVUYzVFhjS1RucEZWazFDVFVkQk1WVkZRMmhOVFdNeWJHNWpNMUoyWTIxVmRWcEhWakpOVWpSM1NFRlpSRlpSVVVSRmVGWjZZVmRrZW1SSE9YbGFVekZ3WW01U2JBcGpiVEZzV2tkc2FHUkhWWGRJYUdOT1RXcFpkMDlFUVRKTlZGRjNUWHBSTTFkb1kwNU5hbGwzVDBSQk1rMVVVWGhOZWxFelYycEJRVTFHYTNkRmQxbElDa3R2V2tsNmFqQkRRVkZaU1V0dldrbDZhakJFUVZGalJGRm5RVVZpZGxoUmRqWXJZa1EzT1RKbFEweHliekJhUlVKdlZXVnpVRk16YnpZMmVVaEhNRkFLZEdkS1lURk9hRUp2UWpSNVZqSm5aVWR1ZG1OWVVVeGFjVGRRVjNaek5YQnNNVWsyZVdOQk5rOHJjSFl6WVRCcVMyRlBRMEptUVhkbloxaHpUVUUwUndwQk1WVmtSSGRGUWk5M1VVVkJkMGxJWjBSQlZFSm5UbFpJVTFWRlJFUkJTMEpuWjNKQ1owVkdRbEZqUkVGNlFXUkNaMDVXU0ZFMFJVWm5VVlZqZFc5dkNtUnBZMngzWnpnd1dHZHRlako2U0dONFJXRlRVbU5OZDBoM1dVUldVakJxUWtKbmQwWnZRVlV6T1ZCd2VqRlphMFZhWWpWeFRtcHdTMFpYYVhocE5Ga0tXa1E0ZDJGM1dVUldVakJTUVZGSUwwSkhSWGRZTkZwa1lVaFNNR05JVFRaTWVUbHVZVmhTYjJSWFNYVlpNamwwVERKb01Wb3laSEJpYldSdFdWZE9iQXBNTW5Sc1kyMDFiR0pJVFhSWk1qbDBZbGhXZFdGWVVqVk1lVFZ1WVZoU2IyUlhTWFprTWpsNVlUSmFjMkl6WkhwTU1rb3hZVmQ0YTB4dWJHaGlWM2hCQ21OdFZtMWplVGx2V2xkR2EyTjVPWFJaVjJ4MVRVUnJSME5wYzBkQlVWRkNaemM0ZDBGUlJVVkxNbWd3WkVoQ2VrOXBPSFprUnpseVdsYzBkVmxYVGpBS1lWYzVkV041Tlc1aFdGSnZaRmRLTVdNeVZubFpNamwxWkVkV2RXUkROV3BpTWpCM1NIZFpTMHQzV1VKQ1FVZEVkbnBCUWtGblVWSmtNamw1WVRKYWN3cGlNMlJtV2tkc2VtTkhSakJaTW1kM1RtZFpTMHQzV1VKQ1FVZEVkbnBCUWtGM1VXOVBWMDB4V1ZSQ2JFMTZWbXhQVkdONlRUSldiRTlVVG10T1JGVTBDbHBFU210T1IxRXhUMFJGTUZwcVdUTmFSRWwzVDFkUmVVOVVRVlJDWjI5eVFtZEZSVUZaVHk5TlFVVkZRa0ZXUTJSWGJITmFSRUZ5UW1kdmNrSm5SVVVLUVZsUEwwMUJSVVpDUWpGdlpGZGtibUZYTlc1YWJVWnFXbE01Y2xwWVNuVmFWM2g2VEZkT2RtSlhNVEZpYld3d1pWUkJaRUpuYjNKQ1owVkZRVmxQTHdwTlFVVkhRa0U1ZVZwWFducE1NbWhzV1ZkU2Vrd3lNV2hoVnpSM1QzZFpTMHQzV1VKQ1FVZEVkbnBCUWtOQlVYUkVRM1J2WkVoU2QyTjZiM1pNTTFKMkNtRXlWblZNYlVacVpFZHNkbUp1VFhWYU1td3dZVWhXYVdSWVRteGpiVTUyWW01U2JHSnVVWFZaTWpsMFRVY3dSME5wYzBkQlVWRkNaemM0ZDBGUmEwVUtXSGQ0WkdGSVVqQmpTRTAyVEhrNWJtRllVbTlrVjBsMVdUSTVkRXd5YURGYU1tUndZbTFrYlZsWFRteE1NblJzWTIwMWJHSklUWFJaTWpsMFlsaFdkUXBoV0ZJMVRIazFibUZZVW05a1YwbDJaREk1ZVdFeVduTmlNMlI2VERKS01XRlhlR3RNYm14b1lsZDRRV050Vm0xamVUbHZXbGRHYTJONU9YUlpWMngxQ2sxRVowZERhWE5IUVZGUlFtYzNPSGRCVVc5RlMyZDNiMDlYVFRGWlZFSnNUWHBXYkU5VVkzcE5NbFpzVDFST2EwNUVWVFJhUkVwclRrZFJNVTlFUlRBS1dtcFpNMXBFU1hkUFYxRjVUMVJCWWtKbmIzSkNaMFZGUVZsUEwwMUJSVXhDUVRCTlF6Tk9iR0pIV1hSaFJ6bDZaRWRXYTAxRlFVZERhWE5IUVZGUlFncG5OemgzUVZGM1JVMW5kM2RoU0ZJd1kwaE5Oa3g1T1c1aFdGSnZaRmRKZFZreU9YUk1NbWd4V2pKa2NHSnRaRzFaVjA1c1RESjBiR050Tld4aVNFMTBDbGt5T1hSaVdGWjFZVmhTTlUxRVowZERhWE5IUVZGUlFtYzNPSGRCVVRCRlMyZDNiMDlYVFRGWlZFSnNUWHBXYkU5VVkzcE5NbFpzVDFST2EwNUVWVFFLV2tSS2EwNUhVVEZQUkVVd1dtcFpNMXBFU1hkUFYxRjVUMVJCWmtKbmIzSkNaMFZGUVZsUEwwMUJSVTlDUWtWTlJETktiRnB1VFhaaFIxWm9Xa2hOZGdwaVYwWndZbXBCWVVKbmIzSkNaMFZGUVZsUEwwMUJSVkJDUVhkTlEycEZkMDU2UlRCT2VsVXhUV3ByZDB4bldVdExkMWxDUWtGSFJIWjZRVUpGUVZGbkNrUkNOVzlrU0ZKM1kzcHZka3d5WkhCa1IyZ3hXV2sxYW1JeU1IWmhTRlp1V2pKc2RWb3lXbWhaTWxWM1IwRlpTMHQzV1VKQ1FVZEVkbnBCUWtWUlVVc0tSRUZuZVU1VVkzbE5SR013VFhwQ2RFSm5iM0pDWjBWRlFWbFBMMDFCUlZOQ1JqaE5XRmRvTUdSSVFucFBhVGgyV2pKc01HRklWbWxNYlU1MllsTTVid3BrVjJSdVlWYzFibHB0Um1wYVV6bHlXbGhLZFZwWGVIcE1WMDUyWWxjeE1XSnRiREJsVXpoMVdqSnNNR0ZJVm1sTU0yUjJZMjEwYldKSE9UTmplVGxwQ21SWGJITmFRelUxV1ZjeGMxRklTbXhhYmsxMllVZFdhRnBJVFhaaVYwWndZbXBCTkVKbmIzSkNaMFZGUVZsUEwwMUJSVlJDUTI5TlMwUnNhazVYUlhjS1dsUk5NVnBVYXpOTmVrNXNXbFJyZWxwRVVURlBSMUY1V2tSU2EwNVVaM2hPUjFreVRqSlJlVTFFYkd0TmFtdDNTVkZaUzB0M1dVSkNRVWRFZG5wQlFncEdRVkZVUkVKR00ySXpTbkphYlhoMlpERTVhMkZZVG5kWldGSnFZVVJDYTBKbmIzSkNaMFZGUVZsUEwwMUJSVlpDUmxsTlZrZG9NR1JJUW5wUGFUaDJDbG95YkRCaFNGWnBURzFPZG1KVE9XOWtWMlJ1WVZjMWJscHRSbXBhVXpseVdsaEtkVnBYZUhwTVYwNTJZbGN4TVdKdGJEQmxVemxvV1ROU2NHSXlOWG9LVEROS01XSnVUWFpOZWtWNFRVUm5lVTlVWjNkT1JGVjJXVmhTTUZwWE1YZGtTRTEyVFZSQlYwSm5iM0pDWjBWRlFWbFBMMDFCUlZkQ1FXZE5RbTVDTVFwWmJYaHdXWHBDUjBKbmIzSkNaMFZGUVZsUEwwMUJSVmxDUkdkTlRtNUtiR05IT0RaaFNGWnVXakpzZFZveVdtaFpNbFYyWVRKV2VXSnRWbk5qZVRGcUNtSXlNWFJrVnpWd1pFaHJObU50Vm0xUGJrcHNXbTVOZG1GSFZtaGFTRTEyWWxkR2NHSnFRMEpwWjFsTFMzZFpRa0pCU0ZkbFVVbEZRV2RTT0VKSWIwRUtaVUZDTWtGT01EbE5SM0pIZUhoRmVWbDRhMlZJU214dVRuZExhVk5zTmpRemFubDBMelJsUzJOdlFYWkxaVFpQUVVGQlFtNDVaR3BMWWtWQlFVRlJSQXBCUldOM1VsRkphRUZQYzJsT1RsTm1kak5rV0RjM2NXTk9PV2wxVGxwcU1tOVVjR3Q1VWt3emQwRjVUa2hVUjB0NFdtVXdRV2xDVURGS1dDdHRPWEowQ21wUVlsbEZXRk5HT0VOM0syaHJOVUp5V2pWbWVIcE9hVnBETTFSSk9VWm1aMnBCUzBKblozRm9hMnBQVUZGUlJFRjNUbTlCUkVKc1FXcEZRUzl6T1RVS2QzZEdVa1JSVlU5WU1EWlFTMVpGZERRMVN6WlNibWhUSzBGVmJUY3dMMHhKV0hGaWNVVXJOR3RHYWxjMWNuVkdlVllyY0c5MFRYaE1XVXRaUVdwQ1lRcHZWRE16WjJGWUx6SmpibEF2UlhNMWJVSnpOMVZDWVdKVmJTOURaa1paVEN0RFFtWXdUM3BHUzAxWWMxbHVNMGRpSzFaU1prRm5hSFZZZUZrcmNVazlDaTB0TFMwdFJVNUVJRU5GVWxSSlJrbERRVlJGTFMwdExTMEsifX19fQ=="}], "timestampVerificationData":{"rfc3161Timestamps":[{"signedTimestamp":"MIICyDADAgEAMIICvwYJKoZIhvcNAQcCoIICsDCCAqwCAQMxDTALBglghkgBZQMEAgEwgbcGCyqGSIb3DQEJEAEEoIGnBIGkMIGhAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgh4l2yHCu2Fcn5cUOXzkBeFDycvtXlAwWfstiCWJHOBICFCoS8RsGtrqrA0MYbJJ1ijGKLC3/GA8yMDI2MDgwNjE0MDM0OFowAwIBAaAypDAwLjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MRUwEwYDVQQDEwxzaWdzdG9yZS10c2GgADGCAdowggHWAgEBMFEwOTEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MSAwHgYDVQQDExdzaWdzdG9yZS10c2Etc2VsZnNpZ25lZAIUOhNULwyQYe68wUMvy4qOiyojiwwwCwYJYIZIAWUDBAIBoIH8MBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjYwODA2MTQwMzQ4WjAvBgkqhkiG9w0BCQQxIgQg01rCf9QOSiyZ4ePyHpLBqsF0VteUJpog/dh5HI/aZF0wgY4GCyqGSIb3DQEJEAIvMX8wfTB7MHkEIIX5J7wHq2LKw7RDVsEO/IGyxog/2nq55thw2dE6zQW3MFUwPaQ7MDkxFTATBgNVBAoTDHNpZ3N0b3JlLmRldjEgMB4GA1UEAxMXc2lnc3RvcmUtdHNhLXNlbGZzaWduZWQCFDoTVC8MkGHuvMFDL8uKjosqI4sMMAoGCCqGSM49BAMCBGYwZAIwPjTPW5DMjgqk0A64hqM6XisNWoNvg6jPTrOWBB56tPdRKkapJd0uAl8aFjYnhRM2AjBGOab1G98aPMpxMrVAJ60YS1Pf0MzpeZhaYTbUf/nREyNTRYeuZ9V30AkZlQ7CYnQ="}]}}, "messageSignature":{"messageDigest":{"algorithm":"SHA2_256", "digest":"FOHiyGfALV5PKjwsZlJ5cWXb85qG+pDJttCfy9Xt7uw="}, "signature":"MEYCIQC+IRtf+n7O44UK2BN6Y2nN17hgiCpPzHFomjID/Jt+SgIhAOzeq0EFMVDbDb+/TUbOzTAgQXXBA//QDXKkUl5AxCC9"}}
build/torch-stable-abi29-cu132-x86_64-linux/nvfp4_gemm/__init__.py ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import ctypes
2
+ import importlib.util
3
+ import sys
4
+ from pathlib import Path
5
+ from types import ModuleType
6
+
7
+
8
+ def _import_from_path(file_path: Path) -> ModuleType:
9
+ # We cannot use the module name as-is, after adding it to `sys.modules`,
10
+ # it would also be used for other imports. So, we make a module name that
11
+ # depends on the path for it to be unique using the hex-encoded hash of
12
+ # the path.
13
+ path_hash = "{:x}".format(ctypes.c_size_t(hash(file_path.absolute())).value)
14
+ module_name = path_hash
15
+ spec = importlib.util.spec_from_file_location(module_name, file_path)
16
+ if spec is None:
17
+ raise ImportError(f"Cannot load spec for {module_name} from {file_path}")
18
+ module = importlib.util.module_from_spec(spec)
19
+ if module is None:
20
+ raise ImportError(f"Cannot load module {module_name} from spec")
21
+ sys.modules[module_name] = module
22
+ spec.loader.exec_module(module) # type: ignore
23
+ return module
24
+
25
+
26
+ globals().update(vars(_import_from_path(Path(__file__).parent.parent / "__init__.py")))