def test_path_negation(): levels = {'c1': 0, 'a1': 1, 'c2': 2, 'a2': 3} manager = BDD(levels) manager.configure(reordering=False) c1, a1, c2, a2 = map(manager.var, ['c1', 'a1', 'c2', 'a2']) bexpr = ((c1 & a1) | (~c1 & ~a1)) & ((c2 & a2) | (~c2 & ~a2)) assert bexpr.low.negated assert not bexpr.high.negated assert len(list(path(bexpr, (True, False, False, False)))) == 3 assert len(list(path(bexpr, (True, True, True, True)))) == 5 def merge(ctx, val, acc): if ctx.is_leaf: return ctx.path_negated ^ ctx.node_val return None def evaluate(vals): return fold_path(merge, bexpr, vals, initial=[]) for val in product(*(4 * [[False, True]])): expected = (val[0] == val[1]) and (val[2] == val[3]) assert evaluate(val) == expected
def to_bdd(circ_or_expr, output=None, manager=None, renamer=None, levels=None): if renamer is None: _count = 0 def renamer(*_): nonlocal _count _count += 1 return f"x{_count}" if not isinstance(circ_or_expr, aiger.BoolExpr): circ = aiger.to_aig(circ_or_expr, allow_lazy=True) assert len(circ.latches) == 0 if output is None: assert len(circ.outputs) == 1 output = fn.first(circ.outputs) expr = aiger.BoolExpr(circ) else: expr = circ_or_expr manager = BDD() if manager is None else manager input_refs_to_var = { ref: renamer(i, ref) for i, ref in enumerate(expr.inputs) } manager.declare(*input_refs_to_var.values()) if levels is not None: assert set(manager.vars.keys()) <= set(levels.keys()) levels = fn.project(levels, manager.vars.keys()) levels = fn.walk_keys(input_refs_to_var.get, levels) manager.reorder(levels) manager.configure(reordering=False) def lift(obj): if isinstance(obj, bool): return manager.true if obj else manager.false return obj inputs = {i: manager.var(input_refs_to_var[i]) for i in expr.inputs} out = expr(inputs, lift=lift) return out, out.bdd, bidict(input_refs_to_var)
def create_manager(): manager = BDD() manager.declare('x', 'y') manager.reorder({'x': 1, 'y': 0}) manager.configure(reordering=False) return manager