1use crate::error::{MinCutError, Result};
34use crate::graph::{DynamicGraph, EdgeId, VertexId, Weight};
35use rand::prelude::*;
36use rand::rngs::StdRng;
37use std::collections::{HashMap, HashSet};
38use std::sync::Arc;
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(edge.source, edge.target, scaled_weight)
189 {
190 edge_weights.insert(new_edge_id, edge.weight);
191 edges_added += 1;
192 }
193 }
194 }
195
196 Ok(())
197 }
198
199 pub fn graph(&self) -> &DynamicGraph {
201 &self.graph
202 }
203
204 pub fn num_edges(&self) -> usize {
206 self.graph.num_edges()
207 }
208
209 pub fn sparsification_ratio(&self) -> f64 {
211 if self.original_edges == 0 {
212 return 1.0;
213 }
214 self.num_edges() as f64 / self.original_edges as f64
215 }
216
217 pub fn approximate_min_cut(&self) -> f64 {
219 if self.graph.num_vertices() == 0 {
221 return 0.0;
222 }
223
224 let vertices = self.graph.vertices();
225 let mut min_cut = f64::INFINITY;
226
227 for v in vertices {
228 let mut cut_weight = 0.0;
229 for (neighbor, _edge_id) in self.graph.neighbors(v) {
230 if let Some(edge) = self.graph.get_edge(v, neighbor) {
231 cut_weight += edge.weight;
232 }
233 }
234 min_cut = min_cut.min(cut_weight);
235 }
236
237 min_cut
238 }
239
240 pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) -> Result<()> {
242 self.strength_calc.invalidate(u);
244 self.strength_calc.invalidate(v);
245
246 let strength = self.strength_calc.compute(u, v);
248
249 let n = self.graph.num_vertices() as f64;
251 let c = 6.0;
252 let prob = sample_probability(strength, self.epsilon, n, c);
253
254 if self.rng.gen::<f64>() < prob {
256 let scaled_weight = weight / prob;
257 if let Ok(new_edge_id) = self.graph.insert_edge(u, v, scaled_weight) {
258 self.edge_weights.insert(new_edge_id, weight);
259 }
260 }
261
262 Ok(())
263 }
264
265 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> Result<()> {
267 self.strength_calc.invalidate(u);
269 self.strength_calc.invalidate(v);
270
271 let _ = self.graph.delete_edge(u, v);
273
274 Ok(())
275 }
276
277 pub fn epsilon(&self) -> f64 {
279 self.epsilon
280 }
281}
282
283pub struct EdgeStrength {
285 graph: Arc<DynamicGraph>,
287 strengths: HashMap<EdgeId, f64>,
289}
290
291impl EdgeStrength {
292 pub fn new(graph: Arc<DynamicGraph>) -> Self {
294 Self {
295 graph,
296 strengths: HashMap::new(),
297 }
298 }
299
300 pub fn compute(&mut self, u: VertexId, v: VertexId) -> f64 {
304 let edge = self.graph.get_edge(u, v);
306 let edge_id = edge.map(|e| e.id);
307
308 if let Some(&strength) = edge_id.and_then(|id| self.strengths.get(&id)) {
310 return strength;
311 }
312
313 let weight_u: f64 = self
316 .graph
317 .neighbors(u)
318 .iter()
319 .filter_map(|(neighbor, _)| self.graph.edge_weight(u, *neighbor))
320 .sum();
321
322 let weight_v: f64 = self
323 .graph
324 .neighbors(v)
325 .iter()
326 .filter_map(|(neighbor, _)| self.graph.edge_weight(v, *neighbor))
327 .sum();
328
329 let strength = weight_u.min(weight_v).max(1.0);
330
331 if let Some(id) = edge_id {
333 self.strengths.insert(id, strength);
334 }
335
336 strength
337 }
338
339 pub fn compute_all(&mut self) -> HashMap<EdgeId, f64> {
341 let edges = self.graph.edges();
342
343 for edge in edges {
344 if !self.strengths.contains_key(&edge.id) {
345 let strength = self.compute(edge.source, edge.target);
346 self.strengths.insert(edge.id, strength);
347 }
348 }
349
350 self.strengths.clone()
351 }
352
353 pub fn invalidate(&mut self, v: VertexId) {
355 let neighbors = self.graph.neighbors(v);
356 for (_neighbor, edge_id) in neighbors {
357 self.strengths.remove(&edge_id);
358 }
359 }
360}
361
362pub struct NagamochiIbaraki {
364 graph: Arc<DynamicGraph>,
366}
367
368impl NagamochiIbaraki {
369 pub fn new(graph: Arc<DynamicGraph>) -> Self {
371 Self { graph }
372 }
373
374 pub fn sparse_k_certificate(&self, k: usize) -> Result<DynamicGraph> {
376 let n = self.graph.num_vertices();
377 if n == 0 {
378 return Err(MinCutError::EmptyGraph);
379 }
380
381 let order = self.min_degree_ordering();
383
384 let connectivity = self.scan_connectivity(&order);
386
387 let sparse = DynamicGraph::with_capacity(n, k * n);
389
390 for v in self.graph.vertices() {
392 sparse.add_vertex(v);
393 }
394
395 for edge in self.graph.edges() {
397 if let Some(&conn) = connectivity.get(&edge.id) {
398 if conn >= k {
399 let _ = sparse.insert_edge(edge.source, edge.target, edge.weight);
400 }
401 }
402 }
403
404 Ok(sparse)
405 }
406
407 fn min_degree_ordering(&self) -> Vec<VertexId> {
409 let mut remaining: HashSet<VertexId> = self.graph.vertices().into_iter().collect();
410 let mut order = Vec::with_capacity(remaining.len());
411
412 let mut degrees: HashMap<VertexId, usize> = self
414 .graph
415 .vertices()
416 .iter()
417 .map(|&v| (v, self.graph.degree(v)))
418 .collect();
419
420 while !remaining.is_empty() {
421 let (&min_v, _) = degrees
423 .iter()
424 .filter(|(v, _)| remaining.contains(v))
425 .min_by_key(|(_, °)| deg)
426 .unwrap();
427
428 order.push(min_v);
429 remaining.remove(&min_v);
430
431 for (neighbor, _) in self.graph.neighbors(min_v) {
433 if remaining.contains(&neighbor) {
434 if let Some(deg) = degrees.get_mut(&neighbor) {
435 *deg = deg.saturating_sub(1);
436 }
437 }
438 }
439 }
440
441 order
442 }
443
444 fn scan_connectivity(&self, order: &[VertexId]) -> HashMap<EdgeId, usize> {
446 let mut connectivity = HashMap::new();
447 let mut scanned = HashSet::new();
448
449 for &v in order.iter().rev() {
450 scanned.insert(v);
451
452 for (neighbor, edge_id) in self.graph.neighbors(v) {
454 if scanned.contains(&neighbor) {
455 let conn = scanned.len();
457 connectivity.insert(edge_id, conn);
458 }
459 }
460 }
461
462 connectivity
463 }
464}
465
466pub fn karger_sparsify(
468 graph: &DynamicGraph,
469 epsilon: f64,
470 seed: Option<u64>,
471) -> Result<SparseGraph> {
472 let config = SparsifyConfig::new(epsilon)?.with_seed(seed.unwrap_or(42));
473
474 SparseGraph::from_graph(graph, config)
475}
476
477fn sample_probability(strength: f64, epsilon: f64, n: f64, c: f64) -> f64 {
479 if strength <= 0.0 {
480 return 0.0;
481 }
482
483 let numerator = c * n.ln();
485 let denominator = epsilon * epsilon * strength;
486
487 (numerator / denominator).min(1.0)
488}
489
490#[cfg(test)]
491mod tests {
492 use super::*;
493
494 fn create_triangle_graph() -> DynamicGraph {
495 let g = DynamicGraph::new();
496 g.insert_edge(1, 2, 1.0).unwrap();
497 g.insert_edge(2, 3, 1.0).unwrap();
498 g.insert_edge(3, 1, 1.0).unwrap();
499 g
500 }
501
502 fn create_complete_graph(n: usize) -> DynamicGraph {
503 let g = DynamicGraph::new();
504 for i in 0..n {
505 for j in (i + 1)..n {
506 g.insert_edge(i as u64, j as u64, 1.0).unwrap();
507 }
508 }
509 g
510 }
511
512 fn create_path_graph(n: usize) -> DynamicGraph {
513 let g = DynamicGraph::new();
514 for i in 0..(n - 1) {
515 g.insert_edge(i as u64, (i + 1) as u64, 1.0).unwrap();
516 }
517 g
518 }
519
520 #[test]
521 fn test_sparsify_config_default() {
522 let config = SparsifyConfig::default();
523 assert_eq!(config.epsilon, 0.1);
524 assert_eq!(config.seed, None);
525 assert_eq!(config.max_edges, None);
526 }
527
528 #[test]
529 fn test_sparsify_config_new() {
530 let config = SparsifyConfig::new(0.2).unwrap();
531 assert_eq!(config.epsilon, 0.2);
532 }
533
534 #[test]
535 fn test_sparsify_config_invalid_epsilon() {
536 assert!(SparsifyConfig::new(0.0).is_err());
537 assert!(SparsifyConfig::new(-0.1).is_err());
538 assert!(SparsifyConfig::new(1.5).is_err());
539 }
540
541 #[test]
542 fn test_sparsify_config_builder() {
543 let config = SparsifyConfig::new(0.1)
544 .unwrap()
545 .with_seed(42)
546 .with_max_edges(10);
547
548 assert_eq!(config.epsilon, 0.1);
549 assert_eq!(config.seed, Some(42));
550 assert_eq!(config.max_edges, Some(10));
551 }
552
553 #[test]
554 fn test_sparse_graph_triangle() {
555 let g = create_triangle_graph();
556 let config = SparsifyConfig::new(0.1).unwrap().with_seed(42);
557
558 let sparse = SparseGraph::from_graph(&g, config).unwrap();
559
560 assert!(sparse.num_edges() <= g.num_edges());
561 assert_eq!(sparse.epsilon(), 0.1);
562 assert_eq!(sparse.graph().num_vertices(), g.num_vertices());
563 }
564
565 #[test]
566 fn test_sparse_graph_sparsification_ratio() {
567 let g = create_complete_graph(10);
568 let config = SparsifyConfig::new(0.2).unwrap().with_seed(123);
569
570 let sparse = SparseGraph::from_graph(&g, config).unwrap();
571 let ratio = sparse.sparsification_ratio();
572
573 assert!(ratio >= 0.0 && ratio <= 1.0);
574 println!("Sparsification ratio: {}", ratio);
575 }
576
577 #[test]
578 fn test_sparse_graph_max_edges() {
579 let g = create_complete_graph(10);
580 let config = SparsifyConfig::new(0.1)
581 .unwrap()
582 .with_seed(42)
583 .with_max_edges(20);
584
585 let sparse = SparseGraph::from_graph(&g, config).unwrap();
586
587 assert!(sparse.num_edges() <= 20);
588 }
589
590 #[test]
591 fn test_sparse_graph_empty_graph() {
592 let g = DynamicGraph::new();
593 let config = SparsifyConfig::default();
594
595 let result = SparseGraph::from_graph(&g, config);
596 assert!(result.is_err());
597 }
598
599 #[test]
600 fn test_sparse_graph_approximate_min_cut() {
601 let g = create_path_graph(5);
602 let config = SparsifyConfig::new(0.1).unwrap().with_seed(42);
603
604 let sparse = SparseGraph::from_graph(&g, config).unwrap();
605 let min_cut = sparse.approximate_min_cut();
606
607 assert!(min_cut >= 0.0);
609 }
610
611 #[test]
612 fn test_sparse_graph_insert_edge() {
613 let g = create_triangle_graph();
614 let config = SparsifyConfig::new(0.1).unwrap().with_seed(42);
615
616 let mut sparse = SparseGraph::from_graph(&g, config).unwrap();
617 let initial_edges = sparse.num_edges();
618
619 sparse.insert_edge(4, 5, 1.0).unwrap();
620
621 assert!(sparse.num_edges() >= initial_edges);
623 }
624
625 #[test]
626 fn test_sparse_graph_delete_edge() {
627 let g = create_triangle_graph();
628 let config = SparsifyConfig::new(0.5).unwrap().with_seed(42);
629
630 let mut sparse = SparseGraph::from_graph(&g, config).unwrap();
631
632 let result = sparse.delete_edge(1, 2);
634 assert!(result.is_ok());
635 }
636
637 #[test]
638 fn test_edge_strength_compute() {
639 let g = create_triangle_graph();
640 let mut strength_calc = EdgeStrength::new(Arc::new(g));
641
642 let strength = strength_calc.compute(1, 2);
643 assert!(strength > 0.0);
644 }
645
646 #[test]
647 fn test_edge_strength_compute_all() {
648 let g = create_complete_graph(5);
649 let mut strength_calc = EdgeStrength::new(Arc::new(g));
650
651 let strengths = strength_calc.compute_all();
652 assert!(!strengths.is_empty());
653
654 for (_, strength) in strengths {
655 assert!(strength > 0.0);
656 }
657 }
658
659 #[test]
660 fn test_edge_strength_invalidate() {
661 let g = create_triangle_graph();
662 let mut strength_calc = EdgeStrength::new(Arc::new(g));
663
664 strength_calc.compute_all();
666 let initial_count = strength_calc.strengths.len();
667
668 strength_calc.invalidate(1);
670
671 assert!(strength_calc.strengths.len() < initial_count);
673 }
674
675 #[test]
676 fn test_nagamochi_ibaraki_min_degree_ordering() {
677 let g = create_path_graph(5);
678 let ni = NagamochiIbaraki::new(Arc::new(g));
679
680 let order = ni.min_degree_ordering();
681 assert_eq!(order.len(), 5);
682
683 let order_set: HashSet<_> = order.iter().copied().collect();
685 assert_eq!(order_set.len(), 5);
686 }
687
688 #[test]
689 fn test_nagamochi_ibaraki_sparse_certificate() {
690 let g = create_complete_graph(6);
691 let ni = NagamochiIbaraki::new(Arc::new(g.clone()));
692
693 let sparse = ni.sparse_k_certificate(3).unwrap();
694
695 assert!(sparse.num_edges() <= g.num_edges());
697 assert_eq!(sparse.num_vertices(), g.num_vertices());
698 }
699
700 #[test]
701 fn test_nagamochi_ibaraki_empty_graph() {
702 let g = DynamicGraph::new();
703 let ni = NagamochiIbaraki::new(Arc::new(g));
704
705 let result = ni.sparse_k_certificate(2);
706 assert!(result.is_err());
707 }
708
709 #[test]
710 fn test_karger_sparsify() {
711 let g = create_complete_graph(8);
712
713 let sparse = karger_sparsify(&g, 0.2, Some(42)).unwrap();
714
715 assert!(sparse.num_edges() <= g.num_edges());
716 assert_eq!(sparse.epsilon(), 0.2);
717 }
718
719 #[test]
720 fn test_karger_sparsify_invalid_epsilon() {
721 let g = create_triangle_graph();
722
723 let result = karger_sparsify(&g, 0.0, Some(42));
724 assert!(result.is_err());
725 }
726
727 #[test]
728 fn test_sample_probability() {
729 let n = 100.0;
730 let epsilon = 0.1;
731 let c = 6.0;
732
733 let prob_high = sample_probability(100.0, epsilon, n, c);
735
736 let prob_low = sample_probability(1.0, epsilon, n, c);
738
739 assert!(prob_high <= prob_low);
740 assert!(prob_high >= 0.0 && prob_high <= 1.0);
741 assert!(prob_low >= 0.0 && prob_low <= 1.0);
742 }
743
744 #[test]
745 fn test_sample_probability_zero_strength() {
746 let prob = sample_probability(0.0, 0.1, 100.0, 6.0);
747 assert_eq!(prob, 0.0);
748 }
749
750 #[test]
751 fn test_sample_probability_always_capped() {
752 let prob = sample_probability(0.001, 0.1, 100.0, 100.0);
753 assert!(prob <= 1.0);
754 }
755
756 #[test]
757 fn test_sparsification_preserves_vertices() {
758 let g = create_complete_graph(10);
759 let original_vertices: HashSet<_> = g.vertices().into_iter().collect();
760
761 let config = SparsifyConfig::new(0.15).unwrap().with_seed(999);
762 let sparse = SparseGraph::from_graph(&g, config).unwrap();
763
764 let sparse_vertices: HashSet<_> = sparse.graph().vertices().into_iter().collect();
765
766 assert_eq!(original_vertices, sparse_vertices);
768 }
769
770 #[test]
771 fn test_sparsification_weighted_graph() {
772 let g = DynamicGraph::new();
773 g.insert_edge(1, 2, 2.0).unwrap();
774 g.insert_edge(2, 3, 3.0).unwrap();
775 g.insert_edge(3, 4, 4.0).unwrap();
776 g.insert_edge(4, 1, 5.0).unwrap();
777
778 let config = SparsifyConfig::new(0.3).unwrap().with_seed(777);
779 let sparse = SparseGraph::from_graph(&g, config).unwrap();
780
781 assert!(sparse.num_edges() <= g.num_edges());
783 }
784
785 #[test]
786 fn test_deterministic_with_seed() {
787 let g = create_complete_graph(8);
788
789 let sparse1 = karger_sparsify(&g, 0.2, Some(12345)).unwrap();
790 let sparse2 = karger_sparsify(&g, 0.2, Some(12345)).unwrap();
791
792 assert_eq!(sparse1.num_edges(), sparse2.num_edges());
794 }
795
796 #[test]
797 fn test_edge_strength_caching() {
798 let g = create_complete_graph(6);
799 let mut strength_calc = EdgeStrength::new(Arc::new(g));
800
801 let strength1 = strength_calc.compute(0, 1);
803
804 let strength2 = strength_calc.compute(0, 1);
806
807 assert_eq!(strength1, strength2);
808 }
809
810 #[test]
811 fn test_nagamochi_ibaraki_scan_connectivity() {
812 let g = create_complete_graph(5);
813 let ni = NagamochiIbaraki::new(Arc::new(g.clone()));
814
815 let order = ni.min_degree_ordering();
816 let connectivity = ni.scan_connectivity(&order);
817
818 assert!(!connectivity.is_empty());
820
821 for (_, conn) in connectivity {
822 assert!(conn > 0);
823 }
824 }
825
826 #[test]
827 fn test_sparse_graph_ratio_bounds() {
828 let g = create_complete_graph(10);
829
830 let config_strict = SparsifyConfig::new(0.01).unwrap().with_seed(42);
832 let sparse_strict = SparseGraph::from_graph(&g, config_strict).unwrap();
833
834 let config_loose = SparsifyConfig::new(0.5).unwrap().with_seed(42);
836 let sparse_loose = SparseGraph::from_graph(&g, config_loose).unwrap();
837
838 assert!(sparse_strict.sparsification_ratio() >= 0.0);
841 assert!(sparse_loose.sparsification_ratio() >= 0.0);
842 }
843}