use diskann::{error::IntoANNResult, utils::VectorRepr, ANNError, ANNResult};
use diskann_quantization::{
alloc::{aligned_slice, AlignedSlice},
num::PowerOfTwo,
};
#[derive(Debug)]
pub struct PQScratch {
pub aligned_pqtable_dist_scratch: AlignedSlice<f32>,
pub aligned_dist_scratch: AlignedSlice<f32>,
pub aligned_pq_coord_scratch: AlignedSlice<u8>,
pub rotated_query: Vec<f32>,
}
impl PQScratch {
const ALIGNED_ALLOC_128: PowerOfTwo = match PowerOfTwo::new(128) {
Ok(v) => v,
Err(_) => unreachable!(),
};
pub fn new(
graph_degree: usize,
dim: usize,
num_pq_chunks: usize,
num_centers: usize,
) -> ANNResult<Self> {
let aligned_pq_coord_scratch =
aligned_slice(graph_degree * num_pq_chunks, PQScratch::ALIGNED_ALLOC_128)
.map_err(ANNError::log_index_error)?;
let aligned_pqtable_dist_scratch =
aligned_slice(num_centers * num_pq_chunks, PQScratch::ALIGNED_ALLOC_128)
.map_err(ANNError::log_index_error)?;
let aligned_dist_scratch = aligned_slice(graph_degree, PQScratch::ALIGNED_ALLOC_128)
.map_err(ANNError::log_index_error)?;
let rotated_query = vec![0.0f32; dim];
Ok(Self {
aligned_pqtable_dist_scratch,
aligned_dist_scratch,
aligned_pq_coord_scratch,
rotated_query,
})
}
pub fn set<T: VectorRepr>(&mut self, dim: usize, query: &[T]) -> ANNResult<()> {
if dim > query.len() {
return Err(ANNError::log_dimension_mismatch_error(format!(
"PQScratch::set: expected query of length >= {dim}, got {}",
query.len()
)));
}
let query = T::as_f32(&query[..dim]).into_ann_result()?;
if query.len() > self.rotated_query.len() {
return Err(ANNError::log_dimension_mismatch_error(format!(
"PQScratch::set: decompressed query of length {} does not fit rotated_query buffer of length {}",
query.len(),
self.rotated_query.len()
)));
}
self.rotated_query[..query.len()].copy_from_slice(&query);
Ok(())
}
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::PQScratch;
#[rstest]
#[case(512, 8, 128, 256)] #[case(59, 16, 37, 41)] fn test_pq_scratch(
#[case] graph_degree: usize,
#[case] dim: usize,
#[case] num_pq_chunks: usize,
#[case] num_centers: usize,
) {
let mut pq_scratch: PQScratch =
PQScratch::new(graph_degree, dim, num_pq_chunks, num_centers).unwrap();
assert_eq!(
(pq_scratch.aligned_pqtable_dist_scratch.as_ptr() as usize)
% PQScratch::ALIGNED_ALLOC_128.raw(),
0
);
assert_eq!(
(pq_scratch.aligned_dist_scratch.as_ptr() as usize)
% PQScratch::ALIGNED_ALLOC_128.raw(),
0
);
assert_eq!(
(pq_scratch.aligned_pq_coord_scratch.as_ptr() as usize)
% PQScratch::ALIGNED_ALLOC_128.raw(),
0
);
let query: Vec<u8> = (1..=dim).map(|i| i as u8).collect();
pq_scratch.set::<u8>(query.len(), &query).unwrap();
(0..query.len()).for_each(|i| {
assert_eq!(pq_scratch.rotated_query[i], query[i] as f32);
});
}
}