def test_eu(self): euspec = eu(sptrue(), spfalse()) self.assertEqual(euspec.type, parser.EU) self.assertIsNotNone(euspec.car) self.assertIsNotNone(euspec.cdr) with self.assertRaises(ValueError): euspec = eu(None, None)
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_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 countex(fsm, state, spec, context): """ Return a TLACE node explaining why state of fsm violates 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 violates spec. """ if spec.type == parser.CONTEXT: return countex(fsm, state, spec.cdr, spec.car) elif spec.type == parser.FALSEEXP: newspec = sptrue() elif spec.type == parser.NOT: newspec = spec.car elif spec.type == parser.OR: newspec = (~spec.car) & (~spec.cdr) elif spec.type == parser.AND: newspec = (~spec.car) | (~spec.cdr) elif spec.type == parser.IMPLIES: newspec = spec.car & (~spec.cdr) elif spec.type == parser.IFF: newspec = (spec.car & (~spec.cdr)) | ((~spec.car) & spec.cdr) elif spec.type == parser.EX: newspec = ax(~spec.car) elif spec.type == parser.EF: newspec = ag(~spec.car) elif spec.type == parser.EG: newspec = af(~spec.car) elif spec.type == parser.EU: newspec = aw(~spec.cdr, (~spec.car) & (~spec.cdr)) elif spec.type == parser.EW: newspec = au(~spec.cdr, (~spec.car) & (~spec.cdr)) elif spec.type == parser.AX: newspec = ex(~spec.car) elif spec.type == parser.AF: newspec = eg(~spec.car) elif spec.type == parser.AG: newspec = ef(~spec.car) elif spec.type == parser.AU: newspec = ew(~spec.cdr, (~spec.car) & (~spec.cdr)) elif spec.type == parser.AW: newspec = eu(~spec.cdr, (~spec.car) & (~spec.cdr)) else: if spec.type == parser.NOT: newspec = spec.car else: newspec = ~spec return Tlacenode(state, (newspec,), None, None) return witness(fsm, state, newspec, context)
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 countex(fsm, state, spec, context): """ Return a TLACE node explaining why state of fsm violates 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 violates spec. """ if spec.type == parser.CONTEXT: return countex(fsm, state, spec.cdr, spec.car) elif spec.type == parser.FALSEEXP: newspec = sptrue() elif spec.type == parser.NOT: newspec = spec.car elif spec.type == parser.OR: newspec = (~spec.car) & (~spec.cdr) elif spec.type == parser.AND: newspec = (~spec.car) | (~spec.cdr) elif spec.type == parser.IMPLIES: newspec = spec.car & (~spec.cdr) elif spec.type == parser.IFF: newspec = (spec.car & (~spec.cdr)) | ((~spec.car) & spec.cdr) elif spec.type == parser.EX: newspec = ax(~spec.car) elif spec.type == parser.EF: newspec = ag(~spec.car) elif spec.type == parser.EG: newspec = af(~spec.car) elif spec.type == parser.EU: newspec = aw(~spec.cdr, (~spec.car) & (~spec.cdr)) elif spec.type == parser.EW: newspec = au(~spec.cdr, (~spec.car) & (~spec.cdr)) elif spec.type == parser.AX: newspec = ex(~spec.car) elif spec.type == parser.AF: newspec = eg(~spec.car) elif spec.type == parser.AG: newspec = ef(~spec.car) elif spec.type == parser.AU: newspec = ew(~spec.cdr, (~spec.car) & (~spec.cdr)) elif spec.type == parser.AW: newspec = eu(~spec.cdr, (~spec.car) & (~spec.cdr)) else: if spec.type == parser.NOT: newspec = spec.car else: newspec = ~spec return Tlacenode(state, (newspec, ), None, None) return witness(fsm, state, newspec, context)
def test_eu(self): euspec = eu(sptrue(), spfalse()) self.assertEqual(euspec.type, parser.EU) self.assertIsNotNone(euspec.car) self.assertIsNotNone(euspec.cdr)
def ast_to_spec(ast): """ Return a PyNuSMV specification representing `ast`. :param ast: an AST-based CTL formula :return: a PyNuSMV specification representing `ast` :rtype: :class:`pynusmv.prop.Spec` :raise: a :exc:`NotImplementedError` if an operator is not implemented """ if isinstance(ast, TrueExp): return true() elif isinstance(ast, FalseExp): return false() elif isinstance(ast, Atom): return atom(ast.value) elif isinstance(ast, Not): return not_(ast_to_spec(ast.child)) elif isinstance(ast, And): return and_(ast_to_spec(ast.left), ast_to_spec(ast.right)) elif isinstance(ast, Or): return or_(ast_to_spec(ast.left), ast_to_spec(ast.right)) elif isinstance(ast, Imply): return imply(ast_to_spec(ast.left), ast_to_spec(ast.right)) elif isinstance(ast, Iff): return iff(ast_to_spec(ast.left), ast_to_spec(ast.right)) elif isinstance(ast, AX): return ax(ast_to_spec(ast.child)) elif isinstance(ast, AF): return af(ast_to_spec(ast.child)) elif isinstance(ast, AG): return ag(ast_to_spec(ast.child)) elif isinstance(ast, AU): return au(ast_to_spec(ast.left), ast_to_spec(ast.right)) elif isinstance(ast, AW): return aw(ast_to_spec(ast.left), ast_to_spec(ast.right)) elif isinstance(ast, EX): return ex(ast_to_spec(ast.child)) elif isinstance(ast, EF): return ef(ast_to_spec(ast.child)) elif isinstance(ast, EG): return eg(ast_to_spec(ast.child)) elif isinstance(ast, EU): return eu(ast_to_spec(ast.left), ast_to_spec(ast.right)) elif isinstance(ast, EW): return ew(ast_to_spec(ast.left), ast_to_spec(ast.right)) # A(phi oU psi) <=> A(phi U (phi & psi)) elif isinstance(ast, AoU): return au(ast_to_spec(ast.left), and_(ast_to_spec(ast.left), ast_to_spec(ast.right))) # A(phi oW psi) <=> A(phi W (phi & psi)) elif isinstance(ast, AoW): return aw(ast_to_spec(ast.left), and_(ast_to_spec(ast.left), ast_to_spec(ast.right))) # A(phi dU psi) <=> A(phi U (!phi & psi)) elif isinstance(ast, AdU): return au(ast_to_spec(ast.left), and_(not_(ast_to_spec(ast.left)), ast_to_spec(ast.right))) # A(phi dW psi) <=> A(phi W (!phi & psi)) elif isinstance(ast, AdW): return aw(ast_to_spec(ast.left), and_(not_(ast_to_spec(ast.left)), ast_to_spec(ast.right))) # E(phi oU psi) <=> E(phi U (phi & psi)) elif isinstance(ast, EoU): return eu(ast_to_spec(ast.left), and_(ast_to_spec(ast.left), ast_to_spec(ast.right))) # E(phi oW psi) <=> E(phi W (phi & psi)) elif isinstance(ast, EoW): return ew(ast_to_spec(ast.left), and_(ast_to_spec(ast.left), ast_to_spec(ast.right))) # E(phi dU psi) <=> E(phi U (!phi & psi)) elif isinstance(ast, EdU): return eu(ast_to_spec(ast.left), and_(not_(ast_to_spec(ast.left)), ast_to_spec(ast.right))) # E(phi dW psi) <=> E(phi W (!phi & psi)) elif isinstance(ast, EdW): return ew(ast_to_spec(ast.left), and_(not_(ast_to_spec(ast.left)), ast_to_spec(ast.right))) else: raise NotImplementedError(NOT_IMPLEMENTED_MSG.format(op=type(ast)))