Train PQ codebooks via k-means on a sample of vectors. Validates that: - `samples` is non-empty. - Every sample has length `self.dim`. - `self.dim % self.m == 0` (PQ requires divisible dimensionality). - At least `self.k` samples are provided (k-means needs ≥ k points). On success, stores the trained codec and subsequent `encode` / `distance_prepared` calls will succeed.
(&mut self, samples: &[&[f32]])
| 188 | /// On success, stores the trained codec and subsequent `encode` / |
| 189 | /// `distance_prepared` calls will succeed. |
| 190 | fn train(&mut self, samples: &[&[f32]]) -> Result<(), RerankError> { |
| 191 | if samples.is_empty() { |
| 192 | return Err(RerankError::BadInput( |
| 193 | "pq train: empty sample set".to_string(), |
| 194 | )); |
| 195 | } |
| 196 | for s in samples { |
| 197 | if s.len() != self.dim { |
| 198 | return Err(RerankError::BadInput(format!( |
| 199 | "pq train: sample has len {} but codec dim is {}", |
| 200 | s.len(), |
| 201 | self.dim |
| 202 | ))); |
| 203 | } |
| 204 | } |
| 205 | if !self.dim.is_multiple_of(self.m) { |
| 206 | return Err(RerankError::BadInput(format!( |
| 207 | "pq train: dim ({}) must be divisible by m ({})", |
| 208 | self.dim, self.m |
| 209 | ))); |
| 210 | } |
| 211 | if samples.len() < self.k { |
| 212 | return Err(RerankError::BadInput(format!( |
| 213 | "pq train: need >= k samples for k-means, got {}", |
| 214 | samples.len() |
| 215 | ))); |
| 216 | } |
| 217 | let codec = PqCodec::train(samples, self.dim, self.m, self.k, self.max_iter); |
| 218 | self.codec = Some(codec); |
| 219 | Ok(()) |
| 220 | } |
| 221 | } |
| 222 | |
| 223 | // ── Tests ───────────────────────────────────────────────────────────────────── |