Skip to main content

lau_network_science/
graph.rs

1use serde::{Deserialize, Serialize};
2use std::collections::{HashMap, HashSet};
3
4/// An undirected graph using adjacency list representation.
5#[derive(Clone, Debug, Serialize, Deserialize)]
6pub struct Graph {
7    /// Adjacency list: node -> set of neighbors
8    adj: HashMap<usize, HashSet<usize>>,
9    n: usize,
10    m: usize,
11    directed: bool,
12}
13
14impl Graph {
15    pub fn new() -> Self {
16        Graph {
17            adj: HashMap::new(),
18            n: 0,
19            m: 0,
20            directed: false,
21        }
22    }
23
24    /// Create a graph with `n` nodes (0..n) and no edges.
25    pub fn with_n_nodes(n: usize) -> Self {
26        let mut adj = HashMap::new();
27        for i in 0..n {
28            adj.insert(i, HashSet::new());
29        }
30        Graph { adj, n, m: 0, directed: false }
31    }
32
33    /// Ensure node exists in the graph.
34    pub fn add_node(&mut self, node: usize) {
35        if !self.adj.contains_key(&node) {
36            self.adj.insert(node, HashSet::new());
37            self.n = self.n.max(node + 1);
38        }
39    }
40
41    /// Add an undirected edge between u and v.
42    pub fn add_edge(&mut self, u: usize, v: usize) {
43        if u == v { return; } // no self-loops by default
44        self.add_node(u);
45        self.add_node(v);
46        if !self.adj[&u].contains(&v) {
47            self.adj.get_mut(&u).unwrap().insert(v);
48            self.adj.get_mut(&v).unwrap().insert(u);
49            self.m += 1;
50        }
51    }
52
53    /// Remove an undirected edge.
54    pub fn remove_edge(&mut self, u: usize, v: usize) {
55        if let Some(neighbors) = self.adj.get_mut(&u) {
56            if neighbors.remove(&v) {
57                self.m -= 1;
58            }
59        }
60        if let Some(neighbors) = self.adj.get_mut(&v) {
61            neighbors.remove(&u);
62        }
63    }
64
65    /// Remove a node and all its incident edges.
66    pub fn remove_node(&mut self, node: usize) {
67        if let Some(neighbors) = self.adj.remove(&node) {
68            for &nb in &neighbors {
69                if let Some(nbs) = self.adj.get_mut(&nb) {
70                    nbs.remove(&node);
71                    self.m -= 1;
72                }
73            }
74        }
75    }
76
77    pub fn node_count(&self) -> usize {
78        self.adj.len()
79    }
80
81    pub fn edge_count(&self) -> usize {
82        self.m
83    }
84
85    pub fn nodes(&self) -> Vec<usize> {
86        self.adj.keys().copied().collect()
87    }
88
89    pub fn has_node(&self, node: usize) -> bool {
90        self.adj.contains_key(&node)
91    }
92
93    pub fn has_edge(&self, u: usize, v: usize) -> bool {
94        self.adj.get(&u).map_or(false, |s| s.contains(&v))
95    }
96
97    pub fn neighbors(&self, node: usize) -> Vec<usize> {
98        self.adj.get(&node).map_or(vec![], |s| s.iter().copied().collect())
99    }
100
101    pub fn degree(&self, node: usize) -> usize {
102        self.adj.get(&node).map_or(0, |s| s.len())
103    }
104
105    pub fn degrees(&self) -> Vec<(usize, usize)> {
106        self.adj.keys().map(|&n| (n, self.degree(n))).collect()
107    }
108
109    /// Get all edges as (u, v) pairs with u < v.
110    pub fn edges(&self) -> Vec<(usize, usize)> {
111        let mut edges = Vec::new();
112        let mut seen = HashSet::new();
113        for (&u, nbs) in &self.adj {
114            for &v in nbs {
115                let (a, b) = if u < v { (u, v) } else { (v, u) };
116                if seen.insert((a, b)) {
117                    edges.push((a, b));
118                }
119            }
120        }
121        edges
122    }
123
124    /// BFS shortest path lengths from `source` to all reachable nodes.
125    pub fn bfs_distances(&self, source: usize) -> HashMap<usize, usize> {
126        let mut dist = HashMap::new();
127        let mut queue = std::collections::VecDeque::new();
128        dist.insert(source, 0);
129        queue.push_back(source);
130        while let Some(u) = queue.pop_front() {
131            let d = dist[&u];
132            for &v in &self.adj[&u] {
133                if !dist.contains_key(&v) {
134                    dist.insert(v, d + 1);
135                    queue.push_back(v);
136                }
137            }
138        }
139        dist
140    }
141
142    /// Check if the graph is connected.
143    pub fn is_connected(&self) -> bool {
144        if self.adj.is_empty() { return true; }
145        let start = *self.adj.keys().next().unwrap();
146        let dist = self.bfs_distances(start);
147        dist.len() == self.adj.len()
148    }
149
150    /// Connected components.
151    pub fn connected_components(&self) -> Vec<Vec<usize>> {
152        let mut visited = HashSet::new();
153        let mut components = Vec::new();
154        for &node in self.adj.keys() {
155            if visited.contains(&node) { continue; }
156            let dist = self.bfs_distances(node);
157            let comp: Vec<usize> = dist.keys().copied().collect();
158            visited.extend(comp.iter().copied());
159            components.push(comp);
160        }
161        components
162    }
163
164    /// Get the adjacency as a HashMap (for external use).
165    pub fn adjacency(&self) -> &HashMap<usize, HashSet<usize>> {
166        &self.adj
167    }
168
169    /// Get the largest connected component as a new Graph.
170    pub fn largest_component(&self) -> Graph {
171        let components = self.connected_components();
172        let largest = components.into_iter().max_by_key(|c| c.len()).unwrap_or_default();
173        let node_set: HashSet<usize> = largest.into_iter().collect();
174        let mut g = Graph::new();
175        for &u in &node_set {
176            for &v in &self.adj[&u] {
177                if node_set.contains(&v) && u < v {
178                    g.add_edge(u, v);
179                }
180            }
181        }
182        g
183    }
184}
185
186/// A directed graph.
187#[derive(Clone, Debug, Serialize, Deserialize)]
188pub struct DirectedGraph {
189    /// out_edges[i] = set of nodes i points to
190    out_edges: HashMap<usize, HashSet<usize>>,
191    /// in_edges[i] = set of nodes pointing to i
192    in_edges: HashMap<usize, HashSet<usize>>,
193    n: usize,
194    m: usize,
195}
196
197impl DirectedGraph {
198    pub fn new() -> Self {
199        DirectedGraph {
200            out_edges: HashMap::new(),
201            in_edges: HashMap::new(),
202            n: 0,
203            m: 0,
204        }
205    }
206
207    pub fn with_n_nodes(n: usize) -> Self {
208        let mut out_edges = HashMap::new();
209        let mut in_edges = HashMap::new();
210        for i in 0..n {
211            out_edges.insert(i, HashSet::new());
212            in_edges.insert(i, HashSet::new());
213        }
214        DirectedGraph { out_edges, in_edges, n, m: 0 }
215    }
216
217    pub fn add_node(&mut self, node: usize) {
218        if !self.out_edges.contains_key(&node) {
219            self.out_edges.insert(node, HashSet::new());
220            self.in_edges.insert(node, HashSet::new());
221            self.n = self.n.max(node + 1);
222        }
223    }
224
225    pub fn add_edge(&mut self, from: usize, to: usize) {
226        if from == to { return; }
227        self.add_node(from);
228        self.add_node(to);
229        if !self.out_edges[&from].contains(&to) {
230            self.out_edges.get_mut(&from).unwrap().insert(to);
231            self.in_edges.get_mut(&to).unwrap().insert(from);
232            self.m += 1;
233        }
234    }
235
236    pub fn node_count(&self) -> usize { self.out_edges.len() }
237    pub fn edge_count(&self) -> usize { self.m }
238    pub fn out_degree(&self, node: usize) -> usize {
239        self.out_edges.get(&node).map_or(0, |s| s.len())
240    }
241    pub fn in_degree(&self, node: usize) -> usize {
242        self.in_edges.get(&node).map_or(0, |s| s.len())
243    }
244    pub fn successors(&self, node: usize) -> Vec<usize> {
245        self.out_edges.get(&node).map_or(vec![], |s| s.iter().copied().collect())
246    }
247    pub fn predecessors(&self, node: usize) -> Vec<usize> {
248        self.in_edges.get(&node).map_or(vec![], |s| s.iter().copied().collect())
249    }
250    pub fn nodes(&self) -> Vec<usize> {
251        self.out_edges.keys().copied().collect()
252    }
253    pub fn has_edge(&self, from: usize, to: usize) -> bool {
254        self.out_edges.get(&from).map_or(false, |s| s.contains(&to))
255    }
256    pub fn out_edges_map(&self) -> &HashMap<usize, HashSet<usize>> {
257        &self.out_edges
258    }
259
260    /// Convert to undirected Graph.
261    pub fn to_undirected(&self) -> Graph {
262        let mut g = Graph::new();
263        for (&from, tos) in &self.out_edges {
264            for &to in tos {
265                g.add_edge(from, to);
266            }
267        }
268        g
269    }
270}