scirs2_graph/base/
graph.rs

1//! Basic undirected graph implementation
2
3use ndarray::{Array1, Array2};
4use petgraph::graph::{Graph as PetGraph, NodeIndex};
5use petgraph::visit::EdgeRef;
6use petgraph::Undirected;
7use std::collections::HashMap;
8
9use super::types::{Edge, EdgeWeight, Node};
10use crate::error::{GraphError, Result};
11pub use petgraph::graph::IndexType;
12
13/// An undirected graph structure
14pub struct Graph<N: Node, E: EdgeWeight, Ix: IndexType = u32> {
15    graph: PetGraph<N, E, Undirected, Ix>,
16    node_indices: HashMap<N, NodeIndex<Ix>>,
17}
18
19impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> Default for Graph<N, E, Ix> {
20    fn default() -> Self {
21        Self::new()
22    }
23}
24
25impl<N: Node + std::fmt::Debug, E: EdgeWeight, Ix: IndexType> Graph<N, E, Ix> {
26    /// Create a new empty undirected graph
27    pub fn new() -> Self {
28        Graph {
29            graph: PetGraph::default(),
30            node_indices: HashMap::new(),
31        }
32    }
33
34    /// Add a node to the graph
35    pub fn add_node(&mut self, node: N) -> NodeIndex<Ix> {
36        if let Some(idx) = self.node_indices.get(&node) {
37            return *idx;
38        }
39
40        let idx = self.graph.add_node(node.clone());
41        self.node_indices.insert(node, idx);
42        idx
43    }
44
45    /// Add an edge between two nodes with a given weight
46    pub fn add_edge(&mut self, source: N, target: N, weight: E) -> Result<()> {
47        let source_idx = self.add_node(source);
48        let target_idx = self.add_node(target);
49
50        self.graph.add_edge(source_idx, target_idx, weight);
51        Ok(())
52    }
53
54    /// Get the adjacency matrix representation of the graph
55    pub fn adjacency_matrix(&self) -> Array2<E>
56    where
57        E: num_traits::Zero + num_traits::One + Copy,
58    {
59        let n = self.graph.node_count();
60        let mut adj_mat = Array2::zeros((n, n));
61
62        for edge in self.graph.edge_references() {
63            let (src, tgt) = (edge.source().index(), edge.target().index());
64            adj_mat[[src, tgt]] = *edge.weight();
65            adj_mat[[tgt, src]] = *edge.weight(); // Undirected graph
66        }
67
68        adj_mat
69    }
70
71    /// Get the degree vector of the graph
72    pub fn degree_vector(&self) -> Array1<usize> {
73        let n = self.graph.node_count();
74        let mut degrees = Array1::zeros(n);
75
76        for (idx, node) in self.graph.node_indices().enumerate() {
77            degrees[idx] = self.graph.neighbors(node).count();
78        }
79
80        degrees
81    }
82
83    /// Get all nodes in the graph
84    pub fn nodes(&self) -> Vec<&N> {
85        self.graph.node_weights().collect()
86    }
87
88    /// Get all edges in the graph
89    pub fn edges(&self) -> Vec<Edge<N, E>>
90    where
91        N: Clone,
92        E: Clone,
93    {
94        let mut result = Vec::new();
95        let node_map: HashMap<NodeIndex<Ix>, &N> = self
96            .graph
97            .node_indices()
98            .map(|idx| (idx, self.graph.node_weight(idx).unwrap()))
99            .collect();
100
101        for edge in self.graph.edge_references() {
102            let source = node_map[&edge.source()].clone();
103            let target = node_map[&edge.target()].clone();
104            let weight = edge.weight().clone();
105
106            result.push(Edge {
107                source,
108                target,
109                weight,
110            });
111        }
112
113        result
114    }
115
116    /// Number of nodes in the graph
117    pub fn node_count(&self) -> usize {
118        self.graph.node_count()
119    }
120
121    /// Number of edges in the graph
122    pub fn edge_count(&self) -> usize {
123        self.graph.edge_count()
124    }
125
126    /// Check if the graph has a node
127    pub fn has_node(&self, node: &N) -> bool {
128        self.node_indices.contains_key(node)
129    }
130
131    /// Get neighbors of a node
132    pub fn neighbors(&self, node: &N) -> Result<Vec<N>>
133    where
134        N: Clone,
135    {
136        if let Some(&idx) = self.node_indices.get(node) {
137            let neighbors: Vec<N> = self
138                .graph
139                .neighbors(idx)
140                .map(|neighbor_idx| self.graph[neighbor_idx].clone())
141                .collect();
142            Ok(neighbors)
143        } else {
144            Err(GraphError::node_not_found_with_context(
145                format!("{node:?}"),
146                self.node_count(),
147                "neighbors",
148            ))
149        }
150    }
151
152    /// Check if an edge exists between two nodes
153    pub fn has_edge(&self, source: &N, target: &N) -> bool {
154        if let (Some(&src_idx), Some(&tgt_idx)) =
155            (self.node_indices.get(source), self.node_indices.get(target))
156        {
157            self.graph.contains_edge(src_idx, tgt_idx)
158        } else {
159            false
160        }
161    }
162
163    /// Get the weight of an edge between two nodes
164    pub fn edge_weight(&self, source: &N, target: &N) -> Result<E>
165    where
166        E: Clone,
167    {
168        if let (Some(&src_idx), Some(&tgt_idx)) =
169            (self.node_indices.get(source), self.node_indices.get(target))
170        {
171            if let Some(edge_ref) = self.graph.find_edge(src_idx, tgt_idx) {
172                Ok(self.graph[edge_ref].clone())
173            } else {
174                Err(GraphError::edge_not_found("unknown", "unknown"))
175            }
176        } else {
177            Err(GraphError::node_not_found("unknown node"))
178        }
179    }
180
181    /// Get the degree of a node (total number of incident edges)
182    pub fn degree(&self, node: &N) -> usize {
183        if let Some(idx) = self.node_indices.get(node) {
184            self.graph.neighbors(*idx).count()
185        } else {
186            0
187        }
188    }
189
190    /// Check if the graph contains a specific node
191    pub fn contains_node(&self, node: &N) -> bool {
192        self.node_indices.contains_key(node)
193    }
194
195    /// Get the node index for a specific node
196    pub fn node_index(&self, node: &N) -> Option<NodeIndex<Ix>> {
197        self.node_indices.get(node).copied()
198    }
199
200    /// Get the internal petgraph structure for more advanced operations
201    pub fn inner(&self) -> &PetGraph<N, E, Undirected, Ix> {
202        &self.graph
203    }
204
205    /// Get a mutable reference to the internal petgraph structure
206    pub fn inner_mut(&mut self) -> &mut PetGraph<N, E, Undirected, Ix> {
207        &mut self.graph
208    }
209}