legume_numeric/matrix/
graph.rs1use std::collections::VecDeque;
9
10pub trait WeightedGraph {
17 fn num_nodes(&self) -> usize;
18 fn num_edges(&self) -> usize;
19 fn neighbors_with_weight<'a>(
20 &'a self,
21 node: usize,
22 ) -> Box<dyn Iterator<Item = (usize, f32)> + 'a>;
23
24 fn degree(&self, node: usize) -> usize {
25 self.neighbors_with_weight(node).count()
26 }
27
28 fn weighted_degree(&self, node: usize) -> f32 {
29 self.neighbors_with_weight(node).map(|(_, w)| w).sum()
30 }
31}
32
33pub struct AdjListGraph {
37 adj: Vec<Vec<(usize, f32)>>,
38 num_edges: usize,
39}
40
41impl AdjListGraph {
42 pub fn from_edges(n_nodes: usize, edges: &[(usize, usize, f32)]) -> Self {
45 let mut adj = vec![Vec::new(); n_nodes];
46 let mut num_edges = 0usize;
47 for &(u, v, w) in edges {
48 if u == v {
49 continue;
50 }
51 adj[u].push((v, w));
52 adj[v].push((u, w));
53 num_edges += 1;
54 }
55 Self { adj, num_edges }
56 }
57
58 pub fn from_unweighted_edges(n_nodes: usize, edges: &[(usize, usize)]) -> Self {
60 let weighted: Vec<(usize, usize, f32)> = edges.iter().map(|&(u, v)| (u, v, 1.0)).collect();
61 Self::from_edges(n_nodes, &weighted)
62 }
63}
64
65impl WeightedGraph for AdjListGraph {
66 fn num_nodes(&self) -> usize {
67 self.adj.len()
68 }
69
70 fn num_edges(&self) -> usize {
71 self.num_edges
72 }
73
74 fn neighbors_with_weight<'a>(
75 &'a self,
76 node: usize,
77 ) -> Box<dyn Iterator<Item = (usize, f32)> + 'a> {
78 Box::new(self.adj[node].iter().copied())
79 }
80}
81
82pub fn connected_components<G: WeightedGraph + ?Sized>(g: &G) -> Vec<usize> {
84 let n = g.num_nodes();
85 let mut label = vec![usize::MAX; n];
86 let mut queue: VecDeque<usize> = VecDeque::new();
87 let mut next_id = 0usize;
88
89 for root in 0..n {
90 if label[root] != usize::MAX {
91 continue;
92 }
93 label[root] = next_id;
94 queue.push_back(root);
95 while let Some(u) = queue.pop_front() {
96 for (v, _) in g.neighbors_with_weight(u) {
97 if label[v] == usize::MAX {
98 label[v] = next_id;
99 queue.push_back(v);
100 }
101 }
102 }
103 next_id += 1;
104 }
105 label
106}
107
108pub fn num_connected_components<G: WeightedGraph + ?Sized>(g: &G) -> usize {
110 connected_components(g)
111 .into_iter()
112 .max()
113 .map(|m| m + 1)
114 .unwrap_or(0)
115}
116
117pub fn weighted_degrees<G: WeightedGraph + ?Sized>(g: &G) -> Vec<f32> {
119 (0..g.num_nodes()).map(|i| g.weighted_degree(i)).collect()
120}
121
122pub fn total_edge_weight<G: WeightedGraph + ?Sized>(g: &G) -> f32 {
125 let mut s = 0f32;
126 for u in 0..g.num_nodes() {
127 for (v, w) in g.neighbors_with_weight(u) {
128 if u < v {
129 s += w;
130 }
131 }
132 }
133 s
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 struct TinyGraph {
141 n: usize,
142 adj: Vec<Vec<(usize, f32)>>,
143 }
144
145 impl WeightedGraph for TinyGraph {
146 fn num_nodes(&self) -> usize {
147 self.n
148 }
149 fn num_edges(&self) -> usize {
150 self.adj.iter().map(|v| v.len()).sum::<usize>() / 2
151 }
152 fn neighbors_with_weight<'a>(
153 &'a self,
154 node: usize,
155 ) -> Box<dyn Iterator<Item = (usize, f32)> + 'a> {
156 Box::new(self.adj[node].iter().copied())
157 }
158 }
159
160 fn two_components() -> TinyGraph {
161 let mut adj = vec![vec![]; 6];
163 adj[0].push((1, 1.0));
164 adj[1].push((0, 1.0));
165 adj[2].push((3, 2.0));
166 adj[3].push((2, 2.0));
167 adj[3].push((4, 3.0));
168 adj[4].push((3, 3.0));
169 TinyGraph { n: 6, adj }
170 }
171
172 #[test]
173 fn connected_components_identifies_three_groups() {
174 let g = two_components();
175 let label = connected_components(&g);
176 assert_eq!(label[0], label[1]);
177 assert_eq!(label[2], label[3]);
178 assert_eq!(label[3], label[4]);
179 assert_ne!(label[0], label[2]);
180 assert_ne!(label[0], label[5]);
181 assert_ne!(label[2], label[5]);
182 assert_eq!(num_connected_components(&g), 3);
183 }
184
185 #[test]
186 fn weighted_degree_and_total_weight() {
187 let g = two_components();
188 assert_eq!(g.degree(3), 2);
189 assert_eq!(g.weighted_degree(3), 5.0);
190 assert_eq!(weighted_degrees(&g), vec![1.0, 1.0, 2.0, 5.0, 3.0, 0.0]);
191 assert_eq!(total_edge_weight(&g), 6.0);
192 }
193}