Beispiel #1
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)
Beispiel #2
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
Beispiel #3
0
def over_post_bdd_abs(src_states_bdd, env_strat=None):
    # make sure the block_funs are current
    update_block_funs()
    # take the step forward and get rid of latches
    conc_src = gamma(src_states_bdd)
    if env_strat is not None:
        conc_strat = gamma(env_strat)
    else:
        conc_strat = bdd.true()
    # to do this, we use an over-simplified transition relation, EXu,Xc
    simple_trans = bdd.true()
    for b in preds.abs_blocks:
        trans_b = bdd.make_eq(bdd.BDD(b), block_funs[b])
        simple_trans &= trans_b.exist_abstract(
            bdd.get_cube(get_controllable_inputs_bdds()))
    simple_trans &= conc_strat & conc_src
    return simple_trans.exist_abstract(
        bdd.get_cube(get_all_latches_as_bdds() +
                     get_uncontrollable_inputs_bdds()))
Beispiel #4
0
def single_post_bdd(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 iterate_latches_and_error():
        temp = bdd.make_eq(bdd.BDD(get_primed_variable(x.lit)),
                           get_bdd_for_aig_lit(x.next))
        b &= temp.and_abstract(strat,
                               bdd.get_cube(get_controllable_inputs_bdds()))
        if restrict_like_crazy:
            b = b.restrict(src_states_bdd)
    b &= src_states_bdd
    b = b.exist_abstract(
        bdd.get_cube(get_all_latches_as_bdds() +
                     get_uncontrollable_inputs_bdds()))
    return unprime_latches_in_bdd(b)
Beispiel #5
0
 def to_bdd(self):
     b = bdd.true()
     for c in self.clauses:
         nu_clause = bdd.false()
         for l in c:
             nu_var = bdd.BDD(abs(l))
             if l < 0:
                 nu_var = ~nu_var
             nu_clause |= nu_var
         b &= nu_clause
     return b
Beispiel #6
0
def compose_transition_bdd():
    global cached_transition

    # check cache
    if cached_transition:
        return cached_transition
    b = bdd.true()
    for x in iterate_latches_and_error():
        b &= bdd.make_eq(bdd.BDD(get_primed_variable(x.lit)),
                         get_bdd_for_aig_lit(x.next))
    cached_transition = b
    log.BDD_DMP(b, "Composed and cached the concrete transition relation")
    return b
Beispiel #7
0
def single_pre_bdd(dst_states_bdd, strat=None):
    if strat is None:
        strat = bdd.true()

    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()]
    if restrict_like_crazy:
        latch_funs = [x.restrict(~dst_states_bdd) for x in latch_funs]
    # take a transition step backwards
    p_bdd = dst_states_bdd.compose(latches, latch_funs)
    # use the given strategy
    p_bdd &= strat
    p_bdd = p_bdd.exist_abstract(
        bdd.get_cube(get_uncontrollable_inputs_bdds() +
                     get_controllable_inputs_bdds()))
    return p_bdd
Beispiel #8
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
Beispiel #9
0
def walk(a_bdd):
    global bdd_gate_cache
    """
    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 bdd_gate_cache:
        return 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 != 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 = walk(t_bdd)
    e_lit = walk(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 = aig.get_optimized_and_lit(a_lit, t_lit)
    na_e_lit = aig.get_optimized_and_lit(aig.negate_lit(a_lit), e_lit)
    n_a_t_lit = aig.negate_lit(a_t_lit)
    n_na_e_lit = aig.negate_lit(na_e_lit)
    ite_lit = aig.get_optimized_and_lit(n_a_t_lit, n_na_e_lit)
    res = aig.negate_lit(ite_lit)
    if a_bdd.is_complement():
        res = aig.negate_lit(res)
    # cache result
    bdd_gate_cache[a_bdd] = res
    return res
Beispiel #10
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()
Beispiel #11
0
def single_pre_env_bdd(dst_states_bdd, env_strat=None, get_strat=False):
    if env_strat is not None:
        strat = env_strat
    else:
        strat = bdd.true()

    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()]
    if restrict_like_crazy:
        latch_funs = [x.restrict(~dst_states_bdd) for x in latch_funs]
    # take a transition step backwards
    p_bdd = dst_states_bdd.compose(latches, latch_funs)
    # use the given strategy
    p_bdd &= strat
    # there is an uncontrollable action such that for all contro...
    temp_bdd = p_bdd.univ_abstract(
        bdd.get_cube(get_controllable_inputs_bdds()))
    p_bdd = temp_bdd.exist_abstract(
        bdd.get_cube(get_uncontrollable_inputs_bdds()))
    if get_strat:
        return temp_bdd
    else:
        return p_bdd
Beispiel #12
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)
Beispiel #13
0
def compose_init_state_bdd():
    b = bdd.true()
    for x in iterate_latches_and_error():
        b &= ~bdd.BDD(x.lit)
    return b
Beispiel #14
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
Beispiel #15
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