Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / platform / torch / custom_ops / gdn / chunk_gated_delta_rule.py: 0%

153 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-08-21 04:29 +0800

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# Keep the validated kernel adapter close to its upstream implementation. 

19# pylint: disable=line-too-long,missing-public-type-hints,missing-public-docstring 

20# pylint: disable=non-google-docstring,disallowed-name,unused-argument,invalid-name 

21# pylint: disable=missing-module-docstring,missing-function-docstring 

22# pylint: disable=abstract-method,arguments-differ 

23 

24import warnings 

25from typing import Optional 

26 

27import torch 

28 

29from .triton.chunk_delta_h import chunk_gated_delta_rule_bwd_dhu, chunk_gated_delta_rule_fwd_h 

30from .triton.chunk_o import chunk_bwd_dqkwg, chunk_bwd_dv_local, chunk_fwd_o 

31from .triton.chunk_scaled_dot_kkt import chunk_scaled_dot_kkt_fwd 

32from .triton.cumsum import chunk_local_cumsum 

33from .triton.solve_tril import solve_tril 

34from .triton.utils import autocast_custom_bwd, autocast_custom_fwd, input_guard 

35from .triton.wy_fast import prepare_wy_repr_bwd, recompute_w_u_fwd 

36 

37 

38def _l2norm(x: torch.Tensor, eps: float = 1e-6) -> tuple[torch.Tensor, torch.Tensor]: 

39 inv_norm = torch.rsqrt((x * x).sum(dim=-1, keepdim=True) + eps) 

40 return (x * inv_norm).to(x.dtype), inv_norm 

41 

42 

43def chunk_gated_delta_rule_fwd_prepare( 

44 k: torch.Tensor, 

45 v: torch.Tensor, 

46 g: torch.Tensor, 

47 beta: torch.Tensor, 

48 cu_seqlens: Optional[torch.LongTensor] = None, 

49 chunk_size: int = 64, 

50): 

51 """Compute forward intermediates that do not depend on the initial state.""" 

52 g = chunk_local_cumsum(g, chunk_size=chunk_size, cu_seqlens=cu_seqlens, head_first=False) 

53 A = chunk_scaled_dot_kkt_fwd( 

54 k=k, 

55 g=g, 

56 beta=beta, 

57 cu_seqlens=cu_seqlens, 

58 chunk_size=chunk_size, 

59 output_dtype=torch.float32, 

60 ) 

61 A = solve_tril(A=A, cu_seqlens=cu_seqlens, output_dtype=k.dtype) 

62 w, u = recompute_w_u_fwd( 

63 k=k, 

64 v=v, 

65 beta=beta, 

66 A=A, 

67 g=g, 

68 cu_seqlens=cu_seqlens, 

69 ) 

70 return g, A, w, u 

71 

72 

73def chunk_gated_delta_rule_fwd_apply_state( 

74 k: torch.Tensor, 

75 g: torch.Tensor, 

76 w: torch.Tensor, 

77 u: torch.Tensor, 

78 initial_state: Optional[torch.Tensor], 

79 output_final_state: bool, 

80 cu_seqlens: Optional[torch.LongTensor] = None, 

81 chunk_size: int = 64, 

82): 

83 """Apply the recurrent initial state and return local state intermediates.""" 

84 return chunk_gated_delta_rule_fwd_h( 

85 k=k, 

86 w=w, 

87 u=u, 

88 g=g, 

89 initial_state=initial_state, 

90 output_final_state=output_final_state, 

91 chunk_size=chunk_size, 

92 cu_seqlens=cu_seqlens, 

93 ) 

94 

95 

96def chunk_gated_delta_rule_fwd_output( 

97 q: torch.Tensor, 

98 k: torch.Tensor, 

99 v_new: torch.Tensor, 

100 h: torch.Tensor, 

101 g: torch.Tensor, 

102 scale: float, 

103 cu_seqlens: Optional[torch.LongTensor] = None, 

104 chunk_size: int = 64, 

105): 

106 """Compute local outputs after recurrent states have been applied.""" 

