Train the pruner from a `MultiVectorStore`. `num_centroids` — number of K-means clusters. `kmeans_iters` — Lloyd iterations. `seed` — deterministic seed for centroid initialisation.
(
store: &MultiVectorStore,
num_centroids: u16,
kmeans_iters: usize,
seed: u64,
)
| 206 | /// * `kmeans_iters` — Lloyd iterations. |
| 207 | /// * `seed` — deterministic seed for centroid initialisation. |
| 208 | pub fn train( |
| 209 | store: &MultiVectorStore, |
| 210 | num_centroids: u16, |
| 211 | kmeans_iters: usize, |
| 212 | seed: u64, |
| 213 | ) -> Self { |
| 214 | let dim = store.dim; |
| 215 | let nc = num_centroids as usize; |
| 216 | |
| 217 | // Collect all document vectors for K-means training. |
| 218 | let all_vectors: Vec<Vec<f32>> = store |
| 219 | .iter() |
| 220 | .flat_map(|doc| doc.vectors.iter().cloned()) |
| 221 | .collect(); |
| 222 | |
| 223 | let centroids = kmeans(&all_vectors, nc, kmeans_iters, seed, dim); |
| 224 | |
| 225 | // Encode each document as a sorted, deduplicated bag of centroid IDs. |
| 226 | let doc_centroids: HashMap<u32, Vec<u16>> = store |
| 227 | .iter() |
| 228 | .map(|doc| { |
| 229 | let mut ids: Vec<u16> = doc |
| 230 | .vectors |
| 231 | .iter() |
| 232 | .map(|v| { |
| 233 | centroids |
| 234 | .iter() |
| 235 | .enumerate() |
| 236 | .map(|(i, c)| (i as u16, scalar_distance(v, c, DistanceMetric::L2))) |
| 237 | .min_by(|a, b| { |
| 238 | a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal) |
| 239 | }) |
| 240 | .map(|(i, _)| i) |
| 241 | .unwrap_or(0) |
| 242 | }) |
| 243 | .collect(); |
| 244 | ids.sort_unstable(); |
| 245 | ids.dedup(); |
| 246 | (doc.doc_id, ids) |
| 247 | }) |
| 248 | .collect(); |
| 249 | |
| 250 | Self { |
| 251 | centroids, |
| 252 | doc_centroids, |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | /// Return candidate doc IDs whose centroid bag overlaps the query's |
| 257 | /// centroid bag. |
nothing calls this directly
no test coverage detected