class MiniMaxM3SparseAiterPAImpl(MiniMaxM3SparseImpl):
"""ROCm AITER page-16 SHUFFLE sparse paged attention."""
def forward(
self,
layer: AttentionLayer,
query: torch.Tensor,
kv_cache: torch.Tensor,
output: torch.Tensor,
) -> torch.Tensor:
from vllm.models.minimax_m3.amd.ops.sparse_pa import (
minimax_m3_sparse_attn_decode_aiter,
minimax_m3_sparse_attn_prefill_aiter,
)
attn_metadata = get_forward_context().attn_metadata
if not isinstance(attn_metadata, dict):
return output
main_md = attn_metadata[layer.layer_name] # type: ignore[attr-defined]
assert isinstance(main_md, MiniMaxM3SparseMetadata)
nd = main_md.num_decode_tokens
num_tokens = main_md.num_actual_tokens
topk = layer.topk_indices_buffer # type: ignore[attr-defined]
assert topk is not None
if self.num_kv_heads != 1:
raise NotImplementedError(
"MiniMax-M3 AITER sparse PA currently requires per-rank "
f"num_kv_heads == 1, got {self.num_kv_heads}"
)
hd = self.head_size
q = query[:num_tokens].view(-1, self.num_heads, hd)
out = output[:num_tokens].view(-1, self.num_heads, hd)
k_cache, v_cache = layer.get_aiter_sparse_pa_kv_cache() # type: ignore[attr-defined]
k_scale = getattr(layer, "_k_scale", None) if self.use_fp8_kv else None
v_scale = getattr(layer, "_v_scale", None) if self.use_fp8_kv else None
if main_md.num_decodes > 0:
d = main_md.decode
assert d is not None
if d.decode_query_len != 1:
raise NotImplementedError(
"MiniMax-M3 AITER sparse PA does not support speculative "
f"decode_query_len={d.decode_query_len}"
)
minimax_m3_sparse_attn_decode_aiter(
q[:nd],
k_cache,
v_cache,
topk[:, :nd, :],
d.block_table,
d.seq_lens,
self.num_kv_heads,
self.scale,
out[:nd],
k_scale=k_scale,
v_scale=v_scale,
)
if main_md.num_prefills > 0:
p = main_md.prefill
assert p is not None
minimax_m3_sparse_attn_prefill_aiter(
q[nd:],
k_cache,
v_cache,
topk[:, nd:num_tokens, :],
p.block_table,
p.cu_seqlens_q,
p.context_lens,
self.num_kv_heads,
self.scale,
out[nd:],
k_scale=k_scale,
v_scale=v_scale,
)
return output