Beispiel #1
0
 def test_false(self):
     with self.assertRaises(Exception):
         Be.false(None)
     expr = Be.false(self._manager)
     self.assertFalse(expr.is_true())
     self.assertTrue(expr.is_false())
     self.assertTrue(expr.is_constant())
Beispiel #2
0
    def bounded_semantics(self, fsm, k, fairness=True):
        """
        Returns a boolean expression corresponding to the bounded semantics of
        the formula denoted by `self` on a path of length k. This combines both
        the semantics in case of a loopy path and the case of a non-loopy path.

        .. note::
            This function takes the same approach as NuSMV and does not enforce
            the absence of loop when using the more restrictive semantic_no_loop.

        :param fsm: the FSM representing the model. It is used to gain access
            to the encoder (-> shift variables) and to obtain the list of
            fairness constraints.
        :param k: the last time that exists in the universe of this expression
        :param fairness: a flag indicating whether or not the fairness constraints
            should be taken into account while generating the formula.
        :return: a boolean expression translating the bounded semantics of this
            formula.
        """
        enc = fsm.encoding
        noloop = self.semantic_no_loop(enc, 0, k)

        w_loop = Be.false(enc.manager)
        for l in range(k):  # [0; k-1]
            fairness_cond = fairness_constraint(fsm, k, l) \
                                 if fairness \
                                 else Be.true(enc.manager)

            w_loop |= (loop_condition(enc, k, l) \
                      & fairness_cond \
                      & self.semantic_with_loop(enc, 0, k, l))

        return noloop | w_loop
Beispiel #3
0
 def test_bounded_semantics_without_loop(self):
     # parse the ltl property
     spec = Node.from_ptr(parse_ltl_spec("G ( y <= 7 )"))
     
     # it must raise exception when the bound is not feasible
     with self.assertRaises(ValueError):
         ltlspec.bounded_semantics_without_loop(self.fsm, spec, bound=-1)
     
     # verify that the generated expression corresponds to what is announced
     no_loop = ltlspec.bounded_semantics_without_loop(self.fsm, spec, 10)
     
     # globally w/o loop is false (this is just a test)
     self.assertEqual(no_loop, Be.false(self.fsm.encoding.manager)) 
     
     # an other more complex generation
     spec = Node.from_ptr(parse_ltl_spec("F (y <= 7)"))
     no_loop = ltlspec.bounded_semantics_without_loop(self.fsm, spec, 10)
     
     #
     # The generated expression is [[f]]^{0}_{k} so (! L_{k}) is not taken 
     # care of. And actually, NuSMV does not generate that part of the 
     # formula: it only enforce the loop condition when the semantics with 
     # loop is used 
     # 
     handcrafted = Be.false(self.fsm.encoding.manager)
     y_le_seven  = Wff(parse_ltl_spec("y <= 7")).to_boolean_wff().to_be(self.fsm.encoding)
     for time_x in reversed(range(11)): # 11 because range 'eats' up the last step
         handcrafted |= self.fsm.encoding.shift_to_time(y_le_seven, time_x)
     
     #### debuging info #####
     #print("noloop  = {}".format(no_loop.to_cnf()))
     #print("hancraft= {}".format(handcrafted.to_cnf()))
     #print(self.fsm.encoding)
     self.assertEqual(no_loop, handcrafted)
Beispiel #4
0
 def test_constant_with_loop(self):
     with tests.Configure(self, __file__, "/example.smv"):
         expr = ast.Constant("TRUE")
         self.assertEqual(Be.true(self.mgr), expr.semantic_with_loop(self.enc, 0, 5, 2))
         
         expr = ast.Constant("FALSE")
         self.assertEqual(Be.false(self.mgr), expr.semantic_with_loop(self.enc, 0, 5, 2))
Beispiel #5
0
 def test_false(self):
     with self.assertRaises(Exception):
         Be.false(None)
     expr = Be.false(self._manager)
     self.assertFalse(expr.is_true())
     self.assertTrue(expr.is_false())
     self.assertTrue(expr.is_constant())
 def test_globally_no_loop(self):
     with Configure(self, __file__, "/models/flipflops.smv"):
         fsm     = self.befsm
         formula = self.nnf("G (a <-> !b)")
         
         #  bound 0
         ref_expr= ltlspec.bounded_semantics_without_loop(fsm, formula, 0)
         expr    = ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, 0, 0)
         self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
         
         # bound 1
         ref_expr= ltlspec.bounded_semantics_without_loop(fsm, formula, 1)
         expr    = ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, 1, 0)
         self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
         
         # ---- other offset ----
         #  bound 0
         offset  = 1
         ref_expr= Be.false(fsm.encoding.manager)
         expr    = ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, 0, offset)
         self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
         
         #  bound 1
         offset  = 1
         ref_expr= Be.false(fsm.encoding.manager)
         expr    = ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, 1, offset)
         self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
Beispiel #7
0
    def bounded_semantics(self, fsm, k, fairness=True):
        """
        Returns a boolean expression corresponding to the bounded semantics of
        the formula denoted by `self` on a path of length k. This combines both
        the semantics in case of a loopy path and the case of a non-loopy path.

        .. note::
            This function takes the same approach as NuSMV and does not enforce
            the absence of loop when using the more restrictive semantic_no_loop.

        :param fsm: the FSM representing the model. It is used to gain access
            to the encoder (-> shift variables) and to obtain the list of
            fairness constraints.
        :param k: the last time that exists in the universe of this expression
        :param fairness: a flag indicating whether or not the fairness constraints
            should be taken into account while generating the formula.
        :return: a boolean expression translating the bounded semantics of this
            formula.
        """
        enc = fsm.encoding
        noloop = self.semantic_no_loop(enc, 0, k)

        w_loop = Be.false(enc.manager)
        for l in range(k):  # [0; k-1]
            fairness_cond = fairness_constraint(fsm, k, l) if fairness else Be.true(enc.manager)

            w_loop |= loop_condition(enc, k, l) & fairness_cond & self.semantic_with_loop(enc, 0, k, l)

        return noloop | w_loop
