Пример #1
0
def merge_some_signals(cube, C, aig, argv):
    # TODO: there must be a more pythonic way of doing all of this
    log.LOG_MSG(str(len(C)) + " sub-games originally")
    cube_deps = aig.get_bdd_deps(cube)
    dep_map = dict()
    for c in C:
        deps = frozenset(cube_deps | aig.get_lit_deps(c))
        log.DBG_MSG("Current deps: " + str(deps))
        found = False
        for key in dep_map:
            if key >= deps:
                log.DBG_MSG("Some key subsumes deps")
                dep_map[key] &= aig.lit2bdd(c)
                found = True
                break
            elif key <= deps:
                log.DBG_MSG("New deps subsumes some key")
                if deps in dep_map:
                    log.DBG_MSG("AND... the deps existed already")
                    dep_map[deps] &= dep_map[key] & aig.lit2bdd(c)
                else:
                    dep_map[deps] = dep_map[key] & aig.lit2bdd(c)
                del dep_map[key]
                found = True
                break
        if not found:
            log.DBG_MSG("Adding a new dict element")
            dep_map[deps] = aig.lit2bdd(c)
    log.LOG_MSG(str(len(dep_map.keys())) + " sub-games after incl. red.")
    for key in dep_map:
        yield ~dep_map[key] & cube
Пример #2
0
def subgame_mapper(games, aig):
    s = None
    cum_s = None
    cnt = 0
    pair_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
        else:
            cum_s &= s
        # 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
        pair_list.append((game, s, w))
    log.DBG_MSG("Solved " + str(cnt) + " sub games.")
    # lets simplify transition functions
    # aig.restrict_latch_next_funs(cum_s)
    return pair_list
Пример #3
0
def fixpoint(s, fun, early_exit=never):
    """ fixpoint of monotone function starting from s """
    prev = None
    cur = s
    cnt = 0
    while prev is None or prev != cur:
        prev = cur
        cur = fun(prev)
        cnt += 1
        if early_exit(cur):
            log.DBG_MSG("Early exit after " + str(cnt) + " steps.")
            return cur
    log.DBG_MSG("Fixpoint reached after " + str(cnt) + " steps.")
    return cur
Пример #4
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])
Пример #5
0
def subgame_reducer(games, aig, argv, a=None, b=None, c=None):
    assert games
    if a is None:
        a = 2
    if b is None:
        b = -1
    if c is None:
        c = -1
    while len(games) >= 2:
        triple_list = []
        # we first compute an fij function for all pairs
        for i in range(0, len(games) - 1):
            for j in range(i + 1, len(games)):
                li = set(aig.get_bdd_latch_deps(games[i][1]))
                lj = set(aig.get_bdd_latch_deps(games[j][1]))
                cij = len(li & lj)
                nij = len(li | lj)
                sij = (games[i][1] & games[j][1]).dag_size()
                triple_list.append((i, j, a * cij + b * nij + c * sij))
        # now we get the best pair according to the fij function
        (i, j, val) = max(triple_list, key=lambda x: x[2])
        log.DBG_MSG("We must reduce games " + str(i) + " and " + str(j))
        # we must reduce games i and j now
        game = ConcGame(BDDAIG(aig).short_error(~(games[i][1] & games[j][1])),
                        use_trans=argv.use_trans)
        w = backward_safety_synth(game)
        if w is None:
            return None
        else:
            s = game.cpre(w, get_strat=True)
        games[i] = (game, s, w)
        games.pop(j)
        # lets simplify the transition relations
        # aig.restrict_latch_next_funs(s)
    return games[0][2]
Пример #6
0
    def add_clause(self, lits, avoid_checks=False):
        # just a simple check to get rid of False in clauses and clauses that
        # are trivially true
        if 1 in lits:
            log.DBG_MSG("The clause includes lit 1. It is trivially true.")
            return self
        if 0 in lits:
            log.DBG_MSG("The clause includes lit 0. I removed it.")
            lits = [l for l in lits if l != 0]
