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_language_contains(self): with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() trace = Trace.create( "Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True, ) v = be_fsm.encoding.by_name["v"] w = be_fsm.encoding.by_name["w"] f = be_fsm.encoding.by_name["f"] i = be_fsm.encoding.by_name["i"] self.assertTrue(v.name in trace) self.assertTrue(w.name in trace) self.assertTrue(f.name in trace) self.assertTrue(i.name in trace) x = parse_simple_expression("x") self.assertFalse(Node.from_ptr(x) in trace)
def generate_sat_problem(fsm, fml, length): fsm = master_be_fsm() offset = 0 wff = bmcutils.make_negated_nnf_boolean_wff(fml).to_node() problem = generate_path(offset, length) \ & ltlspec.bounded_semantics_at_offset(fsm, wff, length, offset) return problem
def test_iter(self): """tests the behavior of assign and value""" with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() # empty trace trace = Trace.create( "Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True, ) step1 = trace.steps[1] yes = Node.from_ptr(parse_simple_expression("TRUE")) no = Node.from_ptr(parse_simple_expression("FALSE")) v = be_fsm.encoding.by_name["v"].name self.assertEqual([], list(step1)) step1 += v, yes self.assertEqual([(v, yes)], list(step1)) # += really ASSIGNS a value, not append step1 += v, no self.assertEqual([(v, no)], list(step1))
def test_assign_value__magicmethod__(self): """tests the behavior of assign and value""" with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() # empty trace trace = Trace.create( "Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True, ) step1 = trace.steps[1] yes = Node.from_ptr(parse_simple_expression("TRUE")) no = Node.from_ptr(parse_simple_expression("FALSE")) v = be_fsm.encoding.by_name["v"].name self.assertIsNone(step1.value[v]) step1 += (v, yes) self.assertEqual(yes, step1.value[v]) step1 += (v, no) self.assertEqual(no, step1.value[v])
def test_constraint_context_theta(self): enc = master_be_fsm().encoding cond = Wff(parse_simple_expression("mouse = down")).to_boolean_wff() theta = diagnosability.constraint_context_theta_initial(cond, 0, 1) manual= enc.shift_to_time(cond.to_be(enc), 0) \ & enc.shift_to_time(cond.to_be(enc), 1) \ self.assertEqual(theta, manual)
def test_mk_observable_vars(self): enc = master_be_fsm().encoding # if a non existing var name is passed, an exception is thrown with self.assertRaises(ValueError): diagnosability.mk_observable_vars(["a"]) observable = diagnosability.mk_observable_vars(["status"]) self.assertEqual(observable, [enc.by_name["status.1"], enc.by_name["status.0"]])
def __enter__(self): """Performs what one would usually do in setUp""" init_nusmv() load_from_file(self.model) go_bmc() self.testcase.sexpfsm = master_bool_sexp_fsm() self.testcase.befsm = master_be_fsm() self.testcase.enc = self.testcase.befsm.encoding self.testcase.mgr = self.testcase.enc.manager
def setUp(self): init_nusmv() load_from_file(tests.current_directory(__file__)+"/models/flipflops_vif.smv") go_bmc() self.enc = master_be_fsm().encoding self.v = self.enc.by_name['v'] self.f = self.enc.by_name['f'] self.i = self.enc.by_name['i'] self.n = self.enc.by_name['next(v)']
def test_master_be_fsm(self): load(self.model()) flatten_hierarchy() encode_variables() build_boolean_model() bmcglob.bmc_setup() # may not provoke C assert failures bmcglob.build_master_be_fsm() self.assertEqual(bmcglob.master_be_fsm(), BeFsm.global_master_instance()) bmcglob.bmc_exit()
def check_problem(pb, length): fsm = master_be_fsm() cnf = pb.to_cnf(Polarity.POSITIVE) solver = SatSolverFactory.create() solver+= cnf solver.polarity(cnf, Polarity.POSITIVE) if solver.solve() == SatSolverResult.SATISFIABLE: cnt_ex = bmcutils.generate_counter_example(fsm, pb, solver, length, "Violation") return ("Violation", cnt_ex) else: return ("Ok", None)
def mk_cnf_with_formula(formula, bound): ''' :return: a `BeCnf` expression representing the verification of `formula` on the loaded model for a `bound` time steps ''' import pynusmv.node as _node import pynusmv.parser as _parser import pynusmv.bmc.glob as _bmc import pynusmv.bmc.ltlspec as _ltlspec prop = _node.Node.from_ptr( _parser.parse_ltl_spec(formula) ) fsm = _bmc.master_be_fsm() problem = _ltlspec.generate_ltl_problem(fsm, prop, bound).to_cnf() return problem
def test_dump_problem(self): load_from_string(""" MODULE main VAR v : boolean; w : boolean; ASSIGN init(v) := TRUE; next(v) := !v; LTLSPEC F G ( w <-> v ) """) with BmcSupport(): fsm = master_be_fsm() for prop in prop_database(): pb = generate_ltl_problem(fsm, prop.expr) bmcutils.dump_problem(fsm.encoding, pb.to_cnf(), prop, 10, 0, bmcutils.DumpType.DIMACS, "dimacs_dump")
def booleanize(name): """ Returns the list of boolean variables names standing for the symbol identified by `name` in the compiled boolean model. .. note:: Obviously, if `name` denotes a boolean variable, the `name` symbol is returned. :param name: the name of the symbol whose boolean representatives are wanted :return: a list of variable names (in Node format) that are used to represent `name` in the encoded boolean model. :raises ValueError: when the requested name cannot be found in the symbol table """ symbol = get_symbol(name) return bmcglob.master_be_fsm().encoding.encode_to_bits(symbol)
def check_ltl(fml, bound, dry_run): fsm = master_be_fsm() import time for i in range(bound+1): start = time.time() problem = generate_sat_problem(fsm, fml, i) end = time.time() if not dry_run: status, trace = check_problem(problem, i) if status != "Ok": return (status, i, trace) else: print("-- No problem at length {}".format(i)) else: print(" 'Problem {}' ; {}".format(i, end-start)) return ("Ok", bound, None)
def mk_observable_vars(var_names): """ Creates the list of the variables (BeVar) that are considered observable in the context of this diagnosability test. :param var_names: a list of string containing the names of the vars that should be visible :return: a list of untimed boolean expressions standing for the various bits of the observable variables. """ if not var_names: return [] else: # otherwise use the user specified set of actions encoding = master_be_fsm().encoding boolean_varnames = reduce(lambda x,y: x+y, map(booleanize, var_names), []) be_variables = list(map(lambda x: encoding.by_name[str(x)],boolean_varnames)) return be_variables
def check_ltl(fml, bound, dry_run): import time fsm = master_be_fsm() for i in range(bound + 1): start = time.time() problem = ltlspec.generate_ltl_problem(fsm, fml, i) end = time.time() if not dry_run: status, trace = check_problem(problem, i) if status != "Ok": return (status, i, trace) else: print("-- No problem at length {}".format(i)) else: print(" 'Problem {}' ; {}".format(i, end - start)) return ("Ok", bound, None)
def mk_observable_vars(var_names): """ Creates the list of the variables (BeVar) that are considered observable in the context of this diagnosability test. :param var_names: a list of string containing the names of the vars that should be visible :return: a list of untimed boolean expressions standing for the various bits of the observable variables. """ if not var_names: return [] else: # otherwise use the user specified set of actions encoding = master_be_fsm().encoding boolean_varnames = reduce(lambda x, y: x + y, map(booleanize, var_names), []) be_variables = list(map(lambda x: encoding.by_name[str(x)], boolean_varnames)) return be_variables
def _at_time(self, time): """ Returns a boolean expression (Be) corresponding to this simple boolean expression (text) at the time step `time`. .. note:: Albeit feasible, working directly with variables as offered by the encoding is a little bit limiting as it de facto rejects any symbol which is not a variable. As a consequence, the DEFINES, or arithmetic expressions are not usable. The use of this function palliates that limitation and makes the use of any simple boolean expression possible. :param time: the time at which the symbol should be shifted. :return: a be expression (Be) corresponding to `self` at `time` """ booleanized = self._booleanize() return master_be_fsm().encoding.shift_to_time(booleanized, time)
def constraint_context_theta_initial(initial_condition, offset_path1, offset_path2): """ Generates the theta part of the context constraint. That is to say, the part of the constraint which imposes a certain initial condition on the initial belief state. :param initial_condition: a Node representing the intial condition to be enforced on the belief state. :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) :return: the theta part of the context constraint encoded in the form of a boolean expression. """ enc = master_be_fsm().encoding # enforce cond on trace 1 w01 = enc.shift_to_time(initial_condition.to_be(enc), offset_path1) # enforce cond on trace 2 w02 = enc.shift_to_time(initial_condition.to_be(enc), offset_path2) return w01 & w02
def check_ltl_onepb(fml, length, no_fairness=False, no_invar=False, dry_run=False): """ This function verifies that the given FSM satisfies the given property for paths with an exact length of `length`. :param fml: an LTL formula parsed with `pynusmv_tools.bmcLTL.parsing` (hence the abstract syntax tree of that formula). Note, this is *NOT* the NuSMV format (Node). :param length: the exact length of the considered paths :param no_fairness: a flag telling whether or not the generated problem should focus on fair executions only (the considered fairness constraints must be declared in the SMV model). :param no_invar: a flag telling whether or not the generated problem should enforce the declared invariants (these must be declared in the SMV text). :return: a tuple ('OK', None) if the property is satisfied on all paths of length `length` :return: a tuple ('Violation', counter_example) if the property is violated. The counter_example passed along is a trace leading to a violation of the property """ fsm = master_be_fsm() pb = generate_problem(fml, fsm, length, no_fairness, no_invar) if not dry_run: cnf = pb.to_cnf(Polarity.POSITIVE) solver = SatSolverFactory.create() solver += cnf solver.polarity(cnf, Polarity.POSITIVE) if solver.solve() == SatSolverResult.SATISFIABLE: cnt_ex = generate_counter_example(fsm, pb, solver, length, str(fml)) return ("Violation", cnt_ex) else: return ("Ok", None) return ("Ok", None)
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_same_observations(observable_vars, offset_path1, offset_path2, length): """ Generates a boolean expression stating that the observable state of both paths should be the same (all input vars are equivalent). :param observable_vars: the list of the boolean variables that are considered visible in the scope of this diagnosability test :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 fact that observations must be the exact same along the two paths. """ fsm = master_be_fsm() constraint = Be.true(fsm.encoding.manager) for time_ in range(length + 1): for v in observable_vars: ep1 = v.at_time[time_ + offset_path1].boolean_expression ep2 = v.at_time[time_ + offset_path2].boolean_expression constraint &= ep1.iff(ep2) return constraint
def constraint_same_observations(observable_vars, offset_path1, offset_path2, length): """ Generates a boolean expression stating that the observable state of both paths should be the same (all input vars are equivalent). :param observable_vars: the list of the boolean variables that are considered visible in the scope of this diagnosability test :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 fact that observations must be the exact same along the two paths. """ fsm = master_be_fsm() constraint = Be.true(fsm.encoding.manager) for time_ in range(length+1): for v in observable_vars: ep1 = v.at_time[time_ + offset_path1].boolean_expression ep2 = v.at_time[time_ + offset_path2].boolean_expression constraint &= ep1.iff(ep2) 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 _booleanize(self): """ Returns a boolean expression (Be) corresponding to this simple boolean expression (text). .. note:: Albeit feasible, working directly with variables as offered by the encoding is a little bit limiting as it de facto rejects any symbol which is not a variable. As a consequence, the DEFINES, or arithmetic expressions are not usable. The use of this function palliates that limitation and makes the use of any simple boolean expression possible. :return: a be expression (Be) corresponding to `self` """ if not self._booleanized: befsm = master_be_fsm() node = parse_simple_expression(self.id) self._booleanized = Wff(node).to_boolean_wff().to_be(befsm.encoding) return self._booleanized
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 diagnosability_violation(observable_names, solver, k): """ Interprets the result (model of the sat solver) and prints the parallel traces (having the same observations) that lead to some critical pair. :param observable_names: the list of names of the variables which are considered observable in the model. :param solver: the solver that responded SatResult.SATISFIABLE to some submitted problem :param k: the bound on the length of the problem submitted to the solver. """ lexicographically = lambda x: str(x) be_enc = master_be_fsm().encoding decoded = be_enc.decode_sat_model(solver.model) # create the trace that will actually get returned counter_ex = "############### DIAGNOSABILITY VIOLATION ############\n" for time in range(k): counter_ex += "*************** TIME {:03} ****************************\n".format(time) counter_ex += "--------------- OBSERVABLE STATE --------------------\n" # add all observable values. for symbol in sorted(decoded[time].keys(), key=lexicographically): if str(symbol) in observable_names: counter_ex += "{} = {}\n".format(symbol, decoded[time][symbol]) counter_ex += "--------------- BELIEF STATE A ----------------------\n" # add all non-observable values of the belief STATE A___ for symbol in sorted(decoded[time].keys(), key=lexicographically): if str(symbol) not in observable_names: counter_ex += "{} = {}\n".format(symbol, decoded[time][symbol]) counter_ex += "--------------- BELIEF STATE B ----------------------\n" # add all non-observable values of the belief STATE B___ for symbol in sorted(decoded[k + 1 + time].keys(), key=lexicographically): if str(symbol) not in observable_names: counter_ex += "{} = {}\n".format(symbol, decoded[1 + k + time][symbol]) return counter_ex
def test_is_complete(self): with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() trace = Trace.create("Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True) # vacuously true self.assertTrue(trace.is_complete(NodeList.from_list([]))) v = be_fsm.encoding.by_name['v'].name self.assertFalse(trace.is_complete(NodeList.from_list([v]))) step = trace.steps[1] yes = Node.from_ptr(parse_simple_expression("TRUE")) step += (v, yes) self.assertTrue(trace.is_complete(NodeList.from_list([v])))
def _booleanize(self): """ Returns a boolean expression (Be) corresponding to this simple boolean expression (text). .. note:: Albeit feasible, working directly with variables as offered by the encoding is a little bit limiting as it de facto rejects any symbol which is not a variable. As a consequence, the DEFINES, or arithmetic expressions are not usable. The use of this function palliates that limitation and makes the use of any simple boolean expression possible. :return: a be expression (Be) corresponding to `self` """ if not self._booleanized: befsm = master_be_fsm() node = parse_simple_expression(self.id) self._booleanized = Wff(node).to_boolean_wff().to_be( befsm.encoding) return self._booleanized
def diagnosability_violation(observable_names, solver, k): """ Interprets the result (model of the sat solver) and prints the parallel traces (having the same observations) that lead to some critical pair. :param observable_names: the list of names of the variables which are considered observable in the model. :param solver: the solver that responded SatResult.SATISFIABLE to some submitted problem :param k: the bound on the length of the problem submitted to the solver. """ lexicographically = lambda x: str(x) be_enc = master_be_fsm().encoding decoded= be_enc.decode_sat_model(solver.model) # create the trace that will actually get returned counter_ex = "############### DIAGNOSABILITY VIOLATION ############\n" for time in range(k+1): counter_ex+= "*************** TIME {:03} ****************************\n".format(time) counter_ex+= "--------------- OBSERVABLE STATE --------------------\n" # add all observable values. for symbol in sorted(decoded[time].keys(), key=lexicographically): if str(symbol) in observable_names: counter_ex+="{} = {}\n".format(symbol, decoded[time][symbol]) counter_ex+= "--------------- BELIEF STATE A ----------------------\n" # add all non-observable values of the belief STATE A___ for symbol in sorted(decoded[time].keys(), key=lexicographically): if str(symbol) not in observable_names: counter_ex+="{} = {}\n".format(symbol, decoded[time][symbol]) counter_ex+= "--------------- BELIEF STATE B ----------------------\n" # add all non-observable values of the belief STATE B___ for symbol in sorted(decoded[k+1+time].keys(), key=lexicographically): if str(symbol) not in observable_names: counter_ex+="{} = {}\n".format(symbol, decoded[1+k+time][symbol]) return counter_ex
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_language_contains(self): with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() trace = Trace.create("Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True) v = be_fsm.encoding.by_name['v'] w = be_fsm.encoding.by_name['w'] f = be_fsm.encoding.by_name['f'] i = be_fsm.encoding.by_name['i'] self.assertTrue(v.name in trace) self.assertTrue(w.name in trace) self.assertTrue(f.name in trace) self.assertTrue(i.name in trace) x = parse_simple_expression("x") self.assertFalse(Node.from_ptr(x) in trace)
def mk_observable_names(args): """ returns the set of symbols names that need to be considered observable in the context of this diagnosability test. .. note:: The output of this function is typically what need to be passed to `mk_observable_vars` :param args: the arguments given on the command line :return: the list of variable names corresponding to the patterns specified by the end user. """ # take observable = set(args.observable) # handle the --observable-inputs (-oi) command line flag if args.observable_inputs: scalr_name = lambda x: str(x.scalar) input_vars = map(scalr_name, master_be_fsm().encoding.input_variables) observable = observable | set(input_vars) # handle the external config file (--observable-file // -of) and the # multiple --observable-regex (-or) command line options regexes = set(args.observable_regex) if args.observable_file: with open(args.observable_file) as f: for line in f: regexes = regexes | set(line.split(";")) for symbol in master_bool_sexp_fsm().symbols_list: for regex in regexes: if fullmatch(regex.strip(), str(symbol)): observable.add(str(symbol)) return observable
def test_is_loopback_when_frozen(self): with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() # empty trace trace = Trace.create( "Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True, ) step1 = trace.steps[1] step2 = trace.append_step() yes = Node.from_ptr(parse_simple_expression("TRUE")) no = Node.from_ptr(parse_simple_expression("FALSE")) v = be_fsm.encoding.by_name["v"].name step1 += (v, yes) step2 += (v, no) trace.freeze() step1.force_loopback() self.assertTrue(step1.is_loopback) self.assertFalse(step2.is_loopback) step2.force_loopback() self.assertTrue(step1.is_loopback) # last step is never a loopback self.assertFalse(step2.is_loopback) trace.thaw() self.assertFalse(step1.is_loopback) self.assertFalse(step2.is_loopback)
def check_ltl_onepb(fml, length, no_fairness=False, no_invar=False, dry_run=False): """ This function verifies that the given FSM satisfies the given property for paths with an exact length of `length`. :param fml: an LTL formula parsed with `tools.bmcLTL.parsing` (hence the abstract syntax tree of that formula). Note, this is *NOT* the NuSMV format (Node). :param length: the exact length of the considered paths :param no_fairness: a flag telling whether or not the generated problem should focus on fair executions only (the considered fairness constraints must be declared in the SMV model). :param no_invar: a flag telling whether or not the generated problem should enforce the declared invariants (these must be declared in the SMV text). :return: a tuple ('OK', None) if the property is satisfied on all paths of length `length` :return: a tuple ('Violation', counter_example) if the property is violated. The counter_example passed along is a trace leading to a violation of the property """ fsm = master_be_fsm() pb = generate_problem(fml, fsm, length, no_fairness, no_invar) if not dry_run: cnf = pb.to_cnf(Polarity.POSITIVE) solver = SatSolverFactory.create() solver+= cnf solver.polarity(cnf, Polarity.POSITIVE) if solver.solve() == SatSolverResult.SATISFIABLE: cnt_ex = generate_counter_example(fsm, pb, solver, length, str(fml)) return ("Violation", cnt_ex) else: return ("Ok", None) return ("Ok", None)
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_concat(self): with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() trace = Trace.create( "Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True, ) spec = Node.from_ptr(parse_ltl_spec("F (w <-> v)")) bound = 2 problem = generate_ltl_problem(be_fsm, spec, bound=bound) # .inline(True) cnf = problem.to_cnf() solver = SatSolverFactory.create() solver += cnf solver.polarity(cnf, Polarity.POSITIVE) solver.solve() other = generate_counter_example(be_fsm, problem, solver, bound) trace.concat(other) self.assertEquals(-1, trace.id) self.assertFalse(trace.is_registered) self.assertEquals("Dummy example", trace.description) self.assertEquals(TraceType.COUNTER_EXAMPLE, trace.type) self.assertTrue(trace.is_volatile) self.assertEquals(2, trace.length) self.assertEquals(2, len(trace)) self.assertFalse(trace.is_empty) self.assertFalse(trace.is_frozen) self.assertTrue(trace.is_thawed)
def test_is_complete(self): with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() trace = Trace.create( "Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True, ) # vacuously true self.assertTrue(trace.is_complete(NodeList.from_list([]))) v = be_fsm.encoding.by_name["v"].name self.assertFalse(trace.is_complete(NodeList.from_list([v]))) step = trace.steps[1] yes = Node.from_ptr(parse_simple_expression("TRUE")) step += (v, yes) self.assertTrue(trace.is_complete(NodeList.from_list([v])))
def test_assign_value__magicmethod__(self): """tests the behavior of assign and value""" with BmcSupport(): sexp_fsm = master_bool_sexp_fsm() be_fsm = master_be_fsm() # empty trace trace = Trace.create("Dummy example", TraceType.COUNTER_EXAMPLE, sexp_fsm.symbol_table, sexp_fsm.symbols_list, is_volatile=True) step1 = trace.steps[1] yes = Node.from_ptr(parse_simple_expression("TRUE")) no = Node.from_ptr(parse_simple_expression("FALSE")) v = be_fsm.encoding.by_name['v'].name self.assertIsNone(step1.value[v]) step1 += (v, yes) self.assertEqual(yes, step1.value[v]) step1 += (v, no) self.assertEqual(no, step1.value[v])
def generate_sat_problem(observable_vars, formula_nodes, length, theta, sigma1, sigma2): """ Generates a SAT problem which is satisfiable iff the given `formula` is *NOT* diagnosable for the loaded model for traces of length `length`. :param observable_vars: the list of the boolean variables that are considered visible in the scope of this diagnosability test :param formula: the node (NuSMV ast representation) representing the formula whose diagnosability is under verification :param length: the maximum length of the generated traces. :param theta: the initial condition placed on the initial belief state (in the form of a :see:`pynusmv.node.Node`) :param sigma1: the shape of the traces considered relevant for the first member of the critical pair in the ongoing diagnosability test (in the form of a :see:`pynusmv.node.Node`) :param sigma2: the shape of the traces considered relevant for the second member of the critical pair in the ongoing diagnosability test (in the form of a :see:`pynusmv.node.Node`) :return: a SAT problem which is satisfiable iff the given formula is not diagnosable on the loaded model. """ fsm = master_be_fsm() offset_1 = 0 offset_2 = length + 1 problem = ( generate_path(offset_1, length) & generate_path(offset_2, length) & constraint_same_observations(observable_vars, offset_1, offset_2, length) & constraint_context_theta_initial(theta, offset_1, offset_2) & bounded_semantics_at_offset(fsm, sigma1, length, offset_1) & bounded_semantics_at_offset(fsm, sigma2, length, offset_2) & constraint_eventually_critical_pair(formula_nodes, offset_1, offset_2, length) ) return problem
def setUp(self): init_nusmv() load_from_file( tests.current_directory(__file__) + "/models/dummy_invarspecs.smv") go_bmc() self.fsm = master_be_fsm()
def setUp(self): init_nusmv() load_from_file(tests.current_directory(__file__)+"/models/dummy_invarspecs.smv") go_bmc() self.fsm = master_be_fsm()
def setUp(self): init_nusmv() glob.load(self.model()) go_bmc() self.enc = master_be_fsm().encoding