Beispiel #8
0
 def test_add_(self):
     # using the algebraic notation
     true  = Be.true(self._manager)
     false = Be.false(self._manager)
     
     self.assertTrue((true  + false).is_true())
     self.assertFalse((true + false).is_false())
     self.assertTrue((true  + false).is_constant())
Beispiel #9
0
 def test_sub_(self):
     # and not
     true  = Be.true(self._manager)
     false = Be.false(self._manager)
     
     self.assertTrue((true  - false).is_true())
     self.assertFalse((true - false).is_false())
     self.assertTrue((true  - false).is_constant())
Beispiel #10
0
 def test_and(self):
     # using the function
     true  = Be.true(self._manager)
     false = Be.false(self._manager)
     
     self.assertFalse(true.and_(false).is_true())
     self.assertTrue(true.and_(false).is_false())
     self.assertTrue(true.and_(false).is_constant())
Beispiel #11
0
 def test__and_(self):
     # using the keyword
     true  = Be.true(self._manager)
     false = Be.false(self._manager)
     
     self.assertFalse((true and false).is_true())
     self.assertTrue((true and false).is_false())
     self.assertTrue((true and false).is_constant())
Beispiel #12
0
    def test__and_(self):
        # using the keyword
        true = Be.true(self._manager)
        false = Be.false(self._manager)

        self.assertFalse((true and false).is_true())
        self.assertTrue((true and false).is_false())
        self.assertTrue((true and false).is_constant())
Beispiel #13
0
    def test_add_(self):
        # using the algebraic notation
        true = Be.true(self._manager)
        false = Be.false(self._manager)

        self.assertTrue((true + false).is_true())
        self.assertFalse((true + false).is_false())
        self.assertTrue((true + false).is_constant())
Beispiel #14
0
    def test_sub_(self):
        # and not
        true = Be.true(self._manager)
        false = Be.false(self._manager)

        self.assertTrue((true - false).is_true())
        self.assertFalse((true - false).is_false())
        self.assertTrue((true - false).is_constant())
Beispiel #15
0
    def test_and(self):
        # using the function
        true = Be.true(self._manager)
        false = Be.false(self._manager)

        self.assertFalse(true.and_(false).is_true())
        self.assertTrue(true.and_(false).is_false())
        self.assertTrue(true.and_(false).is_constant())
    def test_constant_with_loop(self):
        with tests.Configure(self, __file__, "/example.smv"):
            expr = ast.Constant("TRUE")
            self.assertEqual(Be.true(self.mgr),
                             expr.semantic_with_loop(self.enc, 0, 5, 2))

            expr = ast.Constant("FALSE")
            self.assertEqual(Be.false(self.mgr),
                             expr.semantic_with_loop(self.enc, 0, 5, 2))
Beispiel #17
0
def bounded_semantics_at_offset(fsm, formula, bound, offset, fairness=True):
    """
    Generates the Be :math:`[[formula]]_{bound}` corresponding to the bounded semantic 
    of `formula` but encodes it with an `offset` long shift in the timeline of the encoder.

    .. note:: 

        This function plays the same role as `bounded_semantics_all_loops` but allows to 
        position the time blocks at some place we like in the encoder timeline. This is mostly
        helpful if you want to devise verification methods that need to have multiple parallel
        verifications. (ie. diagnosability).

        Note however, that the two implementations are different.

    .. warning::

        So far, the only supported temporal operators are F, G, U, R, X

    :param fsm: the BeFsm for which the property will be verified. Actually, it is only used to 
        provide the encoder used to assign the variables to some time blocks. The api was kept 
        this ways to keep uniformity with its non-offsetted counterpart.
    :param formula: the property for which to generate a verification problem
        represented in a 'node' format (subclass of :class:`pynusmv.node.Node`)
        which corresponds to the format obtained from the ast. (remark: if you
        need to manipulate [ie negate] the formula before passing it, it is
        perfectly valid to pass a node decorated by `Wff.decorate`).
    :param bound: the logical time bound to the problem. (Leave out the offset for this param: if you
        intend to have a problem with at most 10 steps, say bound=10)
    :param offset: the time offset in the encoding block where the sem of this formula will be 
        generated.
    :param fairness: a flag indicating whether or not to take the fairness 
        constraint into account.
    :return: a Be corresponding to the semantics of `formula` for a problem with a maximum of `bound` 
        steps encoded to start at time `offset` in the `fsm` encoding timeline.
    """
    if bound < 0:
        raise ValueError("Bound must be a positive integer")
    if offset < 0:
        raise ValueError("The offset must be a positive integer")

    enc = fsm.encoding
    straight = bounded_semantics_without_loop_at_offset(
        fsm, formula, 0, bound, offset)
    k_loop = Be.false(enc.manager)
    for i in range(bound):
        fairness_cond = utils.fairness_constraint(fsm, offset+bound, offset+i) \
                                 if fairness \
                                 else Be.true(enc.manager)
        k_loop |= ( utils.loop_condition(enc, offset+bound, offset+i) \
                  & fairness_cond \
                  & bounded_semantics_with_loop_at_offset(fsm, formula, 0, bound, i, offset))

    # this is just the sem of the formula
    return straight | k_loop
