diff --git a/.gitignore b/.gitignore index 057efa0f356af5b6b7af02bd7b0e9e8dcbb7f2b3..4ef5b5eae48b78d3f7dd97318ed2794a9123564f 100644 --- a/.gitignore +++ b/.gitignore @@ -54,3 +54,4 @@ out-classes # Isabelle contrib +/bin/ diff --git a/build.sbt b/build.sbt index d7a5a5e090f1d63706130c0554b4e514c470418a..9215fea007959ffb86d5226f3e5427c58697aa9e 100644 --- a/build.sbt +++ b/build.sbt @@ -143,7 +143,7 @@ def ghProject(repo: String, version: String) = RootProject(uri(s"${repo}#${versi lazy val bonsai = ghProject("git://github.com/colder/bonsai.git", "0fec9f97f4220fa94b1f3f305f2e8b76a3cd1539") -lazy val scalaSmtLib = ghProject("git://github.com/regb/scala-smtlib.git", "204018c3f413fc8191c99ae9ccc4806912e02a83") +lazy val scalaSmtLib = ghProject("git://github.com/regb/scala-smtlib.git", "3b6ef4992b6af15d08a7320fd12202f35e97b905") lazy val root = (project in file(".")). configs(RegressionTest, IsabelleTest, IntegrTest). diff --git a/src/main/scala/leon/Main.scala b/src/main/scala/leon/Main.scala index 3a8cb39df090fcb5ca3d3d902bedf6a52557026b..45c62bd0d3a5e82735b98237d2671f485915d13d 100644 --- a/src/main/scala/leon/Main.scala +++ b/src/main/scala/leon/Main.scala @@ -21,7 +21,7 @@ object Main { purescala.FunctionClosure, synthesis.SynthesisPhase, termination.TerminationPhase, - verification.AnalysisPhase, + verification.VerificationPhase, repair.RepairPhase, evaluators.EvaluationPhase, solvers.isabelle.AdaptationPhase, @@ -44,15 +44,15 @@ object Main { val description = "Selection of Leon functionality. Default: verify" val optEval = LeonStringOptionDef("eval", "Evaluate ground functions through code generation or evaluation (default: evaluation)", "default", "[code|default]") - val optTermination = LeonFlagOptionDef("termination", "Check program termination. Can be used along --verify", false) - val optRepair = LeonFlagOptionDef("repair", "Repair selected functions", false) - val optSynthesis = LeonFlagOptionDef("synthesis", "Partial synthesis of choose() constructs", false) - val optIsabelle = LeonFlagOptionDef("isabelle", "Run Isabelle verification", false) - val optNoop = LeonFlagOptionDef("noop", "No operation performed, just output program", false) - val optVerify = LeonFlagOptionDef("verify", "Verify function contracts", false) - val optHelp = LeonFlagOptionDef("help", "Show help message", false) - val optInstrument = LeonFlagOptionDef("instrument", "Instrument the code for inferring time/depth/stack bounds", false) - val optInferInv = LeonFlagOptionDef("inferInv", "Infer invariants from (instrumented) the code", false) + val optTermination = LeonFlagOptionDef("termination", "Check program termination. Can be used along --verify", false) + val optRepair = LeonFlagOptionDef("repair", "Repair selected functions", false) + val optSynthesis = LeonFlagOptionDef("synthesis", "Partial synthesis of choose() constructs", false) + val optIsabelle = LeonFlagOptionDef("isabelle", "Run Isabelle verification", false) + val optNoop = LeonFlagOptionDef("noop", "No operation performed, just output program", false) + val optVerify = LeonFlagOptionDef("verify", "Verify function contracts", false) + val optHelp = LeonFlagOptionDef("help", "Show help message", false) + val optInstrument = LeonFlagOptionDef("instrument", "Instrument the code for inferring time/depth/stack bounds", false) + val optInferInv = LeonFlagOptionDef("inferInv", "Infer invariants from (instrumented) the code", false) override val definedOptions: Set[LeonOptionDef[Any]] = Set(optTermination, optRepair, optSynthesis, optIsabelle, optNoop, optHelp, optEval, optVerify, optInstrument, optInferInv) @@ -74,7 +74,7 @@ object Main { } reporter.info("") - reporter.info("Additional options, by component:") + reporter.title("Additional options, by component:") for (c <- (allComponents - MainComponent - SharedOptions).toSeq.sortBy(_.name) if c.definedOptions.nonEmpty) { reporter.info("") @@ -147,7 +147,7 @@ object Main { import synthesis.SynthesisPhase import termination.TerminationPhase import xlang.FixReportLabels - import verification.AnalysisPhase + import verification.VerificationPhase import repair.RepairPhase import evaluators.EvaluationPhase import solvers.isabelle.IsabellePhase @@ -177,19 +177,19 @@ object Main { ExtractionPhase andThen new PreprocessingPhase(xlangF) - val analysis = if (xlangF) AnalysisPhase andThen FixReportLabels else AnalysisPhase + val verification = if (xlangF) VerificationPhase andThen FixReportLabels else VerificationPhase val pipeProcess: Pipeline[Program, Any] = { if (noopF) RestoreMethods andThen FileOutputPhase else if (synthesisF) SynthesisPhase else if (repairF) RepairPhase - else if (analysisF) Pipeline.both(analysis, TerminationPhase) + else if (analysisF) Pipeline.both(verification, TerminationPhase) else if (terminationF) TerminationPhase else if (isabelleF) IsabellePhase else if (evalF) EvaluationPhase else if (inferInvF) InferInvariantsPhase else if (instrumentF) InstrumentationPhase andThen FileOutputPhase - else analysis + else verification } pipeBegin andThen diff --git a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala index 9e97c6c7bfc097c811d86bc825e5082a9be7c7c1..80f71fd87be7f27e1793e13fe38eb09b78f78d62 100644 --- a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala +++ b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala @@ -14,8 +14,6 @@ import purescala.Extractors._ import purescala.Quantification._ import solvers.{Model, HenkinModel} import solvers.SolverFactory -import synthesis.ConvertHoles.convertHoles -import leon.purescala.ExprOps abstract class RecursiveEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int) extends Evaluator(ctx, prog) { val name = "evaluator" @@ -123,11 +121,14 @@ abstract class RecursiveEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int case en@Ensuring(body, post) => if ( exists{ case Hole(_,_) => true + case WithOracle(_,_) => true case _ => false - }(en)) - e(convertHoles(en, ctx)) - else + }(en)) { + import synthesis.ConversionPhase.convert + e(convert(en, ctx)) + } else { e(en.toAssert) + } case Error(tpe, desc) => throw RuntimeError("Error reached in evaluation: " + desc) diff --git a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala index 299e8995a072084bba0adf0a98603e64b1611aac..0a75dfebc5ae78d8147d0f601d61d38e2e0c05e2 100644 --- a/src/main/scala/leon/frontends/scalac/CodeExtraction.scala +++ b/src/main/scala/leon/frontends/scalac/CodeExtraction.scala @@ -984,11 +984,14 @@ trait CodeExtraction extends ASTExtractors { val b = extractTreeOrNoTree(body) - val closure = post.getType match { - case BooleanType => + val closure = post match { + case IsTyped(_, BooleanType) => val resId = FreshIdentifier("res", b.getType).setPos(post) Lambda(Seq(LeonValDef(resId)), post).setPos(post) - case _ => post + case l: Lambda => l + case other => + val resId = FreshIdentifier("res", b.getType).setPos(post) + Lambda(Seq(LeonValDef(resId)), application(other, Seq(Variable(resId)))).setPos(post) } Ensuring(b, closure) diff --git a/src/main/scala/leon/invariant/datastructure/DisjointSet.scala b/src/main/scala/leon/invariant/datastructure/DisjointSet.scala new file mode 100644 index 0000000000000000000000000000000000000000..4cab7291bfa58dd3d79993233251d7dd589e0019 --- /dev/null +++ b/src/main/scala/leon/invariant/datastructure/DisjointSet.scala @@ -0,0 +1,61 @@ +package leon +package invariant.datastructure + +import scala.collection.mutable.{ Map => MutableMap } +import scala.annotation.migration +import scala.collection.mutable.{Map => MutableMap} + +class DisjointSets[T] { + // A map from elements to their parent and rank + private var disjTree = MutableMap[T, (T, Int)]() + + private def findInternal(x: T): (T, Int) = { + val (p, rank) = disjTree(x) + if (p == x) + (x, rank) + else { + val root = findInternal(p) + // compress path + disjTree(x) = root + root + } + } + + private def findOrCreateInternal(x: T) = + if (!disjTree.contains(x)) { + disjTree += (x -> (x, 1)) + (x, 1) + } else findInternal(x) + + def findOrCreate(x: T) = findOrCreateInternal(x)._1 + + def find(x: T) = findInternal(x)._1 + + def union(x: T, y: T) { + val (rep1, rank1) = findOrCreateInternal(x) + val (rep2, rank2) = findOrCreateInternal(y) + if (rank1 < rank2) { + disjTree(rep1) = (rep2, rank2) + } else if (rank2 < rank1) { + disjTree(rep2) = (rep1, rank1) + } else + disjTree(rep1) = (rep2, rank2 + 1) + } + + def toMap = { + val repToSet = disjTree.keys.foldLeft(MutableMap[T, Set[T]]()) { + case (acc, k) => + val root = find(k) + if (acc.contains(root)) + acc(root) = acc(root) + k + else + acc += (root -> Set(k)) + acc + } + disjTree.keys.map {k => (k -> repToSet(find(k)))}.toMap + } + + override def toString = { + disjTree.toString + } +} \ No newline at end of file diff --git a/src/main/scala/leon/invariant/util/Graph.scala b/src/main/scala/leon/invariant/datastructure/Graph.scala similarity index 99% rename from src/main/scala/leon/invariant/util/Graph.scala rename to src/main/scala/leon/invariant/datastructure/Graph.scala index 22f4e83d12bb96407f37cb548b71482c38376914..484f04668c08a6b02f8acfd2db12b343e4b4c303 100644 --- a/src/main/scala/leon/invariant/util/Graph.scala +++ b/src/main/scala/leon/invariant/datastructure/Graph.scala @@ -1,5 +1,5 @@ package leon -package invariant.util +package invariant.datastructure class DirectedGraph[T] { diff --git a/src/main/scala/leon/invariant/datastructure/Maps.scala b/src/main/scala/leon/invariant/datastructure/Maps.scala new file mode 100644 index 0000000000000000000000000000000000000000..777a79375d874588f3be108d702aa6dc8118df79 --- /dev/null +++ b/src/main/scala/leon/invariant/datastructure/Maps.scala @@ -0,0 +1,117 @@ +package leon +package invariant.util + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Expressions._ +import purescala.ExprOps._ +import purescala.Extractors._ +import purescala.Types._ +import scala.collection.mutable.{ Set => MutableSet, Map => MutableMap } +import scala.annotation.tailrec + +class MultiMap[A, B] extends scala.collection.mutable.HashMap[A, scala.collection.mutable.Set[B]] with scala.collection.mutable.MultiMap[A, B] { + /** + * Creates a new map and does not change the existing map + */ + def append(that: MultiMap[A, B]): MultiMap[A, B] = { + val newmap = new MultiMap[A, B]() + this.foreach { case (k, vset) => newmap += (k -> vset) } + that.foreach { + case (k, vset) => vset.foreach(v => newmap.addBinding(k, v)) + } + newmap + } +} + +/** + * A multimap that allows duplicate entries + */ +class OrderedMultiMap[A, B] extends scala.collection.mutable.HashMap[A, scala.collection.mutable.ListBuffer[B]] { + + def addBinding(key: A, value: B): this.type = { + get(key) match { + case None => + val list = new scala.collection.mutable.ListBuffer[B]() + list += value + this(key) = list + case Some(list) => + list += value + } + this + } + + /** + * Creates a new map and does not change the existing map + */ + def append(that: OrderedMultiMap[A, B]): OrderedMultiMap[A, B] = { + val newmap = new OrderedMultiMap[A, B]() + this.foreach { case (k, vlist) => newmap += (k -> vlist) } + that.foreach { + case (k, vlist) => vlist.foreach(v => newmap.addBinding(k, v)) + } + newmap + } + + /** + * Make the value of every key distinct + */ + def distinct: OrderedMultiMap[A, B] = { + val newmap = new OrderedMultiMap[A, B]() + this.foreach { case (k, vlist) => newmap += (k -> vlist.distinct) } + newmap + } +} + +/** + * Implements a mapping from Seq[A] to B where Seq[A] + * is stored as a Trie + */ +final class TrieMap[A, B] { + var childrenMap = Map[A, TrieMap[A, B]]() + var dataMap = Map[A, B]() + + @tailrec def addBinding(key: Seq[A], value: B) { + key match { + case Seq() => + throw new IllegalStateException("Key is empty!!") + case Seq(x) => + //add the value to the dataMap + if (dataMap.contains(x)) + throw new IllegalStateException("A mapping for key already exists: " + x + " --> " + dataMap(x)) + else + dataMap += (x -> value) + case head +: tail => //here, tail has at least one element + //check if we have an entry for seq(0) if yes go to the children, if not create one + val child = childrenMap.getOrElse(head, { + val ch = new TrieMap[A, B]() + childrenMap += (head -> ch) + ch + }) + child.addBinding(tail, value) + } + } + + @tailrec def lookup(key: Seq[A]): Option[B] = { + key match { + case Seq() => + throw new IllegalStateException("Key is empty!!") + case Seq(x) => + dataMap.get(x) + case head +: tail => //here, tail has at least one element + childrenMap.get(head) match { + case Some(child) => + child.lookup(tail) + case _ => None + } + } + } +} + +class CounterMap[T] extends scala.collection.mutable.HashMap[T, Int] { + def inc(v: T) = { + if (this.contains(v)) + this(v) += 1 + else this += (v -> 1) + } +} \ No newline at end of file diff --git a/src/main/scala/leon/invariant/engine/CompositionalTemplateSolver.scala b/src/main/scala/leon/invariant/engine/CompositionalTemplateSolver.scala index 4c443b3f6e08b7cf381b661a1725862156c1bba6..647616fce1bab0eb01182ad50d75c060ce4ff737 100644 --- a/src/main/scala/leon/invariant/engine/CompositionalTemplateSolver.scala +++ b/src/main/scala/leon/invariant/engine/CompositionalTemplateSolver.scala @@ -8,20 +8,21 @@ import purescala.ExprOps._ import purescala.Extractors._ import purescala.Types._ import invariant.templateSolvers._ -import invariant.util.Util._ import transformations._ import invariant.structure.FunctionUtils._ import transformations.InstUtil._ import leon.invariant.structure.Formula import leon.invariant.structure.Call -import leon.invariant.util.RealToInt -import leon.invariant.util.OrderedMultiMap -import leon.invariant.util.ExpressionTransformer +import leon.invariant.util._ import leon.invariant.factories.TemplateSolverFactory import leon.invariant.util.Minimizer import leon.solvers.Model +import Util._ +import PredicateUtil._ +import ProgramUtil._ +import SolverUtil._ -class CompositionalTimeBoundSolver(ctx: InferenceContext, rootFd: FunDef) +class CompositionalTimeBoundSolver(ctx: InferenceContext, prog: Program, rootFd: FunDef) extends FunctionTemplateSolver { val printIntermediatePrograms = false @@ -30,7 +31,7 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, rootFd: FunDef) val reporter = ctx.reporter def inferTemplate(instProg: Program) = { - (new UnfoldingTemplateSolver(ctx.copy(program = instProg), findRoot(instProg)))() + (new UnfoldingTemplateSolver(ctx, instProg, findRoot(instProg)))() } def findRoot(prog: Program) = { @@ -45,7 +46,7 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, rootFd: FunDef) throw new IllegalStateException("Templates for tpr, rec, time as well as all other templates " + " taken together should not have the any common template variables for compositional analysis") - val origProg = ctx.program + val origProg = prog // add only rec templates for all functions val funToRecTmpl = origProg.definedFunctions.collect { case fd if fd.hasTemplate && fd == rootFd => @@ -81,7 +82,7 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, rootFd: FunDef) }.toMap val compProg = assignTemplateAndCojoinPost(funToTmpl, origProg) val compFunDef = findRoot(compProg) - val nctx = ctx.copy(program = compProg) + //val nctx = ctx.copy(program = compProg) // construct the instantiated tpr bound and check if it monotonically decreases val Operator(Seq(_, tprFun), _) = tprTmpl @@ -118,13 +119,13 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, rootFd: FunDef) if (printIntermediatePrograms) reporter.info("Comp prog: " + compProg) if (debugComposition) reporter.info("Compositional VC: " + vcExpr) - val recTempSolver = new UnfoldingTemplateSolver(nctx, compFunDef) { + val recTempSolver = new UnfoldingTemplateSolver(ctx, compProg, compFunDef) { val minFunc = { - val mizer = new Minimizer(ctx) + val mizer = new Minimizer(ctx, compProg) Some(mizer.minimizeBounds(mizer.computeCompositionLevel(timeTmpl)) _) } override lazy val templateSolver = - TemplateSolverFactory.createTemplateSolver(ctx, constTracker, rootFd, minFunc) + TemplateSolverFactory.createTemplateSolver(ctx, compProg, constTracker, rootFd, minFunc) override def instantiateModel(model: Model, funcs: Seq[FunDef]) = { funcs.collect { case `compFunDef` => @@ -148,12 +149,12 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, rootFd: FunDef) } } - def combineMapsUsingConjunction(maps: List[Map[FunDef, Expr]]) = { + /*def combineMapsUsingConjunction(maps: List[Map[FunDef, Expr]], prog: Program) = { val combMap = new OrderedMultiMap[FunDef, Expr] maps.foreach { _.foreach { case (k, v) => - val origFun = functionByName(k.id.name, ctx.program).get + val origFun = functionByName(k.id.name, prog).get combMap.addBinding(origFun, v) } } @@ -161,7 +162,7 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, rootFd: FunDef) case (acc, (k, vs)) if vs.size == 1 => acc + (k -> vs(0)) case (acc, (k, vs)) => acc + (k -> And(vs.toSeq)) } - } + }*/ def extractSeparateTemplates(funDef: FunDef): (Option[Expr], Option[Expr], Option[Expr], Seq[Expr]) = { if (!funDef.hasTemplate) (None, None, None, Seq[Expr]()) @@ -204,7 +205,7 @@ class CompositionalTimeBoundSolver(ctx: InferenceContext, rootFd: FunDef) } def inferTPRTemplate(tprProg: Program) = { - val tempSolver = new UnfoldingTemplateSolver(ctx.copy(program = tprProg), findRoot(tprProg)) { + val tempSolver = new UnfoldingTemplateSolver(ctx, tprProg, findRoot(tprProg)) { override def constructVC(rootFd: FunDef): (Expr, Expr) = { val body = Equals(getResId(rootFd).get.toVariable, matchToIfThenElse(rootFd.body.get)) val preExpr = diff --git a/src/main/scala/leon/invariant/engine/ConstraintTracker.scala b/src/main/scala/leon/invariant/engine/ConstraintTracker.scala index e50b50bb8b84886bff46c54641f55050fc5f5e52..3058a267eceef7c5ec1aa537c4766bee15a93204 100644 --- a/src/main/scala/leon/invariant/engine/ConstraintTracker.scala +++ b/src/main/scala/leon/invariant/engine/ConstraintTracker.scala @@ -16,13 +16,13 @@ import invariant.factories._ import invariant.util._ import invariant.structure._ -class ConstraintTracker(ctx : InferenceContext, rootFun : FunDef/*, temFactory: TemplateFactory*/) { +class ConstraintTracker(ctx : InferenceContext, program: Program, rootFun : FunDef/*, temFactory: TemplateFactory*/) { //a mapping from functions to its VCs represented as a CNF formula protected var funcVCs = Map[FunDef,Formula]() - val vcRefiner = new RefinementEngine(ctx, this/*, temFactory*/) - val specInstantiator = new SpecInstantiator(ctx, this/*, temFactory*/) + val vcRefiner = new RefinementEngine(ctx, program, this) + val specInstantiator = new SpecInstantiator(ctx, program, this) def getFuncs : Seq[FunDef] = funcVCs.keys.toSeq def hasVC(fdef: FunDef) = funcVCs.contains(fdef) diff --git a/src/main/scala/leon/invariant/engine/InferInvariantsPhase.scala b/src/main/scala/leon/invariant/engine/InferInvariantsPhase.scala index ce69f2b6f755279d333471bacf9aa0579013defd..eca69f0c0f0246dae7840ae9b6511f841fcc90d2 100644 --- a/src/main/scala/leon/invariant/engine/InferInvariantsPhase.scala +++ b/src/main/scala/leon/invariant/engine/InferInvariantsPhase.scala @@ -14,21 +14,17 @@ import invariant.util._ import invariant.structure.FunctionUtils._ import invariant.structure._ import transformations._ -import verification._ -import verification.VCKinds import leon.purescala.ScalaPrinter import leon.purescala.PrettyPrinter /** * @author ravi * This phase performs automatic invariant inference. - * TODO: should time be implicitly made positive */ object InferInvariantsPhase extends SimpleLeonPhase[Program, InferenceReport] { val name = "InferInv" val description = "Invariant Inference" - val optWholeProgram = LeonFlagOptionDef("wholeprogram", "Perform an non-modular whole program analysis", false) val optFunctionUnroll = LeonFlagOptionDef("fullunroll", "Unroll all calls in every unroll step", false) val optWithMult = LeonFlagOptionDef("withmult", "Multiplication is not converted to a recursive function in VCs", false) val optUseReals = LeonFlagOptionDef("usereals", "Interpret the input program as a real program", false) @@ -36,121 +32,18 @@ object InferInvariantsPhase extends SimpleLeonPhase[Program, InferenceReport] { val optInferTemp = LeonFlagOptionDef("inferTemp", "Infer templates by enumeration", false) val optCegis = LeonFlagOptionDef("cegis", "use cegis instead of farkas", false) val optStatsSuffix = LeonStringOptionDef("stats-suffix", "the suffix of the statistics file", "", "s") - val optTimeout = LeonLongOptionDef("timeout", "Timeout after T seconds when trying to prove a verification condition.", 20, "s") + val optVCTimeout = LeonLongOptionDef("vcTimeout", "Timeout after T seconds when trying to prove a verification condition", 20, "s") val optDisableInfer = LeonFlagOptionDef("disableInfer", "Disable automatic inference of auxiliary invariants", false) override val definedOptions: Set[LeonOptionDef[Any]] = - Set(optWholeProgram, optFunctionUnroll, optWithMult, optUseReals, - optMinBounds, optInferTemp, optCegis, optStatsSuffix, optTimeout, + Set(optFunctionUnroll, optWithMult, optUseReals, + optMinBounds, optInferTemp, optCegis, optStatsSuffix, optVCTimeout, optDisableInfer) - //TODO provide options for analyzing only selected functions def apply(ctx: LeonContext, program: Program): InferenceReport = { - - //control printing of statistics - val dumpStats = false - var timeout: Int = 15 - - //defualt true flags - var modularlyAnalyze = true - var targettedUnroll = true - - //default false flags - var tightBounds = false - var withmult = false - var inferTemp = false - var enumerationRelation: (Expr, Expr) => Expr = LessEquals - var useCegis = false - //var maxCegisBound = 200 //maximum bound for the constants in cegis - var maxCegisBound = 1000000000 - var statsSuff = "-stats" + FileCountGUID.getID - var usereals = false - var autoInference = true - - for (opt <- ctx.options) (opt.optionDef.name, opt.value) match { - case ("wholeprogram", true) => - modularlyAnalyze = false - case ("fullunroll", true) => - targettedUnroll = false - case ("minbounds", true) => - tightBounds = true - case ("withmult", true) => - withmult = true - case ("usereals", true) => - usereals = true - case ("disableInfer", true) => - autoInference = false - case ("inferTemp", true) => - inferTemp = true - case ("cegis", true) => - useCegis = true - case ("timeout", timeOut: Int) => - timeout = timeOut - case ("stats-suffix", suffix: String) => - statsSuff = suffix - case _ => - } - - // (a) first run instrumentation phase - val instProg = InstrumentationPhase(ctx, program) - - // (b) convert qmarks to tmpl functions - val funToTmpl = instProg.definedFunctions.collect { - case fd if fd.hasTemplate => - fd -> fd.getTemplate - }.toMap - val qMarksRemovedProg = Util.assignTemplateAndCojoinPost(funToTmpl, instProg, Map()) - - // convert nonlinearity to recursive functions - val newprog = if (usereals) { - (new IntToRealProgram())(qMarksRemovedProg) - } else qMarksRemovedProg - val nlelim = new NonlinearityEliminator(withmult, if (usereals) RealType else IntegerType) - val finalprog = nlelim(newprog) - - val toVerifyPost = validateAndCollectNotValidated(qMarksRemovedProg, ctx, timeout) - - // collect strongest relation for enumeration if defined - var foundStrongest = false - //go over all post-conditions and pick the strongest relation - instProg.definedFunctions.foreach((fd) => { - if (!foundStrongest && fd.hasPostcondition) { - val cond = fd.postcondition.get - simplePostTransform((e) => e match { - case Equals(_, _) => { - enumerationRelation = Equals.apply _ - foundStrongest = true - e - } - case _ => e - })(cond) - } - }) - - //populate the inference context and invoke inferenceEngine - val inferctx = new InferenceContext(program, finalprog, toVerifyPost, ctx, - //multiplication operation - (e1, e2) => FunctionInvocation(TypedFunDef(nlelim.multFun, nlelim.multFun.tparams.map(_.tp)), Seq(e1, e2)), - enumerationRelation = LessEquals, modularlyAnalyze, targettedUnroll, autoInference, - dumpStats, tightBounds, withmult, usereals, inferTemp, useCegis, timeout, maxCegisBound, statsSuff) - val report = (new InferenceEngine(inferctx)).run() + val inferctx = new InferenceContext(program, ctx) + val report = (new InferenceEngine(inferctx)).runWithTimeout() //println("Final Program: \n" +PrettyPrinter.apply(report.finalProgramWoInstrumentation)) report } - - def createLeonContext(ctx: LeonContext, opts: String*): LeonContext = { - Main.processOptions(opts.toList).copy(reporter = ctx.reporter, - interruptManager = ctx.interruptManager, files = ctx.files, timers = ctx.timers) - } - - def validateAndCollectNotValidated(prog: Program, ctx: LeonContext, timeout: Int): Set[String] = { - val verifyPipe = AnalysisPhase - val ctxWithTO = createLeonContext(ctx, "--timeout=" + timeout) - (verifyPipe.run(ctxWithTO, prog)._2).results.collect{ - case (VC(_, fd, VCKinds.Postcondition), Some(vcRes)) if vcRes.isInconclusive => - fd.id.name - case (VC(_, fd, vcKind), Some(vcRes)) if vcRes.isInvalid => - throw new IllegalStateException("Invalid" + vcKind + " for function " + fd.id.name) - }.toSet - } } diff --git a/src/main/scala/leon/invariant/engine/InferenceContext.scala b/src/main/scala/leon/invariant/engine/InferenceContext.scala index bf3d83845b3fcb9b2ecbd3fe108186655dbc26b2..b735cb8de7101f3bf109c6318cc7a05c571ba4b7 100644 --- a/src/main/scala/leon/invariant/engine/InferenceContext.scala +++ b/src/main/scala/leon/invariant/engine/InferenceContext.scala @@ -1,32 +1,116 @@ package leon package invariant.engine +import scala.collection.mutable.{ Map => MutableMap } import purescala.Definitions._ import purescala.Expressions._ -import purescala._ +import purescala.Types._ +import purescala.ExprOps._ +import transformations._ +import invariant.structure.FunctionUtils._ +import invariant.util._ +import verification._ +import verification.VCKinds +import InferInvariantsPhase._ +import Util._ +import ProgramUtil._ /** * @author ravi */ -case class InferenceContext( - val uninstrumentedProgram : Program, - val program : Program, - val toVerifyPostFor: Set[String], - val leonContext : LeonContext, - val multOp: (Expr,Expr) => Expr, - val enumerationRelation : (Expr,Expr) => Expr, - val modularlyAnalyze : Boolean, - val targettedUnroll : Boolean, - val autoInference : Boolean, - val dumpStats : Boolean , - val tightBounds : Boolean, - val withmult : Boolean, - val usereals : Boolean, - val inferTemp : Boolean, - val useCegis : Boolean, - val timeout: Int, //in secs - val maxCegisBound : Int, - val statsSuffix : String) { +class InferenceContext(val initProgram: Program, val leonContext: LeonContext) { + var abort = false // a flag for aborting + + // get options from ctx or initialize them to default values + // the following options are enabled by default + val targettedUnroll = !(leonContext.findOption(optFunctionUnroll).getOrElse(false)) + val autoInference = leonContext.findOption(optDisableInfer).getOrElse(true) + + // the following options are disabled by default + val tightBounds = leonContext.findOption(optMinBounds).getOrElse(false) + val inferTemp = leonContext.findOption(optInferTemp).getOrElse(false) + val withmult = leonContext.findOption(optWithMult).getOrElse(false) + val usereals = leonContext.findOption(optUseReals).getOrElse(false) + val useCegis: Boolean = leonContext.findOption(optCegis).getOrElse(false) + val dumpStats = false + + // the following options have default values + val vcTimeout = leonContext.findOption(optVCTimeout).getOrElse(15L) // in secs + val totalTimeout = leonContext.findOption(SharedOptions.optTimeout) // in secs + val functionsToInfer = leonContext.findOption(SharedOptions.optFunctions) val reporter = leonContext.reporter + val maxCegisBound = 1000 + val statsSuffix = leonContext.findOption(optStatsSuffix).getOrElse("-stats" + FileCountGUID.getID) + + val instrumentedProg = InstrumentationPhase(leonContext, initProgram) + // convets qmarks to templates + val qMarksRemovedProg = { + val funToTmpl = instrumentedProg.definedFunctions.collect { + case fd if fd.hasTemplate => + fd -> fd.getTemplate + }.toMap + assignTemplateAndCojoinPost(funToTmpl, instrumentedProg, Map()) + } + + val nlelim = new NonlinearityEliminator(withmult, if (usereals) RealType else IntegerType) + + val inferProgram = { + // convert nonlinearity to recursive functions + nlelim(if (usereals) + (new IntToRealProgram())(qMarksRemovedProg) + else qMarksRemovedProg) + } + + // other utilities + lazy val enumerationRelation = { + // collect strongest relation for enumeration if defined + var foundStrongest = false + var rel: (Expr, Expr) => Expr = LessEquals.apply _ + //go over all post-conditions and pick the strongest relation + instrumentedProg.definedFunctions.foreach((fd) => { + if (!foundStrongest && fd.hasPostcondition) { + val cond = fd.postcondition.get + postTraversal((e) => e match { + case Equals(_, _) => { + rel = Equals.apply _ + foundStrongest = true + } + case _ => ; + })(cond) + } + }) + rel + } + + def multOp(e1: Expr, e2: Expr) = { + FunctionInvocation(TypedFunDef(nlelim.multFun, nlelim.multFun.tparams.map(_.tp)), Seq(e1, e2)) + } + + val validPosts = MutableMap[String, VCResult]() + + /** + * There should be only one function with funName in the + * program + */ + def isFunctionPostVerified(funName: String) = { + if (validPosts.contains(funName)) { + validPosts(funName).isValid + } else { + val verifyPipe = VerificationPhase + val ctxWithTO = createLeonContext(leonContext, s"--timeout=$vcTimeout", s"--functions=$funName") + (true /: verifyPipe.run(ctxWithTO, qMarksRemovedProg)._2.results) { + case (acc, (VC(_, _, vckind), Some(vcRes))) if vcRes.isInvalid => + throw new IllegalStateException(s"$vckind invalid for function $funName") // TODO: remove the exception + case (acc, (VC(_, _, VCKinds.Postcondition), None)) => + throw new IllegalStateException(s"Postcondition verification returned unknown for function $funName") // TODO: remove the exception + case (acc, (VC(_, _, VCKinds.Postcondition), _)) if validPosts.contains(funName) => + throw new IllegalStateException(s"Multiple postcondition VCs for function $funName") // TODO: remove the exception + case (acc, (VC(_, _, VCKinds.Postcondition), Some(vcRes))) => + validPosts(funName) = vcRes + vcRes.isValid + case (acc, _) => acc + } + } + } } diff --git a/src/main/scala/leon/invariant/engine/InferenceEngine.scala b/src/main/scala/leon/invariant/engine/InferenceEngine.scala index 03202f9dbb3a300a8eca4c2454b295e924dfd1b3..85491acf1924560e5fe020a38176b513da989690 100644 --- a/src/main/scala/leon/invariant/engine/InferenceEngine.scala +++ b/src/main/scala/leon/invariant/engine/InferenceEngine.scala @@ -20,17 +20,45 @@ import invariant.util.Util._ import invariant.structure._ import invariant.structure.FunctionUtils._ import leon.invariant.factories.TemplateFactory +import transformations._ +import leon.utils._ +import Util._ +import PredicateUtil._ +import ProgramUtil._ +import SolverUtil._ /** * @author ravi * This phase performs automatic invariant inference. * TODO: should time be implicitly made positive */ -class InferenceEngine(ctx: InferenceContext) { +class InferenceEngine(ctx: InferenceContext) extends Interruptible { - def run(): InferenceReport = { + val ti = new TimeoutFor(this) + + def interrupt() = { + ctx.abort = true + ctx.leonContext.interruptManager.interrupt() + } + + def recoverInterrupt() = { + ctx.abort = false + } + + def runWithTimeout(progressCallback: Option[InferenceCondition => Unit] = None) = { + ctx.totalTimeout match { + case Some(t) => // timeout in secs + ti.interruptAfter(t * 1000) { + run(progressCallback) + } + case None => + run(progressCallback) + } + } + + private def run(progressCallback: Option[InferenceCondition => Unit] = None): InferenceReport = { val reporter = ctx.reporter - val program = ctx.program + val program = ctx.inferProgram reporter.info("Running Inference Engine...") //register a shutdownhook @@ -40,15 +68,19 @@ class InferenceEngine(ctx: InferenceContext) { val t1 = System.currentTimeMillis() //compute functions to analyze by sorting based on topological order (this is an ascending topological order) val callgraph = CallGraphUtil.constructCallGraph(program, withTemplates = true) - val functionsToAnalyze = if (ctx.modularlyAnalyze) { - callgraph.topologicalOrder - } else { - callgraph.topologicalOrder.reverse + val functionsToAnalyze = ctx.functionsToInfer match { + case Some(rootfuns) => + val rootset = rootfuns.toSet + val rootfds = program.definedFunctions.filter(fd => rootset(InstUtil.userFunctionName(fd))) + val relfuns = rootfds.flatMap(callgraph.transitiveCallees _).toSet + callgraph.topologicalOrder.filter { fd => relfuns(fd) } + case _ => + callgraph.topologicalOrder } //reporter.info("Analysis Order: " + functionsToAnalyze.map(_.id)) var results: Map[FunDef, InferenceCondition] = null if (!ctx.useCegis) { - results = analyseProgram(functionsToAnalyze) + results = analyseProgram(program, functionsToAnalyze, progressCallback) //println("Inferrence did not succeeded for functions: "+functionsToAnalyze.filterNot(succeededFuncs.contains _).map(_.id)) } else { var remFuncs = functionsToAnalyze @@ -57,7 +89,7 @@ class InferenceEngine(ctx: InferenceContext) { breakable { while (b <= maxCegisBound) { Stats.updateCumStats(1, "CegisBoundsTried") - val succeededFuncs = analyseProgram(remFuncs) + val succeededFuncs = analyseProgram(program, remFuncs, progressCallback) remFuncs = remFuncs.filterNot(succeededFuncs.contains _) if (remFuncs.isEmpty) break; b += 5 //increase bounds in steps of 5 @@ -75,12 +107,12 @@ class InferenceEngine(ctx: InferenceContext) { new InferenceReport(results.map(pair => { val (fd, ic) = pair (fd -> List[VC](ic)) - }))(ctx) + }), program)(ctx) } def dumpStats(statsSuffix: String) = { //pick the module id. - val modid = ctx.program.modules.last.id + val modid = ctx.inferProgram.modules.last.id val pw = new PrintWriter(modid + statsSuffix + ".txt") Stats.dumpStats(pw) SpecificStats.dumpOutputs(pw) @@ -94,85 +126,111 @@ class InferenceEngine(ctx: InferenceContext) { * TODO: use function names in inference conditions, so that * we an get rid of dependence on origFd in many places. */ - def analyseProgram(functionsToAnalyze: Seq[FunDef]): Map[FunDef, InferenceCondition] = { + def analyseProgram(startProg: Program, functionsToAnalyze: Seq[FunDef], + progressCallback: Option[InferenceCondition => Unit]): Map[FunDef, InferenceCondition] = { val reporter = ctx.reporter val funToTmpl = if (ctx.autoInference) { //A template generator that generates templates for the functions (here we are generating templates by enumeration) - val tempFactory = new TemplateFactory(Some(new TemplateEnumerator(ctx)), ctx.program, ctx.reporter) - ctx.program.definedFunctions.map(fd => fd -> getOrCreateTemplateForFun(fd)).toMap + // not usef for now + /*val tempFactory = new TemplateFactory(Some(new TemplateEnumerator(ctx, startProg)), + startProg, ctx.reporter)*/ + startProg.definedFunctions.map(fd => fd -> getOrCreateTemplateForFun(fd)).toMap } else - ctx.program.definedFunctions.collect { case fd if fd.hasTemplate => fd -> fd.getTemplate }.toMap - val progWithTemplates = assignTemplateAndCojoinPost(funToTmpl, ctx.program) + startProg.definedFunctions.collect { case fd if fd.hasTemplate => fd -> fd.getTemplate }.toMap + val progWithTemplates = assignTemplateAndCojoinPost(funToTmpl, startProg) var analyzedSet = Map[FunDef, InferenceCondition]() + functionsToAnalyze.filterNot((fd) => { (fd.annotations contains "verified") || (fd.annotations contains "library") || (fd.annotations contains "theoryop") }).foldLeft(progWithTemplates) { (prog, origFun) => - val funDef = functionByName(origFun.id.name, prog).get - reporter.info("- considering function " + funDef.id.name + "...") - - //skip the function if it has been analyzed - if (!analyzedSet.contains(origFun)) { - if (funDef.hasBody && funDef.hasPostcondition) { - val currCtx = ctx.copy(program = prog) - // for stats - Stats.updateCounter(1, "procs") - val solver = - if (funDef.annotations.contains("compose")) //compositional inference ? - new CompositionalTimeBoundSolver(currCtx, funDef) - else - new UnfoldingTemplateSolver(currCtx, funDef) - val t1 = System.currentTimeMillis() - val infRes = solver() - val funcTime = (System.currentTimeMillis() - t1) / 1000.0 - infRes match { - case Some(InferResult(true, model, inferredFuns)) => - val funsWithTemplates = inferredFuns.filter { fd => - val origFd = Util.functionByName(fd.id.name, ctx.program).get - !analyzedSet.contains(origFd) && origFd.hasTemplate - } - // create a inference condition for reporting - var first = true - funsWithTemplates.foreach { fd => - val origFd = Util.functionByName(fd.id.name, ctx.program).get - val inv = TemplateInstantiator.getAllInvariants(model.get, - Map(origFd -> origFd.getTemplate)) - // record the inferred invariants - val ic = new InferenceCondition(Some(inv(origFd)), origFd) - ic.time = if (first) Some(funcTime) else Some(0.0) - // update analyzed set - analyzedSet += (origFd -> ic) - first = false - } - val invs = TemplateInstantiator.getAllInvariants(model.get, - funsWithTemplates.collect { - case fd if fd.hasTemplate => fd -> fd.getTemplate - }.toMap) - if (ctx.modularlyAnalyze) { + if (ctx.abort) { + reporter.info("- Aborting analysis of " + origFun.id.name) + val ic = new InferenceCondition(Seq(), origFun) + ic.time = Some(0) + prog + } else if (origFun.getPostWoTemplate == tru && !origFun.hasTemplate) { + reporter.info("- Nothing to solve for " + origFun.id.name) + prog + } else { + val funDef = functionByName(origFun.id.name, prog).get + reporter.info("- considering function " + funDef.id.name + "...") + //skip the function if it has been analyzed + if (!analyzedSet.contains(origFun)) { + if (funDef.hasBody && funDef.hasPostcondition) { + // for stats + Stats.updateCounter(1, "procs") + val solver = + if (funDef.annotations.contains("compose")) //compositional inference ? + new CompositionalTimeBoundSolver(ctx, prog, funDef) + else + new UnfoldingTemplateSolver(ctx, prog, funDef) + val t1 = System.currentTimeMillis() + val infRes = solver() + val funcTime = (System.currentTimeMillis() - t1) / 1000.0 + infRes match { + case Some(InferResult(true, model, inferredFuns)) => + // create a inference condition for reporting + var first = true + inferredFuns.foreach { fd => + val origFd = functionByName(fd.id.name, startProg).get + val inv = if (origFd.hasTemplate) { + TemplateInstantiator.getAllInvariants(model.get, + Map(origFd -> origFd.getTemplate), prettyInv = true)(origFd) + } else { + val currentInv = TemplateInstantiator.getAllInvariants(model.get, + Map(fd -> fd.getTemplate), prettyInv = true)(fd) + // map result variable in currentInv + val repInv = replace(Map(getResId(fd).get.toVariable -> getResId(origFd).get.toVariable), currentInv) + translateExprToProgram(repInv, prog, startProg) + } + // record the inferred invariants + val inferCond = if (analyzedSet.contains(origFd)) { + val ic = analyzedSet(origFd) + ic.addInv(Seq(inv)) + ic + } else { + val ic = new InferenceCondition(Seq(inv), origFd) + ic.time = if (first) Some(funcTime) else Some(0.0) + // update analyzed set + analyzedSet += (origFd -> ic) + first = false + ic + } + progressCallback.map(cb => cb(inferCond)) + } + val funsWithTemplates = inferredFuns.filter { fd => + val origFd = functionByName(fd.id.name, startProg).get + !analyzedSet.contains(origFd) && origFd.hasTemplate + } + val invs = TemplateInstantiator.getAllInvariants(model.get, + funsWithTemplates.collect { + case fd if fd.hasTemplate => fd -> fd.getTemplate + }.toMap) // create a new program that has the inferred templates val funToTmpl = prog.definedFunctions.collect { case fd if !invs.contains(fd) && fd.hasTemplate => fd -> fd.getTemplate }.toMap assignTemplateAndCojoinPost(funToTmpl, prog, invs) - } else + + case _ => + reporter.info("- Exhausted all templates, cannot infer invariants") + val ic = new InferenceCondition(Seq(), origFun) + ic.time = Some(funcTime) + analyzedSet += (origFun -> ic) prog - case _ => - reporter.info("- Exhausted all templates, cannot infer invariants") - val ic = new InferenceCondition(None, origFun) - ic.time = Some(funcTime) - analyzedSet += (origFun -> ic) - prog + } + } else { + //nothing needs to be done here + reporter.info("Function does not have a body or postcondition") + prog } - } else { - //nothing needs to be done here - reporter.info("Function does not have a body or postcondition") - prog - } - } else prog + } else prog + } } analyzedSet } diff --git a/src/main/scala/leon/invariant/engine/InferenceReport.scala b/src/main/scala/leon/invariant/engine/InferenceReport.scala index 26337c6fff8111ba59f11dfd25213ee61610ac8b..39af145c31bd5a7253475a5cebe047fca4e932b3 100644 --- a/src/main/scala/leon/invariant/engine/InferenceReport.scala +++ b/src/main/scala/leon/invariant/engine/InferenceReport.scala @@ -15,13 +15,33 @@ import invariant.util._ import invariant.structure._ import leon.transformations.InstUtil import leon.purescala.PrettyPrinter +import Util._ +import PredicateUtil._ +import ProgramUtil._ +import SolverUtil._ +import FunctionUtils._ +import purescala._ -class InferenceCondition(val invariant: Option[Expr], funDef: FunDef) - extends VC(BooleanLiteral(true), funDef, null) { +class InferenceCondition(invs: Seq[Expr], funDef: FunDef) + extends VC(BooleanLiteral(true), funDef, null) { var time: Option[Double] = None - lazy val prettyInv = invariant.map(inv => - simplifyArithmetic(InstUtil.replaceInstruVars(Util.multToTimes(inv), fd))) + var invariants = invs + + def addInv(invs: Seq[Expr]) { + invariants ++= invs + } + + lazy val prettyInv = invariants.map(inv => + simplifyArithmetic(InstUtil.replaceInstruVars(multToTimes(inv), fd))) match { + case Seq() => None + case invs => + invs.map(ExpressionTransformer.simplify _).filter(_ != tru) match { + case Seq() => Some(tru) + case Seq(ninv) => Some(ninv) + case ninvs => Some(And(ninvs)) + } + } def status: String = prettyInv match { case None => "unknown" @@ -30,8 +50,8 @@ class InferenceCondition(val invariant: Option[Expr], funDef: FunDef) } } -class InferenceReport(fvcs: Map[FunDef, List[VC]])(implicit ctx: InferenceContext) - extends VerificationReport(ctx.program, Map()) { +class InferenceReport(fvcs: Map[FunDef, List[VC]], program: Program)(implicit ctx: InferenceContext) + extends VerificationReport(program, Map()) { import scala.math.Ordering.Implicits._ val conditions: Seq[InferenceCondition] = @@ -65,7 +85,7 @@ class InferenceReport(fvcs: Map[FunDef, List[VC]])(implicit ctx: InferenceContex }) val summaryStr = { val totalTime = conditions.foldLeft(0.0)((a, ic) => a + ic.time.getOrElse(0.0)) - val inferredConds = conditions.count((ic) => ic.invariant.isDefined) + val inferredConds = conditions.count((ic) => ic.prettyInv.isDefined) "total: %-4d inferred: %-4d unknown: %-4d time: %-3.3f".format( conditions.size, inferredConds, conditions.size - inferredConds, totalTime) } @@ -83,29 +103,151 @@ class InferenceReport(fvcs: Map[FunDef, List[VC]])(implicit ctx: InferenceContex def finalProgram: Program = { val funToTmpl = conditions.collect { - case cd if cd.invariant.isDefined => - cd.fd -> cd.invariant.get + case cd if cd.prettyInv.isDefined => + cd.fd -> cd.prettyInv.get }.toMap - Util.assignTemplateAndCojoinPost(funToTmpl, ctx.program) + assignTemplateAndCojoinPost(funToTmpl, program) } +} - def finalProgramWoInstrumentation: Program = { +object InferenceReportUtil { - val funToUninstFun = ctx.program.definedFunctions.foldLeft(Map[FunDef, FunDef]()) { + /* def programWoInstrumentation(ctx: InferenceContext, p: Program, ics: Seq[InferenceCondition]) = { + val funToUninstFun = p.definedFunctions.foldLeft(Map[FunDef, FunDef]()) { case (acc, fd) => val uninstFunName = InstUtil.userFunctionName(fd) val uninstFdOpt = if (uninstFunName.isEmpty) None - else Util.functionByName(uninstFunName, ctx.uninstrumentedProgram) + else functionByName(uninstFunName, ctx.initProgram) if (uninstFdOpt.isDefined) { acc + (fd -> uninstFdOpt.get) } else acc } - val funToPost = conditions.collect { - case cd if cd.invariant.isDefined && funToUninstFun.contains(cd.fd) => + val funToPost = ics.collect { + case cd if cd.prettyInv.isDefined && funToUninstFun.contains(cd.fd) => funToUninstFun(cd.fd) -> cd.prettyInv.get }.toMap //println("Function to template: " + funToTmpl.map { case (k, v) => s"${k.id.name} --> $v" }.mkString("\n")) - Util.assignTemplateAndCojoinPost(Map(), ctx.uninstrumentedProgram, funToPost, uniqueIdDisplay = false) + assignTemplateAndCojoinPost(Map(), ctx.initProgram, funToPost, uniqueIdDisplay = false) + }*/ + + def pluginResultInInput(ctx: InferenceContext, ics: Seq[InferenceCondition], p: Program) = { + + val solvedICs = ics.filter { _.prettyInv.isDefined } + // mapping from init to output + val outputFunMap = + functionsWOFields(ctx.initProgram.definedFunctions).map { + case fd if fd.isTheoryOperation => (fd -> fd) + case fd => { + val freshId = FreshIdentifier(fd.id.name, fd.returnType) + val newfd = new FunDef(freshId, fd.tparams, fd.params, fd.returnType) + fd -> newfd + } + }.toMap + + def fullNameWoInst(fd: FunDef) = { + val splits = DefOps.fullName(fd)(p).split("-") + if (!splits.isEmpty) splits(0) + else "" + } + + // mapping from p to output + val progFunMap = (Map[FunDef, FunDef]() /: functionsWOFields(p.definedFunctions)) { + case (acc, fd) => + val inFun = functionByFullName(fullNameWoInst(fd), ctx.initProgram) + if (inFun.isDefined) + acc + (fd -> outputFunMap(inFun.get)) + else acc + }.toMap + + // mapping from init to ic + val initICmap = (Map[FunDef, InferenceCondition]() /: solvedICs) { + case (acc, ic) => + val initfd = functionByFullName(fullNameWoInst(ic.fd), ctx.initProgram) + if (initfd.isDefined) { + acc + (initfd.get -> ic) + } else acc + } + + def mapProgram(funMap: Map[FunDef, FunDef]) = { + + def mapExpr(ine: Expr): Expr = { + val replaced = simplePostTransform((e: Expr) => e match { + case FunctionInvocation(tfd, args) if funMap.contains(tfd.fd) => + FunctionInvocation(TypedFunDef(funMap(tfd.fd), tfd.tps), args) + case _ => e + })(ine) + replaced + } + + for ((from, to) <- funMap) { + to.body = from.body.map(mapExpr) + to.precondition = from.precondition.map(mapExpr) + if (initICmap.contains(from)) { + val ic = initICmap(from) + val paramMap = (ic.fd.params zip from.params).map { + case (p1, p2) => + (p1.id.toVariable -> p2.id.toVariable) + }.toMap + val icres = getResId(ic.fd).get + val npost = + if (from.hasPostcondition) { + val resid = getResId(from).get + val inv = replace(Map(icres.toVariable -> resid.toVariable) ++ paramMap, ic.prettyInv.get) + val postBody = from.postWoTemplate.map(post => createAnd(Seq(post, inv))).getOrElse(inv) + Lambda(Seq(ValDef(resid)), postBody) + } else { + val resid = FreshIdentifier(icres.name, icres.getType) + val inv = replace(Map(icres.toVariable -> resid.toVariable) ++ paramMap, ic.prettyInv.get) + Lambda(Seq(ValDef(resid)), inv) + } + to.postcondition = Some(mapExpr(npost)) + } else + to.postcondition = from.postcondition.map(mapExpr) + //copy annotations + from.flags.foreach(to.addFlag(_)) + } + } + mapProgram(outputFunMap ++ progFunMap) + copyProgram(ctx.initProgram, (defs: Seq[Definition]) => defs.map { + case fd: FunDef if outputFunMap.contains(fd) => + outputFunMap(fd) + case d => d + }) + /*if (debugSimplify) + println("After Simplifications: \n" + ScalaPrinter.apply(newprog))*/ + // /newprog + /*if (ic.prettyInv.isDefined) { + val uninstFunName = InstUtil.userFunctionName(ic.fd) + val inputFdOpt = + if (uninstFunName.isEmpty) None + else functionByName(uninstFunName, ctx.initProgram) + inputFdOpt match { + case None => ctx.initProgram + case Some(inFd) => + ProgramUtil.copyProgram(ctx.initProgram, defs => defs map { + case fd if fd.id == inFd.id => + val newfd = new FunDef(FreshIdentifier(inFd.id.name), inFd.tparams, inFd.params, inFd.returnType) + newfd.body = inFd.body + newfd.precondition = inFd.precondition + val fdres = getResId(ic.fd).get + val npost = + if (inFd.hasPostcondition) { + val resid = getResId(inFd).get + val inv = replace(Map(fdres.toVariable -> resid.toVariable), ic.prettyInv.get) + // map also functions here. + val postBody = inFd.postWoTemplate.map(post => createAnd(Seq(post, inv))).getOrElse(inv) + Lambda(Seq(ValDef(resid)), postBody) + } else { + val resid = FreshIdentifier(fdres.name, fdres.getType) + val inv = replace(Map(fdres.toVariable -> resid.toVariable), ic.prettyInv.get) + Lambda(Seq(ValDef(resid)), inv) + } + newfd.postcondition = Some(npost) + newfd + case d => d + }) + } + } else ctx.initProgram*/ } } \ No newline at end of file diff --git a/src/main/scala/leon/invariant/engine/RefinementEngine.scala b/src/main/scala/leon/invariant/engine/RefinementEngine.scala index cce8a3cb45a27693824944ed32c190921691cd2c..6cadb5b4f4563b95dbe6928f24237fb1f5b161c0 100644 --- a/src/main/scala/leon/invariant/engine/RefinementEngine.scala +++ b/src/main/scala/leon/invariant/engine/RefinementEngine.scala @@ -15,14 +15,16 @@ import invariant.util._ import invariant.util.Util._ import invariant.structure._ import FunctionUtils._ +import Util._ +import PredicateUtil._ +import ProgramUtil._ //TODO: the parts of the code that collect the new head functions is ugly and has many side-effects. Fix this. //TODO: there is a better way to compute heads, which is to consider all guards not previous seen -class RefinementEngine(ctx: InferenceContext, ctrTracker: ConstraintTracker) { +class RefinementEngine(ctx: InferenceContext, prog: Program, ctrTracker: ConstraintTracker) { val tru = BooleanLiteral(true) val reporter = ctx.reporter - val prog = ctx.program val cg = CallGraphUtil.constructCallGraph(prog) //this count indicates the number of times we unroll a recursive call @@ -161,7 +163,7 @@ class RefinementEngine(ctx: InferenceContext, ctrTracker: ConstraintTracker) { } else plainBody //note: here we are only adding the template as the postcondition - val idmap = Util.formalToActual(Call(resvar, FunctionInvocation(recFunTyped, recFun.params.map(_.toVariable)))) + val idmap = formalToActual(Call(resvar, FunctionInvocation(recFunTyped, recFun.params.map(_.toVariable)))) val postTemp = replace(idmap, recFun.getTemplate) val vcExpr = ExpressionTransformer.normalizeExpr(And(bodyExpr, Not(postTemp)), ctx.multOp) ctrTracker.addVC(recFun, vcExpr) @@ -185,8 +187,8 @@ class RefinementEngine(ctx: InferenceContext, ctrTracker: ConstraintTracker) { //Important: make sure we use a fresh body expression here val freshBody = freshenLocals(matchToIfThenElse(callee.body.get)) val calleeSummary = - Equals(Util.getFunctionReturnVariable(callee), freshBody) - val argmap1 = Util.formalToActual(call) + Equals(getFunctionReturnVariable(callee), freshBody) + val argmap1 = formalToActual(call) val inlinedSummary = ExpressionTransformer.normalizeExpr(replace(argmap1, calleeSummary), ctx.multOp) if (this.dumpInlinedSummary) diff --git a/src/main/scala/leon/invariant/engine/SpecInstatiator.scala b/src/main/scala/leon/invariant/engine/SpecInstatiator.scala index 9961df29357d1de52c98fe135f106993ab1e388a..e692cb6ab591cabe9732251e9edbc15f11c796cb 100644 --- a/src/main/scala/leon/invariant/engine/SpecInstatiator.scala +++ b/src/main/scala/leon/invariant/engine/SpecInstatiator.scala @@ -18,11 +18,14 @@ import scala.concurrent.duration._ import invariant.templateSolvers._ import invariant.factories._ import invariant.util._ -import invariant.util.Util._ import invariant.structure._ import FunctionUtils._ +import Util._ +import PredicateUtil._ +import ProgramUtil._ +import SolverUtil._ -class SpecInstantiator(ctx: InferenceContext, ctrTracker: ConstraintTracker) { +class SpecInstantiator(ctx: InferenceContext, program: Program, ctrTracker: ConstraintTracker) { val verbose = false @@ -31,7 +34,6 @@ class SpecInstantiator(ctx: InferenceContext, ctrTracker: ConstraintTracker) { val tru = BooleanLiteral(true) val axiomFactory = new AxiomFactory(ctx) //handles instantiation of axiomatic specification - val program = ctx.program //the guards of the set of calls that were already processed protected var exploredGuards = Set[Variable]() @@ -51,7 +53,7 @@ class SpecInstantiator(ctx: InferenceContext, ctrTracker: ConstraintTracker) { if (!disableAxioms) { //remove all multiplication if "withmult" is specified val relavantCalls = if (ctx.withmult) { - newcalls.filter(call => !Util.isMultFunctions(call.fi.tfd.fd)) + newcalls.filter(call => !isMultFunctions(call.fi.tfd.fd)) } else newcalls instantiateAxioms(formula, relavantCalls) } @@ -107,7 +109,7 @@ class SpecInstantiator(ctx: InferenceContext, ctrTracker: ConstraintTracker) { } def specForCall(call: Call): Option[Expr] = { - val argmap = Util.formalToActual(call) + val argmap = formalToActual(call) val callee = call.fi.tfd.fd if (callee.hasPostcondition) { //get the postcondition without templates @@ -130,7 +132,7 @@ class SpecInstantiator(ctx: InferenceContext, ctrTracker: ConstraintTracker) { def templateForCall(call: Call): Option[Expr] = { val callee = call.fi.tfd.fd if (callee.hasTemplate) { - val argmap = Util.formalToActual(call) + val argmap = formalToActual(call) val tempExpr = replace(argmap, callee.getTemplate) val template = if (callee.hasPrecondition) { val freshPre = replace(argmap, freshenLocals(matchToIfThenElse(callee.precondition.get))) @@ -158,7 +160,7 @@ class SpecInstantiator(ctx: InferenceContext, ctrTracker: ConstraintTracker) { val inst2 = instantiateBinaryAxioms(formula, calls) val axiomInsts = inst1 ++ inst2 - Stats.updateCounterStats(Util.atomNum(Util.createAnd(axiomInsts)), "AxiomBlowup", "VC-refinement") + Stats.updateCounterStats(atomNum(createAnd(axiomInsts)), "AxiomBlowup", "VC-refinement") if(verbose) ctx.reporter.info("Number of axiom instances: " + axiomInsts.size) if (this.debugAxiomInstantiation) { @@ -222,9 +224,9 @@ class SpecInstantiator(ctx: InferenceContext, ctrTracker: ConstraintTracker) { (call1.fi.tfd.id == call2.fi.tfd.id) && (call1 != call2) } - val product = Util.cross[Call, Call](newCallsWithAxioms, getBinaxCalls(formula.fd), Some(isInstantiable)).flatMap( + val product = cross[Call, Call](newCallsWithAxioms, getBinaxCalls(formula.fd), Some(isInstantiable)).flatMap( p => Seq((p._1, p._2), (p._2, p._1))) ++ - Util.cross[Call, Call](newCallsWithAxioms, newCallsWithAxioms, Some(isInstantiable)).map(p => (p._1, p._2)) + cross[Call, Call](newCallsWithAxioms, newCallsWithAxioms, Some(isInstantiable)).map(p => (p._1, p._2)) //ctx.reporter.info("# of pairs with axioms: "+product.size) //Stats.updateCumStats(product.size, "Call-pairs-with-axioms") diff --git a/src/main/scala/leon/invariant/engine/TemplateEnumerator.scala b/src/main/scala/leon/invariant/engine/TemplateEnumerator.scala index 17bfdee4b4d761f3d974abac06cc637166f46e98..c568910684e89d78862a516583eb0baf31174888 100644 --- a/src/main/scala/leon/invariant/engine/TemplateEnumerator.scala +++ b/src/main/scala/leon/invariant/engine/TemplateEnumerator.scala @@ -11,47 +11,49 @@ import purescala.Types._ import scala.collection.mutable.{ Set => MutableSet } import java.io._ import scala.collection.mutable.{ Set => MutableSet } -import scala.collection.mutable.{Set => MutableSet} +import scala.collection.mutable.{ Set => MutableSet } import invariant.templateSolvers._ import invariant.factories._ import invariant.util._ import invariant.structure._ +import Util._ +import PredicateUtil._ +import ProgramUtil._ /** - * An enumeration based template generator. - * Enumerates all numerical terms in some order (this enumeration is incomplete for termination). - * TODO: Feature: - * (a) allow template functions and functions with template variables ? - * (b) should we unroll algebraic data types ? - * - * The following function may potentially have complexity O(n^i) where 'n' is the number of functions - * and 'i' is the increment step - * TODO: optimize the running and also reduce the size of the input templates - * - * For now this is incomplete - */ -class TemplateEnumerator(ctx: InferenceContext) extends TemplateGenerator { - val prog = ctx.program + * An enumeration based template generator. + * Enumerates all numerical terms in some order (this enumeration is incomplete for termination). + * TODO: Feature: + * (a) allow template functions and functions with template variables ? + * (b) should we unroll algebraic data types ? + * + * The following function may potentially have complexity O(n^i) where 'n' is the number of functions + * and 'i' is the increment step + * TODO: optimize the running and also reduce the size of the input templates + * + * For now this is incomplete + */ +class TemplateEnumerator(ctx: InferenceContext, prog: Program) extends TemplateGenerator { val reporter = ctx.reporter - //create a call graph for the program - //Caution: this call-graph could be modified later while call the 'getNextTemplate' method - private val callGraph = { - val cg = CallGraphUtil.constructCallGraph(prog) - cg - } + //create a call graph for the program + //Caution: this call-graph could be modified later while call the 'getNextTemplate' method + private val callGraph = { + val cg = CallGraphUtil.constructCallGraph(prog) + cg + } - private var tempEnumMap = Map[FunDef, FunctionTemplateEnumerator]() + private var tempEnumMap = Map[FunDef, FunctionTemplateEnumerator]() - def getNextTemplate(fd : FunDef) : Expr = { - if(tempEnumMap.contains(fd)) tempEnumMap(fd).getNextTemplate() - else { - val enumerator = new FunctionTemplateEnumerator(fd, prog, ctx.enumerationRelation, callGraph, reporter) - tempEnumMap += (fd -> enumerator) - enumerator.getNextTemplate() - } - } + def getNextTemplate(fd: FunDef): Expr = { + if (tempEnumMap.contains(fd)) tempEnumMap(fd).getNextTemplate() + else { + val enumerator = new FunctionTemplateEnumerator(fd, prog, ctx.enumerationRelation, callGraph, reporter) + tempEnumMap += (fd -> enumerator) + enumerator.getNextTemplate() + } + } } /** @@ -59,15 +61,15 @@ class TemplateEnumerator(ctx: InferenceContext) extends TemplateGenerator { * 'op' is a side-effects parameter * Caution: The methods of this class has side-effects on the 'callGraph' parameter */ -class FunctionTemplateEnumerator(rootFun: FunDef, prog: Program, op: (Expr,Expr) => Expr, - callGraph : CallGraph, reporter: Reporter) { +class FunctionTemplateEnumerator(rootFun: FunDef, prog: Program, op: (Expr, Expr) => Expr, + callGraph: CallGraph, reporter: Reporter) { private val MAX_INCREMENTS = 2 private val zero = InfiniteIntegerLiteral(0) //using default op as <= or == (manually adjusted) //private val op = LessEquals - //LessThan - //LessEquals - //Equals.apply _ + //LessThan + //LessEquals + //Equals.apply _ private var currTemp: Expr = null private var incrStep: Int = 0 private var typeTermMap = Map[TypeTree, MutableSet[Expr]]() @@ -82,11 +84,10 @@ class FunctionTemplateEnumerator(rootFun: FunDef, prog: Program, op: (Expr,Expr) def getNextTemplate(): Expr = { //println("Getting next template for function: "+fd.id) - if (incrStep == MAX_INCREMENTS){ + if (incrStep == MAX_INCREMENTS) { //exhausted the templates, so return op(currTemp, zero) - } - else { + } else { incrStep += 1 @@ -104,7 +105,7 @@ class FunctionTemplateEnumerator(rootFun: FunDef, prog: Program, op: (Expr,Expr) } }) - val resVar = Util.getFunctionReturnVariable(rootFun) + val resVar = getFunctionReturnVariable(rootFun) if (newTerms.contains(rootFun.returnType)) { newTerms(rootFun.returnType).add(resVar) } else { @@ -152,10 +153,10 @@ class FunctionTemplateEnumerator(rootFun: FunDef, prog: Program, op: (Expr,Expr) //return all the integer valued terms of newTerms //++ newTerms.getOrElse(Int32Type, Seq[Expr]()) (for now not handling int 32 terms) val numericTerms = (newTerms.getOrElse(RealType, Seq[Expr]()) ++ newTerms.getOrElse(IntegerType, Seq[Expr]())).toSeq - if(!numericTerms.isEmpty) { + if (!numericTerms.isEmpty) { //create a linear combination of intTerms val newTemp = numericTerms.foldLeft(null: Expr)((acc, t: Expr) => { - val summand = Times(t, TemplateIdFactory.freshTemplateVar() : Expr) + val summand = Times(t, TemplateIdFactory.freshTemplateVar(): Expr) if (acc == null) summand else Plus(summand, acc) diff --git a/src/main/scala/leon/invariant/engine/UnfoldingTemplateSolver.scala b/src/main/scala/leon/invariant/engine/UnfoldingTemplateSolver.scala index 5a806fa3cf3d0786637f7b80db4ab9b710c58eab..f54eeef6249651ee7d8ab1517a1982f9e69f6694 100644 --- a/src/main/scala/leon/invariant/engine/UnfoldingTemplateSolver.scala +++ b/src/main/scala/leon/invariant/engine/UnfoldingTemplateSolver.scala @@ -8,6 +8,7 @@ import purescala.Expressions._ import purescala.ExprOps._ import purescala.Extractors._ import purescala.Types._ +import purescala.DefOps._ import solvers._ import solvers.z3.FairZ3Solver import java.io._ @@ -17,11 +18,14 @@ import scala.reflect.runtime.universe import invariant.templateSolvers._ import invariant.factories._ import invariant.util._ -import invariant.util.Util._ import invariant.structure._ import transformations._ import FunctionUtils._ import leon.invariant.templateSolvers.ExtendedUFSolver +import Util._ +import PredicateUtil._ +import ProgramUtil._ +import SolverUtil._ /** * @author ravi @@ -35,14 +39,13 @@ trait FunctionTemplateSolver { def apply(): Option[InferResult] } -class UnfoldingTemplateSolver(ctx: InferenceContext, rootFd: FunDef) extends FunctionTemplateSolver { +class UnfoldingTemplateSolver(ctx: InferenceContext, program: Program, rootFd: FunDef) extends FunctionTemplateSolver { val reporter = ctx.reporter - val program = ctx.program val debugVCs = false - lazy val constTracker = new ConstraintTracker(ctx, rootFd) - lazy val templateSolver = TemplateSolverFactory.createTemplateSolver(ctx, constTracker, rootFd) + lazy val constTracker = new ConstraintTracker(ctx, program, rootFd) + lazy val templateSolver = TemplateSolverFactory.createTemplateSolver(ctx, program, constTracker, rootFd) def constructVC(funDef: FunDef): (Expr, Expr) = { val body = funDef.body.get @@ -55,15 +58,18 @@ class UnfoldingTemplateSolver(ctx: InferenceContext, rootFd: FunDef) extends Fun And(matchToIfThenElse(funDef.precondition.get), plainBody) } else plainBody - val fullPost = matchToIfThenElse(if (funDef.hasTemplate) - if (ctx.toVerifyPostFor.contains(funDef.id.name)) - And(funDef.getPostWoTemplate, funDef.getTemplate) - else - funDef.getTemplate - else if (ctx.toVerifyPostFor.contains(funDef.id.name)) - funDef.getPostWoTemplate - else - BooleanLiteral(true)) + val funName = fullName(funDef, useUniqueIds = false)(program) + val fullPost = matchToIfThenElse( + if (funDef.hasTemplate) { + // if the postcondition is verified do not include it in the sequent + if (ctx.isFunctionPostVerified(funName)) + funDef.getTemplate + else + And(funDef.getPostWoTemplate, funDef.getTemplate) + } else if (!ctx.isFunctionPostVerified(funName)) + funDef.getPostWoTemplate + else + BooleanLiteral(true)) (bodyExpr, fullPost) } @@ -80,41 +86,47 @@ class UnfoldingTemplateSolver(ctx: InferenceContext, rootFd: FunDef) extends Fun var toRefineCalls: Option[Set[Call]] = None var infRes: Option[InferResult] = None do { - Stats.updateCounter(1, "VC-refinement") - /* uncomment if we want to bound refinements - * if (refinementStep >= 5) - throw new IllegalStateException("Done 4 refinements")*/ - val refined = - if (refinementStep >= 1) { - reporter.info("- More unrollings for invariant inference") - - val toUnrollCalls = if (ctx.targettedUnroll) toRefineCalls else None - val unrolledCalls = constTracker.refineVCs(toUnrollCalls) - if (unrolledCalls.isEmpty) { - reporter.info("- Cannot do more unrollings, reached unroll bound") - false - } else true - } else { - constTracker.initialize - true - } - refinementStep += 1 infRes = - if (!refined) + if(ctx.abort) Some(InferResult(false, None, List())) - else { - //solve for the templates in this unroll step - templateSolver.solveTemplates() match { - case (Some(model), callsInPath) => - toRefineCalls = callsInPath - //Validate the model here - instantiateAndValidateModel(model, constTracker.getFuncs.toSeq) - Some(InferResult(true, Some(model), - constTracker.getFuncs.toList)) - case (None, callsInPath) => - toRefineCalls = callsInPath - //here, we do not know if the template is solvable or not, we need to do more unrollings. - None + else{ + Stats.updateCounter(1, "VC-refinement") + /* + * uncomment if we want to bound refinements + * if (refinementStep >= 5) + * throw new IllegalStateException("Done 4 refinements") + */ + val refined = + if (refinementStep >= 1) { + reporter.info("- More unrollings for invariant inference") + + val toUnrollCalls = if (ctx.targettedUnroll) toRefineCalls else None + val unrolledCalls = constTracker.refineVCs(toUnrollCalls) + if (unrolledCalls.isEmpty) { + reporter.info("- Cannot do more unrollings, reached unroll bound") + false + } else true + } else { + constTracker.initialize + true + } + refinementStep += 1 + if (!refined) + Some(InferResult(false, None, List())) + else { + //solve for the templates in this unroll step + templateSolver.solveTemplates() match { + case (Some(model), callsInPath) => + toRefineCalls = callsInPath + //Validate the model here + instantiateAndValidateModel(model, constTracker.getFuncs.toSeq) + Some(InferResult(true, Some(model), + constTracker.getFuncs.toList)) + case (None, callsInPath) => + toRefineCalls = callsInPath + //here, we do not know if the template is solvable or not, we need to do more unrollings. + None + } } } } while (!infRes.isDefined) @@ -175,7 +187,7 @@ class UnfoldingTemplateSolver(ctx: InferenceContext, rootFd: FunDef) extends Fun //create a fundef for each function in the program //note: mult functions are also copied val newFundefs = program.definedFunctions.collect { - case fd @ _ => { //if !Util.isMultFunctions(fd) + case fd @ _ => { //if !isMultFunctions(fd) val newfd = new FunDef(FreshIdentifier(fd.id.name, Untyped, false), fd.tparams, fd.params, fd.returnType) (fd, newfd) } @@ -226,7 +238,7 @@ class UnfoldingTemplateSolver(ctx: InferenceContext, rootFd: FunDef) extends Fun newfd.addFlags(fd.flags) }) - val augmentedProg = Util.copyProgram(program, (defs: Seq[Definition]) => defs.collect { + val augmentedProg = copyProgram(program, (defs: Seq[Definition]) => defs.collect { case fd: FunDef if (newFundefs.contains(fd)) => newFundefs(fd) case d if (!d.isInstanceOf[FunDef]) => d }) diff --git a/src/main/scala/leon/invariant/factories/AxiomFactory.scala b/src/main/scala/leon/invariant/factories/AxiomFactory.scala index 27c7e44c708e2d3126166afc7417ddfab858bc1d..6fdf7fa633def45017de8df5fbab4250ce70928e 100644 --- a/src/main/scala/leon/invariant/factories/AxiomFactory.scala +++ b/src/main/scala/leon/invariant/factories/AxiomFactory.scala @@ -18,6 +18,8 @@ import invariant.engine._ import invariant.util._ import invariant.structure._ import FunctionUtils._ +import Util._ +import PredicateUtil._ class AxiomFactory(ctx : InferenceContext) { @@ -78,7 +80,7 @@ class AxiomFactory(ctx : InferenceContext) { lesse +: acc }) val conseq = LessEquals(call1.retexpr, call2.retexpr) - (Util.createAnd(ants), conseq) + (createAnd(ants), conseq) } //this is applicable only to binary operations diff --git a/src/main/scala/leon/invariant/factories/TemplateFactory.scala b/src/main/scala/leon/invariant/factories/TemplateFactory.scala index 68080a79e8106decdeb6c809d0601c10be637e68..ab1f51359151de13ef8cd506f4f1c9da9472c208 100644 --- a/src/main/scala/leon/invariant/factories/TemplateFactory.scala +++ b/src/main/scala/leon/invariant/factories/TemplateFactory.scala @@ -17,6 +17,9 @@ import invariant.engine._ import invariant.util._ import invariant.structure._ import FunctionUtils._ +import Util._ +import PredicateUtil._ +import ProgramUtil._ object TemplateIdFactory { //a set of template ids @@ -70,7 +73,7 @@ class TemplateFactory(tempGen : Option[TemplateGenerator], prog: Program, report private var templateMap = { //initialize the template map with predefined user maps var muMap = MutableMap[FunDef, Expr]() - Util.functionsWOFields(prog.definedFunctions).foreach { fd => + functionsWOFields(prog.definedFunctions).foreach { fd => val tmpl = fd.template if (tmpl.isDefined) { muMap.update(fd, tmpl.get) @@ -90,8 +93,8 @@ class TemplateFactory(tempGen : Option[TemplateGenerator], prog: Program, report def getDefaultTemplate(fd : FunDef): Expr = { //just consider all the arguments, return values that are integers - val baseTerms = fd.params.filter((vardecl) => Util.isNumericType(vardecl.getType)).map(_.toVariable) ++ - (if(Util.isNumericType(fd.returnType)) Seq(Util.getFunctionReturnVariable(fd)) + val baseTerms = fd.params.filter((vardecl) => isNumericType(vardecl.getType)).map(_.toVariable) ++ + (if(isNumericType(fd.returnType)) Seq(getFunctionReturnVariable(fd)) else Seq()) val lhs = baseTerms.foldLeft(TemplateIdFactory.freshTemplateVar() : Expr)((acc, t)=> { @@ -101,7 +104,6 @@ class TemplateFactory(tempGen : Option[TemplateGenerator], prog: Program, report tempExpr } - /** * Constructs a template using a mapping from the formals to actuals. * Uses default template if a template does not exist for the function and no template generator is provided. @@ -123,7 +125,6 @@ class TemplateFactory(tempGen : Option[TemplateGenerator], prog: Program, report replace(argmap,templateMap(fd)) } - /** * Refines the templates of the functions that were assigned templates using the template generator. */ diff --git a/src/main/scala/leon/invariant/factories/TemplateInstantiator.scala b/src/main/scala/leon/invariant/factories/TemplateInstantiator.scala index 5944f68bd9faa11ddfa5d26d8d057a086aea080c..ccaabc57aabecb9fa22fd94a5972c1a554196729 100644 --- a/src/main/scala/leon/invariant/factories/TemplateInstantiator.scala +++ b/src/main/scala/leon/invariant/factories/TemplateInstantiator.scala @@ -15,26 +15,29 @@ import invariant.util._ import invariant.structure._ import leon.solvers.Model import leon.invariant.util.RealValuedExprEvaluator +import Util._ +import PredicateUtil._ +import ProgramUtil._ object TemplateInstantiator { - /** + /** * Computes the invariant for all the procedures given a mapping for the * template variables. * (Undone) If the mapping does not have a value for an id, then the id is bound to the simplest value */ - def getAllInvariants(model: Model, templates :Map[FunDef, Expr]): Map[FunDef, Expr] = { + def getAllInvariants(model: Model, templates: Map[FunDef, Expr], prettyInv: Boolean = false): Map[FunDef, Expr] = { val invs = templates.map((pair) => { val (fd, t) = pair //flatten the template val freevars = variablesOf(t) val template = ExpressionTransformer.FlattenFunction(t) - val tempvars = Util.getTemplateVars(template) + val tempvars = getTemplateVars(template) val tempVarMap: Map[Expr, Expr] = tempvars.map((v) => { (v, model(v.id)) }).toMap - val instTemplate = instantiate(template, tempVarMap) + val instTemplate = instantiate(template, tempVarMap, prettyInv) //now unflatten it val comprTemp = ExpressionTransformer.unFlatten(instTemplate, freevars) (fd, comprTemp) @@ -46,40 +49,36 @@ object TemplateInstantiator { * Instantiates templated subexpressions of the given expression (expr) using the given mapping for the template variables. * The instantiation also takes care of converting the rational coefficients to integer coefficients. */ - def instantiate(expr: Expr, tempVarMap: Map[Expr, Expr]): Expr = { + def instantiate(expr: Expr, tempVarMap: Map[Expr, Expr], prettyInv: Boolean = false): Expr = { //do a simple post transform and replace the template vars by their values val inv = simplePostTransform((tempExpr: Expr) => tempExpr match { case e @ Operator(Seq(lhs, rhs), op) if ((e.isInstanceOf[Equals] || e.isInstanceOf[LessThan] || e.isInstanceOf[LessEquals] || e.isInstanceOf[GreaterThan] || e.isInstanceOf[GreaterEquals]) && - !Util.getTemplateVars(tempExpr).isEmpty) => { - - //println("Template Expression: "+tempExpr) + !getTemplateVars(tempExpr).isEmpty) => { val linearTemp = LinearConstraintUtil.exprToTemplate(tempExpr) - // println("MODEL\n" + tempVarMap) - instantiateTemplate(linearTemp, tempVarMap) + instantiateTemplate(linearTemp, tempVarMap, prettyInv) } case _ => tempExpr })(expr) inv } - - def validateLiteral(e : Expr) = e match { + def validateLiteral(e: Expr) = e match { case FractionalLiteral(num, denom) => { if (denom == 0) - throw new IllegalStateException("Denominator is zero !! " +e) + throw new IllegalStateException("Denominator is zero !! " + e) if (denom < 0) throw new IllegalStateException("Denominator is negative: " + denom) true } - case IntLiteral(_) => true + case IntLiteral(_) => true case InfiniteIntegerLiteral(_) => true - case _ => throw new IllegalStateException("Not a real literal: " + e) + case _ => throw new IllegalStateException("Not a real literal: " + e) } - def instantiateTemplate(linearTemp: LinearTemplate, tempVarMap: Map[Expr, Expr]): Expr = { + def instantiateTemplate(linearTemp: LinearTemplate, tempVarMap: Map[Expr, Expr], prettyInv: Boolean = false): Expr = { val bigone = BigInt(1) val coeffMap = linearTemp.coeffTemplate.map((entry) => { val (term, coeffTemp) = entry @@ -90,20 +89,19 @@ object TemplateInstantiator { (term -> coeff) }) - val const = if (linearTemp.constTemplate.isDefined){ + val const = if (linearTemp.constTemplate.isDefined) { val constE = replace(tempVarMap, linearTemp.constTemplate.get) val constV = RealValuedExprEvaluator.evaluate(constE) validateLiteral(constV) Some(constV) - } - else None + } else None val realValues: Seq[Expr] = coeffMap.values.toSeq ++ { if (const.isDefined) Seq(const.get) else Seq() } //the coefficients could be fractions ,so collect all the denominators val getDenom = (t: Expr) => t match { case FractionalLiteral(num, denum) => denum - case _ => bigone + case _ => bigone } val denoms = realValues.foldLeft(Set[BigInt]())((acc, entry) => { acc + getDenom(entry) }) @@ -112,8 +110,8 @@ object TemplateInstantiator { val gcd = denoms.foldLeft(bigone)((acc, d) => acc.gcd(d)) val lcm = denoms.foldLeft(BigInt(1))((acc, d) => { val product = (acc * d) - if(product % gcd == 0) - product/ gcd + if (product % gcd == 0) + product / gcd else product }) @@ -129,6 +127,8 @@ object TemplateInstantiator { val intConst = if (const.isDefined) Some(scaleNum(const.get)) else None val linearCtr = new LinearConstraint(linearTemp.op, intCoeffMap, intConst) - linearCtr.toExpr + if (prettyInv) + linearCtr.toPrettyExpr + else linearCtr.toExpr } } \ No newline at end of file diff --git a/src/main/scala/leon/invariant/factories/TemplateSolverFactory.scala b/src/main/scala/leon/invariant/factories/TemplateSolverFactory.scala index 25abefa22e51479c6d56fffd2c9e8a91a8b0bfec..469317998a98f979a9a90770fd9c902544eee584 100644 --- a/src/main/scala/leon/invariant/factories/TemplateSolverFactory.scala +++ b/src/main/scala/leon/invariant/factories/TemplateSolverFactory.scala @@ -17,27 +17,27 @@ import leon.solvers.Model object TemplateSolverFactory { - def createTemplateSolver(ctx: InferenceContext, ctrack: ConstraintTracker, rootFun: FunDef, + def createTemplateSolver(ctx: InferenceContext, prog: Program, ctrack: ConstraintTracker, rootFun: FunDef, // options to solvers minopt: Option[(Expr, Model) => Model] = None, bound: Option[Int] = None): TemplateSolver = { if (ctx.useCegis) { // TODO: find a better way to specify CEGIS total time bound - new CegisSolver(ctx, rootFun, ctrack, 10000, bound) + new CegisSolver(ctx, prog, rootFun, ctrack, 10000, bound) } else { val minimizer = if (ctx.tightBounds && rootFun.hasTemplate) { if (minopt.isDefined) minopt else { //TODO: need to assert that the templates are resource templates - Some((new Minimizer(ctx)).tightenTimeBounds(rootFun.getTemplate) _) + Some((new Minimizer(ctx, prog)).tightenTimeBounds(rootFun.getTemplate) _) } } else None if (ctx.withmult) { - new NLTemplateSolverWithMult(ctx, rootFun, ctrack, minimizer) + new NLTemplateSolverWithMult(ctx, prog, rootFun, ctrack, minimizer) } else { - new NLTemplateSolver(ctx, rootFun, ctrack, minimizer) + new NLTemplateSolver(ctx, prog, rootFun, ctrack, minimizer) } } } diff --git a/src/main/scala/leon/invariant/structure/Constraint.scala b/src/main/scala/leon/invariant/structure/Constraint.scala index 2a6e32ce40f8882ec4e240578bd1a60b35eaee0e..9d2490fe5f8b61b6847636007c61d6ba3c0dba34 100644 --- a/src/main/scala/leon/invariant/structure/Constraint.scala +++ b/src/main/scala/leon/invariant/structure/Constraint.scala @@ -17,6 +17,8 @@ import evaluators._ import java.io._ import solvers.z3.UninterpretedZ3Solver import invariant.util._ +import Util._ +import PredicateUtil._ trait Constraint { def toExpr: Expr @@ -41,19 +43,15 @@ class LinearTemplate(oper: Seq[Expr] => Expr, val coeffTemplate = { //assert if the coefficients are templated expressions assert(coeffTemp.values.foldLeft(true)((acc, e) => { - acc && Util.isTemplateExpr(e) + acc && isTemplateExpr(e) })) - - //print the template mapping - /*println("Coeff Mapping: "+coeffTemp) - println("Constant: "+constTemplate)*/ coeffTemp } val constTemplate = { assert(constTemp match { case None => true - case Some(e) => Util.isTemplateExpr(e) + case Some(e) => isTemplateExpr(e) }) constTemp } @@ -70,13 +68,11 @@ class LinearTemplate(oper: Seq[Expr] => Expr, else Plus(lhs, constTemp.get) } else lhs val expr = oper(Seq(lhs, zero)) - //assert(expr.isInstanceOf[Equals] || expr.isInstanceOf[LessThan] || expr.isInstanceOf[GreaterThan] - // || expr.isInstanceOf[LessEquals] || expr.isInstanceOf[GreaterEquals]) expr } def templateVars: Set[Variable] = { - Util.getTemplateVars(template) + getTemplateVars(template) } def coeffEntryToString(coeffEntry: (Expr, Expr)): String = { @@ -94,6 +90,45 @@ class LinearTemplate(oper: Seq[Expr] => Expr, override def toExpr: Expr = template + /** + * Converts the template to a more human readable form + * by group positive (and negative) terms together + */ + def toPrettyExpr = { + val (lhsCoeff, rhsCoeff) = coeffTemplate.partition { + case (term, InfiniteIntegerLiteral(v)) => + v >= 0 + case _ => true + } + var lhsExprs: Seq[Expr] = lhsCoeff.map(e => Times(e._2, e._1)).toSeq + var rhsExprs: Seq[Expr] = rhsCoeff.map { + case (term, InfiniteIntegerLiteral(v)) => + Times(InfiniteIntegerLiteral(-v), term) // make the coeff +ve + }.toSeq + constTemplate match { + case Some(InfiniteIntegerLiteral(v)) if v < 0 => + rhsExprs :+= InfiniteIntegerLiteral(-v) + case Some(c) => + lhsExprs :+= c + case _ => Nil + } + val lhsExprOpt = ((None: Option[Expr]) /: lhsExprs) { + case (acc, minterm) => + if (acc.isDefined) + Some(Plus(acc.get, minterm)) + else Some(minterm) + } + val rhsExprOpt = ((None: Option[Expr]) /: rhsExprs) { + case (acc, minterm) => + if (acc.isDefined) + Some(Plus(acc.get, minterm)) + else Some(minterm) + } + val lhs = lhsExprOpt.getOrElse(InfiniteIntegerLiteral(0)) + val rhs = rhsExprOpt.getOrElse(InfiniteIntegerLiteral(0)) + oper(Seq(lhs, rhs)) + } + override def toString(): String = { val coeffStr = if (coeffTemplate.isEmpty) "" @@ -143,7 +178,6 @@ class LinearConstraint(opr: Seq[Expr] => Expr, cMap: Map[Expr, Expr], constant: assert(cMap.values.foldLeft(true)((acc, e) => { acc && variablesOf(e).isEmpty })) - //TODO: here we should try to simplify the constant expressions cMap } diff --git a/src/main/scala/leon/invariant/structure/Formula.scala b/src/main/scala/leon/invariant/structure/Formula.scala index 0fedbc7057b6172b43c405caa6a976a24541c81a..daed61ff679cba61666a6c227005c103a31d8d01 100644 --- a/src/main/scala/leon/invariant/structure/Formula.scala +++ b/src/main/scala/leon/invariant/structure/Formula.scala @@ -16,6 +16,8 @@ import solvers.z3._ import invariant.engine._ import invariant.util._ import leon.solvers.Model +import Util._ +import PredicateUtil._ /** * Data associated with a call @@ -99,12 +101,12 @@ class Formula(val fd: FunDef, initexpr: Expr, ctx: InferenceContext) { }) //create a temporary for Or val gor = TVarFactory.createTemp("b", BooleanType).toVariable - val newor = Util.createOr(newargs) + val newor = createOr(newargs) conjuncts += (gor -> newor) gor } case And(args) => { - val newargs = args.map(arg => if (Util.getTemplateVars(e).isEmpty) { + val newargs = args.map(arg => if (getTemplateVars(e).isEmpty) { arg } else { //if the expression has template variables then we separate it using guards @@ -114,7 +116,7 @@ class Formula(val fd: FunDef, initexpr: Expr, ctx: InferenceContext) { disjuncts += (g -> ctrs) g }) - Util.createAnd(newargs) + createAnd(newargs) } case _ => e })(ExpressionTransformer.simplify(simplifyArithmetic( @@ -209,24 +211,24 @@ class Formula(val fd: FunDef, initexpr: Expr, ctx: InferenceContext) { var rest = Seq[Expr]() disjuncts.foreach(entry => { val (g,ctrs) = entry - val ctrExpr = combiningOp(g,Util.createAnd(ctrs.map(_.toExpr))) - if(Util.getTemplateVars(ctrExpr).isEmpty) + val ctrExpr = combiningOp(g,createAnd(ctrs.map(_.toExpr))) + if(getTemplateVars(ctrExpr).isEmpty) rest :+= ctrExpr else paramPart :+= ctrExpr }) val conjs = conjuncts.map((entry) => combiningOp(entry._1, entry._2)).toSeq ++ roots - (Util.createAnd(paramPart), Util.createAnd(rest ++ conjs ++ roots)) + (createAnd(paramPart), createAnd(rest ++ conjs ++ roots)) } def toExpr : Expr={ val disjs = disjuncts.map((entry) => { val (g,ctrs) = entry - combiningOp(g, Util.createAnd(ctrs.map(_.toExpr))) + combiningOp(g, createAnd(ctrs.map(_.toExpr))) }).toSeq val conjs = conjuncts.map((entry) => combiningOp(entry._1, entry._2)).toSeq - Util.createAnd(disjs ++ conjs ++ roots) + createAnd(disjs ++ conjs ++ roots) } //unpack the disjunct and conjuncts by removing all guards @@ -238,7 +240,7 @@ class Formula(val fd: FunDef, initexpr: Expr, ctx: InferenceContext) { case BoolConstraint(g@Variable(_)) if conjuncts.contains(g) => conjuncts(g) case ctr@_ => ctr.toExpr }) - (g, Util.createAnd(newctrs)) + (g, createAnd(newctrs)) }) val rootexprs = roots.map(_ match { case g@Variable(_) if conjuncts.contains(g) => conjuncts(g) @@ -266,13 +268,13 @@ class Formula(val fd: FunDef, initexpr: Expr, ctx: InferenceContext) { unpackedDisjs = newDisjs } //replace all the 'guards' in root using 'unpackedDisjs' - replace(unpackedDisjs, Util.createAnd(rootexprs)) + replace(unpackedDisjs, createAnd(rootexprs)) } override def toString : String = { val disjStrs = disjuncts.map((entry) => { val (g,ctrs) = entry - simplifyArithmetic(combiningOp(g, Util.createAnd(ctrs.map(_.toExpr)))).toString + simplifyArithmetic(combiningOp(g, createAnd(ctrs.map(_.toExpr)))).toString }).toSeq val conjStrs = conjuncts.map((entry) => combiningOp(entry._1, entry._2).toString).toSeq val rootStrs = roots.map(_.toString) diff --git a/src/main/scala/leon/invariant/structure/FunctionUtils.scala b/src/main/scala/leon/invariant/structure/FunctionUtils.scala index f676520cd6907114f3130316cb9e3c38955a02d8..a0acdcda7388ee4b6971106cf78cdb49c42768a4 100644 --- a/src/main/scala/leon/invariant/structure/FunctionUtils.scala +++ b/src/main/scala/leon/invariant/structure/FunctionUtils.scala @@ -10,6 +10,8 @@ import purescala.Types._ import invariant.factories._ import invariant.util._ import Util._ +import PredicateUtil._ +import ProgramUtil._ import scala.language.implicitConversions /** @@ -107,8 +109,8 @@ object FunctionUtils { case a if exists(isQMark)(a) => true case _ => false } - //println(s"Otherpreds: $otherPreds ${qmarksToTmplFunction(Util.createAnd(tempExprs))}") - Util.createAnd(otherPreds :+ qmarksToTmplFunction(Util.createAnd(tempExprs))) + //println(s"Otherpreds: $otherPreds ${qmarksToTmplFunction(createAnd(tempExprs))}") + createAnd(otherPreds :+ qmarksToTmplFunction(createAnd(tempExprs))) case pb if exists(isQMark)(pb) => qmarksToTmplFunction(pb) case other => other diff --git a/src/main/scala/leon/invariant/structure/LinearConstraintUtil.scala b/src/main/scala/leon/invariant/structure/LinearConstraintUtil.scala index 102c2a088446102f6d0f9a1403cd106009fdfb11..7910df7230e82f7ac8b695bc8089fcd64c005e3a 100644 --- a/src/main/scala/leon/invariant/structure/LinearConstraintUtil.scala +++ b/src/main/scala/leon/invariant/structure/LinearConstraintUtil.scala @@ -14,6 +14,8 @@ import java.io._ import invariant.util._ import BigInt._ import Constructors._ +import Util._ +import PredicateUtil._ class NotImplementedException(message: String) extends RuntimeException(message) { @@ -105,7 +107,7 @@ object LinearConstraintUtil { //handle each minterm minterms.foreach((minterm: Expr) => minterm match { - case _ if (Util.isTemplateExpr(minterm)) => { + case _ if (isTemplateExpr(minterm)) => { addConstant(minterm) } case Times(e1, e2) => { @@ -117,7 +119,7 @@ object LinearConstraintUtil { } e1 match { //case c @ InfiniteIntegerLiteral(_) => addCoefficient(e2, c) - case _ if (Util.isTemplateExpr(e1)) => { + case _ if (isTemplateExpr(e1)) => { addCoefficient(e2, e1) } case _ => throw new IllegalStateException("Coefficient not a constant or template expression: " + e1) @@ -219,7 +221,7 @@ object LinearConstraintUtil { || e.isInstanceOf[GreaterEquals])) => { //check if the expression has real valued sub-expressions - val isReal = Util.hasReals(e1) || Util.hasReals(e2) + val isReal = hasReals(e1) || hasReals(e2) //doing something else ... ? // println("[DEBUG] Expr 1 " + e1 + " of type " + e1.getType + " and Expr 2 " + e2 + " of type" + e2.getType) val (newe, newop) = e match { @@ -256,9 +258,9 @@ object LinearConstraintUtil { case Times(_, _) | RealTimes(_, _) => { val Operator(Seq(e1, e2), op) = inExpr val (r1, r2) = (mkLinearRecur(e1), mkLinearRecur(e2)) - if(Util.isTemplateExpr(r1)) { + if(isTemplateExpr(r1)) { PushTimes(r1, r2) - } else if(Util.isTemplateExpr(r2)){ + } else if(isTemplateExpr(r2)){ PushTimes(r2, r1) } else throw new IllegalStateException("Expression not linear: " + Times(r1, r2)) @@ -486,4 +488,3 @@ object LinearConstraintUtil { } } } - diff --git a/src/main/scala/leon/invariant/templateSolvers/CegisSolver.scala b/src/main/scala/leon/invariant/templateSolvers/CegisSolver.scala index 305bd98e34383a572c20618c1e429722f0c91341..98f162fea4b2974702c4672274ed6d8f86075690 100644 --- a/src/main/scala/leon/invariant/templateSolvers/CegisSolver.scala +++ b/src/main/scala/leon/invariant/templateSolvers/CegisSolver.scala @@ -17,18 +17,19 @@ import invariant.util._ import invariant.structure._ import invariant.structure.FunctionUtils._ import leon.invariant.util.RealValuedExprEvaluator._ +import Util._ +import PredicateUtil._ +import SolverUtil._ -class CegisSolver(ctx: InferenceContext, - rootFun: FunDef, - ctrTracker: ConstraintTracker, - timeout: Int, - bound: Option[Int] = None) extends TemplateSolver(ctx, rootFun, ctrTracker) { +class CegisSolver(ctx: InferenceContext, program: Program, + rootFun: FunDef, ctrTracker: ConstraintTracker, + timeout: Int, bound: Option[Int] = None) extends TemplateSolver(ctx, rootFun, ctrTracker) { override def solve(tempIds: Set[Identifier], funcVCs: Map[FunDef, Expr]): (Option[Model], Option[Set[Call]]) = { val initCtr = if (bound.isDefined) { //use a predefined bound on the template variables - Util.createAnd(tempIds.map((id) => { + createAnd(tempIds.map((id) => { val idvar = id.toVariable And(Implies(LessThan(idvar, realzero), GreaterEquals(idvar, InfiniteIntegerLiteral(-bound.get))), Implies(GreaterEquals(idvar, realzero), LessEquals(idvar, InfiniteIntegerLiteral(bound.get)))) @@ -37,10 +38,10 @@ class CegisSolver(ctx: InferenceContext, } else tru val funcs = funcVCs.keys - val formula = Util.createOr(funcs.map(funcVCs.apply _).toSeq) + val formula = createOr(funcs.map(funcVCs.apply _).toSeq) //using reals with bounds does not converge and also results in overflow - val (res, _, model) = (new CegisCore(ctx, timeout, this)).solve(tempIds, formula, initCtr, solveAsInt = true) + val (res, _, model) = (new CegisCore(ctx, program, timeout, this)).solve(tempIds, formula, initCtr, solveAsInt = true) res match { case Some(true) => (Some(model), None) case Some(false) => (None, None) //no solution exists @@ -51,7 +52,7 @@ class CegisSolver(ctx: InferenceContext, } class CegisCore(ctx: InferenceContext, - timeout: Int, + program: Program, timeout: Int, cegisSolver: TemplateSolver) { val fls = BooleanLiteral(false) @@ -60,7 +61,6 @@ class CegisCore(ctx: InferenceContext, val timeoutMillis = timeout.toLong * 1000 val dumpCandidateInvs = true val minimizeSum = false - val program = ctx.program val context = ctx.leonContext val reporter = context.reporter @@ -80,7 +80,7 @@ class CegisCore(ctx: InferenceContext, //for some sanity checks var oldModels = Set[Expr]() def addModel(m: Model) = { - val mexpr = Util.modelToExpr(m) + val mexpr = modelToExpr(m) if (oldModels.contains(mexpr)) throw new IllegalStateException("repeating model !!:" + m) else oldModels += mexpr @@ -94,7 +94,7 @@ class CegisCore(ctx: InferenceContext, val tempVarSum = if (minimizeSum) { //compute the sum of the tempIds - val rootTempIds = Util.getTemplateVars(cegisSolver.rootFun.getTemplate) + val rootTempIds = getTemplateVars(cegisSolver.rootFun.getTemplate) if (rootTempIds.size >= 1) { rootTempIds.tail.foldLeft(rootTempIds.head.asInstanceOf[Expr])((acc, tvar) => Plus(acc, tvar)) } else zero @@ -102,7 +102,7 @@ class CegisCore(ctx: InferenceContext, //convert initCtr to a real-constraint val initRealCtr = ExpressionTransformer.IntLiteralToReal(initCtr) - if (Util.hasInts(initRealCtr)) + if (hasInts(initRealCtr)) throw new IllegalStateException("Initial constraints have integer terms: " + initRealCtr) def cegisRec(model: Model, prevctr: Expr): (Option[Boolean], Expr, Model) = { @@ -128,7 +128,7 @@ class CegisCore(ctx: InferenceContext, val spuriousTempIds = variablesOf(instFormula).intersect(TemplateIdFactory.getTemplateIds) if (!spuriousTempIds.isEmpty) throw new IllegalStateException("Found a template variable in instFormula: " + spuriousTempIds) - if (Util.hasReals(instFormula)) + if (hasReals(instFormula)) throw new IllegalStateException("Reals in instFormula: " + instFormula) //println("solving instantiated vcs...") @@ -166,7 +166,7 @@ class CegisCore(ctx: InferenceContext, //println("Newctr: " +newctr) if (ctx.dumpStats) { - Stats.updateCounterStats(Util.atomNum(newctr), "CegisTemplateCtrs", "CegisIters") + Stats.updateCounterStats(atomNum(newctr), "CegisTemplateCtrs", "CegisIters") } //println("solving template constraints...") @@ -188,7 +188,7 @@ class CegisCore(ctx: InferenceContext, (res1, rti.unmapModel(intModel)) } else { - /*if(InvariantUtil.hasInts(tempctrs)) + /*if(InvarianthasInts(tempctrs)) throw new IllegalStateException("Template constraints have integer terms: " + tempctrs)*/ if (minimizeSum) { minimizeReals(And(newctr, initRealCtr), tempVarSum) diff --git a/src/main/scala/leon/invariant/templateSolvers/FarkasLemmaSolver.scala b/src/main/scala/leon/invariant/templateSolvers/FarkasLemmaSolver.scala index 6e80c47496aa59f5b9b5c6a01085ff0ec877f298..26710b7111b301748340d738fd35a1465364c9da 100644 --- a/src/main/scala/leon/invariant/templateSolvers/FarkasLemmaSolver.scala +++ b/src/main/scala/leon/invariant/templateSolvers/FarkasLemmaSolver.scala @@ -14,8 +14,12 @@ import solvers.SimpleSolverAPI import scala.collection.mutable.{ Map => MutableMap } import invariant.engine._ import invariant.factories._ -import invariant.util.Util._ import invariant.util._ +import Util._ +import ProgramUtil._ +import SolverUtil._ +import PredicateUtil._ +import TimerUtil._ import invariant.structure._ import leon.solvers.TimeoutSolver import leon.solvers.SolverFactory @@ -24,7 +28,7 @@ import leon.solvers.Model import leon.solvers.smtlib.SMTLIBZ3Solver import leon.invariant.util.RealValuedExprEvaluator._ -class FarkasLemmaSolver(ctx: InferenceContext) { +class FarkasLemmaSolver(ctx: InferenceContext, program: Program) { //debug flags val verbose = true @@ -39,9 +43,9 @@ class FarkasLemmaSolver(ctx: InferenceContext) { val useIncrementalSolvingForNLctrs = false //note: NLsat doesn't support incremental solving. It starts from sratch even in incremental solving. val leonctx = ctx.leonContext - val program = ctx.program val reporter = ctx.reporter - val timeout = ctx.timeout + val timeout = ctx.vcTimeout // Note: we are using vcTimeout here as well + /** * This procedure produces a set of constraints that need to be satisfiable for the * conjunction ants and conseqs to be false @@ -54,11 +58,6 @@ class FarkasLemmaSolver(ctx: InferenceContext) { * */ def constraintsForUnsat(linearCtrs: Seq[LinearConstraint], temps: Seq[LinearTemplate]): Expr = { - - //for debugging - /*println("#" * 20) - println(allAnts + " ^ " + allConseqs) - println("#" * 20)*/ this.applyFarkasLemma(linearCtrs ++ temps, Seq(), true) } @@ -253,7 +252,7 @@ class FarkasLemmaSolver(ctx: InferenceContext) { reduceNonlinearity)(nlctrs) //for debugging nonlinear constraints - if (this.debugNLCtrs && Util.hasInts(simpctrs)) { + if (this.debugNLCtrs && hasInts(simpctrs)) { throw new IllegalStateException("Nonlinear constraints have integers: " + simpctrs) } if (verbose && LinearConstraintUtil.isLinear(simpctrs)) { @@ -263,30 +262,31 @@ class FarkasLemmaSolver(ctx: InferenceContext) { reporter.info("InputCtrs: " + nlctrs) reporter.info("SimpCtrs: " + simpctrs) if (this.dumpNLCtrsAsSMTLIB) { - val filename = ctx.program.modules.last.id + "-nlctr" + FileCountGUID.getID + ".smt2" - if (Util.atomNum(simpctrs) >= 5) { + val filename = program.modules.last.id + "-nlctr" + FileCountGUID.getID + ".smt2" + if (atomNum(simpctrs) >= 5) { if (solveAsBitvectors) - Util.toZ3SMTLIB(simpctrs, filename, "QF_BV", leonctx, program, useBitvectors = true, bitvecSize = bvsize) + toZ3SMTLIB(simpctrs, filename, "QF_BV", leonctx, program, useBitvectors = true, bitvecSize = bvsize) else - Util.toZ3SMTLIB(simpctrs, filename, "QF_NRA", leonctx, program) + toZ3SMTLIB(simpctrs, filename, "QF_NRA", leonctx, program) reporter.info("NLctrs dumped to: " + filename) } } } // solve the resulting constraints using solver - val innerSolver = if (solveAsBitvectors) { + val solver = if (solveAsBitvectors) { throw new IllegalStateException("Not supported now. Will be in the future!") //new ExtendedUFSolver(leonctx, program, useBitvectors = true, bitvecSize = bvsize) with TimeoutSolver } else { - // use SMTLIBSolver to solve the constraints so that it can be timed out effectively - new SMTLIBZ3Solver(leonctx, program) with TimeoutSolver - //new ExtendedUFSolver(leonctx, program) with TimeoutSolver + //new AbortableSolver(() => new SMTLIBZ3Solver(leonctx, program) with TimeoutSolver, ctx) + SimpleSolverAPI(new TimeoutSolverFactory(SolverFactory(() => + new SMTLIBZ3Solver(leonctx, program) with TimeoutSolver), timeout * 1000)) } - val solver = SimpleSolverAPI(new TimeoutSolverFactory(SolverFactory(() => innerSolver), timeout * 1000)) + //val solver = SimpleSolverAPI(new TimeoutSolverFactory(SolverFactory(() => innerSolver), )) if (verbose) reporter.info("solving...") val t1 = System.currentTimeMillis() - val (res, model) = solver.solveSAT(simpctrs) + //solver.solveSAT(simpctrs) + val (res, model) = solver.solveSAT(simpctrs) //solver.solveSAT(simpctrs, timeout * 1000) val t2 = System.currentTimeMillis() if (verbose) reporter.info((if (res.isDefined) "solved" else "timed out") + "... in " + (t2 - t1) / 1000.0 + "s") Stats.updateCounterTime((t2 - t1), "NL-solving-time", "disjuncts") diff --git a/src/main/scala/leon/invariant/templateSolvers/NLTemplateSolver.scala b/src/main/scala/leon/invariant/templateSolvers/NLTemplateSolver.scala index 47901d3b9ec6d41ccbfcab75e41db37ebd2aab85..0e0e4ef355e116d717a210cb2b587ad76c3b733b 100644 --- a/src/main/scala/leon/invariant/templateSolvers/NLTemplateSolver.scala +++ b/src/main/scala/leon/invariant/templateSolvers/NLTemplateSolver.scala @@ -25,10 +25,14 @@ import invariant.util._ import invariant.util.ExpressionTransformer._ import invariant.structure._ import invariant.structure.FunctionUtils._ -import leon.invariant.util.RealValuedExprEvaluator._ - -class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: ConstraintTracker, - minimizer: Option[(Expr, Model) => Model]) +import RealValuedExprEvaluator._ +import Util._ +import PredicateUtil._ +import SolverUtil._ + +class NLTemplateSolver(ctx: InferenceContext, program: Program, + rootFun: FunDef, ctrTracker: ConstraintTracker, + minimizer: Option[(Expr, Model) => Model]) extends TemplateSolver(ctx, rootFun, ctrTracker) { //flags controlling debugging @@ -49,12 +53,11 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const val printCallConstriants = false val dumpInstantiatedVC = false - private val program = ctx.program - private val timeout = ctx.timeout + private val timeout = ctx.vcTimeout private val leonctx = ctx.leonContext //flag controlling behavior - private val farkasSolver = new FarkasLemmaSolver(ctx) + private val farkasSolver = new FarkasLemmaSolver(ctx, program) private val startFromEarlierModel = true private val disableCegis = true private val useIncrementalSolvingForVCs = true @@ -84,7 +87,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const } else splitVC(fd) - if (Util.hasReals(rest) && Util.hasInts(rest)) + if (hasReals(rest) && hasInts(rest)) throw new IllegalStateException("Non-param Part has both integers and reals: " + rest) val vcSolver = @@ -95,7 +98,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const vcSolver.assertCnstr(rest) if (debugIncrementalVC) { - assert(Util.getTemplateVars(rest).isEmpty) + assert(getTemplateVars(rest).isEmpty) println("For function: " + fd.id) println("Param part: " + paramPart) /*vcSolver.check match { @@ -178,6 +181,8 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const val (res, newCtr, newModel, newdisjs, newcalls) = invalidateSATDisjunct(inputCtr, model) res match { + case _ if ctx.abort => + (None, None) case None => { //here, we cannot proceed and have to return unknown //However, we can return the calls that need to be unrolled @@ -219,7 +224,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const val tempIds = model.map(_._1) val tempVarMap: Map[Expr, Expr] = model.map((elem) => (elem._1.toVariable, elem._2)).toMap - val inputSize = Util.atomNum(inputCtr) + val inputSize = atomNum(inputCtr) var disjsSolvedInIter = Seq[Expr]() var callsInPaths = Set[Call]() @@ -247,7 +252,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const case (Some(acc), fd) => val disableCounterExs = if (seenPaths.contains(fd)) { blockedCEs = true - Not(Util.createOr(seenPaths(fd))) + Not(createOr(seenPaths(fd))) } else tru getUNSATConstraints(fd, model, disableCounterExs) match { case None => @@ -288,8 +293,8 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const } } else { //check that the new constraints does not have any reals - val newPart = Util.createAnd(newctrs) - val newSize = Util.atomNum(newPart) + val newPart = createAnd(newctrs) + val newSize = atomNum(newPart) Stats.updateCounterStats((newSize + inputSize), "NLsize", "disjuncts") if (verbose) reporter.info("# of atomic predicates: " + newSize + " + " + inputSize) @@ -297,6 +302,9 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const val combCtr = And(prevCtr, newPart) val (res, newModel) = farkasSolver.solveFarkasConstraints(combCtr) res match { + case _ if ctx.abort => + // stop immediately + (None, tru, Model.empty) case None => { //here we have timed out while solving the non-linear constraints if (verbose) @@ -305,7 +313,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const else reporter.info("NLsolver timed-out on the disjunct... blocking this disjunct...") if (!this.disableCegis) { - val (cres, cctr, cmodel) = solveWithCegis(tempIds.toSet, Util.createOr(confDisjuncts), inputCtr, Some(model)) + val (cres, cctr, cmodel) = solveWithCegis(tempIds.toSet, createOr(confDisjuncts), inputCtr, Some(model)) cres match { case Some(true) => { disjsSolvedInIter ++= confDisjuncts @@ -355,7 +363,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const def solveWithCegis(tempIds: Set[Identifier], expr: Expr, precond: Expr, initModel: Option[Model]): (Option[Boolean], Expr, Model) = { - val cegisSolver = new CegisCore(ctx, timeout, this) + val cegisSolver = new CegisCore(ctx, program, timeout.toInt, this) val (res, ctr, model) = cegisSolver.solve(tempIds, expr, precond, solveAsInt = false, initModel) if (!res.isDefined) reporter.info("cegis timed-out on the disjunct...") @@ -374,7 +382,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const def getUNSATConstraints(fd: FunDef, inModel: Model, disableCounterExs: Expr): Option[((Expr, Set[Call]), Expr)] = { val tempVarMap: Map[Expr, Expr] = inModel.map((elem) => (elem._1.toVariable, elem._2)).toMap - val innerSolver = + val solver = if (this.useIncrementalSolvingForVCs) vcSolvers(fd) else new SMTLIBZ3Solver(leonctx, program) with TimeoutSolver val instExpr = if (this.useIncrementalSolvingForVCs) { @@ -399,26 +407,28 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const wr.flush() wr.close() } - if (Util.hasMixedIntReals(instExpr)) { + if (hasMixedIntReals(instExpr)) { throw new IllegalStateException("Instantiated VC of " + fd.id + " contains mixed integer/reals: " + instExpr) } //reporter.info("checking VC inst ...") var t1 = System.currentTimeMillis() - innerSolver.setTimeout(timeout * 1000) + solver.setTimeout(timeout * 1000) val (res, model) = if (this.useIncrementalSolvingForVCs) { - innerSolver.push - innerSolver.assertCnstr(instExpr) - val solRes = innerSolver.check match { + solver.push + solver.assertCnstr(instExpr) + // new InterruptOnSignal(solver).interruptOnSignal(ctx.abort)( + val solRes = solver.check match { + case _ if ctx.abort => + (None, Model.empty) case r @ Some(true) => - (r, innerSolver.getModel) + (r, solver.getModel) case r => (r, Model.empty) } - innerSolver.pop() + solver.pop() solRes } else { - val solver = SimpleSolverAPI(SolverFactory(() => innerSolver)) - solver.solveSAT(instExpr) + SimpleSolverAPI(SolverFactory(() => solver)).solveSAT(instExpr) } val vccTime = (System.currentTimeMillis() - t1) @@ -429,7 +439,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const //for debugging if (this.trackUnpackedVCCTime) { val upVCinst = simplifyArithmetic(TemplateInstantiator.instantiate(ctrTracker.getVC(fd).unpackedExpr, tempVarMap)) - Stats.updateCounterStats(Util.atomNum(upVCinst), "UP-VC-size", "disjuncts") + Stats.updateCounterStats(atomNum(upVCinst), "UP-VC-size", "disjuncts") t1 = System.currentTimeMillis() val (res2, _) = SimpleSolverAPI(SolverFactory(() => new SMTLIBZ3Solver(leonctx, program))).solveSAT(upVCinst) @@ -471,7 +481,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const } } - val evaluator = new DefaultEvaluator(leonctx, program) //as of now used only for debugging + lazy val evaluator = new DefaultEvaluator(leonctx, program) //as of now used only for debugging //a helper method //TODO: this should also handle reals protected def doesSatisfyModel(expr: Expr, model: Model): Boolean = { @@ -537,7 +547,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const //for debugging if (this.debugChooseDisjunct || this.printPathToConsole || this.dumpPathAsSMTLIB || this.verifyInvariant) { val pathctrs = satCtrs.map(_.toExpr) - val plainFormula = Util.createAnd(pathctrs) + val plainFormula = createAnd(pathctrs) val pathcond = simplifyArithmetic(plainFormula) if (this.debugChooseDisjunct) { @@ -549,7 +559,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const if (this.verifyInvariant) { println("checking invariant for path...") - val sat = Util.checkInvariant(pathcond, leonctx, program) + val sat = checkInvariant(pathcond, leonctx, program) } if (this.printPathToConsole) { @@ -566,7 +576,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const if (this.dumpPathAsSMTLIB) { val filename = "pathcond" + FileCountGUID.getID + ".smt2" - Util.toZ3SMTLIB(pathcond, filename, "QF_NIA", leonctx, program) + toZ3SMTLIB(pathcond, filename, "QF_NIA", leonctx, program) println("Path dumped to: " + filename) } } @@ -617,15 +627,15 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const } if (this.debugTheoryReduction) { - val simpPathCond = Util.createAnd((lnctrs ++ temps).map(_.template).toSeq) + val simpPathCond = createAnd((lnctrs ++ temps).map(_.template).toSeq) if (this.verifyInvariant) { println("checking invariant for simp-path...") - Util.checkInvariant(simpPathCond, leonctx, program) + checkInvariant(simpPathCond, leonctx, program) } } if (this.trackNumericalDisjuncts) { - numericalDisjuncts :+= Util.createAnd((lnctrs ++ temps).map(_.template).toSeq) + numericalDisjuncts :+= createAnd((lnctrs ++ temps).map(_.template).toSeq) } val (data, nlctr) = processNumCtrs(lnctrs.toSeq, temps.toSeq) @@ -639,14 +649,14 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const //here we are invalidating A^~(B) if (temps.isEmpty) { //here ants ^ conseq is sat (otherwise we wouldn't reach here) and there is no way to falsify this path - (Util.createAnd(lnctrs.map(_.toExpr)), fls) + (createAnd(lnctrs.map(_.toExpr)), fls) } else { if (this.debugElimination) { //println("Path Constraints (before elim): "+(lnctrs ++ temps)) if (this.verifyInvariant) { println("checking invariant for disjunct before elimination...") - Util.checkInvariant(Util.createAnd((lnctrs ++ temps).map(_.template)), leonctx, program) + checkInvariant(createAnd((lnctrs ++ temps).map(_.template)), leonctx, program) } } //compute variables to be eliminated @@ -659,7 +669,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const Some((ctrs: Seq[LinearConstraint]) => { //println("checking disjunct before elimination...") //println("ctrs: "+ctrs) - val debugRes = Util.checkInvariant(Util.createAnd((ctrs ++ temps).map(_.template)), leonctx, program) + val debugRes = checkInvariant(createAnd((ctrs ++ temps).map(_.template)), leonctx, program) }) } else None val elimLnctrs = LinearConstraintUtil.apply1PRuleOnDisjunct(lnctrs, elimVars, debugger) @@ -669,7 +679,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const println("Path constriants (after elimination): " + elimLnctrs) if (this.verifyInvariant) { println("checking invariant for disjunct after elimination...") - Util.checkInvariant(Util.createAnd((elimLnctrs ++ temps).map(_.template)), leonctx, program) + checkInvariant(createAnd((elimLnctrs ++ temps).map(_.template)), leonctx, program) } } //for stats @@ -701,7 +711,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const //TODO: Use the dependence chains in the formulas to identify what to assertionize // and what can never be implied by solving for the templates - val disjunct = Util.createAnd((newLnctrs ++ temps).map(_.template)) + val disjunct = createAnd((newLnctrs ++ temps).map(_.template)) val implCtrs = farkasSolver.constraintsForUnsat(newLnctrs, temps) //for debugging @@ -709,7 +719,7 @@ class NLTemplateSolver(ctx: InferenceContext, rootFun: FunDef, ctrTracker: Const println("Final Path Constraints: " + disjunct) if (this.verifyInvariant) { println("checking invariant for final disjunct... ") - Util.checkInvariant(disjunct, leonctx, program) + checkInvariant(disjunct, leonctx, program) } } diff --git a/src/main/scala/leon/invariant/templateSolvers/NLTemplateSolverWithMult.scala b/src/main/scala/leon/invariant/templateSolvers/NLTemplateSolverWithMult.scala index 10a232e3fcb4730a4e2d3026ebd5f92db2f51ee5..a2a6524c6d6f52ffa232129b4adb2ff0f794ee42 100644 --- a/src/main/scala/leon/invariant/templateSolvers/NLTemplateSolverWithMult.scala +++ b/src/main/scala/leon/invariant/templateSolvers/NLTemplateSolverWithMult.scala @@ -16,22 +16,25 @@ import invariant.engine._ import invariant.factories._ import invariant.util._ import invariant.structure._ +import Util._ +import PredicateUtil._ +import SolverUtil._ -class NLTemplateSolverWithMult(ctx : InferenceContext, rootFun: FunDef, +class NLTemplateSolverWithMult(ctx : InferenceContext, program: Program, rootFun: FunDef, ctrTracker: ConstraintTracker, minimizer: Option[(Expr, Model) => Model]) - extends NLTemplateSolver(ctx, rootFun, ctrTracker, minimizer) { + extends NLTemplateSolver(ctx, program, rootFun, ctrTracker, minimizer) { val axiomFactory = new AxiomFactory(ctx) override def getVCForFun(fd: FunDef): Expr = { val plainvc = ctrTracker.getVC(fd).toExpr - val nlvc = Util.multToTimes(plainvc) + val nlvc = multToTimes(plainvc) nlvc } override def splitVC(fd: FunDef) : (Expr,Expr) = { val (paramPart, rest) = ctrTracker.getVC(fd).splitParamPart - (Util.multToTimes(paramPart),Util.multToTimes(rest)) + (multToTimes(paramPart),multToTimes(rest)) } override def axiomsForTheory(formula : Formula, calls: Set[Call], model: Model) : Seq[Constraint] = { @@ -61,7 +64,7 @@ class NLTemplateSolverWithMult(ctx : InferenceContext, rootFun: FunDef, } def isMultOp(call : Call) : Boolean = { - Util.isMultFunctions(call.fi.tfd.fd) + isMultFunctions(call.fi.tfd.fd) } def unaryMultAxioms(formula: Formula, calls: Set[Call], predEval: (Expr => Boolean)) : Seq[Expr] = { @@ -81,7 +84,7 @@ class NLTemplateSolverWithMult(ctx : InferenceContext, rootFun: FunDef, def binaryMultAxioms(formula: Formula, calls: Set[Call], predEval: (Expr => Boolean)) : Seq[Expr] = { val mults = calls.filter(call => isMultOp(call) && axiomFactory.hasBinaryAxiom(call)) - val product = Util.cross(mults,mults).collect{ case (c1,c2) if c1 != c2 => (c1,c2) } + val product = cross(mults,mults).collect{ case (c1,c2) if c1 != c2 => (c1,c2) } ctx.reporter.info("Theory axioms: "+product.size) Stats.updateCumStats(product.size, "-Total-theory-axioms") diff --git a/src/main/scala/leon/invariant/templateSolvers/TemplateSolver.scala b/src/main/scala/leon/invariant/templateSolvers/TemplateSolver.scala index 9b360c2db7d2f4442fb253e7fed58a0079b44c84..b373c5e7045aab99264c5403648c2ac968b48ef5 100644 --- a/src/main/scala/leon/invariant/templateSolvers/TemplateSolver.scala +++ b/src/main/scala/leon/invariant/templateSolvers/TemplateSolver.scala @@ -19,6 +19,7 @@ import invariant.util._ import invariant.structure._ import invariant.structure.FunctionUtils._ import leon.solvers.Model +import PredicateUtil._ abstract class TemplateSolver(ctx: InferenceContext, val rootFun: FunDef, ctrTracker: ConstraintTracker) { @@ -33,7 +34,6 @@ abstract class TemplateSolver(ctx: InferenceContext, val rootFun: FunDef, private val dumpVCtoConsole = false private val dumpVCasText = false - private val dumpVCasSMTLIB = false /** * Completes a model by adding mapping to new template variables @@ -73,7 +73,7 @@ abstract class TemplateSolver(ctx: InferenceContext, val rootFun: FunDef, val vc = if (ctx.usereals) ExpressionTransformer.IntLiteralToReal(getVCForFun(fd)) else getVCForFun(fd) - if (dumpVCtoConsole || dumpVCasText || dumpVCasSMTLIB) { + if (dumpVCtoConsole || dumpVCasText) { //val simpForm = simplifyArithmetic(vc) val filename = "vc-" + FileCountGUID.getID if (dumpVCtoConsole) { @@ -87,15 +87,11 @@ abstract class TemplateSolver(ctx: InferenceContext, val rootFun: FunDef, wr.flush() wr.close() } - if (dumpVCasSMTLIB) { - Util.toZ3SMTLIB(vc, filename + ".smt2", "QF_LIA", ctx.leonContext, ctx.program) - println("Printed VC of " + fd.id + " to file: " + filename) - } } if (ctx.dumpStats) { - Stats.updateCounterStats(Util.atomNum(vc), "VC-size", "VC-refinement") - Stats.updateCounterStats(Util.numUIFADT(vc), "UIF+ADT", "VC-refinement") + Stats.updateCounterStats(atomNum(vc), "VC-size", "VC-refinement") + Stats.updateCounterStats(numUIFADT(vc), "UIF+ADT", "VC-refinement") } (fd -> vc) }).toMap @@ -105,7 +101,7 @@ abstract class TemplateSolver(ctx: InferenceContext, val rootFun: FunDef, //val tempOption = if (fd.hasTemplate) Some(fd.getTemplate) else None //if (!tempOption.isDefined) acc //else - acc ++ Util.getTemplateIds(vc) + acc ++ getTemplateIds(vc) } Stats.updateCounterStats(tempIds.size, "TemplateIds", "VC-refinement") diff --git a/src/main/scala/leon/invariant/templateSolvers/UFADTEliminator.scala b/src/main/scala/leon/invariant/templateSolvers/UFADTEliminator.scala index fb7519850bcebaafab437f1d270f2f9d37417dc9..f41ec7e8963a45978fb4a9ac3fe091de48140d10 100644 --- a/src/main/scala/leon/invariant/templateSolvers/UFADTEliminator.scala +++ b/src/main/scala/leon/invariant/templateSolvers/UFADTEliminator.scala @@ -8,10 +8,13 @@ import purescala.ExprOps._ import purescala.Extractors._ import purescala.Types._ import java.io._ -import leon.invariant.util.UndirectedGraph +import invariant.datastructure.UndirectedGraph import scala.util.control.Breaks._ import invariant.util._ import leon.purescala.TypeOps +import PredicateUtil._ +import SolverUtil._ +import Util._ class UFADTEliminator(ctx: LeonContext, program: Program) { @@ -32,7 +35,7 @@ class UFADTEliminator(ctx: LeonContext, program: Program) { val product = vec.foldLeft(Set[(Expr, Expr)]())((acc, call) => { //an optimization: here we can exclude calls to maxFun from axiomatization, they will be inlined anyway - /*val shouldConsider = if(InvariantUtil.isCallExpr(call)) { + /*val shouldConsider = if(InvariantisCallExpr(call)) { val BinaryOperator(_,FunctionInvocation(calledFun,_), _) = call if(calledFun == DepthInstPhase.maxFun) false else true @@ -88,7 +91,7 @@ class UFADTEliminator(ctx: LeonContext, program: Program) { val Operator(Seq(r2 @ Variable(_), _), _) = call2 val resEquals = predEval(Equals(r1, r2)) if (resEquals) { - if (Util.isCallExpr(call1)) { + if (isCallExpr(call1)) { val (ants, _) = axiomatizeCalls(call1, call2) val antsHold = ants.forall(ant => { val Operator(Seq(lvar @ Variable(_), rvar @ Variable(_)), _) = ant @@ -102,7 +105,7 @@ class UFADTEliminator(ctx: LeonContext, program: Program) { def predForEquality(call1: Expr, call2: Expr): Seq[Expr] = { - val eqs = if (Util.isCallExpr(call1)) { + val eqs = if (isCallExpr(call1)) { val (_, rhs) = axiomatizeCalls(call1, call2) Seq(rhs) } else { @@ -125,7 +128,7 @@ class UFADTEliminator(ctx: LeonContext, program: Program) { def predForDisequality(call1: Expr, call2: Expr): Seq[Expr] = { - val (ants, _) = if (Util.isCallExpr(call1)) { + val (ants, _) = if (isCallExpr(call1)) { axiomatizeCalls(call1, call2) } else { axiomatizeADTCons(call1, call2) diff --git a/src/main/scala/leon/invariant/util/CallGraph.scala b/src/main/scala/leon/invariant/util/CallGraph.scala index 73ac3b099389eb33223d83302bc74b82d3b1547f..23f87d5bc0b62163ef40e852d7cc0af0cd658bc3 100644 --- a/src/main/scala/leon/invariant/util/CallGraph.scala +++ b/src/main/scala/leon/invariant/util/CallGraph.scala @@ -8,8 +8,10 @@ import purescala.Expressions._ import purescala.ExprOps._ import purescala.Extractors._ import purescala.Types._ +import ProgramUtil._ import Util._ import invariant.structure.FunctionUtils._ +import invariant.datastructure._ /** * This represents a call graph of the functions in the program diff --git a/src/main/scala/leon/invariant/util/ExpressionTransformer.scala b/src/main/scala/leon/invariant/util/ExpressionTransformer.scala index e38bdf3aa5cc3dd7d1f591bbee464e5dd19bd1a5..6750bd59c144fa656b2d820516d6691e3e169862 100644 --- a/src/main/scala/leon/invariant/util/ExpressionTransformer.scala +++ b/src/main/scala/leon/invariant/util/ExpressionTransformer.scala @@ -13,6 +13,8 @@ import purescala.ScalaPrinter import invariant.structure.Call import invariant.structure.FunctionUtils._ import leon.invariant.factories.TemplateIdFactory +import PredicateUtil._ +import Util._ /** * A collection of transformation on expressions and some utility methods. @@ -40,17 +42,17 @@ object ExpressionTransformer { case And(args) if !insideFunction => { val newargs = args.map((arg) => { val (nexp, ncjs) = transformer(arg, false) - Util.createAnd(nexp +: ncjs.toSeq) + createAnd(nexp +: ncjs.toSeq) }) - (Util.createAnd(newargs), Set()) + (createAnd(newargs), Set()) } case Or(args) if !insideFunction => { val newargs = args.map((arg) => { val (nexp, ncjs) = transformer(arg, false) - Util.createAnd(nexp +: ncjs.toSeq) + createAnd(nexp +: ncjs.toSeq) }) - (Util.createOr(newargs), Set()) + (createOr(newargs), Set()) } case t: Terminal => (t, Set()) @@ -117,7 +119,7 @@ object ExpressionTransformer { val mult = multop(quo, rhs) val divsem = Equals(lhs, Plus(mult, rem)) //TODO: here, we have to use |rhs| - val newexpr = Util.createAnd(Seq(divsem, LessEquals(zero, rem), LessEquals(rem, Minus(rhs, one)))) + val newexpr = createAnd(Seq(divsem, LessEquals(zero, rem), LessEquals(rem, Minus(rhs, one)))) val resset = transform(newexpr, true) (quo, resset._2 + resset._1) } @@ -184,7 +186,7 @@ object ExpressionTransformer { } val (nexp, ncjs) = transform(inexpr, false) val res = if (!ncjs.isEmpty) { - Util.createAnd(nexp +: ncjs.toSeq) + createAnd(nexp +: ncjs.toSeq) } else nexp res } @@ -306,7 +308,7 @@ object ExpressionTransformer { val (nexp, ncjs) = flattenFunc(inExpr, false) if (!ncjs.isEmpty) { - Util.createAnd(nexp +: ncjs.toSeq) + createAnd(nexp +: ncjs.toSeq) } else nexp } @@ -327,8 +329,8 @@ object ExpressionTransformer { case Not(Not(e1)) => nnf(e1) case e @ Not(t: Terminal) => e case e @ Not(FunctionInvocation(_, _)) => e - case Not(And(args)) => Util.createOr(args.map(arg => nnf(Not(arg)))) - case Not(Or(args)) => Util.createAnd(args.map(arg => nnf(Not(arg)))) + case Not(And(args)) => createOr(args.map(arg => nnf(Not(arg)))) + case Not(Or(args)) => createAnd(args.map(arg => nnf(Not(arg)))) case Not(e @ Operator(Seq(e1, e2), op)) => { //matches integer binary relation or a boolean equality if (e1.getType == BooleanType || e1.getType == Int32Type || e1.getType == RealType || e1.getType == IntegerType) { @@ -391,14 +393,14 @@ object ExpressionTransformer { case Or(inArgs) => acc ++ inArgs case _ => acc :+ arg }) - Util.createOr(newArgs) + createOr(newArgs) } case And(args) => { val newArgs = args.foldLeft(Seq[Expr]())((acc, arg) => arg match { case And(inArgs) => acc ++ inArgs case _ => acc :+ arg }) - Util.createAnd(newArgs) + createAnd(newArgs) } case _ => e })(expr) @@ -474,7 +476,7 @@ object ExpressionTransformer { case _ => e })(ine) val closure = (e: Expr) => replace(tempMap, e) - Util.fix(closure)(newinst) + fix(closure)(newinst) } /** @@ -626,7 +628,7 @@ object ExpressionTransformer { def distribute(e: Expr): Expr = { simplePreTransform(_ match { - case e @ FunctionInvocation(TypedFunDef(fd, _), Seq(e1, e2)) if Util.isMultFunctions(fd) => + case e @ FunctionInvocation(TypedFunDef(fd, _), Seq(e1, e2)) if isMultFunctions(fd) => val newe = (e1, e2) match { case (Plus(sum1, sum2), _) => // distribute e2 over e1 diff --git a/src/main/scala/leon/invariant/util/LetTupleSimplifications.scala b/src/main/scala/leon/invariant/util/LetTupleSimplifications.scala index 966121756fd62e0ef2d517f8ad99153b161858fb..bd99992014bd94ab29c099b7a03eb7e452e154c0 100644 --- a/src/main/scala/leon/invariant/util/LetTupleSimplifications.scala +++ b/src/main/scala/leon/invariant/util/LetTupleSimplifications.scala @@ -11,6 +11,7 @@ import java.io._ import java.io._ import purescala.ScalaPrinter import leon.utils._ +import PredicateUtil._ import invariant.structure.Call import invariant.structure.FunctionUtils._ @@ -178,7 +179,7 @@ object LetTupleSimplification { } val newargs: Seq[Expr] = args.map(simplifyArithmetic) val Seq(arg1: Expr, arg2: Expr) = newargs - val simpval = if (!Util.hasCalls(arg1) && !Util.hasCalls(arg2)) { + val simpval = if (!hasCalls(arg1) && !hasCalls(arg2)) { import invariant.structure.LinearConstraintUtil._ val lt = exprToTemplate(LessEquals(Minus(arg1, arg2), InfiniteIntegerLiteral(0))) //now, check if all the variables in 'lt' have only positive coefficients diff --git a/src/main/scala/leon/invariant/util/Minimizer.scala b/src/main/scala/leon/invariant/util/Minimizer.scala index 78e82db2d233d1b980206c96a5bc86a7696611d2..f9ee65b007147b245330ede807e959a25411e3c0 100644 --- a/src/main/scala/leon/invariant/util/Minimizer.scala +++ b/src/main/scala/leon/invariant/util/Minimizer.scala @@ -16,8 +16,9 @@ import invariant.engine.InferenceContext import invariant.factories._ import leon.invariant.templateSolvers.ExtendedUFSolver import leon.invariant.util.RealValuedExprEvaluator._ +import invariant.util.TimerUtil._ -class Minimizer(ctx: InferenceContext) { +class Minimizer(ctx: InferenceContext, program: Program) { val verbose = false val debugMinimization = false @@ -26,14 +27,11 @@ class Minimizer(ctx: InferenceContext) { * TODO: make sure that the template for rootFun is the time template */ val MaxIter = 16 //note we may not be able to represent anything beyond 2^16 - /*val MaxInt = Int.MaxValue - val sqrtMaxInt = 45000 //this is a number that is close a sqrt of 2^31 -*/ val half = FractionalLiteral(1, 2) + val half = FractionalLiteral(1, 2) val two = FractionalLiteral(2, 1) val rzero = FractionalLiteral(0, 1) val mone = FractionalLiteral(-1, 1) - private val program = ctx.program private val leonctx = ctx.leonContext val reporter = leonctx.reporter @@ -57,10 +55,8 @@ class Minimizer(ctx: InferenceContext) { val orderedTempVars = nestMap.toSeq.sortWith((a, b) => a._2 >= b._2).map(_._1) //do a binary search sequentially on each of these tempvars // note: use smtlib solvers so that they can be timedout - val solver = SimpleSolverAPI( - new TimeoutSolverFactory(SolverFactory(() => - new SMTLIBZ3Solver(leonctx, program) with TimeoutSolver), ctx.timeout * 1000)) - //new ExtendedUFSolver(leonctx, program) with TimeoutSolver), ctx.timeout * 1000)) + val solver = new SimpleSolverAPI(new TimeoutSolverFactory(SolverFactory(() => + new SMTLIBZ3Solver(leonctx, program) with TimeoutSolver), ctx.vcTimeout * 1000)) reporter.info("minimizing...") var currentModel = initModel @@ -115,7 +111,7 @@ class Minimizer(ctx: InferenceContext) { } } } - } while (continue && iter < MaxIter) + } while (!ctx.abort && continue && iter < MaxIter) //this is the last ditch effort to make the upper bound constant smaller. //check if the floor of the upper-bound is a solution val currval @ FractionalLiteral(n, d) = @@ -124,7 +120,7 @@ class Minimizer(ctx: InferenceContext) { } else { initModel(tvar.id).asInstanceOf[FractionalLiteral] } - if (d != 1) { + if (d != 1 && !ctx.abort) { val (res, newModel) = solver.solveSAT(And(acc, Equals(tvar, floor(currval)))) if (res == Some(true)) updateState(newModel) @@ -151,7 +147,7 @@ class Minimizer(ctx: InferenceContext) { val (res, newModel) = solver.solveSAT(And(nlctr, Equals(tvar, flval))) res match { case Some(true) => Some(newModel) - case _ => None + case _ => None } } else None } @@ -191,10 +187,10 @@ class Minimizer(ctx: InferenceContext) { 0 } case FunctionInvocation(_, args) => 1 + args.foldLeft(0)((acc, arg) => acc + functionNesting(arg)) - case t: Terminal => 0 + case t: Terminal => 0 /*case UnaryOperator(arg, _) => functionNesting(arg) case BinaryOperator(a1, a2, _) => functionNesting(a1) + functionNesting(a2)*/ - case Operator(args, _) => args.foldLeft(0)((acc, arg) => acc + functionNesting(arg)) + case Operator(args, _) => args.foldLeft(0)((acc, arg) => acc + functionNesting(arg)) } } functionNesting(template) diff --git a/src/main/scala/leon/invariant/util/RealToIntExpr.scala b/src/main/scala/leon/invariant/util/RealToIntExpr.scala new file mode 100644 index 0000000000000000000000000000000000000000..54650b2c4111b32e127070cdc23889708a81b6fd --- /dev/null +++ b/src/main/scala/leon/invariant/util/RealToIntExpr.scala @@ -0,0 +1,73 @@ +package leon +package invariant.util + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Expressions._ +import purescala.ExprOps._ +import purescala.Extractors._ +import purescala.Types._ +import leon.invariant._ +import invariant.factories._ +import solvers._ + +/** + * maps all real valued variables and literals to new integer variables/literals and + * performs the reverse mapping + * Note: this should preserve the template identifier property + */ +class RealToInt { + + val bone = BigInt(1) + var realToIntId = Map[Identifier, Identifier]() + var intToRealId = Map[Identifier, Identifier]() + + def mapRealToInt(inexpr: Expr): Expr = { + val transformer = (e: Expr) => e match { + case FractionalLiteral(num, `bone`) => InfiniteIntegerLiteral(num) + case FractionalLiteral(_, _) => throw new IllegalStateException("Real literal with non-unit denominator") + case v @ Variable(realId) if (v.getType == RealType) => { + val newId = realToIntId.getOrElse(realId, { + //note: the fresh identifier has to be a template identifier if the original one is a template identifier + val freshId = if (TemplateIdFactory.IsTemplateIdentifier(realId)) + TemplateIdFactory.freshIdentifier(realId.name, IntegerType) + else + FreshIdentifier(realId.name, IntegerType, true) + + realToIntId += (realId -> freshId) + intToRealId += (freshId -> realId) + freshId + }) + Variable(newId) + } + case _ => e + } + simplePostTransform(transformer)(inexpr) + } + + def unmapModel(model: Model): Model = { + new Model(model.map(pair => { + val (key, value) = if (intToRealId.contains(pair._1)) { + (intToRealId(pair._1), + pair._2 match { + case InfiniteIntegerLiteral(v) => FractionalLiteral(v.toInt, 1) + case _ => pair._2 + }) + } else pair + (key -> value) + }).toMap) + } + + def mapModel(model: Model): Model = { + new Model(model.collect { + case (k, FractionalLiteral(n, bone)) => + (realToIntId(k), InfiniteIntegerLiteral(n)) + case (k, v) => + if (realToIntId.contains(k)) { + (realToIntId(k), v) + } else { + (k, v) + } + }.toMap) + } +} \ No newline at end of file diff --git a/src/main/scala/leon/invariant/util/SolverUtil.scala b/src/main/scala/leon/invariant/util/SolverUtil.scala new file mode 100644 index 0000000000000000000000000000000000000000..e67385c435ea94b24ef63ed8fe8bdbf41f602570 --- /dev/null +++ b/src/main/scala/leon/invariant/util/SolverUtil.scala @@ -0,0 +1,133 @@ +package leon +package invariant.util + +import utils._ +import purescala.Common._ +import purescala.Definitions._ +import purescala.Expressions._ +import purescala.ExprOps._ +import purescala.Extractors._ +import purescala.Types._ +import scala.collection.mutable.{ Set => MutableSet, Map => MutableMap } +import leon.invariant._ +import solvers.z3._ +import solvers._ +import invariant.engine._ +import invariant.factories._ +import invariant.structure._ +import FunctionUtils._ +import leon.invariant.templateSolvers.ExtendedUFSolver +import java.io._ +import Util._ +import PredicateUtil._ + +object SolverUtil { + + def modelToExpr(model: Model): Expr = { + model.foldLeft(tru: Expr)((acc, elem) => { + val (k, v) = elem + val eq = Equals(k.toVariable, v) + if (acc == tru) eq + else And(acc, eq) + }) + } + + def toZ3SMTLIB(expr: Expr, filename: String, + theory: String, ctx: LeonContext, pgm: Program, + useBitvectors: Boolean = false, + bitvecSize: Int = 32) = { + //create new solver, assert constraints and print + val printSol = new ExtendedUFSolver(ctx, pgm) + printSol.assertCnstr(expr) + val writer = new PrintWriter(filename) + writer.println(printSol.ctrsToString(theory)) + printSol.free() + writer.flush() + writer.close() + } + + /** + * A helper function that can be used to hardcode an invariant and see if it unsatifies the paths + */ + def checkInvariant(expr: Expr, ctx: LeonContext, prog: Program): Option[Boolean] = { + val idmap: Map[Expr, Expr] = variablesOf(expr).collect { + case id @ _ if (id.name.toString == "a?") => id.toVariable -> InfiniteIntegerLiteral(6) + case id @ _ if (id.name.toString == "c?") => id.toVariable -> InfiniteIntegerLiteral(2) + }.toMap + //println("found ids: " + idmap.keys) + if (!idmap.keys.isEmpty) { + val newpathcond = replace(idmap, expr) + //check if this is solvable + val solver = SimpleSolverAPI(SolverFactory(() => new ExtendedUFSolver(ctx, prog))) + solver.solveSAT(newpathcond)._1 match { + case Some(true) => { + println("Path satisfiable for a?,c? -->6,2 ") + Some(true) + } + case _ => { + println("Path unsat for a?,c? --> 6,2") + Some(false) + } + } + } else None + } + + def collectUNSATCores(ine: Expr, ctx: LeonContext, prog: Program): Expr = { + var controlVars = Map[Variable, Expr]() + var newEqs = Map[Expr, Expr]() + val solver = new ExtendedUFSolver(ctx, prog) + val newe = simplePostTransform((e: Expr) => e match { + case And(_) | Or(_) => { + val v = TVarFactory.createTemp("a", BooleanType).toVariable + newEqs += (v -> e) + val newe = Equals(v, e) + + //create new variable and add it in disjunction + val cvar = FreshIdentifier("ctrl", BooleanType, true).toVariable + controlVars += (cvar -> newe) + solver.assertCnstr(Or(newe, cvar)) + v + } + case _ => e + })(ine) + //create new variable and add it in disjunction + val cvar = FreshIdentifier("ctrl", BooleanType, true).toVariable + controlVars += (cvar -> newe) + solver.assertCnstr(Or(newe, cvar)) + + val res = solver.checkAssumptions(controlVars.keySet.map(Not.apply _)) + println("Result: " + res) + val coreExprs = solver.getUnsatCore + val simpcores = coreExprs.foldLeft(Seq[Expr]())((acc, coreExp) => { + val Not(cvar @ Variable(_)) = coreExp + val newexp = controlVars(cvar) + //println("newexp: "+newexp) + newexp match { + // case Iff(v@Variable(_),rhs) if(newEqs.contains(v)) => acc + case Equals(v @ Variable(_), rhs) if (v.getType == BooleanType && rhs.getType == BooleanType && newEqs.contains(v)) => acc + case _ => { + acc :+ newexp + } + } + }) + val cores = Util.fix((e: Expr) => replace(newEqs, e))(createAnd(simpcores.toSeq)) + + solver.free + //cores + ExpressionTransformer.unFlatten(cores, + variablesOf(ine).filterNot(TVarFactory.isTemporary _)) + } + + //tests if the solver uses nlsat + def usesNLSat(solver: AbstractZ3Solver) = { + //check for nlsat + val x = FreshIdentifier("x", RealType).toVariable + val testExpr = Equals(Times(x, x), FractionalLiteral(2, 1)) + solver.assertCnstr(testExpr) + solver.check match { + case Some(true) => true + case _ => false + } + } + +} \ No newline at end of file diff --git a/src/main/scala/leon/invariant/util/TemporaryVarFactory.scala b/src/main/scala/leon/invariant/util/TemporaryVarFactory.scala new file mode 100644 index 0000000000000000000000000000000000000000..c85c7837b1c4dbf35e2a90b87d93bccca929a6c9 --- /dev/null +++ b/src/main/scala/leon/invariant/util/TemporaryVarFactory.scala @@ -0,0 +1,31 @@ +package leon +package invariant.util + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Expressions._ +import purescala.ExprOps._ +import purescala.Types._ +import scala.collection.mutable.{ Set => MutableSet, Map => MutableMap } + +object TVarFactory { + + val temporaries = MutableSet[Identifier]() + //these are dummy identifiers used in 'CaseClassSelector' conversion + val dummyIds = MutableSet[Identifier]() + + def createTemp(name: String, tpe: TypeTree = Untyped): Identifier = { + val freshid = FreshIdentifier(name, tpe, true) + temporaries.add(freshid) + freshid + } + + def createDummy(tpe: TypeTree): Identifier = { + val freshid = FreshIdentifier("dy", tpe, true) + dummyIds.add(freshid) + freshid + } + + def isTemporary(id: Identifier): Boolean = temporaries.contains(id) + def isDummy(id: Identifier): Boolean = dummyIds.contains(id) +} diff --git a/src/main/scala/leon/invariant/util/TimerUtil.scala b/src/main/scala/leon/invariant/util/TimerUtil.scala new file mode 100644 index 0000000000000000000000000000000000000000..1284951709d4c374c56f1115b8b946f1a796855d --- /dev/null +++ b/src/main/scala/leon/invariant/util/TimerUtil.scala @@ -0,0 +1,64 @@ +package leon +package invariant.util + +import utils._ +import solvers._ +import invariant.engine._ +import purescala.Expressions._ + +object TimerUtil { + /** + * Timeout in milliseconds + */ + def scheduleTask(callBack: () => Unit, timeOut: Long): Option[java.util.Timer] = { + if (timeOut > 0) { + val timer = new java.util.Timer(); + timer.schedule(new java.util.TimerTask() { + def run() { + callBack() + timer.cancel() //the timer will be cancelled after it runs + } + }, timeOut); + Some(timer) + } else None + } +} + +class InterruptOnSignal(it: Interruptible) { + + private class Poll(signal: => Boolean, onSignal: => Unit) extends Thread { + private var keepRunning = true + + override def run(): Unit = { + val startTime: Long = System.currentTimeMillis + while (!signal && keepRunning) { + Thread.sleep(100) // a relatively infrequent poll + } + if (signal && keepRunning) { + onSignal + } + } + + def finishedRunning(): Unit = { + keepRunning = false + } + } + + def interruptOnSignal[T](signal: => Boolean)(body: => T): T = { + var recdSignal = false + + val timer = new Poll(signal, { + it.interrupt() + recdSignal = true + }) + + timer.start() + val res = body + timer.finishedRunning() + + if (recdSignal) { + it.recoverInterrupt() + } + res + } +} \ No newline at end of file diff --git a/src/main/scala/leon/invariant/util/TreeUtil.scala b/src/main/scala/leon/invariant/util/TreeUtil.scala new file mode 100644 index 0000000000000000000000000000000000000000..6e3e45f22a49bc3ef70d0d18e3d0371fedac2654 --- /dev/null +++ b/src/main/scala/leon/invariant/util/TreeUtil.scala @@ -0,0 +1,360 @@ +package leon +package invariant.util + +import purescala.Common._ +import purescala.Definitions._ +import purescala.Expressions._ +import purescala.ExprOps._ +import purescala.Extractors._ +import purescala.Types._ +import scala.collection.mutable.{ Set => MutableSet, Map => MutableMap } +import leon.invariant._ +import invariant.engine._ +import invariant.factories._ +import invariant.structure._ +import FunctionUtils._ +import scala.annotation.tailrec +import PredicateUtil._ +import ProgramUtil._ +import Util._ +import solvers._ +import purescala.DefOps._ + +object ProgramUtil { + + def createLeonContext(ctx: LeonContext, opts: String*): LeonContext = { + Main.processOptions(opts.toList).copy(reporter = ctx.reporter, + interruptManager = ctx.interruptManager, files = ctx.files, timers = ctx.timers) + } + + /** + * Here, we exclude empty units that do not have any modules and empty + * modules that do not have any definitions + */ + def copyProgram(prog: Program, mapdefs: (Seq[Definition] => Seq[Definition])): Program = { + prog.copy(units = prog.units.collect { + case unit if (!unit.defs.isEmpty) => unit.copy(defs = unit.defs.collect { + case module : ModuleDef if (!module.defs.isEmpty) => + module.copy(defs = mapdefs(module.defs)) + case other => other + }) + }) + } + + def createTemplateFun(plainTemp: Expr): FunctionInvocation = { + val tmpl = Lambda(getTemplateIds(plainTemp).toSeq.map(id => ValDef(id)), plainTemp) + val tmplFd = new FunDef(FreshIdentifier("tmpl", FunctionType(Seq(tmpl.getType), BooleanType), false), Seq(), Seq(ValDef(FreshIdentifier("arg", tmpl.getType), + Some(tmpl.getType))), BooleanType) + tmplFd.body = Some(BooleanLiteral(true)) + FunctionInvocation(TypedFunDef(tmplFd, Seq()), Seq(tmpl)) + } + + /** + * This is the default template generator. + * Note: we are not creating template for libraries. + */ + def getOrCreateTemplateForFun(fd: FunDef): Expr = { + val plainTemp = if (fd.hasTemplate) fd.getTemplate + else if (fd.annotations.contains("library")) BooleanLiteral(true) + else { + //just consider all the arguments, return values that are integers + val baseTerms = fd.params.filter((vardecl) => isNumericType(vardecl.getType)).map(_.toVariable) ++ + (if (isNumericType(fd.returnType)) Seq(getFunctionReturnVariable(fd)) + else Seq()) + val lhs = baseTerms.foldLeft(TemplateIdFactory.freshTemplateVar(): Expr)((acc, t) => { + Plus(Times(TemplateIdFactory.freshTemplateVar(), t), acc) + }) + val tempExpr = LessEquals(lhs, InfiniteIntegerLiteral(0)) + tempExpr + } + plainTemp + } + + def mapFunctionsInExpr(funmap: Map[FunDef, FunDef])(ine: Expr): Expr = { + simplePostTransform((e: Expr) => e match { + case FunctionInvocation(tfd, args) if funmap.contains(tfd.fd) => + FunctionInvocation(TypedFunDef(funmap(tfd.fd), tfd.tps), args) + case _ => e + })(ine) + } + + /** + * For functions for which `funToTmpl` is not defined, their templates + * will be removed + */ + def assignTemplateAndCojoinPost(funToTmpl: Map[FunDef, Expr], prog: Program, + funToPost: Map[FunDef, Expr] = Map(), uniqueIdDisplay : Boolean = true): Program = { + + val funMap = functionsWOFields(prog.definedFunctions).foldLeft(Map[FunDef, FunDef]()) { + case (accMap, fd) if fd.isTheoryOperation => + accMap + (fd -> fd) + case (accMap, fd) => { + val freshId = FreshIdentifier(fd.id.name, fd.returnType, uniqueIdDisplay) + val newfd = new FunDef(freshId, fd.tparams, fd.params, fd.returnType) + accMap.updated(fd, newfd) + } + } + + // FIXME: This with createAnd (which performs simplifications) gives an error during composition. + val mapExpr = mapFunctionsInExpr(funMap) _ + for ((from, to) <- funMap) { + to.fullBody = if (!funToTmpl.contains(from)) { + mapExpr { + from.fullBody match { + case Ensuring(b, post) => + Ensuring(b, + Lambda(Seq(ValDef(getResId(from).get)), + createAnd(Seq(from.getPostWoTemplate, funToPost.getOrElse(from, tru))))) + case fb => + fb + } + } + } else { + val newTmpl = createTemplateFun(funToTmpl(from)) + mapExpr { + from.fullBody match { + case Require(pre, body) => + val toPost = + Lambda(Seq(ValDef(FreshIdentifier("res", from.returnType))), + createAnd(Seq(newTmpl, funToPost.getOrElse(from, tru)))) + Ensuring(Require(pre, body), toPost) + + case Ensuring(Require(pre, body), post) => + Ensuring(Require(pre, body), + Lambda(Seq(ValDef(getResId(from).get)), + createAnd(Seq(from.getPostWoTemplate, newTmpl, funToPost.getOrElse(from, tru))))) + + case Ensuring(body, post) => + Ensuring(body, + Lambda(Seq(ValDef(getResId(from).get)), + createAnd(Seq(from.getPostWoTemplate, newTmpl, funToPost.getOrElse(from, tru))))) + + case body => + val toPost = + Lambda(Seq(ValDef(FreshIdentifier("res", from.returnType))), + createAnd(Seq(newTmpl, funToPost.getOrElse(from, tru)))) + Ensuring(body, toPost) + } + } + } + //copy annotations + from.flags.foreach(to.addFlag(_)) + } + val newprog = copyProgram(prog, (defs: Seq[Definition]) => defs.map { + case fd: FunDef if funMap.contains(fd) => + funMap(fd) + case d => d + }) + newprog + } + + def functionByName(nm: String, prog: Program) = { + prog.definedFunctions.find(fd => fd.id.name == nm) + } + + def functionByFullName(nm: String, prog: Program) = { + prog.definedFunctions.find(fd => fullName(fd)(prog) == nm) + } + + def functionsWOFields(fds: Seq[FunDef]): Seq[FunDef] = { + fds.filter(_.isRealFunction) + } + + def translateExprToProgram(ine: Expr, currProg: Program, newProg: Program): Expr = { + simplePostTransform((e: Expr) => e match { + case FunctionInvocation(TypedFunDef(fd, tps), args) => + functionByName(fullName(fd)(currProg), newProg) match { + case Some(nfd) => + FunctionInvocation(TypedFunDef(nfd, tps), args) + case _ => + throw new IllegalStateException(s"Cannot find translation for ${fd.id.name}") + } + case _ => e + })(ine) + } + + def getFunctionReturnVariable(fd: FunDef) = { + if (fd.hasPostcondition) getResId(fd).get.toVariable + else ResultVariable(fd.returnType) /*FreshIdentifier("res", fd.returnType).toVariable*/ + } + + def getResId(funDef: FunDef): Option[Identifier] = { + funDef.fullBody match { + case Ensuring(_, post) => { + post match { + case Lambda(Seq(ValDef(fromRes, _)), _) => Some(fromRes) + } + } + case _ => None + } + } + + //compute the formal to the actual argument mapping + def formalToActual(call: Call): Map[Expr, Expr] = { + val fd = call.fi.tfd.fd + val resvar = getFunctionReturnVariable(fd) + val argmap: Map[Expr, Expr] = Map(resvar -> call.retexpr) ++ fd.params.map(_.id.toVariable).zip(call.fi.args) + argmap + } +} + +object PredicateUtil { + + /** + * Checks if the input expression has only template variables as free variables + */ + def isTemplateExpr(expr: Expr): Boolean = { + var foundVar = false + simplePostTransform((e: Expr) => e match { + case Variable(id) => { + if (!TemplateIdFactory.IsTemplateIdentifier(id)) + foundVar = true + e + } + case ResultVariable(_) => { + foundVar = true + e + } + case _ => e + })(expr) + + !foundVar + } + + def getTemplateIds(expr: Expr) = { + variablesOf(expr).filter(TemplateIdFactory.IsTemplateIdentifier) + } + + def getTemplateVars(expr: Expr): Set[Variable] = { + getTemplateIds(expr).map(_.toVariable) + } + + /** + * Checks if the expression has real valued sub-expressions. + */ + def hasReals(expr: Expr): Boolean = { + var foundReal = false + simplePostTransform((e: Expr) => e match { + case _ => { + if (e.getType == RealType) + foundReal = true; + e + } + })(expr) + foundReal + } + + /** + * Checks if the expression has real valued sub-expressions. + * Note: important, <, <=, > etc have default int type. + * However, they can also be applied over real arguments + * So check only if all terminals are real + */ + def hasInts(expr: Expr): Boolean = { + var foundInt = false + simplePostTransform((e: Expr) => e match { + case e: Terminal if (e.getType == Int32Type || e.getType == IntegerType) => { + foundInt = true; + e + } + case _ => e + })(expr) + foundInt + } + + def hasMixedIntReals(expr: Expr): Boolean = { + hasInts(expr) && hasReals(expr) + } + + def atomNum(e: Expr): Int = { + var count: Int = 0 + simplePostTransform((e: Expr) => e match { + case And(args) => { + count += args.size + e + } + case Or(args) => { + count += args.size + e + } + case _ => e + })(e) + count + } + + def numUIFADT(e: Expr): Int = { + var count: Int = 0 + simplePostTransform((e: Expr) => e match { + case FunctionInvocation(_, _) | CaseClass(_, _) | Tuple(_) => { + count += 1 + e + } + case _ => e + })(e) + count + } + + def hasCalls(e: Expr) = numUIFADT(e) >= 1 + + def getCallExprs(ine: Expr): Set[Expr] = { + var calls = Set[Expr]() + simplePostTransform((e: Expr) => e match { + case call @ _ if isCallExpr(e) => { + calls += e + call + } + case _ => e + })(ine) + calls + } + + def isCallExpr(e: Expr): Boolean = e match { + case Equals(Variable(_), FunctionInvocation(_, _)) => true + // case Iff(Variable(_),FunctionInvocation(_,_)) => true + case _ => false + } + + def isADTConstructor(e: Expr): Boolean = e match { + case Equals(Variable(_), CaseClass(_, _)) => true + case Equals(Variable(_), Tuple(_)) => true + case _ => false + } + + def isMultFunctions(fd: FunDef) = { + (fd.id.name == "mult" || fd.id.name == "pmult") && + fd.isTheoryOperation + } + + //replaces occurrences of mult by Times + def multToTimes(ine: Expr): Expr = { + simplePostTransform((e: Expr) => e match { + case FunctionInvocation(TypedFunDef(fd, _), args) if isMultFunctions(fd) => { + Times(args(0), args(1)) + } + case _ => e + })(ine) + } + + def createAnd(exprs: Seq[Expr]): Expr = { + val newExprs = exprs.filterNot(conj => conj == tru) + newExprs match { + case Seq() => tru + case Seq(e) => e + case _ => And(newExprs) + } + } + + def createOr(exprs: Seq[Expr]): Expr = { + val newExprs = exprs.filterNot(disj => disj == fls) + newExprs match { + case Seq() => fls + case Seq(e) => e + case _ => Or(newExprs) + } + } + + def isNumericType(t: TypeTree) = t match { + case IntegerType | RealType => true + case _ => false + } +} diff --git a/src/main/scala/leon/invariant/util/Util.scala b/src/main/scala/leon/invariant/util/Util.scala index 5a101569e5832cfa1340508345cac38e74f1e2f1..f8358dec14f22f420a2b19b90dcf9dd9d308d80d 100644 --- a/src/main/scala/leon/invariant/util/Util.scala +++ b/src/main/scala/leon/invariant/util/Util.scala @@ -5,24 +5,10 @@ import purescala.Common._ import purescala.Definitions._ import purescala.Expressions._ import purescala.ExprOps._ -import purescala.Extractors._ import purescala.Types._ -import scala.collection.mutable.{ Set => MutableSet, Map => MutableMap } -import scala.collection.immutable.Stack -import java.io._ -import leon.invariant._ -import java.io._ -import solvers.z3._ -import solvers._ -import invariant.engine._ -import invariant.factories._ -import invariant.structure._ import leon.purescala.PrettyPrintable import leon.purescala.PrinterContext import purescala.PrinterHelpers._ -import FunctionUtils._ -import leon.invariant.templateSolvers.ExtendedUFSolver -import scala.annotation.tailrec object FileCountGUID { var fileCount = 0 @@ -51,46 +37,6 @@ case class ResultVariable(tpe: TypeTree) extends Expr with Terminal with PrettyP } } -//this used to refer to the time steps of a procedure -case class TimeVariable() extends Expr with Terminal with PrettyPrintable { - val getType = IntegerType - override def toString: String = "#time" - def printWith(implicit pctx: PrinterContext) { - p"#time" - } -} - -//this used to refer to the depth of a procedure -case class DepthVariable() extends Expr with Terminal with PrettyPrintable { - val getType = IntegerType - override def toString: String = "#depth" - def printWith(implicit pctx: PrinterContext) { - p"#time" - } -} - -object TVarFactory { - - val temporaries = MutableSet[Identifier]() - //these are dummy identifiers used in 'CaseClassSelector' conversion - val dummyIds = MutableSet[Identifier]() - - def createTemp(name: String, tpe: TypeTree = Untyped): Identifier = { - val freshid = FreshIdentifier(name, tpe, true) - temporaries.add(freshid) - freshid - } - - def createDummy(tpe: TypeTree): Identifier = { - val freshid = FreshIdentifier("dy", tpe, true) - dummyIds.add(freshid) - freshid - } - - def isTemporary(id: Identifier): Boolean = temporaries.contains(id) - def isDummy(id: Identifier): Boolean = dummyIds.contains(id) -} - object Util { val zero = InfiniteIntegerLiteral(0) @@ -98,396 +44,16 @@ object Util { val tru = BooleanLiteral(true) val fls = BooleanLiteral(false) - /** - * Here, we exclude empty units that do not have any modules and empty - * modules that do not have any definitions - */ - def copyProgram(prog: Program, mapdefs: (Seq[Definition] => Seq[Definition])): Program = { - prog.copy(units = prog.units.collect { - case unit if (!unit.defs.isEmpty) => unit.copy(defs = unit.defs.collect { - case module : ModuleDef if (!module.defs.isEmpty) => - module.copy(defs = mapdefs(module.defs)) - case other => other - }) - }) - } - - def createTemplateFun(plainTemp: Expr): FunctionInvocation = { - val tmpl = Lambda(getTemplateIds(plainTemp).toSeq.map(id => ValDef(id)), plainTemp) - val tmplFd = new FunDef(FreshIdentifier("tmpl", FunctionType(Seq(tmpl.getType), BooleanType), false), Seq(), Seq(ValDef(FreshIdentifier("arg", tmpl.getType), - Some(tmpl.getType))), BooleanType) - tmplFd.body = Some(BooleanLiteral(true)) - FunctionInvocation(TypedFunDef(tmplFd, Seq()), Seq(tmpl)) - } - - /** - * This is the default template generator. - * Note: we are not creating template for libraries. - */ - def getOrCreateTemplateForFun(fd: FunDef): Expr = { - val plainTemp = if (fd.hasTemplate) fd.getTemplate - else if (fd.annotations.contains("library")) BooleanLiteral(true) - else { - //just consider all the arguments, return values that are integers - val baseTerms = fd.params.filter((vardecl) => isNumericType(vardecl.getType)).map(_.toVariable) ++ - (if (isNumericType(fd.returnType)) Seq(Util.getFunctionReturnVariable(fd)) - else Seq()) - val lhs = baseTerms.foldLeft(TemplateIdFactory.freshTemplateVar(): Expr)((acc, t) => { - Plus(Times(TemplateIdFactory.freshTemplateVar(), t), acc) - }) - val tempExpr = LessEquals(lhs, InfiniteIntegerLiteral(0)) - tempExpr - } - plainTemp - } - - def mapFunctionsInExpr(funmap: Map[FunDef, FunDef])(ine: Expr): Expr = { - simplePostTransform((e: Expr) => e match { - case FunctionInvocation(tfd, args) if funmap.contains(tfd.fd) => - FunctionInvocation(TypedFunDef(funmap(tfd.fd), tfd.tps), args) - case _ => e - })(ine) - } - - def assignTemplateAndCojoinPost(funToTmpl: Map[FunDef, Expr], prog: Program, - funToPost: Map[FunDef, Expr] = Map(), uniqueIdDisplay : Boolean = true): Program = { - - val funMap = Util.functionsWOFields(prog.definedFunctions).foldLeft(Map[FunDef, FunDef]()) { - case (accMap, fd) if fd.isTheoryOperation => - accMap + (fd -> fd) - case (accMap, fd) => { - val freshId = FreshIdentifier(fd.id.name, fd.returnType, uniqueIdDisplay) - val newfd = new FunDef(freshId, fd.tparams, fd.params, fd.returnType) - accMap.updated(fd, newfd) - } - } - - // FIXME: This with createAnd (which performs simplifications) gives an error during composition. - val mapExpr = mapFunctionsInExpr(funMap) _ - for ((from, to) <- funMap) { - to.fullBody = if (!funToTmpl.contains(from)) { - mapExpr { - from.fullBody match { - case Ensuring(b, post) => - Ensuring(b, - Lambda(Seq(ValDef(Util.getResId(from).get)), - createAnd(Seq(from.getPostWoTemplate, funToPost.getOrElse(from, tru))))) - case fb => - fb - } - } - } else { - val newTmpl = createTemplateFun(funToTmpl(from)) - mapExpr { - from.fullBody match { - case Require(pre, body) => - val toPost = - Lambda(Seq(ValDef(FreshIdentifier("res", from.returnType))), - createAnd(Seq(newTmpl, funToPost.getOrElse(from, tru)))) - Ensuring(Require(pre, body), toPost) - - case Ensuring(Require(pre, body), post) => - Ensuring(Require(pre, body), - Lambda(Seq(ValDef(Util.getResId(from).get)), - createAnd(Seq(from.getPostWoTemplate, newTmpl, funToPost.getOrElse(from, tru))))) - - case Ensuring(body, post) => - Ensuring(body, - Lambda(Seq(ValDef(Util.getResId(from).get)), - createAnd(Seq(from.getPostWoTemplate, newTmpl, funToPost.getOrElse(from, tru))))) - - case body => - val toPost = - Lambda(Seq(ValDef(FreshIdentifier("res", from.returnType))), - createAnd(Seq(newTmpl, funToPost.getOrElse(from, tru)))) - Ensuring(body, toPost) - } - } - } - //copy annotations - from.flags.foreach(to.addFlag(_)) - } - val newprog = Util.copyProgram(prog, (defs: Seq[Definition]) => defs.map { - case fd: FunDef if funMap.contains(fd) => - funMap(fd) - case d => d - }) - newprog - } - - def functionByName(nm: String, prog: Program) = { - prog.definedFunctions.find(fd => fd.id.name == nm) - } - - def functionsWOFields(fds: Seq[FunDef]): Seq[FunDef] = { - fds.filter(_.isRealFunction) - } - - def isNumericExpr(expr: Expr): Boolean = { - expr.getType == IntegerType || - expr.getType == RealType - } - - def getFunctionReturnVariable(fd: FunDef) = { - if (fd.hasPostcondition) getResId(fd).get.toVariable - else ResultVariable(fd.returnType) /*FreshIdentifier("res", fd.returnType).toVariable*/ - } - - //compute the formal to the actual argument mapping - def formalToActual(call: Call): Map[Expr, Expr] = { - val fd = call.fi.tfd.fd - val resvar = getFunctionReturnVariable(fd) - val argmap: Map[Expr, Expr] = Map(resvar -> call.retexpr) ++ fd.params.map(_.id.toVariable).zip(call.fi.args) - argmap - } - - /** - * Checks if the input expression has only template variables as free variables - */ - def isTemplateExpr(expr: Expr): Boolean = { - var foundVar = false - simplePostTransform((e: Expr) => e match { - case Variable(id) => { - if (!TemplateIdFactory.IsTemplateIdentifier(id)) - foundVar = true - e - } - case ResultVariable(_) => { - foundVar = true - e - } - case _ => e - })(expr) - - !foundVar - } - - def getTemplateIds(expr: Expr) = { - variablesOf(expr).filter(TemplateIdFactory.IsTemplateIdentifier) - } - - def getTemplateVars(expr: Expr): Set[Variable] = { - /*var tempVars = Set[Variable]() - postTraversal(e => e match { - case t @ Variable(id) => - if (TemplateIdFactory.IsTemplateIdentifier(id)) - tempVars += t - case _ => - })(expr) - tempVars*/ - getTemplateIds(expr).map(_.toVariable) - } - - /** - * Checks if the expression has real valued sub-expressions. - */ - def hasReals(expr: Expr): Boolean = { - var foundReal = false - simplePostTransform((e: Expr) => e match { - case _ => { - if (e.getType == RealType) - foundReal = true; - e - } - })(expr) - foundReal - } - - /** - * Checks if the expression has real valued sub-expressions. - * Note: important, <, <=, > etc have default int type. - * However, they can also be applied over real arguments - * So check only if all terminals are real - */ - def hasInts(expr: Expr): Boolean = { - var foundInt = false - simplePostTransform((e: Expr) => e match { - case e: Terminal if (e.getType == Int32Type || e.getType == IntegerType) => { - foundInt = true; - e - } - case _ => e - })(expr) - foundInt - } - - def hasMixedIntReals(expr: Expr): Boolean = { - hasInts(expr) && hasReals(expr) - } - def fix[A](f: (A) => A)(a: A): A = { val na = f(a) if (a == na) a else fix(f)(na) } - def atomNum(e: Expr): Int = { - var count: Int = 0 - simplePostTransform((e: Expr) => e match { - case And(args) => { - count += args.size - e - } - case Or(args) => { - count += args.size - e - } - case _ => e - })(e) - count - } - - def numUIFADT(e: Expr): Int = { - var count: Int = 0 - simplePostTransform((e: Expr) => e match { - case FunctionInvocation(_, _) | CaseClass(_, _) | Tuple(_) => { - count += 1 - e - } - case _ => e - })(e) - count - } - - def hasCalls(e: Expr) = numUIFADT(e) >= 1 - - def getCallExprs(ine: Expr): Set[Expr] = { - var calls = Set[Expr]() - simplePostTransform((e: Expr) => e match { - case call @ _ if Util.isCallExpr(e) => { - calls += e - call - } - case _ => e - })(ine) - calls - } - - def isCallExpr(e: Expr): Boolean = e match { - case Equals(Variable(_), FunctionInvocation(_, _)) => true - // case Iff(Variable(_),FunctionInvocation(_,_)) => true - case _ => false - } - - def isADTConstructor(e: Expr): Boolean = e match { - case Equals(Variable(_), CaseClass(_, _)) => true - case Equals(Variable(_), Tuple(_)) => true - case _ => false - } - - def modelToExpr(model: Model): Expr = { - model.foldLeft(tru: Expr)((acc, elem) => { - val (k, v) = elem - val eq = Equals(k.toVariable, v) - if (acc == tru) eq - else And(acc, eq) - }) - } - def gcd(x: Int, y: Int): Int = { if (x == 0) y else gcd(y % x, x) } - def toZ3SMTLIB(expr: Expr, filename: String, - theory: String, ctx: LeonContext, pgm: Program, - useBitvectors: Boolean = false, - bitvecSize: Int = 32) = { - //create new solver, assert constraints and print - val printSol = new ExtendedUFSolver(ctx, pgm) - printSol.assertCnstr(expr) - val writer = new PrintWriter(filename) - writer.println(printSol.ctrsToString(theory)) - printSol.free() - writer.flush() - writer.close() - } - - /** - * A helper function that can be used to hardcode an invariant and see if it unsatifies the paths - */ - def checkInvariant(expr: Expr, ctx: LeonContext, prog: Program): Option[Boolean] = { - val idmap: Map[Expr, Expr] = variablesOf(expr).collect { - case id @ _ if (id.name.toString == "a?") => id.toVariable -> InfiniteIntegerLiteral(6) - case id @ _ if (id.name.toString == "c?") => id.toVariable -> InfiniteIntegerLiteral(2) - }.toMap - //println("found ids: " + idmap.keys) - if (!idmap.keys.isEmpty) { - val newpathcond = replace(idmap, expr) - //check if this is solvable - val solver = SimpleSolverAPI(SolverFactory(() => new ExtendedUFSolver(ctx, prog))) - solver.solveSAT(newpathcond)._1 match { - case Some(true) => { - println("Path satisfiable for a?,c? -->6,2 ") - Some(true) - } - case _ => { - println("Path unsat for a?,c? --> 6,2") - Some(false) - } - } - } else None - } - - def collectUNSATCores(ine: Expr, ctx: LeonContext, prog: Program): Expr = { - var controlVars = Map[Variable, Expr]() - var newEqs = Map[Expr, Expr]() - val solver = new ExtendedUFSolver(ctx, prog) - val newe = simplePostTransform((e: Expr) => e match { - case And(_) | Or(_) => { - val v = TVarFactory.createTemp("a", BooleanType).toVariable - newEqs += (v -> e) - val newe = Equals(v, e) - - //create new variable and add it in disjunction - val cvar = FreshIdentifier("ctrl", BooleanType, true).toVariable - controlVars += (cvar -> newe) - solver.assertCnstr(Or(newe, cvar)) - v - } - case _ => e - })(ine) - //create new variable and add it in disjunction - val cvar = FreshIdentifier("ctrl", BooleanType, true).toVariable - controlVars += (cvar -> newe) - solver.assertCnstr(Or(newe, cvar)) - - val res = solver.checkAssumptions(controlVars.keySet.map(Not.apply _)) - println("Result: " + res) - val coreExprs = solver.getUnsatCore - val simpcores = coreExprs.foldLeft(Seq[Expr]())((acc, coreExp) => { - val Not(cvar @ Variable(_)) = coreExp - val newexp = controlVars(cvar) - //println("newexp: "+newexp) - newexp match { - // case Iff(v@Variable(_),rhs) if(newEqs.contains(v)) => acc - case Equals(v @ Variable(_), rhs) if (v.getType == BooleanType && rhs.getType == BooleanType && newEqs.contains(v)) => acc - case _ => { - acc :+ newexp - } - } - }) - val cores = Util.fix((e: Expr) => replace(newEqs, e))(Util.createAnd(simpcores.toSeq)) - - solver.free - //cores - ExpressionTransformer.unFlatten(cores, - variablesOf(ine).filterNot(TVarFactory.isTemporary _)) - } - - def isMultFunctions(fd: FunDef) = { - (fd.id.name == "mult" || fd.id.name == "pmult") && - fd.isTheoryOperation - } - //replaces occurrences of mult by Times - def multToTimes(ine: Expr): Expr = { - simplePostTransform((e: Expr) => e match { - case FunctionInvocation(TypedFunDef(fd, _), args) if isMultFunctions(fd) => { - Times(args(0), args(1)) - } - case _ => e - })(ine) - } - /** * A cross product with an optional filter */ @@ -499,219 +65,4 @@ object Util { else product } - - def getResId(funDef: FunDef): Option[Identifier] = { - funDef.fullBody match { - case Ensuring(_, post) => { - post match { - case Lambda(Seq(ValDef(fromRes, _)), _) => Some(fromRes) - case _ => - throw new IllegalStateException("Postcondition with multiple return values!") - } - } - case _ => None - } - } - - def createAnd(exprs: Seq[Expr]): Expr = { - val newExprs = exprs.filterNot(conj => conj == tru) - newExprs match { - case Seq() => tru - case Seq(e) => e - case _ => And(newExprs) - } - } - - def createOr(exprs: Seq[Expr]): Expr = { - val newExprs = exprs.filterNot(disj => disj == fls) - newExprs match { - case Seq() => fls - case Seq(e) => e - case _ => Or(newExprs) - } - } - - def isNumericType(t: TypeTree) = t match { - case IntegerType | RealType => true - case _ => false - } - - //tests if the solver uses nlsat - def usesNLSat(solver: AbstractZ3Solver) = { - //check for nlsat - val x = FreshIdentifier("x", RealType).toVariable - val testExpr = Equals(Times(x, x), FractionalLiteral(2, 1)) - solver.assertCnstr(testExpr) - solver.check match { - case Some(true) => true - case _ => false - } - } -} - -/** - * maps all real valued variables and literals to new integer variables/literals and - * performs the reverse mapping - * Note: this should preserve the template identifier property - */ -class RealToInt { - - val bone = BigInt(1) - var realToIntId = Map[Identifier, Identifier]() - var intToRealId = Map[Identifier, Identifier]() - - def mapRealToInt(inexpr: Expr): Expr = { - val transformer = (e: Expr) => e match { - case FractionalLiteral(num, `bone`) => InfiniteIntegerLiteral(num) - case FractionalLiteral(_, _) => throw new IllegalStateException("Real literal with non-unit denominator") - case v @ Variable(realId) if (v.getType == RealType) => { - val newId = realToIntId.getOrElse(realId, { - //note: the fresh identifier has to be a template identifier if the original one is a template identifier - val freshId = if (TemplateIdFactory.IsTemplateIdentifier(realId)) - TemplateIdFactory.freshIdentifier(realId.name, IntegerType) - else - FreshIdentifier(realId.name, IntegerType, true) - - realToIntId += (realId -> freshId) - intToRealId += (freshId -> realId) - freshId - }) - Variable(newId) - } - case _ => e - } - simplePostTransform(transformer)(inexpr) - } - - def unmapModel(model: Model): Model = { - new Model(model.map(pair => { - val (key, value) = if (intToRealId.contains(pair._1)) { - (intToRealId(pair._1), - pair._2 match { - case InfiniteIntegerLiteral(v) => FractionalLiteral(v.toInt, 1) - case _ => pair._2 - }) - } else pair - (key -> value) - }).toMap) - } - - def mapModel(model: Model): Model = { - new Model(model.collect { - case (k, FractionalLiteral(n, bone)) => - (realToIntId(k), InfiniteIntegerLiteral(n)) - case (k, v) => - if (realToIntId.contains(k)) { - (realToIntId(k), v) - } else { - (k, v) - } - }.toMap) - } -} - -class MultiMap[A, B] extends scala.collection.mutable.HashMap[A, scala.collection.mutable.Set[B]] with scala.collection.mutable.MultiMap[A, B] { - /** - * Creates a new map and does not change the existing map - */ - def append(that: MultiMap[A, B]): MultiMap[A, B] = { - val newmap = new MultiMap[A, B]() - this.foreach { case (k, vset) => newmap += (k -> vset) } - that.foreach { - case (k, vset) => vset.foreach(v => newmap.addBinding(k, v)) - } - newmap - } -} - -/** - * A multimap that allows duplicate entries - */ -class OrderedMultiMap[A, B] extends scala.collection.mutable.HashMap[A, scala.collection.mutable.ListBuffer[B]] { - - def addBinding(key: A, value: B): this.type = { - get(key) match { - case None => - val list = new scala.collection.mutable.ListBuffer[B]() - list += value - this(key) = list - case Some(list) => - list += value - } - this - } - - /** - * Creates a new map and does not change the existing map - */ - def append(that: OrderedMultiMap[A, B]): OrderedMultiMap[A, B] = { - val newmap = new OrderedMultiMap[A, B]() - this.foreach { case (k, vlist) => newmap += (k -> vlist) } - that.foreach { - case (k, vlist) => vlist.foreach(v => newmap.addBinding(k, v)) - } - newmap - } - - /** - * Make the value of every key distinct - */ - def distinct: OrderedMultiMap[A, B] = { - val newmap = new OrderedMultiMap[A, B]() - this.foreach { case (k, vlist) => newmap += (k -> vlist.distinct) } - newmap - } } - -/** - * Implements a mapping from Seq[A] to B where Seq[A] - * is stored as a Trie - */ -final class TrieMap[A, B] { - var childrenMap = Map[A, TrieMap[A, B]]() - var dataMap = Map[A, B]() - - @tailrec def addBinding(key: Seq[A], value: B) { - key match { - case Seq() => - throw new IllegalStateException("Key is empty!!") - case Seq(x) => - //add the value to the dataMap - if (dataMap.contains(x)) - throw new IllegalStateException("A mapping for key already exists: " + x + " --> " + dataMap(x)) - else - dataMap += (x -> value) - case head +: tail => //here, tail has at least one element - //check if we have an entry for seq(0) if yes go to the children, if not create one - val child = childrenMap.getOrElse(head, { - val ch = new TrieMap[A, B]() - childrenMap += (head -> ch) - ch - }) - child.addBinding(tail, value) - } - } - - @tailrec def lookup(key: Seq[A]): Option[B] = { - key match { - case Seq() => - throw new IllegalStateException("Key is empty!!") - case Seq(x) => - dataMap.get(x) - case head +: tail => //here, tail has at least one element - childrenMap.get(head) match { - case Some(child) => - child.lookup(tail) - case _ => None - } - } - } -} - -class CounterMap[T] extends scala.collection.mutable.HashMap[T, Int] { - def inc(v: T) = { - if (this.contains(v)) - this(v) += 1 - else this += (v -> 1) - } -} \ No newline at end of file diff --git a/src/main/scala/leon/purescala/CompleteAbstractDefinitions.scala b/src/main/scala/leon/purescala/CompleteAbstractDefinitions.scala deleted file mode 100644 index db508eaf7964d394f5e11b98590beaa1532a9d5b..0000000000000000000000000000000000000000 --- a/src/main/scala/leon/purescala/CompleteAbstractDefinitions.scala +++ /dev/null @@ -1,24 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package purescala - -import Common._ -import Definitions._ -import Expressions._ - -object CompleteAbstractDefinitions extends UnitPhase[Program] { - - val name = "Materialize abstract functions" - val description = "Inject fake choose-like body in abstract definitions" - - def apply(ctx: LeonContext, program: Program) = { - for (u <- program.units; m <- u.modules; fd <- m.definedFunctions; if fd.body.isEmpty) { - val post = fd.postcondition getOrElse ( - Lambda(Seq(ValDef(FreshIdentifier("res", fd.returnType))), BooleanLiteral(true)) - ) - fd.body = Some(Choose(post)) - } - } - -} diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index 200a9e167e8d70e076b9d13fc7c301331e127138..c106d1393bcf793a7fe5d6944ecd533d84a42e36 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -1474,14 +1474,14 @@ object ExprOps { val solver = SimpleSolverAPI(sf) toCheck.forall { cond => - solver.solveSAT(cond) match { - case (Some(false), _) => - true - case (Some(true), model) => - false - case (None, _) => - // Should we be optimistic here? - false + solver.solveSAT(cond)._1 match { + case Some(false) => + true + case Some(true) => + false + case None => + // Should we be optimistic here? + false } } case _ => diff --git a/src/main/scala/leon/purescala/Expressions.scala b/src/main/scala/leon/purescala/Expressions.scala index 7216652a427e6784142a17f69c730fa61e2ff0aa..b834eb9f1270bca728b2cdbaa63bb98a37b0dd49 100644 --- a/src/main/scala/leon/purescala/Expressions.scala +++ b/src/main/scala/leon/purescala/Expressions.scala @@ -886,7 +886,7 @@ object Expressions { /** $encodingof a synthesizable hole in a program. Represented by `???[tpe]` * in Leon source code. * - * A [[Hole]] gets transformed into a [[Choose]] construct during [[leon.synthesis.ConvertHoles the ConvertHoles phase]]. + * A [[Hole]] gets transformed into a [[Choose]] construct during [[leon.synthesis.ConversionPhase the ConvertHoles phase]]. */ case class Hole(tpe: TypeTree, alts: Seq[Expr]) extends Expr with Extractable { val getType = tpe diff --git a/src/main/scala/leon/purescala/FunctionClosure.scala b/src/main/scala/leon/purescala/FunctionClosure.scala index 20dbd1eeced9c951221665b2a3136b2225681c86..65fd1de8a934bb802f57a39f95e17721e6e78f95 100644 --- a/src/main/scala/leon/purescala/FunctionClosure.scala +++ b/src/main/scala/leon/purescala/FunctionClosure.scala @@ -130,7 +130,12 @@ object FunctionClosure extends TransformationPhase { val freshVals = (inner.paramIds ++ free).map{_.freshen}.map(instantiateType(_, tparamsMap)) val freeMap = (inner.paramIds ++ free).zip(freshVals).toMap - val newFd = inner.duplicate(inner.id.freshen, inner.tparams ++ tpFresh, freshVals.map(ValDef(_)), instantiateType(inner.returnType, tparamsMap)) + val newFd = inner.duplicate( + inner.id.freshen, + inner.tparams ++ tpFresh, + freshVals.map(ValDef(_)), + instantiateType(inner.returnType, tparamsMap) + ) newFd.precondition = Some(and(pc, inner.precOrTrue)) val instBody = instantiateType( @@ -154,11 +159,7 @@ object FunctionClosure extends TransformationPhase { override def apply(ctx: LeonContext, program: Program): Program = { val newUnits = program.units.map { u => u.copy(defs = u.defs map { case m: ModuleDef => - ModuleDef( - m.id, - m.definedClasses ++ m.definedFunctions.flatMap(close), - m.isPackageObject - ) + m.copy(defs = m.definedClasses ++ m.definedFunctions.flatMap(close)) case cd => cd })} diff --git a/src/main/scala/leon/purescala/Quantification.scala b/src/main/scala/leon/purescala/Quantification.scala index 38fa0ae9f81190096aa4f19cbd5a5a988dbfb249..1b00ed1b41a5053fb07a94695b00f595d54453ba 100644 --- a/src/main/scala/leon/purescala/Quantification.scala +++ b/src/main/scala/leon/purescala/Quantification.scala @@ -19,7 +19,7 @@ object Quantification { qargs: A => Set[B] ): Seq[Set[A]] = { def rec(oms: Seq[A], mSet: Set[A], qss: Seq[Set[B]]): Seq[Set[A]] = { - if (qss.exists(_ == quantified)) { + if (qss.contains(quantified)) { Seq(mSet) } else { var res = Seq.empty[Set[A]] @@ -126,12 +126,11 @@ object Quantification { ctx.reporter.warning("E-matching isn't possible without matchers!") if (matchers.exists { case (_, args) => - args.exists(arg => arg match { + args.exists{ case QuantificationMatcher(_, _) => false case Variable(id) => false - case _ if (variablesOf(arg) & quantified).nonEmpty => true - case _ => false - }) + case arg => (variablesOf(arg) & quantified).nonEmpty + } }) ctx.reporter.warning("Matcher arguments must have simple form in " + conjunct) val freeMatchers = matchers.collect { case (Variable(id), args) if free(id) => id -> args } @@ -143,7 +142,7 @@ object Quantification { })) } - if (id2Quant.filter(_._2.nonEmpty).groupBy(_._2).size >= 1) + if (id2Quant.filter(_._2.nonEmpty).groupBy(_._2).nonEmpty) ctx.reporter.warning("Multiple matchers must provide bijective matching in " + conjunct) fold[Set[Identifier]] { case (m, children) => diff --git a/src/main/scala/leon/repair/Repairman.scala b/src/main/scala/leon/repair/Repairman.scala index e5a1eaafe1a73c439a846d0c344e489924edf6eb..1f939c96e9ca70c72c22fac6f1b8378864b6458f 100644 --- a/src/main/scala/leon/repair/Repairman.scala +++ b/src/main/scala/leon/repair/Repairman.scala @@ -167,13 +167,12 @@ class Repairman(ctx0: LeonContext, initProgram: Program, fd: FunDef, verifTimeou val timeoutMs = verifTimeoutMs.getOrElse(3000L) val solverf = SolverFactory.getFromSettings(ctx, program).withTimeout(timeoutMs) val vctx = VerificationContext(ctx, program, solverf, reporter) - val vcs = AnalysisPhase.generateVCs(vctx, Seq(fd)) + val vcs = VerificationPhase.generateVCs(vctx, Seq(fd)) try { - val report = AnalysisPhase.checkVCs( + val report = VerificationPhase.checkVCs( vctx, vcs, - checkInParallel = true, stopAfter = Some({ (vc, vr) => vr.isInvalid }) ) diff --git a/src/main/scala/leon/solvers/Model.scala b/src/main/scala/leon/solvers/Model.scala new file mode 100644 index 0000000000000000000000000000000000000000..ab91f594c954805267dd523fd9f82305ab789798 --- /dev/null +++ b/src/main/scala/leon/solvers/Model.scala @@ -0,0 +1,80 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package solvers + +import purescala.Expressions._ +import purescala.Common.Identifier +import purescala.ExprOps._ + +trait AbstractModel[+This <: Model with AbstractModel[This]] + extends scala.collection.IterableLike[(Identifier, Expr), This] { + + protected val mapping: Map[Identifier, Expr] + + def fill(allVars: Iterable[Identifier]): This = { + val builder = newBuilder + builder ++= mapping ++ (allVars.toSet -- mapping.keys).map(id => id -> simplestValue(id.getType)) + builder.result + } + + def ++(mapping: Map[Identifier, Expr]): This = { + val builder = newBuilder + builder ++= this.mapping ++ mapping + builder.result + } + + def filter(allVars: Iterable[Identifier]): This = { + val builder = newBuilder + for (p <- mapping.filterKeys(allVars.toSet)) { + builder += p + } + builder.result + } + + def iterator = mapping.iterator + def seq = mapping.seq + + def asString(implicit ctx: LeonContext) = { + if (mapping.isEmpty) { + "Model()" + } else { + (for ((k,v) <- mapping.toSeq.sortBy(_._1)) yield { + f" ${k.asString}%-20s -> ${v.asString}" + }).mkString("Model(\n", ",\n", ")") + } + } +} + +trait AbstractModelBuilder[+This <: Model with AbstractModel[This]] + extends scala.collection.mutable.Builder[(Identifier, Expr), This] { + + import scala.collection.mutable.MapBuilder + protected val mapBuilder = new MapBuilder[Identifier, Expr, Map[Identifier, Expr]](Map.empty) + + def +=(elem: (Identifier, Expr)): this.type = { + mapBuilder += elem + this + } + + def clear(): Unit = mapBuilder.clear +} + +class Model(protected val mapping: Map[Identifier, Expr]) + extends AbstractModel[Model] + with (Identifier => Expr) { + + def newBuilder = new ModelBuilder + def isDefinedAt(id: Identifier): Boolean = mapping.isDefinedAt(id) + def get(id: Identifier): Option[Expr] = mapping.get(id) + def getOrElse[E >: Expr](id: Identifier, e: E): E = get(id).getOrElse(e) + def apply(id: Identifier): Expr = get(id).getOrElse { throw new IllegalArgumentException } +} + +object Model { + def empty = new Model(Map.empty) +} + +class ModelBuilder extends AbstractModelBuilder[Model] { + def result = new Model(mapBuilder.result) +} diff --git a/src/main/scala/leon/solvers/Solver.scala b/src/main/scala/leon/solvers/Solver.scala index 67ed8eae5c0edd8ade7dd9c12adb90a3d0ec84e0..10902ebbf49a409d19d584b2c57c799fc5487d0e 100644 --- a/src/main/scala/leon/solvers/Solver.scala +++ b/src/main/scala/leon/solvers/Solver.scala @@ -3,84 +3,11 @@ package leon package solvers -import leon.utils.{Position, DebugSectionSolver, Interruptible} +import leon.utils.{DebugSectionSolver, Interruptible} import purescala.Expressions._ -import purescala.Common.{Tree, Identifier} -import purescala.ExprOps._ +import purescala.Common.Tree import verification.VC -trait AbstractModel[+This <: Model with AbstractModel[This]] - extends scala.collection.IterableLike[(Identifier, Expr), This] { - - protected val mapping: Map[Identifier, Expr] - - def fill(allVars: Iterable[Identifier]): This = { - val builder = newBuilder - builder ++= mapping ++ (allVars.toSet -- mapping.keys).map(id => id -> simplestValue(id.getType)) - builder.result - } - - def ++(mapping: Map[Identifier, Expr]): This = { - val builder = newBuilder - builder ++= this.mapping ++ mapping - builder.result - } - - def filter(allVars: Iterable[Identifier]): This = { - val builder = newBuilder - for (p <- mapping.filterKeys(allVars.toSet)) { - builder += p - } - builder.result - } - - def iterator = mapping.iterator - def seq = mapping.seq - - def asString(implicit ctx: LeonContext) = { - if (mapping.isEmpty) { - "Model()" - } else { - (for ((k,v) <- mapping.toSeq.sortBy(_._1)) yield { - f" ${k.asString}%-20s -> ${v.asString}" - }).mkString("Model(\n", ",\n", ")") - } - } -} - -trait AbstractModelBuilder[+This <: Model with AbstractModel[This]] - extends scala.collection.mutable.Builder[(Identifier, Expr), This] { - - import scala.collection.mutable.MapBuilder - protected val mapBuilder = new MapBuilder[Identifier, Expr, Map[Identifier, Expr]](Map.empty) - - def +=(elem: (Identifier, Expr)): this.type = { - mapBuilder += elem - this - } - - def clear(): Unit = mapBuilder.clear -} - -class Model(protected val mapping: Map[Identifier, Expr]) - extends AbstractModel[Model] - with (Identifier => Expr) { - - def newBuilder = new ModelBuilder - def isDefinedAt(id: Identifier): Boolean = mapping.isDefinedAt(id) - def get(id: Identifier): Option[Expr] = mapping.get(id) - def getOrElse[E >: Expr](id: Identifier, e: E): E = get(id).getOrElse(e) - def apply(id: Identifier): Expr = get(id).getOrElse { throw new IllegalArgumentException } -} - -object Model { - def empty = new Model(Map.empty) -} - -class ModelBuilder extends AbstractModelBuilder[Model] { - def result = new Model(mapBuilder.result) -} - trait Solver extends Interruptible { def name: String val context: LeonContext diff --git a/src/main/scala/leon/solvers/combinators/PortfolioSolver.scala b/src/main/scala/leon/solvers/combinators/PortfolioSolver.scala index 79ee4c3cfdf4d2bf6d514df270fef164455c47ac..4bfc179a10bcdb48a697ccd6b8d33ea85161e725 100644 --- a/src/main/scala/leon/solvers/combinators/PortfolioSolver.scala +++ b/src/main/scala/leon/solvers/combinators/PortfolioSolver.scala @@ -40,18 +40,22 @@ class PortfolioSolver[S <: Solver with Interruptible](val context: LeonContext, // solving val fs = solvers.map { s => Future { - val result = s.check - val model: Model = if (result == Some(true)) { - s.getModel - } else { - Model.empty + try { + val result = s.check + val model: Model = if (result == Some(true)) { + s.getModel + } else { + Model.empty + } + (s, result, model) + } catch { + case _: StackOverflowError => + context.reporter.warning("Stack Overflow while running solver.check()!") + (s, None, Model.empty) } - (s, result, model) } } - fs.foreach(_ onFailure { case ex: Throwable => ex.printStackTrace() }) - val result = Future.find(fs)(_._2.isDefined) val res = Await.result(result, Duration.Inf) match { diff --git a/src/main/scala/leon/solvers/isabelle/Component.scala b/src/main/scala/leon/solvers/isabelle/Component.scala index cf8db26a5d5516222665c2769a005b2eeea74f2e..3b75a8c64fb7a68db86fc30456a53d8df8e2626d 100644 --- a/src/main/scala/leon/solvers/isabelle/Component.scala +++ b/src/main/scala/leon/solvers/isabelle/Component.scala @@ -10,7 +10,7 @@ import leon._ object Component extends LeonComponent { - val name = "isabelle" + val name = "Isabelle" val description = "Isabelle solver" val leonBase = diff --git a/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Solver.scala b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Solver.scala index dfcfbcc3fffa1cc9ae190b169ab1f34ad1bf4310..dc267825c8356451944b5fb6b7f9de5d55504232 100644 --- a/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Solver.scala +++ b/src/main/scala/leon/solvers/smtlib/SMTLIBCVC4Solver.scala @@ -29,9 +29,9 @@ class SMTLIBCVC4Solver(context: LeonContext, program: Program) extends SMTLIBSol } object SMTLIBCVC4Component extends LeonComponent { - val name = "cvc4 solver" + val name = "CVC4 solver" - val description = "Solver invoked when --solvers=smt-cvc4" + val description = "Solver invoked with --solvers=smt-cvc4" val optCVC4Options = new LeonOptionDef[Set[String]] { val name = "solver:cvc4" diff --git a/src/main/scala/leon/synthesis/ConversionPhase.scala b/src/main/scala/leon/synthesis/ConversionPhase.scala new file mode 100644 index 0000000000000000000000000000000000000000..d76cff69b1a630718632f70e8684ff2426506cd2 --- /dev/null +++ b/src/main/scala/leon/synthesis/ConversionPhase.scala @@ -0,0 +1,185 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon +package synthesis + +import purescala.Common._ +import purescala.Expressions._ +import purescala.Types._ +import purescala.ExprOps._ +import purescala.Definitions._ +import purescala.Constructors._ + +object ConversionPhase extends UnitPhase[Program] { + val name = "Eliminate holes, withOracle and abstract definitions" + val description = "Convert Holes, withOracle found in bodies and abstract function bodies to equivalent Choose" + + /** + * This phase does 3 things: + * + * 1) Converts a body with "withOracle{ .. }" into a choose construct: + * + * def foo(a: T) = { + * require(..a..) + * withOracle { o => + * expr(a,o) ensuring { x => post(x) } + * } + * } + * + * gets converted into: + * + * def foo(a: T) { + * require(..a..) + * val o = choose { (o) => { + * val res = expr(a, o) + * pred(res) + * } + * expr(a,o) + * } ensuring { res => + * pred(res) + * } + * + * + * 2) Converts a body with "???" into a choose construct: + * + * def foo(a: T) = { + * require(..a..) + * expr(a, ???) + * } ensuring { x => post(x) } + * + * gets converted into: + * + * def foo(a: T) = { + * require(..a..) + * val h = choose ( h' => + * val res = expr(a, h') + * post(res) + * ) + * expr(a, h) + * } ensuring { res => + * post(res) + * } + * + * 3) Completes abstract definitions: + * + * def foo(a: T) = { + * require(..a..) + * ??? + * } ensuring { res => + * post(res) + * } + * + * gets converted to: + * + * def foo(a: T) = { + * require(..a..) + * choose(x => post(x)) + * } + * + */ + + def convert(e : Expr, ctx : LeonContext) : Expr = { + val (pre, body, post) = breakDownSpecs(e) + + // Ensure that holes are not found in pre and/or post conditions + (pre ++ post).foreach { + preTraversal{ + case h : Hole => + ctx.reporter.error(s"Holes are not supported in pre- or postconditions. @ ${h.getPos}") + case wo: WithOracle => + ctx.reporter.error(s"WithOracle expressions are not supported in pre- or postconditions: ${wo.asString(ctx)} @ ${wo.getPos}") + case _ => + } + } + + def toExpr(h: Hole): (Expr, List[Identifier]) = { + h.alts match { + case Seq() => + val h1 = FreshIdentifier("hole", h.getType, true) + (h1.toVariable, List(h1)) + + case Seq(v) => + val h1 = FreshIdentifier("hole", BooleanType, true) + val h2 = FreshIdentifier("hole", h.getType, true) + (IfExpr(h1.toVariable, h2.toVariable, v), List(h1, h2)) + + case exs => + var ids: List[Identifier] = Nil + val ex = exs.init.foldRight(exs.last)({ (e: Expr, r: Expr) => + val h = FreshIdentifier("hole", BooleanType, true) + ids ::= h + IfExpr(h.toVariable, e, r) + }) + + (ex, ids.reverse) + } + } + + body match { + case Some(body) => + var holes = List[Identifier]() + + val withoutHoles = preMap { + case h : Hole => + val (expr, ids) = toExpr(h) + + holes ++= ids + + Some(expr) + case wo @ WithOracle(os, b) => + withoutSpec(b) map { pred => + val chooseOs = os.map(_.freshen) + + val pred = post.getOrElse( // FIXME: We need to freshen variables + Lambda(chooseOs.map(ValDef(_)), BooleanLiteral(true)) + ) + + letTuple(os, Choose(pred), b) + } + case _ => + None + }(body) + + if (holes.nonEmpty) { + val cids: List[Identifier] = holes.map(_.freshen) + val hToFresh = (holes zip cids.map(_.toVariable)).toMap + + val spec = post match { + case Some(post: Lambda) => + val asLet = letTuple(post.args.map(_.id), withoutHoles, post.body) + + Lambda(cids.map(ValDef(_)), replaceFromIDs(hToFresh, asLet)) + + case _ => + Lambda(cids.map(ValDef(_)), BooleanLiteral(true)) + } + + val choose = Choose(spec) + + val newBody = if (holes.size == 1) { + replaceFromIDs(Map(holes.head -> choose), withoutHoles) + } else { + letTuple(holes, choose, withoutHoles) + } + + withPostcondition(withPrecondition(newBody, pre), post) + + } else { + e + } + + case None => + val newPost = post getOrElse Lambda(Seq(ValDef(FreshIdentifier("res", e.getType))), BooleanLiteral(true)) + withPrecondition(Choose(newPost), pre) + } + } + + + def apply(ctx: LeonContext, pgm: Program): Unit = { + // TODO: remove side-effects + for (fd <- pgm.definedFunctions) { + fd.fullBody = convert(fd.fullBody,ctx) + } + } + +} diff --git a/src/main/scala/leon/synthesis/ConvertHoles.scala b/src/main/scala/leon/synthesis/ConvertHoles.scala deleted file mode 100644 index a53ff8672082d03873141e21c1f11141281095fd..0000000000000000000000000000000000000000 --- a/src/main/scala/leon/synthesis/ConvertHoles.scala +++ /dev/null @@ -1,131 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package synthesis - -import purescala.Common._ -import purescala.Expressions._ -import purescala.Types._ -import purescala.ExprOps._ -import purescala.Definitions._ -import purescala.Constructors._ - -object ConvertHoles extends TransformationPhase { - val name = "Convert Holes to Choose" - val description = "Convert Holes found in bodies to equivalent Choose" - - /** - * This phase converts a body with "withOracle{ .. }" into a choose construct: - * - * def foo(a: T) = { - * require(..a..) - * expr(a, ???) - * } ensuring { x => post(x) } - * - * gets converted into: - * - * def foo(a: T) { - * require(..a..) - * val h = choose ( h' => - * val res = expr(a, h') - * post(res) - * ) - * expr(a, h) - * } ensuring { res => - * post(res) - * } - * - */ - - def convertHoles(e : Expr, ctx : LeonContext) : Expr = { - val (pre, body, post) = breakDownSpecs(e) - - // Ensure that holes are not found in pre and/or post conditions - (pre ++ post).foreach { - preTraversal{ - case h : Hole => - ctx.reporter.error("Holes are not supported in preconditions. @"+ h.getPos) - case _ => - } - } - - body match { - case Some(body) => - var holes = List[Identifier]() - - val withoutHoles = preMap { - case h : Hole => - val (expr, ids) = toExpr(h) - - holes ++= ids - - Some(expr) - case _ => - None - }(body) - - if (holes.nonEmpty) { - val cids: List[Identifier] = holes.map(_.freshen) - val hToFresh = (holes zip cids.map(_.toVariable)).toMap - - val spec = post match { - case Some(post: Lambda) => - val asLet = letTuple(post.args.map(_.id), withoutHoles, post.body) - - Lambda(cids.map(ValDef(_)), replaceFromIDs(hToFresh, asLet)) - - case _ => - Lambda(cids.map(ValDef(_)), BooleanLiteral(true)) - } - - val choose = Choose(spec) - - val newBody = if (holes.size == 1) { - replaceFromIDs(Map(holes.head -> choose), withoutHoles) - } else { - letTuple(holes, choose, withoutHoles) - } - - withPostcondition(withPrecondition(newBody, pre), post) - - } else { - e - } - - case None => e - } - } - - - def apply(ctx: LeonContext, pgm: Program): Program = { - // TODO: remove side-effects - for (fd <- pgm.definedFunctions) { - fd.fullBody = convertHoles(fd.fullBody,ctx) - } - - pgm - } - - def toExpr(h: Hole): (Expr, List[Identifier]) = { - h.alts match { - case Seq() => - val h1 = FreshIdentifier("hole", h.getType, true) - (h1.toVariable, List(h1)) - - case Seq(v) => - val h1 = FreshIdentifier("hole", BooleanType, true) - val h2 = FreshIdentifier("hole", h.getType, true) - (IfExpr(h1.toVariable, h2.toVariable, v), List(h1, h2)) - - case exs => - var ids: List[Identifier] = Nil - val ex = exs.init.foldRight(exs.last)({ (e: Expr, r: Expr) => - val h = FreshIdentifier("hole", BooleanType, true) - ids ::= h - IfExpr(h.toVariable, e, r) - }) - - (ex, ids.reverse) - } - } -} diff --git a/src/main/scala/leon/synthesis/ConvertWithOracle.scala b/src/main/scala/leon/synthesis/ConvertWithOracle.scala deleted file mode 100644 index ac3785c43e454431ebf18ecc2df4dcec78a6a16b..0000000000000000000000000000000000000000 --- a/src/main/scala/leon/synthesis/ConvertWithOracle.scala +++ /dev/null @@ -1,88 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon -package synthesis - -import purescala.Expressions._ -import purescala.ExprOps._ -import purescala.Definitions._ -import purescala.Constructors._ - -object ConvertWithOracle extends TransformationPhase { - val name = "Convert WithOracle to Choose" - val description = "Convert WithOracle found in bodies to equivalent Choose" - - /** - * This phase converts a body with "withOracle{ .. }" into a choose construct: - * - * def foo(a: T) = { - * require(..a..) - * withOracle { o => - * expr(a,o) ensuring { x => post(x) } - * } - * } - * - * gets converted into: - * - * def foo(a: T) { - * require(..a..) - * val o = choose { (o) => { - * val res = expr(a, o) - * pred(res) - * } - * expr(a,o) - * } ensuring { res => - * pred(res) - * } - * - */ - def apply(ctx: LeonContext, pgm: Program): Program = { - // TODO: Remove side-effects - - pgm.definedFunctions.foreach(fd => { - if (fd.hasBody) { - val body = preMap { - case wo @ WithOracle(os, b) => - withoutSpec(b) match { - case Some(pred) => - val chooseOs = os.map(_.freshen) - - val pred = postconditionOf(b) match { - case Some(post) => - post // FIXME we need to freshen variables - case None => - Lambda(chooseOs.map(ValDef(_)), BooleanLiteral(true)) - } - - Some(letTuple(os, Choose(pred), b)) - case None => - None - } - case _ => None - }(fd.body.get) - - fd.body = Some(body) - } - - // Ensure that holes are not found in pre and/or post conditions - fd.precondition.foreach { - preTraversal{ - case _: WithOracle => - ctx.reporter.error("WithOracle expressions are not supported in preconditions. (function "+fd.id.asString(ctx)+")") - case _ => - } - } - - fd.postcondition.foreach { - preTraversal{ - case _: WithOracle => - ctx.reporter.error("WithOracle expressions are not supported in postconditions. (function "+fd.id.asString(ctx)+")") - case _ => - } - } - - }) - - pgm - } -} diff --git a/src/main/scala/leon/synthesis/Synthesizer.scala b/src/main/scala/leon/synthesis/Synthesizer.scala index 7ed4d56a8a74e5a16301026ff2328ba8243d8716..54e305adf11ebc9314c262b4be5b203df8337771 100644 --- a/src/main/scala/leon/synthesis/Synthesizer.scala +++ b/src/main/scala/leon/synthesis/Synthesizer.scala @@ -70,7 +70,7 @@ class Synthesizer(val context : LeonContext, } def validateSolution(search: Search, sol: Solution, timeout: Duration): (Solution, Boolean) = { - import verification.AnalysisPhase._ + import verification.VerificationPhase._ import verification.VerificationContext reporter.info("Solution requires validation") diff --git a/src/main/scala/leon/transformations/InstProgSimplifier.scala b/src/main/scala/leon/transformations/InstProgSimplifier.scala index ff7862db1c535421db229ede3dda6efecad93713..ab650449b58df9897fb89191d3d10bc065b54557 100644 --- a/src/main/scala/leon/transformations/InstProgSimplifier.scala +++ b/src/main/scala/leon/transformations/InstProgSimplifier.scala @@ -9,7 +9,10 @@ import purescala.ExprOps._ import purescala.Types._ import leon.purescala.ScalaPrinter import leon.utils._ -import invariant.util.Util._ +import invariant.util._ +import Util._ +import ProgramUtil._ +import PredicateUtil._ import invariant.util.ExpressionTransformer._ import invariant.structure.FunctionUtils._ import invariant.util.LetTupleSimplification._ diff --git a/src/main/scala/leon/transformations/InstrumentationUtil.scala b/src/main/scala/leon/transformations/InstrumentationUtil.scala index 427cac483cef7d5ee2b45198a71c1154ee1af999..2c461e466298614ebae30df8a33ff901fea72fa5 100644 --- a/src/main/scala/leon/transformations/InstrumentationUtil.scala +++ b/src/main/scala/leon/transformations/InstrumentationUtil.scala @@ -8,6 +8,11 @@ import purescala.ExprOps._ import purescala.Extractors._ import purescala.Types._ import leon.utils.Library +import invariant.util._ +import invariant.util._ +import Util._ +import ProgramUtil._ +import PredicateUtil._ sealed abstract class Instrumentation { val getType: TypeTree @@ -73,7 +78,7 @@ object InstUtil { } def getInstMap(fd: FunDef) = { - val resvar = invariant.util.Util.getResId(fd).get.toVariable // note: every instrumented function has a postcondition + val resvar = getResId(fd).get.toVariable // note: every instrumented function has a postcondition val insts = fd.id.name.split("-").tail // split the name of the function w.r.t '-' (insts.zipWithIndex).foldLeft(Map[Expr, String]()) { case (acc, (instName, i)) => @@ -82,7 +87,7 @@ object InstUtil { } def getInstExpr(fd: FunDef, inst: Instrumentation) = { - val resvar = invariant.util.Util.getResId(fd).get.toVariable // note: every instrumented function has a postcondition + val resvar = getResId(fd).get.toVariable // note: every instrumented function has a postcondition val insts = fd.id.name.split("-").tail // split the name of the function w.r.t '-' val index = insts.indexOf(inst.name) if (index >= 0) @@ -108,6 +113,8 @@ object InstUtil { } def replaceInstruVars(e: Expr, fd: FunDef): Expr = { - replace(getInstVariableMap(fd), e) + val resvar = getResId(fd).get.toVariable + val newres = FreshIdentifier(resvar.id.name, resvar.getType).toVariable + replace(getInstVariableMap(fd) + (TupleSelect(resvar, 1) -> newres), e) } } diff --git a/src/main/scala/leon/transformations/IntToRealProgram.scala b/src/main/scala/leon/transformations/IntToRealProgram.scala index dc7ceb5a64ea7d15fedc48bc3fe6c40a7607555b..21229282b398fcc10cd9548ddaead2f25d8087a3 100644 --- a/src/main/scala/leon/transformations/IntToRealProgram.scala +++ b/src/main/scala/leon/transformations/IntToRealProgram.scala @@ -10,7 +10,10 @@ import purescala.Types._ import leon.purescala.ScalaPrinter import invariant.factories._ -import invariant.util.Util._ +import invariant.util._ +import Util._ +import ProgramUtil._ +import PredicateUtil._ import invariant.structure._ abstract class ProgramTypeTransformer { diff --git a/src/main/scala/leon/transformations/NonlinearityEliminationPhase.scala b/src/main/scala/leon/transformations/NonlinearityEliminationPhase.scala index 77b88f71e557d2bdb90470f8e684168574f4d1bc..d17968dc05b789b936554b9c10dbde22ac360dfb 100644 --- a/src/main/scala/leon/transformations/NonlinearityEliminationPhase.scala +++ b/src/main/scala/leon/transformations/NonlinearityEliminationPhase.scala @@ -2,7 +2,10 @@ package leon package transformations import invariant.factories._ -import invariant.util.Util._ +import invariant.util._ +import Util._ +import ProgramUtil._ +import PredicateUtil._ import invariant.structure.FunctionUtils._ import purescala.ScalaPrinter @@ -147,7 +150,6 @@ class NonlinearityEliminator(skipAxioms: Boolean, domain: TypeTree) { Some(replaceFun(simpBody)) } else None - //add a new postcondition newfd.postcondition = if (fd.postcondition.isDefined) { //we need to handle template and postWoTemplate specially diff --git a/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala b/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala index 20e3aa0e2bb7912992821e3cb2ced21985820f89..87c5795838b460314d19805cbb8ca6caae4ee233 100644 --- a/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala +++ b/src/main/scala/leon/transformations/SerialInstrumentationPhase.scala @@ -10,6 +10,9 @@ import purescala.Types._ import leon.purescala.ScalaPrinter import leon.utils._ import invariant.util._ +import Util._ +import ProgramUtil._ +import PredicateUtil._ import invariant.util.CallGraphUtil import invariant.structure.FunctionUtils._ import scala.collection.mutable.{Map => MutableMap} @@ -68,7 +71,7 @@ class SerialInstrumenter(ctx: LeonContext, program: Program) { //create new functions. Augment the return type of a function iff the postcondition uses //the instrumentation variable or if the function is transitively called from such a function //note: need not instrument fields - val funMap = Util.functionsWOFields(program.definedFunctions).foldLeft(Map[FunDef, FunDef]()) { + val funMap = functionsWOFields(program.definedFunctions).foldLeft(Map[FunDef, FunDef]()) { case (accMap, fd: FunDef) if fd.isTheoryOperation => accMap + (fd -> fd) case (accMap, fd) => { @@ -160,7 +163,7 @@ class SerialInstrumenter(ctx: LeonContext, program: Program) { else List() }.toList.distinct - val newprog = Util.copyProgram(program, (defs: Seq[Definition]) => + val newprog = copyProgram(program, (defs: Seq[Definition]) => defs.map { case fd: FunDef if funMap.contains(fd) => funMap(fd) diff --git a/src/main/scala/leon/utils/PreprocessingPhase.scala b/src/main/scala/leon/utils/PreprocessingPhase.scala index 38b770ba19a468977cf8018c2ac7d438140525a5..87d1003150995eb236e5f3abb738450516c5b7f7 100644 --- a/src/main/scala/leon/utils/PreprocessingPhase.scala +++ b/src/main/scala/leon/utils/PreprocessingPhase.scala @@ -7,11 +7,10 @@ import leon.purescala._ import leon.purescala.Definitions.Program import leon.purescala.Quantification.CheckForalls import leon.solvers.isabelle.AdaptationPhase -import leon.synthesis.{ConvertWithOracle, ConvertHoles} import leon.verification.InjectAsserts -import leon.xlang.XLangDesugaringPhase +import leon.xlang.{NoXLangFeaturesChecking, XLangDesugaringPhase} -class PreprocessingPhase(private val desugarXLang: Boolean = false) extends LeonPhase[Program, Program] { +class PreprocessingPhase(desugarXLang: Boolean = false) extends LeonPhase[Program, Program] { val name = "preprocessing" val description = "Various preprocessings on Leon programs" @@ -26,13 +25,18 @@ class PreprocessingPhase(private val desugarXLang: Boolean = false) extends Leon } } + val checkX = if (desugarXLang) { + NoopPhase[Program]() + } else { + NoXLangFeaturesChecking + } + val pipeBegin = debugTrees("Program after extraction") andThen + checkX andThen MethodLifting andThen TypingPhase andThen - ConvertWithOracle andThen - ConvertHoles andThen - CompleteAbstractDefinitions andThen + synthesis.ConversionPhase andThen CheckADTFieldsTypes andThen InjectAsserts andThen InliningPhase andThen @@ -42,7 +46,7 @@ class PreprocessingPhase(private val desugarXLang: Boolean = false) extends Leon XLangDesugaringPhase andThen debugTrees("Program after xlang desugaring") } else { - xlang.NoXLangFeaturesChecking + NoopPhase[Program]() } val phases = diff --git a/src/main/scala/leon/verification/VerificationCondition.scala b/src/main/scala/leon/verification/VerificationCondition.scala index 33511b23c520f980969934e50a9965a3378816e3..b138559afc861169030cced660a622c91410afab 100644 --- a/src/main/scala/leon/verification/VerificationCondition.scala +++ b/src/main/scala/leon/verification/VerificationCondition.scala @@ -5,7 +5,6 @@ package leon.verification import leon.purescala.Expressions._ import leon.purescala.Definitions._ import leon.purescala.PrettyPrinter -import leon.purescala.Common._ import leon.utils.Positioned import leon.solvers._ diff --git a/src/main/scala/leon/verification/VerificationContext.scala b/src/main/scala/leon/verification/VerificationContext.scala index 42d3eba6242ac653b1d5b831dfcdb158acd2789d..2aaa910ab6450c0de300e36ec42ad71d7b073d84 100644 --- a/src/main/scala/leon/verification/VerificationContext.scala +++ b/src/main/scala/leon/verification/VerificationContext.scala @@ -11,4 +11,6 @@ case class VerificationContext ( program: Program, solverFactory: SolverFactory[Solver], reporter: Reporter -) +) { + val checkInParallel: Boolean = context.findOptionOrDefault(VerificationPhase.optParallelVCs) +} diff --git a/src/main/scala/leon/verification/AnalysisPhase.scala b/src/main/scala/leon/verification/VerificationPhase.scala similarity index 89% rename from src/main/scala/leon/verification/AnalysisPhase.scala rename to src/main/scala/leon/verification/VerificationPhase.scala index 8c6c7d0cef4441a4d62c57214a9cdb39877a1ed2..21119c4e2728c99850e3af0d1c12e498866f771e 100644 --- a/src/main/scala/leon/verification/AnalysisPhase.scala +++ b/src/main/scala/leon/verification/VerificationPhase.scala @@ -7,12 +7,17 @@ import purescala.Definitions._ import purescala.ExprOps._ import scala.concurrent.duration._ +import java.lang.System import solvers._ -object AnalysisPhase extends SimpleLeonPhase[Program,VerificationReport] { - val name = "Analysis" - val description = "Leon Verification" +object VerificationPhase extends SimpleLeonPhase[Program,VerificationReport] { + val name = "Verification" + val description = "Verification of function contracts" + + val optParallelVCs = LeonFlagOptionDef("parallel", "Check verification conditions in parallel", default = false) + + override val definedOptions: Set[LeonOptionDef[Any]] = Set(optParallelVCs) implicit val debugSection = utils.DebugSectionVerification @@ -89,7 +94,6 @@ object AnalysisPhase extends SimpleLeonPhase[Program,VerificationReport] { def checkVCs( vctx: VerificationContext, vcs: Seq[VC], - checkInParallel: Boolean = false, stopAfter: Option[(VC, VCResult) => Boolean] = None ): VerificationReport = { val interruptManager = vctx.context.interruptManager @@ -98,7 +102,7 @@ object AnalysisPhase extends SimpleLeonPhase[Program,VerificationReport] { val initMap: Map[VC, Option[VCResult]] = vcs.map(v => v -> None).toMap - val results = if (checkInParallel) { + val results = if (vctx.checkInParallel) { for (vc <- vcs.par if !stop) yield { val r = checkVC(vctx, vc) if (interruptManager.isInterrupted) interruptManager.recoverInterrupt() @@ -168,6 +172,9 @@ object AnalysisPhase extends SimpleLeonPhase[Program,VerificationReport] { } reporter.synchronized { + if (vctx.checkInParallel) { + reporter.info(s" - Result for '${vc.kind}' VC for ${vc.fd.id} @${vc.getPos}:") + } res.report(vctx) } diff --git a/src/sphinx/options.rst b/src/sphinx/options.rst index c5495f40a99086a3accddd87a7f74fc2769a99a1..5648b042c47f3a252672d698d496a2b3309221a8 100644 --- a/src/sphinx/options.rst +++ b/src/sphinx/options.rst @@ -18,7 +18,7 @@ Choosing which Leon feature to use ---------------------------------- The first group of options determine which feature of Leon will be used. -These options are mutually exclusive. By default, ``--verify`` is chosen. +These options are mutually exclusive (except when noted). By default, ``--verify`` is chosen. * ``--eval`` @@ -62,7 +62,7 @@ These options are mutually exclusive. By default, ``--verify`` is chosen. Additional top-level options ---------------------------- -These options are available by all Leon components: +These options are available to all Leon components: * ``--debug=d1,d2,...`` @@ -197,49 +197,19 @@ These options are available by all Leon components: Support for additional language constructs described in :ref:`xlang`. These constructs are desugared into :ref:`purescala` before other operations. -Invariant Inference -------------------- - -These options are to be used in conjuction with ``--inferInv`` and ``--instrument``. - -* ``--cegis`` - - Use cegis instead of farkas - -* ``--disableInfer`` - - Disable automatic inference of auxiliary invariants - -* ``--fullunroll`` - - Unroll all calls in every unroll step - -* ``--inferTemp`` - - Infer templates by enumeration - -* ``--minbounds`` - - Tighten the inferred time bounds - -* ``--stats-suffix=s`` - - Specifies the suffix of the statistics file - -* ``--usereals`` - - Interpret the input program as a program over real numbers - -* ``--wholeprogram`` +Additional Options (by component) +--------------------------------- - Perform a non-modular whole program analysis +The following options relate to specific components in Leon. Bear in mind +that related components might still use these options, e.g. repair will use +synthesis options and verification options. -* ``--withmult`` +Verification +************ - Do not convert multiplication into a recursive function inside verification conditions +* ``--parallel`` -Additional Options (by component) ---------------------------------- + Check verification conditions in parallel. File Output *********** @@ -259,8 +229,6 @@ Code Extraction Synthesis ********* -These options are also used by repair during the synthesis stage. - * ``--cegis:opttimeout`` Consider a time-out of CE-search as untrusted solution. @@ -366,3 +334,45 @@ Isabelle library when verifiying a Leon source file. Keeping it enabled prevents unsound proofs when postconditions or mappings in the library are wrong. When disabled, a warning is printed. + +Invariant Inference +******************* + +These options are to be used in conjunction with ``--inferInv`` and ``--instrument``. + +* ``--cegis`` + + Use cegis instead of farkas + +* ``--disableInfer`` + + Disable automatic inference of auxiliary invariants + +* ``--fullunroll`` + + Unroll all calls in every unroll step + +* ``--inferTemp`` + + Infer templates by enumeration + +* ``--minbounds`` + + Tighten the inferred time bounds + +* ``--stats-suffix=s`` + + Specifies the suffix of the statistics file + +* ``--usereals`` + + Interpret the input program as a program over real numbers + +* ``--wholeprogram`` + + Perform a non-modular whole program analysis + +* ``--withmult`` + + Do not convert multiplication into a recursive function inside verification conditions + diff --git a/src/test/scala/leon/regression/orb/OrbRegressionSuite.scala b/src/test/scala/leon/regression/orb/OrbRegressionSuite.scala index 24bd55af32bb3f5f1f2e27f1f9e4913f2a406497..85c898d81548800735be9cdf6b1b718b1e052a29 100644 --- a/src/test/scala/leon/regression/orb/OrbRegressionSuite.scala +++ b/src/test/scala/leon/regression/orb/OrbRegressionSuite.scala @@ -18,46 +18,50 @@ class OrbRegressionSuite extends LeonRegressionSuite { } } - private def testInference(f: File, bound: Int) { + private def testInference(f: File, bound: Option[Int] = None) { - val ctx = createLeonContext("--inferInv", "--minbounds", "--timeout="+bound) + val ctx = createLeonContext("--inferInv") val beginPipe = leon.frontends.scalac.ExtractionPhase andThen new leon.utils.PreprocessingPhase val (ctx2, program) = beginPipe.run(ctx, f.getAbsolutePath :: Nil) val processPipe = InferInvariantsPhase val (ctx3, report) = processPipe.run(ctx2, program) - val fails = report.conditions.filterNot(_.invariant.isDefined) - if (!fails.isEmpty) - fail(s"Inference failed for functions ${fails.map(_.fd).mkString("\n")}") + if (report.conditions.isEmpty) + fail(s"Nothing was solved") + else { + val fails = report.conditions.filterNot(_.prettyInv.isDefined) + if (!fails.isEmpty) + fail(s"Inference failed for functions ${fails.map(_.fd).mkString("\n")}") + } } forEachFileIn("regression/orb/timing") { f => test("Timing: " + f.getName) { - testInference(f, 50) + testInference(f) } } forEachFileIn("regression/orb/stack/") { f => test("Stack: " + f.getName) { - testInference(f, 50) + testInference(f) } } forEachFileIn("regression/orb//depth") { f => test("Depth: " + f.getName) { - testInference(f, 50) + testInference(f) } } forEachFileIn("regression/orb/numerical") { f => test("Numerical: " + f.getName) { - testInference(f, 50) + testInference(f) } } forEachFileIn("regression/orb/combined/") { f => test("Multiple Instrumentations: " + f.getName) { - testInference(f, 50) + testInference(f) } } } diff --git a/src/test/scala/leon/regression/repair/RepairSuite.scala b/src/test/scala/leon/regression/repair/RepairSuite.scala index 9fd15b6d4d97f1b8172a5f3704be66c4abd1fffa..50b845f923e2356d1ce4c0c9a27692a78cd30ce5 100644 --- a/src/test/scala/leon/regression/repair/RepairSuite.scala +++ b/src/test/scala/leon/regression/repair/RepairSuite.scala @@ -7,6 +7,7 @@ import leon.test._ import leon.utils._ import leon.frontends.scalac.ExtractionPhase import leon.repair._ +import leon.verification.VerificationPhase class RepairSuite extends LeonRegressionSuite { val pipeline = ExtractionPhase andThen @@ -34,7 +35,8 @@ class RepairSuite extends LeonRegressionSuite { interruptManager = new InterruptManager(reporter), options = Seq( LeonOption(SharedOptions.optFunctions)(Seq(fileToFun(name))), - LeonOption(SharedOptions.optTimeout)(60L) + LeonOption(VerificationPhase.optParallelVCs)(true), + LeonOption(SharedOptions.optTimeout)(180L) ) ) diff --git a/src/test/scala/leon/regression/testcases/TestCasesCompile.scala b/src/test/scala/leon/regression/testcases/TestCasesCompile.scala index 994a619013f38a6ce51e1b15fcdf9d5ef49cc763..333413721e1b1d1e6f058664997ec53209a0d886 100644 --- a/src/test/scala/leon/regression/testcases/TestCasesCompile.scala +++ b/src/test/scala/leon/regression/testcases/TestCasesCompile.scala @@ -4,11 +4,10 @@ package leon.regression.testcases import leon._ import leon.test._ -import org.scalatest.time.SpanSugar._ import java.io.File -import org.scalatest.ParallelTestExecution -class TestCasesCompile extends LeonRegressionSuite { +abstract class TestCasesCompile(testDir: String) extends LeonRegressionSuite { + val pipeline = frontends.scalac.ExtractionPhase andThen new utils.PreprocessingPhase(desugarXLang = true) private def filesIn(path : String): Seq[File] = { @@ -19,11 +18,7 @@ class TestCasesCompile extends LeonRegressionSuite { val baseDir = "regression/testcases/" - val allTests = (filesIn(baseDir+"repair/") ++ - filesIn(baseDir+"runtime/") ++ - filesIn(baseDir+"synthesis/") ++ - filesIn(baseDir+"verification/") ++ - filesIn(baseDir+"web/")).sortBy(_.getAbsolutePath) + val allTests = filesIn(baseDir + testDir) allTests.foreach { f => @@ -45,3 +40,9 @@ class TestCasesCompile extends LeonRegressionSuite { } } } + +class TestcasesCompile1 extends TestCasesCompile("repair/") +class TestcasesCompile2 extends TestCasesCompile("runtime/") +class TestcasesCompile3 extends TestCasesCompile("synthesis/") +class TestcasesCompile4 extends TestCasesCompile("verification/") +class TestcasesCompile5 extends TestCasesCompile("web/") \ No newline at end of file diff --git a/src/test/scala/leon/regression/verification/LibraryVerificationSuite.scala b/src/test/scala/leon/regression/verification/LibraryVerificationSuite.scala index bd44226988a11516cfadb4eec35174972662d263..d350bd04b4c68f7838676048b47b085b5d54e4b8 100644 --- a/src/test/scala/leon/regression/verification/LibraryVerificationSuite.scala +++ b/src/test/scala/leon/regression/verification/LibraryVerificationSuite.scala @@ -6,13 +6,13 @@ import leon._ import leon.test._ import leon.frontends.scalac.ExtractionPhase import leon.utils.PreprocessingPhase -import leon.verification.AnalysisPhase +import leon.verification.VerificationPhase class LibraryVerificationSuite extends LeonRegressionSuite { test("Verify the library") { val pipeline = ExtractionPhase andThen new PreprocessingPhase andThen - AnalysisPhase + VerificationPhase val ctx = Main.processOptions(Seq("--functions=_")).copy(reporter = new TestSilentReporter()) diff --git a/src/test/scala/leon/regression/verification/NewSolversSuite.scala b/src/test/scala/leon/regression/verification/NewSolversSuite.scala index 6eac7183e9244661e024fdaa9f9cc770c16f7768..6065d8232341060f0f0eeae735961ae8c5e73da3 100644 --- a/src/test/scala/leon/regression/verification/NewSolversSuite.scala +++ b/src/test/scala/leon/regression/verification/NewSolversSuite.scala @@ -4,7 +4,7 @@ package leon.regression.verification import _root_.smtlib.interpreters._ import leon._ -import leon.verification.AnalysisPhase +import leon.verification.VerificationPhase /* @EK: Disabled for now as many tests fail class NewSolversSuite extends VerificationSuite { diff --git a/src/test/scala/leon/regression/verification/PureScalaVerificationSuite.scala b/src/test/scala/leon/regression/verification/PureScalaVerificationSuite.scala deleted file mode 100644 index c54ec2b5908dc46f68880c598034518653bbc512..0000000000000000000000000000000000000000 --- a/src/test/scala/leon/regression/verification/PureScalaVerificationSuite.scala +++ /dev/null @@ -1,46 +0,0 @@ -/* Copyright 2009-2015 EPFL, Lausanne */ - -package leon.regression -package verification - -import _root_.smtlib.interpreters._ - -// If you add another regression test, make sure it contains one object whose name matches the file name -// This is because we compile all tests from each folder separately. -class PureScalaVerificationSuite extends VerificationSuite { - - val testDir = "regression/verification/purescala/" - - val optionVariants: List[List[String]] = { - val isZ3Available = try { - Z3Interpreter.buildDefault.free() - true - } catch { - case e: java.io.IOException => - false - } - - val isCVC4Available = try { - CVC4Interpreter.buildDefault.free() - true - } catch { - case e: java.io.IOException => - false - } - - List( - List("--feelinglucky"), - List("--codegen", "--evalground", "--feelinglucky"), - List("--solvers=fairz3,enum", "--codegen", "--evalground", "--feelinglucky") - ) ++ ( - if (isZ3Available) List( - List("--solvers=smt-z3", "--feelinglucky") - ) else Nil - ) ++ ( - if (isCVC4Available) List( - List("--solvers=smt-cvc4", "--feelinglucky") - ) else Nil - ) - } - -} diff --git a/src/test/scala/leon/regression/verification/VerificationSuite.scala b/src/test/scala/leon/regression/verification/VerificationSuite.scala index 48fdd416931ffefefed6c3ee1447320a1e185f48..b23fb0c4e3516121a1489752ed89dcb0835ee2d2 100644 --- a/src/test/scala/leon/regression/verification/VerificationSuite.scala +++ b/src/test/scala/leon/regression/verification/VerificationSuite.scala @@ -6,7 +6,7 @@ import leon._ import leon.test._ import leon.utils.UniqueCounter -import leon.verification.{AnalysisPhase, VerificationReport} +import leon.verification.{VerificationPhase, VerificationReport} import leon.purescala.Definitions.Program import leon.frontends.scalac.ExtractionPhase import leon.utils.PreprocessingPhase @@ -15,8 +15,8 @@ import leon.xlang.FixReportLabels import org.scalatest.{Reporter => _, _} -// If you add another regression test, make sure it contains one object whose name matches the file name -// This is because we compile all tests from each folder separately. +// If you add another regression test, make sure it contains exactly one object, whose name matches the file name. +// This is because we compile all tests from each folder together. trait VerificationSuite extends LeonRegressionSuite { val optionVariants: List[List[String]] @@ -38,7 +38,7 @@ trait VerificationSuite extends LeonRegressionSuite { val analysis = (if (isabelle) AdaptationPhase else NoopPhase[Program]) andThen - AnalysisPhase andThen + VerificationPhase andThen (if (desugarXLang) FixReportLabels else NoopPhase[VerificationReport]) val ctx = createLeonContext(files:_*) @@ -91,37 +91,44 @@ trait VerificationSuite extends LeonRegressionSuite { mkTest(files, cat)(block) } - override def run(testName: Option[String], args: Args): Status = { - forEachFileIn("valid") { output => - val Output(report, reporter) = output - for ((vc, vr) <- report.vrs if vr.isInvalid) { - fail(s"The following verification condition was invalid: $vc @${vc.getPos}") - } - for ((vc, vr) <- report.vrs if vr.isInconclusive) { - fail(s"The following verification condition was inconclusive: $vc @${vc.getPos}") - } - reporter.terminateIfError() + protected def testValid() = forEachFileIn("valid") { output => + val Output(report, reporter) = output + for ((vc, vr) <- report.vrs if vr.isInvalid) { + fail(s"The following verification condition was invalid: $vc @${vc.getPos}") } - - forEachFileIn("invalid") { output => - val Output(report, _) = output - assert(report.totalUnknown === 0, - "There should not be unknown verification conditions.") - assert(report.totalInvalid > 0, - "There should be at least one invalid verification condition.") + for ((vc, vr) <- report.vrs if vr.isInconclusive) { + fail(s"The following verification condition was inconclusive: $vc @${vc.getPos}") } + reporter.terminateIfError() + } - forEachFileIn("unknown") { output => - val Output(report, reporter) = output - for ((vc, vr) <- report.vrs if vr.isInvalid) { - fail(s"The following verification condition was invalid: $vc @${vc.getPos}") - } - assert(report.totalUnknown > 0, - "There should be at least one unknown verification condition.") + protected def testInvalid() = forEachFileIn("invalid") { output => + val Output(report, _) = output + assert(report.totalUnknown === 0, + "There should not be unknown verification conditions.") + assert(report.totalInvalid > 0, + "There should be at least one invalid verification condition.") + } - reporter.terminateIfError() + protected def testUnknown() = forEachFileIn("unknown") { output => + val Output(report, reporter) = output + for ((vc, vr) <- report.vrs if vr.isInvalid) { + fail(s"The following verification condition was invalid: $vc @${vc.getPos}") } + assert(report.totalUnknown > 0, + "There should be at least one unknown verification condition.") + + reporter.terminateIfError() + } + protected def testAll() = { + testValid() + testInvalid() + testUnknown() + } + + override def run(testName: Option[String], args: Args): Status = { + testAll() super.run(testName, args) } } diff --git a/src/test/scala/leon/regression/verification/XLangVerificationSuite.scala b/src/test/scala/leon/regression/verification/XLangVerificationSuite.scala index 3a9783e7036ca82d51298b6eb1751e5ee024d46a..8511ebfea95d4d6a412d82922ffb1e6fb870d312 100644 --- a/src/test/scala/leon/regression/verification/XLangVerificationSuite.scala +++ b/src/test/scala/leon/regression/verification/XLangVerificationSuite.scala @@ -4,6 +4,8 @@ package leon.regression.verification import smtlib.interpreters.Z3Interpreter +// If you add another regression test, make sure it contains exactly one object, whose name matches the file name. +// This is because we compile all tests from each folder together. class XLangVerificationSuite extends VerificationSuite { val optionVariants: List[List[String]] = { diff --git a/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala b/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala new file mode 100644 index 0000000000000000000000000000000000000000..d308e840f159a8ab7de49380d920dfc12ee8d2ad --- /dev/null +++ b/src/test/scala/leon/regression/verification/purescala/PureScalaVerificationSuite.scala @@ -0,0 +1,71 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +package leon.regression.verification +package purescala + +import smtlib.interpreters.{CVC4Interpreter, Z3Interpreter} + +// If you add another regression test, make sure it contains one object whose name matches the file name +// This is because we compile all tests from each folder together. +abstract class PureScalaVerificationSuite extends VerificationSuite { + + val testDir = "regression/verification/purescala/" + + val isZ3Available = try { + Z3Interpreter.buildDefault.free() + true + } catch { + case e: java.io.IOException => + false + } + + val isCVC4Available = try { + CVC4Interpreter.buildDefault.free() + true + } catch { + case e: java.io.IOException => + false + } + + val opts: List[List[String]] = { + List( + List("--feelinglucky"), + List("--codegen", "--evalground", "--feelinglucky"), + List("--solvers=fairz3,enum", "--codegen", "--evalground", "--feelinglucky") + ) ++ ( + if (isZ3Available) List( + List("--solvers=smt-z3", "--feelinglucky") + ) else Nil + ) ++ ( + if (isCVC4Available) List( + List("--solvers=smt-cvc4", "--feelinglucky") + ) else Nil + ) + } + +} + +trait PureScalaValidSuite extends PureScalaVerificationSuite { + override def testAll() = testValid() +} + +class PureScalaValidSuite1 extends PureScalaValidSuite { + val optionVariants = List(opts(0)) +} +class PureScalaValidSuite2 extends PureScalaValidSuite { + val optionVariants = List(opts(1)) +} +class PureScalaValidSuite3 extends PureScalaValidSuite { + val optionVariants = List(opts(2)) +} +class PureScalaValidSuiteZ3 extends PureScalaValidSuite { + val optionVariants = if (isZ3Available) List(opts(3)) else Nil +} +class PureScalaValidSuiteCVC4 extends PureScalaValidSuite { + val optionVariants = if (isCVC4Available) List(opts(4)) else Nil +} + +class PureScalaInvalidSuite extends PureScalaVerificationSuite { + override def testAll() = testInvalid() + val optionVariants = opts +} diff --git a/testcases/verification/datastructures/BinarySearchTreeQuant.scala b/testcases/verification/datastructures/BinarySearchTreeQuant.scala index a435e69ca7a0574937f7368e499c5750983133b3..b5b072550da63b0760ebe55b2a5e5c2553249f86 100644 --- a/testcases/verification/datastructures/BinarySearchTreeQuant.scala +++ b/testcases/verification/datastructures/BinarySearchTreeQuant.scala @@ -2,41 +2,49 @@ import leon.lang._ import leon.collection._ object BSTSimpler { - sealed abstract class Tree + case object Leaf extends Tree case class Node(left: Tree, value: BigInt, right: Tree) extends Tree - case class Leaf() extends Tree - - def content(tree: Tree): Set[BigInt] = tree match { - case Leaf() => Set.empty[BigInt] - case Node(l, v, r) => content(l) ++ Set(v) ++ content(r) - } + sealed abstract class Tree { + def isBST: Boolean = this match { + case Leaf => true + case Node(left, v, right) => { + left.isBST && right.isBST && + forall((x:BigInt) => (left.content.contains(x) ==> x < v)) && + forall((x:BigInt) => (right.content.contains(x) ==> v < x)) + } + } - def isBST(tree: Tree) : Boolean = tree match { - case Leaf() => true - case Node(left, v, right) => { - isBST(left) && isBST(right) && - forall((x:BigInt) => (content(left).contains(x) ==> x < v)) && - forall((x:BigInt) => (content(right).contains(x) ==> v < x)) + def content: Set[BigInt] = this match { + case Leaf => Set.empty[BigInt] + case Node(l, v, r) => l.content ++ Set(v) ++ r.content } - } - def emptySet(): Tree = Leaf() + def insert(value: BigInt): Node = { + require(isBST) + this match { + case Leaf => Node(Leaf, value, Leaf) + case Node(l, v, r) => (if (v < value) { + Node(l, v, r.insert(value)) + } else if (v > value) { + Node(l.insert(value), v, r) + } else { + Node(l, v, r) + }) + } + } ensuring(res => res.isBST && res.content == content ++ Set(value)) - def insert(tree: Tree, value: BigInt): Node = { - require(isBST(tree)) - tree match { - case Leaf() => Node(Leaf(), value, Leaf()) - case Node(l, v, r) => (if (v < value) { - Node(l, v, insert(r, value)) - } else if (v > value) { - Node(insert(l, value), v, r) - } else { - Node(l, v, r) - }) - } - } ensuring(res => isBST(res) && content(res) == content(tree) ++ Set(value)) + def contains(value: BigInt): Boolean = { + require(isBST) + this match { + case Leaf => false + case Node(l,v,r) => (if (v < value) { + r.contains(value) + } else if (v > value) { + l.contains(value) + } else true) + } + } ensuring(res => (res == content.contains(value))) - def createRoot(v: BigInt): Node = { - Node(Leaf(), v, Leaf()) - } ensuring (content(_) == Set(v)) + } + def empty: Tree = Leaf } diff --git a/testcases/web/invariant/01_InsertionSort.scala b/testcases/web/invariant/01_InsertionSort.scala new file mode 100644 index 0000000000000000000000000000000000000000..bcf4b8d98f5b37ad1afd229454990d71f24c8b07 --- /dev/null +++ b/testcases/web/invariant/01_InsertionSort.scala @@ -0,0 +1,26 @@ +import leon.invariant._ +import leon.instrumentation._ + +object InsertionSort { + sealed abstract class List + case class Cons(head: BigInt, tail:List) extends List + case class Nil() extends List + + def size(l : List) : BigInt = (l match { + case Cons(_, xs) => 1 + size(xs) + case _ => 0 + }) + + def sortedIns(e: BigInt, l: List): List = { + l match { + case Cons(x,xs) => if (x <= e) Cons(x,sortedIns(e, xs)) else Cons(e, l) + case _ => Cons(e,Nil()) + } + } ensuring(res => size(res) == size(l) + 1 && time <= ? * size(l) + ?) + + def sort(l: List): List = (l match { + case Cons(x,xs) => sortedIns(x, sort(xs)) + case _ => Nil() + + }) ensuring(res => size(res) == size(l) && time <= ? * (size(l)*size(l)) + ?) +} diff --git a/testcases/web/invariant/02_ListOps.scala b/testcases/web/invariant/02_ListOps.scala new file mode 100644 index 0000000000000000000000000000000000000000..23ca52bbd72b971c0b6915fa9c3fb7576c155a47 --- /dev/null +++ b/testcases/web/invariant/02_ListOps.scala @@ -0,0 +1,60 @@ +import leon.invariant._ +import leon.instrumentation._ + +object ListOperations { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case class Nil() extends List + + def size(l: List): BigInt = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) + + def append(l1: List, l2: List): List = (l1 match { + case Nil() => l2 + case Cons(x, xs) => Cons(x, append(xs, l2)) + + }) ensuring (res => size(l1) + size(l2) == size(res) && time <= ? *size(l1) + ?) + + def reverseRec(l1: List, l2: List): List = (l1 match { + case Nil() => l2 + case Cons(x, xs) => reverseRec(xs, Cons(x, l2)) + + }) ensuring (res => size(l1) + size(l2) == size(res) && time <= ? *size(l1) + ?) + + def reverse(l: List): List = { + reverseRec(l, Nil()) + + } ensuring (res => size(l) == size(res) && time <= ? * size(l) + ?) + + def reverse2(l: List): List = { + l match { + case Nil() => l + case Cons(hd, tl) => append(reverse2(tl), Cons(hd, Nil())) + } + } ensuring (res => size(res) == size(l) && time <= ? *(size(l)*size(l)) + ?) + + def remove(elem: BigInt, l: List): List = { + l match { + case Nil() => Nil() + case Cons(hd, tl) => if (hd == elem) remove(elem, tl) else Cons(hd, remove(elem, tl)) + } + } ensuring (res => size(l) >= size(res) && time <= ? *size(l) + ?) + + def contains(list: List, elem: BigInt): Boolean = (list match { + case Nil() => false + case Cons(x, xs) => x == elem || contains(xs, elem) + + }) ensuring (res => true && time <= ? *size(list) + ?) + + def distinct(l: List): List = ( + l match { + case Nil() => Nil() + case Cons(x, xs) => { + val newl = distinct(xs) + if (contains(newl, x)) newl + else Cons(x, newl) + } + }) ensuring (res => size(l) >= size(res) && time <= ? *(size(l)*size(l)) + ?) +} diff --git a/testcases/web/invariant/03_TreeOps.scala b/testcases/web/invariant/03_TreeOps.scala new file mode 100644 index 0000000000000000000000000000000000000000..6d13fcef7a45ce58846c985daceacb36ae67ca5c --- /dev/null +++ b/testcases/web/invariant/03_TreeOps.scala @@ -0,0 +1,93 @@ +import leon.invariant._ +import leon.instrumentation._ + + +object TreeOperations { + + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case class Nil() extends List + + sealed abstract class Tree + case class Node(left: Tree, value: BigInt, right: Tree) extends Tree + case class Leaf() extends Tree + + def listSize(l: List): BigInt = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + listSize(t) + }) + + def size(t: Tree): BigInt = { + t match { + case Leaf() => 0 + case Node(l, x, r) => { + size(l) + size(r) + 1 + } + } + } + + def height(t: Tree): BigInt = { + t match { + case Leaf() => 0 + case Node(l, x, r) => { + val hl = height(l) + val hr = height(r) + if (hl > hr) hl + 1 else hr + 1 + } + } + } + + def insert(elem: BigInt, t: Tree): Tree = { + t match { + case Leaf() => Node(Leaf(), elem, Leaf()) + case Node(l, x, r) => if (x <= elem) Node(l, x, insert(elem, r)) + else Node(insert(elem, l), x, r) + } + } ensuring (res => height(res) <= height(t) + 1 && time <= ? *height(t) + ?) + + def addAll(l: List, t: Tree): Tree = { + l match { + case Nil() => t + case Cons(x, xs) =>{ + val newt = insert(x, t) + addAll(xs, newt) + } + } + } ensuring(_ => time <= ? *(listSize(l) * (height(t) + listSize(l))) + ? *listSize(l) + ?) + + def remove(elem: BigInt, t: Tree): Tree = { + t match { + case Leaf() => Leaf() + case Node(l, x, r) => { + + if (x < elem) Node(l, x, remove(elem, r)) + else if (x > elem) Node(remove(elem, l), x, r) + else { + t match { + case Node(Leaf(), x, Leaf()) => Leaf() + case Node(Leaf(), x, Node(_, rx, _)) => Node(Leaf(), rx, remove(rx, r)) + case Node(Node(_, lx, _), x, r) => Node(remove(lx, l), lx, r) + case _ => Leaf() + } + } + } + } + } ensuring (res => height(res) <= height(t) && time <= ? *height(t) + ?) + + def removeAll(l: List, t: Tree): Tree = { + l match { + case Nil() => t + case Cons(x, xs) => removeAll(xs, remove(x, t)) + } + } ensuring(_ => time <= ? *(listSize(l) * height(t)) + ? *listSize(l) + ?) + + def contains(elem : BigInt, t : Tree) : Boolean = { + t match { + case Leaf() => false + case Node(l, x, r) => + if(x == elem) true + else if (x < elem) contains(elem, r) + else contains(elem, l) + } + } ensuring (_ => time <= ? * height(t) + ?) +} \ No newline at end of file diff --git a/testcases/web/invariant/04_PropositionalLogic.scala b/testcases/web/invariant/04_PropositionalLogic.scala new file mode 100644 index 0000000000000000000000000000000000000000..afae7b0a0114f64974060c0885dd6d3feff01f2f --- /dev/null +++ b/testcases/web/invariant/04_PropositionalLogic.scala @@ -0,0 +1,114 @@ +import leon.invariant._ +import leon.instrumentation._ + +object PropositionalLogic { + + sealed abstract class Formula + case class And(lhs: Formula, rhs: Formula) extends Formula + case class Or(lhs: Formula, rhs: Formula) extends Formula + case class Implies(lhs: Formula, rhs: Formula) extends Formula + case class Not(f: Formula) extends Formula + case class Literal(id: BigInt) extends Formula + case class True() extends Formula + case class False() extends Formula + + case class Pair(f: Formula, b: Boolean) + + sealed abstract class List + case class Cons(x: Pair, xs: List) extends List + case class Nil() extends List + + def size(f : Formula) : BigInt = (f match { + case And(lhs, rhs) => size(lhs) + size(rhs) + 1 + case Or(lhs, rhs) => size(lhs) + size(rhs) + 1 + case Implies(lhs, rhs) => size(lhs) + size(rhs) + 1 + case Not(f) => size(f) + 1 + case _ => 1 + }) + + def removeImplies(f: Formula): Formula = (f match { + case And(lhs, rhs) => And(removeImplies(lhs), removeImplies(rhs)) + case Or(lhs, rhs) => Or(removeImplies(lhs), removeImplies(rhs)) + case Implies(lhs, rhs) => Or(Not(removeImplies(lhs)),removeImplies(rhs)) + case Not(f) => Not(removeImplies(f)) + case _ => f + + }) ensuring(_ => time <= ? * size(f) + ?) + + def nnf(formula: Formula): Formula = (formula match { + case And(lhs, rhs) => And(nnf(lhs), nnf(rhs)) + case Or(lhs, rhs) => Or(nnf(lhs), nnf(rhs)) + case Implies(lhs, rhs) => Implies(nnf(lhs), nnf(rhs)) + case Not(And(lhs, rhs)) => Or(nnf(Not(lhs)), nnf(Not(rhs))) + case Not(Or(lhs, rhs)) => And(nnf(Not(lhs)), nnf(Not(rhs))) + case Not(Implies(lhs, rhs)) => And(nnf(lhs), nnf(Not(rhs))) + case Not(Not(f)) => nnf(f) + case Not(Literal(_)) => formula + case Literal(_) => formula + case Not(True()) => False() + case Not(False()) => True() + case _ => formula + }) ensuring(_ => time <= ? * size(formula) + ?) + + def isNNF(f: Formula): Boolean = { f match { + case And(lhs, rhs) => isNNF(lhs) && isNNF(rhs) + case Or(lhs, rhs) => isNNF(lhs) && isNNF(rhs) + case Implies(lhs, rhs) => false + case Not(Literal(_)) => true + case Not(_) => false + case _ => true + }} ensuring(_ => time <= ? * size(f) + ?) + + def simplify(f: Formula): Formula = (f match { + case And(lhs, rhs) => { + val sl = simplify(lhs) + val sr = simplify(rhs) + + //if lhs or rhs is false, return false + //if lhs is true return rhs + //if rhs is true return lhs + (sl,sr) match { + case (False(), _) => False() + case (_, False()) => False() + case (True(), _) => sr + case (_, True()) => sl + case _ => And(sl, sr) + } + } + case Or(lhs, rhs) => { + val sl = simplify(lhs) + val sr = simplify(rhs) + + //if lhs or rhs is true, return true + //if lhs is false return rhs + //if rhs is false return lhs + (sl,sr) match { + case (True(), _) => True() + case (_, True()) => True() + case (False(), _) => sr + case (_, False()) => sl + case _ => Or(sl, sr) + } + } + case Implies(lhs, rhs) => { + val sl = simplify(lhs) + val sr = simplify(rhs) + + //if lhs is false return true + //if rhs is true return true + //if lhs is true return rhs + //if rhs is false return Not(rhs) + (sl,sr) match { + case (False(), _) => True() + case (_, True()) => True() + case (True(), _) => sr + case (_, False()) => Not(sl) + case _ => Implies(sl, sr) + } + } + case Not(True()) => False() + case Not(False()) => True() + case _ => f + + }) ensuring(_ => time <= ? *size(f) + ?) +} diff --git a/testcases/web/invariant/01_Binary_Trie.scala b/testcases/web/invariant/05_BinaryTrie.scala similarity index 100% rename from testcases/web/invariant/01_Binary_Trie.scala rename to testcases/web/invariant/05_BinaryTrie.scala diff --git a/testcases/web/invariant/06_String_Buffer.scala b/testcases/web/invariant/06_StringBuffer.scala similarity index 100% rename from testcases/web/invariant/06_String_Buffer.scala rename to testcases/web/invariant/06_StringBuffer.scala diff --git a/testcases/web/invariant/03_List_Concat.scala b/testcases/web/invariant/07_ConcatVariations.scala similarity index 100% rename from testcases/web/invariant/03_List_Concat.scala rename to testcases/web/invariant/07_ConcatVariations.scala diff --git a/testcases/web/invariant/05_Leftist_Heap.scala b/testcases/web/invariant/08_LeftistHeap.scala similarity index 100% rename from testcases/web/invariant/05_Leftist_Heap.scala rename to testcases/web/invariant/08_LeftistHeap.scala diff --git a/testcases/web/invariant/04_Red_Black_Tree.scala b/testcases/web/invariant/09_RedBlackTree.scala similarity index 100% rename from testcases/web/invariant/04_Red_Black_Tree.scala rename to testcases/web/invariant/09_RedBlackTree.scala diff --git a/testcases/web/invariant/02_Binomial_Heap.scala b/testcases/web/invariant/10_BinomialHeap.scala similarity index 100% rename from testcases/web/invariant/02_Binomial_Heap.scala rename to testcases/web/invariant/10_BinomialHeap.scala diff --git a/testcases/web/invariant/11_Amortized_BigNums.scala b/testcases/web/invariant/11_Amortized_BigNums.scala new file mode 100644 index 0000000000000000000000000000000000000000..883d1f45ab8752668070699c840edd7a3de160b9 --- /dev/null +++ b/testcases/web/invariant/11_Amortized_BigNums.scala @@ -0,0 +1,50 @@ +import leon.invariant._ +import leon.instrumentation._ + +object BigNums { + sealed abstract class BigNum + case class Cons(head: BigInt, tail: BigNum) extends BigNum + case class Nil() extends BigNum + + def incrTime(l: BigNum) : BigInt = { + l match { + case Nil() => 1 + case Cons(x, tail) => + if(x == 0) 1 + else 1 + incrTime(tail) + } + } + + def potentialIncr(l: BigNum) : BigInt = { + l match { + case Nil() => 0 + case Cons(x, tail) => + if(x == 0) potentialIncr(tail) + else 1 + potentialIncr(tail) + } + } + + def increment(l: BigNum) : BigNum = { + l match { + case Nil() => Cons(1,l) + case Cons(x, tail) => + if(x == 0) Cons(1, tail) + else Cons(0, increment(tail)) + } + } ensuring (res => time <= ? * incrTime(l) + ? && incrTime(l) + potentialIncr(res) - potentialIncr(l) <= ?) + + /** + * Nop is the number of operations + */ + def incrUntil(nop: BigInt, l: BigNum) : BigNum = { + if(nop == 0) l + else { + incrUntil(nop-1, increment(l)) + } + } ensuring (res => time <= ? * nop + ? * potentialIncr(l) + ?) + + def count(nop: BigInt) : BigNum = { + incrUntil(nop, Nil()) + } ensuring (res => time <= ? * nop + ?) + +} diff --git a/testcases/web/invariant/12_Par_Folds.scala b/testcases/web/invariant/12_Par_Folds.scala new file mode 100755 index 0000000000000000000000000000000000000000..5c4f51d119e18aa74d1fc0c263094a5cf6966a42 --- /dev/null +++ b/testcases/web/invariant/12_Par_Folds.scala @@ -0,0 +1,81 @@ +import leon.instrumentation._ +import leon.invariant._ + +object TreeMaps { + + sealed abstract class Tree + case class Node(left: Tree, value: BigInt, right: Tree) extends Tree + case class Leaf() extends Tree + + def height(t: Tree): BigInt = { + t match { + case Leaf() => 0 + case Node(l, x, r) => { + val hl = height(l) + val hr = height(r) + if (hl > hr) hl + 1 else hr + 1 + } + } + } + + def parallelSearch(elem : BigInt, t : Tree) : Boolean = { + t match { + case Leaf() => false + case Node(l, x, r) => + if(x == elem) true + else { + val r1 = parallelSearch(elem, r) + val r2 = parallelSearch(elem, l) + if(r1 || r2) true + else false + } + } + } ensuring(_ => depth <= ? * height(t) + ?) + + + def squareMap(t : Tree) : Tree = { + t match { + case Leaf() => t + case Node(l, x, r) => + val nl = squareMap(l) + val nr = squareMap(r) + Node(nl, x*x, nr) + } + } ensuring (_ => depth <= ? * height(t) + ?) + + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case class Nil() extends List + + def size(l: List): BigInt = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) + + def fact(n : BigInt) : BigInt = { + require(n >= 0) + + if(n == 1 || n == 0) BigInt(1) + else n * fact(n-1) + + } ensuring(_ => depth <= ? * n + ?) + + def descending(l: List, k: BigInt) : Boolean = { + l match { + case Nil() => true + case Cons(x, t) => x > 0 && x <= k && descending(t, x-1) + } + } + + def factMap(l: List, k: BigInt): List = { + require(descending(l, k) && k >= 0) + + l match { + case Nil() => Nil() + case Cons(x, t) => { + val f = fact(x) + Cons(f, factMap(t, x-1)) + } + + }} ensuring(_ => depth <= ? * k + ?) +} \ No newline at end of file diff --git a/testcases/web/invariant/13_Par_MSort.scala b/testcases/web/invariant/13_Par_MSort.scala new file mode 100644 index 0000000000000000000000000000000000000000..8a818137eb32a3f9eb63297a6219faf219cec4eb --- /dev/null +++ b/testcases/web/invariant/13_Par_MSort.scala @@ -0,0 +1,57 @@ +import leon.instrumentation._ +import leon.invariant._ + +object MergeSort { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case class Nil() extends List + + def size(l: List): BigInt = { + l match { + case Nil() => BigInt(0) + case Cons(x, xs) => 1 + size(xs) + } + } ensuring (res => res >= 0) + + def length(l: List): BigInt = { + l match { + case Nil() => BigInt(0) + case Cons(x, xs) => 1 + length(xs) + } + } ensuring (res => res >= 0 && res == size(l) && depth <= ? * size(l) + ?) + + def split(l: List, n: BigInt): (List, List) = { + require(n >= 0 && n <= size(l)) + if (n <= 0) (Nil(), l) + else + l match { + case Nil() => (Nil(), l) + case Cons(x, xs) => { + val (fst, snd) = split(xs, n - 1) + (Cons(x, fst), snd) + } + } + } ensuring (res => size(res._2) == size(l) - n && size(res._1) == n && depth <= ? * n + ?) + + def merge(l1: List, l2: List): List = (l2 match { + case Nil() => l1 + case Cons(x, xs) => + l1 match { + case Nil() => l2 + case Cons(y, ys) => + if (y < x) + Cons(y, merge(ys, l2)) + else + Cons(x, merge(l1, xs)) + } + }) ensuring (res => size(l1) + size(l2) == size(res) && depth <= ? * size(l1) + ? * size(l2) + ?) + + def mergeSort(l: List): List = (l match { + case Nil() => l + case Cons(x, Nil()) => l + case _ => + val (fst, snd) = split(l, length(l) / 2) + merge(mergeSort(fst), mergeSort(snd)) + + }) ensuring (res => size(res) == size(l) && depth <= ? * size(l) + ?) +} diff --git a/testcases/web/invariant/14_Par_PropLogic.scala b/testcases/web/invariant/14_Par_PropLogic.scala new file mode 100644 index 0000000000000000000000000000000000000000..096dc8f60377a5dd10ad94f3fbd0ddf33c3f6481 --- /dev/null +++ b/testcases/web/invariant/14_Par_PropLogic.scala @@ -0,0 +1,113 @@ +import leon.instrumentation._ +import leon.invariant._ +import leon.annotation._ + +object PropLogicDepth { + + sealed abstract class Formula + case class And(lhs: Formula, rhs: Formula) extends Formula + case class Or(lhs: Formula, rhs: Formula) extends Formula + case class Implies(lhs: Formula, rhs: Formula) extends Formula + case class Not(f: Formula) extends Formula + case class Literal(id: BigInt) extends Formula + case class True() extends Formula + case class False() extends Formula + + def max(x: BigInt, y: BigInt) = if (x >= y) x else y + + def nestingDepth(f: Formula): BigInt = (f match { + case And(lhs, rhs) => max(nestingDepth(lhs), nestingDepth(rhs)) + 1 + case Or(lhs, rhs) => max(nestingDepth(lhs), nestingDepth(rhs)) + 1 + case Implies(lhs, rhs) => max(nestingDepth(lhs), nestingDepth(rhs)) + 1 + case Not(f) => nestingDepth(f) + 1 + case _ => 1 + }) + + def removeImplies(f: Formula): Formula = (f match { + case And(lhs, rhs) => And(removeImplies(lhs), removeImplies(rhs)) + case Or(lhs, rhs) => Or(removeImplies(lhs), removeImplies(rhs)) + case Implies(lhs, rhs) => Or(Not(removeImplies(lhs)), removeImplies(rhs)) + case Not(f) => Not(removeImplies(f)) + case _ => f + + }) ensuring (_ => depth <= ? * nestingDepth(f) + ?) + + def nnf(formula: Formula): Formula = (formula match { + case And(lhs, rhs) => And(nnf(lhs), nnf(rhs)) + case Or(lhs, rhs) => Or(nnf(lhs), nnf(rhs)) + case Implies(lhs, rhs) => Implies(nnf(lhs), nnf(rhs)) + case Not(And(lhs, rhs)) => Or(nnf(Not(lhs)), nnf(Not(rhs))) + case Not(Or(lhs, rhs)) => And(nnf(Not(lhs)), nnf(Not(rhs))) + case Not(Implies(lhs, rhs)) => And(nnf(lhs), nnf(Not(rhs))) + case Not(Not(f)) => nnf(f) + case Not(Literal(_)) => formula + case Literal(_) => formula + case Not(True()) => False() + case Not(False()) => True() + case _ => formula + }) ensuring (_ => depth <= ? * nestingDepth(formula) + ?) + + def isNNF(f: Formula): Boolean = { + f match { + case And(lhs, rhs) => isNNF(lhs) && isNNF(rhs) + case Or(lhs, rhs) => isNNF(lhs) && isNNF(rhs) + case Implies(lhs, rhs) => false + case Not(Literal(_)) => true + case Not(_) => false + case _ => true + } + } ensuring (_ => depth <= ? * nestingDepth(f) + ?) + + def simplify(f: Formula): Formula = (f match { + case And(lhs, rhs) => { + val sl = simplify(lhs) + val sr = simplify(rhs) + + //if lhs or rhs is false, return false + //if lhs is true return rhs + //if rhs is true return lhs + (sl, sr) match { + case (False(), _) => False() + case (_, False()) => False() + case (True(), _) => sr + case (_, True()) => sl + case _ => And(sl, sr) + } + } + case Or(lhs, rhs) => { + val sl = simplify(lhs) + val sr = simplify(rhs) + + //if lhs or rhs is true, return true + //if lhs is false return rhs + //if rhs is false return lhs + (sl, sr) match { + case (True(), _) => True() + case (_, True()) => True() + case (False(), _) => sr + case (_, False()) => sl + case _ => Or(sl, sr) + } + } + case Implies(lhs, rhs) => { + val sl = simplify(lhs) + val sr = simplify(rhs) + + //if lhs is false return true + //if rhs is true return true + //if lhs is true return rhs + //if rhs is false return Not(rhs) + (sl, sr) match { + case (False(), _) => True() + case (_, True()) => True() + case (True(), _) => sr + case (_, False()) => Not(sl) + case _ => Implies(sl, sr) + } + } + case Not(True()) => False() + case Not(False()) => True() + case _ => f + + }) ensuring (_ => depth <= ? * nestingDepth(f) + ?) +} \ No newline at end of file diff --git a/testcases/web/invariant/15_Numerical_SeeSaw.scala b/testcases/web/invariant/15_Numerical_SeeSaw.scala new file mode 100644 index 0000000000000000000000000000000000000000..bb08b70e78145a64d2eadc7e9ca4eec721b0babb --- /dev/null +++ b/testcases/web/invariant/15_Numerical_SeeSaw.scala @@ -0,0 +1,15 @@ +object SeeSaw { + def s(x: BigInt, y: BigInt, z: BigInt): BigInt = { + require(y >= 0) + if (x >= 100) { + y + } else if (x <= z) { + s(x + 1, y + 2, z) + } else if (x <= z + 9) { + s(x + 1, y + 3, z) + } else { + s(x + 2, y + 1, z) + } + } ensuring (res => (100 - x <= 2 * res)) + // postcondition is not inductive +} \ No newline at end of file