chore: import upstream snapshot with attribution
PR Test (NPU) / check-changes (push) Has been cancelled
PR Test (NPU) / pr-gate (push) Has been cancelled
PR Test (NPU) / set-image-config (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-1-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (0) (push) Has been cancelled
PR Test (NPU) / stage-b-test-2-npu-a2 (1) (push) Has been cancelled
PR Test (NPU) / stage-b-test-4-npu-a3 (push) Has been cancelled
PR Test (NPU) / stage-b-test-16-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-1-npu-a3 (push) Has been cancelled
PR Test (NPU) / multimodal-gen-test-2-npu-a3 (push) Has been cancelled
PR Test (Arm64) / pr-gate (push) Has been cancelled
PR Test (Arm64) / check-changes (push) Has been cancelled
PR Test (Arm64) / build-test (push) Has been cancelled
PR Test (sgl-router) / gate (push) Has been cancelled
PR Test (sgl-router) / tier-1 — lint (push) Has been cancelled
PR Test (sgl-router) / tier-2 — build + test (push) Has been cancelled
PR Test (sgl-router) / tier-3 — docker (placeholder) (push) Has been cancelled
PR Test (sgl-router) / tier-3 — k8s integration (push) Has been cancelled
PR Test (sgl-router) / tier-3 — e2e (push) Has been cancelled
PR Test (sgl-router) / finish (push) Has been cancelled
PR Test (NPU) / single-node-poc (map[name:qwen3_6_27b_w8a8_1p_in64k_out1k_50ms runner:linux-aarch64-a3-2 test_case:test/registered/ascend/performance/qwen3_6_27b/test_npu_qwen3_6_27b_w8a8_1p_in64k_out1k_50ms.py test_type:perf]) (push) Has been cancelled
PR Test (NPU) / pr-test-npu-finish (push) Has been cancelled
PR Test (Xeon) / pr-gate (push) Has been cancelled
PR Test (Xeon) / check-changes (push) Has been cancelled
PR Test (Xeon) / build-test (, xeon-gnr, base-b-test-cpu) (push) Has been cancelled
PR Test (XPU) / check-changes (push) Has been cancelled
PR Test (XPU) / pr-gate (push) Has been cancelled
PR Test (XPU) / stage-a-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / wait-for-stage-a (push) Has been cancelled
PR Test (XPU) / stage-b-test-1-gpu-xpu (push) Has been cancelled
PR Test (XPU) / finish (push) Has been cancelled
CI Model Inventory / build-inventory (push) Has been cancelled
Lint / lint (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Compilation Check (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Manual Policy (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark - Request Processing (push) Has been cancelled
PR Benchmark (SMG Components) / Benchmark Summary (push) Has been cancelled
PR Test (SMG) / build-wheel (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on windows (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (x86_64 - auto) (push) Has been cancelled
PR Test (SMG) / python-unit-tests (push) Has been cancelled
PR Test (SMG) / unit-tests (push) Has been cancelled
PR Test (SMG) / benchmarks (push) Has been cancelled
PR Test (SMG) / chat-completions (push) Has been cancelled
PR Test (SMG) / chat-completions-4gpu (push) Has been cancelled
PR Test (SMG) / e2e (push) Has been cancelled
PR Test (SMG) / docker-build-test (push) Has been cancelled
PR Test (SMG) / k8s-integration (push) Has been cancelled
PR Test (SMG) / finish (push) Has been cancelled
PR Test (SMG) / summarize-benchmarks (push) Has been cancelled
Release SGLang Model Gateway Docker Image / publish (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on macos (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - auto) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (aarch64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / build on linux (x86_64 - musllinux_1_1) (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Build SDist (push) Has been cancelled
Release SGLang Model Gateway to PyPI / Upload to PyPI (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (aarch64, 12.9, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu129-matrix (x86_64, 12.9, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu129 (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (aarch64, 13.0, 3.10, arm-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / build-cu130-matrix (x86_64, 13.0, 3.10, x64-kernel-build-node) (push) Has been cancelled
Release SGLang Kernels / release-cu130 (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 700) (push) Has been cancelled
Release SGLang Kernels / build-rocm-matrix (3.10, 720) (push) Has been cancelled
Release SGLang Kernels / release-rocm700 (push) Has been cancelled
Release SGLang Kernels / release-rocm720 (push) Has been cancelled
Release SGLang Kernels / build-musa43 (43, 3.10) (push) Has been cancelled
Release SGLang Kernels / release-musa43 (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:38:16 +08:00
commit 94057c3d3e
7152 changed files with 2120455 additions and 0 deletions
@@ -0,0 +1,13 @@
from sglang.srt.mem_cache.sparsity.algorithms.base_algorithm import (
BaseSparseAlgorithm,
BaseSparseAlgorithmImpl,
)
from sglang.srt.mem_cache.sparsity.algorithms.deepseek_dsa import DeepSeekDSAAlgorithm
from sglang.srt.mem_cache.sparsity.algorithms.quest_algorithm import QuestAlgorithm
__all__ = [
"BaseSparseAlgorithm",
"BaseSparseAlgorithmImpl",
"DeepSeekDSAAlgorithm",
"QuestAlgorithm",
]
@@ -0,0 +1,383 @@
from abc import ABC, abstractmethod
from typing import TYPE_CHECKING
import torch
if TYPE_CHECKING:
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
class BaseSparseAlgorithm(ABC):
"""
Abstract base class for sparse attention algorithms.
This class provides a unified interface for implementing various retrievable KVCache
compression algorithms. Token-wise sparsity is treated as page-wise with page_size=1.
References:
- ChunkKV: https://arxiv.org/abs/2502.00299
- Quest: https://arxiv.org/pdf/2406.10774
- PQCache: https://arxiv.org/abs/2407.12820
- SnapKV: https://arxiv.org/pdf/2404.14469
- Look-ahead QCache: https://arxiv.org/pdf/2505.20334
- and more...
"""
def __init__(self, config, device: torch.device, **kwargs):
self.config = config
self.device = device
self.req_to_token_pool = None
self.states = None
def initialize_representation_pool(
self,
start_layer: int,
end_layer: int,
token_to_kv_pool,
req_to_token_pool,
states,
):
"""
Initialize algorithm-specific representation pool and set context.
Called once during SparseCoordinator initialization. Algorithms allocate
their own representation tensors and store references to context.
Algorithm-specific implementations:
- ChunkKV: Allocate chunk scores [num_chunks, 1] for tracking semantic chunk importance
- Quest: Allocate page representations [num_pages, repr_dim] via key pooling
- PQCache: Allocate centroids [n_subvec, n_centroids, subvec_dim] and token codes [num_tokens, n_subvec]
- SnapKV: Allocate voting scores [num_tokens] and selected positions mask for retention strategy
- Look-ahead QCache: Allocate importance scores [num_tokens], eviction mask, and optional pseudo query cache [cache_size, hidden_dim]
"""
pass
def construct_representations(
self,
layer_id: int,
req_pool_indices: torch.Tensor,
seq_lens: torch.Tensor,
k_buffer: torch.Tensor,
forward_batch: "ForwardBatch",
):
"""
Construct initial representations during prefill phase.
Called at every layer during forward pass. Algorithm internally decides
whether to perform construction.
Typically only constructs once per request during prefill/extend phase.
Algorithm-specific implementations:
- ChunkKV: Compute chunk importance scores via aggregated key L2 norms within semantic chunks
- Quest: Compute page representations via mean pooling of keys within each page
- PQCache: Run K-means clustering to generate centroids and assign each token to nearest centroid
- SnapKV: Select observation window (recent tokens), compute attention weights, aggregate via voting to identify important prefix positions, apply 1D pooling to preserve context
- Look-ahead QCache: Generate pseudo lookahead query (e.g., mean of last k queries), compute KV importance scores, mark low-importance KVs for eviction
"""
pass
def update_representations(
self,
layer_id: int,
req_pool_indices: torch.Tensor,
seq_lens: torch.Tensor,
k_buffer: torch.Tensor,
forward_batch: "ForwardBatch",
):
"""
Incrementally update representations during decode phase.
Called at every layer during forward pass. Algorithm internally decides
whether to update based on:
- self.states.repr_constructed[req_id]: Whether initial construction done
- self.states.last_constructed_page[req_id]: Last constructed page index
- Current seq_lens: To detect new tokens/pages
Algorithm-specific implementations:
- ChunkKV: Incrementally compute importance scores for newly generated chunks during decode
- Quest: Incrementally compute representations for newly generated pages during decode
- PQCache: Assign new tokens to existing centroids (no centroid update during decode)
- SnapKV: Optional: periodically re-run voting with sliding observation window (typically static after prefill)
- Look-ahead QCache: Periodically regenerate pseudo queries and re-evaluate importance scores to adapt to generation dynamics
"""
pass
@abstractmethod
def retrieve_topk(
self,
queries: torch.Tensor,
layer_id: int,
req_pool_indices: torch.Tensor,
sparse_mask: torch.Tensor,
**kwargs,
) -> tuple:
"""
Retrieve top-k important KV indices for sparse attention.
Called before attention computation at each layer. Uses current query
and pre-computed representations to select the most important subset
of KV cache for attention computation.
Args:
queries: [bs, num_heads, head_dim] Current query vectors
layer_id: Current layer index
req_pool_indices: [bs] Request pool indices
sparse_mask: [bs] bool, which requests need sparse attention
attn_metadata: Attention metadata (contains seq_lens, etc.)
**kwargs: Algorithm-specific arguments
Returns:
selected_indices: [bs, max_selected] Selected page/token indices, padded with -1
valid_lengths: [bs] Actual number of selected indices per request
Note:
- Indices are logical positions that will be mapped to physical KV cache by BackendAdaptor
Algorithm-specific implementations:
- ChunkKV: Select top-k chunks based on pre-computed importance scores with layer-wise index reuse
- Quest: Compute query-page similarity using current query and stored page representations, select top-k pages
- PQCache: Calculate query-centroid similarity, use centroid scores to rank tokens, select top-k tokens
- SnapKV: Return union of voted important prefix positions (with clustered neighbors) and observation window tokens
- Look-ahead QCache: Return KVs not marked for eviction (eviction based on pseudo query importance evaluation)
"""
pass
class BaseSparseAlgorithmImpl(BaseSparseAlgorithm):
"""
Implementation base class for sparse attention algorithms.
Provides common infrastructure for algorithms that operate at page/chunk granularity
(token-wise is simply page_size=1):
- Generic construct/update flow with state tracking
- TopK retrieval with recent page retention (can be overridden)
Subclasses need to implement:
- _initialize_representation_pools(): Initialize algorithm-specific representation pools
- _compute_page_representations(): Compute page scores/representations
- _retrieve_page_scores(): Retrieve page scores for TopK selection
Subclasses can also override any method for specialized behavior
"""
def __init__(self, config, device: torch.device, **kwargs):
super().__init__(config, device, **kwargs)
self.sparsity_ratio = config.sparse_extra_config.get("sparsity_ratio", 0.7)
self.num_recent_pages = config.sparse_extra_config.get("num_recent_pages", 4)
self.page_size = config.page_size
def initialize_representation_pool(
self,
start_layer: int,
end_layer: int,
token_to_kv_pool,
req_to_token_pool,
states,
):
self.req_to_token_pool = req_to_token_pool
self.token_to_kv_pool = token_to_kv_pool
self.start_layer = start_layer
self.end_layer = end_layer
self.states = states
total_num_tokens = token_to_kv_pool.get_key_buffer(start_layer).shape[0]
total_num_pages = (total_num_tokens + self.page_size - 1) // self.page_size
# Initialize algorithm-specific representation pools
self._initialize_representation_pools(start_layer, end_layer, total_num_pages)
def construct_representations(
self,
layer_id,
req_pool_indices,
seq_lens,
k_buffer,
forward_batch,
) -> torch.Tensor:
if not forward_batch.forward_mode.is_extend():
return
num_pages = seq_lens // self.page_size
valid_mask = (
~self.states.repr_constructed[req_pool_indices]
& (seq_lens >= self.states.prompt_lens[req_pool_indices])
& (num_pages > 0)
)
if not valid_mask.any():
return
# Compute page representations by subclass
self._compute_page_representations(
layer_id,
req_pool_indices[valid_mask],
seq_lens[valid_mask],
0,
num_pages[valid_mask],
k_buffer,
)
# Update tracking states
if layer_id == self.end_layer - 1:
success_indices = req_pool_indices[valid_mask]
self.states.repr_constructed[success_indices] = True
self.states.last_constructed_page[success_indices] = num_pages[valid_mask]
def update_representations(
self,
layer_id,
req_pool_indices,
seq_lens,
k_buffer,
forward_batch,
) -> torch.Tensor:
if not forward_batch.forward_mode.is_decode_or_idle():
return
start_page = self.states.last_constructed_page[req_pool_indices]
end_page = seq_lens // self.page_size
valid_mask = self.states.repr_constructed[req_pool_indices] & (
start_page < end_page
)
if not valid_mask.any():
return
# Compute page representations by subclass
self._compute_page_representations(
layer_id,
req_pool_indices[valid_mask],
seq_lens[valid_mask],
start_page[valid_mask],
end_page[valid_mask],
k_buffer,
)
# Update tracking states
if layer_id == self.end_layer - 1:
success_indices = req_pool_indices[valid_mask]
self.states.last_constructed_page[success_indices] = end_page[valid_mask]
def retrieve_topk(
self,
queries: torch.Tensor,
layer_id: int,
req_pool_indices: torch.Tensor,
sparse_mask: torch.Tensor,
**kwargs,
) -> tuple:
"""
Default TopK retrieval: score-based selection + recent pages.
Subclasses can override for query-dependent retrieval.
TODO:
1. Using triton kernel to speed up this function
2. Support CUDA Graph
"""
bs, device = queries.shape[0], queries.device
seq_lens_source = kwargs.get("forward_batch", None)
if seq_lens_source is None or not hasattr(seq_lens_source, "seq_lens"):
raise ValueError(
"forward_batch with seq_lens is required for TopK retrieval"
)
seq_lens = seq_lens_source.seq_lens.to(device)
req_to_token = self.req_to_token_pool.req_to_token
max_req_tokens = req_to_token.shape[1]
per_request_indices = []
per_request_lengths = []
for i in range(bs):
if not sparse_mask[i]:
per_request_indices.append(
torch.empty(0, device=device, dtype=torch.int32)
)
per_request_lengths.append(0)
continue
num_pages = int((seq_lens[i].item() + self.page_size - 1) // self.page_size)
if num_pages <= self.num_recent_pages:
per_request_indices.append(
torch.empty(0, device=device, dtype=torch.int32)
)
per_request_lengths.append(0)
continue
page_idx = torch.arange(num_pages, device=device)
page_start_token = req_to_token[
req_pool_indices[i],
(page_idx * self.page_size).clamp(0, max_req_tokens - 1),
]
phys_pages = (page_start_token // self.page_size).unsqueeze(0)
scores = self._retrieve_page_scores(
layer_id,
phys_pages,
req_pool_indices[i : i + 1],
queries[i : i + 1],
)
recent_start = max(num_pages - self.num_recent_pages, 0)
scores = scores.clone()
scores[:, recent_start:] = float("-inf")
history_pages = max(recent_start, 1)
k = max(int(history_pages * self.sparsity_ratio), 1)
k = min(k, history_pages)
topk_idx = torch.topk(scores, k=k, dim=1, sorted=False)[1].squeeze(0)
recent_idx = torch.arange(
recent_start, recent_start + self.num_recent_pages, device=device
)
recent_idx = recent_idx[recent_idx < num_pages]
combined = (
torch.cat([topk_idx, recent_idx], dim=0).sort()[0].to(torch.int32)
)
per_request_indices.append(combined)
per_request_lengths.append(int(combined.numel()))
max_len = max(max(per_request_lengths, default=0), 1)
out_indices = torch.full((bs, max_len), -1, dtype=torch.int32, device=device)
out_lengths = torch.zeros(bs, dtype=torch.int32, device=device)
for i, selected in enumerate(per_request_indices):
length = per_request_lengths[i]
if length == 0:
continue
out_indices[i, :length] = selected
out_lengths[i] = length
return out_indices, out_lengths
def _initialize_representation_pools(
self, start_layer: int, end_layer: int, total_num_pages: int
):
"""Initialize algorithm-specific representation pools for all layers."""
raise NotImplementedError
def _compute_page_representations(
self,
layer_id: int,
reqs: torch.Tensor,
seq_lens: torch.Tensor,
start_page,
end_page: torch.Tensor,
k_buffer: torch.Tensor,
):
"""Compute and store page representations for given page range."""
raise NotImplementedError
def _retrieve_page_scores(
self,
layer_id: int,
phys_pages: torch.Tensor,
req_pool_indices: torch.Tensor,
queries: torch.Tensor,
) -> torch.Tensor:
"""Retrieve page scores for TopK selection."""
raise NotImplementedError
@@ -0,0 +1,80 @@
from typing import Any, Optional
import torch
from sglang.srt.mem_cache.sparsity.algorithms.base_algorithm import (
BaseSparseAlgorithmImpl,
)
class DeepSeekDSAAlgorithm(BaseSparseAlgorithmImpl):
"""
Sparse attention algorithm for DeepSeek DSA.
This algorithm uses DSA's native indexer for TopK retrieval.
Overrides all parent methods as DSA has its own specialized flow.
"""
def __init__(self, config, device: torch.device, **kwargs):
super().__init__(config, device, **kwargs)
def retrieve_topk(
self,
queries: torch.Tensor,
layer_id: int,
req_pool_indices: torch.Tensor,
sparse_mask: torch.Tensor,
attn_metadata: Optional[Any],
**kwargs,
) -> tuple:
indexer, forward_batch, x, q_lora, positions = (
kwargs.get("indexer"),
kwargs.get("forward_batch"),
kwargs.get("x"),
kwargs.get("q_lora"),
kwargs.get("positions"),
)
if any(v is None for v in [indexer, x, q_lora, positions, forward_batch]):
raise ValueError("Required: indexer, forward_batch, x, q_lora, positions")
return (
indexer(
x=x,
q_lora=q_lora,
positions=positions,
forward_batch=forward_batch,
layer_id=layer_id,
),
None,
)
def initialize_representation_pool(
self,
start_layer: int,
end_layer: int,
token_to_kv_pool,
req_to_token_pool,
states,
):
pass
def construct_representations(
self,
layer_id,
req_pool_indices,
seq_lens,
k_buffer,
forward_batch,
):
pass
def update_representations(
self,
layer_id,
req_pool_indices,
seq_lens,
k_buffer,
forward_batch,
):
pass
@@ -0,0 +1,166 @@
"""
Quest sparse attention algorithm.
This implementation follows the Quest paper's bounding-box estimation for
query-aware page selection. For each KV page, it maintains per-dimension
min/max of keys and uses them to upper-bound attention scores without
materializing full dot products.
"""
import logging
import torch
from sglang.srt.mem_cache.sparsity.algorithms.base_algorithm import (
BaseSparseAlgorithmImpl,
)
logger = logging.getLogger(__name__)
class QuestAlgorithm(BaseSparseAlgorithmImpl):
"""Quest page-wise sparse attention using bounding-box criticality."""
def __init__(self, config, device: torch.device, **kwargs):
super().__init__(config, device, **kwargs)
self.page_k_min = {}
self.page_k_max = {}
self.page_valid = {}
def _initialize_representation_pools(
self, start_layer: int, end_layer: int, total_num_pages: int
):
key_buf = self.token_to_kv_pool.get_key_buffer(start_layer)
head_num, head_dim = key_buf.shape[1], key_buf.shape[2]
for layer_id in range(start_layer, end_layer):
self.page_k_min[layer_id] = torch.zeros(
(total_num_pages, head_num, head_dim),
dtype=torch.float32,
device=self.device,
)
self.page_k_max[layer_id] = torch.zeros_like(self.page_k_min[layer_id])
self.page_valid[layer_id] = torch.zeros(
total_num_pages, dtype=torch.bool, device=self.device
)
logger.info(
"Initialized Quest page reps: %d pages, %d layers, head_num=%d, head_dim=%d",
total_num_pages,
end_layer - start_layer,
head_num,
head_dim,
)
def _compute_page_representations(
self,
layer_id: int,
reqs: torch.Tensor,
seq_lens: torch.Tensor,
start_page,
end_page: torch.Tensor,
k_buffer: torch.Tensor,
):
if isinstance(start_page, int):
start_page = torch.full_like(end_page, start_page)
device = k_buffer.device
req_to_token = self.req_to_token_pool.req_to_token
n = reqs.shape[0]
max_pages = int((end_page - start_page).max().item())
if max_pages <= 0:
return
pg_off = torch.arange(max_pages, device=device).unsqueeze(0)
pg_id = start_page.unsqueeze(1) + pg_off
pg_mask = pg_id < end_page.unsqueeze(1)
tok_start = pg_id * self.page_size
tok_off = torch.arange(self.page_size, device=device).view(1, 1, -1)
tok_pos = tok_start.unsqueeze(2) + tok_off
tok_mask = (
tok_pos
< (tok_start + self.page_size).clamp(max=seq_lens.unsqueeze(1)).unsqueeze(2)
) & pg_mask.unsqueeze(2)
phys_tok = req_to_token[
reqs.view(n, 1, 1).expand(n, max_pages, self.page_size),
tok_pos.clamp(0, req_to_token.shape[1] - 1),
].clamp(0, k_buffer.shape[0] - 1)
keys = k_buffer[phys_tok].to(torch.float32)
mask = tok_mask.unsqueeze(-1).unsqueeze(-1)
page_min = torch.where(mask, keys, torch.full_like(keys, float("inf"))).amin(
dim=2
)
page_max = torch.where(mask, keys, torch.full_like(keys, float("-inf"))).amax(
dim=2
)
phys_pg = (
req_to_token[
reqs.unsqueeze(1).expand(n, max_pages),
tok_start.clamp(0, req_to_token.shape[1] - 1),
]
// self.page_size
)
idx = pg_mask.nonzero(as_tuple=False)
if idx.numel() == 0:
return
target_pages = phys_pg[idx[:, 0], idx[:, 1]].clamp(
0, self.page_k_min[layer_id].shape[0] - 1
)
self.page_k_min[layer_id][target_pages] = page_min[idx[:, 0], idx[:, 1]]
self.page_k_max[layer_id][target_pages] = page_max[idx[:, 0], idx[:, 1]]
self.page_valid[layer_id][target_pages] = True
def _retrieve_page_scores(
self,
layer_id: int,
phys_pages: torch.Tensor,
req_pool_indices: torch.Tensor,
queries: torch.Tensor,
) -> torch.Tensor:
# Clamp pages to valid storage range
phys_pages_clamped = phys_pages.clamp(0, self.page_k_min[layer_id].shape[0] - 1)
k_min = self.page_k_min[layer_id][phys_pages_clamped]
k_max = self.page_k_max[layer_id][phys_pages_clamped]
valid_mask = self.page_valid[layer_id][phys_pages_clamped]
# Align query shape to KV heads.
head_dim = k_min.shape[-1]
if queries.dim() == 2:
bs, hidden = queries.shape
if hidden % head_dim != 0:
raise ValueError(
f"Quest query hidden size {hidden} not divisible by head_dim {head_dim}"
)
q_heads = hidden // head_dim
q = queries.view(bs, q_heads, head_dim)
elif queries.dim() == 3:
q = queries
else:
raise ValueError(f"Unsupported query shape for Quest: {queries.shape}")
kv_heads = k_min.shape[-2]
q_heads = q.shape[1]
if q_heads != kv_heads:
if q_heads % kv_heads != 0:
raise ValueError(
f"Query heads {q_heads} not divisible by KV heads {kv_heads}"
)
group = q_heads // kv_heads
# Average grouped query heads to align with KV heads (approximation for MQA/GQA).
q = q.view(q.shape[0], kv_heads, group, head_dim).mean(dim=2)
q = q.to(k_min.dtype).unsqueeze(1) # [bs, 1, kv_heads, head_dim]
criticality = torch.where(q >= 0, q * k_max, q * k_min).sum(dim=(2, 3))
criticality = torch.where(
valid_mask, criticality, torch.full_like(criticality, float("-inf"))
)
return criticality