data_beans/sparse_io/
helpers.rs1#[cfg(feature = "ndarray")]
2use ndarray::prelude::*;
3use rustc_hash::FxHashMap as HashMap;
4
5use super::DMatrix;
6
7pub fn build_name2index_map(_names: &[Box<str>]) -> HashMap<Box<str>, usize> {
8 _names
9 .iter()
10 .enumerate()
11 .map(|(r, name)| (name.clone(), r))
12 .collect()
13}
14
15pub fn take_subset_indices_names(
16 new_indices: &[usize],
17 ntot: usize,
18 old_names: Vec<Box<str>>,
19) -> (HashMap<u64, u64>, Vec<Box<str>>) {
20 let mut old2new: HashMap<u64, u64> = Default::default();
21 let mut new2old = vec![];
22 debug_assert!(ntot == old_names.len());
23 let mut k = 0_u64;
24 for idx in new_indices.iter() {
25 if *idx < ntot {
26 old2new.insert(*idx as u64, k);
27 new2old.push(*idx);
28 k += 1;
29 }
30 }
31
32 let new_names = new2old
33 .iter()
34 .map(|&i| old_names[i].clone())
35 .collect::<Vec<Box<str>>>();
36
37 (old2new, new_names)
38}
39
40pub fn take_subset_indices_names_if_needed(
41 new_indices: Option<&Vec<usize>>,
42 ntot: Option<usize>,
43 old_names: Vec<Box<str>>,
44) -> (HashMap<u64, u64>, Vec<Box<str>>) {
45 let ntot = ntot.unwrap_or(old_names.len());
46 if let Some(new_indices) = new_indices {
47 take_subset_indices_names(new_indices, ntot, old_names)
48 } else {
49 let names = old_names;
50 let identity = (0..(ntot as u64))
51 .zip(0..(ntot as u64))
52 .collect::<HashMap<u64, u64>>();
53 (identity, names)
54 }
55}
56
57#[cfg(feature = "ndarray")]
58pub fn ndarray_to_triplets(array: &Array2<f32>) -> Vec<(u64, u64, f32)> {
59 let eps = 1e-6;
60 array
61 .indexed_iter()
62 .filter(|(_, &elem)| elem.abs() > eps)
63 .map(|((row, col), &value)| (row as u64, col as u64, value))
64 .collect::<Vec<(u64, u64, f32)>>()
65}
66
67pub fn dmatrix_to_triplets(matrix: &DMatrix<f32>) -> Vec<(u64, u64, f32)> {
68 let (nrow, _) = matrix.shape();
69 let eps = 1e-6;
70 matrix
71 .iter() .enumerate()
73 .filter(|(_, &elem)| elem.abs() > eps)
74 .map(|(idx, &value)| {
75 let row = idx % nrow;
76 let col = idx / nrow;
77 (row as u64, col as u64, value)
78 })
79 .collect::<Vec<(u64, u64, f32)>>()
80}
81
82pub fn remove_backend_path(path: &str) -> anyhow::Result<()> {
85 let p = std::path::Path::new(path);
86 if p.exists() {
87 if p.is_file() {
88 std::fs::remove_file(p)?;
89 } else {
90 std::fs::remove_dir_all(p)?;
91 }
92 }
93 Ok(())
94}
95
96pub fn preload_within_budget(nnz: usize, what: &str) -> bool {
107 const BYTES_PER_NNZ: usize = 12;
108 const DEFAULT_BUDGET_BYTES: usize = 8 << 30;
109 let budget = std::env::var("LEGUME_PRELOAD_BUDGET_BYTES")
110 .ok()
111 .and_then(|v| v.parse::<usize>().ok())
112 .unwrap_or(DEFAULT_BUDGET_BYTES);
113 let cost = nnz.saturating_mul(BYTES_PER_NNZ);
114 if cost > budget {
115 log::warn!(
116 "skipping {what} preload: {cost} bytes ({nnz} nnz x {BYTES_PER_NNZ}) exceeds the \
117 {budget}-byte budget (LEGUME_PRELOAD_BUDGET_BYTES to raise); reads stay on the \
118 streaming path"
119 );
120 false
121 } else {
122 true
123 }
124}
125
126pub const SLAB_BUDGET_BYTES: usize = 256 << 20;
131pub const TRIPLET_BYTES: usize = 24;