use indexmap::IndexSet;
use std::time::Instant;
use ann_search_rs::prelude::AnnSearchFloat;
use crate::prelude::*;
use bixverse_methods::methods::nmf_hals::consensus::*;
use bixverse_methods::methods::nmf_hals::nmf_preprocessing::*;
use bixverse_methods::methods::nmf_hals::sparse::*;
use bixverse_methods::methods::nmf_hals::*;
pub fn nmf_single_run_sparse<T>(
data: CompressedSparseData2<T>,
k: usize,
preprocessing: &str,
use_second_layer: bool,
nmf_hals_params: Option<HalsOpts<T>>,
verbose: usize,
) -> Result<NmfResult<T>, ScErrors>
where
T: BixverseFloat + BixverseNumeric + std::iter::Sum<T> + BixverseSimd,
{
let hals_opt = nmf_hals_params.unwrap_or_default();
let nmf_input = sparse_nmf_input(&data, preprocessing, use_second_layer)?;
let nmf_res = nmf_hals(&nmf_input, k, &hals_opt, verbose)?;
Ok(nmf_res)
}
#[allow(clippy::too_many_arguments)]
pub fn nmf_multiple_run_sparse<T>(
data: CompressedSparseData2<T>,
k: usize,
preprocessing: &str,
use_second_layer: bool,
nmf_hals_params: Option<HalsOpts<T>>,
n_runs: usize,
base_seed: usize,
verbose: usize,
) -> Result<StabilisedNmfResult<T>, ScErrors>
where
T: BixverseFloat + BixverseNumeric + std::iter::Sum<T> + BixverseSimd,
{
let hals_opt = nmf_hals_params.unwrap_or_default();
let nmf_input = sparse_nmf_input(&data, preprocessing, use_second_layer)?;
let res = stabilised_nmf(&nmf_input, k, n_runs, base_seed as u64, &hals_opt, verbose)?;
Ok(res)
}
#[allow(clippy::too_many_arguments)]
pub fn nmf_consensus_run_sparse<T>(
data: CompressedSparseData2<T>,
k: usize,
preprocessing: &str,
use_second_layer: bool,
nmf_hals_params: Option<HalsOpts<T>>,
consensus_params: Option<ConsensusParams<T>>,
n_runs: usize,
base_seed: usize,
verbose: usize,
) -> Result<ConsensusNmfResult<T>, ScErrors>
where
T: BixverseFloat + BixverseNumeric + AnnSearchFloat + std::iter::Sum<T> + BixverseSimd,
{
let hals_opt = nmf_hals_params.unwrap_or_default();
let nmf_input = sparse_nmf_input(&data, preprocessing, use_second_layer)?;
nmf_consensus(
&nmf_input,
k,
n_runs,
base_seed as u64,
&hals_opt,
consensus_params,
verbose,
)
.map_err(Into::into)
}
#[allow(clippy::too_many_arguments)]
pub fn nmf_k_sweep_run_sparse<T>(
data: CompressedSparseData2<T>,
k_range: &[usize],
preprocessing: &str,
use_second_layer: bool,
nmf_hals_params: Option<HalsOpts<T>>,
consensus_params: Option<ConsensusParams<T>>,
n_runs: usize,
base_seed: usize,
verbose: usize,
) -> Result<Vec<KSweepEntry<T>>, ScErrors>
where
T: BixverseFloat + BixverseNumeric + AnnSearchFloat + std::iter::Sum<T> + BixverseSimd,
{
let hals_opt = nmf_hals_params.unwrap_or_default();
let nmf_input = sparse_nmf_input(&data, preprocessing, use_second_layer)?;
nmf_k_sweep(
&nmf_input,
k_range,
n_runs,
base_seed as u64,
&hals_opt,
consensus_params,
verbose,
)
.map_err(Into::into)
}
fn sparse_nmf_input<T>(
data: &CompressedSparseData2<T>,
preprocessing: &str,
use_second_layer: bool,
) -> Result<SparseInput<T, T>, ScErrors>
where
T: BixverseFloat + BixverseNumeric + std::iter::Sum<T> + BixverseSimd,
{
let nmf_preprocessing = parse_nmf_processing(preprocessing).unwrap_or_else(|| {
println!(
"Unknown string provided: {:?}. Using the default (No additional pre-processing)",
preprocessing
);
NmfPreprocessing::default()
});
let data_processed = nmf_process_sparse(data, &nmf_preprocessing, use_second_layer)?;
if use_second_layer {
SparseInput::from_secondary(&data_processed).map_err(Into::into)
} else {
SparseInput::from_primary(&data_processed).map_err(Into::into)
}
}
#[allow(clippy::too_many_arguments)]
pub fn nmf_single_run_sc<S: SingleCellReading>(
reader: &S,
gene_indices: &[usize],
cell_indices: &[usize],
k: usize,
preprocessing: &str,
use_second_layer: bool,
nmf_hals_params: Option<HalsOpts<f32>>,
verbose: usize,
) -> Result<NmfResult<f32>, ScErrors> {
let verbosity = parse_verbosity_level(verbose);
let cell_set: IndexSet<u32> = cell_indices.iter().map(|&x| x as u32).collect();
let start_total = Instant::now();
if verbosity.normal_verbosity() {
println!("NMF: Loading data from disk ...")
}
let layer = if use_second_layer {
DataLayerReturn::Norm
} else {
DataLayerReturn::Raw
};
let csc: CompressedSparseData2<f32> =
reader.read_genes_sparse(gene_indices, Some(&cell_set), &layer)?;
if verbosity.normal_verbosity() {
println!(" ... done in {:.2?}", start_total.elapsed())
}
if verbosity.normal_verbosity() {
println!("NMF: Running NMF for single cells ...")
}
let res = nmf_single_run_sparse(
csc,
k,
preprocessing,
use_second_layer,
nmf_hals_params,
verbose,
)?;
if verbosity.normal_verbosity() {
println!(" ... done in {:.2?}", start_total.elapsed())
}
Ok(res)
}
#[allow(clippy::too_many_arguments)]
pub fn nmf_multiple_run_sc<S: SingleCellReading>(
reader: &S,
gene_indices: &[usize],
cell_indices: &[usize],
k: usize,
preprocessing: &str,
use_second_layer: bool,
nmf_hals_params: Option<HalsOpts<f32>>,
n_runs: usize,
base_seed: usize,
verbose: usize,
) -> Result<StabilisedNmfResult<f32>, ScErrors> {
let verbosity = parse_verbosity_level(verbose);
let cell_set: IndexSet<u32> = cell_indices.iter().map(|&x| x as u32).collect();
let start_total = Instant::now();
if verbosity.normal_verbosity() {
println!("NMF: Loading data from disk ...")
}
let layer = if use_second_layer {
DataLayerReturn::Norm
} else {
DataLayerReturn::Raw
};
let csc: CompressedSparseData2<f32> =
reader.read_genes_sparse(gene_indices, Some(&cell_set), &layer)?;
if verbosity.normal_verbosity() {
println!(" ... done in {:.2?}", start_total.elapsed())
}
if verbosity.normal_verbosity() {
println!("NMF: Running multiple NMF runs for single cells ...")
}
let res = nmf_multiple_run_sparse(
csc,
k,
preprocessing,
use_second_layer,
nmf_hals_params,
n_runs,
base_seed,
verbose,
)?;
if verbosity.normal_verbosity() {
println!(" ... done in {:.2?}", start_total.elapsed())
}
Ok(res)
}
fn load_sc_matrix<S: SingleCellReading>(
reader: &S,
gene_indices: &[usize],
cell_indices: &[usize],
use_second_layer: bool,
verbose: usize,
) -> Result<CompressedSparseData2<f32>, ScErrors> {
let verbosity = parse_verbosity_level(verbose);
let cell_set: IndexSet<u32> = cell_indices.iter().map(|&x| x as u32).collect();
let start = Instant::now();
if verbosity.normal_verbosity() {
println!("NMF: Loading data from disk ...")
}
let layer = if use_second_layer {
DataLayerReturn::Norm
} else {
DataLayerReturn::Raw
};
let csc: CompressedSparseData2<f32> =
reader.read_genes_sparse(gene_indices, Some(&cell_set), &layer)?;
if verbosity.normal_verbosity() {
println!(" ... done in {:.2?}", start.elapsed())
}
Ok(csc)
}
#[allow(clippy::too_many_arguments)]
pub fn nmf_consensus_run_sc<S: SingleCellReading>(
reader: &S,
gene_indices: &[usize],
cell_indices: &[usize],
k: usize,
preprocessing: &str,
use_second_layer: bool,
nmf_hals_params: Option<HalsOpts<f32>>,
consensus_params: Option<ConsensusParams<f32>>,
n_runs: usize,
base_seed: usize,
verbose: usize,
) -> Result<ConsensusNmfResult<f32>, ScErrors> {
let verbosity = parse_verbosity_level(verbose);
let csc = load_sc_matrix(
reader,
gene_indices,
cell_indices,
use_second_layer,
verbose,
)?;
if verbosity.normal_verbosity() {
println!("NMF: Running consensus NMF for single cells ...")
}
let start = Instant::now();
let res = nmf_consensus_run_sparse(
csc,
k,
preprocessing,
use_second_layer,
nmf_hals_params,
consensus_params,
n_runs,
base_seed,
verbose,
)?;
if verbosity.normal_verbosity() {
println!(" ... done in {:.2?}", start.elapsed())
}
Ok(res)
}
#[allow(clippy::too_many_arguments)]
pub fn nmf_k_sweep_run_sc<S: SingleCellReading>(
reader: &S,
gene_indices: &[usize],
cell_indices: &[usize],
k_range: &[usize],
preprocessing: &str,
use_second_layer: bool,
nmf_hals_params: Option<HalsOpts<f32>>,
consensus_params: Option<ConsensusParams<f32>>,
n_runs: usize,
base_seed: usize,
verbose: usize,
) -> Result<Vec<KSweepEntry<f32>>, ScErrors> {
let verbosity = parse_verbosity_level(verbose);
let csc = load_sc_matrix(
reader,
gene_indices,
cell_indices,
use_second_layer,
verbose,
)?;
if verbosity.normal_verbosity() {
println!("NMF: Running the k sweep for single cells ...")
}
let start = Instant::now();
let res = nmf_k_sweep_run_sparse(
csc,
k_range,
preprocessing,
use_second_layer,
nmf_hals_params,
consensus_params,
n_runs,
base_seed,
verbose,
)?;
if verbosity.normal_verbosity() {
println!(" ... done in {:.2?}", start.elapsed())
}
Ok(res)
}