use rand::rngs::SmallRng;
use rand::seq::index::sample;
use std::collections::BTreeMap;
#[derive(Clone, Copy, Debug)]
pub(super) struct Sketch {
pub dim: usize,
pub bits: usize,
}
#[cfg(feature = "alg")]
pub(super) fn bins(data_file: &str, sketch: Sketch, seed: u64) -> anyhow::Result<Vec<usize>> {
use data_beans::alg::random_projection::{binary_sort_columns, RandProjOps};
use std::sync::Arc;
let (backend, file) = data_beans::hdf5_io::resolve_backend_file(data_file, None)?;
let mut data = data_beans::sparse_io_vector::SparseIoVec::new();
data.push(
Arc::from(data_beans::sparse_io::open_sparse_matrix(&file, &backend)?),
None,
)?;
let ncol = data.num_columns();
if ncol < 2 {
return Ok(vec![0; ncol]);
}
let proj = data
.project_columns_with_batch_correction_seeded::<usize>(sketch.dim, None, None, seed)?
.proj;
binary_sort_columns(&proj, sketch.bits.min(sketch.dim).min(ncol))
}
#[cfg(not(feature = "alg"))]
pub(super) fn bins(_: &str, _: Sketch, _: u64) -> anyhow::Result<Vec<usize>> {
anyhow::bail!("--sketch needs data-beans built with the `alg` feature (on by default)")
}
pub(super) fn draw_across_bins(
rng: &mut SmallRng,
cells: &[usize],
bin_of: &[usize],
k: usize,
) -> Vec<usize> {
let mut by_bin: BTreeMap<usize, Vec<usize>> = BTreeMap::new();
for &c in cells {
by_bin.entry(bin_of[c]).or_default().push(c);
}
let bins: Vec<Vec<usize>> = by_bin.into_values().collect();
let sizes: Vec<usize> = bins.iter().map(Vec::len).collect();
let shares = even_shares(rng, &sizes, k);
bins.iter()
.zip(shares)
.flat_map(|(bin, n)| {
sample(rng, bin.len(), n)
.into_iter()
.map(|i| bin[i])
.collect::<Vec<_>>()
})
.collect()
}
pub(super) fn even_shares(rng: &mut SmallRng, sizes: &[usize], n: usize) -> Vec<usize> {
let mut share = vec![0; sizes.len()];
let mut order: Vec<usize> = (0..sizes.len()).collect();
order.sort_by_key(|&b| sizes[b]);
let mut left = n.min(sizes.iter().sum());
let mut open = &order[..];
while !open.is_empty() && left > 0 {
let level = left / open.len();
let small = open.partition_point(|&b| sizes[b] <= level);
if small > 0 {
for &b in &open[..small] {
share[b] = sizes[b];
left -= sizes[b];
}
open = &open[small..];
continue;
}
for &b in open {
share[b] = level;
}
for i in sample(rng, open.len(), left - level * open.len()) {
share[open[i]] += 1;
}
break;
}
share
}