Coverage for / home / jenkins / .local / lib / python3.10 / site-packages / hyper_parallel / components / functional / dsa_indexer.py: 84%
25 statements
« prev ^ index » next coverage.py v7.13.1, created at 2026-11-15 07:18 +0800
« 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"""DSA sparse-attention indexer."""
17from typing import Tuple
19import torch # pylint: disable=forbidden-backend-import
20from einops import rearrange
22from hyper_parallel.tools.logging import get_logger
24logger = get_logger()
27def _cann_lightning_indexer(
28 query: torch.Tensor,
29 key: torch.Tensor,
30 weights: torch.Tensor,
31 actual_seq_qlen: torch.Tensor,
32 actual_seq_klen: torch.Tensor,
33 sparse_count: int,
34) -> torch.Tensor:
35 """Call the ops-transformer LightningIndexer V2 with TND metadata."""
36 cu_q = torch.cat((actual_seq_qlen.new_zeros(1), actual_seq_qlen))
37 cu_k = torch.cat((actual_seq_klen.new_zeros(1), actual_seq_klen))
38 kwargs = {
39 "cu_seqlens_q": cu_q,
40 "cu_seqlens_k": cu_k,
41 "layout_q": "TND",
42 "layout_k": "TND",
43 "mask_mode": 3,
44 "cmp_ratio": 1,
45 "return_value": 0,
46 }
47 # The package is optional and is needed only for this selected CANN path.
48 from cann_ops_transformer.ops import ( # pylint: disable=import-outside-toplevel
49 lightning_indexer,
50 lightning_indexer_metadata,
51 )
53 kwargs["metadata"] = lightning_indexer_metadata(
54 query.shape[-2], key.shape[-2], query.shape[-1], sparse_count,
55 cu_seqlens_q=cu_q, cu_seqlens_k=cu_k, batch_size=cu_q.numel() - 1,
56 max_seqlen_q=int((cu_q[1:] - cu_q[:-1]).max().item()),
57 max_seqlen_k=int((cu_k[1:] - cu_k[:-1]).max().item()),
58 layout_q="TND", layout_k="TND", mask_mode=3, cmp_ratio=1,
59 )
60 indices, _ = lightning_indexer(
61 query.contiguous(), key.contiguous(), weights.float().contiguous(), sparse_count, **kwargs,
62 )
63 return indices
66def dsa_indexer(
67 index_query: torch.Tensor,
68 index_key: torch.Tensor,
69 merge_weight: torch.Tensor,
70 actual_seq_qlen: torch.Tensor,
71 actual_seq_kvlen: torch.Tensor,
72 sparse_count: int,
73 *,
74 backend: str = "cann",
75) -> Tuple[torch.Tensor, torch.Tensor, torch.Tensor, torch.Tensor]:
76 """Generate top-k token indices for DSA sparse attention.
78 Args:
79 index_query: Indexer query in BSND layout.
80 index_key: Indexer key in BSND layout.
81 merge_weight: Per-head indexer weights in BSN layout.
82 actual_seq_qlen: Cumulative query lengths as an int32 NPU tensor.
83 actual_seq_kvlen: Cumulative key lengths as an int32 NPU tensor.
84 sparse_count: Number of token indices selected for each query.
85 backend: ``"cann"`` (default) or the legacy ``"omni"`` backend.
87 Returns:
88 Top-k indices in ``[T, N_kv, K]`` int32 layout, followed by the query,
89 key, and merge-weight tensors in TND, TND, and TN layouts. Positions
90 without enough causal candidates are filled with ``-1``. The flattened
91 tensors can be passed directly to :func:`dsa_kl_loss`.
92 """
93 if backend not in ("cann", "omni"):
94 raise ValueError(f"backend must be 'cann' or 'omni', got {backend!r}")
95 index_query_tnd = rearrange(index_query, "b s n d -> (b s) n d")
96 index_key_tnd = rearrange(index_key, "b s n d -> (b s) n d")
97 merge_weight_tnd = rearrange(merge_weight, "b s n -> (b s) n")
98 # Load only the selected optional backend so either can be used independently.
99 # pylint: disable=import-outside-toplevel
100 if backend == "cann":
101 topk_indices = _cann_lightning_indexer(
102 index_query_tnd,
103 index_key_tnd,
104 merge_weight_tnd,
105 actual_seq_qlen,
106 actual_seq_kvlen,
107 sparse_count,
108 )
109 else:
110 logger.warning(
111 "dsa_indexer backend='omni' is deprecated and will be removed in a future release; "
112 "use backend='cann'."
113 )
114 import omni_training_custom_ops # noqa: F401 # pylint: disable=unused-import
116 topk_indices, _ = torch.ops.custom.npu_lightning_indexer_enhance(
117 index_query_tnd,
118 index_key_tnd,
119 merge_weight_tnd,
120 actual_seq_lengths_query=actual_seq_qlen,
121 actual_seq_lengths_key=actual_seq_kvlen,
122 block_table=None,
123 layout_query="TND",
124 layout_key="TND",
125 sparse_count=sparse_count,
126 sparse_mode=3,
127 return_value=False,
128 )
129 # pylint: enable=import-outside-toplevel
130 return topk_indices, index_query_tnd, index_key_tnd, merge_weight_tnd