Example #1
0
 def over_post_bdd(self, src_states_bdd, sys_strat=None):
     """ Over-approximated version of concrete post which can be done even
     without the transition relation """
     strat = BDD.true()
     if sys_strat is not None:
         strat &= sys_strat
     # to do this, we use an over-simplified transition relation, EXu,Xc
     b = BDD.true()
     for x in self.iterate_latches():
         temp = BDD.make_eq(BDD(self.get_primed_var(x.lit)),
                            self.lit2bdd(x.next))
         b &= temp.and_abstract(
             strat,
             BDD.make_cube(
                 imap(funcomp(BDD, symbol_lit),
                      self.iterate_controllable_inputs())))
         b = b.restrict(src_states_bdd)
     b &= src_states_bdd
     b = b.exist_abstract(
         BDD.make_cube(
             imap(
                 funcomp(BDD, symbol_lit),
                 chain(self.iterate_latches(),
                       self.iterate_uncontrollable_inputs()))))
     return self.unprime_latches_in_bdd(b)
Example #2
0
    def upost(self, q):
        assert isinstance(q, BDD)
        if q in self.succ_cache:
            return iter(self.succ_cache[q])
        A = BDD.true()
        M = set()
        while A != BDD.false():
            a = A.get_one_minterm(self.uinputs)
            trans = BDD.make_cube(
                imap(lambda x: BDD.make_eq(BDD(self.aig.get_primed_var(x.lit)),
                                           self.aig.lit2bdd(x.next)
                                           .and_abstract(q, self.latch_cube)),
                     self.aig.iterate_latches()))
            lhs = trans & a
            rhs = self.aig.prime_all_inputs_in_bdd(trans)
            simd = BDD.make_impl(lhs, rhs).univ_abstract(self.platch_cube)\
                .exist_abstract(self.pcinputs_cube)\
                .univ_abstract(self.cinputs_cube)
            simd = self.aig.unprime_all_inputs_in_bdd(simd)

            A &= ~simd
            Mp = set()
            for m in M:
                if not (BDD.make_impl(m, simd) == BDD.true()):
                    Mp.add(m)
            M = Mp
            M.add(a)
        log.DBG_MSG("Upost |M| = " + str(len(M)))
        self.succ_cache[q] = map(lambda x: (q, x), M)
        return iter(self.succ_cache[q])
Example #3
0
    def upost(self, q):
        assert isinstance(q, BDD)
        if q in self.succ_cache:
            return iter(self.succ_cache[q])
        A = BDD.true()
        M = set()
        while A != BDD.false():
            a = A.get_one_minterm(self.uinputs)
            trans = BDD.make_cube(
                imap(
                    lambda x: BDD.make_eq(
                        BDD(self.aig.get_primed_var(x.lit)),
                        self.aig.lit2bdd(x.next).and_abstract(
                            q, self.latch_cube)), self.aig.iterate_latches()))
            lhs = trans & a
            rhs = self.aig.prime_all_inputs_in_bdd(trans)
            simd = BDD.make_impl(lhs, rhs).univ_abstract(self.platch_cube)\
                .exist_abstract(self.pcinputs_cube)\
                .univ_abstract(self.cinputs_cube)
            simd = self.aig.unprime_all_inputs_in_bdd(simd)

            A &= ~simd
            Mp = set()
            for m in M:
                if not (BDD.make_impl(m, simd) == BDD.true()):
                    Mp.add(m)
            M = Mp
            M.add(a)
        log.DBG_MSG("Upost |M| = " + str(len(M)))
        self.succ_cache[q] = map(lambda x: (q, x), M)
        return iter(self.succ_cache[q])
Example #4
0
 def strat_is_inductive(self, strat, use_trans=False):
     strat_dom = strat.exist_abstract(
         BDD.make_cube(imap(funcomp(BDD, symbol_lit),
                            chain(self.iterate_controllable_inputs(),
                                  self.iterate_uncontrollable_inputs()))))
     p_bdd = self.substitute_latches_next(strat_dom, use_trans=use_trans)
     return BDD.make_impl(strat, p_bdd) == BDD.true()
