use ann_search_rs::utils::dist::SimdDistance;
use rustc_hash::FxHashMap;
use crate::core::math::sparse::coo_to_csr;
use crate::graph::community_detections::walktrap_sparse_graph;
use crate::graph::graph_structures::knn_to_sparse_graph;
use crate::prelude::*;
#[derive(Clone, Debug)]
pub struct SuperCellParams {
pub walk_length: usize,
pub graining_factor: f64,
pub use_kernel: bool,
pub k_ith: Option<usize>,
pub max_support: Option<usize>,
pub knn_params: KnnParams,
}
pub fn knn_to_sparse_graph_kernel<T>(
knn: &[Vec<usize>],
dist: &[Vec<T>],
kith: Option<usize>,
) -> SparseGraph<T>
where
T: BixverseFloat + std::iter::Sum + BixverseNumeric,
{
let n = knn.len();
let k = knn.first().map(|v| v.len()).unwrap_or(0);
let kith = kith.unwrap_or_else(|| (k / 2).saturating_sub(2));
let sigmas: Vec<T> = dist.iter().map(|d| d[kith]).collect();
let mut acc: FxHashMap<(usize, usize), (T, usize)> = FxHashMap::default();
for (i, (nbrs, dists)) in knn.iter().zip(dist.iter()).enumerate() {
for (&j, &d) in nbrs.iter().zip(dists.iter()) {
if i == j {
continue;
}
let key = if i < j { (i, j) } else { (j, i) };
let entry = acc.entry(key).or_insert((T::zero(), 0));
entry.0 += d;
entry.1 += 1;
}
}
let mut rows = Vec::with_capacity(acc.len() * 2);
let mut cols = Vec::with_capacity(acc.len() * 2);
let mut vals = Vec::with_capacity(acc.len() * 2);
for ((i, j), (sum, count)) in acc {
let d = sum / T::from_usize(count).unwrap();
let w = (-(d / sigmas[j]).powi(2)).exp() + (-(d / sigmas[i]).powi(2)).exp();
rows.push(i);
cols.push(j);
vals.push(w);
rows.push(j);
cols.push(i);
vals.push(w);
}
let adjacency = coo_to_csr(&rows.index_cast(), &cols.index_cast(), &vals, (n, n));
SparseGraph::new(n, adjacency, false)
}
pub fn supercell<T>(
knn_indices: &[Vec<usize>],
knn_dist: &[Vec<T>],
params: &SuperCellParams,
no_meta_cells: usize,
verbose: usize,
) -> Vec<usize>
where
T: BixverseFloat + std::iter::Sum + BixverseNumeric + SimdDistance,
{
let verbosity = parse_verbosity_level(verbose);
let knn_graph: SparseGraph<T> = if params.use_kernel {
if verbosity.normal_verbosity() {
println!("Using the kernel approach like in SuperCell 2.0")
}
knn_to_sparse_graph_kernel(knn_indices, knn_dist, params.k_ith)
} else {
if verbosity.normal_verbosity() {
println!("Using the original version of SuperCell on the kNN graph")
}
knn_to_sparse_graph(knn_indices, true)
};
walktrap_sparse_graph(
&knn_graph,
params.walk_length,
no_meta_cells,
params.max_support,
verbosity.detailed_verbosity(),
)
}