Пример #1
0
def get_cti(s: Solver, candidate: Expr) -> Optional[Tuple[Diagram, Diagram]]:
    res = logic.check_two_state_implication_all_transitions(s, [candidate],
                                                            candidate,
                                                            minimize=True)
    if res is None:
        return None

    z3m: z3.ModelRef = res[0]
    mod = Trace.from_z3((KEY_OLD, KEY_NEW), z3m)
    return (mod.as_diagram(index=0), mod.as_diagram(index=1))
Пример #2
0
        def edge_covering_checker(p: Phase) -> Optional[Tuple[Phase, Diagram]]:
            t = self.solver.get_translator(KEY_NEW, KEY_OLD)
            f = self.fs[-1]
            prog = syntax.the_program

            with self.solver:
                for c in f.summary_of(p):
                    self.solver.add(t.translate_expr(c, old=True))

                transitions_from_phase = self.automaton.transitions_from(p)

                for trans in prog.transitions():
                    edges_from_phase_matching_prog_trans = [
                        t for t in transitions_from_phase
                        if t.prog_transition_name() == trans.name
                    ]
                    if any(delta.precond is None
                           for delta in edges_from_phase_matching_prog_trans):
                        utils.logger.debug(
                            'transition %s is covered trivially by %s' %
                            (trans.name, p.name()))
                        continue

                    utils.logger.debug(
                        'checking transition %s is covered by %s' %
                        (trans.name, p.name()))

                    with self.solver:
                        self.solver.add(t.translate_transition(trans))
                        preconds = (
                            z3.Not(
                                t.translate_precond_of_transition(
                                    delta.precond(), trans))
                            for delta in edges_from_phase_matching_prog_trans)
                        self.solver.add(z3.And(*preconds))

                        if self.solver.check() != z3.unsat:
                            utils.logger.debug(
                                'phase %s cex to edge covering of transition %s'
                                % (p.name(), trans.name))
                            z3m: z3.ModelRef = self.solver.model()
                            mod = Trace.from_z3([KEY_OLD, KEY_NEW], z3m)
                            self.record_state(mod)
                            diag = mod.as_diagram(i=0)
                            return (p, diag)

                        utils.logger.debug(
                            'transition %s is covered non-trivially by %s' %
                            (trans.name, p.name()))
                        continue

                utils.logger.debug('all edges covered from phase %s' %
                                   p.name())
                return None
Пример #3
0
def is_rel_blocking_relax(trns: Trace, idx: int,
                          derived_rel: Tuple[List[Tuple[syntax.SortedVar, str]], Expr]) -> bool:
    # TODO: probably can obtain the sort from the sortedvar when not using scapy
    free_vars, derived_relation_formula = derived_rel
    free_vars_active_clause = syntax.And(*(active_var(v.name, sort_name) for (v, sort_name) in free_vars))

    diffing_formula = syntax.Exists([v for (v, _) in free_vars],
                                    syntax.And(syntax.Old(syntax.And(free_vars_active_clause,
                                                                     derived_relation_formula)),
                                               syntax.And(free_vars_active_clause,
                                                          syntax.Not(derived_relation_formula))))

    with syntax.the_program.scope.two_state(twostate=True):  # TODO: what is this doing? probably misusing
        diffing_formula.resolve(syntax.the_program.scope, syntax.BoolSort)

    res = trns.eval_double_vocab(diffing_formula, idx)
    assert isinstance(res, bool)
    return cast(bool, res)
Пример #4
0
        def safety_property_checker(
                p: Phase) -> Optional[Tuple[Phase, Diagram]]:
            res = logic.check_implication(self.solver, f.summary_of(p),
                                          (inv.expr
                                           for inv in phases.phase_safety(p)))

            if res is None:
                utils.logger.debug(
                    "Frontier frame phase %s implies safety, summary is %s" %
                    (p.name(), f.summary_of(p)))
                return None

            utils.logger.debug("Frontier frame phase %s cex to safety" %
                               p.name())
            z3m: z3.ModelRef = res
            mod = Trace.from_z3([KEY_ONE], z3m)
            self.record_state(mod)
            diag = mod.as_diagram()
            return (p, diag)
