Пример #1
0
 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))
     
Пример #2
0
    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)
Пример #3
0
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
Пример #4
0
    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))
Пример #5
0
    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])
Пример #6
0
 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)
Пример #7
0
 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"]])
Пример #8
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
Пример #9
0
 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)']
Пример #10
0
 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()
Пример #11
0
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)
Пример #12
0
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)
Пример #13
0
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
Пример #14
0
 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")
Пример #15
0
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)
Пример #16
0
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)
Пример #17
0
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
Пример #18
0
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)
Пример #19
0
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
Пример #20
0
    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)
Пример #21
0
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
Пример #22
0
    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)
Пример #23
0
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
Пример #24
0
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)
Пример #25
0
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
Пример #26
0
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
Пример #27
0
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
Пример #28
0
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
Пример #29
0
    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)
Пример #31
0
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
Пример #32
0
    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])))
Пример #33
0
    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
Пример #34
0
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
Пример #35
0
 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)
Пример #36
0
    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)
Пример #37
0
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
Пример #38
0
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
Пример #39
0
    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)
Пример #40
0
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))
Пример #42
0
    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)
Пример #43
0
    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])))
Пример #44
0
    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])
Пример #45
0
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
Пример #46
0
 def setUp(self):
     init_nusmv()
     load_from_file(
         tests.current_directory(__file__) + "/models/dummy_invarspecs.smv")
     go_bmc()
     self.fsm = master_be_fsm()
Пример #47
0
 def setUp(self):
     init_nusmv()
     load_from_file(tests.current_directory(__file__)+"/models/dummy_invarspecs.smv")
     go_bmc()
     self.fsm = master_be_fsm()
Пример #48
0
 def setUp(self):
     init_nusmv()
     glob.load(self.model())
     go_bmc()
     self.enc = master_be_fsm().encoding