In one of my classes at university, we are learning functional programming through SML/NJ.
I have been given an assignment which requires us to perform a number of operations on Binary Decision Diagrams. (conjunction, disjunction, not, etc.)
Based on this truth table
I have the following function defined
fun IfThenElse(p,q,r) =
if p then q else r;
Then, we have the BDD (ROBDD) datatype declared as such
datatype ROBDD = true
| false
| IfThenElse of string * ROBDD * ROBDD;
So far this is all fairly straightforward. I'm getting lost on actually operating on BDD's, for instance, creating an ROBDD which represents the conjunction of two ROBDD's.
My function declaration so far looks like this
infix bddAnd;
fun op bddAnd(first:ROBDD,second:ROBDD) =
...
It will be called with two ROBDD's, something like this
val conjunction = IfThenElse("p", true, false) bddAnd IfThenElse("q", true, false);
From here, I'm not really sure where to start. My professor has given us this hint:
Of course,
True bddAnd anyROBDD
will be justanyROBDD
. To get the ordering: If you’re asked to compute(IfThenElse(p, ϕ, ψ) bddAnd IfThenElse(q, χ, θ))
the proposition letter at the root of the resultant ROBDD will be either p or q – whichever is less. So you’ll need 3 cases:p < q
,p = q
, andp > q
. Having determined the root, you recurse down the branches
The first part makes sense, but I'm left with two questions.
1. How do I determine the root of any ROBDD?
If it's just true
or false
, it doesn't have one, right? So there should be a special case for just being given a boolean? If we are given a more fleshed out ROBDD, like IfThenElse("p", true, false)
, how do I gain access to p
, in the ROBDD structure? Note that the first argument of an IfThenElse
will always be a letter.
2. How do I recurse through the ROBDD's?
I understand the basics of recursive functions in SML, but I'm confused on how to do it within the ROBDD structure, compared to say, lists. I'm guessing I need to build some sort of curried functions that operate on each argument in the ROBDD, but I'm really not sure how to structure it.
Apologies for the long winded question, but I'm really having a hard time understanding how to operate on the ROBDD structure. Any explanation would be very helpful, thank you!
EDIT:
After some syntax and renaming, my bddAnd
function now looks like this
infix bddAnd;
fun op bddAnd (true, second) = second
| op bddAnd (first, true) = first
| op bddAnd (false, _) = false
| op bddAnd (_, false) = false
| op bddAnd ((left as (IfThenElse (prop1, true1, else1))), (right as (IfThenElse (prop2, true2, else2)))) =
if prop1 < prop2 then
IfThenElse (prop1, true1 bddAnd right, else1 bddAnd right)
else if prop1 > prop2 then
IfThenElse (prop2, true2 bddAnd left, else2 bddAnd left)
else
IfThenElse (prop1, true1 bddAnd right, else1 bddAnd left);
Pattern matching is usually a good starting point.
The cases involving True
and False
are simple:
fun op bddAnd (True, second) = second
| bddAnd (first, True) = first
| bddAnd (False, _) = False
| bddAnd (_, False) = False
the last one is more interesting:
| bddAnd (IfThenElse (v1, t1, e1)) (IfThenElse (v2, t2, e2)) = ... what? ...
As your professor hinted, you need to consider three cases for v1
and v2
:
if v1 < v2 then ...
else if v1 > v2 then ...
else ...
Looking at the first one, v1 < v2
, we should pick v1
as the "root".
It's not very difficult to convince yourself that
(IfThenElse p T1 E1) bddAnd (IfThenElse q T2 E2)
is equivalent to
IfThenElse p (T1 bddAnd (IfThenElse q T2 E2)) (E1 bddAnd (IfThenElse q T2 E2))
that is, you create one "tree" from two by recursing into both branches of the IfThenElse
you chose, bringing the other tree along.
The recursion will terminate since bddAnd
is applied to smaller and smaller arguments, and the result will be ordered as long as the input was ordered (which I assume we are allowed to assume).
Matching the code above,
| bddAnd (left as (IfThenElse (v1, t1, e1))) (right as (IfThenElse (v2, t2, e2))) =
if v1 < v2 then IfThenElse (v1, t1 bddAnd right, e1 bddAnd right)
else ...
(Using as
-patterns to make it easier to refer to the parameters as wholes.)
The remaining two cases left as an exercise.