1# Copyright 2026 Huawei Technologies Co., Ltd
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ============================================================================
15# -*- coding: utf-8 -*-
16# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
17
18# pylint: disable=line-too-long,missing-public-type-hints,missing-public-docstring
19# pylint: disable=used-before-assignment,unsupported-binary-operation,unused-argument
20# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring
21
22from typing import Optional, Tuple
23
24import torch
25import triton
26import triton.language as tl
27
28from .utils import prepare_chunk_indices, prepare_chunk_offsets, get_autotune_config, get_npu_properties
29
30CUBE_CORE_NUM = get_npu_properties()['num_aicore']
31
32
33@triton.heuristics({
34 'USE_G': lambda args: args['g'] is not None,
35 'USE_GK': lambda args: args['gk'] is not None,
36 'USE_INITIAL_STATE': lambda args: args['h0'] is not None,
37 'STORE_FINAL_STATE': lambda args: args['ht'] is not None,
38 'SAVE_NEW_VALUE': lambda args: args['v_new'] is not None,
39 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
40})
41@triton.autotune(
42 configs=get_autotune_config(multibuffer_list=(False,)),
43 key=['H', 'K', 'V', 'BT'],
44)
45@triton.jit(do_not_specialize=['T'])
46def chunk_gated_delta_rule_fwd_kernel_h_blockdim64(
47 k,
48 v,
49 w,
50 v_new,
51 g,
52 gk,
53 h,
54 h0,
55 ht,
56 cu_seqlens,
57 chunk_offsets,
58 T,
59 H: tl.constexpr,
60 K: tl.constexpr,
61 V: tl.constexpr,
62 BT: tl.constexpr,
63 BV: tl.constexpr,
64 NT: tl.constexpr,
65 USE_G: tl.constexpr,
66 USE_GK: tl.constexpr,
67 USE_INITIAL_STATE: tl.constexpr,
68 STORE_FINAL_STATE: tl.constexpr,
69 SAVE_NEW_VALUE: tl.constexpr,
70 IS_VARLEN: tl.constexpr,
71):
72 T_all = T
73 NT_all = NT
74 i_v, i_nh = tl.program_id(0), tl.program_id(1)
75 i_n, i_h = i_nh // H, i_nh % H
76 if IS_VARLEN:
77 bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(cu_seqlens + i_n + 1).to(tl.int32)
78 T = eos - bos
79 NT = tl.cdiv(T, BT)
80 boh = tl.load(chunk_offsets + i_n).to(tl.int32)
81 else:
82 bos, eos = i_n * T, i_n * T + T
83 NT = tl.cdiv(T, BT)
84 boh = i_n * NT
85
86 # Initialize hidden states
87 b_h1 = tl.zeros([64, BV], dtype=tl.float32)
88 if K > 64:
89 b_h2 = tl.zeros([64, BV], dtype=tl.float32)
90 if K > 128:
91 b_h3 = tl.zeros([64, BV], dtype=tl.float32)
92 if K > 192:
93 b_h4 = tl.zeros([64, BV], dtype=tl.float32)
94
95 if IS_VARLEN:
96 v = v + (i_h * T_all + bos) * V
97 k = k + (i_h * T_all + bos) * K
98 w = w + (i_h * T_all + bos) * K
99 g = g + i_h * T_all + bos
100 h = h + (i_h * NT_all + boh) * K * V
101 if SAVE_NEW_VALUE:
102 v_new_base = v_new + (i_h * T_all + bos) * V
103 else:
104 v = v + (i_n * H + i_h) * T * V
105 k = k + (i_n * H + i_h) * T * K
106 w = w + (i_n * H + i_h) * T * K
107 g = g + (i_n * H + i_h) * T
108 h = h + (i_n * H + i_h) * NT * K * V
109 if SAVE_NEW_VALUE:
110 v_new_base = v_new + (i_n * H + i_h) * T * V
111
112 if USE_INITIAL_STATE:
113 h0_ptr = h0 + i_nh * K * V
114 if STORE_FINAL_STATE:
115 ht_ptr = ht + i_nh * K * V
116
117 # Load initial state
118 if USE_INITIAL_STATE:
119 p_h0_1 = tl.make_block_ptr(h0_ptr, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
120 b_h1 += tl.load(p_h0_1, boundary_check=(0, 1)).to(tl.float32)
121 if K > 64:
122 p_h0_2 = tl.make_block_ptr(h0_ptr, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
123 b_h2 += tl.load(p_h0_2, boundary_check=(0, 1)).to(tl.float32)
124 if K > 128:
125 p_h0_3 = tl.make_block_ptr(h0_ptr, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
126 b_h3 += tl.load(p_h0_3, boundary_check=(0, 1)).to(tl.float32)
127 if K > 192:
128 p_h0_4 = tl.make_block_ptr(h0_ptr, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
129 b_h4 += tl.load(p_h0_4, boundary_check=(0, 1)).to(tl.float32)
130
131 # Main recurrence over chunks
132 for i_t in range(NT):
133 # Store current hidden state h_t
134 p_h1 = tl.make_block_ptr(h + i_t * K * V, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
135 tl.store(p_h1, b_h1.to(p_h1.dtype.element_ty), boundary_check=(0, 1))
136 if K > 64:
137 p_h2 = tl.make_block_ptr(h + i_t * K * V, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
138 tl.store(p_h2, b_h2.to(p_h2.dtype.element_ty), boundary_check=(0, 1))
139 if K > 128:
140 p_h3 = tl.make_block_ptr(h + i_t * K * V, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
141 tl.store(p_h3, b_h3.to(p_h3.dtype.element_ty), boundary_check=(0, 1))
142 if K > 192:
143 p_h4 = tl.make_block_ptr(h + i_t * K * V, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
144 tl.store(p_h4, b_h4.to(p_h4.dtype.element_ty), boundary_check=(0, 1))
145
146 # Compute v_residual = v - w @ h
147 p_w = tl.make_block_ptr(w, (T, K), (K, 1), (i_t * BT, 0), (BT, 64), (1, 0))
148 b_w = tl.load(p_w, boundary_check=(0, 1))
149 b_v = tl.dot(b_w, b_h1.to(b_w.dtype))
150 if K > 64:
151 p_w = tl.make_block_ptr(w, (T, K), (K, 1), (i_t * BT, 64), (BT, 64), (1, 0))
152 b_w = tl.load(p_w, boundary_check=(0, 1))
153 b_v += tl.dot(b_w, b_h2.to(b_w.dtype))
154 if K > 128:
155 p_w = tl.make_block_ptr(w, (T, K), (K, 1), (i_t * BT, 128), (BT, 64), (1, 0))
156 b_w = tl.load(p_w, boundary_check=(0, 1))
157 b_v += tl.dot(b_w, b_h3.to(b_w.dtype))
158 if K > 192:
159 p_w = tl.make_block_ptr(w, (T, K), (K, 1), (i_t * BT, 192), (BT, 64), (1, 0))
160 b_w = tl.load(p_w, boundary_check=(0, 1))
161 b_v += tl.dot(b_w, b_h4.to(b_w.dtype))
162
163 p_v = tl.make_block_ptr(v, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
164 b_v = tl.load(p_v, boundary_check=(0, 1)) - b_v
165
166 if SAVE_NEW_VALUE:
167 p_v_new = tl.make_block_ptr(v_new_base, (T, V), (V, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
168 tl.store(p_v_new, b_v.to(p_v_new.dtype.element_ty), boundary_check=(0, 1))
169
170 last_idx = min((i_t + 1) * BT, T) - 1
171
172 # Apply output gate g
173 if USE_G:
174 m_t = (i_t * BT + tl.arange(0, BT)).to(tl.float32) < T
175 b_g_last = tl.load(g + last_idx)
176 p_g = tl.make_block_ptr(g, (T,), (1,), (i_t * BT,), (BT,), (0,))
177 b_g = tl.load(p_g, boundary_check=(0,))
178 b_v *= (m_t * tl.exp(b_g_last - b_g))[:, None]
179 b_g_last_exp = tl.exp(b_g_last)
180 b_h1 *= b_g_last_exp
181 if K > 64:
182 b_h2 *= b_g_last_exp
183 if K > 128:
184 b_h3 *= b_g_last_exp
185 if K > 192:
186 b_h4 *= b_g_last_exp
187
188 # Apply key gate gk
189 if USE_GK:
190 o_k1 = tl.arange(0, 64).to(tl.float32)
191 gk_base_ptr = gk + (i_n * H + i_h) * T * K
192 b_gk_last1 = tl.load(gk_base_ptr + last_idx * K + o_k1, mask=(o_k1 < K), other=0.)
193 b_h1 *= tl.exp(b_gk_last1)[:, None]
194 if K > 64:
195 o_k2 = 64 + o_k1
196 b_gk_last2 = tl.load(gk_base_ptr + last_idx * K + o_k2, mask=(o_k2 < K), other=0.)
197 b_h2 *= tl.exp(b_gk_last2)[:, None]
198 if K > 128:
199 o_k3 = 128 + o_k1
200 b_gk_last3 = tl.load(gk_base_ptr + last_idx * K + o_k3, mask=(o_k3 < K), other=0.)
201 b_h3 *= tl.exp(b_gk_last3)[:, None]
202 if K > 192:
203 o_k4 = 192 + o_k1
204 b_gk_last4 = tl.load(gk_base_ptr + last_idx * K + o_k4, mask=(o_k4 < K), other=0.)
205 b_h4 *= tl.exp(b_gk_last4)[:, None]
206
207 b_v = b_v.to(k.dtype.element_ty)
208
209 # Update hidden state: h += k @ v
210 p_k = tl.make_block_ptr(k, (K, T), (1, K), (0, i_t * BT), (64, BT), (0, 1))
211 b_k = tl.load(p_k, boundary_check=(0, 1))
212 if USE_GK:
213 p_gk = tl.make_block_ptr(gk_base_ptr, (K, T), (1, K), (0, i_t * BT), (64, BT), (0, 1))
214 b_k = (b_k * tl.exp(b_gk_last1[:, None] - tl.load(p_gk, boundary_check=(0, 1)))).to(b_k.dtype)
215 b_h1 += tl.dot(b_k, b_v)
216
217 if K > 64:
218 p_k = tl.make_block_ptr(k, (K, T), (1, K), (64, i_t * BT), (64, BT), (0, 1))
219 b_k = tl.load(p_k, boundary_check=(0, 1))
220 if USE_GK:
221 p_gk = tl.make_block_ptr(gk_base_ptr, (K, T), (1, K), (64, i_t * BT), (64, BT), (0, 1))
222 b_k = (b_k * tl.exp(b_gk_last2[:, None] - tl.load(p_gk, boundary_check=(0, 1)))).to(b_k.dtype)
223 b_h2 += tl.dot(b_k, b_v)
224
225 if K > 128:
226 p_k = tl.make_block_ptr(k, (K, T), (1, K), (128, i_t * BT), (64, BT), (0, 1))
227 b_k = tl.load(p_k, boundary_check=(0, 1))
228 if USE_GK:
229 p_gk = tl.make_block_ptr(gk_base_ptr, (K, T), (1, K), (128, i_t * BT), (64, BT), (0, 1))
230 b_k = (b_k * tl.exp(b_gk_last3[:, None] - tl.load(p_gk, boundary_check=(0, 1)))).to(b_k.dtype)
231 b_h3 += tl.dot(b_k, b_v)
232
233 if K > 192:
234 p_k = tl.make_block_ptr(k, (K, T), (1, K), (192, i_t * BT), (64, BT), (0, 1))
235 b_k = tl.load(p_k, boundary_check=(0, 1))
236 if USE_GK:
237 p_gk = tl.make_block_ptr(gk_base_ptr, (K, T), (1, K), (192, i_t * BT), (64, BT), (0, 1))
238 b_k = (b_k * tl.exp(b_gk_last4[:, None] - tl.load(p_gk, boundary_check=(0, 1)))).to(b_k.dtype)
239 b_h4 += tl.dot(b_k, b_v)
240
241 # Store final state
242 if STORE_FINAL_STATE:
243 p_ht = tl.make_block_ptr(ht_ptr, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
244 tl.store(p_ht, b_h1.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
245 if K > 64:
246 p_ht = tl.make_block_ptr(ht_ptr, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
247 tl.store(p_ht, b_h2.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
248 if K > 128:
249 p_ht = tl.make_block_ptr(ht_ptr, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
250 tl.store(p_ht, b_h3.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
251 if K > 192:
252 p_ht = tl.make_block_ptr(ht_ptr, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
253 tl.store(p_ht, b_h4.to(p_ht.dtype.element_ty), boundary_check=(0, 1))
254
255
256def chunk_gated_delta_rule_fwd_h(
257 k: torch.Tensor,
258 w: torch.Tensor,
259 u: torch.Tensor,
260 g: Optional[torch.Tensor] = None,
261 gk: Optional[torch.Tensor] = None,
262 initial_state: Optional[torch.Tensor] = None,
263 output_final_state: bool = False,
264 chunk_size: int = 64, # default:64
265 save_new_value: bool = True,
266 cu_seqlens: Optional[torch.LongTensor] = None,
267) -> Tuple[torch.Tensor, torch.Tensor, Optional[torch.Tensor]]:
268 B, T, H, K, V = *k.shape, u.shape[-1]
269 BT = chunk_size
270
271 chunk_indices = prepare_chunk_indices(cu_seqlens, chunk_size) if cu_seqlens is not None else None
272 # N: the actual number of sequences in the batch with either equal or variable lengths
273 if cu_seqlens is None:
274 N, NT, chunk_offsets = B, triton.cdiv(T, BT), None
275 else:
276 N, NT, chunk_offsets = len(cu_seqlens) - 1, len(chunk_indices), prepare_chunk_offsets(cu_seqlens, BT)
277 assert K <= 256, "current kernel does not support head dimension larger than 256."
278
279 h = k.new_empty(B, NT, H, K, V).permute(0, 2, 1, 3, 4).contiguous()
280 final_state = k.new_empty(N, H, K, V, dtype=torch.float32) if output_final_state else None
281
282 BV = 128
283
284 v_new = torch.empty_like(u).permute(0, 2, 1, 3).contiguous() if save_new_value else None
285 k = k.permute(0, 2, 1, 3).contiguous()
286 w = w.permute(0, 2, 1, 3).contiguous()
287 u = u.permute(0, 2, 1, 3).contiguous()
288 g = g.permute(0, 2, 1).contiguous()
289 chunk_gated_delta_rule_fwd_kernel_h_blockdim64[(triton.cdiv(V, BV), N * H)](
290 k=k,
291 v=u,
292 w=w,
293 v_new=v_new,
294 g=g,
295 gk=gk,
296 h=h,
297 h0=initial_state,
298 ht=final_state,
299 cu_seqlens=cu_seqlens,
300 chunk_offsets=chunk_offsets,
301 T=T,
302 H=H,
303 K=K,
304 V=V,
305 BT=BT,
306 BV=BV,
307 NT=NT,
308 )
309 h = h.permute(0, 2, 1, 3, 4).contiguous()
310 v_new = v_new.permute(0, 2, 1, 3).contiguous()
311 return h, v_new, final_state
312
313
314@triton.heuristics({
315 'USE_G': lambda args: args['g'] is not None,
316 'USE_GK': lambda args: args['gk'] is not None,
317 'USE_INITIAL_STATE': lambda args: args['dh0'] is not None,
318 'USE_FINAL_STATE_GRADIENT': lambda args: args['dht'] is not None,
319 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
320})
321@triton.autotune(
322 configs=get_autotune_config(multibuffer_list=(True, False)),
323 key=['H', 'K', 'V', 'BT', 'BV', 'USE_G', 'IS_VARLEN'],
324)
325@triton.jit(do_not_specialize=['T'])
326def chunk_gated_delta_rule_bwd_kernel_dhu_blockdim64(
327 q,
328 k,
329 w,
330 g,
331 gk,
332 dht,
333 dh0,
334 do,
335 dh,
336 dv,
337 dv2,
338 cu_seqlens,
339 chunk_offsets,
340 scale,
341 T,
342 H: tl.constexpr,
343 K: tl.constexpr,
344 V: tl.constexpr,
345 BT: tl.constexpr,
346 BV: tl.constexpr,
347 USE_G: tl.constexpr,
348 USE_GK: tl.constexpr,
349 USE_INITIAL_STATE: tl.constexpr,
350 USE_FINAL_STATE_GRADIENT: tl.constexpr,
351 IS_VARLEN: tl.constexpr,
352):
353 T_all = T
354 i_v, i_nh = tl.program_id(0), tl.program_id(1)
355 i_n, i_h = i_nh // H, i_nh % H
356 if IS_VARLEN:
357 bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(cu_seqlens + i_n + 1).to(tl.int32)
358 T = eos - bos
359 NT = tl.cdiv(T, BT)
360 boh = tl.load(chunk_offsets + i_n).to(tl.int32)
361 else:
362 bos, eos = i_n * T, i_n * T + T
363 NT = tl.cdiv(T, BT)
364 boh = i_n * NT
365
366 b_dh1 = tl.zeros([64, BV], dtype=tl.float32)
367 if K > 64:
368 b_dh2 = tl.zeros([64, BV], dtype=tl.float32)
369 if K > 128:
370 b_dh3 = tl.zeros([64, BV], dtype=tl.float32)
371 if K > 192:
372 b_dh4 = tl.zeros([64, BV], dtype=tl.float32)
373
374 q += (bos * H + i_h) * K
375 k += (bos * H + i_h) * K
376 w += (bos * H + i_h) * K
377 do += (bos * H + i_h) * V
378 dv += (bos * H + i_h) * V
379 dv2 += (bos * H + i_h) * V
380 dh += (boh * H + i_h) * K * V
381 if USE_GK:
382 gk += (bos * H + i_h) * K
383
384 if USE_INITIAL_STATE:
385 dh0 += i_nh * K * V
386 if USE_FINAL_STATE_GRADIENT:
387 dht += i_nh * K * V
388
389 stride_v = H * V
390 stride_h = H * K * V
391 stride_k = H * K
392
393 if USE_FINAL_STATE_GRADIENT:
394 p_dht1 = tl.make_block_ptr(dht, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
395 b_dh1 += tl.load(p_dht1, boundary_check=(0, 1))
396 if K > 64:
397 p_dht2 = tl.make_block_ptr(dht, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
398 b_dh2 += tl.load(p_dht2, boundary_check=(0, 1))
399 if K > 128:
400 p_dht3 = tl.make_block_ptr(dht, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
401 b_dh3 += tl.load(p_dht3, boundary_check=(0, 1))
402 if K > 192:
403 p_dht4 = tl.make_block_ptr(dht, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
404 b_dh4 += tl.load(p_dht4, boundary_check=(0, 1))
405
406 for i_t in range(NT - 1, -1, -1):
407 p_dh1 = tl.make_block_ptr(dh + i_t * stride_h, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
408 tl.store(p_dh1, b_dh1.to(p_dh1.dtype.element_ty), boundary_check=(0, 1))
409 if K > 64:
410 p_dh2 = tl.make_block_ptr(dh + i_t * stride_h, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
411 tl.store(p_dh2, b_dh2.to(p_dh2.dtype.element_ty), boundary_check=(0, 1))
412 if K > 128:
413 p_dh3 = tl.make_block_ptr(dh + i_t * stride_h, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
414 tl.store(p_dh3, b_dh3.to(p_dh3.dtype.element_ty), boundary_check=(0, 1))
415 if K > 192:
416 p_dh4 = tl.make_block_ptr(dh + i_t * stride_h, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
417 tl.store(p_dh4, b_dh4.to(p_dh4.dtype.element_ty), boundary_check=(0, 1))
418
419 last_idx = min((i_t + 1) * BT, T) - 1
420 if USE_G:
421 if IS_VARLEN:
422 bos_g = i_h * T_all + bos
423 else:
424 bos_g = (i_n * H + i_h) * T_all
425 bg_last = tl.load(g + bos_g + last_idx)
426 bg_last_exp = tl.exp(bg_last)
427 p_g = tl.make_block_ptr(base=g + bos_g, shape=(T,), strides=(1,), offsets=(i_t * BT,), block_shape=(BT,), order=(0,))
428 b_g = tl.load(p_g, boundary_check=(0,))
429 b_g_exp = tl.exp(b_g)
430
431 p_dv = tl.make_block_ptr(dv, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
432 p_dv2 = tl.make_block_ptr(dv2, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
433 p_do = tl.make_block_ptr(do, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
434
435 b_do = tl.load(p_do, boundary_check=(0, 1))
436
437 # Update dv
438 p_k = tl.make_block_ptr(k, (T, K), (stride_k, 1), (i_t * BT, 0), (BT, 64), (1, 0))
439 b_k = tl.load(p_k, boundary_check=(0, 1))
440 if USE_GK:
441 o_k1 = tl.arange(0, 64)
442 b_gk_last1 = tl.load(gk + last_idx * H * K + o_k1, mask=(o_k1 < K), other=0.)
443 b_dv = tl.dot(b_k, b_dh1.to(b_k.dtype))
444
445 if K > 64:
446 p_k = tl.make_block_ptr(k, (T, K), (stride_k, 1), (i_t * BT, 64), (BT, 64), (1, 0))
447 b_k = tl.load(p_k, boundary_check=(0, 1))
448 if USE_GK:
449 o_k2 = 64 + o_k1
450 b_gk_last2 = tl.load(gk + last_idx * H * K + o_k2, mask=(o_k2 < K), other=0.)
451 b_dv += tl.dot(b_k, b_dh2.to(b_k.dtype))
452
453 if K > 128:
454 p_k = tl.make_block_ptr(k, (T, K), (stride_k, 1), (i_t * BT, 128), (BT, 64), (1, 0))
455 b_k = tl.load(p_k, boundary_check=(0, 1))
456 if USE_GK:
457 o_k3 = 128 + o_k1
458 b_gk_last3 = tl.load(gk + last_idx * H * K + o_k3, mask=(o_k3 < K), other=0.)
459 b_dv += tl.dot(b_k, b_dh3.to(b_k.dtype))
460
461 if K > 192:
462 p_k = tl.make_block_ptr(k, (T, K), (stride_k, 1), (i_t * BT, 192), (BT, 64), (1, 0))
463 b_k = tl.load(p_k, boundary_check=(0, 1))
464 if USE_GK:
465 o_k4 = 192 + o_k1
466 b_gk_last4 = tl.load(gk + last_idx * H * K + o_k4, mask=(o_k4 < K), other=0.)
467 b_dv += tl.dot(b_k, b_dh4.to(b_k.dtype))
468
469 if USE_G:
470 m_t = (i_t * BT + tl.arange(0, BT)).to(tl.float32) < T
471 b_dv *= (m_t * tl.exp(bg_last - b_g))[:, None]
472 b_dv += tl.load(p_dv, boundary_check=(0, 1))
473
474 tl.store(p_dv2, b_dv.to(p_dv.dtype.element_ty), boundary_check=(0, 1))
475 # Update dh
476 p_w = tl.make_block_ptr(w, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1))
477 p_q = tl.make_block_ptr(q, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1))
478 b_w = tl.load(p_w, boundary_check=(0, 1))
479 b_q = tl.load(p_q, boundary_check=(0, 1))
480 if USE_G:
481 b_dh1 *= bg_last_exp
482 b_q = b_q * b_g_exp[None, :]
483 if USE_GK:
484 b_dh1 *= tl.exp(b_gk_last1[:, None])
485 b_dh1 += tl.dot(b_q.to(b_q.dtype), b_do.to(b_q.dtype)) * scale - tl.dot(b_w, b_dv.to(b_w.dtype))
486 if K > 64:
487 p_q = tl.make_block_ptr(q, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1))
488 p_w = tl.make_block_ptr(w, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1))
489 b_q = tl.load(p_q, boundary_check=(0, 1))
490 b_w = tl.load(p_w, boundary_check=(0, 1))
491 if USE_G:
492 b_dh2 *= bg_last_exp
493 b_q = b_q * b_g_exp[None, :]
494 if USE_GK:
495 b_dh2 *= tl.exp(b_gk_last2[:, None])
496 b_dh2 += tl.dot(b_q.to(b_q.dtype), b_do.to(b_q.dtype)) * scale - tl.dot(b_w, b_dv.to(b_w.dtype))
497 if K > 128:
498 p_q = tl.make_block_ptr(q, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1))
499 p_w = tl.make_block_ptr(w, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1))
500 b_q = tl.load(p_q, boundary_check=(0, 1))
501 b_w = tl.load(p_w, boundary_check=(0, 1))
502 if USE_G:
503 b_dh3 *= bg_last_exp
504 b_q = b_q * b_g_exp[None, :]
505 if USE_GK:
506 b_dh3 *= tl.exp(b_gk_last3[:, None])
507 b_dh3 += tl.dot(b_q.to(b_q.dtype), b_do.to(b_q.dtype)) * scale - tl.dot(b_w, b_dv.to(b_w.dtype))
508 if K > 192:
509 p_q = tl.make_block_ptr(q, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1))
510 p_w = tl.make_block_ptr(w, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1))
511 b_q = tl.load(p_q, boundary_check=(0, 1))
512 b_w = tl.load(p_w, boundary_check=(0, 1))
513 if USE_G:
514 b_dh4 *= bg_last_exp
515 b_q = b_q * b_g_exp[None, :]
516 if USE_GK:
517 b_dh4 *= tl.exp(b_gk_last4[:, None])
518 b_dh4 += tl.dot(b_q.to(b_q.dtype), b_do.to(b_q.dtype)) * scale - tl.dot(b_w, b_dv.to(b_w.dtype))
519
520 if USE_INITIAL_STATE:
521 p_dh0 = tl.make_block_ptr(dh0, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
522 tl.store(p_dh0, b_dh1.to(p_dh0.dtype.element_ty), boundary_check=(0, 1))
523 if K > 64:
524 p_dh1 = tl.make_block_ptr(dh0, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
525 tl.store(p_dh1, b_dh2.to(p_dh1.dtype.element_ty), boundary_check=(0, 1))
526 if K > 128:
527 p_dh2 = tl.make_block_ptr(dh0, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
528 tl.store(p_dh2, b_dh3.to(p_dh2.dtype.element_ty), boundary_check=(0, 1))
529 if K > 192:
530 p_dh3 = tl.make_block_ptr(dh0, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
531 tl.store(p_dh3, b_dh4.to(p_dh3.dtype.element_ty), boundary_check=(0, 1))
532
533
534def chunk_gated_delta_rule_bwd_dhu(
535 q: torch.Tensor,
536 k: torch.Tensor,
537 w: torch.Tensor,
538 do: torch.Tensor,
539 dv: torch.Tensor,
540 g: torch.Tensor | None = None,
541 gk: torch.Tensor | None = None,
542 h0: torch.Tensor | None = None,
543 dht: torch.Tensor | None = None,
544 scale: float | None = None,
545 cu_seqlens: torch.LongTensor | None = None,
546 chunk_size: int = 64, # SY: remove this argument and force chunk size 64?
547 chunk_indices: torch.LongTensor | None = None,
548 use_exp2: bool = False,
549) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
550 B, T, H, K, V = *q.shape, do.shape[-1]
551 # N: the actual number of sequences in the batch with either equal or variable lengths
552 BT = 64
553 assert K <= 256, "current kernel does not support head dimension being larger than 256."
554
555 if chunk_indices is None and cu_seqlens is not None:
556 chunk_indices = prepare_chunk_indices(cu_seqlens, chunk_size)
557 if cu_seqlens is None:
558 N, NT, chunk_offsets = B, triton.cdiv(T, BT), None
559 else:
560 N, NT, chunk_offsets = len(cu_seqlens) - 1, len(chunk_indices), prepare_chunk_offsets(cu_seqlens, BT)
561
562 dh = q.new_empty(B, NT, H, K, V)
563 dh0 = torch.empty_like(h0, dtype=torch.float32) if h0 is not None else None
564 dv2 = torch.empty_like(dv)
565
566 BV = 128
567
568 g = g.permute(0, 2, 1).contiguous()
569
570 chunk_gated_delta_rule_bwd_kernel_dhu_blockdim64[(triton.cdiv(V, BV), N * H)](
571 q=q,
572 k=k,
573 w=w,
574 g=g,
575 gk=gk,
576 dht=dht,
577 dh0=dh0,
578 do=do,
579 dh=dh,
580 dv=dv,
581 dv2=dv2,
582 cu_seqlens=cu_seqlens,
583 chunk_offsets=chunk_offsets,
584 scale=scale,
585 T=T,
586 H=H,
587 K=K,
588 V=V,
589 BT=BT,
590 BV=BV,
591 )
592 return dh, dh0, dv2