khtsly commited on
Commit
f0e968d
·
verified ·
1 Parent(s): 852af67

Upload folder using huggingface_hub

Browse files
Files changed (5) hide show
  1. configuration_luau_coder.py +154 -146
  2. luau_gemv_tuned.py +119 -46
  3. luau_infer.py +242 -131
  4. luau_kernels.py +966 -734
  5. modeling_luau_coder.py +116 -216
configuration_luau_coder.py CHANGED
@@ -1,146 +1,154 @@
1
- from __future__ import annotations
2
-
3
- import math
4
-
5
- from transformers import PretrainedConfig
6
-
7
- __all__ = ["LuauCoderConfig"]
8
-
9
-
10
- class LuauCoderConfig(PretrainedConfig):
11
- model_type = "luau_coder"
12
- keys_to_ignore_at_inference = ["past_key_values"]
13
-
14
- def __init__(
15
- self,
16
- vocab_size: int = 32768,
17
- hidden_size: int = 2560,
18
- intermediate_size: int = 6144,
19
- num_hidden_layers: int = 36,
20
- num_attention_heads: int = 20,
21
- num_key_value_heads: int = 5,
22
- head_dim: int = 128,
23
- rms_norm_eps: float = 1e-6,
24
- final_logit_softcapping: float | None = None,
25
- max_position_embeddings: int = 4096,
26
- rope_theta: float = 500000.0,
27
- rope_scaling: dict | None = None,
28
- lerope_alpha: list[float] | None = None,
29
- nope_layers: list[int] | None = None,
30
- diff_v2: bool = True,
31
- xsa: bool = True,
32
- qk_norm: bool = True,
33
- use_output_gate: bool | str | None = False,
34
- attn_res: bool = False,
35
- attn_res_block_size: int = 6,
36
- attn_res_delta_block: bool = False,
37
- attn_res_partial_source: bool = False,
38
- ccgqa: bool = False,
39
- ccgqa_seq_kernel: int = 2,
40
- ccgqa_head_kernel: int = 2,
41
- ccgqa_qk_mean: bool = True,
42
- ccgqa_value_shift: bool = True,
43
- vha: bool = False,
44
- iha: bool = False,
45
- iha_pseudo_heads: int = 2,
46
- iha_window: int = 0,
47
- iha_global_every: int = 5,
48
- initializer_range: float | None = None,
49
- use_cache: bool = True,
50
- tie_word_embeddings: bool = False,
51
- # throughput knobs (no effect on the math)
52
- fused_kernels: bool = True,
53
- compile_elementwise: bool = True,
54
- loss_chunk_size: int = 4096,
55
- bos_token_id: int | None = None,
56
- eos_token_id: int | list[int] | None = 32744,
57
- pad_token_id: int | None = 32745,
58
- **kwargs,
59
- ):
60
- if kwargs.pop("msar", False):
61
- raise NotImplementedError("Block MSAR checkpoints are not supported by this modeling code")
62
- self.vocab_size = vocab_size
63
- self.hidden_size = hidden_size
64
- self.intermediate_size = intermediate_size
65
- self.num_hidden_layers = num_hidden_layers
66
- self.num_attention_heads = num_attention_heads
67
- self.num_key_value_heads = num_key_value_heads
68
- self.head_dim = head_dim
69
- self.rms_norm_eps = rms_norm_eps
70
- self.final_logit_softcapping = final_logit_softcapping # logits -> c * tanh(logits / c)
71
- self.max_position_embeddings = max_position_embeddings
72
- self.rope_theta = rope_theta
73
- self.rope_scaling = rope_scaling
74
- # LeRoPE (arXiv:2607.10134): learned log-frequency scale per RoPE band (head_dim / 2), shared by
75
- # every layer; frequency_m = exp(alpha_m) / theta^(2m / head_dim). None = plain RoPE.
76
- self.lerope_alpha = None if lerope_alpha is None else [float(a) for a in lerope_alpha]
77
- self.nope_layers = sorted(nope_layers or []) # attention without RoPE at these layers
78
- self.diff_v2 = diff_v2
79
- self.xsa = xsa
80
- self.qk_norm = qk_norm
81
- # Gated Attention: False | "elementwise" (W_g's rows in qkv) | "headwise" (gate_proj)
82
- self.use_output_gate = ("elementwise" if use_output_gate is True
83
- else str(use_output_gate).lower() if use_output_gate else False)
84
- self.attn_res = attn_res
85
- self.attn_res_block_size = attn_res_block_size
86
- self.attn_res_delta_block = attn_res_delta_block
87
- self.attn_res_partial_source = attn_res_partial_source
88
- self.ccgqa = bool(ccgqa)
89
- self.ccgqa_seq_kernel = ccgqa_seq_kernel
90
- self.ccgqa_head_kernel = ccgqa_head_kernel
91
- self.ccgqa_qk_mean = ccgqa_qk_mean
92
- self.ccgqa_value_shift = ccgqa_value_shift
93
- self.vha = bool(vha)
94
- self.iha = bool(iha)
95
- self.iha_pseudo_heads = iha_pseudo_heads
96
- self.iha_window = iha_window
97
- self.iha_global_every = iha_global_every
98
- if self.iha and (iha_pseudo_heads < 1 or iha_window < 0 or iha_global_every < 0):
99
- raise ValueError("iha: iha_pseudo_heads >= 1, iha_window >= 0 and iha_global_every >= 0")
100
- # training default ("auto"): 1/sqrt(hidden_size)
101
- self.initializer_range = hidden_size ** -0.5 if initializer_range is None else initializer_range
102
- self.use_cache = use_cache
103
- self.fused_kernels = fused_kernels
104
- self.compile_elementwise = compile_elementwise
105
- self.loss_chunk_size = loss_chunk_size
106
- if num_attention_heads % num_key_value_heads:
107
- raise ValueError(f"num_attention_heads ({num_attention_heads}) must be a multiple of "
108
- f"num_key_value_heads ({num_key_value_heads})")
109
- if head_dim % 2:
110
- raise ValueError("head_dim must be even (RoPE)")
111
- if self.use_output_gate not in (False, "elementwise", "headwise"):
112
- raise ValueError(f"use_output_gate: true / 'elementwise', 'headwise' or false, got {use_output_gate!r}")
113
- if self.ccgqa and not qk_norm:
114
- raise ValueError("ccgqa needs qk_norm")
115
- if self.ccgqa and ccgqa_value_shift and (num_key_value_heads * head_dim) % 2:
116
- raise ValueError("ccgqa_value_shift needs an even num_key_value_heads * head_dim")
117
- if attn_res and math.ceil(num_hidden_layers / attn_res_block_size) > 32:
118
- raise ValueError("attn_res: at most 32 blocks (num_hidden_layers / attn_res_block_size)")
119
- if any(not 0 <= i < num_hidden_layers for i in self.nope_layers):
120
- raise ValueError(f"nope_layers must be in [0, {num_hidden_layers}), got {self.nope_layers}")
121
- if rope_scaling is not None:
122
- rtype = rope_scaling.get("rope_type", rope_scaling.get("type"))
123
- if rtype not in (None, "default", "yarn"):
124
- raise ValueError(f"rope_scaling: only 'yarn' is supported, got {rtype!r}")
125
- super().__init__(bos_token_id=bos_token_id, eos_token_id=eos_token_id,
126
- pad_token_id=pad_token_id, tie_word_embeddings=tie_word_embeddings, **kwargs)
127
-
128
- @property
129
- def output_gate(self) -> str | None:
130
- """None (no gate), "elementwise" or "headwise"."""
131
- return self.use_output_gate or None
132
-
133
- @property
134
- def num_query_heads(self) -> int:
135
- """Query heads the attention kernel runs (2x the output heads with DiffAttn V2)."""
136
- return self.num_attention_heads * (2 if self.diff_v2 else 1)
137
-
138
- @property
139
- def num_q_latent_heads(self) -> int:
140
- """Query heads ``qkv`` produces (VHA: the physical heads every KV group shares)."""
141
- return self.num_query_heads // self.num_key_value_heads if self.vha else self.num_query_heads
142
-
143
- def iha_layer(self, i: int) -> bool:
144
- """Whether layer ``i`` runs IHA (the rest are plain global attention layers)."""
145
- every = self.iha_global_every
146
- return self.iha and not (every and (i + 1) % every == 0)
 
 
 
 
 
 
 
 
 
1
+ """LuauCoder configuration (``trust_remote_code=True``).
2
+
3
+ Mirrors ``lm.config.ModelConfig`` with transformers' standard names.
4
+ ``num_attention_heads`` counts attention output heads: with ``diff_v2`` the model
5
+ runs twice as many query heads against ``num_key_value_heads`` K/V heads.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import math
11
+
12
+ from transformers import PretrainedConfig
13
+
14
+ __all__ = ["LuauCoderConfig"]
15
+
16
+
17
+ class LuauCoderConfig(PretrainedConfig):
18
+ model_type = "luau_coder"
19
+ keys_to_ignore_at_inference = ["past_key_values"]
20
+
21
+ def __init__(
22
+ self,
23
+ vocab_size: int = 32768,
24
+ hidden_size: int = 2560,
25
+ intermediate_size: int = 6144,
26
+ num_hidden_layers: int = 36,
27
+ num_attention_heads: int = 20,
28
+ num_key_value_heads: int = 5,
29
+ head_dim: int = 128,
30
+ rms_norm_eps: float = 1e-6,
31
+ final_logit_softcapping: float | None = None,
32
+ max_position_embeddings: int = 4096,
33
+ rope_theta: float = 500000.0,
34
+ rope_scaling: dict | None = None,
35
+ lerope_alpha: list[float] | None = None,
36
+ nope_layers: list[int] | None = None,
37
+ diff_v2: bool = True,
38
+ xsa: bool = True,
39
+ qk_norm: bool = True,
40
+ use_output_gate: bool | str | None = False,
41
+ attn_res: bool = False,
42
+ attn_res_block_size: int = 6,
43
+ attn_res_delta_block: bool = False,
44
+ attn_res_partial_source: bool = False,
45
+ ccgqa: bool = False,
46
+ ccgqa_seq_kernel: int = 2,
47
+ ccgqa_head_kernel: int = 2,
48
+ ccgqa_qk_mean: bool = True,
49
+ ccgqa_value_shift: bool = True,
50
+ vha: bool = False,
51
+ iha: bool = False,
52
+ iha_pseudo_heads: int = 2,
53
+ iha_window: int = 0,
54
+ iha_global_every: int = 5,
55
+ head_gate_rank: int = 0,
56
+ initializer_range: float | None = None,
57
+ use_cache: bool = True,
58
+ tie_word_embeddings: bool = False,
59
+ # throughput knobs (no effect on the math)
60
+ fused_kernels: bool = True,
61
+ compile_elementwise: bool = True,
62
+ loss_chunk_size: int = 4096,
63
+ bos_token_id: int | None = None,
64
+ eos_token_id: int | list[int] | None = 32744,
65
+ pad_token_id: int | None = 32745,
66
+ **kwargs,
67
+ ):
68
+ self.vocab_size = vocab_size
69
+ self.hidden_size = hidden_size
70
+ self.intermediate_size = intermediate_size
71
+ self.num_hidden_layers = num_hidden_layers
72
+ self.num_attention_heads = num_attention_heads
73
+ self.num_key_value_heads = num_key_value_heads
74
+ self.head_dim = head_dim
75
+ self.rms_norm_eps = rms_norm_eps
76
+ self.final_logit_softcapping = final_logit_softcapping
77
+ self.max_position_embeddings = max_position_embeddings
78
+ self.rope_theta = rope_theta
79
+ self.rope_scaling = rope_scaling
80
+ # LeRoPE: learned log-frequency scale per RoPE band, shared by every layer; None = plain RoPE
81
+ self.lerope_alpha = None if lerope_alpha is None else [float(a) for a in lerope_alpha]
82
+ self.nope_layers = sorted(nope_layers or [])
83
+ self.diff_v2 = diff_v2
84
+ self.xsa = xsa
85
+ self.qk_norm = qk_norm
86
+ # False | "elementwise" | "headwise"
87
+ self.use_output_gate = ("elementwise" if use_output_gate is True
88
+ else str(use_output_gate).lower() if use_output_gate else False)
89
+ self.attn_res = attn_res
90
+ self.attn_res_block_size = attn_res_block_size
91
+ self.attn_res_delta_block = attn_res_delta_block
92
+ self.attn_res_partial_source = attn_res_partial_source
93
+ self.ccgqa = bool(ccgqa)
94
+ self.ccgqa_seq_kernel = ccgqa_seq_kernel
95
+ self.ccgqa_head_kernel = ccgqa_head_kernel
96
+ self.ccgqa_qk_mean = ccgqa_qk_mean
97
+ self.ccgqa_value_shift = ccgqa_value_shift
98
+ self.vha = bool(vha)
99
+ self.iha = bool(iha)
100
+ self.iha_pseudo_heads = iha_pseudo_heads
101
+ self.iha_window = iha_window
102
+ self.iha_global_every = iha_global_every
103
+ # gated byte head: [h, sigmoid(u . h + b) * P h] against lm_head = [W, Z]; 0 = plain head
104
+ self.head_gate_rank = int(head_gate_rank)
105
+ if self.head_gate_rank < 0:
106
+ raise ValueError("head_gate_rank must be >= 0")
107
+ if self.iha and (iha_pseudo_heads < 1 or iha_window < 0 or iha_global_every < 0):
108
+ raise ValueError("iha: iha_pseudo_heads >= 1, iha_window >= 0 and iha_global_every >= 0")
109
+ self.initializer_range = hidden_size ** -0.5 if initializer_range is None else initializer_range
110
+ self.use_cache = use_cache
111
+ self.fused_kernels = fused_kernels
112
+ self.compile_elementwise = compile_elementwise
113
+ self.loss_chunk_size = loss_chunk_size
114
+ if num_attention_heads % num_key_value_heads:
115
+ raise ValueError(f"num_attention_heads ({num_attention_heads}) must be a multiple of "
116
+ f"num_key_value_heads ({num_key_value_heads})")
117
+ if head_dim % 2:
118
+ raise ValueError("head_dim must be even (RoPE)")
119
+ if self.use_output_gate not in (False, "elementwise", "headwise"):
120
+ raise ValueError(f"use_output_gate: true / 'elementwise', 'headwise' or false, got {use_output_gate!r}")
121
+ if self.ccgqa and not qk_norm:
122
+ raise ValueError("ccgqa needs qk_norm")
123
+ if self.ccgqa and ccgqa_value_shift and (num_key_value_heads * head_dim) % 2:
124
+ raise ValueError("ccgqa_value_shift needs an even num_key_value_heads * head_dim")
125
+ if attn_res and math.ceil(num_hidden_layers / attn_res_block_size) > 32:
126
+ raise ValueError("attn_res: at most 32 blocks (num_hidden_layers / attn_res_block_size)")
127
+ if any(not 0 <= i < num_hidden_layers for i in self.nope_layers):
128
+ raise ValueError(f"nope_layers must be in [0, {num_hidden_layers}), got {self.nope_layers}")
129
+ if rope_scaling is not None:
130
+ rtype = rope_scaling.get("rope_type", rope_scaling.get("type"))
131
+ if rtype not in (None, "default", "yarn"):
132
+ raise ValueError(f"rope_scaling: only 'yarn' is supported, got {rtype!r}")
133
+ super().__init__(bos_token_id=bos_token_id, eos_token_id=eos_token_id,
134
+ pad_token_id=pad_token_id, tie_word_embeddings=tie_word_embeddings, **kwargs)
135
+
136
+ @property
137
+ def output_gate(self) -> str | None:
138
+ """None (no gate), "elementwise" or "headwise"."""
139
+ return self.use_output_gate or None
140
+
141
+ @property
142
+ def num_query_heads(self) -> int:
143
+ """Query heads the attention kernel runs (2x the output heads with DiffAttn V2)."""
144
+ return self.num_attention_heads * (2 if self.diff_v2 else 1)
145
+
146
+ @property
147
+ def num_q_latent_heads(self) -> int:
148
+ """Query heads ``qkv`` produces (VHA: the physical heads every KV group shares)."""
149
+ return self.num_query_heads // self.num_key_value_heads if self.vha else self.num_query_heads
150
+
151
+ def iha_layer(self, i: int) -> bool:
152
+ """Whether layer ``i`` runs IHA (the rest are plain global attention layers)."""
153
+ every = self.iha_global_every
154
+ return self.iha and not (every and (i + 1) % every == 0)
luau_gemv_tuned.py CHANGED
@@ -1,46 +1,119 @@
1
- TABLE = {'NVIDIA RTX PRO 6000 Blackwell Server Edition': {'arch': [12, 0, 188],
2
- 'gemv': {'1,1408,1280,0,0': [4, 512, 4, 4, 0],
3
- '1,1408,1280,1,0': [4, 1024, 2, 4, 0],
4
- '1,1408,3840,0,0': [2, 1024, 2, 2, 0],
5
- '1,1408,3840,1,0': [2, 1024, 2, 4, 0],
6
- '1,1802,1408,0,0': [2, 512, 1, 1, 0],
7
- '1,1802,1408,1,0': [2, 512, 1, 3, 0],
8
- '1,32768,1408,0,0': [4, 256, 2, 4, 0],
9
- '1,3840,1408,0,1': [4, 256, 1, 3, 0],
10
- '1,3840,1408,1,1': [4, 512, 1, 3, 0],
11
- '16,1408,1280,0,0': [32, 256, 2, 4, 1],
12
- '16,1408,1280,1,0': [32, 256, 4, 4, 1],
13
- '16,1408,3840,0,0': [16, 256, 2, 4, 1],
14
- '16,1408,3840,1,0': [32, 256, 4, 4, 1],
15
- '16,1802,1408,0,0': [16, 128, 2, 4, 1],
16
- '16,1802,1408,1,0': [32, 128, 4, 4, 1],
17
- '16,32768,1408,0,0': [128, 64, 4, 4, 1],
18
- '16,3840,1408,0,1': [32, 256, 4, 4, 1],
19
- '16,3840,1408,1,1': [32, 128, 4, 4, 1],
20
- '2,1408,1280,0,0': [4, 512, 2, 4, 0],
21
- '2,1408,1280,1,0': [4, 1024, 2, 2, 0],
22
- '2,1408,3840,0,0': [2, 1024, 2, 1, 0],
23
- '2,1408,3840,1,0': [4, 1024, 2, 2, 0],
24
- '2,1802,1408,0,0': [1, 512, 2, 1, 0],
25
- '2,1802,1408,1,0': [2, 512, 2, 2, 0],
26
- '2,32768,1408,0,0': [128, 64, 4, 4, 1],
27
- '2,3840,1408,0,1': [16, 256, 2, 3, 1],
28
- '2,3840,1408,1,1': [2, 512, 1, 3, 0],
29
- '4,1408,1280,0,0': [4, 256, 2, 2, 0],
30
- '4,1408,1280,1,0': [4, 256, 2, 3, 0],
31
- '4,1408,3840,0,0': [2, 1024, 2, 2, 0],
32
- '4,1408,3840,1,0': [4, 1024, 4, 1, 0],
33
- '4,1802,1408,0,0': [2, 512, 2, 3, 0],
34
- '4,1802,1408,1,0': [2, 512, 2, 4, 0],
35
- '4,32768,1408,0,0': [64, 64, 4, 4, 1],
36
- '4,3840,1408,0,1': [16, 256, 2, 3, 1],
37
- '4,3840,1408,1,1': [32, 256, 4, 3, 1],
38
- '8,1408,1280,0,0': [16, 256, 2, 4, 1],
39
- '8,1408,1280,1,0': [32, 256, 4, 4, 1],
40
- '8,1408,3840,0,0': [16, 256, 2, 4, 1],
41
- '8,1408,3840,1,0': [4, 512, 4, 4, 0],
42
- '8,1802,1408,0,0': [32, 256, 4, 3, 1],
43
- '8,1802,1408,1,0': [32, 128, 4, 4, 1],
44
- '8,32768,1408,0,0': [128, 64, 4, 4, 1],
45
- '8,3840,1408,0,1': [32, 256, 4, 3, 1],
46
- '8,3840,1408,1,1': [32, 128, 4, 4, 1]}}}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Pinned decode-GEMV configs per GPU (written by ``benchmark/tune_gemv.py``).
2
+
3
+ ``TABLE[gpu name] = {"arch": [major, minor, SMs], "gemv": {key: (BLOCK_N, BLOCK_K, warps, stages, DOT)}}``;
4
+ a GPU missing here uses an entry of the same architecture and SM count (e.g. the
5
+ Workstation and Server editions of a card), else the kernels' defaults.
6
+ """
7
+
8
+ TABLE = {'NVIDIA GeForce RTX 4060': {'arch': [8, 9, 24],
9
+ 'gemv': {'1,1024,1024,0,0': [1, 1024, 1, 3, 0],
10
+ '1,1024,1024,1,0': [16, 256, 4, 4, 0],
11
+ '1,1024,3072,0,0': [1, 1024, 4, 2, 0],
12
+ '1,1024,3072,1,0': [2, 512, 1, 1, 0],
13
+ '1,1408,1280,0,0': [32, 128, 1, 1, 0],
14
+ '1,1408,1280,1,0': [8, 256, 1, 2, 0],
15
+ '1,1408,3840,0,0': [1, 1024, 1, 2, 0],
16
+ '1,1408,3840,1,0': [4, 1024, 1, 1, 0],
17
+ '1,1422,1792,0,0': [1, 1024, 1, 1, 0],
18
+ '1,1422,1792,1,0': [8, 256, 1, 1, 0],
19
+ '1,1544,1024,0,0': [1, 512, 1, 1, 0],
20
+ '1,1544,1024,1,0': [4, 1024, 2, 2, 0],
21
+ '1,1792,1792,0,0': [4, 256, 1, 4, 0],
22
+ '1,1792,1792,1,0': [4, 256, 1, 2, 0],
23
+ '1,1792,4864,0,0': [4, 256, 1, 1, 0],
24
+ '1,1792,4864,1,0': [2, 1024, 1, 1, 0],
25
+ '1,1802,1408,0,0': [8, 64, 1, 2, 0],
26
+ '1,1802,1408,1,0': [16, 128, 1, 2, 0],
27
+ '1,2048,2048,0,0': [2, 1024, 2, 2, 0],
28
+ '1,2048,2048,1,0': [32, 128, 2, 3, 0],
29
+ '1,2048,6144,0,0': [2, 512, 1, 1, 0],
30
+ '1,2048,6144,1,0': [4, 512, 1, 3, 0],
31
+ '1,257,1792,0,0': [1, 1024, 1, 1, 0],
32
+ '1,257,1792,1,0': [1, 1024, 1, 4, 0],
33
+ '1,2592,2048,0,0': [2, 512, 1, 2, 0],
34
+ '1,2592,2048,1,0': [2, 512, 4, 4, 0],
35
+ '1,3072,1024,0,1': [1, 1024, 1, 3, 0],
36
+ '1,3072,1024,1,1': [4, 1024, 1, 1, 0],
37
+ '1,32768,1024,0,0': [2, 512, 1, 1, 0],
38
+ '1,32768,1408,0,0': [1, 1024, 2, 1, 0],
39
+ '1,32768,2048,0,0': [4, 512, 4, 1, 0],
40
+ '1,3840,1408,0,1': [2, 1024, 1, 4, 0],
41
+ '1,3840,1408,1,1': [8, 512, 1, 3, 0],
42
+ '1,4864,1792,0,1': [1, 1024, 1, 1, 0],
43
+ '1,4864,1792,1,1': [16, 256, 1, 1, 0],
44
+ '1,6144,2048,0,1': [1, 1024, 1, 3, 0],
45
+ '1,6144,2048,1,1': [2, 1024, 1, 3, 0]}},
46
+ 'NVIDIA RTX PRO 6000 Blackwell Server Edition': {'arch': [12, 0, 188],
47
+ 'gemv': {'1,1024,1024,0,0': [2, 1024, 1, 2, 0],
48
+ '1,1024,1024,1,0': [8, 1024, 4, 1, 0],
49
+ '1,1024,3072,0,0': [2, 1024, 2, 2, 0],
50
+ '1,1024,3072,1,0': [2, 1024, 4, 1, 0],
51
+ '1,1408,1280,0,0': [4, 512, 4, 3, 0],
52
+ '1,1408,1280,1,0': [4, 1024, 2, 3, 0],
53
+ '1,1408,3840,0,0': [2, 1024, 2, 2, 0],
54
+ '1,1408,3840,1,0': [2, 1024, 2, 2, 0],
55
+ '1,1422,1792,0,0': [1, 1024, 4, 1, 0],
56
+ '1,1422,1792,1,0': [2, 1024, 1, 3, 0],
57
+ '1,1544,1024,0,0': [1, 1024, 2, 2, 0],
58
+ '1,1544,1024,1,0': [2, 1024, 1, 4, 0],
59
+ '1,1792,1792,0,0': [2, 1024, 2, 4, 0],
60
+ '1,1792,1792,1,0': [2, 1024, 1, 1, 0],
61
+ '1,1792,4864,0,0': [2, 1024, 2, 3, 0],
62
+ '1,1792,4864,1,0': [2, 1024, 1, 2, 0],
63
+ '1,1802,1408,0,0': [2, 512, 1, 3, 0],
64
+ '1,1802,1408,1,0': [2, 512, 1, 2, 0],
65
+ '1,2048,2048,0,0': [2, 1024, 2, 2, 0],
66
+ '1,2048,2048,1,0': [2, 1024, 1, 2, 0],
67
+ '1,2048,6144,0,0': [2, 1024, 4, 3, 0],
68
+ '1,2048,6144,1,0': [2, 1024, 1, 3, 0],
69
+ '1,257,1792,0,0': [4, 1024, 4, 3, 0],
70
+ '1,257,1792,1,0': [4, 1024, 4, 4, 0],
71
+ '1,2592,2048,0,0': [2, 512, 1, 1, 0],
72
+ '1,2592,2048,1,0': [2, 1024, 1, 2, 0],
73
+ '1,3072,1024,0,1': [4, 256, 1, 3, 0],
74
+ '1,3072,1024,1,1': [4, 1024, 2, 1, 0],
75
+ '1,32768,1024,0,0': [2, 256, 1, 1, 0],
76
+ '1,32768,1408,0,0': [1, 1024, 4, 2, 0],
77
+ '1,32768,2048,0,0': [8, 256, 1, 2, 0],
78
+ '1,3840,1408,0,1': [2, 512, 1, 2, 0],
79
+ '1,3840,1408,1,1': [4, 512, 1, 3, 0],
80
+ '1,4864,1792,0,1': [4, 256, 1, 4, 0],
81
+ '1,4864,1792,1,1': [4, 1024, 1, 2, 0],
82
+ '1,6144,2048,0,1': [2, 256, 1, 3, 0],
83
+ '1,6144,2048,1,1': [2, 512, 1, 3, 0],
84
+ '16,1408,1280,0,0': [32, 256, 2, 4, 1],
85
+ '16,1408,1280,1,0': [32, 256, 4, 4, 1],
86
+ '16,1408,3840,0,0': [16, 256, 2, 4, 1],
87
+ '16,1408,3840,1,0': [32, 256, 4, 4, 1],
88
+ '16,1802,1408,0,0': [16, 128, 2, 4, 1],
89
+ '16,1802,1408,1,0': [32, 128, 4, 4, 1],
90
+ '16,32768,1408,0,0': [128, 64, 4, 4, 1],
91
+ '16,3840,1408,0,1': [32, 256, 4, 4, 1],
92
+ '16,3840,1408,1,1': [32, 128, 4, 4, 1],
93
+ '2,1408,1280,0,0': [4, 512, 2, 4, 0],
94
+ '2,1408,1280,1,0': [4, 1024, 2, 2, 0],
95
+ '2,1408,3840,0,0': [2, 1024, 2, 1, 0],
96
+ '2,1408,3840,1,0': [4, 1024, 2, 2, 0],
97
+ '2,1802,1408,0,0': [1, 512, 2, 1, 0],
98
+ '2,1802,1408,1,0': [2, 512, 2, 2, 0],
99
+ '2,32768,1408,0,0': [128, 64, 4, 4, 1],
100
+ '2,3840,1408,0,1': [16, 256, 2, 3, 1],
101
+ '2,3840,1408,1,1': [2, 512, 1, 3, 0],
102
+ '4,1408,1280,0,0': [4, 256, 2, 2, 0],
103
+ '4,1408,1280,1,0': [4, 256, 2, 3, 0],
104
+ '4,1408,3840,0,0': [2, 1024, 2, 2, 0],
105
+ '4,1408,3840,1,0': [4, 1024, 4, 1, 0],
106
+ '4,1802,1408,0,0': [2, 512, 2, 3, 0],
107
+ '4,1802,1408,1,0': [2, 512, 2, 4, 0],
108
+ '4,32768,1408,0,0': [64, 64, 4, 4, 1],
109
+ '4,3840,1408,0,1': [16, 256, 2, 3, 1],
110
+ '4,3840,1408,1,1': [32, 256, 4, 3, 1],
111
+ '8,1408,1280,0,0': [16, 256, 2, 4, 1],
112
+ '8,1408,1280,1,0': [32, 256, 4, 4, 1],
113
+ '8,1408,3840,0,0': [16, 256, 2, 4, 1],
114
+ '8,1408,3840,1,0': [4, 512, 4, 4, 0],
115
+ '8,1802,1408,0,0': [32, 256, 4, 3, 1],
116
+ '8,1802,1408,1,0': [32, 128, 4, 4, 1],
117
+ '8,32768,1408,0,0': [128, 64, 4, 4, 1],
118
+ '8,3840,1408,0,1': [32, 256, 4, 3, 1],
119
+ '8,3840,1408,1,1': [32, 128, 4, 4, 1]}}}
luau_infer.py CHANGED
@@ -1,6 +1,13 @@
 
 
 
 
 
 
1
  from __future__ import annotations
2
 
3
  import math
 
4
  import sys
5
  import time
6
  from collections import deque
@@ -11,7 +18,7 @@ from torch import Tensor
11
 
12
  from .luau_kernels import kernels as K
13
 
14
- K_ = K # (``K`` is also the draft length in the decode code)
15
 
16
  E4M3 = torch.float8_e4m3fn
17
 
@@ -19,7 +26,10 @@ E4M3 = torch.float8_e4m3fn
19
  def supported(config) -> str | None:
20
  """None if the engine can run this config, else why not."""
21
  if getattr(config, "iha", False):
22
- return "IHA layers (no decode kernels yet)"
 
 
 
23
  if not config.qk_norm:
24
  return "needs qk_norm"
25
  if config.num_query_heads // config.num_key_value_heads > 64:
@@ -46,12 +56,17 @@ class _Linear:
46
  if quant == "fp8":
47
  self.w8, self.scale = _q8(self.w)
48
 
 
 
 
 
 
49
  def gemv(self, x: Tensor, y: Tensor, epi: int = 0, n: int | None = None, up_off: int = 0,
50
- cap: float = 0.0) -> None:
51
  if self.w8 is not None:
52
- K.gemv(x, self.w8, y, epi, self.scale, n, up_off, cap)
53
  else:
54
- K.gemv(x, self.w, y, epi, None, n, up_off, cap)
55
 
56
 
57
  class _Layer:
@@ -59,16 +74,17 @@ class _Layer:
59
 
60
 
61
  class Engine:
62
- """Caches, work buffers and CUDA graphs for decoding one ``LuauCoderForCausalLM``: ``prefill``
63
- a (left-padded) prompt batch, then ``step`` a decode graph (``graph(K, settings)``)."""
64
 
65
- MAX_GEMV_ROWS = 16 # rows the skinny GEMV takes (tensor-core tiles from 4); more go to cuBLAS
66
- NSPL = 64 # flash-decoding spans per (row, KV head); their length follows the context
67
  L_ROUND = 256 # cache length granularity
68
- DECODE_T = 16 # tokens per row the decode kernels take; longer calls (prompts) take the prompt path
69
- MAX_DRAFT = DECODE_T - 1 # a verify is K+1 tokens per row (also < the latent ring - 2)
70
 
71
- @torch.no_grad() # (quantising / concatenating parameters must not build autograd graphs)
 
 
72
  def __init__(self, model, quant: str | None = None):
73
  cfg = self.config = model.config
74
  why = supported(cfg)
@@ -84,12 +100,12 @@ class Engine:
84
  self.F = cfg.intermediate_size
85
  self.eps = cfg.rms_norm_eps
86
  self.diff, self.xsa = cfg.diff_v2, cfg.xsa
87
- # output gate: 0 off, 1 per channel (W_g rows in qkv), 2 per head (gate_proj rows appended)
88
  self.gate = {"elementwise": 1, "headwise": 2}.get(cfg.output_gate, 0)
89
  self.gate_w = {1: self.nh * self.hd, 2: self.nh}.get(self.gate, 0) # gate logits per row
90
  self.ccgqa = cfg.ccgqa
91
  self.attn_res = cfg.attn_res
92
- self.delta = bool(cfg.attn_res and cfg.attn_res_delta_block) # Delta Block: mix + stream
93
  self.partial = not self.delta or bool(cfg.attn_res_partial_source) # the partial is routed
94
  self.S_blk = cfg.attn_res_block_size if self.attn_res else 0
95
  self.n_src = (math.ceil(self.L / self.S_blk) + 1) if self.attn_res else 1
@@ -99,14 +115,31 @@ class Engine:
99
  self.gate_off = self.v_off + self.nkv * self.hd
100
  self.lam_off = self.gate_off + self.gate_w
101
  self.n_qkv = self.lam_off + (self.nh if self.diff else 0)
 
 
 
 
 
 
 
 
 
 
 
 
102
  self.embed = m.embed.weight
103
  self.head = _Linear(model.lm_head.weight, None)
 
 
 
 
 
104
  self.final_g = m.norm.weight.float().contiguous()
105
  f32 = lambda t: t.detach().float().contiguous()
106
  self.layers: list[_Layer] = []
107
  for i, blk in enumerate(m.blocks):
108
  a, ly = blk.attn, _Layer()
109
- w = a.qkv.weight # q | k | v (| per-channel W_g) (| per-head W_g) (| W_lambda)
110
  if a.gate_proj is not None:
111
  w = torch.cat((w, a.gate_proj.weight), 0)
112
  if self.diff:
@@ -115,14 +148,22 @@ class Engine:
115
  ly.w13, ly.w2 = _Linear(blk.ffn.w13.weight, quant), _Linear(blk.ffn.w2.weight, quant)
116
  ly.attn_g, ly.ffn_g = f32(blk.attn_norm.weight), f32(blk.ffn_norm.weight)
117
  ly.rope = a.rope
 
118
  ly.qg = f32(a.q_norm.weight)
119
  kg = a.k_norm.weight.float()[None].expand(self.nkv, self.hd)
120
- if self.ccgqa:
 
 
 
 
 
 
121
  kg = kg * a.k_temp.float().exp()[:, None]
 
122
  ly.conv = tuple(t.detach().contiguous() for t in
123
  (a.conv_seq, a.conv_seq_bias, a.conv_head, a.conv_head_bias))
124
  ly.ks, ly.kc = a.conv_seq.shape[1], a.conv_head.shape[1]
125
- else:
126
  ly.conv, ly.ks, ly.kc = None, 1, 1
127
  ly.kg = kg.contiguous()
128
  if self.attn_res:
@@ -142,7 +183,6 @@ class Engine:
142
  self.B = self.Lc = 0
143
  self.graphs: dict = {}
144
 
145
- # buffers
146
 
147
  def _rope_tables(self, n: int) -> None:
148
  if self.cos is None or self.cos.shape[0] < n:
@@ -151,27 +191,33 @@ class Engine:
151
  self.cos, self.sin = cos[0].contiguous(), sin[0].contiguous()
152
 
153
  def allocate(self, B: int, L: int) -> None:
