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,388 @@
|
|
|
1
|
+
"""adaln_producer — conditioning-driven norm, resolved per step.
|
|
2
|
+
|
|
3
|
+
Diffusion hosts modulate every layer with a projection of the current
|
|
4
|
+
timestep embedding. Two facts make this a structure rather than a plain
|
|
5
|
+
GEMM: the conditioning vector takes one of a small fixed set of values
|
|
6
|
+
over a tick (it is a function of the step), and the norm that consumes
|
|
7
|
+
it can fuse modulation and output quantization into one kernel.
|
|
8
|
+
|
|
9
|
+
This implementation splits those two concerns:
|
|
10
|
+
|
|
11
|
+
- :class:`StepLocator` resolves "which step is this" from the
|
|
12
|
+
conditioning tensor using a few high-separation coordinates — a
|
|
13
|
+
fingerprint — instead of a full-width matmul against every stored
|
|
14
|
+
vector. It is pure tensor work (index_select, squared distance,
|
|
15
|
+
argmax), so it traces under dynamo and captures into a graph without
|
|
16
|
+
host-side state. Sibling producers fed by the same conditioning
|
|
17
|
+
stream share one locator, letting the compiler fold the repeated
|
|
18
|
+
lookups.
|
|
19
|
+
- :class:`AdaLNProducer` replaces the host's adaptive norm: it looks up
|
|
20
|
+
the precomputed style row for the current step and runs the fused
|
|
21
|
+
norm+modulate(+static FP8 quantize) kernel, emitting either BF16 or
|
|
22
|
+
FP8 plus the host's gate. The FP8 form is the upstream half of a
|
|
23
|
+
producer→consumer seam: the shared ``act_scale`` lets a packed
|
|
24
|
+
projection skip its own input quantization.
|
|
25
|
+
|
|
26
|
+
Qualification: the conditioning must actually be step-quantized (more
|
|
27
|
+
distinct vectors than ``max_steps`` means it depends on more than the
|
|
28
|
+
step, and a table would alias different inputs onto one row), and the
|
|
29
|
+
chosen fingerprint coordinates must separate the stored vectors by a
|
|
30
|
+
real margin. Either failure raises ``ValueError`` — the host keeps its
|
|
31
|
+
own producer.
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import torch
|
|
37
|
+
|
|
38
|
+
from .. import hub_kernel
|
|
39
|
+
from ...workspace import lease
|
|
40
|
+
from ...guard import CAST_OK, PROCEED, GuardedSeam
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _dedup(pairs, max_steps, rtol):
|
|
44
|
+
conds, outs = [], []
|
|
45
|
+
for cond, out in pairs:
|
|
46
|
+
c = cond.detach().reshape(-1, cond.shape[-1])
|
|
47
|
+
o = out.detach().reshape(-1, out.shape[-1])
|
|
48
|
+
for row in range(c.shape[0]):
|
|
49
|
+
cr = c[row]
|
|
50
|
+
if any(torch.allclose(cr, seen, rtol=rtol,
|
|
51
|
+
atol=1e-6 * cr.abs().max().item() + 1e-12)
|
|
52
|
+
for seen in conds):
|
|
53
|
+
continue
|
|
54
|
+
conds.append(cr.clone())
|
|
55
|
+
outs.append(o[row].clone())
|
|
56
|
+
if len(conds) > max_steps:
|
|
57
|
+
raise ValueError(
|
|
58
|
+
f"adaln_producer: >{max_steps} distinct conditioning "
|
|
59
|
+
"vectors — not step-quantized, keeping the host path")
|
|
60
|
+
if not conds:
|
|
61
|
+
raise ValueError("adaln_producer: no calibration pairs")
|
|
62
|
+
return torch.stack(conds), torch.stack(outs)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class StepLocator(torch.nn.Module):
|
|
66
|
+
"""Resolve the current step index from the conditioning tensor."""
|
|
67
|
+
|
|
68
|
+
def __init__(self, conds: torch.Tensor, n_dims: int = 8,
|
|
69
|
+
rel_margin: float = 1e-3):
|
|
70
|
+
super().__init__()
|
|
71
|
+
c = conds.float()
|
|
72
|
+
steps = c.shape[0]
|
|
73
|
+
if steps == 1:
|
|
74
|
+
dims = torch.zeros(1, dtype=torch.long, device=c.device)
|
|
75
|
+
else:
|
|
76
|
+
diffs = (c.unsqueeze(0) - c.unsqueeze(1)).abs()
|
|
77
|
+
eye = torch.eye(steps, device=c.device, dtype=torch.bool)
|
|
78
|
+
diffs = diffs.masked_fill(eye.unsqueeze(-1), float("inf"))
|
|
79
|
+
minsep = diffs.amin(dim=(0, 1))
|
|
80
|
+
k = min(n_dims, c.shape[1])
|
|
81
|
+
dims = minsep.topk(k).indices.sort().values
|
|
82
|
+
margin = minsep[dims].min().item()
|
|
83
|
+
if margin < rel_margin * c.abs().mean().item():
|
|
84
|
+
raise ValueError(
|
|
85
|
+
"adaln_producer: conditioning vectors are not "
|
|
86
|
+
"separable on any coordinate subset")
|
|
87
|
+
self.register_buffer("fp_dims", dims)
|
|
88
|
+
self.register_buffer("fp_conds", c.index_select(
|
|
89
|
+
1, dims).contiguous())
|
|
90
|
+
|
|
91
|
+
def forward(self, cond: torch.Tensor) -> torch.Tensor:
|
|
92
|
+
flat = cond.reshape(-1, cond.shape[-1]).float()
|
|
93
|
+
c = flat.index_select(1, self.fp_dims)
|
|
94
|
+
scores = -((c.unsqueeze(1) - self.fp_conds) ** 2).sum(-1)
|
|
95
|
+
return scores.argmax(-1)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class StyleTable(GuardedSeam, torch.nn.Module):
|
|
99
|
+
"""Replace only the conditioning projection with its step table.
|
|
100
|
+
|
|
101
|
+
The narrower of the two bind forms: the host keeps its own norm
|
|
102
|
+
(often already fused by the compiler) and only the per-step style
|
|
103
|
+
projection is memoized. Prefer this wherever the norm itself is not
|
|
104
|
+
being upgraded — measurement decides, and the fused form is worth
|
|
105
|
+
its kernel only when it also serves a downstream seam.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
_frt_host_attr = "host_linear"
|
|
109
|
+
_frt_can_fallback = True
|
|
110
|
+
|
|
111
|
+
def __init__(self, host_proj: torch.nn.Module, styles: torch.Tensor,
|
|
112
|
+
locator: StepLocator):
|
|
113
|
+
super().__init__()
|
|
114
|
+
self.host_linear = host_proj
|
|
115
|
+
self.locator = locator
|
|
116
|
+
self.register_buffer("table", styles.contiguous())
|
|
117
|
+
weight = getattr(host_proj, "weight", None)
|
|
118
|
+
self._frt_arm(dtypes=CAST_OK, device=self.table.device,
|
|
119
|
+
k=None if weight is None else int(weight.shape[1]))
|
|
120
|
+
|
|
121
|
+
def forward(self, cond: torch.Tensor) -> torch.Tensor:
|
|
122
|
+
admitted = self._frt_admit(cond)
|
|
123
|
+
if admitted is not PROCEED:
|
|
124
|
+
return admitted
|
|
125
|
+
out = self.table.index_select(0, self.locator(cond))
|
|
126
|
+
return out.reshape(*cond.shape[:-1], out.shape[-1])
|
|
127
|
+
|
|
128
|
+
def __getattr__(self, name):
|
|
129
|
+
try:
|
|
130
|
+
return super().__getattr__(name)
|
|
131
|
+
except AttributeError:
|
|
132
|
+
return getattr(super().__getattr__("host_linear"), name)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
class AdaLNProducer(GuardedSeam, torch.nn.Module):
|
|
136
|
+
"""Adaptive norm replacement: step lookup + fused norm/quantize.
|
|
137
|
+
|
|
138
|
+
This is the one structure in the library that refuses instead of
|
|
139
|
+
falling back, and the reason is that its output dtype is half of an
|
|
140
|
+
agreement with a downstream seam. On the fp8 entry it hands a packed
|
|
141
|
+
projection FP8 activations under a shared static scale; quietly
|
|
142
|
+
reverting to the host norm would hand that consumer BF16 under an FP8
|
|
143
|
+
scale. Two seams negotiated the form together, so neither can leave it
|
|
144
|
+
alone — a call outside the calibrated form raises here and the caller
|
|
145
|
+
detaches the attachment rather than running half of it.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
_frt_host_attr = "host_norm"
|
|
149
|
+
_frt_can_fallback = False
|
|
150
|
+
|
|
151
|
+
def __init__(self, host_norm: torch.nn.Module,
|
|
152
|
+
styles: torch.Tensor, locator: StepLocator,
|
|
153
|
+
act_scale: torch.Tensor | None, rows: int, dim: int,
|
|
154
|
+
norm: str = "rms", out_format: str | None = None):
|
|
155
|
+
super().__init__()
|
|
156
|
+
self.host_norm = host_norm
|
|
157
|
+
self.locator = locator
|
|
158
|
+
self.norm = norm
|
|
159
|
+
# set by attach_broker when this producer joins a stream-scoped
|
|
160
|
+
# materialisation; alone, it materialises its own style
|
|
161
|
+
self.broker = None
|
|
162
|
+
self.slot = 0
|
|
163
|
+
self.writer = True
|
|
164
|
+
self.register_buffer("styles",
|
|
165
|
+
styles.to(torch.bfloat16).contiguous())
|
|
166
|
+
self.out_fp8 = act_scale is not None
|
|
167
|
+
self.out_nvfp4 = out_format == "nvfp4"
|
|
168
|
+
dev = styles.device
|
|
169
|
+
if self.out_nvfp4:
|
|
170
|
+
# NVFP4 wire emission: the fused kernel norms, modulates and
|
|
171
|
+
# quantizes into preallocated packed/SFA buffers, so a
|
|
172
|
+
# downstream pack takes the scale factors once at bind time
|
|
173
|
+
# (accept_wire) and every call — eager, compiled, captured —
|
|
174
|
+
# reads the same storage. Layer flavour serves the DiT form;
|
|
175
|
+
# the rms flavour rides the fp4-fused-ops twins when a host
|
|
176
|
+
# needs it.
|
|
177
|
+
if norm != "layer":
|
|
178
|
+
raise ValueError(
|
|
179
|
+
"adaln_producer: nvfp4 emission currently serves "
|
|
180
|
+
"the layer form")
|
|
181
|
+
kq = hub_kernel("flashrt/adaptive-layernorm-producers",
|
|
182
|
+
">=1")
|
|
183
|
+
self._fn4 = kq.ada_layer_norm_quant_nvfp4_swizzled_bf16
|
|
184
|
+
probe = torch.zeros(rows, dim, device=dev,
|
|
185
|
+
dtype=torch.bfloat16)
|
|
186
|
+
zero = torch.zeros(dim, device=dev, dtype=torch.bfloat16)
|
|
187
|
+
packed, sfa = self._fn4(probe, zero, zero)
|
|
188
|
+
self.wire_packed = lease(tuple(packed.shape), packed.dtype,
|
|
189
|
+
dev, tag="producer_wire")
|
|
190
|
+
self.wire_sfa = lease(tuple(sfa.shape), sfa.dtype, dev,
|
|
191
|
+
tag="producer_wire_sfa")
|
|
192
|
+
elif norm == "layer":
|
|
193
|
+
# LayerNorm hosts (DiT AdaLayerNorm): style is (scale,
|
|
194
|
+
# shift), no gate, and the fused kernel takes the raw
|
|
195
|
+
# scale — it applies the (1 + scale) itself.
|
|
196
|
+
if not self.out_fp8:
|
|
197
|
+
raise ValueError("adaln_producer: layer norm form "
|
|
198
|
+
"currently requires fp8 output")
|
|
199
|
+
kq = hub_kernel("flashrt/adaptive-layernorm-producers", ">=1")
|
|
200
|
+
self._fn = kq.ada_layer_norm_quant_fp8_bf16
|
|
201
|
+
self.register_buffer("act_scale", act_scale)
|
|
202
|
+
else:
|
|
203
|
+
ka = hub_kernel("flashrt/flashrt-adaptive-norms", ">=1")
|
|
204
|
+
if self.out_fp8:
|
|
205
|
+
self._fn = ka.gate_residual_ada_norm_fp8_static_bf16
|
|
206
|
+
self.register_buffer("act_scale", act_scale)
|
|
207
|
+
else:
|
|
208
|
+
self._fn = ka.ada_rms_norm_style_bf16
|
|
209
|
+
# residual=0 / gate=1 turn the gated-residual kernel into a
|
|
210
|
+
# plain modulated norm; both are preallocated for graph replay.
|
|
211
|
+
# The kernel writes the residual buffer in place, so the zero
|
|
212
|
+
# has to be re-established on every call — a buffer that is
|
|
213
|
+
# merely allocated zeroed drifts silently from the second tick
|
|
214
|
+
# onward, and the drift compounds.
|
|
215
|
+
self.w_ones = lease((dim,), torch.bfloat16, dev,
|
|
216
|
+
tag="producer_ones", fill="ones")
|
|
217
|
+
self.resid = lease((rows, dim), torch.bfloat16, dev,
|
|
218
|
+
tag="producer_resid")
|
|
219
|
+
self.gate_ones = lease((rows, dim), torch.bfloat16, dev,
|
|
220
|
+
tag="producer_ones", fill="ones")
|
|
221
|
+
# the rms form works through the preallocated residual and gate
|
|
222
|
+
# buffers, so its row count is fixed; the layer form's kernel
|
|
223
|
+
# takes scale and shift directly and leaves rows free
|
|
224
|
+
self._frt_arm(dtypes=CAST_OK, device=dev, k=int(dim),
|
|
225
|
+
rows=(None if norm == "layer"
|
|
226
|
+
and not self.out_nvfp4 else int(rows)))
|
|
227
|
+
|
|
228
|
+
# ---- block-facing entries -------------------------------------
|
|
229
|
+
# A caller that owns the whole block (see ``impls.decoder_block``)
|
|
230
|
+
# can do two things a norm-boundary caller cannot: resolve the step
|
|
231
|
+
# once and share it across the producers on the same conditioning
|
|
232
|
+
# stream, and hand this producer the residual that is still pending
|
|
233
|
+
# from the previous sublayer. The kernel already computes
|
|
234
|
+
# ``residual + x * gate`` before it norms — at the norm boundary
|
|
235
|
+
# there is nothing to hand it, so the residual is zeroed and the
|
|
236
|
+
# host pays a separate elementwise add. These entries expose the
|
|
237
|
+
# wider contract without changing the standalone one below.
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def can_absorb(self) -> bool:
|
|
241
|
+
"""Whether this producer can fold a pending gated residual."""
|
|
242
|
+
return self.out_fp8 and self.norm == "rms"
|
|
243
|
+
|
|
244
|
+
@property
|
|
245
|
+
def takes_style_rows(self) -> bool:
|
|
246
|
+
"""Whether this form consumes a materialised ``(rows, W)`` style.
|
|
247
|
+
|
|
248
|
+
Only the rms form does. The layer form's kernel takes scale and
|
|
249
|
+
shift as separate one-row arguments, so there is nothing to
|
|
250
|
+
repeat to the row count and nothing for a broker to share — it
|
|
251
|
+
would attach, never be read, and still be reported as active.
|
|
252
|
+
"""
|
|
253
|
+
return self.norm == "rms"
|
|
254
|
+
|
|
255
|
+
def attach_broker(self, broker, slot: int, *, writer: bool) -> None:
|
|
256
|
+
"""Take styles from a stream-scoped broker (see :mod:`.broker`)."""
|
|
257
|
+
self.broker = broker
|
|
258
|
+
self.slot = slot
|
|
259
|
+
self.writer = writer
|
|
260
|
+
|
|
261
|
+
def resolve(self, cond: torch.Tensor):
|
|
262
|
+
"""Step index for this conditioning — shareable across siblings.
|
|
263
|
+
|
|
264
|
+
With a broker only the stream's writer resolves anything: the
|
|
265
|
+
step is a property of the stream, not of this producer, and the
|
|
266
|
+
readers take their styles from the buffer the writer filled.
|
|
267
|
+
"""
|
|
268
|
+
if self.broker is None:
|
|
269
|
+
return self.locator(cond)
|
|
270
|
+
return self.broker.refresh(cond) if self.writer else None
|
|
271
|
+
|
|
272
|
+
def _style2d(self, idx: torch.Tensor) -> torch.Tensor:
|
|
273
|
+
if self.broker is not None:
|
|
274
|
+
return self.broker.slice(self.slot)
|
|
275
|
+
style = self.styles.index_select(0, idx)
|
|
276
|
+
return style.expand(self.resid.shape[0], -1).contiguous()
|
|
277
|
+
|
|
278
|
+
def produce(self, x: torch.Tensor, idx: torch.Tensor):
|
|
279
|
+
"""Normed output plus the full-width gate, both 2D.
|
|
280
|
+
|
|
281
|
+
The standalone ``forward`` slices the gate down to one row for
|
|
282
|
+
the host's broadcast add; a block caller keeps the full rows so
|
|
283
|
+
it can feed the gate straight back into :meth:`absorb`.
|
|
284
|
+
"""
|
|
285
|
+
# a block reaches this entry instead of ``forward``, and the
|
|
286
|
+
# block's own contract already covered the form; count the call so
|
|
287
|
+
# the ledger does not report a producer that ran every tick as one
|
|
288
|
+
# that never ran
|
|
289
|
+
self._frt_touch()
|
|
290
|
+
style2d = self._style2d(idx)
|
|
291
|
+
x2d = x.reshape(-1, x.shape[-1])
|
|
292
|
+
if self.out_fp8:
|
|
293
|
+
self.resid.zero_() # in-place residual: reset per call
|
|
294
|
+
_, y, gate = self._fn(self.resid, x2d, self.gate_ones,
|
|
295
|
+
self.w_ones, style2d, self.act_scale)
|
|
296
|
+
else:
|
|
297
|
+
y, gate = self._fn(x2d, self.w_ones, style2d)
|
|
298
|
+
return y, gate
|
|
299
|
+
|
|
300
|
+
def absorb(self, residual: torch.Tensor, x: torch.Tensor,
|
|
301
|
+
gate: torch.Tensor, idx: torch.Tensor):
|
|
302
|
+
"""Fold a pending ``residual + x * gate`` into this norm.
|
|
303
|
+
|
|
304
|
+
Returns the updated residual stream, the normed (fp8) output and
|
|
305
|
+
this producer's own gate. The residual is copied into the
|
|
306
|
+
kernel's in-place buffer rather than written through: the caller
|
|
307
|
+
owns its tensor and a hidden mutation of it is exactly the kind
|
|
308
|
+
of silent aliasing that only shows up as drift.
|
|
309
|
+
"""
|
|
310
|
+
if not self.can_absorb:
|
|
311
|
+
raise ValueError(
|
|
312
|
+
"adaln_producer: absorb needs the rms form with fp8 "
|
|
313
|
+
"output — the plain entry has no residual argument")
|
|
314
|
+
self._frt_touch()
|
|
315
|
+
style2d = self._style2d(idx)
|
|
316
|
+
shape = self.resid.shape
|
|
317
|
+
self.resid.copy_(residual.reshape(shape))
|
|
318
|
+
return self._fn(self.resid, x.reshape(shape), gate,
|
|
319
|
+
self.w_ones, style2d, self.act_scale)
|
|
320
|
+
|
|
321
|
+
def forward(self, x: torch.Tensor, cond: torch.Tensor | None = None):
|
|
322
|
+
admitted = self._frt_admit(x, cond)
|
|
323
|
+
if admitted is not PROCEED: # unreachable: this form
|
|
324
|
+
return admitted # refuses rather than reverts
|
|
325
|
+
idx = self.locator(cond)
|
|
326
|
+
if self.out_nvfp4:
|
|
327
|
+
style = self.styles.index_select(0, idx)
|
|
328
|
+
scale, shift = style[0].chunk(2, dim=-1)
|
|
329
|
+
self._fn4(
|
|
330
|
+
x.reshape(-1, x.shape[-1]).to(torch.bfloat16)
|
|
331
|
+
.contiguous(),
|
|
332
|
+
scale.contiguous(), shift.contiguous(),
|
|
333
|
+
packed=self.wire_packed, sf_swizzled=self.wire_sfa)
|
|
334
|
+
return self.wire_packed.reshape(
|
|
335
|
+
*x.shape[:-1], x.shape[-1] // 2)
|
|
336
|
+
if self.norm == "layer":
|
|
337
|
+
style = self.styles.index_select(0, idx)
|
|
338
|
+
scale, shift = style[0].chunk(2, dim=-1)
|
|
339
|
+
y = self._fn(
|
|
340
|
+
x.reshape(-1, x.shape[-1]).to(torch.bfloat16)
|
|
341
|
+
.contiguous(),
|
|
342
|
+
scale.contiguous(), shift.contiguous(),
|
|
343
|
+
self.act_scale)
|
|
344
|
+
return y.reshape(x.shape)
|
|
345
|
+
y, gate = self.produce(x, idx)
|
|
346
|
+
return (y.reshape(x.shape),
|
|
347
|
+
gate[:1].reshape(1, 1, gate.shape[-1]).to(x.dtype))
|
|
348
|
+
|
|
349
|
+
def __getattr__(self, name):
|
|
350
|
+
try:
|
|
351
|
+
return super().__getattr__(name)
|
|
352
|
+
except AttributeError:
|
|
353
|
+
return getattr(super().__getattr__("host_norm"), name)
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
def bind_step_locator(pairs, *, max_steps: int = 64,
|
|
357
|
+
dedup_rtol: float = 1e-5, n_dims: int = 8):
|
|
358
|
+
"""Build a locator and the step table from ``(cond, out)`` pairs."""
|
|
359
|
+
conds, outs = _dedup(pairs, max_steps, dedup_rtol)
|
|
360
|
+
return StepLocator(conds, n_dims=n_dims), outs
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def bind_style_table(host_proj: torch.nn.Module, pairs, *,
|
|
364
|
+
locator: StepLocator | None = None,
|
|
365
|
+
max_steps: int = 64) -> StyleTable:
|
|
366
|
+
"""Bind the table-only form onto the conditioning projection."""
|
|
367
|
+
built, styles = bind_step_locator(pairs, max_steps=max_steps)
|
|
368
|
+
return StyleTable(host_proj, styles, locator or built)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def bind_adaln_producer(host_norm: torch.nn.Module, pairs, *,
|
|
372
|
+
act_scale: torch.Tensor | None = None,
|
|
373
|
+
rows: int, dim: int,
|
|
374
|
+
locator: StepLocator | None = None,
|
|
375
|
+
max_steps: int = 64, norm: str = "rms",
|
|
376
|
+
out_format: str | None = None):
|
|
377
|
+
"""Bind an adaptive-norm producer from real ``(cond, style)`` pairs.
|
|
378
|
+
|
|
379
|
+
``pairs`` come from hooking the host's own conditioning projection
|
|
380
|
+
over at least one full tick, so the stored style rows are exactly
|
|
381
|
+
what the host computed. Pass ``act_scale`` to emit FP8 for a
|
|
382
|
+
downstream packed projection; pass ``locator`` to share the step
|
|
383
|
+
lookup with sibling producers on the same conditioning stream.
|
|
384
|
+
"""
|
|
385
|
+
built, styles = bind_step_locator(pairs, max_steps=max_steps)
|
|
386
|
+
return AdaLNProducer(host_norm, styles, locator or built,
|
|
387
|
+
act_scale, rows, dim, norm=norm,
|
|
388
|
+
out_format=out_format)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"""The adaptive-RMS decoder stack region: a cached-prefix expert tower.
|
|
2
|
+
|
|
3
|
+
An action-expert decoder whose every norm is conditioned (scale, shift,
|
|
4
|
+
gate from one dense projection), attending over a prefix another tower
|
|
5
|
+
left in the cache. One region family identifies the stack shape; its
|
|
6
|
+
fused-chain candidate re-expresses the whole per-layer loop in hub
|
|
7
|
+
primitives with static-FP8 GEMMs.
|
|
8
|
+
"""
|