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.
Files changed (122) hide show
  1. flashrt_structures/__init__.py +174 -0
  2. flashrt_structures/adapters/__init__.py +55 -0
  3. flashrt_structures/adapters/diffusers_attention.py +237 -0
  4. flashrt_structures/adapters/diffusers_rotary_attention.py +239 -0
  5. flashrt_structures/adapters/factored_qk_norm_rope.py +252 -0
  6. flashrt_structures/adapters/factored_two_way_attention.py +99 -0
  7. flashrt_structures/adapters/gemma_attention.py +226 -0
  8. flashrt_structures/adapters/packed_qkv_rope.py +342 -0
  9. flashrt_structures/adapters/packed_stream_qk_norm_rope.py +376 -0
  10. flashrt_structures/adapters/qwen_per_head_qk_norm_rope.py +231 -0
  11. flashrt_structures/adapters/sglang_engine.py +207 -0
  12. flashrt_structures/adapters/transformers_attention_interface.py +73 -0
  13. flashrt_structures/adapters/transformers_gated_delta.py +152 -0
  14. flashrt_structures/adapters/transformers_gated_delta_fused.py +96 -0
  15. flashrt_structures/adapters/vllm_engine.py +424 -0
  16. flashrt_structures/adjudicate.py +85 -0
  17. flashrt_structures/aot.py +191 -0
  18. flashrt_structures/autobuild.py +2052 -0
  19. flashrt_structures/beta/__init__.py +43 -0
  20. flashrt_structures/beta/conform.py +94 -0
  21. flashrt_structures/beta/joins.py +113 -0
  22. flashrt_structures/beta/negotiate.py +84 -0
  23. flashrt_structures/beta/ports.py +140 -0
  24. flashrt_structures/decisions.py +80 -0
  25. flashrt_structures/discover.py +623 -0
  26. flashrt_structures/explain.py +70 -0
  27. flashrt_structures/frontdoor.py +572 -0
  28. flashrt_structures/gates.py +465 -0
  29. flashrt_structures/guard.py +421 -0
  30. flashrt_structures/handle.py +189 -0
  31. flashrt_structures/impls/__init__.py +219 -0
  32. flashrt_structures/impls/adaln_producer/__init__.py +8 -0
  33. flashrt_structures/impls/adaln_producer/broker.py +116 -0
  34. flashrt_structures/impls/adaln_producer/fused.py +388 -0
  35. flashrt_structures/impls/adarms_stack/__init__.py +8 -0
  36. flashrt_structures/impls/adarms_stack/fp8_chain.py +832 -0
  37. flashrt_structures/impls/adarms_stack/region.py +102 -0
  38. flashrt_structures/impls/attention_core/__init__.py +132 -0
  39. flashrt_structures/impls/attention_core/fa2_seqused.py +458 -0
  40. flashrt_structures/impls/attention_core/fa4_cute.py +154 -0
  41. flashrt_structures/impls/attention_core/fa4_fp8.py +178 -0
  42. flashrt_structures/impls/attention_core/masked_mha.py +158 -0
  43. flashrt_structures/impls/attention_core/two_way_fa2.py +220 -0
  44. flashrt_structures/impls/cadence_static/__init__.py +18 -0
  45. flashrt_structures/impls/cadence_static/buffers.py +122 -0
  46. flashrt_structures/impls/cadence_static/cross_attention.py +187 -0
  47. flashrt_structures/impls/chain_elements.py +89 -0
  48. flashrt_structures/impls/decode_loop/__init__.py +0 -0
  49. flashrt_structures/impls/decode_loop/fp8_kv.py +206 -0
  50. flashrt_structures/impls/decode_loop/mtp_speculative.py +245 -0
  51. flashrt_structures/impls/decode_loop/whole_step.py +852 -0
  52. flashrt_structures/impls/decoder_block/__init__.py +6 -0
  53. flashrt_structures/impls/decoder_block/attn_sublayer.py +110 -0
  54. flashrt_structures/impls/decoder_block/fused.py +167 -0
  55. flashrt_structures/impls/decoder_ffn/__init__.py +0 -0
  56. flashrt_structures/impls/decoder_ffn/fp8_static.py +310 -0
  57. flashrt_structures/impls/decoder_ffn/fp8_static.yaml +22 -0
  58. flashrt_structures/impls/decoder_ffn/w4a16_static.py +221 -0
  59. flashrt_structures/impls/decoder_ffn/w8a16_static.py +183 -0
  60. flashrt_structures/impls/dit_stack/__init__.py +11 -0
  61. flashrt_structures/impls/dit_stack/fp4_chain.py +417 -0
  62. flashrt_structures/impls/dit_stack/region.py +86 -0
  63. flashrt_structures/impls/fixed_iter/__init__.py +29 -0
  64. flashrt_structures/impls/fixed_iter/openpi.py +264 -0
  65. flashrt_structures/impls/fixed_iter/protocol.py +94 -0
  66. flashrt_structures/impls/gated_delta_core/__init__.py +3 -0
  67. flashrt_structures/impls/gated_delta_core/fused_layer.py +545 -0
  68. flashrt_structures/impls/gated_delta_core/hub_v3.py +152 -0
  69. flashrt_structures/impls/graph_lowering/__init__.py +27 -0
  70. flashrt_structures/impls/graph_lowering/pi052_denoise.py +179 -0
  71. flashrt_structures/impls/graph_lowering/protocol.py +76 -0
  72. flashrt_structures/impls/graph_lowering/qwen3_vl.py +364 -0
  73. flashrt_structures/impls/linear_proj/__init__.py +0 -0
  74. flashrt_structures/impls/linear_proj/fp8_static.py +270 -0
  75. flashrt_structures/impls/linear_proj/nvfp4_balance.py +131 -0
  76. flashrt_structures/impls/linear_proj/nvfp4_dynamic.py +182 -0
  77. flashrt_structures/impls/linear_proj/w8a16_static.py +230 -0
  78. flashrt_structures/impls/modnorm_qkv_chain/__init__.py +0 -0
  79. flashrt_structures/impls/modnorm_qkv_chain/fp8_ptok_table.py +291 -0
  80. flashrt_structures/impls/moe_experts/__init__.py +9 -0
  81. flashrt_structures/impls/moe_experts/nvfp4_dynamic.py +208 -0
  82. flashrt_structures/impls/moe_experts/nvfp4_w4a16.py +129 -0
  83. flashrt_structures/impls/norm_fused/__init__.py +3 -0
  84. flashrt_structures/impls/norm_fused/bf16.py +94 -0
  85. flashrt_structures/impls/norm_fused/fp8_producer.py +84 -0
  86. flashrt_structures/impls/patch_projection/__init__.py +3 -0
  87. flashrt_structures/impls/patch_projection/bf16_flat.py +139 -0
  88. flashrt_structures/impls/prefill_tower/__init__.py +10 -0
  89. flashrt_structures/impls/prefill_tower/fp8_chain.py +955 -0
  90. flashrt_structures/impls/prefill_tower/region.py +99 -0
  91. flashrt_structures/impls/qk_norm_rope/__init__.py +12 -0
  92. flashrt_structures/impls/qk_norm_rope/per_head_gqa.py +199 -0
  93. flashrt_structures/impls/qk_norm_rope/projection_bf16.py +165 -0
  94. flashrt_structures/impls/qkv_pack/__init__.py +5 -0
  95. flashrt_structures/impls/qkv_pack/bf16.py +110 -0
  96. flashrt_structures/impls/qkv_pack/fp8_static.py +435 -0
  97. flashrt_structures/impls/qkv_pack/nvfp4_balance.py +218 -0
  98. flashrt_structures/impls/qkv_rope/__init__.py +3 -0
  99. flashrt_structures/impls/qkv_rope/packed_bias_bf16.py +143 -0
  100. flashrt_structures/impls/step_table.py +113 -0
  101. flashrt_structures/impls/vision_ffn/__init__.py +0 -0
  102. flashrt_structures/impls/vision_ffn/fp8_static.py +261 -0
  103. flashrt_structures/impls/vision_ffn/nvfp4_balance.py +211 -0
  104. flashrt_structures/impls/vision_tower/__init__.py +7 -0
  105. flashrt_structures/impls/vision_tower/fp8_chain.py +533 -0
  106. flashrt_structures/impls/vision_tower/region.py +91 -0
  107. flashrt_structures/matrix.py +126 -0
  108. flashrt_structures/points.py +368 -0
  109. flashrt_structures/prequantized.py +131 -0
  110. flashrt_structures/quantize_on_adopt.py +94 -0
  111. flashrt_structures/recipe.py +438 -0
  112. flashrt_structures/regions.py +208 -0
  113. flashrt_structures/schemes.py +490 -0
  114. flashrt_structures/stages.py +298 -0
  115. flashrt_structures/storage.py +255 -0
  116. flashrt_structures/swap.py +398 -0
  117. flashrt_structures/workspace.py +110 -0
  118. flashrt_structures-0.2.0.dist-info/METADATA +140 -0
  119. flashrt_structures-0.2.0.dist-info/RECORD +122 -0
  120. flashrt_structures-0.2.0.dist-info/WHEEL +5 -0
  121. flashrt_structures-0.2.0.dist-info/licenses/LICENSE +202 -0
  122. flashrt_structures-0.2.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,99 @@