107 return chunk_fwd_o( 

108 q=q, 

109 k=k, 

110 v=v_new, 

111 h=h, 

112 g=g, 

113 scale=scale, 

114 cu_seqlens=cu_seqlens, 

115 chunk_size=chunk_size, 

116 ) 

117 

118 

119def chunk_gated_delta_rule_fwd( 

120 q: torch.Tensor, 

121 k: torch.Tensor, 

122 v: torch.Tensor, 

123 g: torch.Tensor, 

124 beta: torch.Tensor, 

125 scale: float, 

126 initial_state: torch.Tensor, 

127 output_final_state: bool, 

128 cu_seqlens: Optional[torch.LongTensor] = None, 

129 chunk_size: int = 64, 

130): 

131 g, A, w, u = chunk_gated_delta_rule_fwd_prepare( 

132 k=k, 

133 v=v, 

134 g=g, 

135 beta=beta, 

136 cu_seqlens=cu_seqlens, 

137 chunk_size=chunk_size, 

138 ) 

139 h, v_new, final_state = chunk_gated_delta_rule_fwd_apply_state( 

140 k=k, 

141 g=g, 

142 w=w, 

143 u=u, 

144 initial_state=initial_state, 

145 output_final_state=output_final_state, 

146 cu_seqlens=cu_seqlens, 

147 chunk_size=chunk_size, 

148 ) 

149 o = chunk_gated_delta_rule_fwd_output( 

150 q=q, 

151 k=k, 

152 v_new=v_new, 

153 h=h, 

154 g=g, 

155 scale=scale, 

156 cu_seqlens=cu_seqlens, 

157 chunk_size=chunk_size, 

158 ) 

159 return g, o, A, final_state 

160 

161 

162def chunk_gated_delta_rule_bwd_prepare( 

163 q: torch.Tensor, 

164 k: torch.Tensor, 

165 v: torch.Tensor, 

166 g: torch.Tensor, 

167 beta: torch.Tensor, 

168 A: torch.Tensor, 

169 scale: float, 

170 initial_state: Optional[torch.Tensor], 

171 do: torch.Tensor, 

172 cu_seqlens: Optional[torch.LongTensor] = None, 

173 chunk_size: int = 64, 

174): 

175 """Compute backward intermediates that do not depend on final-state grad.""" 

176 w, u = recompute_w_u_fwd( 

177 k=k, 

178 v=v, 

179 beta=beta, 

180 A=A, 

181 g=g, 

182 cu_seqlens=cu_seqlens, 

183 ) 

184 h, v_new, _ = chunk_gated_delta_rule_fwd_apply_state( 

185 k=k, 

186 g=g, 

187 w=w, 

188 u=u, 

189 initial_state=initial_state, 

190 output_final_state=False, 

191 cu_seqlens=cu_seqlens, 

192 chunk_size=chunk_size, 

193 ) 

194 dv = chunk_bwd_dv_local( 

195 q=q, 

196 k=k, 

197 g=g, 

198 do=do, 

199 scale=scale, 

200 cu_seqlens=cu_seqlens, 

201 chunk_size=chunk_size, 

202 ) 

203 return w, h, v_new, dv 

204 

205 

206def chunk_gated_delta_rule_bwd_state( 

207 q: torch.Tensor, 

208 k: torch.Tensor, 

209 w: torch.Tensor, 

210 g: torch.Tensor, 

211 initial_state: Optional[torch.Tensor], 

212 dht: Optional[torch.Tensor], 

213 do: torch.Tensor, 

214 dv: torch.Tensor, 

215 scale: float, 

216 cu_seqlens: Optional[torch.LongTensor] = None, 

217 chunk_size: int = 64, 

218): 

219 """Apply the final-state gradient and produce the initial-state gradient.""" 

220 return chunk_gated_delta_rule_bwd_dhu( 

221 q=q, 

222 k=k, 

223 w=w, 

224 g=g, 

225 h0=initial_state, 

226 dht=dht, 

227 do=do, 

228 dv=dv, 

229 scale=scale, 

230 cu_seqlens=cu_seqlens, 

231 chunk_size=chunk_size, 

232 ) 

233 

234 

