def test_is_not_absorbing(self):
        node = Node('node')

        true_label = Label({})
        node.add_transition(true_label, [('dst1', True)])

        assert not is_final_sink(node)
Example #2
0
def k_reduce(atm: Automaton, k: int, uniform: bool = True) -> Automaton:
    """
    When `uniform` is set, we reset the counter on crossing the border between SCCs.
    When it is not set, k limits the total number of visits to bad states.
    """
    assert k >= 0, k

    finSCC_by_node = build_state_to_final_scc(atm)

    dead_node = Node('dead')
    dead_node.add_transition(LABEL_TRUE, {(dead_node, True)})
    dead_node.k = 0

    new_by_old_k = dict()  # type: Dict[Pair[Node, int], Node]

    def _get_add_node(old_n: Node, k: int) -> Node:
        if k < 0:
            return dead_node
        new_node = new_by_old_k[(old_n, k)] = new_by_old_k.get(
            (old_n, k), Node(old_n.name + 'k' + str(k)))
        new_node.k = k
        return new_node

    old_by_new = dict()  # type: Dict[Node, Node]

    nodes_to_process = set()  # type: Set[Node]
    for n in atm.init_nodes:
        new_n = _get_add_node(n, k)
        old_by_new[new_n] = n
        nodes_to_process.add(new_n)

    processed_nodes = set()  # type: Set[Node]
    processed_nodes.add(dead_node)
    while nodes_to_process:
        new_src = nodes_to_process.pop()
        processed_nodes.add(new_src)
        old_src = old_by_new[new_src]
        for lbl, node_flag_pairs in old_by_new[new_src].transitions.items(
        ):  # type: (Label, Set[Pair[Node, bool]])
            for old_dst, is_fin in node_flag_pairs:
                if is_final_sink(old_dst):
                    new_dst = dead_node
                else:
                    new_dst_k = new_src.k - is_fin if (not uniform or _within_same_finSCC(old_src, old_dst, finSCC_by_node))\
                                else k
                    new_dst = _get_add_node(old_dst, new_dst_k)
                # For "into dead" transitions (lbl, dead) it is possible
                # that it is already present, so we check
                if lbl not in new_src.transitions or (
                        new_dst, False) not in new_src.transitions[lbl]:
                    new_src.add_transition(lbl, {(new_dst, False)})
                else:
                    assert new_dst == dead_node, "I know only the case of repetitions of transitions into dead"
                old_by_new[new_dst] = old_dst
                if new_dst not in processed_nodes:
                    nodes_to_process.add(new_dst)

    return Automaton({_get_add_node(next(iter(atm.init_nodes)), k)},
                     processed_nodes)
    def test_is_absorbing(self):
        node = Node('node')

        true_label = Label({})
        node.add_transition(true_label, [(node, True)])
        node.add_transition(Label({'r':True}), [(node, True)])

        assert is_final_sink(node)
Example #4
0
def _encode_transitions_ucw(reach_func_desc: FuncDesc,
                            rank_func_desc: FuncDesc,
                            tau_desc: FuncDesc,
                            desc_by_output: Dict[Signal, FuncDesc],
                            inputs: List[Signal],
                            q: Node,
                            m: int,
                            i_o: Label,
                            state_to_final_scc: dict = None) -> List[str]:
    # syntax sugar
    def smt_r(smt_m: str, smt_q: str):
        return call_func(rank_func_desc, {
            ARG_MODEL_STATE: smt_m,
            ARG_A_STATE: smt_q
        })

    def smt_reach(smt_m: str, smt_q: str):
        return call_func(reach_func_desc, {
            ARG_MODEL_STATE: smt_m,
            ARG_A_STATE: smt_q
        })

    def smt_tau(smt_m: str, i_o: Label):
        tau_args = build_tau_args_dict(inputs, smt_m, i_o)
        return call_func(tau_desc, tau_args)

    #

    smt_m, smt_q = smt_name_m(m), smt_name_q(q)
    smt_m_next = smt_tau(smt_m, i_o)

    smt_pre = op_and(
        [smt_reach(smt_m, smt_q),
         smt_out(smt_m, i_o, inputs, desc_by_output)])

    smt_post_conjuncts = []
    for q_next, is_fin in q.transitions[i_o]:
        if is_final_sink(q_next):
            smt_post_conjuncts = [false()]
            break

        smt_q_next = smt_name_q(q_next)

        smt_post_conjuncts.append(smt_reach(smt_m_next, smt_q_next))

        greater_op = _get_greater_op_ucw(q, is_fin, q_next, state_to_final_scc)
        if greater_op is not None:
            smt_post_conjuncts.append(
                greater_op(smt_r(smt_m, smt_q), smt_r(smt_m_next, smt_q_next)))

    smt_post = op_and(smt_post_conjuncts)
    pre_implies_post = op_implies(smt_pre, smt_post)
    free_input_args = get_free_input_args(i_o, inputs)

    return [assertion(forall_bool(free_input_args, pre_implies_post))]
