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,154 @@
1
+ """attention_core — the FlashAttention-4 (CuTe DSL) dense form.
2
+
3
+ The community ``kernels-community/flash-attn4`` package, reused as-is:
4
+ the measured Thor comparison put it at operational parity with the
5
+ native FA4 backend across the GROOT profiles (D48 / D72 / causal GQA
6
+ D128), so no FlashRT twin is published. Numerics-preserving BF16 —
7
+ in the variant family's precision ordering it sits directly after FA2,
8
+ ahead of the FP8 forms.
9
+
10
+ Same stateless dense seam as the FA2 dense form: complete Q/K/V per
11
+ call, host SDPA layout, allowed-ranges packing. GQA is native
12
+ (``pack_gqa``); custom softmax scales pass straight through. The DSL
13
+ JIT compiles per shape at first call, which the bind smoke absorbs at
14
+ bind time — a device or dependency the DSL cannot serve surfaces as a
15
+ bind refusal there, not inside the host's forward.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import torch
21
+
22
+ from .. import hub_kernel
23
+ from ...guard import PROCEED, GuardedSeam
24
+ from .fa2_seqused import _allowed_ranges
25
+
26
+ KERNEL_DEP = {
27
+ "provider": "huggingface_kernels",
28
+ "repo": "kernels-community/flash-attn4",
29
+ "version": ">=0",
30
+ }
31
+
32
+
33
+ class DenseAttentionFa4Cute(GuardedSeam, torch.nn.Module):
34
+ """FA4 (CuTe DSL) replacement for an ordinary dense SDPA call.
35
+
36
+ Inputs and outputs use the host SDPA layout ``[B, H, S, D]``; the
37
+ kernel consumes ``[B, S, H, D]``.
38
+ """
39
+
40
+ def __init__(self, q_shape, kv_shape, dtype: torch.dtype, device,
41
+ allowed_ranges=None):
42
+ super().__init__()
43
+ b, heads, seq_q, head_dim = q_shape
44
+ kb, kv_heads, seq_kv, kv_dim = kv_shape
45
+ if kb != b or kv_dim != head_dim:
46
+ raise ValueError(
47
+ "attention_core fa4_cute: Q and KV batch/head "
48
+ "dimensions differ")
49
+ if heads % kv_heads:
50
+ raise ValueError(
51
+ "attention_core fa4_cute: query heads must be "
52
+ "divisible by KV heads")
53
+ self.q_shape = tuple(q_shape)
54
+ self.kv_shape = tuple(kv_shape)
55
+ self.allowed_ranges = tuple(allowed_ranges or ())
56
+ kern = hub_kernel(KERNEL_DEP["repo"], KERNEL_DEP["version"])
57
+ self._fn = kern.flash_attn_func
58
+ if self.allowed_ranges:
59
+ packed_seq = sum(hi - lo for lo, hi in self.allowed_ranges)
60
+ self.register_buffer("packed_k", torch.empty(
61
+ b, packed_seq, kv_heads, head_dim, device=device,
62
+ dtype=dtype))
63
+ self.register_buffer("packed_v",
64
+ torch.empty_like(self.packed_k))
65
+ self._frt_arm(
66
+ dtypes=(dtype,), device=torch.device(device),
67
+ k=int(head_dim), rows=int(b * heads * seq_q))
68
+
69
+ def forward(self, query, key, value, *, scale=None):
70
+ admitted = self._frt_admit(query)
71
+ if admitted is not PROCEED:
72
+ return admitted
73
+ if tuple(query.shape) != self.q_shape:
74
+ raise ValueError(
75
+ "attention_core fa4_cute: query shape moved from "
76
+ f"{self.q_shape} to {tuple(query.shape)}")
77
+ if tuple(key.shape) != self.kv_shape or value.shape != key.shape:
78
+ raise ValueError(
79
+ "attention_core fa4_cute: key/value shape moved from "
80
+ f"{self.kv_shape} to {tuple(key.shape)}/"
81
+ f"{tuple(value.shape)}")
82
+ q = query.transpose(1, 2)
83
+ k = key.transpose(1, 2)
84
+ v = value.transpose(1, 2)
85
+ if not q.is_contiguous():
86
+ q = q.contiguous()
87
+ if self.allowed_ranges:
88
+ offset = 0
89
+ for lo, hi in self.allowed_ranges:
90
+ length = hi - lo
91
+ self.packed_k[:, offset:offset + length].copy_(k[:, lo:hi])
92
+ self.packed_v[:, offset:offset + length].copy_(v[:, lo:hi])
93
+ offset += length
94
+ k, v = self.packed_k, self.packed_v
95
+ else:
96
+ if not k.is_contiguous():
97
+ k = k.contiguous()
98
+ if not v.is_contiguous():
99
+ v = v.contiguous()
100
+ out = self._fn(q, k, v, softmax_scale=scale, causal=False)
101
+ if isinstance(out, tuple):
102
+ out = out[0]
103
+ return out.transpose(1, 2).to(query.dtype)
104
+
105
+
106
+ def bind_dense_attention(captures):
107
+ """Bind one stateless dense FA4-CuTe core from repeated captures.
108
+
109
+ Same qualification walk as the family's other dense binders. The
110
+ DSL owns its own shape envelope, so there is no head-dim table
111
+ here: the bind smoke below runs the real entry at the captured
112
+ shape, and what the DSL cannot compile is a bind refusal.
113
+ """
114
+ if not captures:
115
+ raise ValueError("attention_core fa4_cute: no captures")
116
+ first = captures[0]
117
+ query, key, value = first["q"], first["key"], first["value"]
118
+ allowed_ranges = _allowed_ranges(first.get("mask"))
119
+ if allowed_ranges is None:
120
+ return None
121
+ expected = (tuple(query.shape), tuple(key.shape), tuple(value.shape),
122
+ query.dtype, key.dtype, value.dtype)
123
+ for capture in captures[1:]:
124
+ got = (
125
+ tuple(capture["q"].shape),
126
+ tuple(capture["key"].shape),
127
+ tuple(capture["value"].shape),
128
+ capture["q"].dtype,
129
+ capture["key"].dtype,
130
+ capture["value"].dtype,
131
+ )
132
+ if got != expected:
133
+ raise ValueError(
134
+ "attention_core fa4_cute: shape, dtype, or mask moved "
135
+ f"within one calibration call: {expected} -> {got}")
136
+ if _allowed_ranges(capture.get("mask")) != allowed_ranges:
137
+ raise ValueError(
138
+ "attention_core fa4_cute: mask pattern moved within "
139
+ "one calibration call")
140
+ if not (query.dtype == key.dtype == value.dtype):
141
+ raise ValueError("attention_core fa4_cute: Q/K/V dtypes differ")
142
+ bound = DenseAttentionFa4Cute(
143
+ query.shape, key.shape, query.dtype, query.device,
144
+ allowed_ranges=allowed_ranges)
145
+ with torch.no_grad():
146
+ probe = bound(torch.zeros_like(query),
147
+ torch.zeros_like(key),
148
+ torch.zeros_like(value))
149
+ if probe.shape != query.shape or not torch.isfinite(probe).all():
150
+ raise ValueError(
151
+ "attention_core fa4_cute: bind smoke produced shape "
152
+ f"{tuple(probe.shape)}, "
153
+ f"finite={bool(torch.isfinite(probe).all())}")
154
+ return bound
@@ -0,0 +1,178 @@
1
+ """attention_core — the FA4 (Blackwell FP8) dense form.
2
+
3
+ The SM100-family twin of :class:`.fa2_seqused.DenseAttention`: the same
4
+ stateless dense seam — complete Q/K/V every call, host SDPA layout,
5
+ allowed-ranges packing — executed by the
6
+ ``flashrt/fp8-cross-attention-blackwell`` kernel: non-causal FP8 GQA
7
+ attention at head_dim 128, BF16 out. The two variants split the
8
+ hardware between them through their packages' own arch declarations
9
+ (this one ships ``10.0a/11.0a`` builds, the FA2 runtime ships none for
10
+ those majors), so selection is the ordinary refusal machinery rather
11
+ than a second table: the family binder tries FA2 first and falls to
12
+ this form where FA2's kernel refuses the device. On the devices this
13
+ form serves, FP8 attention is the production hot path, and the parity
14
+ gates downstream judge its quantization like any other impl's.
15
+
16
+ Activation scales are per-tensor static, calibrated from the same real
17
+ captures the qualification reads (amax over every capture, house
18
+ formula). The kernel bakes the ``1/sqrt(head_dim)`` softmax convention;
19
+ a host that calls with any other scale is refused at that call.
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import torch
25
+
26
+ from .. import hub_kernel
27
+ from ...guard import PROCEED, GuardedSeam
28
+ from .fa2_seqused import _allowed_ranges
29
+
30
+ KERNEL_DEP = {
31
+ "provider": "huggingface_kernels",
32
+ "repo": "flashrt/fp8-cross-attention-blackwell",
33
+ "version": ">=1",
34
+ }
35
+
36
+ _FP8 = torch.float8_e4m3fn
37
+ _FP8_MAX = 448.0
38
+ _HEAD_DIM = 128 # the kernel's contract, exact
39
+
40
+
41
+ class DenseAttentionFa4(GuardedSeam, torch.nn.Module):
42
+ """FA4 replacement for an ordinary dense SDPA call.
43
+
44
+ Inputs and outputs use the host SDPA layout ``[B, H, S, D]``; the
45
+ kernel consumes ``[B, S, H, D]``. Q/K/V are quantized per call with
46
+ the calibrated static scales — elementwise work a compiled or
47
+ packaged graph fuses into its neighbours.
48
+ """
49
+
50
+ def __init__(self, q_shape, kv_shape, dtype: torch.dtype, device,
51
+ scales: tuple[float, float, float],
52
+ allowed_ranges=None):
53
+ super().__init__()
54
+ b, heads, seq_q, head_dim = q_shape
55
+ kb, kv_heads, seq_kv, kv_dim = kv_shape
56
+ if kb != b or kv_dim != head_dim:
57
+ raise ValueError(
58
+ "attention_core fa4: Q and KV batch/head dimensions differ")
59
+ if heads % kv_heads:
60
+ raise ValueError(
61
+ "attention_core fa4: query heads must be divisible by "
62
+ "KV heads")
63
+ if head_dim != _HEAD_DIM:
64
+ raise ValueError(
65
+ f"attention_core fa4: head_dim {head_dim} outside the "
66
+ f"kernel contract ({_HEAD_DIM})")
67
+ self.q_shape = tuple(q_shape)
68
+ self.kv_shape = tuple(kv_shape)
69
+ self.allowed_ranges = tuple(allowed_ranges or ())
70
+ self._kfa = hub_kernel(KERNEL_DEP["repo"], KERNEL_DEP["version"])
71
+ self._fn = self._kfa.fp8_gqa_cross_attention_bf16
72
+ self._qs, self._ks, self._vs = (float(s) for s in scales)
73
+ if self.allowed_ranges:
74
+ packed_seq = sum(hi - lo for lo, hi in self.allowed_ranges)
75
+ self.register_buffer("packed_k", torch.empty(
76
+ b, packed_seq, kv_heads, head_dim, device=device,
77
+ dtype=dtype))
78
+ self.register_buffer("packed_v",
79
+ torch.empty_like(self.packed_k))
80
+ self._frt_arm(
81
+ dtypes=(dtype,), device=torch.device(device),
82
+ k=int(head_dim), rows=int(b * heads * seq_q))
83
+
84
+ def _quant(self, t: torch.Tensor, scale: float) -> torch.Tensor:
85
+ return (t.float() / scale).clamp(-_FP8_MAX, _FP8_MAX).to(_FP8)
86
+
87
+ def forward(self, query, key, value, *, scale=None):
88
+ admitted = self._frt_admit(query)
89
+ if admitted is not PROCEED:
90
+ return admitted
91
+ if tuple(query.shape) != self.q_shape:
92
+ raise ValueError(
93
+ "attention_core fa4: query shape moved from "
94
+ f"{self.q_shape} to {tuple(query.shape)}")
95
+ if tuple(key.shape) != self.kv_shape or value.shape != key.shape:
96
+ raise ValueError(
97
+ "attention_core fa4: key/value shape moved from "
98
+ f"{self.kv_shape} to {tuple(key.shape)}/"
99
+ f"{tuple(value.shape)}")
100
+ if scale is not None and abs(
101
+ float(scale) - self.q_shape[-1] ** -0.5) > 1e-9:
102
+ raise ValueError(
103
+ "attention_core fa4: the kernel bakes the default "
104
+ "softmax scale; this host passes another")
105
+ q = query.transpose(1, 2)
106
+ k = key.transpose(1, 2)
107
+ v = value.transpose(1, 2)
108
+ if self.allowed_ranges:
109
+ offset = 0
110
+ for lo, hi in self.allowed_ranges:
111
+ length = hi - lo
112
+ self.packed_k[:, offset:offset + length].copy_(k[:, lo:hi])
113
+ self.packed_v[:, offset:offset + length].copy_(v[:, lo:hi])
114
+ offset += length
115
+ k, v = self.packed_k, self.packed_v
116
+ out = self._fn(
117
+ self._quant(q, self._qs).contiguous(),
118
+ self._quant(k, self._ks).contiguous(),
119
+ self._quant(v, self._vs).contiguous(),
120
+ query_scale=self._qs, key_scale=self._ks,
121
+ value_scale=self._vs)
122
+ return out.transpose(1, 2).to(query.dtype)
123
+
124
+
125
+ def _amax_over(captures, key) -> float:
126
+ amax = 0.0
127
+ for cap in captures:
128
+ amax = max(amax, float(cap[key].detach().float().abs().max()))
129
+ return max(amax / _FP8_MAX, 1e-6)
130
+
131
+
132
+ def bind_dense_attention(captures):
133
+ """Bind one stateless dense FA4 core from repeated host captures.
134
+
135
+ Same qualification walk as the FA2 dense binder — stable shapes and
136
+ dtypes across captures, a mask expressible as contiguous allowed
137
+ ranges — plus the kernel's own contract (head_dim 128) and the
138
+ per-tensor scale calibration this form adds. Returns ``None`` when
139
+ the shape qualification fails so the caller can keep its path;
140
+ raises when the kernel package refuses the device, so the family
141
+ binder can record it and move on.
142
+ """
143
+ if not captures:
144
+ raise ValueError("attention_core fa4: no captures")
145
+ first = captures[0]
146
+ query, key, value = first["q"], first["key"], first["value"]
147
+ if query.shape[-1] != _HEAD_DIM:
148
+ return None
149
+ allowed_ranges = _allowed_ranges(first.get("mask"))
150
+ if allowed_ranges is None:
151
+ return None
152
+ expected = (tuple(query.shape), tuple(key.shape), tuple(value.shape),
153
+ query.dtype, key.dtype, value.dtype)
154
+ for capture in captures[1:]:
155
+ got = (
156
+ tuple(capture["q"].shape),
157
+ tuple(capture["key"].shape),
158
+ tuple(capture["value"].shape),
159
+ capture["q"].dtype,
160
+ capture["key"].dtype,
161
+ capture["value"].dtype,
162
+ )
163
+ if got != expected:
164
+ raise ValueError(
165
+ "attention_core fa4: shape, dtype, or mask moved within "
166
+ f"one calibration call: {expected} -> {got}")
167
+ if _allowed_ranges(capture.get("mask")) != allowed_ranges:
168
+ raise ValueError(
169
+ "attention_core fa4: mask pattern moved within one "
170
+ "calibration call")
171
+ if not (query.dtype == key.dtype == value.dtype):
172
+ raise ValueError("attention_core fa4: Q/K/V dtypes differ")
173
+ scales = (_amax_over(captures, "q"),
174
+ _amax_over(captures, "key"),
175
+ _amax_over(captures, "value"))
176
+ return DenseAttentionFa4(
177
+ query.shape, key.shape, query.dtype, query.device,
178
+ scales, allowed_ranges=allowed_ranges)
@@ -0,0 +1,158 @@
1
+ """attention_core — the allocation-free masked-MHA dense form.
2
+
3
+ The ``flashrt/masked-mha-runtime`` package: a padded-length MHA whose
4
+ softmax reads and writes only the valid key columns, with every buffer
5
+ caller-owned — the form that removed the per-call logits pre-fill
6
+ sweep and, with it, the graph-replay nondeterminism of reading
7
+ uninitialized padding (replays are bitwise). BF16/FP16 at the host's
8
+ own precision; in the family's ordering it follows the FA4 forms and
9
+ precedes the FP8 one.
10
+
11
+ The kernel's layout is batch-free ``(S, H, D)`` per tensor, so this
12
+ form serves the batch-of-one sites the packed dense seam sees
13
+ everywhere in the VLA hosts; a batched site stays with the other
14
+ variants. Masks reduce to contiguous allowed ranges exactly as in the
15
+ FA2 dense form — packed KV rows are the valid length, which is the
16
+ whole masking contract.
17
+ """
18
+
19
+ from __future__ import annotations
20
+
21
+ import torch
22
+
23
+ from .. import hub_kernel
24
+ from ...guard import PROCEED, GuardedSeam
25
+ from .fa2_seqused import _allowed_ranges
26
+
27
+ KERNEL_DEP = {
28
+ "provider": "huggingface_kernels",
29
+ "repo": "flashrt/masked-mha-runtime",
30
+ "version": ">=1",
31
+ }
32
+
33
+
34
+ class DenseAttentionMaskedMha(GuardedSeam, torch.nn.Module):
35
+ """Masked-MHA replacement for a batch-of-one dense SDPA call.
36
+
37
+ Host layout ``[1, H, S, D]`` in and out; the kernel consumes
38
+ ``(S, H, D)`` with a caller-owned padded logits scratch and output,
39
+ both allocated once here — the hot path allocates nothing.
40
+ """
41
+
42
+ def __init__(self, q_shape, kv_shape, dtype: torch.dtype, device,
43
+ allowed_ranges=None):
44
+ super().__init__()
45
+ b, heads, seq_q, head_dim = q_shape
46
+ kb, kv_heads, seq_kv, kv_dim = kv_shape
47
+ if b != 1 or kb != 1:
48
+ raise ValueError(
49
+ "attention_core masked_mha: the kernel layout is "
50
+ "batch-free; only batch-of-one sites qualify")
51
+ if kv_dim != head_dim:
52
+ raise ValueError(
53
+ "attention_core masked_mha: Q and KV head dims differ")
54
+ if heads != kv_heads:
55
+ raise ValueError(
56
+ "attention_core masked_mha: MHA form; GQA sites take "
57
+ "the FA4 variants")
58
+ self.q_shape = tuple(q_shape)
59
+ self.kv_shape = tuple(kv_shape)
60
+ self.allowed_ranges = tuple(allowed_ranges or ())
61
+ kern = hub_kernel(KERNEL_DEP["repo"], KERNEL_DEP["version"])
62
+ self._fn = kern.forward_static
63
+ packed_seq = (sum(hi - lo for lo, hi in self.allowed_ranges)
64
+ if self.allowed_ranges else seq_kv)
65
+ self._packed_seq = packed_seq
66
+ if self.allowed_ranges:
67
+ self.register_buffer("packed_k", torch.empty(
68
+ packed_seq, kv_heads, head_dim, device=device,
69
+ dtype=dtype))
70
+ self.register_buffer("packed_v",
71
+ torch.empty_like(self.packed_k))
72
+ stride = (packed_seq + 7) // 8 * 8
73
+ self.register_buffer("_logits", torch.empty(
74
+ heads, seq_q, stride, device=device, dtype=dtype))
75
+ self.register_buffer("_out", torch.empty(
76
+ seq_q, heads, head_dim, device=device, dtype=dtype))
77
+ self._frt_arm(
78
+ dtypes=(dtype,), device=torch.device(device),
79
+ k=int(head_dim), rows=int(heads * seq_q))
80
+
81
+ def forward(self, query, key, value, *, scale=None):
82
+ admitted = self._frt_admit(query)
83
+ if admitted is not PROCEED:
84
+ return admitted
85
+ if tuple(query.shape) != self.q_shape:
86
+ raise ValueError(
87
+ "attention_core masked_mha: query shape moved from "
88
+ f"{self.q_shape} to {tuple(query.shape)}")
89
+ if tuple(key.shape) != self.kv_shape or value.shape != key.shape:
90
+ raise ValueError(
91
+ "attention_core masked_mha: key/value shape moved from "
92
+ f"{self.kv_shape} to {tuple(key.shape)}/"
93
+ f"{tuple(value.shape)}")
94
+ q = query[0].transpose(0, 1).contiguous() # (S_q, H, D)
95
+ k = key[0].transpose(0, 1)
96
+ v = value[0].transpose(0, 1)
97
+ if self.allowed_ranges:
98
+ offset = 0
99
+ for lo, hi in self.allowed_ranges:
100
+ length = hi - lo
101
+ self.packed_k[offset:offset + length].copy_(k[lo:hi])
102
+ self.packed_v[offset:offset + length].copy_(v[lo:hi])
103
+ offset += length
104
+ k, v = self.packed_k, self.packed_v
105
+ else:
106
+ k = k.contiguous()
107
+ v = v.contiguous()
108
+ out = self._fn(q, k, v, logits=self._logits, out=self._out,
109
+ scale=scale)
110
+ return out.transpose(0, 1).unsqueeze(0).to(query.dtype)
111
+
112
+
113
+ def bind_dense_attention(captures):
114
+ """Bind one masked-MHA core from repeated host captures."""
115
+ if not captures:
116
+ raise ValueError("attention_core masked_mha: no captures")
117
+ first = captures[0]
118
+ query, key, value = first["q"], first["key"], first["value"]
119
+ if query.shape[0] != 1 or query.shape[1] != key.shape[1]:
120
+ return None
121
+ allowed_ranges = _allowed_ranges(first.get("mask"))
122
+ if allowed_ranges is None:
123
+ return None
124
+ expected = (tuple(query.shape), tuple(key.shape), tuple(value.shape),
125
+ query.dtype, key.dtype, value.dtype)
126
+ for capture in captures[1:]:
127
+ got = (
128
+ tuple(capture["q"].shape),
129
+ tuple(capture["key"].shape),
130
+ tuple(capture["value"].shape),
131
+ capture["q"].dtype,
132
+ capture["key"].dtype,
133
+ capture["value"].dtype,
134
+ )
135
+ if got != expected:
136
+ raise ValueError(
137
+ "attention_core masked_mha: shape, dtype, or mask "
138
+ f"moved within one calibration call: {expected} -> {got}")
139
+ if _allowed_ranges(capture.get("mask")) != allowed_ranges:
140
+ raise ValueError(
141
+ "attention_core masked_mha: mask pattern moved within "
142
+ "one calibration call")
143
+ if not (query.dtype == key.dtype == value.dtype):
144
+ raise ValueError(
145
+ "attention_core masked_mha: Q/K/V dtypes differ")
146
+ bound = DenseAttentionMaskedMha(
147
+ query.shape, key.shape, query.dtype, query.device,
148
+ allowed_ranges=allowed_ranges)
149
+ with torch.no_grad():
150
+ probe = bound(torch.zeros_like(query),
151
+ torch.zeros_like(key),
152
+ torch.zeros_like(value))
153
+ if probe.shape != query.shape or not torch.isfinite(probe).all():
154
+ raise ValueError(
155
+ "attention_core masked_mha: bind smoke produced shape "
156
+ f"{tuple(probe.shape)}, "
157
+ f"finite={bool(torch.isfinite(probe).all())}")
158
+ return bound