Beispiel #18
0
 def test__mul_(self):
     # using algebraic notation
     true  = Be.true(self._manager)
     false = Be.false(self._manager)
     
     self.assertFalse((true * false).is_true())
     self.assertTrue((true  * false).is_false())
     self.assertTrue((true  * false).is_constant())
     
     self.assertFalse((true & false).is_true())
     self.assertTrue((true  & false).is_false())
     self.assertTrue((true  & false).is_constant())
Beispiel #19
0
    def test__mul_(self):
        # using algebraic notation
        true = Be.true(self._manager)
        false = Be.false(self._manager)

        self.assertFalse((true * false).is_true())
        self.assertTrue((true * false).is_false())
        self.assertTrue((true * false).is_constant())

        self.assertFalse((true & false).is_true())
        self.assertTrue((true & false).is_false())
        self.assertTrue((true & false).is_constant())
Beispiel #20
0
def bounded_semantics_at_offset(fsm, formula, bound, offset, fairness=True):
    """
    Generates the Be [[formula]]_{bound} corresponding to the bounded semantic 
    of `formula` but encodes it with an `offset` long shift in the timeline of the encoder.

    .. note:: 

        This function plays the same role as `bounded_semantics_all_loops` but allows to 
        position the time blocks at some place we like in the encoder timeline. This is mostly
        helpful if you want to devise verification methods that need to have multiple parallel
        verifications. (ie. diagnosability).

        Note however, that the two implementations are different.

    .. warning::

        So far, the only supported temporal operators are F, G, U, R, X

    :param fsm: the BeFsm for which the property will be verified. Actually, it is only used to 
        provide the encoder used to assign the variables to some time blocks. The api was kept 
        this ways to keep uniformity with its non-offsetted counterpart.
    :param formula: the property for which to generate a verification problem
        represented in a 'node' format (subclass of :see::class:`pynusmv.node.Node`)
        which corresponds to the format obtained from the ast. (remark: if you
        need to manipulate [ie negate] the formula before passing it, it is
        perfectly valid to pass a node decorated by `Wff.decorate`).
    :param bound: the logical time bound to the problem. (Leave out the offset for this param: if you
        intend to have a problem with at most 10 steps, say bound=10)
    :param offset: the time offset in the encoding block where the sem of this formula will be 
        generated.
    :param fairness: a flag indicating whether or not to take the fairness 
        constraint into account.
    :return: a Be corresponding to the semantics of `formula` for a problem with a maximum of `bound` 
        steps encoded to start at time `offset` in the `fsm` encoding timeline.
    """
    if bound< 0:
        raise ValueError("Bound must be a positive integer")
    if offset<0:
        raise ValueError("The offset must be a positive integer")
    
    enc = fsm.encoding
    straight = bounded_semantics_without_loop_at_offset(fsm, formula, 0, bound, offset)
    k_loop   = Be.false(enc.manager)
    for i in range(bound): 
        fairness_cond = utils.fairness_constraint(fsm, offset+bound, offset+i) \
                                 if fairness \
                                 else Be.true(enc.manager)
        k_loop |= ( utils.loop_condition(enc, offset+bound, offset+i) \
                  & fairness_cond \
                  & bounded_semantics_with_loop_at_offset(fsm, formula, 0, bound, i, offset))
    
    # this is just the sem of the formula
    return straight | k_loop    
Beispiel #21
0
    def test_imply(self):
        true = Be.true(self._manager)
        false = Be.false(self._manager)

        # antecedent always true
        self.assertFalse(true.imply(false).is_true())
        self.assertTrue(true.imply(false).is_false())
        self.assertTrue(true.imply(false).is_constant())

        # antecedent always false
        self.assertTrue(false.imply(true).is_true())
        self.assertFalse(false.imply(true).is_false())
        self.assertTrue(false.imply(true).is_constant())
Beispiel #22
0
 def test_imply(self):
     true  = Be.true(self._manager)
     false = Be.false(self._manager)
     
     # antecedent always true
     self.assertFalse(true.imply(false).is_true())
     self.assertTrue( true.imply(false).is_false())
     self.assertTrue( true.imply(false).is_constant())
     
     # antecedent always false
     self.assertTrue( false.imply(true).is_true())
     self.assertFalse(false.imply(true).is_false())
     self.assertTrue( false.imply(true).is_constant())
Beispiel #23
0
    def test_original_problem(self):
        # constant expr always have zero clauses zero vars
        true = Be.true(self._manager)
        false = Be.false(self._manager)
        TF = true or false
        self.assertEqual(TF, TF.to_cnf(Polarity.NOT_SET).original_problem)
        self.assertEqual(TF, TF.to_cnf(Polarity.POSITIVE).original_problem)
        self.assertEqual(TF, TF.to_cnf(Polarity.NEGATIVE).original_problem)

        # with variables
        v = self._fsm.encoding.by_name['v'].boolean_expression
        self.assertEqual(v, v.to_cnf(Polarity.NOT_SET).original_problem)
        self.assertEqual(v, v.to_cnf(Polarity.POSITIVE).original_problem)
        self.assertEqual(v, v.to_cnf(Polarity.NEGATIVE).original_problem)
Beispiel #24
0
    def test_clauses_number(self):
        # constant expr always have zero clauses zero vars
        true = Be.true(self._manager)
        false = Be.false(self._manager)
        TF = true or false
        self.assertEqual(0, TF.to_cnf(Polarity.NOT_SET).clauses_number)
        self.assertEqual(0, TF.to_cnf(Polarity.POSITIVE).clauses_number)
        self.assertEqual(0, TF.to_cnf(Polarity.NEGATIVE).clauses_number)

        # with variables
        v = self._fsm.encoding.by_name['v'].boolean_expression
        self.assertEqual(2, v.to_cnf(Polarity.NOT_SET).clauses_number)
        self.assertEqual(1, v.to_cnf(Polarity.POSITIVE).clauses_number)
        self.assertEqual(1, v.to_cnf(Polarity.NEGATIVE).clauses_number)
