Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / components / functional / dsa_sparse_attention_rescale.py: 0%
69 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-10-25 05:45 +0800
« 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"""DSA sparse-attention rescaling for parameter-sink attention."""
17from typing import Any, Tuple
19import torch # pylint: disable=forbidden-backend-import
20import torch.nn.functional as F # pylint: disable=forbidden-backend-import
21import torch_npu
22from einops import rearrange
23import omni_training_custom_ops # noqa: F401 # pylint: disable=unused-import
26def _sparse_attention_forward(
27 query_nope, compressed_kv, query_rope, key_rope, topk_indices,
28 batch_size, sequence_length, scale, actual_seq_qlen, actual_seq_kvlen,
29):
30 """Run sparse attention and restore the padded output's BSND layout."""
31 query_nope, compressed_kv, query_rope, key_rope = [
32 rearrange(tensor, "b s n d -> (b s) n d")
33 for tensor in (query_nope, compressed_kv, query_rope, key_rope)
34 ]
35 output, softmax_max, softmax_sum = torch.ops.custom.npu_sparse_flash_attention_enhance(
36 query_nope, compressed_kv, compressed_kv, topk_indices, scale,
37 block_table=None,
38 actual_seq_lengths_query=actual_seq_qlen,
39 actual_seq_lengths_kv=actual_seq_kvlen,
40 query_rope=query_rope,
41 key_rope=key_rope,
42 sparse_block_size=1,
43 layout_query="TND",
44 layout_kv="TND",
45 sparse_mode=3,
46 attention_mode=2,
47 return_softmax_lse=True,
48 )
49 if query_rope.size(-1) > 0:
50 output = F.pad(output, [0, query_rope.size(-1)])
51 output = rearrange(output, "(b s) n d -> b s n d", b=batch_size, s=sequence_length)
52 return output, softmax_max, softmax_sum
55def _sink_attention_forward(query_nope, query_rope, sink_key, sink_value, num_heads, scale, keep_prob):
56 """Run the sink branch in SBH layout."""
57 query = torch.cat([query_nope, query_rope], dim=-1)
58 return torch_npu.npu_fusion_attention(
59 rearrange(query, "b s n d -> s b (n d)"),
60 rearrange(sink_key, "b s n d -> s b (n d)"),
61 rearrange(sink_value, "b s n d -> s b (n d)"),
62 num_heads, "SBH",
63 pse=None,
64 padding_mask=None,
65 atten_mask=None,
66 scale=scale,
67 keep_prob=keep_prob,
68 inner_precise=0,
69 sparse_mode=0,
70 actual_seq_qlen=None,
71 actual_seq_kvlen=None,
72 )[:3]
75def _rescale_sparse_outputs(
76 output, softmax_max, softmax_sum, sink_output, sink_softmax_max, sink_softmax_sum,
77 batch_size, sequence_length, num_heads,
78):
79 """Combine the two attention outputs using stable softmax weights."""
80 sink_output = rearrange(sink_output, "s b (n d) -> b s n d", n=num_heads)
81 softmax_max = softmax_max.squeeze(0).view(batch_size, sequence_length, num_heads)
82 softmax_sum = softmax_sum.squeeze(0).view(batch_size, sequence_length, num_heads)
83 sink_softmax_max = sink_softmax_max[:, :, :, 0].transpose(1, 2)
84 sink_softmax_sum = sink_softmax_sum[:, :, :, 0].transpose(1, 2)
85 combined_max = torch.maximum(softmax_max, sink_softmax_max)
86 output_sum = softmax_sum * torch.exp(softmax_max - combined_max)
87 sink_sum = sink_softmax_sum * torch.exp(sink_softmax_max - combined_max)
88 combined_sum = output_sum + sink_sum
89 output_scale = (output_sum / combined_sum).unsqueeze(-1)
90 sink_scale = (sink_sum / combined_sum).unsqueeze(-1)
91 return (output * output_scale + sink_output * sink_scale).to(dtype=output.dtype), output_scale, sink_scale
94def _sparse_attention_backward(ctx, grad_rescaled_output):
95 """Compute sparse gradients and restore their BSND layout."""
96 (
97 query_nope, compressed_kv, query_rope, key_rope, sink_key, _, topk_indices,
98 softmax_max, softmax_sum, _, _, rescaled_output, output_scale, _,
99 ) = ctx.saved_tensors
100 query_nope, compressed_kv, query_rope, key_rope = [
101 rearrange(tensor, "b s n d -> (b s) n d")
102 for tensor in (query_nope, compressed_kv, query_rope, key_rope)
103 ]
104 grad_output = rearrange(output_scale * grad_rescaled_output, "b s n d -> (b s) n d")
105 rescaled_output = rearrange(rescaled_output, "b s n d -> (b s) n d")
106 if query_rope.size(-1) > 0:
107 grad_output = grad_output[:, :, :-query_rope.size(-1)]
108 rescaled_output = rescaled_output[:, :, :-query_rope.size(-1)]
109 gradients = torch.ops.custom.npu_sparse_flash_attention_grad_enhance(
110 query_nope, compressed_kv, compressed_kv, topk_indices,
111 grad_output.to(sink_key.dtype), rescaled_output, softmax_max, softmax_sum, ctx.scale,
112 sparse_block_size=1,
113 actual_seq_qlen=ctx.actual_seq_qlen,
114 actual_seq_kvlen=ctx.actual_seq_kvlen,
115 query_rope=query_rope,
116 key_rope=key_rope,
117 layout="TND",
118 sparse_mode=3,
119 attention_mode=2,
120 deterministic=torch.are_deterministic_algorithms_enabled(),
121 )
122 return tuple(
123 rearrange(tensor, "(b s) n d -> b s n d", b=ctx.batch_size, s=ctx.sequence_length)
124 for tensor in gradients
125 )
128def _sink_attention_backward(ctx, grad_rescaled_output):
129 """Compute sink gradients and restore their BSND layout."""
130 (
131 query_nope, _, query_rope, _, sink_key, sink_value, _, _, _,
132 sink_softmax_max, sink_softmax_sum, rescaled_output, _, sink_scale,
133 ) = ctx.saved_tensors
134 query = rearrange(torch.cat([query_nope, query_rope], dim=-1), "b s n d -> s b (n d)")
135 grad_output = rearrange(sink_scale * grad_rescaled_output, "b s n d -> s b (n d)")
136 grad_query, grad_key, grad_value = torch_npu.npu_fusion_attention_grad(
137 query,
138 rearrange(sink_key, "b s n d -> s b (n d)"),
139 rearrange(sink_value, "b s n d -> s b (n d)"),
140 grad_output.to(sink_key.dtype), ctx.num_heads, "SBH",
141 pse=None,
142 padding_mask=None,
143 atten_mask=None,
144 softmax_max=sink_softmax_max,
145 softmax_sum=sink_softmax_sum,
146 attention_in=rearrange(rescaled_output, "b s n d -> s b (n d)"),
147 scale_value=ctx.scale,
148 inner_precise=0,
149 keep_prob=ctx.keep_prob,
150 actual_seq_qlen=None,
151 actual_seq_kvlen=None,
152 sparse_mode=0,
153 )[:3]
154 return (
155 rearrange(grad_query, "s b (n d) -> b s n d", n=ctx.num_heads),
156 rearrange(grad_key, "s b (n d) -> b s n d", n=sink_key.size(2)),
157 rearrange(grad_value, "s b (n d) -> b s n d", n=sink_value.size(2)),
158 )
161class _SparseAttentionRescale(torch.autograd.Function):
162 """Autograd bridge for sparse attention with separate sink parameters."""
164 @staticmethod
165 def forward(
166 ctx: Any,
167 query_nope: torch.Tensor,
168 compressed_kv: torch.Tensor,
169 query_rope: torch.Tensor,
170 key_rope: torch.Tensor,
171 sink_key: torch.Tensor,
172 sink_value: torch.Tensor,
173 topk_indices: torch.Tensor,
174 batch_size: int,
175 sequence_length: int,
176 num_heads: int,
177 scale: float,
178 keep_prob: float,
179 actual_seq_qlen: torch.Tensor,
180 actual_seq_kvlen: torch.Tensor,
181 ) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
182 """Run sparse and sink attention and rescale their outputs."""
183 output, softmax_max, softmax_sum = _sparse_attention_forward(
184 query_nope, compressed_kv, query_rope, key_rope, topk_indices,
185 batch_size, sequence_length, scale, actual_seq_qlen, actual_seq_kvlen,
186 )
187 sink_output, sink_softmax_max, sink_softmax_sum = _sink_attention_forward(
188 query_nope, query_rope, sink_key, sink_value, num_heads, scale, keep_prob,
189 )
190 rescaled_output, output_scale, sink_scale = _rescale_sparse_outputs(
191 output, softmax_max, softmax_sum, sink_output, sink_softmax_max, sink_softmax_sum,
192 batch_size, sequence_length, num_heads,
193 )
195 ctx.save_for_backward(
196 query_nope,
197 compressed_kv,
198 query_rope,
199 key_rope,
200 sink_key,
201 sink_value,
202 topk_indices,
203 softmax_max,
204 softmax_sum,
205 sink_softmax_max,
206 sink_softmax_sum,
207 rescaled_output,
208 output_scale,
209 sink_scale,
210 )
211 ctx.batch_size = batch_size
212 ctx.sequence_length = sequence_length
213 ctx.num_heads = num_heads
214 ctx.scale = scale
215 ctx.keep_prob = keep_prob
216 ctx.actual_seq_qlen = actual_seq_qlen
217 ctx.actual_seq_kvlen = actual_seq_kvlen
218 return rescaled_output, softmax_max, softmax_sum
220 @staticmethod
221 def backward(
222 ctx: Any,
223 grad_rescaled_output: torch.Tensor,
224 grad_softmax_max: torch.Tensor,
225 grad_softmax_sum: torch.Tensor,
226 ) -> tuple:
227 """Run the explicit sparse- and fusion-attention backward operators."""
228 del grad_softmax_max, grad_softmax_sum
229 grad_query_nope, grad_key, grad_value, grad_query_rope, grad_key_rope = _sparse_attention_backward(
230 ctx, grad_rescaled_output,
231 )
232 sink_grad_query, sink_grad_key, sink_grad_value = _sink_attention_backward(ctx, grad_rescaled_output)
233 sink_grad_query_nope, sink_grad_query_rope = torch.split(
234 sink_grad_query,
235 [ctx.saved_tensors[0].size(-1), ctx.saved_tensors[2].size(-1)],
236 dim=-1,
237 )
238 return (
239 grad_query_nope + sink_grad_query_nope,
240 grad_key + grad_value,
241 grad_query_rope + sink_grad_query_rope,
242 grad_key_rope,
243 sink_grad_key,
244 sink_grad_value,
245 *((None,) * 8),
246 )
249def dsa_sparse_attention_rescale(
250 query_nope: torch.Tensor,
251 compressed_kv: torch.Tensor,
252 query_rope: torch.Tensor,
253 key_rope: torch.Tensor,
254 sink_key: torch.Tensor,
255 sink_value: torch.Tensor,
256 topk_indices: torch.Tensor,
257 batch_size: int,
258 sequence_length: int,
259 num_heads: int,
260 scale: float,
261 keep_prob: float,
262 actual_seq_qlen: torch.Tensor,
263 actual_seq_kvlen: torch.Tensor,
264) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
265 """Combine DSA sparse attention and parameter-sink attention.
267 The four DSA states and both sink states use BSND layout. ``topk_indices``
268 is the int32 output from :func:`dsa_indexer`; sequence lengths are
269 cumulative int32 NPU tensors. The returned BSND output is followed by the
270 sparse-attention softmax maximum and sum used by :func:`dsa_kl_loss`.
271 """
272 return _SparseAttentionRescale.apply(
273 query_nope,
274 compressed_kv,
275 query_rope,
276 key_rope,
277 sink_key,
278 sink_value,
279 topk_indices,
280 batch_size,
281 sequence_length,
282 num_heads,
283 scale,
284 keep_prob,
285 actual_seq_qlen,
286 actual_seq_kvlen,
287 )