use std::cmp::max;
use std::collections::HashMap;
use crate::{DMatrix, DVector, MarkovFlow, utils};
pub fn cluster(
adj: &DMatrix<f64>,
alpha: usize,
beta: f64,
mu: f64,
max_iter: usize,
) -> HashMap<usize, Vec<usize>> {
let flow = MarkovFlow::new(&(adj + adj.transpose()), mu, false);
let max_degree = max(1, utils::get_max_degree(&flow.adjacency_matrix, true)) as f64;
let threshold = (mu * max_degree).powi(-((alpha + 1) as i32));
let attractor_matrix = if alpha > 1 {
clustering_cycle(flow, alpha, beta, max_iter, threshold)
} else {
adj.clone_owned()
};
let cluster_ids = get_cluster_ids(&attractor_matrix);
get_cluster_map(&cluster_ids)
}
pub fn get_tpm(flow: MarkovFlow) -> DMatrix<f64> {
let mut tpm = flow.influence_matrix + flow.dependency_matrix;
tpm.fill_diagonal(0.0);
for (j, mut col) in tpm.column_iter_mut().enumerate() {
col[j] = col.max();
let sum = col.sum();
if sum.abs() < f64::EPSILON {
col.fill(0.0);
col[j] = 1.0;
continue;
}
col *= 1.0 / sum;
}
tpm
}
pub fn clustering_cycle(
flow: MarkovFlow,
alpha: usize,
beta: f64,
max_iter: usize,
threshold: f64,
) -> DMatrix<f64> {
let mut tpm = get_tpm(flow);
let (rows, cols) = tpm.shape();
utils::prune_matrix(&mut tpm, threshold);
let mut last_tpm = DMatrix::<f64>::zeros(rows, cols);
for _ in 0..max_iter {
if tpm.relative_eq(&last_tpm, f64::EPSILON, f64::EPSILON) {
break;
}
last_tpm = tpm.clone_owned();
tpm.pow_mut(alpha as u32);
utils::matrix_elementwise_power(&mut tpm, beta);
utils::prune_matrix(&mut tpm, threshold);
utils::normalize_matrix_columns(&mut tpm);
}
tpm
}
pub fn get_cluster_ids(attractor_matrix: &DMatrix<f64>) -> DVector<usize> {
let (_, cols) = attractor_matrix.shape();
DVector::from_fn(cols, |i, _| attractor_matrix.column(i).imax())
}
pub fn get_cluster_map(cluster_ids: &DVector<usize>) -> HashMap<usize, Vec<usize>> {
let mut cluster_map = HashMap::<usize, Vec<usize>>::new();
for (i, id) in cluster_ids.iter().enumerate() {
if cluster_map.contains_key(id) {
cluster_map.entry(*id).and_modify(|v| v.push(i));
} else {
cluster_map.insert(*id, vec![i]);
}
}
cluster_map
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_trivial_example() {
let adj = DMatrix::from_row_slice(
4,
4,
&[
0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0,
],
);
let cmap = cluster(&adj, 2, 2.0, 2.0, 10 ^ 100);
let reference: HashMap<usize, Vec<usize>> =
[(0, vec![0, 1]), (2, vec![2, 3])].into_iter().collect();
assert_eq!(cmap, reference);
}
}