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.
- flashrt_structures/__init__.py +174 -0
- flashrt_structures/adapters/__init__.py +55 -0
- flashrt_structures/adapters/diffusers_attention.py +237 -0
- flashrt_structures/adapters/diffusers_rotary_attention.py +239 -0
- flashrt_structures/adapters/factored_qk_norm_rope.py +252 -0
- flashrt_structures/adapters/factored_two_way_attention.py +99 -0
- flashrt_structures/adapters/gemma_attention.py +226 -0
- flashrt_structures/adapters/packed_qkv_rope.py +342 -0
- flashrt_structures/adapters/packed_stream_qk_norm_rope.py +376 -0
- flashrt_structures/adapters/qwen_per_head_qk_norm_rope.py +231 -0
- flashrt_structures/adapters/sglang_engine.py +207 -0
- flashrt_structures/adapters/transformers_attention_interface.py +73 -0
- flashrt_structures/adapters/transformers_gated_delta.py +152 -0
- flashrt_structures/adapters/transformers_gated_delta_fused.py +96 -0
- flashrt_structures/adapters/vllm_engine.py +424 -0
- flashrt_structures/adjudicate.py +85 -0
- flashrt_structures/aot.py +191 -0
- flashrt_structures/autobuild.py +2052 -0
- flashrt_structures/beta/__init__.py +43 -0
- flashrt_structures/beta/conform.py +94 -0
- flashrt_structures/beta/joins.py +113 -0
- flashrt_structures/beta/negotiate.py +84 -0
- flashrt_structures/beta/ports.py +140 -0
- flashrt_structures/decisions.py +80 -0
- flashrt_structures/discover.py +623 -0
- flashrt_structures/explain.py +70 -0
- flashrt_structures/frontdoor.py +572 -0
- flashrt_structures/gates.py +465 -0
- flashrt_structures/guard.py +421 -0
- flashrt_structures/handle.py +189 -0
- flashrt_structures/impls/__init__.py +219 -0
- flashrt_structures/impls/adaln_producer/__init__.py +8 -0
- flashrt_structures/impls/adaln_producer/broker.py +116 -0
- flashrt_structures/impls/adaln_producer/fused.py +388 -0
- flashrt_structures/impls/adarms_stack/__init__.py +8 -0
- flashrt_structures/impls/adarms_stack/fp8_chain.py +832 -0
- flashrt_structures/impls/adarms_stack/region.py +102 -0
- flashrt_structures/impls/attention_core/__init__.py +132 -0
- flashrt_structures/impls/attention_core/fa2_seqused.py +458 -0
- flashrt_structures/impls/attention_core/fa4_cute.py +154 -0
- flashrt_structures/impls/attention_core/fa4_fp8.py +178 -0
- flashrt_structures/impls/attention_core/masked_mha.py +158 -0
- flashrt_structures/impls/attention_core/two_way_fa2.py +220 -0
- flashrt_structures/impls/cadence_static/__init__.py +18 -0
- flashrt_structures/impls/cadence_static/buffers.py +122 -0
- flashrt_structures/impls/cadence_static/cross_attention.py +187 -0
- flashrt_structures/impls/chain_elements.py +89 -0
- flashrt_structures/impls/decode_loop/__init__.py +0 -0
- flashrt_structures/impls/decode_loop/fp8_kv.py +206 -0
- flashrt_structures/impls/decode_loop/mtp_speculative.py +245 -0
- flashrt_structures/impls/decode_loop/whole_step.py +852 -0
- flashrt_structures/impls/decoder_block/__init__.py +6 -0
- flashrt_structures/impls/decoder_block/attn_sublayer.py +110 -0
- flashrt_structures/impls/decoder_block/fused.py +167 -0
- flashrt_structures/impls/decoder_ffn/__init__.py +0 -0
- flashrt_structures/impls/decoder_ffn/fp8_static.py +310 -0
- flashrt_structures/impls/decoder_ffn/fp8_static.yaml +22 -0
- flashrt_structures/impls/decoder_ffn/w4a16_static.py +221 -0
- flashrt_structures/impls/decoder_ffn/w8a16_static.py +183 -0
- flashrt_structures/impls/dit_stack/__init__.py +11 -0
- flashrt_structures/impls/dit_stack/fp4_chain.py +417 -0
- flashrt_structures/impls/dit_stack/region.py +86 -0
- flashrt_structures/impls/fixed_iter/__init__.py +29 -0
- flashrt_structures/impls/fixed_iter/openpi.py +264 -0
- flashrt_structures/impls/fixed_iter/protocol.py +94 -0
- flashrt_structures/impls/gated_delta_core/__init__.py +3 -0
- flashrt_structures/impls/gated_delta_core/fused_layer.py +545 -0
- flashrt_structures/impls/gated_delta_core/hub_v3.py +152 -0
- flashrt_structures/impls/graph_lowering/__init__.py +27 -0
- flashrt_structures/impls/graph_lowering/pi052_denoise.py +179 -0
- flashrt_structures/impls/graph_lowering/protocol.py +76 -0
- flashrt_structures/impls/graph_lowering/qwen3_vl.py +364 -0
- flashrt_structures/impls/linear_proj/__init__.py +0 -0
- flashrt_structures/impls/linear_proj/fp8_static.py +270 -0
- flashrt_structures/impls/linear_proj/nvfp4_balance.py +131 -0
- flashrt_structures/impls/linear_proj/nvfp4_dynamic.py +182 -0
- flashrt_structures/impls/linear_proj/w8a16_static.py +230 -0
- flashrt_structures/impls/modnorm_qkv_chain/__init__.py +0 -0
- flashrt_structures/impls/modnorm_qkv_chain/fp8_ptok_table.py +291 -0
- flashrt_structures/impls/moe_experts/__init__.py +9 -0
- flashrt_structures/impls/moe_experts/nvfp4_dynamic.py +208 -0
- flashrt_structures/impls/moe_experts/nvfp4_w4a16.py +129 -0
- flashrt_structures/impls/norm_fused/__init__.py +3 -0
- flashrt_structures/impls/norm_fused/bf16.py +94 -0
- flashrt_structures/impls/norm_fused/fp8_producer.py +84 -0
- flashrt_structures/impls/patch_projection/__init__.py +3 -0
- flashrt_structures/impls/patch_projection/bf16_flat.py +139 -0
- flashrt_structures/impls/prefill_tower/__init__.py +10 -0
- flashrt_structures/impls/prefill_tower/fp8_chain.py +955 -0
- flashrt_structures/impls/prefill_tower/region.py +99 -0
- flashrt_structures/impls/qk_norm_rope/__init__.py +12 -0
- flashrt_structures/impls/qk_norm_rope/per_head_gqa.py +199 -0
- flashrt_structures/impls/qk_norm_rope/projection_bf16.py +165 -0
- flashrt_structures/impls/qkv_pack/__init__.py +5 -0
- flashrt_structures/impls/qkv_pack/bf16.py +110 -0
- flashrt_structures/impls/qkv_pack/fp8_static.py +435 -0
- flashrt_structures/impls/qkv_pack/nvfp4_balance.py +218 -0
- flashrt_structures/impls/qkv_rope/__init__.py +3 -0
- flashrt_structures/impls/qkv_rope/packed_bias_bf16.py +143 -0
- flashrt_structures/impls/step_table.py +113 -0
- flashrt_structures/impls/vision_ffn/__init__.py +0 -0
- flashrt_structures/impls/vision_ffn/fp8_static.py +261 -0
- flashrt_structures/impls/vision_ffn/nvfp4_balance.py +211 -0
- flashrt_structures/impls/vision_tower/__init__.py +7 -0
- flashrt_structures/impls/vision_tower/fp8_chain.py +533 -0
- flashrt_structures/impls/vision_tower/region.py +91 -0
- flashrt_structures/matrix.py +126 -0
- flashrt_structures/points.py +368 -0
- flashrt_structures/prequantized.py +131 -0
- flashrt_structures/quantize_on_adopt.py +94 -0
- flashrt_structures/recipe.py +438 -0
- flashrt_structures/regions.py +208 -0
- flashrt_structures/schemes.py +490 -0
- flashrt_structures/stages.py +298 -0
- flashrt_structures/storage.py +255 -0
- flashrt_structures/swap.py +398 -0
- flashrt_structures/workspace.py +110 -0
- flashrt_structures-0.2.0.dist-info/METADATA +140 -0
- flashrt_structures-0.2.0.dist-info/RECORD +122 -0
- flashrt_structures-0.2.0.dist-info/WHEEL +5 -0
- flashrt_structures-0.2.0.dist-info/licenses/LICENSE +202 -0
- 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}
|