problemreductions/topology/
hypergraph.rs

1//! Hypergraph implementation.
2//!
3//! A hypergraph is a generalization of a graph where edges (called hyperedges)
4//! can connect any number of vertices, not just two.
5
6use serde::{Deserialize, Serialize};
7
8/// A hypergraph where edges can connect any number of vertices.
9///
10/// # Example
11///
12/// ```
13/// use problemreductions::topology::HyperGraph;
14///
15/// // Create a hypergraph with 4 vertices and 2 hyperedges
16/// let hg = HyperGraph::new(4, vec![
17///     vec![0, 1, 2],  // Edge connecting vertices 0, 1, 2
18///     vec![2, 3],     // Edge connecting vertices 2, 3
19/// ]);
20///
21/// assert_eq!(hg.num_vertices(), 4);
22/// assert_eq!(hg.num_edges(), 2);
23/// ```
24#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
25pub struct HyperGraph {
26    num_vertices: usize,
27    edges: Vec<Vec<usize>>,
28}
29
30impl HyperGraph {
31    /// Create a new hypergraph.
32    ///
33    /// # Panics
34    ///
35    /// Panics if any vertex index in an edge is out of bounds.
36    pub fn new(num_vertices: usize, edges: Vec<Vec<usize>>) -> Self {
37        for edge in &edges {
38            for &v in edge {
39                assert!(v < num_vertices, "vertex index {} out of bounds (max {})", v, num_vertices - 1);
40            }
41        }
42        Self { num_vertices, edges }
43    }
44
45    /// Create an empty hypergraph with no edges.
46    pub fn empty(num_vertices: usize) -> Self {
47        Self {
48            num_vertices,
49            edges: Vec::new(),
50        }
51    }
52
53    /// Get the number of vertices.
54    pub fn num_vertices(&self) -> usize {
55        self.num_vertices
56    }
57
58    /// Get the number of hyperedges.
59    pub fn num_edges(&self) -> usize {
60        self.edges.len()
61    }
62
63    /// Get all hyperedges.
64    pub fn edges(&self) -> &[Vec<usize>] {
65        &self.edges
66    }
67
68    /// Get a specific edge by index.
69    pub fn edge(&self, index: usize) -> Option<&Vec<usize>> {
70        self.edges.get(index)
71    }
72
73    /// Check if a hyperedge exists (order-independent).
74    pub fn has_edge(&self, edge: &[usize]) -> bool {
75        let mut sorted = edge.to_vec();
76        sorted.sort();
77        self.edges.iter().any(|e| {
78            let mut e_sorted = e.clone();
79            e_sorted.sort();
80            e_sorted == sorted
81        })
82    }
83
84    /// Get all vertices adjacent to vertex v (share a hyperedge with v).
85    pub fn neighbors(&self, v: usize) -> Vec<usize> {
86        let mut neighbors = Vec::new();
87        for edge in &self.edges {
88            if edge.contains(&v) {
89                for &u in edge {
90                    if u != v && !neighbors.contains(&u) {
91                        neighbors.push(u);
92                    }
93                }
94            }
95        }
96        neighbors
97    }
98
99    /// Get the degree of a vertex (number of hyperedges containing it).
100    pub fn degree(&self, v: usize) -> usize {
101        self.edges.iter().filter(|edge| edge.contains(&v)).count()
102    }
103
104    /// Get all edges containing a specific vertex.
105    pub fn edges_containing(&self, v: usize) -> Vec<&Vec<usize>> {
106        self.edges.iter().filter(|edge| edge.contains(&v)).collect()
107    }
108
109    /// Add a new hyperedge.
110    ///
111    /// # Panics
112    ///
113    /// Panics if any vertex index is out of bounds.
114    pub fn add_edge(&mut self, edge: Vec<usize>) {
115        for &v in &edge {
116            assert!(v < self.num_vertices, "vertex index {} out of bounds", v);
117        }
118        self.edges.push(edge);
119    }
120
121    /// Get the maximum edge size (maximum number of vertices in any hyperedge).
122    pub fn max_edge_size(&self) -> usize {
123        self.edges.iter().map(|e| e.len()).max().unwrap_or(0)
124    }
125
126    /// Check if this is a regular graph (all edges have size 2).
127    pub fn is_regular_graph(&self) -> bool {
128        self.edges.iter().all(|e| e.len() == 2)
129    }
130
131    /// Convert to a regular graph if possible (all edges size 2).
132    /// Returns None if any edge has size != 2.
133    pub fn to_graph_edges(&self) -> Option<Vec<(usize, usize)>> {
134        if !self.is_regular_graph() {
135            return None;
136        }
137        Some(
138            self.edges
139                .iter()
140                .map(|e| (e[0], e[1]))
141                .collect()
142        )
143    }
144}
145
146#[cfg(test)]
147mod tests {
148    use super::*;
149
150    #[test]
151    fn test_hypergraph_basic() {
152        let hg = HyperGraph::new(4, vec![vec![0, 1, 2], vec![2, 3]]);
153        assert_eq!(hg.num_vertices(), 4);
154        assert_eq!(hg.num_edges(), 2);
155    }
156
157    #[test]
158    fn test_hypergraph_empty() {
159        let hg = HyperGraph::empty(5);
160        assert_eq!(hg.num_vertices(), 5);
161        assert_eq!(hg.num_edges(), 0);
162    }
163
164    #[test]
165    fn test_hypergraph_neighbors() {
166        let hg = HyperGraph::new(4, vec![vec![0, 1, 2], vec![2, 3]]);
167        let neighbors = hg.neighbors(2);
168        assert!(neighbors.contains(&0));
169        assert!(neighbors.contains(&1));
170        assert!(neighbors.contains(&3));
171        assert!(!neighbors.contains(&2)); // Not its own neighbor
172    }
173
174    #[test]
175    fn test_hypergraph_has_edge() {
176        let hg = HyperGraph::new(4, vec![vec![0, 1, 2]]);
177        assert!(hg.has_edge(&[0, 1, 2]));
178        assert!(hg.has_edge(&[2, 1, 0])); // Order doesn't matter
179        assert!(!hg.has_edge(&[0, 1]));
180        assert!(!hg.has_edge(&[0, 1, 3]));
181    }
182
183    #[test]
184    fn test_hypergraph_degree() {
185        let hg = HyperGraph::new(4, vec![vec![0, 1, 2], vec![2, 3]]);
186        assert_eq!(hg.degree(0), 1);
187        assert_eq!(hg.degree(2), 2);
188        assert_eq!(hg.degree(3), 1);
189    }
190
191    #[test]
192    fn test_hypergraph_edges_containing() {
193        let hg = HyperGraph::new(4, vec![vec![0, 1, 2], vec![2, 3]]);
194        let edges = hg.edges_containing(2);
195        assert_eq!(edges.len(), 2);
196    }
197
198    #[test]
199    fn test_hypergraph_add_edge() {
200        let mut hg = HyperGraph::empty(4);
201        hg.add_edge(vec![0, 1]);
202        hg.add_edge(vec![1, 2, 3]);
203        assert_eq!(hg.num_edges(), 2);
204    }
205
206    #[test]
207    fn test_hypergraph_max_edge_size() {
208        let hg = HyperGraph::new(4, vec![vec![0, 1], vec![0, 1, 2, 3]]);
209        assert_eq!(hg.max_edge_size(), 4);
210    }
211
212    #[test]
213    fn test_hypergraph_is_regular_graph() {
214        let regular = HyperGraph::new(3, vec![vec![0, 1], vec![1, 2]]);
215        assert!(regular.is_regular_graph());
216
217        let not_regular = HyperGraph::new(4, vec![vec![0, 1, 2]]);
218        assert!(!not_regular.is_regular_graph());
219    }
220
221    #[test]
222    fn test_hypergraph_to_graph_edges() {
223        let hg = HyperGraph::new(3, vec![vec![0, 1], vec![1, 2]]);
224        let edges = hg.to_graph_edges();
225        assert!(edges.is_some());
226        let edges = edges.unwrap();
227        assert_eq!(edges.len(), 2);
228    }
229
230    #[test]
231    fn test_hypergraph_get_edge() {
232        let hg = HyperGraph::new(4, vec![vec![0, 1, 2], vec![2, 3]]);
233        assert_eq!(hg.edge(0), Some(&vec![0, 1, 2]));
234        assert_eq!(hg.edge(1), Some(&vec![2, 3]));
235        assert_eq!(hg.edge(2), None);
236    }
237
238    #[test]
239    #[should_panic(expected = "vertex index 5 out of bounds")]
240    fn test_hypergraph_invalid_vertex() {
241        HyperGraph::new(4, vec![vec![0, 5]]);
242    }
243
244    #[test]
245    #[should_panic(expected = "vertex index 4 out of bounds")]
246    fn test_hypergraph_add_invalid_edge() {
247        let mut hg = HyperGraph::empty(4);
248        hg.add_edge(vec![0, 4]);
249    }
250}