Esempio n. 1
0
def test_to_aig():
    x = aiger.atom('x')
    c1 = aiger.to_aig(x)
    c2 = aiger.to_aig(c1)
    c3 = aiger.to_aig(str(c2))

    with tempfile.TemporaryDirectory() as d:
        path = pathlib.Path(d) / "foo.aag"
        c3.write(path)
        c4 = aiger.to_aig(path)

    for c in [c1, c2, c3, c4]:
        assert isinstance(c1, aiger.AIG)
        assert isinstance(c2, aiger.AIG)
        assert isinstance(c3, aiger.AIG)
        assert isinstance(c4, aiger.AIG)
Esempio n. 2
0
def aig2cnf(circ, *, outputs=None, fresh=None, force_true=True) -> CNF:
    """Convert an AIGER circuit to CNF via the Tseitin transformation."""
    if fresh is None:
        max_var = 0

        def fresh(_):
            nonlocal max_var
            max_var += 1
            return max_var

    circ = aiger.to_aig(circ, allow_lazy=True)
    assert len(circ.latches) == 0

    # Interpret circuit over Lit Boolean Algebra.
    clauses, gate2lit = [], SymbolTable(fresh)

    def lift(obj) -> LitWrapper:
        assert isinstance(obj, (Input, bool))
        if isinstance(obj, bool):
            assert not obj
            obj = ConstFalse()

        gate2lit[obj]  # defaultdict. force add literal for obj.
        return LitWrapper(gate=obj, gate2lit=gate2lit, clauses=clauses)

    inputs = {i: aiger.aig.Input(i) for i in circ.inputs}
    out2lit, _ = circ(inputs=inputs, lift=lift)
    out2lit = {k: v.lit for k, v in out2lit.items()}  # Remove Lit wrapper.
    in2lit = bidict({i: gate2lit[aiger.aig.Input(i)] for i in circ.inputs})

    # Force True/False variable to be true/false.
    if ConstFalse() in gate2lit:
        clauses.append((-gate2lit[ConstFalse()], ))

    # Force outputs to appear as positive variables.
    for name, gate in circ.node_map.items():
        if not isinstance(gate, aiger.aig.Inverter):
            continue

        oldv = out2lit[name] = fresh(gate)
        newv = gate2lit[gate]
        clauses.append((-newv, oldv))
        clauses.append((newv, -oldv))

    if force_true:
        if outputs is None:
            outputs = circ.outputs

        for name in outputs:
            clauses.append((out2lit[name], ))

    return CNF(clauses, in2lit, out2lit, circ.comments)
Esempio n. 3
0
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)
Esempio n. 4
0
def simplify(circ, verbose=False, abc_cmd='abc', aigtoaig_cmd='aigtoaig'):
    circ = aiger.to_aig(circ)

    # avoids confusion and guarantees deletion on exit
    with tempfile.TemporaryDirectory() as tmpdirname:
        tmpdir = Path(tmpdirname)
        aag_path = tmpdir / 'input.aag'
        aig_path = tmpdir / 'input.aig'

        circ.write(aag_path)
        call([aigtoaig_cmd, aag_path, aig_path])
        command = [
            abc_cmd,
            '-c',
            SIMPLIFY_TEMPLATE.format(aig_path)
        ]
        call(command) if verbose else call(command, stdout=PIPE)
        call([aigtoaig_cmd, aig_path, aag_path])
        return aiger.parser.load(aag_path)
