oxicuda_graphalg/shortest_path/
transitive_closure.rs1use std::collections::VecDeque;
14
15use crate::error::{GraphalgError, GraphalgResult};
16use crate::repr::adjacency_list::AdjacencyList;
17
18#[derive(Debug, Clone, PartialEq, Eq)]
22pub struct TransitiveClosure {
23 pub n: usize,
25 reach: Vec<bool>,
27 reflexive: bool,
29}
30
31impl TransitiveClosure {
32 pub fn reachable(&self, u: usize, v: usize) -> GraphalgResult<bool> {
35 if u >= self.n {
36 return Err(GraphalgError::IndexOutOfBounds {
37 index: u,
38 len: self.n,
39 });
40 }
41 if v >= self.n {
42 return Err(GraphalgError::IndexOutOfBounds {
43 index: v,
44 len: self.n,
45 });
46 }
47 Ok(self.reach[u * self.n + v])
48 }
49
50 pub fn matrix(&self) -> &[bool] {
52 &self.reach
53 }
54
55 pub fn is_reflexive(&self) -> bool {
57 self.reflexive
58 }
59}
60
61pub fn transitive_closure(
67 graph: &AdjacencyList,
68 reflexive: bool,
69) -> GraphalgResult<TransitiveClosure> {
70 let n = graph.n;
71 let mut reach = vec![false; n * n];
72
73 for u in 0..n {
75 for &v in graph.neighbors(u)? {
76 reach[u * n + v] = true;
77 }
78 }
79 if reflexive {
80 for v in 0..n {
81 reach[v * n + v] = true;
82 }
83 }
84
85 for k in 0..n {
87 for i in 0..n {
89 if reach[i * n + k] {
90 let base_i = i * n;
91 let base_k = k * n;
92 for j in 0..n {
93 if reach[base_k + j] {
94 reach[base_i + j] = true;
95 }
96 }
97 }
98 }
99 }
100
101 Ok(TransitiveClosure {
102 n,
103 reach,
104 reflexive,
105 })
106}
107
108pub fn transitive_closure_bfs(
116 graph: &AdjacencyList,
117 reflexive: bool,
118) -> GraphalgResult<TransitiveClosure> {
119 let n = graph.n;
120 let mut reach = vec![false; n * n];
121 let mut visited = vec![false; n];
122 let mut queue: VecDeque<usize> = VecDeque::new();
123
124 for s in 0..n {
125 for v in visited.iter_mut() {
127 *v = false;
128 }
129 queue.clear();
130
131 for &v in graph.neighbors(s)? {
135 if !visited[v] {
136 visited[v] = true;
137 reach[s * n + v] = true;
138 queue.push_back(v);
139 }
140 }
141 while let Some(u) = queue.pop_front() {
142 for &w in graph.neighbors(u)? {
143 if !visited[w] {
144 visited[w] = true;
145 reach[s * n + w] = true;
146 queue.push_back(w);
147 }
148 }
149 }
150
151 if reflexive {
152 reach[s * n + s] = true;
153 }
154 }
155
156 Ok(TransitiveClosure {
157 n,
158 reach,
159 reflexive,
160 })
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 fn chain(n: usize) -> AdjacencyList {
169 let mut g = AdjacencyList::new(n);
170 for i in 0..n.saturating_sub(1) {
171 g.add_edge(i, i + 1).expect("edge");
172 }
173 g
174 }
175
176 #[test]
177 fn chain_upper_triangular_irreflexive() {
178 let g = chain(4);
179 let tc = transitive_closure(&g, false).expect("tc");
180 for i in 0..4 {
181 for j in 0..4 {
182 let expected = i < j; assert_eq!(tc.reachable(i, j).expect("reach"), expected, "({i},{j})");
184 }
185 }
186 }
187
188 #[test]
189 fn chain_upper_triangular_reflexive() {
190 let g = chain(4);
191 let tc = transitive_closure(&g, true).expect("tc");
192 for i in 0..4 {
193 for j in 0..4 {
194 let expected = i <= j; assert_eq!(tc.reachable(i, j).expect("reach"), expected, "({i},{j})");
196 }
197 }
198 }
199
200 #[test]
201 fn small_dag_full_matrix() {
202 let mut g = AdjacencyList::new(4);
204 g.add_edge(0, 1).expect("edge");
205 g.add_edge(0, 2).expect("edge");
206 g.add_edge(1, 3).expect("edge");
207 g.add_edge(2, 3).expect("edge");
208 let tc = transitive_closure(&g, false).expect("tc");
209 #[rustfmt::skip]
211 let expected: Vec<bool> = vec![
212 false, true, true, true,
213 false, false, false, true,
214 false, false, false, true,
215 false, false, false, false,
216 ];
217 assert_eq!(tc.matrix(), expected.as_slice());
218 }
219
220 #[test]
221 fn fw_and_bfs_agree_on_several_graphs() {
222 let mut graphs: Vec<AdjacencyList> = Vec::new();
225
226 graphs.push(chain(6));
228
229 let mut g2 = AdjacencyList::new(5);
231 for (u, v) in [(0, 1), (0, 2), (1, 3), (2, 3), (3, 4), (0, 4)] {
232 g2.add_edge(u, v).expect("edge");
233 }
234 graphs.push(g2);
235
236 let mut g3 = AdjacencyList::new(4);
238 for (u, v) in [(0, 1), (1, 2), (2, 0), (2, 3)] {
239 g3.add_edge(u, v).expect("edge");
240 }
241 graphs.push(g3);
242
243 let mut g4 = AdjacencyList::new(5);
245 for (u, v) in [(0, 1), (2, 3), (3, 4)] {
246 g4.add_edge(u, v).expect("edge");
247 }
248 graphs.push(g4);
249
250 let mut g5 = AdjacencyList::new(8);
252 let mut state = 1u64;
253 for _ in 0..20 {
254 state = state.wrapping_mul(6364136223846793005).wrapping_add(1);
255 let u = (state >> 33) as usize % 8;
256 state = state.wrapping_mul(6364136223846793005).wrapping_add(1);
257 let v = (state >> 33) as usize % 8;
258 g5.add_edge(u, v).expect("edge");
259 }
260 graphs.push(g5);
261
262 for g in &graphs {
263 for &reflexive in &[false, true] {
264 let a = transitive_closure(g, reflexive).expect("fw");
265 let b = transitive_closure_bfs(g, reflexive).expect("bfs");
266 assert_eq!(a.matrix(), b.matrix(), "mismatch (reflexive={reflexive})");
267 }
268 }
269 }
270
271 #[test]
272 fn cycle_everyone_reaches_everyone() {
273 let mut g = AdjacencyList::new(3);
275 g.add_edge(0, 1).expect("edge");
276 g.add_edge(1, 2).expect("edge");
277 g.add_edge(2, 0).expect("edge");
278 let tc = transitive_closure(&g, false).expect("tc");
279 for i in 0..3 {
280 for j in 0..3 {
281 assert!(tc.reachable(i, j).expect("reach"), "({i},{j})");
282 }
283 }
284 for v in 0..3 {
286 assert!(tc.reachable(v, v).expect("reach"));
287 }
288 let tb = transitive_closure_bfs(&g, false).expect("bfs");
290 for v in 0..3 {
291 assert!(tb.reachable(v, v).expect("reach"));
292 }
293 }
294
295 #[test]
296 fn reflexive_flag_respected_with_isolated_vertex() {
297 let mut g = AdjacencyList::new(3);
299 g.add_edge(0, 1).expect("edge");
300
301 let irreflexive = transitive_closure(&g, false).expect("tc");
302 for v in 0..3 {
304 assert!(!irreflexive.reachable(v, v).expect("reach"), "diag {v}");
305 }
306
307 let reflexive = transitive_closure(&g, true).expect("tc");
308 for v in 0..3 {
309 assert!(reflexive.reachable(v, v).expect("reach"), "diag {v}");
310 }
311 assert!(!reflexive.reachable(2, 0).expect("reach"));
313 assert!(!reflexive.reachable(2, 1).expect("reach"));
314 }
315
316 #[test]
317 fn reachable_bounds_check() {
318 let g = chain(3);
319 let tc = transitive_closure(&g, false).expect("tc");
320 assert!(matches!(
321 tc.reachable(3, 0),
322 Err(GraphalgError::IndexOutOfBounds { index: 3, len: 3 })
323 ));
324 assert!(matches!(
325 tc.reachable(0, 5),
326 Err(GraphalgError::IndexOutOfBounds { index: 5, len: 3 })
327 ));
328 }
329
330 #[test]
331 fn empty_graph_closure() {
332 let g = AdjacencyList::new(0);
333 let tc = transitive_closure(&g, true).expect("tc");
334 assert_eq!(tc.n, 0);
335 assert!(tc.matrix().is_empty());
336 let tb = transitive_closure_bfs(&g, true).expect("bfs");
337 assert!(tb.matrix().is_empty());
338 }
339
340 #[test]
341 fn is_reflexive_reported() {
342 let g = chain(2);
343 assert!(transitive_closure(&g, true).expect("tc").is_reflexive());
344 assert!(!transitive_closure(&g, false).expect("tc").is_reflexive());
345 }
346}