data_beans/alg/collapse_data/
reassign_cells.rs1use super::*;
9use crate::alg::dc_poisson::{
10 refine_with_candidates, FeatureWeighting, Profiles, RefineContext, RefineParams,
11};
12use nalgebra_sparse::CscMatrix;
13use rand::rngs::SmallRng;
14use rand::SeedableRng;
15
16#[derive(Clone, Debug)]
17pub struct ReassignCellsParams {
18 pub num_gibbs: usize,
19 pub num_greedy: usize,
20 pub feature_weighting: FeatureWeighting,
21 pub seed: u64,
22}
23
24impl Default for ReassignCellsParams {
25 fn default() -> Self {
26 Self {
27 num_gibbs: 3,
28 num_greedy: 10,
29 feature_weighting: FeatureWeighting::FisherInfoNb,
30 seed: 42,
31 }
32 }
33}
34
35pub(crate) fn reassign_cells_to_nodes(
39 csc: &CscMatrix<f32>,
40 col_to_batch: &[usize],
41 num_batches: usize,
42 active: &[bool],
43 node_of_cell: &mut [usize],
44 params: &ReassignCellsParams,
45) -> usize {
46 let n = csc.ncols();
47 assert_eq!(col_to_batch.len(), n);
48 assert_eq!(active.len(), n);
49 assert_eq!(node_of_cell.len(), n);
50 let num_batches = num_batches.max(1);
51 let num_nodes = node_of_cell.iter().copied().max().map_or(0, |m| m + 1);
52 if num_nodes < 2 || params.num_gibbs + params.num_greedy == 0 {
53 return 0;
54 }
55 let cells: Vec<usize> = (0..n).filter(|&c| active[c]).collect();
56 if cells.is_empty() {
57 return 0;
58 }
59
60 let mut group_of_pair = vec![usize::MAX; num_nodes * num_batches];
65 let mut group_pair: Vec<(usize, usize)> = Vec::new();
66 let mut labels: Vec<usize> = Vec::with_capacity(cells.len());
67 for &c in &cells {
68 let key = node_of_cell[c] * num_batches + col_to_batch[c];
69 if group_of_pair[key] == usize::MAX {
70 group_of_pair[key] = group_pair.len();
71 group_pair.push((node_of_cell[c], col_to_batch[c]));
72 }
73 labels.push(group_of_pair[key]);
74 }
75 let k = group_pair.len();
76 let mut groups_of_batch: Vec<Vec<usize>> = vec![Vec::new(); num_batches];
77 for (g, &(_, b)) in group_pair.iter().enumerate() {
78 groups_of_batch[b].push(g);
79 }
80 let candidates: Vec<Vec<usize>> = cells
81 .iter()
82 .map(|&c| groups_of_batch[col_to_batch[c]].clone())
83 .collect();
84
85 let rows: Vec<Vec<(usize, f32)>> = cells
90 .iter()
91 .map(|&c| {
92 let col = csc.col(c);
93 col.row_indices()
94 .iter()
95 .zip(col.values())
96 .map(|(&g, &v)| (g, v))
97 .collect()
98 })
99 .collect();
100 let mut profiles = Profiles::from_gene_sums(&rows, csc.nrows());
101 profiles.apply_feature_weighting(params.feature_weighting);
102 let refine = RefineParams {
103 num_gibbs: params.num_gibbs,
104 num_greedy: params.num_greedy,
105 feature_weighting: params.feature_weighting,
106 seed: params.seed,
107 ..RefineParams::default()
108 };
109 let mut rng = SmallRng::seed_from_u64(params.seed);
110 let moves = refine_with_candidates(
111 &mut labels,
112 &candidates,
113 &mut rng,
114 &RefineContext {
115 profiles: &profiles,
116 k,
117 params: &refine,
118 level_label: "reassign cells",
119 },
120 );
121 for (i, &c) in cells.iter().enumerate() {
122 node_of_cell[c] = group_pair[labels[i]].0;
123 }
124 info!(
125 "reassign cells: {} of {} cells moved between {} top nodes ({} node x batch groups)",
126 moves,
127 cells.len(),
128 num_nodes,
129 k
130 );
131 moves
132}
133
134#[cfg(test)]
135#[path = "reassign_cells_tests.rs"]
136mod tests;