Example #1
0
def bitwise_negate(wordlen, input='x', output='not x'):
    imap, omap = BundleMap({input: wordlen}), BundleMap({output: wordlen})
    return aigbv.AIGBV(
        imap=imap,
        omap=omap,
        aig=aiger.bit_flipper(inputs=imap[input], outputs=omap[output]),
    )
Example #2
0
def kmodels(wordlen: int, k: int, input=None, output=None):
    """Return a circuit taking a wordlen bitvector where only k
    valuations return True. Uses encoding from [1].

    Note that this is equivalent to (~x < k).
    - TODO: Add automated simplification so that the circuits
            are equiv.

    [1]: Chakraborty, Supratik, et al. "From Weighted to Unweighted Model
    Counting." IJCAI. 2015.
    """

    assert 0 <= k < 2**wordlen
    if output is None:
        output = _fresh()

    if input is None:
        input = _fresh()

    imap, omap = BundleMap({input: wordlen}), BundleMap({output: 1})
    atoms = map(aiger.atom, imap[input])

    active = False
    expr = aiger.atom(False)
    for atom, bit in zip(atoms, encode_int(wordlen, k, signed=False)):
        active |= bit
        if not active:  # Skip until first 1.
            continue
        expr = (expr | atom) if bit else (expr & atom)

    aig = expr.aig['o', {expr.output: omap[output][0]}]
    aig |= aiger.sink(imap[input])
    return aigbv.AIGBV(imap=imap, omap=omap, aig=aig)
Example #3
0
def even_popcount_gate(wordlen, input, output):
    imap, omap = BundleMap({input: wordlen}), BundleMap({output: 1})
    return aigbv.AIGBV(
        imap=imap,
        omap=omap,
        aig=aiger.parity_gate(imap[input], omap[output][0]),
    )
Example #4
0
def is_nonzero_gate(wordlen, input='x', output='is_nonzero'):
    imap, omap = BundleMap({input: wordlen}), BundleMap({output: 1})
    return aigbv.AIGBV(
        imap=imap,
        omap=omap,
        aig=aiger.or_gate(imap[input], omap[output][0]),
    )
Example #5
0
def bitwise_binop(binop, wordlen, left='x', right='y', output='x&y'):
    imap = BundleMap({left: wordlen, right: wordlen})
    omap = BundleMap({output: wordlen})

    names = zip(imap[left], imap[right], omap[output])
    return aigbv.AIGBV(
        imap=imap,
        omap=omap,
        aig=reduce(op.or_, (binop([lft, rht], o) for lft, rht, o in names)),
    )
Example #6
0
def identity_gate(wordlen, input='x', output=None):
    if output is None:
        output = input

    imap, omap = BundleMap({input: wordlen}), BundleMap({output: wordlen})
    return aigbv.AIGBV(
        imap=imap,
        omap=omap,
        aig=aiger.identity(inputs=imap[input], outputs=omap[output]),
    )
Example #7
0
def repeat(wordlen, input, output=None):
    if output is None:
        output = input

    imap, omap = BundleMap({input: 1}), BundleMap({output: wordlen})
    return aigbv.AIGBV(
        imap=imap,
        omap=omap,
        aig=aiger.tee({imap[input][0]: list(omap[output])}),
    )
Example #8
0
def index_gate(wordlen, idx, input, output=None):
    assert 0 <= idx < wordlen
    if output is None:
        output = input

    imap, omap = BundleMap({input: wordlen}), BundleMap({output: 1})
    inputs, outputs = imap[input], (imap[input][idx], )

    aig = aiger.sink(set(inputs) - set(outputs)) | aiger.identity(outputs)
    relabels = {outputs[0]: omap[output][0]}
    return aigbv.AIGBV(imap=imap, omap=omap, aig=aig['o', relabels])
Example #9
0
def tee(wordlen, iomap):
    imap = BundleMap({i: wordlen for i in iomap})
    omap = BundleMap({o: wordlen for o in fn.cat(iomap.values())})

    blasted = defaultdict(list)

    for i, outs in iomap.items():
        for o in outs:
            for k, v in zip(imap[i], omap[o]):
                blasted[k].append(v)

    return aigbv.AIGBV(imap=imap, omap=omap, aig=aiger.tee(blasted))
Example #10
0
def unsigned_lt_gate(wordlen, left, right, output):
    omap = BundleMap({output: 1})
    imap = BundleMap({left: wordlen, right: wordlen})

    lefts = map(aiger.atom, imap[left])
    rights = map(aiger.atom, imap[right])

    def test_bit(expr, lr):
        l, r = lr
        expr &= ~(l ^ r)  # l == r.
        expr |= ~l & r  # l < r.
        return expr

    expr = reduce(test_bit, zip(lefts, rights), aiger.atom(False))
    aig = expr.aig['o', {expr.output: omap[output][0]}]
    return aigbv.AIGBV(imap=imap, omap=omap, aig=aig)
