def tryAndVerifyWithSplitterPreds( fun: Function, old_sound_invs: Set[AstExpr], boogie_invs: Set[AstExpr], splitterPreds: List[AstExpr], partialInvs: List[AstExpr], timeout: Optional[int] = None) -> TryAndVerifyResult: """ Wrapper around tryAndVerify_impl that adds implication with the splitter predicates to all candidate invariants. Returns ((p1_overfit, p2_overfit), (p1_nonindg, p2_nonind), sound, violations) Where p1_overfit, p2_ovefit are lists of pairs of overfittted invariants and their respective counterexamples from passes 1 and 2 p1_nonind, p2_nonind are lists of pairs of noninductive invariants and their respective counterexamples from passes 1 and 2 sound is a set of sound invariants violations is a list of any safety violations permitted by the sound invariants """ initial_sound = old_sound_invs.union(partialInvs) boogieTypeEnv = fun.getTypeEnv() z3TypeEnv = boogieToZ3TypeEnv(boogieTypeEnv) # First lets find the invariants that are sound without implication p1_overfitted, p1_nonind, p1_sound, violations =\ tryAndVerify_impl(fun, initial_sound, boogie_invs, timeout) p1_sound = \ set([x for x in p1_sound \ if not conservative_tautology(expr_to_z3(x, z3TypeEnv))]) # Next lets add implication to all unsound invariants from first pass # Also add manually specified partialInvs unsound: List[AstExpr] = [ inv_ctr_pair[0] for inv_ctr_pair in p1_overfitted + p1_nonind ] candidate_precedents: List[AstExpr] = \ [ ast_and(pSet) for pSet in nonempty(powerset(splitterPreds)) ] p2_invs: List[AstExpr] = [ AstBinExpr(precc, "==>", inv) for precc in candidate_precedents for inv in unsound ] p2_invs += partialInvs p2_invs = \ list(set([ x for x in p2_invs \ if not conservative_tautology(expr_to_z3(x, z3TypeEnv)) ])) # And look for any new sound invariants p2_overfitted, p2_nonind, p2_sound, violations = \ tryAndVerify_impl(fun, p1_sound.union(set(partialInvs)), \ p2_invs, timeout) sound = p1_sound.union(p2_sound) return ((p1_overfitted, p2_overfitted), \ (p1_nonind, p2_nonind), \ sound, \ violations)
def loadBoogieLvlSet(lvlSetFile: str) -> Tuple[str, Dict[str, BoogieTraceLvl]]: # Small helper funct to make sure we didn't # accidentally give two levels the same name def assertUniqueKeys(kvs): keys = [x[0] for x in kvs] assert (len(set(keys)) == len(keys)) return dict(kvs) lvlSet = load(open(lvlSetFile, "r"), object_pairs_hook=assertUniqueKeys) lvlSetDir = dirname(abspath(realpath(lvlSetFile))) error("Loading level set " + lvlSet["name"] + " from " + lvlSetFile) lvls: Dict[str, BoogieTraceLvl] = OrderedDict() for t in lvlSet["levels"]: lvlName = t[0] lvlPath = t[1] for i in range(len(lvlPath)): lvlPath[i] = join(lvlSetDir, lvlPath[i]) error("Loading level: ", lvlPath[0]) lvl = loadBoogieFile(lvlPath[0]) lvl["path"] = lvlPath if (len(t) > 2): splitterPreds = [parseExprAst(exp) for exp in t[2]] splitterPred = ast_and(splitterPreds) remainderInv = parseExprAst(t[3]) lvl['data'][0] = [ row for row in lvl['data'][0] if eval_quick(splitterPred, {k: v for k, v in zip(lvl['variables'], row)}) ] if (len(lvl['data'][0]) == 0): error("SKIPPING : ", lvlName, " due to no filtered rows.") continue lvl['partialInv'] = remainderInv lvl['splitterPreds'] = splitterPreds lvls[lvlName] = lvl return (lvlSet["name"], lvls)
def daikonToBoogieExpr(astn: dast.AstExpr) -> bast.AstExpr: if (isinstance(astn, dast.AstUnExpr)): cn = daikonToBoogieExpr(astn.expr) try: boogieOp = { '-': '-', '!': '!', }[astn.op] return bast.AstUnExpr(boogieOp, cn) except: raise Exception("Don't know how to translate " + str(astn)) elif (isinstance(astn, dast.AstBinExpr)): ln, rn = list(map(daikonToBoogieExpr, [astn.lhs, astn.rhs])) # We can translate power operators to a constant power if (astn.op == "**" and isinstance(astn.rhs, dast.AstNumber)): res = ln for _ in range(astn.rhs.num - 1): res = bast.AstBinExpr(res, '*', ln) return res else: try: boogieOp = { '&&': '&&', '||': '||', '==': '==', '!=': '!=', '<': '<', '>': '>', '<=': '<=', '>=': '>=', '+': '+', '-': '-', '*': '*', '/': '/', '%': 'mod', '==>': '==>' }[astn.op] return bast.AstBinExpr(ln, boogieOp, rn) except: raise Exception("Don't know how to translate " + str(astn)) elif (isinstance(astn, dast.AstId)): return bast.AstId(astn.name) elif (isinstance(astn, dast.AstNumber)): return bast.AstNumber(astn.num) elif (isinstance(astn, dast.AstTrue)): return bast.AstTrue() elif (isinstance(astn, dast.AstFalse)): return bast.AstFalse() elif (isinstance(astn, dast.AstIsOneOf)): cn = daikonToBoogieExpr(astn.expr) return bast.ast_or([ bast.AstBinExpr(cn, "==", x) for x in [daikonToBoogieExpr(y) for y in astn.options] ]) elif (isinstance(astn, dast.AstInRange)): cn = daikonToBoogieExpr(astn.expr) low = astn.lower.num hi = astn.upper.num return bast.ast_and([ bast.AstBinExpr(bast.AstNumber(low), "<=", cn), bast.AstBinExpr(cn, "<=", bast.AstNumber(hi)) ]) elif (isinstance(astn, dast.AstIsBoolean)): cn = daikonToBoogieExpr(astn.expr) return bast.ast_or([ bast.AstBinExpr(cn, "==", bast.AstNumber(0)), bast.AstBinExpr(cn, "==", bast.AstNumber(1)) ]) elif (isinstance(astn, dast.AstIsEven)): cn = daikonToBoogieExpr(astn.expr) return bast.AstBinExpr(bast.AstBinExpr(cn, "mod", bast.AstNumber(2)), "==", bast.AstNumber(0)) elif (isinstance(astn, dast.AstIsConstMod)): expr = daikonToBoogieExpr(astn.expr) remainder = daikonToBoogieExpr(astn.remainder) modulo = ccast(daikonToBoogieExpr(astn.modulo), bast.AstNumber) assert modulo.num != 0 return bast.AstBinExpr(bast.AstBinExpr(expr, "mod", modulo), "==", remainder) elif (isinstance(astn, dast.AstHasValues)): if (len(astn.values) > 300): raise Exception("Can't convert HasValues: Too many options: " +\ str(len(astn.values))) cn = daikonToBoogieExpr(astn.expr) values = list(map(daikonToBoogieExpr, astn.values)) return bast.ast_or([bast.AstBinExpr(cn, "==", v) for v in values]) else: raise Exception("Don't know how to translate " + str(astn))
print("Level, Num Invariants Found, Invariants Found, " +\ "Num Sound Invariants, Sound Invariants, IsSolved") else: print("Level, Num Invariants Found, Invariants Found, " +\ "Num Sound Invariants, Sound Invariants") for lvlName, lvl in lvls.items(): (vs, header_vals) = (lvl["variables"], lvl["data"][0]) fuzz_path: str = lvl["path"][0][:-len(".bpl")] + ".fuzz_traces" if (exists(fuzz_path)): for f in listdir(fuzz_path): t = eval(open(fuzz_path + "/" + f).read()); t_loop_headers = [x for x in t if 'LoopHead' in x[0]] assert (len(set([x[0] for x in t_loop_headers])) == 1) if ("splitterPreds" in lvl): splitterPred = bast.ast_and(lvl["splitterPreds"]) t_loop_headers = \ [row for row in t_loop_headers if eval_quick(splitterPred, row[1][0])] t_header_vals = [ [ row[1][0][v] for v in vs ] \ for row in t_loop_headers ] header_vals = header_vals + t_header_vals invs = runDaikon(vs, header_vals, args.no_suppression) binvs = [ ] for inv in invs: try: binvs.append(daikonToBoogieExpr(inv)) except Exception: if (not args.csv_table): sys.stderr.write("Can't translate " + str(inv) + "\n");
if (endsWithNumP.match(lvl_name)): origName = justEnd.split(lvl_name)[0] originalToSplitM[origName] = originalToSplitM.get(origName, []) +\ [ lvl_name ] splitToOriginal[lvl_name] = origName print("Checking splitter predicates non-ovrelapping") for origName in list(originalToSplitM.keys()): # For now we assume each split level has a single splitterPred preds = [ unique(lvls[x]["splitterPreds"]) for x in originalToSplitM[origName] ] for i in range(0, len(preds)): for j in range(i+1, len(preds)): if (not unsatisfiable(expr_to_z3(bast.ast_and([preds[i], preds[j]]), AllIntTypeEnv()))): print("Predicates ", preds[i], "and", preds[j], "from split of ", \ origName, "are not independent") print("Checking conjunction of partial predicates is a solution") for origName in list(originalToSplitM.keys()): # For now we assume each split level has a single splitterPred preds = [ lvls[x]["partialInv"] for x in originalToSplitM[origName] ] if (len(preds) == 1): print("Skipping ", origName, "- only 1 split level (no other side of split)") continue conj = bast.ast_and(preds); lvl = lvls[originalToSplitM[origName][0]] try: (overfit, nonind, sound, violations) = verify(lvl, [conj])