Instructions to use khtsly/Kai-2.0-Base with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use khtsly/Kai-2.0-Base with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="khtsly/Kai-2.0-Base", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("khtsly/Kai-2.0-Base", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use khtsly/Kai-2.0-Base with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "khtsly/Kai-2.0-Base" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khtsly/Kai-2.0-Base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/khtsly/Kai-2.0-Base
- SGLang
How to use khtsly/Kai-2.0-Base with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "khtsly/Kai-2.0-Base" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khtsly/Kai-2.0-Base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "khtsly/Kai-2.0-Base" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "khtsly/Kai-2.0-Base", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use khtsly/Kai-2.0-Base with Docker Model Runner:
docker model run hf.co/khtsly/Kai-2.0-Base
Upload folder using huggingface_hub
Browse files- configuration_luau_coder.py +154 -146
- luau_gemv_tuned.py +119 -46
- luau_infer.py +242 -131
- luau_kernels.py +966 -734
- modeling_luau_coder.py +116 -216
configuration_luau_coder.py
CHANGED
|
@@ -1,146 +1,154 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
self.
|
| 69 |
-
self.
|
| 70 |
-
self.
|
| 71 |
-
self.
|
| 72 |
-
self.
|
| 73 |
-
self.
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
self.
|
| 77 |
-
self.
|
| 78 |
-
self.
|
| 79 |
-
self.
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
self.
|
| 83 |
-
|
| 84 |
-
self.
|
| 85 |
-
self.
|
| 86 |
-
|
| 87 |
-
self.
|
| 88 |
-
|
| 89 |
-
self.
|
| 90 |
-
self.
|
| 91 |
-
self.
|
| 92 |
-
self.
|
| 93 |
-
self.
|
| 94 |
-
self.
|
| 95 |
-
self.
|
| 96 |
-
self.
|
| 97 |
-
self.
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
self.
|
| 102 |
-
self.
|
| 103 |
-
|
| 104 |
-
self.
|
| 105 |
-
self.
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
if
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
if
|
| 118 |
-
raise ValueError("
|
| 119 |
-
if
|
| 120 |
-
raise ValueError(f"
|
| 121 |
-
if
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 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``
|
| 63 |
-
a (left-padded) prompt batch, then ``step`` a decode graph (``graph(K, settings)``)."""
|
| 64 |
|
| 65 |
-
MAX_GEMV_ROWS = 16
|
| 66 |
-
NSPL =
|
| 67 |
L_ROUND = 256 # cache length granularity
|
| 68 |
-
DECODE_T = 16
|
| 69 |
-
MAX_DRAFT = DECODE_T - 1
|
| 70 |
|
| 71 |
-
|
|
|
|
|
|
|
| 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
|
| 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)
|
| 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
|
| 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
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
| 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()
|
| 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 |
-
|
| 166 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 167 |
if self.ccgqa:
|
| 168 |
-
ring = K.RING.value
|
| 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
|
| 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 |
-
|
|
|
|
| 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),
|
| 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 |
-
"""
|
| 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 |
-
|
| 222 |
-
|
| 223 |
-
|
|
|
|
|
|
|
| 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=
|
| 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,
|
| 229 |
-
|
| 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=
|
| 233 |
-
|
| 234 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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 |
-
|
|
|
|
|
|
|
| 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
|
| 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)
|
| 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
|
| 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:
|
| 296 |
o = F.scaled_dot_product_attention(qq, kk, vv, is_causal=True, enable_gqa=True)
|
| 297 |
-
else:
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 308 |
|
| 309 |
@staticmethod
|
| 310 |
def process(logits: Tensor, sp: dict, pres: Tensor | None) -> Tensor:
|
| 311 |
-
"""transformers' logits processors in
|
| 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 |
-
#
|
| 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,))))
|
| 361 |
-
if sp["repetition_penalty"] != 1.0:
|
| 362 |
self.pres.scatter_(1, input_ids, True)
|
| 363 |
R = B * T0
|
| 364 |
-
bf = self._bufs(R, decode=T0 <= self.
|
| 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
|
| 372 |
first = self.sample(logits, sp).masked_fill(self.done, sp["pad"])
|
| 373 |
-
self.done |= first
|
| 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
|
| 383 |
|
| 384 |
def _draft(self, K: int, st: dict) -> Tensor:
|
| 385 |
-
"""Prompt-lookup draft ``(B, K)``
|
| 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))
|
| 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])
|
| 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
|
| 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:
|
| 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
|
| 449 |
-
if sp["repetition_penalty"] != 1.0:
|
| 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
|
| 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.
|
| 472 |
-
raise ValueError(f"draft length {K}: at most {self.
|
| 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)``
|
| 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
|
| 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
|
| 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 |
-
"""
|
| 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
|
| 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
|
| 585 |
-
# sampled batches:
|
| 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
|
| 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
|
| 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
|
| 607 |
-
pad: int | None = None, stop=(), tokenizer=None, streamer=None) -> Tensor:
|
| 608 |
-
"""Prompt + completion ids ``(B * num_samples, T0 + n)``
|
| 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 |
-
|
| 614 |
-
pad = int(pad) if pad is not None else
|
| 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),
|
| 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
|
| 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]] = {}
|
| 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
|
| 682 |
x in tokenizer.decode(gen[b][-tail:], skip_special_tokens=True) for x in stop))
|
| 683 |
if done[b]:
|
| 684 |
-
if t
|
| 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 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
| 170 |
-
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
| 189 |
-
|
| 190 |
-
|
| 191 |
-
|
| 192 |
-
|
| 193 |
-
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
|
| 197 |
-
|
| 198 |
-
|
| 199 |
-
|
| 200 |
-
|
| 201 |
-
|
| 202 |
-
|
| 203 |
-
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
|
| 208 |
-
|
| 209 |
-
|
| 210 |
-
|
| 211 |
-
|
| 212 |
-
|
| 213 |
-
|
| 214 |
-
|
| 215 |
-
|
| 216 |
-
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
|
| 236 |
-
|
| 237 |
-
|
| 238 |
-
|
| 239 |
-
|
| 240 |
-
|
| 241 |
-
|
| 242 |
-
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
| 249 |
-
|
| 250 |
-
|
| 251 |
-
|
| 252 |
-
|
| 253 |
-
|
| 254 |
-
|
| 255 |
-
|
| 256 |
-
|
| 257 |
-
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
|
| 265 |
-
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
|
| 277 |
-
|
| 278 |
-
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
| 282 |
-
|
| 283 |
-
|
| 284 |
-
|
| 285 |
-
|
| 286 |
-
|
| 287 |
-
|
| 288 |
-
|
| 289 |
-
|
| 290 |
-
|
| 291 |
-
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
| 305 |
-
|
| 306 |
-
|
| 307 |
-
|
| 308 |
-
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
|
| 319 |
-
|
| 320 |
-
|
| 321 |
-
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
|
| 330 |
-
|
| 331 |
-
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
| 341 |
-
|
| 342 |
-
|
| 343 |
-
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
|
| 353 |
-
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
|
| 362 |
-
|
| 363 |
-
|
| 364 |
-
|
| 365 |
-
|
| 366 |
-
|
| 367 |
-
|
| 368 |
-
|
| 369 |
-
|
| 370 |
-
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
|
| 381 |
-
|
| 382 |
-
|
| 383 |
-
|
| 384 |
-
|
| 385 |
-
|
| 386 |
-
|
| 387 |
-
|
| 388 |
-
|
| 389 |
-
|
| 390 |
-
|
| 391 |
-
|
| 392 |
-
|
| 393 |
-
|
| 394 |
-
|
| 395 |
-
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
|
| 399 |
-
|
| 400 |
-
|
| 401 |
-
|
| 402 |
-
|
| 403 |
-
|
| 404 |
-
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
ch
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
|
| 423 |
-
|
| 424 |
-
|
| 425 |
-
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
| 441 |
-
|
| 442 |
-
|
| 443 |
-
|
| 444 |
-
|
| 445 |
-
|
| 446 |
-
|
| 447 |
-
|
| 448 |
-
|
| 449 |
-
|
| 450 |
-
|
| 451 |
-
|
| 452 |
-
|
| 453 |
-
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
|
| 457 |
-
|
| 458 |
-
|
| 459 |
-
|
| 460 |
-
|
| 461 |
-
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
|
| 474 |
-
|
| 475 |
-
|
| 476 |
-
|
| 477 |
-
|
| 478 |
-
|
| 479 |
-
|
| 480 |
-
|
| 481 |
-
|
| 482 |
-
|
| 483 |
-
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
| 516 |
-
|
| 517 |
-
|
| 518 |
-
|
| 519 |
-
|
| 520 |
-
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
|
| 524 |
-
|
| 525 |
-
|
| 526 |
-
|
| 527 |
-
|
| 528 |
-
|
| 529 |
-
|
| 530 |
-
|
| 531 |
-
|
| 532 |
-
|
| 533 |
-
|
| 534 |
-
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
tl.
|
| 575 |
-
|
| 576 |
-
tl.
|
| 577 |
-
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
@triton.jit
|
| 589 |
-
def
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
|
| 593 |
-
|
| 594 |
-
|
| 595 |
-
|
| 596 |
-
|
| 597 |
-
|
| 598 |
-
|
| 599 |
-
|
| 600 |
-
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
| 611 |
-
|
| 612 |
-
|
| 613 |
-
|
| 614 |
-
|
| 615 |
-
|
| 616 |
-
|
| 617 |
-
|
| 618 |
-
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
| 628 |
-
|
| 629 |
-
|
| 630 |
-
|
| 631 |
-
|
| 632 |
-
|
| 633 |
-
|
| 634 |
-
|
| 635 |
-
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
|
| 639 |
-
|
| 640 |
-
|
| 641 |
-
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
|
| 645 |
-
|
| 646 |
-
|
| 647 |
-
|
| 648 |
-
|
| 649 |
-
|
| 650 |
-
|
| 651 |
-
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
| 663 |
-
|
| 664 |
-
|
| 665 |
-
|
| 666 |
-
|
| 667 |
-
|
| 668 |
-
|
| 669 |
-
|
| 670 |
-
|
| 671 |
-
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
-
|
| 685 |
-
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
|
| 692 |
-
ok =
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
|
| 717 |
-
|
| 718 |
-
|
| 719 |
-
|
| 720 |
-
|
| 721 |
-
|
| 722 |
-
|
| 723 |
-
|
| 724 |
-
|
| 725 |
-
|
| 726 |
-
|
| 727 |
-
|
| 728 |
-
|
| 729 |
-
|
| 730 |
-
|
| 731 |
-
|
| 732 |
-
|
| 733 |
-
|
| 734 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
# fused elementwise
|
| 42 |
|
| 43 |
|
| 44 |
class _Fused:
|
| 45 |
-
"""``fn`` through ``torch.compile`` on CUDA
|
| 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:
|
| 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()``
|
| 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 |
-
"""
|
| 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
|
| 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
|
| 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
|
| 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):
|
| 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
|
| 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:
|
| 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)
|
| 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
|
| 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
|
| 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
|
| 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 |
-
"""
|
| 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
|
| 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
|
| 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
|
| 451 |
-
self.w: Tensor | None = None
|
| 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] = {}
|
| 456 |
-
self.mixed: set[int] = set()
|
| 457 |
self.done = 0
|
| 458 |
|
| 459 |
|
| 460 |
class _SpanSources(torch.autograd.Function):
|
| 461 |
-
"""Identity on the sources
|
| 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``
|
| 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:
|
| 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
|
| 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:
|
| 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
|
| 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 |
-
#
|
| 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
|
| 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
|
| 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)``
|
| 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 |
-
"""
|
| 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
|
| 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
|
| 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
|
| 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
|
| 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
|
| 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
|
| 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``
|
| 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 |
-
"""
|
| 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
|
| 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)
|
| 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:
|
| 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:
|
| 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 |
-
"""
|
| 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:
|
| 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
|
| 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
|
| 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 |
-
"""
|
| 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
|
| 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
|
| 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 |
-
#
|
| 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:
|
| 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 |
-
"""
|
| 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 |
-
"""
|
| 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:
|
| 1440 |
-
use_cache = self.config.use_cache and not self.training
|
| 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):
|
| 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:
|
| 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 |
-
#
|
| 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:
|
| 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
|
| 1547 |
-
return "custom logits processors
|
| 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)) >
|
| 1557 |
-
return "
|
| 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
|
| 1569 |
-
|
| 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:
|
| 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
|
| 1607 |
if why is not None:
|
| 1608 |
return None
|
| 1609 |
eos = gc.eos_token_id
|
|
|
|
|
|
|
| 1610 |
if isinstance(eos, (list, tuple)):
|
| 1611 |
-
eos =
|
| 1612 |
-
|
|
|
|
| 1613 |
if isinstance(pad, (list, tuple)):
|
| 1614 |
pad = pad[0]
|
| 1615 |
mask = model_kwargs.get("attention_mask")
|
| 1616 |
-
if mask is None:
|
| 1617 |
-
mask = (inputs != pad).long() if pad is not None and pad
|
| 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:
|
| 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
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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``
|
| 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
|