#        if not avoid_checks:
#            if next((c for c in self.clauses if c <= lits), None):
#                log.DBG_MSG("The clause is subsumed by an existing one.")
#                return
#            self.clauses = set(filter(lambda x: not lits <= x, self.clauses))
        self.clauses.add(frozenset(lits))
        return self
Пример #7
0
def extract_output_funcs(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 = get_controllable_inputs_bdds()
    for c in get_controllable_inputs_bdds():
        others = set(set(all_outputs) - set([c]))
        if others:
            others_cube = bdd.get_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=bdd.dag_size)
        output_models[c] = 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
Пример #8
0
def compose_abs_transition_bdd():
    global cached_abs_transition, procd_blocks

    # check cache
    if cached_abs_transition is None:
        log.DBG_MSG("Rebuilding abstract transition relation")
        for b in preds.abs_blocks:
            procd_blocks[b] = False
        c = bdd.true()
    else:
        c = cached_abs_transition

    latches = [x.lit for x in iterate_latches_and_error()]
    latches_bdd = bdd.get_cube(get_all_latches_as_bdds())
    latch_funs = [
        get_bdd_for_aig_lit(x.next) for x in iterate_latches_and_error()
    ]
    for b in preds.abs_blocks:
        if b not in procd_blocks or not procd_blocks[b]:
            procd_blocks[b] = True
            temp = bdd.make_eq(bdd.BDD(get_primed_variable(b)),
                               preds.block_to_bdd[b])
            c &= temp.compose(latches, latch_funs)
    # cache c
    cached_abs_transition = c
    return c.and_abstract(compose_abs_eq_bdd(), latches_bdd)
Пример #9
0
def synthesize():
    if use_trans:
        log.register_sum("trans_time",
                         "Time spent building transition relation: ")
        log.start_clock()
        compose_transition_bdd()
        log.stop_clock("trans_time")
    init_state_bdd = compose_init_state_bdd()
    error_bdd = bdd.BDD(error_fake_latch.lit)
    reach_over = []
    # use abstraction to minimize state space exploration
    if ini_reach:
        initial_abstraction()
        for j in range(ini_reach):
            preds.drop_latches()
            # add some latches
            make_vis = (aig.num_latches() + 1) // ini_reach_latches
            log.DBG_MSG("Making visible " + str(make_vis) + " latches")
            for i in range(make_vis):
                preds.loc_red()
            log.DBG_MSG("Computing reachable states over-app")
            abs_reach_region = fp(compose_abs_init_state_bdd(),
                                  fun=lambda x: x | post_bdd_abs(x))
            reach_over.append(gamma(abs_reach_region))
    # get the winning region for controller

    def min_pre(s):
        for r in reach_over:
            s = s.restrict(r)
        result = pre_env_bdd(s)
        for r in reach_over:
            result = result.restrict(r)
        return s | result

    log.DBG_MSG("Computing fixpoint of UPRE")
    win_region = ~fp(error_bdd,
                     fun=min_pre,
                     early_exit=lambda x: x & init_state_bdd != bdd.false())

    if win_region & init_state_bdd == bdd.false():
        log.LOG_MSG("The spec is unrealizable.")
        log.LOG_ACCUM()
        return (None, None)
    else:
        log.LOG_MSG("The spec is realizable.")
        log.LOG_ACCUM()
        return (win_region, reach_over)
Пример #10
0
 def introduce_error_latch(self):
     if self.error_fake_latch is not None:
         return
     self.error_fake_latch = new_aiger_symbol()
     error_symbol = self.get_err_symbol()
     self.error_fake_latch.lit = self.next_lit()
     self.error_fake_latch.name = "fake_error_latch"
     self.error_fake_latch.next = error_symbol.lit
     log.DBG_MSG("Error fake latch = " + str(self.error_fake_latch.lit))
