1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
//! ## Introduction to BDDs
//!
//! BDD is a *directed acyclic graph* (DAG) with two types of vertices (or nodes). There are two
//! terminal vertices called `1` and `0` which have no outgoing edges. The rest of the vertices
//! are decision vertices. Each decision vertex has an associated Boolean variable $v$ and two
//! outgoing edges $low$ and $high$. In diagrams, $low$ edges are typically drawn as dashed and
//! $high$ edges as solid. The graph has always one *root vertex* (with no predecessors).
//!
//! Semantically, for a given valuation (assignment) of Boolean variables $Var \to \\{ 0, 1 \\}$,
//! we can "evaluate" the graph by starting in the root vertex and choosing the following vertex
//! based on the value of the current decision variable in the given valuation. Once we reach
//! a terminal vertex, we obtain a final Boolean value. For example, consider the formula
//! $a \land \neg b$. The corresponding BDD is the following:
//!
//! ```mermaid
//! graph LR
//! a($a$)
//! b($b$)
//! zero($0$)
//! one($1$)
//! a -.-> zero
//! a --> b
//! b -.-> one
//! b --> zero
//! ```
//!
//! We can see that there is only one path from the root ($a$) to `1` and this path corresponds
//! to the only valuation which satisfies the Boolean formula (i.e. $a = 1; b = 0$).
//!
//! Typically, BDDs assume some **fixed ordering of variables** such that every path from root to
//! terminal follows this ordering (thus *ordered*). Furthermore, in every BDD, all **redundant
//! vertices are removed** (thus *reduced*). The vertex is redundant if both $low$ and $high$
//! point to the same vertex (there is no decision) or if there is another vertex with the same
//! values of $v$, $low$ and $high$ somewhere else in the graph (we can reuse this vertex).
//!
//! When the BDD is ordered and reduced, it is **canonical**, i.e. every equivalent Boolean formula
//! has the same BDD (equality can be thus checked syntactically on the structure of the graph).
//!
//! ## Encoding BDD in an array
//!
//! While BDD is a graph, it would be wasteful to store each node of the BDD as a separate memory
//! object requiring allocations and book keeping. Instead, we sort nodes in each BDD in the
//! DFS post-order (taking low edge first and high edge second, although this decision is arbitrary)
//! of the graph and this way, we can easily save them as a sequence in an array. The only
//! exception are the two terminal nodes which we always place on positions 0 and 1
//! (empty BDD only has the 0 node).
//!
//! Because DFS post-order is unique, we can still check formula equivalence by comparing the two
//! arrays element-wise. Also notice that the root of the BDD is always the last element of the
//! array and children of any node always have smaller indices than the parent.
//!
//! The BDD from the previous section translates to the following array:
//!
//! ```c
//! [0, 1, (b, low = 1, high = 0), (a, low = 0, high = 2)]
//! ```
//!
//! Notice that the edge pointers are now indices into the array itself instead of memory
//! references. This also allows certain memory optimisations (for "small" BDDs, the pointers
//! only need to be 32 bits even on 64 bit platforms, etc.). Also, such representation is trivial
//! to serialize, deserialize or share, since we can just clone the whole array if needed.