liangsu9988 commited on
Commit
da12a7f
·
verified ·
1 Parent(s): 5589423

Promote latest kernel artifacts to main

Browse files
README.md CHANGED
@@ -1,9 +1,98 @@
1
- # flashrt/grouped-moe-gemv
2
 
3
- This repository is a compatibility mirror for older `kernels` clients
4
- that resolve repositories through the default Hugging Face model repo API.
 
5
 
6
- Canonical Kernel Hub repo: https://huggingface.co/kernels/flashrt/grouped-moe-gemv
7
 
8
- Do not edit this mirror by hand. It is generated from the Kernel Hub
9
- `vN` branches and contains the same `build/**` artifacts.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # grouped-moe-gemv
2
 
3
+ FlashRT native CUDA grouped expert projection kernels for Blackwell decode and
4
+ small verify batches. Version 2 adds W4A4 with device-side top-k routing while
5
+ preserving the version 1 W4A16 APIs.
6
 
7
+ ## Hardware Backends
8
 
9
+ - SM110 (Jetson AGX Thor): W4A16 decode and grouped expert GEMV use the
10
+ FlashRT edge backend validated by FlashRT PR #169. This target is compiled
11
+ independently with `FLASHRT_W4A16_EDGE_UNROLL=2`; the SM120 value remains 4.
12
+ - SM120/SM121: W4A16 and block-scaled-MMA W4A4 paths are available.
13
+ - W4A4 is intentionally rejected on SM110 because that implementation uses
14
+ the SM120 block-scaled MMA path. It never silently falls back or launches an
15
+ incompatible cubin.
16
+
17
+ ## Functions
18
+
19
+ - `w4a16_decode_gemv_bf16(x_bf16, weight_packed, sfb, alpha=1.0, out=None)`
20
+ - `grouped_w4a16_gemv_bf16(activations, weight_stack, sfb_stack, alpha_stack, expert_idx, n, w_stride=None, sfb_stride=None, out=None)`
21
+ - `quantize_activations_nvfp4_bf16(activations, packed=None, sfa=None)`
22
+ - `quantize_weights_nvfp4_bf16(weights, packed=None, sfb=None)`
23
+ - `grouped_w4a4_gemv_bf16(activations_packed, weight_stack, sfa, sfb_stack, alpha_stack, expert_idx, out=None)`
24
+ - `grouped_w4a4_gemv_from_bf16(activations, weight_stack, sfb_stack, alpha_stack, expert_idx, packed=None, sfa=None, out=None)`
25
+
26
+ The grouped API runs one BF16-activation x NVFP4-weight GEMV per routed slot.
27
+ It is intended for static routed expert batches where the caller already owns
28
+ packed weights and swizzled scale-factor buffers.
29
+
30
+ On SM120/SM121, the W4A4 API accepts packed activations `[M,K/2]`, expert weights
31
+ `[E,N,K/2]`, and a contiguous device routing tensor `[M,top_k]`. It emits
32
+ `[M,top_k,N]` in one grouped compute launch. For down projections with a
33
+ different activation per routed pair, flatten to `M=routed_pairs, top_k=1`.
34
+
35
+ `K` must be divisible by 16 and `N` by 8. Target `K%64==0` shapes use tuned
36
+ SM120 paths; the remaining `K%16` shapes use a fixed-order SIMT contract path.
37
+ No atomics, host synchronization, or dynamic workspace are used by the native
38
+ ops. Pass `packed`, `sfa`, and `out` buffers to the composed helper for
39
+ allocation-free CUDA Graph capture.
40
+
41
+ ## Example
42
+
43
+ ```python
44
+ from kernels import get_kernel
45
+ import torch
46
+
47
+ try:
48
+ moe = get_kernel(
49
+ "flashrt/grouped-moe-gemv", version=2, trust_remote_code=True
50
+ )
51
+ except TypeError: # kernels==0.12.x compatibility
52
+ moe = get_kernel("flashrt/grouped-moe-gemv", version=2)
53
+
54
+ M, TOP_K, E, N, K = 7, 8, 8, 1024, 2048
55
+ x = torch.randn(M, K, device="cuda", dtype=torch.bfloat16)
56
+ expert_idx = torch.randint(E, (M, TOP_K), device="cuda", dtype=torch.int32)
57
+
58
+ def sf_bytes(rows, dim):
59
+ return ((rows + 127) // 128) * (((dim // 16) + 3) // 4) * 512
60
+
61
+ # Do this once while loading the checkpoint, not in the inference hot path.
62
+ weights_bf16 = torch.randn(E, N, K, device="cuda", dtype=torch.bfloat16)
63
+ weights_packed = torch.empty(E, N, K // 2, device="cuda", dtype=torch.uint8)
64
+ weight_sfs = torch.empty(E, sf_bytes(N, K), device="cuda", dtype=torch.uint8)
65
+ for expert in range(E):
66
+ moe.quantize_weights_nvfp4_bf16(
67
+ weights_bf16[expert],
68
+ packed=weights_packed[expert],
69
+ sfb=weight_sfs[expert],
70
+ )
71
+ weight_alpha = torch.ones(E, device="cuda", dtype=torch.float32)
72
+
73
+ packed = torch.empty(M, K // 2, device="cuda", dtype=torch.uint8)
74
+ sfa = torch.empty(sf_bytes(M, K), device="cuda", dtype=torch.uint8)
75
+ out = torch.empty(M, TOP_K, N, device="cuda", dtype=torch.bfloat16)
76
+ y = moe.grouped_w4a4_gemv_from_bf16(
77
+ x, weights_packed, weight_sfs, weight_alpha, expert_idx,
78
+ packed=packed, sfa=sfa, out=out,
79
+ )
80
+ ```
81
+
82
+ The example buffers may be larger than the minimum; wrappers validate storage.
83
+ For production code, derive SF sizes from the checkpoint packer metadata.
84
+
85
+ ## Dispatch guidance
86
+
87
+ Use the built-artifact benchmark to dispatch rather than selecting only by
88
+ dtype. On the tested cu128 artifact W4A16 wins gate-up, W4A4 wins down verify,
89
+ and down decode is effectively tied kernel-only. A fused/upstream FP4 producer
90
+ removes the standalone quantization charge, but callers still should not assume
91
+ lower precision is automatically faster.
92
+
93
+ ## Validation
94
+
95
+ ```bash
96
+ python grouped-moe-gemv/tests/test_grouped_moe_gemv.py --backend source --mode full
97
+ python grouped-moe-gemv/benchmarks/benchmark.py --backend source
98
+ ```
build/torch213-cxx11-cu130-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT grouped MoE GEMV kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import torch
8
+
9
+ from ._ops import add_op_namespace_prefix, ops
10
+
11
+
12
+ @torch.library.register_fake(add_op_namespace_prefix("w4a16_decode_gemv_bf16"))
13
+ def _w4a16_decode_gemv_fake(
14
+ x_bf16: torch.Tensor,
15
+ weight_packed: torch.Tensor,
16
+ sfb: torch.Tensor,
17
+ alpha: float,
18
+ out: torch.Tensor,
19
+ ) -> None:
20
+ k = x_bf16.shape[0] if x_bf16.dim() == 1 else x_bf16.shape[1]
21
+ if weight_packed.dim() != 2 or weight_packed.shape[1] != k // 2 or out.shape != (weight_packed.shape[0],):
22
+ raise RuntimeError("expected x (K,) or (1,K), weight_packed (N,K/2), out (N,)")
23
+ return None
24
+
25
+
26
+ @torch.library.register_fake(add_op_namespace_prefix("grouped_w4a16_gemv_bf16"))
27
+ def _grouped_w4a16_gemv_fake(
28
+ activations: torch.Tensor,
29
+ weight_stack: torch.Tensor,
30
+ sfb_stack: torch.Tensor,
31
+ alpha_stack: torch.Tensor,
32
+ expert_idx: torch.Tensor,
33
+ w_stride: int,
34
+ sfb_stride: int,
35
+ out: torch.Tensor,
36
+ ) -> None:
37
+ if activations.dim() != 2 or out.dim() != 2 or out.shape[0] != activations.shape[0]:
38
+ raise RuntimeError("expected activations (slots,K), out (slots,N)")
39
+ if expert_idx.shape != (activations.shape[0],):
40
+ raise RuntimeError("expert_idx must have shape (slots,)")
41
+ return None
42
+
43
+
44
+ @torch.library.register_fake(add_op_namespace_prefix("quantize_activations_nvfp4_bf16"))
45
+ def _quantize_activations_nvfp4_fake(
46
+ activations: torch.Tensor,
47
+ packed: torch.Tensor,
48
+ sfa: torch.Tensor,
49
+ ) -> None:
50
+ if activations.dim() != 2:
51
+ raise RuntimeError("activations must have shape (M,K)")
52
+ if packed.shape != (activations.shape[0], activations.shape[1] // 2):
53
+ raise RuntimeError("packed must have shape (M,K/2)")
54
+ return None
55
+
56
+
57
+ @torch.library.register_fake(add_op_namespace_prefix("quantize_weights_nvfp4_bf16"))
58
+ def _quantize_weights_nvfp4_fake(
59
+ weights: torch.Tensor,
60
+ packed: torch.Tensor,
61
+ sfb: torch.Tensor,
62
+ ) -> None:
63
+ if weights.dim() != 2 or packed.shape != (weights.shape[0], weights.shape[1] // 2):
64
+ raise RuntimeError("expected weights (N,K), packed (N,K/2)")
65
+ return None
66
+
67
+
68
+ @torch.library.register_fake(add_op_namespace_prefix("grouped_w4a4_gemv_bf16"))
69
+ def _grouped_w4a4_gemv_fake(
70
+ activations_packed: torch.Tensor,
71
+ weight_stack: torch.Tensor,
72
+ sfa: torch.Tensor,
73
+ sfb_stack: torch.Tensor,
74
+ alpha_stack: torch.Tensor,
75
+ expert_idx: torch.Tensor,
76
+ out: torch.Tensor,
77
+ ) -> None:
78
+ if activations_packed.dim() != 2 or weight_stack.dim() != 3:
79
+ raise RuntimeError("expected activations_packed (M,K/2), weight_stack (E,N,K/2)")
80
+ if expert_idx.dim() != 2:
81
+ raise RuntimeError("expert_idx must have shape (M,top_k)")
82
+ expected = (activations_packed.shape[0], expert_idx.shape[1], weight_stack.shape[1])
83
+ if out.shape != expected:
84
+ raise RuntimeError(f"out must have shape {expected}")
85
+ return None
86
+
87
+
88
+ def _swizzled_sf_bytes(rows: int, dim: int) -> int:
89
+ return ((int(rows) + 127) // 128) * (((int(dim) // 16) + 3) // 4) * 512
90
+
91
+
92
+ def w4a16_decode_gemv_bf16(
93
+ x_bf16: torch.Tensor,
94
+ weight_packed: torch.Tensor,
95
+ sfb: torch.Tensor,
96
+ *,
97
+ alpha: float = 1.0,
98
+ out: Optional[torch.Tensor] = None,
99
+ ) -> torch.Tensor:
100
+ if out is None:
101
+ out = torch.empty((weight_packed.shape[0],), device=x_bf16.device, dtype=torch.bfloat16)
102
+ ops.w4a16_decode_gemv_bf16(x_bf16, weight_packed, sfb, float(alpha), out)
103
+ return out
104
+
105
+
106
+ def grouped_w4a16_gemv_bf16(
107
+ activations: torch.Tensor,
108
+ weight_stack: torch.Tensor,
109
+ sfb_stack: torch.Tensor,
110
+ alpha_stack: torch.Tensor,
111
+ expert_idx: torch.Tensor,
112
+ *,
113
+ n: int,
114
+ w_stride: Optional[int] = None,
115
+ sfb_stride: Optional[int] = None,
116
+ out: Optional[torch.Tensor] = None,
117
+ ) -> torch.Tensor:
118
+ """Run one W4A16 GEMV per routed slot.
119
+
120
+ `weight_stack` is a flat expert stack. `w_stride` and `sfb_stride` are byte
121
+ strides between experts; by default `w_stride = n * K / 2`.
122
+ """
123
+
124
+ k = activations.shape[1]
125
+ if out is None:
126
+ out = torch.empty((activations.shape[0], int(n)), device=activations.device, dtype=torch.bfloat16)
127
+ if w_stride is None:
128
+ w_stride = int(n) * k // 2
129
+ if sfb_stride is None:
130
+ raise RuntimeError("sfb_stride must be provided because swizzled SF size is layout-dependent")
131
+ ops.grouped_w4a16_gemv_bf16(
132
+ activations,
133
+ weight_stack,
134
+ sfb_stack,
135
+ alpha_stack,
136
+ expert_idx,
137
+ int(w_stride),
138
+ int(sfb_stride),
139
+ out,
140
+ )
141
+ return out
142
+
143
+
144
+ def quantize_activations_nvfp4_bf16(
145
+ activations: torch.Tensor,
146
+ *,
147
+ packed: Optional[torch.Tensor] = None,
148
+ sfa: Optional[torch.Tensor] = None,
149
+ ) -> tuple[torch.Tensor, torch.Tensor]:
150
+ """Quantize a BF16 ``[M,K]`` activation once for routed W4A4 GEMV.
151
+
152
+ Pass preallocated ``packed`` and ``sfa`` buffers on CUDA Graph hot paths.
153
+ """
154
+
155
+ m, k = activations.shape
156
+ if packed is None:
157
+ packed = torch.empty((m, k // 2), device=activations.device, dtype=torch.uint8)
158
+ if sfa is None:
159
+ sfa = torch.empty((_swizzled_sf_bytes(m, k),), device=activations.device, dtype=torch.uint8)
160
+ ops.quantize_activations_nvfp4_bf16(activations, packed, sfa)
161
+ return packed, sfa
162
+
163
+
164
+ def quantize_weights_nvfp4_bf16(
165
+ weights: torch.Tensor,
166
+ *,
167
+ packed: Optional[torch.Tensor] = None,
168
+ sfb: Optional[torch.Tensor] = None,
169
+ ) -> tuple[torch.Tensor, torch.Tensor]:
170
+ """Offline/helper quantization for one expert's BF16 ``[N,K]`` weight."""
171
+
172
+ n, k = weights.shape
173
+ if packed is None:
174
+ packed = torch.empty((n, k // 2), device=weights.device, dtype=torch.uint8)
175
+ if sfb is None:
176
+ sfb = torch.empty((_swizzled_sf_bytes(n, k),), device=weights.device, dtype=torch.uint8)
177
+ ops.quantize_weights_nvfp4_bf16(weights, packed, sfb)
178
+ return packed, sfb
179
+
180
+
181
+ def grouped_w4a4_gemv_bf16(
182
+ activations_packed: torch.Tensor,
183
+ weight_stack: torch.Tensor,
184
+ sfa: torch.Tensor,
185
+ sfb_stack: torch.Tensor,
186
+ alpha_stack: torch.Tensor,
187
+ expert_idx: torch.Tensor,
188
+ *,
189
+ out: Optional[torch.Tensor] = None,
190
+ ) -> torch.Tensor:
191
+ """Compute all token/top-k W4A4 expert projections in one launch.
192
+
193
+ Inputs use token-major routing: ``expert_idx[M,top_k]`` and output
194
+ ``[M,top_k,N]``. The device index tensor is read on every graph replay.
195
+ """
196
+
197
+ m = activations_packed.shape[0]
198
+ top_k = expert_idx.shape[1]
199
+ n = weight_stack.shape[1]
200
+ if out is None:
201
+ out = torch.empty((m, top_k, n), device=activations_packed.device, dtype=torch.bfloat16)
202
+ ops.grouped_w4a4_gemv_bf16(
203
+ activations_packed, weight_stack, sfa, sfb_stack, alpha_stack, expert_idx, out
204
+ )
205
+ return out
206
+
207
+
208
+ def grouped_w4a4_gemv_from_bf16(
209
+ activations: torch.Tensor,
210
+ weight_stack: torch.Tensor,
211
+ sfb_stack: torch.Tensor,
212
+ alpha_stack: torch.Tensor,
213
+ expert_idx: torch.Tensor,
214
+ *,
215
+ packed: Optional[torch.Tensor] = None,
216
+ sfa: Optional[torch.Tensor] = None,
217
+ out: Optional[torch.Tensor] = None,
218
+ ) -> torch.Tensor:
219
+ """Quantize ``[M,K]`` once, then launch all ``M*top_k`` projections.
220
+
221
+ Supplying all three buffers makes this two-launch composition allocation
222
+ free and CUDA Graph replay safe.
223
+ """
224
+
225
+ packed, sfa = quantize_activations_nvfp4_bf16(
226
+ activations, packed=packed, sfa=sfa
227
+ )
228
+ return grouped_w4a4_gemv_bf16(
229
+ packed, weight_stack, sfa, sfb_stack, alpha_stack, expert_idx, out=out
230
+ )
231
+
232
+
233
+ __all__ = [
234
+ "grouped_w4a4_gemv_bf16",
235
+ "grouped_w4a4_gemv_from_bf16",
236
+ "grouped_w4a16_gemv_bf16",
237
+ "quantize_activations_nvfp4_bf16",
238
+ "quantize_weights_nvfp4_bf16",
239
+ "w4a16_decode_gemv_bf16",
240
+ ]
build/torch213-cxx11-cu130-x86_64-linux/_grouped_moe_gemv_cuda_1c03930.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:71893fb7929f7bcdbfd877c98c877949d26c86349f5c498edc8912274e2ab2bc
3
+ size 543200
build/torch213-cxx11-cu130-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _grouped_moe_gemv_cuda_1c03930
3
+ ops = torch.ops._grouped_moe_gemv_cuda_1c03930
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_grouped_moe_gemv_cuda_1c03930::{op_name}"
build/torch213-cxx11-cu130-x86_64-linux/grouped_moe_gemv/__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/torch213-cxx11-cu130-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "grouped-moe-gemv",
3
+ "id": "_grouped_moe_gemv_cuda_1c03930",
4
+ "version": 2,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "11.0a",
11
+ "12.0a"
12
+ ]
13
+ },
14
+ "digest": {
15
+ "algorithm": "sha256",
16
+ "files": {
17
+ "__init__.py": "6/7oa/DPHaXhsOCeDDw3KAkskrPXDsVGh0ll4lrN3DU=",
18
+ "_grouped_moe_gemv_cuda_1c03930.abi3.so": "cYk/t5Kfe82/2HfJjId5SdJshjSfXEmO3IkSJ04qsrw=",
19
+ "_ops.py": "dpEMiXfiniPw/mtHu4nyuKUeQDYLyaqFcpmnqiniKIA=",
20
+ "grouped_moe_gemv/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
21
+ }
22
+ },
23
+ "provenance": {
24
+ "kernel-builder": {
25
+ "version": "0.17.0-dev0",
26
+ "sha": "b39ca23f1b36383df00b27b3ffe1276cd5dbea85",
27
+ "dirty": false
28
+ },
29
+ "kernel": {
30
+ "sha": "1c0393023e02142361a2de8ae1b4ae8ececb9730",
31
+ "dirty": false
32
+ }
33
+ }
34
+ }
build/torch213-cxx11-cu132-x86_64-linux/__init__.py ADDED
@@ -0,0 +1,240 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """FlashRT grouped MoE GEMV kernels."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import Optional
6
+
7
+ import torch
8
+
9
+ from ._ops import add_op_namespace_prefix, ops
10
+
11
+
12
+ @torch.library.register_fake(add_op_namespace_prefix("w4a16_decode_gemv_bf16"))
13
+ def _w4a16_decode_gemv_fake(
14
+ x_bf16: torch.Tensor,
15
+ weight_packed: torch.Tensor,
16
+ sfb: torch.Tensor,
17
+ alpha: float,
18
+ out: torch.Tensor,
19
+ ) -> None:
20
+ k = x_bf16.shape[0] if x_bf16.dim() == 1 else x_bf16.shape[1]
21
+ if weight_packed.dim() != 2 or weight_packed.shape[1] != k // 2 or out.shape != (weight_packed.shape[0],):
22
+ raise RuntimeError("expected x (K,) or (1,K), weight_packed (N,K/2), out (N,)")
23
+ return None
24
+
25
+
26
+ @torch.library.register_fake(add_op_namespace_prefix("grouped_w4a16_gemv_bf16"))
27
+ def _grouped_w4a16_gemv_fake(
28
+ activations: torch.Tensor,
29
+ weight_stack: torch.Tensor,
30
+ sfb_stack: torch.Tensor,
31
+ alpha_stack: torch.Tensor,
32
+ expert_idx: torch.Tensor,
33
+ w_stride: int,
34
+ sfb_stride: int,
35
+ out: torch.Tensor,
36
+ ) -> None:
37
+ if activations.dim() != 2 or out.dim() != 2 or out.shape[0] != activations.shape[0]:
38
+ raise RuntimeError("expected activations (slots,K), out (slots,N)")
39
+ if expert_idx.shape != (activations.shape[0],):
40
+ raise RuntimeError("expert_idx must have shape (slots,)")
41
+ return None
42
+
43
+
44
+ @torch.library.register_fake(add_op_namespace_prefix("quantize_activations_nvfp4_bf16"))
45
+ def _quantize_activations_nvfp4_fake(
46
+ activations: torch.Tensor,
47
+ packed: torch.Tensor,
48
+ sfa: torch.Tensor,
49
+ ) -> None:
50
+ if activations.dim() != 2:
51
+ raise RuntimeError("activations must have shape (M,K)")
52
+ if packed.shape != (activations.shape[0], activations.shape[1] // 2):
53
+ raise RuntimeError("packed must have shape (M,K/2)")
54
+ return None
55
+
56
+
57
+ @torch.library.register_fake(add_op_namespace_prefix("quantize_weights_nvfp4_bf16"))
58
+ def _quantize_weights_nvfp4_fake(
59
+ weights: torch.Tensor,
60
+ packed: torch.Tensor,
61
+ sfb: torch.Tensor,
62
+ ) -> None:
63
+ if weights.dim() != 2 or packed.shape != (weights.shape[0], weights.shape[1] // 2):
64
+ raise RuntimeError("expected weights (N,K), packed (N,K/2)")
65
+ return None
66
+
67
+
68
+ @torch.library.register_fake(add_op_namespace_prefix("grouped_w4a4_gemv_bf16"))
69
+ def _grouped_w4a4_gemv_fake(
70
+ activations_packed: torch.Tensor,
71
+ weight_stack: torch.Tensor,
72
+ sfa: torch.Tensor,
73
+ sfb_stack: torch.Tensor,
74
+ alpha_stack: torch.Tensor,
75
+ expert_idx: torch.Tensor,
76
+ out: torch.Tensor,
77
+ ) -> None:
78
+ if activations_packed.dim() != 2 or weight_stack.dim() != 3:
79
+ raise RuntimeError("expected activations_packed (M,K/2), weight_stack (E,N,K/2)")
80
+ if expert_idx.dim() != 2:
81
+ raise RuntimeError("expert_idx must have shape (M,top_k)")
82
+ expected = (activations_packed.shape[0], expert_idx.shape[1], weight_stack.shape[1])
83
+ if out.shape != expected:
84
+ raise RuntimeError(f"out must have shape {expected}")
85
+ return None
86
+
87
+
88
+ def _swizzled_sf_bytes(rows: int, dim: int) -> int:
89
+ return ((int(rows) + 127) // 128) * (((int(dim) // 16) + 3) // 4) * 512
90
+
91
+
92
+ def w4a16_decode_gemv_bf16(
93
+ x_bf16: torch.Tensor,
94
+ weight_packed: torch.Tensor,
95
+ sfb: torch.Tensor,
96
+ *,
97
+ alpha: float = 1.0,
98
+ out: Optional[torch.Tensor] = None,
99
+ ) -> torch.Tensor:
100
+ if out is None:
101
+ out = torch.empty((weight_packed.shape[0],), device=x_bf16.device, dtype=torch.bfloat16)
102
+ ops.w4a16_decode_gemv_bf16(x_bf16, weight_packed, sfb, float(alpha), out)
103
+ return out
104
+
105
+
106
+ def grouped_w4a16_gemv_bf16(
107
+ activations: torch.Tensor,
108
+ weight_stack: torch.Tensor,
109
+ sfb_stack: torch.Tensor,
110
+ alpha_stack: torch.Tensor,
111
+ expert_idx: torch.Tensor,
112
+ *,
113
+ n: int,
114
+ w_stride: Optional[int] = None,
115
+ sfb_stride: Optional[int] = None,
116
+ out: Optional[torch.Tensor] = None,
117
+ ) -> torch.Tensor:
118
+ """Run one W4A16 GEMV per routed slot.
119
+
120
+ `weight_stack` is a flat expert stack. `w_stride` and `sfb_stride` are byte
121
+ strides between experts; by default `w_stride = n * K / 2`.
122
+ """
123
+
124
+ k = activations.shape[1]
125
+ if out is None:
126
+ out = torch.empty((activations.shape[0], int(n)), device=activations.device, dtype=torch.bfloat16)
127
+ if w_stride is None:
128
+ w_stride = int(n) * k // 2
129
+ if sfb_stride is None:
130
+ raise RuntimeError("sfb_stride must be provided because swizzled SF size is layout-dependent")
131
+ ops.grouped_w4a16_gemv_bf16(
132
+ activations,
133
+ weight_stack,
134
+ sfb_stack,
135
+ alpha_stack,
136
+ expert_idx,
137
+ int(w_stride),
138
+ int(sfb_stride),
139
+ out,
140
+ )
141
+ return out
142
+
143
+
144
+ def quantize_activations_nvfp4_bf16(
145
+ activations: torch.Tensor,
146
+ *,
147
+ packed: Optional[torch.Tensor] = None,
148
+ sfa: Optional[torch.Tensor] = None,
149
+ ) -> tuple[torch.Tensor, torch.Tensor]:
150
+ """Quantize a BF16 ``[M,K]`` activation once for routed W4A4 GEMV.
151
+
152
+ Pass preallocated ``packed`` and ``sfa`` buffers on CUDA Graph hot paths.
153
+ """
154
+
155
+ m, k = activations.shape
156
+ if packed is None:
157
+ packed = torch.empty((m, k // 2), device=activations.device, dtype=torch.uint8)
158
+ if sfa is None:
159
+ sfa = torch.empty((_swizzled_sf_bytes(m, k),), device=activations.device, dtype=torch.uint8)
160
+ ops.quantize_activations_nvfp4_bf16(activations, packed, sfa)
161
+ return packed, sfa
162
+
163
+
164
+ def quantize_weights_nvfp4_bf16(
165
+ weights: torch.Tensor,
166
+ *,
167
+ packed: Optional[torch.Tensor] = None,
168
+ sfb: Optional[torch.Tensor] = None,
169
+ ) -> tuple[torch.Tensor, torch.Tensor]:
170
+ """Offline/helper quantization for one expert's BF16 ``[N,K]`` weight."""
171
+
172
+ n, k = weights.shape
173
+ if packed is None:
174
+ packed = torch.empty((n, k // 2), device=weights.device, dtype=torch.uint8)
175
+ if sfb is None:
176
+ sfb = torch.empty((_swizzled_sf_bytes(n, k),), device=weights.device, dtype=torch.uint8)
177
+ ops.quantize_weights_nvfp4_bf16(weights, packed, sfb)
178
+ return packed, sfb
179
+
180
+
181
+ def grouped_w4a4_gemv_bf16(
182
+ activations_packed: torch.Tensor,
183
+ weight_stack: torch.Tensor,
184
+ sfa: torch.Tensor,
185
+ sfb_stack: torch.Tensor,
186
+ alpha_stack: torch.Tensor,
187
+ expert_idx: torch.Tensor,
188
+ *,
189
+ out: Optional[torch.Tensor] = None,
190
+ ) -> torch.Tensor:
191
+ """Compute all token/top-k W4A4 expert projections in one launch.
192
+
193
+ Inputs use token-major routing: ``expert_idx[M,top_k]`` and output
194
+ ``[M,top_k,N]``. The device index tensor is read on every graph replay.
195
+ """
196
+
197
+ m = activations_packed.shape[0]
198
+ top_k = expert_idx.shape[1]
199
+ n = weight_stack.shape[1]
200
+ if out is None:
201
+ out = torch.empty((m, top_k, n), device=activations_packed.device, dtype=torch.bfloat16)
202
+ ops.grouped_w4a4_gemv_bf16(
203
+ activations_packed, weight_stack, sfa, sfb_stack, alpha_stack, expert_idx, out
204
+ )
205
+ return out
206
+
207
+
208
+ def grouped_w4a4_gemv_from_bf16(
209
+ activations: torch.Tensor,
210
+ weight_stack: torch.Tensor,
211
+ sfb_stack: torch.Tensor,
212
+ alpha_stack: torch.Tensor,
213
+ expert_idx: torch.Tensor,
214
+ *,
215
+ packed: Optional[torch.Tensor] = None,
216
+ sfa: Optional[torch.Tensor] = None,
217
+ out: Optional[torch.Tensor] = None,
218
+ ) -> torch.Tensor:
219
+ """Quantize ``[M,K]`` once, then launch all ``M*top_k`` projections.
220
+
221
+ Supplying all three buffers makes this two-launch composition allocation
222
+ free and CUDA Graph replay safe.
223
+ """
224
+
225
+ packed, sfa = quantize_activations_nvfp4_bf16(
226
+ activations, packed=packed, sfa=sfa
227
+ )
228
+ return grouped_w4a4_gemv_bf16(
229
+ packed, weight_stack, sfa, sfb_stack, alpha_stack, expert_idx, out=out
230
+ )
231
+
232
+
233
+ __all__ = [
234
+ "grouped_w4a4_gemv_bf16",
235
+ "grouped_w4a4_gemv_from_bf16",
236
+ "grouped_w4a16_gemv_bf16",
237
+ "quantize_activations_nvfp4_bf16",
238
+ "quantize_weights_nvfp4_bf16",
239
+ "w4a16_decode_gemv_bf16",
240
+ ]
build/torch213-cxx11-cu132-x86_64-linux/_grouped_moe_gemv_cuda_1c03930.abi3.so ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:4486ccc3b61668cb3790d36afc14bfdef065982bbc0a95f417fe29c8470d2bd3
3
+ size 555536
build/torch213-cxx11-cu132-x86_64-linux/_ops.py ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ import torch
2
+ from . import _grouped_moe_gemv_cuda_1c03930
3
+ ops = torch.ops._grouped_moe_gemv_cuda_1c03930
4
+
5
+ def add_op_namespace_prefix(op_name: str):
6
+ """
7
+ Prefix op by namespace.
8
+ """
9
+ return f"_grouped_moe_gemv_cuda_1c03930::{op_name}"
build/torch213-cxx11-cu132-x86_64-linux/grouped_moe_gemv/__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/torch213-cxx11-cu132-x86_64-linux/metadata.json ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "grouped-moe-gemv",
3
+ "id": "_grouped_moe_gemv_cuda_1c03930",
4
+ "version": 2,
5
+ "license": "Apache-2.0",
6
+ "python-depends": [],
7
+ "backend": {
8
+ "type": "cuda",
9
+ "archs": [
10
+ "11.0a",
11
+ "12.0a"
12
+ ]
13
+ },
14
+ "digest": {
15
+ "algorithm": "sha256",
16
+ "files": {
17
+ "__init__.py": "6/7oa/DPHaXhsOCeDDw3KAkskrPXDsVGh0ll4lrN3DU=",
18
+ "_grouped_moe_gemv_cuda_1c03930.abi3.so": "RIbMw7YWaMs3kNNq/BS/3vBlmCu8CpX0F/4pyEcNK9M=",
19
+ "_ops.py": "dpEMiXfiniPw/mtHu4nyuKUeQDYLyaqFcpmnqiniKIA=",
20
+ "grouped_moe_gemv/__init__.py": "DFYPlrhXwYjEqCl/8n0SmWGZV8NFml5DPhMjKfv98GY="
21
+ }
22
+ },
23
+ "provenance": {
24
+ "kernel-builder": {
25
+ "version": "0.17.0-dev0",
26
+ "sha": "b39ca23f1b36383df00b27b3ffe1276cd5dbea85",
27
+ "dirty": false
28
+ },
29
+ "kernel": {
30
+ "sha": "1c0393023e02142361a2de8ae1b4ae8ececb9730",
31
+ "dirty": false
32
+ }
33
+ }
34
+ }