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

57 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-11-15 07:18 +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"""Fusion-attention rescaling for parameter-sink attention.""" 

16 

17from typing import Any, Optional, Sequence, Tuple 

18 

19import torch # pylint: disable=forbidden-backend-import 

20import torch_npu 

21from einops import rearrange 

22 

23 

24def _rescale_attention_outputs( 

25 output: torch.Tensor, 

26 softmax_max: torch.Tensor, 

27 softmax_sum: torch.Tensor, 

28 sink_output: torch.Tensor, 

29 sink_softmax_max: torch.Tensor, 

30 sink_softmax_sum: torch.Tensor, 

31 sequence_length: int, 

32 batch_size: int, 

33 num_heads: int, 

34) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]: 

35 """Merge normal and sink attention outputs using their softmax statistics.""" 

36 output = rearrange(output, "(b s) n d -> s b n d", b=batch_size, s=sequence_length) 

37 sink_output = rearrange(sink_output, "s b (n d) -> s b n d", n=num_heads) 

38 

39 softmax_sum = softmax_sum[:, :, 0].view(batch_size, sequence_length, num_heads).transpose(0, 1) 

40 softmax_max = softmax_max[:, :, 0].view(batch_size, sequence_length, num_heads).transpose(0, 1) 

41 sink_softmax_sum = rearrange(sink_softmax_sum[:, :, :, 0], "b n s -> s b n") 

42 sink_softmax_max = rearrange(sink_softmax_max[:, :, :, 0], "b n s -> s b n") 

43 

44 combined_max = torch.maximum(softmax_max, sink_softmax_max) 

45 output_sum = softmax_sum * torch.exp(softmax_max - combined_max) 

46 sink_sum = sink_softmax_sum * torch.exp(sink_softmax_max - combined_max) 

47 combined_sum = output_sum + sink_sum 

48 output_scale = (output_sum / combined_sum).unsqueeze(3) 

49 sink_scale = (sink_sum / combined_sum).unsqueeze(3) 

50 return ( 

51 (output * output_scale + sink_output * sink_scale).to(dtype=output.dtype), 

52 output_scale, 

53 sink_scale, 

54 ) 

55 

56 

57def _normal_attention_backward(ctx, grad_rescaled_output, saved_tensors): 

58 """Compute the normal branch's Q/K/V gradients.""" 

59 ( 

60 query, key, value, sink_key, _, attention_mask, softmax_max, softmax_sum, 

61 _, _, rescaled_output, output_scale, _, 

62 ) = saved_tensors 

63 grad_output = rearrange(output_scale * grad_rescaled_output, "s b n d -> (b s) n d") 

64 return torch_npu.npu_fusion_attention_grad( 

65 query, key, value, grad_output.to(sink_key.dtype), ctx.num_heads, "TND", 

66 pse=None, 

67 padding_mask=None, 

68 atten_mask=attention_mask, 

69 softmax_max=softmax_max, 

70 softmax_sum=softmax_sum, 

71 attention_in=rearrange(rescaled_output, "s b n d -> (b s) n d"), 

72 scale_value=ctx.scale, 

73 pre_tockens=ctx.pre_tokens, 

74 next_tockens=ctx.next_tokens, 

75 inner_precise=0, 

76 keep_prob=ctx.keep_prob, 

77 actual_seq_qlen=ctx.actual_seq_qlen, 

78 actual_seq_kvlen=ctx.actual_seq_kvlen, 

79 sparse_mode=ctx.sparse_mode, 

80 softmax_layout="TND", 

81 )[:3] 

82 

83 

84def _sink_attention_backward(ctx, grad_rescaled_output, saved_tensors): 

85 """Compute sink gradients and restore the query gradient's TND layout.""" 

86 ( 

87 query, _, _, sink_key, sink_value, _, _, _, 

88 sink_softmax_max, sink_softmax_sum, rescaled_output, _, sink_scale, 

89 ) = saved_tensors 

90 sink_query = rearrange(query, "(b s) n d -> s b (n d)", b=ctx.batch_size, s=ctx.sequence_length) 