Example #5
0
    def extract_output_funs(self, strategy, care_set=None):
        """
        Calculate BDDs for output functions given non-deterministic winning
        strategy.
        """
        if care_set is None:
            care_set = BDD.true()

        output_models = dict()
        all_outputs = [BDD(x.lit) for x in self.iterate_controllable_inputs()]
        for c_symb in self.iterate_controllable_inputs():
            c = BDD(c_symb.lit)
            others = set(set(all_outputs) - set([c]))
            if others:
                others_cube = BDD.make_cube(others)
                c_arena = strategy.exist_abstract(others_cube)
            else:
                c_arena = strategy
            # pairs (x,u) in which c can be true
            can_be_true = c_arena.cofactor(c)
            # pairs (x,u) in which c can be false
            can_be_false = c_arena.cofactor(~c)
            must_be_true = (~can_be_false) & can_be_true
            must_be_false = (~can_be_true) & can_be_false
            local_care_set = care_set & (must_be_true | must_be_false)
            # Restrict operation:
            #   on care_set: must_be_true.restrict(care_set) <-> must_be_true
            c_model = min([must_be_true.safe_restrict(local_care_set),
                          (~must_be_false).safe_restrict(local_care_set)],
                          key=lambda x: x.dag_size())
            output_models[c_symb.lit] = c_model
            log.DBG_MSG("Size of function for " + str(c.get_index()) + " = " +
                        str(c_model.dag_size()))
            strategy &= BDD.make_eq(c, c_model)
        return output_models
Example #6
0
 def strat_is_inductive(self, strat, use_trans=False):
     strat_dom = strat.exist_abstract(
         BDD.make_cube(
             imap(
                 funcomp(BDD, symbol_lit),
                 chain(self.iterate_controllable_inputs(),
                       self.iterate_uncontrollable_inputs()))))
     p_bdd = self.substitute_latches_next(strat_dom, use_trans=use_trans)
     return BDD.make_impl(strat, p_bdd) == BDD.true()
Example #7
0
 def trans_rel_bdd(self):
     # check cache
     if self._cached_transition is not None:
         return self._cached_transition
     b = BDD.true()
     for x in self.iterate_latches():
         b &= BDD.make_eq(BDD(self.get_primed_var(x.lit)),
                          self.lit2bdd(x.next))
     self._cached_transition = b
     log.BDD_DMP(b, "Composed and cached the concrete transition relation.")
     return b
Example #8
0
 def trans_rel_bdd(self):
     # check cache
     if self._cached_transition is not None:
         return self._cached_transition
     b = BDD.true()
     for x in self.iterate_latches():
         b &= BDD.make_eq(BDD(self.get_primed_var(x.lit)),
                          self.lit2bdd(x.next))
     self._cached_transition = b
     log.BDD_DMP(b, "Composed and cached the concrete transition relation.")
     return b
Example #9
0
def comp_synth(games):
    s = BDD.true()
    cum_w = BDD.true()
    cnt = 0
    for game in games:
        assert isinstance(game, BackwardGame)
        w = backward_safety_synth(game)
        cnt += 1
        # short-circuit a negative response
        if w is None:
            log.DBG_MSG("Short-circuit exit after sub-game #" + str(cnt))
            return (None, None)
        if s is None:
            s = game.cpre(w, get_strat=True)
            cum_w = w
        else:
            s &= game.cpre(w, get_strat=True)
            cum_w &= w
        # sanity check before moving forward
        if (not s or not game.init() & s):
            return (None, None)
    # we must aggregate everything now
    log.DBG_MSG("Solved " + str(cnt) + " sub games.")
    return (cum_w, s)