235def chunk_gated_delta_rule_bwd_finish( 

236 q: torch.Tensor, 

237 k: torch.Tensor, 

238 v: torch.Tensor, 

239 g: torch.Tensor, 

240 beta: torch.Tensor, 

241 A: torch.Tensor, 

242 w: torch.Tensor, 

243 h: torch.Tensor, 

244 v_new: torch.Tensor, 

245 dv: torch.Tensor, 

246 do: torch.Tensor, 

247 dh: torch.Tensor, 

248 scale: float, 

249 cu_seqlens: Optional[torch.LongTensor] = None, 

250 chunk_size: int = 64, 

251): 

252 """Finish local tensor gradients after the state-gradient handoff.""" 

253 dq, dk, dw, dg = chunk_bwd_dqkwg( 

254 q=q, 

255 k=k, 

256 v=v_new, 

257 w=w, 

258 g=g, 

259 h=h, 

260 dv=dv, 

261 do=do, 

262 dh=dh, 

263 chunk_size=chunk_size, 

264 scale=scale, 

265 cu_seqlens=cu_seqlens, 

266 ) 

267 dk2, dv, db, dg2 = prepare_wy_repr_bwd( 

268 k=k, 

269 v=v, 

270 beta=beta, 

271 g=g, 

272 A=A, 

273 dw=dw, 

274 du=dv, 

275 cu_seqlens=cu_seqlens, 

276 chunk_size=chunk_size, 

277 ) 

278 dk.add_(dk2) 

279 dg.add_(dg2) 

280 if dg.dtype != torch.float32: 

281 raise ValueError(f"dg current type is {dg.dtype} , should be float32") 

282 dg = chunk_local_cumsum( 

283 dg, 

284 chunk_size=chunk_size, 

285 reverse=True, 

286 cu_seqlens=cu_seqlens, 

287 head_first=False, 

288 ) 

289 return dq, dk, dv, db, dg 

290 

291 

292def chunk_gated_delta_rule_bwd( 

293 q: torch.Tensor, 

294 k: torch.Tensor, 

295 v: torch.Tensor, 

296 g: torch.Tensor, 

297 beta: torch.Tensor, 

298 A: torch.Tensor, 

299 scale: float, 

300 initial_state: torch.Tensor, 

301 do: torch.Tensor, 

302 dht: torch.Tensor, 

303 cu_seqlens: Optional[torch.LongTensor] = None, 

304 chunk_size: int = 64, 

305): 

306 w, h, v_new, dv = chunk_gated_delta_rule_bwd_prepare( 

307 q=q, 

308 k=k, 

309 v=v, 

310 g=g, 

311 beta=beta, 

312 A=A, 

313 scale=scale, 

314 initial_state=initial_state, 

315 do=do, 

316 cu_seqlens=cu_seqlens, 

317 chunk_size=chunk_size, 

318 ) 

319 dh, dh0, dv = chunk_gated_delta_rule_bwd_state( 

320 q=q, 

321 k=k, 

322 w=w, 

323 g=g, 

324 initial_state=initial_state, 

325 dht=dht, 

326 do=do, 

327 dv=dv, 

328 scale=scale, 

329 cu_seqlens=cu_seqlens, 

330 chunk_size=chunk_size, 

331 ) 

332 dq, dk, dv, db, dg = chunk_gated_delta_rule_bwd_finish( 

333 q=q, 

334 k=k, 

335 v=v, 

336 g=g, 

337 beta=beta, 

338 A=A, 

339 w=w, 

340 h=h, 

341 v_new=v_new, 

342 dv=dv, 

343 do=do, 

344 dh=dh, 

345 scale=scale, 

346 cu_seqlens=cu_seqlens, 

347 chunk_size=chunk_size, 

348 ) 

349 return dq, dk, dv, db, dg, dh0 

350 

351 

352@torch.compiler.disable 

353@input_guard 

354def chunk_gated_delta_rule_fwd_prepare_saved( 

355 q: torch.Tensor, 

356 k: torch.Tensor, 

357 v: torch.Tensor, 

358 g: torch.Tensor, 

359 beta: torch.Tensor, 

360 scale: float = None, 

361 use_qk_l2norm_in_kernel: bool = False, 

362 chunk_size: int = 64, 

363): 

