diskann_disk/search/pq/
pq_scratch.rs1use diskann::{ANNError, ANNResult};
8
9use diskann_quantization::alloc::{AlignedAllocator, Poly};
10
11#[derive(Debug)]
12pub struct PQScratch {
14 pub aligned_pqtable_dist_scratch: Poly<[f32], AlignedAllocator>,
17
18 pub aligned_dist_scratch: Poly<[f32], AlignedAllocator>,
21
22 pub aligned_pq_coord_scratch: Poly<[u8], AlignedAllocator>,
25
26 pub query_scratch: Vec<f32>,
30}
31
32impl PQScratch {
33 pub fn new(
38 graph_degree: usize,
39 dim: usize,
40 num_pq_chunks: usize,
41 num_centers: usize,
42 ) -> ANNResult<Self> {
43 let aligned_pq_coord_scratch =
44 Poly::broadcast(0u8, graph_degree * num_pq_chunks, AlignedAllocator::A128)
45 .map_err(ANNError::log_index_error)?;
46 let aligned_pqtable_dist_scratch =
47 Poly::broadcast(0f32, num_centers * num_pq_chunks, AlignedAllocator::A128)
48 .map_err(ANNError::log_index_error)?;
49 let aligned_dist_scratch = Poly::broadcast(0f32, graph_degree, AlignedAllocator::A128)
50 .map_err(ANNError::log_index_error)?;
51 let query_scratch = vec![0.0f32; dim];
52
53 Ok(Self {
54 aligned_pqtable_dist_scratch,
55 aligned_dist_scratch,
56 aligned_pq_coord_scratch,
57 query_scratch,
58 })
59 }
60
61 pub fn set(&mut self, query: &[f32]) -> ANNResult<()> {
69 let dim = self.query_scratch.len();
70 if query.len() != dim {
71 return Err(ANNError::log_dimension_mismatch_error(format!(
72 "PQScratch::set: expected query of length {dim}, got {}",
73 query.len()
74 )));
75 }
76 self.query_scratch.copy_from_slice(query);
77 Ok(())
78 }
79
80 pub(crate) fn max_vectors(&self) -> usize {
83 self.aligned_dist_scratch.len()
84 }
85}
86
87#[cfg(test)]
88mod tests {
89 use diskann_quantization::num::PowerOfTwo;
90 use rstest::rstest;
91
92 use super::PQScratch;
93
94 #[rstest]
95 #[case(512, 8, 128, 256)] #[case(59, 16, 37, 41)] fn test_pq_scratch(
98 #[case] graph_degree: usize,
99 #[case] dim: usize,
100 #[case] num_pq_chunks: usize,
101 #[case] num_centers: usize,
102 ) {
103 let mut pq_scratch: PQScratch =
104 PQScratch::new(graph_degree, dim, num_pq_chunks, num_centers).unwrap();
105
106 assert_eq!(
107 (pq_scratch.aligned_pqtable_dist_scratch.as_ptr() as usize) % PowerOfTwo::V128.raw(),
108 0
109 );
110 assert_eq!(
111 (pq_scratch.aligned_dist_scratch.as_ptr() as usize) % PowerOfTwo::V128.raw(),
112 0
113 );
114 assert_eq!(
115 (pq_scratch.aligned_pq_coord_scratch.as_ptr() as usize) % PowerOfTwo::V128.raw(),
116 0
117 );
118
119 assert_eq!(pq_scratch.max_vectors(), graph_degree);
120
121 let query: Vec<f32> = (1..=dim).map(|i| i as f32).collect();
123 pq_scratch.set(&query).unwrap();
124
125 (0..query.len()).for_each(|i| {
126 assert_eq!(pq_scratch.query_scratch[i], query[i]);
127 });
128 }
129
130 #[test]
131 fn test_pq_scratch_set_rejects_short_query() {
132 let dim = 16;
133 let mut pq_scratch = PQScratch::new(64, dim, 4, 256).unwrap();
134
135 let short_query: Vec<f32> = (1..dim).map(|i| i as f32).collect(); let err = pq_scratch.set(&short_query).unwrap_err();
138 assert_eq!(err.kind(), diskann::ANNErrorKind::DimensionMismatchError);
139 assert!(err.to_string().contains("expected query of length"));
140 }
141
142 #[test]
143 fn test_pq_scratch_set_rejects_oversized_query() {
144 let dim = 8;
145 let mut pq_scratch = PQScratch::new(64, dim, 4, 256).unwrap();
146
147 let long_query: Vec<f32> = (1..=dim + 10).map(|i| i as f32).collect();
149 let err = pq_scratch.set(&long_query).unwrap_err();
150 assert_eq!(err.kind(), diskann::ANNErrorKind::DimensionMismatchError);
151 assert!(err.to_string().contains("expected query of length"));
152 }
153}