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,43 @@
1
+ """Beta: the join between two bound structures, declared rather than implied.
2
+
3
+ Every structure in the catalog declares its own boundary. What none of
4
+ them declares is the *join* — what has to agree between a structure and
5
+ the one it feeds. Today those agreements exist only inside each impl's
6
+ code, so they are never checked, never negotiated, and never optimised
7
+ across. Every join therefore defaults to "materialise a fresh tensor in
8
+ the host's convention", which is what a hand-written runtime never does:
9
+ there the author holds the whole dataflow and picks, per join, the same
10
+ buffer, the same layout, no re-quantisation.
11
+
12
+ The vocabulary here is derived, not designed. Each attribute exists
13
+ because one specific join cost real time or gave a wrong verdict, and
14
+ each carries that incident in its docstring. The evidence for the whole
15
+ idea is one controlled comparison: ``dtype`` is the only join attribute
16
+ that was ever declared and negotiated, and it is the only one that
17
+ stopped causing failures. The other five were never declared and every
18
+ one of them bit.
19
+
20
+ **This is beta.** Three rules keep it honest, and they double as the
21
+ conditions for deleting it:
22
+
23
+ 1. *Descriptive before prescriptive.* It must correctly describe the
24
+ joins that already work before it is allowed to change any behaviour.
25
+ A vocabulary that cannot express what the stack already does is the
26
+ wrong vocabulary.
27
+ 2. *No attribute without a negotiator.* An attribute that no join
28
+ negotiates and no measurement depends on gets deleted, not
29
+ documented.
30
+ 3. *Off by default.* Nothing in the main path consumes this unless asked
31
+ to, and an attribute a port does not declare is simply not negotiated
32
+ — so coverage can only grow.
33
+
34
+ The deprecation signal is stated up front: if each new structure needs a
35
+ new attribute, this is not a vocabulary, it is a junk drawer. The
36
+ attribute set is supposed to converge as structures are added. If it
37
+ does not, delete this package.
38
+ """
39
+
40
+ from .negotiate import JoinRefused, negotiate
41
+ from .ports import ATTRIBUTES, Join, Port
42
+
43
+ __all__ = ["ATTRIBUTES", "Join", "JoinRefused", "Port", "negotiate"]
@@ -0,0 +1,94 @@
1
+ """Check the declarations against the impls they claim to describe.
2
+
3
+ A spec file that nobody checks drifts away from the code and becomes
4
+ worse than no spec, because it is then confidently wrong. Writing these
5
+ ports down already corrected one of my own beliefs about what
6
+ ``attention_core`` can emit; that only stays true if something keeps
7
+ checking.
8
+
9
+ This is deliberately shallow. It asks the impls what they support and
10
+ compares against what the ports claim — it does not run a model. A
11
+ declaration that cannot be checked this way is a declaration that should
12
+ not be made.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ from typing import Sequence
18
+
19
+ from .joins import DECLARED
20
+ from .ports import Port
21
+
22
+
23
+ def _offers(port: Port, attr: str) -> tuple[str, ...]:
24
+ return tuple(port.offers.get(attr, ()))
25
+
26
+
27
+ def check() -> list[str]:
28
+ """Return one line per disagreement; empty means the ports hold."""
29
+ problems: list[str] = []
30
+
31
+ from ..impls.attention_core import fa2_seqused
32
+ from ..impls.linear_proj import fp8_static as proj
33
+ from ..impls.qkv_pack import PackedLinear
34
+
35
+ core_cls = fa2_seqused.PackedKVAttention
36
+
37
+ # buffer=alias is only real if both sides expose the entries that
38
+ # make it real, and if the consumer can be told to stop copying
39
+ for name, (out_port, in_port) in DECLARED.items():
40
+ if "alias" not in _offers(out_port, "buffer"):
41
+ continue
42
+ if out_port.structure == "qkv_pack" and not hasattr(
43
+ PackedLinear, "alias_stash"):
44
+ problems.append(
45
+ f"{name}: qkv_pack declares buffer=alias but has no "
46
+ "alias_stash entry")
47
+ if in_port.structure == "attention_core":
48
+ for entry in ("alias_suffix", "forward_suffix"):
49
+ if not hasattr(core_cls, entry):
50
+ problems.append(
51
+ f"{name}: attention_core declares buffer=alias "
52
+ f"but has no {entry} entry")
53
+
54
+ # layout=bshd on the core's kv port is what forward_suffix takes; the
55
+ # host-layout entry is a different port and must not be confused
56
+ kv_in = DECLARED["qkv_pack->attention_core"][1]
57
+ if _offers(kv_in, "layout") != ("bshd",):
58
+ problems.append(
59
+ "qkv_pack->attention_core: the core's kv port takes the "
60
+ "kernel layout only; forward() is the host-layout entry and "
61
+ "belongs to a different port")
62
+
63
+ # dtype=fp8_static has to be an entry the consumer really has
64
+ for name, (_, in_port) in DECLARED.items():
65
+ if "fp8_static" not in _offers(in_port, "dtype"):
66
+ continue
67
+ if in_port.structure == "linear_proj":
68
+ if "fp8_in" not in proj._BAND:
69
+ problems.append(
70
+ f"{name}: linear_proj declares dtype=fp8_static but "
71
+ "has no fp8_in form")
72
+
73
+ # opacity=must_persist is a claim about a compiler contract, so it
74
+ # has to point at something the compiler cannot see through
75
+ style_out = DECLARED["style_broker->producer"][0]
76
+ if "must_persist" in _offers(style_out, "opacity"):
77
+ import torch
78
+ if not hasattr(torch.ops, "flash_rt_structures") or not hasattr(
79
+ torch.ops.flash_rt_structures, "style_broadcast"):
80
+ problems.append(
81
+ "style_broker->producer: opacity=must_persist is claimed "
82
+ "but the fill is not behind an opaque op, so the "
83
+ "compiler may inline it back into each reader")
84
+
85
+ return problems
86
+
87
+
88
+ def report(problems: Sequence[str] | None = None) -> str:
89
+ problems = check() if problems is None else problems
90
+ if not problems:
91
+ return (f"beta: {len(DECLARED)} declared join(s), all consistent "
92
+ "with the impls")
93
+ return "beta: declarations disagree with the impls\n " + "\n ".join(
94
+ problems)
@@ -0,0 +1,113 @@
1
+ """The joins this stack already has, declared.
2
+
3
+ Descriptive before prescriptive: these ports describe what the bound
4
+ structures do *today*, including the joins that already work. If the
5
+ vocabulary cannot express the stack as it stands, the vocabulary is
6
+ wrong and no behaviour should be changed on top of it.
7
+
8
+ Each port carries the shape of the tensor it is about in its note, so a
9
+ reader composing structures by hand — the explicit door — can see the
10
+ agreement without reading the impl.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ from .ports import Port
16
+
17
+ # --- adaptive norm producing the packed projections' input ------------
18
+ # Works today. The producer emits fp8 with a shared act scale and the
19
+ # pack skips its own input quantization; the two are bound atomically.
20
+ # Declared here to prove the vocabulary can express a join that already
21
+ # holds, not to change it.
22
+ ADALN_OUT = Port(
23
+ structure="adaln_producer", name="y", direction="out",
24
+ offers={"dtype": ("fp8_static", "bf16"),
25
+ "layout": ("row_major",),
26
+ "carry": ("gated_residual", "none"),
27
+ "cadence": ("per_call",)},
28
+ note="(rows, D) normed activation; carry=gated_residual means the "
29
+ "producer absorbed the pending residual rather than the host "
30
+ "closing it with an elementwise add")
31
+
32
+ QKV_PACK_IN = Port(
33
+ structure="qkv_pack", name="x", direction="in",
34
+ offers={"dtype": ("fp8_static", "bf16"),
35
+ "layout": ("row_major",)},
36
+ note="(rows, K) shared input of the sibling projections")
37
+
38
+ DECODER_FFN_IN = Port(
39
+ structure="decoder_ffn", name="x", direction="in",
40
+ offers={"dtype": ("fp8_static", "bf16"),
41
+ "layout": ("row_major",)},
42
+ note="(rows, D) normed input; the bf16 entry fuses its own quantize, "
43
+ "which is why this join only pays when the producer is already "
44
+ "there for another reason")
45
+
46
+ # --- packed projections feeding the attention core --------------------
47
+ # The join that still does cancelling work. The pack writes k/v into its
48
+ # own stash buffers and the core copies them into its packed KV region;
49
+ # both sides can express the alias, nothing negotiates it yet.
50
+ QKV_PACK_OUT = Port(
51
+ structure="qkv_pack", name="kv", direction="out",
52
+ offers={"dtype": ("bf16",),
53
+ "layout": ("bshd", "row_major"),
54
+ "buffer": ("alias", "fresh")},
55
+ note="(rows, N_k) / (rows, N_v) sibling outputs; viewed as "
56
+ "(B, S, H, D) they are already the kernel's layout, and the "
57
+ "stash buffer they land in could be the consumer's region")
58
+
59
+ ATTENTION_CORE_KV_IN = Port(
60
+ structure="attention_core", name="kv", direction="in",
61
+ offers={"dtype": ("bf16",),
62
+ "layout": ("bshd",),
63
+ "buffer": ("alias", "fresh")},
64
+ note="(B, S_suffix, H_kv, D) suffix keys/values; the packed region's "
65
+ "suffix rows are contiguous, so a producer can write into them "
66
+ "directly")
67
+
68
+ ATTENTION_CORE_OUT = Port(
69
+ structure="attention_core", name="out", direction="out",
70
+ offers={"dtype": ("bf16",), "layout": ("bshd", "row_major")},
71
+ note="(B, S, H, D). row_major is free *because* the layout is bshd: "
72
+ "its last two axes are contiguous, so the (.., H*D) reshape the "
73
+ "output projection wants is a view. From bhsd the same reshape "
74
+ "would cost a transpose and a copy — which is the whole reason "
75
+ "layout belongs on the join rather than inside either side")
76
+
77
+ LINEAR_PROJ_IN = Port(
78
+ structure="linear_proj", name="x", direction="in",
79
+ offers={"dtype": ("fp8_static", "bf16"), "layout": ("row_major",)},
80
+ note="(rows, K); the fp8 form has no quantize to amortise, which is "
81
+ "why its work band starts an order of magnitude lower")
82
+
83
+ # --- the stream-scoped style materialisation --------------------------
84
+ # The join that the compiler was allowed to undo until it was declared
85
+ # opaque. Declared here because opacity is otherwise invisible: nothing
86
+ # about the tensors says the arrangement must survive.
87
+ STYLE_BROKER_OUT = Port(
88
+ structure="adaln_producer", name="style", direction="out",
89
+ offers={"layout": ("row_major",),
90
+ "cadence": ("per_step", "per_call"),
91
+ "opacity": ("must_persist", "fusible")},
92
+ note="(rows, 3D) style rows for one step; per_step means one fill "
93
+ "serves every producer on the stream, which only survives when "
94
+ "the fill is opaque to the compiler")
95
+
96
+ STYLE_CONSUMER_IN = Port(
97
+ structure="adaln_producer", name="style", direction="in",
98
+ offers={"layout": ("row_major",),
99
+ "cadence": ("per_step", "per_call"),
100
+ "opacity": ("must_persist", "fusible")},
101
+ note="(rows, 3D) contiguous, as the kernel requires")
102
+
103
+
104
+ #: Every join the stack has today, producer first. The pairs that
105
+ #: already hold are here to be checked against reality; the pairs that
106
+ #: do not are here to be measured.
107
+ DECLARED = {
108
+ "adaln->qkv_pack": (ADALN_OUT, QKV_PACK_IN),
109
+ "adaln->decoder_ffn": (ADALN_OUT, DECODER_FFN_IN),
110
+ "qkv_pack->attention_core": (QKV_PACK_OUT, ATTENTION_CORE_KV_IN),
111
+ "attention_core->linear_proj": (ATTENTION_CORE_OUT, LINEAR_PROJ_IN),
112
+ "style_broker->producer": (STYLE_BROKER_OUT, STYLE_CONSUMER_IN),
113
+ }
@@ -0,0 +1,84 @@
1
+ """Negotiate one join: intersect what both sides offer, name what fails.
2
+
3
+ This generalises what the fp8 producer chain already does by hand — two
4
+ components agreeing on one attribute and binding atomically — to the
5
+ whole vocabulary. The parts that carried their weight there are kept:
6
+
7
+ - the intersection is computed before anything binds, so a join that
8
+ cannot be agreed changes nothing;
9
+ - a refusal names the attribute that failed, not the structure, because
10
+ "refused" must never read as "this structure cannot be joined";
11
+ - an attribute only one side declares stays unnegotiated and is reported
12
+ as such, so a partial declaration is a partial gain rather than a
13
+ silent override.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ from typing import Callable, Mapping, Sequence
19
+
20
+ from .ports import Join, Port
21
+
22
+
23
+ class JoinRefused(ValueError):
24
+ """The two ports have no common value for some attribute."""
25
+
26
+
27
+ def negotiate(
28
+ producer: Port,
29
+ consumer: Port,
30
+ *,
31
+ prefer: Mapping[str, Sequence[str]] | None = None,
32
+ cost: Callable[[str, str], float] | None = None,
33
+ ) -> Join:
34
+ """Agree the attributes both ports declare.
35
+
36
+ ``prefer`` overrides the producer's own preference order for an
37
+ attribute — this is where a measured band belongs, so that a choice
38
+ follows the shapes at hand rather than a fixed opinion baked into
39
+ the port. ``cost`` picks by number instead when one is available;
40
+ the lowest wins.
41
+ """
42
+ if producer.direction != "out" or consumer.direction != "in":
43
+ raise JoinRefused(
44
+ f"{producer.structure}.{producer.name} -> "
45
+ f"{consumer.structure}.{consumer.name}: a join runs from an "
46
+ "out port to an in port")
47
+
48
+ prefer = dict(prefer or {})
49
+ chosen: dict[str, str] = {}
50
+ unconstrained: list[str] = []
51
+
52
+ for attr in sorted(set(producer.offers) | set(consumer.offers)):
53
+ out_side = producer.offers.get(attr)
54
+ in_side = consumer.offers.get(attr)
55
+ if out_side is None or in_side is None:
56
+ # only one side has an opinion: leave the join as the host
57
+ # already has it, and say so
58
+ unconstrained.append(attr)
59
+ continue
60
+ common = [v for v in out_side if v in in_side]
61
+ if not common:
62
+ raise JoinRefused(
63
+ f"{producer.structure}.{producer.name} -> "
64
+ f"{consumer.structure}.{consumer.name}: no common {attr} "
65
+ f"({list(out_side)} vs {list(in_side)}) — the join stays "
66
+ "as the host has it; the structures themselves are fine")
67
+ order = [v for v in prefer.get(attr, ()) if v in common] or common
68
+ if cost is not None:
69
+ chosen[attr] = min(order, key=lambda value: cost(attr, value))
70
+ else:
71
+ chosen[attr] = order[0]
72
+
73
+ return Join(producer=producer, consumer=consumer, chosen=chosen,
74
+ unconstrained=tuple(unconstrained))
75
+
76
+
77
+ def describe(joins: Sequence[Join]) -> str:
78
+ """A readable account of what a set of joins agreed on."""
79
+ lines = []
80
+ for join in joins:
81
+ lines.append(str(join))
82
+ if join.unconstrained:
83
+ lines.append(f" unconstrained: {', '.join(join.unconstrained)}")
84
+ return "\n".join(lines)
@@ -0,0 +1,140 @@
1
+ """The join vocabulary: what two adjacent structures have to agree on.
2
+
3
+ Every attribute below was extracted from a join that had already gone
4
+ wrong, and the incident is recorded with it. Nothing here is speculative
5
+ — an attribute nobody negotiates is meant to be deleted, not kept as
6
+ documentation.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ from dataclasses import dataclass, field
12
+ from typing import Mapping, Sequence
13
+
14
+ # Attribute -> the values a port may declare, most-preferred first where
15
+ # order carries meaning. A port that declares nothing for an attribute
16
+ # leaves it unnegotiated, which is exactly today's behaviour.
17
+ ATTRIBUTES: dict[str, tuple[str, ...]] = {
18
+ # Who holds the activation in what numeric type, and therefore who
19
+ # pays for the conversion.
20
+ #
21
+ # Incident: an fp8 producer and the pack it feeds, bound
22
+ # independently, can diverge — a pack expecting fp8 whose producer
23
+ # did not bind leaves the host to insert a quantize, which the
24
+ # compiler fuses into the upstream gated residual and turns into fp8
25
+ # arithmetic the target cannot lower. Binding the pair atomically
26
+ # fixed it. This is the one join attribute that was ever declared,
27
+ # and the only one that stopped failing.
28
+ "dtype": ("fp8_static", "bf16", "int8_static"),
29
+
30
+ # How the tensor's axes are arranged when it crosses the join.
31
+ #
32
+ # Incident: the host lays q/k/v out as (B, H, S, D) because that is
33
+ # what eager attention wants; the fused kernel wants (B, S, H, D)
34
+ # and transposed back. Two transposes that cancel, plus the copies
35
+ # that make each contiguous, per projection per layer per step —
36
+ # and neither side could see the other, because each was correct on
37
+ # its own.
38
+ "layout": ("row_major", "bshd", "bhsd"),
39
+
40
+ # Who allocates the tensor the consumer reads. ``alias`` is the
41
+ # declarative form of what a hand-written runtime does when it hands
42
+ # the next stage a pointer: the producer writes straight into the
43
+ # region the consumer already owns.
44
+ #
45
+ # Incident: the packed projection writes its k/v into its own stash
46
+ # buffers and the attention core then copies them into its packed
47
+ # KV region — two buffers where one would do.
48
+ "buffer": ("alias", "caller_provided", "fresh"),
49
+
50
+ # Whether a sublayer result is still pending at the join, so the
51
+ # consumer can absorb it instead of the host closing it first.
52
+ #
53
+ # Incident: the adaptive-norm kernel computes ``residual + x * gate``
54
+ # before it norms. Bound at the norm boundary there is no residual to
55
+ # hand it, so it was fed zeros and the host kept its own elementwise
56
+ # add — measured +0.17 ms, refused. The same kernel inside a block
57
+ # boundary replaces that add instead of adding to it, and turns
58
+ # positive. Same kernel, opposite verdict, because the join was
59
+ # declared at the wrong place.
60
+ "carry": ("gated_residual", "none"),
61
+
62
+ # How often the value on this join actually changes, as opposed to
63
+ # how often it is read.
64
+ #
65
+ # Incident: a step's style is fixed for every producer on one
66
+ # conditioning stream, but was materialised per call — 720 launches
67
+ # for work that changes 10 times a tick.
68
+ "cadence": ("per_observation", "per_step", "per_call"),
69
+
70
+ # Whether the compiler may rewrite away the arrangement this join
71
+ # depends on. Not a hardware or dataflow property: a contract with
72
+ # the compiler underneath.
73
+ #
74
+ # Incident: a step-scoped shared buffer, written as plain tensor
75
+ # work, was legally eliminated — the compiler saw a buffer whose
76
+ # only consumers were slices of it and inlined the fill back into
77
+ # each of them. Semantically identical, and the sharing was gone:
78
+ # -0.164 ms instead of -0.419 ms, with the launch count unchanged.
79
+ # A structure layer sitting on a compiler has to be able to say
80
+ # which of its decisions may not be undone.
81
+ "opacity": ("must_persist", "fusible"),
82
+ }
83
+
84
+
85
+ class PortError(ValueError):
86
+ """A port declared something outside the vocabulary."""
87
+
88
+
89
+ @dataclass(frozen=True)
90
+ class Port:
91
+ """One side of a join: what this structure can accept or emit.
92
+
93
+ ``offers`` maps an attribute to the values this port supports, most
94
+ preferred first. An attribute left out is not negotiated — the join
95
+ keeps whatever the host already does, which is why declaring a port
96
+ can only add.
97
+ """
98
+
99
+ structure: str
100
+ name: str
101
+ direction: str # "in" | "out"
102
+ offers: Mapping[str, Sequence[str]] = field(default_factory=dict)
103
+ note: str = ""
104
+
105
+ def __post_init__(self) -> None:
106
+ if self.direction not in ("in", "out"):
107
+ raise PortError(
108
+ f"{self.structure}.{self.name}: direction must be in/out")
109
+ for attr, values in self.offers.items():
110
+ known = ATTRIBUTES.get(attr)
111
+ if known is None:
112
+ raise PortError(
113
+ f"{self.structure}.{self.name}: {attr!r} is not a join "
114
+ f"attribute. The vocabulary is closed on purpose — "
115
+ f"adding one means a new incident to record")
116
+ unknown = [v for v in values if v not in known]
117
+ if unknown:
118
+ raise PortError(
119
+ f"{self.structure}.{self.name}: {attr}={unknown} not in "
120
+ f"{known}")
121
+ if not values:
122
+ raise PortError(
123
+ f"{self.structure}.{self.name}: {attr} offers nothing; "
124
+ "leave the attribute out to keep it unnegotiated")
125
+
126
+
127
+ @dataclass(frozen=True)
128
+ class Join:
129
+ """A negotiated agreement between two ports."""
130
+
131
+ producer: Port
132
+ consumer: Port
133
+ chosen: Mapping[str, str]
134
+ unconstrained: Sequence[str] = () # declared by one side only
135
+ note: str = ""
136
+
137
+ def __str__(self) -> str:
138
+ picks = ", ".join(f"{k}={v}" for k, v in sorted(self.chosen.items()))
139
+ return (f"{self.producer.structure}.{self.producer.name} -> "
140
+ f"{self.consumer.structure}.{self.consumer.name} [{picks}]")
@@ -0,0 +1,80 @@
1
+ """Band decisions: measured once per box, cached, consumed by every tier.
2
+
3
+ The seat-level micro-race was refuted in both directions by
4
+ production-form measurement; what stands is the captured end-to-end
5
+ number. This cache is where those numbers live: a band measurement run
6
+ records the winner per (device, band), and both assembly tiers read it
7
+ — the automatic path to route its formats, the explicit path as its
8
+ default band. An author pin (env) always outranks the cache; an empty
9
+ cache falls to the precision-order default. The cache file is a
10
+ receipt and is transportable: an edge box can ship with its decisions
11
+ measured elsewhere on identical hardware.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import json
17
+ import os
18
+ import pathlib
19
+
20
+ import torch
21
+
22
+
23
+ def _cache_path() -> pathlib.Path:
24
+ return pathlib.Path(os.environ.get(
25
+ "FRT_DECISION_CACHE",
26
+ os.path.expanduser("~/.cache/flashrt/band_decisions.json")))
27
+
28
+
29
+ def _key(band: str) -> str:
30
+ dev = (torch.cuda.get_device_name(0)
31
+ if torch.cuda.is_available() else "cpu")
32
+ return f"{dev}|{band}"
33
+
34
+
35
+ def lookup(band: str, default: str | None = None) -> str | None:
36
+ try:
37
+ data = json.loads(_cache_path().read_text())
38
+ except (OSError, ValueError):
39
+ return default
40
+ entry = data.get(_key(band))
41
+ return entry.get("winner", default) if entry else default
42
+
43
+
44
+ def record(band: str, winner: str, times_ms: dict) -> pathlib.Path:
45
+ path = _cache_path()
46
+ path.parent.mkdir(parents=True, exist_ok=True)
47
+ try:
48
+ data = json.loads(path.read_text())
49
+ except (OSError, ValueError):
50
+ data = {}
51
+ data[_key(band)] = {"winner": winner, "times_ms": times_ms}
52
+ path.write_text(json.dumps(data, indent=1))
53
+ return path
54
+
55
+
56
+ def import_decisions(source: str) -> dict:
57
+ """Merge a decision file measured elsewhere into the local cache.
58
+
59
+ The peak-avoidance path for a memory-tight box: adjudicate on a
60
+ roomy machine of the same device fingerprint, ship the file, and
61
+ the tight box binds warm from its first run — the race (and its
62
+ peak) never happens there. Entries whose device fingerprint does
63
+ not match this box are carried along untouched (the cache is keyed
64
+ by device, so they are inert here and correct if the file travels
65
+ on). Local entries win on conflict: a number measured on this box
66
+ outranks an imported one. Returns a receipt of what was merged.
67
+ """
68
+ imported = json.loads(pathlib.Path(source).read_text())
69
+ path = _cache_path()
70
+ path.parent.mkdir(parents=True, exist_ok=True)
71
+ try:
72
+ local = json.loads(path.read_text())
73
+ except (OSError, ValueError):
74
+ local = {}
75
+ added = [k for k in imported if k not in local]
76
+ merged = dict(imported)
77
+ merged.update(local)
78
+ path.write_text(json.dumps(merged, indent=2))
79
+ return {"imported": len(added), "kept_local": len(local),
80
+ "entries": sorted(added)}