flashrt-structures 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. flashrt_structures/__init__.py +174 -0
  2. flashrt_structures/adapters/__init__.py +55 -0
  3. flashrt_structures/adapters/diffusers_attention.py +237 -0
  4. flashrt_structures/adapters/diffusers_rotary_attention.py +239 -0
  5. flashrt_structures/adapters/factored_qk_norm_rope.py +252 -0
  6. flashrt_structures/adapters/factored_two_way_attention.py +99 -0
  7. flashrt_structures/adapters/gemma_attention.py +226 -0
  8. flashrt_structures/adapters/packed_qkv_rope.py +342 -0
  9. flashrt_structures/adapters/packed_stream_qk_norm_rope.py +376 -0
  10. flashrt_structures/adapters/qwen_per_head_qk_norm_rope.py +231 -0
  11. flashrt_structures/adapters/sglang_engine.py +207 -0
  12. flashrt_structures/adapters/transformers_attention_interface.py +73 -0
  13. flashrt_structures/adapters/transformers_gated_delta.py +152 -0
  14. flashrt_structures/adapters/transformers_gated_delta_fused.py +96 -0
  15. flashrt_structures/adapters/vllm_engine.py +424 -0
  16. flashrt_structures/adjudicate.py +85 -0
  17. flashrt_structures/aot.py +191 -0
  18. flashrt_structures/autobuild.py +2052 -0
  19. flashrt_structures/beta/__init__.py +43 -0
  20. flashrt_structures/beta/conform.py +94 -0
  21. flashrt_structures/beta/joins.py +113 -0
  22. flashrt_structures/beta/negotiate.py +84 -0
  23. flashrt_structures/beta/ports.py +140 -0
  24. flashrt_structures/decisions.py +80 -0
  25. flashrt_structures/discover.py +623 -0
  26. flashrt_structures/explain.py +70 -0
  27. flashrt_structures/frontdoor.py +572 -0
  28. flashrt_structures/gates.py +465 -0
  29. flashrt_structures/guard.py +421 -0
  30. flashrt_structures/handle.py +189 -0
  31. flashrt_structures/impls/__init__.py +219 -0
  32. flashrt_structures/impls/adaln_producer/__init__.py +8 -0
  33. flashrt_structures/impls/adaln_producer/broker.py +116 -0
  34. flashrt_structures/impls/adaln_producer/fused.py +388 -0
  35. flashrt_structures/impls/adarms_stack/__init__.py +8 -0
  36. flashrt_structures/impls/adarms_stack/fp8_chain.py +832 -0
  37. flashrt_structures/impls/adarms_stack/region.py +102 -0
  38. flashrt_structures/impls/attention_core/__init__.py +132 -0
  39. flashrt_structures/impls/attention_core/fa2_seqused.py +458 -0
  40. flashrt_structures/impls/attention_core/fa4_cute.py +154 -0
  41. flashrt_structures/impls/attention_core/fa4_fp8.py +178 -0
  42. flashrt_structures/impls/attention_core/masked_mha.py +158 -0
  43. flashrt_structures/impls/attention_core/two_way_fa2.py +220 -0
  44. flashrt_structures/impls/cadence_static/__init__.py +18 -0
  45. flashrt_structures/impls/cadence_static/buffers.py +122 -0
  46. flashrt_structures/impls/cadence_static/cross_attention.py +187 -0
  47. flashrt_structures/impls/chain_elements.py +89 -0
  48. flashrt_structures/impls/decode_loop/__init__.py +0 -0
  49. flashrt_structures/impls/decode_loop/fp8_kv.py +206 -0
  50. flashrt_structures/impls/decode_loop/mtp_speculative.py +245 -0
  51. flashrt_structures/impls/decode_loop/whole_step.py +852 -0
  52. flashrt_structures/impls/decoder_block/__init__.py +6 -0
  53. flashrt_structures/impls/decoder_block/attn_sublayer.py +110 -0
  54. flashrt_structures/impls/decoder_block/fused.py +167 -0
  55. flashrt_structures/impls/decoder_ffn/__init__.py +0 -0
  56. flashrt_structures/impls/decoder_ffn/fp8_static.py +310 -0
  57. flashrt_structures/impls/decoder_ffn/fp8_static.yaml +22 -0
  58. flashrt_structures/impls/decoder_ffn/w4a16_static.py +221 -0
  59. flashrt_structures/impls/decoder_ffn/w8a16_static.py +183 -0
  60. flashrt_structures/impls/dit_stack/__init__.py +11 -0
  61. flashrt_structures/impls/dit_stack/fp4_chain.py +417 -0
  62. flashrt_structures/impls/dit_stack/region.py +86 -0
  63. flashrt_structures/impls/fixed_iter/__init__.py +29 -0
  64. flashrt_structures/impls/fixed_iter/openpi.py +264 -0
  65. flashrt_structures/impls/fixed_iter/protocol.py +94 -0
  66. flashrt_structures/impls/gated_delta_core/__init__.py +3 -0
  67. flashrt_structures/impls/gated_delta_core/fused_layer.py +545 -0
  68. flashrt_structures/impls/gated_delta_core/hub_v3.py +152 -0
  69. flashrt_structures/impls/graph_lowering/__init__.py +27 -0
  70. flashrt_structures/impls/graph_lowering/pi052_denoise.py +179 -0
  71. flashrt_structures/impls/graph_lowering/protocol.py +76 -0
  72. flashrt_structures/impls/graph_lowering/qwen3_vl.py +364 -0
  73. flashrt_structures/impls/linear_proj/__init__.py +0 -0
  74. flashrt_structures/impls/linear_proj/fp8_static.py +270 -0
  75. flashrt_structures/impls/linear_proj/nvfp4_balance.py +131 -0
  76. flashrt_structures/impls/linear_proj/nvfp4_dynamic.py +182 -0
  77. flashrt_structures/impls/linear_proj/w8a16_static.py +230 -0
  78. flashrt_structures/impls/modnorm_qkv_chain/__init__.py +0 -0
  79. flashrt_structures/impls/modnorm_qkv_chain/fp8_ptok_table.py +291 -0
  80. flashrt_structures/impls/moe_experts/__init__.py +9 -0
  81. flashrt_structures/impls/moe_experts/nvfp4_dynamic.py +208 -0
  82. flashrt_structures/impls/moe_experts/nvfp4_w4a16.py +129 -0
  83. flashrt_structures/impls/norm_fused/__init__.py +3 -0
  84. flashrt_structures/impls/norm_fused/bf16.py +94 -0
  85. flashrt_structures/impls/norm_fused/fp8_producer.py +84 -0
  86. flashrt_structures/impls/patch_projection/__init__.py +3 -0
  87. flashrt_structures/impls/patch_projection/bf16_flat.py +139 -0
  88. flashrt_structures/impls/prefill_tower/__init__.py +10 -0
  89. flashrt_structures/impls/prefill_tower/fp8_chain.py +955 -0
  90. flashrt_structures/impls/prefill_tower/region.py +99 -0
  91. flashrt_structures/impls/qk_norm_rope/__init__.py +12 -0
  92. flashrt_structures/impls/qk_norm_rope/per_head_gqa.py +199 -0
  93. flashrt_structures/impls/qk_norm_rope/projection_bf16.py +165 -0
  94. flashrt_structures/impls/qkv_pack/__init__.py +5 -0
  95. flashrt_structures/impls/qkv_pack/bf16.py +110 -0
  96. flashrt_structures/impls/qkv_pack/fp8_static.py +435 -0
  97. flashrt_structures/impls/qkv_pack/nvfp4_balance.py +218 -0
  98. flashrt_structures/impls/qkv_rope/__init__.py +3 -0
  99. flashrt_structures/impls/qkv_rope/packed_bias_bf16.py +143 -0
  100. flashrt_structures/impls/step_table.py +113 -0
  101. flashrt_structures/impls/vision_ffn/__init__.py +0 -0
  102. flashrt_structures/impls/vision_ffn/fp8_static.py +261 -0
  103. flashrt_structures/impls/vision_ffn/nvfp4_balance.py +211 -0
  104. flashrt_structures/impls/vision_tower/__init__.py +7 -0
  105. flashrt_structures/impls/vision_tower/fp8_chain.py +533 -0
  106. flashrt_structures/impls/vision_tower/region.py +91 -0
  107. flashrt_structures/matrix.py +126 -0
  108. flashrt_structures/points.py +368 -0
  109. flashrt_structures/prequantized.py +131 -0
  110. flashrt_structures/quantize_on_adopt.py +94 -0
  111. flashrt_structures/recipe.py +438 -0
  112. flashrt_structures/regions.py +208 -0
  113. flashrt_structures/schemes.py +490 -0
  114. flashrt_structures/stages.py +298 -0
  115. flashrt_structures/storage.py +255 -0
  116. flashrt_structures/swap.py +398 -0
  117. flashrt_structures/workspace.py +110 -0
  118. flashrt_structures-0.2.0.dist-info/METADATA +140 -0
  119. flashrt_structures-0.2.0.dist-info/RECORD +122 -0
  120. flashrt_structures-0.2.0.dist-info/WHEEL +5 -0
  121. flashrt_structures-0.2.0.dist-info/licenses/LICENSE +202 -0
  122. flashrt_structures-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,291 @@
