weavatrix_graph/algo/
transitive.rs1use super::topological_sort_filtered;
2use crate::{EdgeEndpoints, IndexGraphView};
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct DagTransitive<Node> {
6 reduction: Vec<EdgeEndpoints<Node>>,
7 closure: Vec<EdgeEndpoints<Node>>,
8}
9
10impl<Node> DagTransitive<Node> {
11 #[must_use]
12 pub fn reduction_edges(&self) -> &[EdgeEndpoints<Node>] {
13 &self.reduction
14 }
15
16 #[must_use]
17 pub fn closure_edges(&self) -> &[EdgeEndpoints<Node>] {
18 &self.closure
19 }
20
21 #[must_use]
22 pub fn into_parts(self) -> (Vec<EdgeEndpoints<Node>>, Vec<EdgeEndpoints<Node>>) {
23 (self.reduction, self.closure)
24 }
25}
26
27#[must_use]
31pub fn dag_transitive_reduction_closure<G>(graph: &G) -> Option<DagTransitive<G::Node>>
32where
33 G: IndexGraphView,
34{
35 dag_transitive_reduction_closure_filtered(graph, |_| true)
36}
37
38#[must_use]
43pub fn dag_transitive_reduction_closure_filtered<G, F>(
44 graph: &G,
45 allows_edge: F,
46) -> Option<DagTransitive<G::Node>>
47where
48 G: IndexGraphView,
49 F: Fn(G::Edge) -> bool,
50{
51 let order = topological_sort_filtered(graph, &allows_edge)?;
52 let mut position = vec![None; graph.node_bound()];
53 for (index, &node) in order.iter().enumerate() {
54 position[G::node_slot(node)] = Some(index);
55 }
56 let mut successors = vec![Vec::new(); order.len()];
57 for (edge, endpoints) in graph.edge_references() {
58 if !allows_edge(edge) {
59 continue;
60 }
61 let source = position[G::node_slot(endpoints.source())]?;
62 let target = position[G::node_slot(endpoints.target())]?;
63 successors[source].push(target);
64 }
65 for targets in &mut successors {
66 targets.sort_unstable();
67 targets.dedup();
68 }
69
70 let words = order.len().div_ceil(u64::BITS as usize);
71 let mut reachable = vec![vec![0_u64; words]; order.len()];
72 let mut reduction = Vec::new();
73 for source in (0..order.len()).rev() {
74 for &target in &successors[source] {
75 if contains(&reachable[source], target) {
76 continue;
77 }
78 reduction.push((source, target));
79 let (before_target, target_and_after) = reachable.split_at_mut(target);
80 let source_row = &mut before_target[source];
81 let target_row = &target_and_after[0];
82 insert(source_row, target);
83 for (word, inherited) in source_row.iter_mut().zip(target_row) {
84 *word |= inherited;
85 }
86 }
87 }
88 reduction.sort_unstable();
89 let reduction = reduction
90 .into_iter()
91 .map(|(source, target)| EdgeEndpoints::new(order[source], order[target]))
92 .collect();
93 let mut closure = Vec::new();
94 for (source, row) in reachable.iter().enumerate() {
95 for target in 0..order.len() {
96 if contains(row, target) {
97 closure.push(EdgeEndpoints::new(order[source], order[target]));
98 }
99 }
100 }
101 Some(DagTransitive { reduction, closure })
102}
103
104fn contains(bits: &[u64], index: usize) -> bool {
105 bits[index / u64::BITS as usize] & (1_u64 << (index % u64::BITS as usize)) != 0
106}
107
108fn insert(bits: &mut [u64], index: usize) {
109 bits[index / u64::BITS as usize] |= 1_u64 << (index % u64::BITS as usize);
110}