roaring_graphs/
dag.rs

1//! [Directed Acyclic
2//! Graphs](https://en.wikipedia.org/wiki/Directed_acyclic_graph) (DAGs)
3//! represented as [Strictly Upper Triangular
4//! matrices](https://mathworld.wolfram.com/StrictlyUpperTriangularMatrix.html).
5//!
6//! A create for working with DAGs where it is known upfront (i.e. statically)
7//! that graphs are directed and there are no cycles.
8
9//! There are several assumptions imposed on *your* code:
10//!
11//! 1. DAG vertices are integer numbers which is used to trivially
12//!    test whether adding an edge would form a cycle: edges are only allowed to
13//!    go "forward", i.e. from `u` to `v` iff `u < v`.  Otherwise we panic.
14//! 1. Vertices numbering starts at 0.
15//! 1. The number of vertices is determined at construction time and
16//!    growing/shrinking generally requires a new graph to be constructed.
17//!
18//! In exchange for these assumptions you get these useful properties:
19//! * **Correctness**: Cycles (an illegal state) are unrepresentable.
20//! * **Compactness**: Edges are just bits in a bit set.  The implementation
21//!   stores edges in a roaring bitmap with one bit per *existing* edge.  IOW: A graph with no
22//!   edges uses a constant amount of memory irrespective of its max number of vertices.  A full
23//!   graph is basically a bitset of all ones with one bit per edge.
24//! * **CPU cache locality**: Edges are stored in a [row-major packed
25//!   representation](https://www.intel.com/content/www/us/en/develop/documentation/onemkl-developer-reference-c/top/lapack-routines/matrix-storage-schemes-for-lapack-routines.html)
26//!   so that iteration over the neighbours of a vertex is just an iteration
27//!   over *consecutive* bits in a bit set.
28//! * **Low cognitive overhead**: No need to deal with type-level shenenigans to
29//!   get basic tasks done.
30//! * **Asymptotic complexity reduction**: Generating a random DAG is a `O(|E|)`
31//!   operation.  That was actually the original motivation for writing this
32//!   crate.
33//!
34//! ## Anti-features
35//!
36//! * No support for storing anything in the vertices.
37//! * No support for assigning weights to either edges or vertices.
38//! * No serde impls.  Simply serialize/deserialize the list of edges with a
39//!   library of your choosing.
40//!
41//! # Entry points
42//!
43//! See either [`DirectedAcyclicGraph::new`],
44//! [`DirectedAcyclicGraph::from_edges_iter`], or
45//! [`DirectedAcyclicGraph::from_adjacency_matrix`] for the "entry point" to
46//! this crate.
47
48use std::collections::VecDeque;
49use std::io::Write;
50use std::ops::Range;
51
52use proptest::prelude::*;
53use proptest::strategy::ValueTree;
54use rand::distributions::Uniform;
55use rand::prelude::Distribution;
56use roaring::RoaringBitmap;
57
58use crate::delta_debugging_bitmap::DeltaDebuggingBitmapValueTree;
59use crate::strictly_upper_triangular_logical_matrix::{
60    strictly_upper_triangular_matrix_capacity, RowColumnIterator,
61    StrictlyUpperTriangularLogicalMatrix, index_from_row_column,
62};
63use crate::{TraversableDirectedGraph, Vertex};
64
65/// A mutable, single-threaded directed acyclic graph.
66#[derive(Clone, PartialEq, Eq)]
67pub struct DirectedAcyclicGraph {
68    adjacency_matrix: StrictlyUpperTriangularLogicalMatrix,
69}
70
71impl std::fmt::Debug for DirectedAcyclicGraph {
72    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
73        let ones: Vec<(Vertex, Vertex)> = self.iter_edges().collect();
74        write!(
75            f,
76            "DirectedAcyclicGraph::from_edges_iter({}, vec!{:?}.iter().cloned())",
77            self.get_vertex_count(),
78            ones
79        )?;
80        Ok(())
81    }
82}
83
84impl TraversableDirectedGraph for DirectedAcyclicGraph {
85    fn extend_with_children(&self, u: Vertex, children: &mut Vec<Vertex>) {
86        self.extend_with_children(u, children)
87    }
88
89    fn extend_with_parents(&self, v: Vertex, parents: &mut Vec<Vertex>) {
90        self.extend_with_parents(v, parents)
91    }
92}
93
94impl DirectedAcyclicGraph {
95    /// Constructs a new graph without any edges having at most `vertex_count` vertices.
96    pub fn new(vertex_count: Vertex) -> Self {
97        Self {
98            adjacency_matrix: StrictlyUpperTriangularLogicalMatrix::zeroed(vertex_count),
99        }
100    }
101
102    /// Constructs a DAG from a iterator of edges.
103    ///
104    /// Requires `u < vertex_count && v < vertex_count && u < v` for every edge
105    /// `(u, v)` in `edges`.  Panics otherwise.
106    pub fn from_edges_iter<I: Iterator<Item = (Vertex, Vertex)>>(vertex_count: Vertex, edges: I) -> Self {
107        let adjacency_matrix = StrictlyUpperTriangularLogicalMatrix::from_iter(vertex_count, edges);
108        Self { adjacency_matrix }
109    }
110
111    /// Assumes `edges` is a packed representation of the adjacency matrix representing a strictly upper
112    /// triangular matrix.  Such representation has a useful property: (1) Every bit sequence in
113    /// such a representation corresponds to some valid DAG and (2) Every DAG corresponds to some
114    /// valid bit sequence in such a representation.  Thanks to (1) and (2) taken together, we can
115    /// be sure proptest will cover the entire search space of random DAGs.
116    pub fn from_raw_edges(vertex_count: Vertex, edges: &[bool]) -> Self {
117        assert_eq!(
118            edges.len(),
119            usize::try_from(strictly_upper_triangular_matrix_capacity(vertex_count)).unwrap(),
120        );
121
122        let mut iter = RowColumnIterator::new(vertex_count);
123        let mut adjacency_matrix = StrictlyUpperTriangularLogicalMatrix::zeroed(vertex_count);
124        for value in edges {
125            let (row, column) = iter.next().unwrap();
126            if *value {
127                adjacency_matrix.set(row, column);
128            }
129        }
130
131        let dag = DirectedAcyclicGraph::from_adjacency_matrix(adjacency_matrix);
132        dag
133    }
134
135    /// Construct a DAG from a pre-computed adjacency matrix.
136    pub fn from_adjacency_matrix(adjacency_matrix: StrictlyUpperTriangularLogicalMatrix) -> Self {
137        Self { adjacency_matrix }
138    }
139
140    /// Construct a fully connected DAG with given amount of vertices.
141    pub fn fully_connected(vertex_count: Vertex) -> Self {
142        let mut adjacency_matrix = StrictlyUpperTriangularLogicalMatrix::zeroed(vertex_count);
143        for i in 0..vertex_count {
144            for j in (i + 1)..vertex_count {
145                adjacency_matrix.set(i, j);
146            }
147        }
148        Self { adjacency_matrix }
149    }
150
151    #[inline]
152    pub fn get_vertex_count(&self) -> Vertex {
153        self.adjacency_matrix.size()
154    }
155
156    /// Requires `u < v`.  Panics otherwise.
157    pub fn get_edge(&self, u: Vertex, v: Vertex) -> bool {
158        assert!(u < self.get_vertex_count());
159        assert!(v < self.get_vertex_count());
160        assert!(u < v);
161        self.adjacency_matrix.get(u, v)
162    }
163
164    /// Requires `u < v`.  Panics otherwise.
165    pub fn set_edge(&mut self, u: Vertex, v: Vertex) {
166        assert!(u < self.get_vertex_count());
167        assert!(v < self.get_vertex_count());
168        assert!(u < v);
169        self.adjacency_matrix.set(u, v);
170    }
171
172    /// Requires `u < v`.  Panics otherwise.
173    pub fn clear_edge(&mut self, u: Vertex, v: Vertex) {
174        assert!(u < self.get_vertex_count());
175        assert!(v < self.get_vertex_count());
176        assert!(u < v);
177        self.adjacency_matrix.clear(u, v);
178    }
179
180    /// Each emitted pair `(u, v)` is guaranteed to satisfy `u < v`.
181    pub fn iter_edges(&self) -> impl Iterator<Item = (Vertex, Vertex)> + '_ {
182        self.adjacency_matrix.iter_ones()
183    }
184
185    /// Iterates over vertices `v` such that there's an edge `(u, v)` in the
186    /// DAG.
187    pub fn iter_children(&self, u: Vertex) -> impl Iterator<Item = Vertex> + '_ {
188        self.adjacency_matrix.iter_ones_at_row(u)
189    }
190
191    pub fn extend_with_children(&self, u: Vertex, children: &mut Vec<Vertex>) {
192        children.extend(self.adjacency_matrix.iter_ones_at_row(u))
193    }
194
195    pub fn extend_with_parents(&self, v: Vertex, parents: &mut Vec<Vertex>) {
196        parents.extend(self.adjacency_matrix.iter_ones_at_column(v))
197    }
198
199    /// Consume self and return the underlying adjacency matrix.
200    pub fn into_adjacency_matrix(self) -> StrictlyUpperTriangularLogicalMatrix {
201        self.adjacency_matrix
202    }
203
204    /// Visit all vertices reachable from `vertex` in a depth-first-search (DFS)
205    /// order.
206    pub fn iter_descendants_dfs(&self, start_vertex: Vertex) -> Box<dyn Iterator<Item = Vertex> + '_> {
207        let iter = crate::digraph::DfsDescendantsIterator {
208            digraph: self,
209            visited: RoaringBitmap::new(),
210            to_visit: vec![start_vertex],
211        };
212        let iter = iter.filter(move |vertex| *vertex != start_vertex);
213        Box::new(iter)
214    }
215
216    pub fn iter_ancestors_dfs(&self, start_vertex: Vertex) -> Box<dyn Iterator<Item = Vertex> + '_> {
217        let iter = crate::digraph::DfsAncestorsIterator {
218            digraph: self,
219            visited: RoaringBitmap::new(),
220            to_visit: vec![start_vertex],
221        };
222        let iter = iter.filter(move |vertex| *vertex != start_vertex);
223        Box::new(iter)
224    }
225
226    /// Visit all vertices of a DAG in a depth-first-search (DFS) order.
227    pub fn iter_vertices_dfs(&self) -> Box<dyn Iterator<Item = Vertex> + '_> {
228        let iter = crate::digraph::DfsDescendantsIterator {
229            digraph: self,
230            visited: RoaringBitmap::new(),
231            to_visit: self.get_vertices_without_incoming_edges(),
232        };
233        Box::new(iter)
234    }
235
236    /// Visit all vertices of a DAG in a depth-first-search postorder, i.e. emitting vertices only
237    /// after all their descendants were emitted first.
238    pub fn iter_vertices_dfs_post_order(&self) -> Box<dyn Iterator<Item = Vertex> + '_> {
239        let iter = crate::digraph::DfsPostOrderVerticesIterator {
240            digraph: self,
241            visited: RoaringBitmap::new(),
242            to_visit: self.get_vertices_without_incoming_edges(),
243        };
244        Box::new(iter)
245    }
246
247    /// Visit nodes in a depth-first-search (DFS) emitting edges in postorder, i.e. each node is
248    /// visited after all its descendants were already visited.
249    pub fn iter_edges_dfs_post_order(&self) -> Box<dyn Iterator<Item = (Vertex, Vertex)> + '_> {
250        let iter = crate::digraph::DfsPostOrderEdgesIterator {
251            digraph: self,
252            inner: self.iter_vertices_dfs_post_order(),
253            seen_vertices: RoaringBitmap::new(),
254            buffer: Default::default(),
255        };
256        Box::new(iter)
257    }
258
259    /// Visit all vertices reachable from `vertex` in a depth-first-search
260    /// postorder, i.e. emitting vertices only after all their descendants have been
261    /// emitted first.
262    pub fn iter_descendants_dfs_post_order(
263        &self,
264        vertex: Vertex,
265    ) -> Box<dyn Iterator<Item = Vertex> + '_> {
266        let iter = crate::digraph::DfsPostOrderVerticesIterator {
267            digraph: self,
268            visited: RoaringBitmap::new(),
269            to_visit: vec![vertex],
270        };
271        Box::new(iter)
272    }
273
274    /// Combines [`Self::iter_vertices_dfs_post_order`] with [`slice::reverse()`] to get a
275    /// topologically ordered sequence of vertices of a DAG.
276    pub fn get_topologically_ordered_vertices(&self) -> Vec<Vertex> {
277        let mut result: Vec<Vertex> = Vec::with_capacity(self.get_vertex_count().into());
278        result.extend(self.iter_vertices_dfs_post_order());
279        result.reverse();
280        result
281    }
282
283    /// Computes a mapping: vertex -> set of vertices that are descendants of vertex.
284    pub fn get_descendants(&self) -> Vec<RoaringBitmap> {
285        let mut descendants: Vec<RoaringBitmap> =
286            vec![RoaringBitmap::default(); self.get_vertex_count().into()];
287
288        for u in (0..self.get_vertex_count()).rev() {
289            let mut u_descendants = RoaringBitmap::default();
290            for v in self.iter_children(u) {
291                u_descendants |= &descendants[usize::from(v)];
292                u_descendants.insert(v.into());
293            }
294            descendants[usize::try_from(u).unwrap()] = u_descendants;
295        }
296
297        descendants
298    }
299
300    /// Returns a new DAG that is a [transitive
301    /// reduction](https://en.wikipedia.org/wiki/Transitive_reduction) of a DAG.
302    pub fn transitive_reduction(&self) -> DirectedAcyclicGraph {
303        let mut result = self.clone();
304
305        let descendants = self.get_descendants();
306        for u in 0..self.get_vertex_count() {
307            for v in self.iter_children(u) {
308                for w in descendants[usize::try_from(v).unwrap()].iter() {
309                    let w = Vertex::try_from(w).unwrap();
310                    if w == v {
311                        continue;
312                    }
313                    result.clear_edge(u, w);
314                }
315            }
316        }
317        result
318    }
319
320    /// Returns a new DAG that is a [transitive
321    /// closure](https://en.wikipedia.org/wiki/Transitive_closure) of a DAG.
322    pub fn transitive_closure(&self) -> DirectedAcyclicGraph {
323        let mut result = self.clone();
324
325        // http://www.compsci.hunter.cuny.edu/~sweiss/course_materials/csci335/lecture_notes/chapter08.pdf
326
327        let descendants = self.get_descendants();
328        for u in 0..self.get_vertex_count() {
329            for v in descendants[usize::try_from(u).unwrap()].iter() {
330                let v = Vertex::try_from(v).unwrap();
331                result.set_edge(u, v);
332            }
333        }
334
335        result
336    }
337
338    /// Returns a set "seed" vertices of a DAG from which a traversal may start so
339    /// that the process covers all vertices in the graph.
340    pub fn get_vertices_without_incoming_edges(&self) -> Vec<Vertex> {
341        let incoming_edges_count = {
342            let mut incoming_edges_count: Vec<Vertex> =
343                vec![0; self.get_vertex_count().into()];
344            for (_, v) in self.iter_edges() {
345                incoming_edges_count[usize::try_from(v).unwrap()] += 1;
346            }
347            incoming_edges_count
348        };
349
350        let vertices_without_incoming_edges: Vec<Vertex> = incoming_edges_count
351            .into_iter()
352            .enumerate()
353            .filter(|(_, indegree)| *indegree == 0)
354            .map(|(vertex, _)| Vertex::try_from(vertex).unwrap())
355            .collect();
356
357        vertices_without_incoming_edges
358    }
359
360    /// Visit all vertices reachable from `vertex` in a breadth-first-search (BFS)
361    /// order.
362    pub fn iter_descendants_bfs(&self, vertex: Vertex) -> BfsVerticesIterator {
363        BfsVerticesIterator {
364            dag: self,
365            visited: RoaringBitmap::new(),
366            to_visit: vec![vertex].into(),
367        }
368    }
369
370    /// Visit all vertices of a DAG in a breadth-first-search (BFS) order.
371    pub fn iter_vertices_bfs(&self) -> BfsVerticesIterator {
372        BfsVerticesIterator {
373            dag: self,
374            visited: RoaringBitmap::new(),
375            to_visit: self.get_vertices_without_incoming_edges().into(),
376        }
377    }
378
379    /// Outputs the DAG in the [Graphviz DOT](https://graphviz.org/) format.
380    pub fn to_dot<W: Write>(&self, output: &mut W) -> std::result::Result<(), std::io::Error> {
381        writeln!(output, "digraph dag_{} {{", self.get_vertex_count())?;
382
383        for elem in 0..self.get_vertex_count() {
384            writeln!(output, "\t_{}[label=\"{}\"];", elem, elem)?;
385        }
386
387        writeln!(output, "\n")?;
388
389        for (left, right) in self.iter_edges() {
390            writeln!(output, "\t_{} -> _{};", left, right)?;
391        }
392
393        writeln!(output, "}}")?;
394        Ok(())
395    }
396
397    pub fn to_dot_file<P: AsRef<std::path::Path>>(
398        &self,
399        path: P,
400    ) -> std::result::Result<(), std::io::Error> {
401        let mut file = std::fs::File::create(path)?;
402        self.to_dot(&mut file)?;
403        Ok(())
404    }
405}
406
407pub fn arb_dag(vertex_count: impl Into<Range<Vertex>>, edge_probability: f64) -> UniformEdgeProbabilityStrategy {
408    UniformEdgeProbabilityStrategy {
409        vertex_count: vertex_count.into(),
410        edge_probability,
411    }
412}
413
414#[derive(Debug)]
415pub struct UniformEdgeProbabilityStrategy {
416    vertex_count: Range<Vertex>,
417    edge_probability: f64,
418}
419
420#[derive(Debug)]
421pub struct DirectedAcyclicGraphValueTree {
422    vertex_count: u16,
423    state: ReductionState,
424}
425
426#[derive(Debug)]
427enum ReductionState {
428    ReduceVertices {
429        vertex_mask_tree: DeltaDebuggingBitmapValueTree,
430        edge_bitmap: RoaringBitmap,
431    },
432    ReduceEdges {
433        vertex_mask: RoaringBitmap,
434        edge_bitmap_tree: DeltaDebuggingBitmapValueTree,
435    }
436}
437
438impl ReductionState {
439    fn current_vertex_mask(&self) -> RoaringBitmap {
440        match self {
441            Self::ReduceVertices { vertex_mask_tree, .. } => vertex_mask_tree.current(),
442            Self::ReduceEdges { vertex_mask, .. } => vertex_mask.clone(),
443        }
444    }
445
446    fn current_edge_bitmap(&self) -> RoaringBitmap {
447        match self {
448            Self::ReduceVertices { edge_bitmap, .. } => edge_bitmap.clone(),
449            Self::ReduceEdges { edge_bitmap_tree, .. } => edge_bitmap_tree.current(),
450        }
451    }
452}
453
454impl Strategy for UniformEdgeProbabilityStrategy {
455    type Tree = DirectedAcyclicGraphValueTree;
456
457    type Value = DirectedAcyclicGraph;
458
459    fn new_tree(
460        &self,
461        runner: &mut proptest::test_runner::TestRunner,
462    ) -> proptest::strategy::NewTree<Self> {
463        // Copied out of self.vertex_count.assert_nonempty(), because that's private to proptest
464        if self.vertex_count.is_empty() {
465            panic!(
466                "Invalid use of empty size range. (hint: did you \
467                 accidentally write {}..{} where you meant {}..={} \
468                 somewhere?)",
469                self.vertex_count.start,
470                self.vertex_count.end,
471                self.vertex_count.start,
472                self.vertex_count.end
473            );
474        }
475        let vertex_count =
476            Uniform::new(self.vertex_count.start, self.vertex_count.end - 1).sample(runner.rng());
477
478        let vertex_mask = {
479            let mut b = RoaringBitmap::new();
480            b.insert_range(0..vertex_count as u32);
481            b
482        };
483        
484        let mut edge_bitmap = RoaringBitmap::new();
485        for i in 0..vertex_count {
486            for j in (i + 1)..vertex_count {
487                if runner.rng().gen_bool(self.edge_probability) {
488                    edge_bitmap.insert(index_from_row_column(i, j, vertex_count));
489                }
490            }
491        }
492
493        Ok(DirectedAcyclicGraphValueTree {
494            vertex_count,
495            state: ReductionState::ReduceVertices {
496                vertex_mask_tree: DeltaDebuggingBitmapValueTree::new(vertex_mask),
497                edge_bitmap
498            }
499        })
500    }
501}
502
503impl ValueTree for DirectedAcyclicGraphValueTree {
504    type Value = DirectedAcyclicGraph;
505
506    fn current(&self) -> Self::Value {
507        let vertex_mask = self.state.current_vertex_mask();
508        let edge_map = self.state.current_edge_bitmap();
509
510        let mut from_dst = 0 as Vertex;
511        let mut edges = Vec::with_capacity(strictly_upper_triangular_matrix_capacity(
512            self.vertex_count,
513        ) as usize);
514        for from_src in 0..self.vertex_count {
515            if vertex_mask.contains(from_src as u32) {
516                let mut to_dst = from_dst + 1;
517                for to_src in from_src + 1..self.vertex_count {
518                    if vertex_mask.contains(to_src as u32) {
519                        let edge_idx = index_from_row_column(from_src, to_src, self.vertex_count);
520                        if edge_map.contains(edge_idx) {
521                            edges.push((from_dst, to_dst));
522                        }
523                        to_dst += 1;
524                    }
525                }
526                from_dst += 1;
527            }
528        }
529
530        DirectedAcyclicGraph::from_edges_iter(vertex_mask.len() as Vertex, edges.into_iter())
531    }
532
533    fn simplify(&mut self) -> bool {
534        match &mut self.state {
535            ReductionState::ReduceVertices { vertex_mask_tree, edge_bitmap } => {
536                if !vertex_mask_tree.simplify() {
537                    self.state = ReductionState::ReduceEdges {
538                        vertex_mask: vertex_mask_tree.current(),
539                        edge_bitmap_tree: DeltaDebuggingBitmapValueTree::new(edge_bitmap.clone()),
540                    };
541                }
542                true
543            },
544            ReductionState::ReduceEdges { edge_bitmap_tree, .. } => edge_bitmap_tree.simplify(),
545        }
546    }
547
548    fn complicate(&mut self) -> bool {
549        match &mut self.state {
550            ReductionState::ReduceVertices { vertex_mask_tree, edge_bitmap } => {
551                if !vertex_mask_tree.complicate() {
552                    self.state = ReductionState::ReduceEdges {
553                        vertex_mask: vertex_mask_tree.current(),
554                        edge_bitmap_tree: DeltaDebuggingBitmapValueTree::new(edge_bitmap.clone()),
555                    };
556                }
557                true
558            },
559            ReductionState::ReduceEdges { edge_bitmap_tree, .. } => edge_bitmap_tree.complicate(),
560        }
561    }
562}
563
564/// See [`DirectedAcyclicGraph::iter_vertices_bfs`].
565pub struct BfsVerticesIterator<'a> {
566    dag: &'a DirectedAcyclicGraph,
567    visited: RoaringBitmap,
568    to_visit: VecDeque<Vertex>,
569}
570
571impl<'a> Iterator for BfsVerticesIterator<'a> {
572    type Item = Vertex;
573
574    fn next(&mut self) -> Option<Self::Item> {
575        while let Some(u) = self.to_visit.pop_front() {
576            if self.visited.contains(u.into()) {
577                continue;
578            }
579            self.visited.insert(u.into());
580            self.to_visit.extend(
581                self.dag
582                    .iter_children(u)
583                    .filter(|v| !self.visited.contains((*v).into())),
584            );
585            return Some(u);
586        }
587        None
588    }
589}
590
591#[cfg(test)]
592mod tests {
593    use std::{
594        borrow::Borrow,
595        collections::{BTreeMap, HashSet},
596    };
597
598    use proptest::test_runner::{TestCaseResult, TestError, TestRunner};
599
600    use super::*;
601
602    #[test]
603    #[should_panic = "assertion failed: u < v"]
604    fn negative_test_smallest_dag() {
605        let mut dag = DirectedAcyclicGraph::new(2);
606        assert_eq!(dag.get_edge(0, 0), false);
607        dag.set_edge(0, 0);
608    }
609
610    #[test]
611    fn divisibility_poset_of_12_ordered_pairs() {
612        let divisibility_poset_pairs = vec![
613            (1, 2),
614            (1, 3),
615            (1, 4),
616            (1, 5),
617            (1, 6),
618            (1, 7),
619            (1, 8),
620            (1, 9),
621            (1, 10),
622            (1, 11),
623            (1, 12),
624            (2, 4),
625            (2, 6),
626            (2, 8),
627            (2, 10),
628            (2, 12),
629            (3, 6),
630            (3, 9),
631            (3, 12),
632            (4, 8),
633            (4, 12),
634            (5, 10),
635            (6, 12),
636        ];
637        let dag =
638            DirectedAcyclicGraph::from_edges_iter(12 + 1, divisibility_poset_pairs.into_iter());
639        let dag = dag.transitive_reduction();
640
641        let dag_pairs: HashSet<(Vertex, Vertex)> = HashSet::from_iter(dag.iter_edges_dfs_post_order());
642        let expected = HashSet::from_iter(vec![
643            (3, 9),
644            (2, 6),
645            (6, 12),
646            (1, 7),
647            (1, 11),
648            (5, 10),
649            (3, 6),
650            (2, 10),
651            (1, 2),
652            (4, 12),
653            (2, 4),
654            (4, 8),
655            (1, 5),
656            (1, 3),
657        ]);
658        assert_eq!(dag_pairs, expected);
659    }
660
661    proptest! {
662        // This mostly ensures `iter_edges()` really returns *all* the edges.
663        #[test]
664        fn unblocking_preserves_transitivity(mut dag in arb_dag(0..25, 0.5)) {
665            println!("{:?}", dag);
666            let mut edges: Vec<(Vertex, Vertex)> = dag.iter_edges().collect();
667            while let Some((left, right)) = edges.pop() {
668                dag.clear_edge(left, right);
669            }
670            let edges: Vec<(Vertex, Vertex)> = dag.iter_edges().collect();
671            prop_assert!(edges.is_empty());
672        }
673    }
674
675    /// Does not include the trivial divisors: k | k for every integer k.
676    #[derive(Clone, Debug)]
677    struct IntegerDivisibilityPoset {
678        number: Vertex,
679        divisors_of: BTreeMap<Vertex, Vec<Vertex>>,
680    }
681
682    impl IntegerDivisibilityPoset {
683        fn get_divisors(number: Vertex) -> BTreeMap<Vertex, Vec<Vertex>> {
684            let mut result: BTreeMap<Vertex, Vec<Vertex>> = Default::default();
685            let mut numbers: Vec<Vertex> = vec![number];
686            while let Some(n) = numbers.pop() {
687                let divisors_of_n: Vec<Vertex> = (1..n / 2 + 1).filter(|d| n % d == 0).rev().collect();
688                for divisor in &divisors_of_n {
689                    if !result.contains_key(&divisor) {
690                        numbers.push(*divisor);
691                    }
692                }
693                result.insert(n, divisors_of_n);
694            }
695            result
696        }
697
698        fn of_number(number: Vertex) -> Self {
699            IntegerDivisibilityPoset {
700                number,
701                divisors_of: Self::get_divisors(number),
702            }
703        }
704
705        fn get_pairs(&self) -> Vec<(Vertex, Vertex)> {
706            let mut result = Vec::new();
707
708            for divisor in self.divisors_of.keys() {
709                result.extend(
710                    self.divisors_of[&divisor]
711                        .iter()
712                        .map(|dividend| (*dividend, *divisor)),
713                );
714            }
715
716            result
717        }
718    }
719
720    proptest! {
721        #[test]
722        fn prop_integer_divisibility_poset_isomorphism(size in 3u32..1000u32) {
723            let integer_divisibility_poset = IntegerDivisibilityPoset::of_number(size.try_into().unwrap());
724
725            println!(
726                "{:10} {:?}",
727                integer_divisibility_poset.number, integer_divisibility_poset.divisors_of
728            );
729
730            let pairs = integer_divisibility_poset.get_pairs();
731
732            let dag = DirectedAcyclicGraph::from_edges_iter(
733                integer_divisibility_poset.number + 1,
734                pairs.iter().cloned(),
735            );
736
737            for (left, right) in pairs {
738                prop_assert!(dag.get_edge(left, right));
739            }
740
741            for (left, right) in dag.iter_edges() {
742                prop_assert_eq!(right % left, 0);
743            }
744        }
745    }
746
747    #[test]
748    fn divisibility_poset_12_children() {
749        let divisibility_poset_pairs = vec![
750            (1, 2),
751            (1, 3),
752            (1, 4),
753            (1, 5),
754            (1, 6),
755            (1, 7),
756            (1, 8),
757            (1, 9),
758            (1, 10),
759            (1, 11),
760            (1, 12),
761            (2, 4),
762            (2, 6),
763            (2, 8),
764            (2, 10),
765            (2, 12),
766            (3, 6),
767            (3, 9),
768            (3, 12),
769            (4, 8),
770            (4, 12),
771            (5, 10),
772            (6, 12),
773        ];
774        let dag =
775            DirectedAcyclicGraph::from_edges_iter(12 + 1, divisibility_poset_pairs.into_iter());
776        assert_eq!(dag.iter_children(12).collect::<Vec<Vertex>>(), vec![]);
777        assert_eq!(dag.iter_children(11).collect::<Vec<Vertex>>(), vec![]);
778        assert_eq!(dag.iter_children(9).collect::<Vec<Vertex>>(), vec![]);
779        assert_eq!(dag.iter_children(8).collect::<Vec<Vertex>>(), vec![]);
780        assert_eq!(dag.iter_children(7).collect::<Vec<Vertex>>(), vec![]);
781        assert_eq!(dag.iter_children(6).collect::<Vec<Vertex>>(), vec![12]);
782        assert_eq!(dag.iter_children(5).collect::<Vec<Vertex>>(), vec![10]);
783        assert_eq!(dag.iter_children(4).collect::<Vec<Vertex>>(), vec![8, 12]);
784        assert_eq!(dag.iter_children(3).collect::<Vec<Vertex>>(), vec![6, 9, 12]);
785        assert_eq!(
786            dag.iter_children(2).collect::<Vec<Vertex>>(),
787            vec![4, 6, 8, 10, 12]
788        );
789        assert_eq!(
790            dag.iter_children(1).collect::<Vec<Vertex>>(),
791            vec![2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
792        );
793    }
794
795    #[test]
796    fn divisibility_poset_of_12_descendants() {
797        let divisibility_poset_pairs = vec![
798            (1, 2),
799            (1, 3),
800            (1, 4),
801            (1, 5),
802            (1, 6),
803            (1, 7),
804            (1, 8),
805            (1, 9),
806            (1, 10),
807            (1, 11),
808            (1, 12),
809            (2, 4),
810            (2, 6),
811            (2, 8),
812            (2, 10),
813            (2, 12),
814            (3, 6),
815            (3, 9),
816            (3, 12),
817            (4, 8),
818            (4, 12),
819            (5, 10),
820            (6, 12),
821        ];
822        let dag =
823            DirectedAcyclicGraph::from_edges_iter(12 + 1, divisibility_poset_pairs.into_iter());
824        let descendants = dag.get_descendants();
825        assert_eq!(descendants[12], RoaringBitmap::new());
826        assert_eq!(descendants[11], RoaringBitmap::new());
827        assert_eq!(descendants[10], RoaringBitmap::new());
828        assert_eq!(descendants[9], RoaringBitmap::new());
829        assert_eq!(descendants[8], RoaringBitmap::new());
830        assert_eq!(descendants[7], RoaringBitmap::new());
831        assert_eq!(descendants[6], RoaringBitmap::from_iter(vec![12]));
832        assert_eq!(descendants[5], RoaringBitmap::from_iter(vec![10]));
833        assert_eq!(descendants[4], RoaringBitmap::from_iter(vec![8, 12]));
834        assert_eq!(descendants[3], RoaringBitmap::from_iter(vec![6, 9, 12]),);
835        assert_eq!(
836            descendants[2],
837            RoaringBitmap::from_iter(vec![4, 6, 8, 10, 12]),
838        );
839        assert_eq!(
840            descendants[1],
841            RoaringBitmap::from_iter(vec![2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]),
842        );
843    }
844
845    proptest! {
846        #[test]
847        fn prop_transitive_closure_and_transitive_reduction_intersection_equals_transitive_reduction_modulo_order(
848            dag in arb_dag(0..25, 0.5),
849        ) {
850            println!("{:?}", dag);
851            let transitive_closure: HashSet<(Vertex, Vertex)> =
852                dag.transitive_closure().iter_edges().collect();
853            let transitive_reduction: HashSet<(Vertex, Vertex)> =
854                dag.transitive_reduction().iter_edges().collect();
855            let intersection: HashSet<(Vertex, Vertex)> = transitive_closure
856                .intersection(&transitive_reduction)
857                .cloned()
858                .collect();
859            prop_assert_eq!(intersection, transitive_reduction);
860        }
861    }
862
863    #[test]
864    fn divisibility_poset_of_12_dfs_descendants() {
865        let divisibility_poset_pairs = vec![
866            (1, 2),
867            (1, 3),
868            (1, 4),
869            (1, 5),
870            (1, 6),
871            (1, 7),
872            (1, 8),
873            (1, 9),
874            (1, 10),
875            (1, 11),
876            (1, 12),
877            (2, 4),
878            (2, 6),
879            (2, 8),
880            (2, 10),
881            (2, 12),
882            (3, 6),
883            (3, 9),
884            (3, 12),
885            (4, 8),
886            (4, 12),
887            (5, 10),
888            (6, 12),
889        ];
890        let dag =
891            DirectedAcyclicGraph::from_edges_iter(12 + 1, divisibility_poset_pairs.into_iter());
892
893        assert_eq!(dag.iter_descendants_dfs(12).collect::<Vec<Vertex>>(), vec![]);
894        assert_eq!(dag.iter_descendants_dfs(11).collect::<Vec<Vertex>>(), vec![]);
895        assert_eq!(dag.iter_descendants_dfs(6).collect::<Vec<Vertex>>(), vec![12]);
896    }
897
898    proptest! {
899        #[test]
900        fn traversals_equal_modulo_order(dag in arb_dag(0..25, 0.5)) {
901            let bfs: HashSet<Vertex> = dag.iter_vertices_bfs().collect();
902            let dfs: HashSet<Vertex> = dag.iter_vertices_dfs().collect();
903            let dfs_post_order: HashSet<Vertex> = dag.iter_vertices_dfs_post_order().collect();
904            prop_assert_eq!(&bfs, &dfs);
905            prop_assert_eq!(&dfs_post_order, &dfs);
906            prop_assert_eq!(&dfs_post_order, &bfs);
907        }
908    }
909
910    /// A pseudo-test-case that fails on DAGs that contain a node with two ancestors
911    fn fail_on_two_incoming(dag: impl Borrow<DirectedAcyclicGraph>) -> TestCaseResult {
912        for to in 0..dag.borrow().get_vertex_count() {
913            let mut count = 0;
914            for from in 0..to {
915                count += dag.borrow().get_edge(from, to) as u32;
916                if count >= 2 {
917                    return Err(TestCaseError::Fail(
918                        "contains an edge with two incoming".into(),
919                    ));
920                }
921            }
922        }
923        Ok(())
924    }
925
926    #[test]
927    fn minify_dag_to_3_nodes() {
928        // This is the minimal DAG that has a node with two ancestors
929        let minimal_dag = DirectedAcyclicGraph::from_edges_iter(3, vec![(0, 2), (1, 2)].into_iter());
930        assert!(fail_on_two_incoming(&minimal_dag).is_err());
931
932        // We construct a fully-connected DAG of 10 vertices
933        let vertex_count = 10;
934        let edge_bitmap = DirectedAcyclicGraph::fully_connected(vertex_count).adjacency_matrix.into_bitset();
935        let vertex_mask = {
936            let mut b = RoaringBitmap::new();
937            b.insert_range(0..vertex_count as u32);
938            b
939        };
940
941        let full_graph_tree = DirectedAcyclicGraphValueTree {
942            vertex_count,
943            state: ReductionState::ReduceVertices {
944                vertex_mask_tree: DeltaDebuggingBitmapValueTree::new(vertex_mask),
945                edge_bitmap,
946            },
947        };
948
949        let mut runner = TestRunner::new(Default::default());
950        let result = runner.run_one(full_graph_tree, fail_on_two_incoming);
951
952        // After running the shrinker, the DAG should be exactly the minimal DAG:
953        assert_eq!(
954            result,
955            Err(TestError::Fail(
956                "contains an edge with two incoming".into(),
957                minimal_dag
958            ))
959        );
960    }
961}