Пример #11
0
 def __init__(self, aiger_file_name, intro_error_latch=False):
     self.spec = aiger_init()
     err = aiger_open_and_read_from_file(self.spec, aiger_file_name)
     assert not err, err
     # introduce a fake latch for the error and call the given hook
     self.error_fake_latch = None
     if intro_error_latch:
         self.introduce_error_latch()
     # initialize caches
     self._1l_land_cache = dict()
     self._deps_cache = dict()
     # dump some info about the spec
     if not log.debug:
         return
     latches = [x.lit for x in self.iterate_latches()]
     log.DBG_MSG(str(len(latches)) + " Latches: " + str(latches))
     uinputs = [x.lit for x in self.iterate_uncontrollable_inputs()]
     log.DBG_MSG(str(len(uinputs)) + " U. Inputs: " + str(uinputs))
     cinputs = [x.lit for x in self.iterate_controllable_inputs()]
     log.DBG_MSG(str(len(cinputs)) + " C. Inputs: " + str(cinputs))
Пример #12
0
 def short_error(self, b):
     nu_bddaig = BDDAIG(aig=self)
     nu_bddaig.set_lit2bdd(self.error_fake_latch.next, b)
     latch_deps = self.get_bdd_latch_deps(b)
     if log.debug:
         not_deps = [
             l.lit for l in self.iterate_latches()
             if l.lit not in latch_deps
         ]
         log.DBG_MSG(str(len(not_deps)) + " Latches not needed")
     nu_bddaig.latch_restr = latch_deps
     nu_bddaig.restrict_latch_next_funs(~b)
     return nu_bddaig
Пример #13
0
def backward_safety_synth(game):
    assert isinstance(game, BackwardGame)

    init_state = game.init()
    error_states = game.error()
    log.DBG_MSG("Computing fixpoint of UPRE.")
    win_region = ~fixpoint(error_states,
                           fun=lambda x: x | game.upre(x),
                           early_exit=lambda x: x & init_state)

    if not (win_region & init_state):
        return None
    else:
        return win_region
Пример #14
0
 def declare_winner(controllable, conc_lose):
     log.LOG_MSG("Nr. of predicates: " + str(len(preds.abs_blocks)))
     log.LOG_ACCUM()
     if controllable:
         log.LOG_MSG("The spec is realizable.")
         if compute_win_region:
             # make sure we reached the fixpoint
             log.DBG_MSG("Get winning region")
             return (~fp(bdd.BDD(error_fake_latch.lit) | conc_lose,
                         fun=lambda x: x | pre_env_bdd(x)), [])
         else:
             return (~conc_lose, [])
         log.LOG_MSG("The spec is unrealizable.")
         return (None, [])
Пример #15
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)
Пример #16
0
def initial_abstraction():
    global preds

    introduce_error_latch()
    all_latches = [x.lit for x in aig.iterate_latches()]
    all_latches_and_error = list(all_latches + [error_fake_latch.lit])
    all_latches_and_error_next = (
        [get_bdd_for_aig_lit(x.next) for x in aig.iterate_latches()] +
        [get_bdd_for_aig_lit(error_fake_latch.next)])
    preds = spred.SmartPreds(supp=all_latches_and_error,
                             supp_next=all_latches_and_error_next)
    preds.add_fixed_pred("unsafe", bdd.BDD(error_fake_latch.lit))
    preds.add_fixed_pred("init",
                         bdd.get_clause([bdd.BDD(l) for l in all_latches]))
    log.DBG_MSG("Initial abstraction of the system computed.")
    return True
Пример #17
0
 def _read_delays(self, time_file_name):
     delays = dict()
     latches = [x.lit for x in self.aig.iterate_latches()]
     with open(time_file_name, 'r') as fp:
         for l in range(len(latches)):
             s = fp.readline()
             if s <> "":
                 #print "Doing line:<{0}>".format(s)
                 #print s.split(" ")
                 si = map(lambda x: int(x), s.split(" "))
                 assert (len(si) == 2)
                 delays[latches[l]] = (si[0] * self.factor,
                                       si[1] * self.factor)
             else:
                 delays[latches[l]] = (self.factor, self.factor)
     log.DBG_MSG("Latch delays: " + str(delays))
     return delays
