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)
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]), )
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)
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]), )
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]), )
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)), )
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]), )
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])}), )
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])
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))
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)
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)
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)))