def test_eu_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") # lt & lf & g |= E[ g U ~lf & ~lt & ~g ] eus = eu(fsm, g, ~lf & ~lt & ~g) state = fsm.pick_one_state(eus) witness = explain_eu(fsm, state, g, ~lf & ~lt & ~g) self.assertTrue(witness[0] == state) self.assertTrue(witness[0] <= g | ~lf & ~lt & ~g) for (s, i, sp) in zip(witness[:-2:2], witness[1:-2:2], witness[2:-2:2]): self.assertTrue(s <= g) self.assertTrue(sp <= g) self.assertIsNotNone(i) self.assertTrue(i <= fsm.get_inputs_between_states(s, sp)) self.assertIsNotNone(witness[-2]) self.assertTrue(witness[-2] <= fsm.get_inputs_between_states(witness[-3], witness[-1])) self.assertTrue(witness[-1] <= ~lf) self.assertTrue(witness[-1] <= ~lt) self.assertTrue(witness[-1] <= ~g) self.assertTrue(witness[-1] == ~lf & ~lt & ~g)
def test_eg_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") # lt & lf & !g |= EG !g egs = eg(fsm, ~g) self.assertTrue(egs.isnot_false()) state = fsm.pick_one_state(egs) (path, loop) = explain_eg(fsm, state, ~g) self.assertTrue(path[0] == state) self.assertTrue(path[0] <= ~g & egs) for (s, i, sp) in zip(path[::2], path[1::2], path[2::2]): self.assertTrue(s <= ~g) self.assertTrue(sp <= ~g) self.assertIsNotNone(i) self.assertTrue(i <= fsm.get_inputs_between_states(s, sp)) self.assertIsNotNone(loop) self.assertEqual(len(loop), 2) self.assertIsNotNone(loop[0]) self.assertIsNotNone(loop[1]) self.assertTrue(loop[1] in path) self.assertTrue(loop[0] <= fsm.get_inputs_between_states(path[-1], loop[1]))
def test_d_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("'af.local' -> D<'at','af'> 'af.local'") self.assertEqual(len(specs), 1) spec = specs[0] geg = evalCTLK(fsm, spec) self.assertEqual(geg, true) specs = parseCTLK("'af.local' & D<'at'> 'af.local'") self.assertEqual(len(specs), 1) spec = specs[0] geg = evalCTLK(fsm, spec) self.assertEqual(geg, false) specs = parseCTLK("'af.local' -> D<'af'> 'af.local'") self.assertEqual(len(specs), 1) spec = specs[0] geg = evalCTLK(fsm, spec) self.assertEqual(geg, true)
def test_nfair_si_nfair_model(self): fsm = self.nfair_model() s0 = eval_simple_expression(fsm, "state = s0") s1 = eval_simple_expression(fsm, "state = s1") s2 = eval_simple_expression(fsm, "state = s2") a0 = eval_simple_expression(fsm, "a.a = 0") a1 = eval_simple_expression(fsm, "a.a = 1") a2 = eval_simple_expression(fsm, "a.a = 2") b0 = eval_simple_expression(fsm, "b.a = 0") b1 = eval_simple_expression(fsm, "b.a = 1") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") nfgsia = nfair_gamma_si(fsm, {"a"}) nfgsib = nfair_gamma_si(fsm, {"b"}) self.assertTrue(s0 & a1 & fsm.protocol({"a"}) <= nfgsia) self.assertTrue(s1 & a0 & fsm.protocol({"a"}) <= nfgsia) self.assertEqual((s0 & a1 & fsm.protocol({"a"})) | (s1 & a0 & fsm.protocol({"a"})), nfgsia) self.assertEqual(s1 & b0 & fsm.protocol({"b"}), nfgsib)
def test_constraints_post(self): glob.load_from_file("tests/tools/ctlk/constraints.smv") fsm = glob.mas() self.assertIsNotNone(fsm) false = eval_simple_expression(fsm, "FALSE") false = eval_simple_expression(fsm, "TRUE") p = eval_simple_expression(fsm, "p") q = eval_simple_expression(fsm, "q") a = eval_simple_expression(fsm, "a") self.assertEqual(1, fsm.count_states(fsm.init)) self.assertEqual(2, fsm.count_states(fsm.post(fsm.init))) self.assertEqual(1, fsm.count_states(p & q)) self.assertEqual(1, fsm.count_states(p & ~q)) self.assertEqual(1, fsm.count_states(~p & q)) self.assertEqual(1, fsm.count_states(~p & ~q)) self.assertEqual(2, fsm.count_states(fsm.post(p & q))) self.assertEqual(1, fsm.count_states(fsm.post(p & ~q))) self.assertEqual(1, fsm.count_states(fsm.post(~p & q))) self.assertEqual(1, fsm.count_states(fsm.post(p & q, a))) self.assertEqual(0, fsm.count_states(fsm.post(p & ~q, ~a))) self.assertEqual(0, fsm.count_states(fsm.post(~p & q, a))) self.assertEqual(1, fsm.count_states(fsm.post(~p & q, ~a)))
def test_k(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("K<'at'> 'at.local'") self.assertEqual(len(specs), 1) spec = specs[0] katt = evalCTLK(fsm, spec) self.assertEqual(katt, lt) specs = parseCTLK("K<'at'> 'af.local'") self.assertEqual(len(specs), 1) spec = specs[0] katf = evalCTLK(fsm, spec) self.assertEqual(katf, false) specs = parseCTLK("K<'at'> 'global'") self.assertEqual(len(specs), 1) spec = specs[0] katf = evalCTLK(fsm, spec) self.assertEqual(katf, g)
def test_minimize(self): (fsm, enc, manager) = self.init_model() noadmin = eval_simple_expression(fsm, "admin = none") processing = eval_simple_expression(fsm, "state = processing") self.assertIsNotNone(processing.minimize(noadmin)) self.assertTrue(processing.minimize(noadmin).isnot_false())
def test_equivalent_states(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") odd = eval_simple_expression(fsm, "countsay = odd") true = eval_simple_expression(fsm, "TRUE") self.assertEqual(fsm.equivalent_states(c1p, {"c1"}), c1p) self.assertEqual(fsm.equivalent_states(c1p, {"c2"}), true)
def test_pre(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") unknown = eval_simple_expression(fsm, "countsay = unknown") odd = eval_simple_expression(fsm, "countsay = odd") even = eval_simple_expression(fsm, "countsay = even") true = eval_simple_expression(fsm, "TRUE") self.assertTrue(odd & fsm.bddEnc.statesMask <= fsm.pre(odd)) self.assertTrue(fsm.init <= unknown) self.assertTrue(fsm.pre(unknown).is_false())
def test_iff(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") odd = eval_simple_expression(fsm, "countsay = odd") specs = parseCTLK("'c1.payer' <-> 'countsay = odd'") self.assertEqual(len(specs), 1) spec = specs[0] c1pico = evalCTLK(fsm, spec) self.assertEqual(c1p.iff(odd), c1pico)
def test_k_ef(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") specs = parseCTLK("K<'at'> EF 'af.local'") self.assertEqual(len(specs), 1) spec = specs[0] kef = evalCTLK(fsm, spec) self.assertEqual(kef, true)
def test_ex(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") c2p = eval_simple_expression(fsm, "c2.payer") c3p = eval_simple_expression(fsm, "c3.payer") c1h = eval_simple_expression(fsm, "c1.coin = head") c2h = eval_simple_expression(fsm, "c2.coin = head") c3h = eval_simple_expression(fsm, "c3.coin = head") odd = eval_simple_expression(fsm, "countsay = odd") unk = eval_simple_expression(fsm, "countsay = unknown") specs = parseCTLK("EX ('c1.payer' & ~'c2.payer' & ~'c3.payer')") self.assertEqual(len(specs), 1) spec = specs[0] ex = evalCTLK(fsm, spec) self.assertEqual(ex, c1p & ~c2p & ~c3p & fsm.bddEnc.statesMask) specs = parseCTLK("EX 'countsay = odd'") self.assertEqual(len(specs), 1) spec = specs[0] ex = evalCTLK(fsm, spec) self.assertTrue(odd & fsm.bddEnc.statesMask <= ex) self.assertTrue((unk & c1p & ~c2p & ~c3p) & fsm.bddEnc.statesMask <= ex) self.assertTrue((unk & c1p & c2p & c3p) & fsm.bddEnc.statesMask <= ex)
def test_simple_pre(self): glob.load_from_file("tests/tools/ctlk/agents.smv") fsm = glob.mas() self.assertIsNotNone(fsm) lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") self.assertEqual(fsm.pre(lt & ~lf & ~g), (lt & lf & ~g) | (~lt & ~lf & g)) self.assertEqual(fsm.pre(g), true) self.assertEqual(fsm.pre(lf), lf.iff(g))
def test_simple(self): mas = self.simple() s4 = eval_simple_expression(mas, "s = 4") ac = mas.bddEnc.cube_for_inputs_vars("a") bc = mas.bddEnc.cube_for_inputs_vars("b") self.assertTrue((~(mas.weak_pre(~s4).forsome(bc)) & mas.weak_pre(s4)).forsome(mas.bddEnc.inputsCube))
def test_reachable_states_for_simple_model(self): glob.load_from_file("tests/tools/ctlk/agents.smv") fsm = glob.mas() self.assertIsNotNone(fsm) true = eval_simple_expression(fsm, "TRUE") self.assertEqual(fsm.reachable_states, true)
def test_hashes(self): (fsm, enc, manager) = self.init_model() true = BDD.true(manager) false = BDD.false(manager) init = fsm.init noadmin = eval_simple_expression(fsm, "admin = none") alice = eval_simple_expression(fsm, "admin = alice") processing = eval_simple_expression(fsm, "state = processing") self.assertEqual(hash(true), hash(~false)) self.assertNotEqual(hash(true), hash(false)) self.assertEqual(hash(init & noadmin), hash(init)) bdddict = {true, false, init, noadmin, alice, processing} self.assertEqual(len(bdddict), 6)
def test_uncomparable_sets(self): (fsm, enc, manager) = self.init_model() alice = eval_simple_expression(fsm, "admin = alice") processing = eval_simple_expression(fsm, "state = processing") self.assertFalse(alice <= processing) self.assertFalse(processing <= alice) self.assertFalse(alice < processing) self.assertFalse(processing < alice) self.assertFalse(alice == processing) self.assertTrue(alice != processing) self.assertFalse(alice >= processing) self.assertFalse(processing >= alice) self.assertFalse(alice > processing) self.assertFalse(processing > alice)
def test_cardgame_cax(self): fsm = self.cardgame() spec = parseATL("['player']X 'pcard = Ac'")[0] agents = {atom.value for atom in spec.group} phi = evalATL(fsm, spec.child) self.assertTrue(check(fsm, spec)) sat = evalATL(fsm, spec) initsat = sat & fsm.init first = fsm.pick_one_state(initsat) explanation = explain_cax(fsm, first, agents, evalATL(fsm, spec.child)) #self.show_cex(explanation, spec) self.check_cax(fsm, explanation, agents, phi) spec = parseATL("['dealer']X 'win'")[0] agents = {atom.value for atom in spec.group} phi = evalATL(fsm, spec.child) # False since we do not want initial states #self.assertTrue(check(fsm, spec)) sat = evalATL(fsm, spec) initsat = (sat & eval_simple_expression(fsm, 'step = 1') & fsm.reachable_states) first = fsm.pick_one_state(initsat) explanation = explain_cax(fsm, first, agents, evalATL(fsm, spec.child)) #self.show_cex(explanation, spec) self.check_cax(fsm, explanation, agents, phi)
def test_size(self): (fsm, enc, manager) = self.init_model() true = BDD.true(manager) false = BDD.false(manager) init = fsm.init noadmin = eval_simple_expression(fsm, "admin = none") alice = eval_simple_expression(fsm, "admin = alice") processing = eval_simple_expression(fsm, "state = processing") self.assertEqual(BDD.true().size, 1) self.assertEqual(BDD.false().size, 1) self.assertEqual(fsm.pick_one_state(BDD.true()).size, len(fsm.bddEnc.get_variables_ordering("bits")) + 1) self.assertEqual(init.size, 5) self.assertEqual(processing.size, 3)
def test_d_distributedmodel(self): glob.load_from_file("tests/tools/ctlk/distributed_knowledge.smv") fsm = glob.mas() self.assertIsNotNone(fsm) l1 = eval_simple_expression(fsm, "a1.local") l2 = eval_simple_expression(fsm, "a2.local") l3 = eval_simple_expression(fsm, "a3.local") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("'a1.local' -> D<'a1','a2'> 'a1.local'") self.assertEqual(len(specs), 1) spec = specs[0] a1da1 = evalCTLK(fsm, spec) self.assertEqual(true, a1da1)
def test_reachable(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") spec = parseCTLK("Reachable")[0] self.assertIsNotNone(spec) state = fsm.pick_one_state(~lt & ~lf & ~g) self.assertIsNotNone(state) self.assertTrue(state <= evalCTLK(fsm, spec)) expl = explain_witness(fsm, state, spec) self.assertIsNotNone(expl) self.assertEqual(type(expl), Tlacenode) self.assertEqual(expl.state, state) self.assertEqual(len(expl.atomics), 0) self.assertEqual(len(expl.branches), 1) self.assertEqual(len(expl.universals), 0) branch = expl.branches[0] self.assertEqual(type(branch), TemporalBranch) self.assertEqual(type(branch.specification), Reachable) path = branch.path self.assertIsNone(branch.loop) for (s, i, sp) in zip(path[::2], path[1::2], path[2::2]): self.assertIsNotNone(s) self.assertTrue(s.state <= fsm.reachable_states) self.assertEqual(len(s.atomics), 0) self.assertEqual(len(s.branches), 0) self.assertEqual(len(s.universals), 0) self.assertIsNotNone(sp) self.assertTrue(sp.state <= fsm.reachable_states) self.assertEqual(len(sp.branches), 0) self.assertEqual(len(sp.universals), 0) self.assertTrue(i <= fsm.get_inputs_between_states(sp.state, s.state)) self.assertEqual(len(path[-1].atomics), 1) self.assertEqual(type(path[-1].atomics[0]), Init) print(xml_witness(fsm, expl, spec))
def test_false(self): fsm = self.model() false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("False") self.assertEqual(len(specs), 1) spec = specs[0] self.assertEqual(false, evalCTLK(fsm, spec))
def test_nk_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") # nK<at> af.local nks = nk(fsm, "at", lf) self.assertTrue(nks.isnot_false()) state = fsm.pick_one_state(nks) (s, ag, sp) = explain_nk(fsm, state, "at", lf) self.assertEqual(s, state) self.assertEqual(ag, {"at"}) self.assertTrue(sp <= lf) self.assertTrue(sp <= fsm.equivalent_states(state, {"at"})) self.assertTrue(sp <= fsm.reachable_states)
def test_true(self): fsm = self.model() true = eval_simple_expression(fsm, "TRUE") specs = parseCTLK("True") self.assertEqual(len(specs), 1) spec = specs[0] self.assertEqual(true, evalCTLK(fsm, spec))
def test_k_dincry(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") c2p = eval_simple_expression(fsm, "c2.payer") c3p = eval_simple_expression(fsm, "c3.payer") c1h = eval_simple_expression(fsm, "c1.coin = head") c2h = eval_simple_expression(fsm, "c2.coin = head") c3h = eval_simple_expression(fsm, "c3.coin = head") odd = eval_simple_expression(fsm, "countsay = odd") unk = eval_simple_expression(fsm, "countsay = unknown") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("AG ('c1.payer' " "-> K<'c1'> (~'c2.payer' & ~'c3.payer'))") self.assertEqual(len(specs), 1) spec = specs[0] ik = evalCTLK(fsm, spec) self.assertTrue(fsm.init <= ik)
def test_create_trans(self): fsm = self.model() p = eval_simple_expression(fsm, "p") new_trans = BddTrans.from_string(fsm.bddEnc.symbTable, "next(p) = !p") self.assertEqual(new_trans.post(p), ~p) with self.assertRaises(NuSMVFlatteningError): new_trans = BddTrans.from_string(fsm.bddEnc.symbTable, "next(p) = !p", strcontext="main")
def test_nd_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") # nD<at, af> at.local nds = nd(fsm, ["at", "af"], lt) self.assertTrue(nds.isnot_false()) state = fsm.pick_one_state(nds) (s, ag, sp) = explain_nd(fsm, state, ["at", "af"], lt) self.assertEqual(s, state) self.assertEqual(ag, {"at", "af"}) self.assertTrue(sp <= lt) self.assertTrue((sp <= fsm.equivalent_states(state, {"at"})) & (sp <= fsm.equivalent_states(state, {"af"}))) self.assertTrue(sp <= fsm.reachable_states)
def test_not(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") specs = parseCTLK("~'c1.payer'") self.assertEqual(len(specs), 1) spec = specs[0] c1pCTLK = evalCTLK(fsm, spec) self.assertEqual(~c1p, c1pCTLK)
def test_ne_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") # nE<at, af> g nes = ne(fsm, ["at", "af"], g) self.assertTrue(nes.isnot_false()) state = fsm.pick_one_state(nes) (s, ag, sp) = explain_ne(fsm, state, ["at", "af"], g) self.assertEqual(s, state) self.assertTrue(ag in [{"at"}, {"af"}]) self.assertTrue(sp <= g) self.assertTrue((sp <= fsm.equivalent_states(state, {"at"})) | (sp <= fsm.equivalent_states(state, {"af"}))) self.assertTrue(sp <= fsm.reachable_states)
def test_reachable_states(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") c2p = eval_simple_expression(fsm, "c2.payer") odd = eval_simple_expression(fsm, "countsay = odd") true = eval_simple_expression(fsm, "TRUE") self.assertTrue(fsm.reachable_states.isnot_true()) self.assertTrue(fsm.reachable_states.isnot_false()) self.assertTrue((fsm.init & c1p & c2p).is_false()) self.assertTrue((fsm.post(fsm.init) & c1p & c2p).is_false()) tmp = fsm.reachable_states & (c1p & c2p) while tmp.isnot_false(): s = fsm.pick_one_state(tmp) print(s.get_str_values()) tmp -= s self.assertTrue((fsm.reachable_states & (c1p & c2p)).is_false())
def test_e_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("'global' -> E<'at','af'> 'global'") self.assertEqual(len(specs), 1) spec = specs[0] geg = evalCTLK(fsm, spec) self.assertEqual(geg, true) specs = parseCTLK("'af.local' & E<'at','af'> 'af.local'") self.assertEqual(len(specs), 1) spec = specs[0] afeaf = evalCTLK(fsm, spec) self.assertEqual(afeaf, false)
def test_one_state(self): fsm = self.model() c2p = eval_simple_expression(fsm, "c2.payer") state = choose_one_state(fsm, fsm.init) print() if state is None: print("No chosen state.") else: values = state.get_str_values() for var in values: print(var, "=", values[var])
def test_one_transition(self): smvmodel = """ MODULE main VAR state: {s1, s2}; INIT state = s1 TRANS next(state) = s2 """ model.load_from_string(smvmodel) fsm = model.bddModel() self.assertSetEqual(set(fsm.transitions.keys()), {'time'}) self.assertEqual(fsm.post(fsm.init, transition='time'), eval_simple_expression(fsm, 'state = s2'))
def test_additional_transitions(self): smvmodel = """ MODULE main VAR state: {s1, s2}; IVAR transition : {time, knowledge}; INIT state = s1 TRANS case transition = time : next(state) = s2; transition = knowledge : next(state) = state; esac """ model.load_from_string(smvmodel) transitions = {'reset': 'next(state) = s1'} fsm = model.bddModel(transitions=transitions) self.assertSetEqual(set(fsm.transitions.keys()), {'time', 'knowledge', 'reset'}) self.assertEqual(fsm.post(fsm.init, 'time'), eval_simple_expression(fsm, 'state = s2')) self.assertEqual(fsm.post(fsm.init, 'knowledge'), eval_simple_expression(fsm, 'state = s1')) self.assertEqual(fsm.post(fsm.reachable_states, 'reset'), eval_simple_expression(fsm, 'state = s1'))
def test_split(self): fsm = self.little() aa = eval_simple_expression(fsm, "a.a = 1") ap = eval_simple_expression(fsm, "a.p = 1") ba = eval_simple_expression(fsm, "b.a = 1") bq = eval_simple_expression(fsm, "b.q = 1") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") gamma_inputs = [ var for agent in {"a"} for var in fsm.agents_inputvars[agent] ] gamma_cube = fsm.bddEnc.cube_for_inputs_vars(gamma_inputs) ngamma_cube = fsm.bddEnc.inputsCube - gamma_cube strats = split(fsm, fsm.protocol({"a"}), {"a"}) commstrat = (((~ap & bq & ~aa) | (~ap & ~bq & ~aa)).forsome(ngamma_cube) & fsm.protocol({"a"})) firststrat = (((ap & bq & aa) | (ap & ~bq & aa)).forsome(ngamma_cube) & fsm.protocol({"a"})) secstrat = (((ap & bq & ~aa) | (ap & ~bq & ~aa)).forsome(ngamma_cube) & fsm.protocol({"a"})) self.assertTrue((commstrat | firststrat) in strats) self.assertTrue((commstrat | secstrat) in strats) self.assertSetEqual({commstrat | firststrat, commstrat | secstrat}, strats)
def test_reachable_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") state = fsm.pick_one_state(~lf & ~lt & ~g) self.assertTrue(state <= fsm.reachable_states) witness = explain_reachable(fsm, state) self.assertIsNotNone(witness) self.assertEqual(witness[0], state) for (s, i, sp) in zip(witness[::2], witness[1::2], witness[2::2]): self.assertTrue(s <= fsm.reachable_states) self.assertTrue(sp <= fsm.reachable_states) self.assertIsNotNone(i) self.assertTrue(i <= fsm.get_inputs_between_states(sp, s)) self.assertTrue(witness[-1] <= fsm.init)
def test_inputs(self): smvmodel = """ MODULE main VAR state: {s1, s2}; IVAR run: boolean; INIT state = s1 TRANS next(state) = (run ? s2 : state) """ model.load_from_string(smvmodel) fsm = model.bddModel() self.assertSetEqual(set(fsm.transitions.keys()), {'time'}) self.assertEqual( fsm.post(fsm.init, transition='time'), eval_simple_expression(fsm, 'state = s2 | state = s1')) run = eval_simple_expression(fsm, "run") self.assertEqual(fsm.post(fsm.init, inputs=run), eval_simple_expression(fsm, 'state = s2')) nrun = eval_simple_expression(fsm, "!run") self.assertEqual(fsm.post(fsm.init, inputs=nrun), eval_simple_expression(fsm, 'state = s1'))
def test_ex_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") # lt & lf & g |= EX !lt # because (lt & lf & g, !lt & lf (g | !g)) state = fsm.pick_one_state(lt & lf & g) witness = explain_ex(fsm, state, ~lt) self.assertTrue(witness[0] == state) self.assertTrue(witness[2] <= ~lt) self.assertTrue(witness[2] <= ~lt & lf) self.assertTrue((witness[2] == ~lt & lf & g) | (witness[2] == ~lt & lf & ~g)) self.assertTrue( fsm.get_inputs_between_states(state, witness[2]).isnot_false()) self.assertTrue( witness[1] <= fsm.get_inputs_between_states(state, witness[2]))
def test_pick_inputs(self): fsm = self.model() p = eval_simple_expression(fsm, "p") a = eval_simple_expression(fsm, "a") b0 = eval_simple_expression(fsm, "b = 0") b1 = eval_simple_expression(fsm, "b = 1") b2 = eval_simple_expression(fsm, "b = 2") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") self.assertEqual(len(fsm.pick_all_inputs(a & b1)), 1)
def test_simple_ex(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") specs = parseCTLK("EX 'af.local'") self.assertEqual(len(specs), 1) spec = specs[0] ex = evalCTLK(fsm, spec) # ex : # l1, !l2, !g # !l1, l2, g # l1, l2, g # !l1, !l2, !g self.assertEqual(ex, lf.iff(g)) specs = parseCTLK("EX ('af.local' & 'at.local' & 'global')") self.assertEqual(len(specs), 1) spec = specs[0] ex = evalCTLK(fsm, spec) self.assertEqual(ex, lt & ~lf & ~g | ~lt & lf & g)
def test_nc_simple(self): fsm = self.simplemodel() lt = eval_simple_expression(fsm, "at.local") lf = eval_simple_expression(fsm, "af.local") g = eval_simple_expression(fsm, "global") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") # ~lt & ~lf & g |= nc<at, af> ~lt & ~lf & ~g ncs = nc(fsm, ["at"], ~lt & ~lf & g) self.assertTrue(ncs.isnot_false()) state = fsm.pick_one_state(~lt & lf & g) self.assertTrue(state <= ncs) witness = explain_nc(fsm, state, ["at"], ~lt & ~lf & g) self.assertTrue(witness[0] == state) for (s, ag, sp) in zip(witness[::2], witness[1::2], witness[2::2]): self.assertTrue(s.isnot_false()) self.assertTrue(ag in [{"at"}]) self.assertTrue(sp.isnot_false()) self.assertTrue(sp <= fsm.equivalent_states(s, {"at"})) self.assertTrue(witness[-1] <= ~lt & ~lf & g)
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 test_premod_pre_strat_si(self): fsm = self.premod() p = eval_simple_expression(fsm, "a.p = 1") q = eval_simple_expression(fsm, "b.q = 1") pa = eval_simple_expression(fsm, "a.a = 1") qa = eval_simple_expression(fsm, "b.a = 1") aa = {'a'} bb = {'b'} ag = {'a', 'b'} self.assertEqual((~p & q) | (p & ~q & ~pa), fsm.pre_strat_si(~p & q, aa)) self.assertTrue(fsm.pre_strat_si(p, ag).is_false()) self.assertEqual(fsm.pre_strat_si(~p & q, bb), (~p & q) | (p & q & ~qa)) strat = (pa) self.assertEqual((~p & q & pa), fsm.pre_strat_si(~p & q, aa, strat)) self.assertEqual(fsm.pre_strat_si(~p & q, bb, strat), ((~p & q) | (p & q & ~qa)) & strat) self.assertEqual(fsm.pre_strat_si(~p & q, bb, qa), (~p & q & qa))
def test_nfair_gamma(self): fsm = self.transmission_post_fair() transmit = eval_simple_expression(fsm, "transmitter.action = transmit") false = BDD.false(fsm.bddEnc.DDmanager) self.assertTrue(fsm.reachable_states <= nfair_gamma(fsm, {'transmitter'})) self.assertEqual(false, nfair_gamma(fsm, {'sender'})) strats = split(fsm, fsm.protocol({'transmitter'}), {'transmitter'}) for strat in strats: if (strat & transmit).isnot_false(): self.assertTrue(nfair_gamma(fsm, {'transmitter'}, strat).is_false()) else: self.assertTrue(fsm.reachable_states <= nfair_gamma(fsm, {'transmitter'}, strat))
def test_pick_states_inputs(self): fsm = self.model() p = eval_simple_expression(fsm, "p") a = eval_simple_expression(fsm, "a") b0 = eval_simple_expression(fsm, "b = 0") b1 = eval_simple_expression(fsm, "b = 1") b2 = eval_simple_expression(fsm, "b = 2") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") for si in fsm.pick_all_states_inputs(p & a & b1): print(si.get_str_values()) self.assertEqual(len(fsm.pick_all_states_inputs(p & a & b1)), 1)
def test_next_state_with_inputs(self): fsm = self.model() c2p = eval_simple_expression(fsm, "c2.payer") (inputs, state) = choose_next_state(fsm, fsm.pick_one_state(fsm.init)) print() if state is None: print("No chosen state.") else: if inputs is not None: values = inputs.get_str_values() for var in values: print(var, "=", values[var]) print("-" * 40) values = state.get_str_values() for var in values: print(var, "=", values[var])
def do_constraint(self, arg): if "constraint" not in self.parsers: self.parse_constraint() # Handle arguments parsing errors try: if arg.strip() == "": arg = [] else: arg = [arg.strip()] args = self.parsers["constraint"].parse_args(arg) except ArgumentParsingError as err: print(err, end="") return False if args.constraints is None: if len(self.constraints) <= 0: print("Currently no constraint.") else: print("Current constraints:") for const in self.constraints: print(const) return False else: try: # Parse constraints constBDD = eval_simple_expression(self.fsm, args.constraints) # Restrict the BDD newBdd = self.bdds[-1] & constBDD # If the BDD is not empty, add it to the list and show it if self.fsm.count_states(newBdd) > 0: self.bdds.append(newBdd) self.constraints.append(args.constraints) self._show_last() else: # else backtrack (in fact, do nothing) print("constraint: the constraints are too strong,", "no more states; retry.") except PyNuSMVError as err: print(err) return False return False
def test_next_state_without_inputs(self): glob.load_from_file("tests/pynusmv/models/modules.smv") fsm = glob.mas() self.assertIsNotNone(fsm) c2p = eval_simple_expression(fsm, "top") (inputs, state) = choose_next_state(fsm, fsm.pick_one_state(fsm.init)) print() if state is None: print("No chosen state.") else: if inputs is not None: values = inputs.get_str_values() for var in values: print(var, "=", values[var]) print("-" * 40) values = state.get_str_values() for var in values: print(var, "=", values[var])
def test_splitreach_collapsed_tree(self): fsm = self.collapsed_tree() agents = {"a"} sa0 = eval_simple_expression(fsm, "a.state = 0") sa1 = eval_simple_expression(fsm, "a.state = 1") s0 = eval_simple_expression(fsm, "s = 0") s1 = eval_simple_expression(fsm, "s = 1") s2 = eval_simple_expression(fsm, "s = 2") aa = eval_simple_expression(fsm, "a.action = a") ab = eval_simple_expression(fsm, "a.action = b") splitted_init = psplit(fsm, fsm.init & fsm.protocol(agents), agents) strats = {strat for pustrat in splitted_init for strat in split_reach(fsm, agents, pustrat)} self.assertEqual(len(strats), 2)
def test_c_dincry(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") c2p = eval_simple_expression(fsm, "c2.payer") c3p = eval_simple_expression(fsm, "c3.payer") c1h = eval_simple_expression(fsm, "c1.coin = head") c2h = eval_simple_expression(fsm, "c2.coin = head") c3h = eval_simple_expression(fsm, "c3.coin = head") odd = eval_simple_expression(fsm, "countsay = odd") even = eval_simple_expression(fsm, "countsay = even") unk = eval_simple_expression(fsm, "countsay = unknown") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("'countsay = odd' -> " "C<'c1','c2','c3'> " "('c1.payer' | 'c2.payer' | 'c3.payer')") self.assertEqual(len(specs), 1) spec = specs[0] oc123 = evalCTLK(fsm, spec) self.assertTrue(fsm.reachable_states <= oc123) specs = parseCTLK("'countsay = even' -> " "C<'c1','c2','c3'> " "(~'c1.payer' & ~'c2.payer' & ~'c3.payer')") self.assertEqual(len(specs), 1) spec = specs[0] ec123 = evalCTLK(fsm, spec) self.assertTrue(fsm.reachable_states <= ec123)
def test_d_dincry(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") c2p = eval_simple_expression(fsm, "c2.payer") c3p = eval_simple_expression(fsm, "c3.payer") c1h = eval_simple_expression(fsm, "c1.coin = head") c2h = eval_simple_expression(fsm, "c2.coin = head") c3h = eval_simple_expression(fsm, "c3.coin = head") odd = eval_simple_expression(fsm, "countsay = odd") even = eval_simple_expression(fsm, "countsay = even") unk = eval_simple_expression(fsm, "countsay = unknown") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("('c1.payer' & 'countsay != unknown') " "-> D<'c2','c3'> 'c1.payer'") self.assertEqual(len(specs), 1) spec = specs[0] cud1 = evalCTLK(fsm, spec) self.assertTrue(fsm.reachable_states <= cud1)
def test_dincry_equiv(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") c2p = eval_simple_expression(fsm, "c2.payer") c3p = eval_simple_expression(fsm, "c3.payer") c1h = eval_simple_expression(fsm, "c1.coin = head") c2h = eval_simple_expression(fsm, "c2.coin = head") c3h = eval_simple_expression(fsm, "c3.coin = head") odd = eval_simple_expression(fsm, "countsay = odd") even = eval_simple_expression(fsm, "countsay = even") unk = eval_simple_expression(fsm, "countsay = unknown") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") s = c1p & ~c2p & ~c3p eqs2 = fsm.equivalent_states(s, {"c2"}) eqs3 = fsm.equivalent_states(s, {"c3"}) self.assertEqual(eqs2 & eqs3, ~c2p & ~c3p)
def test_e_dincry(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") c2p = eval_simple_expression(fsm, "c2.payer") c3p = eval_simple_expression(fsm, "c3.payer") c1h = eval_simple_expression(fsm, "c1.coin = head") c2h = eval_simple_expression(fsm, "c2.coin = head") c3h = eval_simple_expression(fsm, "c3.coin = head") odd = eval_simple_expression(fsm, "countsay = odd") even = eval_simple_expression(fsm, "countsay = even") unk = eval_simple_expression(fsm, "countsay = unknown") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") specs = parseCTLK("'countsay = even' -> E<'c1','c2'> ~'c1.payer'") self.assertEqual(len(specs), 1) spec = specs[0] een1 = evalCTLK(fsm, spec) self.assertEqual(een1, true) specs = parseCTLK("E<'c2','c3'> ~'c1.payer'") self.assertEqual(len(specs), 1) spec = specs[0] en1 = evalCTLK(fsm, spec) self.assertTrue(even & fsm.reachable_states <= en1)
def evalATLK(fsm, spec, variant="SF", semantics="group"): """ Return the BDD representing the set of states of fsm satisfying spec. fsm -- a MAS representing the system spec -- an AST-based ATLK specification variant -- the variant of the algorithm to evaluate strategic operators; must be * "SF" for the standard way: splitting in uniform strategies then filtering winning states, * "FS" for the alternating way: filtering winning states, then splitting one conflicting equivalence class, then recurse * "FSF" for the filter-split-filter way: filtering winning states then splitting all remaining actions into uniform strategies, then filtering final winning states. If variant is not in {"SF", "FS", "FSF"}, the standard "SF" way is used. """ if semantics != "group": raise PyNuSMVError("Optimal evalATLK: unsupported semantics:" + semantics) if type(spec) is TrueExp: return BDD.true(fsm.bddEnc.DDmanager) elif type(spec) is FalseExp: return BDD.false(fsm.bddEnc.DDmanager) elif type(spec) is Init: return fsm.init elif type(spec) is Reachable: return fsm.reachable_states elif type(spec) is Atom: return eval_simple_expression(fsm, spec.value) elif type(spec) is Not: return ~evalATLK(fsm, spec.child, variant=variant) elif type(spec) is And: return (evalATLK(fsm, spec.left, variant=variant) & evalATLK(fsm, spec.right, variant=variant)) elif type(spec) is Or: return (evalATLK(fsm, spec.left, variant=variant) | evalATLK(fsm, spec.right, variant=variant)) elif type(spec) is Implies: # a -> b = ~a | b return ((~evalATLK(fsm, spec.left, variant=variant)) | evalATLK(fsm, spec.right, variant=variant)) elif type(spec) is Iff: # a <-> b = (a & b) | (~a & ~b) l = evalATLK(fsm, spec.left, variant=variant) r = evalATLK(fsm, spec.right, variant=variant) return (l & r) | ((~l) & (~r)) elif type(spec) is EX: return ex(fsm, evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is AX: # AX p = ~EX ~p return ~ex(fsm, ~evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is EG: return eg(fsm, evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is AG: # AG p = ~EF ~p = ~E[ true U ~p ] return ~eu(fsm, BDD.true(fsm.bddEnc.DDmanager), ~evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is EU: return eu(fsm, evalATLK(fsm, spec.left, variant=variant), evalATLK(fsm, spec.right, variant=variant)) elif type(spec) is AU: # A[p U q] = ~E[~q W ~p & ~q] = ~(E[~q U ~p & ~q] | EG ~q) p = evalATLK(fsm, spec.left, variant=variant) q = evalATLK(fsm, spec.right, variant=variant) equpq = eu(fsm, ~q, ~q & ~p) egq = eg(fsm, ~q) return ~(equpq | egq) elif type(spec) is EF: # EF p = E[ true U p ] return eu(fsm, BDD.true(fsm.bddEnc.DDmanager), evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is AF: # AF p = ~EG ~p return ~eg(fsm, ~evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is EW: # E[ p W q ] = E[ p U q ] | EG p return (eu(fsm, evalATLK(fsm, spec.left, variant=variant), evalATLK(fsm, spec.right, variant=variant)) | eg(fsm, evalATLK(fsm, spec.left, variant=variant))) elif type(spec) is AW: # A[p W q] = ~E[~q U ~p & ~q] p = evalATLK(fsm, spec.left, variant=variant) q = evalATLK(fsm, spec.right, variant=variant) return ~eu(fsm, ~q, ~p & ~q) elif type(spec) is nK: return nk(fsm, spec.agent.value, evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is K: # K<'a'> p = ~nK<'a'> ~p return ~nk(fsm, spec.agent.value, ~evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is nE: return ne(fsm, [a.value for a in spec.group], evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is E: # E<g> p = ~nE<g> ~p return ~ne(fsm, [a.value for a in spec.group], ~evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is nD: return nd(fsm, [a.value for a in spec.group], evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is D: # D<g> p = ~nD<g> ~p return ~nd(fsm, [a.value for a in spec.group], ~evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is nC: return nc(fsm, [a.value for a in spec.group], evalATLK(fsm, spec.child, variant=variant)) elif type(spec) is C: # C<g> p = ~nC<g> ~p return ~nc(fsm, [a.value for a in spec.group], ~evalATLK(fsm, spec.child, variant=variant)) elif type(spec) in {CEX, CAX, CEG, CAG, CEU, CAU, CEF, CAF, CEW, CAW}: if variant == "SF": return eval_strat(fsm, spec) elif variant == "FS": return eval_strat_improved(fsm, spec) elif variant == "FSF": return eval_strat_FSF(fsm, spec) else: return eval_strat(fsm, spec) else: # TODO Generate error print("[ERROR] evalATLK: unrecognized specification type", spec) return None
def test_split_by_picking(self): fsm = self.little() aa = eval_simple_expression(fsm, "a.a = 1") ap = eval_simple_expression(fsm, "a.p = 1") ba = eval_simple_expression(fsm, "b.a = 1") bq = eval_simple_expression(fsm, "b.q = 1") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") excluded = BDD.false(fsm.bddEnc.DDmanager) strats = fsm.protocol({"a"}) print("protocol of a -- strats to consider") self.show_si(fsm, strats) si = fsm.pick_one_state_inputs(strats) self.assertEqual( len(fsm.pick_all_states(si.forsome(fsm.bddEnc.inputsCube))), 1) s = fsm.pick_one_state(si.forsome(fsm.bddEnc.inputsCube)) print("si:", si.get_str_values()) print("s:", s.get_str_values()) eqs = fsm.equivalent_states(s, {"a"}) print("equivalent to", s.get_str_values()) self.show_s(fsm, eqs) eqcl = strats & eqs print("equivalence class (" + str(s.get_str_values()) + ")") self.show_si(fsm, eqcl) act = eqcl.forsome(fsm.bddEnc.statesCube) print("actions of eq class (" + str(fsm.count_inputs(act)) + ")") self.show_i(fsm, act) gamma_inputs = [ var for agent in {"a"} for var in fsm.agents_inputvars[agent] ] gamma_cube = fsm.bddEnc.cube_for_inputs_vars(gamma_inputs) ngamma_cube = fsm.bddEnc.inputsCube - gamma_cube nact = ~act.forsome(ngamma_cube) # Useless here print("the other actions") self.show_i(fsm, nact) # There is more than one action if act contains other actions than # si self.assertTrue(( act - si.forsome(fsm.bddEnc.statesCube).forsome(ngamma_cube)).is_false()) print("Are there other actions than si's one ?", (act - si.forsome( fsm.bddEnc.statesCube).forsome(ngamma_cube)).isnot_false()) # remove act from Strats excluded = excluded | (strats & eqcl) print("excluded") self.show_si(fsm, excluded) strats = strats - excluded print("new strats") self.show_si(fsm, strats) # Restart the process si = fsm.pick_one_state_inputs(strats) self.assertEqual( len(fsm.pick_all_states(si.forsome(fsm.bddEnc.inputsCube))), 1) s = fsm.pick_one_state(si.forsome(fsm.bddEnc.inputsCube)) print("si:", si.get_str_values()) print("s:", s.get_str_values()) eqs = fsm.equivalent_states(s, {"a"}) print("equivalent to", s.get_str_values()) self.show_s(fsm, eqs) eqcl = strats & eqs print("equivalence class (" + str(s.get_str_values()) + ")") self.show_si(fsm, eqcl) act = eqcl.forsome(fsm.bddEnc.statesCube) print("actions of eq class (" + str(fsm.count_inputs(act)) + ")") self.show_i(fsm, act) print("Are there other actions than si's one ?", (act - si.forsome( fsm.bddEnc.statesCube).forsome(ngamma_cube)).isnot_false()) self.show_i( fsm, (act - si.forsome(fsm.bddEnc.statesCube).forsome(ngamma_cube))) # Need to split eqcl now ! # Keep eqcl to split strats later fulleqcl = eqcl ncss = (eqcl & si.forsome(fsm.bddEnc.statesCube).forsome(ngamma_cube)) eqcls = [ncss] eqcl = eqcl - ncss while eqcl.isnot_false(): si = fsm.pick_one_state_inputs(eqcl) ncss = (eqcl & si.forsome(fsm.bddEnc.statesCube).forsome(ngamma_cube)) eqcls.append(ncss) eqcl = eqcl - ncss for ncss in eqcls: print("new non-conflicting subset") self.show_si(fsm, ncss) splitted = [] for ncss in eqcls: splitted.append(strats - fulleqcl + ncss + excluded) print("show splitted strats") for substrat in splitted: print("new sub strategy") self.show_si(fsm, substrat)
def test_count(self): fsm = self.model() c1p = eval_simple_expression(fsm, "c1.payer") c2p = eval_simple_expression(fsm, "c2.payer") c3p = eval_simple_expression(fsm, "c3.payer") c1ch = eval_simple_expression(fsm, "c1.coin = head") c2ch = eval_simple_expression(fsm, "c2.coin = head") c3ch = eval_simple_expression(fsm, "c3.coin = head") c1se = eval_simple_expression(fsm, "c1.say = equal") c2se = eval_simple_expression(fsm, "c2.say = equal") c3se = eval_simple_expression(fsm, "c3.say = equal") unknown = eval_simple_expression(fsm, "countsay = unknown") odd = eval_simple_expression(fsm, "countsay = odd") even = eval_simple_expression(fsm, "countsay = even") true = eval_simple_expression(fsm, "TRUE") false = eval_simple_expression(fsm, "FALSE") self.assertEqual(1, fsm.count_states(fsm.pick_one_state(c1p))) self.assertEqual( 1, fsm.count_states(c1p & ~c2p & ~c3p & c1ch & c2ch & c3ch & unknown)) self.assertEqual( 1, fsm.count_states(c1p & ~c2p & ~c3p & c1ch & c2ch & c3ch & odd))