use alloc::{collections::BTreeSet, vec, vec::Vec};
use num_traits::Float;
use super::super::randomized_graphs::XorShift64;
const MIN_LEVEL_NODES: usize = 3;
#[derive(Debug, Clone)]
pub struct LevelGraph {
pub n: usize,
pub edges: Vec<(usize, usize)>,
}
#[derive(Debug, Clone)]
pub struct Prolong {
pub n_fine: usize,
survivor: Vec<usize>,
reinsert: Vec<(usize, Vec<usize>)>,
}
impl Prolong {
pub fn apply<F: Float>(&self, coarse: &[F], rng: &mut XorShift64, jitter_scale: F) -> Vec<F> {
debug_assert_eq!(coarse.len(), self.survivor.len() * 2);
let mut fine = vec![F::zero(); self.n_fine * 2];
for (c, &fine_idx) in self.survivor.iter().enumerate() {
for d in 0..2 {
fine[fine_idx * 2 + d] = coarse[c * 2 + d];
}
}
for (child, neighbors) in &self.reinsert {
let mut sum = [F::zero(); 2];
for &nb in neighbors {
for d in 0..2 {
sum[d] = sum[d] + fine[nb * 2 + d];
}
}
let inv = F::one() / F::from(neighbors.len().max(1)).unwrap();
for d in 0..2 {
fine[child * 2 + d] = sum[d] * inv + jitter::<F>(rng) * jitter_scale;
}
}
fine
}
}
fn jitter<F: Float>(rng: &mut XorShift64) -> F {
let bits = rng.next().unwrap() >> 11;
let unit = F::from(bits).unwrap() / F::from(1u64 << 53).unwrap();
unit * F::from(2.0).unwrap() - F::one()
}
pub fn build_hierarchy(
n: usize,
edges: &[(usize, usize)],
rng: &mut XorShift64,
) -> (Vec<LevelGraph>, Vec<Prolong>) {
let mut levels = vec![LevelGraph { n, edges: edges.to_vec() }];
let mut prolongs: Vec<Prolong> = Vec::new();
loop {
let current = levels.last().unwrap();
if current.n <= MIN_LEVEL_NODES {
break;
}
let Some((coarse, prolong)) = coarsen_one_level(current, rng) else {
break;
};
if coarse.n == current.n {
break;
}
levels.push(coarse);
prolongs.push(prolong);
}
(levels, prolongs)
}
fn coarsen_one_level(fine: &LevelGraph, rng: &mut XorShift64) -> Option<(LevelGraph, Prolong)> {
let nf = fine.n;
let mut adj: Vec<BTreeSet<usize>> = vec![BTreeSet::new(); nf];
for &(u, v) in &fine.edges {
if u != v {
adj[u].insert(v);
adj[v].insert(u);
}
}
let mut order: Vec<usize> = (0..fine.edges.len()).collect();
shuffle(&mut order, rng);
let mut marked = vec![false; nf];
let mut matching: Vec<(usize, usize)> = Vec::new();
let mut rest: Vec<(usize, usize)> = Vec::new();
for &ei in &order {
let (u, v) = fine.edges[ei];
if u == v {
continue;
}
if !marked[u] && !marked[v] {
matching.push((u, v));
marked[u] = true;
marked[v] = true;
} else {
rest.push((u, v));
}
}
let mut cover_order: Vec<usize> = (0..rest.len()).collect();
shuffle(&mut cover_order, rng);
let mut edge_cover: Vec<(usize, usize)> = Vec::new();
for &ri in &cover_order {
let (u, v) = rest[ri];
if !marked[u] || !marked[v] {
edge_cover.push((u, v));
marked[u] = true;
marked[v] = true;
}
}
let target = nf / 2;
let mut substitute: Vec<usize> = (0..nf).collect();
let mut active = vec![true; nf];
let mut active_count = nf;
let mut merges: Vec<(usize, Vec<usize>)> = Vec::new();
for &(u0, v0) in matching.iter().chain(edge_cover.iter()) {
if active_count <= target {
break;
}
let u = find(&mut substitute, u0);
let v = find(&mut substitute, v0);
if u == v || !active[u] || !active[v] {
continue;
}
let (parent, child) = if adj[u].len() >= adj[v].len() { (u, v) } else { (v, u) };
if adj[parent].len() > 2 && adj[child].len() > 2 {
continue;
}
let neighbors: Vec<usize> = adj[child].iter().copied().collect();
merges.push((child, neighbors));
let child_adj: Vec<usize> = adj[child].iter().copied().collect();
for w in child_adj {
adj[w].remove(&child);
if w != parent {
adj[parent].insert(w);
adj[w].insert(parent);
}
}
adj[parent].remove(&child);
adj[child].clear();
active[child] = false;
substitute[child] = parent;
active_count -= 1;
}
if active_count == nf {
return None;
}
let mut fine_to_coarse = vec![usize::MAX; nf];
let mut survivor: Vec<usize> = Vec::with_capacity(active_count);
for (idx, is_active) in active.iter().enumerate() {
if *is_active {
fine_to_coarse[idx] = survivor.len();
survivor.push(idx);
}
}
let mut coarse_edges: BTreeSet<(usize, usize)> = BTreeSet::new();
for &fine_idx in &survivor {
let c = fine_to_coarse[fine_idx];
for &w in &adj[fine_idx] {
let cw = fine_to_coarse[w];
let (a, b) = if c < cw { (c, cw) } else { (cw, c) };
if a != b {
coarse_edges.insert((a, b));
}
}
}
merges.reverse();
let prolong = Prolong { n_fine: nf, survivor, reinsert: merges };
let coarse = LevelGraph { n: active_count, edges: coarse_edges.into_iter().collect() };
Some((coarse, prolong))
}
fn find(substitute: &mut [usize], mut x: usize) -> usize {
while substitute[x] != x {
substitute[x] = substitute[substitute[x]];
x = substitute[x];
}
x
}
fn shuffle(items: &mut [usize], rng: &mut XorShift64) {
let n = items.len();
for i in (1..n).rev() {
#[allow(clippy::cast_possible_truncation)]
let j = (rng.next().unwrap() % (i as u64 + 1)) as usize;
items.swap(i, j);
}
}