Пример #18
0
def forward_safety_synth(game):
    assert isinstance(game, ForwardGame)
    init_state = game.init()
    error_states = game.error()
    tracker = game.visit_tracker()
    depend = dict()
    depend[init_state] = set()
    waiting = [(init_state, game.upost(init_state))]
    while waiting and not tracker.is_in_attr(init_state):
        (s, sp_iter) = waiting.pop()
        try:
            sp = next(sp_iter)
        except StopIteration:
            continue  # nothing to do here
        # push the rest of the iterator back into the stack
        waiting.append((s, sp_iter))
        # process s, sp_iter
        if not tracker.is_visited(sp):
            tracker.visit(sp)
            tracker.mark_in_attr(
                sp,
                game.is_env_state(sp) and bool(sp & error_states))
            if sp in depend:
                depend[sp].add((s, iter([sp])))
            else:
                depend[sp] = set([(s, iter([sp]))])
            if tracker.is_in_attr(sp):
                waiting.append((s, iter([sp])))
            else:
                if game.is_env_state(sp):
                    waiting.append((sp, game.upost(sp)))
                else:
                    waiting.append((sp, game.cpost(sp)))
        else:
            local_lose = any(imap(tracker.is_in_attr, game.upost(s)))\
                if game.is_env_state(s)\
                else all(imap(tracker.is_in_attr, game.cpost(s)))
            if local_lose:
                tracker.mark_in_attr(s, True)
                waiting.extend(depend[s])
            if not tracker.is_in_attr(sp):
                depend[sp].add((s, sp))
    log.DBG_MSG("OTFUR, losing[init_state] = " +
                str(tracker.is_in_attr(init_state)))
    return None if tracker.is_in_attr(init_state) else True
Пример #19
0
def compose_abs_eq_bdd():
    global cached_abs_eq, abs_eq_procd_blocks

    # check cache
    if cached_abs_eq is None:
        log.DBG_MSG("Rebuilding abs_eq")
        for b in preds.abs_blocks:
            abs_eq_procd_blocks[b] = False
        c = bdd.true()
    else:
        c = cached_abs_eq

    for b in preds.abs_blocks:
        if b not in procd_blocks or not procd_blocks[b]:
            c &= bdd.make_eq(bdd.BDD(b), preds.block_to_bdd[b])
    # cache c
    cached_abs_eq = c
    return c
Пример #20
0
def update_block_funs():
    global cached_block_funs, block_funs

    latches = [x.lit for x in iterate_latches_and_error()]
    latch_funs = [
        get_bdd_for_aig_lit(x.next) for x in iterate_latches_and_error()
    ]
    # check cache
    if cached_block_funs is None:
        log.DBG_MSG("Rebuilding block_funs")
        block_funs = dict()
        for b in preds.abs_blocks:
            block_funs[b] = gamma(preds.block_to_bdd[b]).compose(
                latches, latch_funs)
    else:
        for b in preds.abs_blocks:
            if b not in block_funs:
                block_funs[b] = gamma(preds.block_to_bdd[b])
                block_funs[b] = block_funs[b].compose(latches, latch_funs)
    # set cache
    cached_block_funs = bdd.true()
Пример #21
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
Пример #22
0
 def cpost(self, s):
     assert isinstance(s, tuple)
     q = s[0]
     au = s[1]
     if s in self.succ_cache:
         L = self.succ_cache[s]
     else:
         L = BDD.make_cube(
             imap(lambda x: BDD.make_eq(BDD(x.lit),
                                        self.aig.lit2bdd(x.next)
                                        .and_abstract(q & au,
                                                      self.latch_cube &
                                                      self.uinputs_cube)),
                  self.aig.iterate_latches()))\
             .exist_abstract(self.cinputs_cube)
         self.succ_cache[s] = L
     M = set()
     while L != BDD.false():
         l = L.get_one_minterm(self.latches)
         L &= ~l
         self.Venv[l] = True
         M.add(l)
     log.DBG_MSG("Cpost |M| = " + str(len(M)))
     return iter(M)
