use ndarray::{Array1, Array2, ArrayView1};
use rand::SeedableRng;
use rand::rngs::StdRng;
use std::collections::{HashMap, HashSet};
const INDEX_HYPERPLANE_SALT: u64 = 0x9E37_79B9_7F4A_7C15;
const SKETCH_PROJECTION_SALT: u64 = 0xC2B2_AE3D_27D4_EB4F;
const DIRECTION_NORM_FLOOR: f64 = 1e-12;
pub const CANDIDATE_BUDGET_MIN: usize = 32;
pub const CANDIDATE_BUDGET_MAX: usize = 128;
pub fn auto_candidate_budget(num_atoms: usize) -> usize {
let log2 = if num_atoms <= 1 {
1
} else {
(usize::BITS - (num_atoms - 1).leading_zeros()) as usize
};
(8 * log2).clamp(CANDIDATE_BUDGET_MIN, CANDIDATE_BUDGET_MAX)
}
pub fn routability_shortlist_size(p: usize, num_atoms: usize, top_s: usize, delta: f64) -> usize {
let k = num_atoms.max(1);
let floor = crate::routability::routability_floor(p.max(1), k, 1, delta);
let subspace = (1.0 / p.max(1) as f64).sqrt();
let union = (floor.floor - subspace).max(0.0);
let band = (p.max(1) as f64) * union * union;
let c = top_s.saturating_add(band.ceil() as usize);
c.clamp(top_s.saturating_add(1), k)
}
pub trait AtomFrameSketch {
fn sketch_dim(&self) -> usize;
fn output_dim(&self) -> usize;
fn num_atoms(&self) -> usize;
fn atom_sketch(&self, atom_id: usize) -> Array1<f64>;
fn atom_bucket_sketches(&self, atom_id: usize) -> Vec<Array1<f64>> {
vec![self.atom_sketch(atom_id)]
}
fn project_direction(&self, atom_id: usize, direction: ArrayView1<f64>) -> Array1<f64>;
fn alignment(&self, atom_id: usize, direction: ArrayView1<f64>) -> f64;
fn query_sketch(&self, direction: ArrayView1<f64>) -> Array1<f64>;
}
pub struct RandomProjectionFrameSketch {
frames: Vec<Array2<f64>>,
projection: Array2<f64>,
output_dim: usize,
sketch_dim: usize,
}
impl RandomProjectionFrameSketch {
pub fn from_decoder_blocks(
decoder_blocks: &[Array2<f64>],
sketch_dim: usize,
seed: u64,
) -> Result<Self, String> {
if decoder_blocks.is_empty() {
return Err("RandomProjectionFrameSketch: need at least one decoder block".into());
}
if sketch_dim == 0 {
return Err("RandomProjectionFrameSketch: sketch_dim must be positive".into());
}
let output_dim = decoder_blocks[0].nrows();
if output_dim == 0 {
return Err("RandomProjectionFrameSketch: output dimension must be positive".into());
}
for (k, block) in decoder_blocks.iter().enumerate() {
if block.nrows() != output_dim {
return Err(format!(
"RandomProjectionFrameSketch: atom {k} has {} output rows, expected {output_dim}",
block.nrows()
));
}
}
let frames: Vec<Array2<f64>> = decoder_blocks.iter().map(orthonormal_frame).collect();
let projection = gaussian_projection(sketch_dim, output_dim, seed ^ SKETCH_PROJECTION_SALT);
Ok(Self {
frames,
projection,
output_dim,
sketch_dim,
})
}
fn in_range_component(&self, atom_id: usize, direction: ArrayView1<f64>) -> Array1<f64> {
let frame = &self.frames[atom_id];
let mut comp = Array1::<f64>::zeros(self.output_dim);
for col in 0..frame.ncols() {
let u = frame.column(col);
let coord: f64 = u.iter().zip(direction.iter()).map(|(&a, &b)| a * b).sum();
for (c, &uval) in comp.iter_mut().zip(u.iter()) {
*c += coord * uval;
}
}
comp
}
}
impl AtomFrameSketch for RandomProjectionFrameSketch {
fn sketch_dim(&self) -> usize {
self.sketch_dim
}
fn output_dim(&self) -> usize {
self.output_dim
}
fn num_atoms(&self) -> usize {
self.frames.len()
}
fn atom_sketch(&self, atom_id: usize) -> Array1<f64> {
let frame = &self.frames[atom_id];
if frame.ncols() == 0 {
let mut s = self.projection.column(0).to_owned();
normalize_in_place(&mut s);
return s;
}
let u0 = frame.column(0);
let mut s = mat_vec(&self.projection, u0);
normalize_in_place(&mut s);
s
}
fn atom_bucket_sketches(&self, atom_id: usize) -> Vec<Array1<f64>> {
let frame = &self.frames[atom_id];
if frame.ncols() == 0 {
return vec![self.atom_sketch(atom_id)];
}
let r = frame.ncols();
let mut sketches = Vec::with_capacity(r * r);
for col in 0..r {
let mut sk = mat_vec(&self.projection, frame.column(col));
normalize_in_place(&mut sk);
sketches.push(sk);
}
for i in 0..r {
for j in (i + 1)..r {
for &sign in &[1.0_f64, -1.0] {
let mut dir = frame.column(i).to_owned();
dir.scaled_add(sign, &frame.column(j));
let mut sk = mat_vec(&self.projection, dir.view());
normalize_in_place(&mut sk);
sketches.push(sk);
}
}
}
sketches
}
fn project_direction(&self, atom_id: usize, direction: ArrayView1<f64>) -> Array1<f64> {
let comp = self.in_range_component(atom_id, direction);
mat_vec(&self.projection, comp.view())
}
fn query_sketch(&self, direction: ArrayView1<f64>) -> Array1<f64> {
let mut s = mat_vec(&self.projection, direction);
normalize_in_place(&mut s);
s
}
fn alignment(&self, atom_id: usize, direction: ArrayView1<f64>) -> f64 {
let dnorm = vec_norm(direction);
if dnorm < DIRECTION_NORM_FLOOR {
return 0.0;
}
let comp = self.in_range_component(atom_id, direction);
(vec_norm(comp.view()) / dnorm).clamp(0.0, 1.0)
}
}
pub struct SaeCandidateIndex {
hyperplanes: Vec<Array2<f64>>,
tables: Vec<HashMap<u64, Vec<usize>>>,
sketch_dim: usize,
num_atoms: usize,
}
#[derive(Clone, Copy, Debug)]
pub struct IndexConfig {
pub num_tables: usize,
pub bits_per_table: usize,
pub multiprobe: bool,
pub seed: u64,
}
impl IndexConfig {
pub fn auto(sketch_dim: usize, num_atoms: usize, seed: u64) -> Self {
let log2 = |n: usize| -> usize {
if n <= 1 {
1
} else {
(usize::BITS - (n - 1).leading_zeros()) as usize
}
};
let bits = log2(num_atoms.max(2)).clamp(1, sketch_dim.max(1).min(63));
let num_tables = log2(num_atoms.max(2)).clamp(4, 16);
Self {
num_tables,
bits_per_table: bits,
multiprobe: true,
seed,
}
}
}
impl SaeCandidateIndex {
pub fn build<S: AtomFrameSketch>(sketch: &S, config: IndexConfig) -> Result<Self, String> {
let sketch_dim = sketch.sketch_dim();
if sketch_dim == 0 {
return Err("SaeCandidateIndex: sketch_dim must be positive".into());
}
if config.num_tables == 0 || config.bits_per_table == 0 {
return Err("SaeCandidateIndex: num_tables and bits_per_table must be positive".into());
}
if config.bits_per_table > 63 {
return Err(format!(
"SaeCandidateIndex: bits_per_table {} exceeds 63 (u64 signature limit)",
config.bits_per_table
));
}
let num_atoms = sketch.num_atoms();
let hyperplanes: Vec<Array2<f64>> = (0..config.num_tables)
.map(|t| {
let table_seed = mix_seed(config.seed ^ INDEX_HYPERPLANE_SALT, t as u64);
gaussian_projection(config.bits_per_table, sketch_dim, table_seed)
})
.collect();
let mut tables: Vec<HashMap<u64, Vec<usize>>> =
(0..config.num_tables).map(|_| HashMap::new()).collect();
for atom_id in 0..num_atoms {
let bucket_sketches = sketch.atom_bucket_sketches(atom_id);
if bucket_sketches.is_empty() {
return Err(format!(
"SaeCandidateIndex: atom {atom_id} produced no bucket representatives"
));
}
for s in &bucket_sketches {
if s.len() != sketch_dim {
return Err(format!(
"SaeCandidateIndex: atom {atom_id} sketch length {} != sketch_dim {sketch_dim}",
s.len()
));
}
for (table, bank) in tables.iter_mut().zip(hyperplanes.iter()) {
let sig = sign_signature(bank, s.view());
let bucket = table.entry(sig).or_default();
if bucket.last() != Some(&atom_id) {
bucket.push(atom_id);
}
}
}
}
Ok(Self {
hyperplanes,
tables,
sketch_dim,
num_atoms,
})
}
pub fn num_atoms(&self) -> usize {
self.num_atoms
}
pub fn gather_candidates(&self, query_sketch: ArrayView1<f64>, multiprobe: bool) -> Vec<usize> {
let mut seen: HashSet<usize> = HashSet::new();
for (table, bank) in self.tables.iter().zip(self.hyperplanes.iter()) {
let (sig, margins) = sign_signature_with_margins(bank, query_sketch);
if let Some(ids) = table.get(&sig) {
seen.extend(ids.iter().copied());
}
if multiprobe {
let flip_bit = lowest_margin_bit(&margins);
let neighbour = canonical_signature(sig ^ (1u64 << flip_bit), bank.nrows());
if let Some(ids) = table.get(&neighbour) {
seen.extend(ids.iter().copied());
}
}
}
let mut out: Vec<usize> = seen.into_iter().collect();
out.sort_unstable();
out
}
pub fn propose<S: AtomFrameSketch>(
&self,
sketch: &S,
direction: ArrayView1<f64>,
candidate_budget: usize,
config_multiprobe: bool,
) -> Proposal {
let query_sketch = sketch.query_sketch(direction);
let gathered = if query_sketch.len() == self.sketch_dim {
self.gather_candidates(query_sketch.view(), config_multiprobe)
} else {
Vec::new()
};
let mut scored: Vec<(usize, f64)> = gathered
.iter()
.map(|&id| (id, sketch.alignment(id, direction)))
.collect();
scored.sort_by(|a, b| {
b.1.partial_cmp(&a.1)
.unwrap_or(std::cmp::Ordering::Equal)
.then(a.0.cmp(&b.0))
});
let keep = candidate_budget.min(scored.len());
let proposed: Vec<usize> = scored[..keep].iter().map(|&(id, _)| id).collect();
let dropped_for_budget: Vec<usize> = scored[keep..].iter().map(|&(id, _)| id).collect();
Proposal {
proposed,
dropped_for_budget,
gathered_count: gathered.len(),
}
}
pub fn recall_report<S: AtomFrameSketch>(
&self,
sketch: &S,
rows: &[(Array1<f64>, Vec<usize>)],
candidate_budget: usize,
multiprobe: bool,
) -> RecallReport {
let mut total_planted: usize = 0;
let mut total_recovered: usize = 0;
let mut misses: Vec<RecallMiss> = Vec::new();
let mut total_gathered: usize = 0;
for (row_idx, (direction, planted)) in rows.iter().enumerate() {
let proposal = self.propose(sketch, direction.view(), candidate_budget, multiprobe);
total_gathered += proposal.gathered_count;
let proposed_set: HashSet<usize> = proposal.proposed.iter().copied().collect();
let dropped_set: HashSet<usize> = proposal.dropped_for_budget.iter().copied().collect();
for &atom in planted {
total_planted += 1;
if proposed_set.contains(&atom) {
total_recovered += 1;
} else {
let reason = if dropped_set.contains(&atom) {
MissReason::TruncatedByBudget
} else {
MissReason::NotGathered
};
misses.push(RecallMiss {
row: row_idx,
atom,
alignment: sketch.alignment(atom, direction.view()),
reason,
});
}
}
}
let recall = if total_planted == 0 {
1.0
} else {
total_recovered as f64 / total_planted as f64
};
let avg_gathered = if rows.is_empty() {
0.0
} else {
total_gathered as f64 / rows.len() as f64
};
RecallReport {
candidate_budget,
num_rows: rows.len(),
total_planted,
total_recovered,
recall,
avg_candidates_gathered: avg_gathered,
num_atoms: self.num_atoms,
misses,
}
}
pub fn proposal_recall_report<S: AtomFrameSketch>(
&self,
sketch: &S,
directions: &[Array1<f64>],
top_s: usize,
candidate_budget: usize,
multiprobe: bool,
) -> ProposalRecallReport {
let mut total_true: usize = 0;
let mut total_recovered: usize = 0;
let mut total_gathered: usize = 0;
let mut misses: Vec<RecallMiss> = Vec::new();
for (row_idx, direction) in directions.iter().enumerate() {
let exact = brute_force_top_s(sketch, direction.view(), top_s);
let proposal = self.propose(sketch, direction.view(), candidate_budget, multiprobe);
total_gathered += proposal.gathered_count;
let proposed_set: HashSet<usize> = proposal.proposed.iter().copied().collect();
let dropped_set: HashSet<usize> = proposal.dropped_for_budget.iter().copied().collect();
for &atom in &exact {
total_true += 1;
if proposed_set.contains(&atom) {
total_recovered += 1;
} else {
let reason = if dropped_set.contains(&atom) {
MissReason::TruncatedByBudget
} else {
MissReason::NotGathered
};
misses.push(RecallMiss {
row: row_idx,
atom,
alignment: sketch.alignment(atom, direction.view()),
reason,
});
}
}
}
let recall = if total_true == 0 {
1.0
} else {
total_recovered as f64 / total_true as f64
};
let avg_gathered = if directions.is_empty() {
0.0
} else {
total_gathered as f64 / directions.len() as f64
};
ProposalRecallReport {
candidate_budget,
top_s,
num_rows: directions.len(),
total_true,
total_recovered,
recall,
avg_candidates_gathered: avg_gathered,
num_atoms: self.num_atoms,
misses,
}
}
pub fn route_exact<S: AtomFrameSketch>(
&self,
sketch: &S,
direction: ArrayView1<f64>,
candidate_budget: usize,
multiprobe: bool,
) -> Option<ExactRoute> {
let proposal = self.propose(sketch, direction, candidate_budget, multiprobe);
let lsh_best = proposal
.proposed
.first()
.copied()
.map(|id| (id, sketch.alignment(id, direction)));
if let Some((b, a_b)) = lsh_best {
if a_b.is_finite() && a_b >= ROUTING_ALIGNMENT_UPPER_BOUND - ROUTING_CERT_EPS {
return Some(ExactRoute {
atom: b,
alignment: a_b,
lsh_certified: true,
lsh_agreed: true,
did_full_scan: false,
});
}
}
let (atom, alignment) = brute_force_best_atom(sketch, direction)?;
let lsh_agreed = lsh_best.is_some_and(|(b, _)| b == atom);
Some(ExactRoute {
atom,
alignment,
lsh_certified: false,
lsh_agreed,
did_full_scan: true,
})
}
}
pub const ROUTING_ALIGNMENT_UPPER_BOUND: f64 = 1.0;
pub const ROUTING_CERT_EPS: f64 = 1e-12;
pub fn brute_force_best_atom<S: AtomFrameSketch>(
sketch: &S,
direction: ArrayView1<f64>,
) -> Option<(usize, f64)> {
let mut best: Option<(usize, f64)> = None;
for id in 0..sketch.num_atoms() {
let a = sketch.alignment(id, direction);
if !a.is_finite() {
continue;
}
match best {
Some((_, ba)) if a <= ba => {}
_ => best = Some((id, a)),
}
}
best
}
pub fn brute_force_top_s<S: AtomFrameSketch>(
sketch: &S,
direction: ArrayView1<f64>,
s: usize,
) -> Vec<usize> {
let mut scored: Vec<(usize, f64)> = (0..sketch.num_atoms())
.filter_map(|id| {
let a = sketch.alignment(id, direction);
a.is_finite().then_some((id, a))
})
.collect();
scored.sort_by(|x, y| {
y.1.partial_cmp(&x.1)
.unwrap_or(std::cmp::Ordering::Equal)
.then(x.0.cmp(&y.0))
});
scored.into_iter().take(s).map(|(id, _)| id).collect()
}
#[derive(Clone, Copy, Debug)]
pub struct ExactRoute {
pub atom: usize,
pub alignment: f64,
pub lsh_certified: bool,
pub lsh_agreed: bool,
pub did_full_scan: bool,
}
#[derive(Clone, Debug)]
pub struct Proposal {
pub proposed: Vec<usize>,
pub dropped_for_budget: Vec<usize>,
pub gathered_count: usize,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum MissReason {
NotGathered,
TruncatedByBudget,
}
#[derive(Clone, Copy, Debug)]
pub struct RecallMiss {
pub row: usize,
pub atom: usize,
pub alignment: f64,
pub reason: MissReason,
}
#[derive(Clone, Debug)]
pub struct RecallReport {
pub candidate_budget: usize,
pub num_rows: usize,
pub total_planted: usize,
pub total_recovered: usize,
pub recall: f64,
pub avg_candidates_gathered: f64,
pub num_atoms: usize,
pub misses: Vec<RecallMiss>,
}
impl RecallReport {
pub fn sublinearity_ratio(&self) -> f64 {
if self.num_atoms == 0 {
0.0
} else {
self.avg_candidates_gathered / self.num_atoms as f64
}
}
}
#[derive(Clone, Debug)]
pub struct ProposalRecallReport {
pub candidate_budget: usize,
pub top_s: usize,
pub num_rows: usize,
pub total_true: usize,
pub total_recovered: usize,
pub recall: f64,
pub avg_candidates_gathered: f64,
pub num_atoms: usize,
pub misses: Vec<RecallMiss>,
}
impl ProposalRecallReport {
pub fn sublinearity_ratio(&self) -> f64 {
if self.num_atoms == 0 {
0.0
} else {
self.avg_candidates_gathered / self.num_atoms as f64
}
}
}
#[inline]
fn mix_seed(base: u64, idx: u64) -> u64 {
let mut state = base
.wrapping_add(idx.wrapping_mul(0x9E37_79B9_7F4A_7C15))
.wrapping_sub(0x9E37_79B9_7F4A_7C15);
gam_linalg::utils::splitmix64(&mut state)
}
fn gaussian_projection(rows: usize, cols: usize, seed: u64) -> Array2<f64> {
use rand::RngExt as _;
let mut rng = StdRng::seed_from_u64(seed);
let mut m = Array2::<f64>::zeros((rows, cols));
for r in 0..rows {
for c in 0..cols {
let u1 = rng.random::<f64>().max(1e-16);
let u2 = rng.random::<f64>();
m[(r, c)] = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
}
}
m
}
fn orthonormal_frame(block: &Array2<f64>) -> Array2<f64> {
let p = block.nrows();
let m = block.ncols();
let mut cols: Vec<Array1<f64>> = Vec::with_capacity(m);
for j in 0..m {
let mut v = block.column(j).to_owned();
for q in &cols {
let proj: f64 = q.iter().zip(v.iter()).map(|(&a, &b)| a * b).sum();
for (vi, &qi) in v.iter_mut().zip(q.iter()) {
*vi -= proj * qi;
}
}
let nrm = vec_norm(v.view());
if nrm > DIRECTION_NORM_FLOOR {
for vi in v.iter_mut() {
*vi /= nrm;
}
cols.push(v);
}
}
let r = cols.len();
let mut u = Array2::<f64>::zeros((p, r));
for (j, col) in cols.into_iter().enumerate() {
u.column_mut(j).assign(&col);
}
u
}
fn mat_vec(m: &Array2<f64>, v: ArrayView1<f64>) -> Array1<f64> {
let mut out = Array1::<f64>::zeros(m.nrows());
for r in 0..m.nrows() {
let row = m.row(r);
out[r] = row.iter().zip(v.iter()).map(|(&a, &b)| a * b).sum();
}
out
}
#[inline]
fn vec_norm(v: ArrayView1<f64>) -> f64 {
v.iter().map(|&x| x * x).sum::<f64>().sqrt()
}
#[inline]
fn normalize_in_place(v: &mut Array1<f64>) {
let n = vec_norm(v.view());
if n > DIRECTION_NORM_FLOOR {
for x in v.iter_mut() {
*x /= n;
}
}
}
fn canonical_signature(sig: u64, bits: usize) -> u64 {
let mask = if bits >= 64 {
u64::MAX
} else {
(1u64 << bits) - 1
};
let complement = (!sig) & mask;
sig.min(complement)
}
fn sign_signature(bank: &Array2<f64>, s: ArrayView1<f64>) -> u64 {
let mut sig = 0u64;
for r in 0..bank.nrows() {
let row = bank.row(r);
let dot: f64 = row.iter().zip(s.iter()).map(|(&a, &b)| a * b).sum();
if dot >= 0.0 {
sig |= 1u64 << r;
}
}
canonical_signature(sig, bank.nrows())
}
fn sign_signature_with_margins(bank: &Array2<f64>, s: ArrayView1<f64>) -> (u64, Vec<f64>) {
let mut sig = 0u64;
let mut margins = Vec::with_capacity(bank.nrows());
for r in 0..bank.nrows() {
let row = bank.row(r);
let dot: f64 = row.iter().zip(s.iter()).map(|(&a, &b)| a * b).sum();
if dot >= 0.0 {
sig |= 1u64 << r;
}
margins.push(dot);
}
(canonical_signature(sig, bank.nrows()), margins)
}
fn lowest_margin_bit(margins: &[f64]) -> usize {
let mut best = 0usize;
let mut best_abs = f64::INFINITY;
for (i, &m) in margins.iter().enumerate() {
let a = m.abs();
if a < best_abs {
best_abs = a;
best = i;
}
}
best
}
#[cfg(test)]
mod tests {
use super::*;
use rand::RngExt as _;
use rand::rngs::StdRng;
fn unit_vec(rng: &mut StdRng, p: usize) -> Array1<f64> {
let mut v = Array1::<f64>::zeros(p);
for x in v.iter_mut() {
let u1 = rng.random::<f64>().max(1e-16);
let u2 = rng.random::<f64>();
*x = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
}
let n = vec_norm(v.view());
if n > DIRECTION_NORM_FLOOR {
for x in v.iter_mut() {
*x /= n;
}
}
v
}
fn synthetic_dictionary(k: usize, p: usize, seed: u64) -> (Vec<Array2<f64>>, Vec<Array1<f64>>) {
let mut rng = StdRng::seed_from_u64(seed);
let mut blocks = Vec::with_capacity(k);
let mut dirs = Vec::with_capacity(k);
for _ in 0..k {
let c = unit_vec(&mut rng, p);
let mut block = Array2::<f64>::zeros((p, 1));
block.column_mut(0).assign(&c);
blocks.push(block);
dirs.push(c);
}
(blocks, dirs)
}
#[test]
fn frame_alignment_is_exact_for_in_range_direction() {
let (blocks, dirs) = synthetic_dictionary(8, 16, 11);
let sketch = RandomProjectionFrameSketch::from_decoder_blocks(&blocks, 12, 7).unwrap();
let d = &dirs[3];
let a = sketch.alignment(3, d.view());
assert!(a > 0.999, "in-range alignment should be ~1, got {a}");
let a_off = sketch.alignment(3, dirs[5].view());
assert!(
a_off < a,
"off-atom alignment {a_off} should be below in-range {a}"
);
}
#[test]
fn routing_confidence_gate_input_separates_off_frame_from_in_frame() {
use crate::encode::CANDIDATE_ROUTING_MIN_ALIGNMENT as GATE;
let p = 6usize;
let mut block_a = Array2::<f64>::zeros((p, 2));
block_a[[0, 0]] = 1.0; block_a[[1, 1]] = 1.0; let mut block_b = Array2::<f64>::zeros((p, 2));
block_b[[2, 0]] = 1.0; block_b[[3, 1]] = 1.0; let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&[block_a, block_b], 16, 4242)
.unwrap();
let mut in_frame_b = Array1::<f64>::zeros(p);
in_frame_b[2] = 0.6;
in_frame_b[3] = 0.8; let a_right = sketch.alignment(1, in_frame_b.view());
let a_wrong = sketch.alignment(0, in_frame_b.view());
assert!(
a_right > 0.999,
"an in-frame direction must align ~1 with its own atom; got {a_right}"
);
assert!(
a_wrong < 1e-9,
"an orthogonal-subspace direction must align ~0 with the wrong atom; got {a_wrong}"
);
assert!(
a_wrong < GATE,
"a mis-routed (orthogonal) atom must fall below the routing gate {GATE}; got {a_wrong}"
);
assert!(
a_right >= GATE,
"the correctly-routed atom must sit at/above the routing gate {GATE}; got {a_right}"
);
let mut uncovered = Array1::<f64>::zeros(p);
uncovered[4] = 1.0;
for atom in 0..2 {
let a = sketch.alignment(atom, uncovered.view());
assert!(
a < GATE,
"an uncovered-subspace direction must fall below the gate for atom {atom}; got {a}"
);
}
}
#[test]
fn build_is_deterministic_for_a_fixed_seed() {
let (blocks, _) = synthetic_dictionary(64, 24, 99);
let s1 = RandomProjectionFrameSketch::from_decoder_blocks(&blocks, 16, 5).unwrap();
let s2 = RandomProjectionFrameSketch::from_decoder_blocks(&blocks, 16, 5).unwrap();
for i in 0..blocks.len() {
let a = s1.atom_sketch(i);
let b = s2.atom_sketch(i);
let diff = vec_norm((&a - &b).view());
assert!(
diff < 1e-12,
"atom {i} sketch differs across builds: {diff:e}"
);
}
let cfg = IndexConfig::auto(16, blocks.len(), 5);
let idx1 = SaeCandidateIndex::build(&s1, cfg).unwrap();
let idx2 = SaeCandidateIndex::build(&s2, cfg).unwrap();
for t in 0..idx1.tables.len() {
assert_eq!(idx1.tables[t].len(), idx2.tables[t].len());
}
}
#[test]
fn planted_atoms_are_recalled_above_floor_at_sublinear_budget() {
let k = 2000usize;
let p = 48usize;
let (blocks, dirs) = synthetic_dictionary(k, p, 2026);
let sketch_dim = 24usize;
let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, 4242).unwrap();
let cfg = IndexConfig::auto(sketch_dim, k, 4242);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let mut rng = StdRng::seed_from_u64(31337);
let n_rows = 200usize;
let mut rows: Vec<(Array1<f64>, Vec<usize>)> = Vec::with_capacity(n_rows);
for _ in 0..n_rows {
let primary = rng.random_range(0..k);
let secondary = rng.random_range(0..k);
let mut d = dirs[primary].clone();
for (di, &si) in d.iter_mut().zip(dirs[secondary].iter()) {
*di += 0.15 * si;
}
let n = vec_norm(d.view());
for di in d.iter_mut() {
*di /= n;
}
rows.push((d, vec![primary]));
}
let candidate_budget = 32usize;
let report = index.recall_report(&sketch, &rows, candidate_budget, cfg.multiprobe);
assert!(
report.sublinearity_ratio() < 0.5,
"gather was not sublinear: avg {} of {} atoms (ratio {:.3})",
report.avg_candidates_gathered,
report.num_atoms,
report.sublinearity_ratio()
);
let floor = 0.80;
assert!(
report.recall >= floor,
"recall {:.3} below floor {floor}; {} misses logged (first few: {:?})",
report.recall,
report.misses.len(),
report
.misses
.iter()
.take(5)
.map(|m| (m.row, m.atom, m.reason, m.alignment))
.collect::<Vec<_>>()
);
let recovered = report.total_recovered;
assert_eq!(
report.total_planted - recovered,
report.misses.len(),
"miss list must account for every unrecovered planted atom"
);
}
#[test]
fn rank2_atoms_are_gathered_at_every_phase() {
let k = 256usize;
let p = 48usize;
let mut rng = StdRng::seed_from_u64(2283);
let mut blocks = Vec::with_capacity(k);
for _ in 0..k {
let a = unit_vec(&mut rng, p);
let b = unit_vec(&mut rng, p);
let mut block = Array2::<f64>::zeros((p, 2));
block.column_mut(0).assign(&a);
block.column_mut(1).assign(&b);
blocks.push(block);
}
let sketch_dim = 24usize;
let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, 99).unwrap();
let cfg = IndexConfig::auto(sketch_dim, k, 99);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let budget = auto_candidate_budget(k);
let phases = 32usize;
let atoms = 16usize;
let mut total = 0usize;
let mut hits = 0usize;
let mut axis_misses: Vec<(usize, usize)> = Vec::new();
for atom in (0..k).step_by(k / atoms) {
let mut u = blocks[atom].column(0).to_owned();
let un = vec_norm(u.view());
u.mapv_inplace(|x| x / un);
let mut w = blocks[atom].column(1).to_owned();
let proj: f64 = w.iter().zip(u.iter()).map(|(&a, &b)| a * b).sum();
for (wi, &ui) in w.iter_mut().zip(u.iter()) {
*wi -= proj * ui;
}
let wn = vec_norm(w.view());
w.mapv_inplace(|x| x / wn);
for ph in 0..phases {
let angle = 2.0 * std::f64::consts::PI * (ph as f64) / (phases as f64);
let mut d = Array1::<f64>::zeros(p);
for i in 0..p {
d[i] = angle.cos() * u[i] + angle.sin() * w[i];
}
let proposal = index.propose(&sketch, d.view(), budget, cfg.multiprobe);
total += 1;
let gathered = proposal.proposed.contains(&atom);
if gathered {
hits += 1;
} else if ph % (phases / 4) == 0 {
axis_misses.push((atom, ph));
}
}
}
assert!(
axis_misses.is_empty(),
"axis-phase queries (±u, ±w) collide with their own bucketed \
representative by construction under the antipodally-canonical \
signature and must never miss: {axis_misses:?}"
);
let recall = hits as f64 / total as f64;
assert!(
recall >= 0.99,
"on-plane phase-swept gather recall {recall:.4} ({hits}/{total}) \
below 0.99 — the multi-representative bucketing regressed"
);
}
#[test]
fn auto_candidate_budget_tracks_the_issue_band() {
assert_eq!(auto_candidate_budget(2), CANDIDATE_BUDGET_MIN);
assert_eq!(auto_candidate_budget(64), 48);
assert_eq!(auto_candidate_budget(1024), 80);
assert_eq!(auto_candidate_budget(100_000), CANDIDATE_BUDGET_MAX);
let mut prev = 0usize;
for k in [2usize, 16, 64, 256, 1024, 4096, 65_536, 1_000_000] {
let c = auto_candidate_budget(k);
assert!(c >= prev, "budget must be monotone in K");
assert!((CANDIDATE_BUDGET_MIN..=CANDIDATE_BUDGET_MAX).contains(&c));
prev = c;
}
}
fn planted_rows(
dirs: &[Array1<f64>],
n_rows: usize,
seed: u64,
) -> Vec<(Array1<f64>, Vec<usize>)> {
let k = dirs.len();
let mut rng = StdRng::seed_from_u64(seed);
let mut rows = Vec::with_capacity(n_rows);
for _ in 0..n_rows {
let primary = rng.random_range(0..k);
let secondary = rng.random_range(0..k);
let mut d = dirs[primary].clone();
for (di, &si) in d.iter_mut().zip(dirs[secondary].iter()) {
*di += 0.15 * si;
}
let n = vec_norm(d.view());
for di in d.iter_mut() {
*di /= n;
}
rows.push((d, vec![primary]));
}
rows
}
#[test]
fn k_ladder_recall_determinism_and_sublinearity() {
let p = 48usize;
let n_rows = 150usize;
let mut ladder_ratios = Vec::new();
for &k in &[64usize, 1024] {
let (blocks, dirs) = synthetic_dictionary(k, p, 9000 + k as u64);
let sketch_dim = 24usize;
let sketch_seed = 71 + k as u64;
let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, sketch_seed)
.unwrap();
let cfg = IndexConfig::auto(sketch_dim, k, sketch_seed);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let rows = planted_rows(&dirs, n_rows, 555 + k as u64);
let budget = auto_candidate_budget(k);
let report = index.recall_report(&sketch, &rows, budget, cfg.multiprobe);
let floor = 0.80;
assert!(
report.recall >= floor,
"K={k}: recall {:.3} below floor {floor}; {} misses (first: {:?})",
report.recall,
report.misses.len(),
report
.misses
.iter()
.take(3)
.map(|m| (m.row, m.atom, m.reason, m.alignment))
.collect::<Vec<_>>()
);
assert_eq!(
report.total_planted - report.total_recovered,
report.misses.len(),
"K={k}: miss list must account for every unrecovered planted atom"
);
let sketch2 =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, sketch_seed)
.unwrap();
let index2 = SaeCandidateIndex::build(&sketch2, cfg).unwrap();
for (direction, _) in rows.iter().take(20) {
let a = index.propose(&sketch, direction.view(), budget, cfg.multiprobe);
let b = index2.propose(&sketch2, direction.view(), budget, cfg.multiprobe);
assert_eq!(
a.proposed, b.proposed,
"K={k}: rebuild must propose identically"
);
}
for (direction, _) in rows.iter().take(20) {
let prop = index.propose(&sketch, direction.view(), budget, cfg.multiprobe);
assert!(prop.proposed.len() <= budget);
}
ladder_ratios.push((k, report.sublinearity_ratio()));
}
let (_, ratio_small) = ladder_ratios[0];
let (k_big, ratio_big) = ladder_ratios[1];
assert!(
ratio_big < ratio_small,
"sublinearity must improve along the ladder: {ladder_ratios:?}"
);
assert!(
ratio_big < 0.25,
"K={k_big}: gather touched {:.1}% of the dictionary",
ratio_big * 100.0
);
}
struct CountingSketch<'a> {
inner: &'a RandomProjectionFrameSketch,
project_calls: std::cell::Cell<usize>,
}
impl AtomFrameSketch for CountingSketch<'_> {
fn sketch_dim(&self) -> usize {
self.inner.sketch_dim()
}
fn output_dim(&self) -> usize {
self.inner.output_dim()
}
fn num_atoms(&self) -> usize {
self.inner.num_atoms()
}
fn atom_sketch(&self, atom_id: usize) -> Array1<f64> {
self.inner.atom_sketch(atom_id)
}
fn project_direction(&self, atom_id: usize, direction: ArrayView1<f64>) -> Array1<f64> {
self.project_calls.set(self.project_calls.get() + 1);
self.inner.project_direction(atom_id, direction)
}
fn alignment(&self, atom_id: usize, direction: ArrayView1<f64>) -> f64 {
self.inner.alignment(atom_id, direction)
}
fn query_sketch(&self, direction: ArrayView1<f64>) -> Array1<f64> {
self.inner.query_sketch(direction)
}
}
#[test]
fn query_probe_touches_no_atom_before_the_gather() {
let k = 512usize;
let p = 32usize;
let (blocks, dirs) = synthetic_dictionary(k, p, 77);
let sketch = RandomProjectionFrameSketch::from_decoder_blocks(&blocks, 16, 13).unwrap();
let cfg = IndexConfig::auto(16, k, 13);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let counting = CountingSketch {
inner: &sketch,
project_calls: std::cell::Cell::new(0),
};
drop(index.propose(&counting, dirs[5].view(), 32, cfg.multiprobe));
assert_eq!(
counting.project_calls.get(),
0,
"the exact query probe must be independent of K: no per-atom \
projection before the gather (#994)"
);
}
fn coherent_cluster_dictionary(
n_clusters: usize,
cluster_size: usize,
p: usize,
spread: f64,
seed: u64,
) -> (Vec<Array2<f64>>, Vec<Array1<f64>>) {
let mut rng = StdRng::seed_from_u64(seed);
let mut blocks = Vec::with_capacity(n_clusters * cluster_size);
let mut dirs = Vec::with_capacity(n_clusters * cluster_size);
for _ in 0..n_clusters {
let center = unit_vec(&mut rng, p);
for _ in 0..cluster_size {
let noise = unit_vec(&mut rng, p);
let mut c = center.clone();
for (ci, &ni) in c.iter_mut().zip(noise.iter()) {
*ci += spread * ni;
}
let n = vec_norm(c.view());
for ci in c.iter_mut() {
*ci /= n;
}
let mut block = Array2::<f64>::zeros((p, 1));
block.column_mut(0).assign(&c);
blocks.push(block);
dirs.push(c);
}
}
(blocks, dirs)
}
#[test]
fn coherent_clusters_are_recalled_with_the_exact_probe() {
let n_clusters = 32usize;
let cluster_size = 32usize;
let k = n_clusters * cluster_size;
let p = 48usize;
let (blocks, dirs) = coherent_cluster_dictionary(n_clusters, cluster_size, p, 0.25, 4242);
let sketch_dim = 24usize;
let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, 99).unwrap();
let cfg = IndexConfig::auto(sketch_dim, k, 99);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let rows = planted_rows(&dirs, 150, 31337);
let budget = auto_candidate_budget(k);
let report = index.recall_report(&sketch, &rows, budget, cfg.multiprobe);
let floor = 0.80;
assert!(
report.recall >= floor,
"coherent-cluster recall {:.3} below floor {floor}; {} misses (first: {:?})",
report.recall,
report.misses.len(),
report
.misses
.iter()
.take(3)
.map(|m| (m.row, m.atom, m.reason, m.alignment))
.collect::<Vec<_>>()
);
assert!(
report.sublinearity_ratio() < 0.5,
"cluster gather touched {:.1}% of the dictionary",
report.sublinearity_ratio() * 100.0
);
}
#[test]
fn exact_probe_matches_shared_projection_of_the_direction() {
let p = 16usize;
let mut rng = StdRng::seed_from_u64(5);
let d = unit_vec(&mut rng, p);
let mut block = Array2::<f64>::zeros((p, 1));
block.column_mut(0).assign(&d);
let sketch = RandomProjectionFrameSketch::from_decoder_blocks(&[block], 8, 21).unwrap();
let via_probe = sketch.query_sketch(d.view());
let via_atom = sketch.atom_sketch(0);
let diff = vec_norm((&via_probe - &via_atom).view());
assert!(
diff < 1e-10,
"query_sketch(d) must equal the rank-1 atom representative of d: diff {diff:e}"
);
}
#[test]
fn route_exact_matches_brute_force_argmax_with_no_silent_miss() {
let k = 1500usize;
let p = 32usize;
let (blocks, dirs) = synthetic_dictionary(k, p, 2027);
let sketch_dim = 24usize;
let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, 909).unwrap();
let cfg = IndexConfig::auto(sketch_dim, k, 909);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let budget = auto_candidate_budget(k);
let mut rng = StdRng::seed_from_u64(8675309);
let n_rows = 300usize;
let mut lsh_only_misses = 0usize; for _ in 0..n_rows {
let d = unit_vec(&mut rng, p);
let (truth_atom, truth_align) = brute_force_best_atom(&sketch, d.view())
.expect("non-empty dictionary has an argmax");
for id in 0..k {
let a = sketch.alignment(id, d.view());
assert!(
a <= truth_align + 1e-12,
"brute force is not the argmax: atom {id} scores {a} > {truth_align}"
);
}
let route = index
.route_exact(&sketch, d.view(), budget, cfg.multiprobe)
.expect("route_exact returns an argmax for a non-empty dictionary");
assert_eq!(
route.atom, truth_atom,
"route_exact must select the brute-force global argmax"
);
assert!(
(route.alignment - truth_align).abs() < 1e-12,
"route_exact alignment {} != brute force {truth_align}",
route.alignment
);
assert!(
route.did_full_scan && !route.lsh_certified,
"a sub-ceiling row must take the exact-scan fallback, not the bound fast path"
);
let proposal = index.propose(&sketch, d.view(), budget, cfg.multiprobe);
if let Some(&lsh_best) = proposal.proposed.first() {
if lsh_best != truth_atom {
lsh_only_misses += 1;
}
} else {
lsh_only_misses += 1;
}
}
assert!(
lsh_only_misses > 0,
"test is vacuous: LSH-alone never missed, so the exact fallback was never exercised"
);
let j = 777usize;
let dj = &dirs[j];
let (truth_atom, _) = brute_force_best_atom(&sketch, dj.view()).unwrap();
assert_eq!(
truth_atom, j,
"the in-frame column is its own unique argmax"
);
let route = index
.route_exact(&sketch, dj.view(), budget, cfg.multiprobe)
.unwrap();
assert_eq!(
route.atom, j,
"fast path must still return the global argmax"
);
assert!(
route.lsh_certified && !route.did_full_scan,
"a ceiling-alignment row must be certified by the universal bound, no scan"
);
assert!(
route.alignment >= ROUTING_ALIGNMENT_UPPER_BOUND - ROUTING_CERT_EPS,
"certified alignment must sit at the universal ceiling; got {}",
route.alignment
);
}
#[test]
fn empty_planted_rows_report_perfect_recall() {
let (blocks, dirs) = synthetic_dictionary(32, 16, 1);
let sketch = RandomProjectionFrameSketch::from_decoder_blocks(&blocks, 12, 3).unwrap();
let cfg = IndexConfig::auto(12, 32, 3);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let rows = vec![(dirs[0].clone(), Vec::<usize>::new())];
let report = index.recall_report(&sketch, &rows, 8, true);
assert_eq!(report.recall, 1.0);
assert!(report.misses.is_empty());
}
#[test]
fn brute_force_top_s_returns_exact_descending_winners() {
let (blocks, dirs) = synthetic_dictionary(64, 24, 4);
let sketch = RandomProjectionFrameSketch::from_decoder_blocks(&blocks, 16, 9).unwrap();
let top = brute_force_top_s(&sketch, dirs[7].view(), 5);
assert_eq!(top.len(), 5, "must return exactly s winners");
assert_eq!(top[0], 7, "the in-range atom is the top-1 exact winner");
let mut prev = f64::INFINITY;
for &id in &top {
let a = sketch.alignment(id, dirs[7].view());
assert!(a <= prev + 1e-12, "top-s must be descending by alignment");
prev = a;
}
let all = brute_force_top_s(&sketch, dirs[0].view(), 1000);
assert_eq!(all.len(), 64);
}
#[test]
fn proposal_recall_license_recovers_exact_top_s_at_frontier_k() {
let k = 2000usize;
let p = 48usize;
let (blocks, dirs) = synthetic_dictionary(k, p, 2026);
let sketch_dim = 24usize;
let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, 4242).unwrap();
let cfg = IndexConfig::auto(sketch_dim, k, 4242);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let rows = planted_rows(&dirs, 200, 31337);
let directions: Vec<Array1<f64>> = rows.into_iter().map(|(d, _)| d).collect();
let budget = auto_candidate_budget(k);
let report = index.proposal_recall_report(&sketch, &directions, 1, budget, cfg.multiprobe);
assert!(
report.sublinearity_ratio() < 0.5,
"license gather was not sublinear: avg {} of {} atoms (ratio {:.3})",
report.avg_candidates_gathered,
report.num_atoms,
report.sublinearity_ratio()
);
let floor = 0.80;
assert!(
report.recall >= floor,
"top-1 recall {:.3} below floor {floor}; {} misses (first: {:?})",
report.recall,
report.misses.len(),
report
.misses
.iter()
.take(5)
.map(|m| (m.row, m.atom, m.reason, m.alignment))
.collect::<Vec<_>>()
);
assert_eq!(
report.total_true - report.total_recovered,
report.misses.len(),
"license miss list must account for every unrecovered true-top-s atom"
);
let s = 4usize;
let multi = index.proposal_recall_report(&sketch, &directions, s, budget, cfg.multiprobe);
assert_eq!(multi.total_true, s * directions.len());
assert!(multi.recall.is_finite() && (0.0..=1.0).contains(&multi.recall));
assert_eq!(
multi.total_true - multi.total_recovered,
multi.misses.len(),
"top-s miss ledger must account for every unrecovered slot at s>1"
);
}
#[test]
fn proposal_recall_license_at_one_million_atoms() {
let k = 1_000_000usize;
let p = 48usize;
let (blocks, dirs) = synthetic_dictionary(k, p, 7);
let sketch_dim = 24usize;
let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, 21).unwrap();
let cfg = IndexConfig::auto(sketch_dim, k, 21);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let n_rows = 24usize;
let mut directions: Vec<Array1<f64>> = Vec::with_capacity(n_rows);
for r in 0..n_rows {
let primary = (r * 2_654_435_761usize) % k;
directions.push(dirs[primary].clone());
}
let budget = auto_candidate_budget(k);
assert_eq!(
budget, CANDIDATE_BUDGET_MAX,
"K=10^6 sits at the budget cap"
);
let s = 1usize;
let report = index.proposal_recall_report(&sketch, &directions, s, budget, cfg.multiprobe);
assert!(
report.sublinearity_ratio() < 0.05,
"million-atom gather touched {:.3}% of the dictionary (avg {} of {})",
report.sublinearity_ratio() * 100.0,
report.avg_candidates_gathered,
report.num_atoms
);
let floor = 0.75;
assert!(
report.recall >= floor,
"K=10^6 top-s recall {:.3} below floor {floor}; {} misses logged",
report.recall,
report.misses.len()
);
assert_eq!(
report.total_true - report.total_recovered,
report.misses.len(),
"every unrecovered true-top-s atom must be logged at K=10^6"
);
eprintln!(
"[e1-license] K={k} C={budget} s={s} recall@s={:.3} avg_gathered={:.1} \
sublinearity={:.5}% misses={}",
report.recall,
report.avg_candidates_gathered,
report.sublinearity_ratio() * 100.0,
report.misses.len()
);
}
#[test]
fn proposal_recall_license_recovers_the_null() {
let k = 128usize;
let p = 16usize;
let (blocks, dirs) = synthetic_dictionary(k, p, 3);
let sketch = RandomProjectionFrameSketch::from_decoder_blocks(&blocks, 12, 5).unwrap();
let cfg = IndexConfig::auto(12, k, 5);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let budget = auto_candidate_budget(k);
let empty: Vec<Array1<f64>> = Vec::new();
let r0 = index.proposal_recall_report(&sketch, &empty, 4, budget, cfg.multiprobe);
assert_eq!(r0.recall, 1.0);
assert_eq!(r0.total_true, 0);
assert!(r0.misses.is_empty());
assert_eq!(r0.sublinearity_ratio(), 0.0);
let real: Vec<Array1<f64>> = dirs.iter().take(8).cloned().collect();
let r1 = index.proposal_recall_report(&sketch, &real, 0, budget, cfg.multiprobe);
assert_eq!(r1.recall, 1.0);
assert_eq!(r1.total_true, 0);
assert!(r1.misses.is_empty());
let zeros: Vec<Array1<f64>> = (0..5).map(|_| Array1::<f64>::zeros(p)).collect();
let r2 = index.proposal_recall_report(&sketch, &zeros, 4, budget, cfg.multiprobe);
assert!(r2.recall.is_finite() && (0.0..=1.0).contains(&r2.recall));
assert_eq!(
r2.total_true - r2.total_recovered,
r2.misses.len(),
"null (zero-direction) rows must still account for every miss"
);
}
#[test]
fn routability_shortlist_size_is_logarithmic_and_floor_derived() {
let p = 48usize;
let s = 1usize;
let delta = 0.2;
let mut prev = 0usize;
for &k in &[64usize, 256, 1024, 4096, 65_536, 1_000_000] {
let c = routability_shortlist_size(p, k, s, delta);
assert!(
c >= prev,
"shortlist size must be monotone in K: {c} < {prev}"
);
assert!(c >= s + 1, "shortlist must exceed the top-s winner count");
assert!(c <= k, "shortlist can never exceed the dictionary");
assert!(
(c as f64) < 4.0 * (k as f64).ln(),
"K={k}: shortlist {c} must stay logarithmic in K"
);
let want_band = (2.0 * (k as f64 / delta).ln()).ceil() as usize;
assert_eq!(
c - s,
want_band.clamp(1, k - s),
"K={k}: shortlist band must equal ⌈2·ln(K/δ)⌉ = p·u²"
);
prev = c;
}
let c_loose = routability_shortlist_size(p, 4096, s, 0.5);
let c_tight = routability_shortlist_size(p, 4096, s, 1e-4);
assert!(
c_tight > c_loose,
"tightening δ must widen the shortlist: {c_tight} !> {c_loose}"
);
}
#[test]
fn two_stage_route_matches_exact_top_s_at_routability_derived_shortlist() {
let k = 2000usize;
let p = 48usize;
let (blocks, dirs) = synthetic_dictionary(k, p, 2026);
let sketch_dim = 24usize;
let sketch =
RandomProjectionFrameSketch::from_decoder_blocks(&blocks, sketch_dim, 4242).unwrap();
let cfg = IndexConfig::auto(sketch_dim, k, 4242);
let index = SaeCandidateIndex::build(&sketch, cfg).unwrap();
let delta = 0.2;
let s = 1usize;
let shortlist = routability_shortlist_size(p, k, s, delta);
let floor = crate::routability::routability_floor(p, k, 1, delta).floor;
let planted_ratio = 1.0 / 0.15;
assert!(
planted_ratio > floor,
"planted target-to-clutter {planted_ratio:.2} must clear the floor {floor:.3}"
);
let rows = planted_rows(&dirs, 200, 31337);
let directions: Vec<Array1<f64>> = rows.into_iter().map(|(d, _)| d).collect();
let report =
index.proposal_recall_report(&sketch, &directions, s, shortlist, cfg.multiprobe);
assert!(
report.sublinearity_ratio() < 0.5,
"two-stage gather was not sublinear: ratio {:.3}",
report.sublinearity_ratio()
);
let bound = 1.0 - delta;
assert!(
report.recall >= bound,
"top-{s} recall {:.3} below the derived bound {bound:.3} at shortlist C={shortlist}; \
{} misses (first: {:?})",
report.recall,
report.misses.len(),
report
.misses
.iter()
.take(5)
.map(|m| (m.row, m.atom, m.reason, m.alignment))
.collect::<Vec<_>>()
);
assert_eq!(
report.total_true - report.total_recovered,
report.misses.len(),
"the miss ledger must account for every unrecovered exact-top-s atom"
);
}
}