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(crate) use bits4::{
40 Bit4ControlError, Bit4Scratch, est_dot_bit4_controlled, prepare_bit4_query_controlled,
41 quantize_bit4_controlled,
42};
43pub use bits4::{
44 Bit4Factors, Bit4Query, dequantize_bit4, est_dot_bit4, est_dot_bit4_batch, prepare_bit4_query,
45 quantize_bit4,
46};
47pub use int8::{
48 Int8Query, Int8Vec, dequantize_int8, dot_int8_query, prepare_int8_query, quantize_int8,
49};
50pub(crate) use rescore::{
51 ExactScoreReuse, RescoreCheckError, exact_squared_l2_with_sink, rescore_top_k_reusing,
52 squared_l2_f64,
53};
54pub use rescore::{
55 RescoreError, RescoreHit, RescoreMetric, RescorePool, RescoreResult, SearchByteCounts,
56 rescore_top_k,
57};
58
59#[derive(Clone, Debug, Eq, PartialEq)]
61pub enum QuantError {
62 Allocation,
64 EmptyVector,
66 DimensionTooLarge {
68 actual: usize,
70 maximum: usize,
72 },
73 NonFinite {
75 index: usize,
77 },
78 NonUnitNorm {
80 squared_norm_bits: u64,
82 tolerance_bits: u64,
84 },
85 OutputLength {
87 expected: usize,
89 actual: usize,
91 },
92 CodeLength {
94 expected: usize,
96 actual: usize,
98 },
99 NonZeroPadding {
101 byte: u8,
103 mask: u8,
105 },
106}
107
108impl std::fmt::Display for QuantError {
109 fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
110 match self {
111 Self::Allocation => formatter.write_str("quantization allocation failed"),
112 Self::EmptyVector => formatter.write_str("quantization vector must not be empty"),
113 Self::DimensionTooLarge { actual, maximum } => write!(
114 formatter,
115 "quantization dimension {actual} exceeds supported maximum {maximum}"
116 ),
117 Self::NonFinite { index } => {
118 write!(
119 formatter,
120 "quantization input is non-finite at coordinate {index}"
121 )
122 }
123 Self::NonUnitNorm {
124 squared_norm_bits,
125 tolerance_bits,
126 } => write!(
127 formatter,
128 "declared L2-normalized vector has squared norm {}; expected 1 within {}",
129 f64::from_bits(*squared_norm_bits),
130 f64::from_bits(*tolerance_bits)
131 ),
132 Self::OutputLength { expected, actual } => write!(
133 formatter,
134 "quantization output length mismatch: expected {expected}, got {actual}"
135 ),
136 Self::CodeLength { expected, actual } => write!(
137 formatter,
138 "packed quantization code length mismatch: expected {expected}, got {actual}"
139 ),
140 Self::NonZeroPadding { byte, mask } => write!(
141 formatter,
142 "packed quantization code has non-zero padding: byte={byte:#04x}, mask={mask:#04x}"
143 ),
144 }
145 }
146}
147
148impl std::error::Error for QuantError {}
149
150#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
156#[repr(u8)]
157pub enum QuantScheme {
158 F32 = 0,
160 F16 = 1,
162 Int8 = 2,
164 #[default]
166 Bit4 = 4,
167}
168
169impl QuantScheme {
170 #[must_use]
172 pub const fn id(self) -> u8 {
173 self as u8
174 }
175
176 #[must_use]
178 pub const fn from_id(id: u8) -> Option<Self> {
179 match id {
180 0 => Some(Self::F32),
181 1 => Some(Self::F16),
182 2 => Some(Self::Int8),
183 4 => Some(Self::Bit4),
184 _ => None,
185 }
186 }
187}
188
189#[cfg(test)]
190mod tests;
191
192#[cfg(test)]
193pub(crate) use rescore::rescore_top_k_with_check;