Пример #23
0
def main(aiger_file_name, out_file):
    aig.parse_into_spec(aiger_file_name)
    log.DBG_MSG("AIG spec file parsed")
    log.LOG_MSG("Nr. of latches: " + str(aig.num_latches()))
    log.DBG_MSG("Latches: " + str([x.lit
                                   for x in iterate_latches_and_error()]))
    log.DBG_MSG("U. Inputs: " +
                str([x.lit for x in aig.iterate_uncontrollable_inputs()]))
    log.DBG_MSG("C. Inputs: " +
                str([x.lit for x in aig.iterate_controllable_inputs()]))
    # realizability and preliminary synthesis
    if use_abs:
        (win_region, reach_over) = abs_synthesis(out_file is not None)
    else:
        (win_region, reach_over) = synthesize()

    if out_file and win_region:
        log.LOG_MSG("Win region bdd node count = " +
                    str(win_region.dag_size()))
        strategy = single_pre_sys_bdd(win_region, get_strat=True)
        log.LOG_MSG("Strategy bdd node count = " + str(strategy.dag_size()))
        func_by_var = extract_output_funcs(strategy, win_region)
        # attempt to minimize the winning region
        for r in reach_over:
            for (c_bdd, func_bdd) in func_by_var.items():
                func_by_var[c_bdd] = func_bdd.safe_restrict(r)
                log.DBG_MSG("Min'd version size " +
                            str(func_by_var[c_bdd].dag_size()))
        # attempt to minimize the winning region
        if min_win:
            bdd.disable_reorder()
            strategy = bdd.true()
            for (c_bdd, func_bdd) in func_by_var.items():
                strategy &= bdd.make_eq(c_bdd, func_bdd)
            win_region = fp(compose_init_state_bdd(),
                            fun=lambda x: x | post_bdd(x, strategy))
            for (c_bdd, func_bdd) in func_by_var.items():
                func_by_var[c_bdd] = func_bdd.safe_restrict(win_region)
                log.DBG_MSG("Min'd version size " +
                            str(func_by_var[c_bdd].dag_size()))
        # model check?
        if model_check:
            strategy = bdd.true()
            for (c_bdd, func_bdd) in func_by_var.items():
                strategy &= bdd.make_eq(c_bdd, func_bdd)
            assert (fp(bdd.BDD(error_fake_latch.lit),
                       fun=lambda x: x | single_pre_bdd(x, strategy))
                    & compose_init_state_bdd()) == bdd.false()
        # print out the strategy
        total_dag = 0
        for (c_bdd, func_bdd) in func_by_var.items():
            total_dag += func_bdd.dag_size()
            model_to_aiger(c_bdd, func_bdd)
        log.LOG_MSG("Sum of func dag sizes = " + str(total_dag))
        log.LOG_MSG("# of added gates = " + str(len(bdd_gate_cache)))
        aig.write_spec(out_file)
        return True
    elif win_region:
        return True
    else:
        return False
Пример #24
0
 def restrict_latch_next_funs(self, b):
     log.DBG_MSG("Restricting next funs")
     for l in self.iterate_latches():
         if l != self.error_fake_latch:
             self.set_lit2bdd(l.next, self.lit2bdd(l.next).safe_restrict(b))