Example #11
0
def source(wordlen, value, name='x', signed=True):
    if isinstance(value, int):
        value = encode_int(wordlen, value, signed)

    omap = BundleMap({name: wordlen})
    aig = aiger.source({name: bit for name, bit in zip(omap[name], value)})
    return aigbv.AIGBV(aig=aig, omap=omap)
Example #12
0
    def _feedback(self, inputs, outputs, initials=None, latches=None,
                  keep_outputs=False):
        # TODO: remove in next version bump and put into wire.
        if latches is None:
            latches = inputs

        def blast(bmap, vals):
            return fn.lmapcat(bmap.get, vals)

        lmap = BundleMap(
            {l: self.imap[i].size for i, l in zip(inputs, latches)}
        )

        if initials is not None:
            l2init = dict(self.aig.latch2init)
            l2init.update(
                {l: v for l, v in zip(latches, initials) if v is not None}
            )
            initials = fn.lcat(l2init[l] for l in latches)

        aig = rebundle_aig(self.aig.feedback(
            inputs=blast(self.imap, inputs), outputs=blast(self.omap, outputs),
            latches=blast(lmap, latches), keep_outputs=keep_outputs,
            initials=initials,
        ))

        return aig
Example #13
0
def split_gate(input, left_wordlen, left, right_wordlen, right):
    omap = BundleMap({left: left_wordlen, right: right_wordlen})

    circ = identity_gate(left_wordlen + right_wordlen, input, input)
    relabels = fn.merge(
        dict(zip(circ.omap[input][:left_wordlen], omap[left])),
        dict(zip(circ.omap[input][left_wordlen:], omap[right])),
    )

    return attr.evolve(circ, omap=omap, aig=circ.aig['o', relabels])
Example #14
0
def combine_gate(left_wordlen, left, right_wordlen, right, output):
    circ = identity_gate(left_wordlen, left, left) \
        | identity_gate(right_wordlen, right, right)

    omap1 = circ.omap
    relabels = {
        k: f"{left}[{i + left_wordlen}]"
        for i, k in enumerate(omap1[right])
    }
    omap2 = BundleMap({left: left_wordlen + right_wordlen})
    circ = attr.evolve(circ, omap=omap2, aig=circ.aig['o', relabels])
    return circ if left == output else circ['o', {left: output}]
Example #15
0
def add_gate(wordlen, left='x', right='y', output='x+y', has_carry=False):
    carry_name = f'{output}_carry'
    assert left != carry_name and right != carry_name

    adder_aig = aiger.source({carry_name: False})

    imap = BundleMap({left: wordlen, right: wordlen})
    omap = BundleMap({
        output: wordlen,
        has_carry: 1
    } if has_carry else {output: wordlen})

    for lname, rname, oname in zip(imap[left], imap[right], omap[output]):
        adder_aig >>= _full_adder(x=lname,
                                  y=rname,
                                  carry_in=carry_name,
                                  result=oname,
                                  carry_out=carry_name)

    if not has_carry:
        adder_aig >>= aiger.sink([output + '_carry'])

    return aigbv.AIGBV(imap=imap, omap=omap, aig=adder_aig)
Example #16
0
def rebundle_names(names):
    grouped_names = fn.group_values(map(unpack_name, names))
    return BundleMap(pmap(fn.walk_values(to_size, grouped_names)))
Example #17
0
def sink(wordlen, inputs):
    imap = BundleMap({i: wordlen for i in inputs})
    return aigbv.AIGBV(imap=imap, aig=aiger.sink(fn.lmapcat(imap.get, inputs)))
Example #18
0
def _diagonal_map(keys):
    return BundleMap({k: 1 for k in keys})
