1mod bits4;
4mod int8;
5mod rescore;
6
7#[cfg(any(test, feature = "test-seams"))]
9#[derive(Default, Debug)]
10#[doc(hidden)]
11pub struct QueryPreparationTestObservations {
12 pub bit4: Vec<(usize, u64)>,
14 pub int8: Vec<usize>,
16}
17
18#[cfg(any(test, feature = "test-seams"))]
19thread_local! {
20 static QUERY_PREPARATIONS: std::cell::RefCell<Option<QueryPreparationTestObservations>> =
21 const { std::cell::RefCell::new(None) };
22}
23
24#[cfg(any(test, feature = "test-seams"))]
26#[doc(hidden)]
27pub fn begin_query_preparation_test_observations() {
28 QUERY_PREPARATIONS
29 .with(|calls| *calls.borrow_mut() = Some(QueryPreparationTestObservations::default()));
30}
31
32#[cfg(any(test, feature = "test-seams"))]
34#[doc(hidden)]
35pub fn take_query_preparation_test_observations() -> QueryPreparationTestObservations {
36 QUERY_PREPARATIONS.with(|calls| calls.take().unwrap_or_default())
37}
38
39pub use bits4::{
40 Bit4Factors, Bit4Query, dequantize_bit4, est_dot_bit4, est_dot_bit4_batch, prepare_bit4_query,
41 quantize_bit4,
42};
43pub use int8::{
44 Int8Query, Int8Vec, dequantize_int8, dot_int8_query, prepare_int8_query, quantize_int8,
45};
46pub(crate) use rescore::{
47 ExactScoreReuse, RescoreCheckError, exact_squared_l2_with_sink, rescore_top_k_reusing,
48 squared_l2_f64,
49};
50pub use rescore::{
51 RescoreError, RescoreHit, RescoreMetric, RescorePool, RescoreResult, SearchByteCounts,
52 rescore_top_k,
53};
54
55#[derive(Clone, Debug, Eq, PartialEq)]
57pub enum QuantError {
58 Allocation,
60 EmptyVector,
62 DimensionTooLarge {
64 actual: usize,
66 maximum: usize,
68 },
69 NonFinite {
71 index: usize,
73 },
74 NonUnitNorm {
76 squared_norm_bits: u64,
78 tolerance_bits: u64,
80 },
81 OutputLength {
83 expected: usize,
85 actual: usize,
87 },
88 CodeLength {
90 expected: usize,
92 actual: usize,
94 },
95 NonZeroPadding {
97 byte: u8,
99 mask: u8,
101 },
102}
103
104impl std::fmt::Display for QuantError {
105 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
106 match self {
107 Self::Allocation => formatter.write_str("quantization allocation failed"),
108 Self::EmptyVector => formatter.write_str("quantization vector must not be empty"),
109 Self::DimensionTooLarge { actual, maximum } => write!(
110 formatter,
111 "quantization dimension {actual} exceeds supported maximum {maximum}"
112 ),
113 Self::NonFinite { index } => {
114 write!(
115 formatter,
116 "quantization input is non-finite at coordinate {index}"
117 )
118 }
119 Self::NonUnitNorm {
120 squared_norm_bits,
121 tolerance_bits,
122 } => write!(
123 formatter,
124 "declared L2-normalized vector has squared norm {}; expected 1 within {}",
125 f64::from_bits(*squared_norm_bits),
126 f64::from_bits(*tolerance_bits)
127 ),
128 Self::OutputLength { expected, actual } => write!(
129 formatter,
130 "quantization output length mismatch: expected {expected}, got {actual}"
131 ),
132 Self::CodeLength { expected, actual } => write!(
133 formatter,
134 "packed quantization code length mismatch: expected {expected}, got {actual}"
135 ),
136 Self::NonZeroPadding { byte, mask } => write!(
137 formatter,
138 "packed quantization code has non-zero padding: byte={byte:#04x}, mask={mask:#04x}"
139 ),
140 }
141 }
142}
143
144impl std::error::Error for QuantError {}
145
146#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
152#[repr(u8)]
153pub enum QuantScheme {
154 F32 = 0,
156 F16 = 1,
158 Int8 = 2,
160 #[default]
162 Bit4 = 4,
163}
164
165impl QuantScheme {
166 #[must_use]
168 pub const fn id(self) -> u8 {
169 self as u8
170 }
171
172 #[must_use]
174 pub const fn from_id(id: u8) -> Option<Self> {
175 match id {
176 0 => Some(Self::F32),
177 1 => Some(Self::F16),
178 2 => Some(Self::Int8),
179 4 => Some(Self::Bit4),
180 _ => None,
181 }
182 }
183}
184
185#[cfg(test)]
186mod tests;
187
188#[cfg(test)]
189pub(crate) use rescore::rescore_top_k_with_check;