def is_realizable(self, use_cegar=False, verbose=False): assert len(self.aig.outputs) is 1 initial_state = {x: val for (x, val) in self.aig.latch2init} bad = BoolExpr(aiger.sink(self.aig.latches) | atom(False).aig) transition_relation = \ _cutlatches_and_rename(self.aig) >> \ aiger.bit_flipper(inputs=self.aig.outputs) for i in itertools.count(): # to infinity and beyond print(f'Iteration {i+1}') tmp = transition_relation >> (~bad).aig # do not go to a bad state miter1 = BoolExpr(tmp >> aiger.and_gate(tmp.outputs)) miter2 = eliminate(miter1, self.system, verbose=verbose) next_bad = bad | eliminate( ~miter2, self.environment, verbose=verbose) # delete comments to avoid them accumulate next_bad = BoolExpr(next_bad.aig.evolve(comments=())) if next_bad(inputs=initial_state): print('Unrealizable') return False print('Fixed point check') if is_equal(bad, next_bad): print('Realizable') return True bad = next_bad
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}) lefts = named_indexes(wordlen, left) rights = named_indexes(wordlen, right) outputs = named_indexes(wordlen, output) for lname, rname, oname in zip(lefts, rights, outputs): 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( aig=adder_aig, input_map=frozenset([(left, lefts), (right, rights)]), output_map=frozenset([(output, outputs)]), )
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 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 index_gate(wordlen, idx, input, output=None): assert 0 <= idx < wordlen if output is None: output = input inputs = named_indexes(wordlen, input) outputs = (inputs[idx], ) aig = aiger.sink(set(inputs) - set(outputs)) \ | aiger.identity(outputs) return aigbv.AIGBV( aig=aig, input_map=frozenset([(input, inputs)]), output_map=frozenset([(output, outputs)]), )
def _encode(self, prev_latch, action, state): (step, lmap), circ1 = self._cutlatches() curr_step = step << aiger.source(prev_latch) for a, v in action.items(): size = circ1.imap[a].size const = aiger_bv.source(size, aiger_bv.decode_int(v, signed=False), name=a, signed=False) curr_step <<= const.aig expr = uatom(1, "##valid") == 1 for k, v in fn.chain(state.items()): expr &= _constraint(k, v) curr_step >>= expr.aig query = curr_step >> aiger.sink(prev_latch.keys()) assert len(query.outputs) == 1 model = solve(query) assert model is not None # Fill in any model don't cares. model = fn.merge({i: False for i in circ1.aig.inputs}, model) # HACK. Put model back into bitvector. coins = circ1.imap.omit(self.inputs).unblast(model) if len(prev_latch) > 0: next_latch_circ = curr_step >> aiger.sink(expr.aig.outputs) next_latch = next_latch_circ(model)[0] assert next_latch.keys() == prev_latch.keys() prev_latch = next_latch return coins, prev_latch
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)))
def spec2monitor(spec): monitor = spec.aig | A.sink(['red', 'yellow']) monitor = monitor['o', {spec.output: 'sat'}] monitor = BV.aig2aigbv(monitor) return SENSOR >> monitor
def sink(wordlen, inputs): blasted_inputs = [named_indexes(wordlen, i) for i in inputs] return aigbv.AIGBV( aig=aiger.sink(fn.lcat(blasted_inputs)), input_map=frozenset(fn.lzip(inputs, blasted_inputs)), )
def coin(prob, input_name=None): # TODO: reimplement in terms of common_denominator_method. prob = utils.to_frac(prob) mux, is_valid = mutex_coins({'H': prob, 'T': 1 - prob}) return mux >> aiger.sink('T'), is_valid