Beispiel #25
0
 def test_reset(self):
     """
     Verifies that resetting the cache provokes no error
     -> verifies only the absence of runtime errors since it is who RBC does 
        the heavy lifting, not the manager
     """
     mgr = BeRbcManager.with_capacity(10)
     # test it doesn't hurt to call this method even though nothing is to
     # be done.
     mgr.reset()
     # conversion to CNF populates the hashes which are being reset so 
     # using this manager to perform cnf conversion makes sense if we want
     # to test the reset works when it actually does something.
     (Be.true(mgr) and Be.false(mgr)).to_cnf(Polarity.POSITIVE)
     mgr.reset()
Beispiel #26
0
 def test_reset(self):
     """
     Verifies that resetting the cache provokes no error
     -> verifies only the absence of runtime errors since it is who RBC does 
        the heavy lifting, not the manager
     """
     mgr = BeRbcManager.with_capacity(10)
     # test it doesn't hurt to call this method even though nothing is to
     # be done.
     mgr.reset()
     # conversion to CNF populates the hashes which are being reset so
     # using this manager to perform cnf conversion makes sense if we want
     # to test the reset works when it actually does something.
     (Be.true(mgr) and Be.false(mgr)).to_cnf(Polarity.POSITIVE)
     mgr.reset()
Beispiel #27
0
 def shift_to_times(self, expr, curr_time, frozen_time, ivar_time, next_time):
     """
     Returns a *timed* Be expression corresponding to `expr` in which:
     
         - all the current state variables are shifted to time `curr_time`
         - all the frozen variables are shifted to time `frozen_time`
         - all the input variables are shifted to time `ivar_time`
         - all the next state variables are shifted to time `next_time`
         
     .. warning:: 
         argument 'expr' must contain only untimed current state variables 
         and untimed frozen variables, otherwise results will be 
         unpredictable. Unfortunately, there is no way to preemptively check
         that a given expression contains only untimed variable so it is up
         to the programmer to make sure he calls this method in an 
         appropriate way. 
     
     :param expr: the expression to shift
     :param curr_time: the time to shift the current variables to
     :param frozen_time: the time to shift the frozen variables to
     :param ivar_time: the time to shift the input variables to
     :param next_time: the time to shift the next state variables to.
     :return: an expression equivalent to `expr` but with the sets of 
         variables shifted to the time blocks.
     """
     ptr = _be.BeEnc_untimed_expr_to_times(self._ptr, 
                                           expr._ptr, 
                                           curr_time,
                                           frozen_time, 
                                           ivar_time, 
                                           next_time)
     return Be(ptr, self.manager)
Beispiel #28
0
 def test_next_no_loop(self):
     with Configure(self, __file__, "/models/flipflops.smv"):
         fsm     = self.befsm
         formula = self.nnf("X (a <-> !b)")
         
         #  bound 0
         ref_expr= ltlspec.bounded_semantics_without_loop(fsm, formula, 0)
         expr    = ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, 0, 0)
         self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
         
         # bound 1
         ref_expr= ltlspec.bounded_semantics_without_loop(fsm, formula, 1)
         expr    = ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, 1, 0)
         self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
         
         # ---- other offset ----
         #  bound 0
         offset  = 1
         ref_expr= Be.false(fsm.encoding.manager)
         expr    = ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, 0, offset)
         self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
         
         #  bound 1
         offset  = 1
         ref_expr= Wff.decorate(ltlspec.car(formula)).to_be(fsm.encoding)
         ref_expr= fsm.encoding.shift_to_time(ref_expr, offset+1)
         expr    = ltlspec.bounded_semantics_without_loop_at_offset(fsm, formula, 0, 1, offset)
         self.assertEqual(canonical_cnf(expr), canonical_cnf(ref_expr))
Beispiel #29
0
def loop_condition(enc, k, l):
    """
    This function generates a Be expression representing the loop condition
    which is necessary to determine that k->l is a backloop.

    Formally, the returned constraint is denoted _{l}L_{k}

    Because the transition relation is encoded in Nusmv as formula (and not as
    a relation per-se), we determine the existence of a backloop between
    l < k and forall var, var(i) == var(k)

    That is to say: if it is possible to encounter two times the same state
    (same state being all variables have the same value in both states) we know
    there is a backloop on the path

    .. note::
        An other implementation of this function (w/ the same semantics) exists
        in :mod:`pynusmv.bmc.utils`. This version is merely re-implemented to
        1. show that it can be easily done
        2. stick closely to the definition given in the paper by Biere et al.
            (see other note)


    :param fsm: the fsm on which the condition will be evaluated
    :param k: the highest time
    :param l: the time where the loop is assumed to start
    :return: a Be expression representing the loop condition that verifies that
        k-l is a loop path.
    """
    cond = Be.true(enc.manager)
    for v in enc.curr_variables:  # for all untimed variable
        vl = v.at_time[l].boolean_expression
        vk = v.at_time[k].boolean_expression
        cond = cond & (vl.iff(vk))
    return cond
