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 + 'static {
42    /// The name of the graph type (e.g., "SimpleGraph", "KingsSubgraph").
43    const NAME: &'static str;
44
45    /// Returns the number of vertices in the graph.
46    fn num_vertices(&self) -> usize;
47
48    /// Returns the number of edges in the graph.
49    fn num_edges(&self) -> usize;
50
51    /// Returns all edges as a list of (u, v) pairs.
52    ///
53    /// For undirected graphs, each edge appears once with u < v.
54    fn edges(&self) -> Vec<(usize, usize)>;
55
56    /// Checks if an edge exists between vertices u and v.
57    fn has_edge(&self, u: usize, v: usize) -> bool;
58
59    /// Returns all neighbors of vertex v.
60    fn neighbors(&self, v: usize) -> Vec<usize>;
61
62    /// Returns the degree of vertex v (number of neighbors).
63    fn degree(&self, v: usize) -> usize {
64        self.neighbors(v).len()
65    }
66
67    /// Returns true if the graph has no vertices.
68    fn is_empty(&self) -> bool {
69        self.num_vertices() == 0
70    }
71
72    /// Iterates over all edges, calling a closure for each.
73    ///
74    /// This can be more efficient than `edges()` when you don't need to collect.
75    fn for_each_edge<F>(&self, mut f: F)
76    where
77        F: FnMut(usize, usize),
78    {
79        for (u, v) in self.edges() {
80            f(u, v);
81        }
82    }
83}
84
85/// Trait for casting a graph to a supertype in the graph hierarchy.
86///
87/// When `A: GraphCast<B>`, graph `A` can be losslessly converted to graph `B`
88/// by extracting the adjacency structure. This enables natural-edge reductions
89/// where a problem on a specific graph type is solved by treating it as a more
90/// general graph.
91pub trait GraphCast<Target: Graph>: Graph {
92    /// Convert this graph to the target graph type.
93    fn cast_graph(&self) -> Target;
94}
95
96/// Any graph can be cast to a `SimpleGraph` by extracting vertices and edges.
97impl<G: Graph> GraphCast<SimpleGraph> for G {
98    fn cast_graph(&self) -> SimpleGraph {
99        SimpleGraph::new(self.num_vertices(), self.edges())
100    }
101}
102
103/// A simple unweighted undirected graph.
104///
105/// This is the default graph type for most problems. It wraps petgraph's
106/// `UnGraph` and implements the `Graph` trait.
107///
108/// # Example
109///
110/// ```
111/// use problemreductions::topology::SimpleGraph;
112/// use problemreductions::topology::Graph;
113///
114/// let graph = SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]);
115/// assert_eq!(graph.num_vertices(), 4);
116/// assert_eq!(graph.num_edges(), 3);
117/// assert!(graph.has_edge(0, 1));
118/// assert!(!graph.has_edge(0, 2));
119/// ```
120#[derive(Debug, Clone, Serialize, Deserialize)]
121pub struct SimpleGraph {
122    inner: UnGraph<(), ()>,
123}
124
125impl SimpleGraph {
126    /// Creates a new graph with the given vertices and edges.
127    ///
128    /// # Arguments
129    ///
130    /// * `num_vertices` - Number of vertices in the graph
131    /// * `edges` - List of edges as (u, v) pairs
132    ///
133    /// # Panics
134    ///
135    /// Panics if any edge references a vertex index >= num_vertices.
136    pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self {
137        let mut inner = UnGraph::new_undirected();
138        for _ in 0..num_vertices {
139            inner.add_node(());
140        }
141        for (u, v) in edges {
142            assert!(
143                u < num_vertices && v < num_vertices,
144                "edge ({}, {}) references vertex >= num_vertices ({})",
145                u,
146                v,
147                num_vertices
148            );
149            inner.add_edge(NodeIndex::new(u), NodeIndex::new(v), ());
150        }
151        Self { inner }
152    }
153
154    /// Creates an empty graph with the given number of vertices.
155    pub fn empty(num_vertices: usize) -> Self {
156        Self::new(num_vertices, vec![])
157    }
158
159    /// Creates a complete graph (all vertices connected).
160    pub fn complete(num_vertices: usize) -> Self {
161        let mut edges = Vec::new();
162        for i in 0..num_vertices {
163            for j in (i + 1)..num_vertices {
164                edges.push((i, j));
165            }
166        }
167        Self::new(num_vertices, edges)
168    }
169
170    /// Creates a path graph (0-1-2-...-n).
171    pub fn path(num_vertices: usize) -> Self {
172        let edges: Vec<_> = (0..num_vertices.saturating_sub(1))
173            .map(|i| (i, i + 1))
174            .collect();
175        Self::new(num_vertices, edges)
176    }
177
178    /// Creates a cycle graph (0-1-2-...-n-0).
179    pub fn cycle(num_vertices: usize) -> Self {
180        if num_vertices < 3 {
181            return Self::path(num_vertices);
182        }
183        let mut edges: Vec<_> = (0..num_vertices - 1).map(|i| (i, i + 1)).collect();
184        edges.push((num_vertices - 1, 0));
185        Self::new(num_vertices, edges)
186    }
187
188    /// Creates a star graph (vertex 0 connected to all others).
189    pub fn star(num_vertices: usize) -> Self {
190        let edges: Vec<_> = (1..num_vertices).map(|i| (0, i)).collect();
191        Self::new(num_vertices, edges)
192    }
193
194    /// Creates a grid graph with the given dimensions.
195    ///
196    /// Vertices are numbered row by row: vertex `r * cols + c` is at row `r`, column `c`.
197    pub fn grid(rows: usize, cols: usize) -> Self {
198        let num_vertices = rows * cols;
199        let mut edges = Vec::new();
200
201        for r in 0..rows {
202            for c in 0..cols {
203                let v = r * cols + c;
204                // Right neighbor
205                if c + 1 < cols {
206                    edges.push((v, v + 1));
207                }
208                // Down neighbor
209                if r + 1 < rows {
210                    edges.push((v, v + cols));
211                }
212            }
213        }
214
215        Self::new(num_vertices, edges)
216    }
217}
218
219impl Graph for SimpleGraph {
220    const NAME: &'static str = "SimpleGraph";
221
222    fn num_vertices(&self) -> usize {
223        self.inner.node_count()
224    }
225
226    fn num_edges(&self) -> usize {
227        self.inner.edge_count()
228    }
229
230    fn edges(&self) -> Vec<(usize, usize)> {
231        self.inner
232            .edge_references()
233            .map(|e| (e.source().index(), e.target().index()))
234            .collect()
235    }
236
237    fn has_edge(&self, u: usize, v: usize) -> bool {
238        self.inner
239            .find_edge(NodeIndex::new(u), NodeIndex::new(v))
240            .is_some()
241    }
242
243    fn neighbors(&self, v: usize) -> Vec<usize> {
244        self.inner
245            .neighbors(NodeIndex::new(v))
246            .map(|n| n.index())
247            .collect()
248    }
249}
250
251impl PartialEq for SimpleGraph {
252    fn eq(&self, other: &Self) -> bool {
253        if self.num_vertices() != other.num_vertices() {
254            return false;
255        }
256        if self.num_edges() != other.num_edges() {
257            return false;
258        }
259        // Check all edges exist in both
260        let mut self_edges: Vec<_> = self.edges();
261        let mut other_edges: Vec<_> = other.edges();
262        // Normalize edge order
263        for e in &mut self_edges {
264            if e.0 > e.1 {
265                *e = (e.1, e.0);
266            }
267        }
268        for e in &mut other_edges {
269            if e.0 > e.1 {
270                *e = (e.1, e.0);
271            }
272        }
273        self_edges.sort();
274        other_edges.sort();
275        self_edges == other_edges
276    }
277}
278
279impl Eq for SimpleGraph {}
280
281use crate::impl_variant_param;
282impl_variant_param!(SimpleGraph, "graph");
283
284#[cfg(test)]
285#[path = "../unit_tests/topology/graph.rs"]
286mod tests;