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,239 @@
|
|
|
1
|
+
"""Rotary Diffusers attention routed through the dense FA2 structure.
|
|
2
|
+
|
|
3
|
+
The adapter is selected by the processor boundary and module slots it can
|
|
4
|
+
reproduce, not by a model or processor class name. It covers the common
|
|
5
|
+
video-transformer form where Q/K are normalised before an optional rotary
|
|
6
|
+
embedding and the processor returns a sequence-major attention result.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import inspect
|
|
12
|
+
|
|
13
|
+
import torch
|
|
14
|
+
|
|
15
|
+
from ..impls.attention_core import bind_dense_attention_best
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _compatible_site(module, processor) -> tuple[bool, str]:
|
|
19
|
+
if not callable(processor):
|
|
20
|
+
return False, "processor is not callable"
|
|
21
|
+
try:
|
|
22
|
+
parameters = inspect.signature(processor.__call__).parameters
|
|
23
|
+
except (TypeError, ValueError, AttributeError):
|
|
24
|
+
return False, "processor call signature is not inspectable"
|
|
25
|
+
if "rotary_emb" not in parameters:
|
|
26
|
+
return False, "processor has no rotary_emb boundary"
|
|
27
|
+
required_modules = ("to_q", "to_k", "to_v", "norm_q", "norm_k")
|
|
28
|
+
for attr in required_modules:
|
|
29
|
+
if not isinstance(getattr(module, attr, None), torch.nn.Module):
|
|
30
|
+
return False, f"attention lacks callable slot {attr!r}"
|
|
31
|
+
try:
|
|
32
|
+
out_proj, out_drop = module.to_out[0], module.to_out[1]
|
|
33
|
+
except (AttributeError, IndexError, KeyError, TypeError):
|
|
34
|
+
return False, "attention lacks the to_out[projection, dropout] slots"
|
|
35
|
+
if not all(isinstance(part, torch.nn.Module)
|
|
36
|
+
for part in (out_proj, out_drop)):
|
|
37
|
+
return False, "attention output slots are not modules"
|
|
38
|
+
heads = getattr(module, "heads", None)
|
|
39
|
+
if not isinstance(heads, int) or heads <= 0:
|
|
40
|
+
return False, "attention lacks a positive integer head count"
|
|
41
|
+
if getattr(module, "add_k_proj", None) is not None:
|
|
42
|
+
return False, "added image KV is not yet an executable form"
|
|
43
|
+
return True, ""
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def _projections(attn, hidden_states, encoder_hidden_states):
|
|
47
|
+
context = hidden_states if encoder_hidden_states is None \
|
|
48
|
+
else encoder_hidden_states
|
|
49
|
+
if getattr(attn, "fused_projections", False):
|
|
50
|
+
if getattr(attn, "is_cross_attention", False):
|
|
51
|
+
query = attn.to_q(hidden_states)
|
|
52
|
+
key, value = attn.to_kv(context).chunk(2, dim=-1)
|
|
53
|
+
else:
|
|
54
|
+
query, key, value = attn.to_qkv(hidden_states).chunk(3, dim=-1)
|
|
55
|
+
else:
|
|
56
|
+
query = attn.to_q(hidden_states)
|
|
57
|
+
key = attn.to_k(context)
|
|
58
|
+
value = attn.to_v(context)
|
|
59
|
+
return query, key, value
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _apply_rotary(hidden_states, rotary_emb):
|
|
63
|
+
if rotary_emb is None:
|
|
64
|
+
return hidden_states
|
|
65
|
+
freqs_cos, freqs_sin = rotary_emb
|
|
66
|
+
x1, x2 = hidden_states.unflatten(-1, (-1, 2)).unbind(-1)
|
|
67
|
+
cos = freqs_cos[..., 0::2]
|
|
68
|
+
sin = freqs_sin[..., 1::2]
|
|
69
|
+
out = torch.empty_like(hidden_states)
|
|
70
|
+
out[..., 0::2] = x1 * cos - x2 * sin
|
|
71
|
+
out[..., 1::2] = x1 * sin + x2 * cos
|
|
72
|
+
return out.type_as(hidden_states)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _qkv(attn, hidden_states, encoder_hidden_states, rotary_emb):
|
|
76
|
+
query, key, value = _projections(
|
|
77
|
+
attn, hidden_states, encoder_hidden_states)
|
|
78
|
+
query = attn.norm_q(query)
|
|
79
|
+
key = attn.norm_k(key)
|
|
80
|
+
query = query.unflatten(2, (attn.heads, -1))
|
|
81
|
+
key = key.unflatten(2, (attn.heads, -1))
|
|
82
|
+
value = value.unflatten(2, (attn.heads, -1))
|
|
83
|
+
query = _apply_rotary(query, rotary_emb)
|
|
84
|
+
key = _apply_rotary(key, rotary_emb)
|
|
85
|
+
return query, key, value
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class _Recorder:
|
|
89
|
+
def __init__(self, original, rows):
|
|
90
|
+
self.original = original
|
|
91
|
+
self.rows = rows
|
|
92
|
+
|
|
93
|
+
def __call__(
|
|
94
|
+
self, attn, hidden_states, encoder_hidden_states=None,
|
|
95
|
+
attention_mask=None, rotary_emb=None, *args, **kwargs,
|
|
96
|
+
):
|
|
97
|
+
if attention_mask is None:
|
|
98
|
+
query, key, value = _qkv(
|
|
99
|
+
attn, hidden_states, encoder_hidden_states, rotary_emb)
|
|
100
|
+
row = {
|
|
101
|
+
"q": query.transpose(1, 2).detach(),
|
|
102
|
+
"key": key.transpose(1, 2).detach(),
|
|
103
|
+
"value": value.transpose(1, 2).detach(),
|
|
104
|
+
"mask": None,
|
|
105
|
+
}
|
|
106
|
+
if self.rows:
|
|
107
|
+
first = self.rows[0]
|
|
108
|
+
expected = tuple(
|
|
109
|
+
(tuple(first[name].shape), first[name].dtype)
|
|
110
|
+
for name in ("q", "key", "value"))
|
|
111
|
+
got = tuple(
|
|
112
|
+
(tuple(row[name].shape), row[name].dtype)
|
|
113
|
+
for name in ("q", "key", "value"))
|
|
114
|
+
if got != expected:
|
|
115
|
+
raise ValueError(
|
|
116
|
+
"attention_core rotary: shape or dtype moved within "
|
|
117
|
+
f"one calibration call: {expected} -> {got}")
|
|
118
|
+
else:
|
|
119
|
+
# Binding needs one real device sample. Subsequent calls only
|
|
120
|
+
# qualify the stable signature; retaining every denoise-step
|
|
121
|
+
# activation would turn calibration length into VRAM usage.
|
|
122
|
+
self.rows.append(row)
|
|
123
|
+
else:
|
|
124
|
+
self.rows.append({"mask": attention_mask.detach()})
|
|
125
|
+
return self.original(
|
|
126
|
+
attn, hidden_states, encoder_hidden_states, attention_mask,
|
|
127
|
+
rotary_emb, *args, **kwargs)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class _FlashRTRotaryAttnProcessor:
|
|
131
|
+
def __init__(self, core, original):
|
|
132
|
+
self.core = core
|
|
133
|
+
self.original = original
|
|
134
|
+
|
|
135
|
+
def __call__(
|
|
136
|
+
self, attn, hidden_states, encoder_hidden_states=None,
|
|
137
|
+
attention_mask=None, rotary_emb=None, *args, **kwargs,
|
|
138
|
+
):
|
|
139
|
+
if attention_mask is not None:
|
|
140
|
+
return self.original(
|
|
141
|
+
attn, hidden_states, encoder_hidden_states, attention_mask,
|
|
142
|
+
rotary_emb, *args, **kwargs)
|
|
143
|
+
query, key, value = _qkv(
|
|
144
|
+
attn, hidden_states, encoder_hidden_states, rotary_emb)
|
|
145
|
+
projection_dtype = query.dtype
|
|
146
|
+
guard = getattr(self.core, "_frt_guard", None)
|
|
147
|
+
accepted_dtypes = tuple(getattr(guard, "dtypes", ()) or ())
|
|
148
|
+
if accepted_dtypes and projection_dtype not in accepted_dtypes:
|
|
149
|
+
return self.original(
|
|
150
|
+
attn, hidden_states, encoder_hidden_states, attention_mask,
|
|
151
|
+
rotary_emb, *args, **kwargs)
|
|
152
|
+
hidden_states = self.core(
|
|
153
|
+
query.transpose(1, 2), key.transpose(1, 2), value.transpose(1, 2))
|
|
154
|
+
hidden_states = hidden_states.transpose(1, 2).flatten(2, 3)
|
|
155
|
+
hidden_states = hidden_states.type_as(query)
|
|
156
|
+
hidden_states = attn.to_out[0](hidden_states)
|
|
157
|
+
return attn.to_out[1](hidden_states)
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
class DiffusersRotaryAttentionAdapter:
|
|
161
|
+
"""Route capability-compatible rotary processors through Hub FA2."""
|
|
162
|
+
|
|
163
|
+
__name__ = "diffusers_rotary_attention"
|
|
164
|
+
|
|
165
|
+
def __call__(self, model, forward, *, prefix_cadence: bool = False):
|
|
166
|
+
del prefix_cadence
|
|
167
|
+
sites = []
|
|
168
|
+
for path, module in model.named_modules():
|
|
169
|
+
processor = getattr(module, "processor", None)
|
|
170
|
+
compatible, _ = _compatible_site(module, processor)
|
|
171
|
+
if compatible:
|
|
172
|
+
sites.append((path, module, processor))
|
|
173
|
+
if not sites:
|
|
174
|
+
return None
|
|
175
|
+
|
|
176
|
+
captures = [[] for _ in sites]
|
|
177
|
+
for (_, module, original), rows in zip(sites, captures):
|
|
178
|
+
module.processor = _Recorder(original, rows)
|
|
179
|
+
try:
|
|
180
|
+
with torch.no_grad():
|
|
181
|
+
forward()
|
|
182
|
+
finally:
|
|
183
|
+
for _, module, original in sites:
|
|
184
|
+
module.processor = original
|
|
185
|
+
|
|
186
|
+
refused = []
|
|
187
|
+
routes = []
|
|
188
|
+
observed = {}
|
|
189
|
+
variants = {}
|
|
190
|
+
for (path, module, original), rows in zip(sites, captures):
|
|
191
|
+
if not rows:
|
|
192
|
+
refused.append((
|
|
193
|
+
f"{path}.processor",
|
|
194
|
+
"attention_core rotary: compatible processor was not "
|
|
195
|
+
"called during calibration",
|
|
196
|
+
))
|
|
197
|
+
continue
|
|
198
|
+
if any(row.get("mask") is not None for row in rows):
|
|
199
|
+
refused.append((
|
|
200
|
+
f"{path}.processor",
|
|
201
|
+
"attention_core rotary: live masks are outside the "
|
|
202
|
+
"unmasked executable form",
|
|
203
|
+
))
|
|
204
|
+
continue
|
|
205
|
+
core = bind_dense_attention_best(rows)
|
|
206
|
+
if core is None:
|
|
207
|
+
refused.append((
|
|
208
|
+
f"{path}.processor",
|
|
209
|
+
"attention_core rotary: Hub FA2 does not cover the "
|
|
210
|
+
"captured head dimension",
|
|
211
|
+
))
|
|
212
|
+
continue
|
|
213
|
+
routed = _FlashRTRotaryAttnProcessor(core, original)
|
|
214
|
+
routes.append((module, original, routed))
|
|
215
|
+
observed[f"{path}.processor::fa2_core"] = core
|
|
216
|
+
variants[f"{path}.processor"] = {
|
|
217
|
+
"bound": getattr(core, "_frt_variant", "fa2"),
|
|
218
|
+
"superseded": list(
|
|
219
|
+
getattr(core, "_frt_variant_trail", ())),
|
|
220
|
+
}
|
|
221
|
+
if not routes:
|
|
222
|
+
return {}, None, {"refused": refused}
|
|
223
|
+
|
|
224
|
+
def enable():
|
|
225
|
+
for module, _, routed in routes:
|
|
226
|
+
module.processor = routed
|
|
227
|
+
|
|
228
|
+
def disable():
|
|
229
|
+
for module, original, _ in routes:
|
|
230
|
+
module.processor = original
|
|
231
|
+
|
|
232
|
+
enable()
|
|
233
|
+
return {}, None, {
|
|
234
|
+
"revert": [disable],
|
|
235
|
+
"observed": observed,
|
|
236
|
+
"toggle": (enable, disable),
|
|
237
|
+
"refused": refused,
|
|
238
|
+
"attention_variants": variants,
|
|
239
|
+
}
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
"""Q/K norm + RoPE adapter for factored two-way attention hosts.
|
|
2
|
+
|
|
3
|
+
The host capability is two independent sibling-QKV projection groups over a
|
|
4
|
+
factored causal/full pack. Each group is consumed by per-head RMSNorm and the
|
|
5
|
+
same pre-expanded rotate-half position table before a factored attention
|
|
6
|
+
processor. This is the Cosmos/MoT form, but the adapter deliberately matches
|
|
7
|
+
those slots and dataflow rather than a model or class name.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import types
|
|
13
|
+
|
|
14
|
+
from ..guard import GuardRefused
|
|
15
|
+
from ..impls.qk_norm_rope import bind_per_head_gqa_qk_norm_rope
|
|
16
|
+
from ..impls.qkv_pack.fp8_static import PackedLinear, StashReader
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_PATHS = (
|
|
20
|
+
(
|
|
21
|
+
("to_q", "to_k", "to_v"),
|
|
22
|
+
("norm_q", "norm_k"),
|
|
23
|
+
"causal_seq",
|
|
24
|
+
),
|
|
25
|
+
(
|
|
26
|
+
("add_q_proj", "add_k_proj", "add_v_proj"),
|
|
27
|
+
("norm_added_q", "norm_added_k"),
|
|
28
|
+
"full_only_seq",
|
|
29
|
+
),
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _pack_parts(plan, path: str, attrs: tuple[str, str, str]):
|
|
34
|
+
head = plan.swaps.get(f"{path}.{attrs[0]}")
|
|
35
|
+
key = plan.swaps.get(f"{path}.{attrs[1]}")
|
|
36
|
+
value = plan.swaps.get(f"{path}.{attrs[2]}")
|
|
37
|
+
if not (
|
|
38
|
+
isinstance(head, PackedLinear)
|
|
39
|
+
and isinstance(key, StashReader)
|
|
40
|
+
and isinstance(value, StashReader)
|
|
41
|
+
and key._packed[0] is head
|
|
42
|
+
and value._packed[0] is head
|
|
43
|
+
):
|
|
44
|
+
return None
|
|
45
|
+
return head
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _epsilon(norm) -> float | None:
|
|
49
|
+
value = getattr(norm, "variance_epsilon", getattr(norm, "eps", None))
|
|
50
|
+
return None if value is None else float(value)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class FactoredQkNormRopeAdapter:
|
|
54
|
+
"""Compose two packed QKV groups with per-head Q/K norm and RoPE."""
|
|
55
|
+
|
|
56
|
+
__name__ = "factored_qk_norm_rope"
|
|
57
|
+
|
|
58
|
+
def __call__(self, model, plan):
|
|
59
|
+
routes = []
|
|
60
|
+
observed = {}
|
|
61
|
+
refused = []
|
|
62
|
+
|
|
63
|
+
for path, module in model.named_modules():
|
|
64
|
+
packs = [_pack_parts(plan, path, attrs) for attrs, _, _ in _PATHS]
|
|
65
|
+
if not any(pack is not None for pack in packs):
|
|
66
|
+
continue
|
|
67
|
+
site = f"{path}::factored_qk_norm_rope"
|
|
68
|
+
|
|
69
|
+
def refuse(reason: str) -> None:
|
|
70
|
+
refused.append((site, f"qk_norm_rope refused: {reason}"))
|
|
71
|
+
|
|
72
|
+
if not all(pack is not None for pack in packs):
|
|
73
|
+
refuse("both causal and full QKV groups must be packed")
|
|
74
|
+
continue
|
|
75
|
+
if module.training:
|
|
76
|
+
refuse("training/dropout form is outside the inference seam")
|
|
77
|
+
continue
|
|
78
|
+
if not all(
|
|
79
|
+
hasattr(module, attr)
|
|
80
|
+
for attr in (
|
|
81
|
+
"head_dim",
|
|
82
|
+
"num_attention_heads",
|
|
83
|
+
"num_key_value_heads",
|
|
84
|
+
"dispatch_attention_fn",
|
|
85
|
+
"to_out",
|
|
86
|
+
"to_add_out",
|
|
87
|
+
)
|
|
88
|
+
):
|
|
89
|
+
refuse("host lacks the complete factored two-way slots")
|
|
90
|
+
continue
|
|
91
|
+
if getattr(module, "cp_mesh", None) is not None:
|
|
92
|
+
refuse("context-parallel packs are outside this single-device seam")
|
|
93
|
+
continue
|
|
94
|
+
|
|
95
|
+
head_dim = int(module.head_dim)
|
|
96
|
+
q_heads = int(module.num_attention_heads)
|
|
97
|
+
kv_heads = int(module.num_key_value_heads)
|
|
98
|
+
if head_dim != 128:
|
|
99
|
+
refuse("current Hub entry requires head_dim=128")
|
|
100
|
+
continue
|
|
101
|
+
|
|
102
|
+
bounds = []
|
|
103
|
+
bad = None
|
|
104
|
+
for pack, (_, norms, key) in zip(packs, _PATHS):
|
|
105
|
+
q_norm = getattr(module, norms[0], None)
|
|
106
|
+
k_norm = getattr(module, norms[1], None)
|
|
107
|
+
q_weight = getattr(q_norm, "weight", None)
|
|
108
|
+
k_weight = getattr(k_norm, "weight", None)
|
|
109
|
+
eps = _epsilon(q_norm)
|
|
110
|
+
if q_weight is None or k_weight is None or eps is None:
|
|
111
|
+
bad = f"{key} Q/K norm weights or epsilon are absent"
|
|
112
|
+
break
|
|
113
|
+
expected = (q_heads * head_dim, kv_heads * head_dim, kv_heads * head_dim)
|
|
114
|
+
if tuple(pack.splits[:3]) != expected:
|
|
115
|
+
bad = f"{key} packed widths {tuple(pack.splits[:3])} != {expected}"
|
|
116
|
+
break
|
|
117
|
+
try:
|
|
118
|
+
bound = bind_per_head_gqa_qk_norm_rope(
|
|
119
|
+
q_weight,
|
|
120
|
+
k_weight,
|
|
121
|
+
row_capacity=pack.rows,
|
|
122
|
+
q_heads=q_heads,
|
|
123
|
+
kv_heads=kv_heads,
|
|
124
|
+
head_dim=head_dim,
|
|
125
|
+
eps=eps,
|
|
126
|
+
)
|
|
127
|
+
except (ValueError, RuntimeError) as exc:
|
|
128
|
+
bad = str(exc)
|
|
129
|
+
break
|
|
130
|
+
bounds.append(bound)
|
|
131
|
+
if bad is not None:
|
|
132
|
+
refuse(bad)
|
|
133
|
+
continue
|
|
134
|
+
|
|
135
|
+
original = module.forward
|
|
136
|
+
had_instance_forward = "forward" in module.__dict__
|
|
137
|
+
causal_pack, full_pack = packs
|
|
138
|
+
causal_bound, full_bound = bounds
|
|
139
|
+
|
|
140
|
+
def routed(
|
|
141
|
+
self,
|
|
142
|
+
pack,
|
|
143
|
+
attention_mask,
|
|
144
|
+
packed_position_embeddings,
|
|
145
|
+
dual_kv_cache=None,
|
|
146
|
+
natten_metadata=None,
|
|
147
|
+
*,
|
|
148
|
+
und_packed=causal_pack,
|
|
149
|
+
gen_packed=full_pack,
|
|
150
|
+
und_bound=causal_bound,
|
|
151
|
+
gen_bound=full_bound,
|
|
152
|
+
):
|
|
153
|
+
del attention_mask
|
|
154
|
+
if dual_kv_cache is not None:
|
|
155
|
+
raise GuardRefused(
|
|
156
|
+
"qk_norm_rope: factored cache mutation is outside the bound seam"
|
|
157
|
+
)
|
|
158
|
+
if natten_metadata is not None:
|
|
159
|
+
raise GuardRefused(
|
|
160
|
+
"qk_norm_rope: neighborhood attention is outside the bound seam"
|
|
161
|
+
)
|
|
162
|
+
if not isinstance(pack, dict) or not all(
|
|
163
|
+
key in pack for key in ("causal_seq", "full_only_seq")
|
|
164
|
+
):
|
|
165
|
+
raise GuardRefused(
|
|
166
|
+
"qk_norm_rope: expected a causal/full factored pack"
|
|
167
|
+
)
|
|
168
|
+
try:
|
|
169
|
+
cos_pack, sin_pack = packed_position_embeddings
|
|
170
|
+
und_cos = cos_pack["causal_seq"]
|
|
171
|
+
und_sin = sin_pack["causal_seq"]
|
|
172
|
+
gen_cos = cos_pack["full_only_seq"]
|
|
173
|
+
gen_sin = sin_pack["full_only_seq"]
|
|
174
|
+
except (KeyError, TypeError, ValueError) as exc:
|
|
175
|
+
raise GuardRefused(
|
|
176
|
+
"qk_norm_rope: position tables do not share the factored layout"
|
|
177
|
+
) from exc
|
|
178
|
+
|
|
179
|
+
q_und, k_und, v_und = und_bound(
|
|
180
|
+
und_packed.joint(pack["causal_seq"]).unsqueeze(0),
|
|
181
|
+
und_cos.unsqueeze(0),
|
|
182
|
+
und_sin.unsqueeze(0),
|
|
183
|
+
)
|
|
184
|
+
q_gen, k_gen, v_gen = gen_bound(
|
|
185
|
+
gen_packed.joint(pack["full_only_seq"]).unsqueeze(0),
|
|
186
|
+
gen_cos.unsqueeze(0),
|
|
187
|
+
gen_sin.unsqueeze(0),
|
|
188
|
+
)
|
|
189
|
+
q_und, k_und, v_und = q_und[0], k_und[0], v_und[0]
|
|
190
|
+
q_gen, k_gen, v_gen = q_gen[0], k_gen[0], v_gen[0]
|
|
191
|
+
|
|
192
|
+
if bool(getattr(getattr(self, "config", None), "freeze_und", False)):
|
|
193
|
+
q_und = q_und.detach()
|
|
194
|
+
k_und = k_und.detach()
|
|
195
|
+
v_und = v_und.detach()
|
|
196
|
+
|
|
197
|
+
query = dict(pack)
|
|
198
|
+
key = dict(pack)
|
|
199
|
+
value = dict(pack)
|
|
200
|
+
query["causal_seq"], query["full_only_seq"] = q_und, q_gen
|
|
201
|
+
key["causal_seq"], key["full_only_seq"] = k_und, k_gen
|
|
202
|
+
value["causal_seq"], value["full_only_seq"] = v_und, v_gen
|
|
203
|
+
attended = self.dispatch_attention_fn(query, key, value)
|
|
204
|
+
|
|
205
|
+
out = dict(pack)
|
|
206
|
+
out["causal_seq"] = self.to_out(attended["causal_seq"])
|
|
207
|
+
out["full_only_seq"] = self.to_add_out(attended["full_only_seq"])
|
|
208
|
+
return out
|
|
209
|
+
|
|
210
|
+
routes.append(
|
|
211
|
+
(
|
|
212
|
+
module,
|
|
213
|
+
packs,
|
|
214
|
+
types.MethodType(routed, module),
|
|
215
|
+
original,
|
|
216
|
+
had_instance_forward,
|
|
217
|
+
)
|
|
218
|
+
)
|
|
219
|
+
observed[f"{path}.causal::per_head_qk_norm_rope"] = causal_bound
|
|
220
|
+
observed[f"{path}.full::per_head_qk_norm_rope"] = full_bound
|
|
221
|
+
|
|
222
|
+
if not routes:
|
|
223
|
+
return {"refused": refused} if refused else None
|
|
224
|
+
|
|
225
|
+
def enable() -> None:
|
|
226
|
+
for module, packs, routed, _, _ in routes:
|
|
227
|
+
for pack in packs:
|
|
228
|
+
pack.enable_joint(3)
|
|
229
|
+
module.forward = routed
|
|
230
|
+
|
|
231
|
+
def disable() -> None:
|
|
232
|
+
for module, packs, _, original, _ in routes:
|
|
233
|
+
module.forward = original
|
|
234
|
+
for pack in packs:
|
|
235
|
+
pack.disable_joint()
|
|
236
|
+
|
|
237
|
+
def revert() -> None:
|
|
238
|
+
for module, packs, _, original, had_instance_forward in routes:
|
|
239
|
+
for pack in packs:
|
|
240
|
+
pack.disable_joint()
|
|
241
|
+
if had_instance_forward:
|
|
242
|
+
module.forward = original
|
|
243
|
+
elif "forward" in module.__dict__:
|
|
244
|
+
del module.forward
|
|
245
|
+
|
|
246
|
+
enable()
|
|
247
|
+
return {
|
|
248
|
+
"observed": observed,
|
|
249
|
+
"revert": [revert],
|
|
250
|
+
"toggle": (enable, disable),
|
|
251
|
+
"refused": refused,
|
|
252
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"""Adapter for hosts exposing a factored two-way attention processor."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ..impls.attention_core import bind_two_way_attention
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FactoredTwoWayAttentionAdapter:
|
|
9
|
+
"""Route compatible factored attention processors through Hub FA2."""
|
|
10
|
+
|
|
11
|
+
__name__ = "factored_two_way_attention"
|
|
12
|
+
|
|
13
|
+
def __call__(self, model, forward, *, prefix_cadence: bool = False):
|
|
14
|
+
del prefix_cadence
|
|
15
|
+
sites = []
|
|
16
|
+
for path, module in model.named_modules():
|
|
17
|
+
processor = getattr(module, "dispatch_attention_fn", None)
|
|
18
|
+
if callable(processor):
|
|
19
|
+
sites.append((path, module, processor))
|
|
20
|
+
if not sites:
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
captures: list[list[dict]] = [[] for _ in sites]
|
|
24
|
+
for index, (_, module, original) in enumerate(sites):
|
|
25
|
+
def record(query, key, value, *, i=index, fn=original):
|
|
26
|
+
captures[i].append(
|
|
27
|
+
{"query": query, "key": key, "value": value})
|
|
28
|
+
return fn(query, key, value)
|
|
29
|
+
|
|
30
|
+
module.dispatch_attention_fn = record
|
|
31
|
+
try:
|
|
32
|
+
with __import__("torch").no_grad():
|
|
33
|
+
forward()
|
|
34
|
+
finally:
|
|
35
|
+
for _, module, original in sites:
|
|
36
|
+
module.dispatch_attention_fn = original
|
|
37
|
+
|
|
38
|
+
if not any(captures):
|
|
39
|
+
return None
|
|
40
|
+
if any(not rows for rows in captures):
|
|
41
|
+
raise ValueError(
|
|
42
|
+
"attention_core two_way: only some discovered processors "
|
|
43
|
+
"were called")
|
|
44
|
+
|
|
45
|
+
cores = []
|
|
46
|
+
routes = []
|
|
47
|
+
for (path, module, original), rows in zip(sites, captures):
|
|
48
|
+
first = rows[0]
|
|
49
|
+
|
|
50
|
+
def shape(pack):
|
|
51
|
+
return (
|
|
52
|
+
tuple(pack["causal_seq"].shape),
|
|
53
|
+
tuple(pack["full_only_seq"].shape),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
expected = (
|
|
57
|
+
shape(first["query"]),
|
|
58
|
+
shape(first["key"]),
|
|
59
|
+
shape(first["value"]),
|
|
60
|
+
)
|
|
61
|
+
for row in rows[1:]:
|
|
62
|
+
got = (
|
|
63
|
+
shape(row["query"]),
|
|
64
|
+
shape(row["key"]),
|
|
65
|
+
shape(row["value"]),
|
|
66
|
+
)
|
|
67
|
+
if got != expected:
|
|
68
|
+
raise ValueError(
|
|
69
|
+
"attention_core two_way: processor shapes move "
|
|
70
|
+
f"within one calibration call: {expected} -> {got}")
|
|
71
|
+
core = bind_two_way_attention(first)
|
|
72
|
+
|
|
73
|
+
def routed(query, key, value, *, bound=core):
|
|
74
|
+
return bound(query, key, value)
|
|
75
|
+
|
|
76
|
+
cores.append((path, core))
|
|
77
|
+
routes.append((module, original, routed))
|
|
78
|
+
|
|
79
|
+
def enable() -> None:
|
|
80
|
+
for module, _, routed in routes:
|
|
81
|
+
module.dispatch_attention_fn = routed
|
|
82
|
+
|
|
83
|
+
def disable() -> None:
|
|
84
|
+
for module, original, _ in routes:
|
|
85
|
+
module.dispatch_attention_fn = original
|
|
86
|
+
|
|
87
|
+
def revert() -> None:
|
|
88
|
+
disable()
|
|
89
|
+
|
|
90
|
+
enable()
|
|
91
|
+
observed = {
|
|
92
|
+
f"{path}.dispatch_attention_fn::fa2_core": core
|
|
93
|
+
for path, core in cores
|
|
94
|
+
}
|
|
95
|
+
return {}, None, {
|
|
96
|
+
"revert": [revert],
|
|
97
|
+
"observed": observed,
|
|
98
|
+
"toggle": (enable, disable),
|
|
99
|
+
}
|