def solve_safety_inc_zz(self, hts, prop, k): self._reset_assertions(self.solver) if TS.has_next(prop): Logger.error( "Invariant checking with next variables only supports FWD strategy" ) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() initt = self.at_time(And(init, invar), 0) Logger.log("Add init at_0", 2) self._add_assertion(self.solver, initt) propt = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add property pat_%d" % 0, 2) self._add_assertion(self.solver, propt) t = 0 while (t < k + 1): self._push(self.solver) even = (t % 2) == 0 th = int(t / 2) if even: eq = And([ EqualsOrIff(self.at_time(v, th), self.at_ptime(v, th - 1)) for v in hts.vars ]) else: eq = And([ EqualsOrIff(self.at_time(v, th + 1), self.at_ptime(v, th - 1)) for v in hts.vars ]) Logger.log("Add equivalence time %d" % t, 2) self._add_assertion(self.solver, eq) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) if even: trans_t = self.unroll(trans, invar, th + 1, th) else: trans_t = self.unroll(trans, invar, th, th + 1) self._add_assertion(self.solver, trans_t) t += 1 return (t - 1, None)
def extend_ts(ts, modifier): affect_init = False if ts.ftrans is None: return (ts, []) new_ftrans = {} vars = [] for (assign, cond_assign_list) in ts.ftrans.items(): fv = get_free_variables(assign) assert len(fv) == 1 var = fv.pop() is_next = TS.has_next(var) refvar = TS.get_ref_var(var) nomvar = Symbol(NOMIN % refvar.symbol_name(), var.symbol_type()) fvar = Symbol(FAULT % refvar.symbol_name(), BOOL) vars.append(nomvar) vars.append(fvar) repldic = dict([(refvar.symbol_name(), nomvar.symbol_name()), \ (TS.get_prime(refvar).symbol_name(), TS.get_prime(nomvar).symbol_name())]) # Remapping nominal behavior to new variable new_ftrans[substitute(assign, repldic)] = [(substitute(c[0], repldic), substitute(c[1], repldic)) for c in cond_assign_list] # Definition of the nominal behavior new_ftrans[refvar] = [(Not(fvar), nomvar)] # Application of the faulty behavior new_ftrans[refvar].append( (fvar, modifier.get_behavior(nomvar, refvar))) ts.trans = And(ts.trans, Implies(fvar, TS.get_prime(fvar))) if affect_init: ts.init = substitute(ts.init, repldic) else: ts.init = And(ts.init, Not(fvar)) # add the vars to the transition system for var in vars: ts.add_var(var) ts.ftrans = new_ftrans return (ts, vars)
def solve_safety_inc_bwd(self, hts, prop, k, assert_property=False): self._reset_assertions(self.solver) if TS.has_next(prop): Logger.error( "Invariant checking with next variables only supports FWD strategy" ) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() formula = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add not property at time %d" % 0, 2) self._add_assertion(self.solver, formula) t = 0 while (t < k + 1): self._push(self.solver) pinit = self.at_ptime(init, t - 1) Logger.log("Add init at time %d" % t, 2) self._add_assertion(self.solver, pinit) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) trans_t = self.unroll(trans, invar, t, t + 1) self._add_assertion(self.solver, trans_t) if assert_property and t > 0: prop_t = self.unroll(TRUE(), prop, t - 1, t) self._add_assertion(self.solver, prop_t) Logger.log("Add property at time %d" % t, 2) t += 1 return (t - 1, None)
def solve_safety_inc(self, hts, prop, k, k_min): if self.config.strategy == VerificationStrategy.ALL: res = self.solve_safety_inc_fwd(hts, prop, k, k_min) if res[1] is not None: return res if self.config.prove and not TS.has_next(prop): res = self.solve_safety_int(hts, prop, k) if res[1] is not None: return res res = self.solve_safety_inc_bwd(hts, prop, k) if res[1] is not None: self.config.strategy == VerificationStrategy.BWD return res res = self.solve_safety_inc_zz(hts, prop, k) self.config.strategy == VerificationStrategy.ZZ return res if self.config.strategy in [ VerificationStrategy.FWD, VerificationStrategy.AUTO ]: return self.solve_safety_inc_fwd(hts, prop, k, k_min) if self.config.strategy == VerificationStrategy.BWD: return self.solve_safety_inc_bwd(hts, prop, k) if self.config.strategy == VerificationStrategy.ZZ: return self.solve_safety_inc_zz(hts, prop, k) # Redirecting strategy selection error if self.config.strategy == VerificationStrategy.INT: Logger.warning( "Interpolation is not available in incremental mode. Switching to not incremental" ) return self.solve_safety_ninc(hts, prop, k) Logger.error("Invalid configuration strategy") return None
def solve_liveness_inc_fwd(self, hts, prop, k, k_min, eventually=False): self._reset_assertions(self.solver) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() if self.config.simplify: Logger.log("Simplifying the Transition System", 1) if Logger.level(1): timer = Logger.start_timer("Simplify") init = simplify(init) trans = simplify(trans) invar = simplify(invar) if Logger.level(1): Logger.get_timer(timer) heqvar = None if not eventually: heqvar = Symbol(HEQVAR, BOOL) self._init_at_time(hts.vars.union(set([heqvar])), k) if self.config.prove: self.solver_klive = self.solver.copy("klive") self._reset_assertions(self.solver_klive) self._add_assertion(self.solver_klive, self.at_time(invar, 0)) if eventually: self._add_assertion(self.solver_klive, self.at_time(init, 0)) propt = FALSE() formula = And(init, invar) formula = self.at_time(formula, 0) Logger.log("Add init and invar", 2) self._add_assertion(self.solver, formula) next_prop = TS.has_next(prop) if next_prop: if k < 1: Logger.error( "Liveness checking with next variables requires at least k=1" ) k_min = 1 t = 0 while (t < k + 1): self._push(self.solver) loopback = FALSE() if t > 0: loopback = self.all_loopbacks(self.hts.vars, t, heqvar) Logger.log("Add loopbacks at time %d" % t, 2) self._add_assertion(self.solver, loopback) if t >= k_min: self._write_smt2_comment(self.solver, "Solving for k=%s" % (t)) Logger.log("\nSolving for k=%s" % (t), 1) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) else: Logger.log("Skipping solving for k=%s (k_min=%s)" % (t, k_min), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) n_prop = Not(prop) if not eventually: n_prop = Or(n_prop, Not(heqvar)) if next_prop: if t > 0: propt = self.at_time(n_prop, t - 1) else: propt = self.at_time(n_prop, t) self._add_assertion(self.solver, propt) if self.config.prove: if t > 0: self._add_assertion(self.solver_klive, trans_t) self._write_smt2_comment(self.solver_klive, "Solving for k=%s" % (t)) if next_prop: if t > 0: propt = self.at_time(Not(prop), t - 1) else: propt = self.at_time(Not(prop), t) self._add_assertion(self.solver_klive, propt) if t >= k_min: if self._solve(self.solver_klive): Logger.log("K-Liveness failed with k=%s" % (t), 1) else: Logger.log("K-Liveness holds with k=%s" % (t), 1) return (t, True) else: self._add_assertion(self.solver_klive, self.at_time(Not(prop), 0)) # self._push(self.solver_klive) # self._add_assertion(self.solver_klive, self.at_time(prop, 0)) # res = self._solve(self.solver_klive) # self._pop(self.solver_klive) # if res: # self._add_assertion(self.solver_klive, self.at_time(prop, 0)) # else: # self._add_assertion(self.solver_klive, self.at_time(Not(prop), 0)) trans_t = self.unroll(trans, invar, t + 1, t) self._add_assertion(self.solver, trans_t) if self.assert_property: prop_t = self.unroll(TRUE(), prop, t, t - 1) self._add_assertion(self.solver, prop_t) Logger.log("Add property at time %d" % t, 2) t += 1 return (t - 1, None)
def solve_problems(self, problems, config): encoder_config = self.problems2encoder_config(config, problems) self.sparser = StringParser(encoder_config) self.lparser = LTLParser() self.coi = ConeOfInfluence() invar_props = [] ltl_props = [] si = False if len(problems.symbolic_inits) == 0: problems.symbolic_inits.add(si) HTSM = 0 HTS2 = 1 HTSD = (HTSM, si) model_extension = config.model_extension if problems.model_extension is None else problems.model_extension assume_if_true = config.assume_if_true or problems.assume_if_true cache_files = config.cache_files or problems.cache_files clean_cache = config.clean_cache modifier = None if model_extension is not None: modifier = lambda hts: ModelExtension.extend( hts, ModelModifiersFactory.modifier_by_name(model_extension)) # generate systems for each problem configuration systems = {} for si in problems.symbolic_inits: encoder_config.symbolic_init = si (systems[(HTSM, si)], invar_props, ltl_props) = self.parse_model(problems.relative_path, \ problems.model_file, \ encoder_config, \ "System 1", \ modifier, \ cache_files=cache_files, \ clean_cache=clean_cache) if problems.equivalence is not None: (systems[(HTS2, si)], _, _) = self.parse_model(problems.relative_path, \ problems.equivalence, \ encoder_config, \ "System 2", \ cache_files=cache_files, \ clean_cache=clean_cache) else: systems[(HTS2, si)] = None if config.safety or config.problems: for invar_prop in invar_props: inv_prob = problems.new_problem() inv_prob.verification = VerificationType.SAFETY inv_prob.name = invar_prop[0] inv_prob.description = invar_prop[1] inv_prob.formula = invar_prop[2] problems.add_problem(inv_prob) if config.ltl or config.problems: for ltl_prop in ltl_props: ltl_prob = problems.new_problem() ltl_prob.verification = VerificationType.LTL ltl_prob.name = ltl_prop[0] ltl_prob.description = ltl_prop[1] ltl_prob.formula = ltl_prop[2] problems.add_problem(ltl_prob) if HTSD in systems: problems._hts = systems[HTSD] for problem in problems.problems: problem.hts = systems[(HTSM, problem.symbolic_init)] if problems._hts is None: problems._hts = problem.hts problem.hts2 = systems[(HTS2, problem.symbolic_init)] if problems._hts2 is None: problems._hts2 = problem.hts2 problem.vcd = problems.vcd or config.vcd or problem.vcd problem.abstract_clock = problems.abstract_clock or config.abstract_clock problem.add_clock = problems.add_clock or config.add_clock problem.coi = problems.coi or config.coi problem.run_coreir_passes = problems.run_coreir_passes problem.relative_path = problems.relative_path problem.cardinality = max(problems.cardinality, config.cardinality) if not problem.full_trace: problem.full_trace = problems.full_trace if not problem.trace_vars_change: problem.trace_vars_change = problems.trace_vars_change if not problem.trace_all_vars: problem.trace_all_vars = problems.trace_all_vars if not problem.clock_behaviors: clk_bhvs = [ p for p in [problems.clock_behaviors, config.clock_behaviors] if p is not None ] if len(clk_bhvs) > 0: problem.clock_behaviors = ";".join(clk_bhvs) if not problem.generators: problem.generators = config.generators Logger.log( "Solving with abstract_clock=%s, add_clock=%s" % (problem.abstract_clock, problem.add_clock), 2) if problem.trace_prefix is not None: problem.trace_prefix = "".join( [problem.relative_path, problem.trace_prefix]) if config.time or problems.time: timer_solve = Logger.start_timer("Problem %s" % problem.name, False) try: self.__solve_problem(problem, config) if problem.verification is None: Logger.log("Unset verification", 2) continue Logger.msg(" %s\n" % problem.status, 0, not (Logger.level(1))) if (assume_if_true) and \ (problem.status == VerificationStatus.TRUE) and \ (problem.assumptions == None) and \ (problem.verification == VerificationType.SAFETY): ass_ts = TS("Previous assumption from property") if TS.has_next(problem.formula): ass_ts.trans = problem.formula else: ass_ts.invar = problem.formula problem.hts.reset_formulae() problem.hts.add_ts(ass_ts) if config.time or problems.time: problem.time = Logger.get_timer(timer_solve, False) except KeyboardInterrupt as e: Logger.msg("\b\b Skipped!\n", 0)
def solve_safety_inc_bwd(self, hts, prop, k, assert_property=False, generalize=None): solver = self.solver.copy("inc_bwd") self._reset_assertions(solver) has_next = TS.has_next(prop) if has_next: prop = TS.to_prev(prop) init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() formula = self.at_ptime(And(Not(prop), invar), -1) Logger.log("Add not property at time %d"%0, 2) self._add_assertion(solver, formula) skip_push = False constraints = TRUE() models = 0 t = 0 k_min = 1 if has_next else 0 while (t < k+1): if not skip_push: self._push(solver) skip_push = False if not skip_push: pinit = self.at_ptime(init, t-1) Logger.log("Add init at time %d"%t, 2) self._add_assertion(solver, pinit) if constraints != TRUE(): for j in range(t+1): self._add_assertion(solver, self.at_ptime(constraints, j-1), "Addditional Constraints") if self.preferred is not None: try: for (var, val) in self.preferred: solver.solver.set_preferred_var(TS.get_timed(var, t), val) except: Logger.warning("Current solver does not support preferred variables") self.preferred = None if (t >= k_min) and self._solve(solver): Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(solver) models += 1 if models > 20: Logger.msg("R", 0, not(Logger.level(1))) self._reset_solver(solver) models = 0 if generalize is not None: constr, res = generalize(model, t) if res: return (t, model) constraints = And(constraints, Not(constr)) skip_push = True continue else: return (t, model) else: Logger.log("No counterexample found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) self._pop(solver) skip_push = False trans_t = self.unroll(trans, invar, t, t+1) self._add_assertion(solver, trans_t) if assert_property and t > 0: prop_t = self.unroll(TRUE(), prop, t-1, t) self._add_assertion(solver, prop_t) Logger.log("Add property at time %d"%t, 2) t += 1 return (t-1, None)
def solve_safety_inc_fwd(self, hts, prop, k, k_min, \ all_vars=False, generalize=None, prove=None): add_unsat_cons = False prove = self.config.prove if prove is None else prove solver_name = "inc_fwd%s"%("_prove" if prove else "") solver = self.solver.copy(solver_name) self._reset_assertions(solver) if prove: solver_ind = self.solver.copy("%s_ind"%solver_name) self._reset_assertions(solver_ind) if all_vars: relevant_vars = hts.vars else: relevant_vars = hts.state_vars | hts.output_vars init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() acc_init = TRUE() acc_prop = TRUE() acc_loop_free = TRUE() trans_t = TRUE() if self.config.simplify: Logger.log("Simplifying the Transition System", 1) if Logger.level(2): timer = Logger.start_timer("Simplify") init = simplify(init) trans = simplify(trans) invar = simplify(invar) if Logger.level(2): Logger.get_timer(timer) n_prop_t = FALSE() init_0 = self.at_time(And(init, invar), 0) Logger.log("Add init and invar", 2) self._add_assertion(solver, init_0) if prove: # add invariants at time 0, but not init self._add_assertion(solver_ind, self.at_time(invar, 0), "invar") next_prop = TS.has_next(prop) if next_prop: if k < 1: Logger.error("Invariant checking with next variables requires at least k=1") k_min = 1 skip_push = False constraints = TRUE() t = k_min for i in range(t): trans_t = self.unroll(trans, invar, i+1, i) self._add_assertion(solver, trans_t) Logger.msg("Unroll and call check-sat without property", 2) # Note: Seems to help a lot to call check-sat here # without this some solvers will run out of memory on large problems # it likely lets them do some internal clean up, and learn some things # about the model self._solve(solver) Logger.msg("_", 0, not(Logger.level(1))) while (t < k+1): if not skip_push: self._push(solver) skip_push = False t_prop = t-1 if next_prop else t if k_min > 0: if (not next_prop) or (next_prop and t>0): if n_prop_t == FALSE(): n_prop_t = self.at_time(Not(prop), t_prop) else: n_prop_t = Or(n_prop_t, self.at_time(Not(prop), t_prop)) else: n_prop_t = self.at_time(Not(prop), t) Logger.log("Add not property at time %d"%t, 2) if not skip_push: self._add_assertion(solver, n_prop_t, "Property") if constraints != TRUE(): self._add_assertion(solver, self.at_time(constraints, t), "Addditional Constraints") if t >= k_min: Logger.log("\nSolving for k=%s"%(t), 1) if self.preferred is not None: try: for (var, val) in self.preferred: for j in range(t+1): solver.solver.set_preferred_var(TS.get_timed(var, j), val) except: Logger.warning("Current solver does not support preferred variables") self.preferred = None if self._solve(solver): Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(solver) if generalize is not None: constr, res = generalize(model, t) if res: return (t, model) constraints = And(constraints, Not(constr)) skip_push = True continue else: return (t, model) else: Logger.log("No counterexample found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) if add_unsat_cons and prove: self._add_assertion(solver, Implies(self.at_time(And(init, invar), 1), self.at_time(Not(prop), t_prop+1))) self._add_assertion(solver, Not(n_prop_t)) else: Logger.log("\nSkipping solving for k=%s (k_min=%s)"%(t,k_min), 1) Logger.msg("_", 0, not(Logger.level(1))) self._pop(solver) skip_push = False if prove: if t > k_min: loop_free = self.loop_free(relevant_vars, t, t-1) # Checking I & T & loopFree acc_init = And(acc_init, self.at_time(Not(init), t)) acc_loop_free = And(acc_loop_free, loop_free) self._push(solver) self._add_assertion(solver, acc_init) self._add_assertion(solver, acc_loop_free) if self._solve(solver): Logger.log("Induction (I & lF) failed with k=%s"%(t), 1) else: Logger.log("Induction (I & lF) holds with k=%s"%(t), 1) return (t, True) self._pop(solver) # Checking T & loopFree & !P self._add_assertion(solver_ind, trans_t, comment="trans") self._add_assertion(solver_ind, loop_free, comment="loop_free") self._push(solver_ind) self._add_assertion(solver_ind, self.at_time(Not(prop), t_prop)) if self._solve(solver_ind): Logger.log("Induction (lF & !P) failed with k=%s"%(t), 1) else: Logger.log("Induction (lF & !P) holds with k=%s"%(t), 1) return (t, True) self._pop(solver_ind) self._add_assertion(solver_ind, self.at_time(prop, t_prop), "prop") else: if not next_prop: self._add_assertion(solver_ind, self.at_time(prop, t_prop), "prop") else: # add skipped transition self._add_assertion(solver_ind, trans_t, comment="trans") trans_t = self.unroll(trans, invar, t+1, t) self._add_assertion(solver, trans_t) t += 1 return (t-1, None)
def solve_safety_inc_int(self, hts, prop, k): init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() solver_proof = self.solver.copy("inc_int_proof") solver = self.solver.copy("inc_int") has_next = TS.has_next(prop) map_10 = dict([(TS.get_timed_name(v.symbol_name(), 1), TS.get_timed_name(v.symbol_name(), 0)) for v in hts.vars]) itp = Interpolator(logic=get_logic(trans)) init = And(init, invar) nprop = Not(prop) def check_overappr(Ri, R): self._reset_assertions(solver_proof) self._add_assertion(solver_proof, And(Ri, Not(R))) if not self._solve(solver_proof): Logger.log("Proof found with k=%s"%(t), 1) return TRUE() Logger.log("Extending initial states (%s)"%int_c, 1) return Or(R, Ri) t = 1 if has_next else 0 trans_t = self.unroll(trans, invar, k+1, gen_list=True) pivot = 2 trans_tA = And(trans_t[:pivot]) init_0 = self.at_time(init, 0) is_sat = True Ri = None self._reset_assertions(solver) while (t < k+1): Logger.log("\nSolving for k=%s"%t, 1) int_c = 0 R = init_0 # trans_t is composed as trans_i, invar_i, trans_i+1, invar_i+1, ... self._add_assertion(solver, trans_t[2*t]) self._add_assertion(solver, trans_t[(2*t)+1]) while True: Logger.log("Add init and invar", 2) self._push(solver) self._add_assertion(solver, R) npropt = self.at_time(nprop, t-1 if has_next else t) Logger.log("Add property time %d"%t, 2) self._add_assertion(solver, npropt) Logger.log("Interpolation at k=%s"%(t), 2) if t > 0: trans_tB = And(trans_t[pivot:(t*2)]) Ri = And(itp.binary_interpolant(And(R, trans_tA), And(trans_tB, npropt))) is_sat = Ri == None if is_sat and self._solve(solver): if R == init_0: Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(solver) return (t, model) else: Logger.log("No counterexample or proof found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) self._pop(solver) break else: self._pop(solver) if Ri is None: break Ri = substitute(Ri, map_10) res = check_overappr(Ri, R) if res == TRUE(): Logger.log("Proof found with k=%s"%(t), 1) return (t, True) R = res int_c += 1 t += 1 return (t-1, None)
def solve_safety_int(self, hts, prop, k): init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() has_next = TS.has_next(prop) map_10 = dict([(TS.get_timed_name(v.symbol_name(), 1), TS.get_timed_name(v.symbol_name(), 0)) for v in hts.vars]) itp = Interpolator(logic=get_logic(trans)) init = And(init, invar) nprop = Not(prop) pivot = 2 t = 1 if has_next else 0 while (t < k+1): Logger.log("\nSolving for k=%s"%t, 1) int_c = 0 init_0 = self.at_time(init, 0) R = init_0 trans_t = self.unroll(trans, invar, t, gen_list=True) trans_tA = And(trans_t[:pivot]) if t > 0 else TRUE() trans_tB = And(trans_t[pivot:]) if t > 0 else TRUE() while True: self._reset_assertions(self.solver) Logger.log("Add init and invar", 2) self._add_assertion(self.solver, R) self._add_assertion(self.solver, And(trans_tA, trans_tB)) npropt = self.at_time(nprop, t-1 if has_next else t) Logger.log("Add property time %d"%t, 2) self._add_assertion(self.solver, npropt) if self._solve(self.solver): if R == init_0: Logger.log("Counterexample found with k=%s"%(t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample or proof found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) break else: if len(trans_t) < 2: Logger.log("No counterexample found with k=%s"%(t), 1) Logger.msg(".", 0, not(Logger.level(1))) break Ri = And(itp.binary_interpolant(And(R, trans_tA), And(trans_tB, npropt))) Ri = substitute(Ri, map_10) self._reset_assertions(self.solver) self._add_assertion(self.solver, And(Ri, Not(R))) if not self._solve(self.solver): Logger.log("Proof found with k=%s"%(t), 1) return (t, True) else: R = Or(R, Ri) int_c += 1 Logger.log("Extending initial states (%s)"%int_c, 1) t += 1 return (t-1, None)
def solve_safety_inc(self, hts, prop, k, k_min, processes=1): retdic = {} if self.config.strategy == VerificationStrategy.MULTI: solvers = [] if self.config.prove: active_workers = [FWDK, VerificationStrategy.INT, VerificationStrategy.BWD, VerificationStrategy.FWD] else: active_workers = [VerificationStrategy.FWD, VerificationStrategy.BWD] active_workers = active_workers[:min(processes, len(active_workers))] workers = len(active_workers) with Manager() as manager: ret = manager.dict({}) # Forward if VerificationStrategy.FWD in active_workers: Logger.log("Starting \"%s\""%(VerificationStrategy.FWD), 1) solvers.append(Process(target=self._run_as_process, \ args=(self.solve_safety_inc_fwd, VerificationStrategy.FWD, ret, \ *[hts, prop, k, k_min, False, None, False]))) # Backward if VerificationStrategy.BWD in active_workers: Logger.log("Starting \"%s\""%(VerificationStrategy.BWD), 1) solvers.append(Process(target=self._run_as_process, \ args=(self.solve_safety_inc_bwd, VerificationStrategy.BWD, ret, \ *[hts, prop, k, False]))) # K-Induction if FWDK in active_workers: Logger.log("Starting \"%s\""%(FWDK), 1) solvers.append(Process(target=self._run_as_process, \ args=(self.solve_safety_inc_fwd, FWDK, ret, \ *[hts, prop, k, k_min, False, None, True]))) # Interpolation if VerificationStrategy.INT in active_workers: Logger.log("Starting \"%s\""%(VerificationStrategy.INT), 1) solvers.append(Process(target=self._run_as_process, \ args=(self.solve_safety_inc_int, VerificationStrategy.INT, ret, \ *[hts, prop, k]))) # Monitoring of the status status_checker = Process(target=self._status_checker, args=[ret, workers]) for solver in solvers: solver.start() status_checker.start() status_checker.join() for solver in solvers: solver.terminate() for key,val in ret.items(): retdic[key] = val tru_res = [(key,val) for key,val in retdic.items() if (val is not None) and (val[1] is not None) and (val[1] == True)] fal_res = [(key,val) for key,val in retdic.items() if (val is not None) and (val[1] is not None) and (val[1] != True)] unk_res = [(key,val) for key,val in retdic.items() if (val is not None) and (val[1] is None)] if (len(tru_res) > 0) and (len(fal_res) > 0): Logger.warning("Unconsistent results") if len(fal_res) > 0: winning = fal_res[0] elif len(tru_res) > 0: winning = tru_res[0] elif len(unk_res) > 0: winning = unk_res[0] else: Logger.error("No solution") Logger.msg("(%s)"%(winning[0]), 0, not(Logger.level(1))) # TODO: figure out a better way to handle this # config object is not settable anymore # if winning[0] == VerificationStrategy.BWD: # self.config.strategy = VerificationStrategy.BWD return winning[1] if self.config.strategy == VerificationStrategy.ALL: res = self.solve_safety_inc_fwd(hts, prop, k, k_min) if res[1] is not None: return res if self.config.prove and not TS.has_next(prop): res = self.solve_safety_int(hts, prop, k) if res[1] is not None: return res res = self.solve_safety_inc_bwd(hts, prop, k) if res[1] is not None: self.config.strategy == VerificationStrategy.BWD return res res = self.solve_safety_inc_zz(hts, prop, k) self.config.strategy == VerificationStrategy.ZZ return res if self.config.strategy in [VerificationStrategy.FWD, VerificationStrategy.AUTO]: return self.solve_safety_inc_fwd(hts, prop, k, k_min) if self.config.strategy == VerificationStrategy.BWD: return self.solve_safety_inc_bwd(hts, prop, k) if self.config.strategy == VerificationStrategy.ZZ: return self.solve_safety_inc_zz(hts, prop, k) if self.config.strategy == VerificationStrategy.INT: return self.solve_safety_inc_int(hts, prop, k) Logger.error("Invalid configuration strategy") return None
def solve_safety_inc_fwd(self, hts, prop, k, k_min, all_vars=False): self._reset_assertions(self.solver) if self.config.prove: self.solver_ind = self.solver.copy("ind") self._reset_assertions(self.solver_ind) if all_vars: relevant_vars = hts.vars else: relevant_vars = hts.state_vars | hts.input_vars | hts.output_vars init = hts.single_init() trans = hts.single_trans() invar = hts.single_invar() acc_init = TRUE() acc_prop = TRUE() acc_loop_free = TRUE() trans_t = TRUE() if self.config.simplify: Logger.log("Simplifying the Transition System", 1) if Logger.level(2): timer = Logger.start_timer("Simplify") init = simplify(init) trans = simplify(trans) invar = simplify(invar) if Logger.level(2): Logger.get_timer(timer) n_prop_t = FALSE() init_0 = self.at_time(And(init, invar), 0) Logger.log("Add init and invar", 2) self._add_assertion(self.solver, init_0) if self.config.prove: # add invariants at time 0, but not init self._add_assertion(self.solver_ind, self.at_time(invar, 0), "invar") next_prop = TS.has_next(prop) if next_prop: if k < 1: Logger.error( "Invariant checking with next variables requires at least k=1" ) k_min = 1 t = 0 while (t < k + 1): self._push(self.solver) t_prop = t - 1 if next_prop else t if k_min > 0: if (not next_prop) or (next_prop and t > 0): n_prop_t = Or(n_prop_t, self.at_time(Not(prop), t_prop)) else: n_prop_t = self.at_time(Not(prop), t) Logger.log("Add not property at time %d" % t, 2) self._add_assertion(self.solver, n_prop_t) if t >= k_min: Logger.log("\nSolving for k=%s" % (t), 1) if self._solve(self.solver): Logger.log("Counterexample found with k=%s" % (t), 1) model = self._get_model(self.solver) return (t, model) else: Logger.log("No counterexample found with k=%s" % (t), 1) Logger.msg(".", 0, not (Logger.level(1))) #self._add_assertion(self.solver, Not(n_prop_t)) else: Logger.log( "\nSkipping solving for k=%s (k_min=%s)" % (t, k_min), 1) Logger.msg(".", 0, not (Logger.level(1))) self._pop(self.solver) if self.config.prove: if t > k_min: loop_free = self.loop_free(relevant_vars, t, t - 1) # Checking I & T & loopFree acc_init = And(acc_init, self.at_time(Not(init), t)) acc_loop_free = And(acc_loop_free, loop_free) self._push(self.solver) self._add_assertion(self.solver, acc_init) self._add_assertion(self.solver, acc_loop_free) if self._solve(self.solver): Logger.log("Induction (I & lF) failed with k=%s" % (t), 1) else: Logger.log("Induction (I & lF) holds with k=%s" % (t), 1) return (t, True) self._pop(self.solver) # Checking T & loopFree & !P self._add_assertion(self.solver_ind, trans_t, comment="trans") self._add_assertion(self.solver_ind, loop_free, comment="loop_free") self._push(self.solver_ind) self._add_assertion(self.solver_ind, self.at_time(Not(prop), t_prop)) if self._solve(self.solver_ind): Logger.log( "Induction (lF & !P) failed with k=%s" % (t), 1) else: Logger.log("Induction (lF & !P) holds with k=%s" % (t), 1) return (t, True) self._pop(self.solver_ind) self._add_assertion(self.solver_ind, self.at_time(prop, t_prop), "prop") else: if not next_prop: self._add_assertion(self.solver_ind, self.at_time(prop, t_prop), "prop") trans_t = self.unroll(trans, invar, t + 1, t) self._add_assertion(self.solver, trans_t) t += 1 return (t - 1, None)
def solve_problems(self, problems_config: ProblemsManager) -> None: general_config = problems_config.general_config model_extension = general_config.model_extension assume_if_true = general_config.assume_if_true self.sparser = StringParser(general_config) self.lparser = LTLParser() self.coi = ConeOfInfluence() modifier = None if general_config.model_extension is not None: modifier = lambda hts: ModelExtension.extend( hts, ModelModifiersFactory.modifier_by_name(general_config. model_extension)) # generate main system system hts, invar_props, ltl_props = self.parse_model( general_config.model_files, problems_config.relative_path, general_config, "System 1", modifier) # Generate second models if any are necessary for problem in problems_config.problems: if problem.verification == VerificationType.EQUIVALENCE: if problem.equal_to is None: raise RuntimeError( "No second model for equivalence " "checking provided for problem {}".format( problem.name)) hts2, _, _ = self.parse_model(problem.equal_to, problems_config.relative_path, general_config, "System 2", modifier) problems_config.add_second_model(problem, hts2) # TODO : contain these types of passes in functions # they should be registered as passes if general_config.init is not None: iparser = InitParser() init_hts, inv_a, ltl_a = iparser.parse_file( general_config.init, general_config) assert inv_a is None and ltl_a is None, "Not expecting assertions from init state file" # remove old inits for ts in hts.tss: ts.init = TRUE() hts.combine(init_hts) hts.single_init(rebuild=True) # set default bit-wise initial values (0 or 1) if general_config.default_initial_value is not None: def_init_val = int(general_config.default_initial_value) try: if int(def_init_val) not in {0, 1}: raise RuntimeError except: raise RuntimeError( "Expecting 0 or 1 for default_initial_value," "but received {}".format(def_init_val)) def_init_ts = TS("Default initial values") new_init = [] initialized_vars = get_free_variables(hts.single_init()) state_vars = hts.state_vars num_def_init_vars = 0 num_state_vars = len(state_vars) const_arr_supported = True if hts.logic == L_ABV: for p in problems_config.problems: if p.solver_name not in CONST_ARRAYS_SUPPORT: const_arr_supported = False Logger.warning( "Using default_initial_value with arrays, " "but one of the selected solvers, " "{} does not support constant arrays. " "Any assumptions on initial array values will " "have to be done manually".format( problem.solver_name)) break for sv in state_vars - initialized_vars: if sv.get_type().is_bv_type(): width = sv.get_type().width if int(def_init_val) == 1: val = BV((2**width) - 1, width) else: val = BV(0, width) num_def_init_vars += 1 elif sv.get_type().is_array_type() and \ sv.get_type().elem_type.is_bv_type() and \ const_arr_supported: svtype = sv.get_type() width = svtype.elem_type.width if int(def_init_val) == 1: val = BV((2**width) - 1, width) else: val = BV(0, width) # create a constant array with a default value val = Array(svtype.index_type, val) else: continue def_init_ts.add_state_var(sv) new_init.append(EqualsOrIff(sv, val)) def_init_ts.set_behavior(simplify(And(new_init)), TRUE(), TRUE()) hts.add_ts(def_init_ts) Logger.msg( "Set {}/{} state elements to zero " "in initial state\n".format(num_def_init_vars, num_state_vars), 1) problems_config.hts = hts # TODO: Update this so that we can control whether embedded assertions are solved automatically if not general_config.skip_embedded: for invar_prop in invar_props: problems_config.add_problem( verification=VerificationType.SAFETY, name=invar_prop[0], description=invar_prop[1], properties=invar_prop[2]) self.properties.append(invar_prop[2]) for ltl_prop in ltl_props: problems_config.add_problem(verification=VerificationType.LTL, name=invar_prop[0], description=invar_prop[1], properties=invar_prop[2]) self.properties.append(ltl_prop[2]) Logger.log( "Solving with abstract_clock=%s, add_clock=%s" % (general_config.abstract_clock, general_config.add_clock), 2) # ensure the miter_out variable exists miter_out = None for problem in problems_config.problems: if problem.name is not None: Logger.log( "\n*** Analyzing problem \"%s\" ***" % (problem.name), 1) Logger.msg("Solving \"%s\" " % problem.name, 0, not (Logger.level(1))) # apply parametric behaviors (such as toggling the clock) # Note: This is supposed to be *before* creating the combined system for equivalence checking # we want this assumption to be applied to both copies of the clock problem_hts = ParametricBehavior.apply_to_problem( problems_config.hts, problem, general_config, self.model_info) if problem.verification == VerificationType.EQUIVALENCE: hts2 = problems_config.get_second_model(problem) problem_hts, miter_out = Miter.combine_systems( hts, hts2, problem.bmc_length, general_config.symbolic_init, problem.properties, True) try: # convert the formulas to PySMT FNodes # lemmas, assumptions and precondition always use the regular parser lemmas, assumptions, precondition = self.convert_formulae( [ problem.lemmas, problem.assumptions, problem.precondition ], parser=self.sparser, relative_path=problems_config.relative_path) if problem.verification != VerificationType.LTL: parser = self.sparser else: parser = self.lparser prop = None if problem.properties is not None: prop = self.convert_formula( problem.properties, relative_path=problems_config.relative_path, parser=parser) assert len(prop) == 1, "Properties should already have been split into " \ "multiple problems but found {} properties here".format(len(prop)) prop = prop[0] self.properties.append(prop) else: if problem.verification == VerificationType.SIMULATION: prop = TRUE() elif (problem.verification is not None) and (problem.verification != VerificationType.EQUIVALENCE): Logger.error( "Property not provided for problem {}".format( problem.name)) if problem.verification == VerificationType.EQUIVALENCE: assert miter_out is not None # set property to be the miter output # if user provided a different equivalence property, this has already # been incorporated in the miter_out prop = miter_out # reset the miter output miter_out = None if precondition: assert len(precondition ) == 1, "There should only be one precondition" prop = Implies(precondition[0], prop) # TODO: keep assumptions separate from the hts # IMPORTANT: CLEAR ANY PREVIOUS ASSUMPTIONS AND LEMMAS # This was previously done in __solve_problem and has been moved here # during the frontend refactor in April 2019 # this is necessary because the problem hts is just a reference to the # overall (shared) HTS problem_hts.assumptions = None problem_hts.lemmas = None # Compute the Cone Of Influence # Returns a *new* hts (not pointing to the original one anymore) if problem.coi: if Logger.level(2): timer = Logger.start_timer("COI") hts = self.coi.compute(hts, prop) if Logger.level(2): Logger.get_timer(timer) if general_config.time: timer_solve = Logger.start_timer( "Problem %s" % problem.name, False) status, trace, traces, region = self.__solve_problem( problem_hts, prop, lemmas, assumptions, problem) # set status for this problem problems_config.set_problem_status(problem, status) # TODO: Determine whether we need both trace and traces assert trace is None or traces is None, "Expecting either a trace or a list of traces" if trace is not None: problem_traces = self.__process_trace( hts, trace, general_config, problem) problems_config.set_problem_traces(problem, problem_traces) if traces is not None: traces_to_add = [] for trace in traces: problem_trace = self.__process_trace( hts, trace, general_config, problem) for pt in problem_trace: traces_to_add.append(pt) problems_config.set_problem_traces(problem, traces_to_add) if problem.verification == VerificationType.PARAMETRIC: assert region is not None problems_config.set_problem_region(problem, region) if status is not None: Logger.msg(" %s\n" % status, 0, not (Logger.level(1))) if (assume_if_true) and \ (status == VerificationStatus.TRUE) and \ (problem.assumptions == None) and \ (problem.verification == VerificationType.SAFETY): # TODO: relax the assumption on problem.assumptions # can still add it, just need to make it an implication ass_ts = TS("Previous assumption from property") if TS.has_next(prop): ass_ts.trans = prop else: ass_ts.invar = prop # add assumptions to main system problem_hts.reset_formulae() problem_hts.add_ts(ass_ts) if general_config.time: problems_config.set_problem_time( problem, Logger.get_timer(timer_solve, False)) except KeyboardInterrupt as e: Logger.msg("\b\b Skipped!\n", 0)
def parametric_safety(self, prop, k_max, k_min, parameters, monotonic=True, at_most=-1): if len(parameters) == 0: Logger.error("Parameters size cannot be 0") lemmas = self.hts.lemmas self._init_at_time(self.hts.vars, k_max) monotonic = True # if monotonic: # for p in parameters: # self.set_preferred((p, False)) self.region = FALSE() generalize = lambda model, t: self._get_param_assignments( model, t, parameters, monotonic) if at_most == -1: cardinality = len(parameters) else: cardinality = at_most prev_cs_count = 0 prove = self.config.prove step = 5 same_res_counter = 0 k = step end = False has_next = TS.has_next(prop) # Strategy selection increase_k = False if cardinality == -2: (t, status) = self.solve_safety_inc_fwd(self.hts, prop, k_max, k_min, all_vars=False, generalize=generalize) else: sn = SortingNetwork.sorting_network(parameters) if increase_k: # Approach with incremental increase of bmc k while k < k_max + 1: for at in range(cardinality): Logger.msg("[%d,%d]" % ((at + 1), k), 0, not (Logger.level(1))) sn_k = sn[at + 1] if at + 1 < len(sn) else FALSE() bound_constr = Or(sn_k, self.region) bound_constr = bound_constr if not has_next else Or( bound_constr, TS.to_next(bound_constr)) self.config.prove = False (t, status) = self.solve_safety_inc_bwd( self.hts, Or(prop, bound_constr), k, generalize=generalize) if (prev_cs_count == self.cs_count): same_res_counter += 1 else: same_res_counter = 0 prev_cs_count = self.cs_count if (prove == True) and ((same_res_counter > 1) or (at == cardinality - 1)): Logger.msg("[>%d,%d]" % ((at + 1), k), 0, not (Logger.level(1))) if (at_most > -1) and (at_most < cardinality): sn_k = sn[at_most - 1] else: sn_k = FALSE() bound_constr = Or(sn_k, self.region) bound_constr = bound_constr if not has_next else Or( bound_constr, TS.to_next(bound_constr)) self.config.prove = True (t, status) = self.solve_safety( self.hts, Or(prop, bound_constr), k, max(k_min, k - step)) if status == True: end = True break if (same_res_counter > 2) and (k < k_max): break if end: break k += step else: # Approach with fixed bmc k for at in range(cardinality): Logger.msg("[%d]" % ((at + 1)), 0, not (Logger.level(1))) sn_k = sn[at + 1] if at + 1 < len(sn) else FALSE() bound_constr = Or(sn_k, self.region) bound_constr = bound_constr if not has_next else Or( bound_constr, TS.to_next(bound_constr)) self.config.prove = False (t, status) = self.solve_safety_inc_bwd( self.hts, Or(prop, bound_constr), k_max, generalize=generalize) if simplify(self.region) == TRUE(): break if (prev_cs_count == self.cs_count): same_res_counter += 1 else: same_res_counter = 0 prev_cs_count = self.cs_count if (prove == True) and ((same_res_counter > 1) or (at == cardinality - 1)): Logger.msg("[>%d]" % ((at + 1)), 0, not (Logger.level(1))) if (at_most > -1) and (at_most < cardinality): sn_k = sn[at_most - 1] else: sn_k = FALSE() bound_constr = Or(sn_k, self.region) bound_constr = bound_constr if not has_next else Or( bound_constr, TS.to_next(bound_constr)) self.config.prove = True (t, status) = self.solve_safety(self.hts, Or(prop, bound_constr), k_max, k_min) if status == True: break traces = None if (self.models is not None) and (simplify(self.region) not in [TRUE(), FALSE()]): traces = [] for (model, time) in self.models: model = self._remap_model(self.hts.vars, model, time) trace = self.generate_trace(model, time, get_free_variables(prop)) traces.append(trace) region = [] dass = {} # Sorting result by size for ass in list(disjunctive_partition(self.region)): cp = list(conjunctive_partition(ass)) size = len(cp) if size not in dass: dass[size] = [] dass[size].append(ass) indexes = list(dass.keys()) indexes.sort() for size in indexes: region += dass[size] if status == True: return (VerificationStatus.TRUE, traces, region) elif status is not None: return (VerificationStatus.FALSE, traces, region) else: return (VerificationStatus.UNK, traces, region)