Beispiel #30
0
 def shift_curr_to_next(self, expr):
     """
     Returns an *untimed* Be expression corresponding to `expr` in which all 
     variables v have been shifted to next(v). Example:
         
         v == True & w == False becomes next(v) == True & next(w) == False
     
     .. note::
         Despite the fact that this operation performs a shift of the 
         variables it remains in the *untimed* block. (next of untimed vars 
         are also untimed vars). Hence the returned expression is an *untimed*
         expression. Therefore, in order to use it in (ie a transition 
         relation unrolling), it must be shifted again to a time block using
         one of :
         
             - :meth:`shift_to_time`
             - :meth:`shift_to_times`
             - :func:`or_interval`
             - :func:`and_interval`
     
     .. warning:: 
         argument 'expr' must contain only untimed current state variables 
         and untimed frozen variables, otherwise results will be 
         unpredictable. Unfortunately, there is no way to preemptively check
         that a given expression contains only untimed variable so it is up
         to the programmer to make sure he calls this method in an 
         appropriate way.
     
     :param expr: the expression to shift
     :return: an expression equivalent to expr but with the variables shifted
         to the next-state portion of the block.
     """
     ptr = _be.BeEnc_shift_curr_to_next(self._ptr, expr._ptr)
     return Be(ptr, self.manager)
Beispiel #31
0
    def invar_dual_forward_unrolling(self, invarspec, i):
        """
        Performs one step in the unrolling of the invarspec property.
        
        In terms of pseudo code, this corresponds to::
        
            if i == 0 : 
                return Invar[0]
            else
                return Trans[i-1] & Invar[i] & Property[i-1]
        
        .. note:: 
            
            this is specific to the INVARSPEC verification
            
        :param invarspec: a booleanized, NNF formula representing an invariant 
            property.
        :param i: the time step for which the unrolling is generated.
        :return: Trans[i-1] & Invar[i] & Property[i-1]
        :raise ValueError: in case the given parameters are incorrect.
        """
        if invarspec is None:
            raise ValueError("an invarspec is expected")
        if i < 0:
            raise ValueError("Time must be a non negative integer")

        return Be(
            _bmc.Bmc_Model_Invar_Dual_forward_unrolling(
                self._fsm._ptr, invarspec._ptr, i), self._fsm.encoding.manager)
Beispiel #32
0
def fairness_constraint(fsm, k, l):
    """
    Computes a step of the constraint to be added to the loop side of the BE 
    when one wants to take fairness into account for the case where we consider 
    the existence of a k-l loop (between k and l obviously).
    
    .. note::
    
        This code was first implemented in Python with PyNuSMV but, since
        the Python implementation proved to be a huge performance bottleneck
        (profiling revealed that useless memory management was dragging the
        whole system behind), it has been translated back to C to deliver much
        better perf. results.
    
    :param fsm: the fsm whose transition relation must be unrolled
    :param k: the maximum (horizon/bound) time of the problem
    :param l: the time where the loop starts 
    :return: a step of the fairness constraint to force fair execution on the
        k-l loop.
    :raises ValueError: when the given `k` and `l` are not consistent with each 
        other or when the bound `k` is negative.
    """
    check_consistency(k, l)

    # Note: this code was first implemented in Python with PyNuSMV but, since
    #       the Python implementation proved to be a huge performance bottleneck
    #       (profiling revealed that useless memory management was dragging the
    #       whole system behind).
    return Be(_lower.fairness_constraint(fsm._ptr, k, l), fsm.encoding.manager)
Beispiel #33
0
    def test_bounded_semantics_with_loop(self):
        # parse the ltl property
        spec = Node.from_ptr(parse_ltl_spec("G ( y <= 7 )"))

        # it must raise exception when the bound is not feasible
        with self.assertRaises(ValueError):
            ltlspec.bounded_semantics_single_loop(self.fsm, spec, -1, -2)
        # it must raise exception when the bound and loop are not consistent
        with self.assertRaises(ValueError):
            ltlspec.bounded_semantics_single_loop(self.fsm, spec, 5, 6)

        # verify that the generated problem corresponds to what is announced
        # without optimisation, the all loops is built as the conjunction of all
        # the possible 'single_loops'
        all_loops = ltlspec.bounded_semantics_all_loops(self.fsm,
                                                        spec,
                                                        10,
                                                        0,
                                                        optimized=False)

        acc_loops = Be.false(self.fsm.encoding.manager)
        for time_t in range(10):
            acc_loops |= ltlspec.bounded_semantics_single_loop(
                self.fsm, spec, 10, time_t)
        self.assertEqual(acc_loops, all_loops)

        # with optimisation, it's different
        all_loops = ltlspec.bounded_semantics_all_loops(self.fsm,
                                                        spec,
                                                        10,
                                                        0,
                                                        optimized=True)
        self.assertNotEqual(acc_loops, all_loops)
Beispiel #34
0
 def init(self, time):
     """
     Retrieves the init states and compiles them into a BE at time `time`
     
     :param time: the time at which to consider the init.
     :return: a Be corresponding to the init states at time `time`
     :raises ValueError: if the specified time is negative.
     """
     if time < 0:
         raise ValueError("Time cannot be negative")
     if time == 0:
         return Be(_bmc.Bmc_Model_GetInit0(self._fsm._ptr),
                   self._fsm.encoding.manager)
     else:
         return Be(_bmc.Bmc_Model_GetInitI(self._fsm._ptr, time),
                   self._fsm.encoding.manager)