364 """Prepare fused GDN forward tensors without consuming the initial state.""" 

365 if scale is None: 

366 scale = k.shape[-1] ** -0.5 

367 

368 q_norm, q_inv_norm = q, q.new_empty(0) 

369 k_norm, k_inv_norm = k, k.new_empty(0) 

370 if use_qk_l2norm_in_kernel: 

371 q_norm, q_inv_norm = _l2norm(q) 

372 k_norm, k_inv_norm = _l2norm(k) 

373 

374 g_cumsum, A, w, u = chunk_gated_delta_rule_fwd_prepare( 

375 k=k_norm, 

376 v=v, 

377 g=g, 

378 beta=beta, 

379 chunk_size=chunk_size, 

380 ) 

381 return ( 

382 q_norm, 

383 k_norm, 

384 q_inv_norm, 

385 k_inv_norm, 

386 g_cumsum, 

387 A, 

388 w, 

389 u, 

390 scale, 

391 ) 

392 

393 

394@torch.compiler.disable 

395@input_guard 

396def chunk_gated_delta_rule_fwd_apply_state_saved( 

397 k_norm: torch.Tensor, 

398 g_cumsum: torch.Tensor, 

399 w: torch.Tensor, 

400 u: torch.Tensor, 

401 initial_state: torch.Tensor = None, 

402 output_final_state: bool = True, 

403 chunk_size: int = 64, 

404): 

405 """Apply an initial state to fused prepared forward tensors.""" 

406 return chunk_gated_delta_rule_fwd_apply_state( 

407 k=k_norm, 

408 g=g_cumsum, 

409 w=w, 

410 u=u, 

411 initial_state=initial_state, 

412 output_final_state=output_final_state, 

413 chunk_size=chunk_size, 

414 ) 

415 

416 

417@torch.compiler.disable 

418@input_guard 

419def chunk_gated_delta_rule_fwd_output_saved( 

420 q_norm: torch.Tensor, 

421 k_norm: torch.Tensor, 

422 g_cumsum: torch.Tensor, 

423 h: torch.Tensor, 

424 v_new: torch.Tensor, 

425 scale: float, 

426 chunk_size: int = 64, 

427): 

428 """Compute fused GDN output from prepared, state-applied tensors.""" 

429 return chunk_gated_delta_rule_fwd_output( 

430 q=q_norm, 

431 k=k_norm, 

432 v_new=v_new, 

433 h=h, 

434 g=g_cumsum, 

435 scale=scale, 

436 chunk_size=chunk_size, 

437 ) 

438 

439 

440@torch.compiler.disable 

441@input_guard 

442def chunk_gated_delta_rule_fwd_saved( 

443 q: torch.Tensor, 

444 k: torch.Tensor, 

445 v: torch.Tensor, 

446 g: torch.Tensor, 

447 beta: torch.Tensor, 

448 scale: float = None, 

449 initial_state: torch.Tensor = None, 

450 use_qk_l2norm_in_kernel: bool = False, 

451 chunk_size: int = 64, 

452): 

453 """Run fused GDN forward and return the tensors required by its backward.""" 

454 ( 

455 q_norm, 

456 k_norm, 

457 q_inv_norm, 

458 k_inv_norm, 

459 g_cumsum, 

460 A, 

461 w, 

462 u, 

463 scale, 

464 ) = chunk_gated_delta_rule_fwd_prepare_saved( 

465 q, 

466 k, 

467 v, 

468 g, 

469 beta, 

470 scale=scale, 

471 use_qk_l2norm_in_kernel=use_qk_l2norm_in_kernel, 

472 chunk_size=chunk_size, 

473 ) 

474 h, v_new, final_state = chunk_gated_delta_rule_fwd_apply_state_saved( 

475 k_norm, 

476 g_cumsum, 

477 w, 

478 u, 

479 initial_state=initial_state, 

480 output_final_state=True, 

481 chunk_size=chunk_size, 

482 ) 

