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 strengthen(property:SpecProperty, ltl2ucw_converter) -> (list, list): """ Return: 'safety' properties (a_s -> g_s), 'liveness' properties (a_s and a_l -> g_l) Also removes ground variables that becomes useless. """ safety_properties = [] liveness_properties = [] denormalized_props = _get_denormalized_property(property) for p in denormalized_props: #: :type: SpecProperty p = p safety_ass = normalize_conjuncts([a for a in p.assumptions if is_safety(a, ltl2ucw_converter)]) all_ass = normalize_conjuncts(p.assumptions) safety_guarantees = [g for g in p.guarantees if is_safety(g, ltl2ucw_converter)] liveness_guarantees = [g for g in p.guarantees if not is_safety(g, ltl2ucw_converter)] safety_properties += [SpecProperty([safety_ass], [sg]) for sg in safety_guarantees] liveness_properties += [SpecProperty([all_ass], [lg]) for lg in liveness_guarantees] return safety_properties, liveness_properties
def test_instantiate_property_cutoff2(self): property = SpecProperty([Bool(True)], [parse_expr('Forall(j) b_j=1')]) result = inst_property(property, 2) expected = SpecProperty([Bool(True)], [parse_expr('b_0=1')]) result_data = _convert_conjunction_to_str(result) expected_data = _convert_conjunction_to_str(expected) self.assertEqual(expected_data, result_data)
def test_instantiate_property_cutoff4(self): property = SpecProperty([parse_expr('Forall (i) a_i = 1')], [parse_expr('Forall(j) b_j=1')]) result = inst_property(property, 4) expected = SpecProperty([parse_expr('a_0=1 * a_1=1 * a_2=1 * a_3=1')], [parse_expr('b_0=1')]) result_data = _convert_conjunction_to_str(result) expected_data = _convert_conjunction_to_str(expected) self.assertEqual(expected_data, result_data)
def test_instantiate_property_cutoff_another_4(self): property = SpecProperty([Bool(True)], [parse_expr('Forall(j,k) b_j=1 -> c_k=1')]) result = inst_property(property, 4) expected = SpecProperty([Bool(True)], [ parse_expr( '(b_0=1 -> c_1=1) * (b_0=1 -> c_2=1) * (b_0=1 -> c_3=1)') ]) result_data = _convert_conjunction_to_str(result) expected_data = _convert_conjunction_to_str(expected) self.assertEqual(expected_data, result_data)
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_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 apply_log_bit_scheduler_optimization(instantiated_property:SpecProperty, scheduler:InterleavingScheduler, new_sched_signal_name:str, cutoff:int) -> SpecProperty: new_assumptions = [_apply_log_bit_optimization(new_sched_signal_name, a, cutoff, scheduler) for a in instantiated_property.assumptions] new_guarantees = [_apply_log_bit_optimization(new_sched_signal_name, g, cutoff, scheduler) for g in instantiated_property.guarantees] return SpecProperty(new_assumptions, new_guarantees)
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 inst_property(property:SpecProperty, cutoff:int) -> SpecProperty: """ forall(i,j) a_i_j -> forall(k) b_k = (!a_0_0 + !a_0_1 + !a_1_0 + !a_1_1 + ..) + (b_0*b_1*b_2..) where maximum values of i,j,k are defined by the min(max_cutoff, actual_cutoff). We optimize instantiations of guarantees by fixing one of the indices. E.g.: forall(i,j) a_i_j -> forall(k) b_k ~ forall(i,j) a_i_j -> b_0 (this is due to isomorphism of processes, TODO: make formal proof) NOTE on quantifier reordering: In the example above we can reorder quantifiers: forall exists (a+b) = exists forall (a+b) But in a general case it is not possible to reorder quantifiers -- it depends on the formula quantified. E.g.: forall(k) exists(l) (a_k XOR b_l) != exists(l) forall(k) (a_k XOR b_l) NOTE on 'forall instantiation' optimization: In the case above we can reorder quantifiers, and we assume that for the formulas of the form forall(i) a_i it is enough to verify a_0 (this assumption is due to symmetry, TODO: prove). Therefore we can instantiate only one guarantee: forall(i,j) a_i_j -> forall(i) b_i ~ forall(i,j) a_i_j -> b_0 NOTE on 'forall optimization' with two and more indices: If the property is "Forall(i,j) a_i_j", can it be replaced with 'a_0_0'? - No. Initially we give the token to a random process (on SMT level we have to tests all the possibilities). This means, that if we proved the property a_0 and process 0 is random <=> we proved Forall (i) a_0. In case of two indices Forall(i,j) a_i_j if we proved Forall(j) a_0_j with the same randomization <=> proved Forall(i,j) a_i_j, but it is incorrect to verify a_0_0 because it is equivalent to Forall (i) a_i_i. """ assumptions = property.assumptions guarantees = [_set_one_index_to_zero(g) for g in property.guarantees] inst_assumptions = [_instantiate_expr(a, cutoff, False) for a in assumptions] inst_guarantees = [_instantiate_expr(g, cutoff, True) for g in guarantees] inst_p = SpecProperty(inst_assumptions, inst_guarantees) return inst_p
def test_localize_one_ass_one_gua(self): """ forall(i) a_i -> forall(j) b_j replaced by forall(i) (a_i -> b_i) """ prop = SpecProperty([parse_expr('Forall (i) a_i=1')], [parse_expr('Forall (j) b_j=1')]) localized_prop = localize(prop) expected_prop_i = SpecProperty( [Bool(True)], [parse_expr('Forall (i) a_i=1 -> b_i=1')]) expected_prop_j = SpecProperty( [Bool(True)], [parse_expr('Forall (j) a_j=1 -> b_j=1')]) expected_prop_str_i = str(expected_prop_i) expected_prop_str_j = str(expected_prop_j) localized_prop_str = str(localized_prop) assert localized_prop_str == expected_prop_str_i \ or localized_prop_str == expected_prop_str_j, str(localized_prop_str)
def _replace_sched_by_true(spec_property: SpecProperty, scheduler) -> SpecProperty: new_assumptions = [ _replace_sched_in_expr_by_true(a, scheduler) for a in spec_property.assumptions ] new_guarantees = [ _replace_sched_in_expr_by_true(g, scheduler) for g in spec_property.guarantees ] return SpecProperty(new_assumptions, new_guarantees)
def _get_denormalized_property(property:SpecProperty) -> list: """ Property assumption may be of the form: forall(i) (safety_i and liveness_i) we introduce 'forall(i)' for each safety_i and liveness_i and therefore get: forall(i) safety_i and forall(i) liveness_i """ denormalized_props = [] denormalized_assumptions = list(chain(*[_denormalize(a) for a in property.assumptions])) denormalized_guarantees = list(chain(*[_denormalize(g) for g in property.guarantees])) for g in denormalized_guarantees: new_p = SpecProperty(denormalized_assumptions, [g]) denormalized_props.append(new_p) return denormalized_props
def _get_acacia_spec(ltl_text: str, part_text: str, logger: logging.Logger) -> (list, list, SpecProperty): input_signals, output_signals, data_by_name = acacia_parser.parse( ltl_text, part_text, logger) if data_by_name is None: return None, None, None spec_properties = [] for (unit_name, unit_data) in data_by_name.items(): assumptions = unit_data[0] guarantees = unit_data[1] spec_properties.append(SpecProperty(assumptions, guarantees)) spec_property = and_properties(spec_properties) return input_signals, output_signals, spec_property
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 _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 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 is_quantified_expr(expr: Expr): return is_quantified_property(SpecProperty([], [expr]))
def _get_automatae(assumptions, guarantees, optimization, cutoff, ltl2ucw_converter: Ltl2UCW, logger): #TODO: check which optimizations are used properties = [SpecProperty(assumptions, [g]) for g in guarantees] logger.info('original properties:\n%s\n', '\n'.join(map(str, properties))) archi = TokRingArchitecture() archi_properties = [ SpecProperty(assumptions, [g]) for g in archi.guarantees() ] logger.info('architecture properties:\n%s\n', '\n'.join(map(str, archi_properties))) if OPTS[optimization] >= OPTS[STRENGTH]: # we don't need this in case of async_hub since its assumptions implies GF(tok), put it is here for simplicity properties = [ SpecProperty(p.assumptions + archi.implications(), p.guarantees) for p in properties ] properties = properties + archi_properties scheduler = InterleavingScheduler() properties = [ SpecProperty(p.assumptions + scheduler.assumptions, p.guarantees) for p in properties ] logger.info('after updating with scheduling assumptions:\n%s\n', '\n'.join(map(str, properties))) #TODO: add support of using other options without using strengthening if OPTS[optimization] >= OPTS[STRENGTH]: logger.info('strengthening properties..') pseudo_safety_properties, pseudo_liveness_properties = strengthen_many( properties, ltl2ucw_converter) properties = pseudo_safety_properties + pseudo_liveness_properties logger.info( 'strengthening resulted in pseudo_safety_properties (a_s -> g_s):\n%s\n', '\n'.join(map(str, pseudo_safety_properties))) logger.info( '..and in pseudo_liveness_properties (a_s&a_l -> g_l):\n%s\n', '\n'.join(map(str, pseudo_liveness_properties))) if OPTS[optimization] >= OPTS[STRENGTH]: properties = [localize(p) for p in properties] logger.info('properties after localizing:\n%s\n', '\n'.join(map(str, properties))) prop_real_cutoff_pairs = [(p, archi.get_cutoff(p)) for p in properties] par_global_property_pairs = [(p, c) for (p, c) in prop_real_cutoff_pairs if c != 2] par_local_property_pairs = [(p, c) for (p, c) in prop_real_cutoff_pairs if c == 2] for (p, c) in par_local_property_pairs: assert p.assumptions == [Bool(True)] assert c == 2 if optimization == SYNC_HUB: # removing GF(sch) from one-indexed properties par_local_property_pairs = [(_replace_sched_by_true(p, scheduler), c) for (p, c) in par_local_property_pairs] if optimization == SYNC_HUB: pass # TODO: should add sync_hub assumptions -- but currently they are added on SMT (Impl) level if optimization == ASYNC_HUB: # by definition async_hub_assumptions are one-indexed async_hub_assumptions = archi.get_async_hub_assumptions() par_local_property_pairs = [ (localize(SpecProperty(async_hub_assumptions, p.guarantees)), c) for (p, c) in par_local_property_pairs ] inst_property_cutoff_pairs = [] for (p, c) in par_global_property_pairs + par_local_property_pairs: inst_c = min(c, cutoff) inst_p = inst_property(p, inst_c) opt_inst_p = apply_log_bit_scheduler_optimization( inst_p, scheduler, SCHED_ID_PREFIX, inst_c) inst_property_cutoff_pairs.append((opt_inst_p, c)) local_properties = [p for (p, c) in inst_property_cutoff_pairs if c == 2] global_property_cutoff_pairs = [(p, min(c, cutoff)) for (p, c) in inst_property_cutoff_pairs if p not in local_properties] logger.info('instantiated local properties:\n%s\n', local_properties) logger.info('instantiated global properties:\n%s\n', global_property_cutoff_pairs) local_automaton = None if len(local_properties) > 0: local_property = and_properties(local_properties) local_automaton = ltl2ucw_converter.convert( expr_from_property(local_property)) glob_automatae_pairs = [] if len(global_property_cutoff_pairs) > 0: glob_automatae_pairs = [ (ltl2ucw_converter.convert(expr_from_property(p)), c) for (p, c) in global_property_cutoff_pairs ] if OPTS[optimization] < OPTS[SYNC_HUB] and local_automaton: if optimization == ASYNC_HUB: glob_automatae_pairs += [(local_automaton, 1)] else: glob_automatae_pairs += [(local_automaton, 2)] sync_automaton = None if OPTS[optimization] >= OPTS[SYNC_HUB]: sync_automaton = local_automaton return sync_automaton, glob_automatae_pairs
def _join_properties(properties: Iterable): properties = list(properties) all_ass = list(chain(p.assumptions for p in properties)) all_gua = list(chain(p.guarantees for p in properties)) return SpecProperty(all_ass, all_gua)