Example #19
0
class AIGBV:
    aig: aiger.AIG
    imap: BundleMap = BundleMap()
    omap: BundleMap = BundleMap()
    lmap: BundleMap = BundleMap()

    simulate = aiger.AIG.simulate
    simulator = aiger.AIG.simulator

    @property
    def aigbv(self):
        return self

    def write(self, path):
        self.aig.write(path)

    @property
    def inputs(self): return set(self.imap.keys())

    @property
    def outputs(self): return set(self.omap.keys())

    @property
    def latches(self): return set(self.lmap.keys())

    @property
    def latch2init(self):
        return self.lmap.unblast(dict(self.aig.latch2init))

    def __call__(self, inputs, latches=None):
        out2val, latch2val = self.aig(
            inputs=self.imap.blast(inputs),
            latches=None if latches is None else self.lmap.blast(latches)
        )
        return self.omap.unblast(out2val), self.lmap.unblast(latch2val)

    def __lshift__(self, other):
        return other >> self

    def __rshift__(self, other):
        interface = self.outputs & other.inputs
        assert not self.latches & other.latches
        assert not (self.outputs - interface) & other.outputs

        return AIGBV(
            aig=self.aig >> other.aig,
            imap=self.imap + other.imap.omit(interface),
            omap=other.omap + self.omap.omit(interface),
            lmap=self.lmap + other.lmap,
        )

    def __or__(self, other):
        assert not self.outputs & other.outputs
        assert not self.latches & other.latches

        shared_inputs = self.inputs & other.inputs
        circ = self
        if shared_inputs:
            relabels1 = {n: common._fresh() for n in shared_inputs}
            relabels2 = {n: common._fresh() for n in shared_inputs}
            circ, other = circ['i', relabels1], other['i', relabels2]

        circ = AIGBV(
            aig=circ.aig | other.aig,
            imap=circ.imap + other.imap,
            omap=circ.omap + other.omap,
            lmap=circ.lmap + other.lmap)

        if shared_inputs:
            for orig in shared_inputs:
                new1, new2 = relabels1[orig], relabels2[orig]
                circ <<= common.tee(self.imap[orig].size, {orig: [new1, new2]})

        return circ

    def __getitem__(self, others):
        kind, relabels = others
        if kind not in {'i', 'o', 'l'}:
            raise NotImplementedError

        attr_name = {'i': 'imap', 'o': 'omap', 'l': 'lmap'}.get(kind)
        bmap1 = getattr(self, attr_name)
        assert not set(relabels.values()) & set(bmap1.keys())
        bmap2 = bmap1.relabel(relabels)
        circ = attr.evolve(self, **{attr_name: bmap2})

        # Update AIG to match new interface.
        relabels_aig = fn.merge(*(
            dict(zip(bmap1[k], bmap2[v])) for k, v in relabels.items()
            if k in bmap1
        ))
        return attr.evolve(circ, aig=circ.aig[kind, relabels_aig])

    def loopback(self, *wirings):
        def wire(circ, wiring):
            return circ._wire(**wiring)

        return reduce(wire, wirings, self)

    def _wire(self, input, output, latch=None, init=None, keep_output=True):
        if latch is None:
            latch = input
        inits = [init] if init is not None else None

        return self._feedback(
            [input], [output], inits, [latch], keep_outputs=keep_output
        )

    def feedback(
        self, inputs, outputs, initials=None, latches=None, keep_outputs=False
    ):
        import warnings
        warnings.warn("deprecated", DeprecationWarning)
        return self._feedback(
            inputs, outputs, initials=initials, latches=latches,
            keep_outputs=keep_outputs
        )

    def _feedback(self, inputs, outputs, initials=None, latches=None,
                  keep_outputs=False):
        # TODO: remove in next version bump and put into wire.
        if latches is None:
            latches = inputs

        def blast(bmap, vals):
            return fn.lmapcat(bmap.get, vals)

        lmap = BundleMap(
            {l: self.imap[i].size for i, l in zip(inputs, latches)}
        )

        if initials is not None:
            l2init = dict(self.aig.latch2init)
            l2init.update(
                {l: v for l, v in zip(latches, initials) if v is not None}
            )
            initials = fn.lcat(l2init[l] for l in latches)

        aig = rebundle_aig(self.aig.feedback(
            inputs=blast(self.imap, inputs), outputs=blast(self.omap, outputs),
            latches=blast(lmap, latches), keep_outputs=keep_outputs,
            initials=initials,
        ))

        return aig

    def unroll(self, horizon, *, init=True, omit_latches=True,
               only_last_outputs=False):
        aig = self.aig.unroll(
            horizon, init=init, omit_latches=omit_latches,
            only_last_outputs=only_last_outputs
        )
        for key in ['inputs', 'outputs', 'latches']:
            relabels = {k: shuffle_id_time(k) for k in getattr(aig, key)}
            aig = aig[key[0], relabels]

        return rebundle_aig(aig)

    def cutlatches(self, latches=None, renamer=None):
        if renamer is None:
            @fn.memoize
            def renamer(_):
                return common._fresh()

        def renamer_bv(name):
            root, idx = unpack_name(name)
            return f"{renamer(root)}[{idx}]"

        aig, lmap = self.aig.cutlatches(latches, renamer=renamer_bv)
        circ = rebundle_aig(aig)
        lmap = self.lmap.unblast(lmap)

        def unblast_vals(vals):
            name, _ = vals[0]
            name = unpack_name(name)[0]
            bdl = Bundle(size=len(vals), name=name)
            return (name, bdl.unblast(dict(vals)))

        lmap = fn.walk_values(unblast_vals, lmap)
        return circ, lmap