def unroll(self, horizon, *, init=True, omit_latches=True, only_last_outputs=False): hist_valid = LTL.atom(self.valid_id) \ .historically() \ .with_output(self.valid_id) monitor = BV.aig2aigbv(hist_valid.aig) circ = (self.circ >> monitor).unroll( horizon, init=init, omit_latches=omit_latches, only_last_outputs=only_last_outputs) if not only_last_outputs: times = range(1, horizon) circ >>= BV.sink(1, (f'{self.valid_id}##time_{t}' for t in times)) valid_id = f'{self.valid_id}##time_{horizon}' assert valid_id in circ.outputs input_encodings = timed_encodings(self.input_encodings, circ.inputs) output_encodings = timed_encodings(self.output_encodings, circ.outputs) return from_aigbv( circ=circ, input_encodings=input_encodings, output_encodings=output_encodings, valid_id=valid_id, )
def test_never_false_redemption(): spec = LTL.atom('x').historically() monitor = BV.aig2aigbv(spec.aig) assert len(monitor.latches) == 1 monitor = monitor['l', {fn.first(monitor.latches): 'z'}] # Environment can save you. x, y = BV.uatom(1, 'x'), BV.uatom(1, 'y') xy = (x | y).with_output('x') # env y can override x. dyn = C.pcirc(xy.aigbv) \ .randomize({'y': {0: 0.75, 1: 0.25}}) horizon = 3 model = from_pcirc(dyn, monitor, steps=horizon) coeff = np.log(2) # Special coeff to make LSE visit powers of 2. actor = improviser(model, coeff) v8 = coeff v7 = 0 v6 = coeff / 4 v5 = np.logaddexp(v6, coeff) v4 = (np.log(8) + v5) / 4 v3 = np.logaddexp(v4, v5) v2 = (3 * np.log(4) + v3) / 4 v1 = np.logaddexp(v2, v3) expected = sorted([v8, v7, v6, v5, v4, v3, v2, v1]) expected = fn.lmap(pytest.approx, expected) vals = sorted(list(actor.node2val.values())) assert all(x == y for x, y in zip(vals, expected)) def lprob(elems): return actor.prob(elems, log=True) assert lprob([]) == 0 assert lprob([1]) == pytest.approx(v3 - v1) for prefix in [[1], [1, 0, 1], [1, 1, 1], [1, 1, 1, 1, 1]]: for bit in [0, 1]: expected = pytest.approx(-np.log(4) + bit * np.log(3)) assert lprob(prefix + [bit]) - lprob(prefix) == expected ctrl = actor.policy() example = [] for env in [None, 0, 0]: example.append(ctrl.send(env)) assert -float('inf') < lprob(example) ctrl = actor.policy(observe_states=True) example = [] for env in [None, ({'x': 1}, None), ({'x': 1}, None)]: example.append(ctrl.send(env)) assert -float('inf') < lprob(example)
def sys3(): mdp = sys1() mdp |= C.circ2mdp(BV.tee(1, {'c': ['c', 'c_next']})) coin = (~C.coin((1, 2), name='c')).with_output('c') mdp <<= coin delay = BV.aig2aigbv(aiger.delay(['c'], [True])) delay = C.circ2mdp(delay) return mdp >> delay
def test_never_false(): spec = LTL.atom('x').historically() monitor = BV.aig2aigbv(spec.aig) dyn = C.pcirc(BV.identity_gate(1, 'x')) horizon = 3 model = from_pcirc(dyn, monitor, steps=horizon) graph = model.graph() assert len(graph.nodes) == horizon + 2 assert len(graph.edges) == 2 * horizon
def test_preimage(): spec, mdp = scenario_reactive() sys1 = mdp.aigbv >> BV.sink(1, ['c_next', '##valid']) sys2 = sys1 >> BV.aig2aigbv(spec.aig) def act(action, coin): return {'a': (action, ), 'c': (coin, )} actions = [act(True, True), act(True, False), act(True, True)] observations = fn.lpluck(0, sys1.simulate(actions)) expr = preimage(observations, sys1) assert expr.inputs == { 'c##time_0', 'c##time_1', 'c##time_2', 'a##time_0', 'a##time_1', 'a##time_2', } bexpr1, manager, order = to_bdd2(sys2, horizon=3) def accepts(bexpr, actions): """Check if bexpr accepts action sequence.""" timed_actions = {} for t, action in enumerate(actions): c, a = action['c'], action['a'] timed_actions.update({ f'c##time_{t}[0]': c[0], f'a##time_{t}[0]': a[0] }) assert timed_actions.keys() == manager.vars.keys() tmp = manager.let(timed_actions, bexpr) assert tmp in (manager.true, manager.false) return tmp == manager.true assert not accepts(bexpr1, actions) bexpr2, _, input2var = aiger_bdd.to_bdd(expr, manager=manager, renamer=lambda _, x: x) assert accepts(bexpr2, actions) assert not accepts(~bexpr2, actions) bexpr3 = xor(bexpr1, bexpr2) assert accepts(bexpr3, actions)
def test_never_false_redemption(): spec = LTL.atom('x').historically() monitor = BV.aig2aigbv(spec.aig) # Environment can save you. x, y = BV.uatom(1, 'x'), BV.uatom(1, 'y') xy = (x | y).with_output('x') # env y can override x. dyn = C.pcirc(xy.aigbv) \ .randomize({'y': {0: 0.4, 1: 0.6}}) horizon = 3 model = from_pcirc(dyn, monitor, steps=horizon) graph = model.graph() assert len(graph.nodes) == 2 * horizon + 2 assert len(graph.edges) == 4 * horizon
def test_smoke(): spec = PLTL.atom('a').historically() spec = BV.aig2aigbv(spec.aig) spec = C.circ2mdp(spec) spec <<= C.coin((1, 8), name='c') spec >>= C.circ2mdp(BV.sink(1, ['c'])) # HACK bdd, manager, order = to_bdd2(spec, horizon=3) assert bdd.dag_size == 4 for i in range(order.total_bits*order.horizon): t = order.time_step(i) var = manager.var_at_level(i) action, t2, _ = TIMED_INPUT_MATCHER.match(var).groups() assert t == int(t2) decision = action in spec.inputs assert decision == order.is_decision(i)
def test_find_env_input(): x, c = map(aiger_ptltl.atom, ('x', 'c')) sys = (x & c).historically().aig sys = circ2mdp(aiger_bv.aig2aigbv(sys)) sys <<= coin((1, 2), name='c') assert sys.inputs == {'x'} assert len(sys.outputs) == 1 out, *_ = sys.outputs start = end = sys.aigbv.latch2init action = {'x': (True, )} coins = sys.find_env_input(start, action, end) _, lmap = sys.aigbv(inputs={**action, **coins}) assert lmap == end
def test_find_coin_flips(): x, c = map(aiger_ptltl.atom, ('x', 'c')) sys = (x & c).historically().aig sys = circ2mdp(aiger_bv.aig2aigbv(sys)) sys <<= coin((1, 2), name='c') assert sys.inputs == {'x'} assert len(sys.outputs) == 1 out, *_ = sys.outputs sys_actions = 3 * [{'x': (True, )}] states = 3 * [{out: (True, )}] actions = sys.encode_trc(sys_actions, states) assert not any(v['c'][0] for v in actions) sys_actions2, states2 = sys.decode_trc(actions) assert sys_actions2 == sys_actions assert states2 == states
def concretize( monitor, sys: C.MDP, horizon: int, manager=None ) -> ConcreteSpec: """ Convert an abstract specification monitor and a i/o transition system into a concrete specification over the horizion. """ # Make format correct. if not isinstance(monitor, C.MDP): assert hasattr(monitor, 'aig') or hasattr(monitor, 'aigbv') if hasattr(monitor, 'aigbv'): monitor = monitor.aigbv else: monitor = BV.aig2aigbv(monitor.aig) monitor = C.MDP(monitor) # Remove ignored outputs of sys. for sym in (monitor.inputs ^ sys.outputs): size = sys._aigbv.omap[sym].size monitor >>= C.MDP(BV.sink(size, [sym])) bexpr, manager, order = to_bdd2(sys >> monitor, horizon) return ConcreteSpec(bexpr, order, sys_inputs=sys.inputs, mdp=sys)
def test_smoke2(): spec, mdp = scenario_reactive() spec_circ = BV.aig2aigbv(spec.aig) mdp >>= C.circ2mdp(spec_circ) output = spec.output bdd, manager, order = to_bdd2(mdp, horizon=3, output=output) assert bdd.dag_size == 7 def translate(mapping): return mapping assert bdd.count(6) == 8 node = bdd assert bdd.bdd.let(translate({ 'a##time_0[0]': True, 'c##time_0[0]': False, 'a##time_1[0]': True, }), bdd) == bdd.bdd.false assert bdd.bdd.let(translate({ 'a##time_0[0]': True, 'c##time_0[0]': True, 'a##time_1[0]': False, }), bdd) == bdd.bdd.false assert node.low == bdd.bdd.false assert bdd.bdd.let(translate({ 'a##time_0[0]': True, 'c##time_0[0]': True, 'a##time_1[0]': True, 'c##time_1[0]': True, 'a##time_2[0]': True, 'c##time_2[0]': True, }), bdd) == bdd.bdd.true assert bdd.bdd.let(translate({ 'a##time_0[0]': True, 'c##time_0[0]': False, 'a##time_1[0]': False, 'c##time_1[0]': False, 'a##time_2[0]': False, 'c##time_2[0]': False, }), bdd) == bdd.bdd.true assert bdd.bdd.let(translate({ 'c##time_0[0]': False, 'a##time_1[0]': True, }), bdd) == bdd.bdd.false assert bdd.bdd.let(translate({ 'c##time_0[0]': True, 'a##time_1[0]': False, }), bdd) == bdd.bdd.false assert bdd.bdd.let(translate({ 'c##time_1[0]': False, 'a##time_2[0]': True, }), bdd) == bdd.bdd.false assert bdd.bdd.let(translate({ 'c##time_1[0]': True, 'a##time_2[0]': False, }), bdd) == bdd.bdd.false assert bdd.bdd.let(translate({ 'c##time_2[0]': False, }), bdd) == bdd assert bdd.bdd.let(translate({ 'c##time_2[0]': True, }), bdd) == bdd
def test_never_false1(): spec = LTL.atom('x').historically() monitor = BV.aig2aigbv(spec.aig) dyn = C.pcirc(BV.identity_gate(1, 'x')) horizon = 4 model = from_pcirc(dyn, monitor, steps=horizon) coeff = np.log(2) actor = model.improviser(rationality=coeff) expected = [ 0, coeff, np.log(1 + 2), np.log(3 + 2), np.log(7 + 2), np.log(15 + 2), ] # LSE visits powers of 2 for the special coeff. expected = fn.lmap(pytest.approx, expected) vals = sorted(list(actor.node2val.values())) assert all(x == y for x, y in zip(expected, vals)) expected = sorted([ np.log(9) - np.log(17), np.log(8) - np.log(17), ]) expected = fn.lmap(pytest.approx, expected) def lprob(elems): return actor.prob(elems, log=True) assert lprob([]) == 0 assert lprob([1]) == pytest.approx(np.log(9) - np.log(17)) assert lprob([1, 1]) == pytest.approx(np.log(5) - np.log(17)) assert lprob([1, 1, 1]) == pytest.approx(np.log(3) - np.log(17)) assert lprob([1, 1, 1, 1]) == pytest.approx(coeff - np.log(17)) # ----------- Fail on first state --------------------- base = np.log(8) - np.log(17) assert lprob([0]) == pytest.approx(base) # Uniform after failing. assert lprob([0, 1]) == pytest.approx(base - np.log(2)) assert lprob([0, 0]) == pytest.approx(base - np.log(2)) assert lprob([0, 0, 0]) == pytest.approx(base - np.log(4)) assert lprob([0, 0, 1]) == pytest.approx(base - np.log(4)) # ----------- Fail on second state --------------------- base = np.log(4) - np.log(17) assert lprob([1, 0]) == pytest.approx(base) assert lprob([1, 0, 0]) == pytest.approx(base - np.log(2)) assert lprob([1, 0, 1]) == pytest.approx(base - np.log(2)) assert lprob([1, 0, 0, 0]) == pytest.approx(base - np.log(4)) assert lprob([1, 1, 0, 1]) == pytest.approx(base - np.log(4)) with pytest.raises(ValueError): lprob([1, 1, 1, 1, 1, 1]) example = list(actor.sample()) assert -float('inf') < lprob(example) ctrl = actor.policy() example = [] for env in [None, 0, 0, 0]: example.append(ctrl.send(env)) assert -float('inf') < lprob(example) actor = model.improviser(psat=0.7) assert actor.sat_prob() == pytest.approx(0.7)
def valid_circ(action): hist_valid = PLTL.atom('valid').historically().with_output('valid') hist_valid = aiger_bv.aig2aigbv(hist_valid.aig) return is_1hot(action) >> hist_valid
def create_sensor(aps): sensor = BV.aig2aigbv(A.empty()) for name, ap in APS.items(): sensor |= ap.with_output(name).aigbv return sensor
def spec2monitor(spec): monitor = spec.aig | A.sink(['red', 'yellow']) monitor = monitor['o', {spec.output: 'sat'}] monitor = BV.aig2aigbv(monitor) return SENSOR >> monitor
import aiger_bv as BV import aiger_coins as C import aiger_ptltl as LTL from mce.equiv import equiv_states X = LTL.atom('x') Y = LTL.atom('y') SYS = C.circ2mdp(BV.aig2aigbv((X.once() | Y.once()).aig)) def test_equiv_states_smoke(): state = SYS._aigbv.latch2init for t in range(3): assert equiv_states(SYS, 3, t, state1=state, state2=state) state1 = SYS.aigbv({'x': (True, ), 'y': (False, )})[1] state2 = SYS.aigbv({'x': (False, ), 'y': (True, )})[1] assert state1 != state2 for t in range(3): assert equiv_states(SYS, 3, t, state1=state1, state2=state2)