Coverage for  / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / components / functional / kimi_delta_attention.py: 0%

160 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-10-25 05:45 +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"""Dense Triton-Ascend KDA execution helpers for Torch training.""" 

16from __future__ import annotations 

17 

18# pylint: disable=forbidden-backend-import 

19 

20__all__ = ["fused_chunk_kda", "fused_chunk_kda_p2p"] 

21 

22from typing import Any, Optional 

23 

24import torch 

25import torch.distributed as dist 

26 

27from .kimi_delta_attention_fla_adapter import get_fla_kda_staged_ops, run_fla_chunk_kda 

28from .kimi_delta_attention_state_summary import ( 

29 apply_kda_state_gradient_summary, 

30 apply_kda_state_summary, 

31 kda_state_gradient_summary_from_prepared, 

32 kda_state_summary_forward_from_prepared, 

33) 

34 

35 

36def _validate_local_shapes( 

37 query: torch.Tensor, 

38 key: torch.Tensor, 

39 value: torch.Tensor, 

40 gate: torch.Tensor, 

41 beta: torch.Tensor, 

42) -> tuple[int, int, int, int]: 

43 """Validate local KDA tensor shapes and return dimensions used by later checks.""" 

44 if (query.ndim, key.ndim, value.ndim, gate.ndim) != (4, 4, 4, 4): 

45 raise ValueError("Fused KDA expects rank-4 query/key/value/gate tensors.") 

46 if beta.ndim != 3: 

47 raise ValueError("Fused KDA expects a rank-3 beta tensor.") 

48 if query.shape != key.shape: 

49 raise ValueError("Fused KDA query and key must have identical shapes.") 

50 batch, sequence_length, num_query_heads, key_dim = query.shape 

51 num_value_heads, value_dim = value.shape[2:] 

52 if value.shape[:2] != (batch, sequence_length): 

53 raise ValueError("Fused KDA value must match query batch and sequence dimensions.") 

54 if gate.shape != (batch, sequence_length, num_value_heads, key_dim): 

55 raise ValueError("Fused KDA gate has an incompatible shape.") 

56 if beta.shape != (batch, sequence_length, num_value_heads): 

57 raise ValueError("Fused KDA beta has an incompatible shape.") 

58 if num_value_heads % num_query_heads: 

59 raise ValueError("Fused KDA value heads must be divisible by query heads.") 

60 return sequence_length, key_dim, value_dim, num_value_heads 

61 

62 

63def _validate_local_inputs( 

64 query: torch.Tensor, 

65 key: torch.Tensor, 

66 value: torch.Tensor, 

67 gate: torch.Tensor, 

68 beta: torch.Tensor, 

69 a_log: torch.Tensor, 

70 dt_bias: torch.Tensor, 

71 *, 

72 chunk_size: int, 

73 lower_bound: float, 

74) -> None: 

75 """Validate the fixed-shape local KDA backend before compiling kernels.""" 

76 sequence_length, key_dim, value_dim, num_value_heads = _validate_local_shapes( 

77 query, key, value, gate, beta 

78 ) 

79 if not ( 

80 query.dtype == key.dtype == value.dtype == gate.dtype == beta.dtype 

81 == torch.bfloat16 

82 ): 

83 raise TypeError("Fused KDA requires q/k/v/gate/beta tensors in bfloat16.") 

84 if a_log.dtype != torch.float32 or dt_bias.dtype != torch.float32: 

85 raise TypeError("Fused KDA requires a_log and dt_bias tensors in float32.") 

86 if key_dim != 128 or value_dim != 128 or chunk_size != 64: 

87 raise NotImplementedError( 

88 "Fused KDA currently requires key_dim=value_dim=128 and chunk_size=64." 

89 ) 

90 if sequence_length % chunk_size: 

91 raise ValueError( 

92 f"Fused KDA sequence length {sequence_length} must be divisible " 

93 f"by chunk_size {chunk_size}." 

94 ) 

95 if a_log.numel() != num_value_heads: 

96 raise ValueError("Fused KDA a_log must contain one value per value head.") 

97 if dt_bias.numel() != num_value_heads * key_dim: 

98 raise ValueError("Fused KDA dt_bias must contain one value per gate channel.") 

99 if not -5.0 <= lower_bound < 0: 

100 raise ValueError("Fused KDA lower_bound must lie in [-5, 0).") 

101 if not query.is_npu: 

102 raise RuntimeError("The fused KDA backend requires Ascend NPU tensors.") 

103 if any( 

104 tensor.device != query.device 

105 for tensor in (key, value, gate, beta, a_log, dt_bias) 

106 ): 