Esempio n. 5
0
def aig2cnf(circ, *, outputs=None, fresh=None, force_true=True):
    """Convert an AIGER circuit to CNF via the Tseitin transformation."""
    if fresh is None:
        max_var = 0

        def fresh(_):
            nonlocal max_var
            max_var += 1
            return max_var

    circ = aiger.to_aig(circ, allow_lazy=True)
    assert len(circ.latches) == 0

    # Define Boolean Algebra over clauses.
    clauses, gate2lit = [], SymbolTable(fresh)

    @attr.s(auto_attribs=True, frozen=True)
    class LitWrapper:
        lit: Hashable
        gate: Node

        @fn.memoize
        def __and__(self, other):
            gate = AndGate(self.gate, other.gate)
            wrapped = LitWrapper(gate2lit[gate], gate)

            out, left, right = wrapped.lit, self.lit, other.lit
            clauses.append((-left, -right, out))     # (left /\ right) -> out
            clauses.append((-out, left))             # out -> left
            clauses.append((-out, right))            # out -> right
            return wrapped

        def __invert__(self):
            gate = Inverter(self.gate)
            gate2lit[gate] = -self.lit
            return LitWrapper(gate2lit[gate], gate)

    def lift(obj) -> LitWrapper:
        assert isinstance(obj, (Input, bool))
        if isinstance(obj, bool):
            assert not obj
            obj = ConstFalse()

        return LitWrapper(gate2lit[obj], obj)

    # Interpret circ over Lit Boolean Algebra.
    inputs = {i: aiger.aig.Input(i) for i in circ.inputs}
    out2lit, _ = circ(inputs=inputs, lift=lift)
    out2lit = {k: v.lit for k, v in out2lit.items()}  # Remove Lit wrapper.
    in2lit = bidict({i: gate2lit[aiger.aig.Input(i)] for i in circ.inputs})

    # Force True/False variable to be true/false.
    if ConstFalse() in gate2lit:
        clauses.append((-gate2lit[ConstFalse()],))

    # Force outputs to appear as positive variables.
    for name, gate in circ.node_map.items():
        if not isinstance(gate, aiger.aig.Inverter):
            continue

        oldv = out2lit[name] = fresh(gate)
        newv = gate2lit[gate]
        clauses.append((-newv,  oldv))
        clauses.append((newv,  -oldv))

    if force_true:
        if outputs is None:
            outputs = circ.outputs

        for name in outputs:
            clauses.append((out2lit[name],))

    return CNF(clauses, in2lit, out2lit, circ.comments)
Esempio n. 6
0
def to_js(circ, suffix="aig", with_header=True) -> str:
    """
    Outputs string with Javascript for stepping
    through AIG represented by circ.

    - suffix: controls suffix of generated step and spec code.

    - with_header: Includes prelude necessary for running generated
      code. Only needed once.
    """
    circ = to_aig(circ)

    count = 0

    def fresh():
        nonlocal count
        count += 1
        return f'x{count}'

    with io.StringIO() as buff:

        @attr.s(frozen=True, auto_attribs=True)
        class Writer:
            var: str
            gate: Node

            @fn.memoize
            def __and__(self, other):
                writer = lift(self.gate & other.gate)
                left, right = self.var, other.var
                buff.write(f'    var {writer.var} = {left} && {right};\n')
                return writer

            @fn.memoize
            def __invert__(self):
                writer = lift(~self.gate)
                buff.write(f'    var {writer.var} = !{self.var};\n')
                return writer

        @fn.memoize
        def lift(gate) -> Writer:
            if isinstance(gate, Input):
                var = f'inputs["{gate.name}"]'
            elif isinstance(gate, LatchIn):
                var = f'latches["{gate.name}"]'
            elif isinstance(gate, bool):
                var = 'false'
            else:
                var = fresh()

            return Writer(var=var, gate=gate)

        # Add latch init code:
        for name, init in circ.latch2init.items():
            init = "true" if init else "false"
            buff.write(f'    if (!("{name}" in latches)) {{ ')
            buff.write(f'latches["{name}"] = {init}')
            buff.write('}\n')
        buff.write('\n')

        # Inline gates as straight line program.
        inputs = {i: Input(i) for i in circ.inputs}
        latches = {i: LatchIn(i) for i in circ.latches}
        omap, lmap = circ(inputs, latches, lift=lift)

        # Collect outputs.
        for name, writer in omap.items():
            buff.write(f'\n    outputs["{name}"] = {writer.var};')

        # Collect outputs.
        for name, writer in lmap.items():
            buff.write(f'\n    latch_outs["{name}"] = {writer.var};')

        hdr = HEADER if with_header else ""
        return hdr + TEMPLATE.format(suffix, buff.getvalue())