1
+ """Per-token-table implementation of the ``modnorm_qkv_chain`` structure.
2
+
3
+ The video-DiT block form: modulation parameters live in the block's own
4
+ ``[1, chunks, D]`` table combined per token with a ``[B, M, chunks, D]``
5
+ timestep embedding, inline in the block's forward. Only a block owner
6
+ can reroute that math, so this impl binds the whole block:
7
+
8
+ - both producer sites run the ``adaptive-layernorm-producers`` per-token
9
+ table entry (table add + chunk selection + no-affine LayerNorm +
10
+ modulation + static FP8 quantize, one pass — the per-block six-chunk
11
+ materialization never exists);
12
+ - the self-attention Q/K/V consume the shared FP8 wire through wire
13
+ projections: the block hands them the quantized activation explicitly
14
+ before calling the host attention, so rotary/SDPA internals stay the
15
+ host's. The handoff is an explicit attribute set per call — never an
16
+ identity-keyed cache (a pointer-keyed bank measurably cross-fed CFG
17
+ branches on this host);
18
+ - the FFN runs the fused FP8 GELU MLP straight from the second producer
19
+ site's wire;
20
+ - the output projection and the whole cross-attention are *not* owned:
21
+ the forward calls whatever is attached there, so their individual
22
+ seams keep composing.
23
+
24
+ Bind needs the four static activation scales the composition consumes
25
+ (``attn_in``/``o_in``/``ffn_in``/``ffn_hid``), measured at the block's
26
+ own sublayer inputs by the ordinary calibration pass.
27
+ """
28
+
29
+ from __future__ import annotations
30
+
31
+ from functools import lru_cache
32
+
33
+ import torch
34
+
35
+ from ...guard import CAST_OK, PROCEED, GuardedSeam
36
+
37
+ PRODUCER_DEP = {
38
+ "provider": "hf",
39
+ "repo": "flashrt/adaptive-layernorm-producers",
40
+ "version": ">=1",
41
+ }
42
+ FFN_DEP = {
43
+ "provider": "hf",
44
+ "repo": "flashrt/flashrt-fp8-ffn",
45
+ "version": ">=1",
46
+ }
47
+
48
+ _FP8 = torch.float8_e4m3fn
49
+ _FP8_MAX = 448.0
50
+
51
+ #: chunk indices in the block table: (shift, scale, gate) for the
52
+ #: attention site and the FFN site, in table order
53
+ _ATTN_CHUNKS = (0, 1, 2)
54
+ _FFN_CHUNKS = (3, 4, 5)
55
+
56
+ SUPPORT = {
57
+ "chunks": 6,
58
+ "D": {"min": 512, "max": 16384, "multiple_of": 2},
59
+ }
60
+
61
+
62
+ @lru_cache(maxsize=1)
63
+ def _producer():
64
+ from flashrt_structures.impls import hub_kernel
65
+
66
+ pkg = hub_kernel(PRODUCER_DEP["repo"], PRODUCER_DEP["version"])
67
+ if not hasattr(pkg, "ada_layer_norm_quant_fp8_ptok_table_bf16"):
68
+ raise ValueError(
69
+ "refused: the installed adaptive-layernorm-producers build "
70
+ "predates the per-token table entry; a package release with "
71
+ "ada_layer_norm_quant_fp8_ptok_table_bf16 is required")
72
+ return pkg
73
+
74
+
75
+ @lru_cache(maxsize=1)
76
+ def _ffn_kernel():
77
+ from flashrt_structures.impls import hub_kernel
78
+
79
+ return hub_kernel(FFN_DEP["repo"], FFN_DEP["version"])
80
+
81
+
82
+ def _q8(w: torch.Tensor):
83
+ s = (w.float().abs().amax() / _FP8_MAX).clamp_min(1e-8)
84
+ packed = (w.float() / s).clamp(-_FP8_MAX, _FP8_MAX).to(_FP8) \
85
+ .contiguous()
86
+ return packed, s.reshape(1)
87
+
88
+
89
+ class WireProj(torch.nn.Module):
90
+ """Projection that consumes the chain's FP8 wire.
91
+
92
+ The owning block sets ``take(x8, scale)`` immediately before the
93
+ host attention runs and the projection consumes it exactly once.
94
+ Called without a wire armed (someone invoking the projection outside
95
+ the chain), it falls back to the retained host projection — counted,
96
+ like every dispatch.
97
+ """
98
+
99
+ def __init__(self, lin, gemm):
100
+ super().__init__()
101
+ w8, ws = _q8(lin.weight.detach())
102
+ self.register_buffer("_w8", w8)
103
+ self.register_buffer("_ws", ws)
104
+ self._bias = (None if lin.bias is None
105
+ else lin.bias.detach().to(torch.bfloat16))
106
+ self._gemm = gemm
107
+ self.host_linear = lin
108
+ self._wire = None
109
+ self.off_wire_calls = 0
110
+
111
+ def take(self, x8, scale):
112
+ self._wire = (x8, scale)
113
+
114
+ def forward(self, x):
115
+ wire = self._wire
116
+ if wire is None:
117
+ if not torch.compiler.is_compiling():
118
+ self.off_wire_calls += 1
119
+ return self.host_linear(x)
120
+ x8, scale = wire
121
+ y = self._gemm(x8, self._w8, scale, self._ws)
122
+ if self._bias is not None:
123
+ y = y + self._bias
124
+ return y.reshape(*x.shape[:-1], self._w8.shape[0]).type_as(x)
125
+
126
+
127
+ class PerTokenModChainBlock(GuardedSeam, torch.nn.Module):
128
+ """Drop-in replacement for one per-token-table DiT block."""
129
+
130
+ _frt_host_attr = "host_block"
131
+ _frt_can_fallback = True
132
+
133
+ def __init__(self, block, wires, scales, ffn_state, producer_fn,
134
+ ffn_fn, table, eps):
135
+ super().__init__()
136
+ self.host_block = block
137
+ self._wires = wires # (q, k, v) WireProj modules
138
+ self._scales = scales # dict of [1] fp32 tensors
139
+ self._ffn = ffn_state
140
+ self._producer = producer_fn
141
+ self._ffn_fn = ffn_fn
142
+ self.register_buffer("_table", table)
143
+ self._eps = eps
144
+ guard = self._frt_arm(dtypes=CAST_OK, device=table.device,
145
+ k=int(table.shape[1]))
146
+ guard.notes["host_form_calls"] = 0
147
+
148
+ def __getattr__(self, name):
149
+ try:
150
+ return super().__getattr__(name)
151
+ except AttributeError:
152
+ if name == "host_block":
153
+ raise
154
+ return getattr(super().__getattr__("host_block"), name)
155
+
156
+ def _host_form(self, *args, **kwargs):
157
+ guard = self._frt_guard
158
+ if guard is not None and not torch.compiler.is_compiling():
159
+ guard.notes["host_form_calls"] += 1
160
+ return self.host_block(*args, **kwargs)
161
+
162
+ def forward(self, hidden_states, encoder_hidden_states, temb,
163
+ rotary_emb, *args, **kwargs):
164
+ admitted = self._frt_admit(hidden_states)
165
+ if admitted is not PROCEED:
166
+ return admitted
167
+ if temb.dim() != 4 or temb.shape[2] != self._table.shape[0]:
168
+ # the broadcast (per-sample) form is the host's own path
169
+ return self._host_form(hidden_states, encoder_hidden_states,
170
+ temb, rotary_emb, *args, **kwargs)
171
+ block = self.host_block
172
+ x = hidden_states.contiguous()
173
+ bsz, seq, dim = x.shape
174
+ tb = getattr(temb, "_frt_bf16", None)
175
+ if tb is None:
176
+ # one cast per transformer call, shared by every block: the
177
+ # attribute dies with the tensor, so there is no cross-call
178
+ # identity to poison
179
+ tb = temb.reshape(-1, temb.shape[2], dim) \
180
+ .to(torch.bfloat16).contiguous()
181
+ temb._frt_bf16 = tb
182
+ s_idx, c_idx, g_idx = _ATTN_CHUNKS
183
+ gate_msa = (self._table[g_idx]
184
+ + temb[0, :, g_idx, :].float()).unsqueeze(0)
185
+ x2d = x.view(-1, dim)
186
+ x8 = self._producer(x2d, tb, self._table,
187
+ self._scales["attn_in"], s_idx, c_idx,
188
+ self._eps)
189
+ for wire in self._wires:
190
+ wire.take(x8, self._scales["attn_in"])
191
+ try:
192
+ # x is passed for its shape only: the wire projections
193
+ # consume the quantized activation, not this tensor's values
194
+ attn = block.attn1(x, None, None, rotary_emb)
195
+ finally:
196
+ for wire in self._wires:
197
+ wire._wire = None
198
+ x = (x.float() + attn * gate_msa).type_as(x)
199
+ n2 = block.norm2(x.float()).type_as(x)
200
+ x = x + block.attn2(n2, encoder_hidden_states, None, None)
201
+ fs_idx, fc_idx, fg_idx = _FFN_CHUNKS
202
+ c_gate = (self._table[fg_idx]
203
+ + temb[0, :, fg_idx, :].float()).unsqueeze(0)
204
+ x8f = self._producer(x.contiguous().view(-1, dim), tb,
205
+ self._table, self._scales["ffn_in"],
206
+ fs_idx, fc_idx, self._eps)
207
+ st = self._ffn
208
+ ff = self._ffn_fn(x8f, st["up_w8"], st["up_b"], st["dn_w8"],
209
+ st["dn_b"], self._scales["ffn_in"],
210
+ st["up_ws"], self._scales["ffn_hid"],
211
+ st["dn_ws"])
212
+ ff = ff.reshape(bsz, seq, dim)
213
+ return (x.float() + ff.float() * c_gate).type_as(x)
214
+
215
+
216
+ @torch.no_grad()
217
+ def bind_block_seam(model, seam, *, points):
218
+ """Bind one per-token-table block; returns the swap dict.
219
+
220
+ The dict carries the block wrapper plus the three wire projections
221
+ under the host attention, so attach/detach treats the whole
222
+ composition as one transaction.
223
+ """
224
+ from flashrt_structures.discover import _resolve
225
+
226
+ block = _resolve(model, seam.path)
227
+ table_param = block.scale_shift_table.detach()
228
+ chunks = int(table_param.shape[1])
229
+ dim = int(table_param.shape[2])
230
+ if chunks != SUPPORT["chunks"]:
231
+ raise ValueError(
232
+ f"refused: {chunks}-chunk table; this impl serves the "
233
+ f"6-chunk (dual-site) layout")
234
+ bounds = SUPPORT["D"]
235
+ if not bounds["min"] <= dim <= bounds["max"] or dim % 2:
236
+ raise ValueError(f"D={dim} outside support envelope")
237
+
238
+ # the collector keys each point by its own placement path (the
239
+ # block's sublayer input), exactly where points.resolve put it
240
+ sites = {"attn_in": ".attn1.to_q", "o_in": ".attn1.to_out.0",
241
+ "ffn_in": ".ffn", "ffn_hid": ".ffn.net.2"}
242
+ scales = {}
243
+ for name, child in sites.items():
244
+ value = (points.scale(seam.path + child, name)
245
+ if points is not None else None)
246
+ if value is None:
247
+ raise ValueError(
248
+ f"refused: calibration point {name!r} was not measured "
249
+ "for this block")
250
+ scales[name] = torch.tensor([float(value)], device="cuda",
251
+ dtype=torch.float32)
252
+
253
+ producer_pkg = _producer()
254
+ ffn_pkg = _ffn_kernel()
255
+ gemm = ffn_pkg.fp8_gemm_bf16
256
+
257
+ wires = tuple(WireProj(getattr(block.attn1, a), gemm)
258
+ for a in ("to_q", "to_k", "to_v"))
259
+ up, dn = block.ffn.net[0].proj, block.ffn.net[2]
260
+ ffn_state = {}
261
+ ffn_state["up_w8"], ffn_state["up_ws"] = _q8(up.weight.detach())
262
+ ffn_state["dn_w8"], ffn_state["dn_ws"] = _q8(dn.weight.detach())
263
+ ffn_state["up_b"] = up.bias.detach().to(torch.bfloat16).contiguous()
264
+ ffn_state["dn_b"] = dn.bias.detach().to(torch.bfloat16).contiguous()
265
+ for key in ("up_w8", "up_ws", "dn_w8", "dn_ws"):
266
+ ffn_state[key] = ffn_state[key].to("cuda")
267
+
268
+ eps = float(getattr(block.norm1, "eps", 1e-6))
269
+ table = table_param.reshape(chunks, dim).float().contiguous() \
270
+ .to("cuda")
271
+ wrapper = PerTokenModChainBlock(
272
+ block, wires, scales, ffn_state,
273
+ producer_pkg.ada_layer_norm_quant_fp8_ptok_table_bf16,
274
+ (getattr(ffn_pkg, "fp8_gelu_mlp_v2_bf16", None)
275
+ or ffn_pkg.fp8_gelu_mlp_bf16), table, eps)
276
+
277
+ # bind-time smoke: both producer sites launch once on zeros before
278
+ # the seam is handed out
279
+ z = torch.zeros(4, dim, device="cuda", dtype=torch.bfloat16)
280
+ zt = torch.zeros(4, chunks, dim, device="cuda", dtype=torch.bfloat16)
281
+ for s_idx, c_idx in (_ATTN_CHUNKS[:2], _FFN_CHUNKS[:2]):
282
+ probe = producer_pkg.ada_layer_norm_quant_fp8_ptok_table_bf16(
283
+ z, zt, table, scales["attn_in"], s_idx, c_idx, eps)
284
+ if probe.shape != (4, dim):
285
+ raise ValueError("refused: producer bind smoke shape "
286
+ f"{tuple(probe.shape)}")
287
+
288
+ swaps = {seam.path: wrapper}
289
+ for attr, wire in zip(("to_q", "to_k", "to_v"), wires):
290
+ swaps[f"{seam.path}.attn1.{attr}"] = wire
291
+ return swaps
@@ -0,0 +1,9 @@
1
+ """``moe_experts`` structure family.
2
+
3
+ The structure is the expert bank of a sparse-MoE block: one module
4
+ holding every expert's projection weights as stacked 3D tensors, called
5
+ with the token batch plus the router's top-k assignment. On the hosts
6
+ this family serves, that bank is where nearly all of the checkpoint's
7
+ weight mass lives — which is exactly why it is the seam worth owning
8
+ when the dense checkpoint does not fit the card.
9
+ """
@@ -0,0 +1,208 @@
1
+ """NVFP4 (W4A4, dynamic activation scales) ``moe_experts`` implementation.
2
+
3
+ The expert bank of a sparse-MoE block stores every expert's projections
4
+ as stacked 3D tensors (``gate_up_proj [E, 2I, H]``, ``down_proj
5
+ [E, H, I]``). Each expert's matrices pack once, at bind time, into the
6
+ grouped kernel's stacked NVFP4 layout, and the forward runs the bank as
7
+ two grouped launches per call: one for every routed gate_up slot
8
+ (``[T, top_k]``), one for every down slot (flattened to ``[T*top_k, 1]``
9
+ because each routed pair carries its own intermediate activation). The
10
+ routing tensor stays on the device end to end — no host sync, fixed
11
+ shapes for a given ``T`` — so the step is legal inside a compiled
12
+ region or a captured graph, and the *same* kernels serve the M=1 decode
13
+ row and the M=K+1 verify pass: one numeric family across both, which
14
+ is what token-identity between a spec verify and the plain step needs.
15
+
16
+ Contributions accumulate in FP32 over the fixed top-k axis before the
17
+ single cast back to the host dtype.
18
+
19
+ Known ceiling, recorded not hidden: weight traffic is per routed slot.
20
+ A long prefill (hundreds of tokens and up) re-reads shared expert
21
+ weights once per slot where a per-expert grouping would read them once;
22
+ until a grouped entry with per-expert accumulation ships, long prompts
23
+ through this bank pay slot-linear traffic.
24
+
25
+ There is no host fallback: binding exists to retire the dense weights
26
+ whose footprint keeps the checkpoint off the card, so the guard refuses
27
+ out-of-form calls instead of falling back.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ from collections.abc import Mapping
33
+ from functools import lru_cache
34
+
35
+ import torch
36
+
37
+ from ...guard import CAST_OK, PROCEED, GuardedSeam
38
+
39
+ KERNEL_DEP = {
40
+ "provider": "huggingface_kernels",
41
+ "repo": "flashrt/grouped-moe-gemv",
42
+ "version": ">=2",
43
+ }
44
+
45
+ #: mirrors the kernel's own checks (K divisible by 16, N by 8) — both
46
+ #: contraction dims of an expert bank are K once: H for gate_up, I for
47
+ #: down; both output dims are N once: 2I and H
48
+ SUPPORT = {
49
+ "K": {"min": 16, "multiple_of": 16},
50
+ "N": {"min": 8, "multiple_of": 8},
51
+ "E": {"min": 1},
52
+ }
53
+
54
+ #: experts are streamed to the GPU in slabs of this many during bind so
55
+ #: the transient footprint stays at slab size, not the full bank
56
+ _BIND_SLAB = 32
57
+
58
+
59
+ @lru_cache(maxsize=1)
60
+ def _kernel():
61
+ from flashrt_structures.impls import hub_kernel
62
+
63
+ return hub_kernel(KERNEL_DEP["repo"], KERNEL_DEP["version"])
64
+
65
+
66
+ def _sf_bytes(rows: int, dim: int) -> int:
67
+ """The kernel's swizzled scale-factor buffer size for one [rows, dim]."""
68
+ return ((rows + 127) // 128) * (((dim // 16) + 3) // 4) * 512
69
+
70
+
71
+ def check_experts(weights: Mapping[str, torch.Tensor]) -> tuple[int, int, int]:
72
+ """Validate an expert bank's shapes; returns ``(E, H, I)``."""
73
+ gu, dn = weights["gate_up_proj"], weights["down_proj"]
74
+ if gu.dim() != 3 or dn.dim() != 3:
75
+ raise ValueError(
76
+ f"expert bank must be 3D stacks, got gate_up "
77
+ f"{tuple(gu.shape)}, down {tuple(dn.shape)}")
78
+ e, two_i, h = gu.shape
79
+ e2, h2, i = dn.shape
80
+ if e != e2 or h != h2 or two_i != 2 * i:
81
+ raise ValueError(
82
+ f"inconsistent expert bank: gate_up {tuple(gu.shape)} vs "
83
+ f"down {tuple(dn.shape)}")
84
+ if e < SUPPORT["E"]["min"]:
85
+ raise ValueError(f"E={e} outside support envelope")
86
+ for name, dim in (("H", h), ("I", i)):
87
+ if dim < SUPPORT["K"]["min"] or dim % SUPPORT["K"]["multiple_of"]:
88
+ raise ValueError(
89
+ f"{name}={dim} must be a positive multiple of "
90
+ f"{SUPPORT['K']['multiple_of']} (it is a contraction dim)")
91
+ if dim % SUPPORT["N"]["multiple_of"]:
92
+ raise ValueError(
93
+ f"{name}={dim} must be a multiple of "
94
+ f"{SUPPORT['N']['multiple_of']} (it is an output dim)")
95
+ return e, h, i
96
+
97
+
98
+ class MoeExpertsNvfp4Dynamic(GuardedSeam, torch.nn.Module):
99
+ """Packed expert bank: two grouped FP4 launches behind the host
100
+ contract, for any short token batch."""
101
+
102
+ _frt_can_fallback = False
103
+
104
+ def __init__(self, gu_packed, gu_sfb, dn_packed, dn_sfb, act_fn,
105
+ num_experts, hidden, inter):
106
+ super().__init__()
107
+ self.register_buffer("_gu_packed", gu_packed)
108
+ self.register_buffer("_gu_sfb", gu_sfb)
109
+ self.register_buffer("_dn_packed", dn_packed)
110
+ self.register_buffer("_dn_sfb", dn_sfb)
111
+ self.register_buffer("_alpha", torch.ones(
112
+ num_experts, device=gu_packed.device, dtype=torch.float32))
113
+ self._act = act_fn
114
+ self._e = num_experts
115
+ self._h = hidden
116
+ self._i = inter
117
+ self._grouped = _kernel().grouped_w4a4_gemv_from_bf16
118
+ self._frt_arm(dtypes=CAST_OK, device=gu_packed.device, k=hidden)
119
+
120
+ def forward(self, hidden_states: torch.Tensor,
121
+ top_k_index: torch.Tensor,
122
+ top_k_weights: torch.Tensor) -> torch.Tensor:
123
+ admitted = self._frt_admit(hidden_states)
124
+ if admitted is not PROCEED:
125
+ return admitted
126
+ t = hidden_states.shape[0]
127
+ k = top_k_index.shape[1]
128
+ idx = top_k_index.to(torch.int32)
129
+ y = self._grouped(hidden_states.contiguous(), self._gu_packed,
130
+ self._gu_sfb, self._alpha, idx)
131
+ gate, up = y.chunk(2, dim=-1)
132
+ inter = self._act(gate) * up # [T, k, I], fresh
133
+ d = self._grouped(inter.reshape(t * k, self._i), self._dn_packed,
134
+ self._dn_sfb, self._alpha,
135
+ idx.reshape(t * k, 1))
136
+ out = (d.view(t, k, self._h).float()
137
+ * top_k_weights[..., None].float()).sum(dim=1)
138
+ return out.to(hidden_states.dtype)
139
+
140
+
141
+ @torch.no_grad()
142
+ def _pack_bank(kern, bank: torch.Tensor, alpha: torch.Tensor,
143
+ probe_gen: torch.Generator) -> tuple[
144
+ torch.Tensor, torch.Tensor, float]:
145
+ """Pack one 3D stack ``[E, N, K]``; returns worst probe-row relL2.
146
+
147
+ The kernel ships no dequantizer, so conversion is accounted at the
148
+ output: one random row through the grouped entry against the BF16
149
+ slab's own matmul, per slab, worst kept for the receipt.
150
+ """
151
+ e, n, kdim = bank.shape
152
+ packed = torch.empty(e, n, kdim // 2, device="cuda", dtype=torch.uint8)
153
+ sfb = torch.empty(e, _sf_bytes(n, kdim), device="cuda",
154
+ dtype=torch.uint8)
155
+ worst = 0.0
156
+ grouped = kern.grouped_w4a4_gemv_from_bf16
157
+ for lo in range(0, e, _BIND_SLAB):
158
+ slab = bank[lo:lo + _BIND_SLAB].to("cuda", torch.bfloat16)
159
+ for j in range(slab.shape[0]):
160
+ kern.quantize_weights_nvfp4_bf16(
161
+ slab[j].contiguous(), packed=packed[lo + j],
162
+ sfb=sfb[lo + j])
163
+ x = (torch.randn(1, kdim, device="cuda", generator=probe_gen,
164
+ dtype=torch.float32) * 0.05).to(torch.bfloat16)
165
+ got = grouped(x, packed, sfb, alpha,
166
+ torch.tensor([[lo]], device="cuda",
167
+ dtype=torch.int32))[0, 0]
168
+ ref = x[0].float() @ slab[0].float().t()
169
+ rel = float((got.float() - ref).norm() / ref.norm().clamp_min(1e-12))
170
+ worst = max(worst, rel)
171
+ del slab
172
+ return packed, sfb, worst
173
+
174
+
175
+ @torch.no_grad()
176
+ def bind_experts_seam(
177
+ weights: Mapping[str, torch.Tensor], act_fn,
178
+ ) -> tuple[MoeExpertsNvfp4Dynamic, dict[str, float]]:
179
+ """Bind one expert bank from its dense 3D stacks.
180
+
181
+ Weights stream to the GPU in expert slabs and pack there; the
182
+ returned dict carries the worst probe-row relative L2 per stack,
183
+ for the adoption receipt. The bound module holds only the packed
184
+ layout — retiring the dense bank is the caller's move (and the
185
+ point).
186
+ """
187
+ e, h, i = check_experts(weights)
188
+ kern = _kernel()
189
+ alpha = torch.ones(e, device="cuda", dtype=torch.float32)
190
+ gen = torch.Generator(device="cuda")
191
+ gen.manual_seed(0)
192
+ gu_packed, gu_sfb, gu_rel = _pack_bank(
193
+ kern, weights["gate_up_proj"], alpha, gen)
194
+ dn_packed, dn_sfb, dn_rel = _pack_bank(
195
+ kern, weights["down_proj"], alpha, gen)
196
+ bound = MoeExpertsNvfp4Dynamic(gu_packed, gu_sfb, dn_packed, dn_sfb,
197
+ act_fn, e, h, i)
198
+ # bind-time smoke: one decode-shaped call through the real entries
199
+ probe = bound(
200
+ torch.zeros(1, h, device=gu_packed.device, dtype=torch.bfloat16),
201
+ torch.zeros(1, 1, device=gu_packed.device, dtype=torch.long),
202
+ torch.ones(1, 1, device=gu_packed.device, dtype=torch.bfloat16))
203
+ if probe.shape != (1, h) or not torch.isfinite(probe).all():
204
+ raise ValueError(
205
+ f"refused: moe_experts nvfp4 bind smoke produced shape "
206
+ f"{tuple(probe.shape)}, "
207
+ f"finite={bool(torch.isfinite(probe).all())}")
208
+ return bound, {"gate_up_proj": gu_rel, "down_proj": dn_rel}
@@ -0,0 +1,129 @@
1
+ """W4A16 implementation of the ``moe_experts`` structure.
2
+
3
+ The SM110 twin of :mod:`.nvfp4_dynamic`: same packed NVFP4 expert bank,
4
+ same external routing contract, but the grouped launch keeps activations
5
+ in BF16 — the ``grouped_w4a4_*`` entries require SM120/SM121
6
+ block-scaled MMA and refuse on Thor, while ``grouped_w4a16_gemv_bf16``
7
+ serves one routed slot per activation row on every arch the package
8
+ ships. The call convention therefore differs: rows are expanded to one
9
+ per routed slot ([T*k, K]) instead of the W4A4 entry's [T, k] batch.
10
+
11
+ Packing is byte-identical to the W4A4 impl (one ``quantize_weights_
12
+ nvfp4_bf16`` layout serves both entries); only the bind-time probe and
13
+ the forward launches change.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from collections.abc import Mapping
19
+
20
+ import torch
21
+
22
+ from ...guard import CAST_OK, PROCEED, GuardedSeam
23
+ from .nvfp4_dynamic import _BIND_SLAB, _kernel, _sf_bytes, check_experts
24
+
25
+ __all__ = ["MoeExpertsW4A16", "bind_experts_seam"]
26
+
27
+
28
+ class MoeExpertsW4A16(GuardedSeam, torch.nn.Module):
29
+ """Packed expert bank behind the host contract, BF16 activations."""
30
+
31
+ _frt_can_fallback = False
32
+
33
+ def __init__(self, gu_packed, gu_sfb, dn_packed, dn_sfb, act_fn,
34
+ num_experts, hidden, inter):
35
+ super().__init__()
36
+ self.register_buffer("_gu_packed", gu_packed)
37
+ self.register_buffer("_gu_sfb", gu_sfb)
38
+ self.register_buffer("_dn_packed", dn_packed)
39
+ self.register_buffer("_dn_sfb", dn_sfb)
40
+ self.register_buffer("_alpha", torch.ones(
41
+ num_experts, device=gu_packed.device, dtype=torch.float32))
42
+ self._act = act_fn
43
+ self._e = num_experts
44
+ self._h = hidden
45
+ self._i = inter
46
+ self._grouped = _kernel().grouped_w4a16_gemv_bf16
47
+ self._frt_arm(dtypes=CAST_OK, device=gu_packed.device, k=hidden)
48
+
49
+ def _launch(self, x, packed, sfb, ids, n):
50
+ return self._grouped(
51
+ x, packed, sfb, self._alpha, ids, n=n,
52
+ w_stride=packed.shape[1] * packed.shape[2],
53
+ sfb_stride=sfb.shape[1])
54
+
55
+ def forward(self, hidden_states: torch.Tensor,
56
+ top_k_index: torch.Tensor,
57
+ top_k_weights: torch.Tensor) -> torch.Tensor:
58
+ admitted = self._frt_admit(hidden_states)
59
+ if admitted is not PROCEED:
60
+ return admitted
61
+ t = hidden_states.shape[0]
62
+ k = top_k_index.shape[1]
63
+ ids = top_k_index.reshape(-1).to(torch.int32)
64
+ x = hidden_states.to(torch.bfloat16).contiguous()
65
+ xr = x.repeat_interleave(k, dim=0) # [T*k, H]
66
+ y = self._launch(xr, self._gu_packed, self._gu_sfb, ids,
67
+ 2 * self._i) # [T*k, 2I]
68
+ gate, up = y.chunk(2, dim=-1)
69
+ inter = (self._act(gate) * up).contiguous() # [T*k, I]
70
+ d = self._launch(inter, self._dn_packed, self._dn_sfb, ids,
71
+ self._h) # [T*k, H]
72
+ out = (d.view(t, k, self._h).float()
73
+ * top_k_weights[..., None].float()).sum(dim=1)
74
+ return out.to(hidden_states.dtype)
75
+
76
+
77
+ @torch.no_grad()
78
+ def _pack_bank(kern, bank: torch.Tensor, alpha: torch.Tensor,
79
+ probe_gen: torch.Generator) -> tuple[
80
+ torch.Tensor, torch.Tensor, float]:
81
+ """Pack one 3D stack ``[E, N, K]``; probe through the W4A16 entry."""
82
+ e, n, kdim = bank.shape
83
+ packed = torch.empty(e, n, kdim // 2, device="cuda", dtype=torch.uint8)
84
+ sfb = torch.empty(e, _sf_bytes(n, kdim), device="cuda",
85
+ dtype=torch.uint8)
86
+ worst = 0.0
87
+ for lo in range(0, e, _BIND_SLAB):
88
+ slab = bank[lo:lo + _BIND_SLAB].to("cuda", torch.bfloat16)
89
+ for j in range(slab.shape[0]):
90
+ kern.quantize_weights_nvfp4_bf16(
91
+ slab[j].contiguous(), packed=packed[lo + j],
92
+ sfb=sfb[lo + j])
93
+ x = (torch.randn(1, kdim, device="cuda", generator=probe_gen,
94
+ dtype=torch.float32) * 0.05).to(torch.bfloat16)
95
+ got = kern.grouped_w4a16_gemv_bf16(
96
+ x, packed, sfb, alpha,
97
+ torch.tensor([lo], device="cuda", dtype=torch.int32),
98
+ n=n, w_stride=n * kdim // 2, sfb_stride=sfb.shape[1])[0]
99
+ ref = x[0].float() @ slab[0].float().t()
100
+ rel = float((got.float() - ref).norm() / ref.norm().clamp_min(1e-12))
101
+ worst = max(worst, rel)
102
+ del slab
103
+ return packed, sfb, worst
104
+
105
+
106
+ @torch.no_grad()
107
+ def bind_experts_seam(
108
+ weights: Mapping[str, torch.Tensor], act_fn,
109
+ ) -> tuple[MoeExpertsW4A16, dict[str, float]]:
110
+ """Bind one expert bank from its dense 3D stacks (W4A16 launches)."""
111
+ e, h, i = check_experts(weights)
112
+ kern = _kernel()
113
+ alpha = torch.ones(e, device="cuda", dtype=torch.float32)
114
+ gen = torch.Generator(device="cuda")
115
+ gen.manual_seed(0)
116
+ gu_packed, gu_sfb, gu_rel = _pack_bank(
117
+ kern, weights["gate_up_proj"], alpha, gen)
118
+ dn_packed, dn_sfb, dn_rel = _pack_bank(
119
+ kern, weights["down_proj"], alpha, gen)
120
+ bound = MoeExpertsW4A16(gu_packed, gu_sfb, dn_packed, dn_sfb,
121
+ act_fn, e, h, i)
122
+ probe = bound(torch.zeros(1, h, device="cuda", dtype=torch.bfloat16),
123
+ torch.zeros(1, 1, device="cuda", dtype=torch.int64),
124
+ torch.ones(1, 1, device="cuda", dtype=torch.float32))
125
+ if probe.shape != (1, h) or not torch.isfinite(probe).all():
126
+ raise ValueError(
127
+ f"refused: w4a16 experts bind smoke produced shape "
128
+ f"{tuple(probe.shape)}, finite={bool(torch.isfinite(probe).all())}")
129
+ return bound, {"gate_up_relL2": gu_rel, "down_relL2": dn_rel}
@@ -0,0 +1,3 @@
1
+ from .bf16 import FusedNorm, bind_norm_fused
2
+
3
+ __all__ = ["FusedNorm", "bind_norm_fused"]