91 grad_output = rearrange(sink_scale * grad_rescaled_output, "s b n d -> s b (n d)") 

92 grad_query, grad_key, grad_value = torch_npu.npu_fusion_attention_grad( 

93 sink_query, sink_key, sink_value, grad_output.to(sink_key.dtype), ctx.num_heads, "SBH", 

94 pse=None, 

95 padding_mask=None, 

96 atten_mask=None, 

97 softmax_max=sink_softmax_max, 

98 softmax_sum=sink_softmax_sum, 

99 attention_in=rearrange(rescaled_output, "s b n d -> s b (n d)"), 

100 scale_value=ctx.scale, 

101 inner_precise=0, 

102 keep_prob=ctx.keep_prob, 

103 actual_seq_qlen=None, 

104 actual_seq_kvlen=None, 

105 sparse_mode=0, 

106 )[:3] 

107 grad_query = rearrange( 

108 grad_query, "s b (n d) -> (b s) n d", b=ctx.batch_size, s=ctx.sequence_length, n=ctx.num_heads, 

109 ) 

110 return grad_query, grad_key, grad_value 

111 

112 

113class _AttentionRescale(torch.autograd.Function): 

114 """Autograd bridge for fusion attention with separate sink parameters.""" 

115 

116 @staticmethod 

117 def forward( 

118 ctx: Any, 

119 query: torch.Tensor, 

120 key: torch.Tensor, 

121 value: torch.Tensor, 

122 sink_key: torch.Tensor, 

123 sink_value: torch.Tensor, 

124 attention_mask: torch.Tensor, 

125 batch_size: int, 

126 sequence_length: int, 

127 num_heads: int, 

128 scale: float, 

129 pre_tokens: int, 

130 next_tokens: int, 

131 keep_prob: float, 

132 sparse_mode: int, 

133 actual_seq_qlen: Optional[Sequence[int]], 

134 actual_seq_kvlen: Optional[Sequence[int]], 

135 ) -> Tuple[torch.Tensor, torch.Tensor]: 

136 """Run normal and sink fusion attention and rescale their outputs.""" 

137 output, softmax_max, softmax_sum = torch_npu.npu_fusion_attention( 

138 query, 

139 key, 

140 value, 

141 num_heads, 

142 "TND", 

143 pse=None, 

144 padding_mask=None, 

145 atten_mask=attention_mask, 

146 scale=scale, 

147 pre_tockens=pre_tokens, 

148 next_tockens=next_tokens, 

149 keep_prob=keep_prob, 

150 inner_precise=0, 

151 sparse_mode=sparse_mode, 

152 actual_seq_qlen=actual_seq_qlen, 

153 actual_seq_kvlen=actual_seq_kvlen, 

154 softmax_layout="TND", 

155 )[:3] 

156 sink_query = rearrange(query, "(b s) n d -> s b (n d)", b=batch_size, s=sequence_length) 

157 sink_output, sink_softmax_max, sink_softmax_sum = torch_npu.npu_fusion_attention( 

158 sink_query, 

159 sink_key, 

160 sink_value, 

161 num_heads, 

162 "SBH", 

163 pse=None, 

164 padding_mask=None, 

165 atten_mask=None, 

166 scale=scale, 

167 keep_prob=keep_prob, 

168 inner_precise=0, 

169 sparse_mode=0, 

170 actual_seq_qlen=None, 

171 actual_seq_kvlen=None, 

172 )[:3] 

173 rescaled_output, output_scale, sink_scale = _rescale_attention_outputs( 

174 output, 

175 softmax_max, 

176 softmax_sum, 

177 sink_output, 

178 sink_softmax_max, 

179 sink_softmax_sum, 

180 sequence_length, 

181 batch_size, 

182 num_heads, 

183 ) 

