1mod bits4;
4mod int8;
5mod rescore;
6
7#[cfg(any(test, feature = "test-support"))]
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-support"))]
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-support"))]
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-support"))]
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 EmptyVector,
60 DimensionTooLarge {
62 actual: usize,
64 maximum: usize,
66 },
67 NonFinite {
69 index: usize,
71 },
72 NonUnitNorm {
74 squared_norm_bits: u64,
76 tolerance_bits: u64,
78 },
79 OutputLength {
81 expected: usize,
83 actual: usize,
85 },
86 CodeLength {
88 expected: usize,
90 actual: usize,
92 },
93 NonZeroPadding {
95 byte: u8,
97 mask: u8,
99 },
100}
101
102impl std::fmt::Display for QuantError {
103 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 match self {
105 Self::EmptyVector => formatter.write_str("quantization vector must not be empty"),
106 Self::DimensionTooLarge { actual, maximum } => write!(
107 formatter,
108 "quantization dimension {actual} exceeds supported maximum {maximum}"
109 ),
110 Self::NonFinite { index } => {
111 write!(
112 formatter,
113 "quantization input is non-finite at coordinate {index}"
114 )
115 }
116 Self::NonUnitNorm {
117 squared_norm_bits,
118 tolerance_bits,
119 } => write!(
120 formatter,
121 "declared L2-normalized vector has squared norm {}; expected 1 within {}",
122 f64::from_bits(*squared_norm_bits),
123 f64::from_bits(*tolerance_bits)
124 ),
125 Self::OutputLength { expected, actual } => write!(
126 formatter,
127 "quantization output length mismatch: expected {expected}, got {actual}"
128 ),
129 Self::CodeLength { expected, actual } => write!(
130 formatter,
131 "packed quantization code length mismatch: expected {expected}, got {actual}"
132 ),
133 Self::NonZeroPadding { byte, mask } => write!(
134 formatter,
135 "packed quantization code has non-zero padding: byte={byte:#04x}, mask={mask:#04x}"
136 ),
137 }
138 }
139}
140
141impl std::error::Error for QuantError {}
142
143#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
149#[repr(u8)]
150pub enum QuantScheme {
151 F32 = 0,
153 F16 = 1,
155 Int8 = 2,
157 #[default]
159 Bit4 = 4,
160}
161
162impl QuantScheme {
163 #[must_use]
165 pub const fn id(self) -> u8 {
166 self as u8
167 }
168
169 #[must_use]
171 pub const fn from_id(id: u8) -> Option<Self> {
172 match id {
173 0 => Some(Self::F32),
174 1 => Some(Self::F16),
175 2 => Some(Self::Int8),
176 4 => Some(Self::Bit4),
177 _ => None,
178 }
179 }
180}
181
182#[cfg(test)]
183mod tests;
184
185#[cfg(test)]
186pub(crate) use rescore::rescore_top_k_with_check;