diff --git a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala index bfcec027cc5ae3a0ebee65277c9f403d16194d92..db658c4c6cc65baaa7ba2c1a486d7b660fe7444d 100644 --- a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala +++ b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala @@ -24,10 +24,9 @@ trait AbstractZ3Solver extends solvers.IncrementalSolverBuilder { class CantTranslateException(t: Z3AST) extends Exception("Can't translate from Z3 tree: " + t) + protected[leon] val z3cfg : Z3Config protected[leon] var z3 : Z3Context = null - protected[leon] var solver : Z3Solver = null protected[leon] var program : Program = null - protected[leon] val z3cfg : Z3Config override def setProgram(prog: Program): Unit = { program = prog @@ -86,7 +85,6 @@ trait AbstractZ3Solver extends solvers.IncrementalSolverBuilder { protected[leon] val mapRangeNoneTesters: MutableMap[TypeTree, Z3FuncDecl] = MutableMap.empty protected[leon] val mapRangeValueSelectors: MutableMap[TypeTree, Z3FuncDecl] = MutableMap.empty - private var neverInitialized = true private var counter = 0 private object nextIntForSymbol { def apply(): Int = { @@ -96,18 +94,22 @@ trait AbstractZ3Solver extends solvers.IncrementalSolverBuilder { } } + var isInitialized = false + protected[leon] def initZ3() { + if (!isInitialized) { + counter = 0 - protected[leon] def restartZ3 : Unit = { - counter = 0 - if (neverInitialized) { - neverInitialized = false z3 = new Z3Context(z3cfg) - solver = z3.mkSolver prepareSorts prepareFunctions - } else { - solver = z3.mkSolver + + isInitialized = true } + } + + protected[leon] def restartZ3() { + isInitialized = false + initZ3() exprToZ3Id = Map.empty z3IdToExpr = Map.empty diff --git a/src/main/scala/leon/solvers/z3/FairZ3Solver.scala b/src/main/scala/leon/solvers/z3/FairZ3Solver.scala index 90ede1bf55a938185fd28303f8ff7e15c1aac161..f784b63fc4bc9d0d8c505691ff6685380fc2af58 100644 --- a/src/main/scala/leon/solvers/z3/FairZ3Solver.scala +++ b/src/main/scala/leon/solvers/z3/FairZ3Solver.scala @@ -34,20 +34,6 @@ class FairZ3Solver(context : LeonContext) extends Solver(context) with AbstractZ ) toggleWarningMessages(true) - private var unrollingBank: UnrollingBank = null - private var blockingSet: Set[Expr] = Set.empty - private var toCheckAgainstModels: Expr = BooleanLiteral(true) - private var varsInVC: Set[Identifier] = Set.empty - - override def restartZ3 : Unit = { - super.restartZ3 - - unrollingBank = new UnrollingBank - blockingSet = Set.empty - toCheckAgainstModels = BooleanLiteral(true) - varsInVC = Set.empty - } - def isKnownDef(funDef: FunDef) : Boolean = functionMap.isDefinedAt(funDef) def functionDefToDecl(funDef: FunDef) : Z3FuncDecl = @@ -73,19 +59,9 @@ class FairZ3Solver(context : LeonContext) extends Solver(context) with AbstractZ functionMap = functionMap + (funDef -> z3Decl) reverseFunctionMap = reverseFunctionMap + (z3Decl -> funDef) } - - if(!Settings.noForallAxioms) { - prepareAxioms - } - - for(funDef <- program.definedFunctions) { - if (funDef.annotations.contains("axiomatize") && !axiomatizedFunctions(funDef)) { - reporter.warning("Function " + funDef.id + " was marked for axiomatization but could not be handled.") - } - } } - private def prepareAxioms : Unit = { + private def prepareAxioms(solver: Z3Solver): Unit = { assert(!Settings.noForallAxioms) program.definedFunctions.foreach(_ match { case fd @ Catamorphism(acd, cases) => { @@ -138,358 +114,29 @@ class FairZ3Solver(context : LeonContext) extends Solver(context) with AbstractZ }) } - override def solve(vc: Expr) = decide(vc, true) - - override def solveSAT(vc : Expr) : (Option[Boolean],Map[Identifier,Expr]) = { - restartZ3 - val (res, model, core) = decideWithModel(vc, false) - (res, model) + override def solve(vc: Expr) = { + val solver = getNewSolver + solver.assertCnstr(Not(vc)) + solver.check.map(!_) } - def decide(vc: Expr, forValidity: Boolean):Option[Boolean] = { - restartZ3 - decideWithModel(vc, forValidity)._1 + override def solveSAT(vc : Expr) : (Option[Boolean],Map[Identifier,Expr]) = { + val solver = getNewSolver + solver.assertCnstr(vc) + (solver.check, solver.getModel) } - private var foundDefinitiveAnswer : Boolean = false override def halt() { - if(!foundDefinitiveAnswer) { - super.halt - if(z3 != null) - z3.softCheckCancel + super.halt + if(z3 ne null) { + z3.softCheckCancel } } override def solveSATWithCores(expression: Expr, assumptions: Set[Expr]): (Option[Boolean], Map[Identifier, Expr], Set[Expr]) = { - restartZ3 - decideWithModel(expression, false, None, Some(assumptions)) - } - - private[this] def decideWithModel(vc: Expr, forValidity: Boolean, evaluator: Option[(Map[Identifier,Expr]) => Boolean] = None, assumptions: Option[Set[Expr]] = None): (Option[Boolean], Map[Identifier,Expr], Set[Expr]) = { - val initializationStopwatch = new Stopwatch("init", false) - val blocking2Z3Stopwatch = new Stopwatch("blocking-set-to-z3", false) - val z3SearchStopwatch = new Stopwatch("z3-search-1", false) - val secondZ3SearchStopwatch = new Stopwatch("z3-search-2", false) - val unrollingStopwatch = new Stopwatch("unrolling", false) - val luckyStopwatch = new Stopwatch("lucky", false) - val validatingStopwatch = new Stopwatch("validating", false) - val decideTopLevelSw = new Stopwatch("top-level", false).start - - // println("Deciding : " + vc) - assumptions match { - case Some(set) => { - if (Settings.useCores) - throw new Exception("Use of cores while checking assumptions is not supported") - if (set.exists(e => e match { - case Variable(_) => false - case Not(Variable(_)) => false - case _ => true - })) - throw new Exception("assumptions may only be boolean literals") - } - case None => - } - - initializationStopwatch.start - - foundDefinitiveAnswer = false - - var definitiveAnswer : Option[Boolean] = None - var definitiveModel : Map[Identifier,Expr] = Map.empty - var definitiveCore : Set[Expr] = Set.empty - - def foundAnswer(answer : Option[Boolean], model : Map[Identifier,Expr] = Map.empty, core: Set[Expr] = Set.empty) : Unit = { - foundDefinitiveAnswer = true - definitiveAnswer = answer - definitiveModel = model - definitiveCore = core - //timer.foreach(t => t.halt) - } - - if (program == null) { - reporter.error("Z3 Solver was not initialized with a PureScala Program.") - None - } - - // naive implementation of unrolling everything every n-th iteration - val unrollingThreshold = 100 - var unrollingCounter = 0 - - val expandedVC = expandLets(if (forValidity) negate(vc) else vc) - toCheckAgainstModels = And(toCheckAgainstModels,expandedVC) - - varsInVC ++= variablesOf(expandedVC) - - reporter.info(" - Initial unrolling...") - val (clauses, guards) = unrollingBank.scanForNewTemplates(expandedVC) - - val cc = toZ3Formula(And(clauses)).get - solver.assertCnstr(cc) - - // these are the optional sequence of assumption literals - val assumptionsAsZ3: Option[Seq[Z3AST]] = assumptions.map(set => set.toSeq.map(toZ3Formula(_).get)) - - blockingSet ++= Set(guards.map(p => if(p._2) Not(Variable(p._1)) else Variable(p._1)) : _*) - - var iterationsLeft : Int = if(Settings.unrollingLevel > 0) Settings.unrollingLevel else 16121984 - - initializationStopwatch.stop - while(!foundDefinitiveAnswer && !forceStop) { - iterationsLeft -= 1 - - blocking2Z3Stopwatch.start - val blockingSetAsZ3 : Seq[Z3AST] = blockingSet.toSeq.map(toZ3Formula(_).get) - // println("Blocking set : " + blockingSet) - blocking2Z3Stopwatch.stop - - solver.push - if(!blockingSetAsZ3.isEmpty) - solver.assertCnstr(z3.mkAnd(blockingSetAsZ3 : _*)) - - reporter.info(" - Running Z3 search...") - val answerModelCore : (Option[Boolean], Z3Model, Set[Expr]) = { - z3SearchStopwatch.start - - val res = assumptionsAsZ3 match { - case Some(as) => solver.checkAssumptions(as: _*) - case None => solver.check() - } - - val z3model = res match { - case Some(true) => solver.getModel - case _ => null - } - - val z3core: Seq[Z3AST] = (res, assumptionsAsZ3) match { - case (Some(false), ass) if ass.isDefined => solver.getUnsatCore.toSeq - case _ => Seq() - } - - z3SearchStopwatch.stop - - val core: Set[Expr] = z3core.map(ast => fromZ3Formula(z3model, ast, Some(BooleanType)) match { - case n @ Not(Variable(_)) => n - case v @ Variable(_) => v - case x => scala.sys.error("Impossible element extracted from core: " + ast + " (as Leon tree : " + x + ")") - }).toSet - - (res, z3model, core) - } - - val (answer, model, core) = answerModelCore // to work around the stupid type inferer - - reporter.info(" - Finished search with blocked literals") - - // if (Settings.useCores) - // reporter.info(" - Core is : " + core) - - val reinterpretedAnswer = if(!UNKNOWNASSAT) answer else (answer match { - case None | Some(true) => Some(true) - case Some(false) => Some(false) - }) - - (reinterpretedAnswer, model) match { - case (None, m) => { // UNKNOWN - // reporter.warning("Z3 doesn't know because: " + z3.getSearchFailure.message) - reporter.warning("Z3 doesn't know because ??") - foundAnswer(None) - } - case (Some(true), m) => { // SAT - validatingStopwatch.start - val (trueModel, model) = if(Settings.verifyModel) - validateAndDeleteModel(m, toCheckAgainstModels, varsInVC, evaluator) - else { - val res = (true, modelToMap(m, varsInVC)) - lazy val modelAsString = res._2.toList.map(p => p._1 + " -> " + p._2).mkString("\n") - reporter.info("- Found a model:") - reporter.info(modelAsString) - res - } - validatingStopwatch.stop - - if (trueModel) { - foundAnswer(Some(false), model) - } else { - reporter.error("Something went wrong. The model should have been valid, yet we got this : ") - reporter.error(model) - foundAnswer(None, model) - } - } - case (Some(false), m) if blockingSet.isEmpty => { - foundAnswer(Some(true), core = core) - solver.pop(1) - } - // This branch is both for with and without unsat cores. The - // distinction is made inside. - case (Some(false), m) => { - solver.pop(1) - - if (Settings.luckyTest && !forceStop) { - // we need the model to perform the additional test - reporter.info(" - Running search without blocked literals (w/ lucky test)") - secondZ3SearchStopwatch.start - - val res2 = assumptionsAsZ3 match { - case Some(as) => solver.checkAssumptions(as: _*) - case None => solver.check() - } - - val z3model2 = res2 match { - case Some(true) => solver.getModel - case _ => null - } - - val z3core2: Seq[Z3AST] = (res2, assumptionsAsZ3) match { - case (Some(false), ass) if ass.isDefined => solver.getUnsatCore.toSeq - case _ => Seq() - } - - secondZ3SearchStopwatch.stop - reporter.info(" - Finished search without blocked literals") - - if (res2 == Some(false)) { - val core2: Set[Expr] = z3core2.map(ast => fromZ3Formula(z3model2, ast, Some(BooleanType)) match { - case n @ Not(Variable(_)) => n - case v @ Variable(_) => v - case x => scala.sys.error("Impossible element extracted from core: " + ast + " (as Leon tree : " + x + ")") - }).toSet - - foundAnswer(Some(true), core = core2) - } else { - luckyStopwatch.start - // we might have been lucky :D - val (wereWeLucky, cleanModel) = validateAndDeleteModel(z3model2, toCheckAgainstModels, varsInVC, evaluator) - if(wereWeLucky) { - foundAnswer(Some(false), cleanModel) - } - luckyStopwatch.stop - } - } else { - // only checking will suffice - reporter.info(" - Running search without blocked literals (w/o lucky test)") - secondZ3SearchStopwatch.start - val result2 = assumptionsAsZ3 match { - case Some(as) => solver.checkAssumptions(as: _*) - case None => solver.check() - } - secondZ3SearchStopwatch.stop - reporter.info(" - Finished search without blocked literals") - - if (result2 == Some(false)) { - val z3model = solver.getModel() - - val core: Set[Expr] = solver.getUnsatCore.map(ast => fromZ3Formula(z3model, ast, Some(BooleanType)) match { - case n @ Not(Variable(_)) => n - case v @ Variable(_) => v - case x => scala.sys.error("Impossible element extracted from core: " + ast + " (as Leon tree : " + x + ")") - }).toSet - - foundAnswer(Some(true), core = core) - } - } - - if(forceStop) { - foundAnswer(None) - } - - if(!foundDefinitiveAnswer) { - reporter.info("- We need to keep going.") - - unrollingStopwatch.start - - val toRelease : Set[Expr] = blockingSet - - // reporter.info(" - toRelease : " + toRelease) - // reporter.info(" - blockingSet : " + blockingSet) - - var fixedForEver : Set[Expr] = Set.empty - - if(Settings.pruneBranches) { - for(ex <- blockingSet) ex match { - case Not(Variable(b)) => { - solver.push - solver.assertCnstr(toZ3Formula(Variable(b)).get) - solver.check match { - case Some(false) => { - //println("We had ~" + b + " in the blocking set. We now know it should stay there forever.") - solver.pop(1) - solver.assertCnstr(toZ3Formula(Not(Variable(b))).get) - fixedForEver = fixedForEver + ex - } - case _ => solver.pop(1) - } - } - case Variable(b) => { - solver.push - solver.assertCnstr(toZ3Formula(Not(Variable(b))).get) - solver.check match { - case Some(false) => { - //println("We had " + b + " in the blocking set. We now know it should stay there forever.") - solver.pop(1) - solver.assertCnstr(toZ3Formula(Variable(b)).get) - fixedForEver = fixedForEver + ex - } - case _ => solver.pop(1) - } - } - case _ => scala.sys.error("Should not have happened.") - } - if(fixedForEver.size > 0) { - reporter.info("- Pruned out " + fixedForEver.size + " branches.") - } - } - - - blockingSet = blockingSet -- toRelease - - val toReleaseAsPairs : Set[(Identifier,Boolean)] = (toRelease -- fixedForEver).map(b => b match { - case Variable(id) => (id, true) - case Not(Variable(id)) => (id, false) - case _ => scala.sys.error("Impossible value in release set: " + b) - }) - - reporter.info(" - more unrollings") - for((id,polarity) <- toReleaseAsPairs) { - val (newClauses,newBlockers) = unrollingBank.unlock(id, !polarity) - - for(ncl <- newClauses) { - solver.assertCnstr(toZ3Formula(ncl).get) - } - blockingSet = blockingSet ++ Set(newBlockers.map(p => if(p._2) Not(Variable(p._1)) else Variable(p._1)) : _*) - } - reporter.info(" - finished unrolling") - unrollingStopwatch.stop - } - } - } - - if(iterationsLeft <= 0) { - reporter.error(" - Max. number of iterations reached.") - foundAnswer(None) - } - } - - decideTopLevelSw.stop - decideTopLevelSw.writeToSummary - initializationStopwatch.writeToSummary - blocking2Z3Stopwatch.writeToSummary - z3SearchStopwatch.writeToSummary - secondZ3SearchStopwatch.writeToSummary - unrollingStopwatch.writeToSummary - luckyStopwatch.writeToSummary - validatingStopwatch.writeToSummary - - if(forceStop) { - (None, Map.empty, Set.empty) - } else { - val realAnswer = if(forValidity) { - definitiveAnswer - } else { - definitiveAnswer.map(!_) - } - - (realAnswer, definitiveModel, definitiveCore) - } + val solver = getNewSolver + solver.assertCnstr(expression) + (solver.checkAssumptions(assumptions), solver.getModel, solver.getUnsatCore) } private def validateAndDeleteModel(model: Z3Model, formula: Expr, variables: Set[Identifier], evaluator: Option[(Map[Identifier,Expr])=>Boolean] = None) : (Boolean, Map[Identifier,Expr]) = { @@ -622,8 +269,22 @@ class FairZ3Solver(context : LeonContext) extends Solver(context) with AbstractZ } def getNewSolver = new solvers.IncrementalSolver { + + initZ3 + val solver = z3.mkSolver + if(!Settings.noForallAxioms) { + prepareAxioms(solver) + } + + for(funDef <- program.definedFunctions) { + if (funDef.annotations.contains("axiomatize") && !axiomatizedFunctions(funDef)) { + reporter.warning("Function " + funDef.id + " was marked for axiomatization but could not be handled.") + } + } + + private var frameGuards = List[Z3AST](z3.mkFreshConst("frame", z3.mkBoolSort)) private var frameExpressions = List[List[Expr]](Nil) private var varsInVC = Set[Identifier]() @@ -669,8 +330,6 @@ class FairZ3Solver(context : LeonContext) extends Solver(context) with AbstractZ frameExpressions = (expression :: frameExpressions.head) :: frameExpressions.tail - solver.assertCnstr(z3.mkImplies(guard, toZ3Formula(expression).get)) - val (newClauses, newGuards) = unrollingBank.scanForNewTemplates(expression) for (cl <- newClauses) { diff --git a/src/main/scala/leon/solvers/z3/UninterpretedZ3Solver.scala b/src/main/scala/leon/solvers/z3/UninterpretedZ3Solver.scala index 16f3bf92af8b53ef072025dc720a1897b48d47e5..87ef4ecd284829c126c82f0b84a7148a0ebf0ae0 100644 --- a/src/main/scala/leon/solvers/z3/UninterpretedZ3Solver.scala +++ b/src/main/scala/leon/solvers/z3/UninterpretedZ3Solver.scala @@ -54,34 +54,32 @@ class UninterpretedZ3Solver(context : LeonContext) extends Solver(context) with // Where the solving occurs override def solveSAT(expression : Expr) : (Option[Boolean],Map[Identifier,Expr]) = { - restartZ3 + val solver = getNewSolver val emptyModel = Map.empty[Identifier,Expr] val unknownResult = (None, emptyModel) val unsatResult = (Some(false), emptyModel) - val result = toZ3Formula(expression).map { asZ3 => - solver.assertCnstr(asZ3) - solver.check match { - case Some(false) => unsatResult - case Some(true) => { - val model = solver.getModel - - if(containsFunctionCalls(expression)) { - unknownResult - } else { - val variables = variablesOf(expression) - (Some(true), modelToMap(model, variables)) - } + solver.assertCnstr(expression) + + val result = solver.check match { + case Some(false) => unsatResult + case Some(true) => { + if(containsFunctionCalls(expression)) { + unknownResult + } else { + (Some(true), solver.getModel) } - case _ => unknownResult } - } getOrElse unknownResult + case _ => unknownResult + } result } def getNewSolver = new solvers.IncrementalSolver { + initZ3 + val solver = z3.mkSolver def push() {