1use crate::error::Result;
70use crate::graph::{DynamicGraph, VertexId, Weight};
71use std::collections::{HashMap, HashSet};
72use std::sync::Arc;
73
74#[derive(Debug, Clone)]
76pub struct DecompositionNode {
77 pub id: usize,
79 pub level: usize,
81 pub vertices: HashSet<VertexId>,
83 pub parent: Option<usize>,
85 pub children: Vec<usize>,
87 pub cut_value: f64,
89 pub dirty: bool,
91}
92
93impl DecompositionNode {
94 fn new_leaf(id: usize, vertex: VertexId) -> Self {
96 let mut vertices = HashSet::new();
97 vertices.insert(vertex);
98 Self {
99 id,
100 level: 0,
101 vertices,
102 parent: None,
103 children: Vec::new(),
104 cut_value: f64::INFINITY,
105 dirty: true,
106 }
107 }
108
109 fn new_internal(id: usize, level: usize, children: Vec<usize>) -> Self {
111 Self {
112 id,
113 level,
114 vertices: HashSet::new(), parent: None,
116 children,
117 cut_value: f64::INFINITY,
118 dirty: true,
119 }
120 }
121
122 pub fn is_leaf(&self) -> bool {
124 self.children.is_empty()
125 }
126
127 pub fn size(&self) -> usize {
129 self.vertices.len()
130 }
131}
132
133pub struct HierarchicalDecomposition {
135 nodes: Vec<DecompositionNode>,
137 vertex_to_leaf: HashMap<VertexId, usize>,
139 root: Option<usize>,
141 min_cut: f64,
143 height: usize,
145 graph: Arc<DynamicGraph>,
147 next_node_id: usize,
149}
150
151impl HierarchicalDecomposition {
152 pub fn build(graph: Arc<DynamicGraph>) -> Result<Self> {
154 let mut decomp = Self {
155 nodes: Vec::new(),
156 vertex_to_leaf: HashMap::new(),
157 root: None,
158 min_cut: f64::INFINITY,
159 height: 0,
160 graph,
161 next_node_id: 0,
162 };
163
164 decomp.build_hierarchy()?;
165 decomp.min_cut = decomp.propagate_updates();
166
167 Ok(decomp)
168 }
169
170 pub fn min_cut_value(&self) -> f64 {
172 self.min_cut
173 }
174
175 pub fn min_cut_partition(&self) -> (HashSet<VertexId>, HashSet<VertexId>) {
177 if self.root.is_none() {
178 return (HashSet::new(), HashSet::new());
179 }
180
181 let (min_node_idx, _) = self.find_min_cut_node();
183 let node = &self.nodes[min_node_idx];
184
185 let partition_a = node.vertices.clone();
187 let all_vertices: HashSet<VertexId> = self.graph.vertices().into_iter().collect();
188 let partition_b: HashSet<VertexId> =
189 all_vertices.difference(&partition_a).copied().collect();
190
191 (partition_a, partition_b)
192 }
193
194 pub fn insert_edge(&mut self, u: VertexId, v: VertexId, _weight: Weight) -> Result<f64> {
196 if let Some(lca_idx) = self.lca_node(u, v) {
198 self.mark_dirty(lca_idx);
200 }
201
202 self.min_cut = self.propagate_updates();
204 Ok(self.min_cut)
205 }
206
207 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> Result<f64> {
209 if let Some(lca_idx) = self.lca_node(u, v) {
211 self.mark_dirty(lca_idx);
213 }
214
215 self.min_cut = self.propagate_updates();
217 Ok(self.min_cut)
218 }
219
220 fn propagate_updates(&mut self) -> f64 {
222 if let Some(root_idx) = self.root {
224 self.recompute_subtree(root_idx);
225 }
226
227 self.find_min_cut_value()
229 }
230
231 fn recompute_subtree(&mut self, node_idx: usize) {
233 let children = self.nodes[node_idx].children.clone();
235 for child_idx in children {
236 if self.nodes[child_idx].dirty {
237 self.recompute_subtree(child_idx);
238 }
239 }
240
241 if self.nodes[node_idx].dirty {
243 let cut_value = self.compute_cut(node_idx);
244 self.nodes[node_idx].cut_value = cut_value;
245 self.nodes[node_idx].dirty = false;
246 }
247 }
248
249 fn lca_node(&self, u: VertexId, v: VertexId) -> Option<usize> {
251 let u_leaf = self.vertex_to_leaf.get(&u)?;
252 let v_leaf = self.vertex_to_leaf.get(&v)?;
253
254 if u_leaf == v_leaf {
255 return Some(*u_leaf);
256 }
257
258 let mut u_ancestors = HashSet::new();
260 let mut current = Some(*u_leaf);
261 while let Some(node_idx) = current {
262 u_ancestors.insert(node_idx);
263 current = self.nodes[node_idx].parent;
264 }
265
266 let mut current = Some(*v_leaf);
268 while let Some(node_idx) = current {
269 if u_ancestors.contains(&node_idx) {
270 return Some(node_idx);
271 }
272 current = self.nodes[node_idx].parent;
273 }
274
275 None
276 }
277
278 fn mark_dirty(&mut self, node_idx: usize) {
280 let mut current = Some(node_idx);
281 while let Some(idx) = current {
282 self.nodes[idx].dirty = true;
283 current = self.nodes[idx].parent;
284 }
285 }
286
287 fn build_hierarchy(&mut self) -> Result<()> {
289 let vertices = self.graph.vertices();
290
291 if vertices.is_empty() {
292 return Ok(());
293 }
294
295 for vertex in &vertices {
297 let node_id = self.next_node_id;
298 self.next_node_id += 1;
299 let leaf = DecompositionNode::new_leaf(node_id, *vertex);
300 let leaf_idx = self.nodes.len();
301 self.nodes.push(leaf);
302 self.vertex_to_leaf.insert(*vertex, leaf_idx);
303 }
304
305 let leaf_indices: Vec<usize> = (0..vertices.len()).collect();
307 if !leaf_indices.is_empty() {
308 self.root = Some(self.build_subtree(&leaf_indices, 1)?);
309 }
310
311 Ok(())
312 }
313
314 fn build_subtree(&mut self, indices: &[usize], level: usize) -> Result<usize> {
316 if indices.len() == 1 {
317 self.nodes[indices[0]].level = 0;
319 self.height = self.height.max(level - 1);
320 return Ok(indices[0]);
321 }
322
323 let mid = indices.len() / 2;
325 let left_indices = &indices[..mid];
326 let right_indices = &indices[mid..];
327
328 let left_idx = self.build_subtree(left_indices, level + 1)?;
330 let right_idx = self.build_subtree(right_indices, level + 1)?;
331
332 let node_id = self.next_node_id;
334 self.next_node_id += 1;
335 let mut internal =
336 DecompositionNode::new_internal(node_id, level, vec![left_idx, right_idx]);
337
338 internal.vertices.extend(&self.nodes[left_idx].vertices);
340 internal.vertices.extend(&self.nodes[right_idx].vertices);
341
342 let internal_idx = self.nodes.len();
343 self.nodes.push(internal);
344
345 self.nodes[left_idx].parent = Some(internal_idx);
347 self.nodes[right_idx].parent = Some(internal_idx);
348
349 self.height = self.height.max(level);
350
351 Ok(internal_idx)
352 }
353
354 fn compute_cut(&self, node_idx: usize) -> f64 {
357 let node = &self.nodes[node_idx];
358
359 if node.vertices.len() == self.graph.num_vertices() {
363 return f64::INFINITY;
365 }
366
367 self.compute_global_cut(&node.vertices)
369 }
370
371 fn compute_global_cut(&self, vertices: &HashSet<VertexId>) -> f64 {
374 let mut cut_weight = 0.0;
375
376 for &u in vertices {
377 for (v, _edge_id) in self.graph.neighbors(u) {
378 if !vertices.contains(&v) {
380 if let Some(weight) = self.graph.edge_weight(u, v) {
381 cut_weight += weight;
382 }
383 }
384 }
385 }
386
387 cut_weight
388 }
389
390 fn find_min_cut_node(&self) -> (usize, f64) {
392 let mut min_idx = 0;
393 let mut min_value = f64::INFINITY;
394
395 for (idx, node) in self.nodes.iter().enumerate() {
396 if node.cut_value < min_value {
397 min_value = node.cut_value;
398 min_idx = idx;
399 }
400 }
401
402 (min_idx, min_value)
403 }
404
405 fn find_min_cut_value(&self) -> f64 {
407 self.find_min_cut_node().1
408 }
409
410 pub fn height(&self) -> usize {
412 self.height
413 }
414
415 pub fn num_nodes(&self) -> usize {
417 self.nodes.len()
418 }
419}
420
421#[derive(Debug, Clone)]
423pub struct LevelInfo {
424 pub level: usize,
426 pub num_nodes: usize,
428 pub avg_cut: f64,
430}
431
432impl HierarchicalDecomposition {
433 pub fn level_info(&self) -> Vec<LevelInfo> {
435 let mut levels: HashMap<usize, Vec<f64>> = HashMap::new();
436
437 for node in &self.nodes {
438 levels
439 .entry(node.level)
440 .or_insert_with(Vec::new)
441 .push(node.cut_value);
442 }
443
444 let mut result: Vec<LevelInfo> = levels
445 .into_iter()
446 .map(|(level, cut_values)| {
447 let num_nodes = cut_values.len();
448 let finite_cuts: Vec<f64> = cut_values
449 .iter()
450 .filter(|&&v| v.is_finite())
451 .copied()
452 .collect();
453 let avg_cut = if finite_cuts.is_empty() {
454 f64::INFINITY
455 } else {
456 finite_cuts.iter().sum::<f64>() / finite_cuts.len() as f64
457 };
458
459 LevelInfo {
460 level,
461 num_nodes,
462 avg_cut,
463 }
464 })
465 .collect();
466
467 result.sort_by_key(|info| info.level);
468 result
469 }
470}
471
472#[cfg(test)]
473mod tests {
474 use super::*;
475
476 fn create_simple_graph() -> Arc<DynamicGraph> {
477 let graph = Arc::new(DynamicGraph::new());
478 graph.insert_edge(1, 2, 1.0).unwrap();
480 graph.insert_edge(2, 3, 1.0).unwrap();
481 graph.insert_edge(3, 1, 1.0).unwrap();
482 graph
483 }
484
485 fn create_disconnectable_graph() -> Arc<DynamicGraph> {
486 let graph = Arc::new(DynamicGraph::new());
487 graph.insert_edge(1, 2, 2.0).unwrap();
490 graph.insert_edge(2, 3, 2.0).unwrap();
491 graph.insert_edge(3, 1, 2.0).unwrap();
492 graph.insert_edge(3, 4, 1.0).unwrap();
494 graph.insert_edge(4, 5, 2.0).unwrap();
496 graph.insert_edge(5, 6, 2.0).unwrap();
497 graph.insert_edge(6, 4, 2.0).unwrap();
498 graph
499 }
500
501 #[test]
502 fn test_build_empty_graph() {
503 let graph = Arc::new(DynamicGraph::new());
504 let decomp = HierarchicalDecomposition::build(graph).unwrap();
505 assert_eq!(decomp.num_nodes(), 0);
506 assert_eq!(decomp.height(), 0);
507 }
508
509 #[test]
510 fn test_build_single_vertex() {
511 let graph = Arc::new(DynamicGraph::new());
512 graph.add_vertex(1);
513 let decomp = HierarchicalDecomposition::build(graph).unwrap();
514 assert_eq!(decomp.num_nodes(), 1);
515 assert_eq!(decomp.height(), 0);
516 assert!(decomp.min_cut_value().is_infinite());
517 }
518
519 #[test]
520 fn test_build_triangle() {
521 let graph = create_simple_graph();
522 let decomp = HierarchicalDecomposition::build(graph).unwrap();
523
524 assert_eq!(decomp.num_nodes(), 5);
526
527 assert!(decomp.height() <= 2);
529
530 assert_eq!(decomp.min_cut_value(), 2.0);
532 }
533
534 #[test]
535 fn test_build_disconnectable() {
536 let graph = create_disconnectable_graph();
537 let decomp = HierarchicalDecomposition::build(graph).unwrap();
538
539 assert_eq!(decomp.num_nodes(), 11);
541
542 let min_cut = decomp.min_cut_value();
546 assert!(min_cut.is_finite() && min_cut >= 1.0);
547 }
548
549 #[test]
550 fn test_min_cut_partition() {
551 let graph = create_disconnectable_graph();
552 let decomp = HierarchicalDecomposition::build(graph).unwrap();
553
554 let (partition_a, partition_b) = decomp.min_cut_partition();
555
556 assert_eq!(partition_a.len() + partition_b.len(), 6);
558
559 assert!(partition_a.len() >= 1 && partition_a.len() <= 5);
561 assert!(partition_b.len() >= 1 && partition_b.len() <= 5);
562
563 let intersection: HashSet<_> = partition_a.intersection(&partition_b).collect();
565 assert!(intersection.is_empty());
566 }
567
568 #[test]
569 fn test_lca_node() {
570 let graph = create_simple_graph();
571 let decomp = HierarchicalDecomposition::build(graph).unwrap();
572
573 let lca = decomp.lca_node(1, 1);
575 assert!(lca.is_some());
576
577 let lca = decomp.lca_node(1, 2);
579 assert!(lca.is_some());
580
581 let lca = decomp.lca_node(1, 3);
582 assert!(lca.is_some());
583 }
584
585 #[test]
586 fn test_mark_dirty() {
587 let graph = create_simple_graph();
588 let mut decomp = HierarchicalDecomposition::build(graph).unwrap();
589
590 for node in &decomp.nodes {
592 assert!(
593 !node.dirty,
594 "Node {} should not be dirty after build",
595 node.id
596 );
597 }
598
599 let leaf_idx = *decomp.vertex_to_leaf.get(&1).unwrap();
601 decomp.mark_dirty(leaf_idx);
602
603 let mut current = Some(leaf_idx);
605 while let Some(idx) = current {
606 assert!(decomp.nodes[idx].dirty, "Node {} should be dirty", idx);
607 current = decomp.nodes[idx].parent;
608 }
609 }
610
611 #[test]
612 fn test_insert_edge() {
613 let graph = create_simple_graph();
614 let mut decomp = HierarchicalDecomposition::build(graph.clone()).unwrap();
615
616 let old_min_cut = decomp.min_cut_value();
617
618 graph.insert_edge(1, 4, 5.0).unwrap();
620 graph.insert_edge(2, 4, 5.0).unwrap();
621
622 let mut decomp = HierarchicalDecomposition::build(graph.clone()).unwrap();
624 let baseline = decomp.min_cut_value();
625
626 graph.insert_edge(3, 4, 3.0).unwrap();
628 let new_min_cut = decomp.insert_edge(3, 4, 3.0).unwrap();
629
630 assert!(new_min_cut.is_finite());
632 }
633
634 #[test]
635 fn test_delete_edge() {
636 let graph = create_disconnectable_graph();
637 let mut decomp = HierarchicalDecomposition::build(graph.clone()).unwrap();
638
639 let old_min_cut = decomp.min_cut_value();
640 assert!(old_min_cut.is_finite());
641
642 graph.delete_edge(1, 2).unwrap();
644 let new_min_cut = decomp.delete_edge(1, 2).unwrap();
645
646 assert!(new_min_cut.is_finite());
649 }
650
651 #[test]
652 fn test_level_info() {
653 let graph = create_simple_graph();
654 let decomp = HierarchicalDecomposition::build(graph).unwrap();
655
656 let levels = decomp.level_info();
657
658 assert!(!levels.is_empty());
660
661 for i in 1..levels.len() {
663 assert!(levels[i].level > levels[i - 1].level);
664 }
665
666 let total_nodes: usize = levels.iter().map(|l| l.num_nodes).sum();
668 assert_eq!(total_nodes, decomp.num_nodes());
669 }
670
671 #[test]
672 fn test_balanced_tree() {
673 let graph = Arc::new(DynamicGraph::new());
674
675 for i in 1..=15 {
677 graph.add_vertex(i);
678 }
679
680 for i in 1..15 {
682 graph.insert_edge(i, i + 1, 1.0).unwrap();
683 }
684
685 let decomp = HierarchicalDecomposition::build(graph).unwrap();
686
687 assert!(
689 decomp.height() <= 4,
690 "Height {} should be <= 4",
691 decomp.height()
692 );
693
694 let leaf_count = decomp.nodes.iter().filter(|n| n.level == 0).count();
696 assert_eq!(leaf_count, 15);
697 }
698
699 #[test]
700 fn test_compute_cut() {
701 let graph = Arc::new(DynamicGraph::new());
702
703 graph.insert_edge(1, 3, 1.0).unwrap();
708 graph.insert_edge(2, 4, 1.0).unwrap();
709
710 let decomp = HierarchicalDecomposition::build(graph).unwrap();
711
712 let min_cut = decomp.min_cut_value();
717 assert!(min_cut.is_finite() && min_cut <= 2.0);
718 }
719
720 #[test]
721 fn test_large_tree() {
722 let graph = Arc::new(DynamicGraph::new());
723
724 for i in 1..=100 {
726 graph.add_vertex(i);
727 }
728
729 for i in 1..100 {
730 graph.insert_edge(i, i + 1, 1.0).unwrap();
731 }
732
733 let decomp = HierarchicalDecomposition::build(graph).unwrap();
734
735 assert!(
737 decomp.height() <= 7,
738 "Height {} should be <= 7",
739 decomp.height()
740 );
741
742 assert_eq!(decomp.min_cut_value(), 1.0);
744
745 assert_eq!(decomp.num_nodes(), 199);
747 }
748
749 #[test]
750 fn test_propagate_updates() {
751 let graph = create_simple_graph();
752 let mut decomp = HierarchicalDecomposition::build(graph).unwrap();
753
754 for i in 0..decomp.nodes.len() {
756 decomp.nodes[i].dirty = true;
757 }
758
759 let min_cut = decomp.propagate_updates();
761
762 for node in &decomp.nodes {
764 assert!(!node.dirty);
765 }
766
767 assert_eq!(min_cut, 2.0);
769 }
770}