Beispiel #35
0
def loop_condition(enc, k, l):
    """
    This function generates a Be expression representing the loop condition
    which is necessary to determine that k->l is a backloop.

    Formally, the returned constraint is denoted _{l}L_{k}

    Because the transition relation is encoded in Nusmv as formula (and not as
    a relation per-se), we determine the existence of a backloop between
    l < k and forall var, var(i) == var(k)

    That is to say: if it is possible to encounter two times the same state
    (same state being all variables have the same value in both states) we know
    there is a backloop on the path

    .. note::
        An other implementation of this function (w/ the same semantics) exists
        in :mod:`pynusmv.bmc.utils`. This version is merely re-implemented to
        1. show that it can be easily done
        2. stick closely to the definition given in the paper by Biere et al.
            (see other note)


    :param fsm: the fsm on which the condition will be evaluated
    :param k: the highest time
    :param l: the time where the loop is assumed to start
    :return: a Be expression representing the loop condition that verifies that
        k-l is a loop path.
    """
    cond = Be.true(enc.manager)
    for v in enc.curr_variables:  # for all untimed variable
        vl = v.at_time[l].boolean_expression
        vk = v.at_time[k].boolean_expression
        cond = cond & (vl.iff(vk))
    return cond
Beispiel #36
0
def bounded_semantics_without_loop_at_offset(fsm, formula, time, bound,
                                             offset):
    """
    Generates the Be :math:`[[formula]]^{time}_{bound}` corresponding to the bounded semantic 
    of `formula` when there is no loop on the path but encodes it with an `offset` long shift 
    in the timeline of the encoder.

    .. note::
    
        This code was first implemented in Python with PyNuSMV but, since
        the Python implementation proved to be a huge performance bottleneck
        (profiling revealed that useless memory management was dragging the
        whole system behind), it has been translated back to C to deliver much
        better perf. results.

    .. note:: 

        This function plays the same role as `bounded_semantics_without_loop` but allows to 
        position the time blocks at some place we like in the encoder timeline. This is mostly
        helpful if you want to devise verification methods that need to have multiple parallel
        verifications. (ie. diagnosability).

        Note however, that the two implementations are different.

    .. warning::

        So far, the only supported temporal operators are F, G, U, R, X

    :param fsm: the BeFsm for which the property will be verified. Actually, it is only used to 
        provide the encoder used to assign the variables to some time blocks. The api was kept 
        this ways to keep uniformity with its non-offsetted counterpart.
    :param formula: the property for which to generate a verification problem
        represented in a 'node' format (subclass of :class:`pynusmv.node.Node`)
        which corresponds to the format obtained from the ast. (remark: if you
        need to manipulate [ie negate] the formula before passing it, it is
        perfectly valid to pass a node decorated by `Wff.decorate`).
    :param time: the logical time at which the semantics is to be evaluated. (Leave out the offset for
        this param. If you intend the 3rd state of a trace, say time 2).
    :param bound: the logical time bound to the problem. (Leave out the offset for this param: if you
        intend to have a problem with at most 10 steps, say bound=10)
    :param offset: the time offset in the encoding block where the sem of this formula will be 
        generated.
    :return: a Be corresponding to the semantics of `formula` at `time` for a problem with a maximum
        of `bound` steps encoded to start at time `offset` in the `fsm` encoding timeline.
    """
    if time < 0:
        raise ValueError("Time must be a positive integer")
    if bound < 0:
        raise ValueError("Bound must be a positive integer")
    if offset < 0:
        raise ValueError("The offset must be a positive integer")

    # Note: this code was first implemented in Python with PyNuSMV but, since
    #       the Python implementation proved to be a huge performance bottleneck
    #       (profiling revealed that useless memory management was dragging the
    #       whole system behind).
    _ptr = _lower.sem_no_loop_offset(fsm._ptr, formula._ptr, time, bound,
                                     offset)
    return Be(_ptr, fsm.encoding.manager)
Beispiel #37
0
 def _semantic(time, cnt):
     """auxiliary function to stop recursing after k steps"""
     # at infinity, it is true: psi is not forced if []phi
     if cnt == k:
         return Be.true(enc.manager)
     psi = self.rhs.semantic_with_loop(enc, time, k, l)
     phi = self.lhs.semantic_with_loop(enc, time, k, l)
     return psi | (phi & _semantic(successor(time, k, l), cnt + 1))
Beispiel #38
0
 def test_fairness_list(self):
     self.assertEqual(1, len(self._TESTED.fairness_list))
     # returned items are boolean expressions
     fairness = self._TESTED.fairness_list[0]
     # manually recoding v = True
     v = self._TESTED.encoding.by_name['v'].boolean_expression
     manual = Be.true(self._TESTED.encoding.manager).iff(v)
     self.assertEqual(fairness, manual)
Beispiel #39
0
 def _semantic(time, cnt):
     """auxiliary function to stop recursing after k steps"""
     # at infinity, it is false: psi MUST happen at some time
     if cnt == k:
         return Be.false(enc.manager)
     psi = self.rhs.semantic_with_loop(enc, time, k, l)
     phi = self.lhs.semantic_with_loop(enc, time, k, l)
     return psi | (phi & _semantic(successor(time, k, l), cnt + 1))
Beispiel #40
0
 def _semantic(time, cnt):
     """auxiliary function to stop recursing after k steps"""
     # at infinity, it is true: psi is not forced if []phi
     if cnt == k:
         return Be.true(enc.manager)
     psi = self.rhs.semantic_with_loop(enc, time, k, l)
     phi = self.lhs.semantic_with_loop(enc, time, k, l)
     return psi | (phi & _semantic(successor(time, k, l), cnt + 1))
Beispiel #41
0
 def test_fairness_list(self):
     self.assertEqual(1, len(self._TESTED.fairness_list))   
     # returned items are boolean expressions
     fairness = self._TESTED.fairness_list[0]
     # manually recoding v = True
     v        = self._TESTED.encoding.by_name['v'].boolean_expression
     manual   = Be.true(self._TESTED.encoding.manager).iff(v)
     self.assertEqual(fairness, manual)
