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,174 @@
|
|
|
1
|
+
"""FlashRT structures — verified, composable model sub-blocks.
|
|
2
|
+
|
|
3
|
+
A structure is a versioned specification of one model region: boundary
|
|
4
|
+
tensors, framework-neutral weight slots, a plain reference implementation
|
|
5
|
+
used as ground truth, and qualification gates. This package hosts the
|
|
6
|
+
structure catalog and its registry. Implementations, host adapters, and
|
|
7
|
+
the qualification harness build on top of these specifications.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
try:
|
|
11
|
+
from flash_rt.catalog.binding import (
|
|
12
|
+
BindingSpec,
|
|
13
|
+
CoverageSegment,
|
|
14
|
+
list_bindings,
|
|
15
|
+
load_binding,
|
|
16
|
+
)
|
|
17
|
+
from flash_rt.catalog.registry import StructureSpec, list_structures, load
|
|
18
|
+
except ImportError as _e: # pragma: no cover - depends on the installed flash-rt
|
|
19
|
+
raise ImportError(
|
|
20
|
+
"flashrt-structures needs a flash-rt that ships the structure "
|
|
21
|
+
"catalog as flash_rt.catalog (FlashRT main after the structures "
|
|
22
|
+
"split; the 0.1.0 wheel on PyPI predates it and still carries the "
|
|
23
|
+
"layer as flash_rt.structures). Install FlashRT from "
|
|
24
|
+
"https://github.com/flashrt-project/FlashRT or upgrade flash-rt."
|
|
25
|
+
) from _e
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def get(name):
|
|
29
|
+
"""Explicit door: pull one structure and bind it yourself.
|
|
30
|
+
|
|
31
|
+
Mirrors ``kernels.get_kernel``: ``get("decoder_ffn").bind(module,
|
|
32
|
+
calibration=[...])`` returns a gated drop-in replacement you plug in
|
|
33
|
+
where you choose. See :mod:`flashrt_structures.handle`.
|
|
34
|
+
"""
|
|
35
|
+
from flashrt_structures.handle import get as _get
|
|
36
|
+
|
|
37
|
+
return _get(name)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def capture(fn, **kwargs):
|
|
41
|
+
"""Capture door: graph a hot stage with declared swap windows.
|
|
42
|
+
|
|
43
|
+
See :func:`flashrt_structures.stages.capture`.
|
|
44
|
+
"""
|
|
45
|
+
from flashrt_structures.stages import capture as _capture
|
|
46
|
+
|
|
47
|
+
return _capture(fn, **kwargs)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def auto_swaps(model, forward, **kwargs):
|
|
51
|
+
"""Distribution layer: discover, calibrate, bind — one pass, no
|
|
52
|
+
per-seam scaffolding. Returns an :class:`AutoPlan` of swaps.
|
|
53
|
+
|
|
54
|
+
See :func:`flashrt_structures.autobuild.auto_swaps`.
|
|
55
|
+
"""
|
|
56
|
+
from flashrt_structures.autobuild import auto_swaps as _auto
|
|
57
|
+
|
|
58
|
+
return _auto(model, forward, **kwargs)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def run_recipe(recipe, model, ctx=None, **kwargs):
|
|
62
|
+
"""Recipe door: assemble declared levers, audit same-process on the
|
|
63
|
+
graph, certify or refuse — one call, one receipt.
|
|
64
|
+
|
|
65
|
+
See :mod:`flashrt_structures.recipe` for ``Recipe``/``Lever``/
|
|
66
|
+
``Gates`` and the switch lifecycle.
|
|
67
|
+
"""
|
|
68
|
+
from flashrt_structures.recipe import run_recipe as _run
|
|
69
|
+
|
|
70
|
+
return _run(recipe, model, ctx, **kwargs)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def attach(model, forward, **kwargs):
|
|
74
|
+
"""One-call front door: discover, calibrate, gate, activate.
|
|
75
|
+
|
|
76
|
+
See :func:`flashrt_structures.frontdoor.attach`. Imported lazily so
|
|
77
|
+
that spec-only consumers do not pay for torch-side machinery.
|
|
78
|
+
"""
|
|
79
|
+
from flashrt_structures.frontdoor import attach as _attach
|
|
80
|
+
|
|
81
|
+
return _attach(model, forward, **kwargs)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def adopt_prequantized(model, fmt="ct_nvfp4", **kwargs):
|
|
85
|
+
"""Checkpoint door: adopt an already-quantized checkpoint by
|
|
86
|
+
converting its packed projections into structure impls.
|
|
87
|
+
|
|
88
|
+
See :func:`flashrt_structures.prequantized.adopt_prequantized`.
|
|
89
|
+
"""
|
|
90
|
+
from flashrt_structures.prequantized import (
|
|
91
|
+
adopt_prequantized as _adopt)
|
|
92
|
+
|
|
93
|
+
return _adopt(model, fmt, **kwargs)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def quantize_on_adopt(model, fmt="moe_experts_nvfp4", **kwargs):
|
|
97
|
+
"""Checkpoint door: quantize a full-precision checkpoint that cannot
|
|
98
|
+
fit the card, converting its dominant structure family (a sparse-MoE
|
|
99
|
+
expert bank) into structure impls at load time.
|
|
100
|
+
|
|
101
|
+
See :func:`flashrt_structures.quantize_on_adopt.quantize_on_adopt`.
|
|
102
|
+
"""
|
|
103
|
+
from flashrt_structures.quantize_on_adopt import (
|
|
104
|
+
quantize_on_adopt as _adopt)
|
|
105
|
+
|
|
106
|
+
return _adopt(model, fmt, **kwargs)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def explain(plan):
|
|
110
|
+
"""Coverage table for one plan: bound / routed / kept / refused,
|
|
111
|
+
each with its reason. See :mod:`flashrt_structures.explain`."""
|
|
112
|
+
from flashrt_structures.explain import explain as _explain
|
|
113
|
+
|
|
114
|
+
return _explain(plan)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def decode_loop(model, *, max_len, compile_step=True,
|
|
118
|
+
compile_prefill=True, kv_band=None):
|
|
119
|
+
"""Serving door: the whole-loop decode form (static cache + compiled
|
|
120
|
+
step + whole-step CUDA graph) over whatever structures are attached.
|
|
121
|
+
|
|
122
|
+
See :mod:`flashrt_structures.impls.decode_loop.whole_step`.
|
|
123
|
+
"""
|
|
124
|
+
from flashrt_structures.impls.decode_loop.whole_step import (
|
|
125
|
+
build_decode_loop)
|
|
126
|
+
|
|
127
|
+
return build_decode_loop(model, max_len=max_len,
|
|
128
|
+
compile_step=compile_step,
|
|
129
|
+
compile_prefill=compile_prefill,
|
|
130
|
+
kv_band=kv_band)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def aot_package(module, args=(), kwargs=None,
|
|
134
|
+
package_path="module_aot.pt2", **opts):
|
|
135
|
+
"""Whole-graph door: export the swapped module and AOT-compile it
|
|
136
|
+
into a reusable package (graph breaks are defects, not fallbacks).
|
|
137
|
+
|
|
138
|
+
See :mod:`flashrt_structures.aot`.
|
|
139
|
+
"""
|
|
140
|
+
from flashrt_structures.aot import aot_package as _pkg
|
|
141
|
+
|
|
142
|
+
return _pkg(module, args=args, kwargs=kwargs,
|
|
143
|
+
package_path=package_path, **opts)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def aot_load(package_path, weights=None):
|
|
147
|
+
"""Load an AOT package back as a callable graph."""
|
|
148
|
+
from flashrt_structures.aot import aot_load as _load
|
|
149
|
+
|
|
150
|
+
return _load(package_path, weights=weights)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
from . import schemes # noqa: E402 (registry: quantisation schemes)
|
|
154
|
+
|
|
155
|
+
__all__ = [
|
|
156
|
+
"BindingSpec",
|
|
157
|
+
"CoverageSegment",
|
|
158
|
+
"StructureSpec",
|
|
159
|
+
"adopt_prequantized",
|
|
160
|
+
"aot_load",
|
|
161
|
+
"aot_package",
|
|
162
|
+
"decode_loop",
|
|
163
|
+
"explain",
|
|
164
|
+
"attach",
|
|
165
|
+
"capture",
|
|
166
|
+
"get",
|
|
167
|
+
"list_bindings",
|
|
168
|
+
"list_structures",
|
|
169
|
+
"load",
|
|
170
|
+
"load_binding",
|
|
171
|
+
"quantize_on_adopt",
|
|
172
|
+
"run_recipe",
|
|
173
|
+
"schemes",
|
|
174
|
+
]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""Host-family adapters — where a structure's seam is host-specific.
|
|
2
|
+
|
|
3
|
+
Importing this package registers the built-in adapters with autobuild.
|
|
4
|
+
Attention seams (attention_core) live here because where the attention
|
|
5
|
+
math runs differs by host family; a static module pattern cannot find
|
|
6
|
+
them, so each family gets a small adapter.
|
|
7
|
+
"""
|
|
8
|
+
from ..autobuild import (
|
|
9
|
+
register_attention_adapter,
|
|
10
|
+
register_gated_delta_adapter,
|
|
11
|
+
register_qk_norm_rope_adapter,
|
|
12
|
+
register_qkv_rope_adapter,
|
|
13
|
+
)
|
|
14
|
+
from .diffusers_attention import DiffusersAttentionAdapter
|
|
15
|
+
from .diffusers_rotary_attention import DiffusersRotaryAttentionAdapter
|
|
16
|
+
from .factored_two_way_attention import FactoredTwoWayAttentionAdapter
|
|
17
|
+
from .factored_qk_norm_rope import FactoredQkNormRopeAdapter
|
|
18
|
+
from .packed_stream_qk_norm_rope import PackedStreamQkNormRopeAdapter
|
|
19
|
+
from .gemma_attention import GemmaAttentionAdapter
|
|
20
|
+
from .transformers_gated_delta import TransformersGatedDeltaAdapter
|
|
21
|
+
from .transformers_gated_delta_fused import (
|
|
22
|
+
TransformersGatedDeltaFusedAdapter,
|
|
23
|
+
)
|
|
24
|
+
from .qwen_per_head_qk_norm_rope import (
|
|
25
|
+
PerHeadGqaQkNormRopeAdapter,
|
|
26
|
+
QwenPerHeadQkNormRopeAdapter,
|
|
27
|
+
)
|
|
28
|
+
from .packed_qkv_rope import PackedQkvRopeAdapter
|
|
29
|
+
|
|
30
|
+
register_qk_norm_rope_adapter(PerHeadGqaQkNormRopeAdapter())
|
|
31
|
+
register_qk_norm_rope_adapter(FactoredQkNormRopeAdapter())
|
|
32
|
+
register_qk_norm_rope_adapter(PackedStreamQkNormRopeAdapter())
|
|
33
|
+
register_qkv_rope_adapter(PackedQkvRopeAdapter())
|
|
34
|
+
register_attention_adapter(GemmaAttentionAdapter())
|
|
35
|
+
register_attention_adapter(FactoredTwoWayAttentionAdapter())
|
|
36
|
+
register_attention_adapter(DiffusersRotaryAttentionAdapter())
|
|
37
|
+
register_attention_adapter(DiffusersAttentionAdapter())
|
|
38
|
+
# the fused-layer form is tried first; it refuses cleanly (missing
|
|
39
|
+
# package entries, out-of-profile layers) and the ladder falls
|
|
40
|
+
# through to the callable-slot form
|
|
41
|
+
register_gated_delta_adapter(TransformersGatedDeltaFusedAdapter())
|
|
42
|
+
register_gated_delta_adapter(TransformersGatedDeltaAdapter())
|
|
43
|
+
|
|
44
|
+
__all__ = [
|
|
45
|
+
"DiffusersAttentionAdapter",
|
|
46
|
+
"DiffusersRotaryAttentionAdapter",
|
|
47
|
+
"GemmaAttentionAdapter",
|
|
48
|
+
"TransformersGatedDeltaAdapter",
|
|
49
|
+
"FactoredTwoWayAttentionAdapter",
|
|
50
|
+
"FactoredQkNormRopeAdapter",
|
|
51
|
+
"PackedStreamQkNormRopeAdapter",
|
|
52
|
+
"PerHeadGqaQkNormRopeAdapter",
|
|
53
|
+
"QwenPerHeadQkNormRopeAdapter",
|
|
54
|
+
"PackedQkvRopeAdapter",
|
|
55
|
+
]
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
"""Attention adapter for capability-compatible Diffusers attention hosts."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
|
|
7
|
+
from ..impls.attention_core import bind_dense_attention_best
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _compatible_site(module, processor) -> tuple[bool, str]:
|
|
11
|
+
"""Whether ``module`` exposes the processor contract reproduced below.
|
|
12
|
+
|
|
13
|
+
Processor class names are deliberately irrelevant. The adapter owns the
|
|
14
|
+
projection/output dataflow, so it admits only sites exposing every slot it
|
|
15
|
+
reads and a callable processor that accepts the ordinary Diffusers
|
|
16
|
+
``(attention, hidden_states, ...)`` boundary.
|
|
17
|
+
"""
|
|
18
|
+
if not callable(processor):
|
|
19
|
+
return False, "processor is not callable"
|
|
20
|
+
for attr in ("to_q", "to_k", "to_v"):
|
|
21
|
+
if not isinstance(getattr(module, attr, None), torch.nn.Module):
|
|
22
|
+
return False, f"attention lacks callable projection slot {attr!r}"
|
|
23
|
+
try:
|
|
24
|
+
out_proj, out_drop = module.to_out[0], module.to_out[1]
|
|
25
|
+
except (AttributeError, IndexError, KeyError, TypeError):
|
|
26
|
+
return False, "attention lacks the to_out[projection, dropout] slots"
|
|
27
|
+
if not all(isinstance(part, torch.nn.Module)
|
|
28
|
+
for part in (out_proj, out_drop)):
|
|
29
|
+
return False, "attention output slots are not modules"
|
|
30
|
+
heads = getattr(module, "heads", None)
|
|
31
|
+
if not isinstance(heads, int) or heads <= 0:
|
|
32
|
+
return False, "attention lacks a positive integer head count"
|
|
33
|
+
required_state = (
|
|
34
|
+
"spatial_norm", "group_norm", "norm_cross", "norm_q", "norm_k",
|
|
35
|
+
"residual_connection", "rescale_output_factor",
|
|
36
|
+
)
|
|
37
|
+
missing = [name for name in required_state if not hasattr(module, name)]
|
|
38
|
+
if missing:
|
|
39
|
+
return False, f"attention lacks processor state {missing}"
|
|
40
|
+
if (getattr(module, "norm_cross", False)
|
|
41
|
+
and not callable(getattr(module, "norm_encoder_hidden_states",
|
|
42
|
+
None))):
|
|
43
|
+
return False, "cross normalization is enabled but has no callable"
|
|
44
|
+
return True, ""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def _qkv(attn, hidden_states, encoder_hidden_states, attention_mask, temb):
|
|
48
|
+
"""Reproduce the capability-compatible Diffusers projection half."""
|
|
49
|
+
if attn.spatial_norm is not None:
|
|
50
|
+
hidden_states = attn.spatial_norm(hidden_states, temb)
|
|
51
|
+
if hidden_states.ndim == 4:
|
|
52
|
+
batch_size, channel, height, width = hidden_states.shape
|
|
53
|
+
hidden_states = hidden_states.view(
|
|
54
|
+
batch_size, channel, height * width).transpose(1, 2)
|
|
55
|
+
batch_size, sequence_length, _ = (
|
|
56
|
+
hidden_states.shape
|
|
57
|
+
if encoder_hidden_states is None else encoder_hidden_states.shape
|
|
58
|
+
)
|
|
59
|
+
if attention_mask is not None:
|
|
60
|
+
attention_mask = attn.prepare_attention_mask(
|
|
61
|
+
attention_mask, sequence_length, batch_size)
|
|
62
|
+
attention_mask = attention_mask.view(
|
|
63
|
+
batch_size, attn.heads, -1, attention_mask.shape[-1])
|
|
64
|
+
if attn.group_norm is not None:
|
|
65
|
+
hidden_states = attn.group_norm(
|
|
66
|
+
hidden_states.transpose(1, 2)).transpose(1, 2)
|
|
67
|
+
query = attn.to_q(hidden_states)
|
|
68
|
+
if encoder_hidden_states is None:
|
|
69
|
+
encoder_hidden_states = hidden_states
|
|
70
|
+
elif attn.norm_cross:
|
|
71
|
+
encoder_hidden_states = attn.norm_encoder_hidden_states(
|
|
72
|
+
encoder_hidden_states)
|
|
73
|
+
key = attn.to_k(encoder_hidden_states)
|
|
74
|
+
value = attn.to_v(encoder_hidden_states)
|
|
75
|
+
head_dim = key.shape[-1] // attn.heads
|
|
76
|
+
query = query.view(
|
|
77
|
+
batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
|
78
|
+
key = key.view(
|
|
79
|
+
batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
|
80
|
+
value = value.view(
|
|
81
|
+
batch_size, -1, attn.heads, head_dim).transpose(1, 2)
|
|
82
|
+
if attn.norm_q is not None:
|
|
83
|
+
query = attn.norm_q(query)
|
|
84
|
+
if attn.norm_k is not None:
|
|
85
|
+
key = attn.norm_k(key)
|
|
86
|
+
return query, key, value, attention_mask
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class _Recorder:
|
|
90
|
+
def __init__(self, original, rows):
|
|
91
|
+
self.original = original
|
|
92
|
+
self.rows = rows
|
|
93
|
+
|
|
94
|
+
def __call__(
|
|
95
|
+
self, attn, hidden_states, encoder_hidden_states=None,
|
|
96
|
+
attention_mask=None, temb=None, *args, **kwargs,
|
|
97
|
+
):
|
|
98
|
+
query, key, value, mask = _qkv(
|
|
99
|
+
attn, hidden_states, encoder_hidden_states, attention_mask, temb)
|
|
100
|
+
self.rows.append({
|
|
101
|
+
"q": query.detach(),
|
|
102
|
+
"key": key.detach(),
|
|
103
|
+
"value": value.detach(),
|
|
104
|
+
"mask": mask.detach() if mask is not None else None,
|
|
105
|
+
})
|
|
106
|
+
return self.original(
|
|
107
|
+
attn, hidden_states, encoder_hidden_states, attention_mask, temb,
|
|
108
|
+
*args, **kwargs)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class _FlashRTDenseAttnProcessor:
|
|
112
|
+
"""Diffusers processor with only the SDPA body replaced by FA2."""
|
|
113
|
+
|
|
114
|
+
def __init__(self, core, original):
|
|
115
|
+
self.core = core
|
|
116
|
+
self.original = original
|
|
117
|
+
|
|
118
|
+
def __call__(
|
|
119
|
+
self, attn, hidden_states, encoder_hidden_states=None,
|
|
120
|
+
attention_mask=None, temb=None, *args, **kwargs,
|
|
121
|
+
):
|
|
122
|
+
# A live mask is served only by a core that baked this site's
|
|
123
|
+
# fixed mask pattern at bind time (packed ranges); a maskless
|
|
124
|
+
# core keeps the host path rather than reuse a frozen mask.
|
|
125
|
+
if attention_mask is not None and not getattr(
|
|
126
|
+
self.core, "allowed_ranges", ()):
|
|
127
|
+
return self.original(
|
|
128
|
+
attn, hidden_states, encoder_hidden_states, attention_mask,
|
|
129
|
+
temb, *args, **kwargs)
|
|
130
|
+
residual = hidden_states
|
|
131
|
+
input_ndim = hidden_states.ndim
|
|
132
|
+
if input_ndim == 4:
|
|
133
|
+
batch_size, channel, height, width = hidden_states.shape
|
|
134
|
+
query, key, value, mask = _qkv(
|
|
135
|
+
attn, hidden_states, encoder_hidden_states, attention_mask, temb)
|
|
136
|
+
projection_dtype = query.dtype
|
|
137
|
+
guard = getattr(self.core, "_frt_guard", None)
|
|
138
|
+
accepted_dtypes = tuple(getattr(guard, "dtypes", ()) or ())
|
|
139
|
+
if accepted_dtypes and projection_dtype not in accepted_dtypes:
|
|
140
|
+
# An upstream composition can change the effective projection
|
|
141
|
+
# dtype after this adapter calibrated. A hidden cast here would
|
|
142
|
+
# silently change the declared boundary and add hot-path work.
|
|
143
|
+
# Keep the host path; the core's zero call count makes the missed
|
|
144
|
+
# route visible to the final-form gate.
|
|
145
|
+
return self.original(
|
|
146
|
+
attn, hidden_states, encoder_hidden_states, attention_mask,
|
|
147
|
+
temb, *args, **kwargs)
|
|
148
|
+
hidden_states = self.core(query, key, value)
|
|
149
|
+
hidden_states = hidden_states.transpose(1, 2).reshape(
|
|
150
|
+
query.shape[0], -1, attn.heads * query.shape[-1])
|
|
151
|
+
hidden_states = hidden_states.to(projection_dtype)
|
|
152
|
+
hidden_states = attn.to_out[0](hidden_states)
|
|
153
|
+
hidden_states = attn.to_out[1](hidden_states)
|
|
154
|
+
if input_ndim == 4:
|
|
155
|
+
hidden_states = hidden_states.transpose(-1, -2).reshape(
|
|
156
|
+
batch_size, channel, height, width)
|
|
157
|
+
if attn.residual_connection:
|
|
158
|
+
hidden_states = hidden_states + residual
|
|
159
|
+
return hidden_states / attn.rescale_output_factor
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class DiffusersAttentionAdapter:
|
|
163
|
+
"""Route called Diffusers SDPA processors through stateless Hub FA2."""
|
|
164
|
+
|
|
165
|
+
__name__ = "diffusers_attention"
|
|
166
|
+
|
|
167
|
+
def __call__(self, model, forward, *, prefix_cadence: bool = False):
|
|
168
|
+
del prefix_cadence
|
|
169
|
+
sites = []
|
|
170
|
+
for path, module in model.named_modules():
|
|
171
|
+
processor = getattr(module, "processor", None)
|
|
172
|
+
compatible, _ = _compatible_site(module, processor)
|
|
173
|
+
if compatible:
|
|
174
|
+
sites.append((path, module, processor))
|
|
175
|
+
if not sites:
|
|
176
|
+
return None
|
|
177
|
+
|
|
178
|
+
refused = []
|
|
179
|
+
captures = [[] for _ in sites]
|
|
180
|
+
for (_, module, original), rows in zip(sites, captures):
|
|
181
|
+
module.processor = _Recorder(original, rows)
|
|
182
|
+
try:
|
|
183
|
+
with torch.no_grad():
|
|
184
|
+
forward()
|
|
185
|
+
finally:
|
|
186
|
+
for _, module, original in sites:
|
|
187
|
+
module.processor = original
|
|
188
|
+
|
|
189
|
+
routes = []
|
|
190
|
+
observed = {}
|
|
191
|
+
variants = {}
|
|
192
|
+
for (path, module, original), rows in zip(sites, captures):
|
|
193
|
+
if not rows:
|
|
194
|
+
refused.append((
|
|
195
|
+
f"{path}.processor",
|
|
196
|
+
"attention_core dense: compatible processor was not "
|
|
197
|
+
"called during calibration",
|
|
198
|
+
))
|
|
199
|
+
continue
|
|
200
|
+
core = bind_dense_attention_best(rows)
|
|
201
|
+
if core is None:
|
|
202
|
+
refused.append((
|
|
203
|
+
f"{path}.processor",
|
|
204
|
+
"attention_core dense: published Hub artifact does not "
|
|
205
|
+
"cover the captured head dimension or mask form",
|
|
206
|
+
))
|
|
207
|
+
continue
|
|
208
|
+
routed = _FlashRTDenseAttnProcessor(core, original)
|
|
209
|
+
routes.append((module, original, routed))
|
|
210
|
+
observed[f"{path}.processor::fa2_core"] = core
|
|
211
|
+
variants[f"{path}.processor"] = {
|
|
212
|
+
"bound": getattr(core, "_frt_variant", "fa2"),
|
|
213
|
+
"superseded": list(
|
|
214
|
+
getattr(core, "_frt_variant_trail", ())),
|
|
215
|
+
}
|
|
216
|
+
if not routes:
|
|
217
|
+
return {}, None, {"refused": refused}
|
|
218
|
+
|
|
219
|
+
def enable() -> None:
|
|
220
|
+
for module, _, routed in routes:
|
|
221
|
+
module.processor = routed
|
|
222
|
+
|
|
223
|
+
def disable() -> None:
|
|
224
|
+
for module, original, _ in routes:
|
|
225
|
+
module.processor = original
|
|
226
|
+
|
|
227
|
+
def revert() -> None:
|
|
228
|
+
disable()
|
|
229
|
+
|
|
230
|
+
enable()
|
|
231
|
+
return {}, None, {
|
|
232
|
+
"revert": [revert],
|
|
233
|
+
"observed": observed,
|
|
234
|
+
"toggle": (enable, disable),
|
|
235
|
+
"refused": refused,
|
|
236
|
+
"attention_variants": variants,
|
|
237
|
+
}
|