Skip to content

Commit bb29f05

Browse files
committed
feat(ivf): add IVF vector index layout with TurboQuant awareness
Creates a new `vortex-ivf` crate that implements a full IVF (Inverted File) vector index as a first-class Vortex layout. Data is clustered by k-means++, written one chunk per cluster, and paired with a centroid child layout that the reader consults at read time. New crate layout: - `vortex-ivf/src/lib.rs` core IvfIndex + k-means - `vortex-ivf/src/kmeans.rs` k-means++ clustering (deterministic, no new deps) - `vortex-ivf/src/partitioned.rs` cluster-boundary bookkeeping for range reads - `vortex-ivf/src/search.rs` in-memory IVF-accelerated cosine search - `vortex-ivf/src/tq.rs` TurboQuant-aware builder that clusters in the SORF-rotated quantized space and rotates queries instead of inverting SORF N times - `vortex-ivf/src/layout/mod.rs` IvfLayout module docs + session registration - `vortex-ivf/src/layout/metadata.rs` Prost metadata (dim, nprobes, num_clusters) - `vortex-ivf/src/layout/query.rs` extract literal query vector from a CosineSimilarity expression tree - `vortex-ivf/src/layout/reader.rs` IvfReader: pruning_evaluation probes the centroids and returns a mask that eliminates all rows in non-probed clusters - `vortex-ivf/src/layout/vtable.rs` VTable binding (data + centroids children) - `vortex-ivf/src/layout/writer.rs` IvfStrategy: LayoutStrategy that runs k-means, partitions rows into one chunk per cluster, and writes centroids as an auxiliary child Key design choices: - Separate crate. vortex-layout depends transitively on vortex-btrblocks, which optionally depends on vortex-tensor, so putting IVF in either crate creates a cargo cycle. A dedicated crate depending on both is the only clean option. - `OwnedLayoutChildren::layout_children` is now public so out-of-tree layout writers can construct `Arc<dyn LayoutChildren>` for `VTable::build`. - TurboQuant integration lives in `tq.rs`. Rather than decompress the whole column, the builder descends to the `Dict(codes, centroids)` FSL and materializes each row's rotated coordinates via a single dict lookup. The query is rotated once by SORF before probing, so the centroid comparison happens entirely in the quantized/rotated space. Test coverage (24 tests pass): - 13 core IVF index tests (build, probe, recall, edge cases) - 3 partitioned-index tests (cluster ranges and selectivity) - 2 TQ-aware tests (self-match through TQ compression, non-TQ rejection) - 2 in-memory IVF search tests (with and without TQ compression) - 4 end-to-end layout tests (write, project, pruning on cosine-similarity expr, pass-through for non-cosine expressions) Signed-off-by: "Claude" <noreply@anthropic.com> https://claude.ai/code/session_01AQwoFbonU23fEamWhFWhSo
1 parent 42252d9 commit bb29f05

22 files changed

Lines changed: 2112 additions & 126 deletions

File tree

Cargo.lock

