def test_parse_ltl2ba_output__skip(self): text = """ never { /* F(r && !g) */ T0_init : /* init */ if :: (1) -> goto T0_init :: (r && !g) -> goto accept_all fi; accept_all : /* 1 */ skip }""" sig_g = QuantifiedSignal('g') sig_r = QuantifiedSignal('r') signal_by_name = {'r': sig_r, 'g': sig_g} initial_nodes, rejecting_nodes, nodes, _ = _get_hacked_ucw( text, signal_by_name) assert len(initial_nodes) == 1, str(len(initial_nodes)) assert len(rejecting_nodes) == 1, str(len(rejecting_nodes)) assert len(nodes) == 2, str(nodes) accept_all_node = [n for n in nodes if n.name == 'accept_all'][0] assert rejecting_nodes == {accept_all_node} assert len(accept_all_node.transitions) == 1 label, dst_set_list = list(accept_all_node.transitions.items())[0] flagged_dst_set = dst_set_list[0] dst, is_rejecting = flagged_dst_set.pop() assert dst == accept_all_node, str(dst) assert label == {}, str(label)
def _instantiate(spec_text): section_by_name = parse_ltl(spec_text, logging.getLogger()) input_signals = [QuantifiedSignal('r', i) for i in range(nof_processes)] output_signals = [QuantifiedSignal('g', i) for i in range(nof_processes)] assumptions = section_by_name[PAR_ASSUMPTIONS] guarantees = section_by_name[PAR_GUARANTEES] inst_assumptions = list( chain( * [_instantiate_expr2(a, nof_processes, False) for a in assumptions])) inst_guarantees = list( chain( *[_instantiate_expr2(g, nof_processes, False) for g in guarantees])) assumptions_as_strings = [ ConverterToWringVisitor().dispatch(a) + ';\n' for a in inst_assumptions ] guarantees_as_strings = [ ConverterToWringVisitor().dispatch(g) + ';\n' for g in inst_guarantees ] return input_signals, output_signals, assumptions_as_strings, guarantees_as_strings
def test_parse_ltl2ba_output__or(self): text = """ never { T0_init : /* init */ if :: (1) -> goto T0_init :: (r) || (!g) -> goto accept_all fi; accept_all : /* 1 */ skip }""" sig_g = QuantifiedSignal('g') sig_r = QuantifiedSignal('r') signal_by_name = {'r': sig_r, 'g': sig_g} initial_nodes, rejecting_nodes, nodes, _ = _get_hacked_ucw( text, signal_by_name) assert len(initial_nodes) == 1, str(len(initial_nodes)) assert len(rejecting_nodes) == 1, str(len(rejecting_nodes)) assert len(nodes) == 2, str(nodes) init_node = [n for n in nodes if n.name == 'T0_init'][0] assert len(init_node.transitions) == 3, len(init_node.transitions) for label, dst_set_list in init_node.transitions.items(): assert len(dst_set_list) == 1, str(dst_set_list) flagged_dst_set = dst_set_list[0] for dst, is_rejecting in flagged_dst_set: assert (label == {} and dst == init_node) \ or (label == {sig_r: True} and dst != init_node) \ or (label == {sig_g: False} and dst != init_node)
def _get_sync_impl(self, bound, init_process_states, local_automaton): impl = SyncImpl( local_automaton, not self._is_moore, [QuantifiedSignal(n, 0) for n in self._anon_input_names], [QuantifiedSignal(n, 0) for n in self._anon_output_names], bound, self._SYS_STATE_TYPE, QuantifiedSignal(self._names.has_tok_signal, 0), QuantifiedSignal(self._names.sends_signal, 0), QuantifiedSignal(self._names.sends_prev_signal, 0), self._TAU_NAME, init_process_states, self._underlying_solver) return impl
def test_parse_ltl2ba_output(self): text = """ never { /* !([](r -> <>g)) */ T0_init : /* init */ if :: (1) -> goto T0_init :: (!g && r) -> goto accept_S2 fi; accept_S2 : /* 1 */ if :: (!g) -> goto accept_S2 fi; }""" sig_g = QuantifiedSignal('g') sig_r = QuantifiedSignal('r') signal_by_name = {'r': sig_r, 'g': sig_g} initial_nodes, rejecting_nodes, nodes, _ = _get_hacked_ucw( text, signal_by_name) assert len(initial_nodes) == 1, str(len(initial_nodes)) assert len(rejecting_nodes) == 1, str(len(rejecting_nodes)) assert len(nodes) == 2, str(nodes) for n in nodes: if n.name == 'T0_init': assert not n in rejecting_nodes assert n in initial_nodes assert len(n.transitions) == 2 for label, dst_set_list in n.transitions.items(): assert len(dst_set_list) == 1, str(dst_set_list) flagged_dst_set = dst_set_list[0] for dst, is_rejecting in flagged_dst_set: assert (dst.name == 'T0_init' and label == {}) or \ (dst.name == 'accept_S2' and label == {sig_g: False, sig_r: True}), \ 'unknown transition: {0} {1}'.format(label, str(dst)) elif n.name == 'accept_S2': assert n in rejecting_nodes assert not n in initial_nodes assert len(n.transitions) == 1 for label, dst_set_list in n.transitions.items(): assert len(dst_set_list) == 1, str(dst_set_list) flagged_dst_set = dst_set_list[0] dst, is_rejecting = flagged_dst_set.pop() assert dst.name == 'accept_S2' and label == {sig_g: False} else: assert False, 'unknown node: {0}'.format(str(n))
def guarantees(self): """ Return G(tok_i -> Fsends_i) """ #TODO: introduce Globally/Finally class expr = UnaryOp( 'G', BinOp( '->', BinOp('=', QuantifiedSignal(HAS_TOK_NAME, 'i'), Number(1)), UnaryOp( 'F', BinOp('=', QuantifiedSignal(SENDS_NAME, 'i'), Number(1))))) tok_released = ForallExpr(['i'], expr) return [tok_released]
def implications(self) -> list: expr = UnaryOp( 'G', UnaryOp('F', BinOp('=', QuantifiedSignal(HAS_TOK_NAME, 'i'), Number(1)))) fair_tok_sched = ForallExpr(['i'], expr) return [fair_tok_sched]
def visit_signal(self, signal:Signal): if not isinstance(signal, QuantifiedSignal): return signal #noinspection PyUnresolvedReferences new_indices = self._replace(signal.binding_indices) new_signal = QuantifiedSignal(signal.name, *new_indices) return new_signal
def test_strengthen2(self): """ Forall(i) GFa_i and G(b_i) -> Forall(j) GF(c_j) and G(d_j) replaced by 'liveness': Forall(i) GFa_i and G(b_i) -> Forall(j) GF(c_j) and 'safety': Forall(i) G(b_i) -> Forall(j) G(d_j) """ a_i, b_i = QuantifiedSignal('a', 'i'), QuantifiedSignal('b', 'i') c_j, d_j = QuantifiedSignal('c', 'j'), QuantifiedSignal('d', 'j') ass = ForallExpr(['i'], BinOp('*', UnaryOp('G', UnaryOp('F', a_i)), UnaryOp('G', b_i))) gua = ForallExpr(['j'], BinOp('*', UnaryOp('G', UnaryOp('F', c_j)), UnaryOp('G', d_j))) property = SpecProperty([ass], [gua]) safety_properties, liveness_properties = strengthen( property, self._get_converter()) assert len(liveness_properties) == 1, str(liveness_properties) assert len(safety_properties) == 1, str(safety_properties) expected_liveness_gua = ForallExpr(['j'], UnaryOp('G', UnaryOp('F', c_j))) #: :type: SpecProperty liveness_prop = liveness_properties[0] assert str(liveness_prop.assumptions) == str([ass]), str(liveness_prop) assert str(liveness_prop.guarantees) == str([expected_liveness_gua]) safety_prop = safety_properties[0] expected_safety_ass = ForallExpr(['i'], UnaryOp('G', b_i)) expected_safety_gua = ForallExpr(['j'], UnaryOp('G', d_j)) expected_safety_prop = SpecProperty([expected_safety_ass], [expected_safety_gua]) assert str(expected_safety_prop) == str(safety_prop), str(safety_prop)
def test_strengthen2(self): """ Forall(i) GFa_i -> Forall(j) GF(b_j) is left as it is """ a_i, b_j = QuantifiedSignal('a', 'i'), QuantifiedSignal('b', 'j') liveness_ass = ForallExpr(['i'], UnaryOp('G', UnaryOp('F', a_i))) liveness_gua = ForallExpr(['j'], UnaryOp('G', UnaryOp('F', b_j))) property = SpecProperty([liveness_ass], [liveness_gua]) safety_properties, liveness_properties = strengthen( property, self._get_converter()) assert len(liveness_properties) == 1, str(liveness_properties) assert len(safety_properties) == 0, str(safety_properties) actual = liveness_properties[0] expected = property assert str(actual) == str( expected), str(actual) + ' vs ' + str(expected)
def test_strengthen1(self): """ Forall(i) GFa_i -> Forall(j) G(b_j) replaced by 'safety': Forall(j) G(b_j) 'liveness': [] """ a_i, b_j = QuantifiedSignal('a', 'i'), QuantifiedSignal('b', 'j') liveness_ass = ForallExpr(['i'], UnaryOp('G', UnaryOp('F', a_i))) safety_gua = ForallExpr(['j'], UnaryOp('G', b_j)) property = SpecProperty([liveness_ass], [safety_gua]) safety_properties, liveness_properties = strengthen( property, self._get_converter()) assert len(liveness_properties) == 0, str(liveness_properties) assert len(safety_properties) == 1, str(safety_properties) actual_guarantees = safety_properties[0].guarantees assert str(actual_guarantees) == str([safety_gua]), \ '\n' + str(actual_guarantees) + '\nvs\n' + str([safety_gua])
def tests_all(self): input_signals, output_signals, data_by_name = parse( test_string_ltl, test_string_part, logging.getLogger()) exp_input_signals = [ QuantifiedSignal(n, 0) for n in 'idle request0 request1'.split() ] assert set(input_signals) == set(exp_input_signals) exp_output_signals = [ QuantifiedSignal(n, 0) for n in 'grant0 grant1'.split() ] assert set(output_signals) == set(exp_output_signals) for (k, v) in data_by_name.items(): print(k, v) u1_assumptions, u1_guarantees = data_by_name['u1'] assert len(u1_assumptions) == 1 assert len(u1_guarantees) == 3 u2_assumptions, u2_guarantees = data_by_name['u2'] assert len(u2_assumptions) == 2 assert len(u2_guarantees) == 3
def _get_log_encoded_expr(signal:QuantifiedSignal, new_sched_signal_name:str, cutoff:int) -> Expr: assert len(signal.binding_indices) == 1 proc_index = signal.binding_indices[0] nof_sched_bits = int(max(1, math.ceil(math.log(cutoff, 2)))) bits = bin_fixed_list(proc_index, nof_sched_bits) #TODO: use quantified signal or signal? conjuncts = [BinOp('=', QuantifiedSignal(new_sched_signal_name, bit_index), Number(1 if bit_value else 0)) for bit_index, bit_value in enumerate(bits)] conjunction = and_expressions(conjuncts) return conjunction
def _get_par_impl(self, automaton: Automaton, model_size: int, nof_processes: int, sys_intern_funcs_postfix): sched_input_signals = get_signals_definition( self._names.sched_signal, get_log_bits(nof_processes))[0] is_active_signals = [ QuantifiedSignal(self._names.active_signal, i) for i in range(nof_processes) ] sends_signals = [ QuantifiedSignal(self._names.sends_signal, i) for i in range(nof_processes) ] sends_prev_signals = [ QuantifiedSignal(self._names.sends_prev_signal, i) for i in range(nof_processes) ] has_tok_signals = [ QuantifiedSignal(self._names.has_tok_signal, i) for i in range(nof_processes) ] orig_input_signals = [ QuantifiedSignal(n, i) for (n, i) in product(self._anon_input_names, range(nof_processes)) ] orig_output_signals = [ QuantifiedSignal(n, i) for (n, i) in product(self._anon_output_names, range(nof_processes)) ] par_impl = ParImpl(automaton, not self._is_moore, orig_input_signals, orig_output_signals, nof_processes, model_size, sched_input_signals, is_active_signals, sends_signals, sends_prev_signals, has_tok_signals, self._SYS_STATE_TYPE, self._TAU_NAME, sys_intern_funcs_postfix, self._underlying_solver) return par_impl
def _parse_signals_from_lines(signal_lines: list) -> list: signals_raw = chain(*[l.split()[1:] for l in signal_lines]) signals = [QuantifiedSignal(n.strip(), 0) for n in signals_raw] return signals
def _get_is_value(signal_name: str, value: Number, *binding_indices): if len(binding_indices) == 0: signal = Signal(signal_name) else: signal = QuantifiedSignal(signal_name, *binding_indices) return BinOp('=', signal, value)
def get_signals_definition(signal_base_name, nof_bits): signals = list( map(lambda i: QuantifiedSignal(signal_base_name, i), range(nof_bits))) args_defs = list(map(lambda s: (s, 'Bool'), signals)) return signals, args_defs
def __init__( self, automaton: Automaton, is_mealy: bool, orig_inputs, orig_outputs, nof_processes: int, nof_local_states: int, sched_inputs, is_active_signals, sends_signals, sends_prev_signals, #sends_prev is input signals, sends_signals are output, though essentially they are the same has_tok_signals, # it is part of the state, but can be emulated as Moore-like output signal state_type, tau_name, internal_funcs_postfix: str, underlying_solver: SolverInterface): super().__init__(is_mealy, underlying_solver) for s in orig_inputs: # TODO: remove me after debug assert isinstance(s, QuantifiedSignal) assert 'prev' not in s.name, str(s) for s in orig_outputs: # TODO: remove me after debug assert isinstance(s, QuantifiedSignal) assert 'prev' not in s.name, str(s) assert 'tok' not in s.name, str(s) self.automaton = automaton self.nof_processes = nof_processes self._state_type = state_type self._has_tok_signals = has_tok_signals self._sends_prev_signals = sends_prev_signals self._sends_signals = sends_signals self._is_active_signals = is_active_signals self._TAU_NAME = tau_name self._IS_ACTIVE_NAME = 'is_active' + internal_funcs_postfix self._EQUAL_BITS_NAME = 'equal_bits' + internal_funcs_postfix self._PREV_IS_SCHED_NAME = 'prev_is_sched' + internal_funcs_postfix self._NEXT_IS_SCHED_NAME = 'next_is_sched' + internal_funcs_postfix self._TAU_SCHED_WRAPPER_NAME = 'tau_sch' + internal_funcs_postfix self._PROC_ID_PREFIX = 'proc' # TODO: check necessity self._nof_proc_bits = get_log_bits(nof_processes) self._sched_signals = sched_inputs # TODO: check necessity self._sched_arg_type_pairs = [(s, 'Bool') for s in self._sched_signals] #intoduced proc_signals to resemble sched_signals self._proc_signals = [ QuantifiedSignal(self._PROC_ID_PREFIX, i) for i in range(get_log_bits(nof_processes)) ] self._proc_arg_type_pairs = [(s, 'Bool') for s in self._proc_signals] self._equals_first_args, _ = get_signals_definition( 'x', self._nof_proc_bits) self._equals_second_args, _ = get_signals_definition( 'y', self._nof_proc_bits) ### BlankImpl interface TODO: use __init__ with arguments? self.states_by_process = self.nof_processes * [ tuple( self.get_state_name(self._state_type, i) for i in range(nof_local_states)) ] self.state_types_by_process = self.nof_processes * [self._state_type] archi_inputs = sends_prev_signals archi_outputs = has_tok_signals + sends_signals all_models_inputs = orig_inputs + archi_inputs #TODO: rename to self.model_inputs self.orig_inputs = _build_model_inputs(nof_processes, all_models_inputs) self.init_states = self._build_init_states() self.outvar_desc_by_process = [ self._build_desc_by_out(_filter_by_proc(i, orig_outputs), _filter_by_proc(i, archi_outputs), _filter_by_proc(i, all_models_inputs)) for i in range(nof_processes) ] self.aux_func_descs_ordered = self._build_aux_func_descs() self.taus_descs = self._build_taus_descs(all_models_inputs) self.model_taus_descs = self._build_model_taus_descs(all_models_inputs)
def assumptions(self): return [ForallExpr(['i'], UnaryOp('G', UnaryOp('F', BinOp('=', QuantifiedSignal(self._FAIR_SCHED_NAME, 'i'), Number(1)))))]