483 output = chunk_gated_delta_rule_fwd_output_saved( 

484 q_norm, 

485 k_norm, 

486 g_cumsum, 

487 h, 

488 v_new, 

489 scale, 

490 chunk_size=chunk_size, 

491 ) 

492 return ( 

493 output.to(q.dtype), 

494 final_state, 

495 q_norm, 

496 k_norm, 

497 q_inv_norm, 

498 k_inv_norm, 

499 g_cumsum, 

500 A, 

501 scale, 

502 ) 

503 

504 

505@torch.compiler.disable 

506@input_guard 

507def chunk_gated_delta_rule_bwd_prepare_saved( 

508 q_norm: torch.Tensor, 

509 k_norm: torch.Tensor, 

510 v: torch.Tensor, 

511 g_cumsum: torch.Tensor, 

512 beta: torch.Tensor, 

513 A: torch.Tensor, 

514 initial_state: torch.Tensor, 

515 grad_output: torch.Tensor, 

516 scale: float, 

517 chunk_size: int = 64, 

518): 

519 """Prepare fused backward tensors before the final-state grad arrives.""" 

520 return chunk_gated_delta_rule_bwd_prepare( 

521 q=q_norm, 

522 k=k_norm, 

523 v=v, 

524 g=g_cumsum, 

525 beta=beta, 

526 A=A, 

527 scale=scale, 

528 initial_state=initial_state, 

529 do=grad_output, 

530 chunk_size=chunk_size, 

531 ) 

532 

533 

534@torch.compiler.disable 

535@input_guard 

536def chunk_gated_delta_rule_bwd_state_saved( 

537 q_norm: torch.Tensor, 

538 k_norm: torch.Tensor, 

539 g_cumsum: torch.Tensor, 

540 w: torch.Tensor, 

541 initial_state: torch.Tensor, 

542 grad_final_state: torch.Tensor, 

543 grad_output: torch.Tensor, 

544 dv_local: torch.Tensor, 

545 scale: float, 

546 chunk_size: int = 64, 

547): 

548 """Consume the final-state grad and produce the initial-state grad.""" 

549 return chunk_gated_delta_rule_bwd_state( 

550 q=q_norm, 

551 k=k_norm, 

552 w=w, 

553 g=g_cumsum, 

554 initial_state=initial_state, 

555 dht=grad_final_state, 

556 do=grad_output, 

557 dv=dv_local, 

558 scale=scale, 

559 chunk_size=chunk_size, 

560 ) 

561 

562 

563@torch.compiler.disable 

564@input_guard 

565def chunk_gated_delta_rule_bwd_finish_saved( 

566 q: torch.Tensor, 

567 k: torch.Tensor, 

568 q_norm: torch.Tensor, 

569 k_norm: torch.Tensor, 

570 v: torch.Tensor, 

571 g_cumsum: torch.Tensor, 

572 beta: torch.Tensor, 

573 A: torch.Tensor, 

574 w: torch.Tensor, 

575 h: torch.Tensor, 

576 v_new: torch.Tensor, 

577 dv: torch.Tensor, 

578 grad_output: torch.Tensor, 

579 dh: torch.Tensor, 

580 q_inv_norm: torch.Tensor, 

581 k_inv_norm: torch.Tensor, 

582 scale: float, 

583 use_qk_l2norm_in_kernel: bool = False, 

584 chunk_size: int = 64, 

585): 

586 """Finish local fused gradients after the P2P state-gradient handoff.""" 

587 dq, dk, dv, dbeta, dg = chunk_gated_delta_rule_bwd_finish( 

588 q=q_norm, 

589 k=k_norm, 

590 v=v, 

591 g=g_cumsum, 

592 beta=beta, 

593 A=A, 

594 w=w, 

595 h=h, 

596 v_new=v_new, 

597 dv=dv, 

598 do=grad_output, 

599 dh=dh, 

600 scale=scale, 

601 chunk_size=chunk_size, 

602 ) 

603 if use_qk_l2norm_in_kernel: 

604 with torch.enable_grad(): 

605 q_leaf = q.detach().requires_grad_(True) 

