def test_localize_two_ass_one_gua(self): """ forall(i,j) a_i_j -> forall(i) b_i replaced by forall(i,j) (a_i_j -> b_i) """ a_i_j_is_true, b_j_is_true = _get_is_true('a', 'i', 'j'), _get_is_true('b', 'j') b_i_is_true = _get_is_true('b', 'i') prop = SpecProperty([ForallExpr(['i', 'j'], a_i_j_is_true)], [ForallExpr(['j'], b_j_is_true)]) localized_prop = localize(prop) expected_prop_i_j1 = SpecProperty( [Bool(True)], [ForallExpr(['i', 'j'], BinOp('->', a_i_j_is_true, b_j_is_true))]) expected_prop_i_j2 = SpecProperty( [Bool(True)], [ForallExpr(['i', 'j'], BinOp('->', a_i_j_is_true, b_i_is_true))]) assert str(localized_prop) == str(expected_prop_i_j1) or \ str(localized_prop) == str(expected_prop_i_j2), \ str(localized_prop)
def test_strengthen3(self): """ Forall(i,j) GFa_i * GFb_i_j * Gc_i_j -> Forall(k,m) GF(d_k_m) * G(e_k) replaced by 'safety': Forall(i) Gc_i_j -> Forall(k) G(e_k) 'liveness': Forall(i,j) GFa_i * GFb_i_j * Gc_i -> Forall(k,m) GF(d_k_m) """ a_i, b_i_j, c_i_j = _get_is_true('a', 'i'), _get_is_true( 'b', 'i', 'j'), _get_is_true('c', 'i', 'j') ass = ForallExpr(['i', 'j'], BinOp( '*', UnaryOp('G', UnaryOp('F', a_i)), BinOp('*', UnaryOp('G', UnaryOp('F', b_i_j)), UnaryOp('G', c_i_j)))) d_k_m = _get_is_true('d', 'k', 'm') e_k = _get_is_true('e', 'k') gua = ForallExpr(['k', 'm'], BinOp('*', UnaryOp('G', UnaryOp('F', d_k_m)), UnaryOp('G', e_k))) property = SpecProperty([ass], [gua]) safety_properties, liveness_properties = strengthen( property, self._get_converter()) #lazy.. print('safety_properties', safety_properties) assert len(safety_properties) == 1, str(safety_properties) assert len(list(chain(*[sp.assumptions for sp in safety_properties]))) == 1 assert len(list(chain(*[sp.guarantees for sp in safety_properties]))) == 1 print('liveness_properties', liveness_properties)
def visit_binary_op(self, binary_op:BinOp): if binary_op.name == 'W': bin_op_expr = BinOp('+', BinOp('U', binary_op.arg1, binary_op.arg2), UnaryOp('G', binary_op.arg1)) return self.dispatch(bin_op_expr) else: return super().visit_binary_op(binary_op)
def test_fix_indices_partially_fixed(self): a_i_1 = _get_is_true('a', 'i', 1) c_0 = _get_is_true('c', 0) expr = ForallExpr(['i'], BinOp('*', a_i_1, c_0)) result = _fix_indices({'i': 2}, expr) a_2_1 = _get_is_true('a', 2, 1) expected_result = BinOp('*', a_2_1, c_0) self.assertEqual(str(result), str(expected_result))
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 and_properties(properties) -> SpecProperty: property_expressions = [ BinOp('->', and_expressions(p.assumptions), and_expressions(p.guarantees)) for p in properties ] return SpecProperty([Bool(True)], [and_expressions(property_expressions)])
def normalize_conjuncts(conjuncts:list) -> Expr: """ sound, complete forall(i,j) a_i_j and forall(i) b_i ----> forall(i,j) (a_i_j and b_i) forall(i) a_i and forall(j) b_j ----> forall(i) (a_i and b_i) """ if len(conjuncts) == 0: return Bool(True) if len(conjuncts) == 1 and isinstance(conjuncts[0], Bool): return conjuncts[0] for e in conjuncts: assert isinstance(e, ForallExpr), 'global non-parameterized properties are not supported' max_indices = max(conjuncts, key=lambda e: len(e.arg1)) new_indices = max_indices.arg1 normalized_underlying_expr = None for e in conjuncts: old_indices = e.arg1 new_by_old = dict((o, new_indices[i]) for i, o in enumerate(old_indices)) new_underlying_e = _replace_indices(new_by_old, e.arg2) if normalized_underlying_expr is None: normalized_underlying_expr = new_underlying_e else: normalized_underlying_expr = BinOp('*', normalized_underlying_expr, new_underlying_e) normalized_expr = ForallExpr(new_indices, normalized_underlying_expr) return normalized_expr
def test_get_conjuncts_and_op(self): a, b = Signal('a'), Signal('b') conjunction_expr = BinOp('*', a, b) conjuncts = _get_conjuncts(conjunction_expr) assert len(conjuncts) == 2 assert a in conjuncts assert b in conjuncts
def test_denormalize(self): a_i, b_i = _get_is_true('a', 'i'), _get_is_true('b', 'i') expr = ForallExpr(['i'], BinOp('*', a_i, b_i)) denormalized_expressions = _denormalize(expr) assert len(denormalized_expressions) == 2, str( denormalized_expressions)
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 test_fix_indices_main(self): expr = parse_expr('Forall(i,j,k,m) (a_i_j_k=1 * b_i_m=1)') result = _fix_indices({'i': 1, 'j': 2, 'k': 3}, expr) expected_result = ForallExpr(['m'], BinOp('*', _get_is_true('a', 1, 2, 3), _get_is_true('b', 1, 'm'))) self.assertEqual(str(expected_result), str(result))
def to_boolean_nusmv(lts: LTS, specification: SpecProperty) -> str: nof_state_bits = int(max(1, math.ceil(math.log(len(lts.states), 2)))) bits_by_state = dict((state, bin_fixed_list(i, nof_state_bits)) for (i, state) in enumerate(sorted(lts.states))) state_bits = lmap(_ith_state_bit, range(nof_state_bits)) _assert_no_intersection(state_bits, list(lts.input_signals) + lts.output_signals) dot_lines = StrAwareList() dot_lines += 'MODULE main' dot_lines += 'IVAR' dot_lines += [ ' {signal} : boolean;'.format(signal=s.name) for s in lts.input_signals ] dot_lines += 'VAR' dot_lines += [' {si} : boolean;'.format(si=si) for si in state_bits] dot_lines += 'DEFINE' dot_lines += [ ' {out_name} := {formula} ;'.format(out_name=out_name, formula=_get_formula( out_name, out_model, bits_by_state)) for (out_name, out_model) in lts.model_by_name.items() ] dot_lines += 'ASSIGN' for i, sb in enumerate(state_bits): sb_init = str(bits_by_state[list(lts.init_states)[0]][i]).upper() dot_lines += ' init({sb}) := {init_sb};'.format(sb=sb, init_sb=sb_init) dot_lines += ' next({sb}) := '.format(sb=sb) dot_lines += ' case' for (label, next_state) in lts.tau_model.items(): sb_next = str(bits_by_state[next_state][i]).upper() dot_lines += ' {formula} : {next_state};'.format( formula=_clause_to_formula(label, bits_by_state), next_state=sb_next) dot_lines += ' TRUE : FALSE;' # default: unreachable states, don't care dot_lines += ' esac;' expr = BinOp('->', and_expressions(specification.assumptions), and_expressions(specification.guarantees)) expr = WeakToUntilConverterVisitor().dispatch( expr) # SMV does not have Weak until dot_lines += 'LTLSPEC ' + AstToSmvProperty().dispatch(expr) return '\n'.join(dot_lines)
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 _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 test_localize_zero_ass(self): """ true -> forall(i) b_i replaced by forall(i) (true -> b_i) """ b_i_is_true = _get_is_true('b', 'i') prop = SpecProperty([Bool(True)], [ForallExpr(['i'], b_i_is_true)]) localized_prop = localize(prop) expected_prop = SpecProperty( [Bool(True)], [ForallExpr(['i'], BinOp('->', Bool(True), b_i_is_true))]) assert str(localized_prop) == str(expected_prop), str(localized_prop)
def test_sched_optimization(self): scheduler = InterleavingScheduler() forall_expr = scheduler.assumptions[0] # Forall(i) GFsched_i=1 instantiated_expr = _instantiate_expr( forall_expr, 2, False) # GFsched_0=1 * GFsched_1=1 result_expr = _apply_log_bit_optimization('sch', instantiated_expr, 2, scheduler) expected_expr = BinOp( '*', UnaryOp('G', UnaryOp('F', _get_is_true('sch', 0))), UnaryOp('G', UnaryOp('F', _get_is_false('sch', 0)))) result_str = _get_sorted_conjuncts_str(result_expr) expected_str = _get_sorted_conjuncts_str(expected_expr) self.assertEqual(expected_str, result_str)
def localize(property:SpecProperty): """ sound, but incomplete forall(i) a_i -> forall(j) g_j => forall(i) (a_i -> g_i) forall(i,j) a_i_j -> forall(k) g_k => forall(i,j) (a_i_j -> g_i) """ if not is_quantified_property(property): return property normalized_ass = normalize_conjuncts(property.assumptions) normalized_gua = normalize_conjuncts(property.guarantees) binding_indices_ass = _get_indices(normalized_ass) binding_indices_gua = _get_indices(normalized_gua) if len(binding_indices_ass) > len(binding_indices_gua): max_expr, other_expr = normalized_ass, normalized_gua else: max_expr, other_expr = normalized_gua, normalized_ass assert isinstance(max_expr, ForallExpr) max_binding_indices = max_expr.arg1 ass_newindex_by_old = dict((o, max_binding_indices[i]) for i, o in enumerate(binding_indices_ass)) gua_newindex_by_old = dict((o, max_binding_indices[i]) for i, o in enumerate(binding_indices_gua)) replaced_ass = _replace_indices(ass_newindex_by_old, normalized_ass) replaced_gua = _replace_indices(gua_newindex_by_old, normalized_gua) replaced_underlying_ass = replaced_ass.arg2 if is_quantified_expr(replaced_ass) else replaced_ass replaced_underlying_gua = replaced_gua.arg2 if is_quantified_expr(replaced_gua) else replaced_gua new_gua = ForallExpr(max_binding_indices, BinOp('->', replaced_underlying_ass, replaced_underlying_gua)) new_property = SpecProperty([Bool(True)], [new_gua]) return new_property
def visit_binary_op(self, binary_op:BinOp): return BinOp(binary_op.name, self.dispatch(binary_op.arg1), self.dispatch(binary_op.arg2))
def test_get_conjuncts_no_conjuncts(self): a, b = Signal('a'), Signal('b') conjunction_expr = BinOp('+', a, b) conjuncts = _get_conjuncts(conjunction_expr) assert conjuncts == [conjunction_expr], str(conjunction_expr)
def test_get_conjuncts_recursion(self): a, b, c = Signal('a'), Signal('b'), Signal('c') conjunction_expr = BinOp('*', a, BinOp('*', b, c)) conjuncts = _get_conjuncts(conjunction_expr) assert len(conjuncts) == 3
def expr_from_property(property: SpecProperty) -> Expr: return BinOp('->', and_expressions(property.assumptions), and_expressions(property.guarantees))
def to_expr(spec_property: SpecProperty) -> Expr: return BinOp('->', and_expressions(spec_property.assumptions), and_expressions(spec_property.guarantees))
def assumptions(self): return [ForallExpr(['i'], UnaryOp('G', UnaryOp('F', BinOp('=', QuantifiedSignal(self._FAIR_SCHED_NAME, 'i'), Number(1)))))]
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)