Пример #5
0
def is_rel_blocking_relax_step(
        trns: Trace, idx: int,
        derived_rel: Tuple[List[Tuple[syntax.SortedVar, str]], Expr]
) -> bool:
    # TODO: probably can obtain the sort from the sortedvar when not using pickle
    free_vars, derived_relation_formula = derived_rel
    free_vars_active_clause = syntax.And(*(active_var(v.name, sort_name) for (v, sort_name) in free_vars))

    diffing_formula = syntax.Exists([v for (v, _) in free_vars],
                                    syntax.And(syntax.And(free_vars_active_clause,
                                                          derived_relation_formula),
                                               syntax.New(syntax.And(free_vars_active_clause,
                                                                     syntax.Not(derived_relation_formula)))))

    with syntax.the_program.scope.fresh_stack():
        with syntax.the_program.scope.n_states(2):
            diffing_formula.resolve(syntax.the_program.scope, syntax.BoolSort)

    res = trns.eval(diffing_formula, idx)
    assert isinstance(res, bool)
    return cast(bool, res)
Пример #6
0
def derived_rels_candidates_from_trace(trns: Trace, more_traces: List[Trace],
                                       max_conj_size: int, max_free_vars: int) -> List[Tuple[List[syntax.SortedVar],Expr]]:
    first_relax_idx = first_relax_step_idx(trns)
    pre_relax_state = trns.as_state(first_relax_idx)
    post_relax_state = trns.as_state(first_relax_idx + 1)
    assert pre_relax_state.univs == post_relax_state.univs


    # relaxed elements
    relaxed_elements = []
    for sort, univ in pre_relax_state.univs.items():
        active_rel_name = 'active_' + sort.name         # TODO: de-duplicate
        pre_active_interp = dict_val_from_rel_name(active_rel_name, pre_relax_state.rel_interp)
        post_active_interp = dict_val_from_rel_name(active_rel_name, post_relax_state.rel_interp)
        pre_active_elements = [tup[0] for (tup, b) in pre_active_interp if b]
        post_active_elements = [tup[0] for (tup, b) in post_active_interp if b]
        assert set(post_active_elements).issubset(set(pre_active_elements))

        for relaxed_elem in utils.OrderedSet(pre_active_elements) - set(post_active_elements):
            relaxed_elements.append((sort, relaxed_elem))

    # pre-relaxation step facts concerning at least one relaxed element (other to be found by UPDR)
    relevant_facts: List[Union[RelationFact,FunctionFact,InequalityFact]] = []

    for rel, rintp in pre_relax_state.rel_interp.items():
        for rfact in rintp:
            (elms, polarity) = rfact
            relation_fact = RelationFact(rel, elms, polarity)
            if set(relation_fact.involved_elms()) & set(ename for (_, ename) in relaxed_elements):
                relevant_facts.append(relation_fact)

    for func, fintp in pre_relax_state.func_interp.items():
        for ffact in fintp:
            (els_params, els_res) = ffact
            function_fact = FunctionFact(func, els_params, els_res)
            if set(function_fact.involved_elms()) & set(ename for (_, ename) in relaxed_elements):
                relevant_facts.append(function_fact)

    for sort, elm in relaxed_elements: # other inequalities presumably handled by UPDR
        for other_elm in pre_relax_state.univs[sort]:
            if other_elm == elm:
                continue
            relevant_facts.append(InequalityFact(elm, other_elm))

    # facts blocking this specific relaxation step
    diff_conjunctions = []
    candidates_cache: Set[str] = set()
    for fact_lst in itertools.combinations(relevant_facts, max_conj_size):
        elements = utils.OrderedSet(itertools.chain.from_iterable(fact.involved_elms() for fact in fact_lst))
        relaxed_elements_relevant = [elm for (_, elm) in relaxed_elements if elm in elements]
        vars_from_elm = dict((elm, syntax.SortedVar(None, syntax.the_program.scope.fresh("v%d" % i), None))
                                for (i, elm) in enumerate(elements))
        parameter_elements = elements - set(relaxed_elements_relevant)
        if len(parameter_elements) > max_free_vars:
            continue

        conjuncts = [fact.as_expr(lambda elm: vars_from_elm[elm].name) for fact in fact_lst]

        # for elm, var in vars_from_elm.items():
        # TODO: make the two loops similar
        for elm in relaxed_elements_relevant:
            var = vars_from_elm[elm]
            sort = pre_relax_state.element_sort(elm)
            active_element_conj = syntax.Apply('active_%s' % sort.name, [syntax.Id(None, var.name)])
            conjuncts.append(active_element_conj)

        derived_relation_formula = syntax.Exists([vars_from_elm[elm]
                                                  for (_, elm) in relaxed_elements
                                                  if elm in vars_from_elm],
                                                 syntax.And(*conjuncts))

        if str(derived_relation_formula) in candidates_cache:
            continue
        candidates_cache.add(str(derived_relation_formula))

        if closing_qa_cycle(syntax.the_program, [pre_relax_state.element_sort(elm) for elm in parameter_elements],
                                                [pre_relax_state.element_sort(elm) for elm in relaxed_elements_relevant]):
            # adding the derived relation would close a quantifier alternation cycle, discard the candidate
            continue

        # if trns.eval_double_vocab(diffing_formula, first_relax_idx):
        if is_rel_blocking_relax(trns, first_relax_idx,
                                 ([(vars_from_elm[elm], pre_relax_state.element_sort(elm).name) for elm in parameter_elements],
                                  derived_relation_formula)):
            # if all(trs.eval_double_vocab(diffing_formula, first_relax_step_idx(trs)) for trs in more_traces):
                diff_conjunctions.append(([vars_from_elm[elm] for elm in parameter_elements],
                                           derived_relation_formula))

    return diff_conjunctions