606 k_leaf = k.detach().requires_grad_(True) 

607 q_recomputed, _ = _l2norm(q_leaf) 

608 k_recomputed, _ = _l2norm(k_leaf) 

609 dq, dk = torch.autograd.grad( 

610 (q_recomputed, k_recomputed), 

611 (q_leaf, k_leaf), 

612 grad_outputs=(dq, dk), 

613 ) 

614 del q_inv_norm, k_inv_norm 

615 return dq, dk, dv, dg, dbeta 

616 

617 

618@torch.compiler.disable 

619@input_guard 

620def chunk_gated_delta_rule_bwd_saved( 

621 q: torch.Tensor, 

622 k: torch.Tensor, 

623 q_norm: torch.Tensor, 

624 k_norm: torch.Tensor, 

625 v: torch.Tensor, 

626 g_cumsum: torch.Tensor, 

627 beta: torch.Tensor, 

628 A: torch.Tensor, 

629 initial_state: torch.Tensor, 

630 grad_output: torch.Tensor, 

631 grad_final_state: torch.Tensor, 

632 q_inv_norm: torch.Tensor, 

633 k_inv_norm: torch.Tensor, 

634 scale: float, 

635 use_qk_l2norm_in_kernel: bool = False, 

636 chunk_size: int = 64, 

637): 

638 """Run fused GDN backward from a context saved by the forward helper.""" 

639 w, h, v_new, dv = chunk_gated_delta_rule_bwd_prepare_saved( 

640 q_norm, 

641 k_norm, 

642 v, 

643 g_cumsum, 

644 beta, 

645 A, 

646 initial_state, 

647 grad_output, 

648 scale, 

649 chunk_size=chunk_size, 

650 ) 

651 dh, dh0, dv = chunk_gated_delta_rule_bwd_state_saved( 

652 q_norm, 

653 k_norm, 

654 g_cumsum, 

655 w, 

656 initial_state, 

657 grad_final_state, 

658 grad_output, 

659 dv, 

660 scale, 

661 chunk_size=chunk_size, 

662 ) 

663 dq, dk, dv, dg, dbeta = chunk_gated_delta_rule_bwd_finish_saved( 

664 q, 

665 k, 

666 q_norm, 

667 k_norm, 

668 v, 

669 g_cumsum, 

670 beta, 

671 A, 

672 w, 

673 h, 

674 v_new, 

675 dv, 

676 grad_output, 

677 dh, 

678 q_inv_norm, 

679 k_inv_norm, 

680 scale, 

681 use_qk_l2norm_in_kernel=use_qk_l2norm_in_kernel, 

682 chunk_size=chunk_size, 

683 ) 

684 return dq, dk, dv, dg, dbeta, dh0 

685 

686 

687class ChunkGatedDeltaRuleFunction(torch.autograd.Function): 

688 """Autograd wrapper for the Triton-Ascend chunk Gated Delta Rule.""" 

689 

690 @staticmethod 

691 @input_guard 

692 @autocast_custom_fwd 

693 def forward( 

694 ctx, 

695 q: torch.Tensor, 

696 k: torch.Tensor, 

697 v: torch.Tensor, 

698 g: torch.Tensor, 

699 beta: torch.Tensor, 

700 scale: float, 

701 initial_state: torch.Tensor, 

702 output_final_state: bool, 

703 cu_seqlens: Optional[torch.LongTensor] = None, 

704 use_qk_l2norm_in_kernel: bool = False, 

705 chunk_size: int = 64, 

706 ): 

707 g, o, A, final_state = chunk_gated_delta_rule_fwd( 

708 q=q, 

709 k=k, 

710 v=v, 

711 g=g, 

712 beta=beta, 

713 scale=scale, 

714 initial_state=initial_state, 

715 output_final_state=output_final_state, 

716 cu_seqlens=cu_seqlens, 

717 chunk_size=chunk_size, 

718 ) 

719 

720 saved_initial_state = initial_state if initial_state is not None else q.new_empty(0) 

721 saved_cu_seqlens = cu_seqlens if cu_seqlens is not None else q.new_empty(0, dtype=torch.long) 

