def test_verify_exactly(self): theta = Node.from_ptr(parse_simple_expression("TRUE")) theta = bmcutils.make_nnf_boolean_wff(theta) sigma_12= Node.from_ptr(parse_ltl_spec("TRUE")) sigma_12= bmcutils.make_nnf_boolean_wff(sigma_12).to_node() obs_names = ["mouse"] obs_vars = diagnosability.mk_observable_vars(obs_names) f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = inactive")) for i in range(5): res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), i, theta, sigma_12, sigma_12) self.assertEqual("No Violation", res) f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = highlight")) res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), 0, theta, sigma_12, sigma_12) self.assertEqual("No Violation", res) res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), 1, theta, sigma_12, sigma_12) self.assertTrue(res.startswith("############### DIAGNOSABILITY VIOLATION")) res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), 2, theta, sigma_12, sigma_12) self.assertTrue(res.startswith("############### DIAGNOSABILITY VIOLATION")) res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), 3, theta, sigma_12, sigma_12) self.assertTrue(res.startswith("############### DIAGNOSABILITY VIOLATION"))
def test_generate_sat_problem(self): theta = Node.from_ptr(parse_simple_expression("TRUE")) theta = bmcutils.make_nnf_boolean_wff(theta) sigma_12 = Node.from_ptr(parse_ltl_spec("TRUE")) sigma_12 = bmcutils.make_nnf_boolean_wff(sigma_12).to_node() observable = diagnosability.mk_observable_vars(["mouse"]) f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = inactive")) for i in range(5): problem = diagnosability.generate_sat_problem( observable, (f1, f2), i, theta, sigma_12, sigma_12) solver = SatSolverFactory.create() cnf = problem.to_cnf() solver += cnf solver.polarity(cnf, Polarity.POSITIVE) self.assertEqual(SatSolverResult.UNSATISFIABLE, solver.solve()) f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = highlight")) for i in range(1, 4): # length zero has no input => only an initial state and the # diagnosability condition is not checked problem = diagnosability.generate_sat_problem( observable, (f1, f2), i, theta, sigma_12, sigma_12) solver = SatSolverFactory.create() cnf = problem.to_cnf() solver += cnf solver.polarity(cnf, Polarity.POSITIVE) self.assertEqual(SatSolverResult.SATISFIABLE, solver.solve())
def test_generate_sat_problem(self): theta = Node.from_ptr(parse_simple_expression("TRUE")) theta = bmcutils.make_nnf_boolean_wff(theta) sigma_12= Node.from_ptr(parse_ltl_spec("TRUE")) sigma_12= bmcutils.make_nnf_boolean_wff(sigma_12).to_node() observable = diagnosability.mk_observable_vars(["mouse"]) f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = inactive")) for i in range(5): problem = diagnosability.generate_sat_problem(observable, (f1, f2), i, theta, sigma_12, sigma_12) solver = SatSolverFactory.create() cnf = problem.to_cnf() solver += cnf solver.polarity(cnf, Polarity.POSITIVE) self.assertEqual(SatSolverResult.UNSATISFIABLE, solver.solve()) f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = highlight")) for i in range(1, 4): # length zero has no input => only an initial state and the # diagnosability condition is not checked problem = diagnosability.generate_sat_problem(observable, (f1, f2), i, theta, sigma_12, sigma_12) solver = SatSolverFactory.create() cnf = problem.to_cnf() solver += cnf solver.polarity(cnf, Polarity.POSITIVE) self.assertEqual(SatSolverResult.SATISFIABLE, solver.solve())
def test_constraint_context_sigma(self): fsm = master_be_fsm() _true = Node.from_ptr(parse_ltl_spec("TRUE")) _true = bmcutils.make_nnf_boolean_wff(_true) _truen= _true.to_node() cond = Wff(parse_ltl_spec("G !(mouse = hover)"))\ .to_boolean_wff()\ .to_negation_normal_form() off_1 = 0 off_2 = 2 length= 1 # sigma1 problem = diagnosability.generate_sat_problem([], (_truen, _truen), length, _true, cond.to_node(), _truen) tm_cond = ltlspec.bounded_semantics_at_offset(fsm, cond.to_node(), length, off_1) canonical_p = tests.canonical_cnf(problem) canonical_f = tests.canonical_cnf(tm_cond) self.assertTrue(all(clause in canonical_p for clause in canonical_f)) # sigma2 problem = diagnosability.generate_sat_problem([], (_truen, _truen), length, _true, _truen, cond.to_node()) tm_cond = ltlspec.bounded_semantics_at_offset(fsm, cond.to_node(), length, off_2) canonical_p = tests.canonical_cnf(problem) canonical_f = tests.canonical_cnf(tm_cond) self.assertTrue(all(clause in canonical_p for clause in canonical_f))
def test_generate_invar_problem(self): # PROBLEM = BASE STEP & INDUCTION for prop in prop_database(): expr = utils.make_nnf_boolean_wff(prop.expr) problem = invarspec.generate_invar_problem(self.fsm, expr) manual = (invarspec.generate_base_step(self.fsm, expr) & invarspec.generate_inductive_step(self.fsm, expr)) self.assertEqual(problem.to_cnf().vars_list, manual.to_cnf().vars_list)
def test_verify_exactly(self): theta = Node.from_ptr(parse_simple_expression("TRUE")) theta = bmcutils.make_nnf_boolean_wff(theta) sigma_12 = Node.from_ptr(parse_ltl_spec("TRUE")) sigma_12 = bmcutils.make_nnf_boolean_wff(sigma_12).to_node() obs_names = ["mouse"] obs_vars = diagnosability.mk_observable_vars(obs_names) f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = inactive")) for i in range(5): res = diagnosability.verify_for_size_exactly_k( obs_names, obs_vars, (f1, f2), i, theta, sigma_12, sigma_12) self.assertEqual("No Violation", res) f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = highlight")) res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), 0, theta, sigma_12, sigma_12) self.assertEqual("No Violation", res) res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), 1, theta, sigma_12, sigma_12) self.assertTrue( res.startswith("############### DIAGNOSABILITY VIOLATION")) res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), 2, theta, sigma_12, sigma_12) self.assertTrue( res.startswith("############### DIAGNOSABILITY VIOLATION")) res = diagnosability.verify_for_size_exactly_k(obs_names, obs_vars, (f1, f2), 3, theta, sigma_12, sigma_12) self.assertTrue( res.startswith("############### DIAGNOSABILITY VIOLATION"))
def constraint_eventually_critical_pair(formula_nodes, offset_path1, offset_path2, length): """ Generates a boolean expression representing the critical pair condition. That is to say, it generates a condition that verifies if it is possible that the two belief states are inconsistent wrt `formula`. :param formula_nodes: the formula whose diagnosability is verified. :param offset_path1: the offset at which path 1 is supposed to start (should be 0) :param offset_path2: the offset at which path 2 is supposed to start (must not intersect with path1) :param length: the length of the path :return: an expression describing the 'critical pair' condition. """ enc = master_be_fsm().encoding c1 = make_nnf_boolean_wff(formula_nodes[0]).to_be(enc) c2 = make_nnf_boolean_wff(formula_nodes[1]).to_be(enc) constraint = Be.false(enc.manager) for time_ in range(length + 1): constraint |= enc.shift_to_time(c1, time_ + offset_path1) & enc.shift_to_time(c2, time_ + offset_path2) return constraint
def constraint_eventually_critical_pair(formula_nodes, offset_path1, offset_path2, length): """ Generates a boolean expression representing the critical pair condition. That is to say, it generates a condition that verifies if it is possible that the two belief states are inconsistent wrt `formula`. :param formula_nodes: the formula whose diagnosability is verified. :param offset_path1: the offset at which path 1 is supposed to start (should be 0) :param offset_path2: the offset at which path 2 is supposed to start (must not intersect with path1) :param length: the length of the path :return: an expression describing the 'critical pair' condition. """ enc = master_be_fsm().encoding c1 = make_nnf_boolean_wff(formula_nodes[0]).to_be(enc) c2 = make_nnf_boolean_wff(formula_nodes[1]).to_be(enc) constraint = Be.false(enc.manager) for time_ in range(length+1): constraint |= ( enc.shift_to_time(c1, time_ + offset_path1) & enc.shift_to_time(c2 , time_ + offset_path2) ) return constraint
def bounded_semantics_all_loops(fsm, prop_node, bound, loop, optimized=True): """ Generates a Be expression corresponding to the bounded semantics of the given LTL formula in the case where the formula is evaluated against a path that contains a loop at any of the positions in the range [loop; bound] In the literature, the resulting formula would be denoted as .. math:: \\bigvee_{j=l}^{k} {}_{j}L_{k} \\wedge {}_{j}[[f]]_{k}^{0} where l is used to denote `loop`, f for `prop_node` and k for the `bound`. .. note:: Fairness is taken into account in the generation of the resulting expression :param fsm: the fsm against which the formula will be evaluated. It is not directly relevant to the generation of the formula for `prop_node` but is used to determine to generate fairness constraints for this model which are combined with `prop_node` constraint. :param prop_node: 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 bound of the problem, that is to say the maximum number of times the problem will be unrolled. This parameter corresponds to the value `k` used in the formal definitions of a bmc problem. :param optimized: a flag indicating whether or not the use of the optimisation for formulas of depth 1 is desired. :return: a boolean expression corresponding to the bounded semantics of `prop_node` in the case where there is may be a loop anywhere on the path between the positions `loop` and `bound` :raises ValueError: when the bound is infeasible (negative value) or when the loop and bound values are inconsistent (loop is greater than the bound but none of the special values described above) """ utils.check_consistency(bound, loop) ltl_wff = utils.make_nnf_boolean_wff(prop_node) if optimized and ltl_wff.depth == 1 and len(fsm.fairness_list) == 0: return bounded_semantics_all_loops_optimisation_depth1( fsm, prop_node, bound) else: be_ptr = _bmc.Bmc_Tableau_GetAllLoops(fsm._ptr, ltl_wff._ptr, bound, loop) return Be(be_ptr, fsm.encoding.manager)
def test_eventually_critical_pair(self): enc = master_be_fsm().encoding f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = highlight")) constraint = diagnosability.constraint_eventually_critical_pair( (f1, f2), 0, 5, 5) nnf1 = bmcutils.make_nnf_boolean_wff(f1).to_be(enc) nnf2 = bmcutils.make_nnf_boolean_wff(f2).to_be(enc) manual = Be.false(enc.manager) for i in range(6): # again, from 0 to 5 manual |= (enc.shift_to_time(nnf1, i) & enc.shift_to_time(nnf2, 5 + i)) # observing the clauses generated in both cases, one observes that # the generated clauses are the same except that the number of the cnf # literals do not match, example: # [-59, -24, 58] # [-65, -24, 64] # This is due to the fact that some 'fresh' cnf literals are used in the # generation of the epxression. Therefore, a comparison (even on the # canonical form of the CNF) is not feasible. # # Satisfiability is just an indication but at least that is .. something solver_c = SatSolverFactory.create() cnf = constraint.to_cnf() solver_c += cnf solver_c.polarity(cnf, Polarity.POSITIVE) result_c = solver_c.solve() solver_m = SatSolverFactory.create() cnf = manual.to_cnf() solver_m += cnf solver_m.polarity(cnf, Polarity.POSITIVE) result_m = solver_m.solve() self.assertEqual(result_c, result_m)
def test_generate_base_step(self): # BASE STEP = (I0 -> P0 ) for prop in prop_database(): expr = utils.make_nnf_boolean_wff(prop.expr) gen = invarspec.generate_base_step(self.fsm, expr) # model = BmcModel() i0 = model.init[0] & model.invar[0] # recall: the prop has to be shifted to time 0 p0 = self.fsm.encoding.shift_to_time(expr.to_be(self.fsm.encoding), 0) manual= i0.imply(p0) self.assertEqual(gen.to_cnf().vars_list, manual.to_cnf().vars_list)
def test_eventually_critical_pair(self): enc= master_be_fsm().encoding f1 = Node.from_ptr(parse_simple_expression("status = active")) f2 = Node.from_ptr(parse_simple_expression("status = highlight")) constraint = diagnosability.constraint_eventually_critical_pair((f1, f2), 0, 5, 5) nnf1 = bmcutils.make_nnf_boolean_wff(f1).to_be(enc) nnf2 = bmcutils.make_nnf_boolean_wff(f2).to_be(enc) manual = Be.false(enc.manager) for i in range(6): # again, from 0 to 5 manual |= ( enc.shift_to_time(nnf1 , i) & enc.shift_to_time(nnf2 , 5+i)) # observing the clauses generated in both cases, one observes that # the generated clauses are the same except that the number of the cnf # literals do not match, example: # [-59, -24, 58] # [-65, -24, 64] # This is due to the fact that some 'fresh' cnf literals are used in the # generation of the epxression. Therefore, a comparison (even on the # canonical form of the CNF) is not feasible. # # Satisfiability is just an indication but at least that is .. something solver_c = SatSolverFactory.create() cnf = constraint.to_cnf() solver_c+= cnf solver_c.polarity(cnf, Polarity.POSITIVE) result_c = solver_c.solve() solver_m = SatSolverFactory.create() cnf = manual.to_cnf() solver_m+= cnf solver_m.polarity(cnf, Polarity.POSITIVE) result_m = solver_m.solve() self.assertEqual(result_c, result_m)
def test_generate_base_step(self): # BASE STEP = (I0 -> P0 ) for prop in prop_database(): expr = utils.make_nnf_boolean_wff(prop.expr) gen = invarspec.generate_base_step(self.fsm, expr) # model = BmcModel() i0 = model.init[0] & model.invar[0] # recall: the prop has to be shifted to time 0 p0 = self.fsm.encoding.shift_to_time(expr.to_be(self.fsm.encoding), 0) manual = i0.imply(p0) self.assertEqual(gen.to_cnf().vars_list, manual.to_cnf().vars_list)
def test_make_nnf_boolean_wff(self): load_from_string(""" MODULE main VAR v : boolean; w : boolean; ASSIGN init(v) := TRUE; next(v) := !v; """) with BmcSupport(): expr = Node.from_ptr(parse_ltl_spec("F G ( w <-> v )")) wff = bmcutils.make_nnf_boolean_wff(expr) self.assertEquals(" F ( G (w <-> v))", str(expr)) self.assertEquals(" F ( G ((!v | w) & (v | !w)))", str(wff)) self.assertEquals(Wff, type(wff))
def check(args, condition_text, observable): """ Performs the verification of the diagnosability of the condition represented by `condition_text` and print its result to stdout. :param args: the arguments that were given on the command line :param condition_text: a string representing the diagnosability condition to be verified in the format 'c1 ; c2'. :param observable: the set of symbols considered observable in the context of this diagnosability test """ try: observable_vars = mk_observable_vars(observable) diagnosability_condition = mk_specs_nodes(condition_text) theta = Node.from_ptr(parse_simple_expression(args.initial_condition)) theta = make_nnf_boolean_wff(theta) sigma1 = Node.from_ptr(parse_ltl_spec(args.sigma1)) sigma1 = make_nnf_boolean_wff(sigma1).to_node() sigma2 = Node.from_ptr(parse_ltl_spec(args.sigma2)) sigma2 = make_nnf_boolean_wff(sigma2).to_node() for k in range(args.bound + 1): result = verify_for_size_exactly_k( observable, observable_vars, diagnosability_condition, k, theta, sigma1, sigma2 ) if "No Violation" != str(result): print("-- {} is *NOT* diagnosable for length {}".format(diagnosability_condition, k)) print(result) return print("-- No counter example found for executions of length <= {}".format(k)) except Exception as e: print("The specified condition contains a syntax error") print(e)
def check(args, condition_text, observable): """ Performs the verification of the diagnosability of the condition represented by `condition_text` and print its result to stdout. :param args: the arguments that were given on the command line :param condition_text: a string representing the diagnosability condition to be verified in the format 'c1 ; c2'. :param observable: the set of symbols considered observable in the context of this diagnosability test """ try: observable_vars = mk_observable_vars(observable) diagnosability_condition = mk_specs_nodes(condition_text) theta = Node.from_ptr(parse_simple_expression(args.initial_condition)) theta = make_nnf_boolean_wff(theta) sigma1= Node.from_ptr(parse_ltl_spec(args.sigma1)) sigma1= make_nnf_boolean_wff(sigma1).to_node() sigma2= Node.from_ptr(parse_ltl_spec(args.sigma2)) sigma2= make_nnf_boolean_wff(sigma2).to_node() for k in range(args.bound+1): result = verify_for_size_exactly_k(observable, observable_vars, diagnosability_condition, k, theta, sigma1, sigma2) if "No Violation" != str(result): print("-- {} is *NOT* diagnosable for length {}".format(diagnosability_condition, k)) print(result) return else: print("-- No counter example at length {}".format(k)) print("-- No counter example found for executions of length <= {}".format(args.bound)) except Exception as e: print("The specified condition contains a syntax error") print(e)
def test_generate_inductive_step(self): # INDUCT = (P0 and R01) -> P1 for prop in prop_database(): expr = utils.make_nnf_boolean_wff(prop.expr) gen = invarspec.generate_inductive_step(self.fsm, expr) # model = BmcModel() r01 = model.unrolling(0, 1) # recall: the prop has to be shifted to time 0 p = expr.to_be(self.fsm.encoding) p0 = self.fsm.encoding.shift_to_time(p, 0) p1 = self.fsm.encoding.shift_to_time(p, 1) manual= (p0 & r01).imply(p1) self.assertEqual(gen.to_cnf().vars_list, manual.to_cnf().vars_list)
def test_generate_inductive_step(self): # INDUCT = (P0 and R01) -> P1 for prop in prop_database(): expr = utils.make_nnf_boolean_wff(prop.expr) gen = invarspec.generate_inductive_step(self.fsm, expr) # model = BmcModel() r01 = model.unrolling(0, 1) # recall: the prop has to be shifted to time 0 p = expr.to_be(self.fsm.encoding) p0 = self.fsm.encoding.shift_to_time(p, 0) p1 = self.fsm.encoding.shift_to_time(p, 1) manual = (p0 & r01).imply(p1) self.assertEqual(gen.to_cnf().vars_list, manual.to_cnf().vars_list)
def bounded_semantics_all_loops(fsm, prop_node, bound, loop, optimized=True): """ Generates a Be expression corresponding to the bounded semantics of the given LTL formula in the case where the formula is evaluated against a path that contains a loop at any of the positions in the range [loop; bound] In the literature, the resulting formula would be denoted as .. math:: \bigvee_{j=l}^{k} _{j}L_{k} \wedge _{j}[[f]]_{k}^{0} where l is used to denote `loop`, f for `prop_node` and k for the `bound`. .. note:: Fairness is taken into account in the generation of the resulting expression :param fsm: the fsm against which the formula will be evaluated. It is not directly relevant to the generation of the formula for `prop_node` but is used to determine to generate fairness constraints for this model which are combined with `prop_node` constraint. :param prop_node: 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 bound of the problem, that is to say the maximum number of times the problem will be unrolled. This parameter corresponds to the value `k` used in the formal definitions of a bmc problem. :param optimized: a flag indicating whether or not the use of the optimisation for formulas of depth 1 is desired. :return: a boolean expression corresponding to the bounded semantics of `prop_node` in the case where there is may be a loop anywhere on the path between the positions `loop` and `bound` :raises ValueError: when the bound is infeasible (negative value) or when the loop and bound values are inconsistent (loop is greater than the bound but none of the special values described above) """ utils.check_consistency(bound, loop) ltl_wff= utils.make_nnf_boolean_wff(prop_node) if optimized and ltl_wff.depth == 1 and len(fsm.fairness_list) == 0: return bounded_semantics_all_loops_optimisation_depth1(fsm, prop_node, bound) else: be_ptr = _bmc.Bmc_Tableau_GetAllLoops(fsm._ptr, ltl_wff._ptr, bound, loop) return Be(be_ptr, fsm.encoding.manager)
def bounded_semantics_without_loop(fsm, prop_node, bound): """ Generates a Be expression corresponding to the bounded semantics of the given LTL formula in the case where the formula is evaluated against paths that contain no loop and have a maximal length of `bound`. .. note:: This function proves to be very useful since the bounded semantics of LTL depends on two cases: (a) when the encountered path contains loops (in that case the unbounded semantics of LTL can be maintained since there exists infinite paths) and (b) the case where there are no possible loops (and the semantics has to be altered slightly). In the literature, the expression generated by this function is denoted .. math:: [[f]]^{0}_{k} With f used to represent the formula `prop_node`, and k for `bound` .. note:: Fairness is taken into account in the generation of the resulting expression :param fsm: the fsm against which the formula will be evaluated. It is not directly relevant to the generation of the formula for `prop_node` but is used to determine to generate fairness constraints for this model which are combined with `prop_node` constraint. :param prop_node: 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 bound of the problem, that is to say the maximum number of times the problem will be unrolled. This parameter corresponds to the value `k` used in the formal definitions of a bmc problem. :return: a boolean expression corresponding to the bounded semantics of `prop_node` in the case where there is no loop on the path. :raises ValueError: when the specified problem bound is negative """ if bound < 0: raise ValueError("The problem bound may not be negative") ltl_wff = utils.make_nnf_boolean_wff(prop_node) be_ptr = _bmc.Bmc_Tableau_GetNoLoop(fsm._ptr, ltl_wff._ptr, bound) return Be(be_ptr, fsm.encoding.manager)
def bounded_semantics_without_loop(fsm, prop_node, bound): """ Generates a Be expression corresponding to the bounded semantics of the given LTL formula in the case where the formula is evaluated against paths that contain no loop and have a maximal length of `bound`. .. note:: This function proves to be very useful since the bounded semantics of LTL depends on two cases: (a) when the encountered path contains loops (in that case the unbounded semantics of LTL can be maintained since there exists infinite paths) and (b) the case where there are no possible loops (and the semantics has to be altered slightly). In the literature, the expression generated by this function is denoted :math:`[[f]]^{0}_{k}` With f used to represent the formula `prop_node`, and k for `bound` .. note:: Fairness is taken into account in the generation of the resulting expression :param fsm: the fsm against which the formula will be evaluated. It is not directly relevant to the generation of the formula for `prop_node` but is used to determine to generate fairness constraints for this model which are combined with `prop_node` constraint. :param prop_node: 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 bound of the problem, that is to say the maximum number of times the problem will be unrolled. This parameter corresponds to the value `k` used in the formal definitions of a bmc problem. :return: a boolean expression corresponding to the bounded semantics of `prop_node` in the case where there is no loop on the path. :raises ValueError: when the specified problem bound is negative """ if bound < 0: raise ValueError("The problem bound may not be negative") ltl_wff = utils.make_nnf_boolean_wff(prop_node) be_ptr = _bmc.Bmc_Tableau_GetNoLoop(fsm._ptr, ltl_wff._ptr, bound) return Be(be_ptr, fsm.encoding.manager)
def bounded_semantics_single_loop(fsm, prop_node, bound, loop): """ Generates a Be expression corresponding to the bounded semantics of the given LTL formula in the case where the formula is evaluated against a path that contains one single loop starting at position `loop`. In the literature, the resulting formula would be denoted as .. math:: _{l}L_{k} \wedge _{l}[[f]]_{k}^{0} where l is used to denote `loop`, f for `prop_node` and k for the `bound`. In other words, the generated boolean expression is the conjunction of the constraint imposing that there be a k-l loop from `bound` to `loop` and that the formula is evaluated at time 0 out of `bound`. .. note:: Fairness is taken into account in the generation of the resulting expression :param fsm: the fsm against which the formula will be evaluated. It is not directly relevant to the generation of the formula for `prop_node` but is used to determine to generate fairness constraints for this model which are combined with `prop_node` constraint. :param prop_node: 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 bound of the problem, that is to say the maximum number of times the problem will be unrolled. This parameter corresponds to the value `k` used in the formal definitions of a bmc problem. :return: a boolean expression corresponding to the bounded semantics of `prop_node` in the case where there is a loop from bound to loop :raises ValueError: when the bound is infeasible (negative value) or when the loop and bound values are inconsistent (loop is greater than the bound but none of the special values described above) """ utils.check_consistency(bound, loop) ltl_wff= utils.make_nnf_boolean_wff(prop_node) be_ptr = _bmc.Bmc_Tableau_GetSingleLoop(fsm._ptr, ltl_wff._ptr, bound, loop) return Be(be_ptr, fsm.encoding.manager)
def bounded_semantics_single_loop(fsm, prop_node, bound, loop): """ Generates a Be expression corresponding to the bounded semantics of the given LTL formula in the case where the formula is evaluated against a path that contains one single loop starting at position `loop`. In the literature, the resulting formula would be denoted as :math:`{}_{l}L_{k} \\wedge {}_{l}[[f]]_{k}^{0}` where l is used to denote `loop`, f for `prop_node` and k for the `bound`. In other words, the generated boolean expression is the conjunction of the constraint imposing that there be a k-l loop from `bound` to `loop` and that the formula is evaluated at time 0 out of `bound`. .. note:: Fairness is taken into account in the generation of the resulting expression :param fsm: the fsm against which the formula will be evaluated. It is not directly relevant to the generation of the formula for `prop_node` but is used to determine to generate fairness constraints for this model which are combined with `prop_node` constraint. :param prop_node: 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 bound of the problem, that is to say the maximum number of times the problem will be unrolled. This parameter corresponds to the value `k` used in the formal definitions of a bmc problem. :return: a boolean expression corresponding to the bounded semantics of `prop_node` in the case where there is a loop from bound to loop :raises ValueError: when the bound is infeasible (negative value) or when the loop and bound values are inconsistent (loop is greater than the bound but none of the special values described above) """ utils.check_consistency(bound, loop) ltl_wff = utils.make_nnf_boolean_wff(prop_node) be_ptr = _bmc.Bmc_Tableau_GetSingleLoop(fsm._ptr, ltl_wff._ptr, bound, loop) return Be(be_ptr, fsm.encoding.manager)
def bounded_semantics_all_loops_optimisation_depth1(fsm, prop_node, bound): """ Generates a Be expression corresponding to the bounded semantics of the given LTL formula in the case where the formula is evaluated against a path that contains a loop at any of the positions in the range [0; bound] and *the 'depth'(:see:`Wff.depth`) of the formula is 1 and no fairness constraint comes into play*. .. note:: Unless you know precisely why you are using this function, it is probably safer to just use bounded_semantics_all_loops with the optimized flag turned on. This optimized generation scheme was proposed by R. Sebastiani in :param fsm: the fsm against which the formula will be evaluated. It is not directly relevant to the generation of the formula for `prop_node` but is used to determine to generate fairness constraints for this model which are combined with `prop_node` constraint. :param prop_node: 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 bound of the problem, that is to say the maximum number of times the problem will be unrolled. This parameter corresponds to the value `k` used in the formal definitions of a bmc problem. :return: a boolean expression corresponding to the bounded semantics of `prop_node` in the case where there is may be a loop anywhere on the path between the positions `loop` and `bound` and the formula has a depth of exactly one. :raises ValueError: when the specified propblem bound is negative """ if bound < 0: raise ValueError("The problem bound may not be negative") ltl_wff= utils.make_nnf_boolean_wff(prop_node) be_ptr = _bmc.Bmc_Tableau_GetAllLoopsDepth1(fsm._ptr, ltl_wff._ptr, bound) return Be(be_ptr, fsm.encoding.manager)
def bounded_semantics_all_loops_optimisation_depth1(fsm, prop_node, bound): """ Generates a Be expression corresponding to the bounded semantics of the given LTL formula in the case where the formula is evaluated against a path that contains a loop at any of the positions in the range [0; bound] and *the 'depth'(:attr:`pynusmv.wff.Wff.depth`) of the formula is 1 and no fairness constraint comes into play*. .. note:: Unless you know precisely why you are using this function, it is probably safer to just use bounded_semantics_all_loops with the optimized flag turned on. :param fsm: the fsm against which the formula will be evaluated. It is not directly relevant to the generation of the formula for `prop_node` but is used to determine to generate fairness constraints for this model which are combined with `prop_node` constraint. :param prop_node: 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 bound of the problem, that is to say the maximum number of times the problem will be unrolled. This parameter corresponds to the value `k` used in the formal definitions of a bmc problem. :return: a boolean expression corresponding to the bounded semantics of `prop_node` in the case where there is may be a loop anywhere on the path between the positions `loop` and `bound` and the formula has a depth of exactly one. :raises ValueError: when the specified propblem bound is negative """ if bound < 0: raise ValueError("The problem bound may not be negative") ltl_wff = utils.make_nnf_boolean_wff(prop_node) be_ptr = _bmc.Bmc_Tableau_GetAllLoopsDepth1(fsm._ptr, ltl_wff._ptr, bound) return Be(be_ptr, fsm.encoding.manager)
def test_constraint_context_sigma(self): fsm = master_be_fsm() _true = Node.from_ptr(parse_ltl_spec("TRUE")) _true = bmcutils.make_nnf_boolean_wff(_true) _truen = _true.to_node() cond = Wff(parse_ltl_spec("G !(mouse = hover)"))\ .to_boolean_wff()\ .to_negation_normal_form() off_1 = 0 off_2 = 2 length = 1 # sigma1 problem = diagnosability.generate_sat_problem([], (_truen, _truen), length, _true, cond.to_node(), _truen) tm_cond = ltlspec.bounded_semantics_at_offset(fsm, cond.to_node(), length, off_1) canonical_p = tests.canonical_cnf(problem) canonical_f = tests.canonical_cnf(tm_cond) self.assertTrue(all(clause in canonical_p for clause in canonical_f)) # sigma2 problem = diagnosability.generate_sat_problem([], (_truen, _truen), length, _true, _truen, cond.to_node()) tm_cond = ltlspec.bounded_semantics_at_offset(fsm, cond.to_node(), length, off_2) canonical_p = tests.canonical_cnf(problem) canonical_f = tests.canonical_cnf(tm_cond) self.assertTrue(all(clause in canonical_p for clause in canonical_f))
def test_dump_dimacs_filename(self): for prop in prop_database(): expr = utils.make_nnf_boolean_wff(prop.expr) problem = invarspec.generate_invar_problem(self.fsm, expr) invarspec.dump_dimacs_filename(self.fsm.encoding, problem.to_cnf(), "testit")