def test_eventually(self): parser = self.parser i_, i_a, i_b, i_ab = self.i_, self.i_a, self.i_b, self.i_ab true = self.true false = self.false assert parser("F a").delta(i_a) == PLOr([ true, PLAnd([ true, PLAnd([ PLAtomic(LTLfUntil([LTLfTrue(), LTLfAtomic("a")])), PLAtomic(LTLfUntil([LTLfTrue(), LTLfTrue()])), ]), ]), ]) assert parser("F a").delta(i_) == PLOr([ false, PLAnd([ true, PLAnd([ PLAtomic(LTLfUntil([LTLfTrue(), LTLfAtomic("a")])), PLAtomic(LTLfUntil([LTLfTrue(), LTLfTrue()])), ]), ]), ]) assert parser("F a").delta(i_a, epsilon=True) == false assert parser("F a").delta(i_, epsilon=True) == false
def test_always(self): parser = self.parser i_, i_a, i_b, i_ab = self.i_, self.i_a, self.i_b, self.i_ab true = self.true false = self.false assert parser("G a").delta(i_a) == PLAnd([ true, PLOr([ false, PLOr([ PLAtomic(LTLfRelease([LTLfFalse(), LTLfAtomic("a")])), PLAtomic(LTLfRelease([LTLfFalse(), LTLfFalse()])), ]), ]), ]) assert parser("G a").delta(i_a) == PLAnd([ true, PLOr([ false, PLOr([ PLAtomic(LTLfRelease([LTLfFalse(), LTLfAtomic("a")])), PLAtomic(LTLfRelease([LTLfFalse(), LTLfFalse()])), ]), ]), ]) assert parser("G a").delta(i_a, epsilon=True) == true assert parser("G a").delta(i_, epsilon=True) == true
def test_and(self): parser = self.parser i_, i_a, i_b, i_ab = self.i_, self.i_a, self.i_b, self.i_ab true = self.true false = self.false assert parser("a & b").delta(i_) == PLAnd([false, false]) assert parser("a & b").delta(i_a) == PLAnd([true, false]) assert parser("a & b").delta(i_b) == PLAnd([false, true]) assert parser("a & b").delta(i_ab) == PLAnd([true, true]) assert parser("a & b").delta(i_, epsilon=True) == false
def test_next(self): parser = self.parser i_, i_a, i_b, i_ab = self.i_, self.i_a, self.i_b, self.i_ab true = self.true false = self.false assert parser("X A").delta(i_) == PLAnd( [PLAtomic(LTLfAtomic("A")), PLAtomic(LTLfEventually(LTLfTrue()).to_nnf())]) assert parser("X A").delta(i_a) == PLAnd( [PLAtomic(LTLfAtomic("A")), PLAtomic(LTLfEventually(LTLfTrue()).to_nnf())]) assert parser("X A").delta(i_b) == PLAnd( [PLAtomic(LTLfAtomic("A")), PLAtomic(LTLfEventually(LTLfTrue()).to_nnf())]) assert parser("X A").delta(i_ab) == PLAnd( [PLAtomic(LTLfAtomic("A")), PLAtomic(LTLfEventually(LTLfTrue()).to_nnf())]) assert parser("X A").delta(i_, epsilon=True) == false
def test_and(self): a, b = self.a, self.b i_, i_a, i_b, i_ab = self.i_, self.i_a, self.i_b, self.i_ab a_and_b = PLAnd([a, b]) assert not a_and_b.truth(i_) assert not a_and_b.truth(i_a) assert not a_and_b.truth(i_b) assert a_and_b.truth(i_ab) not_a_and_not_b = PLAnd([PLNot(a), PLNot(b)]) assert not_a_and_not_b.truth(i_) assert not not_a_and_not_b.truth(i_a) assert not not_a_and_not_b.truth(i_b) assert not not_a_and_not_b.truth(i_ab)
def p_propositional(self, p): """propositional : propositional EQUIVALENCE propositional | propositional IMPLIES propositional | propositional OR propositional | propositional AND propositional | NOT propositional | FALSE | TRUE | ATOM""" if len(p)==4: if p[2] == Symbols.EQUIVALENCE.value: p[0] = PLEquivalence([p[1], p[3]]) elif p[2] == Symbols.IMPLIES.value: p[0] = PLImplies([p[1], p[3]]) elif p[2] == Symbols.OR.value: p[0] = PLOr([p[1], p[3]]) elif p[2] == Symbols.AND.value: p[0] = PLAnd([p[1], p[3]]) else: raise ValueError # else: # p[0] = p[2] elif len(p)==3: p[0] = PLNot(p[2]) elif len(p)==2: if p[1]==Symbols.TRUE.value: p[0] = PLTrue() elif p[1]==Symbols.FALSE.value: p[0] = PLFalse() else: p[0] = PLAtomic(p[1]) else: raise ValueError
def _delta(self, i: PLInterpretation, epsilon=False): if epsilon: return PLFalse() else: return PLAnd( [PLAtomic(self.f), PLAtomic(LTLfNot(LTLfEnd()).to_nnf())])
def delta_diamond(self, f: LDLfFormula, i: PropositionalInterpretation, epsilon=False) -> PLFormula: """Apply delta function for regular expressions in the diamond operator.""" return PLAnd([self.f._delta(i, epsilon), f._delta(i, epsilon)]) # type: ignore
def _delta(self, i: PropositionalInterpretation, epsilon=False): """Apply the delta function.""" if epsilon: return PLFalse() else: return PLAnd( [PLAtomic(self.f), PLAtomic(LTLfNot(LTLfEnd()).to_nnf())])
def prop_and(self, args): if len(args) == 1: return args[0] elif (len(args) - 1) % 2 == 0: subformulas = args[::2] return PLAnd(subformulas) else: raise ParsingError
def _make_transition(Q: FrozenSet[FrozenSet[PLAtomic]], i: PLInterpretation): actions_set = i.true_propositions new_macrostate = set() for q in Q: # delta function applied to every formula in the macro state Q delta_formulas = [f.s.delta(actions_set) for f in q] # find the list of atoms, which are "true" atoms (i.e. propositional atoms) or LDLf formulas atomics = [s for subf in delta_formulas for s in find_atomics(subf)] atom2id = {v: k for k, v in enumerate(atomics)} # type: Dict[int, PLAtomic] id2atom = {v: k for k, v in atom2id.items()} # type: Dict[PLAtomic, int] # "freeze" the found atoms as symbols and build a mapping from symbols to formulas symbol2formula = { atom2id[f] for f in atomics if f != PLTrue() and f != PLFalse() } # build a map from formula to a "freezed" propositional Atomic Formula formula2atomic_formulas = { f: PLAtomic(atom2id[f]) if f != PLTrue() and f != PLFalse() # and not isinstance(f, PLAtomic) else f for f in atomics } # the final list of Propositional Atomic Formulas, one for each formula in the original macro state Q transformed_delta_formulas = [ _transform_delta(f, formula2atomic_formulas) for f in delta_formulas ] # the empty conjunction stands for true if len(transformed_delta_formulas) == 0: conjunctions = PLTrue() elif len(transformed_delta_formulas) == 1: conjunctions = transformed_delta_formulas[0] else: conjunctions = PLAnd(transformed_delta_formulas) # the model in this case is the smallest set of symbols s.t. the conjunction of "freezed" atomic formula # is true. alphabet = frozenset(symbol2formula) models = frozenset(conjunctions.minimal_models(alphabet)) for min_model in models: q_prime = frozenset( {id2atom[s] for s in min_model.true_propositions}) new_macrostate.add(q_prime) return frozenset(new_macrostate)
def delta_box(self, f: LDLfFormula, i: PropositionalInterpretation, epsilon=False): """Apply delta function for regular expressions in the box operator.""" return PLAnd([ f._delta(i, epsilon), LDLfBox(self.f, _FreezedTrue(LDLfBox(self, f)))._delta(i, epsilon), ])
def delta_box(self, f: LDLfFormula, i: PLInterpretation, epsilon=False): # subf = LDLfBox(self.f, T(LDLfBox(self, f))) # k = subf._delta(i, epsilon) # l = [f._delta(i, epsilon), subf] # ff = PLAnd(l) return PLAnd([ f._delta(i, epsilon), LDLfBox(self.f, T(LDLfBox(self, f)))._delta(i, epsilon) ])
def test_parser(): parser = PLParser() sa, sb = "A", "B" a, b = PLAtomic(sa), PLAtomic(sb) a_and_b = parser("A & B") true_a_and_b = PLAnd([a, b]) assert a_and_b == true_a_and_b material_implication = parser("!A | B <-> !(A & !B) <-> A->B") true_material_implication = PLEquivalence( [PLOr([PLNot(a), b]), PLNot(PLAnd([a, PLNot(b)])), PLImplies([a, b])] ) assert material_implication == true_material_implication true_a_and_false_and_true = PLAnd([a, PLFalse(), PLTrue()]) a_and_false_and_true = parser("A & false & true") assert a_and_false_and_true == true_a_and_false_and_true
def _delta(self, i: PLInterpretation, epsilon: bool = False): if epsilon: return PLFalse() f1 = self.formulas[0] f2 = LTLfUntil( self.formulas[1:]) if len(self.formulas) > 2 else self.formulas[1] return PLOr([ f2._delta(i, epsilon), PLAnd([f1._delta(i, epsilon), LTLfNext(self)._delta(i, epsilon)]) ])
def _delta(self, i: PLInterpretation, epsilon=False): if epsilon: return PLTrue() f1 = self.formulas[0] f2 = LTLfRelease( self.formulas[1:]) if len(self.formulas) > 2 else self.formulas[1] return PLAnd([ f2._delta(i, epsilon), PLOr( [f1._delta(i, epsilon), LTLfWeakNext(self)._delta(i, epsilon)]) ])
def _delta(self, i: PropositionalInterpretation, epsilon: bool = False): """Apply the delta function.""" if epsilon: return PLFalse() f1 = self.formulas[0] f2 = (LTLfUntil(self.formulas[1:]) if len(self.formulas) > 2 else self.formulas[1]) return PLOr([ f2._delta(i, epsilon), PLAnd([f1._delta(i, epsilon), LTLfNext(self)._delta(i, epsilon)]), ])
def test_misc(self): a, b = self.a, self.b i_, i_a, i_b, i_ab = self.i_, self.i_a, self.i_b, self.i_ab material_implication = PLEquivalence( [PLOr([PLNot(a), b]), PLNot(PLAnd([a, PLNot(b)])), PLImplies([a, b])] ) # the equivalence is valid (i.e. satisfied for every interpretation) assert material_implication.truth(i_) assert material_implication.truth(i_a) assert material_implication.truth(i_b) assert material_implication.truth(i_ab) a_and_false_and_true = PLAnd([a, PLFalse(), PLTrue()]) assert not a_and_false_and_true.truth(i_) assert not a_and_false_and_true.truth(i_a) assert not a_and_false_and_true.truth(i_b) assert not a_and_false_and_true.truth(i_ab) a_or_false_or_true = PLOr([a, PLFalse(), PLTrue()]) assert a_or_false_or_true.truth(i_) assert a_or_false_or_true.truth(i_a) assert a_or_false_or_true.truth(i_b) assert a_or_false_or_true.truth(i_ab)
def _is_true(Q: FrozenSet[FrozenSet]): if frozenset() in Q: return True conj = [ PLAnd([subf.s.delta(None, epsilon=True) for subf in q]) if len(q) >= 2 else next(iter(q)).s.delta( None, epsilon=True) if len(q) == 1 else PLFalse() for q in Q ] if len(conj) == 0: return False else: pl_conj = PLOr(conj) if len(conj) >= 2 else conj[0] result = pl_conj.truth({}) return result
def test_nnf(): parser = PLParser() sa, sb = "A", "B" a, b = PLAtomic(sa), PLAtomic(sb) i_ = {} i_a = {sa: True} i_b = {sb: True} i_ab = {sa: True, sb: True} not_a_and_b = parser("!(A&B)") nnf_not_a_and_b = parser("!A | !B") assert not_a_and_b.to_nnf() == nnf_not_a_and_b assert nnf_not_a_and_b == nnf_not_a_and_b.to_nnf() dup = parser("!(A | A)") nnf_dup = dup.to_nnf() assert nnf_dup == PLAnd([PLNot(a), PLNot(a)]) material_implication = parser("!A | B <-> !(A & !B) <-> A->B") nnf_material_implication = parser( "((!A | B) & (!A | B) & (!A | B)) | ((A & !B) & (A & !B) & (A & !B))" ) nnf_m = material_implication.to_nnf() assert nnf_m == nnf_material_implication.to_nnf() assert ( nnf_m.truth(i_) == material_implication.truth(i_) == nnf_material_implication.truth(i_) == True ) assert ( nnf_m.truth(i_a) == material_implication.truth(i_a) == nnf_material_implication.truth(i_a) == True ) assert ( nnf_m.truth(i_b) == material_implication.truth(i_b) == nnf_material_implication.truth(i_b) == True ) assert ( nnf_m.truth(i_ab) == material_implication.truth(i_ab) == nnf_material_implication.truth(i_ab) == True )
def test_until(self): parser = self.parser i_, i_a, i_b, i_ab = self.i_, self.i_a, self.i_b, self.i_ab true = self.true false = self.false assert parser("A U B").delta(i_a) == PLOr([ false, PLAnd([ true, PLAtomic(LTLfUntil([LTLfAtomic("A"), LTLfAtomic("B")])), PLAtomic(LTLfEventually(LTLfTrue()).to_nnf()) ]) ]) assert parser("A U B").delta(i_ab, epsilon=True) == false
def test_release(self): parser = self.parser i_, i_a, i_b, i_ab = self.i_, self.i_a, self.i_b, self.i_ab true = self.true false = self.false assert parser("A R B").delta(i_a) == PLAnd([ false, PLOr([ true, PLAtomic(LTLfRelease([LTLfAtomic("A"), LTLfAtomic("B")])), PLAtomic(LTLfAlways(LTLfFalse()).to_nnf()) ]) ]) assert parser("A R B").delta(i_ab, epsilon=True) == true
def test_1(self): sa, sb = "A", "B" a, b = PLAtomic(sa), PLAtomic(sb) i_ = {} i_a = {"A": True} i_b = {"B": True} i_ab = {"A": True, "B": True} tr_false_a_b_ab = [i_, i_a, i_b, i_ab, i_] tt = LDLfLogicalTrue() ff = LDLfLogicalFalse() assert tt.truth(tr_false_a_b_ab, 0) assert not ff.truth(tr_false_a_b_ab, 0) assert not LDLfNot(tt).truth(tr_false_a_b_ab, 0) assert LDLfNot(ff).truth(tr_false_a_b_ab, 0) # assert LDLfAnd([LDLfPropositional(a), LDLfPropositional(b)]).truth( # tr_false_a_b_ab, 3 # ) assert not LDLfDiamond(RegExpPropositional(PLAnd([a, b])), tt).truth( tr_false_a_b_ab, 0) trace = self.trace parser = self.parser formula = "<true*;A&B>tt" parsed_formula = parser(formula) assert parsed_formula.truth(trace, 0) formula = "[(A+!B)*]<C>tt" parsed_formula = parser(formula) assert not parsed_formula.truth(trace, 1) formula = "<?(<!C>tt)><A>tt" parsed_formula = parser(formula) assert parsed_formula.truth(trace, 1) formula = "<!C+A>tt" parsed_formula = parser(formula) assert parsed_formula.truth(trace, 1) formula = "<!C+A>tt" parsed_formula = parser(formula) assert parsed_formula.truth(trace, 1)
def test_truth(): sa, sb = "a", "b" a, b = PLAtomic(sa), PLAtomic(sb) i_ = PLFalseInterpretation() i_a = PLInterpretation({sa}) i_b = PLInterpretation({sb}) i_ab = PLInterpretation({sa, sb}) tr_false_a_b_ab = FiniteTrace([i_, i_a, i_b, i_ab, i_]) tt = LDLfLogicalTrue() ff = LDLfLogicalFalse() assert tt.truth(tr_false_a_b_ab, 0) assert not ff.truth(tr_false_a_b_ab, 0) assert not LDLfNot(tt).truth(tr_false_a_b_ab, 0) assert LDLfNot(ff).truth(tr_false_a_b_ab, 0) assert LDLfAnd([LDLfPropositional(a), LDLfPropositional(b)]).truth(tr_false_a_b_ab, 3) assert not LDLfDiamond(RegExpPropositional(PLAnd([a, b])), tt).truth( tr_false_a_b_ab, 0) parser = LDLfParser() trace = FiniteTrace.from_symbol_sets([{}, {"A"}, {"A"}, {"A", "B"}, {}]) formula = "<true*;A&B>tt" parsed_formula = parser(formula) assert parsed_formula.truth(trace, 0) formula = "[(A+!B)*]<C>tt" parsed_formula = parser(formula) assert not parsed_formula.truth(trace, 1) formula = "<(<!C>tt)?><A>tt" parsed_formula = parser(formula) assert parsed_formula.truth(trace, 1) formula = "<!C+A>tt" parsed_formula = parser(formula) assert parsed_formula.truth(trace, 1)
def _make_transition(marco_q: FrozenSet[FrozenSet[PLAtomic]], i: PropositionalInterpretation): new_macrostate = set() for q in marco_q: # delta function applied to every formula in the macro state Q delta_formulas = [cast(Delta, f.s).delta(i) for f in q] # find atomics -> so also ldlf formulas # replace atomic with custom object # convert to sympy # find the list of atoms, which are "true" atoms # (i.e. propositional atoms) or LDLf formulas atomics = [s for subf in delta_formulas for s in find_atomics(subf)] atom2id = {v: str(k) for k, v in enumerate(atomics)} # type: Dict[PLAtomic, str] id2atom = {v: k for k, v in atom2id.items()} # type: Dict[str, PLAtomic] # build a map from formula to a "freezed" propositional Atomic Formula formula2atomic_formulas = { f: PLAtomic(atom2id[f]) if f != PLTrue() and f != PLFalse() # and not isinstance(f, PLAtomic) else f for f in atomics } # the final list of Propositional Atomic Formulas, # one for each formula in the original macro state Q transformed_delta_formulas = [ _transform_delta(f, formula2atomic_formulas) for f in delta_formulas ] # the empty conjunction stands for true if len(transformed_delta_formulas) == 0: conjunctions = PLTrue() elif len(transformed_delta_formulas) == 1: conjunctions = transformed_delta_formulas[0] else: conjunctions = PLAnd(transformed_delta_formulas) # type: ignore # the model in this case is the smallest set of symbols # s.t. the conjunction of "freezed" atomic formula is true. # alphabet = frozenset(symbol2formula) # models = frozenset(conjunctions.minimal_models(alphabet)) formula = to_sympy(conjunctions, replace=atom2id) # type: ignore all_models = list(sympy.satisfiable(formula, all_models=True)) if len(all_models) == 1 and all_models[0] == BooleanFalse(): models = [] # type: List[Set[str]] elif len(all_models) == 1 and all_models[0] == {True: True}: models = [set()] else: models = list( map(lambda x: {k for k, v in x.items() if v is True}, all_models)) for min_model in models: q_prime = frozenset({id2atom[s] for s in map(str, min_model)}) new_macrostate.add(q_prime) return frozenset(new_macrostate)
def delta_diamond(self, f: LDLfFormula, i: PLInterpretation, epsilon=False): return PLAnd([self.f._delta(i, epsilon), f._delta(i, epsilon)])
def delta_box(self, f: LDLfFormula, i: PLInterpretation, epsilon=False): return PLAnd( [LDLfBox(r, f)._delta(i, epsilon) for r in self.formulas_set])
def test_parser(): parser = LDLfParser() a, b = PLAtomic("A"), PLAtomic("B") tt = LDLfLogicalTrue() ff = LDLfLogicalFalse() true = PLTrue() false = PLFalse() r_true = RegExpPropositional(true) r_false = RegExpPropositional(false) assert tt == parser("tt") assert ff == parser("ff") assert LDLfDiamond(r_true, tt) == parser("<true>tt") assert LDLfDiamond(r_false, tt) == parser("<false>tt") assert parser("!tt & <!A&B>tt") == LDLfAnd([ LDLfNot(tt), LDLfDiamond(RegExpPropositional(PLAnd([PLNot(a), b])), tt) ]) assert parser("[true*]([true]ff | <!A>tt | <(true)*><B>tt)") == LDLfBox( RegExpStar(r_true), LDLfOr([ LDLfBox(r_true, ff), LDLfDiamond(RegExpPropositional(PLNot(a)), tt), LDLfDiamond(RegExpStar(r_true), (LDLfDiamond(RegExpPropositional(b), tt))), ]), ) assert parser("[A&B&A]ff <-> <A&B&A>tt") == LDLfEquivalence([ LDLfBox(RegExpPropositional(PLAnd([a, b, a])), ff), LDLfDiamond(RegExpPropositional(PLAnd([a, b, a])), tt), ]) assert parser("<A+B>tt") == LDLfDiamond( RegExpUnion([RegExpPropositional(a), RegExpPropositional(b)]), tt) assert parser("<A;B>tt") == LDLfDiamond( RegExpSequence([RegExpPropositional(a), RegExpPropositional(b)]), tt) assert parser("<A+(B;A)>end") == LDLfDiamond( RegExpUnion([ RegExpPropositional(a), RegExpSequence([RegExpPropositional(b), RegExpPropositional(a)]), ]), LDLfEnd(), ) assert parser("!(<(!(A<->D))+((B;C)*)+(?!last)>[(true)*]end)") == LDLfNot( LDLfDiamond( RegExpUnion([ RegExpPropositional(PLNot(PLEquivalence([a, PLAtomic("D")]))), RegExpStar( RegExpSequence([ RegExpPropositional(PLAtomic("B")), RegExpPropositional(PLAtomic("C")), ])), RegExpTest(LDLfNot(LDLfLast())), ]), LDLfBox(RegExpStar(RegExpPropositional(PLTrue())), LDLfEnd()), ))
def _delta(self, i: PLInterpretation, epsilon=False): return PLAnd([f._delta(i, epsilon) for f in self.formulas])
def _delta(self, i: PropositionalInterpretation, epsilon=False): """Apply the delta function.""" return PLAnd([f._delta(i, epsilon) for f in self.formulas])