1
+ """The prefill_tower region family: structural identification, candidates.
2
+
3
+ The identifier matches shape, never names: a module carrying a
4
+ ``layers`` list whose blocks each hold a bias-free attention group and
5
+ gated FFN under *plain* affine RMS norms (a 1-D weight and no
6
+ conditioning projection — the conditioned twin belongs to the sibling
7
+ family), plus a rotary table and a plain stack-level norm. The key
8
+ width must be narrower than the query width: the chain's cache layout
9
+ is written for the single-KV band.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import torch
15
+
16
+ from . import fp8_chain
17
+ from ... import regions
18
+
19
+
20
+ def _plain_linear(mod) -> bool:
21
+ return isinstance(mod, torch.nn.Linear) and mod.bias is None
22
+
23
+
24
+ def _plain_norm(mod) -> bool:
25
+ return fp8_chain._plain_norm_weight(mod) is not None
26
+
27
+
28
+ def _block_ok(block) -> bool:
29
+ attn = getattr(block, "self_attn", None)
30
+ mlp = getattr(block, "mlp", None)
31
+ if attn is None or mlp is None:
32
+ return False
33
+ if not all(_plain_linear(getattr(attn, a, None))
34
+ for a in ("q_proj", "k_proj", "v_proj", "o_proj")):
35
+ return False
36
+ if not all(_plain_linear(getattr(mlp, a, None))
37
+ for a in ("gate_proj", "up_proj", "down_proj")):
38
+ return False
39
+ if mlp.gate_proj.out_features != mlp.up_proj.out_features:
40
+ return False
41
+ if not _plain_norm(getattr(block, "input_layernorm", None)):
42
+ return False
43
+ return _plain_norm(getattr(block, "post_attention_layernorm", None))
44
+
45
+
46
+ def identify(model) -> list[str]:
47
+ roots = []
48
+ for path, mod in model.named_modules():
49
+ layers = getattr(mod, "layers", None)
50
+ if not isinstance(layers, torch.nn.ModuleList) or len(layers) < 2:
51
+ continue
52
+ if not callable(getattr(mod, "rotary_emb", None)):
53
+ continue
54
+ head = layers[0]
55
+ attn = getattr(head, "self_attn", None)
56
+ if attn is None or not isinstance(
57
+ getattr(attn, "q_proj", None), torch.nn.Linear):
58
+ continue
59
+ if not _plain_norm(getattr(mod, "norm", None)):
60
+ continue
61
+ if not all(_block_ok(b) for b in layers):
62
+ continue
63
+ if attn.k_proj.out_features >= attn.q_proj.out_features:
64
+ continue
65
+ roots.append(path)
66
+ return roots
67
+
68
+
69
+ def _bind(model, root, probe):
70
+ return fp8_chain.bind_prefill_fp8_chain(model, root, probe)
71
+
72
+
73
+ def _band_candidate(band: str, row: dict) -> regions.RegionCandidate:
74
+ return regions.RegionCandidate(
75
+ name=f"{band}_chain",
76
+ missing=lambda band=band: fp8_chain.missing_symbols(band=band),
77
+ bind=lambda model, root, probe, band=band:
78
+ fp8_chain.bind_prefill_fp8_chain(model, root, probe,
79
+ band=band),
80
+ precision_rank=row["precision_rank"],
81
+ )
82
+
83
+
84
+ #: candidates generate from the band table — a precision band is a
85
+ #: table row in the chain module, never new wiring here
86
+ FAMILY = regions.RegionFamily(
87
+ family="prefill_tower",
88
+ identify=identify,
89
+ candidates=[_band_candidate(band, row)
90
+ for band, row in fp8_chain.BANDS.items()],
91
+ )
92
+
93
+
94
+ def register() -> None:
95
+ """(Re-)register the family — idempotent, import calls it once."""
96
+ regions.register_region_family(FAMILY)
97
+
98
+
99
+ register()
@@ -0,0 +1,12 @@
1
+ from .projection_bf16 import (
2
+ ProjectionQkNormRope,
3
+ bind_projection_qk_norm_rope,
4
+ )
5
+ from .per_head_gqa import PerHeadGqaQkNormRope, bind_per_head_gqa_qk_norm_rope
6
+
7
+ __all__ = [
8
+ "PerHeadGqaQkNormRope",
9
+ "ProjectionQkNormRope",
10
+ "bind_per_head_gqa_qk_norm_rope",
11
+ "bind_projection_qk_norm_rope",
12
+ ]
@@ -0,0 +1,199 @@
1
+ """Per-head GQA Q/K RMSNorm and rotate-half RoPE implementation."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import torch
6
+
7
+ from .. import hub_kernel
8
+ from ...guard import PROCEED, GuardRefused, GuardedSeam
9
+ from ...workspace import lease
10
+
11
+
12
+ class PerHeadGqaQkNormRope(GuardedSeam, torch.nn.Module):
13
+ """Consume packed GQA QKV and produce attention-ready workspaces."""
14
+
15
+ _frt_can_fallback = False
16
+
17
+ def __init__(
18
+ self,
19
+ q_norm_weight: torch.Tensor,
20
+ k_norm_weight: torch.Tensor,
21
+ *,
22
+ row_capacity: int,
23
+ q_heads: int,
24
+ kv_heads: int,
25
+ head_dim: int,
26
+ eps: float = 1e-6,
27
+ workspace_lane: str | None = None,
28
+ ) -> None:
29
+ super().__init__()
30
+ if row_capacity <= 0 or q_heads <= 0 or kv_heads <= 0:
31
+ raise ValueError(
32
+ "qk_norm_rope: row capacity and head counts must be positive"
33
+ )
34
+ if head_dim != 128:
35
+ raise ValueError(
36
+ "qk_norm_rope: per-head GQA kernel requires head_dim == 128"
37
+ )
38
+ if q_norm_weight.shape != (128,) or k_norm_weight.shape != (128,):
39
+ raise ValueError(
40
+ "qk_norm_rope: per-head norm weights must have shape (128,)"
41
+ )
42
+ if q_norm_weight.device != k_norm_weight.device:
43
+ raise ValueError("qk_norm_rope: Q/K norm weights must share device")
44
+
45
+ self.row_capacity = int(row_capacity)
46
+ self.q_heads = int(q_heads)
47
+ self.kv_heads = int(kv_heads)
48
+ self.head_dim = 128
49
+ self.eps = float(eps)
50
+ kernel = hub_kernel("flashrt/flashrt-qkv-cache-rope", ">=1")
51
+ try:
52
+ self._fn = kernel.qkv_split_per_head_norm_rope_bf16
53
+ except AttributeError as exc:
54
+ raise ValueError(
55
+ "qk_norm_rope: flashrt-qkv-cache-rope artifact lacks the "
56
+ "per-head GQA entry"
57
+ ) from exc
58
+ self.register_buffer(
59
+ "q_norm_weight",
60
+ q_norm_weight.detach().to(torch.bfloat16).contiguous(),
61
+ )
62
+ self.register_buffer(
63
+ "k_norm_weight",
64
+ k_norm_weight.detach().to(torch.bfloat16).contiguous(),
65
+ )
66
+ device = q_norm_weight.device
67
+ if workspace_lane is not None:
68
+ # a caller that declares its outputs call-scoped (no cache,
69
+ # consumed inside the layer) shares one workspace per lane
70
+ # across every same-shape layer — the difference between a
71
+ # 19k-token host binding 52 layers and OOMing on the 53rd
72
+ self.q_out = lease((self.row_capacity, self.q_heads, 128),
73
+ torch.bfloat16, device,
74
+ tag=f"qkr_q|{workspace_lane}")
75
+ self.k_out = lease((self.row_capacity, self.kv_heads, 128),
76
+ torch.bfloat16, device,
77
+ tag=f"qkr_k|{workspace_lane}")
78
+ self.v_out = lease((self.row_capacity, self.kv_heads, 128),
79
+ torch.bfloat16, device,
80
+ tag=f"qkr_v|{workspace_lane}")
81
+ else:
82
+ self.register_buffer(
83
+ "q_out",
84
+ torch.empty(
85
+ self.row_capacity,
86
+ self.q_heads,
87
+ 128,
88
+ device=device,
89
+ dtype=torch.bfloat16,
90
+ ),
91
+ persistent=False,
92
+ )
93
+ self.register_buffer(
94
+ "k_out",
95
+ torch.empty(
96
+ self.row_capacity,
97
+ self.kv_heads,
98
+ 128,
99
+ device=device,
100
+ dtype=torch.bfloat16,
101
+ ),
102
+ persistent=False,
103
+ )
104
+ self.register_buffer(
105
+ "v_out",
106
+ torch.empty_like(self.k_out),
107
+ persistent=False,
108
+ )
109
+ self._frt_arm(
110
+ dtypes={torch.bfloat16},
111
+ device=device,
112
+ k=(self.q_heads + 2 * self.kv_heads) * 128,
113
+ row_capacity=self.row_capacity,
114
+ )
115
+
116
+ def forward(
117
+ self,
118
+ packed_qkv: torch.Tensor,
119
+ cos: torch.Tensor,
120
+ sin: torch.Tensor,
121
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
122
+ admitted = self._frt_admit(packed_qkv, cos, sin)
123
+ if admitted is not PROCEED:
124
+ return admitted
125
+ if packed_qkv.dim() != 3:
126
+ raise GuardRefused(
127
+ "qk_norm_rope: packed QKV must have shape (B, T, width)"
128
+ )
129
+ batch, tokens, width = packed_qkv.shape
130
+ rows = batch * tokens
131
+ expected_width = (self.q_heads + 2 * self.kv_heads) * 128
132
+ if width != expected_width or rows > self.row_capacity:
133
+ raise GuardRefused(
134
+ "qk_norm_rope: packed GQA QKV is outside the bound form"
135
+ )
136
+ expected_freq = (batch, tokens, 128)
137
+ if cos.shape != expected_freq or sin.shape != expected_freq:
138
+ raise GuardRefused(
139
+ f"qk_norm_rope: cos/sin must have shape {expected_freq}"
140
+ )
141
+ if (
142
+ cos.dtype is not torch.bfloat16
143
+ or sin.dtype is not torch.bfloat16
144
+ or cos.device != packed_qkv.device
145
+ or sin.device != packed_qkv.device
146
+ ):
147
+ raise GuardRefused(
148
+ "qk_norm_rope: cos/sin must be BF16 on the QKV device"
149
+ )
150
+ if not packed_qkv.is_contiguous():
151
+ raise GuardRefused("qk_norm_rope: packed QKV must be contiguous")
152
+
153
+ q_out = self.q_out[:rows].view(batch, tokens, self.q_heads, 128)
154
+ k_out = self.k_out[:rows].view(batch, tokens, self.kv_heads, 128)
155
+ v_out = self.v_out[:rows].view(batch, tokens, self.kv_heads, 128)
156
+ return self._fn(
157
+ packed_qkv,
158
+ self.q_norm_weight,
159
+ self.k_norm_weight,
160
+ cos,
161
+ sin,
162
+ self.q_heads,
163
+ self.kv_heads,
164
+ eps=self.eps,
165
+ q_out=q_out,
166
+ k_out=k_out,
167
+ v_out=v_out,
168
+ )
169
+
170
+
171
+ def bind_per_head_gqa_qk_norm_rope(
172
+ q_norm_weight: torch.Tensor,
173
+ k_norm_weight: torch.Tensor,
174
+ *,
175
+ row_capacity: int,
176
+ q_heads: int,
177
+ kv_heads: int,
178
+ head_dim: int,
179
+ eps: float = 1e-6,
180
+ workspace_lane: str | None = None,
181
+ ) -> PerHeadGqaQkNormRope:
182
+ """Bind the capacity-guarded per-head GQA implementation.
183
+
184
+ ``q_norm_weight`` and ``k_norm_weight`` are checkpoint-native vectors
185
+ with layout ``[head_dim]``. The packed activation is laid out
186
+ ``[Q heads..., K heads..., V heads...]``, each head contiguous at
187
+ ``head_dim`` elements; no transposition or interleaving of the norm
188
+ weights is performed.
189
+ """
190
+ return PerHeadGqaQkNormRope(
191
+ q_norm_weight,
192
+ k_norm_weight,
193
+ row_capacity=row_capacity,
194
+ q_heads=q_heads,
195
+ kv_heads=kv_heads,
196
+ head_dim=head_dim,
197
+ eps=eps,
198
+ workspace_lane=workspace_lane,
199
+ )
@@ -0,0 +1,165 @@
1
+ """Packed-QKV implementation of projection-scope Q/K norm plus RoPE.
2
+
3
+ This is the Wan form of :mod:`qk_norm_rope`: Q and K are normalized over
4
+ the complete projection before the output is viewed as heads. It consumes
5
+ the contiguous output of a QKV pack and materializes Q/K/V attention
6
+ workspaces in one Hub kernel. Per-head Cosmos/Qwen/audio normalization is a
7
+ different implementation variant of the same catalog structure.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import torch
13
+
14
+ from .. import hub_kernel
15
+ from ...guard import PROCEED, GuardRefused, GuardedSeam
16
+
17
+
18
+ class ProjectionQkNormRope(GuardedSeam, torch.nn.Module):
19
+ """Fixed-shape packed QKV postprocess for projection-scope RMSNorm."""
20
+
21
+ _frt_can_fallback = False
22
+
23
+ def __init__(
24
+ self,
25
+ q_norm_weight: torch.Tensor,
26
+ k_norm_weight: torch.Tensor,
27
+ *,
28
+ batch: int,
29
+ tokens: int,
30
+ heads: int,
31
+ head_dim: int,
32
+ qkv_bias: torch.Tensor | None = None,
33
+ eps: float = 1e-6,
34
+ ) -> None:
35
+ super().__init__()
36
+ if min(batch, tokens, heads, head_dim) <= 0:
37
+ raise ValueError("qk_norm_rope: shape dimensions must be positive")
38
+ if head_dim % 2:
39
+ raise ValueError("qk_norm_rope: head_dim must be even")
40
+ dim = int(heads) * int(head_dim)
41
+ if q_norm_weight.numel() != dim or k_norm_weight.numel() != dim:
42
+ raise ValueError(
43
+ "qk_norm_rope: projection-scope norm weights must each "
44
+ f"contain heads * head_dim = {dim} elements")
45
+ device = q_norm_weight.device
46
+ if k_norm_weight.device != device:
47
+ raise ValueError("qk_norm_rope: Q/K norm weights must share device")
48
+ if qkv_bias is None:
49
+ qkv_bias = torch.zeros(
50
+ 3 * dim, device=device, dtype=torch.bfloat16)
51
+ if qkv_bias.numel() != 3 * dim or qkv_bias.device != device:
52
+ raise ValueError(
53
+ "qk_norm_rope: qkv_bias must contain 3 * heads * head_dim "
54
+ "elements on the norm-weight device")
55
+
56
+ self.batch = int(batch)
57
+ self.tokens = int(tokens)
58
+ self.heads = int(heads)
59
+ self.head_dim = int(head_dim)
60
+ self.dim = dim
61
+ self.eps = float(eps)
62
+ self._fn = hub_kernel(
63
+ "flashrt/flashrt-qkv-cache-rope",
64
+ ">=1",
65
+ ).qkv_split_bias_norm_rope_v_bf16
66
+ self.register_buffer(
67
+ "q_norm_weight",
68
+ q_norm_weight.detach().reshape(dim).to(torch.bfloat16).contiguous(),
69
+ )
70
+ self.register_buffer(
71
+ "k_norm_weight",
72
+ k_norm_weight.detach().reshape(dim).to(torch.bfloat16).contiguous(),
73
+ )
74
+ self.register_buffer(
75
+ "qkv_bias",
76
+ qkv_bias.detach().reshape(3 * dim).to(torch.bfloat16).contiguous(),
77
+ )
78
+ shape = (self.batch, self.tokens, self.heads, self.head_dim)
79
+ self.register_buffer(
80
+ "q_out",
81
+ torch.empty(shape, device=device, dtype=torch.bfloat16),
82
+ persistent=False,
83
+ )
84
+ self.register_buffer(
85
+ "k_out",
86
+ torch.empty(shape, device=device, dtype=torch.bfloat16),
87
+ persistent=False,
88
+ )
89
+ self.register_buffer(
90
+ "v_out",
91
+ torch.empty(shape, device=device, dtype=torch.bfloat16),
92
+ persistent=False,
93
+ )
94
+ self._frt_arm(
95
+ dtypes={torch.bfloat16},
96
+ device=device,
97
+ k=3 * dim,
98
+ rows=self.batch * self.tokens,
99
+ )
100
+
101
+ def forward(
102
+ self,
103
+ packed_qkv: torch.Tensor,
104
+ cos: torch.Tensor,
105
+ sin: torch.Tensor,
106
+ ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
107
+ admitted = self._frt_admit(packed_qkv, cos, sin)
108
+ if admitted is not PROCEED:
109
+ return admitted
110
+ expected = (self.batch, self.tokens, 3 * self.dim)
111
+ if tuple(packed_qkv.shape) != expected:
112
+ raise GuardRefused(
113
+ f"qk_norm_rope: packed QKV shape {tuple(packed_qkv.shape)} "
114
+ f"(bound for {expected})")
115
+ freq_shape = (self.tokens, self.head_dim // 2)
116
+ if tuple(cos.shape) != freq_shape or tuple(sin.shape) != freq_shape:
117
+ raise GuardRefused(
118
+ f"qk_norm_rope: cos/sin must have shape {freq_shape}")
119
+ if (cos.dtype is not torch.float32 or sin.dtype is not torch.float32
120
+ or cos.device != packed_qkv.device
121
+ or sin.device != packed_qkv.device):
122
+ raise GuardRefused(
123
+ "qk_norm_rope: cos/sin must be float32 on the QKV device")
124
+ if not packed_qkv.is_contiguous():
125
+ raise GuardRefused("qk_norm_rope: packed QKV must be contiguous")
126
+
127
+ return self._fn(
128
+ packed_qkv,
129
+ self.qkv_bias,
130
+ self.q_norm_weight,
131
+ self.k_norm_weight,
132
+ cos,
133
+ sin,
134
+ self.heads,
135
+ self.head_dim,
136
+ rope_seq_len=self.tokens,
137
+ eps=self.eps,
138
+ q_out=self.q_out,
139
+ k_out=self.k_out,
140
+ v_out=self.v_out,
141
+ )
142
+
143
+
144
+ def bind_projection_qk_norm_rope(
145
+ q_norm_weight: torch.Tensor,
146
+ k_norm_weight: torch.Tensor,
147
+ *,
148
+ batch: int,
149
+ tokens: int,
150
+ heads: int,
151
+ head_dim: int,
152
+ qkv_bias: torch.Tensor | None = None,
153
+ eps: float = 1e-6,
154
+ ) -> ProjectionQkNormRope:
155
+ """Bind the fixed-shape projection-scope implementation."""
156
+ return ProjectionQkNormRope(
157
+ q_norm_weight,
158
+ k_norm_weight,
159
+ batch=batch,
160
+ tokens=tokens,
161
+ heads=heads,
162
+ head_dim=head_dim,
163
+ qkv_bias=qkv_bias,
164
+ eps=eps,
165
+ )
@@ -0,0 +1,5 @@
1
+ from .fp8_static import (AttnBlockPacked, PackedLinear, StashReader,
2
+ bind_attn_block, bind_qkv_pack)
3
+
4
+ __all__ = ["AttnBlockPacked", "PackedLinear", "StashReader",
5
+ "bind_attn_block", "bind_qkv_pack"]
@@ -0,0 +1,110 @@
1
+ """Non-quantising BF16 implementation of ``qkv_pack``.
2
+
3
+ Sibling projections that proved shared-input fixed-order dataflow are one
4
+ larger BF16 GEMM. No quantisation is introduced: this is the portable
5
+ structural form used when an end-to-end accuracy gate refuses FP8/FP4.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from collections.abc import Sequence
11
+
12
+ import torch
13
+
14
+ from ...guard import CAST_OK, PROCEED, GuardedSeam
15
+
16
+
17
+ class PackedBf16Linear(GuardedSeam, torch.nn.Module):
18
+ _frt_host_attr = "host_linear"
19
+ _frt_can_fallback = True
20
+
21
+ def __init__(self, mods: Sequence[torch.nn.Linear], rows: int):
22
+ super().__init__()
23
+ if len(mods) < 2:
24
+ raise ValueError("qkv_pack: need at least two siblings")
25
+ kdims = {int(mod.weight.shape[1]) for mod in mods}
26
+ if len(kdims) != 1:
27
+ raise ValueError(f"qkv_pack: sibling K dims differ {kdims}")
28
+ self.splits = tuple(int(mod.weight.shape[0]) for mod in mods)
29
+ weight = torch.cat(
30
+ [mod.weight.detach() for mod in mods], dim=0).contiguous()
31
+ bias = None
32
+ if any(mod.bias is not None for mod in mods):
33
+ bias = torch.cat([
34
+ (mod.bias.detach() if mod.bias is not None else
35
+ torch.zeros(mod.weight.shape[0], device=mod.weight.device,
36
+ dtype=mod.weight.dtype))
37
+ for mod in mods
38
+ ]).contiguous()
39
+ self.register_buffer("packed_weight", weight)
40
+ self.register_buffer("packed_bias", bias)
41
+ self.host_linear = mods[0]
42
+ for index, width in enumerate(self.splits[1:], 1):
43
+ self.register_buffer(
44
+ f"stash{index}", torch.empty(
45
+ rows, width, device=weight.device, dtype=weight.dtype))
46
+ self._frt_arm(dtypes=CAST_OK, device=weight.device,
47
+ k=next(iter(kdims)), row_capacity=rows)
48
+
49
+ def _run(self, flat: torch.Tensor) -> torch.Tensor:
50
+ y = torch.nn.functional.linear(
51
+ flat.to(self.packed_weight.dtype), self.packed_weight,
52
+ self.packed_bias)
53
+ offset = self.splits[0]
54
+ for index, width in enumerate(self.splits[1:], 1):
55
+ getattr(self, f"stash{index}")[:flat.shape[0]].copy_(
56
+ y[:, offset:offset + width])
57
+ offset += width
58
+ return y
59
+
60
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
61
+ admitted = self._frt_admit(x)
62
+ if admitted is not PROCEED:
63
+ return admitted
64
+ y = self._run(x.reshape(-1, x.shape[-1]))
65
+ out = y[:, :self.splits[0]].contiguous()
66
+ return out.reshape(*x.shape[:-1], self.splits[0]).to(x.dtype)
67
+
68
+ def __getattr__(self, name):
69
+ try:
70
+ return super().__getattr__(name)
71
+ except AttributeError:
72
+ return getattr(super().__getattr__("host_linear"), name)
73
+
74
+
75
+ class Bf16StashReader(GuardedSeam, torch.nn.Module):
76
+ _frt_host_attr = "host_linear"
77
+ _frt_can_fallback = True
78
+ _frt_requires_sibling_order = True
79
+
80
+ def __init__(self, original: torch.nn.Linear,
81
+ packed: PackedBf16Linear, index: int):
82
+ super().__init__()
83
+ self.host_linear = original
84
+ self._packed = (packed,)
85
+ self.index = int(index)
86
+ head = packed._frt_guard
87
+ self._frt_arm(dtypes=head.dtypes, device=head.device, k=head.k,
88
+ row_capacity=head.row_capacity)
89
+
90
+ def forward(self, x: torch.Tensor) -> torch.Tensor:
91
+ admitted = self._frt_admit(x)
92
+ if admitted is not PROCEED:
93
+ return admitted
94
+ rows = x.numel() // x.shape[-1]
95
+ out = getattr(self._packed[0], f"stash{self.index}")[:rows]
96
+ return out.reshape(*x.shape[:-1], out.shape[-1]).to(x.dtype)
97
+
98
+ def __getattr__(self, name):
99
+ try:
100
+ return super().__getattr__(name)
101
+ except AttributeError:
102
+ return getattr(super().__getattr__("host_linear"), name)
103
+
104
+
105
+ def bind_qkv_pack(mods: Sequence[torch.nn.Linear], *, rows: int):
106
+ packed = PackedBf16Linear(mods, rows)
107
+ return [packed, *(
108
+ Bf16StashReader(mod, packed, index)
109
+ for index, mod in enumerate(mods[1:], 1)
110
+ )]