Beispiel #42
0
 def _semantic(time, cnt):
     """auxiliary function to stop recursing after k steps"""
     # at infinity, it is false: psi MUST happen at some time
     if cnt == k:
         return Be.false(enc.manager)
     psi = self.rhs.semantic_with_loop(enc, time, k, l)
     phi = self.lhs.semantic_with_loop(enc, time, k, l)
     return psi | (phi & _semantic(successor(time, k, l), cnt + 1))
Beispiel #43
0
    def be_index_to_var(self, index):
        """
        Retrieves the BE variable (expression) corresponding to the given index
        (index may be retrieved from the literals managed by this manager)

        :param index: the index
        :return: the be corresponding to this index
        """
        return Be(_be.Be_Index2Var(self._ptr, index), self)
Beispiel #44
0
    def semantic_no_loop(self, enc, i, k):
        """The semantics when there is no loop:: [[lhs W rhs]]_{bound}^{time}"""
        # k is not infinity when there is no loop
        if i > k:
            return Be.false(enc.manager)

        psi = self.rhs.semantic_no_loop(enc, i, k)
        phi = self.lhs.semantic_no_loop(enc, i, k)
        return psi | (phi & self.semantic_no_loop(enc, i + 1, k))
Beispiel #45
0
    def test_inline(self):
        true = Be.true(self._manager)
        self.assertIsNotNone(true.inline(True))
        self.assertIsNotNone(true.inline(False))

        # with a non constant expression
        v = self._fsm.encoding.by_name["v"].boolean_expression
        self.assertIsNotNone(v.inline(True))
        self.assertIsNotNone(v.inline(False))
Beispiel #46
0
    def semantic_no_loop(self, enc, i, k):
        """The semantics when there is no loop:: [[lhs W rhs]]_{bound}^{time}"""
        # k is not infinity when there is no loop
        if i > k:
            return Be.false(enc.manager)

        psi = self.rhs.semantic_no_loop(enc, i, k)
        phi = self.lhs.semantic_no_loop(enc, i, k)
        return psi | (phi & self.semantic_no_loop(enc, i + 1, k))
Beispiel #47
0
    def test_max_var_index(self):
        # constant expr always have zero clauses zero vars
        true = Be.true(self._manager)
        false = Be.false(self._manager)
        TF = true or false
        TF_cnf = TF.to_cnf(Polarity.POSITIVE)

        v = self._fsm.encoding.by_name['v'].boolean_expression
        v_cnf = v.to_cnf(Polarity.POSITIVE)

        self.assertEqual(0, TF_cnf.max_var_index)
        self.assertEqual(5, v_cnf.max_var_index)

        TF_cnf.max_var_index = 5
        v_cnf.max_var_index = 6

        self.assertEqual(5, TF_cnf.max_var_index)
        self.assertEqual(6, v_cnf.max_var_index)
Beispiel #48
0
 def test_inline(self):
     true  = Be.true(self._manager)
     self.assertIsNotNone(true.inline(True))
     self.assertIsNotNone(true.inline(False))
     
     # with a non constant expression
     v = self._fsm.encoding.by_name["v"].boolean_expression
     self.assertIsNotNone(v.inline(True))
     self.assertIsNotNone(v.inline(False))
Beispiel #49
0
    def test_iff(self):
        true = Be.true(self._manager)
        false = Be.false(self._manager)

        self.assertFalse(true.iff(false).is_true())
        self.assertTrue(true.iff(false).is_false())
        self.assertTrue(true.iff(false).is_constant())

        self.assertFalse(false.iff(true).is_true())
        self.assertTrue(false.iff(true).is_false())
        self.assertTrue(false.iff(true).is_constant())

        self.assertTrue(true.iff(true).is_true())
        self.assertFalse(true.iff(true).is_false())
        self.assertTrue(true.iff(true).is_constant())

        self.assertTrue(false.iff(false).is_true())
        self.assertFalse(false.iff(false).is_false())
        self.assertTrue(false.iff(false).is_constant())
Beispiel #50
0
 def test_globally_no_loop(self):
     with tests.Configure(self, __file__, "/example.smv"):
         i,k     = 0,2
         enc     = self.enc
         mgr     = enc.manager
         a       = ast.Proposition("a")
         formula = ast.Globally(a)
         
         tool = formula.semantic_no_loop(enc, i, k)
         self.assertEqual(Be.false(mgr), tool)
Beispiel #51
0
 def verify_step_fairness_constraint(self):
     # must be true
     tool = ast.fairness_constraint(self.befsm, 0, 0)
     self.assertEqual(tool, Be.true(self.mgr))
      
     # loop position does not matter if not feasible
     tool = ast.fairness_constraint(self.befsm, 0, 1)
     self.assertEqual(tool, Be.true(self.mgr))
     
     model= bmcutils.BmcModel()
     # step 0
     tool = ast.fairness_constraint(self.befsm, 1, 0)
     smv  = model.fairness(1, 0) 
     self.assertEqual(tool, smv)
      
     # step 1
     tool = ast.fairness_constraint(self.befsm, 2, 1)
     smv  = model.fairness(2, 1) 
     self.assertEqual(tool, smv)