184 ctx.save_for_backward( 

185 query, 

186 key, 

187 value, 

188 sink_key, 

189 sink_value, 

190 attention_mask, 

191 softmax_max, 

192 softmax_sum, 

193 sink_softmax_max, 

194 sink_softmax_sum, 

195 rescaled_output, 

196 output_scale, 

197 sink_scale, 

198 ) 

199 ctx.batch_size = batch_size 

200 ctx.sequence_length = sequence_length 

201 ctx.num_heads = num_heads 

202 ctx.scale = scale 

203 ctx.pre_tokens = pre_tokens 

204 ctx.next_tokens = next_tokens 

205 ctx.keep_prob = keep_prob 

206 ctx.sparse_mode = sparse_mode 

207 ctx.actual_seq_qlen = actual_seq_qlen 

208 ctx.actual_seq_kvlen = actual_seq_kvlen 

209 return rescaled_output, softmax_max 

210 

211 @staticmethod 

212 def backward(ctx: Any, grad_rescaled_output: torch.Tensor, grad_softmax_max: torch.Tensor) -> tuple: 

213 """Run the explicit fusion-attention backward operators. 

214 

215 Args: 

216 ctx: Autograd context containing the saved forward tensors. 

217 grad_rescaled_output: Gradient of the rescaled attention output. 

218 grad_softmax_max: Unused gradient of the auxiliary softmax maximum. 

219 """ 

220 del grad_softmax_max 

221 saved_tensors = ctx.saved_tensors 

222 grad_query, grad_key, grad_value = _normal_attention_backward(ctx, grad_rescaled_output, saved_tensors) 

223 sink_grad_query, sink_grad_key, sink_grad_value = _sink_attention_backward( 

224 ctx, grad_rescaled_output, saved_tensors, 

225 ) 

226 return ( 

227 grad_query + sink_grad_query, 

228 grad_key, 

229 grad_value, 

230 sink_grad_key, 

231 sink_grad_value, 

232 *((None,) * 11), 

233 ) 

234 

235 

236def attention_rescale( 

237 query: torch.Tensor, 

238 key: torch.Tensor, 

239 value: torch.Tensor, 

240 sink_key: torch.Tensor, 

241 sink_value: torch.Tensor, 

242 attention_mask: torch.Tensor, 

243 batch_size: int, 

244 sequence_length: int, 

245 num_heads: int, 

246 scale: float, 

247 pre_tokens: int, 

248 next_tokens: int, 

249 keep_prob: float, 

250 sparse_mode: int, 

251 actual_seq_qlen: Optional[Sequence[int]], 

252 actual_seq_kvlen: Optional[Sequence[int]], 

253) -> Tuple[torch.Tensor, torch.Tensor]: 

254 """Combine normal and parameter-sink fusion-attention results. 

255 

256 Query, key, and value use TND layout. Sink key and value use SBH layout. 

257 Sequence lengths are cumulative Python sequences, as required by the 

258 NPU TND fusion-attention interface. 

259 

260 Args: 

261 query, key, value: Inputs for the normal attention branch. 

262 sink_key, sink_value: Key and value inputs for the sink branch. 

263 attention_mask: Mask applied to the normal attention branch. 

264 batch_size, sequence_length: Input batch and sequence dimensions. 

265 num_heads: Number of attention heads. 

266 scale: Attention score scale. 

267 pre_tokens, next_tokens: Sparse-attention token limits. 

268 keep_prob: Attention dropout keep probability. 

269 sparse_mode: Sparse-attention mode. 

270 actual_seq_qlen, actual_seq_kvlen: Cumulative query and key/value lengths. 

271 

272 Returns: 

273 Rescaled attention output in SBND layout and the normal attention 

274 softmax maximum. 

275 """ 

276 return _AttentionRescale.apply( 

277 query, 

278 key, 

279 value, 

280 sink_key, 

281 sink_value, 

282 attention_mask, 

283 batch_size, 

284 sequence_length, 

285 num_heads, 

286 scale, 

287 pre_tokens, 

288 next_tokens, 

289 keep_prob, 

290 sparse_mode, 

291 actual_seq_qlen, 

292 actual_seq_kvlen, 

293 )