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))
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
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)
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)
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)
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
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)
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]:
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)
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