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# Copyright (c) 2023-2025, Songlin Yang, Yu Zhang
16
17# pylint: disable=line-too-long,missing-public-type-hints,missing-public-docstring
18# pylint: disable=used-before-assignment,unsupported-binary-operation,unused-argument
19# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring
20# pylint: disable=forbidden-backend-import
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 if K > 256:
278 raise ValueError("current kernel does not support head dimension larger than 256.")
279
280 h = k.new_empty(B, NT, H, K, V).permute(0, 2, 1, 3, 4).contiguous()
281 final_state = k.new_empty(N, H, K, V, dtype=torch.float32) if output_final_state else None
282
283 BV = 128
284
285 v_new = torch.empty_like(u).permute(0, 2, 1, 3).contiguous() if save_new_value else None
286 k = k.permute(0, 2, 1, 3).contiguous()
287 w = w.permute(0, 2, 1, 3).contiguous()
288 u = u.permute(0, 2, 1, 3).contiguous()
289 g = g.permute(0, 2, 1).contiguous()
290 chunk_gated_delta_rule_fwd_kernel_h_blockdim64[(triton.cdiv(V, BV), N * H)](
291 k=k,
292 v=u,
293 w=w,
294 v_new=v_new,
295 g=g,
296 gk=gk,
297 h=h,
298 h0=initial_state,
299 ht=final_state,
300 cu_seqlens=cu_seqlens,
301 chunk_offsets=chunk_offsets,
302 T=T,
303 H=H,
304 K=K,
305 V=V,
306 BT=BT,
307 BV=BV,
308 NT=NT,
309 )
310 h = h.permute(0, 2, 1, 3, 4).contiguous()
311 v_new = v_new.permute(0, 2, 1, 3).contiguous()
312 return h, v_new, final_state
313
314
315@triton.heuristics({
316 'USE_G': lambda args: args['g'] is not None,
317 'USE_GK': lambda args: args['gk'] is not None,
318 'USE_INITIAL_STATE': lambda args: args['dh0'] is not None,
319 'USE_FINAL_STATE_GRADIENT': lambda args: args['dht'] is not None,
320 'IS_VARLEN': lambda args: args['cu_seqlens'] is not None,
321})
322@triton.autotune(
323 configs=get_autotune_config(multibuffer_list=(True, False)),
324 key=['H', 'K', 'V', 'BT', 'BV', 'USE_G', 'IS_VARLEN'],
325)
326@triton.jit(do_not_specialize=['T'])
327def chunk_gated_delta_rule_bwd_kernel_dhu_blockdim64(
328 q,
329 k,
330 w,
331 g,
332 gk,
333 dht,
334 dh0,
335 do,
336 dh,
337 dv,
338 dv2,
339 cu_seqlens,
340 chunk_offsets,
341 scale,
342 T,
343 H: tl.constexpr,
344 K: tl.constexpr,
345 V: tl.constexpr,
346 BT: tl.constexpr,
347 BV: tl.constexpr,
348 USE_G: tl.constexpr,
349 USE_GK: tl.constexpr,
350 USE_INITIAL_STATE: tl.constexpr,
351 USE_FINAL_STATE_GRADIENT: tl.constexpr,
352 IS_VARLEN: tl.constexpr,
353):
354 T_all = T
355 i_v, i_nh = tl.program_id(0), tl.program_id(1)
356 i_n, i_h = i_nh // H, i_nh % H
357 if IS_VARLEN:
358 bos, eos = tl.load(cu_seqlens + i_n).to(tl.int32), tl.load(cu_seqlens + i_n + 1).to(tl.int32)
359 T = eos - bos
360 NT = tl.cdiv(T, BT)
361 boh = tl.load(chunk_offsets + i_n).to(tl.int32)
362 else:
363 bos, eos = i_n * T, i_n * T + T
364 NT = tl.cdiv(T, BT)
365 boh = i_n * NT
366
367 b_dh1 = tl.zeros([64, BV], dtype=tl.float32)
368 if K > 64:
369 b_dh2 = tl.zeros([64, BV], dtype=tl.float32)
370 if K > 128:
371 b_dh3 = tl.zeros([64, BV], dtype=tl.float32)
372 if K > 192:
373 b_dh4 = tl.zeros([64, BV], dtype=tl.float32)
374
375 q += (bos * H + i_h) * K
376 k += (bos * H + i_h) * K
377 w += (bos * H + i_h) * K
378 do += (bos * H + i_h) * V
379 dv += (bos * H + i_h) * V
380 dv2 += (bos * H + i_h) * V
381 dh += (boh * H + i_h) * K * V
382 if USE_GK:
383 gk += (bos * H + i_h) * K
384
385 if USE_INITIAL_STATE:
386 dh0 += i_nh * K * V
387 if USE_FINAL_STATE_GRADIENT:
388 dht += i_nh * K * V
389
390 stride_v = H * V
391 stride_h = H * K * V
392 stride_k = H * K
393
394 if USE_FINAL_STATE_GRADIENT:
395 p_dht1 = tl.make_block_ptr(dht, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
396 b_dh1 += tl.load(p_dht1, boundary_check=(0, 1))
397 if K > 64:
398 p_dht2 = tl.make_block_ptr(dht, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
399 b_dh2 += tl.load(p_dht2, boundary_check=(0, 1))
400 if K > 128:
401 p_dht3 = tl.make_block_ptr(dht, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
402 b_dh3 += tl.load(p_dht3, boundary_check=(0, 1))
403 if K > 192:
404 p_dht4 = tl.make_block_ptr(dht, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
405 b_dh4 += tl.load(p_dht4, boundary_check=(0, 1))
406
407 for i_t in range(NT - 1, -1, -1):
408 p_dh1 = tl.make_block_ptr(dh + i_t * stride_h, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
409 tl.store(p_dh1, b_dh1.to(p_dh1.dtype.element_ty), boundary_check=(0, 1))
410 if K > 64:
411 p_dh2 = tl.make_block_ptr(dh + i_t * stride_h, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
412 tl.store(p_dh2, b_dh2.to(p_dh2.dtype.element_ty), boundary_check=(0, 1))
413 if K > 128:
414 p_dh3 = tl.make_block_ptr(dh + i_t * stride_h, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
415 tl.store(p_dh3, b_dh3.to(p_dh3.dtype.element_ty), boundary_check=(0, 1))
416 if K > 192:
417 p_dh4 = tl.make_block_ptr(dh + i_t * stride_h, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
418 tl.store(p_dh4, b_dh4.to(p_dh4.dtype.element_ty), boundary_check=(0, 1))
419
420 last_idx = min((i_t + 1) * BT, T) - 1
421 if USE_G:
422 if IS_VARLEN:
423 bos_g = i_h * T_all + bos
424 else:
425 bos_g = (i_n * H + i_h) * T_all
426 bg_last = tl.load(g + bos_g + last_idx)
427 bg_last_exp = tl.exp(bg_last)
428 p_g = tl.make_block_ptr(base=g + bos_g, shape=(T,), strides=(1,), offsets=(i_t * BT,), block_shape=(BT,), order=(0,))
429 b_g = tl.load(p_g, boundary_check=(0,))
430 b_g_exp = tl.exp(b_g)
431
432 p_dv = tl.make_block_ptr(dv, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
433 p_dv2 = tl.make_block_ptr(dv2, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
434 p_do = tl.make_block_ptr(do, (T, V), (stride_v, 1), (i_t * BT, i_v * BV), (BT, BV), (1, 0))
435
436 b_do = tl.load(p_do, boundary_check=(0, 1))
437
438 # Update dv
439 p_k = tl.make_block_ptr(k, (T, K), (stride_k, 1), (i_t * BT, 0), (BT, 64), (1, 0))
440 b_k = tl.load(p_k, boundary_check=(0, 1))
441 if USE_GK:
442 o_k1 = tl.arange(0, 64)
443 b_gk_last1 = tl.load(gk + last_idx * H * K + o_k1, mask=(o_k1 < K), other=0.)
444 b_dv = tl.dot(b_k, b_dh1.to(b_k.dtype))
445
446 if K > 64:
447 p_k = tl.make_block_ptr(k, (T, K), (stride_k, 1), (i_t * BT, 64), (BT, 64), (1, 0))
448 b_k = tl.load(p_k, boundary_check=(0, 1))
449 if USE_GK:
450 o_k2 = 64 + o_k1
451 b_gk_last2 = tl.load(gk + last_idx * H * K + o_k2, mask=(o_k2 < K), other=0.)
452 b_dv += tl.dot(b_k, b_dh2.to(b_k.dtype))
453
454 if K > 128:
455 p_k = tl.make_block_ptr(k, (T, K), (stride_k, 1), (i_t * BT, 128), (BT, 64), (1, 0))
456 b_k = tl.load(p_k, boundary_check=(0, 1))
457 if USE_GK:
458 o_k3 = 128 + o_k1
459 b_gk_last3 = tl.load(gk + last_idx * H * K + o_k3, mask=(o_k3 < K), other=0.)
460 b_dv += tl.dot(b_k, b_dh3.to(b_k.dtype))
461
462 if K > 192:
463 p_k = tl.make_block_ptr(k, (T, K), (stride_k, 1), (i_t * BT, 192), (BT, 64), (1, 0))
464 b_k = tl.load(p_k, boundary_check=(0, 1))
465 if USE_GK:
466 o_k4 = 192 + o_k1
467 b_gk_last4 = tl.load(gk + last_idx * H * K + o_k4, mask=(o_k4 < K), other=0.)
468 b_dv += tl.dot(b_k, b_dh4.to(b_k.dtype))
469
470 if USE_G:
471 m_t = (i_t * BT + tl.arange(0, BT)).to(tl.float32) < T
472 b_dv *= (m_t * tl.exp(bg_last - b_g))[:, None]
473 b_dv += tl.load(p_dv, boundary_check=(0, 1))
474
475 tl.store(p_dv2, b_dv.to(p_dv.dtype.element_ty), boundary_check=(0, 1))
476 # Update dh
477 p_w = tl.make_block_ptr(w, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1))
478 p_q = tl.make_block_ptr(q, (K, T), (1, stride_k), (0, i_t * BT), (64, BT), (0, 1))
479 b_w = tl.load(p_w, boundary_check=(0, 1))
480 b_q = tl.load(p_q, boundary_check=(0, 1))
481 if USE_G:
482 b_dh1 *= bg_last_exp
483 b_q = b_q * b_g_exp[None, :]
484 if USE_GK:
485 b_dh1 *= tl.exp(b_gk_last1[:, None])
486 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))
487 if K > 64:
488 p_q = tl.make_block_ptr(q, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1))
489 p_w = tl.make_block_ptr(w, (K, T), (1, stride_k), (64, i_t * BT), (64, BT), (0, 1))
490 b_q = tl.load(p_q, boundary_check=(0, 1))
491 b_w = tl.load(p_w, boundary_check=(0, 1))
492 if USE_G:
493 b_dh2 *= bg_last_exp
494 b_q = b_q * b_g_exp[None, :]
495 if USE_GK:
496 b_dh2 *= tl.exp(b_gk_last2[:, None])
497 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))
498 if K > 128:
499 p_q = tl.make_block_ptr(q, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1))
500 p_w = tl.make_block_ptr(w, (K, T), (1, stride_k), (128, i_t * BT), (64, BT), (0, 1))
501 b_q = tl.load(p_q, boundary_check=(0, 1))
502 b_w = tl.load(p_w, boundary_check=(0, 1))
503 if USE_G:
504 b_dh3 *= bg_last_exp
505 b_q = b_q * b_g_exp[None, :]
506 if USE_GK:
507 b_dh3 *= tl.exp(b_gk_last3[:, None])
508 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))
509 if K > 192:
510 p_q = tl.make_block_ptr(q, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1))
511 p_w = tl.make_block_ptr(w, (K, T), (1, stride_k), (192, i_t * BT), (64, BT), (0, 1))
512 b_q = tl.load(p_q, boundary_check=(0, 1))
513 b_w = tl.load(p_w, boundary_check=(0, 1))
514 if USE_G:
515 b_dh4 *= bg_last_exp
516 b_q = b_q * b_g_exp[None, :]
517 if USE_GK:
518 b_dh4 *= tl.exp(b_gk_last4[:, None])
519 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))
520
521 if USE_INITIAL_STATE:
522 p_dh0 = tl.make_block_ptr(dh0, (K, V), (V, 1), (0, i_v * BV), (64, BV), (1, 0))
523 tl.store(p_dh0, b_dh1.to(p_dh0.dtype.element_ty), boundary_check=(0, 1))
524 if K > 64:
525 p_dh1 = tl.make_block_ptr(dh0, (K, V), (V, 1), (64, i_v * BV), (64, BV), (1, 0))
526 tl.store(p_dh1, b_dh2.to(p_dh1.dtype.element_ty), boundary_check=(0, 1))
527 if K > 128:
528 p_dh2 = tl.make_block_ptr(dh0, (K, V), (V, 1), (128, i_v * BV), (64, BV), (1, 0))
529 tl.store(p_dh2, b_dh3.to(p_dh2.dtype.element_ty), boundary_check=(0, 1))
530 if K > 192:
531 p_dh3 = tl.make_block_ptr(dh0, (K, V), (V, 1), (192, i_v * BV), (64, BV), (1, 0))
532 tl.store(p_dh3, b_dh4.to(p_dh3.dtype.element_ty), boundary_check=(0, 1))
533
534
535def chunk_gated_delta_rule_bwd_dhu(
536 q: torch.Tensor,
537 k: torch.Tensor,
538 w: torch.Tensor,
539 do: torch.Tensor,
540 dv: torch.Tensor,
541 g: torch.Tensor | None = None,
542 gk: torch.Tensor | None = None,
543 h0: torch.Tensor | None = None,
544 dht: torch.Tensor | None = None,
545 scale: float | None = None,
546 cu_seqlens: torch.LongTensor | None = None,
547 chunk_size: int = 64, # SY: remove this argument and force chunk size 64?
548 chunk_indices: torch.LongTensor | None = None,
549 use_exp2: bool = False,
550) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
551 B, T, H, K, V = *q.shape, do.shape[-1]
552 # N: the actual number of sequences in the batch with either equal or variable lengths
553 BT = 64
554 if K > 256:
555 raise ValueError("current kernel does not support head dimension being larger than 256.")
556
557 if chunk_indices is None and cu_seqlens is not None:
558 chunk_indices = prepare_chunk_indices(cu_seqlens, chunk_size)
559 if cu_seqlens is None:
560 N, NT, chunk_offsets = B, triton.cdiv(T, BT), None
561 else:
562 N, NT, chunk_offsets = len(cu_seqlens) - 1, len(chunk_indices), prepare_chunk_offsets(cu_seqlens, BT)
563
564 dh = q.new_empty(B, NT, H, K, V)
565 dh0 = torch.empty_like(h0, dtype=torch.float32) if h0 is not None else None
566 dv2 = torch.empty_like(dv)
567
568 BV = 128
569
570 g = g.permute(0, 2, 1).contiguous()
571
572 chunk_gated_delta_rule_bwd_kernel_dhu_blockdim64[(triton.cdiv(V, BV), N * H)](
573 q=q,
574 k=k,
575 w=w,
576 g=g,
577 gk=gk,
578 dht=dht,
579 dh0=dh0,
580 do=do,
581 dh=dh,
582 dv=dv,
583 dv2=dv2,
584 cu_seqlens=cu_seqlens,
585 chunk_offsets=chunk_offsets,
586 scale=scale,
587 T=T,
588 H=H,
589 K=K,
590 V=V,
591 BT=BT,
592 BV=BV,
593 )
594 return dh, dh0, dv2