Пример #25
0
def synth_from_spec(aig, argv):
    # Explicit approach
    if argv.use_symb:
        assert argv.out_file is None
        symgame = SymblicitGame(aig)
        w = forward_safety_synth(symgame)
    # Symbolic approach with compositional opts
    elif argv.decomp is not None:
        game_it = decompose(aig, argv)
        # if there was no decomposition possible then call simple
        # solver
        if game_it is None:
            argv.decomp = None
            return synth_from_spec(aig, argv)
        if argv.comp_algo == 1:
            # solve and aggregate sub-games
            (w, strat) = comp_synth(game_it)
            # back to the general game
            if w is None:
                return False
            log.DBG_MSG("Interm. win region bdd node count = " +
                        str(w.dag_size()))
            game = ConcGame(BDDAIG(aig).short_error(~strat),
                            use_trans=argv.use_trans)
            w = backward_safety_synth(game)
        elif argv.comp_algo == 2:
            games_mapped = subgame_mapper(game_it, aig)
            # local aggregation yields None if short-circ'd
            if games_mapped is None:
                return False
            w = subgame_reducer(games_mapped, aig, argv)
        elif argv.comp_algo == 3:
            # solve games by up-down algo
            gen_game = ConcGame(aig, use_trans=argv.use_trans)
            w = comp_synth3(game_it, gen_game)
        elif argv.comp_algo == 4:
            # solve games by up-down algo
            gen_game = ConcGame(aig, use_trans=argv.use_trans)
            w = comp_synth4(game_it, gen_game)
        else:
            raise NotImplementedError()
    # Symbolic approach (avoiding compositional opts)
    else:
        game = ConcGame(aig, use_trans=argv.use_trans)
        w = backward_safety_synth(game)
    # final check
    if w is None:
        return False
    log.DBG_MSG("Win region bdd node count = " + str(w.dag_size()))
    # synthesis from the realizability analysis
    if w is not None:
        if argv.out_file is not None:
            log.DBG_MSG("Win region bdd node count = " + str(w.dag_size()))
            c_input_info = []
            n_strategy = aig.cpre_bdd(w, get_strat=True)
            func_per_output = aig.extract_output_funs(n_strategy, care_set=w)
            if argv.only_transducer:
                for c in aig.iterate_controllable_inputs():
                    c_input_info.append((c.lit, c.name))
            for (c, func_bdd) in func_per_output.items():
                aig.input2and(c, aig.bdd2aig(func_bdd))
            if argv.only_transducer:
                aig.remove_outputs()
                for (l, n) in c_input_info:
                    aig.add_output(l, n)
            aig.write_spec(argv.out_file)
        return True
    else:
        return False
