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