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)
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)
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))]
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)]
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()