def verify_inductive_invariant(s: Solver, inv: List[Expr]) -> None: prog = syntax.the_program inits = [init.expr for init in prog.inits()] safeties = [inv.expr for inv in prog.invs() if inv.is_safety] assert logic.check_implication(s, inits, inv) is None assert logic.check_implication(s, inv, safeties) is None assert logic.check_two_state_implication_all_transitions( s, inv, syntax.And(*inv), minimize=False) is None
def record_predicate(self, e: Expr) -> None: for x in self.predicates: if x == e or (logic.check_implication(self.solver, [x], [e], minimize=False) is None and logic.check_implication(self.solver, [e], [x], minimize=False) is None): break else: self.predicates.append(e) utils.logger.info( f'learned new predicate, now have {len(self.predicates)} {e}')
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 record_predicate(self, e: Expr) -> None: for x in self.predicates: if x == e or equiv_expr(self.solver, x, e): break else: j = len(self.predicates) self.predicates.append(e) if logic.check_implication(self.solver, self.human_invariant, [e], minimize=False) is None: self.human_invariant_implies.add(j) for i, q in enumerate(self.human_invariant): if equiv_expr(self.solver, e, q): msg = '(which is a clause of the human invariant)' assert i not in self.human_invariant_to_predicate self.human_invariant_to_predicate[i] = j break else: msg = '(which is implied by the human invariant)' else: msg = '' utils.logger.info( f'learned new predicate, now have {len(self.predicates)} {e} {msg}' )
def is_frame_inductive(self, i: int) -> bool: for p in self.automaton.phases(): if logic.check_implication(self.solver, self[i + 1].summary_of(p), self[i].summary_of(p), minimize=False) is not None: return False return True
def print_status(self) -> None: '''Print information useful for comparison with primal dual houdini TODO: convert from barbaric print() to civilized xml logging ''' # update self.inductive_invariant inv = set(range(len(self.predicates))) changes = True while changes: changes = False for i in sorted(inv - self.inductive_invariant): if logic.check_two_state_implication_all_transitions( self.solver, [self.predicates[j] for j in sorted(inv)], self.predicates[i], minimize=False) is not None: changes = True inv.remove(i) self.inductive_invariant |= inv # print information print( f'\n[{datetime.now()}] Current predicates ({len(self.predicates)} total, {len(self.inductive_invariant)} proven, {len(self.human_invariant_implies)} implied by human invariant):' ) for i, p in enumerate(self.predicates): note = '({})'.format({ phase.name(): max((j for j, f in enumerate(self.fs) if p in f.summary_of(phase)), default=-1) for phase in self.automaton.phases() }) if i in self.inductive_invariant: note += f' (invariant)' if i in self.human_invariant_implies: note += f' (implied by human invariant)' print(f' predicates[{i:3}]{note}: {self.predicates[i]}') for i, p in enumerate(self.human_invariant): if (i not in self.human_invariant_proved and len(self.inductive_invariant) > 0 and logic.check_implication(self.solver, [ self.predicates[j] for j in sorted(self.inductive_invariant) ], [p], minimize=False) is None): self.human_invariant_proved.add(i) print( f'\n[{datetime.now()}] Current human invariant ({len(self.human_invariant)} total, {len(self.human_invariant_to_predicate)} learned, {len(self.human_invariant_proved)} proven):' ) for i, p in enumerate(self.human_invariant): notes = [] if i in self.human_invariant_proved: notes.append('proved') if i in self.human_invariant_to_predicate: notes.append( f'learned as predicates[{self.human_invariant_to_predicate[i]}]' ) note = '(' + ', '.join(notes) + ')' print(f' human_invariant[{i:3}]{note}: {p}') print()
def _simplify_summary(self, f: MySet[Expr]) -> None: l = [] for c in reversed(f.l): f_minus_c = [x for x in f.l if x in f.s and x is not c] if c not in self.safeties and \ logic.check_implication(self.solver, f_minus_c, [c], minimize=False) is None: f.s.remove(c) else: l.append(c) l.reverse() f.l = l
def _simplify_summary(self, f: MySet[Expr], p: Phase) -> None: l = [] for c in reversed(f.l): f_minus_c = [x for x in f.l if x in f.s and x is not c] if c not in phases.phase_safety(p) and \ logic.check_implication(self.solver, f_minus_c, [c], minimize=False) is None: utils.logger.debug('removed %s' % c) f.s.remove(c) else: l.append(c) l.reverse() f.l = l
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 itp_gen(s: Solver) -> None: k = 4 prog = syntax.the_program safety = syntax.And(*(inv.expr for inv in prog.invs() if inv.is_safety)) inits = [init.expr for init in prog.inits()] utils.logger.info("initial state: %s" % str(inits)) utils.logger.info("proving safety property: %s" % safety) candidate = [safety] while True: cti = get_cti(s, syntax.And(*candidate)) if cti is None: break pre_diag = cti[0] with logic.BoundedReachabilityCheck(s, syntax.the_program, k) as bmc_checker: core = bmc_checker.check_and_core(pre_diag) pre_diag.minimize_from_core(core) pre_diag.generalize( s, lambda diag: generalize_cex_omission_checker(s, diag, k)) e = syntax.Not(pre_diag.to_ast()) utils.logger.info('adding new clause to the invariant: %s' % str(e)) candidate.append(e) res = logic.check_implication(s, inits, candidate) if res is not None: utils.logger.always_print( "Failure: candidate %s excludes initial states" % ' & '.join(str(clause) for clause in candidate)) else: utils.logger.always_print("Success! Inductive invariant:") for clause in candidate: utils.logger.always_print(str(clause))
def brat(s: Solver) -> None: k = utils.args.depth prog = syntax.the_program safety = syntax.And(*(inv.expr for inv in prog.invs() if inv.is_safety)) inits = [init.expr for init in prog.inits()] utils.logger.info("initial state: %s" % str(inits)) utils.logger.info("proving safety property: %s" % safety) current_frame = inits prev_frame: List[Expr] = [syntax.FalseExpr] bad_cache: Set[Diagram] = set() idx = 0 while logic.check_implication(s, current_frame, prev_frame, minimize=False) is not None: idx += 1 prev_frame = current_frame if not utils.args.decrease_depth: current_depth = k else: current_depth = k - idx + 1 if current_depth <= 0: assert False, "exhaused bmc depth: becoming non-positive" current_frame = brat_next_frame(s, prev_frame, current_depth, inits, safety, bad_cache, utils.args.minimize_models) utils.logger.info("Frame: %d" % idx) for c in current_frame: utils.logger.info(str(c)) utils.logger.always_print("Success! Inductive invariant:") for clause in current_frame: utils.logger.always_print(str(clause)) verify_inductive_invariant(s, current_frame)
def itp_gen(s: Solver) -> None: k = utils.args.forward_depth prog = syntax.the_program safety = syntax.And(*(inv.expr for inv in prog.invs() if inv.is_safety)) inits = [init.expr for init in prog.inits()] utils.logger.info("initial state: %s" % str(inits)) utils.logger.info("proving safety property: %s" % safety) candidate = [safety] while True: cti = get_cti(s, syntax.And(*candidate)) if cti is None: break pre_diag = cti[0] pre_diag.generalize(s, lambda diag: bmc_upto_bound( s, syntax.Not(diag.to_ast()), k) is None, order=utils.args.generalization_order) e = syntax.Not(pre_diag.to_ast()) utils.logger.info('adding new clause to the invariant: %s' % str(e)) candidate.append(e) if logic.check_implication(s, inits, candidate) is not None: utils.logger.always_print( "Failure: candidate %s excludes initial states" % ' & '.join(str(clause) for clause in candidate)) else: utils.logger.always_print("Success! Inductive invariant:") for clause in candidate: utils.logger.always_print(str(clause))
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 valid_in_initial_frame(self, e: Expr) -> bool: return logic.check_implication(self.solver, self.fs[0].summary(), [e], minimize=False) is None
def equiv_expr(solver: Solver, e1: Expr, e2: Expr) -> bool: return (logic.check_implication(solver, [e1], [e2], minimize=False) is None and logic.check_implication(solver, [e2], [e1], minimize=False) is None)
def is_frame_inductive(self, i: int) -> bool: res = logic.check_implication(self.solver, self[i + 1].summary(), self[i].summary(), minimize=False) return res is None
bstate_min = min(self.backwards_reachable_states, key=lambda b: (b.known_absent_until_frame, b.num_steps_to_bad), default=None) if bstate_min is None or (min_frame_no := bstate_min.known_absent_until_frame) == len(self) - 1: break if isinstance(state := bstate_min.state_or_expr, State): eval_res = state.eval(syntax.And(*(self[min_frame_no + 1].summary()))) assert isinstance(eval_res, bool) if eval_res: return bstate_min else: expr = state res = logic.check_implication(self.solver, 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
def valid_in_initial_frame(s: Solver, inits: List[Expr], e: Expr) -> bool: return logic.check_implication(s, inits, [e], minimize=False) is None