use super::graph_prep::{lay_out_by_piece, PreparedGraph};
use super::Mat;
use nalgebra::{DMatrix, SymmetricEigen};
#[must_use]
pub fn diffusion_map_2d_on_graph(n: usize, edges: &[(usize, usize, f32)], t: usize) -> Mat {
let graph = PreparedGraph::new(n, edges);
lay_out_by_piece(&graph, |piece, _| connected_diffusion_map(piece, t), None)
}
fn connected_diffusion_map(graph: &PreparedGraph, t: usize) -> Mat {
let m = graph.n();
let mut w = DMatrix::<f64>::zeros(m, m);
for &(i, j, a) in graph.edges() {
w[(i, j)] += f64::from(a);
w[(j, i)] += f64::from(a);
}
let d: Vec<f64> = (0..m).map(|i| w.row(i).sum()).collect();
let mut out = Mat::zeros(m, 2);
let inv_sqrt: Vec<f64> = d.iter().map(|x| x.sqrt().recip()).collect();
for i in 0..m {
for j in 0..m {
w[(i, j)] *= inv_sqrt[i] * inv_sqrt[j];
}
}
let eig = SymmetricEigen::new(w);
let mut order: Vec<usize> = (0..m).collect();
order.sort_by(|&a, &b| eig.eigenvalues[b].total_cmp(&eig.eigenvalues[a]));
for (dim, &k) in order.iter().skip(1).take(2).enumerate() {
let scale = eig.eigenvalues[k].powi(i32::try_from(t).unwrap_or(i32::MAX));
for i in 0..m {
out[(i, dim)] = (eig.eigenvectors[(i, k)] * inv_sqrt[i] * scale) as f32;
}
}
out
}
#[cfg(test)]
mod tests;