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,417 @@
|
|
|
1
|
+
"""The fused NVFP4 launch chain over an alternating DiT block stack.
|
|
2
|
+
|
|
3
|
+
Eight kernels per layer, no elementwise traffic between them: the
|
|
4
|
+
AdaLN and the pre-FFN norm emit FP4 directly, the attention output
|
|
5
|
+
projection and the FFN down-projection carry the residual add in
|
|
6
|
+
their epilogues, and the FFN up-projection emits bias+GELU straight
|
|
7
|
+
back to FP4 for the down GEMM. The per-layer AdaLN modulators come
|
|
8
|
+
from one stacked projection per distinct timestep, resolved at run
|
|
9
|
+
time by nearest-neighbour match — the step-table form, computed at
|
|
10
|
+
bind. Cross-attention keys and values go through the block's own
|
|
11
|
+
``to_k``/``to_v`` **resolved at call time**, so whatever seat holds
|
|
12
|
+
that path when the call happens (a cadence bank, a quantized linear,
|
|
13
|
+
the host) serves it — the chain never captures a module reference at
|
|
14
|
+
bind (the o_proj dead-seat lesson).
|
|
15
|
+
|
|
16
|
+
This is a region candidate, not a specialist: it binds through the
|
|
17
|
+
adapter contract (observed guard, enable/disable toggle, bit-exact
|
|
18
|
+
revert), its activation is decided by :mod:`..regions` receipts, and
|
|
19
|
+
every out-of-contract call falls back to the retained host forward
|
|
20
|
+
with the reason on the ledger. Contract checks run eager-only and
|
|
21
|
+
step aside during CUDA graph capture — the captured window is
|
|
22
|
+
certified by its own gate.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import types
|
|
28
|
+
from typing import Any, Callable
|
|
29
|
+
|
|
30
|
+
import torch
|
|
31
|
+
|
|
32
|
+
from .. import KernelUnavailable, hub_kernel
|
|
33
|
+
from ...guard import GuardedSeam
|
|
34
|
+
|
|
35
|
+
GEMM_PACKAGE = "flashrt/fp4-gemm"
|
|
36
|
+
NORM_PACKAGE = "flashrt/adaptive-layernorm-producers"
|
|
37
|
+
GEMM_SYMBOLS = ("nvfp4_gemm_bias_bf16", "nvfp4_gemm_bias_residual_bf16",
|
|
38
|
+
"nvfp4_gemm_bias_gelu_nvfp4", "quantize_fp4_sfa_bf16")
|
|
39
|
+
NORM_SYMBOLS = ("ada_layer_norm_quant_nvfp4_swizzled_bf16",
|
|
40
|
+
"layer_norm_no_affine_quant_nvfp4_swizzled_bf16")
|
|
41
|
+
|
|
42
|
+
#: bind-time smoke over the whole routed stack, on every probe step.
|
|
43
|
+
#: The late-binding lesson calibrates this: a per-layer error class of
|
|
44
|
+
#: ~2e-3 compounds across ~32 layers, so the stack floor sits below
|
|
45
|
+
#: the per-seam 0.995 line on purpose; the arm's end-to-end parity
|
|
46
|
+
#: gate (0.99 against the host's own eager run) stays the judge.
|
|
47
|
+
SMOKE_FLOOR = 0.985
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def missing_symbols() -> list[str]:
|
|
51
|
+
"""The factual prerequisites this box does not meet (may be empty)."""
|
|
52
|
+
gaps: list[str] = []
|
|
53
|
+
for repo, symbols in ((GEMM_PACKAGE, GEMM_SYMBOLS),
|
|
54
|
+
(NORM_PACKAGE, NORM_SYMBOLS)):
|
|
55
|
+
try:
|
|
56
|
+
kern = hub_kernel(repo, ">=1")
|
|
57
|
+
except KernelUnavailable:
|
|
58
|
+
gaps.append(repo)
|
|
59
|
+
continue
|
|
60
|
+
gaps.extend(f"{repo}:{s}" for s in symbols
|
|
61
|
+
if not hasattr(kern, s))
|
|
62
|
+
return gaps
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class BoundDitFp4Chain(GuardedSeam, torch.nn.Module):
|
|
66
|
+
"""The chain's bind-time state: quantized weights and step tables.
|
|
67
|
+
|
|
68
|
+
Weight tables and step tables are plain tensor attributes, not
|
|
69
|
+
buffers — this module is a ledger citizen (``plan.observed``), not
|
|
70
|
+
a state_dict citizen; the truth of every weight stays with the
|
|
71
|
+
host modules it absorbs, which is what makes revert and fallback
|
|
72
|
+
bit-exact for free.
|
|
73
|
+
"""
|
|
74
|
+
|
|
75
|
+
_frt_can_fallback = False # fallback is the routed closure's job
|
|
76
|
+
|
|
77
|
+
def __init__(self) -> None:
|
|
78
|
+
super().__init__()
|
|
79
|
+
self.table: list[dict] = []
|
|
80
|
+
self.blocks: list = [] # plain list: no child registration
|
|
81
|
+
self.dims: dict = {}
|
|
82
|
+
self.t_keys = None
|
|
83
|
+
self.mods_table = None
|
|
84
|
+
self.tails_table = None
|
|
85
|
+
self.text_idx = None
|
|
86
|
+
self.image_idx = None
|
|
87
|
+
self.mask_shape = None
|
|
88
|
+
self.kernels: dict = {}
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _stack_parts(dit) -> tuple[list, int, int, int, int]:
|
|
92
|
+
blocks = list(dit.transformer_blocks)
|
|
93
|
+
head = blocks[0]
|
|
94
|
+
return (blocks, head.num_attention_heads, head.attention_head_dim,
|
|
95
|
+
head.dim, len(blocks))
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@torch.no_grad()
|
|
99
|
+
def _quantize(bound: BoundDitFp4Chain, dit, kg) -> None:
|
|
100
|
+
"""NVFP4-pack every stack GEMM weight from the pristine host."""
|
|
101
|
+
blocks, _nh, _hd, _dim, _n = _stack_parts(dit)
|
|
102
|
+
|
|
103
|
+
def quant(w: torch.Tensor):
|
|
104
|
+
return kg.quantize_fp4_sfa_bf16(
|
|
105
|
+
w.detach().to("cuda", torch.bfloat16).contiguous(),
|
|
106
|
+
is_sfb=True)
|
|
107
|
+
|
|
108
|
+
def bias(module: torch.nn.Module):
|
|
109
|
+
return module.bias.detach().to(
|
|
110
|
+
"cuda", torch.bfloat16).contiguous()
|
|
111
|
+
|
|
112
|
+
for block in blocks:
|
|
113
|
+
attn = block.attn1
|
|
114
|
+
is_self = attn.to_k.in_features == attn.to_q.in_features
|
|
115
|
+
entry: dict[str, Any] = {"is_self": is_self}
|
|
116
|
+
if is_self:
|
|
117
|
+
w = torch.cat([attn.to_q.weight, attn.to_k.weight,
|
|
118
|
+
attn.to_v.weight], dim=0)
|
|
119
|
+
entry["qkv"] = quant(w)
|
|
120
|
+
entry["qkv_b"] = torch.cat(
|
|
121
|
+
[attn.to_q.bias, attn.to_k.bias, attn.to_v.bias]
|
|
122
|
+
).detach().to("cuda", torch.bfloat16).contiguous()
|
|
123
|
+
else:
|
|
124
|
+
entry["q"] = quant(attn.to_q.weight)
|
|
125
|
+
entry["q_b"] = bias(attn.to_q)
|
|
126
|
+
entry["o"] = quant(attn.to_out[0].weight)
|
|
127
|
+
entry["o_b"] = bias(attn.to_out[0])
|
|
128
|
+
entry["up"] = quant(block.ff.net[0].proj.weight)
|
|
129
|
+
entry["up_b"] = bias(block.ff.net[0].proj)
|
|
130
|
+
entry["down"] = quant(block.ff.net[2].weight)
|
|
131
|
+
entry["down_b"] = bias(block.ff.net[2])
|
|
132
|
+
bound.table.append(entry)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
@torch.no_grad()
|
|
136
|
+
def _step_tables(bound: BoundDitFp4Chain, dit, seen, masks) -> None:
|
|
137
|
+
"""Per-step modulators from one stacked projection per timestep.
|
|
138
|
+
|
|
139
|
+
Computing them in-graph reads the stacked modulator weights once
|
|
140
|
+
per step — pure bandwidth, and the profile named that one skinny
|
|
141
|
+
GEMM as a whole regression. Here they are computed once per
|
|
142
|
+
distinct timestep the probe saw; the run resolves the step by
|
|
143
|
+
nearest-neighbour match, pure tensor ops, capture-safe.
|
|
144
|
+
"""
|
|
145
|
+
blocks, _nh, _hd, dim, n_layers = _stack_parts(dit)
|
|
146
|
+
ada_w = torch.cat([b.norm1.linear.weight for b in blocks], dim=0)
|
|
147
|
+
ada_b = torch.cat([b.norm1.linear.bias for b in blocks], dim=0)
|
|
148
|
+
silu = torch.nn.functional.silu
|
|
149
|
+
keys, mods, tails = [], [], []
|
|
150
|
+
for t, temb in seen:
|
|
151
|
+
keys.append(t)
|
|
152
|
+
mods.append(torch.nn.functional.linear(silu(temb), ada_w, ada_b)
|
|
153
|
+
.view(n_layers, 2, dim).to(torch.bfloat16))
|
|
154
|
+
shift, scale = dit.proj_out_1(silu(temb)).chunk(2, dim=1)
|
|
155
|
+
tails.append(torch.stack(
|
|
156
|
+
[shift.reshape(dim), scale.reshape(dim)]).to(torch.bfloat16))
|
|
157
|
+
image_rows = (masks["image"] & masks["backbone"]).reshape(-1)
|
|
158
|
+
text_rows = (~masks["image"] & masks["backbone"]).reshape(-1)
|
|
159
|
+
bound.t_keys = torch.cat(keys).contiguous()
|
|
160
|
+
bound.mods_table = torch.stack(mods).contiguous()
|
|
161
|
+
bound.tails_table = torch.stack(tails).contiguous()
|
|
162
|
+
bound.text_idx = torch.where(text_rows)[0].contiguous()
|
|
163
|
+
bound.image_idx = torch.where(image_rows)[0].contiguous()
|
|
164
|
+
bound.mask_shape = tuple(masks["image"].shape)
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def _make_run(bound: BoundDitFp4Chain, dit) -> Callable:
|
|
168
|
+
"""The chain body: eight launches per layer over the stack."""
|
|
169
|
+
kg, kq = bound.kernels["kg"], bound.kernels["kq"]
|
|
170
|
+
ada_fp4 = kq.ada_layer_norm_quant_nvfp4_swizzled_bf16
|
|
171
|
+
ln_fp4 = kq.layer_norm_no_affine_quant_nvfp4_swizzled_bf16
|
|
172
|
+
gemm_bias = kg.nvfp4_gemm_bias_bf16
|
|
173
|
+
gemm_bias_res = kg.nvfp4_gemm_bias_residual_bf16
|
|
174
|
+
gemm_gelu_fp4 = kg.nvfp4_gemm_bias_gelu_nvfp4
|
|
175
|
+
quant_act = kg.quantize_fp4_sfa_bf16
|
|
176
|
+
sdpa = torch.nn.functional.scaled_dot_product_attention
|
|
177
|
+
nh, hd = bound.dims["nh"], bound.dims["hd"]
|
|
178
|
+
dim, n_layers = bound.dims["dim"], bound.dims["n_layers"]
|
|
179
|
+
every_n = bound.dims["every_n"]
|
|
180
|
+
table = bound.table
|
|
181
|
+
blocks = bound.blocks
|
|
182
|
+
|
|
183
|
+
def layer(li, h, sa, scale, shift, enc, rows):
|
|
184
|
+
entry = table[li]
|
|
185
|
+
xp, xs = ada_fp4(h, scale, shift)
|
|
186
|
+
if entry["is_self"]:
|
|
187
|
+
qkv = gemm_bias(xp, entry["qkv"][0], xs, entry["qkv"][1],
|
|
188
|
+
entry["qkv_b"])
|
|
189
|
+
packs = qkv.view(sa, 3, nh, hd).permute(1, 2, 0, 3)
|
|
190
|
+
o = sdpa(packs[0].unsqueeze(0), packs[1].unsqueeze(0),
|
|
191
|
+
packs[2].unsqueeze(0))
|
|
192
|
+
else:
|
|
193
|
+
q = gemm_bias(xp, entry["q"][0], xs, entry["q"][1],
|
|
194
|
+
entry["q_b"])
|
|
195
|
+
# call-time resolution: whoever seats to_k/to_v now (a
|
|
196
|
+
# cadence bank, a quantized linear, the host) serves this
|
|
197
|
+
attn = blocks[li].attn1
|
|
198
|
+
kb = attn.to_k(enc).index_select(-2, rows)
|
|
199
|
+
vb = attn.to_v(enc).index_select(-2, rows)
|
|
200
|
+
skv = rows.shape[0]
|
|
201
|
+
o = sdpa(q.view(1, sa, nh, hd).transpose(1, 2),
|
|
202
|
+
kb.reshape(1, skv, nh, hd).transpose(1, 2),
|
|
203
|
+
vb.reshape(1, skv, nh, hd).transpose(1, 2))
|
|
204
|
+
o = o.transpose(1, 2).reshape(sa, dim).contiguous()
|
|
205
|
+
op, osf = quant_act(o)
|
|
206
|
+
h = gemm_bias_res(op, entry["o"][0], osf, entry["o"][1],
|
|
207
|
+
entry["o_b"], h)
|
|
208
|
+
np_, ns = ln_fp4(h)
|
|
209
|
+
hp, hs = gemm_gelu_fp4(np_, entry["up"][0], ns, entry["up"][1],
|
|
210
|
+
entry["up_b"])
|
|
211
|
+
return gemm_bias_res(hp, entry["down"][0], hs, entry["down"][1],
|
|
212
|
+
entry["down_b"], h)
|
|
213
|
+
|
|
214
|
+
def run(hidden_states, encoder_hidden_states, timestep,
|
|
215
|
+
return_all_hidden_states):
|
|
216
|
+
t = timestep.reshape(-1)[:1].float()
|
|
217
|
+
idx = (bound.t_keys - t).abs().argmin().reshape(1)
|
|
218
|
+
mods = bound.mods_table.index_select(0, idx)[0]
|
|
219
|
+
tail = bound.tails_table.index_select(0, idx)[0]
|
|
220
|
+
|
|
221
|
+
b, sa, d = hidden_states.shape
|
|
222
|
+
h = hidden_states.reshape(sa, d).to(torch.bfloat16).contiguous()
|
|
223
|
+
all_h = [hidden_states] if return_all_hidden_states else None
|
|
224
|
+
for li in range(n_layers):
|
|
225
|
+
if li % 2 == 1:
|
|
226
|
+
h = layer(li, h, sa, mods[li, 0], mods[li, 1], None, None)
|
|
227
|
+
else:
|
|
228
|
+
rows = (bound.text_idx if li % (2 * every_n) == 0
|
|
229
|
+
else bound.image_idx)
|
|
230
|
+
h = layer(li, h, sa, mods[li, 0], mods[li, 1],
|
|
231
|
+
encoder_hidden_states, rows)
|
|
232
|
+
if all_h is not None:
|
|
233
|
+
all_h.append(h.reshape(b, sa, d))
|
|
234
|
+
|
|
235
|
+
h = h.reshape(b, sa, d).type_as(hidden_states)
|
|
236
|
+
h = (dit.norm_out(h) * (1 + tail[1].reshape(1, 1, d))
|
|
237
|
+
+ tail[0].reshape(1, 1, d))
|
|
238
|
+
out = dit.proj_out_2(h)
|
|
239
|
+
if return_all_hidden_states:
|
|
240
|
+
return out, all_h
|
|
241
|
+
return out
|
|
242
|
+
|
|
243
|
+
return run
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def bind_dit_fp4_chain(model, root: str,
|
|
247
|
+
probe: Callable[[], Any]) -> dict:
|
|
248
|
+
"""Bind the chain onto the stack at ``root``; adapter contract out.
|
|
249
|
+
|
|
250
|
+
One probe run does all the observation: the distinct timesteps
|
|
251
|
+
(step tables), the attention masks (compact-row banks), and every
|
|
252
|
+
stack call's arguments and output (the smoke reference). The
|
|
253
|
+
routed form must track the host on **every** probe step above
|
|
254
|
+
``SMOKE_FLOOR`` or the whole bind refuses — no partial routing of
|
|
255
|
+
a stack. Returns ``{"refused": reason}`` on any refusal, with the
|
|
256
|
+
host untouched.
|
|
257
|
+
"""
|
|
258
|
+
try:
|
|
259
|
+
kg = hub_kernel(GEMM_PACKAGE, ">=1")
|
|
260
|
+
kq = hub_kernel(NORM_PACKAGE, ">=1")
|
|
261
|
+
except KernelUnavailable as exc:
|
|
262
|
+
return {"refused": f"dit_fp4_chain: {exc}"}
|
|
263
|
+
gaps = missing_symbols()
|
|
264
|
+
if gaps:
|
|
265
|
+
return {"refused": f"dit_fp4_chain missing: {', '.join(gaps)}"}
|
|
266
|
+
|
|
267
|
+
dit = model.get_submodule(root) if root else model
|
|
268
|
+
blocks, nh, hd, dim, n_layers = _stack_parts(dit)
|
|
269
|
+
|
|
270
|
+
bound = BoundDitFp4Chain()
|
|
271
|
+
bound.kernels = {"kg": kg, "kq": kq}
|
|
272
|
+
bound.blocks = blocks
|
|
273
|
+
bound.dims = {"nh": nh, "hd": hd, "dim": dim, "n_layers": n_layers,
|
|
274
|
+
"every_n": getattr(dit, "attend_text_every_n_blocks", 2)}
|
|
275
|
+
_quantize(bound, dit, kg)
|
|
276
|
+
|
|
277
|
+
# ---- one probe: timesteps, masks, and the smoke reference ----
|
|
278
|
+
seen: list[tuple[torch.Tensor, torch.Tensor]] = []
|
|
279
|
+
masks: dict = {}
|
|
280
|
+
calls: list[tuple[tuple, dict, torch.Tensor]] = []
|
|
281
|
+
|
|
282
|
+
def note(_module, args, output):
|
|
283
|
+
t = args[0].reshape(-1)[:1].float()
|
|
284
|
+
if not any(torch.allclose(t, prev) for prev, _ in seen):
|
|
285
|
+
seen.append((t.detach().clone(), output.detach().clone()))
|
|
286
|
+
|
|
287
|
+
def grab(_module, args, kwargs, output):
|
|
288
|
+
img = kwargs.get("image_mask")
|
|
289
|
+
bb = kwargs.get("backbone_attention_mask")
|
|
290
|
+
if img is not None and bb is not None and not masks:
|
|
291
|
+
masks["image"] = img.detach().clone()
|
|
292
|
+
masks["backbone"] = bb.detach().clone()
|
|
293
|
+
out = output[0] if isinstance(output, tuple) else output
|
|
294
|
+
calls.append((args, dict(kwargs), out.detach().clone()))
|
|
295
|
+
|
|
296
|
+
hooks = [dit.timestep_encoder.register_forward_hook(note),
|
|
297
|
+
dit.register_forward_hook(grab, with_kwargs=True)]
|
|
298
|
+
try:
|
|
299
|
+
with torch.inference_mode():
|
|
300
|
+
probe()
|
|
301
|
+
finally:
|
|
302
|
+
for hook in hooks:
|
|
303
|
+
hook.remove()
|
|
304
|
+
if not seen:
|
|
305
|
+
return {"refused": "dit_fp4_chain: probe saw no timesteps"}
|
|
306
|
+
if not masks:
|
|
307
|
+
return {"refused": "dit_fp4_chain: probe saw no attention masks"}
|
|
308
|
+
if not calls:
|
|
309
|
+
return {"refused": "dit_fp4_chain: probe never called the stack"}
|
|
310
|
+
|
|
311
|
+
_step_tables(bound, dit, seen, masks)
|
|
312
|
+
run = _make_run(bound, dit)
|
|
313
|
+
guard = bound._frt_arm(dtypes=(torch.bfloat16,),
|
|
314
|
+
device=bound.t_keys.device)
|
|
315
|
+
guard.notes["n_layers"] = n_layers
|
|
316
|
+
guard.notes["steps"] = len(seen)
|
|
317
|
+
|
|
318
|
+
# ---- smoke: the routed stack against every captured host call ----
|
|
319
|
+
worst = None
|
|
320
|
+
with torch.inference_mode():
|
|
321
|
+
for args, kwargs, ref in calls:
|
|
322
|
+
parsed = _parse_call(args, kwargs)
|
|
323
|
+
if parsed is None:
|
|
324
|
+
return {"refused": "dit_fp4_chain: probe call shape "
|
|
325
|
+
"outside the routed contract"}
|
|
326
|
+
got = run(*parsed)
|
|
327
|
+
got = got[0] if isinstance(got, tuple) else got
|
|
328
|
+
cos = torch.nn.functional.cosine_similarity(
|
|
329
|
+
got.float().flatten(), ref.float().flatten(), dim=0)
|
|
330
|
+
worst = float(cos) if worst is None else min(worst,
|
|
331
|
+
float(cos))
|
|
332
|
+
if worst is None or worst < SMOKE_FLOOR:
|
|
333
|
+
return {"refused": f"dit_fp4_chain smoke cos {worst} < "
|
|
334
|
+
f"{SMOKE_FLOOR} across {len(calls)} probe "
|
|
335
|
+
"step(s)"}
|
|
336
|
+
guard.notes["smoke_cos"] = round(worst, 6)
|
|
337
|
+
|
|
338
|
+
# ---- route ----
|
|
339
|
+
saved = dit.__dict__.get("forward")
|
|
340
|
+
host_forward = dit.forward
|
|
341
|
+
|
|
342
|
+
def routed(_dit_self, *args, **kwargs):
|
|
343
|
+
# the ledger is eager-only, like every guard: a compiler
|
|
344
|
+
# tracing this sees constant-False branches and no side
|
|
345
|
+
# effects (no graph breaks in the hot loop), and a capturing
|
|
346
|
+
# stream skips the Python state the replay would never run
|
|
347
|
+
compiling = torch.compiler.is_compiling()
|
|
348
|
+
capturing = (False if compiling
|
|
349
|
+
else torch.cuda.is_current_stream_capturing())
|
|
350
|
+
eager = not compiling and not capturing
|
|
351
|
+
if eager:
|
|
352
|
+
guard.calls += 1
|
|
353
|
+
parsed = _parse_call(args, kwargs)
|
|
354
|
+
if parsed is None:
|
|
355
|
+
if not eager:
|
|
356
|
+
raise RuntimeError(
|
|
357
|
+
"dit_fp4_chain: out-of-contract call during "
|
|
358
|
+
"capture/compile — fix the eager path first")
|
|
359
|
+
guard.fallbacks += 1
|
|
360
|
+
guard.last_reason = "call outside the routed contract"
|
|
361
|
+
return host_forward(*args, **kwargs)
|
|
362
|
+
if eager:
|
|
363
|
+
img = kwargs.get("image_mask")
|
|
364
|
+
if (img is not None
|
|
365
|
+
and tuple(img.shape) != bound.mask_shape):
|
|
366
|
+
guard.fallbacks += 1
|
|
367
|
+
guard.last_reason = "attention mask shape changed"
|
|
368
|
+
return host_forward(*args, **kwargs)
|
|
369
|
+
return run(*parsed)
|
|
370
|
+
|
|
371
|
+
def enable() -> None:
|
|
372
|
+
dit.forward = types.MethodType(routed, dit)
|
|
373
|
+
|
|
374
|
+
def disable() -> None:
|
|
375
|
+
if saved is not None:
|
|
376
|
+
dit.forward = saved
|
|
377
|
+
elif "forward" in dit.__dict__:
|
|
378
|
+
del dit.forward
|
|
379
|
+
|
|
380
|
+
def revert() -> None:
|
|
381
|
+
disable()
|
|
382
|
+
bound.table.clear()
|
|
383
|
+
bound.blocks = []
|
|
384
|
+
|
|
385
|
+
enable()
|
|
386
|
+
return {
|
|
387
|
+
"observed": {f"{root}::dit_fp4_chain": bound},
|
|
388
|
+
"revert": [revert],
|
|
389
|
+
"toggle": (enable, disable),
|
|
390
|
+
"smoke_cos": worst,
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def _parse_call(args: tuple, kwargs: dict):
|
|
395
|
+
"""The routed contract: the native stack signature, batch of one.
|
|
396
|
+
|
|
397
|
+
Anything else — an unexpected keyword, a missing timestep, a
|
|
398
|
+
batched call — is the host's, not the chain's.
|
|
399
|
+
"""
|
|
400
|
+
known = ("hidden_states", "encoder_hidden_states", "timestep",
|
|
401
|
+
"encoder_attention_mask", "return_all_hidden_states",
|
|
402
|
+
"image_mask", "backbone_attention_mask")
|
|
403
|
+
if any(k not in known for k in kwargs):
|
|
404
|
+
return None
|
|
405
|
+
merged = dict(zip(known, args))
|
|
406
|
+
if set(merged).intersection(kwargs):
|
|
407
|
+
return None
|
|
408
|
+
merged.update(kwargs)
|
|
409
|
+
hidden = merged.get("hidden_states")
|
|
410
|
+
enc = merged.get("encoder_hidden_states")
|
|
411
|
+
t = merged.get("timestep")
|
|
412
|
+
if hidden is None or enc is None or t is None:
|
|
413
|
+
return None
|
|
414
|
+
if hidden.dim() != 3 or hidden.shape[0] != 1:
|
|
415
|
+
return None
|
|
416
|
+
return (hidden, enc, t,
|
|
417
|
+
bool(merged.get("return_all_hidden_states", False)))
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""The dit_block region family: structural identification, candidates.
|
|
2
|
+
|
|
3
|
+
The identifier matches shape, never names: a module carrying a
|
|
4
|
+
``transformer_blocks`` list whose blocks each hold one attention group
|
|
5
|
+
(``to_q``/``to_k``/``to_v``/``to_out``), a two-linear gated FFN, and a
|
|
6
|
+
per-block AdaLN projection — with at least one cross block (key width
|
|
7
|
+
differs from query width) and the stack-level tail (timestep encoder,
|
|
8
|
+
final norm, the two output projections). That is the span the fused
|
|
9
|
+
chain candidate knows how to absorb; a host that merely resembles it
|
|
10
|
+
is exactly what the bind-time smoke gate exists to refuse.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import torch
|
|
16
|
+
|
|
17
|
+
from . import fp4_chain
|
|
18
|
+
from ... import regions
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _linear(mod) -> bool:
|
|
22
|
+
return isinstance(mod, torch.nn.Linear) and mod.bias is not None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _block_ok(block) -> bool:
|
|
26
|
+
attn = getattr(block, "attn1", None)
|
|
27
|
+
ff = getattr(block, "ff", None)
|
|
28
|
+
norm1 = getattr(block, "norm1", None)
|
|
29
|
+
if attn is None or ff is None or norm1 is None:
|
|
30
|
+
return False
|
|
31
|
+
if not all(_linear(getattr(attn, a, None))
|
|
32
|
+
for a in ("to_q", "to_k", "to_v")):
|
|
33
|
+
return False
|
|
34
|
+
out = getattr(attn, "to_out", None)
|
|
35
|
+
if out is None or len(out) < 1 or not _linear(out[0]):
|
|
36
|
+
return False
|
|
37
|
+
net = getattr(ff, "net", None)
|
|
38
|
+
if (net is None or len(net) < 3
|
|
39
|
+
or not _linear(getattr(net[0], "proj", None))
|
|
40
|
+
or not _linear(net[2])):
|
|
41
|
+
return False
|
|
42
|
+
if not _linear(getattr(norm1, "linear", None)):
|
|
43
|
+
return False
|
|
44
|
+
return isinstance(getattr(block, "dim", None), int)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def identify(model) -> list[str]:
|
|
48
|
+
roots = []
|
|
49
|
+
for path, mod in model.named_modules():
|
|
50
|
+
blocks = getattr(mod, "transformer_blocks", None)
|
|
51
|
+
if not isinstance(blocks, torch.nn.ModuleList) or len(blocks) < 2:
|
|
52
|
+
continue
|
|
53
|
+
if not all(_block_ok(b) for b in blocks):
|
|
54
|
+
continue
|
|
55
|
+
if not all(hasattr(mod, a) for a in
|
|
56
|
+
("timestep_encoder", "norm_out",
|
|
57
|
+
"proj_out_1", "proj_out_2")):
|
|
58
|
+
continue
|
|
59
|
+
if not any(b.attn1.to_k.in_features != b.attn1.to_q.in_features
|
|
60
|
+
for b in blocks):
|
|
61
|
+
continue
|
|
62
|
+
roots.append(path)
|
|
63
|
+
return roots
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _bind(model, root, probe):
|
|
67
|
+
return fp4_chain.bind_dit_fp4_chain(model, root, probe)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
FAMILY = regions.RegionFamily(
|
|
71
|
+
family="dit_block",
|
|
72
|
+
identify=identify,
|
|
73
|
+
candidates=[regions.RegionCandidate(
|
|
74
|
+
name="fp4_chain",
|
|
75
|
+
missing=fp4_chain.missing_symbols,
|
|
76
|
+
bind=_bind,
|
|
77
|
+
)],
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def register() -> None:
|
|
82
|
+
"""(Re-)register the family — idempotent, import calls it once."""
|
|
83
|
+
regions.register_region_family(FAMILY)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
register()
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Executable fixed-iteration schedule normalization.
|
|
2
|
+
|
|
3
|
+
The stage catalog describes iterative pipelines semantically. This package
|
|
4
|
+
is the executable bridge for hosts whose Python spelling is not graph-safe:
|
|
5
|
+
host-family adapters expose the same ``init -> K * step -> readout`` schedule
|
|
6
|
+
as a fixed callable that :func:`flashrt_structures.capture` can compile and
|
|
7
|
+
capture without changing the host repository.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from .protocol import (
|
|
11
|
+
FixedIterationLowering,
|
|
12
|
+
FixedIterationRefused,
|
|
13
|
+
normalize_fixed_iteration,
|
|
14
|
+
register_fixed_iteration_adapter,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
# Built-ins register at import time. They use semantic capabilities and
|
|
18
|
+
# signatures, never model IDs.
|
|
19
|
+
from .openpi import OpenPIFixedIterationAdapter
|
|
20
|
+
|
|
21
|
+
register_fixed_iteration_adapter(OpenPIFixedIterationAdapter())
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"FixedIterationLowering",
|
|
25
|
+
"FixedIterationRefused",
|
|
26
|
+
"OpenPIFixedIterationAdapter",
|
|
27
|
+
"normalize_fixed_iteration",
|
|
28
|
+
"register_fixed_iteration_adapter",
|
|
29
|
+
]
|