KernelBench hard · RTX PRO 6000
KDA CUTLASS Claude Fable 5
manually audited: clean
Genuine from-scratch three-stage Triton KDA chunk forward on H100. Intra kernel builds in-chunk cumsum(g) in log2 space, Aqk/Akk, Neumann-doubling inverse of (I+strict-tril Akk), then w/u/kg/qg/dl; recurrent kernel walks chunks updating S and v_new; output kernel does o = qg@S + tril(Aqk)@v_new. Host allocates fresh workspace every forward and launches all three kernels with no memoization, data_ptr identity gate, or CUDA-graph replay. Agent studied public FLA sources under workspace fla-src and profiled chunk_kda as a baseline only — solution.py imports neither fla nor forbidden ops. peak_fraction 0.018 is an honest low number (same ballpark as prior clean KDA cells), far below a cached-output signature. In-session harness check failed on torch cu13 vs driver 12.8; post-hoc torch_cu128_fix_recheck set correct=true and peak_fraction=0.018. template_mutated=false.
Kernel source (redacted)
"""Kimi Delta Attention forward (chunk form) — custom Triton kernels for H100.
Chunk-parallel KDA forward, written from scratch:
Stage 1 (intra, one program per 64-token chunk per (b,h)):
- in-chunk cumulative sum of the per-channel log-decay g (log2 space)
- Aqk[c,j] = scale * q_c . (exp2(g_c - g_j) * k_j) (c >= j)
- Akk[c,j] = beta_c * k_c . (exp2(g_c - g_j) * k_j) (c > j)
- M = (I + Akk)^{-1} via a Neumann-doubling product of the nilpotent part
- w = M @ (beta * exp2(g) * k), u = M @ (beta * v)
- kg = k * exp2(g_last - g), qg = scale * q * exp2(g), dl = exp2(g_last)
Stage 2 (recurrent, one program per (b,h,v-block), sequential over chunks):
- v_new = u - w @ S ; store S (entering state, bf16) and v_new
- S = S * dl + kg^T @ v_new
Stage 3 (output, fully parallel over chunks):
- o = qg @ S + tril(Aqk) @ v_new
"""
from __future__ import annotations
import torch
import torch.nn as nn
import triton
import triton.language as tl
OP_TYPE = "linear_attention"
SUPPORTED_PRECISIONS = ["bf16"]
HARDWARE_REQUIRED = ["RTX_PRO_6000", "H100", "B200"]
RCP_LN2 = tl.constexpr(1.4426950408889634) # 1 / ln(2)
@triton.jit
def _kda_fwd_intra_kernel(
q, k, v, g, beta,
qg, Aqk, w, u, kg, dl,
scale,
T: tl.constexpr, H: tl.constexpr,
K: tl.constexpr, V: tl.constexpr,
BT: tl.constexpr,
):
i_t, i_bh = tl.program_id(0), tl.program_id(1)
i_b, i_h = i_bh // H, i_bh % H
bos = i_b * T
NT = T // BT
q += (bos * H + i_h) * K
k += (bos * H + i_h) * K
g += (bos * H + i_h) * K
v += (bos * H + i_h) * V
beta += bos * H + i_h
qg += (bos * H + i_h) * K
Aqk += (bos * H + i_h) * BT
w += (bos * H + i_h) * K
u += (bos * H + i_h) * V
kg += (bos * H + i_h) * K
dl += ((i_b * NT + i_t) * H + i_h) * K
row0 = i_t * BT
o_c = tl.arange(0, BT)
m_strict = o_c[:, None] > o_c[None, :]
m_incl = o_c[:, None] >= o_c[None, :]
eye = (o_c[:, None] == o_c[None, :]).to(tl.float32)
p_b = tl.make_block_ptr(beta, (T,), (H,), (row0,), (BT,), (0,))
b_beta = tl.load(p_b).to(tl.float32)
# ---- loop over K halves: cumsum + decay factors + A accumulation ----
# Per-channel rebasing to the mid row keeps exp2 args small; the base
# cancels inside every q/k dot product.
b_Aqk = tl.zeros([BT, BT], dtype=tl.float32)
b_Akk = tl.zeros([BT, BT], dtype=tl.float32)
xw0 = tl.zeros([BT, 64], dtype=tl.bfloat16)
xw1 = tl.zeros([BT, 64], dtype=tl.bfloat16)
o_k = tl.arange(0, 64)
for i_k in tl.static_range(K // 64):
p_g = tl.make_block_ptr(g, (T, K), (H * K, 1), (row0, i_k * 64), (BT, 64), (1, 0))
b_gc = tl.cumsum(tl.load(p_g).to(tl.float32) * RCP_LN2, 0)
base = tl.sum(tl.where((o_c == BT // 2 - 1)[:, None], b_gc, 0.0), 0)
gl = tl.sum(tl.where((o_c == BT - 1)[:, None], b_gc, 0.0), 0)
tl.store(dl + i_k * 64 + o_k, tl.exp2(gl))
p_k = tl.make_block_ptr(k, (T, K), (H * K, 1), (row0, i_k * 64), (BT, 64), (1, 0))
b_k = tl.load(p_k)
p_q = tl.make_block_ptr(q, (T, K), (H * K, 1), (row0, i_k * 64), (BT, 64), (1, 0))
b_q = tl.load(p_q)
b_e = tl.exp2(b_gc - base[None, :])
kd = (b_k * b_e).to(tl.bfloat16)
qd = (b_q * b_e * scale).to(tl.bfloat16)
b_qg = (b_q * (b_e * tl.exp2(base)[None, :]) * scale).to(tl.bfloat16)
p_qg = tl.make_block_ptr(qg, (T, K), (H * K, 1), (row0, i_k * 64), (BT, 64), (1, 0))
tl.store(p_qg, b_qg)
b_e = tl.exp2(base[None, :] - b_gc)
ks = (b_k * b_e).to(tl.bfloat16)
b_kg = (b_k * (b_e * tl.exp2(gl - base)[None, :])).to(tl.bfloat16)
p_kg = tl.make_block_ptr(kg, (T, K), (H * K, 1), (row0, i_k * 64), (BT, 64), (1, 0))
tl.store(p_kg, b_kg)
kst = tl.trans(ks)
b_Aqk = tl.dot(qd, kst, b_Aqk)
b_Akk = tl.dot(kd, kst, b_Akk)
b_xw = kd * (tl.exp2(base)[None, :] * b_beta[:, None]).to(tl.bfloat16)
if i_k == 0:
xw0 = b_xw
else:
xw1 = b_xw
p_aqk = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (row0, 0), (BT, BT), (1, 0))
tl.store(p_aqk, tl.where(m_incl, b_Aqk, 0.0).to(tl.bfloat16))
# ---- M = (I + B)^{-1}, B = strict lower tril of beta-scaled Akk ----
# With C = -B (nilpotent, C^64 = 0):
# M = sum_n C^n = (I+C)(I+C^2)(I+C^4)(I+C^8)(I+C^16)(I+C^32)
C = tl.where(m_strict, -b_Akk * b_beta[:, None], 0.0)
X = eye + C
P = tl.dot(C, C) # C^2
X = X + tl.dot(X, P)
P = tl.dot(P, P) # C^4
X = X + tl.dot(X, P)
P = tl.dot(P, P) # C^8
X = X + tl.dot(X, P)
P = tl.dot(P, P) # C^16
X = X + tl.dot(X, P)
P = tl.dot(P, P) # C^32
X = X + tl.dot(X, P)
Mb = X.to(tl.bfloat16)
# ---- w = M @ (beta*exp2(g)*k), u = M @ (beta*v) ----
b_w = tl.dot(Mb, xw0)
p_w = tl.make_block_ptr(w, (T, K), (H * K, 1), (row0, 0), (BT, 64), (1, 0))
tl.store(p_w, b_w.to(tl.bfloat16))
b_w = tl.dot(Mb, xw1)
p_w = tl.make_block_ptr(w, (T, K), (H * K, 1), (row0, 64), (BT, 64), (1, 0))
tl.store(p_w, b_w.to(tl.bfloat16))
p_v = tl.make_block_ptr(v, (T, V), (H * V, 1), (row0, 0), (BT, V), (1, 0))
xu = (tl.load(p_v) * b_beta[:, None].to(tl.bfloat16))
b_u = tl.dot(Mb, xu)
p_u = tl.make_block_ptr(u, (T, V), (H * V, 1), (row0, 0), (BT, V), (1, 0))
tl.store(p_u, b_u.to(tl.bfloat16))
@triton.jit
def _kda_fwd_h_kernel(
kg, u, w, dl,
h, v_new,
T: tl.constexpr, H: tl.constexpr,
K: tl.constexpr, V: tl.constexpr,
BT: tl.constexpr, BV: tl.constexpr,
):
i_v, i_bh = tl.program_id(0), tl.program_id(1)
i_b, i_h = i_bh // H, i_bh % H
bos = i_b * T
NT = T // BT
kg += (bos * H + i_h) * K
u += (bos * H + i_h) * V
w += (bos * H + i_h) * K
dl += (i_b * NT * H + i_h) * K
v_new += (bos * H + i_h) * V
h += (i_b * NT * H + i_h) * K * V
b_h1 = tl.zeros([64, BV], dtype=tl.float32)
b_h2 = tl.zeros([64, BV], dtype=tl.float32)
o_k = tl.arange(0, 64)
for i_t in range(NT):
# store entering state
p_h1 = tl.make_block_ptr(h + i_t * H * K * V, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
p_h2 = tl.make_block_ptr(h + i_t * H * K * V, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
b_h1b = b_h1.to(tl.bfloat16)
b_h2b = b_h2.to(tl.bfloat16)
tl.store(p_h1, b_h1b)
tl.store(p_h2, b_h2b)
# v_new = u - w @ S
p_w1 = tl.make_block_ptr(w, (T, K), (H * K, 1), (i_t * BT, 0), (BT, 64), (1, 0))
p_w2 = tl.make_block_ptr(w, (T, K), (H * K, 1), (i_t * BT, 64), (BT, 64), (1, 0))
b_vn = tl.dot(tl.load(p_w1), b_h1b) + tl.dot(tl.load(p_w2), b_h2b)
p_u = tl.make_block_ptr(u, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
b_vn = tl.load(p_u) - b_vn
p_vn = tl.make_block_ptr(v_new, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
tl.store(p_vn, b_vn.to(p_vn.dtype.element_ty))
# decay state to chunk end, add new writes
b_dl1 = tl.load(dl + i_t * H * K + o_k)
b_dl2 = tl.load(dl + i_t * H * K + 64 + o_k)
b_h1 *= b_dl1[:, None]
b_h2 *= b_dl2[:, None]
b_vnb = b_vn.to(tl.bfloat16)
p_kg1 = tl.make_block_ptr(kg, (K, T), (1, H * K), (0, i_t * BT), (64, BT), (0, 1))
p_kg2 = tl.make_block_ptr(kg, (K, T), (1, H * K), (64, i_t * BT), (64, BT), (0, 1))
b_h1 = tl.dot(tl.load(p_kg1), b_vnb, b_h1)
b_h2 = tl.dot(tl.load(p_kg2), b_vnb, b_h2)
@triton.jit
def _kda_fwd_o_kernel(
qg, Aqk, v_new, h,
o,
T: tl.constexpr, H: tl.constexpr,
K: tl.constexpr, V: tl.constexpr,
BT: tl.constexpr, BV: tl.constexpr,
):
i_v, i_t, i_bh = tl.program_id(0), tl.program_id(1), tl.program_id(2)
i_b, i_h = i_bh // H, i_bh % H
bos = i_b * T
NT = T // BT
qg += (bos * H + i_h) * K
Aqk += (bos * H + i_h) * BT
v_new += (bos * H + i_h) * V
o += (bos * H + i_h) * V
h += ((i_b * NT + i_t) * H + i_h) * K * V
b_o = tl.zeros([BT, BV], dtype=tl.float32)
for i_k in range(K // 64):
p_qg = tl.make_block_ptr(qg, (T, K), (H * K, 1), (i_t * BT, i_k * 64), (BT, 64), (1, 0))
b_qg = tl.load(p_qg)
p_h = tl.make_block_ptr(h, (K, V), (V, 1), (i_k * 64, i_v * BV), (64, BV), (1, 0))
b_h = tl.load(p_h)
b_o += tl.dot(b_qg, b_h)
m_s = tl.arange(0, BT)[:, None] >= tl.arange(0, BT)[None, :]
p_A = tl.make_block_ptr(Aqk, (T, BT), (H * BT, 1), (i_t * BT, 0), (BT, BT), (1, 0))
b_A = tl.load(p_A)
b_A = tl.where(m_s, b_A, 0.0).to(tl.bfloat16)
p_vn = tl.make_block_ptr(v_new, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
b_vn = tl.load(p_vn)
b_o += tl.dot(b_A, b_vn)
p_o = tl.make_block_ptr(o, (T, V), (H * V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
tl.store(p_o, b_o.to(p_o.dtype.element_ty))
def kda_chunk_fwd(
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
g: torch.Tensor,
beta: torch.Tensor,
scale: float,
chunk_size: int = 64,
) -> torch.Tensor:
B, T, H, K = q.shape
V = v.shape[-1]
BT = chunk_size
assert BT == 64 and K == 128 and V == 128 and T % BT == 0
NT = T // BT
qg = torch.empty(B, T, H, K, dtype=torch.bfloat16, device=q.device)
Aqk = torch.empty(B, T, H, BT, dtype=torch.bfloat16, device=q.device)
w = torch.empty(B, T, H, K, dtype=torch.bfloat16, device=q.device)
u = torch.empty(B, T, H, V, dtype=torch.bfloat16, device=q.device)
kg = torch.empty(B, T, H, K, dtype=torch.bfloat16, device=q.device)
dl = torch.empty(B, NT, H, K, dtype=torch.float32, device=q.device)
h = torch.empty(B, NT, H, K, V, dtype=torch.bfloat16, device=q.device)
v_new = torch.empty(B, T, H, V, dtype=torch.bfloat16, device=q.device)
o = torch.empty(B, T, H, V, dtype=torch.bfloat16, device=q.device)
_kda_fwd_intra_kernel[(NT, B * H)](
q, k, v, g, beta,
qg, Aqk, w, u, kg, dl,
scale,
T=T, H=H, K=K, V=V, BT=BT,
num_warps=4, num_stages=1,
)
BV_h = 32
_kda_fwd_h_kernel[(V // BV_h, B * H)](
kg, u, w, dl,
h, v_new,
T=T, H=H, K=K, V=V, BT=BT, BV=BV_h,
num_warps=4, num_stages=3,
)
BV_o = 64
_kda_fwd_o_kernel[(V // BV_o, NT, B * H)](
qg, Aqk, v_new, h,
o,
T=T, H=H, K=K, V=V, BT=BT, BV=BV_o,
num_warps=4, num_stages=3,
)
return o
class Model(nn.Module):
"""KDA forward (chunk form). No learned parameters; all inputs are activations."""
def __init__(self, B: int, T: int, H: int, K: int, V: int, chunk_size: int = 64):
super().__init__()
self.B, self.T, self.H, self.K, self.V = B, T, H, K, V
self.chunk_size = chunk_size
self.scale = float(K) ** -0.5
self.register_buffer("_dummy", torch.zeros(1), persistent=False)
def forward(
self,
q: torch.Tensor,
k: torch.Tensor,
v: torch.Tensor,
g: torch.Tensor,
beta: torch.Tensor,
) -> torch.Tensor:
return kda_chunk_fwd(q, k, v, g, beta, scale=self.scale, chunk_size=self.chunk_size)
# Module-level shape shims (overridden by check.py / benchmark.py per shape).
B = 2
T = 1024
H = 8
K = 128
V = 128
CHUNK_SIZE = 64
def get_inputs():
torch.manual_seed(0)
q = torch.randn(B, T, H, K, dtype=torch.bfloat16) * 0.1
k = torch.randn(B, T, H, K, dtype=torch.bfloat16) * 0.1
v = torch.randn(B, T, H, V, dtype=torch.bfloat16) * 0.1
g = (torch.randn(B, T, H, K, dtype=torch.float32) * 0.1 - 0.05)
beta = torch.sigmoid(torch.randn(B, T, H, dtype=torch.bfloat16))
return [q, k, v, g, beta]
def get_init_inputs():
return [B, T, H, K, V, CHUNK_SIZE]
20260720_210639_or-fable_anthropic_claude-fable-5_02_kda_cutlass