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,221 @@
|
|
|
1
|
+
"""Weight-only NVFP4 implementation of the ``decoder_ffn`` structure.
|
|
2
|
+
|
|
3
|
+
Composes the fused W4A16 gate/up -> activation -> down block from the
|
|
4
|
+
``flashrt/weight-only-ffn`` Hub kernel behind the structure boundary —
|
|
5
|
+
the 4-bit twin of ``w8a16_static``, with the same decode-band envelope
|
|
6
|
+
and half the weight bytes. Weights are packed to NVFP4 (E2M1 data plus
|
|
7
|
+
per-16-element-block scale factors) at bind time; activations stay
|
|
8
|
+
BF16, so binding needs no calibration data, and qualification still
|
|
9
|
+
runs the parity gate on real host activations like every other
|
|
10
|
+
implementation.
|
|
11
|
+
|
|
12
|
+
The kernel's auto dispatch is qualified more narrowly than the INT8
|
|
13
|
+
twin's, and this impl mirrors that table exactly rather than stretching
|
|
14
|
+
it: M in [1, 3], with a per-M minimum on total weight elements (the
|
|
15
|
+
kernel refuses below it — ``weight-only-ffn`` ``torch_binding.cpp``,
|
|
16
|
+
the W4 branch). Calls outside the band are dispatched to the retained
|
|
17
|
+
host module by declared plan, counted in the ledger.
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
from __future__ import annotations
|
|
21
|
+
|
|
22
|
+
from collections.abc import Mapping
|
|
23
|
+
from functools import lru_cache
|
|
24
|
+
|
|
25
|
+
import torch
|
|
26
|
+
|
|
27
|
+
from ...guard import CAST_OK, PROCEED, GuardedSeam
|
|
28
|
+
|
|
29
|
+
KERNEL_DEP = {
|
|
30
|
+
"provider": "huggingface_kernels",
|
|
31
|
+
"repo": "flashrt/weight-only-ffn",
|
|
32
|
+
"version": ">=1",
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
_ENTRYPOINTS = {"gelu": "w4a16_geglu_ffn_bf16", "silu": "w4a16_swiglu_ffn_bf16"}
|
|
36
|
+
|
|
37
|
+
SUPPORT = {
|
|
38
|
+
"D": {"min": 512, "max": 16384, "multiple_of": 64},
|
|
39
|
+
"F": {"min": 1024, "max": 16384, "multiple_of": 64},
|
|
40
|
+
"M": {"min": 1, "max": 3},
|
|
41
|
+
"m_classes": ("micro",),
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#: the kernel's own auto-dispatch qualification: per M, the minimum
|
|
45
|
+
#: total weight elements (gate+up+down) it accepts. Copied from the W4
|
|
46
|
+
#: branch of the package's torch_binding.cpp — the kernel raises below
|
|
47
|
+
#: these, so the band dispatch must agree with them, not rediscover
|
|
48
|
+
#: them as runtime errors.
|
|
49
|
+
_AUTO_FLOOR = {1: 12 << 20, 2: 32 << 20, 3: 64 << 20}
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _in_band(m: int, weight_elements: int) -> bool:
|
|
53
|
+
floor = _AUTO_FLOOR.get(m)
|
|
54
|
+
return floor is not None and weight_elements >= floor
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@lru_cache(maxsize=1)
|
|
58
|
+
def _kernel():
|
|
59
|
+
from flashrt_structures.impls import hub_kernel
|
|
60
|
+
|
|
61
|
+
return hub_kernel(KERNEL_DEP["repo"], KERNEL_DEP["version"])
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _entrypoint(variant: Mapping[str, str]):
|
|
65
|
+
name = variant.get("activation", "gelu")
|
|
66
|
+
if name not in _ENTRYPOINTS:
|
|
67
|
+
raise ValueError(f"unsupported activation: {name!r}")
|
|
68
|
+
return getattr(_kernel(), _ENTRYPOINTS[name])
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _check(weights: Mapping[str, torch.Tensor]) -> tuple[int, int]:
|
|
72
|
+
w_gate, w_up, w_down = (weights["w_gate"], weights["w_up"],
|
|
73
|
+
weights["w_down"])
|
|
74
|
+
dim_f, dim_d = w_gate.shape
|
|
75
|
+
if w_up.shape != (dim_f, dim_d) or w_down.shape != (dim_d, dim_f):
|
|
76
|
+
raise ValueError(
|
|
77
|
+
f"inconsistent weight dims: gate {tuple(w_gate.shape)}, "
|
|
78
|
+
f"up {tuple(w_up.shape)}, down {tuple(w_down.shape)}")
|
|
79
|
+
for name, dim in (("D", dim_d), ("F", dim_f)):
|
|
80
|
+
bounds = SUPPORT[name]
|
|
81
|
+
if not bounds["min"] <= dim <= bounds["max"]:
|
|
82
|
+
raise ValueError(
|
|
83
|
+
f"{name}={dim} outside support envelope "
|
|
84
|
+
f"[{bounds['min']}, {bounds['max']}]")
|
|
85
|
+
if dim % bounds["multiple_of"]:
|
|
86
|
+
raise ValueError(
|
|
87
|
+
f"{name}={dim} must be a multiple of "
|
|
88
|
+
f"{bounds['multiple_of']}")
|
|
89
|
+
return dim_d, dim_f
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class BoundDecoderFfnW4A16:
|
|
93
|
+
"""MLP-seam callable: normed activations in, FFN output out (BF16)."""
|
|
94
|
+
|
|
95
|
+
def __init__(self, ffn_fn, gate_up_q, gate_up_sfb, down_q, down_sfb,
|
|
96
|
+
dim_d, weight_elements):
|
|
97
|
+
self._ffn = ffn_fn
|
|
98
|
+
self._gate_up_q = gate_up_q
|
|
99
|
+
self._gate_up_sfb = gate_up_sfb
|
|
100
|
+
self._down_q = down_q
|
|
101
|
+
self._down_sfb = down_sfb
|
|
102
|
+
self._dim_d = dim_d
|
|
103
|
+
self._weight_elements = weight_elements
|
|
104
|
+
|
|
105
|
+
def ffn(self, normed: torch.Tensor) -> torch.Tensor:
|
|
106
|
+
shape = normed.shape
|
|
107
|
+
x = normed.reshape(-1, shape[-1])
|
|
108
|
+
m = x.shape[0]
|
|
109
|
+
if not _in_band(m, self._weight_elements):
|
|
110
|
+
raise ValueError(
|
|
111
|
+
f"M={m} outside the W4A16 auto-dispatch qualification "
|
|
112
|
+
f"(M in [1, 3], weight elements >= "
|
|
113
|
+
f"{_AUTO_FLOOR.get(min(m, 3), 0)} at this M; "
|
|
114
|
+
f"have {self._weight_elements})")
|
|
115
|
+
out = self._ffn(x.to(torch.bfloat16).contiguous(),
|
|
116
|
+
self._gate_up_q, self._gate_up_sfb,
|
|
117
|
+
self._down_q, self._down_sfb, variant=0)
|
|
118
|
+
return out.reshape(shape).to(normed.dtype)
|
|
119
|
+
|
|
120
|
+
__call__ = ffn
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
class FusedGluMlpW4A16(GuardedSeam, torch.nn.Module):
|
|
124
|
+
"""MLP-seam module with declared M-dispatch.
|
|
125
|
+
|
|
126
|
+
The weight-only kernel covers the decode band (M in [1, 8]); calls
|
|
127
|
+
with larger M are dispatched to the retained host module. This is
|
|
128
|
+
part of the declared plan — per-M dispatch on the real workload —
|
|
129
|
+
not a fallback: both paths are first-class and the qualification
|
|
130
|
+
record states which band the kernel serves. The ledger keeps the two
|
|
131
|
+
apart under separate names for exactly that reason, and still counts
|
|
132
|
+
the dispatch: "by design" is a reason for a path to exist, not a
|
|
133
|
+
reason for its share of the calls to be unknown.
|
|
134
|
+
|
|
135
|
+
``original`` is retained whole (host MLP naming varies across model
|
|
136
|
+
families), and attribute lookups fall through to it so hosts that
|
|
137
|
+
introspect the module they call keep working.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
_frt_host_attr = "host_mlp"
|
|
141
|
+
_frt_can_fallback = True
|
|
142
|
+
|
|
143
|
+
def __init__(self, bound: BoundDecoderFfnW4A16,
|
|
144
|
+
original: torch.nn.Module | None = None):
|
|
145
|
+
super().__init__()
|
|
146
|
+
self._bound = bound
|
|
147
|
+
if original is not None:
|
|
148
|
+
self.host_mlp = original
|
|
149
|
+
guard = self._frt_arm(dtypes=CAST_OK,
|
|
150
|
+
device=bound._gate_up_q.device,
|
|
151
|
+
k=int(bound._dim_d))
|
|
152
|
+
guard.notes["dispatched_by_band"] = 0
|
|
153
|
+
|
|
154
|
+
def __getattr__(self, name):
|
|
155
|
+
try:
|
|
156
|
+
return super().__getattr__(name)
|
|
157
|
+
except AttributeError:
|
|
158
|
+
if name == "host_mlp":
|
|
159
|
+
raise
|
|
160
|
+
return getattr(super().__getattr__("host_mlp"), name)
|
|
161
|
+
|
|
162
|
+
def forward(self, hidden: torch.Tensor) -> torch.Tensor:
|
|
163
|
+
admitted = self._frt_admit(hidden)
|
|
164
|
+
if admitted is not PROCEED:
|
|
165
|
+
return admitted
|
|
166
|
+
m = hidden.numel() // hidden.shape[-1]
|
|
167
|
+
if not _in_band(m, self._bound._weight_elements):
|
|
168
|
+
host = self._frt_host()
|
|
169
|
+
if host is not None:
|
|
170
|
+
guard = self._frt_guard
|
|
171
|
+
if guard is not None and not torch.compiler.is_compiling():
|
|
172
|
+
guard.notes["dispatched_by_band"] += 1
|
|
173
|
+
return host(hidden)
|
|
174
|
+
return self._bound.ffn(hidden)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@torch.no_grad()
|
|
178
|
+
def bind_mlp_seam(
|
|
179
|
+
weights: Mapping[str, torch.Tensor],
|
|
180
|
+
*,
|
|
181
|
+
variant: Mapping[str, str],
|
|
182
|
+
original: torch.nn.Module | None = None,
|
|
183
|
+
):
|
|
184
|
+
"""Bind the MLP-seam slice of ``decoder_ffn`` with weight-only NVFP4.
|
|
185
|
+
|
|
186
|
+
``weights`` uses checkpoint-native ``[out, in]`` projection layout
|
|
187
|
+
(``w_gate``/``w_up``: ``[F, D]``, ``w_down``: ``[D, F]``). No
|
|
188
|
+
calibration data is required: quantization is per-16-element-block
|
|
189
|
+
on weights only (``quantize_w4_weight_bf16`` returns the packed
|
|
190
|
+
E2M1 data and the SFB scale tensor the FFN entry points consume).
|
|
191
|
+
"""
|
|
192
|
+
dim_d, dim_f = _check(weights)
|
|
193
|
+
weight_elements = 3 * dim_d * dim_f
|
|
194
|
+
if weight_elements < _AUTO_FLOOR[1]:
|
|
195
|
+
raise ValueError(
|
|
196
|
+
f"refused: {weight_elements} weight elements is below the "
|
|
197
|
+
f"kernel's auto-dispatch floor ({_AUTO_FLOOR[1]}) even at "
|
|
198
|
+
f"M=1; the W4A16 path cannot serve this seam at any M")
|
|
199
|
+
k = _kernel()
|
|
200
|
+
ffn_fn = _entrypoint(variant)
|
|
201
|
+
gate_up = torch.cat(
|
|
202
|
+
[weights["w_gate"].to("cuda", torch.bfloat16),
|
|
203
|
+
weights["w_up"].to("cuda", torch.bfloat16)], dim=0).contiguous()
|
|
204
|
+
down = weights["w_down"].to("cuda", torch.bfloat16).contiguous()
|
|
205
|
+
gate_up_q, gate_up_sfb = k.quantize_w4_weight_bf16(gate_up)
|
|
206
|
+
down_q, down_sfb = k.quantize_w4_weight_bf16(down)
|
|
207
|
+
bound = BoundDecoderFfnW4A16(
|
|
208
|
+
ffn_fn, gate_up_q, gate_up_sfb, down_q, down_sfb, dim_d,
|
|
209
|
+
weight_elements)
|
|
210
|
+
# bind-time smoke: one M=1 launch through the real entry point before
|
|
211
|
+
# the seam is handed out. A stale build or missing symbol must
|
|
212
|
+
# surface here as a clean bind refusal, not later inside the host's
|
|
213
|
+
# forward — identical output cannot catch it there, because the
|
|
214
|
+
# fallback path is numerically exact.
|
|
215
|
+
probe = bound.ffn(torch.zeros(1, dim_d, device=gate_up_q.device,
|
|
216
|
+
dtype=torch.bfloat16))
|
|
217
|
+
if probe.shape != (1, dim_d) or not torch.isfinite(probe).all():
|
|
218
|
+
raise ValueError(
|
|
219
|
+
f"refused: w4a16 bind smoke produced shape "
|
|
220
|
+
f"{tuple(probe.shape)}, finite={bool(torch.isfinite(probe).all())}")
|
|
221
|
+
return FusedGluMlpW4A16(bound, original=original)
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"""Weight-only INT8 implementation of the ``decoder_ffn`` structure.
|
|
2
|
+
|
|
3
|
+
Composes the fused W8A16 gate/up -> activation -> down block from the
|
|
4
|
+
``flashrt/weight-only-ffn`` Hub kernel behind the structure boundary.
|
|
5
|
+
Activations stay BF16, so binding needs no calibration data: packing is
|
|
6
|
+
a pure weight transform, and qualification still runs the parity gate on
|
|
7
|
+
real host activations like every other implementation.
|
|
8
|
+
|
|
9
|
+
The kernel's optimized dispatch covers the decode band (M in [1, 8]);
|
|
10
|
+
larger M is outside the support envelope and is refused at call time
|
|
11
|
+
rather than routed to a slow path.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
from collections.abc import Mapping
|
|
17
|
+
from functools import lru_cache
|
|
18
|
+
|
|
19
|
+
import torch
|
|
20
|
+
|
|
21
|
+
from ...guard import CAST_OK, PROCEED, GuardedSeam
|
|
22
|
+
|
|
23
|
+
KERNEL_DEP = {
|
|
24
|
+
"provider": "huggingface_kernels",
|
|
25
|
+
"repo": "flashrt/weight-only-ffn",
|
|
26
|
+
"version": ">=1",
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
_ENTRYPOINTS = {"gelu": "w8a16_geglu_ffn_bf16", "silu": "w8a16_swiglu_ffn_bf16"}
|
|
30
|
+
|
|
31
|
+
SUPPORT = {
|
|
32
|
+
"D": {"min": 512, "max": 16384, "multiple_of": 64},
|
|
33
|
+
"F": {"min": 1024, "max": 16384, "multiple_of": 64},
|
|
34
|
+
"M": {"min": 1, "max": 8},
|
|
35
|
+
"m_classes": ("micro",),
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@lru_cache(maxsize=1)
|
|
40
|
+
def _kernel():
|
|
41
|
+
from flashrt_structures.impls import hub_kernel
|
|
42
|
+
|
|
43
|
+
return hub_kernel(KERNEL_DEP["repo"], KERNEL_DEP["version"])
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _entrypoint(variant: Mapping[str, str]):
|
|
47
|
+
name = variant.get("activation", "gelu")
|
|
48
|
+
if name not in _ENTRYPOINTS:
|
|
49
|
+
raise ValueError(f"unsupported activation: {name!r}")
|
|
50
|
+
return getattr(_kernel(), _ENTRYPOINTS[name])
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _check(weights: Mapping[str, torch.Tensor]) -> tuple[int, int]:
|
|
54
|
+
w_gate, w_up, w_down = (weights["w_gate"], weights["w_up"],
|
|
55
|
+
weights["w_down"])
|
|
56
|
+
dim_f, dim_d = w_gate.shape
|
|
57
|
+
if w_up.shape != (dim_f, dim_d) or w_down.shape != (dim_d, dim_f):
|
|
58
|
+
raise ValueError(
|
|
59
|
+
f"inconsistent weight dims: gate {tuple(w_gate.shape)}, "
|
|
60
|
+
f"up {tuple(w_up.shape)}, down {tuple(w_down.shape)}")
|
|
61
|
+
for name, dim in (("D", dim_d), ("F", dim_f)):
|
|
62
|
+
bounds = SUPPORT[name]
|
|
63
|
+
if not bounds["min"] <= dim <= bounds["max"]:
|
|
64
|
+
raise ValueError(
|
|
65
|
+
f"{name}={dim} outside support envelope "
|
|
66
|
+
f"[{bounds['min']}, {bounds['max']}]")
|
|
67
|
+
if dim % bounds["multiple_of"]:
|
|
68
|
+
raise ValueError(
|
|
69
|
+
f"{name}={dim} must be a multiple of "
|
|
70
|
+
f"{bounds['multiple_of']}")
|
|
71
|
+
return dim_d, dim_f
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class BoundDecoderFfnW8A16:
|
|
75
|
+
"""MLP-seam callable: normed activations in, FFN output out (BF16)."""
|
|
76
|
+
|
|
77
|
+
def __init__(self, ffn_fn, gate_up_q, gate_up_scale, down_q, down_scale,
|
|
78
|
+
dim_d):
|
|
79
|
+
self._ffn = ffn_fn
|
|
80
|
+
self._gate_up_q = gate_up_q
|
|
81
|
+
self._gate_up_scale = gate_up_scale
|
|
82
|
+
self._down_q = down_q
|
|
83
|
+
self._down_scale = down_scale
|
|
84
|
+
self._dim_d = dim_d
|
|
85
|
+
|
|
86
|
+
def ffn(self, normed: torch.Tensor) -> torch.Tensor:
|
|
87
|
+
shape = normed.shape
|
|
88
|
+
x = normed.reshape(-1, shape[-1])
|
|
89
|
+
m = x.shape[0]
|
|
90
|
+
m_max = SUPPORT["M"]["max"]
|
|
91
|
+
if m > m_max:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
f"M={m} outside the weight-only decode envelope "
|
|
94
|
+
f"[1, {m_max}]")
|
|
95
|
+
variant = 0 if m <= 4 else 3
|
|
96
|
+
out = self._ffn(x.to(torch.bfloat16).contiguous(),
|
|
97
|
+
self._gate_up_q, self._gate_up_scale,
|
|
98
|
+
self._down_q, self._down_scale, variant=variant)
|
|
99
|
+
return out.reshape(shape).to(normed.dtype)
|
|
100
|
+
|
|
101
|
+
__call__ = ffn
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
class FusedGluMlpW8A16(GuardedSeam, torch.nn.Module):
|
|
105
|
+
"""MLP-seam module with declared M-dispatch.
|
|
106
|
+
|
|
107
|
+
The weight-only kernel covers the decode band (M in [1, 8]); calls
|
|
108
|
+
with larger M are dispatched to the retained host module. This is
|
|
109
|
+
part of the declared plan — per-M dispatch on the real workload —
|
|
110
|
+
not a fallback: both paths are first-class and the qualification
|
|
111
|
+
record states which band the kernel serves. The ledger keeps the two
|
|
112
|
+
apart under separate names for exactly that reason, and still counts
|
|
113
|
+
the dispatch: "by design" is a reason for a path to exist, not a
|
|
114
|
+
reason for its share of the calls to be unknown.
|
|
115
|
+
|
|
116
|
+
``original`` is retained whole (host MLP naming varies across model
|
|
117
|
+
families), and attribute lookups fall through to it so hosts that
|
|
118
|
+
introspect the module they call keep working.
|
|
119
|
+
"""
|
|
120
|
+
|
|
121
|
+
_frt_host_attr = "host_mlp"
|
|
122
|
+
_frt_can_fallback = True
|
|
123
|
+
|
|
124
|
+
def __init__(self, bound: BoundDecoderFfnW8A16,
|
|
125
|
+
original: torch.nn.Module | None = None):
|
|
126
|
+
super().__init__()
|
|
127
|
+
self._bound = bound
|
|
128
|
+
if original is not None:
|
|
129
|
+
self.host_mlp = original
|
|
130
|
+
guard = self._frt_arm(dtypes=CAST_OK,
|
|
131
|
+
device=bound._gate_up_q.device,
|
|
132
|
+
k=int(bound._dim_d))
|
|
133
|
+
guard.notes["dispatched_by_band"] = 0
|
|
134
|
+
|
|
135
|
+
def __getattr__(self, name):
|
|
136
|
+
try:
|
|
137
|
+
return super().__getattr__(name)
|
|
138
|
+
except AttributeError:
|
|
139
|
+
if name == "host_mlp":
|
|
140
|
+
raise
|
|
141
|
+
return getattr(super().__getattr__("host_mlp"), name)
|
|
142
|
+
|
|
143
|
+
def forward(self, hidden: torch.Tensor) -> torch.Tensor:
|
|
144
|
+
admitted = self._frt_admit(hidden)
|
|
145
|
+
if admitted is not PROCEED:
|
|
146
|
+
return admitted
|
|
147
|
+
m = hidden.numel() // hidden.shape[-1]
|
|
148
|
+
if m > SUPPORT["M"]["max"]:
|
|
149
|
+
host = self._frt_host()
|
|
150
|
+
if host is not None:
|
|
151
|
+
guard = self._frt_guard
|
|
152
|
+
if guard is not None and not torch.compiler.is_compiling():
|
|
153
|
+
guard.notes["dispatched_by_band"] += 1
|
|
154
|
+
return host(hidden)
|
|
155
|
+
return self._bound.ffn(hidden)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@torch.no_grad()
|
|
159
|
+
def bind_mlp_seam(
|
|
160
|
+
weights: Mapping[str, torch.Tensor],
|
|
161
|
+
*,
|
|
162
|
+
variant: Mapping[str, str],
|
|
163
|
+
original: torch.nn.Module | None = None,
|
|
164
|
+
):
|
|
165
|
+
"""Bind the MLP-seam slice of ``decoder_ffn`` with weight-only INT8.
|
|
166
|
+
|
|
167
|
+
``weights`` uses checkpoint-native ``[out, in]`` projection layout
|
|
168
|
+
(``w_gate``/``w_up``: ``[F, D]``, ``w_down``: ``[D, F]``). No
|
|
169
|
+
calibration data is required: quantization is per-output-channel on
|
|
170
|
+
weights only.
|
|
171
|
+
"""
|
|
172
|
+
dim_d, _ = _check(weights)
|
|
173
|
+
k = _kernel()
|
|
174
|
+
ffn_fn = _entrypoint(variant)
|
|
175
|
+
gate_up = torch.cat(
|
|
176
|
+
[weights["w_gate"].to("cuda", torch.bfloat16),
|
|
177
|
+
weights["w_up"].to("cuda", torch.bfloat16)], dim=0).contiguous()
|
|
178
|
+
down = weights["w_down"].to("cuda", torch.bfloat16).contiguous()
|
|
179
|
+
gate_up_q, gate_up_scale = k.quantize_w8_weight_bf16(gate_up)
|
|
180
|
+
down_q, down_scale = k.quantize_w8_weight_bf16(down)
|
|
181
|
+
bound = BoundDecoderFfnW8A16(
|
|
182
|
+
ffn_fn, gate_up_q, gate_up_scale, down_q, down_scale, dim_d)
|
|
183
|
+
return FusedGluMlpW8A16(bound, original=original)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"""The DiT stack region: one span, more than one executable shape.
|
|
2
|
+
|
|
3
|
+
An alternating self/cross DiT block stack is the first region family —
|
|
4
|
+
the span where hardware disagreed about structure itself. On a device
|
|
5
|
+
whose hub packages ship the fused NVFP4 epilogue symbols, the fastest
|
|
6
|
+
form is a launch chain: norms emit FP4 directly, residuals ride the
|
|
7
|
+
GEMM epilogues, the per-step modulators come from a bind-time table.
|
|
8
|
+
On a device without those symbols the seat-by-seat composition is the
|
|
9
|
+
form, and nothing here activates. The choice is a receipt
|
|
10
|
+
(:mod:`flashrt_structures.regions`), never a device branch.
|
|
11
|
+
"""
|