data_beans/sparse_io_vector/
batch.rs1#![allow(dead_code)]
2
3use super::*;
4
5impl SparseIoVec {
6 pub fn register_batches_ndarray<T>(
17 &mut self,
18 feature_matrix: &ndarray::Array2<f32>,
19 batch_membership: &[T],
20 ) -> anyhow::Result<()>
21 where
22 T: Sync + Send + std::hash::Hash + Eq + Clone + ToString,
23 {
24 {
25 debug_assert_eq!(batch_membership.len(), feature_matrix.ncols());
26 }
27 self._register_batches(
28 feature_matrix,
29 batch_membership,
30 |feature_matrix, batch_cells| {
31 let columns = batch_cells
32 .iter()
33 .map(|&c| feature_matrix.column(c))
34 .collect::<Vec<_>>();
35 ColumnDict::<usize>::from_ndarray_views(columns, batch_cells.clone())
36 },
37 )
38 }
39
40 pub fn register_batches_dmatrix<T>(
47 &mut self,
48 feature_matrix: &nalgebra::DMatrix<f32>,
49 batch_membership: &[T],
50 ) -> anyhow::Result<()>
51 where
52 T: Sync + Send + std::hash::Hash + Eq + Clone + ToString,
53 {
54 {
55 debug_assert_eq!(batch_membership.len(), feature_matrix.ncols());
56 }
57
58 self._register_batches(
59 feature_matrix,
60 batch_membership,
61 |feature_matrix, batch_cells| {
62 let columns = batch_cells
63 .iter()
64 .map(|&c| feature_matrix.column(c))
65 .collect::<Vec<_>>();
66 ColumnDict::<usize>::from_dvector_views(columns, batch_cells.clone())
67 },
68 )
69 }
70
71 fn _register_batches<M, F, T>(
72 &mut self,
73 feature_matrix: &M,
74 batch_membership: &[T],
75 create_column_dict: F,
76 ) -> anyhow::Result<()>
77 where
78 M: Sync,
79 F: Fn(&M, &Vec<usize>) -> ColumnDict<usize> + Sync,
80 T: Sync + Send + std::hash::Hash + Eq + Clone + ToString,
81 {
82 let batches = partition_by_membership(batch_membership, None);
83
84 let ntot = self.num_columns();
85 let mut col_to_batch = vec![0; ntot];
86
87 let n_threads = rayon::current_num_threads();
96 let outer_parallel = batches.len() >= n_threads;
97
98 info!(
99 "building per-batch kNN indices ({} batches, {} cells, {}) ...",
100 batches.len(),
101 ntot,
102 if outer_parallel {
103 "parallel over batches"
104 } else {
105 "sequential over batches"
106 }
107 );
108
109 let prog_bar =
110 crate::sparse_data_visitors::styled_progress_bar(batches.len() as u64, "batches kNN");
111
112 let mut batches_vec: Vec<_> = batches.into_iter().collect();
119 batches_vec.sort_by(|a, b| a.0.to_string().cmp(&b.0.to_string()));
120 let mut enumerated: Vec<_> = batches_vec.iter().enumerate().collect();
124 enumerated.sort_by_key(|(_, (_, cells))| std::cmp::Reverse(cells.len()));
125 let mut idx_name_glob_dict: Vec<_> = if outer_parallel {
126 enumerated
127 .into_par_iter()
128 .progress_with(prog_bar.clone())
129 .map(|(batch_index, (batch_name, batch_glob_indices))| {
130 (
131 batch_index,
132 batch_name.to_string().into_boxed_str(),
133 batch_glob_indices.clone(),
134 create_column_dict(feature_matrix, batch_glob_indices),
135 )
136 })
137 .collect()
138 } else {
139 enumerated
140 .into_iter()
141 .progress_with(prog_bar.clone())
142 .map(|(batch_index, (batch_name, batch_glob_indices))| {
143 (
144 batch_index,
145 batch_name.to_string().into_boxed_str(),
146 batch_glob_indices.clone(),
147 create_column_dict(feature_matrix, batch_glob_indices),
148 )
149 })
150 .collect()
151 };
152 prog_bar.finish_and_clear();
153
154 idx_name_glob_dict.sort_by_key(|&(idx, _, _, _)| idx);
155
156 let mut batch_names = vec![];
157 let mut batch_to_cols = vec![];
158 let mut dictionaries = vec![];
159
160 for (batch_idx, batch_name, glob_indices, dict) in idx_name_glob_dict.into_iter() {
161 dict.names()
162 .iter()
163 .for_each(|&cell| col_to_batch[cell] = batch_idx);
164
165 batch_names.push(batch_name);
166 batch_to_cols.push(glob_indices);
167 dictionaries.push(dict);
168 }
169
170 self.derived.batch_knn_lookup = Some(dictionaries);
171 self.derived.col_to_batch = Some(col_to_batch);
172 self.derived.batch_to_cols = Some(batch_to_cols);
173 self.derived.batch_idx_to_name = Some(batch_names);
174
175 if self.num_batches() > 2 {
176 self.sort_batch_proximity()?;
177 }
178
179 Ok(())
180 }
181
182 fn sort_batch_proximity(&mut self) -> anyhow::Result<()> {
183 let lookups = self
184 .derived
185 .batch_knn_lookup
186 .as_ref()
187 .ok_or(anyhow::anyhow!("no knn lookup"))?;
188
189 use nalgebra::DMatrix;
190
191 info!("retrieving batch-specific lookups");
192 let batch_data = lookups
193 .iter()
194 .flat_map(|dict| {
195 let data: Vec<f32> = dict.points().flatten().copied().collect();
196 let ncols = dict.num_points();
197 let nrows = data.len() / ncols;
198 DMatrix::from_vec(nrows, ncols, data)
199 .column_mean()
200 .data
201 .as_vec()
202 .clone()
203 })
204 .collect::<Vec<_>>();
205
206 let ncols = self.num_batches();
207 let nrows = batch_data.len() / ncols;
208 let batch_features = DMatrix::<f32>::from_vec(nrows, ncols, batch_data);
209
210 info!(
211 "built feature matrix across batches: {} x {}",
212 batch_features.nrows(),
213 batch_features.ncols()
214 );
215
216 let nbatches = self.num_batches();
217 let batches = (0..nbatches).collect();
218
219 let dict = ColumnDict::<usize>::from_dvector_views(
220 batch_features.column_iter().collect(),
221 batches,
222 );
223
224 let ret: Vec<Vec<usize>> = (0..nbatches)
225 .into_par_iter()
226 .map(|b| {
227 dict.search_by_query_name(&b, nbatches, false)
228 .map(|(others, _)| others)
229 })
230 .collect::<anyhow::Result<Vec<Vec<usize>>>>()?;
231 self.derived.between_batch_proximity = Some(ret);
232
233 Ok(())
234 }
235
236 pub fn batch_name_map(&self) -> Option<HashMap<Box<str>, usize>> {
237 self.derived.batch_idx_to_name.as_ref().map(|names| {
238 names
239 .iter()
240 .enumerate()
241 .map(|(idx, name)| (name.clone(), idx))
242 .collect::<HashMap<Box<str>, usize>>()
243 })
244 }
245
246 pub fn num_batches(&self) -> usize {
247 if let Some(v) = &self.derived.batch_to_cols {
248 v.len()
249 } else if let Some(v) = &self.derived.batch_knn_lookup {
250 v.len()
251 } else {
252 0
253 }
254 }
255
256 pub fn batch_knn_lookup(&self) -> Option<&Vec<ColumnDict<usize>>> {
260 self.derived.batch_knn_lookup.as_ref()
261 }
262
263 pub fn register_batch_membership<T>(&mut self, batch_membership: &[T])
267 where
268 T: Sync + Send + std::hash::Hash + Eq + Clone + ToString,
269 {
270 let batches = partition_by_membership(batch_membership, None);
271 let ntot = self.num_columns();
272 let mut col_to_batch = vec![0; ntot];
273
274 let mut sorted_batches: Vec<_> = batches.into_iter().collect();
275 sorted_batches.sort_by(|a, b| a.0.to_string().cmp(&b.0.to_string()));
276
277 let mut batch_names = Vec::with_capacity(sorted_batches.len());
278 let mut batch_to_cols = Vec::with_capacity(sorted_batches.len());
279
280 for (batch_idx, (batch_name, glob_indices)) in sorted_batches.into_iter().enumerate() {
281 for &cell in &glob_indices {
282 col_to_batch[cell] = batch_idx;
283 }
284 batch_names.push(batch_name.to_string().into_boxed_str());
285 batch_to_cols.push(glob_indices);
286 }
287
288 self.derived.col_to_batch = Some(col_to_batch);
289 self.derived.batch_to_cols = Some(batch_to_cols);
290 self.derived.batch_idx_to_name = Some(batch_names);
291 }
292
293 pub fn register_column_multiplicity(&mut self, multiplicity: &[f32]) -> anyhow::Result<()> {
312 let ntot = self.num_columns();
313 anyhow::ensure!(
314 multiplicity.len() == ntot,
315 "column multiplicity has {} entries but there are {ntot} columns",
316 multiplicity.len(),
317 );
318 if let Some((i, w)) = multiplicity
319 .iter()
320 .enumerate()
321 .find(|(_, w)| !w.is_finite() || **w <= 0.0)
322 {
323 anyhow::bail!("column {i} has multiplicity {w}; weights must be finite and positive");
324 }
325 self.derived.col_multiplicity = Some(multiplicity.to_vec());
326 Ok(())
327 }
328
329 #[must_use]
331 pub fn column_multiplicity(&self, col: usize) -> f32 {
332 self.derived
333 .col_multiplicity
334 .as_ref()
335 .map_or(1.0, |m| m[col])
336 }
337
338 #[must_use]
340 pub fn has_column_multiplicity(&self) -> bool {
341 self.derived.col_multiplicity.is_some()
342 }
343
344 #[must_use]
351 pub fn column_multiplicities(&self) -> Option<&[f32]> {
352 self.derived.col_multiplicity.as_deref()
353 }
354
355 pub fn batch_names(&self) -> Option<Vec<Box<str>>> {
356 self.derived.batch_idx_to_name.clone()
357 }
358
359 pub fn batch_to_columns(&self, batch: usize) -> Option<&Vec<usize>> {
360 if let Some(batch_to_cols) = &self.derived.batch_to_cols {
361 Some(&batch_to_cols[batch])
362 } else {
363 None
364 }
365 }
366
367 pub fn get_batch_membership<I>(&self, cells: I) -> Vec<usize>
368 where
369 I: Iterator<Item = usize>,
370 {
371 let cell_to_batch = self
372 .derived
373 .col_to_batch
374 .as_ref()
375 .expect("cell_to_batch not initialized");
376 cells.into_iter().map(|c| cell_to_batch[c]).collect()
377 }
378
379 pub fn column_names(&self) -> anyhow::Result<Vec<Box<str>>> {
380 debug_assert_eq!(self.num_columns(), self.column_names_with_data_tag.len());
381 Ok(self.column_names_with_data_tag.clone())
382 }
383}