107 raise ValueError("Fused KDA inputs must reside on the same NPU device.") 

108 get_fla_kda_staged_ops() 

109 

110 

111def _validate_p2p_inputs( 

112 query: torch.Tensor, 

113 key: torch.Tensor, 

114 value: torch.Tensor, 

115 gate: torch.Tensor, 

116 beta: torch.Tensor, 

117 a_log: torch.Tensor, 

118 dt_bias: torch.Tensor, 

119 *, 

120 chunk_size: int, 

121 lower_bound: float, 

122 cp_rank: int, 

123 cp_size: int, 

124) -> None: 

125 """Validate the fixed-shape KDA P2P backend before communication starts.""" 

126 _validate_local_inputs( 

127 query, 

128 key, 

129 value, 

130 gate, 

131 beta, 

132 a_log, 

133 dt_bias, 

134 chunk_size=chunk_size, 

135 lower_bound=lower_bound, 

136 ) 

137 if cp_size <= 0 or not 0 <= cp_rank < cp_size: 

138 raise ValueError(f"Invalid KDA P2P rank {cp_rank} for cp_size {cp_size}.") 

139 

140 

141def fused_chunk_kda( 

142 query: torch.Tensor, 

143 key: torch.Tensor, 

144 value: torch.Tensor, 

145 gate: torch.Tensor, 

146 beta: torch.Tensor, 

147 *, 

148 a_log: torch.Tensor, 

149 dt_bias: torch.Tensor, 

150 scale: Optional[float] = None, 

151 lower_bound: float = -5.0, 

152 chunk_size: int = 64, 

153 safe_gate: bool = True, 

154) -> torch.Tensor: 

155 """Run the local Triton-Ascend KDA backend without CP communication. 

156 

157 Args: 

158 query: Local query tensor [B,T,H,K]. 

159 key: Matching local key tensor. 

160 value: Local value tensor [B,T,HV,V]. 

161 gate: Per-token gate logits [B,T,HV,K]. 

162 beta: Per-token beta logits [B,T,HV]. 

163 a_log: Decay parameter with one value per value head. 

164 dt_bias: Learned bias with one value per gate channel. 

165 scale: Optional attention scaling factor. 

166 lower_bound: Lower bound for the activated gate. 

167 chunk_size: Tokens per local recurrence chunk. 

168 safe_gate: Use the staged lower-bounded gate implementation. 

169 """ 

170 _validate_local_inputs( 

171 query, 

172 key, 

173 value, 

174 gate, 

175 beta, 

176 a_log, 

177 dt_bias, 

178 chunk_size=chunk_size, 

179 lower_bound=lower_bound, 

180 ) 

181 output, _ = run_fla_chunk_kda( 

182 query, 

183 key, 

184 value, 

185 gate, 

186 beta, 

187 a_log=a_log, 

188 dt_bias=dt_bias.reshape(-1), 

189 scale=scale, 

190 lower_bound=lower_bound, 

191 chunk_size=chunk_size, 

192 safe_gate=safe_gate, 

193 ) 

194 return output 

195 

196 

197class _KDAStateP2PFunction(torch.autograd.Function): 

198 """Run shared local KDA and autograd for P2P or gathered state exchange.""" 

199 

200 @staticmethod 

201 def forward( # pylint: disable=arguments-differ,too-many-arguments,too-many-locals 

202 ctx: Any, 

203 query: torch.Tensor, 

204 key: torch.Tensor, 

205 value: torch.Tensor, 

206 gate_raw: torch.Tensor, 

207 beta_raw: torch.Tensor, 

208 a_log: torch.Tensor, 

209 dt_bias: torch.Tensor, 

210 scale: float, 

211 lower_bound: float, 

212 chunk_size: int, 

213 safe_gate: bool, 

214 cp_group: Any, 

215 prev_rank: int, 

216 next_rank: int, 

217 cp_rank: int, 

218 cp_size: int, 

219 state_cp: Any, 

220 ) -> torch.Tensor: 

221 """Prepare local KDA, propagate its state, and produce token outputs. 

222 

223 Args: 

224 ctx: Autograd context holding tensors and communication metadata. 

225 query: Local query tensor [B,T,H,K]. 

226 key: Matching local key tensor. 

227 value: Local value tensor [B,T,HV,V]. 

228 gate_raw: Unactivated per-token gate logits. 

229 beta_raw: Unactivated per-token beta logits. 

230 a_log: Decay parameter with one value per value head. 

231 dt_bias: Learned bias with one value per gate channel. 

232 scale: Optional attention scaling factor. 

233 lower_bound: Lower bound for the activated gate. 

234 chunk_size: Tokens per local recurrence chunk. 

235 safe_gate: Use the staged lower-bounded gate implementation. 

236 cp_group: Precreated chronological CP process group. 

237 prev_rank: Global rank of the preceding sequence partition. 

238 next_rank: Global rank of the following sequence partition. 

239 cp_rank: Chronological index within the CP group. 

240 cp_size: Number of state partitions. 

241 state_cp: Optional gather executor; None selects P2P. 

242 """ 