Lines changed: 21 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ members = [
1616
"vortex-compressor",
1717
"vortex-btrblocks",
1818
"vortex-layout",
19+
"vortex-ivf",
1920
"vortex-scan",
2021
"vortex-file",
2122
"vortex-ipc",
@@ -278,6 +279,7 @@ vortex-flatbuffers = { version = "0.1.0", path = "./vortex-flatbuffers", default
278279
vortex-fsst = { version = "0.1.0", path = "./encodings/fsst", default-features = false }
279280
vortex-io = { version = "0.1.0", path = "./vortex-io", default-features = false }
280281
vortex-ipc = { version = "0.1.0", path = "./vortex-ipc", default-features = false }
282+
vortex-ivf = { version = "0.1.0", path = "./vortex-ivf", default-features = false }
281283
vortex-layout = { version = "0.1.0", path = "./vortex-layout", default-features = false }
282284
vortex-mask = { version = "0.1.0", path = "./vortex-mask", default-features = false }
283285
vortex-metrics = { version = "0.1.0", path = "./vortex-metrics", default-features = false }

vortex-ivf/Cargo.toml

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
[package]
2+
name = "vortex-ivf"
3+
authors = { workspace = true }
4+
categories = { workspace = true }
5+
description = "IVF (Inverted File) vector index layout for Vortex"
6+
edition = { workspace = true }
7+
homepage = { workspace = true }
8+
include = { workspace = true }
9+
keywords = { workspace = true }
10+
license = { workspace = true }
11+
readme = { workspace = true }
12+
repository = { workspace = true }
13+
rust-version = { workspace = true }
14+
version = { workspace = true }
15+
16+
[lints]
17+
workspace = true
18+
19+
[dependencies]
20+
async-trait = { workspace = true }
21+
futures = { workspace = true }
22+
itertools = { workspace = true }
23+
parking_lot = { workspace = true }
24+
prost = { workspace = true }
25+
tracing = { workspace = true }
26+
vortex-array = { workspace = true }
27+
vortex-buffer = { workspace = true }
28+
vortex-error = { workspace = true }
29+
vortex-io = { workspace = true }
30+
vortex-layout = { workspace = true }
31+
vortex-mask = { workspace = true }
32+
vortex-session = { workspace = true }
33+
vortex-tensor = { workspace = true }
34+
35+
[dev-dependencies]
36+
rstest = { workspace = true }
37+
vortex-io = { workspace = true, features = ["tokio"] }
38+
vortex-layout = { workspace = true, features = ["_test-harness"] }

vortex-ivf/public-api.lock

Lines changed: 285 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,285 @@
1+
pub mod vortex_ivf
2+
3+
pub mod vortex_ivf::layout
4+
5+
pub mod vortex_ivf::layout::writer
6+
7+
pub struct vortex_ivf::layout::writer::IvfLayoutOptions
8+
9+
pub vortex_ivf::layout::writer::IvfLayoutOptions::max_iterations: u32
10+
11+
pub vortex_ivf::layout::writer::IvfLayoutOptions::nprobes: u32
12+
13+
pub vortex_ivf::layout::writer::IvfLayoutOptions::num_clusters: u32
14+
15+
pub vortex_ivf::layout::writer::IvfLayoutOptions::seed: u64
16+
17+
impl core::clone::Clone for vortex_ivf::layout::writer::IvfLayoutOptions
18+
19+
pub fn vortex_ivf::layout::writer::IvfLayoutOptions::clone(&self) -> vortex_ivf::layout::writer::IvfLayoutOptions
20+
21+
impl core::convert::From<vortex_ivf::layout::writer::IvfLayoutOptions> for vortex_ivf::IvfBuildConfig
22+
23+
pub fn vortex_ivf::IvfBuildConfig::from(value: vortex_ivf::layout::writer::IvfLayoutOptions) -> Self
24+
25+
impl core::default::Default for vortex_ivf::layout::writer::IvfLayoutOptions
26+
27+
pub fn vortex_ivf::layout::writer::IvfLayoutOptions::default() -> Self
28+
29+
impl core::fmt::Debug for vortex_ivf::layout::writer::IvfLayoutOptions
30+
31+
pub fn vortex_ivf::layout::writer::IvfLayoutOptions::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
32+
33+
pub struct vortex_ivf::layout::writer::IvfStrategy
34+
35+
impl vortex_ivf::layout::writer::IvfStrategy
36+
37+
pub fn vortex_ivf::layout::writer::IvfStrategy::new<D: vortex_layout::strategy::LayoutStrategy, C: vortex_layout::strategy::LayoutStrategy>(data: D, centroids: C, options: vortex_ivf::layout::writer::IvfLayoutOptions) -> Self
38+
39+
impl vortex_layout::strategy::LayoutStrategy for vortex_ivf::layout::writer::IvfStrategy
40+
41+
pub fn vortex_ivf::layout::writer::IvfStrategy::buffered_bytes(&self) -> u64
42+
43+
pub fn vortex_ivf::layout::writer::IvfStrategy::write_stream<'life0, 'life1, 'async_trait>(&'life0 self, ctx: vortex_array::ArrayContext, segment_sink: vortex_layout::segments::sink::SegmentSinkRef, stream: vortex_layout::sequence::SendableSequentialStream, eof: vortex_layout::sequence::SequencePointer, session: &'life1 vortex_session::VortexSession) -> core::pin::Pin<alloc::boxed::Box<(dyn core::future::future::Future<Output = vortex_error::VortexResult<vortex_layout::layout::LayoutRef>> + core::marker::Send + 'async_trait)>> where Self: 'async_trait, 'life0: 'async_trait, 'life1: 'async_trait
44+
45+
pub struct vortex_ivf::layout::Ivf
46+
47+
impl core::fmt::Debug for vortex_ivf::layout::Ivf
48+
49+
pub fn vortex_ivf::layout::Ivf::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
50+
51+
impl vortex_layout::vtable::VTable for vortex_ivf::layout::Ivf
52+
53+
pub type vortex_ivf::layout::Ivf::Encoding = vortex_ivf::layout::IvfLayoutEncoding
54+
55+
pub type vortex_ivf::layout::Ivf::Layout = vortex_ivf::layout::IvfLayout
56+
57+
pub type vortex_ivf::layout::Ivf::Metadata = vortex_array::metadata::ProstMetadata<vortex_ivf::layout::IvfLayoutMetadata>
58+
59+
pub fn vortex_ivf::layout::Ivf::build(_encoding: &Self::Encoding, _dtype: &vortex_array::dtype::DType, _row_count: u64, metadata: &<Self::Metadata as vortex_array::metadata::DeserializeMetadata>::Output, _segment_ids: alloc::vec::Vec<vortex_layout::segments::SegmentId>, children: &dyn vortex_layout::children::LayoutChildren, _ctx: &vortex_session::registry::ReadContext) -> vortex_error::VortexResult<Self::Layout>
60+
61+
pub fn vortex_ivf::layout::Ivf::child(layout: &Self::Layout, idx: usize) -> vortex_error::VortexResult<vortex_layout::layout::LayoutRef>
62+
63+
pub fn vortex_ivf::layout::Ivf::child_type(_layout: &Self::Layout, idx: usize) -> vortex_layout::layout::LayoutChildType
64+
65+
pub fn vortex_ivf::layout::Ivf::dtype(layout: &Self::Layout) -> &vortex_array::dtype::DType
66+
67+
pub fn vortex_ivf::layout::Ivf::encoding(_layout: &Self::Layout) -> vortex_layout::encoding::LayoutEncodingRef
68+
69+
pub fn vortex_ivf::layout::Ivf::id(_encoding: &Self::Encoding) -> vortex_layout::layout::LayoutId
70+
71+
pub fn vortex_ivf::layout::Ivf::metadata(layout: &Self::Layout) -> Self::Metadata
72+
73+
pub fn vortex_ivf::layout::Ivf::nchildren(_layout: &Self::Layout) -> usize
74+
75+
pub fn vortex_ivf::layout::Ivf::new_reader(layout: &Self::Layout, name: alloc::sync::Arc<str>, segment_source: alloc::sync::Arc<dyn vortex_layout::segments::source::SegmentSource>, session: &vortex_session::VortexSession) -> vortex_error::VortexResult<vortex_layout::reader::LayoutReaderRef>
76+
77+
pub fn vortex_ivf::layout::Ivf::row_count(layout: &Self::Layout) -> u64
78+
79+
pub fn vortex_ivf::layout::Ivf::segment_ids(_layout: &Self::Layout) -> alloc::vec::Vec<vortex_layout::segments::SegmentId>
80+
81+
pub fn vortex_ivf::layout::Ivf::with_children(layout: &mut Self::Layout, children: alloc::vec::Vec<vortex_layout::layout::LayoutRef>) -> vortex_error::VortexResult<()>
82+
83+
pub struct vortex_ivf::layout::IvfLayout
84+
85+
impl vortex_ivf::layout::IvfLayout
86+
87+
pub fn vortex_ivf::layout::IvfLayout::centroids(&self) -> &vortex_layout::layout::LayoutRef
88+
89+
pub fn vortex_ivf::layout::IvfLayout::data(&self) -> &vortex_layout::layout::LayoutRef
90+
91+
pub fn vortex_ivf::layout::IvfLayout::dim(&self) -> u32
92+
93+
pub fn vortex_ivf::layout::IvfLayout::nprobes(&self) -> u32
94+
95+
pub fn vortex_ivf::layout::IvfLayout::num_clusters(&self) -> u32
96+
97+
pub fn vortex_ivf::layout::IvfLayout::try_new(data: vortex_layout::layout::LayoutRef, centroids: vortex_layout::layout::LayoutRef, dim: u32, num_clusters: u32, nprobes: u32) -> vortex_error::VortexResult<Self>
98+
99+
impl core::clone::Clone for vortex_ivf::layout::IvfLayout
100+
101+
pub fn vortex_ivf::layout::IvfLayout::clone(&self) -> vortex_ivf::layout::IvfLayout
102+
103+
impl core::convert::AsRef<dyn vortex_layout::layout::Layout> for vortex_ivf::layout::IvfLayout
104+
105+
pub fn vortex_ivf::layout::IvfLayout::as_ref(&self) -> &dyn vortex_layout::layout::Layout
106+
107+
impl core::convert::From<vortex_ivf::layout::IvfLayout> for vortex_layout::layout::LayoutRef
108+
109+
pub fn vortex_layout::layout::LayoutRef::from(value: vortex_ivf::layout::IvfLayout) -> vortex_layout::layout::LayoutRef
110+
111+
impl core::fmt::Debug for vortex_ivf::layout::IvfLayout
112+
113+
pub fn vortex_ivf::layout::IvfLayout::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
114+
115+
impl core::ops::deref::Deref for vortex_ivf::layout::IvfLayout
116+
117+
pub type vortex_ivf::layout::IvfLayout::Target = dyn vortex_layout::layout::Layout
118+
119+
pub fn vortex_ivf::layout::IvfLayout::deref(&self) -> &Self::Target
120+
121+
impl vortex_layout::layout::IntoLayout for vortex_ivf::layout::IvfLayout
122+
123+
pub fn vortex_ivf::layout::IvfLayout::into_layout(self) -> vortex_layout::layout::LayoutRef
124+
125+
pub struct vortex_ivf::layout::IvfLayoutEncoding
126+
127+
impl core::convert::AsRef<dyn vortex_layout::encoding::LayoutEncoding> for vortex_ivf::layout::IvfLayoutEncoding
128+
129+
pub fn vortex_ivf::layout::IvfLayoutEncoding::as_ref(&self) -> &dyn vortex_layout::encoding::LayoutEncoding
130+
131+
impl core::fmt::Debug for vortex_ivf::layout::IvfLayoutEncoding
132+
133+
pub fn vortex_ivf::layout::IvfLayoutEncoding::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
134+
135+
impl core::ops::deref::Deref for vortex_ivf::layout::IvfLayoutEncoding
136+
137+
pub type vortex_ivf::layout::IvfLayoutEncoding::Target = dyn vortex_layout::encoding::LayoutEncoding
138+
139+
pub fn vortex_ivf::layout::IvfLayoutEncoding::deref(&self) -> &Self::Target
140+
141+
pub struct vortex_ivf::layout::IvfLayoutMetadata
142+
143+
pub vortex_ivf::layout::IvfLayoutMetadata::dim: u32
144+
145+
pub vortex_ivf::layout::IvfLayoutMetadata::nprobes: u32
146+
147+
pub vortex_ivf::layout::IvfLayoutMetadata::num_clusters: u32
148+
149+
impl vortex_ivf::layout::IvfLayoutMetadata
150+
151+
pub fn vortex_ivf::layout::IvfLayoutMetadata::new(dim: u32, nprobes: u32, num_clusters: u32) -> Self
152+
153+
impl core::default::Default for vortex_ivf::layout::IvfLayoutMetadata
154+
155+
pub fn vortex_ivf::layout::IvfLayoutMetadata::default() -> Self
156+
157+
impl core::fmt::Debug for vortex_ivf::layout::IvfLayoutMetadata
158+
159+
pub fn vortex_ivf::layout::IvfLayoutMetadata::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
160+
161+
impl prost::message::Message for vortex_ivf::layout::IvfLayoutMetadata
162+
163+
pub fn vortex_ivf::layout::IvfLayoutMetadata::clear(&mut self)
164+
165+
pub fn vortex_ivf::layout::IvfLayoutMetadata::encoded_len(&self) -> usize
166+
167+
pub const vortex_ivf::layout::DEFAULT_NPROBES: u32
168+
169+
pub const vortex_ivf::layout::IVF_LAYOUT_ID: &str
170+
171+
pub fn vortex_ivf::layout::register_ivf_layout(session: &vortex_session::VortexSession)
172+
173+
pub mod vortex_ivf::partitioned
174+
175+
pub struct vortex_ivf::partitioned::IvfPartitionedIndex
176+
177+
impl vortex_ivf::partitioned::IvfPartitionedIndex
178+
179+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::cluster_offsets(&self) -> &[u64]
180+
181+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::from_index(index: vortex_ivf::IvfIndex) -> Self
182+
183+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::index(&self) -> &vortex_ivf::IvfIndex
184+
185+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::permutation(&self) -> &[u32]
186+
187+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::probe_ranges(&self, probed_clusters: &[usize]) -> alloc::vec::Vec<core::ops::range::Range<u64>>
188+
189+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::query_ranges(&self, query: &[f32], nprobes: usize) -> vortex_error::VortexResult<alloc::vec::Vec<core::ops::range::Range<u64>>>
190+
191+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::selectivity(&self, ranges: &[core::ops::range::Range<u64>]) -> f64
192+
193+
impl core::clone::Clone for vortex_ivf::partitioned::IvfPartitionedIndex
194+
195+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::clone(&self) -> vortex_ivf::partitioned::IvfPartitionedIndex
196+
197+
impl core::fmt::Debug for vortex_ivf::partitioned::IvfPartitionedIndex
198+
199+
pub fn vortex_ivf::partitioned::IvfPartitionedIndex::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
200+
201+
pub mod vortex_ivf::search
202+
203+
pub fn vortex_ivf::search::build_ivf_index(data: &vortex_array::array::erased::ArrayRef, config: &vortex_ivf::IvfBuildConfig, ctx: &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult<vortex_ivf::IvfIndex>
204+
205+
pub fn vortex_ivf::search::ivf_similarity_search<T: vortex_array::dtype::ptype::NativePType + core::convert::Into<vortex_array::scalar::typed_view::primitive::pvalue::PValue>>(data: vortex_array::array::erased::ArrayRef, index: &vortex_ivf::IvfIndex, query: &[T], threshold: T, nprobes: usize, ctx: &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult<vortex_array::arrays::bool::vtable::BoolArray>
206+
207+
pub mod vortex_ivf::tq
208+
209+
pub struct vortex_ivf::tq::TurboQuantIvfIndex
210+
211+
impl vortex_ivf::tq::TurboQuantIvfIndex
212+
213+
pub fn vortex_ivf::tq::TurboQuantIvfIndex::index(&self) -> &vortex_ivf::IvfIndex
214+
215+
pub fn vortex_ivf::tq::TurboQuantIvfIndex::probe(&self, query: &[f32], nprobes: usize) -> vortex_error::VortexResult<alloc::vec::Vec<usize>>
216+
217+
pub fn vortex_ivf::tq::TurboQuantIvfIndex::sorf_options(&self) -> &vortex_tensor::scalar_fns::sorf_transform::SorfOptions
218+
219+
impl core::clone::Clone for vortex_ivf::tq::TurboQuantIvfIndex
220+
221+
pub fn vortex_ivf::tq::TurboQuantIvfIndex::clone(&self) -> vortex_ivf::tq::TurboQuantIvfIndex
222+
223+
impl core::fmt::Debug for vortex_ivf::tq::TurboQuantIvfIndex
224+
225+
pub fn vortex_ivf::tq::TurboQuantIvfIndex::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
226+
227+
pub fn vortex_ivf::tq::build_ivf_from_turboquant(data: &vortex_array::array::erased::ArrayRef, config: &vortex_ivf::IvfBuildConfig, ctx: &mut vortex_array::executor::ExecutionCtx) -> vortex_error::VortexResult<vortex_ivf::tq::TurboQuantIvfIndex>
228+
229+
pub fn vortex_ivf::tq::rotate_query(query: &[f32], options: &vortex_tensor::scalar_fns::sorf_transform::SorfOptions) -> vortex_error::VortexResult<alloc::vec::Vec<f32>>
230+
231+
pub struct vortex_ivf::IvfBuildConfig
232+
233+
pub vortex_ivf::IvfBuildConfig::max_iterations: u32
234+
235+
pub vortex_ivf::IvfBuildConfig::num_clusters: u32
236+
237+
pub vortex_ivf::IvfBuildConfig::seed: u64
238+
239+
impl core::clone::Clone for vortex_ivf::IvfBuildConfig
240+
241+
pub fn vortex_ivf::IvfBuildConfig::clone(&self) -> vortex_ivf::IvfBuildConfig
242+
243+
impl core::convert::From<vortex_ivf::layout::writer::IvfLayoutOptions> for vortex_ivf::IvfBuildConfig
244+
245+
pub fn vortex_ivf::IvfBuildConfig::from(value: vortex_ivf::layout::writer::IvfLayoutOptions) -> Self
246+
247+
impl core::default::Default for vortex_ivf::IvfBuildConfig
248+
249+
pub fn vortex_ivf::IvfBuildConfig::default() -> Self
250+
251+
impl core::fmt::Debug for vortex_ivf::IvfBuildConfig
252+
253+
pub fn vortex_ivf::IvfBuildConfig::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
254+
255+
pub struct vortex_ivf::IvfIndex
256+
257+
impl vortex_ivf::IvfIndex
258+
259+
pub fn vortex_ivf::IvfIndex::assignments(&self) -> &[u32]
260+
261+
pub fn vortex_ivf::IvfIndex::build(vectors: &[f32], dim: usize, config: &vortex_ivf::IvfBuildConfig) -> vortex_error::VortexResult<Self>
262+
263+
pub fn vortex_ivf::IvfIndex::build_probe_mask(&self, probed_clusters: &[usize]) -> alloc::vec::Vec<bool>
264+
265+
pub fn vortex_ivf::IvfIndex::centroids(&self) -> &[f32]
266+
267+
pub fn vortex_ivf::IvfIndex::cluster_sizes(&self) -> alloc::vec::Vec<usize>
268+
269+
pub fn vortex_ivf::IvfIndex::dim(&self) -> usize
270+
271+
pub fn vortex_ivf::IvfIndex::num_clusters(&self) -> usize
272+
273+
pub fn vortex_ivf::IvfIndex::num_vectors(&self) -> usize
274+
275+
pub fn vortex_ivf::IvfIndex::probe(&self, query: &[f32], nprobes: usize) -> vortex_error::VortexResult<alloc::vec::Vec<usize>>
276+
277+
pub fn vortex_ivf::IvfIndex::query_mask(&self, query: &[f32], nprobes: usize) -> vortex_error::VortexResult<alloc::vec::Vec<bool>>
278+
279+
impl core::clone::Clone for vortex_ivf::IvfIndex
280+
281+
pub fn vortex_ivf::IvfIndex::clone(&self) -> vortex_ivf::IvfIndex
282+
283+
impl core::fmt::Debug for vortex_ivf::IvfIndex
284+
285+
pub fn vortex_ivf::IvfIndex::fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result
Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,28 @@
77
//! Lloyd's algorithm for refinement. All computation is in f32. Vectors
88
//! are expected in row-major layout: a flat `[n * dim]` slice.
99
10-
use crate::scalar_fns::sorf_transform::splitmix64::SplitMix64;
10+
/// A minimal deterministic PRNG used for k-means++ initialization.
11+
///
12+
/// This is a SplitMix64 implementation that matches the one used in other parts of Vortex
13+
/// (see `vortex-tensor::scalar_fns::sorf_transform::splitmix64`). It is duplicated here
14+
/// to avoid a public dependency on that private module.
15+
struct SplitMix64 {
16+
state: u64,
17+
}
18+
19+
impl SplitMix64 {
20+
fn new(seed: u64) -> Self {
21+
Self { state: seed }
22+
}
23+
24+
fn next_u64(&mut self) -> u64 {
25+
self.state = self.state.wrapping_add(0x9E37_79B9_7F4A_7C15);
26+
let mut z = self.state;
27+
z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9);
28+
z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB);
29+
z ^ (z >> 31)
30+
}
31+
}
1132

1233
/// Result of k-means clustering.
1334
pub(super) struct KMeansResult {

0 commit comments

Comments
 (0)