722 ctx.save_for_backward(q, k, v, g, beta, A, saved_initial_state, saved_cu_seqlens) 

723 ctx.has_initial_state = initial_state is not None 

724 ctx.has_cu_seqlens = cu_seqlens is not None 

725 ctx.scale = scale 

726 ctx.chunk_size = chunk_size 

727 return o.to(q.dtype), final_state 

728 

729 @staticmethod 

730 @input_guard 

731 @autocast_custom_bwd 

732 def backward( 

733 ctx, 

734 do: torch.Tensor, 

735 dht: torch.Tensor 

736 ): 

737 q, k, v, g, beta, A, initial_state, cu_seqlens = ctx.saved_tensors 

738 if not ctx.has_initial_state: 

739 initial_state = None 

740 if not ctx.has_cu_seqlens: 

741 cu_seqlens = None 

742 dq, dk, dv, db, dg, dh0 = chunk_gated_delta_rule_bwd( 

743 q=q, 

744 k=k, 

745 v=v, 

746 g=g, 

747 beta=beta, 

748 A=A, 

749 scale=ctx.scale, 

750 initial_state=initial_state, 

751 do=do, 

752 dht=dht, 

753 cu_seqlens=cu_seqlens, 

754 chunk_size=ctx.chunk_size, 

755 ) 

756 return dq.to(q), dk.to(k), dv.to(v), dg.to(g), db.to(beta), None, dh0, None, None, None, None 

757 

758 

759@torch.compiler.disable 

760def chunk_gated_delta_rule( 

761 q: torch.Tensor, 

762 k: torch.Tensor, 

763 v: torch.Tensor, 

764 g: torch.Tensor, 

765 beta: torch.Tensor, 

766 scale: float = None, 

767 initial_state: torch.Tensor = None, 

768 output_final_state: bool = False, 

769 use_qk_l2norm_in_kernel: bool = False, 

770 cu_seqlens: Optional[torch.LongTensor] = None, 

771 chunk_size: int = 64, 

772 head_first: bool = False, 

773): 

774 r""" 

775 Args: 

776 q (torch.Tensor): 

777 queries of shape `[B, T, H, K]`. 

778 k (torch.Tensor): 

779 keys of shape `[B, T, H, K]`. 

780 v (torch.Tensor): 

781 values of shape `[B, T, H, V]`. 

782 g (torch.Tensor): 

783 (forget) gating tensor (in log space!) of shape `[B, T, H]`. 

784 beta (torch.Tensor): 

785 betas of shape `[B, T, H]`. 

786 scale (Optional[float]): 

787 Scale factor for the RetNet attention scores. 

788 If not provided, it will default to `1 / sqrt(K)`. Default: `None`. 

789 initial_state (Optional[torch.Tensor]): 

790 Initial state of shape `[N, H, K, V]` for `N` input sequences. 

791 For equal-length input sequences, `N` equals the batch size `B`. 

792 Default: `None`. 

793 output_final_state (Optional[bool]): 

794 Whether to output the final state of shape `[N, H, K, V]`. Default: `False`. 

795 use_qk_l2norm_in_kernel (bool): 

796 Whether to apply L2norm to the q/k tensor internally. Default: `False`. 

797 cu_seqlens (torch.LongTensor): 

798 Cumulative sequence lengths of shape `[N+1]` used for variable-length training, 

799 consistent with the FlashAttention API. 

800 head_first (Optional[bool]): 

801 Whether the inputs are in the head-first format. Default: `False`. 

802 This argument has been deprecated. 

803 Returns: 

804 o (torch.Tensor): 

805 Outputs of shape `[B, T, H, V]`. 

806 final_state (torch.Tensor): 

807 Final state of shape `[N, H, K, V]` if `output_final_state=True` else `None`. 

808 

809 Examples:: 

810 >>> import torch 

811 >>> import torch.nn.functional as F 

812 >>> from einops import rearrange 

813 >>> from fla.ops.gated_delta_rule import chunk_gated_delta_rule 

814 # inputs with equal lengths 

815 >>> B, T, H, K, V = 4, 2048, 4, 512, 512 

816 >>> q = torch.randn(B, T, H, K, dtype=torch.bfloat16, device='cuda') 

817 >>> k = F.normalize(torch.randn(B, T, H, K, dtype=torch.bfloat16, device='cuda'), p=2, dim=-1) 

818 >>> v = torch.randn(B, T, H, V, dtype=torch.bfloat16, device='cuda') 

819 >>> beta = torch.rand(B, T, H, dtype=torch.bfloat16, device='cuda').sigmoid() 

820 >>> g = F.logsigmoid(torch.rand(B, T, H, dtype=torch.bfloat16, device='cuda')) 

821 >>> h0 = torch.randn(B, H, K, V, dtype=torch.bfloat16, device='cuda') 

822 >>> o, ht = chunk_gated_delta_rule( 

823 q, k, v, g, beta, 

824 initial_state=h0, 

825 output_final_state=True 

826 ) 

827 # for variable-length inputs, the batch size `B` is expected to be 1 and `cu_seqlens` is required 

828 >>> q, k, v, beta, g = map(lambda x: rearrange(x, 'b t ... -> 1 (b t) ...'), (q, k, v, beta, g)) 

829 # for a batch with 4 sequences, `cu_seqlens` with 5 start/end positions are expected 

830 >>> cu_seqlens = q.new_tensor([0, 2048, 4096, 6144, 8192], dtype=torch.long) 

831 >>> o, ht = chunk_gated_delta_rule( 

832 q, k, v, g, beta, 

833 initial_state=h0, 

834 output_final_state=True, 

835 cu_seqlens=cu_seqlens 

836 ) 

837 """ 

