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,832 @@
|
|
|
1
|
+
"""The fused static-FP8 launch chain over an AdaRMS decoder stack.
|
|
2
|
+
|
|
3
|
+
Per layer: one fused gated-residual/AdaRMS/quantize producer feeds a
|
|
4
|
+
merged-QKV FP8 GEMM, a split+RoPE kernel writes the suffix K/V into a
|
|
5
|
+
chain-owned cache behind the copied prefix, FA2 attends over exactly
|
|
6
|
+
the used keys, and the same producer carries the post-attention
|
|
7
|
+
residual into the FFN's merged gate/up GEMM. The activation quantizer
|
|
8
|
+
sites are calibrated on the probe run against the pristine host.
|
|
9
|
+
|
|
10
|
+
Two equivalences are established at bind, not assumed:
|
|
11
|
+
|
|
12
|
+
- The host rotates pairs ``(i, i + half)`` (rotate-half); the split
|
|
13
|
+
kernel rotates adjacent pairs. The q/k projection rows are permuted
|
|
14
|
+
at quantize time so the kernel's layout carries the host's rotation
|
|
15
|
+
— attention dot products are invariant under a shared permutation
|
|
16
|
+
of the head dimension, and V stays in host order so the output
|
|
17
|
+
projection sees host layout.
|
|
18
|
+
- The host masks with a dense additive mask over
|
|
19
|
+
``[prefix | pad | suffix]``. The chain checks on the probe call
|
|
20
|
+
that every query row shares that exact pattern, then expresses it
|
|
21
|
+
as a used-key count: prefix rows copied, suffix rows appended, FA2
|
|
22
|
+
told the total. A mask outside this shape refuses the bind.
|
|
23
|
+
|
|
24
|
+
This is a region candidate: adapter contract in, receipts decide
|
|
25
|
+
activation, out-of-contract calls fall back to the retained host
|
|
26
|
+
forward. Contract checks run eager-only and step aside during
|
|
27
|
+
capture — the captured window is certified by its own gate.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
from __future__ import annotations
|
|
31
|
+
|
|
32
|
+
import types
|
|
33
|
+
from typing import Any, Callable
|
|
34
|
+
|
|
35
|
+
import torch
|
|
36
|
+
|
|
37
|
+
from .. import KernelUnavailable, hub_kernel
|
|
38
|
+
from ..chain_elements import (
|
|
39
|
+
ATTN_RUNGS, FP8_MAX, attention_rungs as _attention_rungs,
|
|
40
|
+
cache_kv as _cache_kv, fp8_weight as _fp8_weight,
|
|
41
|
+
gelu_tanh_like as _gelu_tanh_like,
|
|
42
|
+
interleave_rows as _interleave_rows)
|
|
43
|
+
from ...guard import GuardedSeam
|
|
44
|
+
|
|
45
|
+
GEMM_PACKAGE = "flashrt/fp8-gemm"
|
|
46
|
+
NORM_PACKAGE = "flashrt/flashrt-adaptive-norms"
|
|
47
|
+
ROPE_PACKAGE = "flashrt/flashrt-qkv-cache-rope"
|
|
48
|
+
GEMM_SYMBOLS = ("fp8_linear_bf16",)
|
|
49
|
+
NORM_SYMBOLS = ("gate_residual_ada_norm_fp8_static_bf16",)
|
|
50
|
+
ROPE_SYMBOLS = ("qkv_split_rope_kvcache_bf16",)
|
|
51
|
+
FUSE_PACKAGE = "flashrt/transformer-fused-ops"
|
|
52
|
+
FUSE_SYMBOLS = ("gate_geglu_merged_quant_fp8_static_bf16",
|
|
53
|
+
"quantize_fp8_static_bf16")
|
|
54
|
+
#: the mixed-precision band, the native decoder's own form: the three
|
|
55
|
+
#: norm-fed GEMMs ride W4A4 NVFP4 with dynamic block scales, the down
|
|
56
|
+
#: projection stays static FP8 (its input is the fused GEGLU's FP8)
|
|
57
|
+
FP4_GEMM_PACKAGE = "flashrt/fp4-gemm"
|
|
58
|
+
FP4_NORM_PACKAGE = "flashrt/fp4-fused-ops"
|
|
59
|
+
FP4_GEMM_SYMBOLS = ("nvfp4_gemm_bias_bf16", "quantize_fp4_sfa_bf16")
|
|
60
|
+
FP4_NORM_SYMBOLS = ("gate_res_ada_rms_norm_quant_nvfp4_swizzled_bf16",)
|
|
61
|
+
|
|
62
|
+
#: the band table IS the recipe: one row per precision band, naming
|
|
63
|
+
#: the extra packages the band assembles, its candidate rank, and the
|
|
64
|
+
#: down-projection flavor. The chain body stays one form; adding a
|
|
65
|
+
#: band means adding a row (and its element closures in
|
|
66
|
+
#: :func:`_band_elements`), never another branch in the loop.
|
|
67
|
+
#: ``fp4_full`` is the native decoder's complete form — all four
|
|
68
|
+
#: projections ride NVFP4, the GEGLU emits packed FP4 directly — and
|
|
69
|
+
#: it qualifies the moment its producer ships a bf16 entry.
|
|
70
|
+
BANDS: dict[str, dict] = {
|
|
71
|
+
"fp8": {"packages": (), "precision_rank": 0, "dn": "fp8"},
|
|
72
|
+
"fp4": {"packages": ((FP4_GEMM_PACKAGE, FP4_GEMM_SYMBOLS),
|
|
73
|
+
(FP4_NORM_PACKAGE, FP4_NORM_SYMBOLS)),
|
|
74
|
+
"precision_rank": 1, "dn": "fp8"},
|
|
75
|
+
"fp4_full": {"packages": (
|
|
76
|
+
(FP4_GEMM_PACKAGE, FP4_GEMM_SYMBOLS),
|
|
77
|
+
(FP4_NORM_PACKAGE, FP4_NORM_SYMBOLS
|
|
78
|
+
+ ("gelu_mul_nvfp4_bf16",))),
|
|
79
|
+
"precision_rank": 2, "dn": "fp4"},
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
#: the attention element resolves per host: the house CuTe FA4
|
|
83
|
+
#: runtime first — it carries the D256 2CTA forward this stack's
|
|
84
|
+
#: 8-query/1-KV heads need, which the community FA4 package does not
|
|
85
|
+
#: expose — then the FA2 used-keys entry. The chain cache holds
|
|
86
|
+
#: exactly the used keys, so both rungs run the same dense
|
|
87
|
+
#: non-causal call into a caller-owned output; a rung that loads but
|
|
88
|
+
#: cannot execute is eliminated by the bind-time functional probe,
|
|
89
|
+
#: not by device lists.
|
|
90
|
+
#: whole-stack smoke on every probe call; the arm's end-to-end parity
|
|
91
|
+
#: gate (0.99 vs the host's own eager run) stays the judge
|
|
92
|
+
SMOKE_FLOOR = 0.985
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def missing_symbols_fp4() -> list[str]:
|
|
96
|
+
return missing_symbols(band="fp4")
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def missing_symbols(band: str = "fp8") -> list[str]:
|
|
100
|
+
"""The factual prerequisites this box does not meet (may be empty)."""
|
|
101
|
+
gaps: list[str] = []
|
|
102
|
+
packages = ((GEMM_PACKAGE, GEMM_SYMBOLS),
|
|
103
|
+
(NORM_PACKAGE, NORM_SYMBOLS),
|
|
104
|
+
(ROPE_PACKAGE, ROPE_SYMBOLS),
|
|
105
|
+
(FUSE_PACKAGE, FUSE_SYMBOLS)) + BANDS[band]["packages"]
|
|
106
|
+
for repo, symbols in packages:
|
|
107
|
+
try:
|
|
108
|
+
kern = hub_kernel(repo, ">=1")
|
|
109
|
+
except KernelUnavailable:
|
|
110
|
+
gaps.append(repo)
|
|
111
|
+
continue
|
|
112
|
+
gaps.extend(f"{repo}:{s}" for s in symbols
|
|
113
|
+
if not hasattr(kern, s))
|
|
114
|
+
if not _attention_rungs():
|
|
115
|
+
gaps.append("attention: " + " or ".join(
|
|
116
|
+
r[1] for r in ATTN_RUNGS))
|
|
117
|
+
return gaps
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class BoundAdaRmsFp8Chain(GuardedSeam, torch.nn.Module):
|
|
121
|
+
"""Bind-time state: FP8 weights, style stack, chain-owned caches.
|
|
122
|
+
|
|
123
|
+
Plain tensor attributes, not buffers — a ledger citizen, not a
|
|
124
|
+
state_dict citizen; the truth of every weight stays with the host
|
|
125
|
+
modules the chain absorbs, which keeps revert bit-exact for free.
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
_frt_can_fallback = False # fallback is the routed closure's job
|
|
129
|
+
|
|
130
|
+
def __init__(self) -> None:
|
|
131
|
+
super().__init__()
|
|
132
|
+
self.table: list[dict] = []
|
|
133
|
+
self.dims: dict = {}
|
|
134
|
+
self.buf: dict = {}
|
|
135
|
+
self.style_w_t = None
|
|
136
|
+
self.style_b = None
|
|
137
|
+
self.rope = None
|
|
138
|
+
self.scaling = 1.0
|
|
139
|
+
self.eps = 1e-6
|
|
140
|
+
self.p_used = 0
|
|
141
|
+
self.total_keys = 0
|
|
142
|
+
self.out_ctor = None
|
|
143
|
+
self.kperm = None
|
|
144
|
+
self.kernels: dict = {}
|
|
145
|
+
#: the step-table form (the stack's own house pattern): style
|
|
146
|
+
#: modulations are baked per probed step at bind, and the run
|
|
147
|
+
#: resolves the live conditioning to a table by on-device
|
|
148
|
+
#: nearest-neighbour match — no Python state, so the same
|
|
149
|
+
#: data-driven selection is what a compile traces and a
|
|
150
|
+
#: capture bakes
|
|
151
|
+
self.step_conds = None
|
|
152
|
+
self.style_table = None
|
|
153
|
+
self.fin_table = None
|
|
154
|
+
self.band = "fp8"
|
|
155
|
+
self.zero_bias: dict = {}
|
|
156
|
+
#: armed by the region wire (autobuild) once a bound producer
|
|
157
|
+
#: guarantees the chain caches' prefix rows are written
|
|
158
|
+
#: in-graph before the first decoder step: the per-step prefix
|
|
159
|
+
#: gather leaves the graph. Bind-time state — constant by
|
|
160
|
+
#: capture time, so both a capture and a compile bake one arm.
|
|
161
|
+
self.prefix_wired = False
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _stack_parts(stack):
|
|
165
|
+
layers = list(stack.layers)
|
|
166
|
+
attn = layers[0].self_attn
|
|
167
|
+
head_dim = getattr(attn, "head_dim", None)
|
|
168
|
+
if not isinstance(head_dim, int):
|
|
169
|
+
raise ValueError("attention exposes no integer head_dim")
|
|
170
|
+
nh = attn.q_proj.out_features // head_dim
|
|
171
|
+
kv = attn.k_proj.out_features // head_dim
|
|
172
|
+
dim = attn.q_proj.in_features
|
|
173
|
+
hidden = layers[0].mlp.gate_proj.out_features
|
|
174
|
+
return layers, nh, kv, head_dim, dim, hidden
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
@torch.no_grad()
|
|
178
|
+
def _quantize(bound: BoundAdaRmsFp8Chain, layers, amax: dict) -> None:
|
|
179
|
+
"""Pack every stack GEMM from the pristine host: static FP8 with
|
|
180
|
+
the calibrated activation scale folded into alpha, or — on the
|
|
181
|
+
mixed band — NVFP4 with dynamic block scales for the norm-fed
|
|
182
|
+
GEMMs (the down projection stays FP8; its input already is)."""
|
|
183
|
+
nh, kv, hd = (bound.dims[k] for k in ("nh", "kv", "hd"))
|
|
184
|
+
fp4 = BANDS[bound.band]["packages"] != ()
|
|
185
|
+
dn_fp4 = BANDS[bound.band]["dn"] == "fp4"
|
|
186
|
+
quant4 = (bound.kernels["kg4"].quantize_fp4_sfa_bf16
|
|
187
|
+
if fp4 else None)
|
|
188
|
+
for i, ly in enumerate(layers):
|
|
189
|
+
attn, mlp = ly.self_attn, ly.mlp
|
|
190
|
+
a_qkv, a_o, a_gu, a_dn = (amax[(i, s)] / FP8_MAX for s in
|
|
191
|
+
("qkv", "o", "gu", "dn"))
|
|
192
|
+
qkv_w = torch.cat([
|
|
193
|
+
_interleave_rows(attn.q_proj.weight, nh, hd),
|
|
194
|
+
_interleave_rows(attn.k_proj.weight, kv, hd),
|
|
195
|
+
attn.v_proj.weight], dim=0)
|
|
196
|
+
gu_w = torch.cat([mlp.gate_proj.weight, mlp.up_proj.weight], dim=0)
|
|
197
|
+
entry: dict[str, Any] = {}
|
|
198
|
+
for name, w, act in (("qkv", qkv_w, a_qkv),
|
|
199
|
+
("o", attn.o_proj.weight, a_o),
|
|
200
|
+
("gu", gu_w, a_gu),
|
|
201
|
+
("dn", mlp.down_proj.weight, a_dn)):
|
|
202
|
+
if fp4 and (name != "dn" or dn_fp4):
|
|
203
|
+
wp, wsf = quant4(
|
|
204
|
+
w.detach().to("cuda", torch.bfloat16)
|
|
205
|
+
.contiguous(), is_sfb=True)
|
|
206
|
+
entry[name] = (wp, wsf)
|
|
207
|
+
if w.shape[0] not in bound.zero_bias:
|
|
208
|
+
bound.zero_bias[w.shape[0]] = torch.zeros(
|
|
209
|
+
w.shape[0], device="cuda",
|
|
210
|
+
dtype=torch.bfloat16)
|
|
211
|
+
continue
|
|
212
|
+
packed, w_scale = _fp8_weight(w)
|
|
213
|
+
entry[name] = packed
|
|
214
|
+
entry[f"a_{name}"] = act * w_scale
|
|
215
|
+
entry["sc_qkv"] = torch.tensor([a_qkv], device="cuda",
|
|
216
|
+
dtype=torch.float32)
|
|
217
|
+
entry["sc_gu"] = torch.tensor([a_gu], device="cuda",
|
|
218
|
+
dtype=torch.float32)
|
|
219
|
+
entry["inv_o"] = 1.0 / a_o if a_o > 0 else 1.0
|
|
220
|
+
entry["inv_dn"] = 1.0 / a_dn if a_dn > 0 else 1.0
|
|
221
|
+
entry["t_sc_o"] = torch.tensor([a_o], device="cuda",
|
|
222
|
+
dtype=torch.float32)
|
|
223
|
+
entry["t_sc_dn"] = torch.tensor([a_dn], device="cuda",
|
|
224
|
+
dtype=torch.float32)
|
|
225
|
+
bound.table.append(entry)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def _style_stack(bound: BoundAdaRmsFp8Chain, stack, layers) -> None:
|
|
229
|
+
"""One stacked projection serves every norm's (scale, shift, gate)."""
|
|
230
|
+
norms = []
|
|
231
|
+
for ly in layers:
|
|
232
|
+
norms.extend((ly.input_layernorm, ly.post_attention_layernorm))
|
|
233
|
+
norms.append(stack.norm)
|
|
234
|
+
w = torch.cat([n.dense.weight.detach().float() for n in norms], dim=0)
|
|
235
|
+
b = torch.cat([n.dense.bias.detach().float() for n in norms], dim=0)
|
|
236
|
+
bound.style_w_t = w.t().contiguous().to("cuda")
|
|
237
|
+
bound.style_b = b.to("cuda").unsqueeze(0)
|
|
238
|
+
bound.eps = float(getattr(norms[0], "eps", 1e-6))
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
def _mask_facts(mask: torch.Tensor, seq: int) -> tuple[int, int] | None:
|
|
242
|
+
"""Read ``[prefix | pad | suffix]`` out of the additive mask, or
|
|
243
|
+
refuse: every row identical, valid keys one prefix run plus the
|
|
244
|
+
whole suffix."""
|
|
245
|
+
if mask.dim() != 4 or mask.shape[0] != 1 or mask.shape[-2] != seq:
|
|
246
|
+
return None
|
|
247
|
+
rows = mask[0, 0] if mask.shape[1] == 1 else mask[0, :1, :, :][0]
|
|
248
|
+
valid = rows == 0
|
|
249
|
+
if not bool((valid == valid[:1]).all()):
|
|
250
|
+
return None
|
|
251
|
+
row = valid[0]
|
|
252
|
+
total = row.shape[0]
|
|
253
|
+
p_raw = total - seq
|
|
254
|
+
if p_raw < 1 or not bool(row[p_raw:].all()):
|
|
255
|
+
return None
|
|
256
|
+
prefix = row[:p_raw]
|
|
257
|
+
p_used = int(prefix.sum())
|
|
258
|
+
if p_used < 1 or not bool(prefix[:p_used].all()):
|
|
259
|
+
return None
|
|
260
|
+
return p_used, p_raw
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def _build_rope(bound: BoundAdaRmsFp8Chain, stack,
|
|
264
|
+
position_ids: torch.Tensor) -> bool:
|
|
265
|
+
hd = bound.dims["hd"]
|
|
266
|
+
half = hd // 2
|
|
267
|
+
dummy = torch.zeros(1, position_ids.shape[1], hd, device="cuda",
|
|
268
|
+
dtype=torch.float32)
|
|
269
|
+
cos, sin = stack.rotary_emb(dummy, position_ids.to("cuda"))
|
|
270
|
+
cos, sin = cos[0].float(), sin[0].float()
|
|
271
|
+
if not torch.allclose(cos[:, :half], cos[:, half:], atol=1e-5):
|
|
272
|
+
return False
|
|
273
|
+
rope = torch.empty(position_ids.shape[1], hd, device="cuda",
|
|
274
|
+
dtype=torch.bfloat16)
|
|
275
|
+
rope[:, 0::2] = cos[:, :half].to(torch.bfloat16)
|
|
276
|
+
rope[:, 1::2] = sin[:, :half].to(torch.bfloat16)
|
|
277
|
+
bound.rope = rope
|
|
278
|
+
return True
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _alloc(bound: BoundAdaRmsFp8Chain) -> None:
|
|
282
|
+
S, D, nh, kv, hd, H, L = (bound.dims[k] for k in
|
|
283
|
+
("seq", "dim", "nh", "kv", "hd",
|
|
284
|
+
"hidden", "layers"))
|
|
285
|
+
T = bound.total_keys
|
|
286
|
+
dev, bf = "cuda", torch.bfloat16
|
|
287
|
+
b = bound.buf
|
|
288
|
+
b["zero"] = torch.zeros(S, D, device=dev, dtype=bf)
|
|
289
|
+
b["ones_w"] = torch.ones(D, device=dev, dtype=bf)
|
|
290
|
+
b["res"] = torch.empty(S, D, device=dev, dtype=bf)
|
|
291
|
+
b["xn8"] = torch.empty(S, D, device=dev, dtype=torch.float8_e4m3fn)
|
|
292
|
+
b["g1"] = torch.empty(S, D, device=dev, dtype=bf)
|
|
293
|
+
b["g2"] = torch.empty(S, D, device=dev, dtype=bf)
|
|
294
|
+
b["dn"] = torch.empty(S, D, device=dev, dtype=bf)
|
|
295
|
+
b["qkv"] = torch.empty(S, (nh + 2 * kv) * hd, device=dev, dtype=bf)
|
|
296
|
+
b["q"] = torch.empty(1, S, nh, hd, device=dev, dtype=bf)
|
|
297
|
+
b["gu"] = torch.empty(S, 2 * H, device=dev, dtype=bf)
|
|
298
|
+
b["hid8"] = torch.empty(S, H, device=dev,
|
|
299
|
+
dtype=torch.float8_e4m3fn)
|
|
300
|
+
b["o8"] = torch.empty(S, nh * hd, device=dev,
|
|
301
|
+
dtype=torch.float8_e4m3fn)
|
|
302
|
+
b["kc"] = [torch.zeros(1, T, kv, hd, device=dev, dtype=bf)
|
|
303
|
+
for _ in range(L)]
|
|
304
|
+
b["vc"] = [torch.zeros(1, T, kv, hd, device=dev, dtype=bf)
|
|
305
|
+
for _ in range(L)]
|
|
306
|
+
b["seqused"] = torch.full((1,), T, device=dev, dtype=torch.int32)
|
|
307
|
+
b["att"] = torch.empty(1, S, nh, hd, device=dev, dtype=bf)
|
|
308
|
+
if BANDS[bound.band]["packages"] != ():
|
|
309
|
+
# static FP4 out-buffers, sized by the quantizer's own
|
|
310
|
+
# allocator: the SF tensor's tile-padding entries are zeroed
|
|
311
|
+
# once here and never written by the producers, so one
|
|
312
|
+
# zero-padded pair per site stays valid across every replay —
|
|
313
|
+
# no per-call allocation, no per-call SF memset in the graph
|
|
314
|
+
quant4 = bound.kernels["kg4"].quantize_fp4_sfa_bf16
|
|
315
|
+
b["xp4"], b["xsf4"] = quant4(b["zero"])
|
|
316
|
+
b["op4"], b["osf4"] = quant4(
|
|
317
|
+
torch.zeros(S, nh * hd, device=dev, dtype=bf))
|
|
318
|
+
# the norm kernel's style contract is contiguous (rows, 3*dim):
|
|
319
|
+
# one static stage, filled by a single broadcast copy per step
|
|
320
|
+
b["st4"] = torch.empty(2 * L, S, 3 * D, device=dev, dtype=bf)
|
|
321
|
+
if BANDS[bound.band]["dn"] == "fp4":
|
|
322
|
+
b["hp4"], b["hsf4"] = quant4(
|
|
323
|
+
torch.zeros(S, bound.dims["hidden"], device=dev,
|
|
324
|
+
dtype=bf))
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _make_attend(bound: BoundAdaRmsFp8Chain, mode: str, kern):
|
|
328
|
+
"""One rung of the attention ladder, closed over the buffers."""
|
|
329
|
+
b = bound.buf
|
|
330
|
+
S, nh, hd = (bound.dims[k] for k in ("seq", "nh", "hd"))
|
|
331
|
+
scaling = bound.scaling
|
|
332
|
+
att2 = b["att"].view(S, nh * hd)
|
|
333
|
+
if mode == "fa4_cute":
|
|
334
|
+
def attend(layer_index):
|
|
335
|
+
kern.forward_static(
|
|
336
|
+
b["q"], b["kc"][layer_index], b["vc"][layer_index],
|
|
337
|
+
b["att"], softmax_scale=scaling, causal=False,
|
|
338
|
+
pack_gqa=True, seqused_k=b["seqused"])
|
|
339
|
+
return att2
|
|
340
|
+
return attend
|
|
341
|
+
lse = kern.allocate_outputs(b["q"])[1]
|
|
342
|
+
|
|
343
|
+
def attend(layer_index):
|
|
344
|
+
kern.forward_seqused_static(
|
|
345
|
+
b["q"], b["kc"][layer_index], b["vc"][layer_index],
|
|
346
|
+
b["seqused"], out=b["att"], softmax_lse=lse,
|
|
347
|
+
softmax_scale=scaling)
|
|
348
|
+
return att2
|
|
349
|
+
return attend
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _band_elements(bound: BoundAdaRmsFp8Chain):
|
|
353
|
+
"""The band row's two element closures, plus the style stager.
|
|
354
|
+
|
|
355
|
+
Every band expresses the same three moves — stage the step's
|
|
356
|
+
styles, feed a style-conditioned producer into a projection site,
|
|
357
|
+
project the attention output — so the chain loop stays one form
|
|
358
|
+
and a band is a table row plus this closure pair.
|
|
359
|
+
|
|
360
|
+
- ``stage_styles(styles)``: per-step staging; returns the handle
|
|
361
|
+
the producer consumes.
|
|
362
|
+
- ``norm_project(styles, slot, x, prev_gate, e, site, out,
|
|
363
|
+
gate_out)``: gated-residual AdaRMS producer into the ``site``
|
|
364
|
+
GEMM, result in ``out``, the norm's gate in ``gate_out``.
|
|
365
|
+
- ``out_project(att2, e, out)``: quantize the attention output and
|
|
366
|
+
run the output projection into ``out``.
|
|
367
|
+
"""
|
|
368
|
+
b = bound.buf
|
|
369
|
+
kg = bound.kernels["kg"]
|
|
370
|
+
kn = bound.kernels["kn"]
|
|
371
|
+
kf = bound.kernels["kf"]
|
|
372
|
+
eps = bound.eps
|
|
373
|
+
S, D, L = (bound.dims[k] for k in ("seq", "dim", "layers"))
|
|
374
|
+
row = BANDS[bound.band]
|
|
375
|
+
if row["packages"] != ():
|
|
376
|
+
norm4 = bound.kernels["kn4"] .gate_res_ada_rms_norm_quant_nvfp4_swizzled_bf16
|
|
377
|
+
gemm4 = bound.kernels["kg4"].nvfp4_gemm_bias_bf16
|
|
378
|
+
quant4 = bound.kernels["kg4"].quantize_fp4_sfa_bf16
|
|
379
|
+
zb = bound.zero_bias
|
|
380
|
+
xp4, xsf4 = b["xp4"], b["xsf4"]
|
|
381
|
+
op4, osf4 = b["op4"], b["osf4"]
|
|
382
|
+
st4 = b["st4"]
|
|
383
|
+
# bind-time layout probe: a producer that accepts a single
|
|
384
|
+
# style row drops both the staging copy and the kernel's
|
|
385
|
+
# full-rows style read; an older producer falls back to the
|
|
386
|
+
# staged form. The smoke and the captured parity gate judge.
|
|
387
|
+
try:
|
|
388
|
+
_t = torch.zeros(2, D, device="cuda", dtype=torch.bfloat16)
|
|
389
|
+
norm4(_t, _t, _t.clone(),
|
|
390
|
+
torch.zeros(1, 3 * D, device="cuda",
|
|
391
|
+
dtype=torch.bfloat16))
|
|
392
|
+
torch.cuda.synchronize()
|
|
393
|
+
rows1 = True
|
|
394
|
+
except Exception: # noqa: BLE001 — a layout fact, not an error
|
|
395
|
+
rows1 = False
|
|
396
|
+
|
|
397
|
+
if rows1:
|
|
398
|
+
def stage_styles(styles):
|
|
399
|
+
return styles
|
|
400
|
+
else:
|
|
401
|
+
def stage_styles(styles):
|
|
402
|
+
# one broadcast copy stages every layer's rows for
|
|
403
|
+
# the step — the per-norm expand+contiguous kernels
|
|
404
|
+
# never enter the graph
|
|
405
|
+
st4.copy_(styles[:2 * L].expand(-1, S, -1))
|
|
406
|
+
return st4
|
|
407
|
+
|
|
408
|
+
def norm_project(styles, slot, x, prev_gate, e, site, out,
|
|
409
|
+
gate_out):
|
|
410
|
+
norm4(x, prev_gate, b["res"], styles[slot], packed=xp4,
|
|
411
|
+
sf_swizzled=xsf4, gate=gate_out)
|
|
412
|
+
wp, wsf = e[site]
|
|
413
|
+
gemm4(xp4, wp, xsf4, wsf, zb[out.shape[1]], out=out)
|
|
414
|
+
|
|
415
|
+
def out_project(att2, e, out):
|
|
416
|
+
quant4(att2, op4, osf4)
|
|
417
|
+
gemm4(op4, e["o"][0], osf4, e["o"][1], zb[D], out=out)
|
|
418
|
+
|
|
419
|
+
if row["dn"] == "fp4":
|
|
420
|
+
geglu4 = bound.kernels["kn4"].gelu_mul_nvfp4_bf16
|
|
421
|
+
hp4, hsf4 = b["hp4"], b["hsf4"]
|
|
422
|
+
|
|
423
|
+
def down_project(e):
|
|
424
|
+
geglu4(b["gu"], packed=hp4, sfa=hsf4)
|
|
425
|
+
gemm4(hp4, e["dn"][0], hsf4, e["dn"][1], zb[D],
|
|
426
|
+
out=b["dn"])
|
|
427
|
+
else:
|
|
428
|
+
kf_ = bound.kernels["kf"]
|
|
429
|
+
kg_ = bound.kernels["kg"]
|
|
430
|
+
|
|
431
|
+
def down_project(e):
|
|
432
|
+
kf_.gate_geglu_merged_quant_fp8_static_bf16(
|
|
433
|
+
b["gu"], e["t_sc_dn"], out=b["hid8"])
|
|
434
|
+
kg_.fp8_linear_bf16(b["hid8"], e["dn"],
|
|
435
|
+
alpha=e["a_dn"], out=b["dn"])
|
|
436
|
+
|
|
437
|
+
return stage_styles, norm_project, out_project, down_project
|
|
438
|
+
|
|
439
|
+
def stage_styles(styles):
|
|
440
|
+
return styles
|
|
441
|
+
|
|
442
|
+
def norm_project(styles, slot, x, prev_gate, e, site, out,
|
|
443
|
+
gate_out):
|
|
444
|
+
kn.gate_residual_ada_norm_fp8_static_bf16(
|
|
445
|
+
b["res"], x, prev_gate, b["ones_w"], styles[slot],
|
|
446
|
+
e["sc_" + site], eps, out=b["xn8"], gate_out=gate_out)
|
|
447
|
+
kg.fp8_linear_bf16(b["xn8"], e[site], alpha=e["a_" + site],
|
|
448
|
+
out=out)
|
|
449
|
+
|
|
450
|
+
def out_project(att2, e, out):
|
|
451
|
+
kf.quantize_fp8_static_bf16(att2, e["t_sc_o"], out=b["o8"])
|
|
452
|
+
kg.fp8_linear_bf16(b["o8"], e["o"], alpha=e["a_o"], out=out)
|
|
453
|
+
|
|
454
|
+
def down_project(e):
|
|
455
|
+
kf.gate_geglu_merged_quant_fp8_static_bf16(
|
|
456
|
+
b["gu"], e["t_sc_dn"], out=b["hid8"])
|
|
457
|
+
kg.fp8_linear_bf16(b["hid8"], e["dn"], alpha=e["a_dn"],
|
|
458
|
+
out=b["dn"])
|
|
459
|
+
|
|
460
|
+
return stage_styles, norm_project, out_project, down_project
|
|
461
|
+
|
|
462
|
+
|
|
463
|
+
def _make_run(bound: BoundAdaRmsFp8Chain):
|
|
464
|
+
kg = bound.kernels["kg"]
|
|
465
|
+
kn = bound.kernels["kn"]
|
|
466
|
+
kr = bound.kernels["kr"]
|
|
467
|
+
kf = bound.kernels["kf"]
|
|
468
|
+
attend = bound.kernels["attend"]
|
|
469
|
+
S, D, nh, kv, hd, H, L = (bound.dims[k] for k in
|
|
470
|
+
("seq", "dim", "nh", "kv", "hd",
|
|
471
|
+
"hidden", "layers"))
|
|
472
|
+
P, T = bound.p_used, bound.total_keys
|
|
473
|
+
b = bound.buf
|
|
474
|
+
table = bound.table
|
|
475
|
+
eps = bound.eps
|
|
476
|
+
qkv3 = b["qkv"].view(1, S, (nh + 2 * kv) * hd)
|
|
477
|
+
fp8 = torch.float8_e4m3fn
|
|
478
|
+
(stage_styles, norm_project, out_project,
|
|
479
|
+
down_project) = _band_elements(bound)
|
|
480
|
+
|
|
481
|
+
kperm = bound.kperm
|
|
482
|
+
|
|
483
|
+
rf = torch.profiler.record_function
|
|
484
|
+
|
|
485
|
+
def run(x2d, cond, prefix_kv):
|
|
486
|
+
if not bound.prefix_wired:
|
|
487
|
+
with rf("ad:prefix"):
|
|
488
|
+
for l, (pk, pv) in enumerate(prefix_kv):
|
|
489
|
+
# rotated pairs are adjacent; the host cached
|
|
490
|
+
# prefix keys in rotate-half layout — gather them
|
|
491
|
+
# into the shared permutation so q·k stays
|
|
492
|
+
# layout-consistent. A wired prefix skips this:
|
|
493
|
+
# the producer already wrote these rows in chain
|
|
494
|
+
# layout, and kperm∘kperm_inv is the identity.
|
|
495
|
+
torch.index_select(pk, -1, kperm,
|
|
496
|
+
out=b["kc"][l][0, :P, 0])
|
|
497
|
+
b["vc"][l][0, :P, 0].copy_(pv)
|
|
498
|
+
# nearest-neighbour step resolution, fully on-device: the
|
|
499
|
+
# schedule is a bind-time fact, so the live conditioning names
|
|
500
|
+
# its baked table without a host round-trip or Python state
|
|
501
|
+
with rf("ad:style"):
|
|
502
|
+
step = (bound.step_conds
|
|
503
|
+
- cond.float()).abs().sum(-1).argmin()
|
|
504
|
+
styles = bound.style_table.index_select(
|
|
505
|
+
0, step.view(1))[0]
|
|
506
|
+
fin = bound.fin_table.index_select(0, step.view(1))[0]
|
|
507
|
+
styles = stage_styles(styles)
|
|
508
|
+
res = b["res"]
|
|
509
|
+
res.copy_(x2d)
|
|
510
|
+
delta, gate = b["zero"], b["zero"]
|
|
511
|
+
rf_layers = rf("ad:layers")
|
|
512
|
+
rf_layers.__enter__()
|
|
513
|
+
for l in range(L):
|
|
514
|
+
e = table[l]
|
|
515
|
+
norm_project(styles, 2 * l, delta, gate, e, "qkv",
|
|
516
|
+
b["qkv"], b["g1"])
|
|
517
|
+
kr.qkv_split_rope_kvcache_bf16(
|
|
518
|
+
qkv3, bound.rope, nh, kv, hd, P, q_out=b["q"],
|
|
519
|
+
k_cache=b["kc"][l], v_cache=b["vc"][l], max_seq_len=T)
|
|
520
|
+
att2 = attend(l)
|
|
521
|
+
out_project(att2, e, b["dn"])
|
|
522
|
+
norm_project(styles, 2 * l + 1, b["dn"], b["g1"], e, "gu",
|
|
523
|
+
b["gu"], b["g2"])
|
|
524
|
+
down_project(e)
|
|
525
|
+
delta, gate = b["dn"], b["g2"]
|
|
526
|
+
rf_layers.__exit__(None, None, None)
|
|
527
|
+
with rf("ad:tail"):
|
|
528
|
+
res = res.float() + gate.float() * delta.float()
|
|
529
|
+
normed = res * torch.rsqrt(
|
|
530
|
+
res.square().mean(-1, keepdim=True) + eps)
|
|
531
|
+
out = (normed * (1 + fin[0]) + fin[1]).to(torch.bfloat16)
|
|
532
|
+
return bound.out_ctor(last_hidden_state=out.view(1, S, D),
|
|
533
|
+
past_key_values=None)
|
|
534
|
+
|
|
535
|
+
return run
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
def bind_adarms_fp4_chain(model, root: str,
|
|
539
|
+
probe: Callable[[], Any]) -> dict:
|
|
540
|
+
return bind_adarms_fp8_chain(model, root, probe, band="fp4")
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def bind_adarms_fp8_chain(model, root: str,
|
|
544
|
+
probe: Callable[[], Any],
|
|
545
|
+
band: str = "fp8") -> dict:
|
|
546
|
+
"""Bind the chain onto the stack at ``root``; adapter contract out.
|
|
547
|
+
|
|
548
|
+
One probe run does all the observation: the suffix calls (smoke
|
|
549
|
+
references), the mask facts, the prefix K/V, and the activation
|
|
550
|
+
amax at every quantizer site. The routed form must track the host
|
|
551
|
+
on **every** probe call above ``SMOKE_FLOOR`` or the whole bind
|
|
552
|
+
refuses — no partial routing. Returns ``{"refused": reason}`` on
|
|
553
|
+
any refusal, with the host untouched.
|
|
554
|
+
"""
|
|
555
|
+
try:
|
|
556
|
+
kg = hub_kernel(GEMM_PACKAGE, ">=1")
|
|
557
|
+
kn = hub_kernel(NORM_PACKAGE, ">=1")
|
|
558
|
+
kr = hub_kernel(ROPE_PACKAGE, ">=1")
|
|
559
|
+
kf = hub_kernel(FUSE_PACKAGE, ">=1")
|
|
560
|
+
kg4 = kn4 = None
|
|
561
|
+
if BANDS[band]["packages"]:
|
|
562
|
+
kg4 = hub_kernel(FP4_GEMM_PACKAGE, ">=1")
|
|
563
|
+
kn4 = hub_kernel(FP4_NORM_PACKAGE, ">=1")
|
|
564
|
+
except KernelUnavailable as exc:
|
|
565
|
+
return {"refused": f"adarms_{band}_chain: {exc}"}
|
|
566
|
+
rungs = _attention_rungs()
|
|
567
|
+
gaps = missing_symbols(band=band)
|
|
568
|
+
if gaps:
|
|
569
|
+
return {"refused": f"adarms_fp8_chain missing: {', '.join(gaps)}"}
|
|
570
|
+
|
|
571
|
+
stack = model.get_submodule(root) if root else model
|
|
572
|
+
layers, nh, kv, hd, dim, hidden = _stack_parts(stack)
|
|
573
|
+
if kv != 1:
|
|
574
|
+
return {"refused": f"adarms_fp8_chain: kv_heads {kv} outside "
|
|
575
|
+
"the single-KV band"}
|
|
576
|
+
if not _gelu_tanh_like(layers[0].mlp.act_fn):
|
|
577
|
+
return {"refused": "adarms_fp8_chain: FFN activation is not "
|
|
578
|
+
"tanh-GELU"}
|
|
579
|
+
scalings = {float(ly.self_attn.scaling) for ly in layers}
|
|
580
|
+
if len(scalings) != 1:
|
|
581
|
+
return {"refused": "adarms_fp8_chain: per-layer attention "
|
|
582
|
+
"scaling differs"}
|
|
583
|
+
|
|
584
|
+
bound = BoundAdaRmsFp8Chain()
|
|
585
|
+
bound.kernels = {"kg": kg, "kn": kn, "kr": kr, "kf": kf,
|
|
586
|
+
"kg4": kg4, "kn4": kn4}
|
|
587
|
+
bound.band = band
|
|
588
|
+
bound.scaling = scalings.pop()
|
|
589
|
+
bound.dims = {"nh": nh, "kv": kv, "hd": hd, "dim": dim,
|
|
590
|
+
"hidden": hidden, "layers": len(layers)}
|
|
591
|
+
|
|
592
|
+
# ---- one probe: calls, mask facts, prefix K/V, amax sites ----
|
|
593
|
+
calls: list[dict] = []
|
|
594
|
+
amax: dict[tuple[int, str], float] = {}
|
|
595
|
+
|
|
596
|
+
def note(site):
|
|
597
|
+
def hook(_m, args):
|
|
598
|
+
x = args[0]
|
|
599
|
+
peak = float(x.detach().abs().amax())
|
|
600
|
+
amax[site] = max(amax.get(site, 0.0), peak)
|
|
601
|
+
return hook
|
|
602
|
+
|
|
603
|
+
hooks = []
|
|
604
|
+
for i, ly in enumerate(layers):
|
|
605
|
+
hooks.append(ly.self_attn.q_proj.register_forward_pre_hook(
|
|
606
|
+
note((i, "qkv"))))
|
|
607
|
+
hooks.append(ly.self_attn.o_proj.register_forward_pre_hook(
|
|
608
|
+
note((i, "o"))))
|
|
609
|
+
hooks.append(ly.mlp.gate_proj.register_forward_pre_hook(
|
|
610
|
+
note((i, "gu"))))
|
|
611
|
+
hooks.append(ly.mlp.down_proj.register_forward_pre_hook(
|
|
612
|
+
note((i, "dn"))))
|
|
613
|
+
|
|
614
|
+
# the host calls the stack's ``forward`` directly, so capture is an
|
|
615
|
+
# instance-attribute wrap, not a forward hook
|
|
616
|
+
saved_probe = stack.__dict__.get("forward")
|
|
617
|
+
host_forward = stack.forward
|
|
618
|
+
|
|
619
|
+
def capturing(_self, *args, **kwargs):
|
|
620
|
+
out = host_forward(*args, **kwargs)
|
|
621
|
+
embs = kwargs.get("inputs_embeds")
|
|
622
|
+
cond = kwargs.get("adarms_cond")
|
|
623
|
+
pkv = kwargs.get("past_key_values")
|
|
624
|
+
hidden = getattr(out, "last_hidden_state", None)
|
|
625
|
+
if (embs is not None and cond is not None and pkv is not None
|
|
626
|
+
and hidden is not None
|
|
627
|
+
and embs.dim() == 3 and embs.shape[0] == 1):
|
|
628
|
+
entry = {
|
|
629
|
+
"x": embs.detach().clone(),
|
|
630
|
+
"cond": cond.detach().clone(),
|
|
631
|
+
"mask": kwargs.get("attention_mask"),
|
|
632
|
+
"pos": kwargs.get("position_ids"),
|
|
633
|
+
"out": hidden.detach().clone(),
|
|
634
|
+
"out_type": type(out),
|
|
635
|
+
}
|
|
636
|
+
entry["mask"] = (entry["mask"].detach().clone()
|
|
637
|
+
if entry["mask"] is not None else None)
|
|
638
|
+
entry["pos"] = (entry["pos"].detach().clone()
|
|
639
|
+
if entry["pos"] is not None else None)
|
|
640
|
+
if not any("kv" in c for c in calls):
|
|
641
|
+
entry["kv"] = [
|
|
642
|
+
(_cache_kv(pkv, i)[0].detach().clone(),
|
|
643
|
+
_cache_kv(pkv, i)[1].detach().clone())
|
|
644
|
+
for i in range(len(layers))]
|
|
645
|
+
calls.append(entry)
|
|
646
|
+
return out
|
|
647
|
+
|
|
648
|
+
stack.forward = types.MethodType(capturing, stack)
|
|
649
|
+
try:
|
|
650
|
+
with torch.inference_mode():
|
|
651
|
+
probe()
|
|
652
|
+
finally:
|
|
653
|
+
for hook in hooks:
|
|
654
|
+
hook.remove()
|
|
655
|
+
if saved_probe is not None:
|
|
656
|
+
stack.forward = saved_probe
|
|
657
|
+
else:
|
|
658
|
+
stack.__dict__.pop("forward", None)
|
|
659
|
+
|
|
660
|
+
if not calls:
|
|
661
|
+
return {"refused": "adarms_fp8_chain: probe never made a "
|
|
662
|
+
"suffix call"}
|
|
663
|
+
first = calls[0]
|
|
664
|
+
if first["mask"] is None or first["pos"] is None:
|
|
665
|
+
return {"refused": "adarms_fp8_chain: probe call carried no "
|
|
666
|
+
"mask or positions"}
|
|
667
|
+
S = first["x"].shape[1]
|
|
668
|
+
facts = _mask_facts(first["mask"], S)
|
|
669
|
+
if facts is None:
|
|
670
|
+
return {"refused": "adarms_fp8_chain: mask outside the "
|
|
671
|
+
"[prefix|pad|suffix] band"}
|
|
672
|
+
p_used, p_raw = facts
|
|
673
|
+
pos = first["pos"][0]
|
|
674
|
+
want = torch.arange(p_used, p_used + S, device=pos.device)
|
|
675
|
+
if not torch.equal(pos.to(want.dtype), want):
|
|
676
|
+
return {"refused": "adarms_fp8_chain: positions are not the "
|
|
677
|
+
"contiguous suffix run"}
|
|
678
|
+
for c in calls[1:]:
|
|
679
|
+
if (c["x"].shape != first["x"].shape
|
|
680
|
+
or (c["mask"] is not None
|
|
681
|
+
and c["mask"].shape != first["mask"].shape)):
|
|
682
|
+
return {"refused": "adarms_fp8_chain: probe calls disagree "
|
|
683
|
+
"on shape"}
|
|
684
|
+
if any((i, s) not in amax or amax[(i, s)] <= 0.0
|
|
685
|
+
for i in range(len(layers))
|
|
686
|
+
for s in ("qkv", "o", "gu", "dn")):
|
|
687
|
+
return {"refused": "adarms_fp8_chain: calibration saw a dead "
|
|
688
|
+
"quantizer site"}
|
|
689
|
+
|
|
690
|
+
bound.dims["seq"] = S
|
|
691
|
+
bound.p_used = p_used
|
|
692
|
+
bound.total_keys = p_used + S
|
|
693
|
+
bound.out_ctor = first["out_type"]
|
|
694
|
+
|
|
695
|
+
_style_stack(bound, stack, layers)
|
|
696
|
+
if not _build_rope(bound, stack, first["pos"]):
|
|
697
|
+
return {"refused": "adarms_fp8_chain: rotary table is not "
|
|
698
|
+
"half-duplicated"}
|
|
699
|
+
_quantize(bound, layers, amax)
|
|
700
|
+
_alloc(bound)
|
|
701
|
+
|
|
702
|
+
# ---- the attention ladder: first rung that actually executes ----
|
|
703
|
+
attend, attn_mode = None, None
|
|
704
|
+
rung_trail = []
|
|
705
|
+
for mode, kern in rungs:
|
|
706
|
+
try:
|
|
707
|
+
candidate = _make_attend(bound, mode, kern)
|
|
708
|
+
candidate(0)
|
|
709
|
+
torch.cuda.synchronize()
|
|
710
|
+
except Exception as exc: # noqa: BLE001 — a dead rung, next one
|
|
711
|
+
rung_trail.append(f"{mode}: {type(exc).__name__}")
|
|
712
|
+
continue
|
|
713
|
+
attend, attn_mode = candidate, mode
|
|
714
|
+
break
|
|
715
|
+
if attend is None:
|
|
716
|
+
return {"refused": "adarms_fp8_chain: no attention rung "
|
|
717
|
+
f"executes here ({'; '.join(rung_trail)})"}
|
|
718
|
+
bound.kernels["attend"] = attend
|
|
719
|
+
|
|
720
|
+
half = hd // 2
|
|
721
|
+
kperm = torch.empty(hd, dtype=torch.long, device="cuda")
|
|
722
|
+
kperm[0::2] = torch.arange(half, device="cuda")
|
|
723
|
+
kperm[1::2] = torch.arange(half, hd, device="cuda")
|
|
724
|
+
bound.kperm = kperm
|
|
725
|
+
|
|
726
|
+
# bake one style table per distinct probed step (the step-table
|
|
727
|
+
# form): the run resolves the live conditioning by nearest match
|
|
728
|
+
n_norms = 2 * len(layers) + 1
|
|
729
|
+
step_conds, style_tables, fin_tables = [], [], []
|
|
730
|
+
with torch.no_grad():
|
|
731
|
+
for c in calls:
|
|
732
|
+
cnd = c["cond"].float().cuda()
|
|
733
|
+
if any(torch.allclose(cnd, prev) for prev in step_conds):
|
|
734
|
+
continue
|
|
735
|
+
st = torch.addmm(bound.style_b, cnd, bound.style_w_t)
|
|
736
|
+
step_conds.append(cnd)
|
|
737
|
+
# rows=1 broadcast: the norm kernels accept a single
|
|
738
|
+
# style row, so the table stays one row per norm
|
|
739
|
+
style_tables.append(
|
|
740
|
+
st.view(n_norms, 1, 3 * dim)
|
|
741
|
+
.to(torch.bfloat16).contiguous())
|
|
742
|
+
fin_tables.append(
|
|
743
|
+
st[0, (n_norms - 1) * 3 * dim:].view(3, dim).clone())
|
|
744
|
+
bound.step_conds = torch.cat(step_conds, dim=0)
|
|
745
|
+
bound.style_table = torch.stack(style_tables)
|
|
746
|
+
bound.fin_table = torch.stack(fin_tables)
|
|
747
|
+
prefix_kv = [(k[0, 0, :p_used].contiguous().clone(),
|
|
748
|
+
v[0, 0, :p_used].contiguous().clone())
|
|
749
|
+
for k, v in first["kv"]]
|
|
750
|
+
run = _make_run(bound)
|
|
751
|
+
guard = bound._frt_arm(dtypes=(torch.bfloat16,),
|
|
752
|
+
device=torch.device("cuda"))
|
|
753
|
+
guard.notes["n_layers"] = len(layers)
|
|
754
|
+
guard.notes["suffix_calls"] = len(calls)
|
|
755
|
+
guard.notes["p_used"] = p_used
|
|
756
|
+
guard.notes["attention"] = attn_mode
|
|
757
|
+
if rung_trail:
|
|
758
|
+
guard.notes["attention_fell_through"] = rung_trail
|
|
759
|
+
|
|
760
|
+
# ---- smoke: the routed stack against every captured call ----
|
|
761
|
+
worst = None
|
|
762
|
+
with torch.inference_mode():
|
|
763
|
+
for c in calls:
|
|
764
|
+
got = run(c["x"][0].to(torch.bfloat16), c["cond"], prefix_kv)
|
|
765
|
+
cos = torch.nn.functional.cosine_similarity(
|
|
766
|
+
got.last_hidden_state.float().flatten(),
|
|
767
|
+
c["out"].float().flatten(), dim=0)
|
|
768
|
+
worst = float(cos) if worst is None else min(worst,
|
|
769
|
+
float(cos))
|
|
770
|
+
if worst is None or worst < SMOKE_FLOOR:
|
|
771
|
+
return {"refused": f"adarms_fp8_chain smoke cos {worst} < "
|
|
772
|
+
f"{SMOKE_FLOOR} across {len(calls)} probe "
|
|
773
|
+
"call(s)"}
|
|
774
|
+
guard.notes["smoke_cos"] = round(worst, 6)
|
|
775
|
+
|
|
776
|
+
# ---- route ----
|
|
777
|
+
saved = stack.__dict__.get("forward")
|
|
778
|
+
n_layers = len(layers)
|
|
779
|
+
x_shape = tuple(first["x"].shape)
|
|
780
|
+
mask_shape = tuple(first["mask"].shape)
|
|
781
|
+
|
|
782
|
+
def routed(_self, *args, **kwargs):
|
|
783
|
+
compiling = torch.compiler.is_compiling()
|
|
784
|
+
capturing_now = (False if compiling
|
|
785
|
+
else torch.cuda.is_current_stream_capturing())
|
|
786
|
+
eager = not compiling and not capturing_now
|
|
787
|
+
if eager:
|
|
788
|
+
guard.calls += 1
|
|
789
|
+
embs = kwargs.get("inputs_embeds")
|
|
790
|
+
cond = kwargs.get("adarms_cond")
|
|
791
|
+
pkv = kwargs.get("past_key_values")
|
|
792
|
+
mask = kwargs.get("attention_mask")
|
|
793
|
+
ok = (not args and embs is not None and cond is not None
|
|
794
|
+
and pkv is not None
|
|
795
|
+
and tuple(embs.shape) == x_shape
|
|
796
|
+
and (mask is None or tuple(mask.shape) == mask_shape))
|
|
797
|
+
if not ok:
|
|
798
|
+
if not eager:
|
|
799
|
+
raise RuntimeError(
|
|
800
|
+
"adarms_fp8_chain: out-of-contract call during "
|
|
801
|
+
"capture/compile — fix the eager path first")
|
|
802
|
+
guard.fallbacks += 1
|
|
803
|
+
guard.last_reason = "call outside the routed contract"
|
|
804
|
+
return host_forward(*args, **kwargs)
|
|
805
|
+
prefix = []
|
|
806
|
+
for i in range(n_layers):
|
|
807
|
+
k, v = _cache_kv(pkv, i)
|
|
808
|
+
prefix.append((k[0, 0, :bound.p_used],
|
|
809
|
+
v[0, 0, :bound.p_used]))
|
|
810
|
+
return run(embs[0].to(torch.bfloat16), cond, prefix)
|
|
811
|
+
|
|
812
|
+
def enable() -> None:
|
|
813
|
+
stack.forward = types.MethodType(routed, stack)
|
|
814
|
+
|
|
815
|
+
def disable() -> None:
|
|
816
|
+
if saved is not None:
|
|
817
|
+
stack.forward = saved
|
|
818
|
+
elif "forward" in stack.__dict__:
|
|
819
|
+
del stack.forward
|
|
820
|
+
|
|
821
|
+
def revert() -> None:
|
|
822
|
+
disable()
|
|
823
|
+
bound.table.clear()
|
|
824
|
+
bound.buf.clear()
|
|
825
|
+
|
|
826
|
+
enable()
|
|
827
|
+
return {
|
|
828
|
+
"observed": {f"{root}::adarms_fp8_chain": bound},
|
|
829
|
+
"revert": [revert],
|
|
830
|
+
"toggle": (enable, disable),
|
|
831
|
+
"smoke_cos": worst,
|
|
832
|
+
}
|