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 test_reduce(self): """ Forall(k,m,l) Gb_k_m replaced by Forall(k,m) Gb_k_m """ b_k_m = _get_is_true('b', 'k', 'm') expr = ForallExpr(['k', 'm', 'l'], UnaryOp('G', b_k_m)) actual_expr = _reduce_quantifiers(expr) expected_expr = ForallExpr(['k', 'm'], UnaryOp('G', b_k_m)) assert str(actual_expr) == str(expected_expr), str(actual_expr)
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 visit_forall(self, node:ForallExpr): #: :type: tuple binding_indices_visited = self.dispatch(node.arg1) #: :type: Expr quantified_expr_visited = self.dispatch(node.arg2) return ForallExpr(binding_indices_visited, quantified_expr_visited)
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_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 _reduce_quantifiers(expr:ForallExpr) -> Expr: """ Remove useless binding indices """ quantified_signals_finder = QuantifiedSignalsFinderVisitor() quantified_signals_finder.dispatch(expr) quantified_signals = quantified_signals_finder.quantified_signals indices_used = set(chain(*[qs.binding_indices for qs in quantified_signals])) return ForallExpr(indices_used, expr.arg2)
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 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 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 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 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 _fix_indices(value_by_index:dict, expr:Expr): """ if 'index' is not in the value_by_index therefore leave it as is """ if not is_quantified_expr(expr): return expr newindex_by_oldindex = dict((i, i) for i in _get_indices(expr)) newindex_by_oldindex.update(value_by_index) replaced_expr = _replace_indices(newindex_by_oldindex, expr) indices = _get_indices(replaced_expr) new_indices = list(filter(lambda i: isinstance(i, str), indices)) if len(new_indices) == 0: return replaced_expr.arg2 else: assert isinstance(expr, ForallExpr) return ForallExpr(new_indices, replaced_expr.arg2)
def _denormalize(conjunct:Expr) -> list: """ Forall(i) a_i and b_i replaced by Forall(i) a_i and Forall(i) b_i """ normalized_conjunct = normalize_conjuncts([conjunct]) if not is_quantified_property(SpecProperty([normalized_conjunct], [])): return [normalized_conjunct] #: :type: ForallExpr forall_expr = conjunct quantified_expr = forall_expr.arg2 conjunctions = _get_conjuncts(quantified_expr) return [_reduce_quantifiers(ForallExpr(forall_expr.arg1, c)) for c in conjunctions]
def assumptions(self): return [ForallExpr(['i'], UnaryOp('G', UnaryOp('F', BinOp('=', QuantifiedSignal(self._FAIR_SCHED_NAME, 'i'), Number(1)))))]