154
- """Caches for ``B`` sequences of up to ``L`` tokens (kept while big enough:
155
- CUDA graphs hold the addresses)."""
156
  L = -(-L // self.L_ROUND) * self.L_ROUND
157
  if B == self.B and L <= self.Lc:
158
  return
159
  L = max(L, self.Lc if B == self.B else 0)
160
- self.graphs.clear() # free the old graphs' pools and caches before the new ones (8 GB cards)
161
- self.kc = self.vc = self.lat = self.vh = self.hist = self.pres = None
162
  torch.cuda.empty_cache()
163
  self.B, self.Lc = B, L
164
  dev, dt = self.dev, self.dtype
165
- self.kc = [torch.zeros(B, self.nkv, L, self.hd, device=dev, dtype=dt) for _ in range(self.L)]
166
- self.vc = [torch.zeros(B, self.nkv, L, self.hd, device=dev, dtype=dt) for _ in range(self.L)]
 
 
 
 
 
 
 
167
  if self.ccgqa:
168
- ring = K.RING.value # conv latents / value-shift halves: only the last 2 slots are read
169
  self.lat = [torch.zeros(B, ring, self.nxq + self.nkv, self.hd, device=dev, dtype=dt)
170
  for _ in range(self.L)]
171
  self.vh = [torch.zeros(B, ring, self.nkv * self.hd // 2, device=dev, dtype=dt) for _ in range(self.L)]
172
  else:
173
  self.lat = self.vh = [None] * self.L
174
- self._rope_tables(L)
175
  self.start = torch.zeros(B, dtype=torch.int32, device=dev)
176
  self.pres = torch.zeros(B, self.config.vocab_size, dtype=torch.bool, device=dev)
177
  self.done = torch.zeros(B, dtype=torch.bool, device=dev)
@@ -179,65 +225,108 @@ class Engine:
179
  self.cur = torch.zeros(B, dtype=torch.int32, device=dev)
180
  self.pcur = torch.zeros(B, dtype=torch.int32, device=dev)
181
  self.seed = torch.zeros(1, dtype=torch.long, device=dev)
 
 
 
 
 
 
 
 
 
 
 
 
 
182
 
183
  def _bufs(self, R: int, decode: bool = True) -> dict:
184
- """Work buffers for ``R`` rows (``decode``: with the decode-attention scratch; a prompt
185
- over ``DECODE_T`` tokens per row attends through sdpa and needs none)."""
186
  dev, dt = self.dev, self.dtype
187
  G = self.nq // self.nkv
188
  g_pad = max(16, 1 << (G - 1).bit_length())
189
  nspl = self.NSPL if decode else 0
190
- return dict(
 
191
  S=torch.zeros(R, self.n_src, self.D, device=dev, dtype=dt),
192
  xn=torch.empty(R, self.D, device=dev, dtype=dt),
193
  qkv=torch.empty(R, self.n_qkv, device=dev, dtype=dt),
194
- q=torch.empty(R, self.nq, self.hd, device=dev, dtype=dt),
195
- y=torch.zeros(R, self.nh * self.hd, device=dev, dtype=dt), # (prefill pad rows: never written)
196
  f=torch.empty(R, self.F, device=dev, dtype=dt),
197
- ml=torch.empty(R, self.nkv, nspl, 2, g_pad, device=dev, dtype=torch.float32),
198
- acc=torch.empty(R, self.nkv, nspl, g_pad, self.hd, device=dev, dtype=torch.float32),
199
  cnt=torch.zeros(R, self.nkv, device=dev, dtype=torch.int32),
200
  )
 
 
 
 
 
 
 
 
201
 
202
- # forward
203
 
204
  def _forward(self, bf: dict, ids: Tensor, pos: Tensor, slot: Tensor, start: Tensor, T: int,
205
  last: Tensor | None, logits: Tensor) -> None:
206
- """Rows ``R = B * T`` (``T`` consecutive tokens per sequence). Writes the caches
207
- and the logits of rows ``last`` (None: all) into ``logits`` (fp32)."""
208
  R = ids.shape[0]
209
  small = R <= self.MAX_GEMV_ROWS
210
  S, xn, qkv, q, y, f = bf["S"], bf["xn"], bf["qkv"], bf["q"], bf["y"], bf["f"]
 
 
 
 
 
 
 
 
 
 
211
  for i, ly in enumerate(self.layers):
212
  # ---- attention input
213
  if i == 0:
214
- K.mix_norm(S, 1, 1, xn, ly.attn_g, self.eps, ids=ids, emb=self.embed)
215
  elif self.attn_res:
216
  K.mix_norm(S, ly.attn_ne, 2, xn, ly.attn_g, self.eps, w=ly.attn_w, add=self.delta,
217
- partial=self.partial)
218
  else:
219
- K.mix_norm(S, 1, 0, xn, ly.attn_g, self.eps)
220
  self._lin(ly.qkv, xn, qkv, small)
221
- if self.ccgqa and T > self.DECODE_T: # a prompt: token tiles (one conv-weight read per tile)
222
- K.qk_prefill(qkv, self.lat[i], self.vh[i], pos, self.cos, self.sin, ly.conv, ly.qg, ly.kg, q,
223
- self.kc[i], self.vc[i], T, self.eps, nq=self.nq, nkv=self.nkv, nxq=self.nxq,
 
 
224
  hd=self.hd, v_off=self.v_off, ks=ly.ks, kc_taps=ly.kc,
225
  qk_mean=bool(self.config.ccgqa_qk_mean),
226
- vshift=bool(self.config.ccgqa_value_shift), rope=ly.rope)
227
  else:
228
- K.qk_decode(qkv, self.lat[i], self.vh[i], pos, slot, self.cos, self.sin, ly.conv, ly.qg, ly.kg, q,
229
- self.kc[i], self.vc[i], T, self.eps, nq=self.nq, nkv=self.nkv, nxq=self.nxq,
230
  hd=self.hd, v_off=self.v_off, ks=ly.ks, kc_taps=ly.kc, ccgqa=self.ccgqa,
231
  qk_mean=bool(self.ccgqa and self.config.ccgqa_qk_mean),
232
- vshift=bool(self.ccgqa and self.config.ccgqa_value_shift), rope=ly.rope)
233
- if T <= self.DECODE_T:
234
- K.attention(q, self.kc[i], self.vc[i], slot, start, qkv, y, bf["ml"], bf["acc"], bf["cnt"], T,
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  nq=self.nq, nkv=self.nkv, hd=self.hd, nspl=self.NSPL,
236
  lam_off=self.lam_off, gate_off=self.gate_off, diff=self.diff, xsa=self.xsa,
237
- gate=self.gate)
238
  else:
239
- self._attention_sdpa(i, q, qkv, y, slot, start, T)
240
- # ---- attention output -> partial (AttnRes: source slot block+1)
241
  dst = S[:, ly.block + 1] if self.attn_res else S[:, 0]
242
  fresh = self.attn_res and ly.block_start
243
  self._lin(ly.o, y, dst, small, epi=0 if fresh else 1)
@@ -247,36 +336,42 @@ class Engine:
247
  partial=self.partial)
248
  else:
249
  K.mix_norm(S, 1, 0, xn, ly.ffn_g, self.eps)
250
- self._lin(ly.w13, xn, f, small, epi=2)
251
  self._lin(ly.w2, f, dst, small, epi=1)
252
  # ---- output
253
  Sl = S if last is None else S[last]
254
- xo = xn if last is None else xn[:Sl.shape[0]]
 
 
255
  if self.delta:
256
  K.mix_norm(Sl, self.n_src, 3, xo, self.final_g, self.eps)
257
  elif self.attn_res:
258
  K.mix_norm(Sl, self.n_src, 2, xo, self.final_g, self.eps, w=self.out_w)
259
  else:
260
  K.mix_norm(Sl, 1, 0, xo, self.final_g, self.eps)
 
 
 
 
 
261
  if xo.shape[0] <= self.MAX_GEMV_ROWS:
262
  self.head.gemv(xo, logits, epi=3, cap=self.cap)
263
  else:
264
  z = (xo @ self.head.w.T).float()
265
  logits.copy_(self.cap * torch.tanh(z / self.cap) if self.cap else z)
266
 
267
- def _lin(self, lin: _Linear, x: Tensor, y: Tensor, small: bool, epi: int = 0) -> None:
268
- """``y = x @ W.T`` with the GEMV epilogue ``epi`` (0 store, 1 add, 2 SwiGLU): the skinny
269
- GEMV for decode-sized ``small`` calls, else cuBLAS + the epilogue in torch."""
270
  if small:
271
  if epi == 2:
272
- lin.gemv(x, y, 2, n=self.F, up_off=self.F)
273
  else:
274
- lin.gemv(x, y, epi)
275
  return
276
  z = x @ lin.w.T
277
  if epi == 2:
278
  g, u = z.chunk(2, -1)
279
- torch.mul(F.silu(g), u, out=y) # bf16 like the port's eager SwiGLU (no fp32 copies)
280
  elif epi == 1:
281
  y.add_(z)
282
  else:
@@ -289,12 +384,12 @@ class Engine:
289
  R = q.shape[0]
290
  B = R // T
291
  sl = slot.view(B, T).long()
292
- n = T # a prompt: slots 0 .. T-1
293
  qq = q.view(B, T, self.nq, self.hd).transpose(1, 2)
294
  kk, vv = self.kc[i][:, :, :n], self.vc[i][:, :, :n]
295
- if not self._padded: # causal flash attention (the masked path can't skip the upper half)
296
  o = F.scaled_dot_product_attention(qq, kk, vv, is_causal=True, enable_gqa=True)
297
- else: # left padding: a row's keys from its first real token; pad rows see themselves (no empty rows)
298
  keys = torch.arange(n, device=self.dev)
299
  mask = ((keys >= start.view(B, T, 1).long()) & (keys <= sl[..., None])) | (keys == sl[..., None])
300
  o = F.scaled_dot_product_attention(qq, kk, vv, attn_mask=mask[:, None], enable_gqa=True)
@@ -304,12 +399,43 @@ class Engine:
304
  out = _diff_xsa_fn(o.transpose(1, 2), lam, own, self.nh, self.nkv, self.xsa, gate)
305
  y.copy_(out.reshape(R, -1))
306
 
307
- # sampling
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
308
 
309
  @staticmethod
310
  def process(logits: Tensor, sp: dict, pres: Tensor | None) -> Tensor:
311
- """transformers' logits processors in ``generate()``'s order, in torch: the reference
312
- the sampler kernel (``luau_kernels.sample``) is tested against."""
313
  x = logits
314
  if sp["repetition_penalty"] != 1.0:
315
  p = sp["repetition_penalty"]
@@ -343,11 +469,7 @@ class Engine:
343
  ids=y, T=1)
344
  return y
345
 
346
- # prefill / decode
347
- #
348
- # Decode state (device): ``hist`` (B, Lc) every token by cache slot, ``cur`` the slot of
349
- # the newest token (sampled, not yet run through the model), ``pcur`` its document
350
- # position, ``done`` (EOS seen / stopped by the host: the row is frozen).
351
 
352
  def prefill(self, input_ids: Tensor, attention_mask: Tensor, sp: dict) -> Tensor:
353
  """Fill the caches with left-padded prompts ``(B, T0)``; returns the first sampled tokens."""
@@ -357,20 +479,20 @@ class Engine:
357
  self.start.copy_(T0 - am.sum(1))
358
  self.done.zero_()
359
  self.pres.zero_()
360
- self.seed.fill_(int(torch.randint(1 << 30, (1,)))) # the sampler kernel's Philox seed (manual_seed)
361
- if sp["repetition_penalty"] != 1.0: # every prompt id, pads too (as transformers' processor)
362
  self.pres.scatter_(1, input_ids, True)
363
  R = B * T0
364
- bf = self._bufs(R, decode=T0 <= self.DECODE_T)
365
  pos = (am.cumsum(1) - 1).clamp_min(0).to(torch.int32).reshape(R)
366
  slot = torch.arange(T0, device=self.dev, dtype=torch.int32).repeat(B)
367
  start = self.start.repeat_interleave(T0)
368
  last = torch.arange(B, device=self.dev) * T0 + T0 - 1
369
  logits = torch.empty(B, self.config.vocab_size, device=self.dev, dtype=torch.float32)
370
  self._forward(bf, input_ids.reshape(R), pos, slot, start, T0, last, logits)
371
- self.last_logits = logits # (the prompts' next-token logits, for inspection / tests)
372
  first = self.sample(logits, sp).masked_fill(self.done, sp["pad"])
373
- self.done |= first == sp["eos"]
374
  if sp["repetition_penalty"] != 1.0:
375
  self.pres.scatter_(1, first[:, None], True)
376
  self.hist[:, :T0] = input_ids
@@ -379,34 +501,28 @@ class Engine:
379
  self.pcur.copy_(pos.view(B, T0)[:, -1] + 1)
380
  return first
381
 
382
- NGRAM = 3 # longest suffix the draft looks up
383
 
384
  def _draft(self, K: int, st: dict) -> Tensor:
385
- """Prompt-lookup draft ``(B, K)``: the tokens after the latest earlier occurrence of
386
- the longest (up to ``NGRAM``) suffix of the history; no occurrence: anything."""
387
  Lc, NG = self.Lc, self.NGRAM
388
  cur = self.cur.long()[:, None]
389
  hp = st["hp"]
390
  hp[:, NG:].copy_(self.hist)
391
  j = st["jidx"]
392
- tails = self.hist.gather(1, (cur - st["ng"]).clamp_min(0)) # tails[:, i] = hist[cur - i]
393
  lo = self.start.long()[:, None]
394
  m = j < cur
395
  best = cur[:, 0] - 1
396
  for i in range(NG):
397
- m = m & (hp[:, NG - i:NG - i + Lc] == tails[:, i:i + 1]) # hist[j - i] == hist[cur - i]
398
  cand = torch.where(m & (j - i >= lo) & (cur - i >= lo), j, -1).amax(1)
399
  best = torch.where(cand >= 0, cand, best)
400
  idx = (best[:, None] + 1 + st["ark"]).clamp_max(cur).clamp(0, Lc - 1)
401
  return self.hist.gather(1, idx)
402
 
403
  def _step(self, st: dict, K: int, sp: dict) -> None:
404
- """One decode step: the newest token plus a ``K``-token draft through the model
405
- (``T = K+1`` rows per sequence), then accept the agreeing draft prefix and sample
406
- the token after it. Greedy: exactly greedy decoding. Sampling: speculative
407
- sampling with a point-mass draft (accept ``d`` with prob ``p(d)``, else draw from
408
- ``p`` without ``d``): exactly the model's distribution. A repetition penalty counts
409
- each verify row's earlier draft tokens as seen, as sequential decoding would."""
410
  B, T, V, Lc = self.B, K + 1, self.config.vocab_size, self.Lc
411
  ar, ar64 = st["ar"], st["ar64"]
412
  cur = self.cur
@@ -417,7 +533,7 @@ class Engine:
417
  pos = self.pcur[:, None] + ar
418
  self._forward(st["bf"], ids.reshape(-1), pos.reshape(-1), slot.reshape(-1),
419
  self.start.repeat_interleave(T), T, None, st["logits"])
420
- if sp["sample"] or sp["repetition_penalty"] != 1.0: # one Triton kernel per row
421
  self.seed.add_(1)
422
  R = B * T
423
  y, pd, u = st["y"], st["pd"], st["u"]
@@ -445,8 +561,8 @@ class Engine:
445
  out = y
446
  frozen = self.done.clone()
447
  out = torch.where(frozen[:, None] | (ar64 > n[:, None]), sp["pad"], out)
448
- self.done |= (out == sp["eos"]).any(1)
449
- if sp["repetition_penalty"] != 1.0: # every token this step emitted
450
  self.pres.scatter_(1, torch.where(ar64 <= n[:, None], out, out[:, :1]), True)
451
  self.hist.scatter_(1, (cur.long()[:, None] + 1 + ar64).clamp_max(Lc - 1), out)
452
  adv = torch.where(frozen, 0, n + 1).to(torch.int32)
@@ -463,13 +579,12 @@ class Engine:
463
  t.copy_(s)
464
 
465
  def graph(self, K: int, sp: dict) -> dict:
466
- """The CUDA graph of a decode step with a ``K``-token draft (built once per
467
- cache / sampling settings; warm-up runs are undone)."""
468
  key = (K, tuple(sorted(sp.items())))
469
  if key in self.graphs:
470
  return self.graphs[key]
471
- if K > self.MAX_DRAFT:
472
- raise ValueError(f"draft length {K}: at most {self.MAX_DRAFT}")
473
  B, T, dev = self.B, K + 1, self.dev
474
  st = dict(bf=self._bufs(B * T), K=K,
475
  logits=torch.empty(B * T, self.config.vocab_size, device=dev, dtype=torch.float32),
@@ -491,7 +606,6 @@ class Engine:
491
  with torch.cuda.graph(g):
492
  self._step(st, K, sp)
493
  st["graph"] = g
494
- # time it (for the speculation policy), then undo
495
  g.replay()
496
  torch.cuda.synchronize()
497
  e0, e1 = torch.cuda.Event(enable_timing=True), torch.cuda.Event(enable_timing=True)
@@ -507,22 +621,15 @@ class Engine:
507
 
508
  @staticmethod
509
  def step(st: dict) -> Tensor:
510
- """Replay a step graph: ``(B, K+2)`` = the new tokens (``pad`` after the accepted
511
- ones) and the number of accepted draft tokens (-1: the row was frozen)."""
512
  st["graph"].replay()
513
  return st["res"]
514
 
515
 
516
- # ---------decoding loop
517
-
518
-
519
  class _Lookup:
520
- """Host mirror of the engine's prompt-lookup draft (``Engine._draft``) for one row:
521
- how many draft tokens speculation would have accepted at each recent position
522
- (greedy: exactly; sampling: the same odds), and from that the tokens per step a
523
- run of speculative steps would have made there. It decides when to speculate."""
524
 
525
- WINDOW = 96 # recent positions the estimate looks at
526
 
527
  def __init__(self, K: int, ng: int):
528
  self.K, self.ng = K, ng
@@ -532,8 +639,7 @@ class _Lookup:
532
  self.acc: dict[int, int] = {} # position -> draft tokens it would accept
533
 
534
  def add(self, t: int) -> None:
535
- """The row's next token: resolves the pending drafts it continues or breaks, then drafts
536
- from the new position."""
537
  for d in self.pending:
538
  if d[3] and d[1] < len(d[0]) and d[0][d[1]] == t:
539
  d[1] += 1
@@ -570,26 +676,17 @@ class _Lookup:
570
 
571
 
572
  class FusedDecoder:
573
- """Decoding with ``Engine``: the prompt prefilled by the fused kernels, each decode
574
- step one CUDA graph of ~9 Triton kernels per layer with sampling on the device. The
575
- host reads tokens up to ``DEPTH`` steps behind the GPU (streaming, stop strings, EOS).
576
-
577
- With ``spec`` K > 0 a step can also verify a K-token prompt-lookup draft (same
578
- output: greedy stays greedy, sampling keeps the model's distribution). A K-step
579
- costs more than a plain one, so it runs only while the recent history says it
580
- pays: expected tokens per step (1 + accepted) above the measured cost ratio
581
- (sampled batches: off; ``spec < 0`` forces it at every step)."""
582
 
583
  DEPTH = 8
584
- MARGIN = 1.1 # speculate when the expected tokens per step beat the step cost ratio by this
585
- # sampled batches: their verify steps cost ~2x a plain one and the estimate (each row's own
586
- # history, read DEPTH steps late) misjudged them in benchmarks: off unless forced (spec < 0)
587
  MARGIN_BATCH_SAMPLED = float("inf")
588
- MIN_CACHE = 4096 # cache length allocated at least for B <= 8 (graphs are reused while it fits)
589
 
590
  def __init__(self, model, quant: str | None = None, spec: int = 0):
591
  self.engine = Engine(model, quant)
592
- self.spec, self.always = abs(spec), spec < 0 # negative: speculate at every step (testing)
593
  self.stats = {}
594
 
595
  def generate(self, input_ids: Tensor, attention_mask: Tensor, args, tok, streamer=None) -> Tensor:
@@ -603,18 +700,17 @@ class FusedDecoder:
603
 
604
  def decode(self, input_ids: Tensor, attention_mask: Tensor | None, *, max_new_tokens: int,
605
  sample: bool = False, temperature: float = 1.0, top_k: int = 0, top_p: float = 1.0,
606
- min_p: float = 0.0, repetition_penalty: float = 1.0, num_samples: int = 1, eos: int | None = None,
607
- pad: int | None = None, stop=(), tokenizer=None, streamer=None) -> Tensor:
608
- """Prompt + completion ids ``(B * num_samples, T0 + n)`` with ``pad`` after a row's end,
609
- like transformers' ``generate()``. Left-padded prompts; ``stop`` strings need ``tokenizer``."""
610
  eng = self.engine
611
  if attention_mask is None:
612
  attention_mask = torch.ones_like(input_ids)
613
- eos = -1 if eos is None else int(eos)
614
- pad = int(pad) if pad is not None else max(eos, 0)
615
  stop = list(stop or ())
616
  sp = dict(sample=bool(sample), temperature=float(temperature), top_k=int(top_k or 0), top_p=float(top_p),
617
- min_p=float(min_p or 0.0), repetition_penalty=float(repetition_penalty), eos=eos, pad=pad)
618
  if not sp["sample"]:
619
  sp.update(temperature=1.0, top_k=0, top_p=1.0, min_p=0.0)
620
  if num_samples > 1:
@@ -627,13 +723,18 @@ class FusedDecoder:
627
  margin = self.MARGIN_BATCH_SAMPLED if sp["sample"] and B > 1 else self.MARGIN
628
  K = self.spec if self.always or margin < float("inf") else 0
629
  eng.allocate(B, max(T0 + n_new + (self.DEPTH + 3) * (K + 1), self.MIN_CACHE if B <= 8 else 0))
 
630
  first = eng.prefill(input_ids, attention_mask, sp)
 
 
 
 
631
  if streamer is not None:
632
  streamer.put(input_ids.cpu()) # skipped as the prompt
633
  t_setup = time.perf_counter()
634
  g0 = eng.graph(0, sp) if n_new > 1 else None
635
  gk = eng.graph(K, sp) if K and n_new > 1 else None
636
- setup = time.perf_counter() - t_setup # first use per batch size / settings: compile + capture
637
  ratio = gk["ms"] / g0["ms"] if gk else 0.0
638
  looks = None
639
  if gk:
@@ -641,7 +742,7 @@ class FusedDecoder:
641
  for b, row in enumerate(input_ids.tolist()):
642
  for t in row[int(T0 - attention_mask[b].sum()):]:
643
  looks[b].add(t)
644
- pinned: dict[int, list[Tensor]] = {} # per step kind: a ring of pinned host buffers
645
  pushed: dict[int, int] = {}
646
  queue: deque = deque()
647
 
@@ -678,12 +779,22 @@ class FusedDecoder:
678
  gen[b].append(t)
679
  if looks is not None:
680
  looks[b].add(t)
681
- done[b] = t == eos or len(gen[b]) >= n_new or (bool(stop) and any(
682
  x in tokenizer.decode(gen[b][-tail:], skip_special_tokens=True) for x in stop))
683
  if done[b]:
684
- if t != eos: # the device doesn't know yet: freeze the row
685
  eng.done[b] = True
686
  break
 
 
 
 
 
 
 
 
 
 
687
  if streamer is not None and len(gen[0]) > streamed:
688
  streamer.put(torch.tensor(gen[0][streamed:]))
689
  streamed = len(gen[0])
 
1
+ """Fused decoding engine for the LuauCoder HF port.
2
+
3
+ ~9 Triton kernels per layer (``luau_kernels.py``), one CUDA graph per step, and
4
+ prompt-lookup speculative decoding that keeps the model's own output.
5
+ """
6
+
7
  from __future__ import annotations
8
 
9
  import math
10
+ import os
11
  import sys
12
  import time
13
  from collections import deque
 
18
 
19
  from .luau_kernels import kernels as K
20
 
21
+ K_ = K
22
 
23
  E4M3 = torch.float8_e4m3fn
24
 
 
26
  def supported(config) -> str | None:
27
  """None if the engine can run this config, else why not."""
28
  if getattr(config, "iha", False):
29
+ if config.diff_v2:
30
+ return "IHA with DiffAttn V2"
31
+ if not 1 <= config.iha_pseudo_heads <= 4:
32
+ return "IHA with more than 4 pseudo-heads"
33
  if not config.qk_norm:
34
  return "needs qk_norm"
35
  if config.num_query_heads // config.num_key_value_heads > 64:
 
56
  if quant == "fp8":
57
  self.w8, self.scale = _q8(self.w)
58
 
59
+ @property
60
+ def weight(self) -> Tensor:
61
+ """What the GEMV reads (the L2 prefetch target)."""
62
+ return self.w8 if self.w8 is not None else self.w
63
+
64
  def gemv(self, x: Tensor, y: Tensor, epi: int = 0, n: int | None = None, up_off: int = 0,
65
+ cap: float = 0.0, pf: Tensor | None = None) -> None:
66
  if self.w8 is not None:
67
+ K.gemv(x, self.w8, y, epi, self.scale, n, up_off, cap, pf=pf)
68
  else:
69
+ K.gemv(x, self.w, y, epi, None, n, up_off, cap, pf=pf)
70
 
71
 
72
  class _Layer:
 
74
 
75
 
76
  class Engine:
77
+ """Caches, work buffers and CUDA graphs for decoding one ``LuauCoderForCausalLM``."""
 
78
 
79
+ MAX_GEMV_ROWS = 16 # rows the skinny GEMV takes; more go to cuBLAS
80
+ NSPL = 32
81
  L_ROUND = 256 # cache length granularity
82
+ DECODE_T = 16 # tokens per row of the decode kernels; longer calls take the prompt path
83
+ MAX_DRAFT = DECODE_T - 1
84
 
85
+ MAX_TILE_ROWS = 128
86
+
87
+ @torch.no_grad()
88
  def __init__(self, model, quant: str | None = None):
89
  cfg = self.config = model.config
90
  why = supported(cfg)
 
100
  self.F = cfg.intermediate_size
101
  self.eps = cfg.rms_norm_eps
102
  self.diff, self.xsa = cfg.diff_v2, cfg.xsa
103
+ # output gate: 0 off, 1 per channel, 2 per head
104
  self.gate = {"elementwise": 1, "headwise": 2}.get(cfg.output_gate, 0)
105
  self.gate_w = {1: self.nh * self.hd, 2: self.nh}.get(self.gate, 0) # gate logits per row
106
  self.ccgqa = cfg.ccgqa
107
  self.attn_res = cfg.attn_res
108
+ self.delta = bool(cfg.attn_res and cfg.attn_res_delta_block)
109
  self.partial = not self.delta or bool(cfg.attn_res_partial_source) # the partial is routed
110
  self.S_blk = cfg.attn_res_block_size if self.attn_res else 0
111
  self.n_src = (math.ceil(self.L / self.S_blk) + 1) if self.attn_res else 1
 
115
  self.gate_off = self.v_off + self.nkv * self.hd
116
  self.lam_off = self.gate_off + self.gate_w
117
  self.n_qkv = self.lam_off + (self.nh if self.diff else 0)
118
+ self.iha_p = cfg.iha_pseudo_heads if getattr(cfg, "iha", False) else 0
119
+ self.win = cfg.iha_window * self.iha_p if self.iha_p else 0
120
+ P1 = max(self.iha_p, 1)
121
+ self.decode_t = (min(self.DECODE_T, self.MAX_TILE_ROWS // (P1 * (self.nq // self.nkv)))
122
+ if self.iha_p else self.DECODE_T)
123
+ self.max_draft = self.decode_t - 1
124
+ self.NSPL = int(os.environ.get("LUAU_NSPL", self.NSPL))
125
+ # IHA layers see at most win pseudo-keys: few long spans
126
+ self.win_nspl = int(os.environ.get("LUAU_WIN_NSPL", self.NSPL if not self.win else 16))
127
+ self.win_bl = int(os.environ.get("LUAU_WIN_BL", 128))
128
+ self.attn_bl = int(os.environ.get("LUAU_ATTN_BL", 64))
129
+ self.win_warps = int(os.environ.get("LUAU_WIN_WARPS", 0)) or None
130
  self.embed = m.embed.weight
131
  self.head = _Linear(model.lm_head.weight, None)
132
+ hg = getattr(model, "head_gate", None)
133
+ self.hg_rank = hg.rank if hg is not None else 0
134
+ if hg is not None:
135
+ self.hg_proj = _Linear(hg.proj.to(self.dtype), None)
136
+ self.hg_bias = hg.bias.detach().float().contiguous()
137
  self.final_g = m.norm.weight.float().contiguous()
138
  f32 = lambda t: t.detach().float().contiguous()
139
  self.layers: list[_Layer] = []
140
  for i, blk in enumerate(m.blocks):
141
  a, ly = blk.attn, _Layer()
142
+ w = a.qkv.weight # q | k | v (| W_g) (| W_lambda)
143
  if a.gate_proj is not None:
144
  w = torch.cat((w, a.gate_proj.weight), 0)
145
  if self.diff:
 
148
  ly.w13, ly.w2 = _Linear(blk.ffn.w13.weight, quant), _Linear(blk.ffn.w2.weight, quant)
149
  ly.attn_g, ly.ffn_g = f32(blk.attn_norm.weight), f32(blk.ffn_norm.weight)
150
  ly.rope = a.rope
151
+ ly.iha = bool(getattr(a, "iha", False))
152
  ly.qg = f32(a.q_norm.weight)
153
  kg = a.k_norm.weight.float()[None].expand(self.nkv, self.hd)
154
+ if ly.iha:
155
+ bf = lambda t: t.detach().to(self.dtype).contiguous()
156
+ ly.aq, ly.ak, ly.av, ly.ar = bf(a.iha_q), bf(a.iha_k), bf(a.iha_v), bf(a.iha_r)
157
+ ly.kg2 = f32(a.k_norm.weight)
158
+ ly.temp = (a.k_temp.float().exp() if self.ccgqa
159
+ else torch.ones(self.nkv, device=self.dev)).contiguous()
160
+ elif self.ccgqa:
161
  kg = kg * a.k_temp.float().exp()[:, None]
162
+ if self.ccgqa:
163
  ly.conv = tuple(t.detach().contiguous() for t in
164
  (a.conv_seq, a.conv_seq_bias, a.conv_head, a.conv_head_bias))
165
  ly.ks, ly.kc = a.conv_seq.shape[1], a.conv_head.shape[1]
166
+ if not self.ccgqa:
167
  ly.conv, ly.ks, ly.kc = None, 1, 1
168
  ly.kg = kg.contiguous()
169
  if self.attn_res:
 
183
  self.B = self.Lc = 0
184
  self.graphs: dict = {}
185
 
 
186
 
187
  def _rope_tables(self, n: int) -> None:
188
  if self.cos is None or self.cos.shape[0] < n:
 
191
  self.cos, self.sin = cos[0].contiguous(), sin[0].contiguous()
192
 
193
  def allocate(self, B: int, L: int) -> None:
194
+ """Caches for ``B`` sequences of up to ``L`` tokens (kept while big enough for the CUDA graphs)."""
 
195
  L = -(-L // self.L_ROUND) * self.L_ROUND
196
  if B == self.B and L <= self.Lc:
197
  return
198
  L = max(L, self.Lc if B == self.B else 0)
199
+ self.graphs.clear() # free the old graphs first (8 GB cards)
200
+ self.kc = self.vc = self.lat = self.vh = self.hist = self.pres = self.kl = self.vl = None
201
  torch.cuda.empty_cache()
202
  self.B, self.Lc = B, L
203
  dev, dt = self.dev, self.dtype
204
+ # IHA layers: interleaved pseudo-keys / -values
205
+ P1 = max(self.iha_p, 1)
206
+ self.kc = [torch.zeros(B, self.nkv, L * (P1 if ly.iha else 1), self.hd, device=dev, dtype=dt)
207
+ for ly in self.layers]
208
+ self.vc = [torch.zeros(B, self.nkv, L * (P1 if ly.iha else 1), self.hd, device=dev, dtype=dt)
209
+ for ly in self.layers]
210
+ if self.iha_p:
211
+ self.kl = torch.zeros(B, self.nkv, L, self.hd, device=dev, dtype=dt)
212
+ self.vl = torch.zeros(B, self.nkv, L, self.hd, device=dev, dtype=dt)
213
  if self.ccgqa:
214
+ ring = K.RING.value
215
  self.lat = [torch.zeros(B, ring, self.nxq + self.nkv, self.hd, device=dev, dtype=dt)
216
  for _ in range(self.L)]
217
  self.vh = [torch.zeros(B, ring, self.nkv * self.hd // 2, device=dev, dtype=dt) for _ in range(self.L)]
218
  else:
219
  self.lat = self.vh = [None] * self.L
220
+ self._rope_tables(L * P1)
221
  self.start = torch.zeros(B, dtype=torch.int32, device=dev)
222
  self.pres = torch.zeros(B, self.config.vocab_size, dtype=torch.bool, device=dev)
223
  self.done = torch.zeros(B, dtype=torch.bool, device=dev)
 
225
  self.cur = torch.zeros(B, dtype=torch.int32, device=dev)
226
  self.pcur = torch.zeros(B, dtype=torch.int32, device=dev)
227
  self.seed = torch.zeros(1, dtype=torch.long, device=dev)
228
+ self.eos_ids = torch.full((self.MAX_EOS,), -1, dtype=torch.long, device=dev)
229
+
230
+ MAX_EOS = 8
231
+
232
+ def set_eos(self, eos: tuple[int, ...]) -> None:
233
+ if len(eos) > self.MAX_EOS:
234
+ raise ValueError(f"at most {self.MAX_EOS} EOS ids, got {len(eos)}")
235
+ self.eos_ids.fill_(-1)
236
+ if eos:
237
+ self.eos_ids[:len(eos)] = torch.tensor(eos, dtype=torch.long, device=self.dev)
238
+
239
+ def _is_eos(self, t: Tensor) -> Tensor:
240
+ return (t[..., None] == self.eos_ids).any(-1)
241
 
242
  def _bufs(self, R: int, decode: bool = True) -> dict:
243
+ """Work buffers for ``R`` rows."""
 
244
  dev, dt = self.dev, self.dtype
245
  G = self.nq // self.nkv
246
  g_pad = max(16, 1 << (G - 1).bit_length())
247
  nspl = self.NSPL if decode else 0
248
+ P1 = max(self.iha_p, 1)
249
+ bf = dict(
250
  S=torch.zeros(R, self.n_src, self.D, device=dev, dtype=dt),
251
  xn=torch.empty(R, self.D, device=dev, dtype=dt),
252
  qkv=torch.empty(R, self.n_qkv, device=dev, dtype=dt),
253
+ q=torch.empty(R * P1, self.nq, self.hd, device=dev, dtype=dt),
254
+ y=torch.zeros(R, self.nh * self.hd, device=dev, dtype=dt),
255
  f=torch.empty(R, self.F, device=dev, dtype=dt),
256
+ ml=torch.empty(R * P1, self.nkv, nspl, 2, g_pad, device=dev, dtype=torch.float32),
257
+ acc=torch.empty(R * P1, self.nkv, nspl, g_pad, self.hd, device=dev, dtype=torch.float32),
258
  cnt=torch.zeros(R, self.nkv, device=dev, dtype=torch.int32),
259
  )
260
+ if self.iha_p:
261
+ bf["ql"] = torch.empty(R, self.nq, self.hd, device=dev, dtype=dt)
262
+ bf["vslot"] = torch.empty(R * P1, device=dev, dtype=torch.int32)
263
+ bf["vstart"] = torch.empty(R * P1, device=dev, dtype=torch.int32)
264
+ if self.hg_rank:
265
+ bf["haug"] = torch.zeros(R, self.D + self.hg_rank, device=dev, dtype=dt)
266
+ bf["hgy"] = torch.empty(R, self.hg_rank + 1, device=dev, dtype=dt)
267
+ return bf
268
 
 
269
 
270
  def _forward(self, bf: dict, ids: Tensor, pos: Tensor, slot: Tensor, start: Tensor, T: int,
271
  last: Tensor | None, logits: Tensor) -> None:
272
+ """Forward ``R = B * T`` rows: writes the caches and the fp32 logits of rows ``last`` (None: all)."""
 
273
  R = ids.shape[0]
274
  small = R <= self.MAX_GEMV_ROWS
275
  S, xn, qkv, q, y, f = bf["S"], bf["xn"], bf["qkv"], bf["q"], bf["y"], bf["f"]
276
+ P = self.iha_p
277
+ if P:
278
+ ar = torch.arange(P, device=self.dev, dtype=torch.int32)
279
+ torch.add(slot[:, None] * P, ar, out=bf["vslot"][:R * P].view(R, P))
280
+ bf["vstart"][:R * P].view(R, P).copy_((start * P)[:, None].expand(R, P))
281
+ vslot, vstart = bf["vslot"][:R * P], bf["vstart"][:R * P]
282
+ # L2 prefetch: each GEMV's weights stream in while the kernels before it run
283
+ sites = os.environ.get("LUAU_PF_SITES", "qk,w13").split(",")
284
+ pfs = {k: ((lambda lin: lin.weight) if small and k in sites else (lambda lin: None))
285
+ for k in ("mix", "qk", "attn", "w13")}
286
  for i, ly in enumerate(self.layers):
287
  # ---- attention input
288
  if i == 0:
289
+ K.mix_norm(S, 1, 1, xn, ly.attn_g, self.eps, ids=ids, emb=self.embed, pf=pfs["mix"](ly.qkv))
290
  elif self.attn_res:
291
  K.mix_norm(S, ly.attn_ne, 2, xn, ly.attn_g, self.eps, w=ly.attn_w, add=self.delta,
292
+ partial=self.partial, pf=pfs["mix"](ly.qkv))
293
  else:
294
+ K.mix_norm(S, 1, 0, xn, ly.attn_g, self.eps, pf=pfs["mix"](ly.qkv))
295
  self._lin(ly.qkv, xn, qkv, small)
296
+ ql, kc, vc = (bf["ql"], self.kl, self.vl) if ly.iha else (q, self.kc[i], self.vc[i])
297
+ rope = ly.rope and not ly.iha
298
+ if self.ccgqa and T > self.decode_t:
299
+ K.qk_prefill(qkv, self.lat[i], self.vh[i], pos, self.cos, self.sin, ly.conv, ly.qg, ly.kg, ql,
300
+ kc, vc, T, self.eps, nq=self.nq, nkv=self.nkv, nxq=self.nxq,
301
  hd=self.hd, v_off=self.v_off, ks=ly.ks, kc_taps=ly.kc,
302
  qk_mean=bool(self.config.ccgqa_qk_mean),
303
+ vshift=bool(self.config.ccgqa_value_shift), rope=rope)
304
  else:
305
+ K.qk_decode(qkv, self.lat[i], self.vh[i], pos, slot, self.cos, self.sin, ly.conv, ly.qg, ly.kg, ql,
306
+ kc, vc, T, self.eps, nq=self.nq, nkv=self.nkv, nxq=self.nxq,
307
  hd=self.hd, v_off=self.v_off, ks=ly.ks, kc_taps=ly.kc, ccgqa=self.ccgqa,
308
  qk_mean=bool(self.ccgqa and self.config.ccgqa_qk_mean),
309
+ vshift=bool(self.ccgqa and self.config.ccgqa_value_shift), rope=rope,
310
+ pf=pfs["qk"](ly.o))
311
+ if ly.iha:
312
+ K.iha_mix(ql, self.kl, self.vl, ly.aq, ly.ak, ly.av, ly.qg, ly.kg2, ly.temp, pos, slot,
313
+ self.cos, self.sin, q, self.kc[i], self.vc[i], T, self.eps, P=P, nq=self.nq,
314
+ nkv=self.nkv, hd=self.hd, rope=ly.rope)
315
+ if T <= self.decode_t:
316
+ K.attention(q[:R * P], self.kc[i], self.vc[i], vslot, vstart, qkv, y, bf["ml"], bf["acc"],
317
+ bf["cnt"], T, nq=self.nq, nkv=self.nkv, hd=self.hd, nspl=self.win_nspl,
318
+ bl=self.win_bl, warps=self.win_warps,
319
+ lam_off=self.lam_off, gate_off=self.gate_off, diff=False, xsa=self.xsa,
320
+ gate=self.gate, win=self.win, iha_p=P, iha_r=ly.ar, pf=pfs["attn"](ly.w13))
321
+ else:
322
+ self._iha_attention_sdpa(i, ly, q, qkv, y, start, T)
323
+ elif T <= self.decode_t:
324
+ K.attention(q[:R], self.kc[i], self.vc[i], slot, start, qkv, y, bf["ml"], bf["acc"], bf["cnt"], T,
325
  nq=self.nq, nkv=self.nkv, hd=self.hd, nspl=self.NSPL,
326
  lam_off=self.lam_off, gate_off=self.gate_off, diff=self.diff, xsa=self.xsa,
327
+ gate=self.gate, pf=pfs["attn"](ly.w13), bl=self.attn_bl)
328
  else:
329
+ self._attention_sdpa(i, q[:R], qkv, y, slot, start, T)
 
330
  dst = S[:, ly.block + 1] if self.attn_res else S[:, 0]
331
  fresh = self.attn_res and ly.block_start
332
  self._lin(ly.o, y, dst, small, epi=0 if fresh else 1)
 
336
  partial=self.partial)
337
  else:
338
  K.mix_norm(S, 1, 0, xn, ly.ffn_g, self.eps)
339
+ self._lin(ly.w13, xn, f, small, epi=2, pf=pfs["w13"](ly.w2))
340
  self._lin(ly.w2, f, dst, small, epi=1)
341
  # ---- output
342
  Sl = S if last is None else S[last]
343
+ n_out = Sl.shape[0]
344
+ xo = (bf["haug"][:n_out, :self.D] if self.hg_rank else
345
+ xn if last is None else xn[:n_out])
346
  if self.delta:
347
  K.mix_norm(Sl, self.n_src, 3, xo, self.final_g, self.eps)
348
  elif self.attn_res:
349
  K.mix_norm(Sl, self.n_src, 2, xo, self.final_g, self.eps, w=self.out_w)
350
  else:
351
  K.mix_norm(Sl, 1, 0, xo, self.final_g, self.eps)
352
+ if self.hg_rank:
353
+ hy = bf["hgy"][:n_out]
354
+ self._lin(self.hg_proj, xo, hy, n_out <= self.MAX_GEMV_ROWS)
355
+ K.head_gate(hy, self.hg_bias, bf["haug"], self.D, self.hg_rank)
356
+ xo = bf["haug"][:n_out]
357
  if xo.shape[0] <= self.MAX_GEMV_ROWS:
358
  self.head.gemv(xo, logits, epi=3, cap=self.cap)
359
  else:
360
  z = (xo @ self.head.w.T).float()
361
  logits.copy_(self.cap * torch.tanh(z / self.cap) if self.cap else z)
362
 
363
+ def _lin(self, lin: _Linear, x: Tensor, y: Tensor, small: bool, epi: int = 0, pf: Tensor | None = None) -> None:
364
+ """``y = x @ W.T`` with epilogue ``epi``: skinny GEMV for small calls, else cuBLAS."""
 
365
  if small:
366
  if epi == 2:
367
+ lin.gemv(x, y, 2, n=self.F, up_off=self.F, pf=pf)
368
  else:
369
+ lin.gemv(x, y, epi, pf=pf)
370
  return
371
  z = x @ lin.w.T
372
  if epi == 2:
373
  g, u = z.chunk(2, -1)
374
+ torch.mul(F.silu(g), u, out=y)
375
  elif epi == 1:
376
  y.add_(z)
377
  else:
 
384
  R = q.shape[0]
385
  B = R // T
386
  sl = slot.view(B, T).long()
387
+ n = T
388
  qq = q.view(B, T, self.nq, self.hd).transpose(1, 2)
389
  kk, vv = self.kc[i][:, :, :n], self.vc[i][:, :, :n]
390
+ if not self._padded:
391
  o = F.scaled_dot_product_attention(qq, kk, vv, is_causal=True, enable_gqa=True)
392
+ else:
393
  keys = torch.arange(n, device=self.dev)
394
  mask = ((keys >= start.view(B, T, 1).long()) & (keys <= sl[..., None])) | (keys == sl[..., None])
395
  o = F.scaled_dot_product_attention(qq, kk, vv, attn_mask=mask[:, None], enable_gqa=True)
 
399
  out = _diff_xsa_fn(o.transpose(1, 2), lam, own, self.nh, self.nkv, self.xsa, gate)
400
  y.copy_(out.reshape(R, -1))
401
 
402
+ IHA_CHUNK = 2048
403
+
404
+ def _iha_attention_sdpa(self, i: int, ly, q: Tensor, qkv: Tensor, y: Tensor, start: Tensor, T: int) -> None:
405
+ """Prefill attention of an IHA layer, in bands of ``IHA_CHUNK`` pseudo-rows."""
406
+ port = sys.modules[type(self.model).__module__]
407
+ P, nq, nkv, hd = self.iha_p, self.nq, self.nkv, self.hd
408
+ R = y.shape[0]
409
+ B = R // T
410
+ Tv = T * P
411
+ qq = q[:R * P].view(B, Tv, nq, hd).transpose(1, 2)
412
+ kk, vv = self.kc[i][:, :, :Tv], self.vc[i][:, :, :Tv]
413
+ out = torch.empty(B, Tv, nq, hd, device=self.dev, dtype=self.dtype)
414
+ first = start.view(B, T)[:, 0].long() * P
415
+ W = self.win
416
+ for a in range(0, Tv, self.IHA_CHUNK):
417
+ e = min(Tv, a + self.IHA_CHUNK)
418
+ k0 = max(0, a - W + 1) if W else 0
419
+ dq = torch.arange(a, e, device=self.dev)[:, None] - torch.arange(k0, e, device=self.dev)[None]
420
+ mask = dq >= 0
421
+ if W:
422
+ mask = mask & (dq < W)
423
+ if self._padded:
424
+ keys = torch.arange(k0, e, device=self.dev)
425
+ mask = (mask[None] & (keys >= first[:, None, None])) | (dq == 0)[None]
426
+ mask = mask[:, None]
427
+ o = F.scaled_dot_product_attention(qq[:, :, a:e], kk[:, :, k0:e], vv[:, :, k0:e], attn_mask=mask,
428
+ enable_gqa=True)
429
+ out[:, a:e] = o.transpose(1, 2)
430
+ yc = torch.einsum("btphd,hp->bthd", out.view(B, T, P, nq, hd), ly.ar)
431
+ vp = vv.reshape(B, nkv, T, P, hd).permute(0, 2, 3, 1, 4)
432
+ gate = qkv[:, self.gate_off:self.gate_off + self.gate_w].view(B, T, -1) if self.gate else None
433
+ y.copy_(port._iha_diff_xsa(yc, None, vp, self.nh, nkv, self.xsa, gate).reshape(R, -1))
434
+
435
 
436
  @staticmethod
437
  def process(logits: Tensor, sp: dict, pres: Tensor | None) -> Tensor:
438
+ """transformers' logits processors in torch (reference for the sampler kernel)."""
 
439
  x = logits
440
  if sp["repetition_penalty"] != 1.0:
441
  p = sp["repetition_penalty"]
 
469
  ids=y, T=1)
470
  return y
471
 
472
+ # decode state: hist (token by cache slot), cur (newest slot), pcur (its position), done
 
 
 
 
473
 
474
  def prefill(self, input_ids: Tensor, attention_mask: Tensor, sp: dict) -> Tensor:
475
  """Fill the caches with left-padded prompts ``(B, T0)``; returns the first sampled tokens."""
 
479
  self.start.copy_(T0 - am.sum(1))
480
  self.done.zero_()
481
  self.pres.zero_()
482
+ self.seed.fill_(int(torch.randint(1 << 30, (1,))))
483
+ if sp["repetition_penalty"] != 1.0:
484
  self.pres.scatter_(1, input_ids, True)
485
  R = B * T0
486
+ bf = self._bufs(R, decode=T0 <= self.decode_t)
487
  pos = (am.cumsum(1) - 1).clamp_min(0).to(torch.int32).reshape(R)
488
  slot = torch.arange(T0, device=self.dev, dtype=torch.int32).repeat(B)
489
  start = self.start.repeat_interleave(T0)
490
  last = torch.arange(B, device=self.dev) * T0 + T0 - 1
491
  logits = torch.empty(B, self.config.vocab_size, device=self.dev, dtype=torch.float32)
492
  self._forward(bf, input_ids.reshape(R), pos, slot, start, T0, last, logits)
493
+ self.last_logits = logits
494
  first = self.sample(logits, sp).masked_fill(self.done, sp["pad"])
495
+ self.done |= self._is_eos(first)
496
  if sp["repetition_penalty"] != 1.0:
497
  self.pres.scatter_(1, first[:, None], True)
498
  self.hist[:, :T0] = input_ids
 
501
  self.pcur.copy_(pos.view(B, T0)[:, -1] + 1)
502
  return first
503
 
504
+ NGRAM = 3
505
 
506
  def _draft(self, K: int, st: dict) -> Tensor:
507
+ """Prompt-lookup draft ``(B, K)`` from the latest earlier occurrence of the history's suffix."""
 
508
  Lc, NG = self.Lc, self.NGRAM
509
  cur = self.cur.long()[:, None]
510
  hp = st["hp"]
511
  hp[:, NG:].copy_(self.hist)
512
  j = st["jidx"]
513
+ tails = self.hist.gather(1, (cur - st["ng"]).clamp_min(0))
514
  lo = self.start.long()[:, None]
515
  m = j < cur
516
  best = cur[:, 0] - 1
517
  for i in range(NG):
518
+ m = m & (hp[:, NG - i:NG - i + Lc] == tails[:, i:i + 1])
519
  cand = torch.where(m & (j - i >= lo) & (cur - i >= lo), j, -1).amax(1)
520
  best = torch.where(cand >= 0, cand, best)
521
  idx = (best[:, None] + 1 + st["ark"]).clamp_max(cur).clamp(0, Lc - 1)
522
  return self.hist.gather(1, idx)
523
 
524
  def _step(self, st: dict, K: int, sp: dict) -> None:
525
+ """One decode step: the newest token plus a ``K``-token draft, then accept and sample."""
 
 
 
 
 
526
  B, T, V, Lc = self.B, K + 1, self.config.vocab_size, self.Lc
527
  ar, ar64 = st["ar"], st["ar64"]
528
  cur = self.cur
 
533
  pos = self.pcur[:, None] + ar
534
  self._forward(st["bf"], ids.reshape(-1), pos.reshape(-1), slot.reshape(-1),
535
  self.start.repeat_interleave(T), T, None, st["logits"])
536
+ if sp["sample"] or sp["repetition_penalty"] != 1.0:
537
  self.seed.add_(1)
538
  R = B * T
539
  y, pd, u = st["y"], st["pd"], st["u"]
 
561
  out = y
562
  frozen = self.done.clone()
563
  out = torch.where(frozen[:, None] | (ar64 > n[:, None]), sp["pad"], out)
564
+ self.done |= self._is_eos(out).any(1)
565
+ if sp["repetition_penalty"] != 1.0:
566
  self.pres.scatter_(1, torch.where(ar64 <= n[:, None], out, out[:, :1]), True)
567
  self.hist.scatter_(1, (cur.long()[:, None] + 1 + ar64).clamp_max(Lc - 1), out)
568
  adv = torch.where(frozen, 0, n + 1).to(torch.int32)
 
579
  t.copy_(s)
580
 
581
  def graph(self, K: int, sp: dict) -> dict:
582
+ """The CUDA graph of a decode step with a ``K``-token draft."""
 
583
  key = (K, tuple(sorted(sp.items())))
584
  if key in self.graphs:
585
  return self.graphs[key]
586
+ if K > self.max_draft:
587
+ raise ValueError(f"draft length {K}: at most {self.max_draft}")
588
  B, T, dev = self.B, K + 1, self.dev
589
  st = dict(bf=self._bufs(B * T), K=K,
590
  logits=torch.empty(B * T, self.config.vocab_size, device=dev, dtype=torch.float32),
 
606
  with torch.cuda.graph(g):
607
  self._step(st, K, sp)
608
  st["graph"] = g
 
609
  g.replay()
610
  torch.cuda.synchronize()
611
  e0, e1 = torch.cuda.Event(enable_timing=True), torch.cuda.Event(enable_timing=True)
 
621
 
622
  @staticmethod
623
  def step(st: dict) -> Tensor:
624
+ """Replay a step graph: ``(B, K+2)`` new tokens and the accepted draft count (-1: frozen)."""
 
625
  st["graph"].replay()
626
  return st["res"]
627
 
628
 
 
 
 
629
  class _Lookup:
630
+ """Host mirror of the engine's draft for one row: estimates whether speculation pays."""
 
 
 
631
 
632
+ WINDOW = 96
633
 
634
  def __init__(self, K: int, ng: int):
635
  self.K, self.ng = K, ng
 
639
  self.acc: dict[int, int] = {} # position -> draft tokens it would accept
640
 
641
  def add(self, t: int) -> None:
642
+ """Advance by the row's next token."""
 
643
  for d in self.pending:
644
  if d[3] and d[1] < len(d[0]) and d[0][d[1]] == t:
645
  d[1] += 1
 
676
 
677
 
678
  class FusedDecoder:
679
+ """Decoding loop around ``Engine``; speculates only while the recent history says it pays."""
 
 
 
 
 
 
 
 
680
 
681
  DEPTH = 8
682
+ MARGIN = 1.1
683
+ # sampled batches: speculation off unless forced (spec < 0)
 
684
  MARGIN_BATCH_SAMPLED = float("inf")
685
+ MIN_CACHE = 4096
686
 
687
  def __init__(self, model, quant: str | None = None, spec: int = 0):
688
  self.engine = Engine(model, quant)
689
+ self.spec, self.always = min(abs(spec), self.engine.max_draft), spec < 0 # < 0: always
690
  self.stats = {}
691
 
692
  def generate(self, input_ids: Tensor, attention_mask: Tensor, args, tok, streamer=None) -> Tensor:
 
700
 
701
  def decode(self, input_ids: Tensor, attention_mask: Tensor | None, *, max_new_tokens: int,
702
  sample: bool = False, temperature: float = 1.0, top_k: int = 0, top_p: float = 1.0,
703
+ min_p: float = 0.0, repetition_penalty: float = 1.0, num_samples: int = 1, eos=None,
704
+ pad: int | None = None, stop=(), tokenizer=None, streamer=None, stopping_criteria=None) -> Tensor:
705
+ """Prompt + completion ids ``(B * num_samples, T0 + n)``, ``pad`` after a row's end."""
 
706
  eng = self.engine
707
  if attention_mask is None:
708
  attention_mask = torch.ones_like(input_ids)
709
+ eos_set = tuple(dict.fromkeys(int(e) for e in ((eos,) if isinstance(eos, int) else (eos or ()))))
710
+ pad = int(pad) if pad is not None else (eos_set[0] if eos_set else 0)
711
  stop = list(stop or ())
712
  sp = dict(sample=bool(sample), temperature=float(temperature), top_k=int(top_k or 0), top_p=float(top_p),
713
+ min_p=float(min_p or 0.0), repetition_penalty=float(repetition_penalty), pad=pad)
714
  if not sp["sample"]:
715
  sp.update(temperature=1.0, top_k=0, top_p=1.0, min_p=0.0)
716
  if num_samples > 1:
 
723
  margin = self.MARGIN_BATCH_SAMPLED if sp["sample"] and B > 1 else self.MARGIN
724
  K = self.spec if self.always or margin < float("inf") else 0
725
  eng.allocate(B, max(T0 + n_new + (self.DEPTH + 3) * (K + 1), self.MIN_CACHE if B <= 8 else 0))
726
+ eng.set_eos(eos_set)
727
  first = eng.prefill(input_ids, attention_mask, sp)
728
+ crit_ids = None
729
+ if stopping_criteria:
730
+ crit_ids = torch.full((B, T0 + n_new + K + 1), pad, dtype=input_ids.dtype, device=input_ids.device)
731
+ crit_ids[:, :T0] = input_ids
732
  if streamer is not None:
733
  streamer.put(input_ids.cpu()) # skipped as the prompt
734
  t_setup = time.perf_counter()
735
  g0 = eng.graph(0, sp) if n_new > 1 else None
736
  gk = eng.graph(K, sp) if K and n_new > 1 else None
737
+ setup = time.perf_counter() - t_setup
738
  ratio = gk["ms"] / g0["ms"] if gk else 0.0
739
  looks = None
740
  if gk:
 
742
  for b, row in enumerate(input_ids.tolist()):
743
  for t in row[int(T0 - attention_mask[b].sum()):]:
744
  looks[b].add(t)
745
+ pinned: dict[int, list[Tensor]] = {}
746
  pushed: dict[int, int] = {}
747
  queue: deque = deque()
748
 
 
779
  gen[b].append(t)
780
  if looks is not None:
781
  looks[b].add(t)
782
+ done[b] = t in eos_set or len(gen[b]) >= n_new or (bool(stop) and any(
783
  x in tokenizer.decode(gen[b][-tail:], skip_special_tokens=True) for x in stop))
784
  if done[b]:
785
+ if t not in eos_set:
786
  eng.done[b] = True
787
  break
788
+ if crit_ids is not None and not all(done):
789
+ n = max(len(g) for g in gen)
790
+ for b, g in enumerate(gen):
791
+ if g:
792
+ crit_ids[b, T0:T0 + len(g)] = torch.tensor(g, dtype=crit_ids.dtype)
793
+ hit = torch.as_tensor(stopping_criteria(crit_ids[:, :T0 + n], None)).reshape(-1).tolist()
794
+ for b in range(B):
795
+ if not done[b] and hit[b if len(hit) > 1 else 0]:
796
+ done[b] = True
797
+ eng.done[b] = True
798
  if streamer is not None and len(gen[0]) > streamed:
799
  streamer.put(torch.tensor(gen[0][streamed:]))
800
  streamed = len(gen[0])
luau_kernels.py CHANGED
@@ -1,734 +1,966 @@
1
- from __future__ import annotations
2
-
3
- import os
4
- import sys
5
- from pathlib import Path
6
-
7
- import torch
8
-
9
- try: # (remote code: transformers only requires imports outside try blocks)
10
- import triton
11
- import triton.language as tl
12
- except ImportError as exc: # pragma: no cover
13
- raise ImportError("the fused LuauCoder decoding kernels need triton") from exc
14
-
15
- from .luau_gemv_tuned import TABLE as _TABLE
16
-
17
- LOG2E = 1.4426950408889634
18
-
19
- #: Programmatic dependent launch (SM90+): each kernel is launched while the previous one
20
- #: runs and waits for it in ``_pdl`` (griddepcontrol), hiding the launch gap between the
21
- #: ~260 small kernels of a decode step. ``LUAU_INFER_PDL=0`` turns it off.
22
- PDL = (torch.cuda.is_available() and torch.cuda.get_device_capability()[0] >= 9
23
- and os.environ.get("LUAU_INFER_PDL", "1") != "0")
24
-
25
-
26
- @triton.jit
27
- def _pdl(ON: tl.constexpr):
28
- """Wait for the previous kernel's writes, then let the next kernel launch."""
29
- if ON:
30
- tl.extra.cuda.gdc_wait()
31
- tl.extra.cuda.gdc_launch_dependents()
32
-
33
-
34
- # AttnRes mix + RMSNorm
35
-
36
-
37
- @triton.jit
38
- def _mix_norm_kernel(s_ptr, s_row, s_ent, ids_ptr, emb_ptr, w_ptr, g_ptr, out_ptr, o_row, eps, D,
39
- NE: tl.constexpr, NE_PAD: tl.constexpr, MODE: tl.constexpr, BLOCK: tl.constexpr,
40
- ADD: tl.constexpr = 0, PART: tl.constexpr = 1, PDL: tl.constexpr = False):
41
- """MODE 0: h = S[r, 0]; 1: h = embed[ids[r]] (also stored to S[r, 0]);
42
- 2: h = AttnRes mix of S[r, 0..NE-1] with score weight w (``ADD``: Delta Block,
43
- plus their sum; ``PART`` off: entry NE-1, the partial, isn't routed); 3: h =
44
- their sum (its output). out = rmsnorm(h) * g."""
45
- _pdl(PDL)
46
- r = tl.program_id(0).to(tl.int64)
47
- offs = tl.arange(0, BLOCK)
48
- msk = offs < D
49
- if MODE == 1:
50
- tok = tl.load(ids_ptr + r).to(tl.int64)
51
- hb = tl.load(emb_ptr + tok * D + offs, mask=msk, other=0.0)
52
- tl.store(s_ptr + r * s_row + offs, hb, mask=msk)
53
- h = hb.to(tl.float32)
54
- elif MODE == 0:
55
- h = tl.load(s_ptr + r * s_row + offs, mask=msk, other=0.0).to(tl.float32)
56
- else: # every entry in one 2-D tile: one reduction per quantity, not one per entry
57
- w = tl.load(w_ptr + offs, mask=msk, other=0.0)
58
- j = tl.arange(0, NE_PAD)
59
- jm = j < NE
60
- v = tl.load(s_ptr + r * s_row + j[:, None] * s_ent + offs[None, :], mask=jm[:, None] & msk[None, :],
61
- other=0.0).to(tl.float32)
62
- if MODE == 3:
63
- h = tl.sum(v, 0)
64
- else:
65
- sc = tl.sum(v * w[None, :], 1) * tl.rsqrt(tl.sum(v * v, 1) / D + eps)
66
- sc = tl.where(j < NE - 1 + PART, sc, float("-inf"))
67
- e = tl.exp(sc - tl.max(sc, 0))
68
- p = e / tl.sum(e, 0)
69
- h = tl.sum((p + ADD)[:, None] * v, 0)
70
- h = h.to(out_ptr.dtype.element_ty).to(tl.float32) # the mix output is bf16
71
- g = tl.load(g_ptr + offs, mask=msk, other=0.0).to(tl.float32)
72
- y = h * tl.rsqrt(tl.sum(h * h, 0) / D + eps) * g
73
- tl.store(out_ptr + r * o_row + offs, y.to(out_ptr.dtype.element_ty), mask=msk)
74
-
75
-
76
- def mix_norm(S: torch.Tensor, n_ent: int, mode: int, out: torch.Tensor, g: torch.Tensor, eps: float,
77
- w: torch.Tensor | None = None, ids: torch.Tensor | None = None,
78
- emb: torch.Tensor | None = None, add: bool = False, partial: bool = True) -> None:
79
- """``S`` ``(R, NS, D)`` sources (row-major per row), ``out`` ``(R, D)``."""
80
- R, _, D = S.shape
81
- block = triton.next_power_of_2(D)
82
- ne_pad = triton.next_power_of_2(n_ent) if mode >= 2 else 1
83
- warps = max(4, min(16, ne_pad * block // 1024))
84
- _mix_norm_kernel[(R,)](S, S.stride(0), S.stride(1), ids if ids is not None else S,
85
- emb if emb is not None else S, w if w is not None else g, g, out,
86
- out.stride(0), eps, D, NE=n_ent, NE_PAD=ne_pad, MODE=mode, BLOCK=block,
87
- ADD=int(add), PART=int(partial), num_warps=warps, PDL=PDL, launch_pdl=PDL)
88
-
89
-
90
- # skinny GEMM
91
-
92
-
93
- _TUNED_PATH = Path(__file__).with_name("luau_gemv_tuned.py")
94
- _TUNED: dict | None = None
95
-
96
-
97
- def _tuned() -> dict:
98
- """Pinned GEMV configs for this GPU (``benchmark/tune_gemv.py``): its own entry, else one
99
- of the same architecture and SM count, else {}."""
100
- global _TUNED
101
- if _TUNED is None:
102
- name = torch.cuda.get_device_name()
103
- p = torch.cuda.get_device_properties(0)
104
- arch = [p.major, p.minor, p.multi_processor_count]
105
- hit = _TABLE.get(name) or next((e for e in _TABLE.values() if e.get("arch") == arch), None)
106
- _TUNED = dict(hit["gemv"]) if hit else {}
107
- return _TUNED
108
-
109
-
110
- def gemv_key(m_pad: int, n: int, k: int, fp8: bool, epi: int) -> str:
111
- return f"{m_pad},{n},{k},{int(fp8)},{int(epi == 2)}"
112
-
113
-
114
- def gemv_config(m_pad: int, n: int, k: int, fp8: bool, epi: int) -> tuple[int, int, int, int, int]:
115
- """``(BLOCK_N, BLOCK_K, num_warps, num_stages, DOT)``: pinned, else a bandwidth-friendly
116
- default (``DOT``: tensor-core tiles, rows padded to 16; the CUDA-core FMAs of the
117
- broadcast path become the limit from ~4 rows)."""
118
- hit = _tuned().get(gemv_key(m_pad, n, k, fp8, epi))
119
- if hit:
120
- return tuple(hit) + (0,) * (5 - len(hit))
121
- if m_pad >= 4:
122
- return 32, 128, 4, 3, 1
123
- bk = 512 if fp8 else 256
124
- bn = max(1, min(16, 2048 // (bk * m_pad) * 4))
125
- return bn, bk, 4, 2, 0
126
-
127
-
128
- @triton.jit
129
- def _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn, nm, K, M_PAD: tl.constexpr, BLOCK_N: tl.constexpr,
130
- BLOCK_K: tl.constexpr, DOT: tl.constexpr):
131
- if DOT: # tensor cores: [M_PAD >= 16, BK] x [BK, BN], bf16 operands (e4m3 -> bf16 is exact)
132
- acc = tl.zeros([M_PAD, BLOCK_N], dtype=tl.float32)
133
- for k0 in range(0, K, BLOCK_K):
134
- rk = k0 + tl.arange(0, BLOCK_K)
135
- km = rk < K
136
- x = tl.load(x_ptr + rm[:, None] * x_row + rk[None, :], mask=mm[:, None] & km[None, :], other=0.0)
137
- w = tl.load(w_ptr + rn[:, None] * w_row + rk[None, :], mask=nm[:, None] & km[None, :], other=0.0)
138
- acc = tl.dot(x.to(tl.bfloat16), tl.trans(w.to(tl.bfloat16)), acc)
139
- return acc
140
- acc3 = tl.zeros([M_PAD, BLOCK_N, BLOCK_K], dtype=tl.float32)
141
- for k0 in range(0, K, BLOCK_K):
142
- rk = k0 + tl.arange(0, BLOCK_K)
143
- km = rk < K
144
- x = tl.load(x_ptr + rm[:, None] * x_row + rk[None, :], mask=mm[:, None] & km[None, :], other=0.0)
145
- w = tl.load(w_ptr + rn[:, None] * w_row + rk[None, :], mask=nm[:, None] & km[None, :], other=0.0)
146
- acc3 += x.to(tl.float32)[:, None, :] * w.to(tl.float32)[None, :, :]
147
- return tl.sum(acc3, 2)
148
-
149
-
150
- @triton.jit
151
- def _gemv_kernel(x_ptr, x_row, w_ptr, w_row, ws_ptr, y_ptr, y_row, M, N, K, up_off, cap,
152
- M_PAD: tl.constexpr, EPI: tl.constexpr, FP8: tl.constexpr,
153
- BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr, DOT: tl.constexpr = False,
154
- PDL: tl.constexpr = False):
155
- """y[m, n] = x[m] . W[n] for M <= M_PAD rows. EPI 0: store bf16; 1: y += bf16(.)
156
- (residual add in place); 2: SwiGLU, n < up_off reads gate row n and up row up_off+n;
157
- 3: fp32 logits cap * tanh(bf16(.) / cap) (cap 0: none). FP8: W e4m3 times ws[n]."""
158
- _pdl(PDL)
159
- pid = tl.program_id(0)
160
- rn = pid * BLOCK_N + tl.arange(0, BLOCK_N)
161
- nm = rn < N
162
- rm = tl.arange(0, M_PAD)
163
- mm = rm < M
164
- acc = _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn, nm, K, M_PAD, BLOCK_N, BLOCK_K, DOT)
165
- if FP8:
166
- acc = acc * tl.load(ws_ptr + rn, mask=nm, other=0.0)[None, :]
167
- ym = mm[:, None] & nm[None, :]
168
- yp = y_ptr + rm[:, None] * y_row + rn[None, :]
169
- if EPI == 2:
170
- up = _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn + up_off, nm, K, M_PAD, BLOCK_N, BLOCK_K, DOT)
171
- if FP8:
172
- up = up * tl.load(ws_ptr + up_off + rn, mask=nm, other=0.0)[None, :]
173
- gate = acc.to(tl.bfloat16).to(tl.float32)
174
- up = up.to(tl.bfloat16).to(tl.float32)
175
- tl.store(yp, (gate * tl.sigmoid(gate) * up).to(y_ptr.dtype.element_ty), mask=ym)
176
- elif EPI == 1:
177
- res = tl.load(yp, mask=ym, other=0.0).to(tl.float32)
178
- tl.store(yp, (acc.to(tl.bfloat16).to(tl.float32) + res).to(y_ptr.dtype.element_ty), mask=ym)
179
- elif EPI == 3:
180
- z = acc.to(tl.bfloat16).to(tl.float32)
181
- if cap > 0:
182
- t = z / cap
183
- e = tl.exp(-2.0 * tl.abs(t))
184
- th = (1.0 - e) / (1.0 + e)
185
- z = cap * tl.where(t < 0, -th, th)
186
- tl.store(yp, z, mask=ym)
187
- else:
188
- tl.store(yp, acc.to(y_ptr.dtype.element_ty), mask=ym)
189
-
190
-
191
- def gemv(x: torch.Tensor, w: torch.Tensor, y: torch.Tensor, epi: int = 0, scale: torch.Tensor | None = None,
192
- n: int | None = None, up_off: int = 0, cap: float = 0.0, config: tuple | None = None) -> None:
193
- """``x`` ``(M, K)`` (row stride free), ``w`` ``(N_w, K)``, ``y`` ``(M, n)``; ``n`` output
194
- columns (SwiGLU: the hidden size, ``up_off`` = its up rows' offset)."""
195
- M, K = x.shape
196
- N = n if n is not None else w.shape[0]
197
- m_pad = max(1, triton.next_power_of_2(M))
198
- fp8 = scale is not None
199
- cfg = tuple(config or gemv_config(m_pad, N, K, fp8, epi))
200
- bn, bk, warps, stages, dot = cfg + (0,) * (5 - len(cfg))
201
- _gemv_kernel[(triton.cdiv(N, bn),)](x, x.stride(0), w, w.stride(0), scale if fp8 else w, y, y.stride(0),
202
- M, N, K, up_off, float(cap), M_PAD=max(16, m_pad) if dot else m_pad,
203
- EPI=epi, FP8=fp8, BLOCK_N=bn, BLOCK_K=bk, DOT=bool(dot),
204
- num_warps=warps, num_stages=stages, PDL=PDL, launch_pdl=PDL)
205
-
206
-
207
- # CCGQA / QK-norm + RoPE + cache write
208
-
209
- #: Slots of the CCGQA conv-latent / value-shift rings. A step reads the two tokens before
210
- #: its first one, so a verify of up to RING - 3 drafted tokens never overwrites them.
211
- RING: tl.constexpr = tl.constexpr(32)
212
-
213
-
214
- @triton.jit
215
- def _lat(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, r, t, b, slot, pos, col, cidx, LAG: tl.constexpr):
216
- """The pre-conv latent of the row's token ``LAG`` back (fp32; zero across a document start):
217
- the call's own qkv rows while ``t >= LAG``, else the latent ring (slot mod ``RING``)."""
218
- if LAG == 0:
219
- v = tl.load(qkv_ptr + r * qkv_row + col + cidx).to(tl.float32)
220
- elif t >= LAG:
221
- v = tl.load(qkv_ptr + (r - LAG) * qkv_row + col + cidx, mask=pos >= LAG, other=0.0).to(tl.float32)
222
- else:
223
- v = tl.load(lat_ptr + b * lat_b + ((slot - LAG) % RING) * lat_l + col + cidx, mask=pos >= LAG,
224
- other=0.0).to(tl.float32)
225
- return tl.where(pos >= LAG, v, 0.0)
226
-
227
-
228
- @triton.jit
229
- def _conv(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, cs_ptr, csb_ptr, ch_ptr, chb_ptr, h, r, t, b, slot, pos, col,
230
- HD: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr):
231
- """Virtual head ``h``'s causal conv (depthwise ``KS`` taps, then per-head dense ``KC``
232
- taps; lag-ordered): every load is independent, one ``[HD, HD]`` tile per dense tap."""
233
- d = tl.arange(0, HD)
234
- b1 = tl.load(csb_ptr + h * HD + d).to(tl.float32)
235
- acc = tl.load(chb_ptr + h * HD + d).to(tl.float32)
236
- for j in tl.static_range(KC):
237
- u = b1
238
- for i in tl.static_range(KS):
239
- a = tl.load(cs_ptr + (h * HD + d) * KS + i).to(tl.float32)
240
- u += a * _lat(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, r, t, b, slot, pos, col, d, i + j)
241
- w2 = tl.load(ch_ptr + ((h * KC + j) * HD + d[:, None]) * HD + d[None, :]).to(tl.float32)
242
- acc += tl.sum(w2 * u[:, None], 0)
243
- return acc
244
-
245
-
246
- @triton.jit
247
- def _qk_decode_kernel(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, vh_ptr, vh_b, vh_l,
248
- pos_ptr, slot_ptr, cos_ptr, sin_ptr, cs_ptr, csb_ptr, ch_ptr, chb_ptr, qg_ptr, kg_ptr,
249
- q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h, T, eps,
250
- NQ: tl.constexpr, NKV: tl.constexpr, NXQ: tl.constexpr, HD: tl.constexpr,
251
- V_OFF: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr, CCGQA: tl.constexpr,
252
- QK_MEAN: tl.constexpr, VSHIFT: tl.constexpr, ROPE: tl.constexpr,
253
- PDL: tl.constexpr = False):
254
- """One (row, virtual head): heads ``< NQ`` are queries (written to ``q``), the
255
- rest keys (written to the K cache, with the value head to the V cache)."""
256
- _pdl(PDL)
257
- r = tl.program_id(0).to(tl.int64)
258
- h = tl.program_id(1)
259
- b = r // T
260
- t = r % T
261
- pos = tl.load(pos_ptr + r).to(tl.int64)
262
- slot = tl.load(slot_ptr + r).to(tl.int64)
263
- HALF: tl.constexpr = HD // 2
264
- G: tl.constexpr = NQ // NKV
265
- d = tl.arange(0, HD)
266
- is_k = h >= NQ
267
- g = h - NQ
268
- ph = tl.where(is_k, NXQ + g, h % NXQ) # physical latent head (VHA: h % NXQ)
269
- col = ph * HD
270
- base = qkv_ptr + r * qkv_row
271
- keep = t + RING >= T # only a call's last RING tokens go to the rings (no write races)
272
- if CCGQA:
273
- x = _conv(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, cs_ptr, csb_ptr, ch_ptr, chb_ptr, h, r, t, b, slot,
274
- pos, col, HD, KS, KC)
275
- if QK_MEAN: # mu_q = (x_q + x_k(group)) / 2, mu_k = the group mean of mu_q
276
- if is_k:
277
- qm = tl.zeros([HD], dtype=tl.float32)
278
- for jh in tl.static_range(G):
279
- qm += tl.load(base + ((g * G + jh) % NXQ) * HD + d).to(tl.float32)
280
- x += 0.5 * (qm / G + tl.load(base + col + d).to(tl.float32))
281
- else:
282
- x += 0.5 * (tl.load(base + col + d).to(tl.float32)
283
- + tl.load(base + (NXQ + h // G) * HD + d).to(tl.float32))
284
- else:
285
- x = tl.load(base + col + d).to(tl.float32)
286
- gain = tl.load(tl.where(is_k, kg_ptr + g * HD, qg_ptr) + d)
287
- x = x * tl.rsqrt(tl.sum(x * x, 0) / HD + eps) * gain
288
- if not CCGQA: # plain QK-norm rounds to bf16 before RoPE
289
- x = x.to(tl.bfloat16).to(tl.float32)
290
- lo, hi = tl.split(tl.permute(tl.reshape(x, [2, HALF]), (1, 0)))
291
- if ROPE:
292
- i2 = tl.arange(0, HALF)
293
- c = tl.load(cos_ptr + pos * HALF + i2)
294
- s = tl.load(sin_ptr + pos * HALF + i2)
295
- lo, hi = lo * c - hi * s, hi * c + lo * s
296
- lo_i = tl.arange(0, HALF)
297
- if is_k:
298
- dst = kc_ptr + b * kc_b + g * kc_h + slot * HD
299
- else:
300
- dst = q_ptr + r * q_row + h * HD
301
- tl.store(dst + lo_i, lo.to(q_ptr.dtype.element_ty))
302
- tl.store(dst + HALF + lo_i, hi.to(q_ptr.dtype.element_ty))
303
- if is_k: # the value head: half of the flat value channels are the previous token's
304
- ch = g * HD + d
305
- own = tl.load(base + V_OFF + ch)
306
- v = own
307
- if VSHIFT:
308
- VH: tl.constexpr = NKV * HD // 2
309
- upper = ch >= VH
310
- if t >= 1:
311
- prev = tl.load(base - qkv_row + V_OFF + ch, mask=upper, other=0.0)
312
- else:
313
- prev = tl.load(vh_ptr + b * vh_b + ((slot - 1) % RING) * vh_l + ch - VH,
314
- mask=upper & (slot >= 1), other=0.0)
315
- v = tl.where(upper, tl.where(pos > 0, prev, 0.0), own)
316
- tl.store(vh_ptr + b * vh_b + (slot % RING) * vh_l + ch - VH, own, mask=upper & keep)
317
- tl.store(vc_ptr + b * kc_b + g * kc_h + slot * HD + d, v.to(vc_ptr.dtype.element_ty))
318
- if CCGQA: # the latent ring: physical q head p written by query program p, k heads by theirs
319
- if ((h < NXQ) | is_k) & keep:
320
- tl.store(lat_ptr + b * lat_b + (slot % RING) * lat_l + col + d, tl.load(base + col + d))
321
-
322
-
323
- def qk_decode(qkv, lat, vh, pos, slot, cos, sin, conv, qg, kg, q, kc, vc, T: int, eps: float, *, nq, nkv,
324
- nxq, hd, v_off, ks, kc_taps, ccgqa, qk_mean, vshift, rope) -> None:
325
- R = qkv.shape[0]
326
- cs, csb, ch, chb = conv if conv is not None else (qkv,) * 4
327
- _qk_decode_kernel[(R, nq + nkv)](
328
- qkv, qkv.stride(0), lat if lat is not None else qkv, lat.stride(0) if lat is not None else 0,
329
- lat.stride(1) if lat is not None else 0, vh if vh is not None else qkv,
330
- vh.stride(0) if vh is not None else 0, vh.stride(1) if vh is not None else 0,
331
- pos, slot, cos, sin, cs, csb, ch, chb, qg, kg, q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1),
332
- T, eps, NQ=nq, NKV=nkv, NXQ=nxq, HD=hd, V_OFF=v_off, KS=ks, KC=kc_taps, CCGQA=ccgqa,
333
- QK_MEAN=qk_mean, VSHIFT=vshift, ROPE=rope, num_warps=8, PDL=PDL, launch_pdl=PDL)
334
-
335
-
336
- @triton.jit
337
- def _tile_lat(qkv_ptr, qkv_row, r0, t, tm, pos, col, d, T, LAG: tl.constexpr):
338
- """``[BT, HD]`` pre-conv latents ``LAG`` tokens back of a prompt tile (zero across a
339
- document start or before the prompt)."""
340
- ok = tm & (pos >= LAG) & (t >= LAG)
341
- return tl.load(qkv_ptr + (r0 + t - LAG)[:, None] * qkv_row + col + d[None, :], mask=ok[:, None],
342
- other=0.0).to(tl.float32)
343
-
344
-
345
- @triton.jit
346
- def _qk_prefill_kernel(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, vh_ptr, vh_b, vh_l, pos_ptr, cos_ptr, sin_ptr,
347
- cs_ptr, csb_ptr, ch_ptr, chb_ptr, qg_ptr, kg_ptr, q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h,
348
- T, eps, NQ: tl.constexpr, NKV: tl.constexpr, NXQ: tl.constexpr, HD: tl.constexpr,
349
- V_OFF: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr, QK_MEAN: tl.constexpr,
350
- VSHIFT: tl.constexpr, ROPE: tl.constexpr, BT: tl.constexpr):
351
- """``qk_decode`` for a prompt from slot 0, ``BT`` tokens of one virtual head per program:
352
- the head's dense conv taps are read once per tile and applied on tensor cores (bf16 taps,
353
- the input split into bf16 hi + lo parts), not once per token."""
354
- tt = tl.program_id(0)
355
- h = tl.program_id(1)
356
- b = tl.program_id(2).to(tl.int64)
357
- HALF: tl.constexpr = HD // 2
358
- G: tl.constexpr = NQ // NKV
359
- t = tt * BT + tl.arange(0, BT)
360
- tm = t < T
361
- r0 = b * T
362
- rows = r0 + t
363
- pos = tl.load(pos_ptr + rows, mask=tm, other=0).to(tl.int64)
364
- d = tl.arange(0, HD)
365
- is_k = h >= NQ
366
- g = h - NQ
367
- ph = tl.where(is_k, NXQ + g, h % NXQ)
368
- col = ph * HD
369
- b1 = tl.load(csb_ptr + h * HD + d).to(tl.float32)
370
- x = tl.zeros([BT, HD], dtype=tl.float32) + tl.load(chb_ptr + h * HD + d).to(tl.float32)[None, :]
371
- for j in tl.static_range(KC):
372
- u = tl.zeros([BT, HD], dtype=tl.float32) + b1[None, :]
373
- for i in tl.static_range(KS):
374
- a = tl.load(cs_ptr + (h * HD + d) * KS + i).to(tl.float32)
375
- u += a[None, :] * _tile_lat(qkv_ptr, qkv_row, r0, t, tm, pos, col, d, T, i + j)
376
- w2 = tl.load(ch_ptr + ((h * KC + j) * HD + d[:, None]) * HD + d[None, :]).to(tl.bfloat16)
377
- u_hi = u.to(tl.bfloat16) # the taps are bf16 already: u = hi + lo, two bf16 MMAs (~16-bit u)
378
- x = tl.dot(u_hi, w2, x)
379
- x = tl.dot((u - u_hi.to(tl.float32)).to(tl.bfloat16), w2, x)
380
- base = qkv_ptr + rows[:, None] * qkv_row
381
- if QK_MEAN:
382
- if is_k:
383
- qm = tl.zeros([BT, HD], dtype=tl.float32)
384
- for jh in tl.static_range(G):
385
- qm += tl.load(base + ((g * G + jh) % NXQ) * HD + d[None, :], mask=tm[:, None], other=0.0).to(
386
- tl.float32)
387
- x += 0.5 * (qm / G + tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0).to(tl.float32))
388
- else:
389
- x += 0.5 * (tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0).to(tl.float32)
390
- + tl.load(base + (NXQ + h // G) * HD + d[None, :], mask=tm[:, None], other=0.0).to(
391
- tl.float32))
392
- gain = tl.load(tl.where(is_k, kg_ptr + g * HD, qg_ptr) + d)
393
- x = x * tl.rsqrt(tl.sum(x * x, 1) / HD + eps)[:, None] * gain[None, :]
394
- lo, hi = tl.split(tl.permute(tl.reshape(x, [BT, 2, HALF]), (0, 2, 1)))
395
- i2 = tl.arange(0, HALF)
396
- if ROPE:
397
- c = tl.load(cos_ptr + pos[:, None] * HALF + i2[None, :])
398
- sn = tl.load(sin_ptr + pos[:, None] * HALF + i2[None, :])
399
- lo, hi = lo * c - hi * sn, hi * c + lo * sn
400
- if is_k:
401
- dst = kc_ptr + b * kc_b + g * kc_h + t[:, None] * HD
402
- else:
403
- dst = q_ptr + rows[:, None] * q_row + h * HD
404
- tl.store(dst + i2[None, :], lo.to(q_ptr.dtype.element_ty), mask=tm[:, None])
405
- tl.store(dst + HALF + i2[None, :], hi.to(q_ptr.dtype.element_ty), mask=tm[:, None])
406
- keep = tm & (t + RING >= T)
407
- if is_k:
408
- ch = g * HD + d
409
- own = tl.load(base + V_OFF + ch[None, :], mask=tm[:, None], other=0.0)
410
- v = own
411
- if VSHIFT:
412
- VH: tl.constexpr = NKV * HD // 2
413
- upper = (ch >= VH)[None, :]
414
- prev = tl.load(base - qkv_row + V_OFF + ch[None, :], mask=tm[:, None] & upper & (t >= 1)[:, None],
415
- other=0.0)
416
- v = tl.where(upper, tl.where((pos > 0)[:, None], prev, 0.0), own)
417
- tl.store(vh_ptr + b * vh_b + (t % RING)[:, None] * vh_l + ch[None, :] - VH, own,
418
- mask=keep[:, None] & upper)
419
- tl.store(vc_ptr + b * kc_b + g * kc_h + t[:, None] * HD + d[None, :], v.to(vc_ptr.dtype.element_ty),
420
- mask=tm[:, None])
421
- if (h < NXQ) | is_k:
422
- tl.store(lat_ptr + b * lat_b + (t % RING)[:, None] * lat_l + col + d[None, :],
423
- tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0), mask=keep[:, None])
424
-
425
-
426
- def qk_prefill(qkv, lat, vh, pos, cos, sin, conv, qg, kg, q, kc, vc, T: int, eps: float, *, nq, nkv, nxq, hd,
427
- v_off, ks, kc_taps, qk_mean, vshift, rope) -> None:
428
- """CCGQA ``qk_decode`` for ``B`` prompts of ``T`` tokens starting at slot 0."""
429
- B = qkv.shape[0] // T
430
- cs, csb, ch, chb = conv
431
- bt = 32 if hd <= 128 else 16 # (64-token tiles: ptxas falls to 40 registers and spills ~20x slower)
432
- _qk_prefill_kernel[(triton.cdiv(T, bt), nq + nkv, B)](
433
- qkv, qkv.stride(0), lat, lat.stride(0), lat.stride(1), vh if vh is not None else qkv,
434
- vh.stride(0) if vh is not None else 0, vh.stride(1) if vh is not None else 0, pos, cos, sin,
435
- cs, csb, ch, chb, qg, kg, q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1), T, eps, NQ=nq, NKV=nkv,
436
- NXQ=nxq, HD=hd, V_OFF=v_off, KS=ks, KC=kc_taps, QK_MEAN=qk_mean, VSHIFT=vshift, ROPE=rope, BT=bt,
437
- num_warps=4)
438
-
439
-
440
- # flash decoding
441
- #
442
- # grid (span, KV head, sequence). A program reads its span of one KV head's keys once for
443
- # every query of the sequence's T rows (T > 1: a speculative verify; each row has its own
444
- # causal limit) and all G query heads of the group, stores the unnormalised partials, and
445
- # the last span program of (sequence, KV head) merges them and finishes the rows.
446
-
447
-
448
- @triton.jit
449
- def _spans(start, slot, NSPL: tl.constexpr, BL: tl.constexpr):
450
- """``(span, n_spans)``: keys ``[start, slot]`` cut into at most ``NSPL`` spans of a multiple
451
- of ``BL`` keys, sized from the length on the device (one graph serves every length)."""
452
- n = slot + 1 - start
453
- span = tl.maximum(tl.cdiv(tl.cdiv(n, NSPL), BL), 1) * BL
454
- return span, tl.where(n > 0, tl.cdiv(n, span), 0)
455
-
456
-
457
- @triton.jit
458
- def _merge(ml_ptr, acc_ptr, base0, n_sp, hrow, HD: tl.constexpr, G_PAD: tl.constexpr, GH_PAD: tl.constexpr,
459
- NSPL: tl.constexpr, CH: tl.constexpr):
460
- """Merge the ``n_sp`` span partials of the query heads ``hrow`` (in the group): the maxima
461
- in one load, the accumulators ``CH`` spans at a time -> bf16-rounded output."""
462
- d = tl.arange(0, HD)
463
- si = tl.arange(0, NSPL)
464
- m = tl.load(ml_ptr + (base0 + si)[:, None] * 2 * G_PAD + hrow[None, :], mask=(si < n_sp)[:, None],
465
- other=float("-inf"))
466
- l = tl.load(ml_ptr + (base0 + si)[:, None] * 2 * G_PAD + G_PAD + hrow[None, :], mask=(si < n_sp)[:, None],
467
- other=0.0)
468
- mg = tl.max(m, 0)
469
- mg = tl.where(mg == float("-inf"), 0.0, mg)
470
- lsum = tl.sum(tl.exp2(m - mg[None, :]) * l, 0)
471
- o = tl.zeros([GH_PAD, HD], dtype=tl.float32)
472
- for c0 in range(0, n_sp, CH):
473
- cs = c0 + tl.arange(0, CH)
474
- cm = cs < n_sp
475
- mc = tl.load(ml_ptr + (base0 + cs)[:, None] * 2 * G_PAD + hrow[None, :], mask=cm[:, None],
476
- other=float("-inf"))
477
- a = tl.load(acc_ptr + ((base0 + cs)[:, None, None] * G_PAD + hrow[None, :, None]) * HD
478
- + d[None, None, :], mask=cm[:, None, None], other=0.0)
479
- o += tl.sum(tl.exp2(mc - mg[None, :])[:, :, None] * a, 0)
480
- o = o / tl.maximum(lsum, 1e-30)[:, None]
481
- return o.to(tl.bfloat16).to(tl.float32)
482
-
483
-
484
- @triton.jit
485
- def _finish(ml_ptr, acc_ptr, r, g, b, n_sp, slot, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row,
486
- lam_off, gate_off, NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr,
487
- HD: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr, GATE: tl.constexpr):
488
- """Merge row ``r``'s spans for KV head ``g`` and finish DiffAttn V2 + XSA (+ gate) into ``y``."""
489
- base0 = (r * NKV + g) * NSPL
490
- d = tl.arange(0, HD)
491
- if DIFF:
492
- GH: tl.constexpr = G // 2
493
- GH_PAD: tl.constexpr = G_PAD // 2
494
- i = tl.arange(0, GH_PAD)
495
- im = i < GH
496
- y0 = _merge(ml_ptr, acc_ptr, base0, n_sp, 2 * i, HD, G_PAD, GH_PAD, NSPL, CH)
497
- y1 = _merge(ml_ptr, acc_ptr, base0, n_sp, 2 * i + 1, HD, G_PAD, GH_PAD, NSPL, CH)
498
- lam = tl.load(qkv_ptr + r * qkv_row + lam_off + g * GH + i, mask=im, other=0.0).to(tl.float32)
499
- y = y0 - tl.sigmoid(lam)[:, None] * y1
500
- else:
501
- GH: tl.constexpr = G
502
- GH_PAD: tl.constexpr = G_PAD
503
- i = tl.arange(0, GH_PAD)
504
- im = i < GH
505
- y = _merge(ml_ptr, acc_ptr, base0, n_sp, i, HD, G_PAD, GH_PAD, NSPL, CH)
506
- if XSA:
507
- v = tl.load(vc_ptr + b * kc_b + g * kc_h + slot * HD + d).to(tl.float32)
508
- vn = v / tl.maximum(tl.sqrt(tl.sum(v * v, 0)), 1e-12)
509
- y = y - tl.sum(y * vn[None, :], 1)[:, None] * vn[None, :]
510
- oh = g * GH + i
511
- if GATE == 1: # per channel
512
- gt = tl.load(qkv_ptr + r * qkv_row + gate_off + oh[:, None] * HD + d[None, :], mask=im[:, None],
513
- other=0.0).to(tl.float32)
514
- y = y * tl.sigmoid(gt)
515
- elif GATE == 2: # per head
516
- gt = tl.load(qkv_ptr + r * qkv_row + gate_off + oh, mask=im, other=0.0).to(tl.float32)
517
- y = y * tl.sigmoid(gt)[:, None]
518
- tl.store(y_ptr + r * y_row + oh[:, None] * HD + d[None, :], y.to(y_ptr.dtype.element_ty), mask=im[:, None])
519
-
520
-
521
- @triton.jit
522
- def _fence():
523
- """``fence.acq_rel.gpu`` in every thread (CUDA's ``__threadfence``)."""
524
- tl.inline_asm_elementwise("fence.acq_rel.gpu; mov.u32 $0, 0;", "=r", [], dtype=tl.int32, is_pure=False,
525
- pack=1)
526
-
527
-
528
- @triton.jit
529
- def _attn_kernel(q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h, slot_ptr, start_ptr, ml_ptr, acc_ptr, cnt_ptr,
530
- qkv_ptr, qkv_row, y_ptr, y_row, T, qk_scale, lam_off, gate_off,
531
- NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr, QR: tl.constexpr,
532
- HD: tl.constexpr, BL: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr,
533
- GATE: tl.constexpr, FINISH: tl.constexpr, PDL: tl.constexpr = False):
534
- """``FINISH`` (one row per sequence): the last span program finishes the row; else
535
- ``_finish_kernel`` does, a program per row."""
536
- _pdl(PDL)
537
- sp = tl.program_id(0)
538
- g = tl.program_id(1)
539
- b = tl.program_id(2).to(tl.int64)
540
- r0 = b * T
541
- start = tl.load(start_ptr + r0)
542
- last = tl.load(slot_ptr + r0 + T - 1) # the sequence's newest row reaches furthest
543
- span, n_sp = _spans(start, last, NSPL, BL)
544
- lo = start + sp * span
545
- hi = tl.minimum(last + 1, lo + span)
546
- if lo < hi:
547
- qi = tl.arange(0, QR) # query row = (token t, head j of the group)
548
- t = qi // G
549
- j = qi % G
550
- qm = t < T
551
- lim = tl.load(slot_ptr + r0 + t, mask=qm, other=-1) # each row's own causal limit
552
- d = tl.arange(0, HD)
553
- q = tl.load(q_ptr + (r0 + t)[:, None] * q_row + (g * G + j)[:, None] * HD + d[None, :],
554
- mask=qm[:, None], other=0.0)
555
- m_i = tl.full([QR], float("-inf"), tl.float32)
556
- l_i = tl.zeros([QR], dtype=tl.float32)
557
- acc = tl.zeros([QR, HD], dtype=tl.float32)
558
- kbase = b * kc_b + g * kc_h
559
- for k0 in range(lo, hi, BL):
560
- kk = k0 + tl.arange(0, BL)
561
- km = kk < hi
562
- k = tl.load(kc_ptr + kbase + kk[:, None] * HD + d[None, :], mask=km[:, None], other=0.0)
563
- s = tl.dot(q, tl.trans(k)) * qk_scale # base-2 logits
564
- s = tl.where(km[None, :] & (kk[None, :] <= lim[:, None]), s, float("-inf"))
565
- m_new = tl.maximum(m_i, tl.max(s, 1))
566
- m_ok = tl.where(m_new == float("-inf"), 0.0, m_new) # a row with no key yet
567
- alpha = tl.exp2(m_i - m_ok)
568
- p = tl.exp2(s - m_ok[:, None])
569
- l_i = l_i * alpha + tl.sum(p, 1)
570
- v = tl.load(vc_ptr + kbase + kk[:, None] * HD + d[None, :], mask=km[:, None], other=0.0)
571
- acc = acc * alpha[:, None] + tl.dot(p.to(v.dtype), v)
572
- m_i = m_new
573
- base = ((r0 + t) * NKV + g) * NSPL + sp
574
- tl.store(ml_ptr + base * 2 * G_PAD + j, m_i, mask=qm)
575
- tl.store(ml_ptr + base * 2 * G_PAD + G_PAD + j, l_i, mask=qm)
576
- tl.store(acc_ptr + (base * G_PAD + j)[:, None] * HD + d[None, :], acc, mask=qm[:, None])
577
- if FINISH:
578
- _fence()
579
- tl.debug_barrier()
580
- old = tl.atomic_add(cnt_ptr + b * NKV + g, 1, sem="acq_rel", scope="gpu")
581
- if old == n_sp - 1: # the last span merges and finishes the row
582
- _fence()
583
- tl.atomic_xchg(cnt_ptr + b * NKV + g, 0, sem="relaxed", scope="gpu")
584
- _finish(ml_ptr, acc_ptr, r0, g, b, n_sp, last, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row,
585
- lam_off, gate_off, NKV, NSPL, G, G_PAD, HD, CH, DIFF, XSA, GATE)
586
-
587
-
588
- @triton.jit
589
- def _finish_kernel(ml_ptr, slot_ptr, start_ptr, acc_ptr, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row, T,
590
- lam_off, gate_off, NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr,
591
- HD: tl.constexpr, BL: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr,
592
- GATE: tl.constexpr, PDL: tl.constexpr = False):
593
- """The finish of a multi-row (verify / short prompt) attention, a program per (row, KV head)."""
594
- _pdl(PDL)
595
- r = tl.program_id(0).to(tl.int64)
596
- g = tl.program_id(1)
597
- b = r // T
598
- _, n_sp = _spans(tl.load(start_ptr + b * T), tl.load(slot_ptr + b * T + T - 1), NSPL, BL)
599
- _finish(ml_ptr, acc_ptr, r, g, b, n_sp, tl.load(slot_ptr + r), qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr,
600
- y_row, lam_off, gate_off, NKV, NSPL, G, G_PAD, HD, CH, DIFF, XSA, GATE)
601
-
602
-
603
- #: Smallest query tile of the decode attention, and whether a 1-row step finishes in the
604
- #: attention kernel (saves a launch) or in ``_finish_kernel`` like a verify. A row's sums
605
- #: round the same way only in the same tile and code: tests set 32 / False so a 1-row step
606
- #: and a verify match bitwise.
607
- QR_MIN = 16
608
- FUSED_FINISH = True
609
-
610
-
611
- def attention(q, kc, vc, slot, start, qkv, y, ml, acc, cnt, T: int, *, nq, nkv, hd, nspl, lam_off,
612
- gate_off, diff, xsa, gate) -> None:
613
- """``q`` ``(R, nq, hd)`` for ``R = B * T`` rows (``T`` consecutive tokens per sequence),
614
- caches ``(B, nkv, L, hd)``, ``y`` ``(R, nh * hd)``; scratch ``ml`` ``(R, nkv, nspl, 2, G_PAD)``
615
- / ``acc`` ``(R, nkv, nspl, G_PAD, hd)`` fp32 and zeroed counters ``cnt`` (>= ``B * nkv``
616
- int32, left zeroed)."""
617
- R = q.shape[0]
618
- G = nq // nkv
619
- g_pad = max(16, triton.next_power_of_2(G))
620
- qr = max(QR_MIN, triton.next_power_of_2(T * G))
621
- ch = 8 if g_pad <= 16 else 4
622
- _attn_kernel[(nspl, nkv, R // T)](q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1), slot, start, ml, acc,
623
- cnt, qkv, qkv.stride(0), y, y.stride(0), T, hd ** -0.5 * LOG2E, lam_off,
624
- gate_off,
625
- NKV=nkv, NSPL=nspl, G=G, G_PAD=g_pad, QR=qr, HD=hd, BL=64,
626
- CH=ch, DIFF=diff, XSA=xsa, GATE=gate, FINISH=T == 1 and FUSED_FINISH,
627
- num_warps=4 if qr <= 32 else 8, num_stages=2, PDL=PDL, launch_pdl=PDL)
628
- if T > 1 or not FUSED_FINISH:
629
- _finish_kernel[(R, nkv)](ml, slot, start, acc, qkv, qkv.stride(0), vc, kc.stride(0), kc.stride(1), y,
630
- y.stride(0), T, lam_off, gate_off, NKV=nkv, NSPL=nspl, G=G, G_PAD=g_pad, HD=hd,
631
- BL=64, CH=ch, DIFF=diff, XSA=xsa, GATE=gate, num_warps=4, PDL=PDL,
632
- launch_pdl=PDL)
633
-
634
-
635
- # sampling
636
-
637
-
638
- @triton.jit
639
- def _sample_kernel(x_ptr, x_row, V, inv_temp, top_k, top_p, min_p, seed_ptr, excl_ptr, y_ptr, pd_ptr, u_ptr,
640
- pres_ptr, pres_row, ids_ptr, penalty, T,
641
- TOP_K: tl.constexpr, TOP_P: tl.constexpr, MIN_P: tl.constexpr, EXCL: tl.constexpr,
642
- REP: tl.constexpr, GREEDY: tl.constexpr, T_MAX: tl.constexpr, BLOCK: tl.constexpr,
643
- ITERS: tl.constexpr):
644
- """One row: transformers' repetition penalty -> temperature -> top-k -> top-p -> min-p,
645
- then a draw (``GREEDY``: the argmax of the penalised logits).
646
-
647
- ``REP``: tokens in the row's history (``pres[b]``, rows ``b * T + t``) and in this call's
648
- own inputs ``ids[b, 1..t]`` (a verify's earlier draft tokens) get ``x < 0 ? x * penalty :
649
- x / penalty``.
650
-
651
- Thresholds instead of a sort: top-k keeps ``x >= `` the k-th largest value, top-p
652
- keeps ``p >= tau`` for the largest ``tau`` whose kept mass reaches ``top_p`` (the
653
- smallest top set, as transformers' ascending cumsum), both found by bisection to
654
- below an ulp. The draw inverts the CDF of the kept mass with one Philox uniform.
655
- ``EXCL``: also the probability of token ``excl`` under the result (``pd``, for
656
- speculative acceptance, with a uniform ``u``) and the draw made without it."""
657
- r = tl.program_id(0).to(tl.int64)
658
- offs = tl.arange(0, BLOCK)
659
- msk = offs < V
660
- x = tl.load(x_ptr + r * x_row + offs, mask=msk, other=float("-inf"))
661
- if REP:
662
- b = r // T
663
- t = r % T
664
- seen = tl.load(pres_ptr + b * pres_row + offs, mask=msk, other=0) != 0
665
- for i in tl.static_range(1, T_MAX):
666
- if i <= t:
667
- seen = seen | (offs == tl.load(ids_ptr + b * T + i))
668
- x = tl.where(seen, tl.where(x < 0, x * penalty, x / penalty), x)
669
- if GREEDY:
670
- tl.store(y_ptr + r, tl.argmax(x, 0).to(tl.int64))
671
- else:
672
- x = x * inv_temp
673
- if TOP_K:
674
- lo = tl.min(tl.where(msk, x, float("inf")), 0)
675
- hi = tl.max(x, 0)
676
- for _ in range(ITERS): # count(x >= lo) >= k > count(x >= hi)
677
- mid = lo + (hi - lo) * 0.5
678
- ok = tl.sum((x >= mid).to(tl.int32), 0) >= top_k
679
- lo = tl.where(ok, mid, lo)
680
- hi = tl.where(ok, hi, mid)
681
- kth = tl.min(tl.where(x >= lo, x, float("inf")), 0)
682
- x = tl.where(x >= kth, x, float("-inf"))
683
- m = tl.max(x, 0)
684
- p = tl.exp(x - m) # max 1
685
- z = tl.sum(p, 0)
686
- if TOP_P:
687
- lo = tl.zeros([], dtype=tl.float32)
688
- hi = tl.full([], 1.0, tl.float32) # mass(p >= lo) >= top_p * z > mass(p >= hi) (or hi = 1)
689
- goal = top_p * z
690
- for _ in range(ITERS):
691
- mid = lo + (hi - lo) * 0.5
692
- ok = tl.sum(tl.where(p >= mid, p, 0.0), 0) >= goal
693
- lo = tl.where(ok, mid, lo)
694
- hi = tl.where(ok, hi, mid)
695
- tau = tl.min(tl.where(p >= lo, p, float("inf")), 0)
696
- p = tl.where(p >= tau, p, 0.0)
697
- if MIN_P:
698
- p = tl.where(p >= min_p, p, 0.0) # max is 1: min_p * max = min_p
699
- seed = tl.load(seed_ptr)
700
- if EXCL:
701
- e = tl.load(excl_ptr + r)
702
- pd = tl.sum(tl.where(offs == e, p, 0.0), 0) / tl.sum(p, 0)
703
- tl.store(pd_ptr + r, pd)
704
- tl.store(u_ptr + r, tl.rand(seed + 7919, r.to(tl.int32)))
705
- p = tl.where(offs == e, 0.0, p)
706
- # inverse CDF: one uniform per row; the first kept token whose running mass passes it
707
- cdf = tl.cumsum(p, 0)
708
- u = tl.rand(seed, r.to(tl.int32)) * tl.max(cdf, 0)
709
- tok = tl.min(tl.where((cdf > u) & (p > 0), offs, BLOCK), 0)
710
- tl.store(y_ptr + r, tl.where(tok < V, tok, tl.argmax(p, 0)).to(tl.int64))
711
-
712
-
713
- def sample(x: torch.Tensor, seed: torch.Tensor, y: torch.Tensor, *, temperature: float, top_k: int, top_p: float,
714
- min_p: float, excl: torch.Tensor | None = None, pd: torch.Tensor | None = None,
715
- u: torch.Tensor | None = None, greedy: bool = False, penalty: float = 1.0,
716
- pres: torch.Tensor | None = None, ids: torch.Tensor | None = None, T: int = 1) -> None:
717
- """Draw ``y`` ``(R,)`` from fp32 logits ``x`` ``(R, V)``; ``seed`` a 1-element int64 device
718
- tensor (advance it between calls). With ``excl``: ``pd`` / ``u`` as in ``_sample_kernel``.
719
- ``penalty != 1``: the repetition penalty over ``pres`` ``(B, V)`` bool and the call's input
720
- ids ``(B * T,)`` (rows ``b * T + t``)."""
721
- R, V = x.shape
722
- block = triton.next_power_of_2(V)
723
- rep = penalty != 1.0
724
- _sample_kernel[(R,)](x, x.stride(0), V, 1.0 / temperature, int(top_k), float(top_p), float(min_p), seed,
725
- excl if excl is not None else y, y, pd if pd is not None else x,
726
- u if u is not None else x, pres.view(torch.uint8) if rep else y,
727
- pres.stride(0) if rep else 0, ids if rep else y, float(penalty), T,
728
- TOP_K=bool(top_k) and top_k < V, TOP_P=top_p < 1.0, MIN_P=bool(min_p),
729
- EXCL=excl is not None, REP=rep, GREEDY=greedy, T_MAX=T, BLOCK=block, ITERS=40,
730
- num_warps=32 if block >= 16384 else 8)
731
-
732
-
733
- kernels = sys.modules[__name__] # ``from .luau_kernels import kernels as K``: transformers'
734
- # remote code only follows ``from .module import ...`` lines
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Triton kernels for ``luau_infer`` (fused decoding of the LuauCoder HF port).
2
+
3
+ Every kernel works on *rows*: one row per token of the call (``B`` rows when
4
+ decoding, ``B * T`` for a prefill or a speculative verify), each with its own
5
+ document position ``pos`` (RoPE, conv / value-shift masks), cache ``slot``
6
+ (where its K/V go) and ``start`` (the first valid key slot of its sequence:
7
+ left padding). Nothing reads a Python scalar that changes between steps, so a
8
+ decode step replays as a CUDA graph.
9
+
10
+ * ``mix_norm`` Block AttnRes depth mix (a softmax over the sources, one tile)
11
+ + RMSNorm, or the embedding gather + RMSNorm (layer 0).
12
+ * ``gemv`` skinny ``x @ W.T`` (bf16 or rowwise-FP8 weights), epilogues:
13
+ store, residual add, SwiGLU (gate|up rows), softcap logits.
14
+ * ``qk_decode`` CCGQA conv + QK-mean + QK-norm + gains + RoPE + value shift,
15
+ writes K/V to the cache and the conv latents to their ring.
16
+ * ``attention`` GQA flash-decoding: each program reads a span of one KV
17
+ head's keys once for all its query heads and all the
18
+ sequence's rows (a speculative verify); the last span
19
+ program merges and finishes DiffAttn V2 + XSA (+ gate).
20
+ * ``qk_prefill`` ``qk_decode`` for prompts, in token tiles.
21
+ * ``iha_mix`` IHA: pseudo-head mixes of the latent q / k / v, norm, temperature,
22
+ RoPE at virtual positions, interleaved pseudo-K/V cache write;
23
+ ``attention`` then runs over pseudo-rows with a sliding window and
24
+ its finish collapses them (+ Gram-Schmidt XSA, gate).
25
+ * ``head_gate`` the gated byte head's ``[h, sigmoid(u.h + b) P h]`` input.
26
+ * ``sample`` repetition penalty / temperature / top-k / top-p / min-p + draw
27
+ (or greedy), one row per program.
28
+ """
29
+
30
+ from __future__ import annotations
31
+
32
+ import os
33
+ import sys
34
+ from pathlib import Path
35
+
36
+ import torch
37
+
38
+ try: # (remote code: transformers only requires imports outside try blocks)
39
+ import triton
40
+ import triton.language as tl
41
+ except ImportError as exc: # pragma: no cover
42
+ raise ImportError("the fused LuauCoder decoding kernels need triton") from exc
43
+
44
+ from .luau_gemv_tuned import TABLE as _TABLE
45
+
46
+ LOG2E = 1.4426950408889634
47
+
48
+ # programmatic dependent launch (SM90+); LUAU_INFER_PDL=0 turns it off
49
+ PDL = (torch.cuda.is_available() and torch.cuda.get_device_capability()[0] >= 9
50
+ and os.environ.get("LUAU_INFER_PDL", "1") != "0")
51
+
52
+ # L2 prefetch of the next GEMV's weights (SM90+); LUAU_INFER_PREFETCH=0 turns it off
53
+ BULK = torch.cuda.is_available() and torch.cuda.get_device_capability()[0] >= 9
54
+ PREFETCH = BULK and os.environ.get("LUAU_INFER_PREFETCH", "1") != "0"
55
+
56
+
57
+ def _pf(pf) -> dict:
58
+ """Launch kwargs of an optional prefetch target ``pf`` (a tensor)."""
59
+ if pf is None or not PREFETCH:
60
+ return dict(pf_ptr=None, pf_bytes=0, PF=False, BULK=False)
61
+ return dict(pf_ptr=pf, pf_bytes=pf.numel() * pf.element_size(), PF=True, BULK=BULK)
62
+
63
+
64
+ @triton.jit
65
+ def _pdl(ON: tl.constexpr):
66
+ """Wait for the previous kernel's writes, then let the next kernel launch."""
67
+ if ON:
68
+ tl.extra.cuda.gdc_wait()
69
+ tl.extra.cuda.gdc_launch_dependents()
70
+
71
+
72
+ @triton.jit
73
+ def _prefetch(ptr, nbytes, PF: tl.constexpr, BULK: tl.constexpr):
74
+ """Fire-and-forget L2 prefetch of ``nbytes`` at ``ptr``, split over the kernel's programs."""
75
+ if PF:
76
+ pid = tl.program_id(0) + tl.num_programs(0) * (tl.program_id(1) + tl.num_programs(1) * tl.program_id(2))
77
+ npg = tl.num_programs(0) * tl.num_programs(1) * tl.num_programs(2)
78
+ CH: tl.constexpr = 4096 if BULK else 128
79
+ n = tl.cdiv(nbytes, CH)
80
+ per = tl.cdiv(n, npg)
81
+ lo = pid * per
82
+ hi = tl.minimum(n, lo + per)
83
+ base = ptr.to(tl.pointer_type(tl.int8))
84
+ for i0 in range(lo, hi, 256):
85
+ i = i0 + tl.arange(0, 256)
86
+ m = (i < hi).to(tl.int32)
87
+ a = base + i.to(tl.int64) * CH
88
+ if BULK:
89
+ sz = tl.minimum(nbytes - i.to(tl.int64) * CH, CH).to(tl.int32)
90
+ tl.inline_asm_elementwise(
91
+ "{ .reg .pred p; setp.ne.b32 p, $3, 0; @p cp.async.bulk.prefetch.L2.global [$1], $2; "
92
+ "mov.u32 $0, 0; }", "=r,l,r,r", [a, sz, m], dtype=tl.int32, is_pure=False, pack=1)
93
+ else:
94
+ tl.inline_asm_elementwise(
95
+ "{ .reg .pred p; setp.ne.b32 p, $2, 0; @p prefetch.global.L2 [$1]; mov.u32 $0, 0; }",
96
+ "=r,l,r", [a, m], dtype=tl.int32, is_pure=False, pack=1)
97
+
98
+
99
+ @triton.jit
100
+ def _mix_norm_kernel(s_ptr, s_row, s_ent, ids_ptr, emb_ptr, w_ptr, g_ptr, out_ptr, o_row, eps, D,
101
+ NE: tl.constexpr, NE_PAD: tl.constexpr, MODE: tl.constexpr, BLOCK: tl.constexpr,
102
+ ADD: tl.constexpr = 0, PART: tl.constexpr = 1, PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
103
+ """MODE 0: h = S[r, 0]; 1: embedding gather; 2: AttnRes mix; 3: sum of entries. out = rmsnorm(h) * g."""
104
+ r = tl.program_id(0).to(tl.int64)
105
+ offs = tl.arange(0, BLOCK)
106
+ msk = offs < D
107
+ g = tl.load(g_ptr + offs, mask=msk, other=0.0).to(tl.float32)
108
+ if MODE == 2:
109
+ w = tl.load(w_ptr + offs, mask=msk, other=0.0)
110
+ _prefetch(pf_ptr, pf_bytes, PF, BULK)
111
+ _pdl(PDL)
112
+ if MODE == 1:
113
+ tok = tl.load(ids_ptr + r).to(tl.int64)
114
+ hb = tl.load(emb_ptr + tok * D + offs, mask=msk, other=0.0)
115
+ tl.store(s_ptr + r * s_row + offs, hb, mask=msk)
116
+ h = hb.to(tl.float32)
117
+ elif MODE == 0:
118
+ h = tl.load(s_ptr + r * s_row + offs, mask=msk, other=0.0).to(tl.float32)
119
+ else:
120
+ j = tl.arange(0, NE_PAD)
121
+ jm = j < NE
122
+ v = tl.load(s_ptr + r * s_row + j[:, None] * s_ent + offs[None, :], mask=jm[:, None] & msk[None, :],
123
+ other=0.0).to(tl.float32)
124
+ if MODE == 3:
125
+ h = tl.sum(v, 0)
126
+ else:
127
+ sc = tl.sum(v * w[None, :], 1) * tl.rsqrt(tl.sum(v * v, 1) / D + eps)
128
+ sc = tl.where(j < NE - 1 + PART, sc, float("-inf"))
129
+ e = tl.exp(sc - tl.max(sc, 0))
130
+ p = e / tl.sum(e, 0)
131
+ h = tl.sum((p + ADD)[:, None] * v, 0)
132
+ h = h.to(out_ptr.dtype.element_ty).to(tl.float32) # the mix output is bf16
133
+ y = h * tl.rsqrt(tl.sum(h * h, 0) / D + eps) * g
134
+ tl.store(out_ptr + r * o_row + offs, y.to(out_ptr.dtype.element_ty), mask=msk)
135
+
136
+
137
+ def mix_norm(S: torch.Tensor, n_ent: int, mode: int, out: torch.Tensor, g: torch.Tensor, eps: float,
138
+ w: torch.Tensor | None = None, ids: torch.Tensor | None = None,
139
+ emb: torch.Tensor | None = None, add: bool = False, partial: bool = True, pf=None) -> None:
140
+ """``S`` ``(R, NS, D)`` sources (row-major per row), ``out`` ``(R, D)``."""
141
+ R, _, D = S.shape
142
+ block = triton.next_power_of_2(D)
143
+ ne_pad = triton.next_power_of_2(n_ent) if mode >= 2 else 1
144
+ warps = max(4, min(16, ne_pad * block // 1024))
145
+ _mix_norm_kernel[(R,)](S, S.stride(0), S.stride(1), ids if ids is not None else S,
146
+ emb if emb is not None else S, w if w is not None else g, g, out,
147
+ out.stride(0), eps, D, NE=n_ent, NE_PAD=ne_pad, MODE=mode, BLOCK=block,
148
+ ADD=int(add), PART=int(partial), num_warps=warps, PDL=PDL, launch_pdl=PDL, **_pf(pf))
149
+
150
+
151
+ _TUNED_PATH = Path(__file__).with_name("luau_gemv_tuned.py")
152
+ _TUNED: dict | None = None
153
+
154
+
155
+ def _tuned() -> dict:
156
+ """Pinned GEMV configs for this GPU (``benchmark/tune_gemv.py``), else {}."""
157
+ global _TUNED
158
+ if _TUNED is None:
159
+ name = torch.cuda.get_device_name()
160
+ p = torch.cuda.get_device_properties(0)
161
+ arch = [p.major, p.minor, p.multi_processor_count]
162
+ hit = _TABLE.get(name) or next((e for e in _TABLE.values() if e.get("arch") == arch), None)
163
+ _TUNED = dict(hit["gemv"]) if hit else {}
164
+ return _TUNED
165
+
166
+
167
+ def gemv_key(m_pad: int, n: int, k: int, fp8: bool, epi: int) -> str:
168
+ return f"{m_pad},{n},{k},{int(fp8)},{int(epi == 2)}"
169
+
170
+
171
+ def gemv_config(m_pad: int, n: int, k: int, fp8: bool, epi: int) -> tuple[int, int, int, int, int]:
172
+ """``(BLOCK_N, BLOCK_K, num_warps, num_stages, DOT)``: pinned, else a default."""
173
+ hit = _tuned().get(gemv_key(m_pad, n, k, fp8, epi))
174
+ if hit:
175
+ return tuple(hit) + (0,) * (5 - len(hit))
176
+ if m_pad >= 4:
177
+ return 32, 128, 4, 3, 1
178
+ bk = 512 if fp8 else 256
179
+ bn = max(1, min(16, 2048 // (bk * m_pad) * 4))
180
+ return bn, bk, 4, 2, 0
181
+
182
+
183
+ @triton.jit
184
+ def _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn, nm, K, M_PAD: tl.constexpr, BLOCK_N: tl.constexpr,
185
+ BLOCK_K: tl.constexpr, DOT: tl.constexpr):
186
+ if DOT:
187
+ acc = tl.zeros([M_PAD, BLOCK_N], dtype=tl.float32)
188
+ for k0 in range(0, K, BLOCK_K):
189
+ rk = k0 + tl.arange(0, BLOCK_K)
190
+ km = rk < K
191
+ x = tl.load(x_ptr + rm[:, None] * x_row + rk[None, :], mask=mm[:, None] & km[None, :], other=0.0)
192
+ w = tl.load(w_ptr + rn[:, None] * w_row + rk[None, :], mask=nm[:, None] & km[None, :], other=0.0)
193
+ acc = tl.dot(x.to(tl.bfloat16), tl.trans(w.to(tl.bfloat16)), acc)
194
+ return acc
195
+ acc3 = tl.zeros([M_PAD, BLOCK_N, BLOCK_K], dtype=tl.float32)
196
+ for k0 in range(0, K, BLOCK_K):
197
+ rk = k0 + tl.arange(0, BLOCK_K)
198
+ km = rk < K
199
+ x = tl.load(x_ptr + rm[:, None] * x_row + rk[None, :], mask=mm[:, None] & km[None, :], other=0.0)
200
+ w = tl.load(w_ptr + rn[:, None] * w_row + rk[None, :], mask=nm[:, None] & km[None, :], other=0.0)
201
+ acc3 += x.to(tl.float32)[:, None, :] * w.to(tl.float32)[None, :, :]
202
+ return tl.sum(acc3, 2)
203
+
204
+
205
+ @triton.jit
206
+ def _gemv_kernel(x_ptr, x_row, w_ptr, w_row, ws_ptr, y_ptr, y_row, M, N, K, up_off, cap,
207
+ M_PAD: tl.constexpr, EPI: tl.constexpr, FP8: tl.constexpr,
208
+ BLOCK_N: tl.constexpr, BLOCK_K: tl.constexpr, DOT: tl.constexpr = False,
209
+ PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
210
+ """y[m, n] = x[m] . W[n]. EPI 0: store; 1: residual add; 2: SwiGLU; 3: softcapped fp32 logits."""
211
+ _prefetch(pf_ptr, pf_bytes, PF, BULK)
212
+ _pdl(PDL)
213
+ pid = tl.program_id(0)
214
+ rn = pid * BLOCK_N + tl.arange(0, BLOCK_N)
215
+ nm = rn < N
216
+ rm = tl.arange(0, M_PAD)
217
+ mm = rm < M
218
+ acc = _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn, nm, K, M_PAD, BLOCK_N, BLOCK_K, DOT)
219
+ if FP8:
220
+ acc = acc * tl.load(ws_ptr + rn, mask=nm, other=0.0)[None, :]
221
+ ym = mm[:, None] & nm[None, :]
222
+ yp = y_ptr + rm[:, None] * y_row + rn[None, :]
223
+ if EPI == 2:
224
+ up = _gemv_tile(x_ptr, x_row, w_ptr, w_row, rm, mm, rn + up_off, nm, K, M_PAD, BLOCK_N, BLOCK_K, DOT)
225
+ if FP8:
226
+ up = up * tl.load(ws_ptr + up_off + rn, mask=nm, other=0.0)[None, :]
227
+ gate = acc.to(tl.bfloat16).to(tl.float32)
228
+ up = up.to(tl.bfloat16).to(tl.float32)
229
+ tl.store(yp, (gate * tl.sigmoid(gate) * up).to(y_ptr.dtype.element_ty), mask=ym)
230
+ elif EPI == 1:
231
+ res = tl.load(yp, mask=ym, other=0.0).to(tl.float32)
232
+ tl.store(yp, (acc.to(tl.bfloat16).to(tl.float32) + res).to(y_ptr.dtype.element_ty), mask=ym)
233
+ elif EPI == 3:
234
+ z = acc.to(tl.bfloat16).to(tl.float32)
235
+ if cap > 0:
236
+ t = z / cap
237
+ e = tl.exp(-2.0 * tl.abs(t))
238
+ th = (1.0 - e) / (1.0 + e)
239
+ z = cap * tl.where(t < 0, -th, th)
240
+ tl.store(yp, z, mask=ym)
241
+ else:
242
+ tl.store(yp, acc.to(y_ptr.dtype.element_ty), mask=ym)
243
+
244
+
245
+ def gemv(x: torch.Tensor, w: torch.Tensor, y: torch.Tensor, epi: int = 0, scale: torch.Tensor | None = None,
246
+ n: int | None = None, up_off: int = 0, cap: float = 0.0, config: tuple | None = None, pf=None) -> None:
247
+ """``x`` ``(M, K)`` (row stride free), ``w`` ``(N_w, K)``, ``y`` ``(M, n)``; ``n`` output
248
+ columns (SwiGLU: the hidden size, ``up_off`` = its up rows' offset)."""
249
+ M, K = x.shape
250
+ N = n if n is not None else w.shape[0]
251
+ m_pad = max(1, triton.next_power_of_2(M))
252
+ fp8 = scale is not None
253
+ cfg = tuple(config or gemv_config(m_pad, N, K, fp8, epi))
254
+ bn, bk, warps, stages, dot = cfg + (0,) * (5 - len(cfg))
255
+ _gemv_kernel[(triton.cdiv(N, bn),)](x, x.stride(0), w, w.stride(0), scale if fp8 else w, y, y.stride(0),
256
+ M, N, K, up_off, float(cap), M_PAD=max(16, m_pad) if dot else m_pad,
257
+ EPI=epi, FP8=fp8, BLOCK_N=bn, BLOCK_K=bk, DOT=bool(dot),
258
+ num_warps=warps, num_stages=stages, PDL=PDL, launch_pdl=PDL, **_pf(pf))
259
+
260
+
261
+ # CCGQA conv-latent / value-shift ring slots; a verify of up to RING - 3 drafts never overwrites
262
+ RING: tl.constexpr = tl.constexpr(32)
263
+
264
+
265
+ @triton.jit
266
+ def _lat(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, r, t, b, slot, pos, col, cidx, LAG: tl.constexpr):
267
+ """The pre-conv latent of the row's token ``LAG`` back (zero across a document start)."""
268
+ if LAG == 0:
269
+ v = tl.load(qkv_ptr + r * qkv_row + col + cidx).to(tl.float32)
270
+ elif t >= LAG:
271
+ v = tl.load(qkv_ptr + (r - LAG) * qkv_row + col + cidx, mask=pos >= LAG, other=0.0).to(tl.float32)
272
+ else:
273
+ v = tl.load(lat_ptr + b * lat_b + ((slot - LAG) % RING) * lat_l + col + cidx, mask=pos >= LAG,
274
+ other=0.0).to(tl.float32)
275
+ return tl.where(pos >= LAG, v, 0.0)
276
+
277
+
278
+ @triton.jit
279
+ def _conv(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, cs_ptr, csb_ptr, ch_ptr, chb_ptr, h, r, t, b, slot, pos, col,
280
+ HD: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr):
281
+ """Virtual head ``h``'s causal conv."""
282
+ d = tl.arange(0, HD)
283
+ b1 = tl.load(csb_ptr + h * HD + d).to(tl.float32)
284
+ acc = tl.load(chb_ptr + h * HD + d).to(tl.float32)
285
+ for j in tl.static_range(KC):
286
+ u = b1
287
+ for i in tl.static_range(KS):
288
+ a = tl.load(cs_ptr + (h * HD + d) * KS + i).to(tl.float32)
289
+ u += a * _lat(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, r, t, b, slot, pos, col, d, i + j)
290
+ w2 = tl.load(ch_ptr + ((h * KC + j) * HD + d[:, None]) * HD + d[None, :]).to(tl.float32)
291
+ acc += tl.sum(w2 * u[:, None], 0)
292
+ return acc
293
+
294
+
295
+ @triton.jit
296
+ def _conv_pre(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, b1, acc, a0, a1, w0, w1, r, t, b, slot, pos, col,
297
+ HD: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr):
298
+ """``_conv`` (``KS``, ``KC`` <= 2) on taps already in registers (loaded before the PDL wait)."""
299
+ d = tl.arange(0, HD)
300
+ for j in tl.static_range(KC):
301
+ u = b1
302
+ for i in tl.static_range(KS):
303
+ a = a0 if i == 0 else a1
304
+ u += a * _lat(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, r, t, b, slot, pos, col, d, i + j)
305
+ w2 = w0 if j == 0 else w1
306
+ acc += tl.sum(w2 * u[:, None], 0)
307
+ return acc
308
+
309
+
310
+ @triton.jit
311
+ def _qk_decode_kernel(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, vh_ptr, vh_b, vh_l,
312
+ pos_ptr, slot_ptr, cos_ptr, sin_ptr, cs_ptr, csb_ptr, ch_ptr, chb_ptr, qg_ptr, kg_ptr,
313
+ q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h, T, eps,
314
+ NQ: tl.constexpr, NKV: tl.constexpr, NXQ: tl.constexpr, HD: tl.constexpr,
315
+ V_OFF: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr, CCGQA: tl.constexpr,
316
+ QK_MEAN: tl.constexpr, VSHIFT: tl.constexpr, ROPE: tl.constexpr,
317
+ PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
318
+ """One (row, virtual head): queries go to ``q``, keys / values to the caches."""
319
+ h = tl.program_id(1)
320
+ PRE: tl.constexpr = CCGQA and KS <= 2 and KC <= 2
321
+ if PRE:
322
+ dd = tl.arange(0, HD)
323
+ cb1 = tl.load(csb_ptr + h * HD + dd).to(tl.float32)
324
+ cacc = tl.load(chb_ptr + h * HD + dd).to(tl.float32)
325
+ ca0 = tl.load(cs_ptr + (h * HD + dd) * KS).to(tl.float32)
326
+ ca1 = tl.load(cs_ptr + (h * HD + dd) * KS + (KS - 1)).to(tl.float32)
327
+ cw0 = tl.load(ch_ptr + ((h * KC) * HD + dd[:, None]) * HD + dd[None, :]).to(tl.float32)
328
+ cw1 = tl.load(ch_ptr + ((h * KC + (KC - 1)) * HD + dd[:, None]) * HD + dd[None, :]).to(tl.float32)
329
+ r = tl.program_id(0).to(tl.int64)
330
+ b = r // T
331
+ t = r % T
332
+ pos = tl.load(pos_ptr + r).to(tl.int64)
333
+ slot = tl.load(slot_ptr + r).to(tl.int64)
334
+ _prefetch(pf_ptr, pf_bytes, PF, BULK)
335
+ _pdl(PDL)
336
+ HALF: tl.constexpr = HD // 2
337
+ G: tl.constexpr = NQ // NKV
338
+ d = tl.arange(0, HD)
339
+ is_k = h >= NQ
340
+ g = h - NQ
341
+ ph = tl.where(is_k, NXQ + g, h % NXQ)
342
+ col = ph * HD
343
+ base = qkv_ptr + r * qkv_row
344
+ keep = t + RING >= T # only a call's last RING tokens go to the rings (no write races)
345
+ if CCGQA:
346
+ if PRE:
347
+ x = _conv_pre(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, cb1, cacc, ca0, ca1, cw0, cw1, r, t, b, slot,
348
+ pos, col, HD, KS, KC)
349
+ else:
350
+ x = _conv(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, cs_ptr, csb_ptr, ch_ptr, chb_ptr, h, r, t, b, slot,
351
+ pos, col, HD, KS, KC)
352
+ if QK_MEAN:
353
+ if is_k:
354
+ qm = tl.zeros([HD], dtype=tl.float32)
355
+ for jh in tl.static_range(G):
356
+ qm += tl.load(base + ((g * G + jh) % NXQ) * HD + d).to(tl.float32)
357
+ x += 0.5 * (qm / G + tl.load(base + col + d).to(tl.float32))
358
+ else:
359
+ x += 0.5 * (tl.load(base + col + d).to(tl.float32)
360
+ + tl.load(base + (NXQ + h // G) * HD + d).to(tl.float32))
361
+ else:
362
+ x = tl.load(base + col + d).to(tl.float32)
363
+ gain = tl.load(tl.where(is_k, kg_ptr + g * HD, qg_ptr) + d)
364
+ x = x * tl.rsqrt(tl.sum(x * x, 0) / HD + eps) * gain
365
+ if not CCGQA: # plain QK-norm rounds to bf16 before RoPE
366
+ x = x.to(tl.bfloat16).to(tl.float32)
367
+ lo, hi = tl.split(tl.permute(tl.reshape(x, [2, HALF]), (1, 0)))
368
+ if ROPE:
369
+ i2 = tl.arange(0, HALF)
370
+ c = tl.load(cos_ptr + pos * HALF + i2)
371
+ s = tl.load(sin_ptr + pos * HALF + i2)
372
+ lo, hi = lo * c - hi * s, hi * c + lo * s
373
+ lo_i = tl.arange(0, HALF)
374
+ if is_k:
375
+ dst = kc_ptr + b * kc_b + g * kc_h + slot * HD
376
+ else:
377
+ dst = q_ptr + r * q_row + h * HD
378
+ tl.store(dst + lo_i, lo.to(q_ptr.dtype.element_ty))
379
+ tl.store(dst + HALF + lo_i, hi.to(q_ptr.dtype.element_ty))
380
+ if is_k:
381
+ ch = g * HD + d
382
+ own = tl.load(base + V_OFF + ch)
383
+ v = own
384
+ if VSHIFT:
385
+ VH: tl.constexpr = NKV * HD // 2
386
+ upper = ch >= VH
387
+ if t >= 1:
388
+ prev = tl.load(base - qkv_row + V_OFF + ch, mask=upper, other=0.0)
389
+ else:
390
+ prev = tl.load(vh_ptr + b * vh_b + ((slot - 1) % RING) * vh_l + ch - VH,
391
+ mask=upper & (slot >= 1), other=0.0)
392
+ v = tl.where(upper, tl.where(pos > 0, prev, 0.0), own)
393
+ tl.store(vh_ptr + b * vh_b + (slot % RING) * vh_l + ch - VH, own, mask=upper & keep)
394
+ tl.store(vc_ptr + b * kc_b + g * kc_h + slot * HD + d, v.to(vc_ptr.dtype.element_ty))
395
+ if CCGQA:
396
+ if ((h < NXQ) | is_k) & keep:
397
+ tl.store(lat_ptr + b * lat_b + (slot % RING) * lat_l + col + d, tl.load(base + col + d))
398
+
399
+
400
+ def qk_decode(qkv, lat, vh, pos, slot, cos, sin, conv, qg, kg, q, kc, vc, T: int, eps: float, *, nq, nkv,
401
+ nxq, hd, v_off, ks, kc_taps, ccgqa, qk_mean, vshift, rope, pf=None) -> None:
402
+ R = qkv.shape[0]
403
+ cs, csb, ch, chb = conv if conv is not None else (qkv,) * 4
404
+ _qk_decode_kernel[(R, nq + nkv)](
405
+ qkv, qkv.stride(0), lat if lat is not None else qkv, lat.stride(0) if lat is not None else 0,
406
+ lat.stride(1) if lat is not None else 0, vh if vh is not None else qkv,
407
+ vh.stride(0) if vh is not None else 0, vh.stride(1) if vh is not None else 0,
408
+ pos, slot, cos, sin, cs, csb, ch, chb, qg, kg, q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1),
409
+ T, eps, NQ=nq, NKV=nkv, NXQ=nxq, HD=hd, V_OFF=v_off, KS=ks, KC=kc_taps, CCGQA=ccgqa,
410
+ QK_MEAN=qk_mean, VSHIFT=vshift, ROPE=rope, num_warps=8, PDL=PDL, launch_pdl=PDL, **_pf(pf))
411
+
412
+
413
+ @triton.jit
414
+ def _tile_lat(qkv_ptr, qkv_row, r0, t, tm, pos, col, d, T, LAG: tl.constexpr):
415
+ """``[BT, HD]`` pre-conv latents ``LAG`` tokens back of a prompt tile."""
416
+ ok = tm & (pos >= LAG) & (t >= LAG)
417
+ return tl.load(qkv_ptr + (r0 + t - LAG)[:, None] * qkv_row + col + d[None, :], mask=ok[:, None],
418
+ other=0.0).to(tl.float32)
419
+
420
+
421
+ @triton.jit
422
+ def _qk_prefill_kernel(qkv_ptr, qkv_row, lat_ptr, lat_b, lat_l, vh_ptr, vh_b, vh_l, pos_ptr, cos_ptr, sin_ptr,
423
+ cs_ptr, csb_ptr, ch_ptr, chb_ptr, qg_ptr, kg_ptr, q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h,
424
+ T, eps, NQ: tl.constexpr, NKV: tl.constexpr, NXQ: tl.constexpr, HD: tl.constexpr,
425
+ V_OFF: tl.constexpr, KS: tl.constexpr, KC: tl.constexpr, QK_MEAN: tl.constexpr,
426
+ VSHIFT: tl.constexpr, ROPE: tl.constexpr, BT: tl.constexpr):
427
+ """``qk_decode`` for a prompt, ``BT`` tokens of one virtual head per program (taps on tensor cores)."""
428
+ tt = tl.program_id(0)
429
+ h = tl.program_id(1)
430
+ b = tl.program_id(2).to(tl.int64)
431
+ HALF: tl.constexpr = HD // 2
432
+ G: tl.constexpr = NQ // NKV
433
+ t = tt * BT + tl.arange(0, BT)
434
+ tm = t < T
435
+ r0 = b * T
436
+ rows = r0 + t
437
+ pos = tl.load(pos_ptr + rows, mask=tm, other=0).to(tl.int64)
438
+ d = tl.arange(0, HD)
439
+ is_k = h >= NQ
440
+ g = h - NQ
441
+ ph = tl.where(is_k, NXQ + g, h % NXQ)
442
+ col = ph * HD
443
+ b1 = tl.load(csb_ptr + h * HD + d).to(tl.float32)
444
+ x = tl.zeros([BT, HD], dtype=tl.float32) + tl.load(chb_ptr + h * HD + d).to(tl.float32)[None, :]
445
+ for j in tl.static_range(KC):
446
+ u = tl.zeros([BT, HD], dtype=tl.float32) + b1[None, :]
447
+ for i in tl.static_range(KS):
448
+ a = tl.load(cs_ptr + (h * HD + d) * KS + i).to(tl.float32)
449
+ u += a[None, :] * _tile_lat(qkv_ptr, qkv_row, r0, t, tm, pos, col, d, T, i + j)
450
+ w2 = tl.load(ch_ptr + ((h * KC + j) * HD + d[:, None]) * HD + d[None, :]).to(tl.bfloat16)
451
+ u_hi = u.to(tl.bfloat16)
452
+ x = tl.dot(u_hi, w2, x)
453
+ x = tl.dot((u - u_hi.to(tl.float32)).to(tl.bfloat16), w2, x)
454
+ base = qkv_ptr + rows[:, None] * qkv_row
455
+ if QK_MEAN:
456
+ if is_k:
457
+ qm = tl.zeros([BT, HD], dtype=tl.float32)
458
+ for jh in tl.static_range(G):
459
+ qm += tl.load(base + ((g * G + jh) % NXQ) * HD + d[None, :], mask=tm[:, None], other=0.0).to(
460
+ tl.float32)
461
+ x += 0.5 * (qm / G + tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0).to(tl.float32))
462
+ else:
463
+ x += 0.5 * (tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0).to(tl.float32)
464
+ + tl.load(base + (NXQ + h // G) * HD + d[None, :], mask=tm[:, None], other=0.0).to(
465
+ tl.float32))
466
+ gain = tl.load(tl.where(is_k, kg_ptr + g * HD, qg_ptr) + d)
467
+ x = x * tl.rsqrt(tl.sum(x * x, 1) / HD + eps)[:, None] * gain[None, :]
468
+ lo, hi = tl.split(tl.permute(tl.reshape(x, [BT, 2, HALF]), (0, 2, 1)))
469
+ i2 = tl.arange(0, HALF)
470
+ if ROPE:
471
+ c = tl.load(cos_ptr + pos[:, None] * HALF + i2[None, :])
472
+ sn = tl.load(sin_ptr + pos[:, None] * HALF + i2[None, :])
473
+ lo, hi = lo * c - hi * sn, hi * c + lo * sn
474
+ if is_k:
475
+ dst = kc_ptr + b * kc_b + g * kc_h + t[:, None] * HD
476
+ else:
477
+ dst = q_ptr + rows[:, None] * q_row + h * HD
478
+ tl.store(dst + i2[None, :], lo.to(q_ptr.dtype.element_ty), mask=tm[:, None])
479
+ tl.store(dst + HALF + i2[None, :], hi.to(q_ptr.dtype.element_ty), mask=tm[:, None])
480
+ keep = tm & (t + RING >= T)
481
+ if is_k:
482
+ ch = g * HD + d
483
+ own = tl.load(base + V_OFF + ch[None, :], mask=tm[:, None], other=0.0)
484
+ v = own
485
+ if VSHIFT:
486
+ VH: tl.constexpr = NKV * HD // 2
487
+ upper = (ch >= VH)[None, :]
488
+ prev = tl.load(base - qkv_row + V_OFF + ch[None, :], mask=tm[:, None] & upper & (t >= 1)[:, None],
489
+ other=0.0)
490
+ v = tl.where(upper, tl.where((pos > 0)[:, None], prev, 0.0), own)
491
+ tl.store(vh_ptr + b * vh_b + (t % RING)[:, None] * vh_l + ch[None, :] - VH, own,
492
+ mask=keep[:, None] & upper)
493
+ tl.store(vc_ptr + b * kc_b + g * kc_h + t[:, None] * HD + d[None, :], v.to(vc_ptr.dtype.element_ty),
494
+ mask=tm[:, None])
495
+ if (h < NXQ) | is_k:
496
+ tl.store(lat_ptr + b * lat_b + (t % RING)[:, None] * lat_l + col + d[None, :],
497
+ tl.load(base + col + d[None, :], mask=tm[:, None], other=0.0), mask=keep[:, None])
498
+
499
+
500
+ def qk_prefill(qkv, lat, vh, pos, cos, sin, conv, qg, kg, q, kc, vc, T: int, eps: float, *, nq, nkv, nxq, hd,
501
+ v_off, ks, kc_taps, qk_mean, vshift, rope) -> None:
502
+ """CCGQA ``qk_decode`` for ``B`` prompts of ``T`` tokens starting at slot 0."""
503
+ B = qkv.shape[0] // T
504
+ cs, csb, ch, chb = conv
505
+ bt = 32 if hd <= 128 else 16 # 64-token tiles spill
506
+ _qk_prefill_kernel[(triton.cdiv(T, bt), nq + nkv, B)](
507
+ qkv, qkv.stride(0), lat, lat.stride(0), lat.stride(1), vh if vh is not None else qkv,
508
+ vh.stride(0) if vh is not None else 0, vh.stride(1) if vh is not None else 0, pos, cos, sin,
509
+ cs, csb, ch, chb, qg, kg, q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1), T, eps, NQ=nq, NKV=nkv,
510
+ NXQ=nxq, HD=hd, V_OFF=v_off, KS=ks, KC=kc_taps, QK_MEAN=qk_mean, VSHIFT=vshift, ROPE=rope, BT=bt,
511
+ num_warps=4)
512
+
513
+
514
+ # grid (span, KV head, sequence); the last span program merges the partials and finishes the rows
515
+
516
+
517
+ @triton.jit
518
+ def _spans(start, slot, NSPL: tl.constexpr, BL: tl.constexpr, MINB: tl.constexpr = 1):
519
+ """``(span, n_spans)`` for keys ``[start, slot]``, sized on the device."""
520
+ n = slot + 1 - start
521
+ span = tl.maximum(tl.cdiv(tl.cdiv(n, NSPL), BL), MINB) * BL
522
+ return span, tl.where(n > 0, tl.cdiv(n, span), 0)
523
+
524
+
525
+ @triton.jit
526
+ def _merge(ml_ptr, acc_ptr, base0, n_sp, hrow, HD: tl.constexpr, G_PAD: tl.constexpr, GH_PAD: tl.constexpr,
527
+ NSPL: tl.constexpr, CH: tl.constexpr):
528
+ """Merge the span partials of the query heads ``hrow`` -> bf16-rounded output."""
529
+ d = tl.arange(0, HD)
530
+ si = tl.arange(0, NSPL)
531
+ m = tl.load(ml_ptr + (base0 + si)[:, None] * 2 * G_PAD + hrow[None, :], mask=(si < n_sp)[:, None],
532
+ other=float("-inf"))
533
+ l = tl.load(ml_ptr + (base0 + si)[:, None] * 2 * G_PAD + G_PAD + hrow[None, :], mask=(si < n_sp)[:, None],
534
+ other=0.0)
535
+ mg = tl.max(m, 0)
536
+ mg = tl.where(mg == float("-inf"), 0.0, mg)
537
+ lsum = tl.sum(tl.exp2(m - mg[None, :]) * l, 0)
538
+ o = tl.zeros([GH_PAD, HD], dtype=tl.float32)
539
+ for c0 in range(0, n_sp, CH):
540
+ cs = c0 + tl.arange(0, CH)
541
+ cm = cs < n_sp
542
+ mc = tl.load(ml_ptr + (base0 + cs)[:, None] * 2 * G_PAD + hrow[None, :], mask=cm[:, None],
543
+ other=float("-inf"))
544
+ a = tl.load(acc_ptr + ((base0 + cs)[:, None, None] * G_PAD + hrow[None, :, None]) * HD
545
+ + d[None, None, :], mask=cm[:, None, None], other=0.0)
546
+ o += tl.sum(tl.exp2(mc - mg[None, :])[:, :, None] * a, 0)
547
+ o = o / tl.maximum(lsum, 1e-30)[:, None]
548
+ return o.to(tl.bfloat16).to(tl.float32)
549
+
550
+
551
+ @triton.jit
552
+ def _finish(ml_ptr, acc_ptr, r, g, b, n_sp, slot, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row,
553
+ lam_off, gate_off, NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr,
554
+ HD: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr, GATE: tl.constexpr):
555
+ """Merge row ``r``'s spans for KV head ``g`` and finish DiffAttn V2 + XSA (+ gate) into ``y``."""
556
+ base0 = (r * NKV + g) * NSPL
557
+ d = tl.arange(0, HD)
558
+ if DIFF:
559
+ GH: tl.constexpr = G // 2
560
+ GH_PAD: tl.constexpr = G_PAD // 2
561
+ i = tl.arange(0, GH_PAD)
562
+ im = i < GH
563
+ y0 = _merge(ml_ptr, acc_ptr, base0, n_sp, 2 * i, HD, G_PAD, GH_PAD, NSPL, CH)
564
+ y1 = _merge(ml_ptr, acc_ptr, base0, n_sp, 2 * i + 1, HD, G_PAD, GH_PAD, NSPL, CH)
565
+ lam = tl.load(qkv_ptr + r * qkv_row + lam_off + g * GH + i, mask=im, other=0.0).to(tl.float32)
566
+ y = y0 - tl.sigmoid(lam)[:, None] * y1
567
+ else:
568
+ GH: tl.constexpr = G
569
+ GH_PAD: tl.constexpr = G_PAD
570
+ i = tl.arange(0, GH_PAD)
571
+ im = i < GH
572
+ y = _merge(ml_ptr, acc_ptr, base0, n_sp, i, HD, G_PAD, GH_PAD, NSPL, CH)
573
+ if XSA:
574
+ v = tl.load(vc_ptr + b * kc_b + g * kc_h + slot * HD + d).to(tl.float32)
575
+ vn = v / tl.maximum(tl.sqrt(tl.sum(v * v, 0)), 1e-12)
576
+ y = y - tl.sum(y * vn[None, :], 1)[:, None] * vn[None, :]
577
+ oh = g * GH + i
578
+ if GATE == 1: # per channel
579
+ gt = tl.load(qkv_ptr + r * qkv_row + gate_off + oh[:, None] * HD + d[None, :], mask=im[:, None],
580
+ other=0.0).to(tl.float32)
581
+ y = y * tl.sigmoid(gt)
582
+ elif GATE == 2: # per head
583
+ gt = tl.load(qkv_ptr + r * qkv_row + gate_off + oh, mask=im, other=0.0).to(tl.float32)
584
+ y = y * tl.sigmoid(gt)[:, None]
585
+ tl.store(y_ptr + r * y_row + oh[:, None] * HD + d[None, :], y.to(y_ptr.dtype.element_ty), mask=im[:, None])
586
+
587
+
588
+ @triton.jit
589
+ def _fence():
590
+ """``fence.acq_rel.gpu`` in every thread (CUDA's ``__threadfence``)."""
591
+ tl.inline_asm_elementwise("fence.acq_rel.gpu; mov.u32 $0, 0;", "=r", [], dtype=tl.int32, is_pure=False,
592
+ pack=1)
593
+
594
+
595
+ @triton.jit
596
+ def _seq_keys(start_ptr, slot_ptr, r0, T, win, WIN: tl.constexpr):
597
+ """A sequence's key range ``[start, last]``."""
598
+ start = tl.load(start_ptr + r0)
599
+ if WIN:
600
+ start = tl.maximum(start, tl.load(slot_ptr + r0) - win + 1)
601
+ return start, tl.load(slot_ptr + r0 + T - 1)
602
+
603
+
604
+ @triton.jit
605
+ def _unit(x, tol):
606
+ """``x / |x|``; 0 where ``|x| <= tol`` (the port's ``_unit``)."""
607
+ n = tl.sqrt(tl.sum(x * x, 0))
608
+ return tl.where(n > tol, x / tl.maximum(n, tol), 0.0)
609
+
610
+
611
+ @triton.jit
612
+ def _iha_finish(ml_ptr, acc_ptr, rt, g, b, n_sp, vs0, r_ptr, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr,
613
+ y_row, gate_off, P: tl.constexpr, NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr,
614
+ G_PAD: tl.constexpr, HD: tl.constexpr, CH: tl.constexpr, XSA: tl.constexpr, GATE: tl.constexpr):
615
+ """IHA finish: merge, collapse with ``R``, Gram-Schmidt XSA and the gate for one token row."""
616
+ d = tl.arange(0, HD)
617
+ i = tl.arange(0, G_PAD)
618
+ im = i < G
619
+ oh = g * G + i
620
+ y = tl.zeros([G_PAD, HD], dtype=tl.float32)
621
+ for p in tl.static_range(P):
622
+ yp = _merge(ml_ptr, acc_ptr, ((rt * P + p) * NKV + g) * NSPL, n_sp, i, HD, G_PAD, G_PAD, NSPL, CH)
623
+ rr = tl.load(r_ptr + oh * P + p, mask=im, other=0.0).to(tl.float32)
624
+ y += rr[:, None] * yp
625
+ y = y.to(tl.bfloat16).to(tl.float32)
626
+ if XSA:
627
+ vb = vc_ptr + b * kc_b + g * kc_h + vs0 * HD
628
+ e0 = tl.zeros([HD], dtype=tl.float32)
629
+ e1 = tl.zeros([HD], dtype=tl.float32)
630
+ e2 = tl.zeros([HD], dtype=tl.float32)
631
+ for p in tl.static_range(P):
632
+ w = _unit(tl.load(vb + p * HD + d).to(tl.float32), 1e-12)
633
+ w = w - tl.sum(w * e0, 0) * e0
634
+ w = w - tl.sum(w * e1, 0) * e1
635
+ w = w - tl.sum(w * e2, 0) * e2
636
+ e = _unit(w, 1e-2)
637
+ y = y - tl.sum(y * e[None, :], 1)[:, None] * e[None, :]
638
+ if p == 0:
639
+ e0 = e
640
+ elif p == 1:
641
+ e1 = e
642
+ elif p == 2:
643
+ e2 = e
644
+ if GATE == 1:
645
+ gt = tl.load(qkv_ptr + rt * qkv_row + gate_off + oh[:, None] * HD + d[None, :], mask=im[:, None],
646
+ other=0.0).to(tl.float32)
647
+ y = y * tl.sigmoid(gt)
648
+ elif GATE == 2:
649
+ gt = tl.load(qkv_ptr + rt * qkv_row + gate_off + oh, mask=im, other=0.0).to(tl.float32)
650
+ y = y * tl.sigmoid(gt)[:, None]
651
+ tl.store(y_ptr + rt * y_row + oh[:, None] * HD + d[None, :], y.to(y_ptr.dtype.element_ty), mask=im[:, None])
652
+
653
+
654
+ @triton.jit
655
+ def _attn_kernel(q_ptr, q_row, kc_ptr, vc_ptr, kc_b, kc_h, slot_ptr, start_ptr, ml_ptr, acc_ptr, cnt_ptr,
656
+ qkv_ptr, qkv_row, y_ptr, y_row, T, qk_scale, lam_off, gate_off, win, r_ptr,
657
+ NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr, QR: tl.constexpr,
658
+ HD: tl.constexpr, BL: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr,
659
+ GATE: tl.constexpr, FINISH: tl.constexpr, WIN: tl.constexpr = False, IHA: tl.constexpr = 0,
660
+ MINB: tl.constexpr = 1, PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
661
+ """Flash-decoding attention; ``FINISH``: the last span program finishes the row."""
662
+ sp = tl.program_id(0)
663
+ g = tl.program_id(1)
664
+ b = tl.program_id(2).to(tl.int64)
665
+ r0 = b * T
666
+ start, last = _seq_keys(start_ptr, slot_ptr, r0, T, win, WIN)
667
+ span, n_sp = _spans(start, last, NSPL, BL, MINB)
668
+ lo = start + sp * span
669
+ hi = tl.minimum(last + 1, lo + span)
670
+ qi = tl.arange(0, QR)
671
+ t = qi // G
672
+ j = qi % G
673
+ qm = t < T
674
+ lim = tl.load(slot_ptr + r0 + t, mask=qm, other=-1)
675
+ _prefetch(pf_ptr, pf_bytes, PF, BULK)
676
+ _pdl(PDL)
677
+ if lo < hi:
678
+ d = tl.arange(0, HD)
679
+ q = tl.load(q_ptr + (r0 + t)[:, None] * q_row + (g * G + j)[:, None] * HD + d[None, :],
680
+ mask=qm[:, None], other=0.0)
681
+ m_i = tl.full([QR], float("-inf"), tl.float32)
682
+ l_i = tl.zeros([QR], dtype=tl.float32)
683
+ acc = tl.zeros([QR, HD], dtype=tl.float32)
684
+ kbase = b * kc_b + g * kc_h
685
+ for k0 in range(lo, hi, BL):
686
+ kk = k0 + tl.arange(0, BL)
687
+ km = kk < hi
688
+ k = tl.load(kc_ptr + kbase + kk[:, None] * HD + d[None, :], mask=km[:, None], other=0.0)
689
+ s = tl.dot(q, tl.trans(k)) * qk_scale # base-2 logits
690
+ ok = km[None, :] & (kk[None, :] <= lim[:, None])
691
+ if WIN:
692
+ ok = ok & (kk[None, :] > lim[:, None] - win)
693
+ s = tl.where(ok, s, float("-inf"))
694
+ m_new = tl.maximum(m_i, tl.max(s, 1))
695
+ m_ok = tl.where(m_new == float("-inf"), 0.0, m_new) # a row with no key yet
696
+ alpha = tl.exp2(m_i - m_ok)
697
+ p = tl.exp2(s - m_ok[:, None])
698
+ l_i = l_i * alpha + tl.sum(p, 1)
699
+ v = tl.load(vc_ptr + kbase + kk[:, None] * HD + d[None, :], mask=km[:, None], other=0.0)
700
+ acc = acc * alpha[:, None] + tl.dot(p.to(v.dtype), v)
701
+ m_i = m_new
702
+ base = ((r0 + t) * NKV + g) * NSPL + sp
703
+ tl.store(ml_ptr + base * 2 * G_PAD + j, m_i, mask=qm)
704
+ tl.store(ml_ptr + base * 2 * G_PAD + G_PAD + j, l_i, mask=qm)
705
+ tl.store(acc_ptr + (base * G_PAD + j)[:, None] * HD + d[None, :], acc, mask=qm[:, None])
706
+ if FINISH:
707
+ _fence()
708
+ tl.debug_barrier()
709
+ old = tl.atomic_add(cnt_ptr + b * NKV + g, 1, sem="acq_rel", scope="gpu")
710
+ if old == n_sp - 1:
711
+ _fence()
712
+ tl.atomic_xchg(cnt_ptr + b * NKV + g, 0, sem="relaxed", scope="gpu")
713
+ if IHA:
714
+ _iha_finish(ml_ptr, acc_ptr, b, g, b, n_sp, tl.load(slot_ptr + r0), r_ptr, qkv_ptr, qkv_row,
715
+ vc_ptr, kc_b, kc_h, y_ptr, y_row, gate_off, IHA, NKV, NSPL, G, G_PAD, HD, CH,
716
+ XSA, GATE)
717
+ else:
718
+ _finish(ml_ptr, acc_ptr, r0, g, b, n_sp, last, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr,
719
+ y_row, lam_off, gate_off, NKV, NSPL, G, G_PAD, HD, CH, DIFF, XSA, GATE)
720
+
721
+
722
+ @triton.jit
723
+ def _finish_kernel(ml_ptr, slot_ptr, start_ptr, acc_ptr, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row, T,
724
+ lam_off, gate_off, NKV: tl.constexpr, NSPL: tl.constexpr, G: tl.constexpr, G_PAD: tl.constexpr,
725
+ HD: tl.constexpr, BL: tl.constexpr, CH: tl.constexpr, DIFF: tl.constexpr, XSA: tl.constexpr,
726
+ GATE: tl.constexpr, MINB: tl.constexpr = 1, PDL: tl.constexpr = False):
727
+ """The finish of a multi-row (verify / short prompt) attention, a program per (row, KV head)."""
728
+ _pdl(PDL)
729
+ r = tl.program_id(0).to(tl.int64)
730
+ g = tl.program_id(1)
731
+ b = r // T
732
+ _, n_sp = _spans(tl.load(start_ptr + b * T), tl.load(slot_ptr + b * T + T - 1), NSPL, BL, MINB)
733
+ _finish(ml_ptr, acc_ptr, r, g, b, n_sp, tl.load(slot_ptr + r), qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr,
734
+ y_row, lam_off, gate_off, NKV, NSPL, G, G_PAD, HD, CH, DIFF, XSA, GATE)
735
+
736
+
737
+ @triton.jit
738
+ def _iha_finish_kernel(ml_ptr, slot_ptr, start_ptr, acc_ptr, qkv_ptr, qkv_row, vc_ptr, kc_b, kc_h, y_ptr, y_row,
739
+ Tt, win, r_ptr, gate_off, P: tl.constexpr, NKV: tl.constexpr, NSPL: tl.constexpr,
740
+ G: tl.constexpr, G_PAD: tl.constexpr, HD: tl.constexpr, BL: tl.constexpr, CH: tl.constexpr,
741
+ XSA: tl.constexpr, GATE: tl.constexpr, WIN: tl.constexpr, MINB: tl.constexpr = 1,
742
+ PDL: tl.constexpr = False):
743
+ """IHA's finish of a multi-token call, a program per (token row, KV head)."""
744
+ _pdl(PDL)
745
+ rt = tl.program_id(0).to(tl.int64)
746
+ g = tl.program_id(1)
747
+ b = rt // Tt
748
+ start, last = _seq_keys(start_ptr, slot_ptr, b * Tt * P, Tt * P, win, WIN)
749
+ _, n_sp = _spans(start, last, NSPL, BL, MINB)
750
+ _iha_finish(ml_ptr, acc_ptr, rt, g, b, n_sp, tl.load(slot_ptr + rt * P), r_ptr, qkv_ptr, qkv_row, vc_ptr,
751
+ kc_b, kc_h, y_ptr, y_row, gate_off, P, NKV, NSPL, G, G_PAD, HD, CH, XSA, GATE)
752
+
753
+
754
+ # tests set 32 / False so a 1-row step and a verify match bitwise
755
+ QR_MIN = 16
756
+ FUSED_FINISH = True
757
+ # smallest span, in 64-key blocks
758
+ ATTN_MINB = 1
759
+
760
+
761
+ def attention(q, kc, vc, slot, start, qkv, y, ml, acc, cnt, T: int, *, nq, nkv, hd, nspl, lam_off,
762
+ gate_off, diff, xsa, gate, win: int = 0, iha_p: int = 0, iha_r=None, pf=None, bl: int = 64,
763
+ warps: int | None = None) -> None:
764
+ """Flash-decoding attention for ``R = B * T`` rows (``win``: sliding window; ``iha_p``: pseudo-rows)."""
765
+ if iha_p:
766
+ assert not diff, "IHA with DiffAttn V2: no fused finish"
767
+ T = T * iha_p
768
+ R = q.shape[0]
769
+ G = nq // nkv
770
+ g_pad = max(16, triton.next_power_of_2(G))
771
+ qr = max(QR_MIN, triton.next_power_of_2(T * G))
772
+ ch = 8 if g_pad <= 16 else 4
773
+ _attn_kernel[(nspl, nkv, R // T)](q, q.stride(0), kc, vc, kc.stride(0), kc.stride(1), slot, start, ml, acc,
774
+ cnt, qkv, qkv.stride(0), y, y.stride(0), T, hd ** -0.5 * LOG2E, lam_off,
775
+ gate_off, int(win), iha_r if iha_p else q,
776
+ NKV=nkv, NSPL=nspl, G=G, G_PAD=g_pad, QR=qr, HD=hd, BL=bl,
777
+ CH=ch, DIFF=diff, XSA=xsa, GATE=gate,
778
+ FINISH=T == max(iha_p, 1) and FUSED_FINISH, WIN=win > 0, IHA=iha_p,
779
+ MINB=ATTN_MINB,
780
+ num_warps=warps or (4 if qr <= 32 else 8), num_stages=2, PDL=PDL, launch_pdl=PDL,
781
+ **_pf(pf))
782
+ if iha_p:
783
+ if T > iha_p or not FUSED_FINISH:
784
+ _iha_finish_kernel[(R // iha_p, nkv)](ml, slot, start, acc, qkv, qkv.stride(0), vc, kc.stride(0),
785
+ kc.stride(1), y, y.stride(0), T // iha_p, int(win), iha_r,
786
+ gate_off, P=iha_p, NKV=nkv, NSPL=nspl, G=G, G_PAD=g_pad,
787
+ HD=hd, BL=bl, CH=ch, XSA=xsa, GATE=gate, WIN=win > 0, MINB=ATTN_MINB,
788
+ num_warps=4, PDL=PDL, launch_pdl=PDL)
789
+ return
790
+ if T > 1 or not FUSED_FINISH:
791
+ _finish_kernel[(R, nkv)](ml, slot, start, acc, qkv, qkv.stride(0), vc, kc.stride(0), kc.stride(1), y,
792
+ y.stride(0), T, lam_off, gate_off, NKV=nkv, NSPL=nspl, G=G, G_PAD=g_pad, HD=hd,
793
+ BL=bl, CH=ch, DIFF=diff, XSA=xsa, GATE=gate, MINB=ATTN_MINB, num_warps=4, PDL=PDL,
794
+ launch_pdl=PDL)
795
+
796
+
797
+ @triton.jit
798
+ def _iha_mix_kernel(q_ptr, q_row, kl_ptr, vl_ptr, kl_b, kl_h, aq_ptr, ak_ptr, av_ptr, qg_ptr, kg_ptr, temp_ptr,
799
+ pos_ptr, slot_ptr, cos_ptr, sin_ptr, qv_ptr, qv_row, kc_ptr, vc_ptr, kc_b, kc_h, T, eps,
800
+ P: tl.constexpr, NQ: tl.constexpr, NQ_PAD: tl.constexpr, NKV: tl.constexpr, HD: tl.constexpr,
801
+ ROPE: tl.constexpr, PDL: tl.constexpr = False, pf_ptr=None, pf_bytes=0, PF: tl.constexpr = False, BULK: tl.constexpr = False):
802
+ """One (row, pseudo-head p): IHA mixes, norm, temperature and RoPE, then cache / ``qv`` writes."""
803
+ r = tl.program_id(0).to(tl.int64)
804
+ p = tl.program_id(1)
805
+ b = r // T
806
+ HALF: tl.constexpr = HD // 2
807
+ rslot = tl.load(slot_ptr + r).to(tl.int64)
808
+ pos = tl.load(pos_ptr + r).to(tl.int64) * P + p
809
+ slot = rslot * P + p
810
+ d = tl.arange(0, HD)
811
+ i2 = tl.arange(0, HALF)
812
+ h = tl.arange(0, NQ_PAD)
813
+ hm = h < NQ
814
+ at = tl.load(aq_ptr + (h[None, :] * NQ + h[:, None]) * P + p, mask=hm[:, None] & hm[None, :], other=0.0)
815
+ qgain = tl.load(qg_ptr + d)
816
+ kgain = tl.load(kg_ptr + d)
817
+ if ROPE:
818
+ c = tl.load(cos_ptr + pos * HALF + i2)
819
+ s = tl.load(sin_ptr + pos * HALF + i2)
820
+ _prefetch(pf_ptr, pf_bytes, PF, BULK)
821
+ _pdl(PDL)
822
+ # queries: one tensor-core tile
823
+ q = tl.load(q_ptr + r * q_row + h[:, None] * HD + d[None, :], mask=hm[:, None], other=0.0)
824
+ x = tl.dot(at.to(tl.bfloat16), q.to(tl.bfloat16)).to(tl.bfloat16).to(tl.float32) # at[h, m]
825
+ x = x * tl.rsqrt(tl.sum(x * x, 1) / HD + eps)[:, None] * qgain[None, :]
826
+ x = x.to(tl.bfloat16).to(tl.float32)
827
+ lo, hi = tl.split(tl.permute(tl.reshape(x, [NQ_PAD, 2, HALF]), (0, 2, 1)))
828
+ if ROPE:
829
+ lo, hi = lo * c[None, :] - hi * s[None, :], hi * c[None, :] + lo * s[None, :]
830
+ dst = qv_ptr + (r * P + p) * qv_row + h[:, None] * HD + i2[None, :]
831
+ tl.store(dst, lo.to(qv_ptr.dtype.element_ty), mask=hm[:, None])
832
+ tl.store(dst + HALF, hi.to(qv_ptr.dtype.element_ty), mask=hm[:, None])
833
+ # keys / values: CUDA-core sums
834
+ src = b * kl_b + rslot * HD
835
+ for g in tl.static_range(NKV):
836
+ k = tl.zeros([HD], dtype=tl.float32)
837
+ v = tl.zeros([HD], dtype=tl.float32)
838
+ for m in tl.static_range(NKV):
839
+ ak = tl.load(ak_ptr + (m * NKV + g) * P + p).to(tl.float32)
840
+ av = tl.load(av_ptr + (m * NKV + g) * P + p).to(tl.float32)
841
+ k += ak * tl.load(kl_ptr + src + m * kl_h + d).to(tl.float32)
842
+ v += av * tl.load(vl_ptr + src + m * kl_h + d).to(tl.float32)
843
+ k = k.to(tl.bfloat16).to(tl.float32)
844
+ k = (k * tl.rsqrt(tl.sum(k * k, 0) / HD + eps) * kgain).to(tl.bfloat16).to(tl.float32)
845
+ k = (k * tl.load(temp_ptr + g)).to(tl.bfloat16).to(tl.float32)
846
+ klo, khi = tl.split(tl.permute(tl.reshape(k, [2, HALF]), (1, 0)))
847
+ if ROPE:
848
+ klo, khi = klo * c - khi * s, khi * c + klo * s
849
+ kd = kc_ptr + b * kc_b + g * kc_h + slot * HD
850
+ tl.store(kd + i2, klo.to(kc_ptr.dtype.element_ty))
851
+ tl.store(kd + HALF + i2, khi.to(kc_ptr.dtype.element_ty))
852
+ tl.store(vc_ptr + b * kc_b + g * kc_h + slot * HD + d, v.to(vc_ptr.dtype.element_ty))
853
+
854
+
855
+ def iha_mix(q, kl, vl, aq, ak, av, qg, kg, temp, pos, slot, cos, sin, qv, kc, vc, T: int, eps: float, *,
856
+ P: int, nq: int, nkv: int, hd: int, rope: bool, pf=None) -> None:
857
+ """``q`` ``(R, nq, hd)`` latent queries, ``kl`` / ``vl`` ``(B, nkv, L, hd)`` latent keys / values at the
858
+ rows' slots, mixes ``(H, H, P)`` bf16, ``qv`` ``(R * P, nq, hd)``, caches ``(B, nkv, L * P, hd)``."""
859
+ R = q.shape[0]
860
+ _iha_mix_kernel[(R, P)](q, q.stride(0), kl, vl, kl.stride(0), kl.stride(1), aq, ak, av, qg, kg, temp, pos,
861
+ slot, cos, sin, qv, qv.stride(0), kc, vc, kc.stride(0), kc.stride(1), T, eps,
862
+ P=P, NQ=nq, NQ_PAD=max(16, triton.next_power_of_2(nq)), NKV=nkv, HD=hd, ROPE=rope,
863
+ num_warps=4, PDL=PDL, launch_pdl=PDL, **_pf(pf))
864
+
865
+
866
+ @triton.jit
867
+ def _head_gate_kernel(y_ptr, y_row, b_ptr, h_ptr, h_row, D, RK: tl.constexpr, BLOCK: tl.constexpr,
868
+ PDL: tl.constexpr = False):
869
+ """Gated byte head input ``[h, sigmoid(u . h + b) * P h]``."""
870
+ _pdl(PDL)
871
+ r = tl.program_id(0).to(tl.int64)
872
+ j = tl.arange(0, BLOCK)
873
+ jm = j < RK
874
+ a = tl.load(y_ptr + r * y_row + RK).to(tl.float32) + tl.load(b_ptr).to(tl.float32)
875
+ gt = tl.sigmoid(a.to(tl.bfloat16).to(tl.float32)).to(tl.bfloat16).to(tl.float32)
876
+ yp = tl.load(y_ptr + r * y_row + j, mask=jm, other=0.0).to(tl.float32)
877
+ tl.store(h_ptr + r * h_row + D + j, (gt * yp).to(h_ptr.dtype.element_ty), mask=jm)
878
+
879
+
880
+ def head_gate(y, bias, h, D: int, rank: int) -> None:
881
+ _head_gate_kernel[(y.shape[0],)](y, y.stride(0), bias, h, h.stride(0), D, RK=rank,
882
+ BLOCK=triton.next_power_of_2(rank), num_warps=4, PDL=PDL, launch_pdl=PDL)
883
+
884
+
885
+ @triton.jit
886
+ def _sample_kernel(x_ptr, x_row, V, inv_temp, top_k, top_p, min_p, seed_ptr, excl_ptr, y_ptr, pd_ptr, u_ptr,
887
+ pres_ptr, pres_row, ids_ptr, penalty, T,
888
+ TOP_K: tl.constexpr, TOP_P: tl.constexpr, MIN_P: tl.constexpr, EXCL: tl.constexpr,
889
+ REP: tl.constexpr, GREEDY: tl.constexpr, T_MAX: tl.constexpr, BLOCK: tl.constexpr,
890
+ ITERS: tl.constexpr):
891
+ """One row: repetition penalty, temperature, top-k, top-p, min-p (thresholds by bisection), then a
892
+ draw."""
893
+ r = tl.program_id(0).to(tl.int64)
894
+ offs = tl.arange(0, BLOCK)
895
+ msk = offs < V
896
+ x = tl.load(x_ptr + r * x_row + offs, mask=msk, other=float("-inf"))
897
+ if REP:
898
+ b = r // T
899
+ t = r % T
900
+ seen = tl.load(pres_ptr + b * pres_row + offs, mask=msk, other=0) != 0
901
+ for i in tl.static_range(1, T_MAX):
902
+ if i <= t:
903
+ seen = seen | (offs == tl.load(ids_ptr + b * T + i))
904
+ x = tl.where(seen, tl.where(x < 0, x * penalty, x / penalty), x)
905
+ if GREEDY:
906
+ tl.store(y_ptr + r, tl.argmax(x, 0).to(tl.int64))
907
+ else:
908
+ x = x * inv_temp
909
+ if TOP_K:
910
+ lo = tl.min(tl.where(msk, x, float("inf")), 0)
911
+ hi = tl.max(x, 0)
912
+ for _ in range(ITERS): # count(x >= lo) >= k > count(x >= hi)
913
+ mid = lo + (hi - lo) * 0.5
914
+ ok = tl.sum((x >= mid).to(tl.int32), 0) >= top_k
915
+ lo = tl.where(ok, mid, lo)
916
+ hi = tl.where(ok, hi, mid)
917
+ kth = tl.min(tl.where(x >= lo, x, float("inf")), 0)
918
+ x = tl.where(x >= kth, x, float("-inf"))
919
+ m = tl.max(x, 0)
920
+ p = tl.exp(x - m) # max 1
921
+ z = tl.sum(p, 0)
922
+ if TOP_P:
923
+ lo = tl.zeros([], dtype=tl.float32)
924
+ hi = tl.full([], 1.0, tl.float32) # mass(p >= lo) >= top_p * z > mass(p >= hi) (or hi = 1)
925
+ goal = top_p * z
926
+ for _ in range(ITERS):
927
+ mid = lo + (hi - lo) * 0.5
928
+ ok = tl.sum(tl.where(p >= mid, p, 0.0), 0) >= goal
929
+ lo = tl.where(ok, mid, lo)
930
+ hi = tl.where(ok, hi, mid)
931
+ tau = tl.min(tl.where(p >= lo, p, float("inf")), 0)
932
+ p = tl.where(p >= tau, p, 0.0)
933
+ if MIN_P:
934
+ p = tl.where(p >= min_p, p, 0.0) # max is 1: min_p * max = min_p
935
+ seed = tl.load(seed_ptr)
936
+ if EXCL:
937
+ e = tl.load(excl_ptr + r)
938
+ pd = tl.sum(tl.where(offs == e, p, 0.0), 0) / tl.sum(p, 0)
939
+ tl.store(pd_ptr + r, pd)
940
+ tl.store(u_ptr + r, tl.rand(seed + 7919, r.to(tl.int32)))
941
+ p = tl.where(offs == e, 0.0, p)
942
+ cdf = tl.cumsum(p, 0)
943
+ u = tl.rand(seed, r.to(tl.int32)) * tl.max(cdf, 0)
944
+ tok = tl.min(tl.where((cdf > u) & (p > 0), offs, BLOCK), 0)
945
+ tl.store(y_ptr + r, tl.where(tok < V, tok, tl.argmax(p, 0)).to(tl.int64))
946
+
947
+
948
+ def sample(x: torch.Tensor, seed: torch.Tensor, y: torch.Tensor, *, temperature: float, top_k: int, top_p: float,
949
+ min_p: float, excl: torch.Tensor | None = None, pd: torch.Tensor | None = None,
950
+ u: torch.Tensor | None = None, greedy: bool = False, penalty: float = 1.0,
951
+ pres: torch.Tensor | None = None, ids: torch.Tensor | None = None, T: int = 1) -> None:
952
+ """Draw ``y`` ``(R,)`` from fp32 logits ``x`` ``(R, V)``."""
953
+ R, V = x.shape
954
+ block = triton.next_power_of_2(V)
955
+ rep = penalty != 1.0
956
+ _sample_kernel[(R,)](x, x.stride(0), V, 1.0 / temperature, int(top_k), float(top_p), float(min_p), seed,
957
+ excl if excl is not None else y, y, pd if pd is not None else x,
958
+ u if u is not None else x, pres.view(torch.uint8) if rep else y,
959
+ pres.stride(0) if rep else 0, ids if rep else y, float(penalty), T,
960
+ TOP_K=bool(top_k) and top_k < V, TOP_P=top_p < 1.0, MIN_P=bool(min_p),
961
+ EXCL=excl is not None, REP=rep, GREEDY=greedy, T_MAX=T, BLOCK=block, ITERS=40,
962
+ num_warps=32 if block >= 16384 else 8)
963
+
964
+
965
+ kernels = sys.modules[__name__] # ``from .luau_kernels import kernels as K``: transformers'
966
+ # remote code only follows ``from .module import ...`` lines
modeling_luau_coder.py CHANGED
@@ -1,3 +1,9 @@
 
 
 
 
 
 
1
  from __future__ import annotations
2
 
3
  import copy
@@ -35,16 +41,11 @@ __all__ = ["LuauCoderCache", "LuauCoderForCausalLM", "LuauCoderModel", "LuauCode
35
 
36
  _V5 = int(transformers.__version__.split(".")[0]) >= 5
37
  IGNORE_INDEX = -100
38
- _HW_TANH = torch.version.hip is None # PTX tanh.approx in the fused CE; ROCm falls back to sigmoid
39
-
40
-
41
- # fused elementwise
42
 
43
 
44
  class _Fused:
45
- """``fn`` through ``torch.compile`` on CUDA (one generated kernel per
46
- direction); eager on CPU, inside an outer compile, when disabled, or after
47
- a compilation failure (warned once)."""
48
 
49
  def __init__(self, fn):
50
  self.fn = fn
@@ -60,7 +61,7 @@ class _Fused:
60
  return self.compiled(*args)
61
  except torch.OutOfMemoryError:
62
  raise
63
- except Exception as exc: # no working Inductor / Triton toolchain here
64
  self.failed = True
65
  warnings.warn(f"torch.compile of {self.fn.__name__} failed ({exc!r}); running it eagerly",
66
  stacklevel=2)
@@ -71,9 +72,7 @@ _CONSTS: dict = {}
71
 
72
 
73
  def _cached(tensors: tuple, fn):
74
- """``fn()``, a value derived from the parameters ``tensors``. Without grad
75
- (inference, outside ``torch.compile``) it is cached until one of them changes:
76
- decoding would otherwise rebuild these fp32 weights at every token."""
77
  if torch.is_grad_enabled() or torch.compiler.is_compiling():
78
  return fn()
79
  key = tuple((t.data_ptr(), t._version) for t in tensors)
@@ -117,10 +116,7 @@ def _qk_rope_fn(q: Tensor, k: Tensor, qw: Tensor | None, kw: Tensor | None, cos:
117
 
118
  def _diff_xsa_fn(y: Tensor, lam: Tensor | None, v: Tensor, nh: int, nkv: int, xsa: bool,
119
  gate: Tensor | None = None) -> Tensor:
120
- """``y`` ``(B, T, nq, d)`` attention output, ``lam`` ``(B, T, nh)`` lambda
121
- logits (None: no DiffAttn), ``v`` ``(B, T, nkv, d)`` the tokens' own values,
122
- ``gate`` output-gate logits ``(B, T, nh * d)`` per channel or ``(B, T, nh)`` per head
123
- (None: no gate), applied after XSA."""
124
  B, T, _, hd = y.shape
125
  if lam is not None:
126
  y = y.view(B, T, nh, 2, hd).float()
@@ -144,8 +140,7 @@ def _unit(x: Tensor, tol: float) -> Tensor:
144
 
145
  def _iha_diff_xsa(y: Tensor, lam: Tensor | None, vp: Tensor, nh: int, nkv: int, xsa: bool,
146
  gate: Tensor | None = None) -> Tensor:
147
- """``_diff_xsa_fn`` for IHA's collapsed heads (training: ``_iha_diff_xsa_gate``): XSA removes the
148
- span of the token's own pseudo-values ``vp`` ``(B, T, P, nkv, d)``, by modified Gram-Schmidt."""
149
  B, T, _, hd = y.shape
150
  if lam is not None:
151
  y = y.view(B, T, nh, 2, hd).float()
@@ -168,8 +163,7 @@ def _iha_diff_xsa(y: Tensor, lam: Tensor | None, vp: Tensor, nh: int, nkv: int,
168
 
169
 
170
  def _iha_attention(q: Tensor, k: Tensor, v: Tensor, mask: Tensor, scaling: float) -> Tensor:
171
- """GQA-native attention with an explicit mask: ``q`` ``(B, nq, Tq, d)``, ``k`` / ``v`` ``(B, nkv, Tk, d)``,
172
- ``mask`` ``(B|1, 1, 1, Tq, Tk)`` bool -> ``(B, Tq, nq, d)``; fp32 scores and softmax."""
173
  B, nq, Tq, d = q.shape
174
  nkv = k.shape[1]
175
  s = torch.matmul(q.reshape(B, nkv, nq // nkv, Tq, d).float(), k.float().unsqueeze(2).transpose(-1, -2)) * scaling
@@ -178,10 +172,9 @@ def _iha_attention(q: Tensor, k: Tensor, v: Tensor, mask: Tensor, scaling: float
178
 
179
 
180
  def _iha_mix_init(alpha: Tensor, stride: int) -> None:
181
- """Training's init: pseudo-head ``p`` of head ``h`` starts as base head ``(h + p * stride) % H``
182
- plus a small Gaussian (``alpha`` ``(H, H, P)``; ``stride`` 2 for DiffAttn's query heads)."""
183
  H, _, P = alpha.shape
184
- if getattr(alpha, "_is_hf_initialized", False): # loaded from a checkpoint
185
  return
186
  with torch.no_grad():
187
  alpha.normal_(std=0.1 / H ** 0.5)
@@ -198,20 +191,15 @@ def _swiglu_fn(gu: Tensor) -> Tensor:
198
  def _ccgqa_fn(qk: Tensor, tail: Tensor, v: Tensor, before: Tensor | None, pos: Tensor, W: Tensor,
199
  b: Tensor, q_gain: Tensor, k_gain: Tensor, cos: Tensor | None, sin: Tensor | None, nq: int,
200
  nkv: int, eps: float, qk_mean: bool) -> tuple[Tensor, Tensor, Tensor, Tensor, Tensor | None]:
201
- """CCGQA ``q`` ``(B, T, nq, d)``, ``k`` / ``v`` ``(B, T, nkv, d)`` from the q|k latents
202
- ``qk`` ``(B, T, nxq+nkv, d)`` (``nxq < nq``: VHA's physical query heads) and the raw
203
- values, in fp32 like ``ccgqa_qk_reference``. ``tail`` holds the ``K-1`` latents
204
- before ``qk`` and ``before`` the previous token's value-shift half (None: no value
205
- shift); both come back updated. ``W`` / ``b`` / gains: ``_ccgqa_consts``. Tap ``m``
206
- reads token ``t-m`` only if ``pos[t] >= m``."""
207
  B, T, _, hd = qk.shape
208
  K, nxq = tail.shape[1] + 1, qk.shape[2] - nkv
209
  ext = torch.cat((tail, qk), 1) # (B, K-1+T, nxq+nkv, d)
210
 
211
- def virtual(x: Tensor) -> Tensor: # VHA: query head h reads physical head h % nxq
212
  return x if nxq == nq else torch.cat((x[:, :, :nxq].repeat(1, 1, nq // nxq, 1), x[:, :, nxq:]), 2)
213
 
214
- win = virtual(ext).unfold(1, K, 1) # (B, T, H, d, K): slot j = lag K-1-j
215
  lag = torch.arange(K - 1, -1, -1, device=qk.device)
216
  win = torch.where((pos[..., None] >= lag)[:, :, None, None, :], win.float(), 0.0)
217
  H = win.shape[2]
@@ -229,7 +217,7 @@ def _ccgqa_fn(qk: Tensor, tail: Tensor, v: Tensor, before: Tensor | None, pos: T
229
  if before is None:
230
  return q, k, v.contiguous(), tail, None
231
  flat = v.reshape(B, T, nkv * hd)
232
- half = nkv * hd // 2 # the W_V-bar half of the channels is the previous token's
233
  shifted = torch.where((pos > 0)[..., None], torch.cat((before, flat[:, :, half:]), 1)[:, :T], 0)
234
  v = torch.cat((flat[:, :, :half], shifted), -1).view(B, T, nkv, hd)
235
  return q, k, v, tail, flat[:, -1:, half:]
@@ -271,7 +259,7 @@ _ccgqa = _Fused(_ccgqa_fn)
271
  _MAX_HIDDEN = 8192 # whole row in registers
272
  _MAX_SOURCES = 32
273
  _SPAN_BWD = dict(R=64, num_warps=2)
274
- _TPP = 8 # tokens per program in the per-token backward
275
 
276
  if HAVE_TRITON:
277
 
@@ -280,8 +268,7 @@ if HAVE_TRITON:
280
  s_st, s_sk, x_st, o_st, st_st, eps,
281
  D: tl.constexpr, NB: tl.constexpr, NB_PAD: tl.constexpr, BLOCK: tl.constexpr,
282
  ADD: tl.constexpr, PART: tl.constexpr):
283
- """One token: online softmax over the NB sources + x, one read of each
284
- (``ADD``: plus their sum, the residual stream; ``PART`` off: x isn't routed)."""
285
  t = tl.program_id(0).to(tl.int64)
286
  offs = tl.arange(0, BLOCK)
287
  msk = offs < D
@@ -327,9 +314,7 @@ if HAVE_TRITON:
327
  D: tl.constexpr, NB: tl.constexpr, NB_PAD: tl.constexpr,
328
  HAS_RES: tl.constexpr, TPP: tl.constexpr, BLOCK: tl.constexpr,
329
  ADD: tl.constexpr):
330
- """TPP tokens: <g, v_i> for every entry (one read each), the full
331
- gradient of x (plus the residual-add path's, ``HAS_RES``), the scalars
332
- the deferred source gradient needs, and the x part of dw."""
333
  pid = tl.program_id(0)
334
  offs = tl.arange(0, BLOCK)
335
  msk = offs < D
@@ -375,8 +360,7 @@ if HAVE_TRITON:
375
  s_st, s_sk, ds_st, ds_sk, N, coef_c, coef_q,
376
  D: tl.constexpr, NB: tl.constexpr, NC: tl.constexpr, NC_PAD: tl.constexpr,
377
  R: tl.constexpr, CD: tl.constexpr):
378
- """dS for every source of one block from all NC mixes that read it (one
379
- read + one write per source), plus the S part of each mix's dw."""
380
  pid_t = tl.program_id(0)
381
  cols = tl.program_id(1) * CD + tl.arange(0, CD)
382
  mc = cols < D
@@ -428,9 +412,7 @@ def _ar_forward(x: Tensor, S: Tensor, w: Tensor, eps: float, additive: bool,
428
 
429
  def attn_res_reference(x: Tensor, sources: Tensor, norm_weight: Tensor, query: Tensor,
430
  eps: float, additive: bool = False, partial: bool = True) -> Tensor:
431
- """Eager fp32 mix; ``x`` ``(N, D)``, ``sources`` ``(N, k, D)``. ``additive``: Delta
432
- Block, the mix added to the stream ``x + sum(sources)``. ``partial=False``: ``x``
433
- takes no part in the softmax."""
434
  values = torch.cat((sources, x.unsqueeze(1)), dim=1).float()
435
  keys = values * torch.rsqrt(values.square().mean(-1, keepdim=True) + eps)
436
  logits = (keys * (norm_weight.float() * query.float())).sum(-1)
@@ -447,19 +429,18 @@ class _SpanState:
447
 
448
  def __init__(self, n_calls: int, additive: bool = False, partial: bool = True):
449
  self.n_calls = n_calls
450
- self.additive, self.partial = additive, partial # Delta Block; x is routed
451
- self.w: Tensor | None = None # (n_calls, D) fp32 score weights
452
  self.gbuf: Tensor | None = None # (n_calls, N, D) output grads, filled in backward
453
  self.coef: Tensor | None = None # (n_calls, 3, N, k) fp32 a / b / e
454
  self.dw_x: Tensor | None = None # (n_calls, D) fp32 x part of dw
455
- self.res: dict[int, Tensor] = {} # call -> residual-add gradient of its x
456
- self.mixed: set[int] = set() # calls whose mix backward has run
457
  self.done = 0
458
 
459
 
460
  class _SpanSources(torch.autograd.Function):
461
- """Identity on the sources ``S``; its backward (after every mix of the
462
- block) builds ``dS`` and the score-weight gradients in one pass."""
463
 
464
  @staticmethod
465
  def forward(ctx, S: Tensor, state: _SpanState, *weights: Tensor) -> Tensor:
@@ -499,9 +480,7 @@ class _SpanSources(torch.autograd.Function):
499
 
500
 
501
  class _ResidualAdd(torch.autograd.Function):
502
- """``x + y`` where ``x`` is also read by mix ``call``: the backward hands
503
- the gradient of ``x`` to that mix's backward kernel, which sums it into
504
- ``dx``, instead of autograd summing the two in a separate pass."""
505
 
506
  @staticmethod
507
  def forward(ctx, x: Tensor, y: Tensor, state: _SpanState, call: int) -> Tensor:
@@ -511,7 +490,7 @@ class _ResidualAdd(torch.autograd.Function):
511
  @staticmethod
512
  def backward(ctx, g: Tensor):
513
  st = ctx.state
514
- if ctx.call in st.mixed: # the mix already ran its backward: let autograd sum
515
  return g, g, None, None
516
  st.res[ctx.call] = g
517
  return None, g, None, None
@@ -558,14 +537,7 @@ class _DepthMix(torch.autograd.Function):
558
 
559
 
560
  class AttnResSpan:
561
- """The sources ``S`` ``(N, k, D)`` of one block and the mixes reading them.
562
-
563
- ``sites[c]`` is the ``(norm, query)`` pair of the ``c``-th mix that reads
564
- ``S``. With grad enabled every site must be called exactly once per
565
- forward. Paths: Triton with autograd (training), Triton forward only (no
566
- grad: inference / generation), eager fp32 reference (CPU, no Triton,
567
- ``fused_kernels=False``). ``additive``: Delta Block mixes; ``partial``: ``x`` is routed too.
568
- """
569
 
570
  def __init__(self, S: Tensor, sites: list[tuple[Tensor, Tensor]], eps: float, fused: bool,
571
  additive: bool = False, partial: bool = True):
@@ -606,8 +578,6 @@ class AttnResSpan:
606
  return x + y
607
 
608
 
609
- # fused LM head + CE
610
-
611
  def softcap_logits(logits: Tensor, cap: float | None) -> Tensor:
612
  """``cap * tanh(logits / cap)`` (``final_logit_softcapping``); identity for None / 0."""
613
  return cap * torch.tanh(logits / cap) if cap else logits
@@ -617,7 +587,7 @@ if HAVE_TRITON:
617
 
618
  @triton.jit
619
  def _tanh(x, HW: tl.constexpr):
620
- if HW: # one MUFU op (sm_75+); rel. error 2^-11, under the bf16 rounding of the logits
621
  return tl.inline_asm_elementwise("tanh.approx.f32 $0, $1;", "=r,r", [x],
622
  dtype=tl.float32, is_pure=True, pack=1)
623
  return 2.0 * tl.sigmoid(2.0 * x) - 1.0
@@ -626,9 +596,7 @@ if HAVE_TRITON:
626
  def _ce_fwd_bwd_kernel(logits_ptr, stride_row, labels_ptr, loss_ptr, scale_ptr, V, cap, inv_cap,
627
  IGNORE: tl.constexpr, SOFTCAP: tl.constexpr, HW_TANH: tl.constexpr,
628
  BLOCK: tl.constexpr):
629
- """NLL of one row, then its logits overwritten by ``(softmax - onehot) * scale``
630
- (with ``SOFTCAP``: of ``cap * tanh(logits / cap)``, times ``1 - tanh^2``).
631
- Base 2 throughout so ``log2(e)`` folds into the ``ex2`` argument."""
632
  LOG2E: tl.constexpr = 1.4426950408889634
633
  LN2: tl.constexpr = 0.6931471805599453
634
  row = tl.program_id(0).to(tl.int64)
@@ -637,8 +605,7 @@ if HAVE_TRITON:
637
  valid = label != IGNORE
638
  cap2 = cap * LOG2E
639
  safe_label = tl.where(valid, label, 0)
640
- # the target stays out of the sum-exp so 1 - p_t is s_nt / s, not
641
- # 1 - round(p_t) (Softmax Collapse, Barron et al. 2026)
642
  m = -float("inf")
643
  s_nt = 0.0
644
  for off in range(0, V, BLOCK):
@@ -659,12 +626,12 @@ if HAVE_TRITON:
659
  else:
660
  target = target * LOG2E
661
  s = tl.exp2(target - m) + s_nt
662
- u = 1.0 + s_nt # target is the max: loss = log1p(s_nt) (Goldberg's log1p)
663
  log1p = tl.where(u == 1.0, s_nt, tl.log(u) * (s_nt / (u - 1.0)))
664
  loss = tl.where(target >= m, log1p, (m + tl.log2(s) - target) * LN2)
665
  tl.store(loss_ptr + row, tl.where(valid, loss, 0.0))
666
  r = tl.where(valid, tl.load(scale_ptr), 0.0) / s
667
- g_t = -s_nt * r # -(1 - p_t) * scale: the row sums to zero
668
  for off in range(0, V, BLOCK):
669
  cols = off + tl.arange(0, BLOCK)
670
  msk = cols < V
@@ -689,9 +656,7 @@ def _accumulate_dw(dw: Tensor, dlogits: Tensor, h: Tensor) -> None:
689
 
690
 
691
  class _LinearCrossEntropy(torch.autograd.Function):
692
- """Chunked ``CE(h @ W.T, y)`` (sum over valid targets times ``scale``):
693
- per chunk one logits GEMM, one Triton pass that writes ``dlogits`` over
694
- the logits, and the two gradient GEMMs, so the backward only rescales."""
695
 
696
  @staticmethod
697
  def forward(ctx, hidden: Tensor, weight: Tensor, labels: Tensor, scale: Tensor, chunk: int,
@@ -748,14 +713,8 @@ def fused_linear_cross_entropy(hidden: Tensor, weight: Tensor, labels: Tensor,
748
  chunk, softcap)
749
 
750
 
751
- # CCGQA
752
-
753
-
754
  def compose_conv(w1: Tensor, b1: Tensor, w2: Tensor, b2: Tensor) -> tuple[Tensor, Tensor]:
755
- """Depthwise taps ``w1`` ``(H*hd, ks)`` + ``b1`` and per-head taps ``w2``
756
- ``(H, kc, hd_in, hd_out)`` + ``b2`` ``(H, hd)`` (taps in lag order, 0 = current
757
- token) -> one masked causal conv: fp32 ``W`` ``(H, ks+kc-1, hd, hd)``, ``b``
758
- ``(H, hd)``. Exact inside a document (``lm/kernels/ccgqa.py``)."""
759
  H, kc, hd, _ = w2.shape
760
  ks = w1.shape[1]
761
  a, w2f = w1.float().view(H, hd, ks, 1), w2.float()
@@ -766,10 +725,7 @@ def compose_conv(w1: Tensor, b1: Tensor, w2: Tensor, b2: Tensor) -> tuple[Tensor
766
 
767
 
768
  class LuauCoderCache(DynamicCache):
769
- """``DynamicCache`` plus CCGQA's per-layer decode state: the last ``K-1``
770
- pre-conv q|k latents and the previous token's raw value-shift half.
771
- Rewinding (``crop``: assisted / speculative decoding) is not supported
772
- once that state exists."""
773
 
774
  def __init__(self, *args, **kwargs):
775
  super().__init__(*args, **kwargs)
@@ -816,18 +772,7 @@ class _InPlaceStates(dict):
816
 
817
 
818
  class LuauCoderStaticCache:
819
- """Preallocated cache for CUDA-graph decoding (``generate.py``): K/V buffers
820
- ``(B, nkv, L, d)`` per layer, CCGQA's conv / value-shift state, and which
821
- buffer slots hold real keys. Every tensor keeps its address and the write
822
- slots live on the device, so a captured decode step replays as is.
823
-
824
- Before each forward the caller sets ``idx`` (the slots of this call's
825
- tokens, ``(T,)``) and marks them in ``key_valid``, and passes
826
- ``position_ids`` (each token's position in its document; a left-padded
827
- row starts at its first real token). Query ``t`` attends to the valid
828
- slots ``<= idx[t]``. Only ``generate.py``'s loop drives it: not for
829
- ``generate()``.
830
- """
831
 
832
  is_static = True
833
 
@@ -871,12 +816,8 @@ def _conv_states(cache) -> dict:
871
  return states
872
 
873
 
874
- # RoPE
875
-
876
-
877
  def _rope_settings(config: LuauCoderConfig) -> tuple[float, dict | None]:
878
- """``(theta, yarn params or None)`` from a v4 (``rope_theta`` + ``rope_scaling``)
879
- or v5 (``rope_parameters``) config."""
880
  params = getattr(config, "rope_parameters", None) or getattr(config, "rope_scaling", None) or {}
881
  theta = float(params.get("rope_theta", getattr(config, "rope_theta", 500000.0)))
882
  rtype = params.get("rope_type", params.get("type"))
@@ -886,8 +827,7 @@ def _rope_settings(config: LuauCoderConfig) -> tuple[float, dict | None]:
886
 
887
 
888
  def rope_inv_freq(head_dim: int, theta: float, yarn: dict | None) -> tuple[Tensor, float]:
889
- """Inverse frequencies ``(head_dim/2,)`` fp64 and the cos/sin scale; YaRN
890
- "NTK-by-parts" as in the training stack and transformers' ``rope_type: yarn``."""
891
  dim = head_dim
892
  pos_freqs = theta ** (torch.arange(0, dim, 2, dtype=torch.float64, device="cpu") / dim)
893
  extrap = 1.0 / pos_freqs
@@ -915,16 +855,13 @@ def rope_inv_freq(head_dim: int, theta: float, yarn: dict | None) -> tuple[Tenso
915
 
916
 
917
  def lerope_inv_freq(head_dim: int, theta: float, alpha: list[float], yarn: dict | None) -> tuple[Tensor, float]:
918
- """LeRoPE frequencies fp64 and the cos/sin scale, as the training stack's ``lerope_freqs``: the
919
- RoPE (+ YaRN) frequencies times ``exp(alpha)`` (transformers' Mistral Large 4 convention)."""
920
  inv, mscale = rope_inv_freq(head_dim, theta, yarn)
921
  return inv * torch.tensor(alpha, dtype=torch.float64, device="cpu").exp(), mscale
922
 
923
 
924
  class LuauCoderRotaryEmbedding(nn.Module):
925
- """fp32 cos/sin tables (computed in fp64 like training), gathered by
926
- ``position_ids``. The tables are plain attributes, not buffers, so a
927
- ``model.to(bfloat16)`` never rounds them."""
928
 
929
  def __init__(self, config: LuauCoderConfig):
930
  super().__init__()
@@ -946,13 +883,8 @@ class LuauCoderRotaryEmbedding(nn.Module):
946
  return self._cos[position_ids], self._sin[position_ids]
947
 
948
 
949
- # modules
950
-
951
-
952
  class LuauCoderRMSNorm(nn.Module):
953
- """``learnable=False`` (q/k norm): the gain is a persistent buffer, fixed at 1 as in training
954
- (same key and shape, so checkpoints and the fused kernels are unchanged; not a parameter, so it
955
- is neither counted nor trained)."""
956
 
957
  def __init__(self, dim: int, eps: float, fuse: bool = True, learnable: bool = True):
958
  super().__init__()
@@ -993,10 +925,7 @@ def eager_attention_forward(module: nn.Module, query: Tensor, key: Tensor, value
993
 
994
 
995
  def _decode_attention(q: Tensor, k: Tensor, v: Tensor, mask: Tensor, scaling: float) -> Tensor:
996
- """One query token against a static cache, GQA-native: ``q`` ``(B, nq, 1, d)``,
997
- ``k`` / ``v`` ``(B, nkv, L, d)``, ``mask`` ``(B, 1, 1, L)`` bool -> ``(B, 1, nq, d)``.
998
- Each KV head is read once (the backends' masked path repeats K/V per query head,
999
- which dominates a decode step at a long cache); fp32 scores and softmax."""
1000
  B, nq, _, d = q.shape
1001
  nkv = k.shape[1]
1002
  s = torch.matmul(q.reshape(B, nkv, nq // nkv, d).float(), k.float().transpose(-1, -2)) * scaling
@@ -1005,15 +934,13 @@ def _decode_attention(q: Tensor, k: Tensor, v: Tensor, mask: Tensor, scaling: fl
1005
 
1006
 
1007
  class LuauCoderAttention(nn.Module):
1008
- """DiffAttn V2 + XSA. Query heads ``2i`` / ``2i+1`` are adjacent, so both
1009
- fall in the same GQA group and share K and V."""
1010
 
1011
  def __init__(self, config: LuauCoderConfig, layer_idx: int):
1012
  super().__init__()
1013
  self.config, self.layer_idx = config, layer_idx
1014
  self.nh, self.nkv, self.hd = config.num_attention_heads, config.num_key_value_heads, config.head_dim
1015
  self.nq = config.num_query_heads
1016
- # VHA: qkv makes nxq physical q heads; query head h reads physical h % nxq
1017
  self.nxq = getattr(config, "num_q_latent_heads", self.nq)
1018
  self.diff, self.xsa = config.diff_v2, config.xsa
1019
  self.num_key_value_groups = self.nq // self.nkv
@@ -1024,20 +951,15 @@ class LuauCoderAttention(nn.Module):
1024
  self.rope = layer_idx not in (getattr(config, "nope_layers", None) or ())
1025
  self.iha = config.iha_layer(layer_idx)
1026
  D = config.hidden_size
1027
- # output gate: per channel, its W_g rows follow q|k|v in the fused qkv weight;
1028
- # per head (LFM3), a separate dim -> nh gate_proj
1029
  self.g_rows = self.nh * self.hd if config.output_gate == "elementwise" else 0
1030
  self.qkv = nn.Linear(D, (self.nxq + 2 * self.nkv) * self.hd + self.g_rows, bias=False)
1031
  self.lambda_proj = nn.Linear(D, self.nh, bias=False) if self.diff else None
1032
  self.gate_proj = nn.Linear(D, self.nh, bias=False) if config.output_gate == "headwise" else None
1033
  self.o_proj = nn.Linear(self.nh * self.hd, D, bias=False)
1034
- # gains fixed at 1 like training (lm/model.py: RMSNorm(learnable=False))
1035
  self.q_norm = LuauCoderRMSNorm(self.hd, self.eps, learnable=False) if config.qk_norm else None
1036
  self.k_norm = LuauCoderRMSNorm(self.hd, self.eps, learnable=False) if config.qk_norm else None
1037
  self.ccgqa = getattr(config, "ccgqa", False)
1038
  if self.ccgqa:
1039
- # q|k latents (q heads, then k heads): depthwise causal conv taps
1040
- # conv_seq[c, i] and per-head dense taps conv_head[h, j, c_in, c_out], lag order
1041
  H, ks, kc = self.nq + self.nkv, config.ccgqa_seq_kernel, config.ccgqa_head_kernel
1042
  self.conv_taps = ks + kc - 1
1043
  self.qk_mean, self.value_shift = config.ccgqa_qk_mean, config.ccgqa_value_shift
@@ -1045,7 +967,7 @@ class LuauCoderAttention(nn.Module):
1045
  self.conv_seq_bias = nn.Parameter(torch.zeros(H * self.hd))
1046
  self.conv_head = nn.Parameter(torch.empty(H, kc, self.hd, self.hd))
1047
  self.conv_head_bias = nn.Parameter(torch.zeros(H, self.hd))
1048
- self.k_temp = nn.Parameter(torch.zeros(self.nkv)) # key scale exp(t), per KV head
1049
  if self.iha:
1050
  P = self.iha_p = config.iha_pseudo_heads
1051
  self.iha_q = nn.Parameter(torch.empty(self.nq, self.nq, P))
@@ -1063,7 +985,7 @@ class LuauCoderAttention(nn.Module):
1063
  conv_pos = kwargs.pop("conv_pos", None)
1064
  iha_ctx = kwargs.pop("iha_ctx", None)
1065
  rope = self.rope
1066
- if not self.rope or self.iha: # IHA layers rotate later, at the virtual positions
1067
  cos = sin = None
1068
  if self.ccgqa:
1069
  qk, v = qkv.split([self.nxq + self.nkv, self.nkv], dim=2)
@@ -1078,7 +1000,6 @@ class LuauCoderAttention(nn.Module):
1078
  y, vp = self._iha_attend(q, k, v, iha_ctx, rope, past_key_values)
1079
  probs = None
1080
  else:
1081
- # (B, H, T, d) views: flash transposes them straight back, sdpa takes the strides
1082
  q, k_all, v_all = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)
1083
  if past_key_values is not None:
1084
  k_all, v_all = past_key_values.update(k_all, v_all, self.layer_idx)
@@ -1089,7 +1010,7 @@ class LuauCoderAttention(nn.Module):
1089
  fn = eager_attention_forward if impl in (None, "eager") else ALL_ATTENTION_FUNCTIONS[impl]
1090
  y, probs = fn(self, q, k_all, v_all, attention_mask, dropout=0.0, scaling=self.scaling,
1091
  position_ids=position_ids, **kwargs) # (B, T, nq, d)
1092
- if self.diff and self.gate_proj is not None: # one GEMM for both nh-wide projections
1093
  lam, gate = F.linear(x, torch.cat((self.lambda_proj.weight, self.gate_proj.weight))).split(self.nh, -1)
1094
  else:
1095
  lam = self.lambda_proj(x) if self.diff else None
@@ -1104,11 +1025,7 @@ class LuauCoderAttention(nn.Module):
1104
 
1105
  def _iha_attend(self, q: Tensor, k: Tensor, v: Tensor, ctx: dict, rope: bool,
1106
  cache: Cache | None) -> tuple[Tensor, Tensor]:
1107
- """Training's ``Attention._iha_attend`` on latent, unrotated q / k / v: pseudo-heads, QK-norm,
1108
- temperature, RoPE at virtual positions (``ctx``: ``LuauCoderModel._iha_context``), then
1109
- attention over the interleaved tokens, with the pseudo-keys in the KV cache as extra heads
1110
- ``(B, nkv * P, T, d)``. Returns the collapsed ``(B, T, nq, d)`` and the pseudo-values
1111
- ``(B, T, P, nkv, d)``."""
1112
  B, T, _, hd = q.shape
1113
  P, nq, nkv = self.iha_p, self.nq, self.nkv
1114
 
@@ -1129,7 +1046,7 @@ class LuauCoderAttention(nn.Module):
1129
  if cache is not None:
1130
  keys, vals = cache.update(keys, vals, self.layer_idx)
1131
 
1132
- def interleave(t: Tensor) -> Tensor: # (B, nkv * P, T', d) -> (B, nkv, Tk * P, d), the tokens in reach
1133
  t = t[:, :, ctx["first"]:]
1134
  return t.reshape(B, nkv, P, -1, hd).transpose(2, 3).reshape(B, nkv, -1, hd)
1135
 
@@ -1142,8 +1059,7 @@ class LuauCoderAttention(nn.Module):
1142
 
1143
  def _ccgqa_qkv(self, qk: Tensor, v: Tensor, cos: Tensor | None, sin: Tensor | None,
1144
  pos: Tensor | None, cache: Cache | None) -> tuple[Tensor, Tensor, Tensor]:
1145
- """CCGQA ``q`` / ``k`` / ``v`` (``_ccgqa_fn``), carrying the conv and value-shift
1146
- state in ``cache``. ``pos`` ``(B, T)``: each token's position in its document."""
1147
  B, T = qk.shape[:2]
1148
  if pos is None:
1149
  pos = torch.arange(T, device=qk.device).expand(B, T)
@@ -1161,9 +1077,7 @@ class LuauCoderAttention(nn.Module):
1161
  return q, k, v
1162
 
1163
  def _ccgqa_consts(self) -> tuple[Tensor, Tensor, Tensor, Tensor]:
1164
- """fp32 composed conv ``W`` as ``(H, d*K, d)`` bmm operands (row ``c*K + j`` =
1165
- input channel ``c`` at window slot ``j``, lag ``K-1-j``), its bias, the q gain
1166
- and the k gain times ``exp(temp)``; cached without grad (``_cached``)."""
1167
  params = (self.conv_seq, self.conv_seq_bias, self.conv_head, self.conv_head_bias,
1168
  self.q_norm.weight, self.k_norm.weight, self.k_temp)
1169
 
@@ -1171,7 +1085,6 @@ class LuauCoderAttention(nn.Module):
1171
  W, b = compose_conv(*params[:4])
1172
  H, K, hd, _ = W.shape
1173
  W = W.flip(1).permute(0, 2, 1, 3).reshape(H, hd * K, hd)
1174
- # IHA applies the temperature after its mix and norm
1175
  kg = params[5].float() if self.iha else params[5].float() * params[6].float().exp()[:, None]
1176
  return W, b, params[4].float(), kg
1177
 
@@ -1201,8 +1114,6 @@ class LuauCoderDecoderLayer(GradientCheckpointingLayer):
1201
  self.ffn = LuauCoderMLP(config)
1202
  self.attn_res = config.attn_res
1203
  if self.attn_res:
1204
- # one pseudo-query and key-norm gain per sublayer input; layer 0's
1205
- # attention reads the embedding alone, so it has nothing to mix
1206
  self.block_start = layer_idx % config.attn_res_block_size == 0
1207
  self.attn_res_norm = nn.Parameter(torch.ones(D)) if layer_idx else None
1208
  self.attn_res_query = nn.Parameter(torch.zeros(D)) if layer_idx else None
@@ -1223,10 +1134,7 @@ class LuauCoderDecoderLayer(GradientCheckpointingLayer):
1223
 
1224
  def _forward_attn_res(self, partial: Tensor, attn_span: AttnResSpan | None, mlp_span: AttnResSpan,
1225
  cos, sin, attention_mask, position_ids, past_key_values, **kwargs):
1226
- """``partial`` ``(B, T, D)``: the running sum of the current block's
1227
- sublayer outputs (at a block start: the finished block's, which
1228
- ``mlp_span`` already holds as its newest source). Returns the new
1229
- partial and this layer's attention input."""
1230
  B, T, D = partial.shape
1231
  N = B * T
1232
  flat = partial.reshape(N, D)
@@ -1234,17 +1142,12 @@ class LuauCoderDecoderLayer(GradientCheckpointingLayer):
1234
  a, _ = self.attn(self.attn_norm(h.view(B, T, D)), cos, sin, attention_mask, position_ids,
1235
  past_key_values, **kwargs)
1236
  a = a.reshape(N, D).to(flat.dtype)
1237
- # each partial is read by exactly one mix and one residual add: the add's
1238
- # gradient is summed inside that mix's backward kernel
1239
  flat = a if self.block_start else attn_span.residual(flat, a, self.attn_call)
1240
  m = mlp_span.mix(flat, self.mlp_call)
1241
  f = self.ffn(self.ffn_norm(m.view(B, T, D))).reshape(N, D).to(flat.dtype)
1242
  return mlp_span.residual(flat, f, self.mlp_call).view(B, T, D), h.view(B, T, D)
1243
 
1244
 
1245
- # models
1246
-
1247
-
1248
  class LuauCoderPreTrainedModel(PreTrainedModel):
1249
  config_class = LuauCoderConfig
1250
  config: LuauCoderConfig
@@ -1262,9 +1165,7 @@ class LuauCoderPreTrainedModel(PreTrainedModel):
1262
 
1263
  @torch.no_grad()
1264
  def _init_weights(self, module: nn.Module) -> None:
1265
- """The training stack's init: N(0, std) inputs, N(0, std / sqrt(2L))
1266
- residual-branch outputs, lambda logits at half scale, unit norms,
1267
- zero AttnRes pseudo-queries (uniform depth mix)."""
1268
  cfg = self.config
1269
  std = cfg.initializer_range
1270
  out_std = std / math.sqrt(2 * cfg.num_hidden_layers)
@@ -1275,7 +1176,7 @@ class LuauCoderPreTrainedModel(PreTrainedModel):
1275
  _init.normal_(module.lambda_proj.weight, mean=0.0, std=0.5 * std)
1276
  if module.gate_proj is not None:
1277
  _init.normal_(module.gate_proj.weight, mean=0.0, std=std)
1278
- if module.ccgqa: # ZAYA init: conv weights N(0, std), zero biases, unit key scale
1279
  _init.normal_(module.conv_seq, mean=0.0, std=std)
1280
  _init.normal_(module.conv_head, mean=0.0, std=std)
1281
  for p in (module.conv_seq_bias, module.conv_head_bias, module.k_temp):
@@ -1295,6 +1196,9 @@ class LuauCoderPreTrainedModel(PreTrainedModel):
1295
  elif isinstance(module, LuauCoderForCausalLM):
1296
  if not cfg.tie_word_embeddings:
1297
  _init.normal_(module.lm_head.weight, mean=0.0, std=std)
 
 
 
1298
  for name, p in module.named_parameters(recurse=False):
1299
  if name.endswith("_res_norm"):
1300
  _init.ones_(p)
@@ -1303,13 +1207,10 @@ class LuauCoderPreTrainedModel(PreTrainedModel):
1303
 
1304
  @classmethod
1305
  def _supports_default_dynamic_cache(cls) -> bool:
1306
- # generate() then leaves the cache to forward(), which makes a LuauCoderCache
1307
- # (so CCGQA's conv state follows beam reordering / batch expansion)
1308
  return False
1309
 
1310
  def gradient_checkpointing_enable(self, gradient_checkpointing_kwargs=None, **kwargs):
1311
- # AttnRes sources reach the layers as keyword arguments: reentrant
1312
- # checkpointing would silently drop their gradients
1313
  gc_kwargs = dict(gradient_checkpointing_kwargs or {})
1314
  gc_kwargs["use_reentrant"] = False
1315
  return super().gradient_checkpointing_enable(gradient_checkpointing_kwargs=gc_kwargs, **kwargs)
@@ -1344,7 +1245,7 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1344
  self.norm = LuauCoderRMSNorm(config.hidden_size, config.rms_norm_eps, config.compile_elementwise)
1345
  self.rotary_emb = LuauCoderRotaryEmbedding(config)
1346
  if config.attn_res:
1347
- if not config.attn_res_delta_block: # final mix over every block before the output norm
1348
  self.out_res_norm = nn.Parameter(torch.ones(config.hidden_size))
1349
  self.out_res_query = nn.Parameter(torch.zeros(config.hidden_size))
1350
  self._plan_attn_res()
@@ -1352,15 +1253,7 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1352
  self.post_init()
1353
 
1354
  def _plan_attn_res(self) -> None:
1355
- """Which block's sources each mix reads, and its call index there.
1356
-
1357
- Block ``m`` (layers ``mS .. mS+S-1``) is read by its layers' MLP mixes
1358
- (call ``2j``), their attention mixes (``2j-1``, ``j >= 1``) and the
1359
- next block's first attention mix, which runs before that block is
1360
- pushed (``2S-1``); the last block's final slot is the output mix, which
1361
- Delta Block doesn't have. Sites are stored as ``(layer, kind)`` and resolved to parameters at
1362
- every forward (loading can replace the parameter objects).
1363
- """
1364
  S, L = self.config.attn_res_block_size, self.config.num_hidden_layers
1365
  n_spans = (L + S - 1) // S
1366
  self._span_sites: list[list] = [[None] * (2 * min(S, L - m * S)) for m in range(n_spans)]
@@ -1394,10 +1287,7 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1394
 
1395
  def _iha_context(self, position_ids: Tensor, pos: Tensor, attention_mask: Tensor | None,
1396
  past_len: int) -> dict:
1397
- """What every IHA layer of this forward shares: cos / sin ``(B|1, T * P, d/2)`` at virtual positions
1398
- ``position * P + p``, the first real token still in reach and the mask ``(B|1, 1, 1, T * P, Tk * P)``
1399
- over row indices: causal in virtual order, the window, padding and packed documents (``pos``
1400
- restarting). A row with no valid key (left padding) attends to itself: its softmax stays finite."""
1401
  cfg = self.config
1402
  P, W = cfg.iha_pseudo_heads, cfg.iha_window
1403
  if attention_mask is not None and attention_mask.dim() != 2:
@@ -1436,8 +1326,8 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1436
  kwargs.pop("output_attentions", None)
1437
  output_hidden_states = (output_hidden_states if output_hidden_states is not None
1438
  else self.config.output_hidden_states)
1439
- if use_cache is None: # no cache while training: it only costs memory and disables
1440
- use_cache = self.config.use_cache and not self.training # packed-sequence masks
1441
  if self.gradient_checkpointing and self.training:
1442
  use_cache = False
1443
  if inputs_embeds is None:
@@ -1448,7 +1338,6 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1448
  past_key_values = None
1449
  B, T, D = inputs_embeds.shape
1450
  if getattr(past_key_values, "is_static", False):
1451
- # no host syncs (CUDA-graph capture): the cache holds the slots and valid keys
1452
  if position_ids is None or attention_mask is not None:
1453
  raise ValueError("a LuauCoderStaticCache takes position_ids and no attention_mask")
1454
  if self.config.iha:
@@ -1468,8 +1357,6 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1468
  layer_kw = dict(cos=cos, sin=sin, attention_mask=mask, position_ids=position_ids,
1469
  past_key_values=past_key_values, **kwargs)
1470
  if self.config.ccgqa or self.config.iha:
1471
- # document positions for the conv mask: position_ids restart per packed document,
1472
- # and a 2-D padding mask starts each row at its first real token
1473
  pos = position_ids.long().expand(B, T)
1474
  if attention_mask is not None and attention_mask.dim() == 2:
1475
  pos = torch.minimum(pos, (attention_mask.long().cumsum(-1) - 1)[:, -T:])
@@ -1480,7 +1367,7 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1480
 
1481
  if self.config.attn_res:
1482
  x = inputs_embeds
1483
- if torch.is_autocast_enabled(x.device.type): # bf16 depth stream, like the sublayers
1484
  x = x.to(torch.get_autocast_dtype(x.device.type))
1485
  partial = x
1486
  sources = x.reshape(B * T, 1, D)[:, :0]
@@ -1489,7 +1376,7 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1489
  delta = self.config.attn_res_delta_block
1490
  route_x = self.config.attn_res_partial_source or not delta
1491
  for i, blk in enumerate(self.blocks):
1492
- if blk.block_start: # the finished block (or the embedding) becomes a source
1493
  sources = torch.cat((sources, partial.reshape(B * T, 1, D)), dim=1)
1494
  sites = [self._site(*s) for s in self._span_sites[len(spans)]]
1495
  spans.append(AttnResSpan(sources, sites, eps, fused, delta, route_x))
@@ -1512,9 +1399,7 @@ class LuauCoderModel(LuauCoderPreTrainedModel):
1512
  hidden_states=tuple(hidden) if output_hidden_states else None)
1513
 
1514
 
1515
- # fused generate()
1516
-
1517
- # generation-config fields the fused decoder doesn't implement, with their neutral values
1518
  _FUSED_NEUTRAL = {
1519
  "num_beams": (None, 1), "num_beam_groups": (None, 1), "diversity_penalty": (None, 0.0), "penalty_alpha": (None,),
1520
  "dola_layers": (None,), "constraints": (None,), "force_words_ids": (None,), "prompt_lookup_num_tokens": (None,),
@@ -1528,8 +1413,6 @@ _FUSED_NEUTRAL = {
1528
  "output_hidden_states": (None, False), "return_dict_in_generate": (None, False), "token_healing": (None, False),
1529
  "max_time": (None,), "use_mtp": (None, False),
1530
  }
1531
- # generate() keyword arguments (model kwargs) the fused path accepts; anything else falls back.
1532
- # (``use_cache`` / ``generation_mode`` are accepted and ignored: the fused path always caches.)
1533
  _FUSED_MODEL_KWARGS = {"input_ids", "attention_mask", "use_cache", "tokenizer", "generation_mode"}
1534
 
1535
 
@@ -1538,13 +1421,13 @@ def _fused_fallback_reason(model, inputs, gc, model_kwargs, logits_processor, st
1538
  """None if ``generate()`` can run on the fused decoder with these settings, else why not."""
1539
  if not (HAVE_TRITON and model.device.type == "cuda") or model.training:
1540
  return "needs CUDA + triton, eval mode"
1541
- if model.dtype != torch.bfloat16: # its GEMMs round like bf16 ones: an fp32 model keeps its own precision
1542
  return f"a {model.dtype} model (the fused decoder runs bf16 models)"
1543
  used = [k for k, v in extra.items() if v is not None and v is not False]
1544
  if used:
1545
  return f"unsupported generate() arguments {used}"
1546
- if logits_processor or stopping_criteria:
1547
- return "custom logits processors / stopping criteria"
1548
  if set(model_kwargs) - _FUSED_MODEL_KWARGS:
1549
  return f"model kwargs {sorted(set(model_kwargs) - _FUSED_MODEL_KWARGS)}"
1550
  for name, ok in _FUSED_NEUTRAL.items():
@@ -1553,8 +1436,8 @@ def _fused_fallback_reason(model, inputs, gc, model_kwargs, logits_processor, st
1553
  if not isinstance(inputs, Tensor) or inputs.dim() != 2 or inputs.is_floating_point():
1554
  return "needs input_ids (B, T)"
1555
  eos = gc.eos_token_id
1556
- if isinstance(eos, (list, tuple)) and len(set(eos)) > 1:
1557
- return "several EOS tokens"
1558
  if gc.stop_strings and model_kwargs.get("tokenizer") is None:
1559
  return "stop_strings without a tokenizer"
1560
  mask = model_kwargs.get("attention_mask")
@@ -1565,17 +1448,8 @@ def _fused_fallback_reason(model, inputs, gc, model_kwargs, logits_processor, st
1565
 
1566
 
1567
  class _FusedGenerationMixin:
1568
- """``generate()`` through the fused decoding engine (``luau_infer``: ~9 Triton kernels per
1569
- layer, each step one CUDA graph, prompt-lookup speculation with the same output) whenever
1570
- every setting is one it reproduces exactly: greedy or sampling (temperature, top-k, top-p,
1571
- min-p), repetition penalty, ``max_new_tokens`` / ``max_length``, EOS / pad, stop strings,
1572
- ``num_return_sequences``, left-padded batches and a streamer. Anything else (beam search,
1573
- assisted / contrastive decoding, custom logits processors or stopping criteria, scores /
1574
- dict outputs, ...) is transformers' own ``generate()``.
1575
-
1576
- It runs bf16 models, on the weights as loaded (no quantisation). Extra keyword
1577
- arguments: ``fused_generate=False`` always uses transformers' path; ``fused_spec`` the
1578
- speculative draft length (default 6, used while it pays; 0 = off)."""
1579
 
1580
  def generate(self, inputs=None, generation_config=None, logits_processor=None, stopping_criteria=None,
1581
  prefix_allowed_tokens_fn=None, synced_gpus=None, assistant_model=None, streamer=None,
@@ -1598,23 +1472,26 @@ class _FusedGenerationMixin:
1598
  """The fused decoder's output, or None when these settings need transformers' loop."""
1599
  try:
1600
  gc, model_kwargs = self._prepare_generation_config(copy.deepcopy(generation_config), **kwargs)
1601
- except Exception: # another transformers version's signature: its own generate() decides
1602
  return None
1603
  if inputs is None:
1604
  inputs = model_kwargs.get("input_ids")
1605
  why = _fused_fallback_reason(self, inputs, gc, model_kwargs, logits_processor, stopping_criteria, hf)
1606
- self._fused_last_reason = why # why the last generate() fell back (None: it ran fused)
1607
  if why is not None:
1608
  return None
1609
  eos = gc.eos_token_id
 
 
1610
  if isinstance(eos, (list, tuple)):
1611
- eos = eos[0] if eos else None
1612
- pad = gc.pad_token_id if gc.pad_token_id is not None else eos
 
1613
  if isinstance(pad, (list, tuple)):
1614
  pad = pad[0]
1615
  mask = model_kwargs.get("attention_mask")
1616
- if mask is None: # as transformers infers it: pad tokens (when pad != eos) are padding
1617
- mask = (inputs != pad).long() if pad is not None and pad != eos and bool((inputs == pad).any()) \
1618
  else torch.ones_like(inputs)
1619
  n_new = gc.max_new_tokens if gc.max_new_tokens is not None else (gc.max_length or 20) - inputs.shape[1]
1620
  stop = gc.stop_strings or []
@@ -1626,23 +1503,39 @@ class _FusedGenerationMixin:
1626
  sample=bool(gc.do_sample), temperature=gc.temperature or 1.0, top_k=gc.top_k or 0,
1627
  top_p=1.0 if gc.top_p is None else gc.top_p, min_p=gc.min_p or 0.0,
1628
  repetition_penalty=gc.repetition_penalty or 1.0, num_samples=gc.num_return_sequences or 1,
1629
- eos=eos, pad=pad, stop=stop, tokenizer=model_kwargs.get("tokenizer"), streamer=streamer)
 
1630
 
1631
  def _fused_decoder(self, spec: int):
1632
  """The decoder of these weights, kept between calls (rebuilt when a parameter changed)."""
1633
  from .luau_infer import FusedDecoder
1634
  try:
1635
  key = (spec,) + tuple((p.data_ptr(), p._version) for p in self.parameters())
1636
- except RuntimeError: # inference tensors have no version counter
1637
  key = (spec,) + tuple(p.data_ptr() for p in self.parameters())
1638
  hit = self.__dict__.get("_fused")
1639
  if hit is None or hit[0] != key:
1640
- self.__dict__["_fused"] = None # free the old engine's caches first
1641
  with torch.inference_mode():
1642
  self.__dict__["_fused"] = (key, FusedDecoder(self, None, spec))
1643
  return self.__dict__["_fused"][1]
1644
 
1645
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1646
  class LuauCoderForCausalLM(LuauCoderPreTrainedModel, _FusedGenerationMixin, GenerationMixin):
1647
  _tied_weights_keys = {"lm_head.weight": "model.embed.weight"} if _V5 else ["lm_head.weight"]
1648
 
@@ -1650,7 +1543,11 @@ class LuauCoderForCausalLM(LuauCoderPreTrainedModel, _FusedGenerationMixin, Gene
1650
  super().__init__(config)
1651
  self.model = LuauCoderModel(config)
1652
  self.vocab_size = config.vocab_size
1653
- self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
 
 
 
 
1654
  self.post_init()
1655
 
1656
  def get_input_embeddings(self) -> nn.Embedding:
@@ -1671,15 +1568,18 @@ class LuauCoderForCausalLM(LuauCoderPreTrainedModel, _FusedGenerationMixin, Gene
1671
  use_cache: bool | None = None, logits_to_keep: int | Tensor = 0,
1672
  output_hidden_states: bool | None = None, return_dict: bool | None = None,
1673
  **kwargs) -> CausalLMOutputWithPast:
1674
- """``labels`` follow the HF convention (unshifted, ``-100`` ignored);
1675
- ``shift_labels`` (already shifted) takes precedence. With labels and
1676
- grad enabled the fused LM-head + CE kernel runs and ``logits`` is None."""
1677
  num_items_in_batch = kwargs.pop("num_items_in_batch", None)
1678
  shift_labels = kwargs.pop("shift_labels", None)
1679
  out = self.model(input_ids=input_ids, attention_mask=attention_mask, position_ids=position_ids,
1680
  past_key_values=past_key_values, inputs_embeds=inputs_embeds,
1681
  use_cache=use_cache, output_hidden_states=output_hidden_states, **kwargs)
1682
  h = out.last_hidden_state
 
 
 
 
 
1683
  if labels is not None and shift_labels is None:
1684
  shift_labels = F.pad(labels[:, 1:], (0, 1), value=IGNORE_INDEX)
1685
  loss = logits = None
 
1
+ """LuauCoder for transformers (``trust_remote_code=True``).
2
+
3
+ Same math and parameter names as ``lm/model.py``. ``generate()`` uses the fused
4
+ engine in ``luau_infer.py`` when its settings allow.
5
+ """
6
+
7
  from __future__ import annotations
8
 
9
  import copy
 
41
 
42
  _V5 = int(transformers.__version__.split(".")[0]) >= 5
43
  IGNORE_INDEX = -100
44
+ _HW_TANH = torch.version.hip is None # PTX tanh.approx; ROCm uses sigmoid
 
 
 
45
 
46
 
47
  class _Fused:
48
+ """``fn`` through ``torch.compile`` on CUDA; eager otherwise or after a compile failure."""
 
 
49
 
50
  def __init__(self, fn):
51
  self.fn = fn
 
61
  return self.compiled(*args)
62
  except torch.OutOfMemoryError:
63
  raise
64
+ except Exception as exc:
65
  self.failed = True
66
  warnings.warn(f"torch.compile of {self.fn.__name__} failed ({exc!r}); running it eagerly",
67
  stacklevel=2)
 
72
 
73
 
74
  def _cached(tensors: tuple, fn):
75
+ """``fn()`` derived from ``tensors``; cached without grad until one of them changes."""
 
 
76
  if torch.is_grad_enabled() or torch.compiler.is_compiling():
77
  return fn()
78
  key = tuple((t.data_ptr(), t._version) for t in tensors)
 
116
 
117
  def _diff_xsa_fn(y: Tensor, lam: Tensor | None, v: Tensor, nh: int, nkv: int, xsa: bool,
118
  gate: Tensor | None = None) -> Tensor:
119
+ """DiffAttn -> XSA -> output gate (each optional)."""
 
 
 
120
  B, T, _, hd = y.shape
121
  if lam is not None:
122
  y = y.view(B, T, nh, 2, hd).float()
 
140
 
141
  def _iha_diff_xsa(y: Tensor, lam: Tensor | None, vp: Tensor, nh: int, nkv: int, xsa: bool,
142
  gate: Tensor | None = None) -> Tensor:
143
+ """``_diff_xsa_fn`` for IHA's collapsed heads: XSA removes the span of the pseudo-values."""
 
144
  B, T, _, hd = y.shape
145
  if lam is not None:
146
  y = y.view(B, T, nh, 2, hd).float()
 
163
 
164
 
165
  def _iha_attention(q: Tensor, k: Tensor, v: Tensor, mask: Tensor, scaling: float) -> Tensor:
166
+ """GQA attention with an explicit bool mask; fp32 scores and softmax."""
 
167
  B, nq, Tq, d = q.shape
168
  nkv = k.shape[1]
169
  s = torch.matmul(q.reshape(B, nkv, nq // nkv, Tq, d).float(), k.float().unsqueeze(2).transpose(-1, -2)) * scaling
 
172
 
173
 
174
  def _iha_mix_init(alpha: Tensor, stride: int) -> None:
175
+ """Training's IHA mix init."""
 
176
  H, _, P = alpha.shape
177
+ if getattr(alpha, "_is_hf_initialized", False):
178
  return
179
  with torch.no_grad():
180
  alpha.normal_(std=0.1 / H ** 0.5)
 
191
  def _ccgqa_fn(qk: Tensor, tail: Tensor, v: Tensor, before: Tensor | None, pos: Tensor, W: Tensor,
192
  b: Tensor, q_gain: Tensor, k_gain: Tensor, cos: Tensor | None, sin: Tensor | None, nq: int,
193
  nkv: int, eps: float, qk_mean: bool) -> tuple[Tensor, Tensor, Tensor, Tensor, Tensor | None]:
194
+ """CCGQA q / k / v from the q|k latents in fp32; ``tail`` / ``before`` carry decode state."""
 
 
 
 
 
195
  B, T, _, hd = qk.shape
196
  K, nxq = tail.shape[1] + 1, qk.shape[2] - nkv
197
  ext = torch.cat((tail, qk), 1) # (B, K-1+T, nxq+nkv, d)
198
 
199
+ def virtual(x: Tensor) -> Tensor:
200
  return x if nxq == nq else torch.cat((x[:, :, :nxq].repeat(1, 1, nq // nxq, 1), x[:, :, nxq:]), 2)
201
 
202
+ win = virtual(ext).unfold(1, K, 1) # slot j = lag K-1-j
203
  lag = torch.arange(K - 1, -1, -1, device=qk.device)
204
  win = torch.where((pos[..., None] >= lag)[:, :, None, None, :], win.float(), 0.0)
205
  H = win.shape[2]
 
217
  if before is None:
218
  return q, k, v.contiguous(), tail, None
219
  flat = v.reshape(B, T, nkv * hd)
220
+ half = nkv * hd // 2
221
  shifted = torch.where((pos > 0)[..., None], torch.cat((before, flat[:, :, half:]), 1)[:, :T], 0)
222
  v = torch.cat((flat[:, :, :half], shifted), -1).view(B, T, nkv, hd)
223
  return q, k, v, tail, flat[:, -1:, half:]
 
259
  _MAX_HIDDEN = 8192 # whole row in registers
260
  _MAX_SOURCES = 32
261
  _SPAN_BWD = dict(R=64, num_warps=2)
262
+ _TPP = 8
263
 
264
  if HAVE_TRITON:
265
 
 
268
  s_st, s_sk, x_st, o_st, st_st, eps,
269
  D: tl.constexpr, NB: tl.constexpr, NB_PAD: tl.constexpr, BLOCK: tl.constexpr,
270
  ADD: tl.constexpr, PART: tl.constexpr):
271
+ """One token: online softmax over the NB sources + x."""
 
272
  t = tl.program_id(0).to(tl.int64)
273
  offs = tl.arange(0, BLOCK)
274
  msk = offs < D
 
314
  D: tl.constexpr, NB: tl.constexpr, NB_PAD: tl.constexpr,
315
  HAS_RES: tl.constexpr, TPP: tl.constexpr, BLOCK: tl.constexpr,
316
  ADD: tl.constexpr):
317
+ """Per-token mix backward."""
 
 
318
  pid = tl.program_id(0)
319
  offs = tl.arange(0, BLOCK)
320
  msk = offs < D
 
360
  s_st, s_sk, ds_st, ds_sk, N, coef_c, coef_q,
361
  D: tl.constexpr, NB: tl.constexpr, NC: tl.constexpr, NC_PAD: tl.constexpr,
362
  R: tl.constexpr, CD: tl.constexpr):
363
+ """dS for every source of one block from all mixes that read it."""
 
364
  pid_t = tl.program_id(0)
365
  cols = tl.program_id(1) * CD + tl.arange(0, CD)
366
  mc = cols < D
 
412
 
413
  def attn_res_reference(x: Tensor, sources: Tensor, norm_weight: Tensor, query: Tensor,
414
  eps: float, additive: bool = False, partial: bool = True) -> Tensor:
415
+ """Eager fp32 mix reference."""
 
 
416
  values = torch.cat((sources, x.unsqueeze(1)), dim=1).float()
417
  keys = values * torch.rsqrt(values.square().mean(-1, keepdim=True) + eps)
418
  logits = (keys * (norm_weight.float() * query.float())).sum(-1)
 
429
 
430
  def __init__(self, n_calls: int, additive: bool = False, partial: bool = True):
431
  self.n_calls = n_calls
432
+ self.additive, self.partial = additive, partial
433
+ self.w: Tensor | None = None
434
  self.gbuf: Tensor | None = None # (n_calls, N, D) output grads, filled in backward
435
  self.coef: Tensor | None = None # (n_calls, 3, N, k) fp32 a / b / e
436
  self.dw_x: Tensor | None = None # (n_calls, D) fp32 x part of dw
437
+ self.res: dict[int, Tensor] = {}
438
+ self.mixed: set[int] = set()
439
  self.done = 0
440
 
441
 
442
  class _SpanSources(torch.autograd.Function):
443
+ """Identity on the sources; its backward builds ``dS`` once per block."""
 
444
 
445
  @staticmethod
446
  def forward(ctx, S: Tensor, state: _SpanState, *weights: Tensor) -> Tensor:
 
480
 
481
 
482
  class _ResidualAdd(torch.autograd.Function):
483
+ """``x + y``; the gradient of ``x`` is summed inside mix ``call``'s backward kernel."""
 
 
484
 
485
  @staticmethod
486
  def forward(ctx, x: Tensor, y: Tensor, state: _SpanState, call: int) -> Tensor:
 
490
  @staticmethod
491
  def backward(ctx, g: Tensor):
492
  st = ctx.state
493
+ if ctx.call in st.mixed:
494
  return g, g, None, None
495
  st.res[ctx.call] = g
496
  return None, g, None, None
 
537
 
538
 
539
  class AttnResSpan:
540
+ """The sources of one block and the mixes reading them; each site is called once per forward."""
 
 
 
 
 
 
 
541
 
542
  def __init__(self, S: Tensor, sites: list[tuple[Tensor, Tensor]], eps: float, fused: bool,
543
  additive: bool = False, partial: bool = True):
 
578
  return x + y
579
 
580
 
 
 
581
  def softcap_logits(logits: Tensor, cap: float | None) -> Tensor:
582
  """``cap * tanh(logits / cap)`` (``final_logit_softcapping``); identity for None / 0."""
583
  return cap * torch.tanh(logits / cap) if cap else logits
 
587
 
588
  @triton.jit
589
  def _tanh(x, HW: tl.constexpr):
590
+ if HW:
591
  return tl.inline_asm_elementwise("tanh.approx.f32 $0, $1;", "=r,r", [x],
592
  dtype=tl.float32, is_pure=True, pack=1)
593
  return 2.0 * tl.sigmoid(2.0 * x) - 1.0
 
596
  def _ce_fwd_bwd_kernel(logits_ptr, stride_row, labels_ptr, loss_ptr, scale_ptr, V, cap, inv_cap,
597
  IGNORE: tl.constexpr, SOFTCAP: tl.constexpr, HW_TANH: tl.constexpr,
598
  BLOCK: tl.constexpr):
599
+ """NLL of one row; its logits are overwritten with their gradient (base 2)."""
 
 
600
  LOG2E: tl.constexpr = 1.4426950408889634
601
  LN2: tl.constexpr = 0.6931471805599453
602
  row = tl.program_id(0).to(tl.int64)
 
605
  valid = label != IGNORE
606
  cap2 = cap * LOG2E
607
  safe_label = tl.where(valid, label, 0)
608
+ # target kept out of the sum-exp (Softmax Collapse)
 
609
  m = -float("inf")
610
  s_nt = 0.0
611
  for off in range(0, V, BLOCK):
 
626
  else:
627
  target = target * LOG2E
628
  s = tl.exp2(target - m) + s_nt
629
+ u = 1.0 + s_nt
630
  log1p = tl.where(u == 1.0, s_nt, tl.log(u) * (s_nt / (u - 1.0)))
631
  loss = tl.where(target >= m, log1p, (m + tl.log2(s) - target) * LN2)
632
  tl.store(loss_ptr + row, tl.where(valid, loss, 0.0))
633
  r = tl.where(valid, tl.load(scale_ptr), 0.0) / s
634
+ g_t = -s_nt * r
635
  for off in range(0, V, BLOCK):
636
  cols = off + tl.arange(0, BLOCK)
637
  msk = cols < V
 
656
 
657
 
658
  class _LinearCrossEntropy(torch.autograd.Function):
659
+ """Chunked ``CE(h @ W.T, y)`` that computes gradients during the forward."""
 
 
660
 
661
  @staticmethod
662
  def forward(ctx, hidden: Tensor, weight: Tensor, labels: Tensor, scale: Tensor, chunk: int,
 
713
  chunk, softcap)
714
 
715
 
 
 
 
716
  def compose_conv(w1: Tensor, b1: Tensor, w2: Tensor, b2: Tensor) -> tuple[Tensor, Tensor]:
717
+ """Compose the depthwise and per-head convs into one masked causal conv."""
 
 
 
718
  H, kc, hd, _ = w2.shape
719
  ks = w1.shape[1]
720
  a, w2f = w1.float().view(H, hd, ks, 1), w2.float()
 
725
 
726
 
727
  class LuauCoderCache(DynamicCache):
728
+ """``DynamicCache`` plus CCGQA's conv / value-shift decode state (no ``crop`` once it exists)."""
 
 
 
729
 
730
  def __init__(self, *args, **kwargs):
731
  super().__init__(*args, **kwargs)
 
772
 
773
 
774
  class LuauCoderStaticCache:
775
+ """Preallocated cache for ``generate.py``'s CUDA-graph loop; the caller sets ``idx`` / ``key_valid``."""
 
 
 
 
 
 
 
 
 
 
 
776
 
777
  is_static = True
778
 
 
816
  return states
817
 
818
 
 
 
 
819
  def _rope_settings(config: LuauCoderConfig) -> tuple[float, dict | None]:
820
+ """``(theta, yarn params or None)`` from a v4 or v5 config."""
 
821
  params = getattr(config, "rope_parameters", None) or getattr(config, "rope_scaling", None) or {}
822
  theta = float(params.get("rope_theta", getattr(config, "rope_theta", 500000.0)))
823
  rtype = params.get("rope_type", params.get("type"))
 
827
 
828
 
829
  def rope_inv_freq(head_dim: int, theta: float, yarn: dict | None) -> tuple[Tensor, float]:
830
+ """Inverse frequencies fp64 and the cos/sin scale (YaRN as in training)."""
 
831
  dim = head_dim
832
  pos_freqs = theta ** (torch.arange(0, dim, 2, dtype=torch.float64, device="cpu") / dim)
833
  extrap = 1.0 / pos_freqs
 
855
 
856
 
857
  def lerope_inv_freq(head_dim: int, theta: float, alpha: list[float], yarn: dict | None) -> tuple[Tensor, float]:
858
+ """LeRoPE frequencies and the cos/sin scale."""
 
859
  inv, mscale = rope_inv_freq(head_dim, theta, yarn)
860
  return inv * torch.tensor(alpha, dtype=torch.float64, device="cpu").exp(), mscale
861
 
862
 
863
  class LuauCoderRotaryEmbedding(nn.Module):
864
+ """fp32 cos/sin tables gathered by ``position_ids`` (plain attributes so ``.to(bf16)`` skips them)."""
 
 
865
 
866
  def __init__(self, config: LuauCoderConfig):
867
  super().__init__()
 
883
  return self._cos[position_ids], self._sin[position_ids]
884
 
885
 
 
 
 
886
  class LuauCoderRMSNorm(nn.Module):
887
+ """``learnable=False``: the gain is a buffer fixed at 1."""
 
 
888
 
889
  def __init__(self, dim: int, eps: float, fuse: bool = True, learnable: bool = True):
890
  super().__init__()
 
925
 
926
 
927
  def _decode_attention(q: Tensor, k: Tensor, v: Tensor, mask: Tensor, scaling: float) -> Tensor:
928
+ """Single-token decode attention against a static cache, reading each KV head once."""
 
 
 
929
  B, nq, _, d = q.shape
930
  nkv = k.shape[1]
931
  s = torch.matmul(q.reshape(B, nkv, nq // nkv, d).float(), k.float().transpose(-1, -2)) * scaling
 
934
 
935
 
936
  class LuauCoderAttention(nn.Module):
937
+ """DiffAttn V2 + XSA attention."""
 
938
 
939
  def __init__(self, config: LuauCoderConfig, layer_idx: int):
940
  super().__init__()
941
  self.config, self.layer_idx = config, layer_idx
942
  self.nh, self.nkv, self.hd = config.num_attention_heads, config.num_key_value_heads, config.head_dim
943
  self.nq = config.num_query_heads
 
944
  self.nxq = getattr(config, "num_q_latent_heads", self.nq)
945
  self.diff, self.xsa = config.diff_v2, config.xsa
946
  self.num_key_value_groups = self.nq // self.nkv
 
951
  self.rope = layer_idx not in (getattr(config, "nope_layers", None) or ())
952
  self.iha = config.iha_layer(layer_idx)
953
  D = config.hidden_size
 
 
954
  self.g_rows = self.nh * self.hd if config.output_gate == "elementwise" else 0
955
  self.qkv = nn.Linear(D, (self.nxq + 2 * self.nkv) * self.hd + self.g_rows, bias=False)
956
  self.lambda_proj = nn.Linear(D, self.nh, bias=False) if self.diff else None
957
  self.gate_proj = nn.Linear(D, self.nh, bias=False) if config.output_gate == "headwise" else None
958
  self.o_proj = nn.Linear(self.nh * self.hd, D, bias=False)
 
959
  self.q_norm = LuauCoderRMSNorm(self.hd, self.eps, learnable=False) if config.qk_norm else None
960
  self.k_norm = LuauCoderRMSNorm(self.hd, self.eps, learnable=False) if config.qk_norm else None
961
  self.ccgqa = getattr(config, "ccgqa", False)
962
  if self.ccgqa:
 
 
963
  H, ks, kc = self.nq + self.nkv, config.ccgqa_seq_kernel, config.ccgqa_head_kernel
964
  self.conv_taps = ks + kc - 1
965
  self.qk_mean, self.value_shift = config.ccgqa_qk_mean, config.ccgqa_value_shift
 
967
  self.conv_seq_bias = nn.Parameter(torch.zeros(H * self.hd))
968
  self.conv_head = nn.Parameter(torch.empty(H, kc, self.hd, self.hd))
969
  self.conv_head_bias = nn.Parameter(torch.zeros(H, self.hd))
970
+ self.k_temp = nn.Parameter(torch.zeros(self.nkv)) # key scale exp(t)
971
  if self.iha:
972
  P = self.iha_p = config.iha_pseudo_heads
973
  self.iha_q = nn.Parameter(torch.empty(self.nq, self.nq, P))
 
985
  conv_pos = kwargs.pop("conv_pos", None)
986
  iha_ctx = kwargs.pop("iha_ctx", None)
987
  rope = self.rope
988
+ if not self.rope or self.iha:
989
  cos = sin = None
990
  if self.ccgqa:
991
  qk, v = qkv.split([self.nxq + self.nkv, self.nkv], dim=2)
 
1000
  y, vp = self._iha_attend(q, k, v, iha_ctx, rope, past_key_values)
1001
  probs = None
1002
  else:
 
1003
  q, k_all, v_all = q.transpose(1, 2), k.transpose(1, 2), v.transpose(1, 2)
1004
  if past_key_values is not None:
1005
  k_all, v_all = past_key_values.update(k_all, v_all, self.layer_idx)
 
1010
  fn = eager_attention_forward if impl in (None, "eager") else ALL_ATTENTION_FUNCTIONS[impl]
1011
  y, probs = fn(self, q, k_all, v_all, attention_mask, dropout=0.0, scaling=self.scaling,
1012
  position_ids=position_ids, **kwargs) # (B, T, nq, d)
1013
+ if self.diff and self.gate_proj is not None:
1014
  lam, gate = F.linear(x, torch.cat((self.lambda_proj.weight, self.gate_proj.weight))).split(self.nh, -1)
1015
  else:
1016
  lam = self.lambda_proj(x) if self.diff else None
 
1025
 
1026
  def _iha_attend(self, q: Tensor, k: Tensor, v: Tensor, ctx: dict, rope: bool,
1027
  cache: Cache | None) -> tuple[Tensor, Tensor]:
1028
+ """IHA attention with the pseudo-keys cached as extra heads."""
 
 
 
 
1029
  B, T, _, hd = q.shape
1030
  P, nq, nkv = self.iha_p, self.nq, self.nkv
1031
 
 
1046
  if cache is not None:
1047
  keys, vals = cache.update(keys, vals, self.layer_idx)
1048
 
1049
+ def interleave(t: Tensor) -> Tensor:
1050
  t = t[:, :, ctx["first"]:]
1051
  return t.reshape(B, nkv, P, -1, hd).transpose(2, 3).reshape(B, nkv, -1, hd)
1052
 
 
1059
 
1060
  def _ccgqa_qkv(self, qk: Tensor, v: Tensor, cos: Tensor | None, sin: Tensor | None,
1061
  pos: Tensor | None, cache: Cache | None) -> tuple[Tensor, Tensor, Tensor]:
1062
+ """CCGQA q / k / v with conv and value-shift state in ``cache``."""
 
1063
  B, T = qk.shape[:2]
1064
  if pos is None:
1065
  pos = torch.arange(T, device=qk.device).expand(B, T)
 
1077
  return q, k, v
1078
 
1079
  def _ccgqa_consts(self) -> tuple[Tensor, Tensor, Tensor, Tensor]:
1080
+ """fp32 composed conv weights, bias and gains (cached without grad)."""
 
 
1081
  params = (self.conv_seq, self.conv_seq_bias, self.conv_head, self.conv_head_bias,
1082
  self.q_norm.weight, self.k_norm.weight, self.k_temp)
1083
 
 
1085
  W, b = compose_conv(*params[:4])
1086
  H, K, hd, _ = W.shape
1087
  W = W.flip(1).permute(0, 2, 1, 3).reshape(H, hd * K, hd)
 
1088
  kg = params[5].float() if self.iha else params[5].float() * params[6].float().exp()[:, None]
1089
  return W, b, params[4].float(), kg
1090
 
 
1114
  self.ffn = LuauCoderMLP(config)
1115
  self.attn_res = config.attn_res
1116
  if self.attn_res:
 
 
1117
  self.block_start = layer_idx % config.attn_res_block_size == 0
1118
  self.attn_res_norm = nn.Parameter(torch.ones(D)) if layer_idx else None
1119
  self.attn_res_query = nn.Parameter(torch.zeros(D)) if layer_idx else None
 
1134
 
1135
  def _forward_attn_res(self, partial: Tensor, attn_span: AttnResSpan | None, mlp_span: AttnResSpan,
1136
  cos, sin, attention_mask, position_ids, past_key_values, **kwargs):
1137
+ """Block AttnRes layer; returns the new partial and this layer's attention input."""
 
 
 
1138
  B, T, D = partial.shape
1139
  N = B * T
1140
  flat = partial.reshape(N, D)
 
1142
  a, _ = self.attn(self.attn_norm(h.view(B, T, D)), cos, sin, attention_mask, position_ids,
1143
  past_key_values, **kwargs)
1144
  a = a.reshape(N, D).to(flat.dtype)
 
 
1145
  flat = a if self.block_start else attn_span.residual(flat, a, self.attn_call)
1146
  m = mlp_span.mix(flat, self.mlp_call)
1147
  f = self.ffn(self.ffn_norm(m.view(B, T, D))).reshape(N, D).to(flat.dtype)
1148
  return mlp_span.residual(flat, f, self.mlp_call).view(B, T, D), h.view(B, T, D)
1149
 
1150
 
 
 
 
1151
  class LuauCoderPreTrainedModel(PreTrainedModel):
1152
  config_class = LuauCoderConfig
1153
  config: LuauCoderConfig
 
1165
 
1166
  @torch.no_grad()
1167
  def _init_weights(self, module: nn.Module) -> None:
1168
+ """The training stack's init."""
 
 
1169
  cfg = self.config
1170
  std = cfg.initializer_range
1171
  out_std = std / math.sqrt(2 * cfg.num_hidden_layers)
 
1176
  _init.normal_(module.lambda_proj.weight, mean=0.0, std=0.5 * std)
1177
  if module.gate_proj is not None:
1178
  _init.normal_(module.gate_proj.weight, mean=0.0, std=std)
1179
+ if module.ccgqa: # ZAYA init
1180
  _init.normal_(module.conv_seq, mean=0.0, std=std)
1181
  _init.normal_(module.conv_head, mean=0.0, std=std)
1182
  for p in (module.conv_seq_bias, module.conv_head_bias, module.k_temp):
 
1196
  elif isinstance(module, LuauCoderForCausalLM):
1197
  if not cfg.tie_word_embeddings:
1198
  _init.normal_(module.lm_head.weight, mean=0.0, std=std)
1199
+ elif isinstance(module, LuauCoderHeadGate):
1200
+ _init.normal_(module.proj, mean=0.0, std=std)
1201
+ _init.zeros_(module.bias)
1202
  for name, p in module.named_parameters(recurse=False):
1203
  if name.endswith("_res_norm"):
1204
  _init.ones_(p)
 
1207
 
1208
  @classmethod
1209
  def _supports_default_dynamic_cache(cls) -> bool:
 
 
1210
  return False
1211
 
1212
  def gradient_checkpointing_enable(self, gradient_checkpointing_kwargs=None, **kwargs):
1213
+ # reentrant checkpointing would drop the AttnRes sources' gradients
 
1214
  gc_kwargs = dict(gradient_checkpointing_kwargs or {})
1215
  gc_kwargs["use_reentrant"] = False
1216
  return super().gradient_checkpointing_enable(gradient_checkpointing_kwargs=gc_kwargs, **kwargs)
 
1245
  self.norm = LuauCoderRMSNorm(config.hidden_size, config.rms_norm_eps, config.compile_elementwise)
1246
  self.rotary_emb = LuauCoderRotaryEmbedding(config)
1247
  if config.attn_res:
1248
+ if not config.attn_res_delta_block:
1249
  self.out_res_norm = nn.Parameter(torch.ones(config.hidden_size))
1250
  self.out_res_query = nn.Parameter(torch.zeros(config.hidden_size))
1251
  self._plan_attn_res()
 
1253
  self.post_init()
1254
 
1255
  def _plan_attn_res(self) -> None:
1256
+ """Assign each mix its source span and call index (resolved to parameters every forward)."""
 
 
 
 
 
 
 
 
1257
  S, L = self.config.attn_res_block_size, self.config.num_hidden_layers
1258
  n_spans = (L + S - 1) // S
1259
  self._span_sites: list[list] = [[None] * (2 * min(S, L - m * S)) for m in range(n_spans)]
 
1287
 
1288
  def _iha_context(self, position_ids: Tensor, pos: Tensor, attention_mask: Tensor | None,
1289
  past_len: int) -> dict:
1290
+ """Shared IHA state: virtual-position cos / sin, window start and mask."""
 
 
 
1291
  cfg = self.config
1292
  P, W = cfg.iha_pseudo_heads, cfg.iha_window
1293
  if attention_mask is not None and attention_mask.dim() != 2:
 
1326
  kwargs.pop("output_attentions", None)
1327
  output_hidden_states = (output_hidden_states if output_hidden_states is not None
1328
  else self.config.output_hidden_states)
1329
+ if use_cache is None:
1330
+ use_cache = self.config.use_cache and not self.training
1331
  if self.gradient_checkpointing and self.training:
1332
  use_cache = False
1333
  if inputs_embeds is None:
 
1338
  past_key_values = None
1339
  B, T, D = inputs_embeds.shape
1340
  if getattr(past_key_values, "is_static", False):
 
1341
  if position_ids is None or attention_mask is not None:
1342
  raise ValueError("a LuauCoderStaticCache takes position_ids and no attention_mask")
1343
  if self.config.iha:
 
1357
  layer_kw = dict(cos=cos, sin=sin, attention_mask=mask, position_ids=position_ids,
1358
  past_key_values=past_key_values, **kwargs)
1359
  if self.config.ccgqa or self.config.iha:
 
 
1360
  pos = position_ids.long().expand(B, T)
1361
  if attention_mask is not None and attention_mask.dim() == 2:
1362
  pos = torch.minimum(pos, (attention_mask.long().cumsum(-1) - 1)[:, -T:])
 
1367
 
1368
  if self.config.attn_res:
1369
  x = inputs_embeds
1370
+ if torch.is_autocast_enabled(x.device.type):
1371
  x = x.to(torch.get_autocast_dtype(x.device.type))
1372
  partial = x
1373
  sources = x.reshape(B * T, 1, D)[:, :0]
 
1376
  delta = self.config.attn_res_delta_block
1377
  route_x = self.config.attn_res_partial_source or not delta
1378
  for i, blk in enumerate(self.blocks):
1379
+ if blk.block_start:
1380
  sources = torch.cat((sources, partial.reshape(B * T, 1, D)), dim=1)
1381
  sites = [self._site(*s) for s in self._span_sites[len(spans)]]
1382
  spans.append(AttnResSpan(sources, sites, eps, fused, delta, route_x))
 
1399
  hidden_states=tuple(hidden) if output_hidden_states else None)
1400
 
1401
 
1402
+ # unsupported generation-config fields and their neutral values
 
 
1403
  _FUSED_NEUTRAL = {
1404
  "num_beams": (None, 1), "num_beam_groups": (None, 1), "diversity_penalty": (None, 0.0), "penalty_alpha": (None,),
1405
  "dola_layers": (None,), "constraints": (None,), "force_words_ids": (None,), "prompt_lookup_num_tokens": (None,),
 
1413
  "output_hidden_states": (None, False), "return_dict_in_generate": (None, False), "token_healing": (None, False),
1414
  "max_time": (None,), "use_mtp": (None, False),
1415
  }
 
 
1416
  _FUSED_MODEL_KWARGS = {"input_ids", "attention_mask", "use_cache", "tokenizer", "generation_mode"}
1417
 
1418
 
 
1421
  """None if ``generate()`` can run on the fused decoder with these settings, else why not."""
1422
  if not (HAVE_TRITON and model.device.type == "cuda") or model.training:
1423
  return "needs CUDA + triton, eval mode"
1424
+ if model.dtype != torch.bfloat16:
1425
  return f"a {model.dtype} model (the fused decoder runs bf16 models)"
1426
  used = [k for k, v in extra.items() if v is not None and v is not False]
1427
  if used:
1428
  return f"unsupported generate() arguments {used}"
1429
+ if logits_processor:
1430
+ return "custom logits processors"
1431
  if set(model_kwargs) - _FUSED_MODEL_KWARGS:
1432
  return f"model kwargs {sorted(set(model_kwargs) - _FUSED_MODEL_KWARGS)}"
1433
  for name, ok in _FUSED_NEUTRAL.items():
 
1436
  if not isinstance(inputs, Tensor) or inputs.dim() != 2 or inputs.is_floating_point():
1437
  return "needs input_ids (B, T)"
1438
  eos = gc.eos_token_id
1439
+ if isinstance(eos, (list, tuple)) and len(set(eos)) > 8:
1440
+ return "more than 8 EOS tokens"
1441
  if gc.stop_strings and model_kwargs.get("tokenizer") is None:
1442
  return "stop_strings without a tokenizer"
1443
  mask = model_kwargs.get("attention_mask")
 
1448
 
1449
 
1450
  class _FusedGenerationMixin:
1451
+ """``generate()`` through the fused engine when every setting is supported, else transformers'.
1452
+ Extra kwargs: ``fused_generate``, ``fused_spec`` (draft length, 0 = off)."""
 
 
 
 
 
 
 
 
 
1453
 
1454
  def generate(self, inputs=None, generation_config=None, logits_processor=None, stopping_criteria=None,
1455
  prefix_allowed_tokens_fn=None, synced_gpus=None, assistant_model=None, streamer=None,
 
1472
  """The fused decoder's output, or None when these settings need transformers' loop."""
1473
  try:
1474
  gc, model_kwargs = self._prepare_generation_config(copy.deepcopy(generation_config), **kwargs)
1475
+ except Exception:
1476
  return None
1477
  if inputs is None:
1478
  inputs = model_kwargs.get("input_ids")
1479
  why = _fused_fallback_reason(self, inputs, gc, model_kwargs, logits_processor, stopping_criteria, hf)
1480
+ self._fused_last_reason = why
1481
  if why is not None:
1482
  return None
1483
  eos = gc.eos_token_id
1484
+ if hasattr(eos, "tolist"):
1485
+ eos = eos.tolist()
1486
  if isinstance(eos, (list, tuple)):
1487
+ eos = [int(e) for e in eos] or None
1488
+ first_eos = eos[0] if isinstance(eos, list) else eos
1489
+ pad = gc.pad_token_id if gc.pad_token_id is not None else first_eos
1490
  if isinstance(pad, (list, tuple)):
1491
  pad = pad[0]
1492
  mask = model_kwargs.get("attention_mask")
1493
+ if mask is None:
1494
+ mask = (inputs != pad).long() if pad is not None and pad not in (eos if isinstance(eos, list) else [eos]) and bool((inputs == pad).any()) \
1495
  else torch.ones_like(inputs)
1496
  n_new = gc.max_new_tokens if gc.max_new_tokens is not None else (gc.max_length or 20) - inputs.shape[1]
1497
  stop = gc.stop_strings or []
 
1503
  sample=bool(gc.do_sample), temperature=gc.temperature or 1.0, top_k=gc.top_k or 0,
1504
  top_p=1.0 if gc.top_p is None else gc.top_p, min_p=gc.min_p or 0.0,
1505
  repetition_penalty=gc.repetition_penalty or 1.0, num_samples=gc.num_return_sequences or 1,
1506
+ eos=eos, pad=pad, stop=stop, tokenizer=model_kwargs.get("tokenizer"), streamer=streamer,
1507
+ stopping_criteria=stopping_criteria or None)
1508
 
1509
  def _fused_decoder(self, spec: int):
1510
  """The decoder of these weights, kept between calls (rebuilt when a parameter changed)."""
1511
  from .luau_infer import FusedDecoder
1512
  try:
1513
  key = (spec,) + tuple((p.data_ptr(), p._version) for p in self.parameters())
1514
+ except RuntimeError:
1515
  key = (spec,) + tuple(p.data_ptr() for p in self.parameters())
1516
  hit = self.__dict__.get("_fused")
1517
  if hit is None or hit[0] != key:
1518
+ self.__dict__["_fused"] = None
1519
  with torch.inference_mode():
1520
  self.__dict__["_fused"] = (key, FusedDecoder(self, None, spec))
1521
  return self.__dict__["_fused"][1]
1522
 
1523
 
1524
+ class LuauCoderHeadGate(nn.Module):
1525
+ """Gated low-rank head term: input ``[h, sigmoid(u . h + b) * P h]``, ``lm_head.weight = [W, Z]``."""
1526
+
1527
+ def __init__(self, hidden_size: int, rank: int):
1528
+ super().__init__()
1529
+ self.rank = rank
1530
+ self.proj = nn.Parameter(torch.empty(rank + 1, hidden_size))
1531
+ self.bias = nn.Parameter(torch.zeros(1))
1532
+
1533
+ def forward(self, h: Tensor) -> Tensor:
1534
+ y = F.linear(h, self.proj.to(h.dtype))
1535
+ gate = torch.sigmoid(y[..., self.rank:] + self.bias.to(h.dtype))
1536
+ return torch.cat([h, gate * y[..., :self.rank]], dim=-1)
1537
+
1538
+
1539
  class LuauCoderForCausalLM(LuauCoderPreTrainedModel, _FusedGenerationMixin, GenerationMixin):
1540
  _tied_weights_keys = {"lm_head.weight": "model.embed.weight"} if _V5 else ["lm_head.weight"]
1541
 
 
1543
  super().__init__(config)
1544
  self.model = LuauCoderModel(config)
1545
  self.vocab_size = config.vocab_size
1546
+ r = getattr(config, "head_gate_rank", 0)
1547
+ if r and config.tie_word_embeddings:
1548
+ raise ValueError("head_gate_rank needs an untied head")
1549
+ self.head_gate = LuauCoderHeadGate(config.hidden_size, r) if r else None
1550
+ self.lm_head = nn.Linear(config.hidden_size + r, config.vocab_size, bias=False)
1551
  self.post_init()
1552
 
1553
  def get_input_embeddings(self) -> nn.Embedding:
 
1568
  use_cache: bool | None = None, logits_to_keep: int | Tensor = 0,
1569
  output_hidden_states: bool | None = None, return_dict: bool | None = None,
1570
  **kwargs) -> CausalLMOutputWithPast:
1571
+ """HF-convention ``labels``; with labels and grad the fused CE runs and ``logits`` is None."""
 
 
1572
  num_items_in_batch = kwargs.pop("num_items_in_batch", None)
1573
  shift_labels = kwargs.pop("shift_labels", None)
1574
  out = self.model(input_ids=input_ids, attention_mask=attention_mask, position_ids=position_ids,
1575
  past_key_values=past_key_values, inputs_embeds=inputs_embeds,
1576
  use_cache=use_cache, output_hidden_states=output_hidden_states, **kwargs)
1577
  h = out.last_hidden_state
1578
+ if self.head_gate is not None:
1579
+ if shift_labels is None and labels is None and isinstance(logits_to_keep, int) and logits_to_keep:
1580
+ h = h[:, -logits_to_keep:, :]
1581
+ logits_to_keep = 0
1582
+ h = self.head_gate(h)
1583
  if labels is not None and shift_labels is None:
1584
  shift_labels = F.pad(labels[:, 1:], (0, 1), value=IGNORE_INDEX)
1585
  loss = logits = None