243 ops = get_fla_kda_staged_ops() 

244 

245 # FLA consumes base-2 log gates: exp(x) = exp2(x / ln(2)). 

246 # Use the same conversion when recomputing gates in backward. 

247 rcp_ln2 = 1.4426950408889634 

248 

249 query, key, value, gate_raw, beta_raw = ( 

250 tensor.contiguous() 

251 for tensor in (query, key, value, gate_raw, beta_raw) 

252 ) 

253 query, query_rstd = ops.l2norm_fwd(query) 

254 key, key_rstd = ops.l2norm_fwd(key) 

255 beta = ops.fused_beta_sigmoid(beta_raw) 

256 gate = ops.kda_gate_chunk_cumsum( 

257 g=gate_raw, A_log=a_log, dt_bias=dt_bias, scale=rcp_ln2, chunk_size=chunk_size, 

258 lower_bound=lower_bound, 

259 ) 

260 

261 state_shape = (query.shape[0], value.shape[2], query.shape[-1], value.shape[-1]) 

262 initial_state = None 

263 recv_handle = None 

264 if state_cp is None and cp_rank > 0: 

265 initial_state = torch.empty(state_shape, device=query.device, dtype=torch.float32) 

266 recv_handle = dist.irecv(initial_state, src=prev_rank, group=cp_group) 

267 

268 w, u, _, kg, attention_qk, attention_kk = ops.chunk_kda_fwd_intra( 

269 q=query, k=key, v=value, gk=gate, beta=beta, scale=scale, chunk_size=chunk_size, safe_gate=safe_gate, 

270 disable_recompute=True, 

271 ) 

272 state_ext = None 

273 transition = None 

274 if state_cp is None and cp_rank == 0 and cp_size > 1: 

275 # Zero entry uses S directly, and rank 0 has no reverse-state recipient. 

276 state_ext, transition = kda_state_summary_forward_from_prepared( 

277 kg, w, u, gate, chunk_size=chunk_size, output_transition=False, 

278 ) 

279 elif state_cp is not None or cp_rank < cp_size - 1: 

280 state_ext, transition = kda_state_summary_forward_from_prepared(kg, w, u, gate, chunk_size=chunk_size) 

281 if recv_handle is not None: 

282 recv_handle.wait() 

283 

284 send_handle = None 

285 send_state = None 

286 if state_cp is not None: 

287 initial_state = state_cp.forward(state_ext, transition) 

288 elif cp_rank < cp_size - 1: 

289 final_state = apply_kda_state_summary(state_ext, transition, initial_state) 

290 send_state = final_state.contiguous() 

291 send_handle = dist.isend(send_state, dst=next_rank, group=cp_group) 

292 

293 states, value_new, _ = ops.chunk_gated_delta_rule_fwd_h( 

294 k=kg, w=w, u=u, gk=gate, initial_state=initial_state, output_final_state=False, chunk_size=chunk_size, 

295 ) 

296 output = ops.chunk_gla_fwd_o_gk( 

297 q=query, v=value_new, g=gate, A=attention_qk, h=states, scale=scale, chunk_size=chunk_size, 

298 ) 

299 if send_handle is not None: 

300 send_handle.wait() 

301 

302 saved_initial_state = initial_state 

303 if saved_initial_state is None: 

304 saved_initial_state = query.new_empty(0, dtype=torch.float32) 

305 ctx.save_for_backward( 

306 query, query_rstd, key, key_rstd, value, gate_raw, beta_raw, a_log, dt_bias, attention_qk, 

307 attention_kk, transition if transition is not None else query.new_empty(0), saved_initial_state, 

308 ) 

309 ctx.scale = scale 

310 ctx.lower_bound = lower_bound 

311 ctx.chunk_size = chunk_size 

312 ctx.safe_gate = safe_gate 

313 ctx.cp_group = cp_group 

314 ctx.prev_rank = prev_rank 

315 ctx.next_rank = next_rank 

316 ctx.cp_rank = cp_rank 

317 ctx.cp_size = cp_size 

318 ctx.state_cp = state_cp 

319 return output.to(value.dtype) 

320 

321 @staticmethod 

