¶Simplifying a BDD relative to an implication
[Andersen2011] is the canonical BDD introduction that I read during grad school.
https://www.cmi.ac.in/~madhavan/courses/verification-2011/andersen-bdd.pdf
It describes an algorithm called Simplify, which simplifies one BDD relation to another. That is, given two BDDs representing functions d and u, Simplify(d, u) returns another BDD u' where d ∧ u == d ∧ u'.
For constraint sets, d will be an implication describing that two individual constraints simplify relative to each other, c₁ → c₂. (If c₁ is true, then we know that c₂ is true as well.) The simplification will remove parts of the BDD where we make a redundant check on c₂ (that is, where we've already established that c₁ is true, and therefore don't need to check c₂).
Here we specialize the Simplify algorithm where d is fixed to the implication c₁ → c₂.
The text of the algorithm from [Andersen2011] is:
fn Simplify(d, u) { if d = 0 then return 0 else if u ≤ 1 then return u else if d = 1 then return Mk(var(u), Simplify(d, low(u)), Simplify(d, high(u))) else if var(d) = var(u) then if low(d) = 0 then return Simplify(high(d), high(u)) else if high(d) = 0 then return Simplify(low(d), low(u)) else return Mk(var(u), Simplify(low(d), low(u)), Simplify(high(d), high(u))) else if var(d) < var(u) then return Mk(var(d), Simplify(low(d), u), Simplify(high(d), u)) else return Mk(var(u), Simplify(d, low(u)), Simplify(d, high(u))) }
There are two cases to consider: c₁ < c₂ and c₂ < c₁. (That is c₁ appears before or after c₂ in the BDD variable ordering; we're not talking about subtyping between the two constraints or the types they contain.)
¶Case 1: c₁ < c₂
d₁ = c₁ → c₂ = ¬c₁ ∨ c₂ var(d₁) = c₁ low(d₁) = 1 high(d₁) = d₂ var(d₂) = c₂ low(d₂) = 0 high(d₂) = 1
fn Simplify(d₁, u) { if u ≤ 1 then return u else if var(u) = c₁ then return Mk(c₁, Simplify(1, low(u)), Simplify(d₂, high(u))) else if c₁ < var(u) then return Mk(c₁, Simplify(1, u), Simplify(d₂, u)) else return Mk(var(u), Simplify(d₁, low(u)), Simplify(d₁, high(u))) }