bixverse_methods/methods/nmf_hals/
nmf_preprocessing.rs1use faer::{Mat, MatRef};
4use rayon::prelude::*;
5
6use crate::prelude::*;
7use bixverse_core::core::math::matrix_helpers::col_sds;
8use bixverse_core::core::math::sparse::sparse_col_sds_csc;
9
10use crate::prelude::BixverseFloat;
15
16#[derive(Clone, Copy, Debug, Default)]
18pub enum NmfPreprocessing {
19 #[default]
21 None,
22 SdScaling,
24 SqrtSdScaling,
26}
27
28pub fn ensure_csc(
43 data: CompressedSparseData2<f32>,
44 verbosity: Verbosity,
45) -> CompressedSparseData2<f32> {
46 if data.cs_type.is_csr() {
47 if verbosity.detailed_verbosity() {
48 println!("NMF: data was provided as CSR. Transposing to CSC.")
49 }
50 data.transform()
51 } else {
52 data
53 }
54}
55
56pub fn parse_nmf_processing(s: &str) -> Option<NmfPreprocessing> {
69 match s.to_lowercase().as_str() {
70 "none" => Some(NmfPreprocessing::None),
71 "sd" | "scaling" => Some(NmfPreprocessing::SdScaling),
72 "sqrt_sd" => Some(NmfPreprocessing::SqrtSdScaling),
73 _ => None,
74 }
75}
76
77pub fn nmf_process_dense<T: BixverseFloat>(x: MatRef<T>, process: &NmfPreprocessing) -> Mat<T> {
93 let epsilon = T::from_f32(1e-6).unwrap();
94
95 match process {
96 NmfPreprocessing::None => x.to_owned(),
97 NmfPreprocessing::SdScaling => {
98 let mut sds = col_sds(x);
99 sds.par_iter_mut()
101 .for_each(|x| *x = if *x <= epsilon { T::one() } else { *x });
102
103 Mat::from_fn(x.nrows(), x.ncols(), |i, j| x[(i, j)] / sds[j])
104 }
105 NmfPreprocessing::SqrtSdScaling => {
106 let mut sds = col_sds(x);
107 sds.par_iter_mut()
108 .for_each(|x| *x = if *x <= epsilon { T::one() } else { x.sqrt() });
109
110 Mat::from_fn(x.nrows(), x.ncols(), |i, j| x[(i, j)] / sds[j])
111 }
112 }
113}
114
115pub fn nmf_process_sparse<T>(
132 x: &CompressedSparseData2<T>,
133 process: &NmfPreprocessing,
134 use_second_layer: bool,
135) -> Result<CompressedSparseData2<T>, MethodsErrors>
136where
137 T: BixverseFloat + BixverseSimd,
138{
139 let epsilon = T::from_f32(1e-6).unwrap();
140
141 match process {
142 NmfPreprocessing::None => Ok(x.clone()),
143 NmfPreprocessing::SdScaling | NmfPreprocessing::SqrtSdScaling => {
144 let use_sqrt = matches!(process, NmfPreprocessing::SqrtSdScaling);
145 let mut sds = sparse_col_sds_csc(x, use_second_layer)?;
146
147 sds.par_iter_mut().for_each(|s| {
148 *s = if *s <= epsilon {
149 T::one()
150 } else if use_sqrt {
151 s.sqrt()
152 } else {
153 *s
154 };
155 });
156
157 let mut out = x.clone();
158 let active_data: &mut Vec<T> = if use_second_layer {
159 out.data_2.as_mut().ok_or(CoreErrors::Data2NotAvailable)?
160 } else {
161 &mut out.data
162 };
163
164 let ncols = x.shape().1;
165 for j in 0..ncols {
166 let start = x.indptr[j] as usize;
167 let end = x.indptr[j + 1] as usize;
168 let divisor = sds[j];
169 for v in &mut active_data[start..end] {
170 *v /= divisor;
171 }
172 }
173
174 Ok(out)
175 }
176 }
177}