def assert_any_transition(s: Solver, t: Z3Translator, state_index: int, allow_stutter: bool = False) -> None: prog = syntax.the_program uid = str(state_index) tids = [] for transition in prog.transitions(): tid = z3.Bool(get_transition_indicator(uid, transition.name)) tids.append(tid) s.add( z3.Implies( tid, t.translate_expr( New(transition.as_twostate_formula(prog.scope), state_index)))) if allow_stutter: tid = z3.Bool(get_transition_indicator(uid, '$stutter')) tids.append(tid) frame = syntax.And(*DefinitionDecl._frame(prog.scope, mods=())) s.add(z3.Implies(tid, t.translate_expr(New(frame, state_index)))) s.add(z3.Or(*tids))
def check_bmc(s: Solver, safety: Expr, depth: int, preconds: Optional[Iterable[Expr]] = None, minimize: Optional[bool] = None) -> Optional[Trace]: prog = syntax.the_program if preconds is None: preconds = (init.expr for init in prog.inits()) t = s.get_translator(depth + 1) with s.new_frame(): for precond in preconds: s.add(t.translate_expr(precond)) s.add(t.translate_expr(syntax.New(syntax.Not(safety), depth))) for i in range(depth): s.add(t.translate_expr(New(safety, i))) assert_any_transition(s, t, i, allow_stutter=False) res = s.check() if res == solver.sat: z3m = s.model(minimize=minimize) m = Z3Translator.model_to_trace(z3m, depth + 1) return m elif res == solver.unknown: print('unknown!') return None
def model( self, assumptions: Optional[Sequence[z3.ExprRef]] = None, minimize: Optional[bool] = None, sorts_to_minimize: Optional[Iterable[z3.SortRef]] = None, relations_to_minimize: Optional[Iterable[z3.FuncDeclRef]] = None, ) -> z3.ModelRef: if self.cvc4_model is not None: return cast(z3.ModelRef, self.cvc4_model) assert not self.use_cvc4, 'using cvc4 but self.cvc4_model is None!' if minimize is None: minimize = utils.args.minimize_models if minimize: if sorts_to_minimize is None: sorts_to_minimize = [ Z3Translator.sort_to_z3(s) for s in self.scope.sorts.values() if not syntax.has_annotation(s, 'no_minimize') ] if relations_to_minimize is None: m = self.z3solver.model() ds = {str(d) for d in m.decls()} rels_to_minimize = [] for r in self.scope.relations.values(): if r.is_derived() or syntax.has_annotation( r, 'no_minimize'): continue if not r.mutable: z3r = Z3Translator.relation_to_z3(r, None) if isinstance(z3r, z3.ExprRef): rels_to_minimize.append(z3r.decl()) else: rels_to_minimize.append(z3r) else: # ODED: TODO: think about this, using keys here seems strange for k in Z3Translator._get_keys(self.num_states): z3r = Z3Translator.relation_to_z3(r, k) if isinstance(z3r, z3.ExprRef): z3r = z3r.decl() if str(z3r) in ds: rels_to_minimize.append(z3r) return self._minimal_model(assumptions, sorts_to_minimize, rels_to_minimize) else: return self.z3solver.model()
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 mod = Z3Translator.model_to_trace(res[0], 2) return Diagram(mod.as_state(0)), Diagram(mod.as_state(1))
def find_predecessor( self, j: int, diag_or_expr: Union[Diagram, Expr] ) -> Tuple[CheckSatResult, Union[Optional[MySet[int]], Tuple[DefinitionDecl, Trace]]]: pre_frame = self[j] prog = syntax.the_program solver = self.solver t = self.solver.get_translator(2) 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, new=True) else: return t.translate_expr(New(diag_or_expr)) 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)) solver.add(to_z3()) for ition in prog.transitions(): with solver.new_frame(): solver.add(t.translate_expr(ition.as_twostate_formula(prog.scope))) if (res := solver.check(trackers())) == sat: m = Z3Translator.model_to_trace(solver.model(trackers()), 2) state = m.as_state(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 (sat, (ition, m)) elif res == 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 == 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)
def to_z3(self, t: Z3Translator, new: bool = False) -> z3.ExprRef: # TODO: make this return Expr, not z3.ExprRef bs = t.bind(self.binder) with t.scope.in_scope(self.binder, bs): z3conjs = [] self.trackers = [] self.reverse_map: List[Tuple[_RelevantDecl, int]] = [] i = 0 for (d, j, c) in self.conjuncts(): p = z3.Bool('p%d' % i) self.trackers.append(p) self.reverse_map.append((d, j)) z3conjs.append(p == t.translate_expr(New(c) if new else c)) i += 1 if bs: return z3.Exists(bs, z3.And(*z3conjs)) else: return z3.And(*z3conjs)
def translate_transition_call(s: Solver, lator: translator.Z3Translator, state_index: int, c: syntax.TransitionCall) -> z3.ExprRef: prog = syntax.the_program ition = prog.scope.get_definition(c.target) assert ition is not None bs = lator.bind(ition.binder) qs: List[Optional[z3.ExprRef]] = [b for b in bs] if c.args is not None: for j, a in enumerate(c.args): if not isinstance(a, syntax.Star): bs[j] = lator.translate_expr(New(a, state_index)) qs[j] = None qs1 = [q for q in qs if q is not None] with lator.scope.in_scope(ition.binder, bs): body = lator.translate_expr( New(ition._framed_body(lator.scope), state_index)) if qs1: return z3.Exists(qs1, body) else: return body
def check_solver(s: Solver, num_states: int, minimize: Optional[bool] = None) -> Optional[Trace]: res = s.check() m = None if res != solver.unsat: if res != solver.sat: assert res == solver.unknown utils.logger.always_print('unknown!') utils.logger.always_print('reason unknown: ' + s.reason_unknown()) assert False, 'unexpected unknown from z3!' assert res == solver.sat m = Z3Translator.model_to_trace(s.model(minimize=minimize), num_states) return m
self[min_frame_no + 1].summary(), [syntax.Not(expr)], 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 = Z3Translator.model_to_trace(res, 1).as_state(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]: for i in range(len(self) - 1): if self.is_frame_inductive(i): return self[i + 1]