#[must_use]
pub fn sparse_topk_select(scores: &[f32], topk: usize, num_valid_blocks: usize) -> Vec<i32> {
let n = scores.len().min(num_valid_blocks);
if topk == 0 {
return Vec::new();
}
if n == 0 {
return vec![-1; topk];
}
let k = topk.min(n);
let mut heap_scores: Vec<f32> = vec![f32::NEG_INFINITY; k];
let mut heap_ids: Vec<i32> = vec![-1; k];
for (i, &raw_score) in scores.iter().take(n).enumerate() {
let score = if raw_score.is_nan() {
f32::NEG_INFINITY
} else {
raw_score
};
if score <= heap_scores[0] {
continue;
}
let mut pos: usize = 0;
while pos + 1 < k && heap_scores[pos + 1] < score {
heap_scores[pos] = heap_scores[pos + 1];
heap_ids[pos] = heap_ids[pos + 1];
pos += 1;
}
heap_scores[pos] = score;
heap_ids[pos] = i as i32;
}
let mut out = Vec::with_capacity(topk);
for slot in 0..k {
let src = k - 1 - slot;
if heap_scores[src].is_finite() {
out.push(heap_ids[src]);
} else {
out.push(-1);
}
}
for _ in k..topk {
out.push(-1);
}
out
}
#[must_use]
pub fn sparse_topk_select_batch(
scores: &[f32],
rows: usize,
num_blocks: usize,
topk: usize,
num_valid_blocks: &[usize],
) -> Vec<i32> {
assert_eq!(scores.len(), rows * num_blocks);
assert_eq!(num_valid_blocks.len(), rows);
let mut out = Vec::with_capacity(rows * topk);
for r in 0..rows {
let row = &scores[r * num_blocks..(r + 1) * num_blocks];
let cap = num_valid_blocks[r].min(num_blocks);
out.extend(sparse_topk_select(row, topk, cap));
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn topk_selects_largest_in_descending_order() {
let scores = vec![0.1, 5.0, 3.0, 4.0, 2.0, 1.0];
let out = sparse_topk_select(&scores, 3, scores.len());
assert_eq!(out, vec![1, 3, 2]);
}
#[test]
fn topk_pads_when_topk_exceeds_valid_blocks() {
let scores = vec![1.0, 2.0];
let out = sparse_topk_select(&scores, 4, scores.len());
assert_eq!(out, vec![1, 0, -1, -1]);
}
#[test]
fn topk_respects_num_valid_blocks() {
let scores = vec![0.1, 5.0, 100.0, 4.0];
let out = sparse_topk_select(&scores, 2, 2);
assert_eq!(out, vec![1, 0]);
}
#[test]
fn topk_handles_nan_as_neg_inf() {
let scores = vec![f32::NAN, 1.0, f32::NAN, 2.0];
let out = sparse_topk_select(&scores, 3, scores.len());
assert_eq!(out, vec![3, 1, -1]);
}
#[test]
fn topk_stable_across_ties_by_first_occurrence() {
let scores = vec![1.0, 2.0, 2.0, 2.0];
let out = sparse_topk_select(&scores, 2, scores.len());
assert_eq!(out, vec![1, 2]);
}
#[test]
fn topk_zero_topk_returns_empty() {
let out = sparse_topk_select(&[1.0, 2.0], 0, 2);
assert!(out.is_empty());
}
#[test]
fn topk_empty_valid_blocks_returns_all_padding() {
let out = sparse_topk_select(&[1.0, 2.0], 3, 0);
assert_eq!(out, vec![-1, -1, -1]);
}
#[test]
fn topk_batch_selects_per_row() {
let scores = vec![0.1, 5.0, 3.0, 4.0, 9.0, 0.0, 0.0, 0.0, 1.0, 2.0, 3.0, 4.0];
let caps = vec![4usize, 4, 2];
let out = sparse_topk_select_batch(&scores, 3, 4, 2, &caps);
assert_eq!(&out[0..2], &[1, 3]);
assert_eq!(&out[2..4], &[0, 1]);
assert_eq!(&out[4..6], &[1, 0]);
}
#[test]
fn topk_all_neg_inf_scores_yield_padding() {
let scores = vec![f32::NEG_INFINITY; 4];
let out = sparse_topk_select(&scores, 2, 4);
assert_eq!(out, vec![-1, -1]);
}
}