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 test_delta(): parser = LDLfParser() sa, sb, sc = "A", "B", "C" a, b, c = PLAtomic(sa), PLAtomic(sb), PLAtomic(sc) i_ = PLFalseInterpretation() i_a = PLInterpretation({sa}) i_b = PLInterpretation({sb}) i_ab = PLInterpretation({sa, sb}) true = PLTrue() false = PLFalse() tt = LDLfLogicalTrue() ff = LDLfLogicalFalse() assert parser("<A>tt").delta(i_) == false assert parser("<A>tt").delta(i_a) == PLAtomic(tt) assert parser("<A>tt").delta(i_b) == false assert parser("<A>tt").delta(i_ab) == PLAtomic(tt) assert parser("[B]ff").delta(i_) == true assert parser("[B]ff").delta(i_a) == true assert parser("[B]ff").delta(i_b) == PLAtomic(ff) assert parser("[B]ff").delta(i_ab) == PLAtomic(ff) f = parser("!(<!(A<->B)+(B;A)*+(!last)?>[(true)*]end)") assert f.delta(i_) == f.to_nnf().delta(i_) assert f.delta(i_ab) == f.to_nnf().delta(i_ab) assert f.delta(i_, epsilon=True) == f.to_nnf().delta(i_, epsilon=True) assert f.delta(i_ab, epsilon=True) == f.to_nnf().delta(i_ab, epsilon=True) # with epsilon=True, the result is either PLTrue or PLFalse assert f.delta(i_, epsilon=True) in [PLTrue(), PLFalse()]
def test_delta(): parser = LDLfParser() i_ = {} i_a = {"A": True} i_b = {"B": True} i_ab = {"A": True, "B": True} true = PLTrue() false = PLFalse() tt = PLAtomic(LDLfLogicalTrue()) ff = PLAtomic(LDLfLogicalFalse()) assert parser("<A>tt").delta(i_) == false assert parser("<A>tt").delta(i_a) == tt assert parser("<A>tt").delta(i_b) == false assert parser("<A>tt").delta(i_ab) == tt assert parser("[B]ff").delta(i_) == true assert parser("[B]ff").delta(i_a) == true assert parser("[B]ff").delta(i_b) == ff assert parser("[B]ff").delta(i_ab) == ff f = parser("!(<?(!last)>end)") assert f.delta(i_) == f.to_nnf().delta(i_) assert f.delta(i_ab) == f.to_nnf().delta(i_ab) assert f.delta(i_, epsilon=True) == f.to_nnf().delta(i_, epsilon=True) assert f.delta(i_ab, epsilon=True) == f.to_nnf().delta(i_ab, epsilon=True) # with epsilon=True, the result is either PLTrue or PLFalse assert f.delta(i_, epsilon=True) in [PLTrue(), PLFalse()]
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_diamond(self, f: LDLfFormula, i: PLInterpretation, epsilon=False): if epsilon: return PLFalse() if self.pl_formula.truth(i): return PLAtomic(_expand(f)) else: return PLFalse()
def _delta(self, i: PLInterpretation, epsilon=False): if isinstance(self.f, LTLfAtomic) or isinstance(self.f, LTLfEnd): if epsilon: return PLFalse() else: return PLTrue() if self.f._delta( i, epsilon) == PLFalse() else PLFalse() else: # the formula must be in NNF form!!! raise Exception
def _delta(self, i: PropositionalInterpretation, epsilon=False): """Apply the delta function.""" if epsilon: return PLFalse() result = self.f._delta(i, epsilon=epsilon) if result == PLTrue(): return PLFalse() else: return PLTrue()
def delta_diamond(self, f: LDLfFormula, i: PropositionalInterpretation, epsilon=False): """Apply delta function for regular expressions in the diamond operator.""" if epsilon: return PLFalse() if self.pl_formula.truth(i): return PLAtomic(_expand(f)) else: return PLFalse()
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(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 delta(self, i: PLInterpretation, epsilon=False) -> PLFormula: f = self.to_nnf() d = f._delta(i, epsilon) if epsilon: # By definition, if epsilon=True, then the result must be either PLTrue or PLFalse # Now, the output is a Propositional Formula with only PLTrue or PLFalse as atomics # Hence, we just evaluate the formula with a dummy PLInterpretation d = PLTrue() if d.truth(None) else PLFalse() return d
def p_formula_atom(self, p): """formula : ATOM | TRUE | FALSE""" if p[1] == Symbols.TRUE.value: p[0] = PLTrue() elif p[1] == Symbols.FALSE.value: p[0] = PLFalse() else: p[0] = PLAtomic(p[1])
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: PropositionalInterpretation, epsilon=False) -> PLFormula: """Apply the delta function.""" f = self.to_nnf() d = f._delta(i, epsilon=epsilon) if epsilon: # By definition, if epsilon=True, then the result must be either True or False. # The output is a Propositional Formula with only True or False as atomics. # Hence, we just evaluate the formula with the empty propositional interpretation. d = PLTrue() if d.truth({}) else PLFalse() return d
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 _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 setup_class(cls): cls.parser = LTLfParser() cls.i_, cls.i_a, cls.i_b, cls.i_ab = ( {}, { "a": True }, { "b": True }, { "a": True, "b": True }, ) cls.true = PLTrue() cls.false = PLFalse()
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): return PLFalse()
def _delta(self, i: PLInterpretation, epsilon: bool = False): if epsilon: return PLFalse() else: return PLTrue()
def prop_false(self, args): assert len(args) == 1 return PLFalse()
def _delta(self, i: PropositionalInterpretation, epsilon: bool = False): """Apply the delta function.""" if epsilon: return PLFalse() return PLTrue() if PLAtomic(self.s).truth(i) else PLFalse()
def setup_class(cls): cls.parser = LTLfParser() cls.i_, cls.i_a, cls.i_b, cls.i_ab = \ PLFalseInterpretation(), PLInterpretation({"A"}), PLInterpretation({"B"}), PLInterpretation({"A", "B"}) cls.true = PLTrue() cls.false = PLFalse()
def _delta(self, i: PropositionalInterpretation, epsilon: bool = False): """Apply the delta function.""" return PLFalse()
def _delta(self, i: PLInterpretation, epsilon: bool = False): if epsilon: return PLFalse() return PLTrue() if PLAtomic(self.s).truth(i) else PLFalse()
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 _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 to_nnf(self) -> LDLfFormula: """Transform to NNF.""" return LDLfDiamond(RegExpPropositional(PLFalse()), LDLfLogicalTrue())