package jazz.circuit.bdd; /////////////////////////////////////////////////////////////////////////////// // // Binary Decision Diagrams (BDD's) // // We use a class Bdd to wrap-up the class _Bdd since it is true that // "_Bdd abstracts BooleanAlgebra" but it is not the case that "_Bdd // implements BooleanAlgebra" (since the BooleanAlgebra interface requires // that all its operators (e.g., "&", "|", "^") be typed as follows // // <T: BooleanAlgebra>(x: T, y: T): T // // which is not the case of the implementation below for _Bdd). This wrapper // class will not be necessary when interfaces can be abstracted. Bdds // implement equality and the BooleanAlgebra interface. // /////////////////////////////////////////////////////////////////////////////// import jazz.circuit.*; import jazz.unsafe.Hashtable; import jazz.unsafe.Counter; public final class Bdd implements BooleanAlgebra { // Creates a new set of ordered variables public static newVars(n: int)(names: String[n]): Bdd[n]; // Creation of a bdd from a function computing the truth table. This method // evaluates "f" two to the power of "n" times. public static fromFunction(n: int) (vars: Bdd[n]) (f: fun(boolean[]): boolean): Bdd; // Returns a function computing the m bdds (with sharing) in a given boolean // algebra (e.g., two periodics, two nets, etc.), given the number of // arguments "n" of the function, an array giving the "n" variables over // which the bdd is computed (these variables are, in this order, the // arguments of the result function), as well as the proper values of "zero" // and "one" for the target boolean algebra. public static toFunction<T: BooleanAlgebra>(n: int) (vars: Bdd[n]) (m: int) (bdds: Bdd[m]) (zero: T, one: T) : fun(T[]): T[]; // Bdd constants public static zero: Bdd; public static one: Bdd; // Private definition of the bdd bdd: _Bdd; } /////////////////////////////////////////////////////////////////////////////// // // Implementation // /////////////////////////////////////////////////////////////////////////////// abstract class _Bdd { id: int; definition(): String; } // Constants zero class _Zero extends _Bdd { } // Constants one class _One extends _Bdd { } // Variables class _Var extends _Bdd implements Comparable { name: String; } // Internal bdd nodes class _Node extends _Bdd { // The variables used to differentiate the two branches v: _Var; // The two branches "var = false" and "var = true" tt: _Bdd; ff: _Bdd; } // Class constants Bdd.zero = new Bdd(bdd = _zero); Bdd.one = new Bdd(bdd = _one); // Global variables and constants var table = Hashtable.create(); var counter = Counter.create(0); var _zero = new _Zero(id = counter.next()); var _one = new _One(id = counter.next()); // Equality of bdds Builtin.(==)(b1@Bdd, b2@Bdd) = (b1.bdd == b2.bdd); // Display toString@Bdd() = bdd.definition(); // Creates a new variable Bdd.newVars(n)(names) = vars { vars = [new Bdd(bdd = newVar(names[0])), i -> (vars[i-1].bdd; new Bdd(bdd = newVar(names[i])))]; } // Creation of a bdd from a truth table represented by a function Bdd.fromFunction(n)(vars)(fn) = new Bdd(bdd = create(n)([])) { // Leaf node fun leaf(args) = fn(args) ? _one : _zero; // Update the argument array with the value of one more variable fun update(k)(args, v) = [i -> i == k ? v : args[i]]; // Create the bdd fun create(k)(args) = (k == 0 ? leaf(args) : cond((_Var) vars[n - k].bdd, create(k-1)(update(n - k)(args, true)), create(k-1)(update(n - k)(args, false)))); } // Boolean algebra operators Builtin.(&)(x@Bdd, y@Bdd) = new Bdd(bdd = and(x.bdd, y.bdd)); Builtin.(|)(x@Bdd, y@Bdd) = new Bdd(bdd = or(x.bdd, y.bdd)); Builtin.(~)(x@Bdd) = new Bdd(bdd = not(x.bdd)); Builtin.(^)(x@Bdd, y@Bdd) = new Bdd(bdd = or(and(x.bdd, not(y.bdd)), and(not(x.bdd), y.bdd))); Builtin.cond(x@Bdd, y@Bdd, z@Bdd) = new Bdd(bdd = or(and(x.bdd, y.bdd), and(not(x.bdd), z.bdd))); // Return a function computing the bdd over some boolean algebra Bdd.toFunction<T>(n)(vars)(m)(bdds)(z, o) = fn { // The function computing the bdd fun fn(args) = (defs.assoc(n)(ids, args) ; definitions) { // Hash table to track nodes and variables with known definitions defs = Hashtable.create(); // Array of variable ids ids = [i -> vars[i].bdd.id]; // Definitions of the bdds definitions = [i -> definition(bdds[i].bdd)]; // Definition of a bdd fun definition(b: _Bdd): T; definition(b@_Zero) = z; definition(b@_One) = o; definition(v@_Var) = defs.get(v.id, error("undefined variable")); definition(n@_Node) = defs.get(n.id, Builtin.cond(definition(n.v), definition(n.tt), definition(n.ff))); } } // Equality of bdds Builtin.(==)(b1@_Bdd, b2@_Bdd) = (b1.id == b2.id); // Variable ordering Builtin.(<=)(v1@_Var, v2@_Var) = (v1.id <= v2.id); Builtin.(==)(v1@_Var, v2@_Var) = (v1.id == v2.id); toString@_Bdd() = format("N%d", id); definition@_Bdd() = def(this) { // Name after renumbering fun name(n: _Bdd): String; // Display of a bdd node fun str(b: _Bdd): String; // Local variables used to compute the bdd fun locals(b: _Bdd): String; // Definition of a bdd fun def(b: _Bdd): String; // Hash table to track bdds that have already been displayed var displayed = Hashtable.create(); // Counter used to renumber the nodes var counter = Counter.create(0); // Renumbering var number = Hashtable.create(); name(n@_Bdd) = format("n%d", number.get(n.id, counter.next())); def(b@_Bdd) = str(b); def(n@_Node) = format("%s {%s\n}", name(n), locals(n)); str(c@_Zero) = "false"; str(c@_One) = "true"; str(v@_Var) = format("%s", v.name); str(n@_Node) = name(n); locals(b@_Bdd) = ""; locals(n@_Node) = disp ? (nn; ntt; ltt; nff; lff ; format("\n %s = %s ? %s : %s;%s%s", nn, n.v.name, ntt, nff, ltt, lff)) : "" { // The value "disp" is true iff the key "n.id" has been put in the table. disp = displayed.check(n.id, 0); // Use these definitions to force the evaluation order (and the numbering) nn = name(n); ntt = str(n.tt); nff = str(n.ff); ltt = locals(n.tt); lff = locals(n.ff); } } // Creates a new variable (make sure the ids are assigned in sequence) fun newVar(name) = (id; new _Var(name = name, id = id)) { id = counter.next(); } // Creates or retrieves the appropriate node fun cond(v:_Var, tt: _Bdd, ff: _Bdd) = (ff == tt ? ff : (ff == _zero && tt == _one) ? v : table.get(key, n)) { // Key used to lookup the table of already built nodes key = (v.id << 32) | (tt.id << 16) | (ff.id); // New node n = new _Node(id = counter.next(), v = v, tt = tt, ff = ff); } // And fun and(x: _Bdd, y: _Bdd): _Bdd; and(x@_Zero, y@_Bdd) = x; and(x@_One, y@_Bdd) = y; and(x@_Var, y@_Bdd) = and(y, x); and(x@_Var, y@_Var) = (x == y ? x : x < y ? cond(x, y, _zero) : cond(y, x, _zero)); and(x@_Node, y@_Bdd) = and(y, x); and(x@_Node, y@_Var) = (x.v == y ? cond(x.v, x.tt, _zero) : x.v < y ? cond(x.v, and(x.tt, y), and(x.ff, y)) : cond(y, x, _zero)); and(x@_Node, y@_Node) = (x.v == y.v ? cond(x.v, and(x.tt, y.tt), and(x.ff, y.ff)) : x.v < y.v ? cond(x.v, and(x.tt, y), and(x.ff, y)) : cond(y.v, and(x, y.tt), and(x, y.ff))); // Or fun or(x: _Bdd, y: _Bdd): _Bdd; or(x@_Bdd, y@_Bdd) = not(and(not(x), not(y))); // Not fun not(x: _Bdd): _Bdd; not(x@_Zero) = _one; not(x@_One) = _zero; not(x@_Var) = cond(x, _zero, _one); not(x@_Node) = cond(x.v, not(x.tt), not(x.ff));