Пример #26
0
def abs_synthesis(compute_win_region=False):
    global use_abs

    # declare winner
    def declare_winner(controllable, conc_lose):
        log.LOG_MSG("Nr. of predicates: " + str(len(preds.abs_blocks)))
        log.LOG_ACCUM()
        if controllable:
            log.LOG_MSG("The spec is realizable.")
            if compute_win_region:
                # make sure we reached the fixpoint
                log.DBG_MSG("Get winning region")
                return (~fp(bdd.BDD(error_fake_latch.lit) | conc_lose,
                            fun=lambda x: x | pre_env_bdd(x)), [])
            else:
                return (~conc_lose, [])
            log.LOG_MSG("The spec is unrealizable.")
            return (None, [])

    # make sure that we have something to abstract
    if aig.num_latches() == 0:
        log.WRN_MSG("No latches in spec. Defaulting to regular synthesis.")
        use_abs = False
        return synthesize()
    # update loss steps
    local_loss_steps = (aig.num_latches() + 1) // loss_steps
    log.DBG_MSG("Loss steps = " + str(local_loss_steps))
    # registered quants
    steps = 0
    log.register_sum("ref_cnt", "Nr. of refinements: ")
    log.register_sum("abs_time", "Time spent abstracting: ")
    log.register_sum("uabs_time", "Time spent computing under-app of fp: ")
    log.register_sum("oabs_time", "Time spent exhausting info of over-app: ")
    log.register_average("unsafe_bdd_size",
                         "Average unsafe iterate bdd size: ")
    # create the abstract game
    initial_abstraction()
    error_bdd = alpha_under(bdd.BDD(error_fake_latch.lit))
    # add some latches
    if ini_latch:
        make_vis = (aig.num_latches() + 1) // ini_latch
        log.DBG_MSG("Making visible " + str(make_vis) + " latches")
        for i in range(make_vis):
            preds.loc_red()
    # first over-approx of the reachable region
    reachable_bdd = bdd.true()

    # The REAL algo
    while True:
        log.start_clock()
        if use_trans:
            transition_bdd = compose_abs_transition_bdd()
            log.BDD_DMP(transition_bdd, "transition relation")
        init_state_bdd = compose_abs_init_state_bdd()
        log.BDD_DMP(init_state_bdd, "initial state set")
        log.BDD_DMP(error_bdd, "unsafe state set")
        log.stop_clock("abs_time")

        # STEP 1: check if under-approx is losing
        log.DBG_MSG("Computing over approx of FP")
        log.start_clock()
        under_fp = fp(error_bdd,
                      fun=lambda x:
                      (reachable_bdd & (x | pre_env_bdd_uabs(x))))
        log.stop_clock("uabs_time")
        if (init_state_bdd & under_fp) != bdd.false():
            return declare_winner(False)

        # STEP 2: exhaust information from the abstract game, i.e.
        # update the reachability information we have
        log.start_clock()
        prev_reach = bdd.false()
        reach = reachable_bdd
        while prev_reach != reach:
            prev_reach = reach
            # STEP 2.1: check if the over-approx is winning
            log.DBG_MSG("Computing over approx of FP")
            over_fp = fp(under_fp,
                         fun=lambda x: (reach & (x | pre_env_bdd_abs(x))))
            if (over_fp & init_state_bdd) == bdd.false():
                log.DBG_MSG("FP of the over-approx losing region not initial")
                return declare_winner(True, gamma(under_fp))
            # if there is no early exit we compute a strategy for Env
            env_strats = pre_env_bdd_abs(over_fp, get_strat=True)
            log.DBG_MSG("Computing over approx of Reach")
            reach = fp(init_state_bdd,
                       fun=lambda x:
                       (reach & (x | post_bdd_abs(x, env_strats))))
        log.stop_clock("oabs_time")

        # STEP 3: refine or declare controllable
        log.DBG_MSG("Concretizing the strategy of Env")
        conc_env_strats = gamma(env_strats)
        conc_reach = gamma(reach)
        conc_under_fp = gamma(under_fp)
        log.DBG_MSG("Taking one step of UPRE in the concrete game")
        conc_step = single_pre_env_bdd(conc_under_fp,
                                       env_strat=conc_env_strats)
        conc_step &= conc_reach
        if bdd.make_impl(conc_step, conc_under_fp) == bdd.true():
            log.DBG_MSG("The concrete step revealed we are at the FP")
            return declare_winner(True, conc_under_fp)
        else:
            # drop latches every number of steps
            reset = False
            if (steps != 0 and steps % local_loss_steps == 0):
                log.DBG_MSG("Dropping all visible latches!")
                reset = preds.drop_latches()
            # add new predicates and reset caches if necessary
            nu_losing_region = conc_step | conc_under_fp
            reset |= preds.add_fixed_pred("reach", conc_reach)
            reset |= preds.add_fixed_pred("unsafe", nu_losing_region)
            # find interesting set of latches
            log.DBG_MSG("Localization reduction step.")
            reset |= preds.loc_red(not_imply=nu_losing_region)
            log.DBG_MSG("# of predicates = " + str(len(preds.abs_blocks)))
            if reset:
                reset_caches()
            # update error bdd
            log.push_accumulated("unsafe_bdd_size",
                                 nu_losing_region.dag_size())
            error_bdd = alpha_under(nu_losing_region)
            # update reachable area
            reachable_bdd = alpha_over(conc_reach)
            steps += 1
            log.push_accumulated("ref_cnt", 1)
Пример #27
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