Beispiel #52
0
    def test_xor_(self):
        # using the operator
        true = Be.true(self._manager)
        false = Be.false(self._manager)

        self.assertTrue((true ^ false).is_true())
        self.assertFalse((true ^ false).is_false())
        self.assertTrue((true ^ false).is_constant())

        self.assertTrue((false ^ true).is_true())
        self.assertFalse((false ^ true).is_false())
        self.assertTrue((false ^ true).is_constant())

        self.assertFalse((true ^ true).is_true())
        self.assertTrue((true ^ true).is_false())
        self.assertTrue((true ^ true).is_constant())

        self.assertFalse((false ^ false).is_true())
        self.assertTrue((false ^ false).is_false())
        self.assertTrue((false ^ false).is_constant())
Beispiel #53
0
 def verify_invariants_constraint(self, bound):
     model  = bmcutils.BmcModel() 
         
     manual = Be.true(self.mgr) 
     for i in range(bound+1):
         manual &= model.invar[i]
     
     tool = gen.invariants_constraint(self.befsm, bound)
     
     self.assertEqual(tests.canonical_cnf(tool), 
                      tests.canonical_cnf(manual))
Beispiel #54
0
    def test_to_cnf(self):
        true = Be.true(self._manager)
        self.assertIsNotNone(true.to_cnf(Polarity.NOT_SET))
        self.assertIsNotNone(true.to_cnf(Polarity.NEGATIVE))
        self.assertIsNotNone(true.to_cnf(Polarity.POSITIVE))

        # with a non constant expression
        v = self._fsm.encoding.by_name["v"].boolean_expression
        self.assertIsNotNone(v.to_cnf(Polarity.NOT_SET))
        self.assertIsNotNone(v.to_cnf(Polarity.NEGATIVE))
        self.assertIsNotNone(v.to_cnf(Polarity.POSITIVE))
Beispiel #55
0
    def semantic_with_loop(self, enc, i, k, l):
        # without moving at least one step, it is impossible to go through a loop
        if k == 0:
            return Be.false(enc.manager)

        def _semantic(time, cnt):
            if cnt == k:
                return Be.false(enc.manager)
            now = self.prop.semantic_with_loop(enc, time, k, l)
            return now | _semantic(successor(time, k, l), cnt + 1)

        return _semantic(i, 0)
Beispiel #56
0
 def test_next_noloop(self):
     with tests.Configure(self, __file__, "/example.smv"):
         i,k     = 0,2
         enc     = self.enc
         # One step
         a       = ast.Proposition("a")
         formula = ast.Next(a)
         tool    = formula.semantic_no_loop(enc, i, k)
         manual  = a.semantic_no_loop(enc, 1, k)
         nusmv   = ltlspec.bounded_semantics(self.befsm, 
                                             Node.from_ptr(parse_ltl_spec("X a")),
                                             bound = k, 
                                             loop  = bmcutils.no_loopback())
         
         s_tool   = tests.canonical_cnf(tool)
         s_manual = tests.canonical_cnf(manual)
         s_nusmv  = tests.canonical_cnf(nusmv)
         
         self.assertEqual(s_tool, s_nusmv)
         self.assertEqual(s_tool, s_manual)
         
         # two steps
         formula = ast.Next(ast.Next(a))
         tool    = formula.semantic_no_loop(enc, i, k)
         manual  = a.semantic_no_loop(enc, 2, k)
         nusmv   = ltlspec.bounded_semantics(self.befsm, 
                                             Node.from_ptr(parse_ltl_spec("X X a")),
                                             bound = k, 
                                             loop  = bmcutils.no_loopback())
         
         s_tool   = tests.canonical_cnf(tool)
         s_manual = tests.canonical_cnf(manual)
         s_nusmv  = tests.canonical_cnf(nusmv)
         
         self.assertEqual(s_tool, s_nusmv)
         self.assertEqual(s_tool, s_manual)
         
         # Three steps (getting over k)
         formula = ast.Next(ast.Next(ast.Next(a)))
         tool    = formula.semantic_no_loop(enc, i, k)
         manual  = Be.false(enc.manager)
         nusmv   = ltlspec.bounded_semantics(self.befsm, 
                                             Node.from_ptr(parse_ltl_spec("X X X a")),
                                             bound = k, 
                                             loop  = bmcutils.no_loopback())
         
         s_tool   = tests.canonical_cnf(tool)
         s_manual = tests.canonical_cnf(manual)
         s_nusmv  = tests.canonical_cnf(nusmv)
         
         self.assertEqual(s_tool, s_nusmv)
         self.assertEqual(s_tool, s_manual)
Beispiel #57
0
 def test_constraint_same_observations(self):
     observable = diagnosability.mk_observable_vars(["mouse"])
     constraint = diagnosability.constraint_same_observations(observable, 0, 5, 5)
     model      = bmcutils.BmcModel()
     
     manual     = Be.true(model._fsm.encoding.manager)
     for i in range(6): # because we want to go from 0 through 5
         for v in model._fsm.encoding.input_variables:
             v_1 = v.at_time[i].boolean_expression
             v_2 = v.at_time[5+i].boolean_expression
             manual &= v_1.iff(v_2)
     
     self.assertEqual(manual, constraint)
Beispiel #58
0
    def semantic_with_loop(self, enc, i, k, l):
        """The semantics when there is a loop:: _{l}[[lhs W rhs]]_{bound}^{time}"""
        # without moving at least one step, it is impossible to go through a loop
        if k == 0:
            return Be.false(enc.manager)

        def _semantic(time, cnt):
            """auxiliary function to stop recursing after k steps"""
            # at infinity, it is true: psi is not forced if []phi
            if cnt == k:
                return Be.true(enc.manager)
            psi = self.rhs.semantic_with_loop(enc, time, k, l)
            phi = self.lhs.semantic_with_loop(enc, time, k, l)
            return psi | (phi & _semantic(successor(time, k, l), cnt + 1))

        return _semantic(i, 0)