diff --git a/src/main/scala/leon/solvers/QuantificationSolver.scala b/src/main/scala/leon/solvers/QuantificationSolver.scala index 4f56903c578da6c2d1b71f19268e8885cb93e99e..8ffe0a004cd23c39da6cd373774497ee78835974 100644 --- a/src/main/scala/leon/solvers/QuantificationSolver.scala +++ b/src/main/scala/leon/solvers/QuantificationSolver.scala @@ -27,7 +27,6 @@ class HenkinModelBuilder(domains: HenkinDomains) trait QuantificationSolver extends Solver { val program: Program - def getModel: HenkinModel protected lazy val requireQuantification = program.definedFunctions.exists { fd => purescala.ExprOps.exists { case _: Forall => true case _ => false } (fd.fullBody) diff --git a/src/main/scala/leon/solvers/SolverFactory.scala b/src/main/scala/leon/solvers/SolverFactory.scala index 42d7ead0235a13ad3465dcd1fe64fd80db33ed25..4f8b1f50ae346887c945823ff429761be1a34a78 100644 --- a/src/main/scala/leon/solvers/SolverFactory.scala +++ b/src/main/scala/leon/solvers/SolverFactory.scala @@ -31,13 +31,13 @@ object SolverFactory { } val definedSolvers = Map( - "fairz3" -> "Native Z3 with z3-templates for unfolding (default)", + "fairz3" -> "Native Z3 with z3-templates for unrolling (default)", "smt-cvc4" -> "CVC4 through SMT-LIB", "smt-z3" -> "Z3 through SMT-LIB", "smt-z3-q" -> "Z3 through SMT-LIB, with quantified encoding", "smt-cvc4-proof" -> "CVC4 through SMT-LIB, in-solver inductive reasoning, for proofs only", "smt-cvc4-cex" -> "CVC4 through SMT-LIB, in-solver finite-model-finding, for counter-examples only", - "unrollz3" -> "Native Z3 with leon-templates for unfolding", + "unrollz3" -> "Native Z3 with leon-templates for unrolling", "ground" -> "Only solves ground verification conditions by evaluating them", "enum" -> "Enumeration-based counter-example-finder", "isabelle" -> "Isabelle2015 through libisabelle with various automated tactics" diff --git a/src/main/scala/leon/solvers/combinators/UnrollingSolver.scala b/src/main/scala/leon/solvers/combinators/UnrollingSolver.scala index 1235d69c6d16a2726971cac386732fdcea501b95..5f002ccdacb54b16515fa0d54f7573ba2ebc82cb 100644 --- a/src/main/scala/leon/solvers/combinators/UnrollingSolver.scala +++ b/src/main/scala/leon/solvers/combinators/UnrollingSolver.scala @@ -4,6 +4,7 @@ package leon package solvers package combinators +import purescala.Printable import purescala.Common._ import purescala.Definitions._ import purescala.Quantification._ @@ -13,118 +14,205 @@ import purescala.ExprOps._ import purescala.Types._ import utils._ -import z3.FairZ3Component.{optFeelingLucky, optUseCodeGen, optAssumePre, optNoChecks, optUnfoldFactor} import templates._ import evaluators._ import Template._ -class UnrollingSolver(val context: LeonContext, val program: Program, underlying: Solver) - extends Solver - with NaiveAssumptionSolver - with EvaluatingSolver - with QuantificationSolver { +trait UnrollingProcedure extends LeonComponent { + val name = "Unroll-P" + val description = "Leon Unrolling Procedure" - val feelingLucky = context.findOptionOrDefault(optFeelingLucky) - val useCodeGen = context.findOptionOrDefault(optUseCodeGen) - val assumePreHolds = context.findOptionOrDefault(optAssumePre) - val disableChecks = context.findOptionOrDefault(optNoChecks) - val unfoldFactor = context.findOptionOrDefault(optUnfoldFactor) + val optUnrollFactor = LeonLongOptionDef("unrollfactor", "Number of unfoldings to perform in each unfold step", default = 1, "<PosInt>") + val optFeelingLucky = LeonFlagOptionDef("feelinglucky", "Use evaluator to find counter-examples early", false) + val optCheckModels = LeonFlagOptionDef("checkmodels", "Double-check counter-examples with evaluator", false) + val optUnrollCores = LeonFlagOptionDef("unrollcores", "Use unsat-cores to drive unfolding while remaining fair", false) + val optUseCodeGen = LeonFlagOptionDef("codegen", "Use compiled evaluator instead of interpreter", false) + val optAssumePre = LeonFlagOptionDef("assumepre", "Assume precondition holds (pre && f(x) = body) when unfolding", false) + val optPartialModels = LeonFlagOptionDef("partialmodels", "Extract domains for quantifiers and bounded first-class functions", false) - protected var lastCheckResult : (Boolean, Option[Boolean], Option[HenkinModel]) = (false, None, None) + override val definedOptions: Set[LeonOptionDef[Any]] = + Set(optCheckModels, optFeelingLucky, optUseCodeGen, optUnrollCores, optAssumePre, optUnrollFactor, optPartialModels) +} - private val freeVars = new IncrementalSet[Identifier]() - private val constraints = new IncrementalSeq[Expr]() +object UnrollingProcedure extends UnrollingProcedure - protected var interrupted : Boolean = false +trait AbstractUnrollingSolver[T] + extends UnrollingProcedure + with Solver + with EvaluatingSolver + with QuantificationSolver { - val reporter = context.reporter + val unfoldFactor = context.findOptionOrDefault(optUnrollFactor) + val feelingLucky = context.findOptionOrDefault(optFeelingLucky) + val checkModels = context.findOptionOrDefault(optCheckModels) + val useCodeGen = context.findOptionOrDefault(optUseCodeGen) + val unrollUnsatCores = context.findOptionOrDefault(optUnrollCores) + val assumePreHolds = context.findOptionOrDefault(optAssumePre) + val partialModels = context.findOptionOrDefault(optPartialModels) - def name = "U:"+underlying.name + protected var foundDefinitiveAnswer = false + protected var definitiveAnswer : Option[Boolean] = None + protected var definitiveModel : Model = Model.empty + protected var definitiveCore : Set[Expr] = Set.empty - def free() { - underlying.free() + def check: Option[Boolean] = { + genericCheck(Set.empty) } - val templateGenerator = new TemplateGenerator(new TemplateEncoder[Expr] { - def encodeId(id: Identifier): Expr= { - Variable(id.freshen) - } + def getModel: Model = if (foundDefinitiveAnswer && definitiveAnswer.getOrElse(false)) { + definitiveModel + } else { + Model.empty + } - def encodeExpr(bindings: Map[Identifier, Expr])(e: Expr): Expr = { - replaceFromIDs(bindings, e) - } + def getUnsatCore: Set[Expr] = if (foundDefinitiveAnswer && !definitiveAnswer.getOrElse(true)) { + definitiveCore + } else { + Set.empty + } - def substitute(substMap: Map[Expr, Expr]): Expr => Expr = { - (e: Expr) => replace(substMap, e) - } + private val freeVars = new IncrementalMap[Identifier, T]() + private val constraints = new IncrementalSeq[Expr]() - def mkNot(e: Expr) = not(e) - def mkOr(es: Expr*) = orJoin(es) - def mkAnd(es: Expr*) = andJoin(es) - def mkEquals(l: Expr, r: Expr) = Equals(l, r) - def mkImplies(l: Expr, r: Expr) = implies(l, r) - }, assumePreHolds) + protected var interrupted : Boolean = false - val unrollingBank = new UnrollingBank(context, templateGenerator) + protected val reporter = context.reporter - val solver = underlying + lazy val templateGenerator = new TemplateGenerator(templateEncoder, assumePreHolds) + lazy val unrollingBank = new UnrollingBank(context, templateGenerator) - def assertCnstr(expression: Expr) { - constraints += expression + def push(): Unit = { + unrollingBank.push() + constraints.push() + freeVars.push() + } - val freeIds = variablesOf(expression) + def pop(): Unit = { + unrollingBank.pop() + constraints.pop() + freeVars.pop() + } - freeVars ++= freeIds + override def reset() = { + foundDefinitiveAnswer = false + interrupted = false - val newVars = freeIds.map(_.toVariable: Expr) + unrollingBank.reset() + constraints.reset() + freeVars.reset() + } - val bindings = newVars.zip(newVars).toMap + override def interrupt(): Unit = { + interrupted = true + } - val newClauses = unrollingBank.getClauses(expression, bindings) + override def recoverInterrupt(): Unit = { + interrupted = false + } + def assertCnstr(expression: Expr, bindings: Map[Identifier, T]): Unit = { + constraints += expression + freeVars ++= bindings + + val newClauses = unrollingBank.getClauses(expression, bindings.map { case (k, v) => Variable(k) -> v }) for (cl <- newClauses) { - solver.assertCnstr(cl) + solverAssert(cl) } } - override def dbg(msg: => Any) = underlying.dbg(msg) - - def push() { - unrollingBank.push() - solver.push() - freeVars.push() - constraints.push() + def foundAnswer(res: Option[Boolean], model: Model = Model.empty, core: Set[Expr] = Set.empty) = { + foundDefinitiveAnswer = true + definitiveAnswer = res + definitiveModel = model + definitiveCore = core } - def pop() { - unrollingBank.pop() - solver.pop() - freeVars.pop() - constraints.pop() + implicit val printable: T => Printable + val templateEncoder: TemplateEncoder[T] + + def solverAssert(cnstr: T): Unit + + /** We define solverCheckAssumptions in CPS in order for solvers that don't + * support this feature to be able to use the provided [[solverCheck]] CPS + * construction. + */ + def solverCheckAssumptions[R](assumptions: Seq[T])(block: Option[Boolean] => R): R = + solverCheck(assumptions)(block) + + def checkAssumptions(assumptions: Set[Expr]): Option[Boolean] = + genericCheck(assumptions) + + /** Provides CPS solver.check call. CPS is necessary in order for calls that + * depend on solver.getModel to be able to access the model BEFORE the call + * to solver.pop() is issued. + * + * The underlying solver therefore performs the following sequence of + * solver calls: + * {{{ + * solver.push() + * for (cls <- clauses) solver.assertCnstr(cls) + * val res = solver.check + * block(res) + * solver.pop() + * }}} + * + * This ordering guarantees that [[block]] can safely call solver.getModel. + * + * This sequence of calls can also be used to mimic solver.checkAssumptions() + * for solvers that don't support the construct natively. + */ + def solverCheck[R](clauses: Seq[T])(block: Option[Boolean] => R): R + + def solverUnsatCore: Option[Seq[T]] + + trait ModelWrapper { + def get(id: Identifier): Option[Expr] + def eval(elem: T, tpe: TypeTree): Option[Expr] } - def check: Option[Boolean] = { - genericCheck(Set()) - } + def solverGetModel: ModelWrapper - def hasFoundAnswer = lastCheckResult._1 + private def emit(silenceErrors: Boolean)(msg: String) = + if (silenceErrors) reporter.debug(msg) else reporter.warning(msg) - private def extractModel(model: Model): HenkinModel = { - val allVars = freeVars.toSet + private def extractModel(wrapper: ModelWrapper): Model = + new Model(freeVars.toMap.map(p => p._1 -> wrapper.get(p._1).get)) - def extract(b: Expr, m: Matcher[Expr]): Set[Seq[Expr]] = { - val QuantificationTypeMatcher(fromTypes, _) = m.tpe - val optEnabler = evaluator.eval(b, model).result + private def validateModel(model: Model, assumptions: Seq[Expr], silenceErrors: Boolean): Boolean = { + val expr = andJoin(assumptions ++ constraints) + + evaluator.check(expr, model) match { + case EvaluationResults.CheckSuccess => + reporter.debug("- Model validated.") + true + + case EvaluationResults.CheckValidityFailure => + reporter.debug("- Invalid model.") + false + + case EvaluationResults.CheckRuntimeFailure(msg) => + emit(silenceErrors)("- Model leads to evaluation error: " + msg) + false + + case EvaluationResults.CheckQuantificationFailure(msg) => + emit(silenceErrors)("- Model leads to quantification error: " + msg) + false + } + } + + private def getPartialModel: HenkinModel = { + val wrapped = solverGetModel - if (optEnabler == Some(BooleanLiteral(true))) { - val optArgs = m.args.map(arg => evaluator.eval(arg.encoded, model).result) + def extract(b: T, m: Matcher[T]): Option[Seq[Expr]] = { + val QuantificationTypeMatcher(fromTypes, _) = m.tpe + val optEnabler = wrapped.eval(b, BooleanType) + optEnabler.filter(_ == BooleanLiteral(true)).flatMap { _ => + val optArgs = (m.args zip fromTypes).map { case (arg, tpe) => wrapped.eval(arg.encoded, tpe) } if (optArgs.forall(_.isDefined)) { - Set(optArgs.map(_.get)) + Some(optArgs.map(_.get)) } else { - Set.empty + None } - } else { - Set.empty } } @@ -134,229 +222,319 @@ class UnrollingSolver(val context: LeonContext, val program: Program, underlying case (tpe, domain) => tpe -> domain.flatMap { case (b, m) => extract(b, m) }.toSet } - val funDomains: Map[Identifier, Set[Seq[Expr]]] = partialInsts.map { - case (Variable(id), domain) => id -> domain.flatMap { case (b, m) => extract(b, m) }.toSet + val funDomains: Map[Identifier, Set[Seq[Expr]]] = freeVars.map { case (id, idT) => + id -> partialInsts.get(idT).toSeq.flatten.flatMap { case (b, m) => extract(b, m) }.toSet } val lambdaDomains: Map[Lambda, Set[Seq[Expr]]] = lambdaInsts.map { case (l, domain) => l -> domain.flatMap { case (b, m) => extract(b, m) }.toSet } + val model = extractModel(wrapped) val asDMap = purescala.Quantification.extractModel(model.toMap, funDomains, typeDomains, evaluator) val domains = new HenkinDomains(lambdaDomains, typeDomains) new HenkinModel(asDMap, domains) } - def foundAnswer(res: Option[Boolean], model: Option[HenkinModel] = None) = { - lastCheckResult = (true, res, model) - } + private def getTotalModel(silenceErrors: Boolean = false): (Boolean, Model) = { + if (interrupted) { + (false, Model.empty) + } else { + var valid = false + var wrapper = solverGetModel - def validatedModel(silenceErrors: Boolean = false): (Boolean, HenkinModel) = { - val lastModel = solver.getModel - val clauses = templateGenerator.manager.checkClauses - val optModel = if (clauses.isEmpty) Some(lastModel) else { - solver.push() - for (clause <- clauses) { - solver.assertCnstr(clause) - } + val clauses = templateGenerator.manager.checkClauses + if (clauses.isEmpty) { + valid = true + } else { + reporter.debug(" - Verifying model transitivity") - reporter.debug(" - Verifying model transitivity") - val solverModel = solver.check match { - case Some(true) => - Some(solver.getModel) - - case Some(false) => - val msg = "- Transitivity independence not guaranteed for model" - if (silenceErrors) { - reporter.debug(msg) - } else { - reporter.warning(msg) - } - None + val timer = context.timers.solvers.check.start() + solverCheck(clauses) { res => + timer.stop() + + reporter.debug(" - Finished transitivity check") - case None => - val msg = "- Unknown for transitivity independence!?" - if (silenceErrors) { - reporter.debug(msg) - } else { - reporter.warning(msg) + res match { + case Some(true) => + wrapper = solverGetModel + valid = true + + case Some(false) => + emit(silenceErrors)(" - Transitivity independence not guaranteed for model") + + case None => + emit(silenceErrors)(" - Unknown for transitivity independence!?") } - None + } } - solver.pop() - solverModel + (valid, extractModel(wrapper)) } + } - optModel match { - case None => - (false, extractModel(lastModel)) + def genericCheck(assumptions: Set[Expr]): Option[Boolean] = { + foundDefinitiveAnswer = false + + val encoder = templateGenerator.encoder.encodeExpr(freeVars.toMap) _ + val assumptionsSeq : Seq[Expr] = assumptions.toSeq + val encodedAssumptions : Seq[T] = assumptionsSeq.map(encoder) + val encodedToAssumptions : Map[T, Expr] = (encodedAssumptions zip assumptionsSeq).toMap + + def encodedCoreToCore(core: Seq[T]): Set[Expr] = { + core.flatMap(ast => encodedToAssumptions.get(ast) match { + case Some(n @ Not(Variable(_))) => Some(n) + case Some(v @ Variable(_)) => Some(v) + case _ => None + }).toSet + } - case Some(m) => - val model = extractModel(m) + while(!foundDefinitiveAnswer && !interrupted) { + reporter.debug(" - Running search...") - val expr = andJoin(constraints.toSeq) - val fullModel = model set freeVars.toSet + val timer = context.timers.solvers.check.start() + solverCheckAssumptions(encodedAssumptions.toSeq ++ unrollingBank.satisfactionAssumptions) { res => + timer.stop() - (evaluator.check(expr, fullModel) match { - case EvaluationResults.CheckSuccess => - reporter.debug("- Model validated.") - true + reporter.debug(" - Finished search with blocked literals") - case EvaluationResults.CheckValidityFailure => - reporter.debug("- Invalid model.") - false + res match { + case None => + foundAnswer(None) - case EvaluationResults.CheckRuntimeFailure(msg) => - if (silenceErrors) { - reporter.debug("- Model leads to evaluation error: " + msg) + case Some(true) => // SAT + val (stop, model) = if (!partialModels) { + getTotalModel(silenceErrors = true) } else { - reporter.warning("- Model leads to evaluation error: " + msg) + (true, getPartialModel) } - false - case EvaluationResults.CheckQuantificationFailure(msg) => - if (silenceErrors) { - reporter.debug("- Model leads to quantification error: " + msg) - } else { - reporter.warning("- Model leads to quantification error: " + msg) + if (!interrupted) { + if (!stop) { + if (!unrollingBank.canInstantiate) { + reporter.error("Something went wrong. The model is not transitive yet we can't instantiate!?") + reporter.error(model.asString(context)) + foundAnswer(None, model) + } else { + // further quantifier instantiations are required! + val newClauses = unrollingBank.instantiateQuantifiers + reporter.debug(" - more instantiations") + + for (cls <- newClauses) { + solverAssert(cls) + } + + reporter.debug(" - finished instantiating") + } + } else { + val valid = !checkModels || validateModel(model, assumptionsSeq, silenceErrors = false) + + if (valid) { + foundAnswer(Some(true), model) + } else { + reporter.error("Something went wrong. The model should have been valid, yet we got this : ") + reporter.error(model.asString(context)) + foundAnswer(None, model) + } + } } - false - }, fullModel) - } - } - - def genericCheck(assumptions: Set[Expr]): Option[Boolean] = { - lastCheckResult = (false, None, None) - while(!hasFoundAnswer && !interrupted) { - reporter.debug(" - Running search...") - - solver.push() - solver.assertCnstr(andJoin((assumptions ++ unrollingBank.satisfactionAssumptions).toSeq)) - val res = solver.check - - reporter.debug(" - Finished search with blocked literals") + if (interrupted) { + foundAnswer(None) + } - res match { - case None => - solver.pop() + case Some(false) if !unrollingBank.canUnroll => + solverUnsatCore match { + case Some(core) => + val exprCore = encodedCoreToCore(core) + foundAnswer(Some(false), core = exprCore) + case None => + foundAnswer(Some(false)) + } - reporter.ifDebug { debug => - reporter.debug("Solver returned unknown!?") - } - foundAnswer(None) + case Some(false) => + if (unrollUnsatCores) { + solverUnsatCore match { + case Some(core) => + unrollingBank.decreaseAllGenerations() + + for (c <- core) templateGenerator.encoder.extractNot(c) match { + case Some(b) => unrollingBank.promoteBlocker(b) + case None => reporter.fatalError("Unexpected blocker polarity for unsat core unrolling: " + c) + } + case None => + reporter.fatalError("Can't unroll unsat core for incompatible solver " + name) + } + } - case Some(true) => // SAT - val (valid, model) = if (!this.disableChecks && requireQuantification) { - validatedModel(silenceErrors = false) - } else { - true -> extractModel(solver.getModel) - } + if (!interrupted) { + if (feelingLucky) { + reporter.debug(" - Running search without blocked literals (w/ lucky test)") + } else { + reporter.debug(" - Running search without blocked literals (w/o lucky test)") + } - solver.pop() - if (valid) { - foundAnswer(Some(true), Some(model)) - } else { - reporter.error("Something went wrong. The model should have been valid, yet we got this : ") - reporter.error(model.asString(context)) - foundAnswer(None, Some(model)) - } + val timer = context.timers.solvers.check.start() + solverCheckAssumptions(encodedAssumptions.toSeq ++ unrollingBank.refutationAssumptions) { res2 => + timer.stop() + + reporter.debug(" - Finished search without blocked literals") + + res2 match { + case Some(false) => + solverUnsatCore match { + case Some(core) => + val exprCore = encodedCoreToCore(core) + foundAnswer(Some(false), core = exprCore) + case None => + foundAnswer(Some(false)) + } + + case Some(true) => + if (this.feelingLucky && !interrupted) { + // we might have been lucky :D + val model = extractModel(solverGetModel) + val valid = validateModel(model, assumptionsSeq, silenceErrors = true) + if (valid) foundAnswer(Some(true), model) + } + + case None => + foundAnswer(None) + } + } + } - case Some(false) if !unrollingBank.canUnroll => - solver.pop() - foundAnswer(Some(false)) + if (interrupted) { + foundAnswer(None) + } - case Some(false) => - //debug("UNSAT BECAUSE: "+solver.getUnsatCore.mkString("\n AND \n")) - //debug("UNSAT BECAUSE: "+core.mkString(" AND ")) - solver.pop() + if (!foundDefinitiveAnswer) { + reporter.debug("- We need to keep going") - if (!interrupted) { - if (feelingLucky) { - reporter.debug(" - Running search without blocked literals (w/ lucky test)") - } else { - reporter.debug(" - Running search without blocked literals (w/o lucky test)") - } + // unfolling `unfoldFactor` times + for (i <- 1 to unfoldFactor.toInt) { + val toRelease = unrollingBank.getBlockersToUnlock - solver.push() - solver.assertCnstr(andJoin(assumptions.toSeq ++ unrollingBank.refutationAssumptions)) - val res2 = solver.check + reporter.debug(" - more unrollings") - res2 match { - case Some(false) => - //reporter.debug("UNSAT WITHOUT Blockers") - foundAnswer(Some(false)) + val newClauses = unrollingBank.unrollBehind(toRelease) - case Some(true) => - if (feelingLucky && !interrupted) { - // we might have been lucky :D - val (valid, model) = validatedModel(silenceErrors = true) - if (valid) foundAnswer(Some(true), Some(model)) + for (ncl <- newClauses) { + solverAssert(ncl) } + } - case None => - foundAnswer(None) + reporter.debug(" - finished unrolling") } - solver.pop() - } + } + } + } - if(interrupted) { - foundAnswer(None) - } + if (interrupted) { + None + } else { + definitiveAnswer + } + } +} - if(!hasFoundAnswer) { - reporter.debug("- We need to keep going.") +class UnrollingSolver(val context: LeonContext, val program: Program, underlying: Solver) + extends AbstractUnrollingSolver[Expr] { - // unfolling `unfoldFactor` times - for (i <- 1 to unfoldFactor.toInt) { - val toRelease = unrollingBank.getBlockersToUnlock + override val name = "U:"+underlying.name - reporter.debug(" - more unrollings") + def free() { + underlying.free() + } - val newClauses = unrollingBank.unrollBehind(toRelease) + val printable = (e: Expr) => e - for (ncl <- newClauses) { - solver.assertCnstr(ncl) - } - } + val templateEncoder = new TemplateEncoder[Expr] { + def encodeId(id: Identifier): Expr= { + Variable(id.freshen) + } - reporter.debug(" - finished unrolling") - } - } + def encodeExpr(bindings: Map[Identifier, Expr])(e: Expr): Expr = { + replaceFromIDs(bindings, e) } - if(interrupted) { - None - } else { - lastCheckResult._2 + def substitute(substMap: Map[Expr, Expr]): Expr => Expr = { + (e: Expr) => replace(substMap, e) } + + def mkNot(e: Expr) = not(e) + def mkOr(es: Expr*) = orJoin(es) + def mkAnd(es: Expr*) = andJoin(es) + def mkEquals(l: Expr, r: Expr) = Equals(l, r) + def mkImplies(l: Expr, r: Expr) = implies(l, r) + + def extractNot(e: Expr): Option[Expr] = e match { + case Not(b) => Some(b) + case _ => None + } + } + + val solver = underlying + + def assertCnstr(expression: Expr): Unit = { + assertCnstr(expression, variablesOf(expression).map(id => id -> id.toVariable).toMap) + } + + def solverAssert(cnstr: Expr): Unit = { + solver.assertCnstr(cnstr) + } + + def solverCheck[R](clauses: Seq[Expr])(block: Option[Boolean] => R): R = { + solver.push() + for (cls <- clauses) solver.assertCnstr(cls) + val res = solver.check + val r = block(res) + solver.pop() + r + } + + def solverUnsatCore = None + + def solverGetModel: ModelWrapper = new ModelWrapper { + val model = solver.getModel + def get(id: Identifier): Option[Expr] = model.get(id) + def eval(elem: Expr, tpe: TypeTree): Option[Expr] = evaluator.eval(elem, model).result } - def getModel: HenkinModel = { - lastCheckResult match { - case (true, Some(true), Some(m)) => - m.filter(freeVars.toSet) - case _ => - HenkinModel.empty + override def dbg(msg: => Any) = underlying.dbg(msg) + + override def push(): Unit = { + super.push() + solver.push() + } + + override def pop(): Unit = { + super.pop() + solver.pop() + } + + override def foundAnswer(res: Option[Boolean], model: Model = Model.empty, core: Set[Expr] = Set.empty) = { + super.foundAnswer(res, model, core) + + if (!interrupted && res == None && model == None) { + reporter.ifDebug { debug => + debug("Unknown result!?") + } } } - override def reset() = { + override def reset(): Unit = { underlying.reset() - lastCheckResult = (false, None, None) - freeVars.reset() - constraints.reset() - interrupted = false + super.reset() } override def interrupt(): Unit = { - interrupted = true + super.interrupt() solver.interrupt() } override def recoverInterrupt(): Unit = { solver.recoverInterrupt() - interrupted = false + super.recoverInterrupt() } } diff --git a/src/main/scala/leon/solvers/templates/QuantificationManager.scala b/src/main/scala/leon/solvers/templates/QuantificationManager.scala index bc31267c09d8023390ac541b0cb99ecece6691ea..1662596002ac49202f10206cec6a338fc7377116 100644 --- a/src/main/scala/leon/solvers/templates/QuantificationManager.scala +++ b/src/main/scala/leon/solvers/templates/QuantificationManager.scala @@ -287,6 +287,16 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage def instantiations: Map[MatcherKey, Matchers] = (funMap.toMap ++ tpeMap.map { case (tpe,ms) => TypeKey(tpe) -> ms }).mapValues(_.toMatchers) + + def consume: Matchers = { + val matchers = funMap.iterator.flatMap(_._2.toMatchers).toSet ++ tpeMap.flatMap(_._2.toMatchers) + funMap.clear + tpeMap.clear + matchers + } + + def isEmpty = funMap.isEmpty && tpeMap.isEmpty + def nonEmpty = !isEmpty } private class InstantiationContext private ( @@ -755,6 +765,16 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage instCtx.instantiate(Set(blocker), matcher)(quantifications.toSeq : _*) } + def hasIgnored: Boolean = ignored.nonEmpty + + def instantiateIgnored: Instantiation[T] = { + var instantiation = Instantiation.empty[T] + for ((b,m) <- ignored.consume) { + instantiation ++= instantiateMatcher(b, m) + } + instantiation + } + private type SetDef = (T, (Identifier, T), (Identifier, T), Seq[T], T, T, T) private val setConstructors: MutableMap[TypeTree, SetDef] = MutableMap.empty diff --git a/src/main/scala/leon/solvers/templates/TemplateEncoder.scala b/src/main/scala/leon/solvers/templates/TemplateEncoder.scala index f3eb2ad28f3bab2e72770f388e6ecbe92524dc54..16d7b3cdc7f695eb7524895fd880de4f23a1083c 100644 --- a/src/main/scala/leon/solvers/templates/TemplateEncoder.scala +++ b/src/main/scala/leon/solvers/templates/TemplateEncoder.scala @@ -18,4 +18,6 @@ trait TemplateEncoder[T] { def mkAnd(ts: T*): T def mkEquals(l: T, r: T): T def mkImplies(l: T, r: T): T + + def extractNot(v: T): Option[T] } diff --git a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala index 59be52775406a78c22a55773dfd161db003b21bb..2ab8f2b2b52dbf2cc335519e481ed7954e2fa3d6 100644 --- a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala +++ b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala @@ -65,7 +65,7 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T], val invocationEqualsBody : Option[Expr] = lambdaBody match { case Some(body) if isRealFunDef => - val b : Expr = And( + val b : Expr = and( liftedEquals(invocation, body, lambdaArguments), Equals(invocation, body)) diff --git a/src/main/scala/leon/solvers/templates/UnrollingBank.scala b/src/main/scala/leon/solvers/templates/UnrollingBank.scala index 4543262d74204dd9f77d3eeea8882bf543956a90..893b39cb82e71bace9f4b26c2696188f825a031f 100644 --- a/src/main/scala/leon/solvers/templates/UnrollingBank.scala +++ b/src/main/scala/leon/solvers/templates/UnrollingBank.scala @@ -91,6 +91,7 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat def refutationAssumptions = manager.assumptions def canUnroll = callInfos.nonEmpty || appInfos.nonEmpty + def canInstantiate = manager.hasIgnored def currentBlockers = callInfos.map(_._2._3).toSeq ++ appInfos.map(_._2._4).toSeq @@ -218,9 +219,9 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat def promoteBlocker(b: T) = { if (callInfos contains b) { - val (_, origGen, ast, fis) = callInfos(b) + val (_, origGen, notB, fis) = callInfos(b) - callInfos += b -> (1, origGen, ast, fis) + callInfos += b -> (1, origGen, notB, fis) } if (blockerToApps contains b) { @@ -231,6 +232,22 @@ class UnrollingBank[T <% Printable](ctx: LeonContext, templateGenerator: Templat } } + def instantiateQuantifiers: Seq[T] = { + val (newExprs, callBlocks, appBlocks) = manager.instantiateIgnored + val blockExprs = freshAppBlocks(appBlocks.keys) + val gen = (callInfos.values.map(_._1) ++ appInfos.values.map(_._1)).min + + for ((b, newInfos) <- callBlocks) { + registerCallBlocker(nextGeneration(gen), b, newInfos) + } + + for ((newApp, newInfos) <- appBlocks) { + registerAppBlocker(nextGeneration(gen), newApp, newInfos) + } + + newExprs ++ blockExprs + } + def unrollBehind(ids: Seq[T]): Seq[T] = { assert(ids.forall(id => (callInfos contains id) || (blockerToApps contains id))) diff --git a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala index 7c3486ff533a630bbd1bce6bde31d86969163e7c..e2d1ab0fae778878845fa51f85786325a0041c54 100644 --- a/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala +++ b/src/main/scala/leon/solvers/z3/AbstractZ3Solver.scala @@ -30,7 +30,7 @@ trait AbstractZ3Solver extends Solver { val library = program.library - protected[z3] val reporter : Reporter = context.reporter + protected val reporter : Reporter = context.reporter context.interruptManager.registerForInterrupts(this) @@ -51,7 +51,7 @@ trait AbstractZ3Solver extends Solver { protected[leon] val z3cfg : Z3Config protected[leon] var z3 : Z3Context = null - override def free() { + override def free(): Unit = { freed = true if (z3 ne null) { z3.delete() @@ -59,18 +59,13 @@ trait AbstractZ3Solver extends Solver { } } - protected[z3] var interrupted = false - - override def interrupt() { - interrupted = true + override def interrupt(): Unit = { if(z3 ne null) { z3.interrupt() } } - override def recoverInterrupt() { - interrupted = false - } + override def recoverInterrupt(): Unit = () def functionDefToDecl(tfd: TypedFunDef): Z3FuncDecl = { functions.cachedB(tfd) { @@ -777,7 +772,7 @@ trait AbstractZ3Solver extends Solver { z3.mkFreshConst(id.uniqueName, typeToSort(id.getType)) } - def reset() = { + def reset(): Unit = { throw new CantResetException(this) } diff --git a/src/main/scala/leon/solvers/z3/FairZ3Solver.scala b/src/main/scala/leon/solvers/z3/FairZ3Solver.scala index c975fd2d37e7563137d9b1fd4cbe0f2cbb7892f4..812812aa3f4b39a9f3e2994d46080d4b009c0067 100644 --- a/src/main/scala/leon/solvers/z3/FairZ3Solver.scala +++ b/src/main/scala/leon/solvers/z3/FairZ3Solver.scala @@ -17,6 +17,7 @@ import purescala.ExprOps._ import purescala.Types._ import solvers.templates._ +import solvers.combinators._ import Template._ import evaluators._ @@ -25,32 +26,17 @@ import termination._ class FairZ3Solver(val context: LeonContext, val program: Program) extends AbstractZ3Solver - with Z3ModelReconstruction - with FairZ3Component - with EvaluatingSolver - with QuantificationSolver { + with AbstractUnrollingSolver[Z3AST] + with Z3ModelReconstruction { enclosing => - val feelingLucky = context.findOptionOrDefault(optFeelingLucky) - val checkModels = context.findOptionOrDefault(optCheckModels) - val useCodeGen = context.findOptionOrDefault(optUseCodeGen) - val evalGroundApps = context.findOptionOrDefault(optEvalGround) - val unrollUnsatCores = context.findOptionOrDefault(optUnrollCores) - val assumePreHolds = context.findOptionOrDefault(optAssumePre) - val disableChecks = context.findOptionOrDefault(optNoChecks) - - assert(!checkModels || !disableChecks, "Options \"checkmodels\" and \"nochecks\" are mutually exclusive") - protected val errors = new IncrementalBijection[Unit, Boolean]() protected def hasError = errors.getB(()) contains true protected def addError() = errors += () -> true - protected[z3] def getEvaluator : Evaluator = evaluator - - private val terminator : TerminationChecker = new SimpleTerminationChecker(context, program) - - protected[z3] def getTerminator : TerminationChecker = terminator + override protected val reporter = context.reporter + override def reset(): Unit = super[AbstractZ3Solver].reset() // FIXME: Dirty hack to bypass z3lib bug. Assumes context is the same over all instances of FairZ3Solver protected[leon] val z3cfg = context.synchronized { new Z3Config( @@ -60,53 +46,76 @@ class FairZ3Solver(val context: LeonContext, val program: Program) )} toggleWarningMessages(true) - private def extractModel(model: Z3Model, ids: Set[Identifier]): HenkinModel = { - def extract(b: Z3AST, m: Matcher[Z3AST]): Set[Seq[Expr]] = { - val QuantificationTypeMatcher(fromTypes, _) = m.tpe - val optEnabler = model.evalAs[Boolean](b) - - if (optEnabler == Some(true)) { - val optArgs = (m.args zip fromTypes).map { - p => softFromZ3Formula(model, model.eval(p._1.encoded, true).get, p._2) - } - - if (optArgs.forall(_.isDefined)) { - Set(optArgs.map(_.get)) - } else { - Set.empty - } - } else { - Set.empty - } - } + def solverCheck[R](clauses: Seq[Z3AST])(block: Option[Boolean] => R): R = { + solver.push() + for (cls <- clauses) solver.assertCnstr(cls) + val res = solver.check + val r = block(res) + solver.pop() + r + } - val (typeInsts, partialInsts, lambdaInsts) = templateGenerator.manager.instantiations + override def solverCheckAssumptions[R](assumptions: Seq[Z3AST])(block: Option[Boolean] => R): R = { + solver.push() // FIXME: remove when z3 bug is fixed + val res = solver.checkAssumptions(assumptions : _*) + solver.pop() // FIXME: remove when z3 bug is fixed + block(res) + } - val typeDomains: Map[TypeTree, Set[Seq[Expr]]] = typeInsts.map { - case (tpe, domain) => tpe -> domain.flatMap { case (b, m) => extract(b, m) }.toSet + def solverGetModel: ModelWrapper = new ModelWrapper { + val model = solver.getModel + + /* + val functionsModel: Map[Z3FuncDecl, (Seq[(Seq[Z3AST], Z3AST)], Z3AST)] = model.getModelFuncInterpretations.map(i => (i._1, (i._2, i._3))).toMap + val functionsAsMap: Map[Identifier, Expr] = functionsModel.flatMap(p => { + if (functions containsB p._1) { + val tfd = functions.toA(p._1) + if (!tfd.hasImplementation) { + val (cses, default) = p._2 + val ite = cses.foldLeft(fromZ3Formula(model, default, tfd.returnType))((expr, q) => IfExpr( + andJoin(q._1.zip(tfd.params).map(a12 => Equals(fromZ3Formula(model, a12._1, a12._2.getType), Variable(a12._2.id)))), + fromZ3Formula(model, q._2, tfd.returnType), + expr)) + Seq((tfd.id, ite)) + } else Seq() + } else Seq() + }) + + val constantFunctionsAsMap: Map[Identifier, Expr] = model.getModelConstantInterpretations.flatMap(p => { + if(functions containsB p._1) { + val tfd = functions.toA(p._1) + if(!tfd.hasImplementation) { + Seq((tfd.id, fromZ3Formula(model, p._2, tfd.returnType))) + } else Seq() + } else Seq() + }).toMap + + val leonModel = extractModel(model, freeVars.toSet) + val fullModel = leonModel ++ (functionsAsMap ++ constantFunctionsAsMap) + */ + + def get(id: Identifier): Option[Expr] = variables.getB(id.toVariable).flatMap { + z3ID => eval(z3ID, id.getType) } - val funDomains: Map[Identifier, Set[Seq[Expr]]] = partialInsts.flatMap { - case (c, domain) => variables.getA(c).collect { - case Variable(id) => id -> domain.flatMap { case (b, m) => extract(b, m) }.toSet + def eval(elem: Z3AST, tpe: TypeTree): Option[Expr] = tpe match { + case BooleanType => model.evalAs[Boolean](elem).map(BooleanLiteral) + case Int32Type => model.evalAs[Int](elem).map(IntLiteral).orElse { + model.eval(elem).flatMap(t => softFromZ3Formula(model, t, Int32Type)) + } + case IntegerType => model.evalAs[Int](elem).map(InfiniteIntegerLiteral(_)) + case other => model.eval(elem) match { + case None => None + case Some(t) => softFromZ3Formula(model, t, other) } } - - val lambdaDomains: Map[Lambda, Set[Seq[Expr]]] = lambdaInsts.map { - case (l, domain) => l -> domain.flatMap { case (b, m) => extract(b, m) }.toSet - } - - val asMap = modelToMap(model, ids) - val asDMap = purescala.Quantification.extractModel(asMap, funDomains, typeDomains, evaluator) - val domains = new HenkinDomains(lambdaDomains, typeDomains) - new HenkinModel(asDMap, domains) } - implicit val z3Printable = (z3: Z3AST) => new Printable { + val printable = (z3: Z3AST) => new Printable { def asString(implicit ctx: LeonContext) = z3.toString } - val templateGenerator = new TemplateGenerator(new TemplateEncoder[Z3AST] { + val templateEncoder = new TemplateEncoder[Z3AST] { def encodeId(id: Identifier): Z3AST = { idToFreshZ3Id(id) } @@ -127,31 +136,33 @@ class FairZ3Solver(val context: LeonContext, val program: Program) def mkAnd(es: Z3AST*) = z3.mkAnd(es : _*) def mkEquals(l: Z3AST, r: Z3AST) = z3.mkEq(l, r) def mkImplies(l: Z3AST, r: Z3AST) = z3.mkImplies(l, r) - }, assumePreHolds) + def extractNot(l: Z3AST): Option[Z3AST] = z3.getASTKind(l) match { + case Z3AppAST(decl, args) => z3.getDeclKind(decl) match { + case Z3DeclKind.OpNot => Some(args.head) + case Z3DeclKind.OpUninterpreted => None + } + case ast => None + } + } initZ3() val solver = z3.mkSolver() - private val freeVars = new IncrementalSet[Identifier]() - private val constraints = new IncrementalSeq[Expr]() - - val tr = implicitly[Z3AST => Printable] - - val unrollingBank = new UnrollingBank(context, templateGenerator) - private val incrementals: List[IncrementalState] = List( - errors, freeVars, constraints, functions, generics, lambdas, sorts, variables, - constructors, selectors, testers, unrollingBank + errors, functions, generics, lambdas, sorts, variables, + constructors, selectors, testers ) - def push() { + override def push(): Unit = { + super.push() solver.push() incrementals.foreach(_.push()) } - def pop() { + override def pop(): Unit = { + super.pop() solver.pop(1) incrementals.foreach(_.pop()) } @@ -160,7 +171,7 @@ class FairZ3Solver(val context: LeonContext, val program: Program) if (hasError) { None } else { - fairCheck(Set()) + super.check } } @@ -168,330 +179,43 @@ class FairZ3Solver(val context: LeonContext, val program: Program) if (hasError) { None } else { - fairCheck(assumptions) + super.checkAssumptions(assumptions) } } - var foundDefinitiveAnswer = false - var definitiveAnswer : Option[Boolean] = None - var definitiveModel : HenkinModel = HenkinModel.empty - var definitiveCore : Set[Expr] = Set.empty - - def assertCnstr(expression: Expr) { + def assertCnstr(expression: Expr): Unit = { try { - val newFreeVars = variablesOf(expression) - freeVars ++= newFreeVars + val bindings = variablesOf(expression).map(id => id -> variables.cachedB(Variable(id)) { + templateGenerator.encoder.encodeId(id) + }).toMap - // We make sure all free variables are registered as variables - freeVars.foreach { v => - variables.cachedB(Variable(v)) { - templateGenerator.encoder.encodeId(v) - } - } - - constraints += expression - - val newClauses = unrollingBank.getClauses(expression, variables.aToB) - - for (cl <- newClauses) { - solver.assertCnstr(cl) - } + assertCnstr(expression, bindings) } catch { case _: Unsupported => addError() } } - def getModel = { - definitiveModel - } - - def getUnsatCore = { - definitiveCore + def solverAssert(cnstr: Z3AST): Unit = { + solver.assertCnstr(cnstr) } - def fairCheck(assumptions: Set[Expr]): Option[Boolean] = { - foundDefinitiveAnswer = false - - def entireFormula = andJoin(assumptions.toSeq ++ constraints.toSeq) - - def foundAnswer(answer: Option[Boolean], model: HenkinModel = HenkinModel.empty, core: Set[Expr] = Set.empty) : Unit = { - foundDefinitiveAnswer = true - definitiveAnswer = answer - definitiveModel = model - definitiveCore = core - } - - // these are the optional sequence of assumption literals - val assumptionsAsZ3: Seq[Z3AST] = assumptions.map(toZ3Formula(_)).toSeq - val assumptionsAsZ3Set: Set[Z3AST] = assumptionsAsZ3.toSet - - def z3CoreToCore(core: Seq[Z3AST]): Set[Expr] = { - core.filter(assumptionsAsZ3Set).map(ast => fromZ3Formula(null, ast, 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 - } + def solverUnsatCore = Some(solver.getUnsatCore) - def validatedModel(silenceErrors: Boolean) : (Boolean, HenkinModel) = { - if (interrupted) { - (false, HenkinModel.empty) - } else { - val lastModel = solver.getModel - val clauses = templateGenerator.manager.checkClauses - val optModel = if (clauses.isEmpty) Some(lastModel) else { - solver.push() - for (clause <- clauses) { - solver.assertCnstr(clause) - } - - reporter.debug(" - Enforcing model transitivity") - val timer = context.timers.solvers.z3.check.start() - solver.push() // FIXME: remove when z3 bug is fixed - val res = solver.checkAssumptions((assumptionsAsZ3 ++ unrollingBank.satisfactionAssumptions) :_*) - solver.pop() // FIXME: remove when z3 bug is fixed - timer.stop() - - val solverModel = res match { - case Some(true) => - Some(solver.getModel) - - case Some(false) => - val msg = "- Transitivity independence not guaranteed for model" - if (silenceErrors) { - reporter.debug(msg) - } else { - reporter.warning(msg) - } - None - - case None => - val msg = "- Unknown for transitivity independence!?" - if (silenceErrors) { - reporter.debug(msg) - } else { - reporter.warning(msg) - } - None - } - - solver.pop() - solverModel - } + override def foundAnswer(res: Option[Boolean], model: Model = Model.empty, core: Set[Expr] = Set.empty) = { + super.foundAnswer(res, model, core) - val model = optModel getOrElse lastModel - - val functionsModel: Map[Z3FuncDecl, (Seq[(Seq[Z3AST], Z3AST)], Z3AST)] = model.getModelFuncInterpretations.map(i => (i._1, (i._2, i._3))).toMap - val functionsAsMap: Map[Identifier, Expr] = functionsModel.flatMap(p => { - if (functions containsB p._1) { - val tfd = functions.toA(p._1) - if (!tfd.hasImplementation) { - val (cses, default) = p._2 - val ite = cses.foldLeft(fromZ3Formula(model, default, tfd.returnType))((expr, q) => IfExpr( - andJoin( - q._1.zip(tfd.params).map(a12 => Equals(fromZ3Formula(model, a12._1, a12._2.getType), Variable(a12._2.id))) - ), - fromZ3Formula(model, q._2, tfd.returnType), - expr)) - Seq((tfd.id, ite)) - } else Seq() - } else Seq() - }) - - val constantFunctionsAsMap: Map[Identifier, Expr] = model.getModelConstantInterpretations.flatMap(p => { - if(functions containsB p._1) { - val tfd = functions.toA(p._1) - if(!tfd.hasImplementation) { - Seq((tfd.id, fromZ3Formula(model, p._2, tfd.returnType))) - } else Seq() - } else Seq() - }).toMap - - val leonModel = extractModel(model, freeVars.toSet) - val fullModel = leonModel ++ (functionsAsMap ++ constantFunctionsAsMap) - - if (!optModel.isDefined) { - (false, leonModel) - } else { - (evaluator.check(entireFormula, fullModel) match { - case EvaluationResults.CheckSuccess => - reporter.debug("- Model validated.") - true - - case EvaluationResults.CheckValidityFailure => - reporter.debug("- Invalid model.") - false - - case EvaluationResults.CheckRuntimeFailure(msg) => - if (silenceErrors) { - reporter.debug("- Model leads to evaluation error: " + msg) - } else { - reporter.warning("- Model leads to evaluation error: " + msg) - } - false - - case EvaluationResults.CheckQuantificationFailure(msg) => - if (silenceErrors) { - reporter.debug("- Model leads to quantification error: " + msg) - } else { - reporter.warning("- Model leads to quantification error: " + msg) - } - false - }, leonModel) + if (!interrupted && res == None && model == None) { + reporter.ifDebug { debug => + if (solver.getReasonUnknown != "canceled") { + debug("Z3 returned unknown: " + solver.getReasonUnknown) } } } + } - while(!foundDefinitiveAnswer && !interrupted) { - - //val blockingSetAsZ3 : Seq[Z3AST] = blockingSet.toSeq.map(toZ3Formula(_).get) - // println("Blocking set : " + blockingSet) - - reporter.debug(" - Running Z3 search...") - - //reporter.debug("Searching in:\n"+solver.getAssertions.toSeq.mkString("\nAND\n")) - //reporter.debug("Unroll. Assumptions:\n"+unrollingBank.z3CurrentZ3Blockers.mkString(" && ")) - //reporter.debug("Userland Assumptions:\n"+assumptionsAsZ3.mkString(" && ")) - - val timer = context.timers.solvers.z3.check.start() - solver.push() // FIXME: remove when z3 bug is fixed - val res = solver.checkAssumptions((assumptionsAsZ3 ++ unrollingBank.satisfactionAssumptions) :_*) - solver.pop() // FIXME: remove when z3 bug is fixed - timer.stop() - - reporter.debug(" - Finished search with blocked literals") - - lazy val allVars: Set[Identifier] = freeVars.toSet - - res match { - case None => - reporter.ifDebug { debug => - if (solver.getReasonUnknown != "canceled") { - debug("Z3 returned unknown: " + solver.getReasonUnknown) - } - } - foundAnswer(None) - - case Some(true) => // SAT - val (valid, model) = if (!this.disableChecks && (this.checkModels || requireQuantification)) { - validatedModel(false) - } else { - true -> extractModel(solver.getModel, allVars) - } - - if (valid) { - foundAnswer(Some(true), model) - } else { - reporter.error("Something went wrong. The model should have been valid, yet we got this : ") - reporter.error(model.asString(context)) - foundAnswer(None, model) - } - - case Some(false) if !unrollingBank.canUnroll => - - val core = z3CoreToCore(solver.getUnsatCore()) - - foundAnswer(Some(false), core = core) - - // This branch is both for with and without unsat cores. The - // distinction is made inside. - case Some(false) => - - def coreElemToBlocker(c: Z3AST): (Z3AST, Boolean) = { - z3.getASTKind(c) match { - case Z3AppAST(decl, args) => - z3.getDeclKind(decl) match { - case Z3DeclKind.OpNot => - (args.head, true) - case Z3DeclKind.OpUninterpreted => - (c, false) - } - - case ast => - (c, false) - } - } - - if (unrollUnsatCores) { - unrollingBank.decreaseAllGenerations() - - for (c <- solver.getUnsatCore()) { - val (z3ast, pol) = coreElemToBlocker(c) - assert(pol) - - unrollingBank.promoteBlocker(z3ast) - } - - } - - //debug("UNSAT BECAUSE: "+solver.getUnsatCore.mkString("\n AND \n")) - //debug("UNSAT BECAUSE: "+core.mkString(" AND ")) - - if (!interrupted) { - if (this.feelingLucky) { - // we need the model to perform the additional test - reporter.debug(" - Running search without blocked literals (w/ lucky test)") - } else { - reporter.debug(" - Running search without blocked literals (w/o lucky test)") - } - - val timer = context.timers.solvers.z3.check.start() - solver.push() // FIXME: remove when z3 bug is fixed - val res2 = solver.checkAssumptions((assumptionsAsZ3 ++ unrollingBank.refutationAssumptions) : _*) - solver.pop() // FIXME: remove when z3 bug is fixed - timer.stop() - - reporter.debug(" - Finished search without blocked literals") - - res2 match { - case Some(false) => - //reporter.debug("UNSAT WITHOUT Blockers") - foundAnswer(Some(false), core = z3CoreToCore(solver.getUnsatCore)) - case Some(true) => - //reporter.debug("SAT WITHOUT Blockers") - if (this.feelingLucky && !interrupted) { - // we might have been lucky :D - val (wereWeLucky, cleanModel) = validatedModel(true) - - if(wereWeLucky) { - foundAnswer(Some(true), cleanModel) - } - } - - case None => - foundAnswer(None) - } - } - - if(interrupted) { - foundAnswer(None) - } - - if(!foundDefinitiveAnswer) { - reporter.debug("- We need to keep going.") - - val toRelease = unrollingBank.getBlockersToUnlock - - reporter.debug(" - more unrollings") - - val newClauses = unrollingBank.unrollBehind(toRelease) - - for(ncl <- newClauses) { - solver.assertCnstr(ncl) - } - - //readLine() - - reporter.debug(" - finished unrolling") - } - } - } - - if(interrupted) { - None - } else { - definitiveAnswer - } + override def interrupt(): Unit = { + super[AbstractZ3Solver].interrupt() + super[AbstractUnrollingSolver].interrupt() } }