Пример #7
0
    def find_predecessor(
        self, j: int, diag_or_expr: Union[Diagram, Expr]
    ) -> Tuple[z3.CheckSatResult, Union[Optional[MySet[int]], Tuple[
            DefinitionDecl, Trace]]]:
        pre_frame = self[j]
        prog = syntax.the_program
        solver = self.solver
        t = self.solver.get_translator((KEY_OLD, KEY_NEW))

        if utils.args.use_z3_unsat_cores:
            core: Optional[MySet[int]] = MySet()
        else:
            core = None

        def to_z3() -> z3.ExprRef:
            if isinstance(diag_or_expr, Diagram):
                return diag_or_expr.to_z3(t, state_index=1)
            else:
                return t.translate_expr(diag_or_expr, index=1)

        def trackers() -> List[z3.ExprRef]:
            if isinstance(diag_or_expr, Diagram):
                return diag_or_expr.trackers
            else:
                return []

        with solver.new_frame(), solver.mark_assumptions_necessary():
            for f in pre_frame.summary():
                solver.add(t.translate_expr(f, index=0))
            solver.add(to_z3())
            for ition in prog.transitions():
                with solver.new_frame():
                    solver.add(t.translate_transition(ition))
                    if (res := solver.check(trackers())) == z3.sat:
                        m = Trace.from_z3((KEY_OLD, KEY_NEW),
                                          solver.model(trackers()))
                        state = State(m, 0)
                        src = self.currently_blocking
                        assert src is not None
                        steps_from_cex = src.known_absent_until_frame + 1 - j + src.num_steps_to_bad
                        bstate = BackwardReachableState(
                            len(self.backwards_reachable_states), state,
                            steps_from_cex)
                        self.record_backwards_reachable_state(bstate)
                        return (z3.sat, (ition, m))
                    elif res == z3.unsat:
                        if utils.args.use_z3_unsat_cores and isinstance(
                                diag_or_expr, Diagram):
                            assert core is not None
                            # carefully retrieve the unsat core before calling check again
                            uc = solver.unsat_core()
                            res = solver.check(
                                [diag_or_expr.trackers[i] for i in core])
                            if res == z3.unsat:
                                continue
                            for x in sorted(uc, key=lambda y: y.decl().name()):
                                assert isinstance(x, z3.ExprRef)
                                core.add(int(x.decl().name()[1:]))
                    else:
                        for e in solver.assertions():
                            print(e)
                        assert False, ('z3 returned unknown', res)
Пример #8
0
                                              minimize=False)
                if res is not None:
                    return bstate_min

            bstate_min.known_absent_until_frame += 1

        utils.logger.info(
            'no existing states to block. looking for a new state.')

        f = self.fs[-1]
        if len(self.safeties) == 0 or (res := logic.check_implication(
                self.solver, f.summary(), self.safeties)) is None:
            utils.logger.info('frontier is safe. nothing new to block either.')
            return None

        state = State(Trace.from_z3((KEY_ONE, ), res), 0)
        assert len(self) >= 2
        bstate = BackwardReachableState(len(self.backwards_reachable_states),
                                        state, 0)
        bstate.known_absent_until_frame = len(self) - 2
        self.record_backwards_reachable_state(bstate)
        return bstate

    def record_backwards_reachable_state(
            self, state: BackwardReachableState) -> None:
        utils.logger.info(
            f'discovered state #{len(self.backwards_reachable_states)}')
        utils.logger.info(str(state))
        self.backwards_reachable_states.append(state)

    def get_inductive_frame(self) -> Optional[Frame]:
Пример #9
0
    def find_predecessor_from_src_phase(
        self, t: syntax.Z3Translator, pre_frame: Frame, src_phase: Phase,
        transitions: Sequence[PhaseTransition], diag: Diagram,
        core: Optional[MySet[int]]
    ) -> Tuple[z3.CheckSatResult, Optional[Tuple[PhaseTransition, Tuple[
            Phase, Diagram]]]]:

        prog = syntax.the_program
        solver = self.solver
        with solver:

            for f in pre_frame.summary_of(src_phase):
                solver.add(t.translate_expr(f, old=True))

            for phase_transition in transitions:
                delta = phase_transition.transition_decl()
                trans = prog.scope.get_definition(delta.transition)
                assert trans is not None
                precond = delta.precond

                with utils.LogTag(utils.logger,
                                  'find-pred',
                                  transition=delta.transition,
                                  weight=str(
                                      phase_transition.avg_time_traversing())):
                    with solver:
                        solver.add(
                            t.translate_transition(trans, precond=precond))
                        before_check = datetime.now()
                        res = solver.check(diag.trackers)
                        phase_transition.time_spent_traversing += (
                            datetime.now() - before_check).total_seconds()
                        phase_transition.count_traversed += 1

                        if res != z3.unsat:
                            utils.logger.debug('found predecessor via %s' %
                                               trans.name)
                            m = Trace.from_z3([KEY_OLD, KEY_NEW],
                                              solver.model(diag.trackers))
                            # if utils.logger.isEnabledFor(logging.DEBUG):
                            #     utils.logger.debug(str(m))
                            self.record_state(m)
                            return (res, (phase_transition,
                                          (src_phase, m.as_diagram(i=0))))
                        elif utils.args.use_z3_unsat_cores:
                            assert core is not None
                            uc = solver.unsat_core()
                            # if utils.logger.isEnabledFor(logging.DEBUG):
                            #     utils.logger.debug('uc')
                            #     utils.logger.debug(str(sorted(uc, key=lambda y: y.decl().name())))
                            #     utils.logger.debug('assertions')
                            #     utils.logger.debug(str(solver.assertions()))

                            res = solver.check(
                                [diag.trackers[i] for i in core])
                            if res == z3.unsat:
                                utils.logger.debug(
                                    'but existing core sufficient, skipping')
                                continue

                            for x in sorted(uc, key=lambda y: y.decl().name()):
                                assert isinstance(x, z3.ExprRef)
                                core.add(int(x.decl().name()[1:]))
                            if utils.logger.isEnabledFor(logging.DEBUG):
                                utils.logger.debug('new core')
                                utils.logger.debug(str(sorted(core)))

            return (z3.unsat, None)
Пример #10
0
    def push_conjunct(self,
                      frame_no: int,
                      c: Expr,
                      p: Phase,
                      frame_old_count: Optional[int] = None) -> None:
        is_safety = c in phases.phase_safety(p)

        f = self.fs[frame_no]
        while True:
            with utils.LogTag(utils.logger,
                              'pushing-conjunct-attempt',
                              lvl=logging.DEBUG,
                              frame=str(frame_no),
                              conj=str(c)):
                utils.logger.debug('frame %s phase %s attempting to push %s' %
                                   (frame_no, p.name(), c))

                res = self.clause_implied_by_transitions_from_frame(
                    f, p, c, minimize=is_safety or utils.args.block_may_cexs)
                if res is None:
                    utils.logger.debug('frame %s phase %s managed to push %s' %
                                       (frame_no, p.name(), c))

                    if utils.args.smoke_test and utils.logger.isEnabledFor(
                            logging.DEBUG):
                        utils.logger.debug('smoke testing...')
                        # TODO: phases
                        logic.check_bmc(self.solver, c, frame_no + 1)

                    # assert self.clause_implied_by_transitions_from_frame(f, p, c) is None
                    self[frame_no + 1].strengthen(p, c)
                    self.debug_assert_inductive_trace()
                    break

                pre_phase, (m, t) = res
                mod = Trace.from_z3([KEY_OLD, KEY_NEW], m)
                self.record_state(mod)
                diag = mod.as_diagram(i=0)

                if utils.logger.isEnabledFor(logging.DEBUG):
                    utils.logger.debug(
                        'frame %s failed to immediately push %s due to '
                        'transition %s' % (frame_no, c, t.pp()))
                    # utils.logger.debug(str(mod))
                if is_safety:
                    utils.logger.debug(
                        'note: current clause is safety condition')
                    self.block(diag,
                               frame_no,
                               pre_phase, [(None, c), (t, diag)],
                               safety_goal=True)
                else:
                    if utils.args.block_may_cexs:
                        ans = self.block(diag,
                                         frame_no,
                                         pre_phase, [(None, c), (t, diag)],
                                         safety_goal=False)
                        if isinstance(ans, CexFound):
                            break
                    else:
                        break