Example #10
0
 def over_post_bdd(self, src_states_bdd, sys_strat=None):
     """ Over-approximated version of concrete post which can be done even
     without the transition relation """
     strat = BDD.true()
     if sys_strat is not None:
         strat &= sys_strat
     # to do this, we use an over-simplified transition relation, EXu,Xc
     b = BDD.true()
     for x in self.iterate_latches():
         temp = BDD.make_eq(BDD(self.get_primed_var(x.lit)),
                            self.lit2bdd(x.next))
         b &= temp.and_abstract(
             strat,
             BDD.make_cube(imap(
                 funcomp(BDD, symbol_lit),
                 self.iterate_controllable_inputs()
             )))
         b = b.restrict(src_states_bdd)
     b &= src_states_bdd
     b = b.exist_abstract(
         BDD.make_cube(imap(funcomp(BDD, symbol_lit),
                       chain(self.iterate_latches(),
                             self.iterate_uncontrollable_inputs()))))
     return self.unprime_latches_in_bdd(b)
Example #11
0
def comp_synth(games):
    s = BDD.true()
    cum_w = BDD.true()
    cnt = 0
    for game in games:
        assert isinstance(game, BackwardGame)
        w = backward_safety_synth(game)
        cnt += 1
        # short-circuit a negative response
        if w is None:
            log.DBG_MSG("Short-circuit exit after sub-game #" + str(cnt))
            return (None, None)
        if s is None:
            s = game.cpre(w, get_strat=True)
            cum_w = w
        else:
            s &= game.cpre(w, get_strat=True)
            cum_w &= w
        # sanity check before moving forward
        if (not s or not game.init() & s):
            return (None, None)
    # we must aggregate everything now
    log.DBG_MSG("Solved " + str(cnt) + " sub games.")
    return (cum_w, s)
Example #12
0
    def bdd2aig(self, a_bdd):
        """
        Walk given BDD node (recursively). If given input BDD requires
        intermediate AND gates for its representation, the function adds them.
        Literal representing given input BDD is `not` added to the spec.
        """
        if a_bdd in self.bdd_gate_cache:
            return self.bdd_gate_cache[a_bdd]

        if a_bdd.is_constant():
            res = int(a_bdd == BDD.true())   # in aiger 0/1 = False/True
            return res
        # get an index of variable,
        # all variables used in bdds also introduced in aiger,
        # except fake error latch literal,
        # but fake error latch will not be used in output functions (at least
        # we don't need this..)
        a_lit = a_bdd.get_index()
        assert (a_lit != self.error_fake_latch.lit),\
               ("using error latch in the " +
                "definition of output " +
                "function is not allowed")
        t_bdd = a_bdd.then_child()
        e_bdd = a_bdd.else_child()
        t_lit = self.bdd2aig(t_bdd)
        e_lit = self.bdd2aig(e_bdd)
        # ite(a_bdd, then_bdd, else_bdd)
        # = a*then + !a*else
        # = !(!(a*then) * !(!a*else))
        # -> in general case we need 3 more ANDs
        a_t_lit = self.get_optimized_and_lit(a_lit, t_lit)
        na_e_lit = self.get_optimized_and_lit(negate_lit(a_lit), e_lit)
        n_a_t_lit = negate_lit(a_t_lit)
        n_na_e_lit = negate_lit(na_e_lit)
        ite_lit = self.get_optimized_and_lit(n_a_t_lit, n_na_e_lit)
        res = negate_lit(ite_lit)
        if a_bdd.is_complement():
            res = negate_lit(res)
        # cache result
        self.bdd_gate_cache[a_bdd] = res
        return res
