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,219 @@
1
+ """Structure implementations.
2
+
3
+ ``hub_kernel`` is the shared, process-wide hub loader: two impls that
4
+ depend on the same kernel repo must share one loaded module — a second
5
+ ``kernels.get_kernel`` import of the same repo re-registers its fake
6
+ ops and torch.library raises.
7
+
8
+ The loader also checks the package's own hardware declaration. A Hub
9
+ kernel package ships ``metadata.json`` with the CUDA archs it was built
10
+ for; that file is maintained on the kernels side and is the single
11
+ source of truth for hardware support — this layer reads it, it does not
12
+ keep a second table. A device outside the declared archs gets a clean
13
+ refusal here, before the kernel produces an unrelated-looking runtime
14
+ error; the refusal is caught by the binder and recorded in the plan
15
+ notes like any other. A package without metadata is loaded as before —
16
+ absence of a declaration is not evidence of incompatibility.
17
+ """
18
+
19
+ import os
20
+ import json
21
+ import pathlib
22
+ import re
23
+ from functools import lru_cache
24
+
25
+
26
+ def _device_cc() -> tuple[int, int] | None:
27
+ """Compute capability of the current CUDA device, or ``None``."""
28
+ import torch
29
+
30
+ if not torch.cuda.is_available():
31
+ return None
32
+ return torch.cuda.get_device_capability()
33
+
34
+
35
+ def _declared_archs(module) -> list[str] | None:
36
+ """The package's own ``backend.archs`` declaration, if it ships one."""
37
+ try:
38
+ meta = pathlib.Path(module.__file__).parent / "metadata.json"
39
+ if not meta.is_file():
40
+ return None
41
+ archs = json.loads(meta.read_text()).get("backend", {}).get("archs")
42
+ return list(archs) if archs else None
43
+ except (OSError, ValueError, AttributeError):
44
+ return None
45
+
46
+
47
+ _CUDA_ARCH = re.compile(
48
+ r"^(?P<major>[0-9]+)\.(?P<minor>[0-9]+)"
49
+ r"(?P<specific>a)?(?P<ptx>\+PTX)?$")
50
+
51
+
52
+ def _cuda_arch_supports_device(
53
+ arch: str, device_cc: tuple[int, int]) -> bool:
54
+ """Whether one Hub CUDA arch declaration can execute on ``device_cc``.
55
+
56
+ Plain cubins are binary-compatible with later minor capabilities in
57
+ the same major family. Generic PTX is forward-compatible with any
58
+ greater compute capability. Architecture-specific ``a`` targets are
59
+ exact-only, including when they also carry PTX.
60
+ """
61
+ match = _CUDA_ARCH.fullmatch(arch)
62
+ if match is None:
63
+ return False
64
+ target = (int(match["major"]), int(match["minor"]))
65
+ if match["specific"]:
66
+ return device_cc == target
67
+ if match["ptx"]:
68
+ return device_cc >= target
69
+ return device_cc[0] == target[0] and device_cc[1] >= target[1]
70
+
71
+
72
+ class KernelUnavailable(ValueError):
73
+ """This host cannot supply this kernel package.
74
+
75
+ One exception type for every way the distribution layer can come up
76
+ empty — the repository is not published, not staged in an offline
77
+ cache, has no build variant for the host, or will not import here.
78
+ They differ only in what an operator has to go fix, which is what
79
+ the message carries; to a caller they are the same event, and the
80
+ same one the arch declaration produces: *not here*.
81
+
82
+ A ``ValueError`` subclass on purpose. Every layer that already
83
+ treats a refusal as an outcome to record rather than an error to
84
+ propagate — the variant families, the recipe engine's per-lever
85
+ build — catches ``ValueError``, and an absent package must not be
86
+ the one refusal that aborts a run instead of being written down.
87
+ """
88
+
89
+
90
+ #: every package this process could not supply, in the order it was
91
+ #: asked for. Skipping an unavailable package keeps a run moving, which
92
+ #: is the right behaviour — but a package that is *broken* here and one
93
+ #: that was simply never shipped here both come out as "skipped", and
94
+ #: only the first is somebody's bug. So nothing is inferred and nothing
95
+ #: is dropped: the original failure is recorded verbatim and travels
96
+ #: into the receipt, where a reader can tell the two apart.
97
+ _UNAVAILABLE: list[dict] = []
98
+
99
+
100
+ def unavailable_report() -> list[dict]:
101
+ """Packages this process asked for and could not get."""
102
+ return [dict(row) for row in _UNAVAILABLE]
103
+
104
+
105
+ def clear_unavailable_report() -> None:
106
+ _UNAVAILABLE.clear()
107
+
108
+
109
+ def _record_unavailable(repo: str, version: str, cause: BaseException):
110
+ row = {
111
+ "repo": repo,
112
+ "version": version,
113
+ "error": type(cause).__name__,
114
+ "detail": str(cause)[:400],
115
+ }
116
+ if not any(r["repo"] == repo and r["error"] == row["error"]
117
+ for r in _UNAVAILABLE):
118
+ _UNAVAILABLE.append(row)
119
+ return row
120
+
121
+
122
+ def _check_arch(repo: str, module) -> None:
123
+ archs = _declared_archs(module)
124
+ if archs is None:
125
+ return
126
+ cc = _device_cc()
127
+ if cc is None:
128
+ # no CUDA device: binding fails later at weight transfer anyway;
129
+ # the arch check has nothing truthful to say here
130
+ return
131
+ want = f"{cc[0]}.{cc[1]}"
132
+ if any(_cuda_arch_supports_device(a, cc) for a in archs):
133
+ return
134
+ refusal = KernelUnavailable(
135
+ f"refused: kernel package {repo!r} declares archs {archs}, "
136
+ f"device is sm {want}")
137
+ _record_unavailable(repo, "declared-archs", refusal)
138
+ raise refusal
139
+
140
+
141
+ #: modules cached independently of the arch check: ``get_kernel`` must
142
+ #: run at most once per repo even when the check refuses (a second load
143
+ #: re-registers the package's fake ops and torch.library raises — the
144
+ #: refusal path must not manufacture that error on retry)
145
+ _LOADED: dict[tuple[str, str], object] = {}
146
+
147
+
148
+ @lru_cache(maxsize=None)
149
+ def hub_kernel(repo: str, version: str):
150
+ try:
151
+ from kernels import get_kernel
152
+ except ImportError as absent:
153
+ # The client itself is missing or shadowed. This is the same
154
+ # event as every other way a package fails to arrive - "not
155
+ # here" - and it must travel as one, or the layers that catch a
156
+ # refusal to record it and keep going will instead abort on the
157
+ # one unavailability nobody declared. It is also the state a
158
+ # fresh ``pip install flash-rt`` is in, since the client is not
159
+ # a hard dependency, so the message says how to leave it.
160
+ _record_unavailable(repo, version, absent)
161
+ raise KernelUnavailable(
162
+ f"kernel package {repo!r} ({version}) is unavailable on this "
163
+ f"host: the kernel client is not installed "
164
+ f"({type(absent).__name__}: {absent}).\n"
165
+ f" pip install kernels\n"
166
+ f"or install this distribution with its hub extra:\n"
167
+ f" pip install 'flash-rt[hub]'") from absent
168
+
169
+ key = (repo, version)
170
+ if key not in _LOADED:
171
+ # author pin for artifact bisection: an exact hub revision
172
+ # outranks version resolution for this repo only. A perf or
173
+ # correctness drift that arrives with a rebuilt artifact is
174
+ # isolated by flipping one env var, not by editing caches.
175
+ rev = os.environ.get(
176
+ "FRT_KERNEL_REV_" + re.sub(r"[^A-Za-z0-9]", "_",
177
+ repo).upper())
178
+ try:
179
+ import inspect as _ins
180
+ _kw = {}
181
+ if "trust_remote_code" in _ins.signature(
182
+ get_kernel).parameters:
183
+ # the trust gate arrived with newer kernels; our own
184
+ # first-party artifacts are the explicit trust set
185
+ _kw["trust_remote_code"] = True
186
+ try:
187
+ try:
188
+ _LOADED[key] = (get_kernel(repo, revision=rev,
189
+ **_kw)
190
+ if rev
191
+ else get_kernel(repo,
192
+ version=version,
193
+ **_kw))
194
+ except ValueError as ve:
195
+ # newer kernels resolve an exact integer version
196
+ # where older ones accepted a range string; the
197
+ # range's floor is the same request in both bands
198
+ m = re.match(r"^\s*>=\s*v?(\d+)", str(version))
199
+ if not (m and "available versions" in str(ve)):
200
+ raise
201
+ _LOADED[key] = get_kernel(
202
+ repo, version=int(m.group(1)), **_kw)
203
+ except TypeError:
204
+ # kernels<0.13 — the band transformers pins — has no
205
+ # semver resolution kwarg; the default revision is
206
+ # exactly what that library resolved before semver
207
+ # tags existed. Widest-band compat: 0.12 through 0.16
208
+ # serve the same call site.
209
+ _LOADED[key] = (get_kernel(repo, revision=rev) if rev
210
+ else get_kernel(repo)) # pre-semver band
211
+ except (OSError, RuntimeError, ValueError) as unavailable:
212
+ _record_unavailable(repo, version, unavailable)
213
+ raise KernelUnavailable(
214
+ f"kernel package {repo!r} ({version}) is unavailable on "
215
+ f"this host: {type(unavailable).__name__}: "
216
+ f"{unavailable}") from unavailable
217
+ module = _LOADED[key]
218
+ _check_arch(repo, module)
219
+ return module
@@ -0,0 +1,8 @@
1
+ from .broker import StyleBroker, bind_style_broker
2
+ from .fused import (AdaLNProducer, StepLocator, StyleTable,
3
+ bind_adaln_producer, bind_step_locator,
4
+ bind_style_table)
5
+
6
+ __all__ = ["AdaLNProducer", "StepLocator", "StyleBroker", "StyleTable",
7
+ "bind_adaln_producer", "bind_step_locator",
8
+ "bind_style_broker", "bind_style_table"]
@@ -0,0 +1,116 @@
1
+ """Step-scoped style materialisation shared across one conditioning stream.
2
+
3
+ The adaptive-norm kernel takes ``style`` as a contiguous ``(rows, W)``
4
+ tensor, so each producer expands its one style row to the row count and
5
+ materialises it. That is correct and, done per producer, wasteful in a
6
+ way no single producer can see: every producer on one conditioning
7
+ stream resolves the *same* step, and the whole stream's styles are
8
+ therefore fixed for the duration of a step. Measured on pi05, the
9
+ per-producer form spends 0.68 ms in 720 launches moving 221 MB — a
10
+ volume worth about 0.15 ms at this card's bandwidth, so the cost is the
11
+ launches, not the bytes.
12
+
13
+ The broker turns per-call work into per-step work. One index lookup and
14
+ one copy fill a ``(P, rows, W)`` buffer for every producer in the
15
+ stream; each producer then reads ``buf[slot]``, which is a contiguous
16
+ view, so the kernel's contract is untouched. This is the
17
+ ``cadence_static`` idea applied inside a structure rather than around a
18
+ module: hold what changes at step cadence, not at call cadence.
19
+
20
+ Two things make it safe rather than merely fast:
21
+
22
+ - the writer is the producer the host actually calls first, taken from
23
+ the observed call order of the calibration pass, not from the order
24
+ the modules happen to sit in the tree;
25
+ - the readers depend on the writer through the buffer itself, the same
26
+ ordering the packed-projection stash relies on inside compiled and
27
+ captured graphs.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import torch
33
+ from torch import nn
34
+
35
+
36
+ @torch.library.custom_op("flash_rt_structures::style_broadcast",
37
+ mutates_args={"out"})
38
+ def _style_broadcast(src: torch.Tensor, out: torch.Tensor) -> None:
39
+ """Fill ``out`` (P, rows, W) by repeating each row of ``src`` (P, W).
40
+
41
+ Opaque on purpose. Written as plain tensor work, the compiler sees a
42
+ buffer whose only consumers are slices of it and inlines the fill
43
+ into each consumer — a correct buffer elimination that happens to
44
+ undo the sharing this broker exists for. Measured: the fill stayed
45
+ at 720 launches, they merely moved bucket. Behind an opaque op the
46
+ fill happens once and the readers read.
47
+ """
48
+ out.copy_(src.unsqueeze(1).expand_as(out))
49
+
50
+
51
+ @_style_broadcast.register_fake
52
+ def _style_broadcast_fake(src: torch.Tensor, out: torch.Tensor) -> None:
53
+ return None
54
+
55
+
56
+ class StyleBroker(nn.Module):
57
+ """One conditioning stream's styles, materialised once per step."""
58
+
59
+ def __init__(self, locator, tables, rows: int):
60
+ super().__init__()
61
+ widths = {t.shape[-1] for t in tables}
62
+ if len(widths) != 1:
63
+ raise ValueError(
64
+ f"style_broker: producers differ in style width {widths}")
65
+ steps = {t.shape[0] for t in tables}
66
+ if len(steps) != 1:
67
+ raise ValueError(
68
+ f"style_broker: producers differ in step count {steps}")
69
+ self.locator = locator
70
+ self.slots = len(tables)
71
+ self.rows = rows
72
+ # [steps, slots, W]: one index_select picks the whole stream's
73
+ # styles for the current step
74
+ self.register_buffer("stack", torch.stack(
75
+ [t.to(torch.bfloat16) for t in tables], dim=1).contiguous())
76
+ self.register_buffer("buf", torch.empty(
77
+ self.slots, rows, widths.pop(), device=self.stack.device,
78
+ dtype=torch.bfloat16))
79
+
80
+ def refresh(self, cond: torch.Tensor) -> torch.Tensor:
81
+ """Resolve the step and materialise every slot. Writer only."""
82
+ idx = self.locator(cond)
83
+ sel = self.stack.index_select(0, idx).reshape(self.slots, -1)
84
+ torch.ops.flash_rt_structures.style_broadcast(sel, self.buf)
85
+ return idx
86
+
87
+ def slice(self, slot: int) -> torch.Tensor:
88
+ """This producer's style for the current step, contiguous."""
89
+ return self.buf[slot]
90
+
91
+
92
+ def bind_style_broker(producers, rows: int) -> StyleBroker | None:
93
+ """Attach one broker to producers already bound on the same stream.
94
+
95
+ ``producers`` must be in the order the host calls them; the first is
96
+ the writer. Returns ``None`` when there is nothing to share (a single
97
+ producer pays the same either way), leaving every producer on its own
98
+ materialisation — this composes onto bound producers and can only
99
+ remove work, never add a requirement.
100
+ """
101
+ # a form that never reads a materialised style has nothing to share:
102
+ # attaching anyway would hold a buffer nobody reads and still report
103
+ # a broker as active. Found on the second host, where every producer
104
+ # is the layer form.
105
+ producers = [p for p in producers if p.takes_style_rows]
106
+ if len(producers) < 2:
107
+ return None
108
+ locator = producers[0].locator
109
+ if any(p.locator is not locator for p in producers):
110
+ raise ValueError(
111
+ "style_broker: producers do not share a step locator, so they "
112
+ "are not one conditioning stream")
113
+ broker = StyleBroker(locator, [p.styles for p in producers], rows)
114
+ for slot, producer in enumerate(producers):
115
+ producer.attach_broker(broker, slot, writer=(slot == 0))
116
+ return broker