1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
//! # ruvector-turboquant
//!
//! **Turbo4**: a 4-bit Lloyd-Max quantized vector *datatype* (ADR-296) —
//! Qdrant-style primary-storage quantization, not a search-time cache:
//!
//! * deterministic randomized Hadamard rotation (sign ⊙ permute ⊙ block-FWHT
//! rounds, seeded SplitMix64 — bit-stable across platforms and versions,
//! no `rand` dependency);
//! * precomputed 16-level Lloyd-Max tables for the rotated (≈ Gaussian)
//! coordinates — no training pass, online ingest;
//! * packed nibble codes: `D/2 + 8` bytes per vector (≈ 7.9× vs f32 at
//! 1536-D) — **the original float vector is never stored**;
//! * direct scoring on packed codes: symmetric (code×code, for graph
//! construction), asymmetric (int8 query×code, for traversal), and exact
//! f32 rescoring — with runtime-dispatched AVX2 kernels and a scalar
//! oracle they are tested bit-exact against.
//!
//! ```
//! use ruvector_turboquant::{Metric, Turbo4Codec, score};
//!
//! let dim = 128;
//! let codec = Turbo4Codec::new(dim, 42).unwrap();
//! let a: Vec<f32> = (0..dim).map(|i| (i as f32 * 0.37).sin()).collect();
//! let b: Vec<f32> = (0..dim).map(|i| (i as f32 * 0.11).cos()).collect();
//!
//! let code_a = codec.encode(&a).unwrap(); // 64 + 8 bytes, floats discarded
//! let code_b = codec.encode(&b).unwrap();
//! let query = codec.encode_query(&a).unwrap();
//!
//! let d_sym = score::symmetric_distance(Metric::Euclidean, &code_a, &code_b, dim);
//! let d_asym = score::asymmetric_distance(Metric::Euclidean, &query.blob, &code_b, dim);
//! let d_exact = score::rescore(Metric::Euclidean, &query, &code_b, dim);
//! assert!((d_asym - d_sym).abs() < 0.15 * d_sym.max(1.0));
//! assert!((d_exact - d_asym).abs() < 0.1 * d_asym.max(1.0));
//! ```
pub use ;
pub use ;
pub use Rotation;
pub use ;
/// Errors from the Turbo4 codec.