use faer::MatRef;
use rayon::prelude::*;
use rustc_hash::FxHashMap;
use std::collections::VecDeque;
use crate::prelude::*;
pub fn intersection_size_sorted(a: &[usize], b: &[usize]) -> usize {
let mut count = 0;
let mut i = 0;
let mut j = 0;
while i < a.len() && j < b.len() {
match a[i].cmp(&b[j]) {
std::cmp::Ordering::Equal => {
count += 1;
i += 1;
j += 1;
}
std::cmp::Ordering::Less => i += 1,
std::cmp::Ordering::Greater => j += 1,
}
}
count
}
pub fn cluster_stability<T>(cluster_matrix: &MatRef<i32>) -> Vec<(T, T)>
where
T: BixverseFloat,
{
let n_features = cluster_matrix.nrows();
let n_iter = cluster_matrix.ncols();
let bootstrap_cluster_maps: Vec<FxHashMap<i32, Vec<usize>>> = (0..n_iter)
.into_par_iter()
.map(|boot_idx| {
let mut clusters_map: FxHashMap<i32, Vec<usize>> = FxHashMap::default();
for feature_idx in 0..n_features {
let cluster_id = cluster_matrix[(feature_idx, boot_idx)];
clusters_map
.entry(cluster_id)
.or_default()
.push(feature_idx);
}
clusters_map
})
.collect();
(0..n_features)
.into_par_iter()
.map(|feature_idx| {
let n_pairs = (n_iter * (n_iter - 1)) / 2;
let mut jaccard_scores = Vec::with_capacity(n_pairs);
for i in 0..(n_iter - 1) {
for j in (i + 1)..n_iter {
let cluster_i = cluster_matrix[(feature_idx, i)];
let cluster_j = cluster_matrix[(feature_idx, j)];
let members_i = bootstrap_cluster_maps[i]
.get(&cluster_i)
.map(|v| v.as_slice())
.unwrap_or(&[]);
let members_j = bootstrap_cluster_maps[j]
.get(&cluster_j)
.map(|v| v.as_slice())
.unwrap_or(&[]);
let intersection_size = intersection_size_sorted(members_i, members_j);
let union_size = members_i.len() + members_j.len() - intersection_size;
let jaccard = if union_size == 0 {
0.0
} else {
intersection_size as f64 / union_size as f64
};
jaccard_scores.push(jaccard);
}
}
let mean_jaccard = jaccard_scores.iter().sum::<f64>() / jaccard_scores.len() as f64;
let variance = jaccard_scores
.iter()
.map(|x| (x - mean_jaccard).powi(2))
.sum::<f64>()
/ jaccard_scores.len() as f64;
let std_jaccard = variance.sqrt();
(
T::from_f64(mean_jaccard).unwrap(),
T::from_f64(std_jaccard).unwrap(),
)
})
.collect()
}
pub fn split_cor_mat_by_sign<T>(cor_mat: &MatRef<T>) -> Vec<i32>
where
T: BixverseFloat,
{
let n = cor_mat.ncols();
let total_elem = n * (n - 1) / 2;
let mut off_diag = Vec::with_capacity(total_elem);
for i in 0..n {
for j in (i + 1)..n {
off_diag.push(*cor_mat.get(i, j));
}
}
let all_pos = off_diag.iter().all(|x| *x >= T::zero());
let all_neg = off_diag.iter().all(|x| *x <= T::zero());
if all_pos {
return vec![1; n];
}
if all_neg {
return vec![-1; n];
}
let mut visited = vec![false; n];
let mut groups = Vec::new();
for i in 0..n {
if !visited[i] {
let mut component = Vec::new();
let mut queue = VecDeque::new();
queue.push_back(i);
visited[i] = true;
while let Some(node) = queue.pop_front() {
component.push(node);
for j in 0..n {
if !visited[j] && *cor_mat.get(node, j) > T::zero() {
visited[j] = true;
queue.push_back(j);
}
}
}
groups.push(component);
}
}
let mut result = vec![-1; n];
for group in groups {
let has_positive_with_first =
group.contains(&0) || group.iter().any(|&idx| *cor_mat.get(0, idx) > T::zero());
let label = if has_positive_with_first { 1 } else { -1 };
for &idx in &group {
result[idx] = label;
}
}
result
}