322 def backward( # pylint: disable=arguments-differ,too-many-locals 

323 ctx: Any, 

324 grad_output: torch.Tensor, 

325 ) -> tuple[Any, ...]: 

326 """Reverse the state wavefront, then finish the fused local backward. 

327 

328 Args: 

329 ctx: Autograd context holding tensors and communication metadata. 

330 grad_output: Upstream gradient for the local token output. 

331 """ 

332 ops = get_fla_kda_staged_ops() 

333 

334 # FLA consumes base-2 log gates: exp(x) = exp2(x / ln(2)). 

335 # Use the same conversion when recomputing gates in backward. 

336 rcp_ln2 = 1.4426950408889634 

337 

338 ( 

339 query, query_rstd, key, key_rstd, value, gate_raw, beta_raw, a_log, dt_bias, 

340 attention_qk, attention_kk, transition, saved_initial_state, 

341 ) = ctx.saved_tensors 

342 initial_state = saved_initial_state if saved_initial_state.numel() else None 

343 grad_output = grad_output.contiguous() 

344 

345 grad_final_state = None 

346 recv_handle = None 

347 if ctx.state_cp is None and ctx.cp_rank < ctx.cp_size - 1: 

348 grad_final_state = torch.empty( 

349 (query.shape[0], value.shape[2], query.shape[-1], value.shape[-1]), device=query.device, 

350 dtype=torch.float32, 

351 ) 

352 recv_handle = dist.irecv(grad_final_state, src=ctx.next_rank, group=ctx.cp_group) 

353 

354 beta = ops.fused_beta_sigmoid(beta_raw) 

355 gate = ops.kda_gate_chunk_cumsum( 

356 g=gate_raw, A_log=a_log, dt_bias=dt_bias, scale=rcp_ln2, chunk_size=ctx.chunk_size, 

357 lower_bound=ctx.lower_bound, 

358 ) 

359 w, u, query_gated, key_gated = ops.recompute_w_u_fwd( 

360 q=query, k=key, v=value, beta=beta, A=attention_kk, gk=gate, 

361 ) 

362 states, value_new, _ = ops.chunk_gated_delta_rule_fwd_h( 

363 k=key_gated, w=w, u=u, gk=gate, initial_state=initial_state, output_final_state=False, 

364 chunk_size=ctx.chunk_size, 

365 ) 

366 grad_attention_qk, grad_value_local = ops.chunk_kda_bwd_dav( 

367 q=query, k=key, v=value_new, do=grad_output, A=attention_qk, scale=ctx.scale, 

368 chunk_size=ctx.chunk_size, 

369 ) 

370 grad_state_ext = None 

371 if ctx.state_cp is not None or ctx.cp_rank > 0: 

372 grad_state_ext = kda_state_gradient_summary_from_prepared( 

373 query_gated, key_gated, w, gate, grad_output, grad_value_local, ctx.scale, 

374 chunk_size=ctx.chunk_size, 

375 ) 

376 if recv_handle is not None: 

377 recv_handle.wait() 

378 send_handle = None 

379 send_state_gradient = None 

380 if ctx.state_cp is not None: 

381 grad_final_state = ctx.state_cp.backward(grad_state_ext, transition) 

382 elif ctx.cp_rank > 0: 

383 grad_initial_state = apply_kda_state_gradient_summary(grad_state_ext, transition, grad_final_state) 

384 send_state_gradient = grad_initial_state.contiguous() 

385 send_handle = dist.isend(send_state_gradient, dst=ctx.prev_rank, group=ctx.cp_group) 

386 

387 grad_states, _, grad_value = ops.chunk_gated_delta_rule_bwd_dhu( 

388 q=query_gated, k=key_gated, w=w, gk=gate, h0=initial_state, dht=grad_final_state, do=grad_output, 

389 dv=grad_value_local, scale=ctx.scale, chunk_size=ctx.chunk_size, 

390 ) 

391 # These recomputed tensors have no consumers after dhu. Dropping local 

392 # references lets the allocator reuse their storage on this stream. 

393 del query_gated, key_gated, w, u, grad_value_local, _ 

394 ( 

395 grad_query, grad_key, grad_value, grad_beta, grad_gate, grad_attention_kk, 

396 ) = ops.chunk_kda_bwd_wy_dqkg_fused( 

397 q=query, k=key, v=value, v_new=value_new, g=gate, beta=beta, A=attention_kk, h=states, do=grad_output, 

398 dh=grad_states, dv=grad_value, scale=ctx.scale, chunk_size=ctx.chunk_size, 

399 ) 

400 # Release recomputed states before intra backward allocates its outputs. 

401 del states, grad_states, value_new 

