def test_until_with_loop(self): with tests.Configure(self, __file__, "/example.smv"): enc = self.enc i,k,l= 0,2,0 a = ast.Proposition("a") b = ast.Proposition("b") expr = ast.Until(a, b) tool = expr.semantic_with_loop(enc, i,k,l) manual = b.semantic_with_loop(enc, i, k, l) | \ (a.semantic_with_loop(enc, i, k, l) & b.semantic_with_loop(enc, i+1, k, l)) spec = Node.from_ptr(parse_ltl_spec("a U b")) nusmv = ltlspec.bounded_semantics(self.befsm, spec, bound=k, loop=l) tool &= bmcutils.loop_condition(enc, k, l) manual &= bmcutils.loop_condition(enc, k, l) # normalized string representation of the BE's (make them comparable) s_tool = tests.canonical_cnf(tool) s_nusmv = tests.canonical_cnf(nusmv) s_manual= tests.canonical_cnf(manual) self.assertEqual(s_tool, s_manual) self.assertEqual(s_tool, s_nusmv)
def test_until_with_loop(self): with tests.Configure(self, __file__, "/example.smv"): enc = self.enc i, k, l = 0, 2, 0 a = ast.Proposition("a") b = ast.Proposition("b") expr = ast.Until(a, b) tool = expr.semantic_with_loop(enc, i, k, l) manual = b.semantic_with_loop(enc, i, k, l) | \ (a.semantic_with_loop(enc, i, k, l) & b.semantic_with_loop(enc, i+1, k, l)) spec = Node.from_ptr(parse_ltl_spec("a U b")) nusmv = ltlspec.bounded_semantics(self.befsm, spec, bound=k, loop=l) tool &= bmcutils.loop_condition(enc, k, l) manual &= bmcutils.loop_condition(enc, k, l) # normalized string representation of the BE's (make them comparable) s_tool = tests.canonical_cnf(tool) s_nusmv = tests.canonical_cnf(nusmv) s_manual = tests.canonical_cnf(manual) self.assertEqual(s_tool, s_manual) self.assertEqual(s_tool, s_nusmv)
def test_next_with_loop(self): with tests.Configure(self, __file__, "/example.smv"): i, k, l = 0, 2, 0 enc = self.enc # One step a = ast.Proposition("a") formula = ast.Next(a) tool = formula.semantic_with_loop(enc, i, k, l) manual = a.semantic_with_loop(enc, 1, k, l) nusmv = ltlspec.bounded_semantics(self.befsm, Node.from_ptr( parse_ltl_spec("X a")), bound=k, loop=l) loop_cond = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(tool & loop_cond) s_manual = tests.canonical_cnf(manual & loop_cond) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_nusmv) self.assertEqual(s_tool, s_manual) # two steps formula = ast.Next(ast.Next(a)) tool = formula.semantic_with_loop(enc, i, k, l) manual = a.semantic_with_loop(enc, 0, k, l) nusmv = ltlspec.bounded_semantics(self.befsm, Node.from_ptr( parse_ltl_spec("X X a")), bound=k, loop=l) loop_cond = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(tool & loop_cond) s_manual = tests.canonical_cnf(manual & loop_cond) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_nusmv) self.assertEqual(s_tool, s_manual) # Three steps (getting over k) formula = ast.Next(ast.Next(ast.Next(a))) tool = formula.semantic_with_loop(enc, i, k, l) manual = a.semantic_with_loop(enc, 1, k, l) nusmv = ltlspec.bounded_semantics(self.befsm, Node.from_ptr( parse_ltl_spec("X X X a")), bound=k, loop=l) loop_cond = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(tool & loop_cond) s_manual = tests.canonical_cnf(manual & loop_cond) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_nusmv) self.assertEqual(s_tool, s_manual)
def test_next_with_loop(self): with tests.Configure(self, __file__, "/example.smv"): i,k,l = 0,2,0 enc = self.enc # One step a = ast.Proposition("a") formula = ast.Next(a) tool = formula.semantic_with_loop(enc, i, k, l) manual = a.semantic_with_loop(enc, 1, k, l) nusmv = ltlspec.bounded_semantics(self.befsm, Node.from_ptr(parse_ltl_spec("X a")), bound = k, loop = l) loop_cond = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(tool & loop_cond) s_manual= tests.canonical_cnf(manual & loop_cond) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_nusmv) self.assertEqual(s_tool, s_manual) # two steps formula = ast.Next(ast.Next(a)) tool = formula.semantic_with_loop(enc, i, k, l) manual = a.semantic_with_loop(enc, 0, k, l) nusmv = ltlspec.bounded_semantics(self.befsm, Node.from_ptr(parse_ltl_spec("X X a")), bound = k, loop = l) loop_cond = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(tool & loop_cond) s_manual= tests.canonical_cnf(manual & loop_cond) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_nusmv) self.assertEqual(s_tool, s_manual) # Three steps (getting over k) formula = ast.Next(ast.Next(ast.Next(a))) tool = formula.semantic_with_loop(enc, i, k, l) manual = a.semantic_with_loop(enc, 1, k, l) nusmv = ltlspec.bounded_semantics(self.befsm, Node.from_ptr(parse_ltl_spec("X X X a")), bound = k, loop = l) loop_cond = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(tool & loop_cond) s_manual= tests.canonical_cnf(manual & loop_cond) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_nusmv) self.assertEqual(s_tool, s_manual)
def test_releases(self): with Configure(self, __file__, "/models/flipflops.smv"): fsm = self.befsm formula = self.nnf("(a V b)") # bound 0 offset = 0 bound = 0 ref_expr= ltlspec.bounded_semantics(fsm, formula, bound) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr)) # bound 1 offset = 0 bound = 1 ref_expr= ltlspec.bounded_semantics(fsm, formula, bound) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset) # VERIFIED manually, complains only about the CNF clauses literals and that's OK. # self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr)) # ---- other offset ---- # bound 2 offset = 2 bound = 1 # because of the way the loop condition is encoded ! ref_expr= ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, bound, offset)\ | ( ltlspec.bounded_semantics_with_loop_at_offset(fsm, formula, 0, bound, 0, offset) & bmcutils.loop_condition(self.enc, offset+bound, offset+0)) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset)
def test_eventually_with_loop(self): with tests.Configure(self, __file__, "/example.smv"): i,k,l = 0,2,0 enc = self.enc a = ast.Proposition("a") formula = ast.Eventually(a) tool = formula.semantic_with_loop(enc, i, k, l) manual = a.semantic_with_loop(enc, i+1, k, l) |\ a.semantic_with_loop(enc, i , k, l) nusmv = ltlspec.bounded_semantics( self.befsm, Node.from_ptr(parse_ltl_spec("F a")), bound = k, loop = l) # normalized string representation of the BE's (make them comparable) loop_cond = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(tool & loop_cond) s_manual= tests.canonical_cnf(manual & loop_cond) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_nusmv, s_tool) self.assertEqual(s_manual, s_tool)
def test_until_with_loop(self): with Configure(self, __file__, "/models/flipflops.smv"): fsm = self.befsm formula = self.nnf("(a U b)") # bound 1 offset = 0 bound = 1 loop = 0 # remember: the NuSMV std apis incorporate the loop condition ! ref_expr= ltlspec.bounded_semantics_single_loop(fsm, formula, bound, loop) expr = ltlspec.bounded_semantics_with_loop_at_offset(fsm, formula, 0, bound, loop, offset) \ & bmcutils.loop_condition(self.enc, bound, loop) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr)) # ---- other offset ---- # bound 2 offset = 1 bound = 2 loop = 0 car = Wff.decorate(ltlspec.car(formula)).to_be(fsm.encoding) cdr = Wff.decorate(ltlspec.cdr(formula)).to_be(fsm.encoding) # because of the way the loop condition is encoded ! ref_expr= self.enc.shift_to_time(cdr, offset) | (self.enc.shift_to_time(car, offset) \ & self.enc.shift_to_time(cdr, offset+1)) expr = ltlspec.bounded_semantics_with_loop_at_offset(fsm, formula, 0, bound, loop, offset) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr)) # because loop < i, the condition must be the same as before expr = ltlspec.bounded_semantics_with_loop_at_offset(fsm, formula, 1, bound, loop, offset) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
def test_eventually_with_loop(self): with tests.Configure(self, __file__, "/example.smv"): i, k, l = 0, 2, 0 enc = self.enc a = ast.Proposition("a") formula = ast.Eventually(a) tool = formula.semantic_with_loop(enc, i, k, l) manual = a.semantic_with_loop(enc, i+1, k, l) |\ a.semantic_with_loop(enc, i , k, l) nusmv = ltlspec.bounded_semantics(self.befsm, Node.from_ptr( parse_ltl_spec("F a")), bound=k, loop=l) # normalized string representation of the BE's (make them comparable) loop_cond = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(tool & loop_cond) s_manual = tests.canonical_cnf(manual & loop_cond) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_nusmv, s_tool) self.assertEqual(s_manual, s_tool)
def test_loop_condition(self): with tests.Configure(self, __file__, "/example.smv"): enc = self.enc a = enc.by_name['a'] b = enc.by_name['b'] # self looping on zero _, k, l = 0, 0, 0 loop_cond = ast.loop_condition(enc, k, l) # note: frozen and ivar are not taken into account manual = a.at_time[0].boolean_expression.iff(a.at_time[0].boolean_expression) &\ b.at_time[0].boolean_expression.iff(b.at_time[0].boolean_expression) nusmv = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(loop_cond) s_manual = tests.canonical_cnf(manual) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_manual) self.assertEqual(s_tool, s_nusmv) # simple k-l loop 5 - 0 _, k, l = 0, 5, 0 loop_cond = ast.loop_condition(enc, k, l) manual = a.at_time[5].boolean_expression.iff(a.at_time[0].boolean_expression) &\ b.at_time[5].boolean_expression.iff(b.at_time[0].boolean_expression) nusmv = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(loop_cond) s_manual = tests.canonical_cnf(manual) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_manual) self.assertEqual(s_tool, s_nusmv) # simple k-l loop 5 - 0 _, k, l = 0, 5, 2 loop_cond = ast.loop_condition(enc, k, l) manual = a.at_time[5].boolean_expression.iff(a.at_time[2].boolean_expression) &\ b.at_time[5].boolean_expression.iff(b.at_time[2].boolean_expression) nusmv = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(loop_cond) s_manual = tests.canonical_cnf(manual) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_manual) self.assertEqual(s_tool, s_nusmv)
def test_loop_condition(self): with tests.Configure(self, __file__, "/example.smv"): enc = self.enc a = enc.by_name['a'] b = enc.by_name['b'] # self looping on zero _,k,l = 0,0,0 loop_cond = ast.loop_condition(enc, k, l) # note: frozen and ivar are not taken into account manual = a.at_time[0].boolean_expression.iff(a.at_time[0].boolean_expression) &\ b.at_time[0].boolean_expression.iff(b.at_time[0].boolean_expression) nusmv = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(loop_cond) s_manual = tests.canonical_cnf(manual) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_manual) self.assertEqual(s_tool, s_nusmv) # simple k-l loop 5 - 0 _,k,l = 0,5,0 loop_cond = ast.loop_condition(enc, k, l) manual = a.at_time[5].boolean_expression.iff(a.at_time[0].boolean_expression) &\ b.at_time[5].boolean_expression.iff(b.at_time[0].boolean_expression) nusmv = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(loop_cond) s_manual = tests.canonical_cnf(manual) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_manual) self.assertEqual(s_tool, s_nusmv) # simple k-l loop 5 - 0 _,k,l = 0,5,2 loop_cond = ast.loop_condition(enc, k, l) manual = a.at_time[5].boolean_expression.iff(a.at_time[2].boolean_expression) &\ b.at_time[5].boolean_expression.iff(b.at_time[2].boolean_expression) nusmv = bmcutils.loop_condition(enc, k, l) s_tool = tests.canonical_cnf(loop_cond) s_manual = tests.canonical_cnf(manual) s_nusmv = tests.canonical_cnf(nusmv) self.assertEqual(s_tool, s_manual) self.assertEqual(s_tool, s_nusmv)
def test_loop_condition_consistent_values(self): # test case 3: only the state variables are considered load_from_string(""" MODULE main IVAR i : boolean; VAR v : boolean; w : boolean; ASSIGN init(v) := TRUE; next(v) := !v; """) with BmcSupport(): mdl = bmcutils.BmcModel() enc = mdl._fsm.encoding # loop and bound must be consistent (bound >= 0) with self.assertRaises(ValueError): bmcutils.loop_condition(enc, -5, 1) # loop and bound must be consistent (loop <= bound) with self.assertRaises(ValueError): bmcutils.loop_condition(enc, 2, 5)
def bounded_semantics_at_offset(fsm, formula, bound, offset, fairness=True): """ Generates the Be :math:`[[formula]]_{bound}` corresponding to the bounded semantic of `formula` but encodes it with an `offset` long shift in the timeline of the encoder. .. note:: This function plays the same role as `bounded_semantics_all_loops` but allows to position the time blocks at some place we like in the encoder timeline. This is mostly helpful if you want to devise verification methods that need to have multiple parallel verifications. (ie. diagnosability). Note however, that the two implementations are different. .. warning:: So far, the only supported temporal operators are F, G, U, R, X :param fsm: the BeFsm for which the property will be verified. Actually, it is only used to provide the encoder used to assign the variables to some time blocks. The api was kept this ways to keep uniformity with its non-offsetted counterpart. :param formula: the property for which to generate a verification problem represented in a 'node' format (subclass of :class:`pynusmv.node.Node`) which corresponds to the format obtained from the ast. (remark: if you need to manipulate [ie negate] the formula before passing it, it is perfectly valid to pass a node decorated by `Wff.decorate`). :param bound: the logical time bound to the problem. (Leave out the offset for this param: if you intend to have a problem with at most 10 steps, say bound=10) :param offset: the time offset in the encoding block where the sem of this formula will be generated. :param fairness: a flag indicating whether or not to take the fairness constraint into account. :return: a Be corresponding to the semantics of `formula` for a problem with a maximum of `bound` steps encoded to start at time `offset` in the `fsm` encoding timeline. """ if bound < 0: raise ValueError("Bound must be a positive integer") if offset < 0: raise ValueError("The offset must be a positive integer") enc = fsm.encoding straight = bounded_semantics_without_loop_at_offset( fsm, formula, 0, bound, offset) k_loop = Be.false(enc.manager) for i in range(bound): fairness_cond = utils.fairness_constraint(fsm, offset+bound, offset+i) \ if fairness \ else Be.true(enc.manager) k_loop |= ( utils.loop_condition(enc, offset+bound, offset+i) \ & fairness_cond \ & bounded_semantics_with_loop_at_offset(fsm, formula, 0, bound, i, offset)) # this is just the sem of the formula return straight | k_loop
def bounded_semantics_at_offset(fsm, formula, bound, offset, fairness=True): """ Generates the Be [[formula]]_{bound} corresponding to the bounded semantic of `formula` but encodes it with an `offset` long shift in the timeline of the encoder. .. note:: This function plays the same role as `bounded_semantics_all_loops` but allows to position the time blocks at some place we like in the encoder timeline. This is mostly helpful if you want to devise verification methods that need to have multiple parallel verifications. (ie. diagnosability). Note however, that the two implementations are different. .. warning:: So far, the only supported temporal operators are F, G, U, R, X :param fsm: the BeFsm for which the property will be verified. Actually, it is only used to provide the encoder used to assign the variables to some time blocks. The api was kept this ways to keep uniformity with its non-offsetted counterpart. :param formula: the property for which to generate a verification problem represented in a 'node' format (subclass of :see::class:`pynusmv.node.Node`) which corresponds to the format obtained from the ast. (remark: if you need to manipulate [ie negate] the formula before passing it, it is perfectly valid to pass a node decorated by `Wff.decorate`). :param bound: the logical time bound to the problem. (Leave out the offset for this param: if you intend to have a problem with at most 10 steps, say bound=10) :param offset: the time offset in the encoding block where the sem of this formula will be generated. :param fairness: a flag indicating whether or not to take the fairness constraint into account. :return: a Be corresponding to the semantics of `formula` for a problem with a maximum of `bound` steps encoded to start at time `offset` in the `fsm` encoding timeline. """ if bound< 0: raise ValueError("Bound must be a positive integer") if offset<0: raise ValueError("The offset must be a positive integer") enc = fsm.encoding straight = bounded_semantics_without_loop_at_offset(fsm, formula, 0, bound, offset) k_loop = Be.false(enc.manager) for i in range(bound): fairness_cond = utils.fairness_constraint(fsm, offset+bound, offset+i) \ if fairness \ else Be.true(enc.manager) k_loop |= ( utils.loop_condition(enc, offset+bound, offset+i) \ & fairness_cond \ & bounded_semantics_with_loop_at_offset(fsm, formula, 0, bound, i, offset)) # this is just the sem of the formula return straight | k_loop
def test_loop_condition_single_var(self): # test case 1: model with one single var load_from_string(""" MODULE main VAR v : boolean; ASSIGN init(v) := TRUE; next(v) := !v; """) with BmcSupport(): mdl = bmcutils.BmcModel() enc = mdl._fsm.encoding cond = bmcutils.loop_condition(enc, 3, 1) v3 = enc.by_name['v'].at_time[3].boolean_expression v1 = enc.by_name['v'].at_time[1].boolean_expression cond2 = v1.iff(v3) self.assertEqual(cond, cond2)
def test_globally(self): with Configure(self, __file__, "/models/flipflops.smv"): fsm = self.befsm formula = self.nnf("G (a <-> !b)") # bound 0 offset = 0 bound = 0 ref_expr= ltlspec.bounded_semantics(fsm, formula, bound) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr)) # bound 1 offset = 0 bound = 1 ref_expr= ltlspec.bounded_semantics(fsm, formula, bound) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr)) # ---- other offset ---- # bound 0 offset = 2 bound = 0 ref_expr= Be.false(self.enc.manager) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr)) # bound 1 offset = 2 bound = 1 ref_expr= ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, bound, offset) \ |( ltlspec.bounded_semantics_with_loop_at_offset(fsm, formula, 0, bound, 0, offset) \ & bmcutils.loop_condition(self.enc, bound+offset, offset)) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
def test_next(self): with Configure(self, __file__, "/models/flipflops.smv"): fsm = self.befsm formula = self.nnf("X (a <-> !b)") # bound 0 offset = 0 bound = 0 ref_expr= ltlspec.bounded_semantics(fsm, formula, bound) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset) self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr)) # bound 1 offset = 0 bound = 1 # done this way to avoid the depth 1 optimisation car = Wff.decorate(ltlspec.car(formula)).to_be(fsm.encoding) ref_expr= self.enc.shift_to_time(car, offset+1) \ | (self.enc.shift_to_time(car, offset) & bmcutils.loop_condition(self.enc, bound, 0)) expr = ltlspec.bounded_semantics_at_offset(fsm, formula, bound, offset)
def test_loop_condition_two_var(self): # test case 1: model with one more variables load_from_string(""" MODULE main VAR v : boolean; w : boolean; ASSIGN init(v) := TRUE; next(v) := !v; """) with BmcSupport(): mdl = bmcutils.BmcModel() enc = mdl._fsm.encoding cond = bmcutils.loop_condition(enc, 3, 1) v = enc.by_name['v'] w = enc.by_name['w'] cond2 = (v.at_time[1].boolean_expression.iff( v.at_time[3].boolean_expression) & w.at_time[1].boolean_expression.iff( w.at_time[3].boolean_expression)) self.assertEqual(cond, cond2)
def test_loop_condition_not_only_state(self): # test case 3: only the state variables are considered load_from_string(""" MODULE main IVAR i : boolean; VAR v : boolean; w : boolean; ASSIGN init(v) := TRUE; next(v) := !v; """) with BmcSupport(): mdl = bmcutils.BmcModel() enc = mdl._fsm.encoding cond = bmcutils.loop_condition(enc, 3, 1) v = enc.by_name['v'] w = enc.by_name['w'] cond2 = (v.at_time[1].boolean_expression.iff( v.at_time[3].boolean_expression) & w.at_time[1].boolean_expression.iff( w.at_time[3].boolean_expression)) self.assertEqual(cond, cond2)