def test_explain(self): fsm = self.init_model() initState = fsm.pick_one_state(fsm.init) adminNone = eval_ctl_spec(fsm, atom("admin = none")) adminAlice = eval_ctl_spec(fsm, atom("admin = alice")) egAlice = eval_ctl_spec(fsm, eg(atom("admin = alice"))) euNoneUAlice = eval_ctl_spec(fsm, eu(atom("admin = none"), atom("admin = alice"))) spec = Spec(parse_ctl_spec("EX admin = none")) path = explain(fsm, initState, spec) self.assertEqual(initState, path[0]) self.assertTrue(path[2] <= adminNone) state = fsm.pick_one_state(egAlice) spec = Spec(parse_ctl_spec("EG admin = alice")) path = explain(fsm, state, spec) self.assertEqual(state, path[0]) self.assertIn(path[-1], path[:-1]) for i in range(0,len(path), 2): self.assertTrue(path[i] <= adminAlice) self.assertTrue(path[-1] <= adminAlice) self.assertTrue(initState <= euNoneUAlice) spec = Spec(parse_ctl_spec("E[admin = none U admin = alice]")) path = explain(fsm, initState, spec) self.assertEqual(initState, path[0]) for i in range(2,len(path)-2,2): self.assertTrue(path[i] <= adminNone) self.assertTrue(path[-1] <= adminAlice)
def test_explain_ex(self): fsm = self.init_model() manager = fsm.bddEnc.DDmanager init = fsm.init initState = fsm.pick_one_state(init) adminNone = eval_ctl_spec(fsm, atom("admin = none")) exAdminNone = eval_ctl_spec(fsm, ex(atom("admin = none"))) self.assertTrue(initState <= exAdminNone) path = explainEX(fsm, initState, adminNone) self.assertEqual(initState, path[0]) self.assertTrue(path[2] <= adminNone)
def witness(fsm, state, spec, context): """ Return a TLACE node explaining why state of fsm satisfies spec. fsm -- a pynusmv.fsm.BddFsm representing the system. state -- a pynusmv.dd.BDD representing a state of fsm. spec -- a pynusmv.spec.spec.Spec node representing the specification. context -- a pynusmv.spec.spec.Spec representing the context of spec in fsm. Return a tlacenode.Tlacenode explaining why state of fsm satisfies spec. """ if spec.type == parser.CONTEXT: return witness(fsm, state, spec.cdr, spec.car) elif spec.type == parser.TRUEEXP: return Tlacenode(state, None, None, None) elif spec.type == parser.NOT: return countex(fsm, state, spec.car, context) elif spec.type == parser.OR: if state.entailed(eval_ctl_spec(fsm, spec.car, context)): return witness(fsm, state, spec.car, context) else: return witness(fsm, state, spec.cdr, context) elif spec.type == parser.AND: n1 = witness(fsm, state, spec.car, context) n2 = witness(fsm, state, spec.cdr, context) return Tlacenode(state, n1.atomics + n2.atomics, n1.branches + n2.branches, n1.universals + n2.universals) elif spec.type == parser.IMPLIES: newspec = (~spec.car) | spec.cdr return witness(fsm, state, newspec, context) elif spec.type == parser.IFF: newspec = (spec.car & spec.cdr) | ((~spec.car) & (~spec.cdr)) return witness(fsm, state, newspec, context) elif (spec.type == parser.EX or spec.type == parser.EF or spec.type == parser.EG or spec.type == parser.EU or spec.type == parser.EW): return Tlacenode(state, None, (witness_branch(fsm, state, spec, context, spec), ), None) elif (spec.type == parser.AX or spec.type == parser.AF or spec.type == parser.AG or spec.type == parser.AU or spec.type == parser.AW): return Tlacenode(state, None, None, (spec, )) else: # deal with unmanaged formulas by returning a single # TLACE node. This includes the case of atomic propositions. # All unrecognized operators are considered as atomics return Tlacenode(state, (spec, ), None, None)
def test_explain_eg_first_state(self): fsm = self.inputs_model() manager = fsm.bddEnc.DDmanager init = fsm.init p = eval_ctl_spec(fsm, atom("p")) egp = eval_ctl_spec(fsm, eg(atom("p"))) self.assertTrue(egp <= p) state = fsm.pick_one_state(egp) path, (inloop, loop) = explainEG(fsm, state, p) self.assertEqual(state, path[0]) self.assertIn(loop, path) self.assertEqual(len(path), 1) for i in range(0, len(path), 2): self.assertTrue(path[i] <= p) self.assertTrue(path[-1] <= p)
def test_ex(self): glob.load("tests/pynusmv/models/admin.smv") glob.compute_model() fsm = glob.prop_database().master.bddFsm alice = mc.eval_simple_expression(fsm, "admin = alice") spec = prop.Spec(parser.parse_ctl_spec("EX admin = alice")) exalice = mc.eval_ctl_spec(fsm, spec) self.assertEqual(mc.ex(fsm, alice), exalice)
def test_explain_eu(self): fsm = self.init_model() manager = fsm.bddEnc.DDmanager init = fsm.init initState = fsm.pick_one_state(init) adminNone = eval_ctl_spec(fsm, atom("admin = none")) adminAlice = eval_ctl_spec(fsm, atom("admin = alice")) euNoneUAlice = eval_ctl_spec(fsm, eu(atom("admin = none"), atom("admin = alice"))) self.assertTrue(initState <= euNoneUAlice) path = explainEU(fsm, initState, adminNone, adminAlice) self.assertEqual(initState, path[0]) for i in range(2,len(path)-2,2): self.assertTrue(path[i] <= adminNone) self.assertTrue(path[-1] <= adminAlice)
def _boundary(fsm, gamma, phi, states): """ Third auxiliary set, as defined by Chan at CAV 2000. :param fsm: the concerned FSM :param gamma: an AST-based CTL query :param phi: an AST-based CTL formula :param states: a set of states :return: the boundary of the first auxiliary set (that is, the first states on each path starting from `states` that are not in the first auxiliary set and at which `gamma[False]` does not hold) :rtype: :class:`pynusmv.dd.BDD` """ psi = replace_placeholder(gamma, FalseExp()) new_phi = And(phi, Not(psi)) return ((states | fsm.post(_reachable(fsm, new_phi, states))) - (eval_ctl_spec(fsm, ast_to_spec(phi)) | eval_ctl_spec(fsm, ast_to_spec(psi))))
def test_explain_eg(self): fsm = self.init_model() manager = fsm.bddEnc.DDmanager init = fsm.init adminAlice = eval_ctl_spec(fsm, atom("admin = alice")) egAlice = eval_ctl_spec(fsm, eg(atom("admin = alice"))) self.assertTrue(egAlice <= adminAlice) state = fsm.pick_one_state(egAlice) self.assertTrue(BDD.false(manager) < state <= adminAlice) self.assertTrue(state <= egAlice) path, (inloop, loop) = explainEG(fsm, state, adminAlice) self.assertEqual(state, path[0]) self.assertIn(loop, path) for i in range(0,len(path), 2): self.assertTrue(path[i] <= adminAlice) self.assertTrue(path[-1] <= adminAlice)
def test_eval_atom(self): fsm = self.init_model() manager = fsm.bddEnc.DDmanager init = fsm.init initState = fsm.pick_one_state(init) adminNone = eval_ctl_spec(fsm, atom("admin = none")) self.assertTrue((initState & adminNone).isnot_false()) self.assertTrue(init <= adminNone)
def test_au(self): glob.load("tests/pynusmv/models/admin.smv") glob.compute_model() fsm = glob.prop_database().master.bddFsm none = mc.eval_simple_expression(fsm, "admin = none") alice = mc.eval_simple_expression(fsm, "admin = alice") spec = prop.Spec( parser.parse_ctl_spec("A[admin = none U admin = alice]")) aunonealice = mc.eval_ctl_spec(fsm, spec) self.assertEqual(mc.au(fsm, none, alice), aunonealice)
def _f_sol(fsm, gamma, phi, states, cycle): """ Find the states of `fsm` with highest indices among the states at which `gamma` has a solution, as defined by Samer and Veith at TIME'05. :param fsm: the concerned FSM :param gamma: an AST-based CTL query :param phi: an AST-based CTL formula :param states: a set of states :param cycle: True if there is a cycle, False otherwise :return: the states of `fsm` with highest indices among the states at which `gamma` has a solution :rtype: :class:`pynusmv.dd.BDD` """ if cycle: c = _cycle(fsm, gamma, phi, states) else: c = BDD.false(fsm.bddEnc.DDmanager) chi = replace_placeholder(gamma, TrueExp()) psi = replace_placeholder(gamma, FalseExp()) new_phi = And(phi, Not(psi)) u_1 = fixpoint(lambda z: ((c - eval_ctl_spec(fsm, ast_to_spec(chi))) & fsm.post(z)), BDD.true(fsm.bddEnc.DDmanager)) # Greatest fixed point. u_2 = u_1 | (_boundary(fsm, gamma, phi, states) - eval_ctl_spec(fsm, ast_to_spec(chi))) u_3 = fixpoint(lambda z: (((u_2 | fsm.pre(z)) & _reachable(fsm, new_phi, states)) - eval_ctl_spec(fsm, ast_to_spec(chi))), BDD.false(fsm.bddEnc.DDmanager)) # Least fixed point. return (((fsm.pre(u_3) & _reachable(fsm, new_phi, states)) | _boundary(fsm, gamma, phi, states) | c) & eval_ctl_spec(fsm, ast_to_spec(chi)))
def _reachable(fsm, phi, states): """ First auxiliary set, as defined by Chan at CAV 2000. :param fsm: the concerned FSM :param phi: an AST-based CTL formula :param states: a set of states :return: the states of `fsm` that are reachable from states in `states` by going only through states at which `phi` holds :rtype: :class:`pynusmv.dd.BDD` """ return fixpoint(lambda z: ((states | fsm.post(z)) & eval_ctl_spec(fsm, ast_to_spec(phi))), BDD.false(fsm.bddEnc.DDmanager)) # Least fixed point.
def test_eg(self): glob.load("tests/pynusmv/models/admin.smv") glob.compute_model() fsm = glob.prop_database().master.bddFsm alice = mc.eval_simple_expression(fsm, "admin = alice") spec = prop.Spec(parser.parse_ctl_spec("EG admin = alice")) egalice = mc.eval_ctl_spec(fsm, spec) self.assertEqual(mc.eg(fsm, alice), egalice) self.assertEqual( egalice, fixpoint(lambda Z: alice & fsm.pre(Z), BDD.true()) & fsm.reachable_states)
def solve_ctlqx(fsm, query): """ Compute the unique set of solution states of `fsm` to `query` at the initial states of `fsm`, as defined by Samer and Veith at TIME'05. :param fsm: the concerned FSM :param query: an AST-based CTL query **that belongs to fragment CTLQx** :return: the unique set of solution states of `fsm` to `query` at the initial states of `fsm` if there is a solution, None otherwise :rtype: :class:`pynusmv.dd.BDD` .. note:: The characteristic function of the output is an exact solution to `query` in `fsm`. .. note:: See :func:`pytlq.checker.check_ctlqx` for the verification that `query` belongs to fragment CTLQx. """ if (eval_ctl_spec(fsm, ast_to_spec(replace_placeholder(query, TrueExp()))) .is_true()): return _e_sol(fsm, query, fsm.init) else: return None
def check(fsm, spec): """ Check whether fsm satisfies spec or not. Return (True, None) if spec is satisfied, and (False, cntex) otherwise, where cntex is TLACE node explaining the violation. """ initbdd = fsm.init specbdd = eval_ctl_spec(fsm, spec) # Get violating states violating = initbdd & ~specbdd # If some initial states are not in specbdd, the spec if violated if violating.isnot_false(): # Compute a counter-example state = fsm.pick_one_state(violating) return (False, explain(fsm, state, spec)) # Otherwise, it is satisfied else: return (True, None)
def _e_sol(fsm, gamma, states): """ Compute the unique set of solution states of `fsm` to `gamma` at `states`, as defined by Samer and Veith at TIME'05. :param fsm: the concerned FSM :param gamma: an AST-based CTL query that belongs to fragment CTLQx :param states: a set of states :return: the unique set of solution states of `fsm` to `gamma` at `states` :rtype: :class:`pynusmv.dd.BDD` .. note:: Samer and Veith's algorithm has been extended to support the negation in front of the placeholder. """ if isinstance(gamma, Placeholder): return states # Extension to support the negation in front of the placeholder. elif isinstance(gamma, Not) and isinstance(gamma.child, Placeholder): return fsm.reachable_states - states elif isinstance(gamma, And): if count_placeholders(gamma.left): return _e_sol(fsm, gamma.left, states) elif count_placeholders(gamma.right): return _e_sol(fsm, gamma.right, states) elif isinstance(gamma, Or): if count_placeholders(gamma.left): return _e_sol(fsm, gamma.left, states - eval_ctl_spec(fsm, ast_to_spec(gamma.right))) elif count_placeholders(gamma.right): return _e_sol(fsm, gamma.right, states - eval_ctl_spec(fsm, ast_to_spec(gamma.left))) elif isinstance(gamma, AX): return _e_sol(fsm, gamma.child, fsm.post(states)) elif isinstance(gamma, AF): return _e_sol(fsm, AU(TrueExp(), gamma.child), states) elif isinstance(gamma, AG): return _e_sol(fsm, AoW(gamma.child, FalseExp()), states) elif isinstance(gamma, AU): if count_placeholders(gamma.left): return _e_sol(fsm, AoW(Or(gamma.right, gamma.left), gamma.right), states) elif count_placeholders(gamma.right): return _e_sol(fsm, gamma.right, _f_sol(fsm, gamma.right, gamma.left, states, True)) elif isinstance(gamma, AoU): if count_placeholders(gamma.left): return _e_sol(fsm, AoW(gamma.left, gamma.right), states) elif count_placeholders(gamma.right): return _e_sol(fsm, AU(gamma.left, And(gamma.left, gamma.right)), states) elif isinstance(gamma, AdU): return _e_sol(fsm, AdW(gamma.left, gamma.right), states) elif isinstance(gamma, AW): if count_placeholders(gamma.left): return _e_sol(fsm, AoW(Or(gamma.right, gamma.left), gamma.right), states) elif count_placeholders(gamma.right): return _e_sol(fsm, gamma.right, _f_sol(fsm, gamma.right, gamma.left, states, False)) elif isinstance(gamma, AoW): if count_placeholders(gamma.left): return _e_sol(fsm, gamma.left, states | fsm.post(_reachable(fsm, Not(gamma.right), states))) elif count_placeholders(gamma.right): return _e_sol(fsm, AW(gamma.left, And(gamma.left, gamma.right)), states) elif isinstance(gamma, AdW): return _e_sol(fsm, gamma.right, ((states | fsm.post(_reachable(fsm, gamma.left, states))) - eval_ctl_spec(fsm, ast_to_spec(gamma.left))))
def witness(fsm, state, spec, context): """ Return a TLACE node explaining why state of fsm satisfies spec. fsm -- a pynusmv.fsm.BddFsm representing the system. state -- a pynusmv.dd.BDD representing a state of fsm. spec -- a pynusmv.spec.spec.Spec node representing the specification. context -- a pynusmv.spec.spec.Spec representing the context of spec in fsm. Return a tlacenode.Tlacenode explaining why state of fsm satisfies spec. """ if spec.type == parser.CONTEXT: return witness(fsm, state, spec.cdr, spec.car) elif spec.type == parser.TRUEEXP: return Tlacenode(state, None, None, None) elif spec.type == parser.NOT: return countex(fsm, state, spec.car, context) elif spec.type == parser.OR: if state.entailed(eval_ctl_spec(fsm, spec.car, context)): return witness(fsm, state, spec.car, context) else: return witness(fsm, state, spec.cdr, context) elif spec.type == parser.AND: n1 = witness(fsm, state, spec.car, context) n2 = witness(fsm, state, spec.cdr, context) return Tlacenode(state, n1.atomics + n2.atomics, n1.branches + n2.branches, n1.universals + n2.universals) elif spec.type == parser.IMPLIES: newspec = (~spec.car) | spec.cdr return witness(fsm, state, newspec, context) elif spec.type == parser.IFF: newspec = (spec.car & spec.cdr) | ((~spec.car) & (~spec.cdr)) return witness(fsm, state, newspec, context) elif (spec.type == parser.EX or spec.type == parser.EF or spec.type == parser.EG or spec.type == parser.EU or spec.type == parser.EW): return Tlacenode(state, None, (witness_branch(fsm, state, spec, context, spec),), None) elif (spec.type == parser.AX or spec.type == parser.AF or spec.type == parser.AG or spec.type == parser.AU or spec.type == parser.AW): return Tlacenode(state, None, None, (spec,)) else: # deal with unmanaged formulas by returning a single # TLACE node. This includes the case of atomic propositions. # All unrecognized operators are considered as atomics return Tlacenode(state, (spec,), None, None)
def witness_branch(fsm, state, spec, context, originalspec): """ Return a TLACE branch explaining why state of fsm satisfies spec. fsm -- a pynusmv.fsm.BddFsm representing the system. state -- a pynusmv.dd.BDD representing a state of fsm. spec -- a pynusmv.spec.spec.Spec node representing the specification. context -- a pynusmv.spec.spec.Spec representing the context of spec in fsm. originalspec -- a pynusmv.spec.spec.Spec representing the original spec; used to annotate the produced branch, despite updated specs. Return a tlacebranch.Tlacebranch explaining why state of fsm satisfies spec. Throw a NonExistentialSpecError if spec is not existential. """ if spec.type == parser.EX: f = eval_ctl_spec(fsm, spec.car, context) path = explainEX(fsm, state, f) branch = (Tlacenode(path[0]), path[1], witness(fsm, path[2], spec.car, context)) return Tlacebranch(originalspec, branch) elif spec.type == parser.EF: newspec = eu(sptrue(), spec.car) return witness_branch(fsm, state, newspec, context, originalspec) elif spec.type == parser.EG: f = eval_ctl_spec(fsm, spec.car, context) (path, (inloop, loopstate)) = explainEG(fsm, state, f) branch = [] # intermediate states for s, i in zip(path[::2], path[1::2]): wit = witness(fsm, s, spec.car, context) branch.append(wit) branch.append(i) # manage the loop if s == loopstate: loop = wit # last state branch.append(witness(fsm, path[-1], spec.car, context)) return Tlacebranch(originalspec, tuple(branch), (inloop, loop)) elif spec.type == parser.EU: f = eval_ctl_spec(fsm, spec.car, context) g = eval_ctl_spec(fsm, spec.cdr, context) path = explainEU(fsm, state, f, g) branch = [] # intermediate states for s, i in zip(path[::2], path[1::2]): branch.append(witness(fsm, s, spec.car, context)) branch.append(i) # last state branch.append(witness(fsm, path[-1], spec.cdr, context)) return Tlacebranch(originalspec, tuple(branch)) elif spec.type == parser.EW: euspec = eu(spec.car, spec.cdr) egspec = eg(spec.car) if state.entailed(eval_ctl_spec(fsm, euspec, context)): return witness_branch(fsm, state, euspec, context, originalspec) else: return witness_branch(fsm, state, egspec, context, originalspec) else: # Default case, throw an exception because spec is not existential raise NonExistentialSpecError()
def test_wrong_type(self): fsm, _, _ = self.init_model() spec = atom("admin = starting") specbdd = eval_ctl_spec(fsm, spec) self.assertTrue(specbdd.is_false())
def eval_ctl(fsm, spec, context = None): """ Evaluate spec in fsm. Return the BDD representing all states of fsm satisfying spec. """ if spec.type == parser.CONTEXT: return eval_ctl(fsm, spec.cdr, spec.car) elif spec.type == parser.FALSEEXP: return BDD.false(fsm.bddEnc.DDmanager) elif spec.type == parser.TRUEEXP: return BDD.true(fsm.bddEnc.DDmanager) elif spec.type == parser.NOT: return ~eval_ctl(fsm, spec.car, context) elif spec.type == parser.OR: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return left | right elif spec.type == parser.AND: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return left & right elif spec.type == parser.IMPLIES: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return ~left | right elif spec.type == parser.IFF: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return (left & right) | (~left & ~right) elif spec.type == parser.EX: return ex(fsm, eval_ctl(fsm, spec.car, context)) elif spec.type == parser.EF: left = BDD.true(fsm.bddEnc.DDmanager) right = eval_ctl(fsm, spec.car, context) return eu(fsm, left, right) elif spec.type == parser.EG: return eg(fsm, eval_ctl(fsm, spec.car, context)) elif spec.type == parser.EU: return eu(fsm, eval_ctl(fsm, spec.car, context), eval_ctl(fsm, spec.cdr, context)) elif spec.type == parser.EW: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return eg(fsm, left) | eu(fsm, left, right) elif spec.type == parser.AX: left = eval_ctl(fsm, spec.car, context) return ~ex(fsm, ~left) elif spec.type == parser.AF: left = eval_ctl(fsm, spec.car, context) return ~eg(fsm, ~left) elif spec.type == parser.AG: left = eval_ctl(fsm, spec.car, context) true = BDD.true(fsm.bddEnc.DDmanager) return ~eu(fsm, true, ~left) elif spec.type == parser.AU: # A[p U q] = ¬E[¬q W (¬p & ¬q)] = ¬(E[¬q U (¬p & ¬q)] | EG ¬q) left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return ~(eu(fsm, ~right, ~left & ~right) | eg(fsm, ~right)) elif spec.type == parser.AW: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return ~eu(fsm, ~right, ~left & ~right) else: return eval_ctl_spec(fsm, spec)
def test_success_or(self): fsm, _, _ = self.init_model() spec = atom("admin = alice | admin = bob") specbdd = eval_ctl_spec(fsm, spec) self.assertTrue((specbdd & fsm.init).is_false())
def eval_ctl(fsm, spec, context=None): """ Evaluate spec in fsm. Return the BDD representing all states of fsm satisfying spec. """ if spec.type == parser.CONTEXT: return eval_ctl(fsm, spec.cdr, spec.car) elif spec.type == parser.FALSEEXP: return BDD.false(fsm.bddEnc.DDmanager) elif spec.type == parser.TRUEEXP: return BDD.true(fsm.bddEnc.DDmanager) elif spec.type == parser.NOT: return ~eval_ctl(fsm, spec.car, context) elif spec.type == parser.OR: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return left | right elif spec.type == parser.AND: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return left & right elif spec.type == parser.IMPLIES: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return ~left | right elif spec.type == parser.IFF: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return (left & right) | (~left & ~right) elif spec.type == parser.EX: return ex(fsm, eval_ctl(fsm, spec.car, context)) elif spec.type == parser.EF: left = BDD.true(fsm.bddEnc.DDmanager) right = eval_ctl(fsm, spec.car, context) return eu(fsm, left, right) elif spec.type == parser.EG: return eg(fsm, eval_ctl(fsm, spec.car, context)) elif spec.type == parser.EU: return eu(fsm, eval_ctl(fsm, spec.car, context), eval_ctl(fsm, spec.cdr, context)) elif spec.type == parser.EW: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return eg(fsm, left) | eu(fsm, left, right) elif spec.type == parser.AX: left = eval_ctl(fsm, spec.car, context) return ~ex(fsm, ~left) elif spec.type == parser.AF: left = eval_ctl(fsm, spec.car, context) return ~eg(fsm, ~left) elif spec.type == parser.AG: left = eval_ctl(fsm, spec.car, context) true = BDD.true(fsm.bddEnc.DDmanager) return ~eu(fsm, true, ~left) elif spec.type == parser.AU: # A[p U q] = ¬E[¬q W (¬p & ¬q)] = ¬(E[¬q U (¬p & ¬q)] | EG ¬q) left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return ~(eu(fsm, ~right, ~left & ~right) | eg(fsm, ~right)) elif spec.type == parser.AW: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) return ~eu(fsm, ~right, ~left & ~right) else: return eval_ctl_spec(fsm, spec)
def eval_ctl(fsm, spec, context=None): """ Evaluate spec in fsm. Return the BDD representing all reachable states of fsm satisfying spec. """ if spec.type == parser.CONTEXT: sat = eval_ctl(fsm, spec.cdr, spec.car) elif spec.type == parser.FALSEEXP: sat = BDD.false(fsm.bddEnc.DDmanager) elif spec.type == parser.TRUEEXP: sat = BDD.true(fsm.bddEnc.DDmanager) elif spec.type == parser.NOT: sat = ~eval_ctl(fsm, spec.car, context) elif spec.type == parser.OR: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) sat = left | right elif spec.type == parser.AND: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) sat = left & right elif spec.type == parser.IMPLIES: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) sat = ~left | right elif spec.type == parser.IFF: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) sat = (left & right) | (~left & ~right) elif spec.type == parser.EX: sat = ex(fsm, eval_ctl(fsm, spec.car, context)) elif spec.type == parser.EF: left = BDD.true(fsm.bddEnc.DDmanager) right = eval_ctl(fsm, spec.car, context) sat = eu(fsm, left, right) elif spec.type == parser.EG: sat = eg(fsm, eval_ctl(fsm, spec.car, context)) elif spec.type == parser.EU: sat = eu(fsm, eval_ctl(fsm, spec.car, context), eval_ctl(fsm, spec.cdr, context)) elif spec.type == parser.EW: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) sat = eg(fsm, left) | eu(fsm, left, right) elif spec.type == parser.AX: left = eval_ctl(fsm, spec.car, context) sat = ~ex(fsm, ~left) elif spec.type == parser.AF: left = eval_ctl(fsm, spec.car, context) sat = ~eg(fsm, ~left) elif spec.type == parser.AG: left = eval_ctl(fsm, spec.car, context) true = BDD.true(fsm.bddEnc.DDmanager) sat = ~eu(fsm, true, ~left) elif spec.type == parser.AU: # A[p U q] = !E[!q W (!p & !q)] = !(E[!q U (!p & !q)] | EG !q) left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) sat = ~(eu(fsm, ~right, ~left & ~right) | eg(fsm, ~right)) elif spec.type == parser.AW: left = eval_ctl(fsm, spec.car, context) right = eval_ctl(fsm, spec.cdr, context) sat = ~eu(fsm, ~right, ~left & ~right) else: sat = eval_ctl_spec(fsm, spec) return sat & fsm.reachable_states
spec = prop.expr print(spec) # print(fsm.count_states(fsm.init)) # for state in fsm.pick_all_states(fsm.init): # print(state.get_str_values()) # for state in fsm.pick_all_states(fsm.post(fsm.init)): # print(state.get_str_values()) # trans = BddTrans.from_string(fsm.bddEnc.symbTable,"next(time) = 0") # for state in fsm.pick_all_states(trans.post(fsm.init)): # print(state.get_str_values()) bdd = pynusmv.mc.eval_ctl_spec(fsm, spec) # & fsm.reachable_states explanation = explain(fsm, fsm.init & ~eval_ctl_spec(fsm, spec), spec) # print(explanation) for state, inputs in zip(explanation[::2], explanation[1::2]): if state == explanation[-1]: print("-- Loop starts here") print(state.get_str_values()) print(inputs.get_str_values()) # satstates = fsm.pick_all_states(bdd) # # print(satstates) # # for state in satstates: # print(state.get_str_values()) # trans = BddTrans.from_string(fsm.bddEnc.symbTable) # for state in fsm.pick_all_states(trans.post(fsm.init)):