use crate::qc::{blocks_per_leaf, fold_in_fixed_order};
use crate::sparse_io_vector::SparseIoVec;
use legume_numeric::matrix::sparse_stat::SparseRunningStatistics;
use legume_numeric::matrix::utils::generate_minibatch_intervals;
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 = fold_in_fixed_order(
&jobs,
blocks_per_leaf(jobs.len()),
&|| SparseRunningStatistics::<f32>::new(n_features),
&|acc, &(lb, ub)| {
let chunk = data_vec.read_columns_csc(lb..ub)?;
acc.add_csc(&chunk);
prog_bar.inc(1);
Ok(())
},
&|a, b| a.merge(&b),
)?;
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, ..) = fold_in_fixed_order(
&jobs,
blocks_per_leaf(jobs.len()),
&|| -> Acc {
(
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(),
)
},
&|(rows, folded, buf, seen, touched, vals), &(lb, ub)| {
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;
}
}
prog_bar.inc(1);
Ok(())
},
&|(ra, fa, ..), (rb, fb, ..)| {
ra.merge(&rb);
fa.merge(&fb);
},
)?;
prog_bar.finish_and_clear();
Ok((row_stats, folded_stats))
}
#[cfg(test)]
#[path = "sparse_streaming_tests.rs"]
mod tests;