402 grad_query, grad_key, grad_beta, grad_gate = ops.chunk_kda_bwd_intra( 

403 q=query, k=key, g=gate, beta=beta, dAqk=grad_attention_qk, dAkk=grad_attention_kk, dq=grad_query, 

404 dk=grad_key, db=grad_beta, dg=grad_gate, chunk_size=ctx.chunk_size, safe_gate=ctx.safe_gate, 

405 ) 

406 

407 num_query_heads = query.shape[2] 

408 num_value_heads = value.shape[2] 

409 if num_value_heads > num_query_heads: 

410 groups = num_value_heads // num_query_heads 

411 grad_query = grad_query.view(*grad_query.shape[:2], num_query_heads, groups, grad_query.shape[-1]).sum( 

412 dim=3, 

413 ) 

414 grad_key = grad_key.view(*grad_key.shape[:2], num_query_heads, groups, grad_key.shape[-1]).sum(dim=3) 

415 

416 grad_gate = ops.chunk_local_cumsum(grad_gate, chunk_size=ctx.chunk_size, reverse=True) 

417 grad_gate, grad_a_log, grad_dt_bias = ops.kda_gate_bwd( 

418 g=gate_raw, A_log=a_log, dt_bias=dt_bias, dyg=grad_gate, lower_bound=ctx.lower_bound, 

419 ) 

420 grad_beta = ops.fused_beta_sigmoid_bwd(beta_raw, grad_beta) 

421 grad_query = ops.l2norm_bwd(query, query_rstd, grad_query) 

422 grad_key = ops.l2norm_bwd(key, key_rstd, grad_key) 

423 if send_handle is not None: 

424 send_handle.wait() 

425 

426 return ( 

427 grad_query, grad_key, grad_value, grad_gate, grad_beta, grad_a_log, grad_dt_bias, None, None, None, 

428 None, None, None, None, None, None, None, 

429 ) 

430 

431 

432@torch.compiler.disable 

433def fused_chunk_kda_p2p( 

434 query: torch.Tensor, 

435 key: torch.Tensor, 

436 value: torch.Tensor, 

437 gate: torch.Tensor, 

438 beta: torch.Tensor, 

439 *, 

440 a_log: torch.Tensor, 

441 dt_bias: torch.Tensor, 

442 cp_group: Any, 

443 prev_rank: int, 

444 next_rank: int, 

445 cp_rank: int, 

446 cp_size: int, 

447 scale: Optional[float] = None, 

448 lower_bound: float = -5.0, 

449 chunk_size: int = 64, 

450 safe_gate: bool = True, 

451 state_cp: Any = None, 

452) -> torch.Tensor: 

453 """Run one sequence-sharded KDA segment with a selected state CP executor. 

454 

455 The historical P2P entry name is retained for existing callers; passing 

456 state_cp selects AllGather or grouped AllGather/P2P with the same local ops. 

457 

458 Args: 

459 query: Query input [B,T,H,K]. 

460 key: Matching key input. 

461 value: Value input [B,T,HV,V]. 

462 gate: Per-token gate logits. 

463 beta: Per-token beta logits. 

464 a_log: FP32 decay parameter per value head. 

465 dt_bias: FP32 gate-channel bias. 

466 cp_group: Existing chronological state process group. 

467 prev_rank: Global predecessor rank for P2P. 

468 next_rank: Global successor rank for P2P. 

469 cp_rank: Chronological index within the state group. 

470 cp_size: Number of state partitions. 

471 scale: Optional attention scale. 

472 lower_bound: Lower bound for the activated gate. 

473 chunk_size: Tokens per local chunk. 

474 safe_gate: Enable the staged safe-gate path. 

475 state_cp: Optional stateless AG executor; None selects existing P2P. 

476 

477 Returns: 

478 Output tokens in the local sequence layout. 

479 """ 

480 _validate_p2p_inputs( 

481 query, 

482 key, 

483 value, 

484 gate, 

485 beta, 

486 a_log, 

487 dt_bias, 

488 chunk_size=chunk_size, 

489 lower_bound=lower_bound, 

490 cp_rank=cp_rank, 

491 cp_size=cp_size, 

492 ) 

493 effective_scale = query.shape[-1] ** -0.5 if scale is None else float(scale) 

494 return _KDAStateP2PFunction.apply( 

495 query, 

496 key, 

497 value, 

498 gate, 

499 beta, 

500 a_log, 

501 dt_bias.reshape(-1), 

502 effective_scale, 

503 float(lower_bound), 

504 chunk_size, 

505 safe_gate, 

506 cp_group, 

507 prev_rank, 

508 next_rank, 

509 cp_rank, 

510 cp_size, 

511 state_cp, 

512 )