Skip to main content

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/// A simple unweighted undirected graph.
86///
87/// This is the default graph type for most problems. It wraps petgraph's
88/// `UnGraph` and implements the `Graph` trait.
89///
90/// # Example
91///
92/// ```
93/// use problemreductions::topology::SimpleGraph;
94/// use problemreductions::topology::Graph;
95///
96/// let graph = SimpleGraph::new(4, vec![(0, 1), (1, 2), (2, 3)]);
97/// assert_eq!(graph.num_vertices(), 4);
98/// assert_eq!(graph.num_edges(), 3);
99/// assert!(graph.has_edge(0, 1));
100/// assert!(!graph.has_edge(0, 2));
101/// ```
102#[derive(Debug, Clone)]
103pub struct SimpleGraph {
104    inner: UnGraph<(), ()>,
105}
106
107impl SimpleGraph {
108    /// Creates a new graph with the given vertices and edges.
109    ///
110    /// # Arguments
111    ///
112    /// * `num_vertices` - Number of vertices in the graph
113    /// * `edges` - List of edges as (u, v) pairs
114    ///
115    /// # Panics
116    ///
117    /// Panics if any edge references a vertex index >= num_vertices.
118    pub fn new(num_vertices: usize, edges: Vec<(usize, usize)>) -> Self {
119        let mut inner = UnGraph::new_undirected();
120        for _ in 0..num_vertices {
121            inner.add_node(());
122        }
123        for (u, v) in edges {
124            assert!(
125                u < num_vertices && v < num_vertices,
126                "edge ({}, {}) references vertex >= num_vertices ({})",
127                u,
128                v,
129                num_vertices
130            );
131            inner.add_edge(NodeIndex::new(u), NodeIndex::new(v), ());
132        }
133        Self { inner }
134    }
135
136    /// Creates an empty graph with the given number of vertices.
137    pub fn empty(num_vertices: usize) -> Self {
138        Self::new(num_vertices, vec![])
139    }
140
141    /// Creates a complete graph (all vertices connected).
142    pub fn complete(num_vertices: usize) -> Self {
143        let mut edges = Vec::new();
144        for i in 0..num_vertices {
145            for j in (i + 1)..num_vertices {
146                edges.push((i, j));
147            }
148        }
149        Self::new(num_vertices, edges)
150    }
151
152    /// Creates a path graph (0-1-2-...-n).
153    pub fn path(num_vertices: usize) -> Self {
154        let edges: Vec<_> = (0..num_vertices.saturating_sub(1))
155            .map(|i| (i, i + 1))
156            .collect();
157        Self::new(num_vertices, edges)
158    }
159
160    /// Creates a cycle graph (0-1-2-...-n-0).
161    pub fn cycle(num_vertices: usize) -> Self {
162        if num_vertices < 3 {
163            return Self::path(num_vertices);
164        }
165        let mut edges: Vec<_> = (0..num_vertices - 1).map(|i| (i, i + 1)).collect();
166        edges.push((num_vertices - 1, 0));
167        Self::new(num_vertices, edges)
168    }
169
170    /// Creates a star graph (vertex 0 connected to all others).
171    pub fn star(num_vertices: usize) -> Self {
172        let edges: Vec<_> = (1..num_vertices).map(|i| (0, i)).collect();
173        Self::new(num_vertices, edges)
174    }
175
176    /// Creates a grid graph with the given dimensions.
177    ///
178    /// Vertices are numbered row by row: vertex `r * cols + c` is at row `r`, column `c`.
179    pub fn grid(rows: usize, cols: usize) -> Self {
180        let num_vertices = rows * cols;
181        let mut edges = Vec::new();
182
183        for r in 0..rows {
184            for c in 0..cols {
185                let v = r * cols + c;
186                // Right neighbor
187                if c + 1 < cols {
188                    edges.push((v, v + 1));
189                }
190                // Down neighbor
191                if r + 1 < rows {
192                    edges.push((v, v + cols));
193                }
194            }
195        }
196
197        Self::new(num_vertices, edges)
198    }
199}
200
201impl Graph for SimpleGraph {
202    const NAME: &'static str = "SimpleGraph";
203
204    fn num_vertices(&self) -> usize {
205        self.inner.node_count()
206    }
207
208    fn num_edges(&self) -> usize {
209        self.inner.edge_count()
210    }
211
212    fn edges(&self) -> Vec<(usize, usize)> {
213        self.inner
214            .edge_references()
215            .map(|e| (e.source().index(), e.target().index()))
216            .collect()
217    }
218
219    fn has_edge(&self, u: usize, v: usize) -> bool {
220        self.inner
221            .find_edge(NodeIndex::new(u), NodeIndex::new(v))
222            .is_some()
223    }
224
225    fn neighbors(&self, v: usize) -> Vec<usize> {
226        self.inner
227            .neighbors(NodeIndex::new(v))
228            .map(|n| n.index())
229            .collect()
230    }
231}
232
233impl PartialEq for SimpleGraph {
234    fn eq(&self, other: &Self) -> bool {
235        if self.num_vertices() != other.num_vertices() {
236            return false;
237        }
238        if self.num_edges() != other.num_edges() {
239            return false;
240        }
241        // Check all edges exist in both
242        let mut self_edges: Vec<_> = self.edges();
243        let mut other_edges: Vec<_> = other.edges();
244        // Normalize edge order
245        for e in &mut self_edges {
246            if e.0 > e.1 {
247                *e = (e.1, e.0);
248            }
249        }
250        for e in &mut other_edges {
251            if e.0 > e.1 {
252                *e = (e.1, e.0);
253            }
254        }
255        self_edges.sort();
256        other_edges.sort();
257        self_edges == other_edges
258    }
259}
260
261impl Eq for SimpleGraph {}
262
263impl Serialize for SimpleGraph {
264    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
265        use serde::ser::SerializeStruct;
266        let mut state = serializer.serialize_struct("SimpleGraph", 2)?;
267        state.serialize_field("num_vertices", &self.num_vertices())?;
268        let edges: Vec<(usize, usize)> = self.edges();
269        state.serialize_field("edges", &edges)?;
270        state.end()
271    }
272}
273
274impl<'de> Deserialize<'de> for SimpleGraph {
275    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
276        #[derive(Deserialize)]
277        struct GraphData {
278            num_vertices: usize,
279            edges: Vec<(usize, usize)>,
280        }
281        let data = GraphData::deserialize(deserializer)?;
282        Ok(SimpleGraph::new(data.num_vertices, data.edges))
283    }
284}
285
286use crate::impl_variant_param;
287impl_variant_param!(SimpleGraph, "graph");
288
289#[cfg(test)]
290#[path = "../unit_tests/topology/graph.rs"]
291mod tests;