Skip to content

Commit c61eee8

Browse files
authored
Merge pull request #41 from mitchmindtree/feature/transitive-reduction
2 parents 8536a4b + e8f0f3d commit c61eee8

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# Changelog
2+
3+
## unreleased
4+
5+
* Add `Dag::transitive_reduce` ([#36][#36]).
6+
7+
[#36]: https://github.com/mitchmindtree/daggy/pull/36

src/lib.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,40 @@ where
641641
{
642642
walker::Recursive::new(start, recursive_fn)
643643
}
644+
645+
fn transitive_reduce_iter(
646+
&mut self,
647+
curr_node: NodeIndex<Ix>,
648+
ancestors: &mut Vec<NodeIndex<Ix>>,
649+
) {
650+
let mut redundant_edges = Vec::new();
651+
for (_, child) in self.children(curr_node).iter(self) {
652+
for (edge, coparent) in self.parents(child).iter(self) {
653+
if ancestors.contains(&coparent) {
654+
redundant_edges.push(edge);
655+
}
656+
}
657+
}
658+
for edge in redundant_edges {
659+
self.remove_edge(edge);
660+
}
661+
662+
ancestors.push(curr_node);
663+
664+
let mut children = self.children(curr_node);
665+
while let Some((_, child)) = children.walk_next(self) {
666+
self.transitive_reduce_iter(child, ancestors)
667+
}
668+
669+
ancestors.pop();
670+
}
671+
672+
/// Mutates the DAG into its [transitive reduction](https://en.wikipedia.org/wiki/Directed_acyclic_graph#Transitive_closure_and_transitive_reduction)
673+
pub fn transitive_reduce(&mut self, roots: Vec<NodeIndex<Ix>>) {
674+
for root in roots {
675+
self.transitive_reduce_iter(root, &mut Vec::new())
676+
}
677+
}
644678
}
645679

646680
/// After adding a new edge to the graph, we use this function immediately after to check whether

tests/transitive_reduce.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
extern crate daggy;
2+
3+
use daggy::Dag;
4+
5+
#[test]
6+
fn transitive_reduce() {
7+
let mut dag = Dag::<&str, &str>::new();
8+
9+
// construct example DAG from wikipedia
10+
11+
let a = dag.add_node("a");
12+
13+
let (_, b) = dag.add_child(a, "a->b", "b");
14+
let (_, c) = dag.add_child(a, "a->c", "c");
15+
let (_, d) = dag.add_child(a, "a->d", "d");
16+
let (_, e) = dag.add_child(a, "a->e", "e");
17+
18+
dag.add_edge(b, d, "b->d").unwrap();
19+
20+
dag.add_edge(c, d, "c->d").unwrap();
21+
dag.add_edge(c, e, "c->e").unwrap();
22+
23+
dag.add_edge(d, e, "d->e").unwrap();
24+
25+
assert_eq!(dag.edge_count(), 8);
26+
27+
dag.transitive_reduce(vec![a]);
28+
29+
assert_eq!(dag.edge_count(), 5);
30+
31+
// test case where the alternate route from the parent is more than one node long
32+
33+
dag.add_edge(a, e, "a->e").unwrap();
34+
35+
assert_eq!(dag.edge_count(), 6);
36+
37+
dag.transitive_reduce(vec![a]);
38+
39+
assert_eq!(dag.edge_count(), 5);
40+
}

0 commit comments

Comments
 (0)