Example #13
0
def decompose(aig, argv):
    if argv.decomp == 1:
        if lit_is_negated(aig.error_fake_latch.next):
            log.DBG_MSG("Decomposition opt possible (BIG OR case)")
            (A, B) = aig.get_1l_land(strip_lit(aig.error_fake_latch.next))
            return imap(
                lambda a: ConcGame(BDDAIG(aig).short_error(a),
                                   use_trans=argv.use_trans),
                merge_some_signals(BDD.true(), A, aig, argv))
        else:
            (A, B) = aig.get_1l_land(aig.error_fake_latch.next)
            if not B:
                log.DBG_MSG("No decomposition opt possible")
                return None
            else:
                log.DBG_MSG("Decomposition opt possible (A ^ [C v D] case)")
                log.DBG_MSG(str(len(A)) + " AND leaves: " + str(A))
            # critical heuristic: which OR leaf do we distribute?
            # here I propose to choose the one with the most children
            b = B.pop()
            (C, D) = aig.get_1l_land(b)
            for bp in B:
                (Cp, Dp) = aig.get_1l_land(bp)
                if len(Cp) > len(C):
                    b = bp
                    C = Cp
            log.DBG_MSG("Chosen OR: " + str(b))
            rem_AND_leaves = filter(lambda x: strip_lit(x) != b, A)
            rdeps = set()
            for r in rem_AND_leaves:
                rdeps |= aig.get_lit_deps(strip_lit(r))
            log.DBG_MSG("Rem. AND leaves' deps: " + str(rdeps))
            cube = BDD.make_cube(map(aig.lit2bdd, rem_AND_leaves))
            log.DBG_MSG(
                str(len(C)) + " OR leaves: " + str(map(aig.get_lit_name, C)))
            return imap(
                lambda a: ConcGame(BDDAIG(aig).short_error(a),
                                   use_trans=argv.use_trans),
                merge_some_signals(cube, C, aig, argv))
    elif argv.decomp == 2:
        raise NotImplementedError
Example #14
0
    def bdd2aig(self, a_bdd):
        """
        Walk given BDD node (recursively). If given input BDD requires
        intermediate AND gates for its representation, the function adds them.
        Literal representing given input BDD is `not` added to the spec.
        """
        if a_bdd in self.bdd_gate_cache:
            return self.bdd_gate_cache[a_bdd]

        if a_bdd.is_constant():
            res = int(a_bdd == BDD.true())  # in aiger 0/1 = False/True
            return res
        # get an index of variable,
        # all variables used in bdds also introduced in aiger,
        # except fake error latch literal,
        # but fake error latch will not be used in output functions (at least
        # we don't need this..)
        a_lit = a_bdd.get_index()
        assert (a_lit != self.error_fake_latch.lit),\
               ("using error latch in the " +
                "definition of output " +
                "function is not allowed")
        t_bdd = a_bdd.then_child()
        e_bdd = a_bdd.else_child()
        t_lit = self.bdd2aig(t_bdd)
        e_lit = self.bdd2aig(e_bdd)
        # ite(a_bdd, then_bdd, else_bdd)
        # = a*then + !a*else
        # = !(!(a*then) * !(!a*else))
        # -> in general case we need 3 more ANDs
        a_t_lit = self.get_optimized_and_lit(a_lit, t_lit)
        na_e_lit = self.get_optimized_and_lit(negate_lit(a_lit), e_lit)
        n_a_t_lit = negate_lit(a_t_lit)
        n_na_e_lit = negate_lit(na_e_lit)
        ite_lit = self.get_optimized_and_lit(n_a_t_lit, n_na_e_lit)
        res = negate_lit(ite_lit)
        if a_bdd.is_complement():
            res = negate_lit(res)
        # cache result
        self.bdd_gate_cache[a_bdd] = res
        return res
