示例#1
0
def _create_state_labels(locs, ctx: AutomatonContext):
    # TODO support multiple locations
    assert len(locs) == 1, "Support only a single location"
    this_loc = locs[0]
    if "transient-values" not in this_loc:
        # TODO put an empty circuit.
        return BV.source(wordlen=1, value=1, name="dummy", signed=False) \
               >> BV.sink(1, inputs=['dummy'])
    label_assignments = []
    labels_assigned = set()
    for assignment in this_loc["transient-values"]:
        var_name = assignment["ref"]
        val = assignment["value"]
        labels_assigned.add(var_name)
        circ = _translate_expression(val, ctx.scope) \
            .resize(ctx.scope.get_aig_variable(var_name).size) \
            .with_output(var_name) \
            .aigbv
        label_assignments.append(circ)
    loc_circuit = par_compose(label_assignments)
    for v in ctx.scope.transient_variables:
        if not v.is_local:
            loc_circuit |= BV.source(
                wordlen=1,
                value=int(v.name in labels_assigned),
                name=f'{v.name}-mod',
                signed=False,
            )
    return loc_circuit
示例#2
0
def translate_jani(data: json):
    global_scope = JaniScope()
    if "constants" in data:
        _translate_constants(data["constants"], global_scope)
    if "variables" in data:
        _translate_variables(data["variables"], global_scope)
    if len(data["automata"]) != 1:
        # TODO
        raise NotImplementedError("Only support monolithic jani.")
    aut, *_ = data["automata"]

    aut_encoding = _translate_automaton(aut,
                                        global_scope.make_local_scope_copy())
    # TODO use mod variables to select which variables to update
    for var in global_scope.variables:
        aut_encoding = aut_encoding >> BV.sink(1, [var.name + "-mod"])
    # TODO use transient mod-variables to check validity
    # TODO use transient mod-variables being set to false to indicate
    #  use of default values

    if len(global_scope.variables) == 0:
        return aut_encoding
    wires, relabels = [], {}
    for var in global_scope.variables:
        name = f'global-{var.name}'
        wires.append({
            'input': var.name,
            'output': var.name,
            'init': var.initial,
            'latch': name,
            'keep_output': True,
        })
        relabels[var.name] = name
    return aut_encoding.loopback(*wires)['o', relabels]
示例#3
0
    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,
        )
示例#4
0
def cone(circ: BV.AIGBV, output: str) -> BV.AIGBV:
    """Return cone of influence aigbv output."""
    for out in circ.outputs - {output}:
        size = circ.omap[out].size
        circ >>= BV.sink(size, [out])
    assert len(circ.outputs) == 1
    assert fn.first(circ.outputs) == output
    return circ
示例#5
0
    def _transition_coin(self, start, action, end):
        # 1. Init latches to start.
        circ = self.aigbv.reinit(start)

        # 2. Omit observations. `end` specifies latches.
        for out in self.outputs:
            circ >>= BV.sink(circ.omap[out].size, [out])
        assert circ.outputs == {'##valid'}

        # 3. Create circuit to check valid coin flips.
        assert circ.omap['##valid'].size == 1
        is_valid = BV.UnsignedBVExpr(circ.unroll(1))

        circ >>= BV.sink(1, {'##valid'})  # Assume circ has no outputs now.

        # 4. Expose latchouts via unrolling.
        circ = circ.unroll(1, omit_latches=False)
        end = {f'{k}##time_1': v for k, v in end.items()}
        action = {f'{k}##time_0': v for k, v in action.items()}
        assert set(end.keys()) == circ.outputs
        assert set(action.keys()) <= circ.inputs

        # 5. Create circuit to check if inputs lead to end.
        test_equals = uatom(1, 1)
        for k, v in end.items():
            size = circ.omap[k].size
            test_equals &= uatom(size, k) == uatom(size, v)
        match_end = BV.UnsignedBVExpr(circ >> test_equals.aigbv)

        # 6. Create circuit to assert inputs match action.
        match_action = uatom(1, 1)
        for k, v in action.items():
            size = circ.imap[k].size
            match_action &= uatom(size, k) == uatom(size, v)

        return aigc.Coin(
            expr=match_end & match_action,
            valid=is_valid & match_action,
        )
示例#6
0
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)
示例#7
0
    def preimage(expr):
        assert expr.inputs <= unrolled_d.outputs

        circ = expr.aigbv
        for name in unrolled_d.outputs - expr.inputs:
            circ >>= BV.sink(omap[name].size, [name])

        valid = BV.uatom(1, unrolled_d.valid_id)
        sat = BV.uatom(1, expr.output)

        preimg = (unrolled_d >> circ).aigbv >> (sat & valid).aigbv
        assert preimg.inputs == unrolled_d.inputs

        return BV.UnsignedBVExpr(preimg)
示例#8
0
def test_reweighted():
    spec, sys = scenario_reactive()

    # Hack too re-weight coinl
    sys2 = C.coin((1, 4), 'c') >> C.MDP(sys.aigbv >> BV.sink(1, ['##valid']))
    cspec2 = concretize(spec, sys2, 3)

    graph, root, _ = spec2graph(cspec2)

    assert nx.is_directed_acyclic_graph(graph)
    assert len(graph.nodes) == 12
    assert len(graph.edges) == 20

    for node in graph.nodes:
        assert graph.out_degree[node] <= 2
示例#9
0
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)
示例#10
0
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)
示例#11
0
def test_minimdp():
    x, y = BV.uatom(2, 'main-x'), BV.uatom(2, 'main-y')
    circ = translate_file("tests/minimdp.jani")
    assert circ.outputs == {'main-x', 'main-y'}

    # Fix edge and check probability of ending on x=3 given valid run.
    # TODO this currently only works with one.
    query = circ << BV.source(2, 0, 'edge', False)
    query >>= BV.sink(2, ['main-y'])
    query >>= (BV.uatom(2, 'main-x') == 3).aigbv

    assert infer.prob(query.unroll(1, only_last_outputs=True)) == approx(0)
    assert infer.prob(query.unroll(2, only_last_outputs=True)) == approx(1 / 4)
    assert infer.prob(query.unroll(3, only_last_outputs=True)) == approx(1 / 3)

    # Randomize edge and check probability of ending on x=y given valid run.
    query = circ.randomize({'edge': {0: 0.5, 1: 0.5}})
    query >>= (x == y).aigbv

    assert infer.prob(query.unroll(1, only_last_outputs=True)) == approx(1 / 4)
    assert infer.prob(query.unroll(2, only_last_outputs=True)) == approx(1 / 4)
    assert infer.prob(query.unroll(3,
                                   only_last_outputs=True)) == approx(13 / 38)
def sys2():
    mdp = sys1()
    mdp <<= C.coin((1, 8), name='c')
    mdp >>= C.circ2mdp(BV.sink(1, ['c']))  # HACK
    return mdp