Example #5
0
def _encode_transitions_nbw(m: int, q: Node, reach_func_desc: FuncDesc,
                            rank_func_desc: FuncDesc, tau_desc: FuncDesc,
                            desc_by_output: Dict[Signal, FuncDesc],
                            inputs: List[Signal]) -> List[str]:
    # syntax sugar
    def smt_r(smt_m: str, smt_q: str):
        return call_func(rank_func_desc, {
            ARG_MODEL_STATE: smt_m,
            ARG_A_STATE: smt_q
        })

    def smt_reach(smt_m: str, smt_q: str):
        return call_func(reach_func_desc, {
            ARG_MODEL_STATE: smt_m,
            ARG_A_STATE: smt_q
        })

    def smt_tau(smt_m: str, i_o: Label):
        tau_args = build_tau_args_dict(inputs, smt_m, i_o)
        return call_func(tau_desc, tau_args)

    # reach(q,t) ->
    #     OR{(q,io,q') \in \delta(q)}:
    #         sys_out=o & reach(q',t') & rank(q,t,q',t')

    s_m = smt_name_m(m)
    s_q = smt_name_q(q)
    s_pre = smt_reach(s_m, s_q)

    s_disjuncts = list()  # type: List[str]
    for lbl, qn_flag_pairs in q.transitions.items(
    ):  # type: (Label, Set[Tuple[Node, bool]])
        s_m_next = smt_tau(s_m, lbl)
        s_out = smt_out(s_m, lbl, inputs, desc_by_output)
        free_inputs = get_free_input_args(lbl, inputs)
        for (q_next, is_acc) in qn_flag_pairs:
            if is_final_sink(q_next):
                s_disj = exists_bool(free_inputs, s_out)
                s_disjuncts.append(s_disj)
                break
            s_q_next = smt_name_q(q_next)
            s_reach = smt_reach(s_m_next, s_q_next)
            if is_acc:
                s_rank = true()  # TODO: SCCs
            else:
                s_rank = op_gt(smt_r(s_m, s_q), smt_r(s_m_next, s_q_next))

            s_disj = exists_bool(free_inputs, op_and([s_out, s_reach, s_rank]))
            s_disjuncts.append(s_disj)

    s_assertion = op_implies(s_pre, op_or(s_disjuncts))
    return [assertion(s_assertion)]
Example #6
0
def atm_to_verilog(atm: Automaton, sys_inputs: Iterable[Signal],
                   sys_outputs: Iterable[Signal], module_name: str,
                   bad_out_name: str) -> str:
    assert len(lfilter(lambda n: is_final_sink(n), atm.nodes)) == 1,\
        'for now I support only one bad state which must be a sink'
    sys_inputs = set(sys_inputs)
    sys_outputs = set(sys_outputs)

    verilog_by_sig = {s: 'controllable_' + s.name for s in sys_outputs}
    verilog_by_sig.update({s: s.name for s in sys_inputs})

    verilog_by_node = {q: '__q' + q.name for q in atm.nodes}
    sink_q = lfilter(lambda n: is_final_sink(n), atm.nodes)[0]

    module_inputs = list(
        chain(map(lambda i: i.name, sys_inputs),
              map(lambda o: 'controllable_' + o.name, sys_outputs)))

    s = StrAwareList()
    s += 'module {module_name}({inputs}, {output});'.format(
        module_name=module_name,
        inputs=', '.join(module_inputs),
        output=bad_out_name)
    s.newline()

    s += '\n'.join('input %s;' % i for i in module_inputs)
    s.newline()

    s += 'output %s;' % bad_out_name
    s.newline()

    s += '\n'.join('reg %s;' % vq for vq in verilog_by_node.values())
    s.newline()

    s += 'wire %s;' % bad_out_name
    s += 'assign {bad} = {sink_q};'.format(bad=bad_out_name,
                                           sink_q=verilog_by_node[sink_q])
    s.newline()

    s += 'initial begin'
    s += '\n'.join('%s = 1;' % verilog_by_node[iq] for iq in atm.init_nodes)
    s += '\n'.join('%s = 0;' % verilog_by_node[q]
                   for q in atm.nodes - atm.init_nodes)
    s += 'end'
    s.newline()

    s += 'always@($global_clock)'
    s += 'begin'

    def lbl_to_v(lbl: Label) -> str:
        return ' && '.join(
            ('!%s', '%s')[lbl[s]] % verilog_by_sig[s] for s in lbl) or '1'

    for q in atm.nodes:
        incoming_edges = incoming_transitions(q, atm)
        if not incoming_edges:
            update_expr = '0'
        else:
            update_expr = ' || '.join('{src} && {lbl}'.format(
                src=verilog_by_node[edge[0]], lbl=lbl_to_v(edge[1]))
                                      for edge in incoming_edges)
        s += '  {q} <= {update_expr};'.format(q=verilog_by_node[q],
                                              update_expr=update_expr)
    s += 'end'
    s += 'endmodule'
    return s.to_str()