1#![allow(dead_code)]
2
3use super::*;
4
5pub type MatchedColumnsCsc = (CscMatrix<f32>, Vec<usize>, Vec<usize>, Vec<f32>);
8
9impl SparseIoVec {
10 fn matched_columns_triplets_on_one_target<I>(
28 &self,
29 cells: I,
30 target_batch: usize,
31 knn: usize,
32 skip_same_batch: bool,
33 ) -> anyhow::Result<TripletsMatched>
34 where
35 I: Iterator<Item = usize> + Clone,
36 {
37 let lookups = self
38 .derived
39 .batch_knn_lookup
40 .as_ref()
41 .ok_or(anyhow::anyhow!("no knn lookup"))?;
42
43 let cell_to_batch = self
44 .derived
45 .col_to_batch
46 .as_ref()
47 .ok_or(anyhow::anyhow!("no cell to batch"))?;
48
49 debug_assert!(target_batch < self.num_batches());
50
51 let mut distances = Vec::new();
52 let mut source_columns = Vec::new();
53 let mut matched_columns = Vec::new();
54
55 for glob in cells {
56 let source_batch = cell_to_batch[glob]; if skip_same_batch && source_batch == target_batch {
59 continue; }
61
62 if let (Some(source_lookup), Some(target_lookup)) =
63 (lookups.get(source_batch), lookups.get(target_batch))
64 {
65 let (matched, matched_distances) =
66 source_lookup.match_by_query_name_against(&glob, knn, target_lookup)?;
67 for (glob_matched, dist) in matched.into_iter().zip(matched_distances) {
68 if glob == glob_matched {
69 continue; }
71 source_columns.push(glob);
72 matched_columns.push(glob_matched);
73 distances.push(dist);
74 }
75 }
76 }
77
78 self.read_matched(source_columns, matched_columns, distances)
79 }
80
81 pub(super) fn read_matched(
88 &self,
89 source_columns: Vec<usize>,
90 matched_columns: Vec<usize>,
91 distances: Vec<f32>,
92 ) -> anyhow::Result<TripletsMatched> {
93 let (shape, triplets) = self.columns_triplets(matched_columns.iter().copied())?;
94 Ok(TripletsMatched {
95 shape,
96 triplets,
97 source_columns,
98 matched_columns,
99 distances,
100 })
101 }
102
103 fn matched_columns_triplets<I>(
117 &self,
118 cells: I,
119 target_batches: &[usize],
120 knn_columns: usize,
121 skip_same_batch: bool,
122 ) -> anyhow::Result<TripletsMatched>
123 where
124 I: Iterator<Item = usize>,
125 {
126 let cells: Vec<usize> = cells.collect();
127
128 let nrows = self.num_rows();
129 let nbatches = self.num_batches();
130 let ncols = cells.len();
131 let approx_ncols = ncols * knn_columns * nbatches;
132
133 let mut tot_triplets: Vec<(u64, u64, f32)> = Vec::with_capacity(approx_ncols);
134 let mut tot_distances: Vec<f32> = Vec::with_capacity(approx_ncols);
135 let mut tot_sources: Vec<usize> = Vec::with_capacity(approx_ncols);
136 let mut tot_matched: Vec<usize> = Vec::with_capacity(approx_ncols);
137 let mut tot_ncells_matched: usize = 0;
138
139 for &target_b in target_batches.iter() {
140 let TripletsMatched {
141 shape,
142 triplets,
143 source_columns,
144 matched_columns,
145 distances,
146 } = self.matched_columns_triplets_on_one_target(
147 cells.iter().cloned(),
148 target_b,
149 knn_columns,
150 skip_same_batch,
151 )?;
152
153 tot_triplets.extend(
154 triplets
155 .into_iter()
156 .map(|(i, j, z_ij)| (i, j + (tot_ncells_matched as u64), z_ij)),
157 );
158
159 tot_distances.extend(distances);
160 tot_ncells_matched += shape.1;
161 tot_sources.extend(source_columns);
162 tot_matched.extend(matched_columns);
163 }
164
165 let shape = (nrows, tot_ncells_matched);
166
167 Ok(TripletsMatched {
168 shape,
169 triplets: tot_triplets,
170 source_columns: tot_sources,
171 matched_columns: tot_matched,
172 distances: tot_distances,
173 })
174 }
175
176 fn neighbouring_columns_triplets<I>(
190 &self,
191 cells: I,
192 knn_batches: usize,
193 knn_columns: usize,
194 skip_same_batch: bool,
195 skip_batches: Option<&[usize]>,
196 ) -> anyhow::Result<TripletsMatched>
197 where
198 I: Iterator<Item = usize>,
199 {
200 let lookups = self
201 .derived
202 .batch_knn_lookup
203 .as_ref()
204 .ok_or(anyhow::anyhow!("no knn lookup"))?;
205
206 let cell_to_batch = self
207 .derived
208 .col_to_batch
209 .as_ref()
210 .ok_or(anyhow::anyhow!("no cell to batch"))?;
211
212 let approx_ncol = knn_columns * knn_batches;
213
214 let mut distances = Vec::with_capacity(approx_ncol);
215 let mut source_columns = Vec::with_capacity(approx_ncol);
216 let mut matched_columns = Vec::with_capacity(approx_ncol);
217
218 let nbatches = self.num_batches();
219
220 let neighbouring_batches_by_source: Vec<Vec<usize>> = (0..nbatches)
222 .map(
223 |source_batch| match self.derived.between_batch_proximity.as_ref() {
224 Some(prox) => prox[source_batch]
225 .iter()
226 .copied()
227 .filter(|&b| skip_batches.is_none_or(|skip| !skip.contains(&b)))
228 .filter(|&b| !skip_same_batch || b != source_batch)
229 .collect(),
230 _ => (0..nbatches)
231 .filter(|&b| skip_batches.is_none_or(|skip| !skip.contains(&b)))
232 .filter(|&b| !skip_same_batch || b != source_batch)
233 .collect(),
234 },
235 )
236 .collect();
237
238 for glob_index in cells {
239 let source_batch = cell_to_batch[glob_index];
240
241 for &target_batch in &neighbouring_batches_by_source[source_batch] {
242 if let (Some(source_lookup), Some(target_lookup)) =
243 (lookups.get(source_batch), lookups.get(target_batch))
244 {
245 let (matched, matched_distances) = source_lookup.match_by_query_name_against(
246 &glob_index,
247 knn_columns,
248 target_lookup,
249 )?;
250 for (glob_matched_index, dist) in matched.into_iter().zip(matched_distances) {
251 if glob_index == glob_matched_index {
252 continue;
253 }
254 source_columns.push(glob_index);
255 matched_columns.push(glob_matched_index);
256 distances.push(dist);
257 }
258 }
259 }
260 }
261
262 self.read_matched(source_columns, matched_columns, distances)
263 }
264
265 fn query_columns_by_data_triplets<T>(
266 &self,
267 query: T,
268 knn_per_batch: usize,
269 ) -> anyhow::Result<TripletsMatched>
270 where
271 T: MakeVecPoint,
272 {
273 let lookups = self
274 .derived
275 .batch_knn_lookup
276 .as_ref()
277 .ok_or(anyhow::anyhow!("no knn lookup"))?;
278
279 let approx_knn = self.num_batches() * knn_per_batch;
280 let mut matched_columns = Vec::with_capacity(approx_knn);
281 let mut distances = Vec::with_capacity(approx_knn);
282
283 let q = query.to_vp();
284 for lookup in lookups {
285 let (matched, matched_distances) =
286 lookup.search_by_query_data(q.as_slice(), knn_per_batch)?;
287
288 for (&glob_idx, &dist) in matched.iter().zip(matched_distances.iter()) {
289 matched_columns.push(glob_idx);
290 distances.push(dist);
291 }
292 }
293
294 let sources = matched_columns.clone();
295 self.read_matched(sources, matched_columns, distances)
296 }
297
298 #[allow(clippy::type_complexity)]
312 pub fn read_neighbouring_columns_csc<I>(
313 &self,
314 cells: I,
315 knn_batches: usize,
316 knn_columns: usize,
317 skip_same_batch: bool,
318 skip_batches: Option<&[usize]>,
319 ) -> anyhow::Result<(CscMatrix<f32>, Vec<usize>, Vec<usize>, Vec<f32>)>
320 where
321 I: Iterator<Item = usize>,
322 {
323 let TripletsMatched {
324 shape: (nrow, ncol),
325 triplets,
326 source_columns,
327 matched_columns,
328 distances,
329 } = self.neighbouring_columns_triplets(
330 cells,
331 knn_batches,
332 knn_columns,
333 skip_same_batch,
334 skip_batches,
335 )?;
336
337 Ok((
338 CscMatrix::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
339 source_columns,
340 matched_columns,
341 distances,
342 ))
343 }
344
345 #[cfg(feature = "ndarray")]
346 pub fn read_neighbouring_columns_ndarray<I>(
359 &self,
360 cells: I,
361 knn_batches: usize,
362 knn_columns: usize,
363 skip_same_batch: bool,
364 skip_batches: Option<&[usize]>,
365 ) -> anyhow::Result<(ndarray::Array2<f32>, Vec<usize>, Vec<f32>)>
366 where
367 I: Iterator<Item = usize>,
368 {
369 let TripletsMatched {
370 shape: (nrow, ncol),
371 triplets,
372 source_columns,
373 distances,
374 ..
375 } = self.neighbouring_columns_triplets(
376 cells,
377 knn_batches,
378 knn_columns,
379 skip_same_batch,
380 skip_batches,
381 )?;
382 Ok((
383 ndarray::Array2::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
384 source_columns,
385 distances,
386 ))
387 }
388
389 pub fn read_neighbouring_columns_dmatrix<I>(
402 &self,
403 cells: I,
404 knn_batches: usize,
405 knn_columns: usize,
406 skip_same_batch: bool,
407 skip_batches: Option<&[usize]>,
408 ) -> anyhow::Result<(nalgebra::DMatrix<f32>, Vec<usize>, Vec<f32>)>
409 where
410 I: Iterator<Item = usize>,
411 {
412 let TripletsMatched {
413 shape: (nrow, ncol),
414 triplets,
415 source_columns,
416 distances,
417 ..
418 } = self.neighbouring_columns_triplets(
419 cells,
420 knn_batches,
421 knn_columns,
422 skip_same_batch,
423 skip_batches,
424 )?;
425 Ok((
426 DMatrix::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
427 source_columns,
428 distances,
429 ))
430 }
431
432 pub fn read_matched_columns_csc<I>(
445 &self,
446 cells: I,
447 target_batches: &[usize],
448 knn: usize,
449 skip_same_batch: bool,
450 ) -> anyhow::Result<(CscMatrix<f32>, Vec<usize>, Vec<f32>)>
451 where
452 I: Iterator<Item = usize>,
453 {
454 let (matrix, source_columns, _, distances) = self.read_matched_columns_with_targets_csc(
455 cells,
456 target_batches,
457 knn,
458 skip_same_batch,
459 )?;
460 Ok((matrix, source_columns, distances))
461 }
462
463 pub fn read_matched_columns_with_targets_csc<I>(
472 &self,
473 cells: I,
474 target_batches: &[usize],
475 knn: usize,
476 skip_same_batch: bool,
477 ) -> anyhow::Result<MatchedColumnsCsc>
478 where
479 I: Iterator<Item = usize>,
480 {
481 let TripletsMatched {
482 shape: (nrow, ncol),
483 triplets,
484 source_columns,
485 matched_columns,
486 distances,
487 } = self.matched_columns_triplets(cells, target_batches, knn, skip_same_batch)?;
488 Ok((
489 CscMatrix::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
490 source_columns,
491 matched_columns,
492 distances,
493 ))
494 }
495
496 #[cfg(feature = "ndarray")]
497 pub fn read_matched_columns_ndarray<I>(
510 &self,
511 cells: I,
512 target_batches: &[usize],
513 knn: usize,
514 skip_same_batch: bool,
515 ) -> anyhow::Result<(ndarray::Array2<f32>, Vec<usize>, Vec<f32>)>
516 where
517 I: Iterator<Item = usize>,
518 {
519 let TripletsMatched {
520 shape: (nrow, ncol),
521 triplets,
522 source_columns,
523 distances,
524 ..
525 } = self.matched_columns_triplets(cells, target_batches, knn, skip_same_batch)?;
526
527 Ok((
528 ndarray::Array2::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
529 source_columns,
530 distances,
531 ))
532 }
533
534 pub fn read_matched_columns_dmatrix<I>(
547 &self,
548 cells: I,
549 target_batches: &[usize],
550 knn: usize,
551 skip_same_batch: bool,
552 ) -> anyhow::Result<(nalgebra::DMatrix<f32>, Vec<usize>, Vec<f32>)>
553 where
554 I: Iterator<Item = usize>,
555 {
556 let TripletsMatched {
557 shape: (nrow, ncol),
558 triplets,
559 source_columns,
560 distances,
561 ..
562 } = self.matched_columns_triplets(cells, target_batches, knn, skip_same_batch)?;
563
564 Ok((
565 DMatrix::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
566 source_columns,
567 distances,
568 ))
569 }
570
571 pub fn query_columns_by_data_csc<T>(
582 &self,
583 query: T,
584 knn_per_batch: usize,
585 ) -> anyhow::Result<(CscMatrix<f32>, Vec<usize>, Vec<f32>)>
586 where
587 T: MakeVecPoint,
588 {
589 let TripletsMatched {
590 shape: (nrow, ncol),
591 triplets,
592 source_columns,
593 distances,
594 ..
595 } = self.query_columns_by_data_triplets(query, knn_per_batch)?;
596
597 Ok((
598 CscMatrix::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
599 source_columns,
600 distances,
601 ))
602 }
603
604 #[cfg(feature = "ndarray")]
605 pub fn query_columns_by_data_ndarray<T>(
616 &self,
617 query: T,
618 knn_per_batch: usize,
619 ) -> anyhow::Result<(ndarray::Array2<f32>, Vec<usize>, Vec<f32>)>
620 where
621 T: MakeVecPoint,
622 {
623 let TripletsMatched {
624 shape: (nrow, ncol),
625 triplets,
626 source_columns,
627 distances,
628 ..
629 } = self.query_columns_by_data_triplets(query, knn_per_batch)?;
630
631 Ok((
632 ndarray::Array2::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
633 source_columns,
634 distances,
635 ))
636 }
637
638 pub fn query_columns_by_data_dmatrix<T>(
649 &self,
650 query: T,
651 knn_per_batch: usize,
652 ) -> anyhow::Result<(nalgebra::DMatrix<f32>, Vec<usize>, Vec<f32>)>
653 where
654 T: MakeVecPoint,
655 {
656 let TripletsMatched {
657 shape: (nrow, ncol),
658 triplets,
659 source_columns,
660 distances,
661 ..
662 } = self.query_columns_by_data_triplets(query, knn_per_batch)?;
663
664 Ok((
665 DMatrix::<f32>::from_nonzero_triplets(nrow, ncol, &triplets)?,
666 source_columns,
667 distances,
668 ))
669 }
670}