Skip to content

Commit 058582d

Browse files
committed
feat: hhtl_cascade_search score_fn callback — LEAF level pluggable
score_fn(row, col) -> f32 replaces the hardcoded 1.0 placeholder. lance-graph passes LanceDB VectorSearch as the LEAF backend. ndarray provides the cascade logic. lance-graph provides the data. 23 p64 tests passing. https://claude.ai/code/session_01M3at4EuHVvQ8S95mSnKgtK
1 parent 4a7ddee commit 058582d

1 file changed

Lines changed: 10 additions & 7 deletions

File tree

crates/p64/src/lib.rs

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1454,10 +1454,15 @@ pub mod sparse256 {
14541454
/// - Skip entire 32-element HEEL blocks if the 8×8 super-block is empty
14551455
/// - Skip 4-element TWIG blocks if the palette bit is 0
14561456
/// - Only compute exact distance for active palette entries
1457-
pub fn hhtl_cascade_search(
1457+
///
1458+
/// `score_fn`: callback that computes the actual score for a (row, col) pair.
1459+
/// This is where the LEAF level lives — LanceDB vector search, DistanceMatrix
1460+
/// lookup, or BF16 dot product. The cascade doesn't know or care which.
1461+
pub fn hhtl_cascade_search<F: Fn(u8, u8) -> f32>(
14581462
palette: &Palette64,
14591463
query_row: u8,
14601464
scores: &mut [f32; 256],
1465+
score_fn: F,
14611466
) -> usize {
14621467
let heel_row = query_row / 32;
14631468
let hip_row = (query_row / 4) % 8;
@@ -1478,14 +1483,11 @@ pub mod sparse256 {
14781483
let block_col = bits.trailing_zeros() as usize;
14791484
bits &= bits - 1;
14801485

1481-
// This block is active — compute 4 scores
14821486
let base_col = block_col * 4;
14831487
for k in 0..4 {
14841488
let col = base_col + k;
14851489
if col < 256 {
1486-
// Placeholder: actual score computation goes here
1487-
// In production: ZeckF8 distance or BF16 dot product
1488-
scores[col] = 1.0;
1490+
scores[col] = score_fn(query_row, col as u8);
14891491
computed += 1;
14901492
}
14911493
}
@@ -1864,15 +1866,16 @@ mod tests {
18641866
}
18651867

18661868
let mut scores = [0.0f32; 256];
1867-
let computed = hhtl_cascade_search(&palette, 0, &mut scores);
1869+
let score_fn = |row: u8, col: u8| -> f32 { 1.0 - (row as f32 - col as f32).abs() / 256.0 };
1870+
let computed = hhtl_cascade_search(&palette, 0, &mut scores, &score_fn);
18681871

18691872
eprintln!("HHTL cascade: computed {} of 256 scores", computed);
18701873

18711874
// Only 4 scores should be computed (one block of 4)
18721875
assert_eq!(computed, 4, "Should only compute active block entries");
18731876

18741877
// Row 128 → block_row = 128/32*8 + (128/4)%8 = 4*8 + 0 = 32
1875-
let computed2 = hhtl_cascade_search(&palette, 128, &mut scores);
1878+
let computed2 = hhtl_cascade_search(&palette, 128, &mut scores, &score_fn);
18761879
assert_eq!(computed2, 4);
18771880
}
18781881

0 commit comments

Comments
 (0)