Example #15
0
def decompose(aig, argv):
    if argv.decomp == 1:
        if lit_is_negated(aig.error_fake_latch.next):
            log.DBG_MSG("Decomposition opt possible (BIG OR case)")
            (A, B) = aig.get_1l_land(strip_lit(aig.error_fake_latch.next))
            return imap(lambda a: ConcGame(
                BDDAIG(aig).short_error(a),
                use_trans=argv.use_trans),
                merge_some_signals(BDD.true(), A, aig, argv))
        else:
            (A, B) = aig.get_1l_land(aig.error_fake_latch.next)
            if not B:
                log.DBG_MSG("No decomposition opt possible")
                return None
            else:
                log.DBG_MSG("Decomposition opt possible (A ^ [C v D] case)")
                log.DBG_MSG(str(len(A)) + " AND leaves: " + str(A))
            # critical heuristic: which OR leaf do we distribute?
            # here I propose to choose the one with the most children
            b = B.pop()
            (C, D) = aig.get_1l_land(b)
            for bp in B:
                (Cp, Dp) = aig.get_1l_land(bp)
                if len(Cp) > len(C):
                    b = bp
                    C = Cp
            log.DBG_MSG("Chosen OR: " + str(b))
            rem_AND_leaves = filter(lambda x: strip_lit(x) != b, A)
            rdeps = set()
            for r in rem_AND_leaves:
                rdeps |= aig.get_lit_deps(strip_lit(r))
            log.DBG_MSG("Rem. AND leaves' deps: " + str(rdeps))
            cube = BDD.make_cube(map(aig.lit2bdd, rem_AND_leaves))
            log.DBG_MSG(str(len(C)) + " OR leaves: " +
                        str(map(aig.get_lit_name, C)))
            return imap(lambda a: ConcGame(
                BDDAIG(aig).short_error(a),
                use_trans=argv.use_trans), merge_some_signals(cube, C, aig,
                                                              argv))
    elif argv.decomp == 2:
        raise NotImplementedError
Example #16
0
    def extract_output_funs(self, strategy, care_set=None):
        """
        Calculate BDDs for output functions given non-deterministic winning
        strategy.
        """
        if care_set is None:
            care_set = BDD.true()

        output_models = dict()
        all_outputs = [BDD(x.lit) for x in self.iterate_controllable_inputs()]
        for c_symb in self.iterate_controllable_inputs():
            c = BDD(c_symb.lit)
            others = set(set(all_outputs) - set([c]))
            if others:
                others_cube = BDD.make_cube(others)
                c_arena = strategy.exist_abstract(others_cube)
            else:
                c_arena = strategy
            # pairs (x,u) in which c can be true
            can_be_true = c_arena.cofactor(c)
            # pairs (x,u) in which c can be false
            can_be_false = c_arena.cofactor(~c)
            must_be_true = (~can_be_false) & can_be_true
            must_be_false = (~can_be_true) & can_be_false
            local_care_set = care_set & (must_be_true | must_be_false)
            # Restrict operation:
            #   on care_set: must_be_true.restrict(care_set) <-> must_be_true
            c_model = min([
                must_be_true.safe_restrict(local_care_set),
                (~must_be_false).safe_restrict(local_care_set)
            ],
                          key=lambda x: x.dag_size())
            output_models[c_symb.lit] = c_model
            log.DBG_MSG("Size of function for " + str(c.get_index()) + " = " +
                        str(c_model.dag_size()))
            strategy &= BDD.make_eq(c, c_model)
        return output_models
Example #17
0
 def init_state_bdd(self):
     b = BDD.true()
     for x in self.iterate_latches():
         b &= ~BDD(x.lit)
     return b
Example #18
0
def comp_synth4(games, gen_game):
    s = None
    cum_s = None
    cum_w = None
    cnt = 0
    triple_list = []
    for game in games:
        assert isinstance(game, BackwardGame)
        w = backward_safety_synth(game)
        cnt += 1
        # short-circuit a negative response
        if w is None:
            log.DBG_MSG("Short-circuit exit 1 after sub-game #" + str(cnt))
            return None
        s = game.cpre(w, get_strat=True)
        if cum_s is None:
            cum_s = s
            cum_w = w
        else:
            cum_s &= s
            cum_w &= w
        # another short-circuit exit
        if (not cum_s or not game.init() & cum_s):
            log.DBG_MSG("Short-circuit exit 2 after sub-game #" + str(cnt))
            return None
        triple_list.append((game, s, w))
    log.DBG_MSG("Solved " + str(cnt) + " sub games.")
    # lets simplify transition functions
    gen_game.aig.restrict_latch_next_funs(cum_s)
    # what comes next is a fixpoint computation using a UPRE
    # step at a time in the global game and using it to get more
    # information from the local sub-games
    lose = BDD.true()
    lose_next = ~cum_w | gen_game.error()
    while lose_next != lose:
        lose = lose_next
        log.DBG_MSG("Doing global UPRE")
        lose_next = lose | gen_game.upre(lose)
        for i in range(len(triple_list)):
            wt = triple_list[i][2]
            gamet = triple_list[i][0]
            local_deps = set([x.lit for x in gamet.aig.iterate_latches()])
            rem_lats = gen_game.aig.get_bdd_latch_deps(lose_next) - local_deps
            pt = lose_next
            if rem_lats:
                pt = lose_next.univ_abstract(BDD.make_cube(map(BDD, rem_lats)))
            # log.BDD_DMP(lose_next, "global losing area iterate")
            # log.BDD_DMP(pt, "new losing area")
            assert BDD.make_impl(~wt, pt) == BDD.true()
            if BDD.make_impl(pt, ~wt) != BDD.true():
                gamet.short_error = pt
                wt = backward_safety_synth(gamet)
                if (wt is None or not gamet.init() & wt):
                    log.DBG_MSG("Short-circuit exit 3")
                    return None
                st = gamet.cpre(wt, get_strat=True)
                gen_game.aig.restrict_latch_next_funs(wt)
                triple_list[i] = (gamet, st, wt)
        for t in triple_list:
            lose_next |= ~t[2]
    # after the fixpoint has been reached we can compute the error
    win = ~lose
    if (not win or not gen_game.init() & win):
        return None
    else:
        return win
