1use std::collections::{HashMap, HashSet};
34use std::sync::Arc;
35use rand::prelude::*;
36use rand::rngs::StdRng;
37use crate::graph::{DynamicGraph, VertexId, EdgeId, Weight};
38use crate::error::{MinCutError, Result};
39
40#[derive(Debug, Clone)]
42pub struct SparsifyConfig {
43 pub epsilon: f64,
45 pub seed: Option<u64>,
47 pub max_edges: Option<usize>,
49}
50
51impl Default for SparsifyConfig {
52 fn default() -> Self {
53 Self {
54 epsilon: 0.1,
55 seed: None,
56 max_edges: None,
57 }
58 }
59}
60
61impl SparsifyConfig {
62 pub fn new(epsilon: f64) -> Result<Self> {
64 if epsilon <= 0.0 || epsilon > 1.0 {
65 return Err(MinCutError::InvalidEpsilon(epsilon));
66 }
67 Ok(Self {
68 epsilon,
69 seed: None,
70 max_edges: None,
71 })
72 }
73
74 pub fn with_seed(mut self, seed: u64) -> Self {
76 self.seed = Some(seed);
77 self
78 }
79
80 pub fn with_max_edges(mut self, max_edges: usize) -> Self {
82 self.max_edges = Some(max_edges);
83 self
84 }
85}
86
87pub struct SparseGraph {
89 graph: DynamicGraph,
91 edge_weights: HashMap<EdgeId, Weight>,
93 epsilon: f64,
95 original_edges: usize,
97 rng: StdRng,
99 strength_calc: EdgeStrength,
101}
102
103impl SparseGraph {
104 pub fn from_graph(graph: &DynamicGraph, config: SparsifyConfig) -> Result<Self> {
106 if config.epsilon <= 0.0 || config.epsilon > 1.0 {
107 return Err(MinCutError::InvalidEpsilon(config.epsilon));
108 }
109
110 let original_edges = graph.num_edges();
111 let n = graph.num_vertices();
112
113 if n == 0 {
114 return Err(MinCutError::EmptyGraph);
115 }
116
117 let rng = match config.seed {
119 Some(seed) => StdRng::seed_from_u64(seed),
120 None => StdRng::from_entropy(),
121 };
122
123 let sparse_graph = DynamicGraph::with_capacity(n, original_edges);
125
126 for v in graph.vertices() {
128 sparse_graph.add_vertex(v);
129 }
130
131 let mut edge_weights = HashMap::new();
132 let mut strength_calc = EdgeStrength::new(Arc::new(graph.clone()));
133
134 Self::benczur_karger_sparsify(
136 graph,
137 &sparse_graph,
138 &mut edge_weights,
139 &mut strength_calc,
140 config.epsilon,
141 &mut StdRng::seed_from_u64(config.seed.unwrap_or(42)),
142 config.max_edges,
143 )?;
144
145 Ok(Self {
146 graph: sparse_graph,
147 edge_weights,
148 epsilon: config.epsilon,
149 original_edges,
150 rng,
151 strength_calc,
152 })
153 }
154
155 fn benczur_karger_sparsify(
157 original: &DynamicGraph,
158 sparse: &DynamicGraph,
159 edge_weights: &mut HashMap<EdgeId, Weight>,
160 strength_calc: &mut EdgeStrength,
161 epsilon: f64,
162 rng: &mut StdRng,
163 max_edges: Option<usize>,
164 ) -> Result<()> {
165 let n = original.num_vertices() as f64;
166 let c = 6.0; let strengths = strength_calc.compute_all();
170
171 let edges = original.edges();
172 let mut edges_added = 0;
173 let target_edges = max_edges.unwrap_or(usize::MAX);
174
175 for edge in edges {
176 let strength = strengths.get(&edge.id).copied().unwrap_or(edge.weight);
178
179 let prob = sample_probability(strength, epsilon, n, c);
181
182 if rng.gen::<f64>() < prob && edges_added < target_edges {
184 let scaled_weight = edge.weight / prob;
186
187 if let Ok(new_edge_id) = sparse.insert_edge(
189 edge.source,
190 edge.target,
191 scaled_weight
192 ) {
193 edge_weights.insert(new_edge_id, edge.weight);
194 edges_added += 1;
195 }
196 }
197 }
198
199 Ok(())
200 }
201
202 pub fn graph(&self) -> &DynamicGraph {
204 &self.graph
205 }
206
207 pub fn num_edges(&self) -> usize {
209 self.graph.num_edges()
210 }
211
212 pub fn sparsification_ratio(&self) -> f64 {
214 if self.original_edges == 0 {
215 return 1.0;
216 }
217 self.num_edges() as f64 / self.original_edges as f64
218 }
219
220 pub fn approximate_min_cut(&self) -> f64 {
222 if self.graph.num_vertices() == 0 {
224 return 0.0;
225 }
226
227 let vertices = self.graph.vertices();
228 let mut min_cut = f64::INFINITY;
229
230 for v in vertices {
231 let mut cut_weight = 0.0;
232 for (neighbor, _edge_id) in self.graph.neighbors(v) {
233 if let Some(edge) = self.graph.get_edge(v, neighbor) {
234 cut_weight += edge.weight;
235 }
236 }
237 min_cut = min_cut.min(cut_weight);
238 }
239
240 min_cut
241 }
242
243 pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) -> Result<()> {
245 self.strength_calc.invalidate(u);
247 self.strength_calc.invalidate(v);
248
249 let strength = self.strength_calc.compute(u, v);
251
252 let n = self.graph.num_vertices() as f64;
254 let c = 6.0;
255 let prob = sample_probability(strength, self.epsilon, n, c);
256
257 if self.rng.gen::<f64>() < prob {
259 let scaled_weight = weight / prob;
260 if let Ok(new_edge_id) = self.graph.insert_edge(u, v, scaled_weight) {
261 self.edge_weights.insert(new_edge_id, weight);
262 }
263 }
264
265 Ok(())
266 }
267
268 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> Result<()> {
270 self.strength_calc.invalidate(u);
272 self.strength_calc.invalidate(v);
273
274 let _ = self.graph.delete_edge(u, v);
276
277 Ok(())
278 }
279
280 pub fn epsilon(&self) -> f64 {
282 self.epsilon
283 }
284}
285
286pub struct EdgeStrength {
288 graph: Arc<DynamicGraph>,
290 strengths: HashMap<EdgeId, f64>,
292}
293
294impl EdgeStrength {
295 pub fn new(graph: Arc<DynamicGraph>) -> Self {
297 Self {
298 graph,
299 strengths: HashMap::new(),
300 }
301 }
302
303 pub fn compute(&mut self, u: VertexId, v: VertexId) -> f64 {
307 let edge = self.graph.get_edge(u, v);
309 let edge_id = edge.map(|e| e.id);
310
311 if let Some(&strength) = edge_id.and_then(|id| self.strengths.get(&id)) {
313 return strength;
314 }
315
316 let weight_u: f64 = self.graph.neighbors(u)
319 .iter()
320 .filter_map(|(neighbor, _)| self.graph.edge_weight(u, *neighbor))
321 .sum();
322
323 let weight_v: f64 = self.graph.neighbors(v)
324 .iter()
325 .filter_map(|(neighbor, _)| self.graph.edge_weight(v, *neighbor))
326 .sum();
327
328 let strength = weight_u.min(weight_v).max(1.0);
329
330 if let Some(id) = edge_id {
332 self.strengths.insert(id, strength);
333 }
334
335 strength
336 }
337
338 pub fn compute_all(&mut self) -> HashMap<EdgeId, f64> {
340 let edges = self.graph.edges();
341
342 for edge in edges {
343 if !self.strengths.contains_key(&edge.id) {
344 let strength = self.compute(edge.source, edge.target);
345 self.strengths.insert(edge.id, strength);
346 }
347 }
348
349 self.strengths.clone()
350 }
351
352 pub fn invalidate(&mut self, v: VertexId) {
354 let neighbors = self.graph.neighbors(v);
355 for (_neighbor, edge_id) in neighbors {
356 self.strengths.remove(&edge_id);
357 }
358 }
359}
360
361pub struct NagamochiIbaraki {
363 graph: Arc<DynamicGraph>,
365}
366
367impl NagamochiIbaraki {
368 pub fn new(graph: Arc<DynamicGraph>) -> Self {
370 Self { graph }
371 }
372
373 pub fn sparse_k_certificate(&self, k: usize) -> Result<DynamicGraph> {
375 let n = self.graph.num_vertices();
376 if n == 0 {
377 return Err(MinCutError::EmptyGraph);
378 }
379
380 let order = self.min_degree_ordering();
382
383 let connectivity = self.scan_connectivity(&order);
385
386 let sparse = DynamicGraph::with_capacity(n, k * n);
388
389 for v in self.graph.vertices() {
391 sparse.add_vertex(v);
392 }
393
394 for edge in self.graph.edges() {
396 if let Some(&conn) = connectivity.get(&edge.id) {
397 if conn >= k {
398 let _ = sparse.insert_edge(edge.source, edge.target, edge.weight);
399 }
400 }
401 }
402
403 Ok(sparse)
404 }
405
406 fn min_degree_ordering(&self) -> Vec<VertexId> {
408 let mut remaining: HashSet<VertexId> = self.graph.vertices().into_iter().collect();
409 let mut order = Vec::with_capacity(remaining.len());
410
411 let mut degrees: HashMap<VertexId, usize> = self.graph.vertices()
413 .iter()
414 .map(|&v| (v, self.graph.degree(v)))
415 .collect();
416
417 while !remaining.is_empty() {
418 let (&min_v, _) = degrees.iter()
420 .filter(|(v, _)| remaining.contains(v))
421 .min_by_key(|(_, °)| deg)
422 .unwrap();
423
424 order.push(min_v);
425 remaining.remove(&min_v);
426
427 for (neighbor, _) in self.graph.neighbors(min_v) {
429 if remaining.contains(&neighbor) {
430 if let Some(deg) = degrees.get_mut(&neighbor) {
431 *deg = deg.saturating_sub(1);
432 }
433 }
434 }
435 }
436
437 order
438 }
439
440 fn scan_connectivity(&self, order: &[VertexId]) -> HashMap<EdgeId, usize> {
442 let mut connectivity = HashMap::new();
443 let mut scanned = HashSet::new();
444
445 for &v in order.iter().rev() {
446 scanned.insert(v);
447
448 for (neighbor, edge_id) in self.graph.neighbors(v) {
450 if scanned.contains(&neighbor) {
451 let conn = scanned.len();
453 connectivity.insert(edge_id, conn);
454 }
455 }
456 }
457
458 connectivity
459 }
460}
461
462pub fn karger_sparsify(
464 graph: &DynamicGraph,
465 epsilon: f64,
466 seed: Option<u64>,
467) -> Result<SparseGraph> {
468 let config = SparsifyConfig::new(epsilon)?
469 .with_seed(seed.unwrap_or(42));
470
471 SparseGraph::from_graph(graph, config)
472}
473
474fn sample_probability(strength: f64, epsilon: f64, n: f64, c: f64) -> f64 {
476 if strength <= 0.0 {
477 return 0.0;
478 }
479
480 let numerator = c * n.ln();
482 let denominator = epsilon * epsilon * strength;
483
484 (numerator / denominator).min(1.0)
485}
486
487#[cfg(test)]
488mod tests {
489 use super::*;
490
491 fn create_triangle_graph() -> DynamicGraph {
492 let g = DynamicGraph::new();
493 g.insert_edge(1, 2, 1.0).unwrap();
494 g.insert_edge(2, 3, 1.0).unwrap();
495 g.insert_edge(3, 1, 1.0).unwrap();
496 g
497 }
498
499 fn create_complete_graph(n: usize) -> DynamicGraph {
500 let g = DynamicGraph::new();
501 for i in 0..n {
502 for j in (i + 1)..n {
503 g.insert_edge(i as u64, j as u64, 1.0).unwrap();
504 }
505 }
506 g
507 }
508
509 fn create_path_graph(n: usize) -> DynamicGraph {
510 let g = DynamicGraph::new();
511 for i in 0..(n - 1) {
512 g.insert_edge(i as u64, (i + 1) as u64, 1.0).unwrap();
513 }
514 g
515 }
516
517 #[test]
518 fn test_sparsify_config_default() {
519 let config = SparsifyConfig::default();
520 assert_eq!(config.epsilon, 0.1);
521 assert_eq!(config.seed, None);
522 assert_eq!(config.max_edges, None);
523 }
524
525 #[test]
526 fn test_sparsify_config_new() {
527 let config = SparsifyConfig::new(0.2).unwrap();
528 assert_eq!(config.epsilon, 0.2);
529 }
530
531 #[test]
532 fn test_sparsify_config_invalid_epsilon() {
533 assert!(SparsifyConfig::new(0.0).is_err());
534 assert!(SparsifyConfig::new(-0.1).is_err());
535 assert!(SparsifyConfig::new(1.5).is_err());
536 }
537
538 #[test]
539 fn test_sparsify_config_builder() {
540 let config = SparsifyConfig::new(0.1).unwrap()
541 .with_seed(42)
542 .with_max_edges(10);
543
544 assert_eq!(config.epsilon, 0.1);
545 assert_eq!(config.seed, Some(42));
546 assert_eq!(config.max_edges, Some(10));
547 }
548
549 #[test]
550 fn test_sparse_graph_triangle() {
551 let g = create_triangle_graph();
552 let config = SparsifyConfig::new(0.1).unwrap().with_seed(42);
553
554 let sparse = SparseGraph::from_graph(&g, config).unwrap();
555
556 assert!(sparse.num_edges() <= g.num_edges());
557 assert_eq!(sparse.epsilon(), 0.1);
558 assert_eq!(sparse.graph().num_vertices(), g.num_vertices());
559 }
560
561 #[test]
562 fn test_sparse_graph_sparsification_ratio() {
563 let g = create_complete_graph(10);
564 let config = SparsifyConfig::new(0.2).unwrap().with_seed(123);
565
566 let sparse = SparseGraph::from_graph(&g, config).unwrap();
567 let ratio = sparse.sparsification_ratio();
568
569 assert!(ratio >= 0.0 && ratio <= 1.0);
570 println!("Sparsification ratio: {}", ratio);
571 }
572
573 #[test]
574 fn test_sparse_graph_max_edges() {
575 let g = create_complete_graph(10);
576 let config = SparsifyConfig::new(0.1).unwrap()
577 .with_seed(42)
578 .with_max_edges(20);
579
580 let sparse = SparseGraph::from_graph(&g, config).unwrap();
581
582 assert!(sparse.num_edges() <= 20);
583 }
584
585 #[test]
586 fn test_sparse_graph_empty_graph() {
587 let g = DynamicGraph::new();
588 let config = SparsifyConfig::default();
589
590 let result = SparseGraph::from_graph(&g, config);
591 assert!(result.is_err());
592 }
593
594 #[test]
595 fn test_sparse_graph_approximate_min_cut() {
596 let g = create_path_graph(5);
597 let config = SparsifyConfig::new(0.1).unwrap().with_seed(42);
598
599 let sparse = SparseGraph::from_graph(&g, config).unwrap();
600 let min_cut = sparse.approximate_min_cut();
601
602 assert!(min_cut >= 0.0);
604 }
605
606 #[test]
607 fn test_sparse_graph_insert_edge() {
608 let g = create_triangle_graph();
609 let config = SparsifyConfig::new(0.1).unwrap().with_seed(42);
610
611 let mut sparse = SparseGraph::from_graph(&g, config).unwrap();
612 let initial_edges = sparse.num_edges();
613
614 sparse.insert_edge(4, 5, 1.0).unwrap();
615
616 assert!(sparse.num_edges() >= initial_edges);
618 }
619
620 #[test]
621 fn test_sparse_graph_delete_edge() {
622 let g = create_triangle_graph();
623 let config = SparsifyConfig::new(0.5).unwrap().with_seed(42);
624
625 let mut sparse = SparseGraph::from_graph(&g, config).unwrap();
626
627 let result = sparse.delete_edge(1, 2);
629 assert!(result.is_ok());
630 }
631
632 #[test]
633 fn test_edge_strength_compute() {
634 let g = create_triangle_graph();
635 let mut strength_calc = EdgeStrength::new(Arc::new(g));
636
637 let strength = strength_calc.compute(1, 2);
638 assert!(strength > 0.0);
639 }
640
641 #[test]
642 fn test_edge_strength_compute_all() {
643 let g = create_complete_graph(5);
644 let mut strength_calc = EdgeStrength::new(Arc::new(g));
645
646 let strengths = strength_calc.compute_all();
647 assert!(!strengths.is_empty());
648
649 for (_, strength) in strengths {
650 assert!(strength > 0.0);
651 }
652 }
653
654 #[test]
655 fn test_edge_strength_invalidate() {
656 let g = create_triangle_graph();
657 let mut strength_calc = EdgeStrength::new(Arc::new(g));
658
659 strength_calc.compute_all();
661 let initial_count = strength_calc.strengths.len();
662
663 strength_calc.invalidate(1);
665
666 assert!(strength_calc.strengths.len() < initial_count);
668 }
669
670 #[test]
671 fn test_nagamochi_ibaraki_min_degree_ordering() {
672 let g = create_path_graph(5);
673 let ni = NagamochiIbaraki::new(Arc::new(g));
674
675 let order = ni.min_degree_ordering();
676 assert_eq!(order.len(), 5);
677
678 let order_set: HashSet<_> = order.iter().copied().collect();
680 assert_eq!(order_set.len(), 5);
681 }
682
683 #[test]
684 fn test_nagamochi_ibaraki_sparse_certificate() {
685 let g = create_complete_graph(6);
686 let ni = NagamochiIbaraki::new(Arc::new(g.clone()));
687
688 let sparse = ni.sparse_k_certificate(3).unwrap();
689
690 assert!(sparse.num_edges() <= g.num_edges());
692 assert_eq!(sparse.num_vertices(), g.num_vertices());
693 }
694
695 #[test]
696 fn test_nagamochi_ibaraki_empty_graph() {
697 let g = DynamicGraph::new();
698 let ni = NagamochiIbaraki::new(Arc::new(g));
699
700 let result = ni.sparse_k_certificate(2);
701 assert!(result.is_err());
702 }
703
704 #[test]
705 fn test_karger_sparsify() {
706 let g = create_complete_graph(8);
707
708 let sparse = karger_sparsify(&g, 0.2, Some(42)).unwrap();
709
710 assert!(sparse.num_edges() <= g.num_edges());
711 assert_eq!(sparse.epsilon(), 0.2);
712 }
713
714 #[test]
715 fn test_karger_sparsify_invalid_epsilon() {
716 let g = create_triangle_graph();
717
718 let result = karger_sparsify(&g, 0.0, Some(42));
719 assert!(result.is_err());
720 }
721
722 #[test]
723 fn test_sample_probability() {
724 let n = 100.0;
725 let epsilon = 0.1;
726 let c = 6.0;
727
728 let prob_high = sample_probability(100.0, epsilon, n, c);
730
731 let prob_low = sample_probability(1.0, epsilon, n, c);
733
734 assert!(prob_high <= prob_low);
735 assert!(prob_high >= 0.0 && prob_high <= 1.0);
736 assert!(prob_low >= 0.0 && prob_low <= 1.0);
737 }
738
739 #[test]
740 fn test_sample_probability_zero_strength() {
741 let prob = sample_probability(0.0, 0.1, 100.0, 6.0);
742 assert_eq!(prob, 0.0);
743 }
744
745 #[test]
746 fn test_sample_probability_always_capped() {
747 let prob = sample_probability(0.001, 0.1, 100.0, 100.0);
748 assert!(prob <= 1.0);
749 }
750
751 #[test]
752 fn test_sparsification_preserves_vertices() {
753 let g = create_complete_graph(10);
754 let original_vertices: HashSet<_> = g.vertices().into_iter().collect();
755
756 let config = SparsifyConfig::new(0.15).unwrap().with_seed(999);
757 let sparse = SparseGraph::from_graph(&g, config).unwrap();
758
759 let sparse_vertices: HashSet<_> = sparse.graph().vertices().into_iter().collect();
760
761 assert_eq!(original_vertices, sparse_vertices);
763 }
764
765 #[test]
766 fn test_sparsification_weighted_graph() {
767 let g = DynamicGraph::new();
768 g.insert_edge(1, 2, 2.0).unwrap();
769 g.insert_edge(2, 3, 3.0).unwrap();
770 g.insert_edge(3, 4, 4.0).unwrap();
771 g.insert_edge(4, 1, 5.0).unwrap();
772
773 let config = SparsifyConfig::new(0.3).unwrap().with_seed(777);
774 let sparse = SparseGraph::from_graph(&g, config).unwrap();
775
776 assert!(sparse.num_edges() <= g.num_edges());
778 }
779
780 #[test]
781 fn test_deterministic_with_seed() {
782 let g = create_complete_graph(8);
783
784 let sparse1 = karger_sparsify(&g, 0.2, Some(12345)).unwrap();
785 let sparse2 = karger_sparsify(&g, 0.2, Some(12345)).unwrap();
786
787 assert_eq!(sparse1.num_edges(), sparse2.num_edges());
789 }
790
791 #[test]
792 fn test_edge_strength_caching() {
793 let g = create_complete_graph(6);
794 let mut strength_calc = EdgeStrength::new(Arc::new(g));
795
796 let strength1 = strength_calc.compute(0, 1);
798
799 let strength2 = strength_calc.compute(0, 1);
801
802 assert_eq!(strength1, strength2);
803 }
804
805 #[test]
806 fn test_nagamochi_ibaraki_scan_connectivity() {
807 let g = create_complete_graph(5);
808 let ni = NagamochiIbaraki::new(Arc::new(g.clone()));
809
810 let order = ni.min_degree_ordering();
811 let connectivity = ni.scan_connectivity(&order);
812
813 assert!(!connectivity.is_empty());
815
816 for (_, conn) in connectivity {
817 assert!(conn > 0);
818 }
819 }
820
821 #[test]
822 fn test_sparse_graph_ratio_bounds() {
823 let g = create_complete_graph(10);
824
825 let config_strict = SparsifyConfig::new(0.01).unwrap().with_seed(42);
827 let sparse_strict = SparseGraph::from_graph(&g, config_strict).unwrap();
828
829 let config_loose = SparsifyConfig::new(0.5).unwrap().with_seed(42);
831 let sparse_loose = SparseGraph::from_graph(&g, config_loose).unwrap();
832
833 assert!(sparse_strict.sparsification_ratio() >= 0.0);
836 assert!(sparse_loose.sparsification_ratio() >= 0.0);
837 }
838}