use crate::sparse_io_vector::SparseIoVec;
use indicatif::ParallelProgressIterator;
use legume_numeric::matrix::sparse_stat::SparseRunningStatistics;
use legume_numeric::matrix::utils::generate_minibatch_intervals;
use rayon::prelude::*;
pub fn streaming_sparse_running_stats(
data_vec: &SparseIoVec,
block_size: Option<usize>,
progress_label: &str,
) -> anyhow::Result<SparseRunningStatistics<f32>> {
let n_features = data_vec.num_rows();
let n_total = data_vec.num_columns();
let jobs = generate_minibatch_intervals(n_total, n_features, block_size);
let prog_bar = legume_numeric::matrix::progress::new_progress_bar(jobs.len() as u64)
.with_message(format!("{progress_label} blocks"));
let stats: SparseRunningStatistics<f32> = jobs
.par_iter()
.progress_with(prog_bar.clone())
.try_fold(
|| SparseRunningStatistics::<f32>::new(n_features),
|mut acc, &(lb, ub)| -> anyhow::Result<SparseRunningStatistics<f32>> {
let chunk = data_vec.read_columns_csc(lb..ub)?;
acc.add_csc(&chunk);
Ok(acc)
},
)
.try_reduce(
|| SparseRunningStatistics::<f32>::new(n_features),
|mut a, b| {
a.merge(&b);
Ok(a)
},
)?;
prog_bar.finish_and_clear();
Ok(stats)
}
pub fn streaming_sparse_running_stats_folded(
data_vec: &SparseIoVec,
block_size: Option<usize>,
progress_label: &str,
row_to_gene: &[u32],
n_folded: usize,
) -> anyhow::Result<(SparseRunningStatistics<f32>, SparseRunningStatistics<f32>)> {
let n_features = data_vec.num_rows();
anyhow::ensure!(
row_to_gene.len() == n_features,
"row_to_gene has {} entries for a {}-row matrix",
row_to_gene.len(),
n_features
);
if let Some(&bad) = row_to_gene.iter().max() {
anyhow::ensure!(
(bad as usize) < n_folded,
"row_to_gene names bucket {} but only {} were declared",
bad,
n_folded
);
}
let n_total = data_vec.num_columns();
let jobs = generate_minibatch_intervals(n_total, n_features, block_size);
let prog_bar = legume_numeric::matrix::progress::new_progress_bar(jobs.len() as u64)
.with_message(format!("{progress_label} blocks"));
type Acc = (
SparseRunningStatistics<f32>,
SparseRunningStatistics<f32>,
Vec<f32>,
Vec<bool>,
Vec<usize>,
Vec<f32>,
);
let (row_stats, folded_stats) = jobs
.par_iter()
.progress_with(prog_bar.clone())
.try_fold(
|| {
(
SparseRunningStatistics::<f32>::new(n_features),
SparseRunningStatistics::<f32>::new(n_folded),
vec![0.0f32; n_folded],
vec![false; n_folded],
Vec::<usize>::new(),
Vec::<f32>::new(),
)
},
|(mut rows, mut folded, mut buf, mut seen, mut touched, mut vals),
&(lb, ub)|
-> anyhow::Result<Acc> {
let chunk = data_vec.read_columns_csc(lb..ub)?;
rows.add_csc(&chunk);
for col in chunk.col_iter() {
touched.clear();
for (&r, &v) in col.row_indices().iter().zip(col.values().iter()) {
let g = row_to_gene[r] as usize;
if !seen[g] {
seen[g] = true;
touched.push(g);
}
buf[g] += v;
}
vals.clear();
vals.extend(touched.iter().map(|&g| buf[g]));
folded.add_sparse_column(&touched, &vals);
for &g in touched.iter() {
buf[g] = 0.0;
seen[g] = false;
}
}
Ok((rows, folded, buf, seen, touched, vals))
},
)
.map(|acc| acc.map(|(rows, folded, ..)| (rows, folded)))
.try_reduce(
|| {
(
SparseRunningStatistics::<f32>::new(n_features),
SparseRunningStatistics::<f32>::new(n_folded),
)
},
|(mut ra, mut fa), (rb, fb)| {
ra.merge(&rb);
fa.merge(&fb);
Ok((ra, fa))
},
)?;
prog_bar.finish_and_clear();
Ok((row_stats, folded_stats))
}