1use crate::base::{EdgeWeight, Hypergraph, IndexType, Node};
7use crate::error::{GraphError, Result};
8use std::collections::{HashMap, HashSet, VecDeque};
9
10#[derive(Debug, Clone)]
12pub struct HypergraphCut<N: Node> {
13 pub partition_a: HashSet<N>,
15 pub partition_b: HashSet<N>,
17 pub cut_hyperedges: Vec<usize>,
19 pub cut_weight: f64,
21}
22
23#[derive(Debug, Clone)]
25pub struct MinimalTransversal<N: Node> {
26 pub nodes: HashSet<N>,
28 pub hit_hyperedges: Vec<usize>,
30 pub size: usize,
32}
33
34#[allow(dead_code)]
46pub fn minimal_transversals<N, E, Ix>(
47 hypergraph: &Hypergraph<N, E, Ix>,
48 max_size: Option<usize>,
49) -> Vec<MinimalTransversal<N>>
50where
51 N: Node + Clone + Ord + std::fmt::Debug,
52 E: EdgeWeight,
53 Ix: IndexType,
54{
55 let mut transversals = Vec::new();
56 let nodes: Vec<N> = hypergraph.nodes().cloned().collect();
57 let hyperedges = hypergraph.hyperedges();
58
59 if nodes.is_empty() || hyperedges.is_empty() {
60 return transversals;
61 }
62
63 let max_size = max_size.unwrap_or(nodes.len());
64
65 for size in 1..=max_size.min(nodes.len()) {
67 let combinations = generate_combinations(&nodes, size);
68
69 for candidate in combinations {
70 let candidate_set: HashSet<N> = candidate.into_iter().collect();
71
72 let mut hit_hyperedges = Vec::new();
74 let mut hits_all = true;
75
76 for hyperedge in hyperedges {
77 let hyperedge_nodes: std::collections::HashSet<N> =
78 hyperedge.nodes.iter().cloned().collect();
79 if candidate_set
80 .intersection(&hyperedge_nodes)
81 .next()
82 .is_some()
83 {
84 hit_hyperedges.push(hyperedge.id);
85 } else {
86 hits_all = false;
87 break;
88 }
89 }
90
91 if hits_all {
92 let mut is_minimal = true;
94 for existing in &transversals {
95 if existing.nodes.is_subset(&candidate_set) && existing.nodes != candidate_set {
96 is_minimal = false;
97 break;
98 }
99 }
100
101 if is_minimal {
102 transversals
104 .retain(|t| !candidate_set.is_subset(&t.nodes) || t.nodes == candidate_set);
105
106 transversals.push(MinimalTransversal {
107 size: candidate_set.len(),
108 nodes: candidate_set,
109 hit_hyperedges,
110 });
111 }
112 }
113 }
114 }
115
116 transversals
117}
118
119#[allow(dead_code)]
121fn generate_combinations<T: Clone>(items: &[T], k: usize) -> Vec<Vec<T>> {
122 if k == 0 {
123 return vec![vec![]];
124 }
125 if k > items.len() {
126 return vec![];
127 }
128 if k == items.len() {
129 return vec![items.to_vec()];
130 }
131
132 let mut result = Vec::new();
133
134 let first = items[0].clone();
136 let rest = &items[1..];
137 for mut combo in generate_combinations(rest, k - 1) {
138 combo.insert(0, first.clone());
139 result.push(combo);
140 }
141
142 result.extend(generate_combinations(rest, k));
144
145 result
146}
147
148#[allow(dead_code)]
160pub fn minimum_vertex_cut<N, E, Ix>(
161 hypergraph: &Hypergraph<N, E, Ix>,
162 source: &N,
163 target: &N,
164) -> Result<HypergraphCut<N>>
165where
166 N: Node + Clone + Ord + std::fmt::Debug,
167 E: EdgeWeight
168 + Clone
169 + Default
170 + scirs2_core::numeric::Zero
171 + std::ops::Add<Output = E>
172 + Into<f64>,
173 Ix: IndexType,
174{
175 if !hypergraph.has_node(source) || !hypergraph.has_node(target) {
176 return Err(GraphError::node_not_found("node"));
177 }
178
179 if source == target {
180 return Err(GraphError::InvalidGraph(
181 "Source and target cannot be the same".to_string(),
182 ));
183 }
184
185 let _graph = hypergraph.to_graph();
188
189 let all_nodes: HashSet<N> = hypergraph.nodes().cloned().collect();
192
193 let mut partition_a = HashSet::new();
195 let mut partition_b = HashSet::new();
196
197 partition_a.insert(source.clone());
198 partition_b.insert(target.clone());
199
200 for node in &all_nodes {
202 if node != source && node != target {
203 if partition_a.len() <= partition_b.len() {
204 partition_a.insert(node.clone());
205 } else {
206 partition_b.insert(node.clone());
207 }
208 }
209 }
210
211 let mut cut_hyperedges = Vec::new();
213 let mut cut_weight = 0.0;
214
215 for hyperedge in hypergraph.hyperedges() {
216 let hyperedge_nodes: std::collections::HashSet<N> =
217 hyperedge.nodes.iter().cloned().collect();
218 let has_a = hyperedge_nodes.intersection(&partition_a).next().is_some();
219 let has_b = hyperedge_nodes.intersection(&partition_b).next().is_some();
220
221 if has_a && has_b {
222 cut_hyperedges.push(hyperedge.id);
223 cut_weight += hyperedge.weight.clone().into();
224 }
225 }
226
227 Ok(HypergraphCut {
228 partition_a,
229 partition_b,
230 cut_hyperedges,
231 cut_weight,
232 })
233}
234
235#[allow(dead_code)]
248pub fn hyperedge_connectivity<N, E, Ix>(
249 hypergraph: &Hypergraph<N, E, Ix>,
250 source: &N,
251 target: &N,
252) -> Result<usize>
253where
254 N: Node + Clone + Ord + std::fmt::Debug,
255 E: EdgeWeight + Clone,
256 Ix: IndexType,
257{
258 if !hypergraph.has_node(source) || !hypergraph.has_node(target) {
259 return Err(GraphError::node_not_found("node"));
260 }
261
262 if source == target {
263 return Ok(0);
264 }
265
266 if !hypergraph.are_connected(source, target) {
268 return Ok(0);
269 }
270
271 let hyperedges = hypergraph.hyperedges();
273 let connecting_hyperedges = hypergraph.connecting_hyperedges(source, target);
274
275 if !connecting_hyperedges.is_empty() {
277 return Ok(1);
278 }
279
280 for hyperedge in hyperedges {
283 let mut temphypergraph: Hypergraph<N, E, Ix> = Hypergraph::new();
284
285 for node in hypergraph.nodes() {
287 temphypergraph.add_node(node.clone());
288 }
289
290 for other_hyperedge in hyperedges {
292 if other_hyperedge.id != hyperedge.id {
293 temphypergraph.add_hyperedge(
294 other_hyperedge.nodes.clone(),
295 other_hyperedge.weight.clone(),
296 )?;
297 }
298 }
299
300 if !temphypergraph.are_connected(source, target) {
302 return Ok(1);
303 }
304 }
305
306 Ok(2)
309}
310
311#[allow(dead_code)]
322pub fn hypergraph_diameter<N, E, Ix>(hypergraph: &Hypergraph<N, E, Ix>) -> Option<usize>
323where
324 N: Node + Clone + Ord + std::fmt::Debug,
325 E: EdgeWeight,
326 Ix: IndexType,
327{
328 let nodes: Vec<N> = hypergraph.nodes().cloned().collect();
329 if nodes.len() < 2 {
330 return Some(0);
331 }
332
333 let mut max_distance = 0;
334
335 for i in 0..nodes.len() {
336 for j in (i + 1)..nodes.len() {
337 if let Some(distance) = hypergraph_distance(hypergraph, &nodes[i], &nodes[j]) {
338 max_distance = max_distance.max(distance);
339 } else {
340 return None;
342 }
343 }
344 }
345
346 Some(max_distance)
347}
348
349#[allow(dead_code)]
361pub fn hypergraph_distance<N, E, Ix>(
362 hypergraph: &Hypergraph<N, E, Ix>,
363 source: &N,
364 target: &N,
365) -> Option<usize>
366where
367 N: Node + Clone + Ord + std::fmt::Debug,
368 E: EdgeWeight,
369 Ix: IndexType,
370{
371 if source == target {
372 return Some(0);
373 }
374
375 if !hypergraph.has_node(source) || !hypergraph.has_node(target) {
376 return None;
377 }
378
379 let mut visited = HashSet::new();
381 let mut queue = VecDeque::new();
382 let mut distances = HashMap::new();
383
384 queue.push_back(source.clone());
385 visited.insert(source.clone());
386 distances.insert(source.clone(), 0);
387
388 while let Some(current) = queue.pop_front() {
389 let current_distance = distances[¤t];
390
391 for hyperedge in hypergraph.hyperedges_containing_node(¤t) {
393 for neighbor in &hyperedge.nodes {
395 if neighbor != ¤t && !visited.contains(neighbor) {
396 visited.insert(neighbor.clone());
397 distances.insert(neighbor.clone(), current_distance + 1);
398 queue.push_back(neighbor.clone());
399
400 if neighbor == target {
401 return Some(current_distance + 1);
402 }
403 }
404 }
405 }
406 }
407
408 None
409}
410
411#[allow(dead_code)]
424pub fn hypergraph_connected_components<N, E, Ix>(
425 hypergraph: &Hypergraph<N, E, Ix>,
426) -> Vec<HashSet<N>>
427where
428 N: Node + Clone + Ord + std::fmt::Debug,
429 E: EdgeWeight,
430 Ix: IndexType,
431{
432 let mut components = Vec::new();
433 let mut visited: HashSet<N> = HashSet::new();
434
435 for node in hypergraph.nodes() {
436 if !visited.contains(node) {
437 let mut component = HashSet::new();
438 let mut stack = vec![node.clone()];
439
440 while let Some(current) = stack.pop() {
441 if !visited.contains(¤t) {
442 visited.insert(current.clone());
443 component.insert(current.clone());
444
445 let neighbors = hypergraph.neighbors(¤t);
447 for neighbor in neighbors {
448 if !visited.contains(&neighbor) {
449 stack.push(neighbor);
450 }
451 }
452 }
453 }
454
455 if !component.is_empty() {
456 components.push(component);
457 }
458 }
459 }
460
461 components
462}
463
464#[allow(dead_code)]
474pub fn ishypergraph_connected<N, E, Ix>(hypergraph: &Hypergraph<N, E, Ix>) -> bool
475where
476 N: Node + Clone + Ord + std::fmt::Debug,
477 E: EdgeWeight,
478 Ix: IndexType,
479{
480 let components = hypergraph_connected_components(hypergraph);
481 components.len() <= 1
482}
483
484#[cfg(test)]
485mod tests {
486 use super::*;
487 use crate::base::Hypergraph;
488
489 #[test]
490 fn test_minimal_transversals_simple() {
491 let mut hypergraph: Hypergraph<i32, f64> = Hypergraph::new();
492
493 hypergraph
495 .add_hyperedge_from_vec(vec![1, 2], 1.0)
496 .expect("Operation failed");
497 hypergraph
498 .add_hyperedge_from_vec(vec![2, 3], 1.0)
499 .expect("Operation failed");
500 hypergraph
501 .add_hyperedge_from_vec(vec![1, 3], 1.0)
502 .expect("Operation failed");
503
504 let transversals = minimal_transversals(&hypergraph, Some(2));
505
506 assert!(!transversals.is_empty());
508
509 for transversal in &transversals {
511 assert_eq!(transversal.hit_hyperedges.len(), 3);
512 }
513 }
514
515 #[test]
516 fn testhypergraph_distance() {
517 let mut hypergraph: Hypergraph<&str, f64> = Hypergraph::new();
518
519 hypergraph
521 .add_hyperedge_from_vec(vec!["A", "B"], 1.0)
522 .expect("Operation failed");
523 hypergraph
524 .add_hyperedge_from_vec(vec!["B", "C"], 1.0)
525 .expect("Operation failed");
526 hypergraph
527 .add_hyperedge_from_vec(vec!["C", "D"], 1.0)
528 .expect("Operation failed");
529
530 assert_eq!(hypergraph_distance(&hypergraph, &"A", &"A"), Some(0));
531 assert_eq!(hypergraph_distance(&hypergraph, &"A", &"B"), Some(1));
532 assert_eq!(hypergraph_distance(&hypergraph, &"A", &"C"), Some(2));
533 assert_eq!(hypergraph_distance(&hypergraph, &"A", &"D"), Some(3));
534
535 hypergraph.add_node("E");
537 assert_eq!(hypergraph_distance(&hypergraph, &"A", &"E"), None);
538 }
539
540 #[test]
541 fn testhypergraph_diameter() {
542 let mut hypergraph: Hypergraph<i32, f64> = Hypergraph::new();
543
544 hypergraph
546 .add_hyperedge_from_vec(vec![1, 2], 1.0)
547 .expect("Operation failed");
548 hypergraph
549 .add_hyperedge_from_vec(vec![2, 3], 1.0)
550 .expect("Operation failed");
551 hypergraph
552 .add_hyperedge_from_vec(vec![3, 4], 1.0)
553 .expect("Operation failed");
554
555 assert_eq!(hypergraph_diameter(&hypergraph), Some(3));
556
557 hypergraph
559 .add_hyperedge_from_vec(vec![1, 4], 1.0)
560 .expect("Operation failed");
561 assert_eq!(hypergraph_diameter(&hypergraph), Some(2));
562 }
563
564 #[test]
565 fn testhypergraph_connected_components() {
566 let mut hypergraph: Hypergraph<&str, f64> = Hypergraph::new();
567
568 hypergraph
570 .add_hyperedge_from_vec(vec!["A", "B"], 1.0)
571 .expect("Operation failed");
572 hypergraph
573 .add_hyperedge_from_vec(vec!["B", "C"], 1.0)
574 .expect("Operation failed");
575
576 hypergraph
577 .add_hyperedge_from_vec(vec!["D", "E"], 1.0)
578 .expect("Operation failed");
579
580 let components = hypergraph_connected_components(&hypergraph);
581 assert_eq!(components.len(), 2);
582
583 let sizes: Vec<usize> = components.iter().map(|c| c.len()).collect();
585 assert!(sizes.contains(&3)); assert!(sizes.contains(&2)); }
588
589 #[test]
590 fn test_ishypergraph_connected() {
591 let mut hypergraph: Hypergraph<i32, f64> = Hypergraph::new();
592
593 hypergraph
595 .add_hyperedge_from_vec(vec![1, 2, 3], 1.0)
596 .expect("Operation failed");
597 assert!(ishypergraph_connected(&hypergraph));
598
599 hypergraph.add_node(4);
601 assert!(!ishypergraph_connected(&hypergraph));
602
603 hypergraph
605 .add_hyperedge_from_vec(vec![3, 4], 1.0)
606 .expect("Operation failed");
607 assert!(ishypergraph_connected(&hypergraph));
608 }
609
610 #[test]
611 fn test_minimum_vertex_cut() {
612 let mut hypergraph: Hypergraph<&str, f64> = Hypergraph::new();
613
614 hypergraph
616 .add_hyperedge_from_vec(vec!["A", "B"], 1.0)
617 .expect("Operation failed");
618 hypergraph
619 .add_hyperedge_from_vec(vec!["B", "C"], 1.0)
620 .expect("Operation failed");
621 hypergraph
622 .add_hyperedge_from_vec(vec!["C", "D"], 1.0)
623 .expect("Operation failed");
624
625 let cut = minimum_vertex_cut(&hypergraph, &"A", &"D").expect("Operation failed");
626
627 assert!(!cut.partition_a.is_empty());
628 assert!(!cut.partition_b.is_empty());
629 assert!(!cut.cut_hyperedges.is_empty());
630 assert!(cut.cut_weight > 0.0);
631 }
632
633 #[test]
634 fn test_hyperedge_connectivity() {
635 let mut hypergraph: Hypergraph<i32, f64> = Hypergraph::new();
636
637 hypergraph
639 .add_hyperedge_from_vec(vec![1, 2], 1.0)
640 .expect("Operation failed");
641 assert_eq!(
642 hyperedge_connectivity(&hypergraph, &1, &2).expect("Operation failed"),
643 1
644 );
645
646 hypergraph
648 .add_hyperedge_from_vec(vec![1, 3, 4], 1.2)
649 .expect("Operation failed");
650
651 assert!(
653 hypergraph.are_connected(&1, &4),
654 "Nodes 1 and 4 should be directly connected in the same hyperedge"
655 );
656
657 let connectivity = hyperedge_connectivity(&hypergraph, &1, &4).expect("Operation failed");
658 assert!(
659 connectivity >= 1,
660 "Expected connectivity >= 1, got {connectivity}"
661 );
662
663 hypergraph.add_node(5); assert_eq!(
666 hyperedge_connectivity(&hypergraph, &1, &5).expect("Operation failed"),
667 0
668 );
669
670 assert_eq!(
672 hyperedge_connectivity(&hypergraph, &1, &1).expect("Operation failed"),
673 0
674 );
675 }
676
677 #[test]
678 fn test_generate_combinations() {
679 let items = vec![1, 2, 3, 4];
680
681 let combinations = generate_combinations(&items, 2);
682 assert_eq!(combinations.len(), 6); let combinations = generate_combinations(&items, 0);
685 assert_eq!(combinations.len(), 1);
686 assert!(combinations[0].is_empty());
687
688 let combinations = generate_combinations(&items, 5);
689 assert!(combinations.is_empty());
690 }
691
692 #[test]
693 fn test_emptyhypergraph() {
694 let hypergraph: Hypergraph<i32, f64> = Hypergraph::new();
695
696 let transversals = minimal_transversals(&hypergraph, None);
697 assert!(transversals.is_empty());
698
699 assert_eq!(hypergraph_diameter(&hypergraph), Some(0));
700
701 let components = hypergraph_connected_components(&hypergraph);
702 assert!(components.is_empty());
703
704 assert!(ishypergraph_connected(&hypergraph));
705 }
706}