problemreductions/topology/
graph.rs

1//! Graph trait and SimpleGraph implementation.
2//!
3//! This module provides a `Graph` trait that abstracts over different graph
4//! representations, following Julia's Graphs.jl `AbstractGraph` pattern.
5//!
6//! Supported graph types:
7//! - [`SimpleGraph`]: Standard unweighted graph (wrapper around petgraph)
8//! - [`UnitDiskGraph`]: Vertices with 2D positions, edges based on distance
9//! - [`HyperGraph`]: Edges can connect any number of vertices (via adapter)
10
11use petgraph::graph::{NodeIndex, UnGraph};
12use petgraph::visit::EdgeRef;
13use serde::{Deserialize, Serialize};
14
15/// Trait for graph types, following Julia's Graphs.jl AbstractGraph pattern.
16///
17/// This trait abstracts over different graph representations, allowing
18/// problems to be generic over the underlying graph type.
19///
20/// # Example
21///
22/// ```rust,ignore
23/// use problemreductions::topology::{Graph, SimpleGraph};
24///
25/// fn count_triangles<G: Graph>(graph: &G) -> usize {
26///     let mut count = 0;
27///     for u in 0..graph.num_vertices() {
28///         for v in graph.neighbors(u) {
29///             if v > u {
30///                 for w in graph.neighbors(v) {
31///                     if w > v && graph.has_edge(u, w) {
32///                         count += 1;
33///                     }
34///                 }
35///             }
36///         }
37///     }
38///     count
39/// }
40/// ```
41pub trait Graph: Clone + Send + Sync {
42    /// Returns the number of vertices in the graph.
43    fn num_vertices(&self) -> usize;
44
45    /// Returns the number of edges in the graph.
46    fn num_edges(&self) -> usize;
47
48    /// Returns all edges as a list of (u, v) pairs.
49    ///
50    /// For undirected graphs, each edge appears once with u < v.
51    fn edges(&self) -> Vec<(usize, usize)>;
52
53    /// Checks if an edge exists between vertices u and v.
54    fn has_edge(&self, u: usize, v: usize) -> bool;
55
56    /// Returns all neighbors of vertex v.
57    fn neighbors(&self, v: usize) -> Vec<usize>;
58
59    /// Returns the degree of vertex v (number of neighbors).
60    fn degree(&self, v: usize) -> usize {
61        self.neighbors(v).len()
62    }
63
64    /// Returns true if the graph has no vertices.
65    fn is_empty(&self) -> bool {
66        self.num_vertices() == 0
67    }
68
69    /// Iterates over all edges, calling a closure for each.
70    ///
71    /// This can be more efficient than `edges()` when you don't need to collect.
72    fn for_each_edge<F>(&self, mut f: F)
73    where
74        F: FnMut(usize, usize),
75    {
76        for (u, v) in self.edges() {
77            f(u, v);
78        }
79    }
80}
81
82/// A simple unweighted undirected graph.
83///
84/// This is the default graph type for most problems. It wraps petgraph's
85/// `UnGraph` and implements the `Graph` trait.
86///
87/// # Example
88///
89/// ```
90/// use problemreductions::topology::SimpleGraph;
91/// use problemreductions::topology::Graph;
92///
93/// let graph = SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]);
94/// assert_eq!(graph.num_vertices(), 4);
95/// assert_eq!(graph.num_edges(), 3);
96/// assert!(graph.has_edge(0, 1));
97/// assert!(!graph.has_edge(0, 2));
98/// ```
99#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct SimpleGraph {
101    inner: UnGraph<(), ()>,
102}
103
104impl SimpleGraph {
105    /// Creates a new graph with the given vertices and edges.
106    ///
107    /// # Arguments
108    ///
109    /// * `num_vertices` - Number of vertices in the graph
110    /// * `edges` - List of edges as (u, v) pairs
111    ///
112    /// # Panics
113    ///
114    /// Panics if any edge references a vertex index >= num_vertices.
115    pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self {
116        let mut inner = UnGraph::new_undirected();
117        for _ in 0..num_vertices {
118            inner.add_node(());
119        }
120        for (u, v) in edges {
121            assert!(
122                u < num_vertices && v < num_vertices,
123                "edge ({}, {}) references vertex >= num_vertices ({})",
124                u,
125                v,
126                num_vertices
127            );
128            inner.add_edge(NodeIndex::new(u), NodeIndex::new(v), ());
129        }
130        Self { inner }
131    }
132
133    /// Creates an empty graph with the given number of vertices.
134    pub fn empty(num_vertices: usize) -> Self {
135        Self::new(num_vertices, vec![])
136    }
137
138    /// Creates a complete graph (all vertices connected).
139    pub fn complete(num_vertices: usize) -> Self {
140        let mut edges = Vec::new();
141        for i in 0..num_vertices {
142            for j in (i + 1)..num_vertices {
143                edges.push((i, j));
144            }
145        }
146        Self::new(num_vertices, edges)
147    }
148
149    /// Creates a path graph (0-1-2-...-n).
150    pub fn path(num_vertices: usize) -> Self {
151        let edges: Vec<_> = (0..num_vertices.saturating_sub(1))
152            .map(|i| (i, i + 1))
153            .collect();
154        Self::new(num_vertices, edges)
155    }
156
157    /// Creates a cycle graph (0-1-2-...-n-0).
158    pub fn cycle(num_vertices: usize) -> Self {
159        if num_vertices < 3 {
160            return Self::path(num_vertices);
161        }
162        let mut edges: Vec<_> = (0..num_vertices - 1).map(|i| (i, i + 1)).collect();
163        edges.push((num_vertices - 1, 0));
164        Self::new(num_vertices, edges)
165    }
166
167    /// Creates a star graph (vertex 0 connected to all others).
168    pub fn star(num_vertices: usize) -> Self {
169        let edges: Vec<_> = (1..num_vertices).map(|i| (0, i)).collect();
170        Self::new(num_vertices, edges)
171    }
172
173    /// Creates a grid graph with the given dimensions.
174    ///
175    /// Vertices are numbered row by row: vertex `r * cols + c` is at row `r`, column `c`.
176    pub fn grid(rows: usize, cols: usize) -> Self {
177        let num_vertices = rows * cols;
178        let mut edges = Vec::new();
179
180        for r in 0..rows {
181            for c in 0..cols {
182                let v = r * cols + c;
183                // Right neighbor
184                if c + 1 < cols {
185                    edges.push((v, v + 1));
186                }
187                // Down neighbor
188                if r + 1 < rows {
189                    edges.push((v, v + cols));
190                }
191            }
192        }
193
194        Self::new(num_vertices, edges)
195    }
196}
197
198impl Graph for SimpleGraph {
199    fn num_vertices(&self) -> usize {
200        self.inner.node_count()
201    }
202
203    fn num_edges(&self) -> usize {
204        self.inner.edge_count()
205    }
206
207    fn edges(&self) -> Vec<(usize, usize)> {
208        self.inner
209            .edge_references()
210            .map(|e| (e.source().index(), e.target().index()))
211            .collect()
212    }
213
214    fn has_edge(&self, u: usize, v: usize) -> bool {
215        self.inner
216            .find_edge(NodeIndex::new(u), NodeIndex::new(v))
217            .is_some()
218    }
219
220    fn neighbors(&self, v: usize) -> Vec<usize> {
221        self.inner
222            .neighbors(NodeIndex::new(v))
223            .map(|n| n.index())
224            .collect()
225    }
226}
227
228impl PartialEq for SimpleGraph {
229    fn eq(&self, other: &Self) -> bool {
230        if self.num_vertices() != other.num_vertices() {
231            return false;
232        }
233        if self.num_edges() != other.num_edges() {
234            return false;
235        }
236        // Check all edges exist in both
237        let mut self_edges: Vec<_> = self.edges();
238        let mut other_edges: Vec<_> = other.edges();
239        // Normalize edge order
240        for e in &mut self_edges {
241            if e.0 > e.1 {
242                *e = (e.1, e.0);
243            }
244        }
245        for e in &mut other_edges {
246            if e.0 > e.1 {
247                *e = (e.1, e.0);
248            }
249        }
250        self_edges.sort();
251        other_edges.sort();
252        self_edges == other_edges
253    }
254}
255
256impl Eq for SimpleGraph {}
257
258#[cfg(test)]
259mod tests {
260    use super::*;
261
262    #[test]
263    fn test_simple_graph_new() {
264        let graph = SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]);
265        assert_eq!(graph.num_vertices(), 4);
266        assert_eq!(graph.num_edges(), 3);
267    }
268
269    #[test]
270    fn test_simple_graph_empty() {
271        let graph = SimpleGraph::empty(5);
272        assert_eq!(graph.num_vertices(), 5);
273        assert_eq!(graph.num_edges(), 0);
274    }
275
276    #[test]
277    fn test_simple_graph_complete() {
278        let graph = SimpleGraph::complete(4);
279        assert_eq!(graph.num_vertices(), 4);
280        assert_eq!(graph.num_edges(), 6); // C(4,2) = 6
281    }
282
283    #[test]
284    fn test_simple_graph_path() {
285        let graph = SimpleGraph::path(5);
286        assert_eq!(graph.num_vertices(), 5);
287        assert_eq!(graph.num_edges(), 4);
288        assert!(graph.has_edge(0, 1));
289        assert!(graph.has_edge(3, 4));
290        assert!(!graph.has_edge(0, 4));
291    }
292
293    #[test]
294    fn test_simple_graph_cycle() {
295        let graph = SimpleGraph::cycle(4);
296        assert_eq!(graph.num_vertices(), 4);
297        assert_eq!(graph.num_edges(), 4);
298        assert!(graph.has_edge(0, 1));
299        assert!(graph.has_edge(3, 0)); // Cycle edge
300    }
301
302    #[test]
303    fn test_simple_graph_star() {
304        let graph = SimpleGraph::star(5);
305        assert_eq!(graph.num_vertices(), 5);
306        assert_eq!(graph.num_edges(), 4);
307        assert!(graph.has_edge(0, 1));
308        assert!(graph.has_edge(0, 4));
309        assert!(!graph.has_edge(1, 2));
310    }
311
312    #[test]
313    fn test_simple_graph_grid() {
314        let graph = SimpleGraph::grid(2, 3);
315        assert_eq!(graph.num_vertices(), 6);
316        // 2 rows: 2 horizontal edges per row = 4
317        // 3 cols: 1 vertical edge per col = 3
318        assert_eq!(graph.num_edges(), 7);
319    }
320
321    #[test]
322    fn test_simple_graph_has_edge() {
323        let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2)]);
324        assert!(graph.has_edge(0, 1));
325        assert!(graph.has_edge(1, 0)); // Undirected
326        assert!(graph.has_edge(1, 2));
327        assert!(!graph.has_edge(0, 2));
328    }
329
330    #[test]
331    fn test_simple_graph_neighbors() {
332        let graph = SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3)]);
333        let mut neighbors = graph.neighbors(0);
334        neighbors.sort();
335        assert_eq!(neighbors, vec![1, 2, 3]);
336        assert_eq!(graph.neighbors(1), vec![0]);
337    }
338
339    #[test]
340    fn test_simple_graph_degree() {
341        let graph = SimpleGraph::new(4, vec![(0, 1), (0, 2), (0, 3)]);
342        assert_eq!(graph.degree(0), 3);
343        assert_eq!(graph.degree(1), 1);
344    }
345
346    #[test]
347    fn test_simple_graph_is_empty() {
348        let empty = SimpleGraph::empty(0);
349        assert!(empty.is_empty());
350
351        let non_empty = SimpleGraph::empty(1);
352        assert!(!non_empty.is_empty());
353    }
354
355    #[test]
356    fn test_simple_graph_for_each_edge() {
357        let graph = SimpleGraph::new(3, vec![(0, 1), (1, 2)]);
358        let mut count = 0;
359        graph.for_each_edge(|_, _| count += 1);
360        assert_eq!(count, 2);
361    }
362
363    #[test]
364    fn test_simple_graph_eq() {
365        let g1 = SimpleGraph::new(3, vec![(0, 1), (1, 2)]);
366        let g2 = SimpleGraph::new(3, vec![(1, 2), (0, 1)]); // Different order
367        let g3 = SimpleGraph::new(3, vec![(0, 1)]);
368
369        assert_eq!(g1, g2);
370        assert_ne!(g1, g3);
371    }
372
373    #[test]
374    #[should_panic(expected = "edge (0, 5) references vertex >= num_vertices")]
375    fn test_simple_graph_invalid_edge() {
376        SimpleGraph::new(3, vec![(0, 5)]);
377    }
378}