Example #19
0
 def init_state_bdd(self):
     b = BDD.true()
     for x in self.iterate_latches():
         b &= ~BDD(x.lit)
     return b
Example #20
0
def comp_synth4(games, gen_game):
    s = None
    cum_s = None
    cum_w = None
    cnt = 0
    triple_list = []
    for game in games:
        assert isinstance(game, BackwardGame)
        w = backward_safety_synth(game)
        cnt += 1
        # short-circuit a negative response
        if w is None:
            log.DBG_MSG("Short-circuit exit 1 after sub-game #" + str(cnt))
            return None
        s = game.cpre(w, get_strat=True)
        if cum_s is None:
            cum_s = s
            cum_w = w
        else:
            cum_s &= s
            cum_w &= w
        # another short-circuit exit
        if (not cum_s or not game.init() & cum_s):
            log.DBG_MSG("Short-circuit exit 2 after sub-game #" + str(cnt))
            return None
        triple_list.append((game, s, w))
    log.DBG_MSG("Solved " + str(cnt) + " sub games.")
    # lets simplify transition functions
    gen_game.aig.restrict_latch_next_funs(cum_s)
    # what comes next is a fixpoint computation using a UPRE
    # step at a time in the global game and using it to get more
    # information from the local sub-games
    lose = BDD.true()
    lose_next = ~cum_w | gen_game.error()
    while lose_next != lose:
        lose = lose_next
        log.DBG_MSG("Doing global UPRE")
        lose_next = lose | gen_game.upre(lose)
        for i in range(len(triple_list)):
            wt = triple_list[i][2]
            gamet = triple_list[i][0]
            local_deps = set([x.lit for x in gamet.aig.iterate_latches()])
            rem_lats = gen_game.aig.get_bdd_latch_deps(lose_next) - local_deps
            pt = lose_next
            if rem_lats:
                pt = lose_next.univ_abstract(
                    BDD.make_cube(map(BDD, rem_lats)))
            # log.BDD_DMP(lose_next, "global losing area iterate")
            # log.BDD_DMP(pt, "new losing area")
            assert BDD.make_impl(~wt, pt) == BDD.true()
            if BDD.make_impl(pt, ~wt) != BDD.true():
                gamet.short_error = pt
                wt = backward_safety_synth(gamet)
                if (wt is None or not gamet.init() & wt):
                    log.DBG_MSG("Short-circuit exit 3")
                    return None
                st = gamet.cpre(wt, get_strat=True)
                gen_game.aig.restrict_latch_next_funs(wt)
                triple_list[i] = (gamet, st, wt)
        for t in triple_list:
            lose_next |= ~t[2]
    # after the fixpoint has been reached we can compute the error
    win = ~lose
    if (not win or not gen_game.init() & win):
        return None
    else:
        return win