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 prev_frame_constraint(diag: Diagram) -> bool: pre_frame = self[j - 1].summary() return ( logic.check_two_state_implication_all_transitions( self.solver, pre_frame, syntax.Not(diag.to_ast()), minimize=False ) is None and self.valid_in_initial_frame(syntax.Not(diag.to_ast())) )
def brat_next_frame(s: Solver, prev_frame: List[Expr], bound: int, inits: List[Expr], safety: Expr, bad_cache: Set[Diagram], minimize: bool) -> List[Expr]: current_frame: List[Expr] = new_frame(s, prev_frame) for bad_model in bad_cache: if logic.check_implication(s, current_frame, [syntax.Not(bad_model.to_ast())]) is None: continue current_frame.append( post_image_prime_consequence( s, prev_frame, inits, bad_model, gen_order=utils.args.generalization_order)) while (bad_trace := bmc_upto_bound( s, safety, bound, preconds=current_frame, minimize=minimize, relaxed_semantics=utils.args.relax_backwards)) is not None: bad_model = Diagram(bad_trace.as_state(0)) utils.logger.debug("Example to block: %s" % str(bad_model)) bad_cache.add(bad_model) learned_clause = post_image_prime_consequence( s, prev_frame, inits, bad_model, gen_order=utils.args.generalization_order) utils.logger.info("Learned clause: %s" % str(learned_clause)) current_frame.append(learned_clause)
def block( self, diag_or_expr: Union[Diagram, Expr], j: int, trace: RelaxedTrace ) -> None: utils.logger.info(f'block({j})') def as_expr() -> Expr: return diag_or_expr.to_ast() if isinstance(diag_or_expr, Diagram) else diag_or_expr if j == 0 or (j == 1 and not self.valid_in_initial_frame(syntax.Not(as_expr()))): utils.logger.always_print('\n'.join(((t.name + ' ') if t is not None else '') + str(diag) for t, diag in trace)) print('abstract counterexample: the system has no universal inductive invariant proving safety') if utils.args.checkpoint_out: self.store_frames(utils.args.checkpoint_out) raise AbstractCounterexample() while True: res, x = self.find_predecessor(j - 1, diag_or_expr) if res == unsat: assert x is None or isinstance(x, MySet) core: Optional[MySet[int]] = x self.augment_core_for_init(diag_or_expr, core) break assert isinstance(x, tuple), (res, x) trans, cti = x pre_diag = Diagram(cti.as_state(0)) trace.append((trans, pre_diag)) self.block(pre_diag, j - 1, trace) trace.pop() if isinstance(diag_or_expr, Diagram): diag_or_expr.minimize_from_core(core) def prev_frame_constraint(diag: Diagram) -> bool: pre_frame = self[j - 1].summary() return ( logic.check_two_state_implication_all_transitions( self.solver, pre_frame, syntax.Not(diag.to_ast()), minimize=False ) is None and self.valid_in_initial_frame(syntax.Not(diag.to_ast())) ) if isinstance(diag_or_expr, Diagram): diag_or_expr.generalize(self.solver, prev_frame_constraint) e = syntax.Not(as_expr()) utils.logger.info(f'block({j}) using {e}') self.add(e, j) k = j while k + 1 < len(self) and self.push_conjunct(k, e): utils.logger.info(f'and pushed to {k + 1}') k += 1
def establish_safety(self) -> None: while bstate := self.find_something_to_block(): self.currently_blocking = bstate if isinstance(bstate.state_or_expr, State): diag_or_expr: Union[Diagram, Expr] = Diagram(bstate.state_or_expr) else: diag_or_expr = bstate.state_or_expr frame_no = bstate.known_absent_until_frame + 1 utils.logger.info(f'will block state #{bstate.id} in frame {frame_no}') self.block(diag_or_expr, frame_no, [(None, diag_or_expr)]) bstate.known_absent_until_frame += 1
def oneshot_compute_inv(s: Solver, bound: int, inits: List[Expr], safety: Expr, minimize: bool) -> List[Expr]: current_frame: List[Expr] = [syntax.TrueExpr] while (bad_trace := bmc_upto_bound( s, safety, bound, preconds=current_frame, minimize=minimize, relaxed_semantics=utils.args.relax_backwards)) is not None: bad_model = Diagram(bad_trace.as_state(0)) learned_clause = bmc_prime_consequence(s, utils.args.forward_depth, inits, bad_model, utils.args.relax_forwards, utils.args.generalization_order) utils.logger.info("Learned clause: %s" % str(learned_clause)) current_frame.append(learned_clause)
def find_predecessor( self, pre_frame: Frame, current_phase: Phase, diag: Diagram ) -> Tuple[z3.CheckSatResult, Union[Optional[MySet[int]], Tuple[ PhaseTransition, Tuple[Phase, Diagram]]]]: t = self.solver.get_translator(KEY_NEW, KEY_OLD) if utils.args.use_z3_unsat_cores: core: Optional[MySet[int]] = MySet() else: core = None with self.solver: with self.solver.mark_assumptions_necessary(): self.solver.add(diag.to_z3(t)) transitions_into = self.automaton.transitions_to_grouped_by_src( current_phase) for src in self._predecessor_precedence( current_phase, list(transitions_into.keys())): transitions = transitions_into[src] assert transitions utils.logger.debug( "check predecessor of %s from %s by %s" % (current_phase.name(), src.name(), transitions)) (sat_res, pre_diag) = self.find_predecessor_from_src_phase( t, pre_frame, src, transitions, diag, core) if sat_res == z3.unsat: continue return (sat_res, pre_diag) if utils.args.use_z3_unsat_cores: assert core is not None ret_core: Optional[MySet[int]] = MySet(sorted(core)) else: ret_core = None return (z3.unsat, ret_core)
def augment_core_for_init(self, p: Phase, diag: Diagram, core: Optional[MySet[int]]) -> None: if core is None or not utils.args.use_z3_unsat_cores: return t = self.solver.get_translator(KEY_ONE) with self.solver: for init in self.fs[0].summary_of(p): self.solver.add(t.translate_expr(init)) self.solver.add(diag.to_z3(t)) res = self.solver.check(diag.trackers) assert res == z3.unsat uc = self.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(self.solver.assertions())) res = self.solver.check([diag.trackers[i] for i in core]) if res == z3.unsat: utils.logger.debug( 'augment_core_for_init: existing core sufficient') return 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('augment_core_for_init: new core') utils.logger.debug(str(sorted(core)))
def bmc_constraint(diag: Diagram) -> bool: return bmc_upto_bound(s, syntax.Not(diag.to_ast()), bound, preconds=inits, relaxed_semantics=relaxed_semantics) is None
def prev_frame_constraint(diag: Diagram) -> bool: return (logic.check_two_state_implication_all_transitions( s, prev_frame, syntax.Not(diag.to_ast()), minimize=False) is None and valid_in_initial_frame(s, inits, syntax.Not( diag.to_ast())))
def valid_in_initial_frame(self, s: Solver, p: Phase, diag: Diagram) -> Optional[z3.ModelRef]: return logic.check_implication(s, self.fs[0].summary_of(p), [syntax.Not(diag.to_ast())])
def block(self, diag: Diagram, j: int, p: Phase, trace: Optional[RelaxedTrace] = None, safety_goal: bool = True) -> Union[Blocked, CexFound]: if trace is None: trace = [] if j == 0 or (j == 1 and self.valid_in_initial_frame( self.solver, p, diag) is not None): if safety_goal: utils.logger.always_print('\n'.join( ((t.pp() + ' ') if t is not None else '') + str(diag) for t, diag in trace)) print( 'abstract counterexample: the system has no universal inductive invariant proving safety' ) # TODO: placeholder for analyzing relaxed trace # import relaxed_traces # print(relaxed_traces.diagram_trace_to_explicitly_relaxed_trace(trace, phases.phase_safety(p))) if utils.args.checkpoint_out: self.store_frames(utils.args.checkpoint_out) raise AbstractCounterexample() else: if utils.logger.isEnabledFor(logging.DEBUG): utils.logger.debug('failed to block diagram') return CexFound() while True: with utils.LogTag(utils.logger, 'block-attempt'): if utils.logger.isEnabledFor(logging.DEBUG): utils.logger.debug('blocking diagram in frame %s' % j) utils.logger.debug(str(diag)) self.print_frame(j - 1, lvl=logging.DEBUG) res, x = self.find_predecessor(self[j - 1], p, diag) if res == z3.unsat: utils.logger.debug('no predecessor: blocked!') assert x is None or isinstance(x, MySet) core: Optional[MySet[int]] = x self.augment_core_for_init(p, diag, core) break assert isinstance(x, tuple), (res, x) trans, (pre_phase, pre_diag) = x trace.append((trans, pre_diag)) ans = self.block(pre_diag, j - 1, pre_phase, trace, safety_goal) if not isinstance(ans, Blocked): return ans trace.pop() if utils.logger.isEnabledFor(logging.DEBUG) and core is not None: utils.logger.debug('core %s' % core) utils.logger.debug('unminimized diag\n%s' % diag) diag.minimize_from_core(core) diag.generalize(self.solver, self[j - 1], self.automaton.transitions_to_grouped_by_src(p), p == self.automaton.init_phase(), j) e = syntax.Not(diag.to_ast()) if utils.logger.isEnabledFor(logging.DEBUG): utils.logger.debug( 'adding new clause to frames 0 through %d phase %s' % (j, p.name())) if utils.logger.isEnabledFor(logging.INFO): utils.logger.info("[%d] %s" % (j, str(e))) self.add(p, e, j) utils.logger.debug("Done blocking") return Blocked()
def block(self, diag: Diagram, j: int, p: Phase, trace: Optional[List[Tuple[Optional[PhaseTransition], Union[Diagram, Expr]]]] = None, safety_goal: bool = True) -> Union[Blocked, CexFound]: if trace is None: trace = [] if j == 0 or (j == 1 and self.valid_in_initial_frame( self.solver, p, diag) is not None): if safety_goal: utils.logger.always_print('\n'.join( ((t.pp() + ' ') if t is not None else '') + str(diag) for t, diag in trace)) print('abstract counterexample') raise Exception('abstract counterexample') else: if utils.logger.isEnabledFor(logging.DEBUG): utils.logger.debug('failed to block diagram') # utils.logger.debug(str(diag)) return CexFound() # print fs while True: with utils.LogTag(utils.logger, 'block-attempt'): if utils.logger.isEnabledFor(logging.DEBUG): utils.logger.debug('blocking diagram in frame %s' % j) utils.logger.debug(str(diag)) self.print_frame(j - 1, lvl=logging.DEBUG) res, x = self.find_predecessor(self[j - 1], p, diag) if res == z3.unsat: utils.logger.debug('no predecessor: blocked!') assert x is None or isinstance(x, MySet) core: Optional[MySet[int]] = x self.augment_core_for_init(p, diag, core) break assert isinstance(x, tuple), (res, x) trans, (pre_phase, pre_diag) = x trace.append((trans, pre_diag)) ans = self.block(pre_diag, j - 1, pre_phase, trace, safety_goal) if not isinstance(ans, Blocked): return ans trace.pop() if utils.logger.isEnabledFor(logging.DEBUG) and core is not None: utils.logger.debug('core %s' % core) utils.logger.debug('unminimized diag\n%s' % diag) diag.minimize_from_core(core) diag.generalize(self.solver, self[j - 1], self.automaton.transitions_to_grouped_by_src(p), p == self.automaton.init_phase(), j) e = syntax.Not(diag.to_ast()) if utils.logger.isEnabledFor(logging.DEBUG): utils.logger.debug( 'adding new clause to frames 0 through %d phase %s' % (j, p.name())) if utils.logger.isEnabledFor(logging.INFO): utils.logger.info("[%d] %s" % (j, str(e))) self.add(p, e, j) utils.logger.debug("Done blocking") return Blocked()