diskann_quantization/algorithms/transforms/
mod.rs1use std::num::NonZeroUsize;
8
9#[cfg(feature = "flatbuffers")]
10use flatbuffers::{FlatBufferBuilder, WIPOffset};
11use rand::RngCore;
12use thiserror::Error;
13
14use crate::alloc::{Allocator, AllocatorError, ScopedAllocator, TryClone};
15#[cfg(feature = "flatbuffers")]
16use crate::flatbuffers as fb;
17
18mod double_hadamard;
20mod null;
21mod padding_hadamard;
22
23crate::utils::features! {
24 #![feature = "linalg"]
25 mod random_rotation;
26}
27
28mod utils;
29
30#[cfg(test)]
31#[cfg(not(miri))]
32mod test_utils;
33
34pub use double_hadamard::{DoubleHadamard, DoubleHadamardError};
36pub use null::NullTransform;
37pub use padding_hadamard::{PaddingHadamard, PaddingHadamardError};
38pub use utils::TransformFailed;
39
40crate::utils::features! {
41 #![feature = "linalg"]
42 pub use random_rotation::RandomRotation;
43}
44
45crate::utils::features! {
46 #![all(feature = "linalg", feature = "flatbuffers")]
47 pub use random_rotation::RandomRotationError;
48}
49
50crate::utils::features! {
51 #![feature = "flatbuffers"]
52 pub use null::NullTransformError;
53}
54
55#[derive(Debug, Clone, Copy)]
60#[non_exhaustive]
61pub enum TransformKind {
62 PaddingHadamard { target_dim: TargetDim },
80
81 DoubleHadamard { target_dim: TargetDim },
94
95 Null,
97
98 #[cfg(feature = "linalg")]
103 #[cfg_attr(docsrs, doc(cfg(feature = "linalg")))]
104 RandomRotation { target_dim: TargetDim },
105}
106
107#[derive(Debug, Clone, Error)]
108pub enum NewTransformError {
109 #[error("random number generator is required for {0:?}")]
110 RngMissing(TransformKind),
111 #[error(transparent)]
112 AllocatorError(#[from] AllocatorError),
113}
114
115#[derive(Debug)]
116#[cfg_attr(test, derive(PartialEq))]
117pub enum Transform<A>
118where
119 A: Allocator,
120{
121 PaddingHadamard(PaddingHadamard<A>),
122 DoubleHadamard(DoubleHadamard<A>),
123 Null(NullTransform),
124
125 #[cfg(feature = "linalg")]
126 #[cfg_attr(docsrs, doc(cfg(feature = "linalg")))]
127 RandomRotation(RandomRotation),
128}
129
130impl<A> Transform<A>
131where
132 A: Allocator,
133{
134 pub fn new(
144 transform_kind: TransformKind,
145 dim: NonZeroUsize,
146 rng: Option<&mut dyn RngCore>,
147 allocator: A,
148 ) -> Result<Self, NewTransformError> {
149 match transform_kind {
150 TransformKind::PaddingHadamard { target_dim } => {
151 let rng = rng.ok_or(NewTransformError::RngMissing(transform_kind))?;
152 Ok(Transform::PaddingHadamard(PaddingHadamard::new(
153 dim, target_dim, rng, allocator,
154 )?))
155 }
156 TransformKind::DoubleHadamard { target_dim } => {
157 let rng = rng.ok_or(NewTransformError::RngMissing(transform_kind))?;
158 Ok(Transform::DoubleHadamard(DoubleHadamard::new(
159 dim, target_dim, rng, allocator,
160 )?))
161 }
162 TransformKind::Null => Ok(Transform::Null(NullTransform::new(dim))),
163 #[cfg(feature = "linalg")]
164 TransformKind::RandomRotation { target_dim } => {
165 let rng = rng.ok_or(NewTransformError::RngMissing(transform_kind))?;
166 Ok(Transform::RandomRotation(RandomRotation::new(
167 dim, target_dim, rng,
168 )))
169 }
170 }
171 }
172
173 pub(crate) fn input_dim(&self) -> usize {
174 match self {
175 Self::PaddingHadamard(t) => t.input_dim(),
176 Self::DoubleHadamard(t) => t.input_dim(),
177 Self::Null(t) => t.dim(),
178 #[cfg(feature = "linalg")]
179 Self::RandomRotation(t) => t.input_dim(),
180 }
181 }
182 pub(crate) fn output_dim(&self) -> usize {
183 match self {
184 Self::PaddingHadamard(t) => t.output_dim(),
185 Self::DoubleHadamard(t) => t.output_dim(),
186 Self::Null(t) => t.dim(),
187 #[cfg(feature = "linalg")]
188 Self::RandomRotation(t) => t.output_dim(),
189 }
190 }
191
192 pub(crate) fn preserves_norms(&self) -> bool {
193 match self {
194 Self::PaddingHadamard(t) => t.preserves_norms(),
195 Self::DoubleHadamard(t) => t.preserves_norms(),
196 Self::Null(t) => t.preserves_norms(),
197 #[cfg(feature = "linalg")]
198 Self::RandomRotation(t) => t.preserves_norms(),
199 }
200 }
201
202 pub(crate) fn transform_into(
203 &self,
204 dst: &mut [f32],
205 src: &[f32],
206 allocator: ScopedAllocator<'_>,
207 ) -> Result<(), TransformFailed> {
208 match self {
209 Self::PaddingHadamard(t) => t.transform_into(dst, src, allocator),
210 Self::DoubleHadamard(t) => t.transform_into(dst, src, allocator),
211 Self::Null(t) => t.transform_into(dst, src),
212 #[cfg(feature = "linalg")]
213 Self::RandomRotation(t) => t.transform_into(dst, src),
214 }
215 }
216}
217
218impl<A> TryClone for Transform<A>
219where
220 A: Allocator,
221{
222 fn try_clone(&self) -> Result<Self, AllocatorError> {
223 match self {
224 Self::PaddingHadamard(t) => Ok(Self::PaddingHadamard(t.try_clone()?)),
225 Self::DoubleHadamard(t) => Ok(Self::DoubleHadamard(t.try_clone()?)),
226 Self::Null(t) => Ok(Self::Null(t.clone())),
227 #[cfg(feature = "linalg")]
228 Self::RandomRotation(t) => Ok(Self::RandomRotation(t.clone())),
229 }
230 }
231}
232
233#[cfg(feature = "flatbuffers")]
234#[cfg_attr(docsrs, doc(cfg(feature = "flatbuffers")))]
235#[derive(Debug, Clone, Copy, Error, PartialEq)]
236#[non_exhaustive]
237pub enum TransformError {
238 #[error(transparent)]
239 PaddingHadamardError(#[from] PaddingHadamardError),
240 #[error(transparent)]
241 DoubleHadamardError(#[from] DoubleHadamardError),
242 #[error(transparent)]
243 NullTransformError(#[from] NullTransformError),
244 #[cfg(feature = "linalg")]
245 #[cfg_attr(docsrs, doc(cfg(feature = "linalg")))]
246 #[error(transparent)]
247 RandomRotationError(#[from] RandomRotationError),
248 #[error("invalid transform kind")]
249 InvalidTransformKind,
250}
251
252#[cfg(feature = "flatbuffers")]
253impl<A> Transform<A>
254where
255 A: Allocator,
256{
257 pub(crate) fn pack<'a, FA>(
259 &self,
260 buf: &mut FlatBufferBuilder<'a, FA>,
261 ) -> WIPOffset<fb::transforms::Transform<'a>>
262 where
263 FA: flatbuffers::Allocator + 'a,
264 {
265 let (kind, offset) = match self {
266 Self::PaddingHadamard(t) => (
267 fb::transforms::TransformKind::PaddingHadamard,
268 t.pack(buf).as_union_value(),
269 ),
270 Self::DoubleHadamard(t) => (
271 fb::transforms::TransformKind::DoubleHadamard,
272 t.pack(buf).as_union_value(),
273 ),
274 Self::Null(t) => (
275 fb::transforms::TransformKind::NullTransform,
276 t.pack(buf).as_union_value(),
277 ),
278 #[cfg(feature = "linalg")]
279 Self::RandomRotation(t) => (
280 fb::transforms::TransformKind::RandomRotation,
281 t.pack(buf).as_union_value(),
282 ),
283 };
284
285 fb::transforms::Transform::create(
286 buf,
287 &fb::transforms::TransformArgs {
288 transform_type: kind,
289 transform: Some(offset),
290 },
291 )
292 }
293
294 pub(crate) fn try_unpack(
297 alloc: A,
298 proto: fb::transforms::Transform<'_>,
299 ) -> Result<Self, TransformError> {
300 if let Some(transform) = proto.transform_as_padding_hadamard() {
301 return Ok(Self::PaddingHadamard(PaddingHadamard::try_unpack(
302 alloc, transform,
303 )?));
304 }
305
306 #[cfg(feature = "linalg")]
307 if let Some(transform) = proto.transform_as_random_rotation() {
308 return Ok(Self::RandomRotation(RandomRotation::try_unpack(transform)?));
309 }
310
311 if let Some(transform) = proto.transform_as_double_hadamard() {
312 return Ok(Self::DoubleHadamard(DoubleHadamard::try_unpack(
313 alloc, transform,
314 )?));
315 }
316
317 if let Some(transform) = proto.transform_as_null_transform() {
318 return Ok(Self::Null(NullTransform::try_unpack(transform)?));
319 }
320
321 Err(TransformError::InvalidTransformKind)
322 }
323}
324
325#[derive(Debug, Clone, Copy)]
331pub enum TargetDim {
332 Same,
343
344 Natural,
350
351 Override(NonZeroUsize),
356}
357
358#[cfg(test)]
359#[cfg(not(miri))]
360test_utils::delegate_transformer!(Transform<crate::alloc::GlobalAllocator>);