def test_image_rename_map_checks(): ordering = {'x': 0, 'xp': 1, 'y': 2, 'yp': 3, 'z': 4, 'zp': 5} bdd = BDD(ordering) # non-adjacent rename = {0: 2, 3: 4} qvars = set() r = _bdd.image(1, 1, rename, qvars, bdd) assert r == 1, r r = _bdd.preimage(1, 1, rename, qvars, bdd) assert r == 1, r # overlapping keys and values rename = {0: 1, 1: 2} with nt.assert_raises(AssertionError): _bdd.image(1, 1, rename, qvars, bdd) with nt.assert_raises(AssertionError): _bdd.preimage(1, 1, rename, qvars, bdd) # may be in support after quantification ? trans = bdd.add_expr('x -> xp') source = bdd.add_expr('x & y') qvars = {0} rename = {1: 0, 3: 2} with nt.assert_raises(AssertionError): _bdd.image(trans, source, rename, qvars, bdd) # in support of `target` ? qvars = set() target = bdd.add_expr('y & yp') rename = {2: 3} with nt.assert_raises(AssertionError): _bdd.preimage(trans, target, rename, qvars, bdd)
def test_collect_garbage(): # all nodes are garbage g = BDD({'x': 0, 'y': 1}) u = g.add_expr('x & y') n = len(g) assert n == 4, n uref = g._ref[abs(u)] assert uref == 0, uref _, v, w = g._succ[abs(u)] vref = g._ref[abs(v)] wref = g._ref[w] assert vref == 5, vref assert wref == 1, wref g.collect_garbage() n = len(g) assert n == 1, n assert u not in g, g._succ assert w not in g, g._succ # some nodes not garbage # projection of x is garbage g = BDD({'x': 0, 'y': 1}) u = g.add_expr('x & y') n = len(g) assert n == 4, n g._ref[abs(u)] += 1 uref = g._ref[abs(u)] assert uref == 1, uref g.collect_garbage() n = len(g) assert n == 3, n
def test_level_to_variable(): ordering = {'x': 0, 'y': 1} g = BDD(ordering) assert g.var_at_level(0) == 'x' assert g.var_at_level(1) == 'y' with nt.assert_raises(AssertionError): g.var_at_level(10)
def test_image_rename_map_checks(): ordering = {'x': 0, 'xp': 1, 'y': 2, 'yp': 3, 'z': 4, 'zp': 5} bdd = BDD(ordering) # non-adjacent rename = {0: 2, 3: 4} qvars = set() with nt.assert_raises(AssertionError): _bdd.image(1, 1, rename, qvars, bdd) with nt.assert_raises(AssertionError): _bdd.preimage(1, 1, rename, qvars, bdd) # overlapping keys and values rename = {0: 1, 1: 2} with nt.assert_raises(AssertionError): _bdd.image(1, 1, rename, qvars, bdd) with nt.assert_raises(AssertionError): _bdd.preimage(1, 1, rename, qvars, bdd) # may be in support after quantification ? trans = bdd.add_expr('x -> xp') source = bdd.add_expr('x & y') qvars = {0} rename = {1: 0, 3: 2} with nt.assert_raises(AssertionError): _bdd.image(trans, source, rename, qvars, bdd) # in support of `target` ? qvars = set() target = bdd.add_expr('y & yp') rename = {2: 3} with nt.assert_raises(AssertionError): _bdd.preimage(trans, target, rename, qvars, bdd)
def test_elimination(): ordering = {'x': 0, 'y': 1} g = BDD(ordering) g.roots.add(2) # high == low, so node 2 is redundant g._succ[2] = (0, 3, 3) g._succ[3] = (1, -1, 1) h = g.reduction() assert set(h) == {1, 2}
def x_and_not_y(): # remember: # 2 = !(x & !y) # -2 = x & !y ordering = {'x': 0, 'y': 1} g = BDD(ordering) u = 3 v = -1 w = 1 t = (1, v, w) g._succ[u] = t g._pred[t] = u g._ref[abs(v)] += 1 g._ref[abs(w)] += 1 g._ref[abs(u)] = 0 u = 2 v = 1 w = 3 t = (0, v, w) g._succ[u] = t g._pred[t] = u g._ref[abs(v)] += 1 g._ref[abs(w)] += 1 g._ref[abs(u)] = 0 g._min_free = 4 return g
def test_add_expr(): ordering = {'x': 0, 'y': 1} g = BDD(ordering) # x ix = ordering['x'] u = g.add_expr('x') h = ref_var(ix) compare(u, g, h) # x and y u = g.add_expr('x && y') h = ref_x_and_y() compare(u, g, h)
def test_isomorphism(): ordering = {'x': 0} g = BDD(ordering) g.roots.update([2, 3]) g._succ[2] = (0, -1, 1) g._succ[3] = (0, -1, 1) h = g.reduction() assert set(h) == {1, 2}, set(h) assert 0 not in h assert h._succ[1] == (1, None, None) assert h._succ[2] == (0, -1, 1) assert h.roots == {2}
def test_rename(): ordering = {'x': 0, 'xp': 1} g = BDD(ordering) x = g.add_expr('x') xp = g.add_expr('xp') dvars = {'x': 'xp'} xrenamed = _bdd.rename(x, g, dvars) assert xrenamed == xp, xrenamed ordering = {'x': 0, 'xp': 1, 'y': 2, 'yp': 3, 'z': 4, 'zp': 5} g = BDD(ordering) u = g.add_expr('x && y && ! z') dvars = {'x': 'xp', 'y': 'yp', 'z': 'zp'} urenamed = _bdd.rename(u, g, dvars) up = g.add_expr('xp && yp && ! zp') assert urenamed == up, urenamed # assertion violations # non-neighbors dvars = {'x': 'yp'} with nt.assert_raises(AssertionError): _bdd.rename(u, g, dvars) # u not in bdd dvars = {'x': 'xp'} with nt.assert_raises(AssertionError): _bdd.rename(15, g, dvars) # y essential for u dvars = {'xp': 'y'} with nt.assert_raises(AssertionError): _bdd.rename(u, g, dvars) # old and new vars intersect dvars = {'x': 'x'} with nt.assert_raises(AssertionError): _bdd.rename(u, g, dvars)
def test_reduce_combined(): """Fig.5 in 1986 Bryant TOC""" ordering = {'x': 0, 'y': 1, 'z': 2} g = BDD(ordering) g.roots.add(2) g._succ[2] = (0, 3, 4) g._succ[3] = (1, -1, 5) g._succ[4] = (1, 5, 6) g._succ[5] = (2, -1, 1) g._succ[6] = (2, -1, 1) h = g.reduction() assert 1 in h assert ordering == h.ordering r = nx.MultiDiGraph() r.add_node(1, level=3) r.add_node(2, level=0) r.add_node(3, level=1) r.add_node(4, level=2) r.add_edge(2, 3, value=False, complement=False) r.add_edge(2, 4, value=True, complement=False) r.add_edge(3, 4, value=True, complement=False) r.add_edge(3, 1, value=False, complement=True) r.add_edge(4, 1, value=False, complement=True) r.add_edge(4, 1, value=True, complement=False) (u, ) = h.roots compare(u, h, r)
def test_dump_load_manager(): prefix = 'test_dump_load_manager' g = BDD({'x': 0, 'y': 1}) e = 'x & !y' u = g.add_expr(e) g.incref(u) fname = prefix + '.p' g._dump_manager(fname) h = g._load_manager(fname) assert g.assert_consistent() u_ = h.add_expr(e) assert u == u_, (u, u_)
def test_descendants(): ordering = dict(x=0, y=1) b = BDD(ordering) u = b.add_expr('x && y') v = b.add_expr('x | y') roots = [u, v] nodes = b.descendants(roots) nodes_u = b.descendants([u]) nodes_v = b.descendants([v]) assert u in nodes_u, nodes_u assert v in nodes_v, nodes_v assert u in nodes, nodes assert v in nodes, nodes assert 1 in nodes_u, nodes_u assert 1 in nodes_v, nodes_v assert 1 in nodes, nodes assert len(nodes_u) == 3, nodes_u assert len(nodes_v) == 3, nodes_v assert nodes_u != nodes_v, (nodes_u, nodes_v) assert len(nodes) == 4, nodes assert nodes == nodes_u.union(nodes_v), (nodes, b._succ) # no roots roots = [] nodes = b.descendants(roots) assert len(nodes) == 0, nodes
def test_rename(): ordering = {'x': 0, 'xp': 1} g = BDD(ordering) x = g.add_expr('x') xp = g.add_expr('xp') dvars = {'x': 'xp'} xrenamed = _bdd.rename(x, g, dvars) assert xrenamed == xp, xrenamed ordering = {'x': 0, 'xp': 1, 'y': 2, 'yp': 3, 'z': 4, 'zp': 5} g = BDD(ordering) u = g.add_expr('x && y && ! z') dvars = {'x': 'xp', 'y': 'yp', 'z': 'zp'} urenamed = _bdd.rename(u, g, dvars) up = g.add_expr('xp && yp && ! zp') assert urenamed == up, urenamed # assertion violations # non-neighbors dvars = {'x': 'yp'} r = _bdd.rename(u, g, dvars) r_ = g.add_expr('yp && y && ! z') assert r == r_, (r, r_) # u not in bdd dvars = {'x': 'xp'} with nt.assert_raises(AssertionError): _bdd.rename(1000, g, dvars) # y essential for u dvars = {'xp': 'y'} with nt.assert_raises(AssertionError): _bdd.rename(u, g, dvars) # old and new vars intersect dvars = {'x': 'x'} with nt.assert_raises(AssertionError): _bdd.rename(u, g, dvars)
def two_vars_xy(): ordering = {'x': 0, 'y': 1} g = BDD(ordering) u = 2 t = (0, -1, 1) g._succ[u] = t g._pred[t] = u g._ref[u] = 1 u = 3 t = (1, -1, 1) g._succ[u] = t g._pred[t] = u g._ref[u] = 1 g._min_free = u + 1 return g
def test_bdd_to_mdd(): from dd.bdd import BDD ordering = {'x': 0, 'y': 1} bdd = BDD(ordering) u = bdd.add_expr('x & ! y') bdd.incref(u) # BDD -> MDD dvars = dict( x=dict(level=1, len=2, bitnames=['x']), y=dict(level=0, len=2, bitnames=['y'])) mdd, umap = dd.mdd.bdd_to_mdd(bdd, dvars) # pd = dd.mdd.to_pydot(mdd) # pd.write_pdf('mdd.pdf') # bdd.dump('bdd.pdf') v = umap[abs(u)] if u < 0: v = -v print(v)
def test_pick_iter(): # x & y g = x_and_y() u = 4 bits = {'x', 'y'} s = [{'x': 1, 'y': 1}] compare_iter_to_list_of_sets(u, g, s, bits) # care_bits == support (default) bits = None compare_iter_to_list_of_sets(u, g, s, bits) # # x | y g = x_or_y() u = 4 # support bits = None s = [{'x': 1, 'y': 0}, {'x': 1, 'y': 1}, {'x': 0, 'y': 1}] compare_iter_to_list_of_sets(u, g, s, bits) # only what appears along traversal bits = set() s = [{'x': 1}, {'x': 0, 'y': 1}] compare_iter_to_list_of_sets(u, g, s, bits) # bits < support bits = {'x'} s = [{'x': 1}, {'x': 0, 'y': 1}] compare_iter_to_list_of_sets(u, g, s, bits) bits = {'y'} s = [{'x': 1, 'y': 0}, {'x': 1, 'y': 1}, {'x': 0, 'y': 1}] compare_iter_to_list_of_sets(u, g, s, bits) # # x & !y g = x_and_not_y() u = -2 bits = {'x', 'y'} s = [{'x': 1, 'y': 0}] compare_iter_to_list_of_sets(u, g, s, bits) # gaps in order order = {'x': 0, 'y': 1, 'z': 2} bdd = BDD(order) u = bdd.add_expr('x & z') (m, ) = bdd.pick_iter(u) assert m == {'x': 1, 'z': 1}, m
def test_dump_load(): prefix = 'test_dump_load' g = BDD({'x': 0, 'y': 1}) e = 'x & !y' u = g.add_expr(e) g.incref(u) fname = prefix + '.p' g.dump(fname) h = BDD.load(fname) assert h.assert_consistent() u_ = h.add_expr(e) assert u == u_, (u, u_) h.dump(prefix + '.pdf')
def test_descendants(): ordering = dict(x=0, y=1) b = BDD(ordering) u = b.add_expr('x && y') v = b.add_expr('x | y') roots = [u, v] nodes = b.descendants(roots) nodes_u = b.descendants([u]) nodes_v = b.descendants([v]) assert u in nodes_u, nodes_u assert v in nodes_v, nodes_v assert u in nodes, nodes assert v in nodes, nodes assert 1 in nodes_u, nodes_u assert 1 in nodes_v, nodes_v assert 1 in nodes, nodes assert len(nodes_u) == 3, nodes_u assert len(nodes_v) == 3, nodes_v assert nodes_u != nodes_v, (nodes_u, nodes_v) assert len(nodes) == 4, nodes assert nodes == nodes_u.union(nodes_v), ( nodes, b._succ) # no roots roots = [] nodes = b.descendants(roots) assert len(nodes) == 0, nodes
def test_sat_iter(): # x & y g = x_and_y() u = 2 s = [{'x': 1, 'y': 1}] compare_iter_to_list_of_sets(u, g, s) # x | y g = x_or_y() u = 2 s = [{'x': 1}, {'x': 0, 'y': 1}] compare_iter_to_list_of_sets(u, g, s) # x & !y g = x_and_not_y() u = -2 s = [{'x': 1, 'y': 0}] compare_iter_to_list_of_sets(u, g, s) # gaps in order order = {'x': 0, 'y': 1, 'z': 2} bdd = BDD(order) u = bdd.add_expr('x & z') (m,) = bdd.sat_iter(u) assert m == {'x': 1, 'z': 1}, m
def test_sat_iter(): # x & y g = x_and_y() u = 2 s = [{'x': 1, 'y': 1}] compare_iter_to_list_of_sets(u, g, s) # x | y g = x_or_y() u = 2 s = [{'x': 1}, {'x': 0, 'y': 1}] compare_iter_to_list_of_sets(u, g, s) # x & !y g = x_and_not_y() u = -2 s = [{'x': 1, 'y': 0}] compare_iter_to_list_of_sets(u, g, s) # gaps in order order = {'x': 0, 'y': 1, 'z': 2} bdd = BDD(order) u = bdd.add_expr('x & z') (m, ) = bdd.sat_iter(u) assert m == {'x': 1, 'z': 1}, m
def test_top_cofactor(): ordering = {'x': 0, 'y': 1} g = BDD(ordering) x = ordering['x'] y = ordering['y'] u = g.find_or_add(y, -1, 1) assert g._top_cofactor(u, x) == (u, u) assert g._top_cofactor(u, y) == (-1, 1) u = g.find_or_add(x, -1, 1) assert g._top_cofactor(u, x) == (-1, 1) assert g._top_cofactor(-u, x) == (1, -1)
def test_var(): b = BDD() with nt.assert_raises(AssertionError): b.var('x') j = b.add_var('x') u = b.var('x') assert u > 0, u level, low, high = b.succ(u) assert level == j, (level, j) assert low == b.false, low assert high == b.true, high
def load(fname): """Return a `BDD` loaded from DDDMP file `fname`. If no `.orderedvarnames` appear in the file, then `.suppvarnames` and `.permids` are used instead. In the second case, the variable levels contains blanks. To avoid blanks, the levels are re-indexed here. This has no effect if `.orderedvarnames` appears in the file. DDDMP files are dumped by [CUDD](http://vlsi.colorado.edu/~fabio/CUDD/). """ parser = Parser() bdd_succ, n_vars, ordering, roots = parser.parse(fname) # reindex to ensure no blanks perm = {k: var for var, k in ordering.iteritems()} perm = {i: perm[k] for i, k in enumerate(sorted(perm))} new_ordering = {var: k for k, var in perm.iteritems()} old2new = {ordering[var]: new_ordering[var] for var in ordering} # convert bdd = BDD(new_ordering) umap = {-1: -1, 1: 1} for j in xrange(len(new_ordering) - 1, -1, -1): for u, (k, v, w) in bdd_succ.iteritems(): # terminal ? if v is None: assert w is None, w continue # non-terminal i = old2new[k] if i != j: continue p, q = umap[abs(v)], umap[w] if v < 0: p = -p r = bdd.find_or_add(i, p, q) umap[abs(u)] = r bdd.roots.update(roots) return bdd
def test_bdd_to_mdd(): from dd.bdd import BDD ordering = {'x': 0, 'y': 1} bdd = BDD(ordering) u = bdd.add_expr('x & ! y') bdd.incref(u) # BDD -> MDD dvars = dict(x=dict(level=1, len=2, bitnames=['x']), y=dict(level=0, len=2, bitnames=['y'])) mdd, umap = dd.mdd.bdd_to_mdd(bdd, dvars) pd = dd.mdd.to_pydot(mdd) pd.write_pdf('mdd.pdf') bdd.dump('bdd.pdf') v = umap[abs(u)] if u < 0: v = -v print(v)
def test_ite(): ordering = {'x': 0, 'y': 1} g = BDD(ordering) # x ix = ordering['x'] x = g.find_or_add(ix, -1, 1) h = ref_var(ix) compare(x, g, h) # y iy = ordering['y'] y = g.find_or_add(iy, -1, 1) h = ref_var(iy) compare(y, g, h) # x and y u = g.ite(x, y, -1) h = ref_x_and_y() compare(u, g, h) # x or y u = g.ite(x, 1, y) h = ref_x_or_y() compare(u, g, h) # negation assert g.ite(x, -1, 1) == -x, g._succ assert g.ite(-x, -1, 1) == x, g._succ
def test_swap(): # x, y g = BDD({'x': 0, 'y': 1}) x = g.add_expr('x') y = g.add_expr('y') g.incref(x) g.incref(y) n = len(g) assert n == 3, n nold, n = g.swap('x', 'y') assert n == 3, n assert nold == n, nold assert g.ordering == {'y': 0, 'x': 1}, g.ordering assert g.assert_consistent() # functions remain invariant x_ = g.add_expr('x') y_ = g.add_expr('y') assert x == x_, (x, x_, g._succ) assert y == y_, (y, y_, g._succ) # external reference counts remain unchanged assert g._ref[abs(x)] == 1 assert g._ref[abs(y)] == 1 # x & y g = BDD({'x': 0, 'y': 1}) u = g.add_expr('x & y') g.incref(u) nold, n = g.swap('x', 'y') assert nold == n, (nold, n) assert g.ordering == {'y': 0, 'x': 1}, g.ordering u_ = g.add_expr('x & y') assert u == u_, (u, u_) assert g.assert_consistent() # reference counts unchanged assert g._ref[abs(u)] == 1 # x & !y # tests handling of complement edges e = 'x & !y' g = x_and_not_y() u = g.add_expr(e) g.incref(u) g.collect_garbage() n = len(g) assert n == 3, n nold, n = g.swap('x', 'y') assert n == 3, n assert nold == n, nold assert g.ordering == {'x': 1, 'y': 0} assert g.assert_consistent() u_ = g.add_expr(e) # function u must have remained unaffected assert u_ == u, (u, u_, g._succ) # invert swap of: # x & !y nold, n = g.swap('x', 'y') assert n == 3, n assert nold == n, nold assert g.ordering == {'x': 0, 'y': 1} assert g.assert_consistent() u_ = g.add_expr(e) assert u_ == u, (u, u_, g._succ) # Figs. 6.24, 6.25 Baier 2008 g = BDD({'z1': 0, 'y1': 1, 'z2': 2, 'y2': 3, 'z3': 4, 'y3': 5}) u = g.add_expr('(z1 & y1) | (z2 & y2) | (z3 & y3)') g.incref(u) n = len(g) assert n == 16, n g.collect_garbage() n = len(g) assert n == 7, n # sift to inefficient order g.swap('y1', 'z2') # z1, z2, y1, y2, z3, y3 g.swap('y2', 'z3') # z1, z2, y1, z3, y2, y3 g.swap('y1', 'z3') # z1, z2, z3, y1, y2, y3 n = len(g) assert n == 15, n assert g.assert_consistent() new_ordering = {'z1': 0, 'z2': 1, 'z3': 2, 'y1': 3, 'y2': 4, 'y3': 5} assert g.ordering == new_ordering, g.ordering u_ = g.add_expr('(z1 & y1) | (z2 & y2) | (z3 & y3)') assert u_ == u, (u, u_, g._succ)
def test_cofactor(): ordering = {'x': 0, 'y': 1, 'z': 2} g = BDD(ordering) # u not in g with nt.assert_raises(AssertionError): g.cofactor(5, {'x': 0, 'y': 1, 'z': 0}) # x & y e = g.add_expr('x && y') x = g.add_expr('x') assert g.cofactor(x, {'x': 0}) == -1 assert g.cofactor(x, {'x': 1}) == 1 assert g.cofactor(-x, {'x': 0}) == 1 assert g.cofactor(-x, {'x': 1}) == -1 y = g.add_expr('y') assert g.cofactor(e, {'x': 1}) == y assert g.cofactor(e, {'x': 0}) == -1 assert g.cofactor(e, {'y': 1}) == x assert g.cofactor(e, {'y': 0}) == -1 assert g.cofactor(-e, {'x': 0}) == 1 assert g.cofactor(-e, {'x': 1}) == -y assert g.cofactor(-e, {'y': 0}) == 1 assert g.cofactor(-e, {'y': 1}) == -x
def test_compose(): ordering = {'x': 0, 'y': 1, 'z': 2} g = BDD(ordering) # x & (x | z) a = g.add_expr('x && y') b = g.add_expr('x || z') c = g.compose(a, 'y', b) d = g.add_expr('x && (x || z)') assert c == d, (c, d) # (y | z) & x ordering = {'x': 0, 'y': 1, 'z': 2, 'w': 3} g = BDD(ordering) a = g.add_expr('(x && y) || z') b = g.add_expr('(y || z) && x') c = g.compose(a, 'z', b) assert c == b, (c, b) # long expr ordering = {'x': 0, 'y': 1, 'z': 2, 'w': 3} g = BDD(ordering) a = g.add_expr('(x && y) || (!z || (w && y && x))') b = g.add_expr('(y || z) && x') c = g.compose(a, 'y', b) d = g.add_expr('(x && ((y || z) && x)) ||' ' (!z || (w && ((y || z) && x) && x))') assert c == d, (c, d) # complemented edges ordering = {'x': 0, 'y': 1} g = BDD(ordering) f = g.add_expr('x <-> y') var = 'y' new_level = 0 var_node = g.find_or_add(new_level, -1, 1) u = g.compose(f, var, var_node) assert u == 1, g.to_expr(u)
def test_find_or_add(): ordering = {'x': 0, 'y': 1} g = BDD(ordering) # init n = len(g) m = g._min_free assert n == 1, n assert m == 2, m # elimination rule i = 0 v = -1 w = 1 n = len(g) u = g.find_or_add(i, v, v) n_ = len(g) assert n == n_, (n, n_) assert u == v, (u, v) assert not g._pred, g._pred # unchanged min_free v = 1 m = g._min_free g.find_or_add(i, v, v) m_ = g._min_free assert m_ == m, (m_, m) # add new node g = BDD(ordering) v = -1 w = 1 n = len(g) m = g._min_free assert n == 1, n u = g.find_or_add(i, v, w) n_ = len(g) m_ = g._min_free assert u != v, (u, v) assert n_ == n + 1, (n, n_) assert m_ == m + 1, (m, m_) assert g._succ[u] == (i, -1, 1) assert (i, v, w) in g._pred assert abs(u) in g._ref assert g._ref[abs(u)] == 0 assert g._ref[abs(v)] == 2, g._ref # independent increase of reference counters v = u w = w refv = g._ref[abs(v)] refw = g._ref[w] u = g.find_or_add(i, v, w) refv_ = g._ref[abs(v)] refw_ = g._ref[w] assert refv + 1 == refv_, (refv, refv_) assert refw + 1 == refw_, (refw, refw_) # add existing n = len(g) m = g._min_free refv = g._ref[abs(v)] refw = g._ref[w] r = g.find_or_add(i, v, w) n_ = len(g) m_ = g._min_free refv_ = g._ref[abs(v)] refw_ = g._ref[w] assert n == n_, (n, n_) assert m == m_, (m, m_) assert u == r, u assert refv == refv_, (refv, refv_) assert refw == refw_, (refw, refw_) # only non-terminals can be added with nt.assert_raises(AssertionError): g.find_or_add(2, -1, 1) # low and high must already exist with nt.assert_raises(AssertionError): g.find_or_add(0, 3, 4) # canonicity of complemented edges # v < 0, w > 0 g = BDD(ordering) i = 0 v = -1 w = 1 u = g.find_or_add(i, v, w) assert u > 0, u # v > 0, w < 0 v = 1 w = -1 u = g.find_or_add(i, v, w) assert u < 0, u assert abs(u) in g._succ, u _, v, w = g._succ[abs(u)] assert v < 0, v assert w > 0, w # v < 0, w < 0 v = -1 w = -2 u = g.find_or_add(i, v, w) assert u < 0, u _, v, w = g._succ[abs(u)] assert v > 0, v assert w > 0, w
def test_next_free_int(): g = BDD() # contiguous g._succ = {1, 2, 3} n = g._next_free_int(start=1, debug=True) assert n == 4, n n = g._next_free_int(start=3, debug=True) assert n == 4, n # with blanks g._succ = {1, 3} n = g._next_free_int(start=1, debug=True) assert n == 2, n n = g._next_free_int(start=3) assert n == 4, n # full g._succ = {1, 2, 3} g.max_nodes = 3 with nt.assert_raises(Exception): g._next_free_int(start=1)
def test_compose(): ordering = {'x': 0, 'y': 1, 'z': 2} g = BDD(ordering) # x & (x | z) a = g.add_expr('x && y') b = g.add_expr('x || z') c = g.compose(a, 'y', b) d = g.add_expr('x && (x || z)') assert c == d, (c, d) # (y | z) & x ordering = {'x': 0, 'y': 1, 'z': 2, 'w': 3} g = BDD(ordering) a = g.add_expr('(x && y) || z') b = g.add_expr('(y || z) && x') c = g.compose(a, 'z', b) assert c == b, (c, b) # long expr ordering = {'x': 0, 'y': 1, 'z': 2, 'w': 3} g = BDD(ordering) a = g.add_expr('(x && y) || (!z || (w && y && x))') b = g.add_expr('(y || z) && x') c = g.compose(a, 'y', b) d = g.add_expr( '(x && ((y || z) && x)) ||' ' (!z || (w && ((y || z) && x) && x))') assert c == d, (c, d) # complemented edges ordering = {'x': 0, 'y': 1} g = BDD(ordering) f = g.add_expr('x <-> y') var = 'y' new_level = 0 var_node = g.find_or_add(new_level, -1, 1) u = g.compose(f, var, var_node) assert u == 1, g.to_expr(u)
def test_quantify(): ordering = {'x': 0, 'y': 1, 'z': 2} g = BDD(ordering) # x & y e = g.add_expr('x && ! y') x = g.add_expr('x') not_y = g.add_expr('! y') assert g.quantify(e, {'x'}) == not_y assert g.quantify(e, {'x'}, forall=True) == -1 assert g.quantify(e, {'y'}) == x assert g.quantify(e, {'x'}, forall=True) == -1 # x | y | z e = g.add_expr('x || y || z') xy = g.add_expr('x || y') yz = g.add_expr('y || z') zx = g.add_expr('z || x') assert g.quantify(e, {'x'}) assert g.quantify(e, {'y'}) assert g.quantify(e, {'z'}) assert g.quantify(e, {'z'}, forall=True) == xy assert g.quantify(e, {'x'}, forall=True) == yz assert g.quantify(e, {'y'}, forall=True) == zx # complement edges u = -x v = g.quantify(u, {'y'}, forall=True) assert v == -x, g.to_expr(v) # multiple values: test recursion e = g.add_expr('x & y & z') x = g.add_expr('x') r = g.quantify(e, {'y', 'z'}) assert r == x, r
def test_rename_syntax(): b = BDD() [b.add_var(var) for var in ['x', 'y', 'z', 'w']] # single substitution u = b.add_expr('\S y / x: True') assert u == b.true, u u = b.add_expr('\S y / x: False') assert u == b.false, u u = b.add_expr('\S y / x: x') u_ = b.add_expr('y') assert u == u_, (u, u_) u = b.add_expr('\S y / x: z') u_ = b.add_expr('z') assert u == u_, (u, u_) u = b.add_expr('\S y / x: x & z') u_ = b.add_expr('y & z') assert u == u_, (u, u_) # multiple substitution u = b.add_expr('\S y / x, w / z: x & z') u_ = b.add_expr('y & w') assert u == u_, (u, u_) u = b.add_expr('\S y / x, w / z: z | ! x') u_ = b.add_expr('w | ! y') assert u == u_, (u, u_)
def test_sifting(): # Figs. 6.24, 6.25 Baier 2008 g = BDD({'z1': 0, 'z2': 1, 'z3': 2, 'y1': 3, 'y2': 4, 'y3': 5}) u = g.add_expr('(z1 & y1) | (z2 & y2) | (z3 & y3)') g.incref(u) g.collect_garbage() n = len(g) assert n == 15, n _bdd.reorder(g) u_ = g.add_expr('(z1 & y1) | (z2 & y2) | (z3 & y3)') g.incref(u) g.collect_garbage() g.assert_consistent() assert u == u_, (u, u_)
def test_dump_load(): prefix = 'test_dump_load' fname = prefix + '.p' dvars = dict(x=0, y=1) # dump b = BDD(dvars) e = 'x & !y' u_dumped = b.add_expr(e) b.dump(fname, [u_dumped]) b.dump(fname) # no roots # load b = BDD(dvars) b.add_expr('x | y') u_new = b.add_expr(e) umap = b.load(fname) u_loaded = umap[abs(u_dumped)] if u_dumped < 0: u_loaded = -u_loaded assert u_loaded == u_new, ( u_dumped, u_loaded, u_new, umap) assert b.assert_consistent()
def test_quantifier_syntax(): b = BDD() [b.add_var(var) for var in ['x', 'y']] # constants u = b.add_expr('\E x: True') assert u == b.true, u u = b.add_expr('\E x, y: True') assert u == b.true, u u = b.add_expr('\E x: False') assert u == b.false, u u = b.add_expr('\A x: True') assert u == b.true, u u = b.add_expr('\A x: False') assert u == b.false, u u = b.add_expr('\A x, y: False') assert u == b.false, u # variables u = b.add_expr('\E x: x') assert u == b.true, u u = b.add_expr('\A x: x') assert u == b.false, u u = b.add_expr('\E x, y: x') assert u == b.true, u u = b.add_expr('\E x, y: y') assert u == b.true, u u = b.add_expr('\A x: y') assert u == b.var('y'), u u = b.add_expr('\A x: ! y') u_ = b.apply('not', b.var('y')) assert u == u_, (u, u_)
def test_swap(): # x, y g = BDD({'x': 0, 'y': 1}) x = g.add_expr('x') y = g.add_expr('y') g.incref(x) g.incref(y) n = len(g) assert n == 3, n nold, n = g.swap('x', 'y') assert n == 3, n assert nold == n, nold assert g.ordering == {'y': 0, 'x': 1}, g.ordering assert g.assert_consistent() # functions remain invariant x_ = g.add_expr('x') y_ = g.add_expr('y') assert x == x_, (x, x_, g._succ) assert y == y_, (y, y_, g._succ) # external reference counts remain unchanged assert g._ref[abs(x)] == 1 assert g._ref[abs(y)] == 1 # x & y g = BDD({'x': 0, 'y': 1}) u = g.add_expr('x & y') g.incref(u) nold, n = g.swap('x', 'y') assert nold == n, (nold, n) assert g.ordering == {'y': 0, 'x': 1}, g.ordering u_ = g.add_expr('x & y') assert u == u_, (u, u_) assert g.assert_consistent() # reference counts unchanged assert g._ref[abs(u)] == 1 # x & !y # tests handling of complement edges e = 'x & !y' g = x_and_not_y() u = g.add_expr(e) g.incref(u) g.collect_garbage() n = len(g) assert n == 3, n nold, n = g.swap('x', 'y') assert n == 3, n assert nold == n, nold assert g.ordering == {'x': 1, 'y': 0} assert g.assert_consistent() u_ = g.add_expr(e) # function u must have remained unaffected assert u_ == u, (u, u_, g._succ) # invert swap of: # x & !y nold, n = g.swap('x', 'y') assert n == 3, n assert nold == n, nold assert g.ordering == {'x': 0, 'y': 1} assert g.assert_consistent() u_ = g.add_expr(e) assert u_ == u, (u, u_, g._succ) # Figs. 6.24, 6.25 Baier 2008 g = BDD({'z1': 0, 'y1': 1, 'z2': 2, 'y2': 3, 'z3': 4, 'y3': 5}) u = g.add_expr('(z1 & y1) | (z2 & y2) | (z3 & y3)') g.incref(u) n = len(g) assert n == 16, n g.collect_garbage() n = len(g) assert n == 7, n # sift to inefficient order g.swap('y1', 'z2') # z1, z2, y1, y2, z3, y3 g.swap('y2', 'z3') # z1, z2, y1, z3, y2, y3 g.swap('y1', 'z3') # z1, z2, z3, y1, y2, y3 n = len(g) assert n == 15, n assert g.assert_consistent() new_ordering = { 'z1': 0, 'z2': 1, 'z3': 2, 'y1': 3, 'y2': 4, 'y3': 5} assert g.ordering == new_ordering, g.ordering u_ = g.add_expr('(z1 & y1) | (z2 & y2) | (z3 & y3)') assert u_ == u, (u, u_, g._succ)
def test_preimage(): # exists: x, y # forall: z ordering = {'x': 0, 'xp': 1, 'y': 2, 'yp': 3, 'z': 4, 'zp': 5} rename = {0: 1, 2: 3, 4: 5} g = BDD(ordering) f = g.add_expr('!x') t = g.add_expr('x <-> !xp') qvars = {1, 3} p = preimage(t, f, rename, qvars, g) x = g.add_expr('x') assert x == p, (x, p) # a cycle # (x & y) -> (!x & y) -> # (!x & !y) -> (x & !y) -> wrap around t = g.add_expr('((x & y) -> (!xp & yp)) && ' '((!x & y) -> (!xp & !yp)) && ' '((!x & !y) -> (xp & !yp)) && ' '((x & !y) -> (xp & yp))') f = g.add_expr('x && y') p = preimage(t, f, rename, qvars, g) assert p == g.add_expr('x & !y') f = g.add_expr('x && !y') p = preimage(t, f, rename, qvars, g) assert p == g.add_expr('!x & !y') # backward reachable set f = g.add_expr('x & y') oldf = None while oldf != f: p = preimage(t, f, rename, qvars, g) oldf = f f = g.apply('or', p, oldf) assert f == 1 # go around once f = g.add_expr('x & y') start = f for i in xrange(4): f = preimage(t, f, rename, qvars, g) end = f assert start == end # forall z exists x, y t = g.add_expr('(' ' ((x & y) -> (zp & xp & !yp)) | ' ' ((x & y) -> (!zp & !xp & yp))' ') & ' '(!(x & y) -> False)') f = g.add_expr('x && !y') ep = preimage(t, f, rename, qvars, g) p = g.quantify(ep, {'zp'}, forall=True) assert p == -1 f = g.add_expr('(x & !y) | (!x & y)') ep = preimage(t, f, rename, qvars, g) p = g.quantify(ep, {'zp'}, forall=True) assert p == g.add_expr('x & y')
def test_rename(): ordering = {'x': 0, 'xp': 1} g = BDD(ordering) x = g.add_expr('x') xp = g.add_expr('xp') dvars = {'x': 'xp'} xrenamed = g.let(dvars, x) assert xrenamed == xp, xrenamed ordering = {'x': 0, 'xp': 1, 'y': 2, 'yp': 3, 'z': 4, 'zp': 5} g = BDD(ordering) u = g.add_expr('x && y && ! z') dvars = {'x': 'xp', 'y': 'yp', 'z': 'zp'} urenamed = g.let(dvars, u) up = g.add_expr('xp && yp && ! zp') assert urenamed == up, urenamed # assertion violations # non-neighbors dvars = {'x': 'yp'} r = g.let(dvars, u) r_ = g.add_expr('yp && y && ! z') assert r == r_, (r, r_) # u not in bdd dvars = {'x': 'xp'} with nt.assert_raises(AssertionError): g.let(dvars, 1000) # y essential for u dvars = {'xp': 'y'} with nt.assert_raises(AssertionError): g.let(dvars, u) # old and new vars intersect dvars = {'x': 'x'} with nt.assert_raises(AssertionError): g.let(dvars, u)
def test_preimage(): # exists: x, y # forall: z ordering = {'x': 0, 'xp': 1, 'y': 2, 'yp': 3, 'z': 4, 'zp': 5} rename = {0: 1, 2: 3, 4: 5} g = BDD(ordering) f = g.add_expr('!x') t = g.add_expr('x <-> !xp') qvars = {1, 3} p = preimage(t, f, rename, qvars, g) x = g.add_expr('x') assert x == p, (x, p) # a cycle # (x & y) -> (!x & y) -> # (!x & !y) -> (x & !y) -> wrap around t = g.add_expr( '((x & y) -> (!xp & yp)) && ' '((!x & y) -> (!xp & !yp)) && ' '((!x & !y) -> (xp & !yp)) && ' '((x & !y) -> (xp & yp))') f = g.add_expr('x && y') p = preimage(t, f, rename, qvars, g) assert p == g.add_expr('x & !y') f = g.add_expr('x && !y') p = preimage(t, f, rename, qvars, g) assert p == g.add_expr('!x & !y') # backward reachable set f = g.add_expr('x & y') oldf = None while oldf != f: p = preimage(t, f, rename, qvars, g) oldf = f f = g.apply('or', p, oldf) assert f == 1 # go around once f = g.add_expr('x & y') start = f for i in range(4): f = preimage(t, f, rename, qvars, g) end = f assert start == end # forall z exists x, y t = g.add_expr( '(' ' ((x & y) -> (zp & xp & !yp)) | ' ' ((x & y) -> (!zp & !xp & yp))' ') & ' '(!(x & y) -> False)') f = g.add_expr('x && !y') ep = preimage(t, f, rename, qvars, g) p = g.quantify(ep, {'zp'}, forall=True) assert p == -1 f = g.add_expr('(x & !y) | (!x & y)') ep = preimage(t, f, rename, qvars, g) p = g.quantify(ep, {'zp'}, forall=True) assert p == g.add_expr('x & y')