838 if q.dtype != k.dtype or k.dtype != v.dtype: 

839 raise ValueError( 

840 f"q current type is {q.dtype}, k current type is {k.dtype}, " 

841 f"v current type is {v.dtype}, they should be equal" 

842 ) 

843 if q.dtype == torch.float32: 

844 raise ValueError( 

845 "ChunkGatedDeltaRuleFunction does not support float32. Please use bfloat16." 

846 ) 

847 if len(beta.shape) != 3: 

848 raise ValueError( 

849 f"beta current shape len is {len(beta.shape)}, beta must be of shape [B, T, H] if head_first=False, or [B, H, T] otherwise." 

850 ) 

851 

852 if head_first: 

853 warnings.warn( 

854 "head_first is deprecated and will be removed in a future version. " 

855 "Please use head_first=False for now instead." 

856 ) 

857 if not head_first and q.shape[1] < q.shape[2]: 

858 warnings.warn( 

859 f"Input tensor shape suggests potential format mismatch: seq_len ({q.shape[1]}) < num_heads ({q.shape[2]}). " 

860 "This may indicate the inputs were passed in head-first format [B, H, T, ...] " 

861 "when head_first=False was specified. " 

862 "Please verify your input tensor format matches the expected shape [B, T, H, ...]." 

863 ) 

864 if cu_seqlens is not None: 

865 if q.shape[0] != 1: 

866 raise ValueError( 

867 f"The batch size is expected to be 1 rather than {q.shape[0]} when using `cu_seqlens`." 

868 f"Please flatten variable-length inputs before processing." 

869 ) 

870 if initial_state is not None and initial_state.shape[0] != len(cu_seqlens) - 1: 

871 raise ValueError( 

872 f"The number of initial states is expected to be equal to the number of input sequences, " 

873 f"i.e., {len(cu_seqlens) - 1} rather than {initial_state.shape[0]}." 

874 ) 

875 if scale is None: 

876 scale = k.shape[-1] ** -0.5 

877 

878 if use_qk_l2norm_in_kernel: 

879 q, _ = _l2norm(q) 

880 k, _ = _l2norm(k) 

881 

882 o, final_state = ChunkGatedDeltaRuleFunction.apply( 

883 q, 

884 k, 

885 v, 

886 g, 

887 beta, 

888 scale, 

889 initial_state, 

890 output_final_state, 

891 cu_seqlens, 

892 False, 

893 chunk_size, 

894 ) 

895 return o, final_state