diff --git a/src/main/scala/leon/plugin/CodeExtraction.scala b/src/main/scala/leon/plugin/CodeExtraction.scala index babf6d728b383740202d0802c449d1549509265d..ad3dc998a2d023022250c0e6de4b8b4db00cbcbe 100644 --- a/src/main/scala/leon/plugin/CodeExtraction.scala +++ b/src/main/scala/leon/plugin/CodeExtraction.scala @@ -374,6 +374,11 @@ trait CodeExtraction extends Extractors { varSubsts(b.symbol) = (() => Variable(newID)) WildcardPattern(Some(newID)) } + case b @ Bind(name, Typed(Ident(nme.WILDCARD), tpe)) => { + val newID = FreshIdentifier(name.toString).setType(scalaType2PureScala(unit,silent)(tpe.tpe)) + varSubsts(b.symbol) = (() => Variable(newID)) + WildcardPattern(Some(newID)) + } case a @ Apply(fn, args) if fn.isType && a.tpe.typeSymbol.isCase && classesToClasses.keySet.contains(a.tpe.typeSymbol) => { val cd = classesToClasses(a.tpe.typeSymbol).asInstanceOf[CaseClassDef] assert(args.size == cd.fields.size) @@ -660,7 +665,7 @@ trait CodeExtraction extends Extractors { case ExInt32Literal(v) => IntLiteral(v).setType(Int32Type) case ExBooleanLiteral(v) => BooleanLiteral(v).setType(BooleanType) case ExUnitLiteral() => UnitLiteral - + case ExLocally(body) => rec(body) case ExTyped(e,tpt) => rec(e) case ExIdentifier(sym,tpt) => varSubsts.get(sym) match { case Some(fun) => fun() @@ -1016,6 +1021,7 @@ trait CodeExtraction extends Extractors { val fd = defsToDefs(sy) FunctionInvocation(fd, ar.map(rec(_))).setType(fd.returnType).setPosInfo(lc.pos.line,lc.pos.column) } + case pm @ ExPatternMatching(sel, cses) => { val rs = rec(sel) val rc = cses.map(rewriteCaseDef(_)) diff --git a/src/main/scala/leon/plugin/Extractors.scala b/src/main/scala/leon/plugin/Extractors.scala index e301ab021fd893bdff68900c448c6415805496c0..b6b15a1628037bc385145f2699d5a0b3483cba40 100644 --- a/src/main/scala/leon/plugin/Extractors.scala +++ b/src/main/scala/leon/plugin/Extractors.scala @@ -304,6 +304,16 @@ trait Extractors { } } + object ExLocally { + def unapply(tree: Apply) : Option[Tree] = tree match { + case Apply(TypeApply(ExSelected("scala", "Predef", "locally"), _), List(body)) => + Some(body) + + case _ => + None + } + } + object ExTupleExtract { def unapply(tree: Select) : Option[(Tree,Int)] = tree match { case Select(lhs, n) => { diff --git a/src/main/scala/leon/purescala/Common.scala b/src/main/scala/leon/purescala/Common.scala index fde8be1a91953db0af6efedcf58fcc50b189b055..16b8959f3930b30fc040ff1263588c3ad38cf9a1 100644 --- a/src/main/scala/leon/purescala/Common.scala +++ b/src/main/scala/leon/purescala/Common.scala @@ -57,6 +57,9 @@ object Common { object FreshIdentifier { def apply(name: String, alwaysShowUniqueID: Boolean = false) : Identifier = new Identifier(name, UniqueCounter.nextGlobal, UniqueCounter.next(name), alwaysShowUniqueID) + + def apply(name: String, forceId: Int): Identifier = new Identifier(name, UniqueCounter.nextGlobal, forceId, true) + } trait ScalacPositional { diff --git a/src/main/scala/leon/purescala/ScalaPrinter.scala b/src/main/scala/leon/purescala/ScalaPrinter.scala index f957848c9b9a16d0a91881524702c71cc4ec9898..d9a82aa8c9cfdad9636d827f9f3ad342fa14a61d 100644 --- a/src/main/scala/leon/purescala/ScalaPrinter.scala +++ b/src/main/scala/leon/purescala/ScalaPrinter.scala @@ -15,6 +15,12 @@ object ScalaPrinter { retSB.toString } + def apply(tree: Expr, lvl: Int): String = { + val retSB = new StringBuffer + pp(tree, retSB, lvl) + retSB.toString + } + def apply(tpe: TypeTree): String = { val retSB = new StringBuffer pp(tpe, retSB, 0) diff --git a/src/main/scala/leon/purescala/TreeOps.scala b/src/main/scala/leon/purescala/TreeOps.scala index 2a0acb3f2c51362d6cee172beb6d624104ea8abc..b0516a56f56a777bf6ee78affa519751f701e51c 100644 --- a/src/main/scala/leon/purescala/TreeOps.scala +++ b/src/main/scala/leon/purescala/TreeOps.scala @@ -463,9 +463,9 @@ object TreeOps { def simplifyLets(expr: Expr) : Expr = { def simplerLet(t: Expr) : Option[Expr] = t match { - case letExpr @ Let(i, t: Terminal, b) => Some(replace(Map((Variable(i) -> t)), b)) + case letExpr @ Let(i, t: Terminal, b) if !containsChoose(b) => Some(replace(Map((Variable(i) -> t)), b)) - case letExpr @ Let(i,e,b) => { + case letExpr @ Let(i,e,b) if !containsChoose(b) => { val occurences = treeCatamorphism[Int]((e:Expr) => e match { case Variable(x) if x == i => 1 case _ => 0 @@ -479,7 +479,7 @@ object TreeOps { } } - case letTuple @ LetTuple(ids, Tuple(exprs), body) => + case letTuple @ LetTuple(ids, Tuple(exprs), body) if !containsChoose(body) => var newBody = body @@ -513,7 +513,7 @@ object TreeOps { Some(LetTuple(remIds, Tuple(remExprs), newBody)) } - case l @ LetTuple(ids, tExpr, body) => + case l @ LetTuple(ids, tExpr, body) if !containsChoose(body) => val TupleType(types) = tExpr.getType val arity = ids.size // A map containing vectors of the form (0, ..., 1, ..., 0) where the one corresponds to the index of the identifier in the @@ -541,7 +541,7 @@ object TreeOps { None } - case _ => None + case _ => None } searchAndReplaceDFS(simplerLet)(expr) } @@ -1171,8 +1171,117 @@ object TreeOps { simplePreTransform(pre)(expr) } - // This function could be made ridiculously faster by using push/pop... - def simplifyPaths(solver : Solver)(expr : Expr) : Expr = { + def simplifyPaths(solver : Solver): Expr => Expr = { + new SimplifierWithPaths(solver).transform _ + } + + trait Transformer { + def transform(e: Expr): Expr + } + + trait Traverser[T] { + def traverse(e: Expr): T + } + + abstract class TransformerWithPC extends Transformer { + type C + + protected val initC: C + + protected def register(cond: Expr, path: C): C + + protected def rec(e: Expr, path: C): Expr = e match { + case Let(i, e @ FunctionInvocation(fd, callArgs), b) if fd.hasPostcondition => + val argsMap = fd.args.map(vd => Variable(vd.id)) zip callArgs + + val newPost = TreeOps.replace(Map(ResultVariable() -> Variable(i)) ++ argsMap, fd.postcondition.get) + + val pred = newPost + + val se = rec(e, path) + val sb = rec(b, register(pred, path)) + Let(i, se, sb) + + case Let(i, e, b) => + val se = rec(e, path) + val sb = rec(b, register(Equals(Variable(i), se), path)) + Let(i, se, sb) + + case MatchExpr(scrut, cases) => + val rs = rec(scrut, path) + + var soFar = path + + MatchExpr(rs, cases.map { c => + val patternExpr = conditionForPattern(rs, c.pattern) + + val subPath = register(patternExpr, soFar) + soFar = register(Not(patternExpr), soFar) + + c match { + case SimpleCase(p, rhs) => + SimpleCase(p, rec(rhs, subPath)) + case GuardedCase(p, g, rhs) => + GuardedCase(p, g, rec(rhs, subPath)) + } + }) + + case LetTuple(is, e, b) => + val se = rec(e, path) + val sb = rec(b, register(Equals(Tuple(is.map(Variable(_))), se), path)) + LetTuple(is, se, sb) + + case IfExpr(cond, then, elze) => + val rc = rec(cond, path) + + IfExpr(rc, rec(then, register(rc, path)), rec(elze, register(Not(rc), path))) + + case And(es) => { + var soFar = path + And(for(e <- es) yield { + val se = rec(e, soFar) + soFar = register(se, soFar) + se + }) + } + + case Or(es) => { + var soFar = path + Or(for(e <- es) yield { + val se = rec(e, soFar) + soFar = register(Not(se), soFar) + se + }) + } + + + case UnaryOperator(e, builder) => + builder(rec(e, path)) + + case BinaryOperator(e1, e2, builder) => + builder(rec(e1, path), rec(e2, path)) + + case NAryOperator(es, builder) => + builder(es.map(rec(_, path))) + + case t : Terminal => t + + case _ => + sys.error("Expression "+e+" ["+e.getClass+"] is not extractable") + } + + def transform(e: Expr): Expr = { + rec(e, initC) + } + } + + class SimplifierWithPaths(solver: Solver) extends TransformerWithPC { + type C = List[Expr] + + val initC = Nil + + protected def register(e: Expr, c: C) = e :: c + def impliedBy(e : Expr, path : Seq[Expr]) : Boolean = try { solver.solve(Implies(And(path), e)) match { case Some(true) => true @@ -1191,11 +1300,30 @@ object TreeOps { case _ : Exception => false } - def rec(e : Expr, path : Seq[Expr]): Expr = e match { - case Let(i, e, b) => - // The path condition for the body of the Let is the same as outside, plus an equality to constrain the newly bound variable. - val se = rec(e, path) - Let(i, se, rec(b, Equals(Variable(i), se) +: path)) + protected override def rec(e: Expr, path: C) = e match { + case IfExpr(cond, then, elze) => + super.rec(e, path) match { + case IfExpr(BooleanLiteral(true) , t, _) => t + case IfExpr(BooleanLiteral(false), _, e) => e + case ite => ite + } + + case And(es) => { + var soFar = path + var continue = true + var r = And(for(e <- es if continue) yield { + val se = rec(e, soFar) + if(se == BooleanLiteral(false)) continue = false + soFar = register(se, soFar) + se + }) + + if (continue) { + r + } else { + BooleanLiteral(false) + } + } case MatchExpr(scrut, cases) => val rs = rec(scrut, path) @@ -1206,7 +1334,7 @@ object TreeOps { // unsupported for now e } else { - MatchExpr(rs, cases.flatMap { c => + MatchExpr(rs, cases.flatMap { c => val patternExpr = conditionForPattern(rs, c.pattern) if (stillPossible && !contradictedBy(patternExpr, path)) { @@ -1227,41 +1355,21 @@ object TreeOps { }) } - case LetTuple(is, e, b) => - // Similar to the Let case - val se = rec(e, path) - LetTuple(is, se, rec(b, Equals(Tuple(is.map(Variable(_))), se) +: path)) - - case IfExpr(cond, then, elze) => - val rc = rec(cond, path) - rc match { - case BooleanLiteral(true) => rec(then, path) - case BooleanLiteral(false) => rec(elze, path) - case _ => IfExpr(rc, rec(then, rc +: path), rec(elze, Not(rc) +: path)) - } - - case And(es) => { - var extPath = path - var continue = true - var r = And(for(e <- es if continue) yield { - val se = rec(e, extPath) - if(se == BooleanLiteral(false)) continue = false - extPath = se +: extPath - se - }) - if (continue) r else BooleanLiteral(false) - } - case Or(es) => { - var extPath = path + var soFar = path var continue = true - val r = Or(for(e <- es if continue) yield { - val se = rec(e, extPath) + var r = Or(for(e <- es if continue) yield { + val se = rec(e, soFar) if(se == BooleanLiteral(true)) continue = false - extPath = Not(se) +: extPath + soFar = register(Not(se), soFar) se }) - if (continue) r else BooleanLiteral(true) + + if (continue) { + r + } else { + BooleanLiteral(true) + } } case b if b.getType == BooleanType && impliedBy(b, path) => @@ -1270,14 +1378,135 @@ object TreeOps { case b if b.getType == BooleanType && contradictedBy(b, path) => BooleanLiteral(false) + case _ => + super.rec(e, path) + } + } + + class ChooseCollectorWithPaths extends TransformerWithPC with Traverser[Seq[(Choose, Expr)]] { + type C = Seq[Expr] + val initC = Nil + def register(e: Expr, path: C) = path :+ e + + var results: Seq[(Choose, Expr)] = Nil + + override def rec(e: Expr, path: C) = e match { + case c : Choose => + results = results :+ (c, And(path)) + c + case _ => + super.rec(e, path) + } + + def traverse(e: Expr) = { + results = Nil + rec(e, initC) + results + } + } + + class ScopeSimplifier extends Transformer { + + case class Scope(inScope: Set[Identifier] = Set(), oldToNew: Map[Identifier, Identifier] = Map(), funDefs: Map[FunDef, FunDef] = Map()) { + + def register(oldNew: (Identifier, Identifier)): Scope = { + val (oldId, newId) = oldNew + copy(inScope = inScope + newId, oldToNew = oldToNew + oldNew) + } + + def registerFunDef(oldNew: (FunDef, FunDef)): Scope = { + copy(funDefs = funDefs + oldNew) + } + } + + protected def genId(id: Identifier, scope: Scope): Identifier = { + val existCount = scope.inScope.count(_.name == id.name) + + FreshIdentifier(id.name, existCount).setType(id.getType) + } + + protected def rec(e: Expr, scope: Scope): Expr = e match { + case Let(i, e, b) => + val si = genId(i, scope) + val se = rec(e, scope) + val sb = rec(b, scope.register(i -> si)) + Let(si, se, sb) + + case LetTuple(is, e, b) => + var newScope = scope + val sis = for (i <- is) yield { + val si = genId(i, newScope) + newScope = newScope.register(i -> si) + si + } + + val se = rec(e, scope) + val sb = rec(b, newScope) + LetTuple(sis, se, sb) + + case MatchExpr(scrut, cases) => + val rs = rec(scrut, scope) + + def trPattern(p: Pattern, scope: Scope): (Pattern, Scope) = { + val (newBinder, newScope) = p.binder match { + case Some(id) => + val newId = genId(id, scope) + val newScope = scope.register(id -> newId) + (Some(newId), newScope) + case None => + (None, scope) + } + + var curScope = newScope + var newSubPatterns = for (sp <- p.subPatterns) yield { + val (subPattern, subScope) = trPattern(sp, curScope) + curScope = subScope + subPattern + } + + val newPattern = p match { + case InstanceOfPattern(b, ctd) => + InstanceOfPattern(newBinder, ctd) + case WildcardPattern(b) => + WildcardPattern(newBinder) + case CaseClassPattern(b, ccd, sub) => + CaseClassPattern(newBinder, ccd, newSubPatterns) + case TuplePattern(b, sub) => + TuplePattern(newBinder, newSubPatterns) + } + + + (newPattern, curScope) + } + + MatchExpr(rs, cases.map { c => + val (newP, newScope) = trPattern(c.pattern, scope) + + c match { + case SimpleCase(p, rhs) => + SimpleCase(newP, rec(rhs, newScope)) + case GuardedCase(p, g, rhs) => + GuardedCase(newP, rec(g, newScope), rec(rhs, newScope)) + } + }) + + case Variable(id) => + Variable(scope.oldToNew.getOrElse(id, id)) + + case FunctionInvocation(fd, args) => + val newFd = scope.funDefs.getOrElse(fd, fd) + val newArgs = args.map(rec(_, scope)) + + FunctionInvocation(newFd, newArgs) + case UnaryOperator(e, builder) => - builder(rec(e, path)) + builder(rec(e, scope)) case BinaryOperator(e1, e2, builder) => - builder(rec(e1, path), rec(e2, path)) + builder(rec(e1, scope), rec(e2, scope)) case NAryOperator(es, builder) => - builder(es.map(rec(_, path))) + builder(es.map(rec(_, scope))) case t : Terminal => t @@ -1285,7 +1514,9 @@ object TreeOps { sys.error("Expression "+e+" ["+e.getClass+"] is not extractable") } - rec(expr, Nil) + def transform(e: Expr): Expr = { + rec(e, Scope()) + } } // Eliminates tuples of arity 0 and 1. This function also affects types! @@ -1415,7 +1646,15 @@ object TreeOps { } def collectChooses(e: Expr): List[Choose] = { - collect({ case c: Choose => c })(e) + new ChooseCollectorWithPaths().traverse(e).map(_._1).toList + } + + def containsChoose(e: Expr): Boolean = { + simplePreTransform{ + case Choose(_, _) => return true + case e => e + }(e) + false } def valuateWithModel(model: Map[Identifier, Expr])(id: Identifier): Expr = { diff --git a/src/main/scala/leon/purescala/Trees.scala b/src/main/scala/leon/purescala/Trees.scala index f522f4f0dd1ebe88d9918d2ef338bb76b81498e2..f1bd339c5170ecbfc2443c74c0363f49313eccaa 100644 --- a/src/main/scala/leon/purescala/Trees.scala +++ b/src/main/scala/leon/purescala/Trees.scala @@ -23,8 +23,15 @@ object Trees { * the expected type. */ case class Error(description: String) extends Expr with Terminal with ScalacPositional - case class Choose(vars: List[Identifier], pred: Expr) extends Expr with ScalacPositional with UnaryExtractable { - def extract = Some((pred, (e: Expr) => Choose(vars, e).setPosInfo(this).setType(TupleType(vars.map(_.getType))))) + case class Choose(vars: List[Identifier], pred: Expr) extends Expr with ScalacPositional with UnaryExtractable with FixedType { + + assert(!vars.isEmpty) + + val fixedType = if (vars.size > 1) TupleType(vars.map(_.getType)) else vars.head.getType + + def extract = { + Some((pred, (e: Expr) => Choose(vars, e).setPosInfo(this))) + } } /* Like vals */ diff --git a/src/main/scala/leon/synthesis/ChooseInfo.scala b/src/main/scala/leon/synthesis/ChooseInfo.scala new file mode 100644 index 0000000000000000000000000000000000000000..adfec0f1750ffeda8498f1858932de1ee1c6a9eb --- /dev/null +++ b/src/main/scala/leon/synthesis/ChooseInfo.scala @@ -0,0 +1,34 @@ +package leon +package synthesis + +import purescala.Definitions._ +import purescala.Trees._ +import purescala.TreeOps.ChooseCollectorWithPaths + +case class ChooseInfo(ctx: LeonContext, + prog: Program, + fd: FunDef, + pc: Expr, + ch: Choose, + options: SynthesisOptions) { + + val synthesizer = new Synthesizer(ctx, Some(fd), prog, Problem.fromChoose(ch), options) + val problem = Problem.fromChoose(ch, pc) +} + +object ChooseInfo { + def extractFromProgram(ctx: LeonContext, prog: Program, options: SynthesisOptions): List[ChooseInfo] = { + def noop(u:Expr, u2: Expr) = u + + var results = List[ChooseInfo]() + + // Look for choose() + for (f <- prog.definedFunctions.sortBy(_.id.toString) if f.body.isDefined) { + for ((ch, path) <- new ChooseCollectorWithPaths().traverse(f.body.get)) { + results = ChooseInfo(ctx, prog, f, path, ch, options) :: results + } + } + + results.reverse + } +} diff --git a/src/main/scala/leon/synthesis/FileInterface.scala b/src/main/scala/leon/synthesis/FileInterface.scala index b761841826c82ef89f06bc5e184632dab46f5644..0fd787e733a911bcfb129e3151e5925b5ecdf602 100644 --- a/src/main/scala/leon/synthesis/FileInterface.scala +++ b/src/main/scala/leon/synthesis/FileInterface.scala @@ -5,9 +5,9 @@ import purescala.Trees._ import purescala.ScalaPrinter import java.io.File -class FileInterface(reporter: Reporter, origFile: File) { +class FileInterface(reporter: Reporter) { - def updateFile(solutions: Map[Choose, Expr], ignoreMissing: Boolean = false) { + def updateFile(origFile: File, solutions: Map[ChooseInfo, Expr], ignoreMissing: Boolean = false) { import java.io.{File, BufferedWriter, FileWriter} val FileExt = """^(.+)\.([^.]+)$""".r @@ -35,7 +35,7 @@ class FileInterface(reporter: Reporter, origFile: File) { } } - def substitueChooses(str: String, solutions: Map[Choose, Expr], ignoreMissing: Boolean = false): String = { + def substitueChooses(str: String, solutions: Map[ChooseInfo, Expr], ignoreMissing: Boolean = false): String = { var lines = List[Int]() // Compute line positions @@ -58,6 +58,27 @@ class FileInterface(reporter: Reporter, origFile: File) { } } + def getLineIndentation(offset: Int): Int = { + var i = str.lastIndexOf('\n', offset)+1 + + var res = 0; + + while (i < str.length) { + val c = str.charAt(i) + i += 1 + + if (c == ' ') { + res += 1 + } else if (c == '\t') { + res += 4 + } else { + i = str.length + } + } + + res + } + lastFound = -1 var newStr = str @@ -71,7 +92,9 @@ class FileInterface(reporter: Reporter, origFile: File) { // compute scala equivalent of the position: val scalaOffset = str.substring(lineoffset, lastFound).replaceAll("\t", " "*8).length - solutions.find(_._1.posIntInfo == (lineno, scalaOffset)) match { + val indent = getLineIndentation(lastFound) + + solutions.find(_._1.ch.posIntInfo == (lineno, scalaOffset)) match { case Some((choose, solution)) => var lvl = 0; var i = lastFound + 6; @@ -89,7 +112,7 @@ class FileInterface(reporter: Reporter, origFile: File) { i += 1 } while(continue) - val newCode = ScalaPrinter(solution) + val newCode = ScalaPrinter(solution, indent/2) newStr = (newStr.substring(0, lastFound+newStrOffset))+newCode+(newStr.substring(i+newStrOffset, newStr.length)) newStrOffset += -(i-lastFound)+newCode.length diff --git a/src/main/scala/leon/synthesis/Heuristics.scala b/src/main/scala/leon/synthesis/Heuristics.scala index cb18ef781aec214808b3d9275b03c992deef524b..858938a91af30dc690f6cc9689cc62c777674826 100644 --- a/src/main/scala/leon/synthesis/Heuristics.scala +++ b/src/main/scala/leon/synthesis/Heuristics.scala @@ -38,7 +38,7 @@ object HeuristicInstantiation { Some(s) } - def apply(problem: Problem, rule: Rule, subProblems: List[Problem], onSuccess: List[Solution] => Option[Solution]): RuleInstantiation = { + def apply(problem: Problem, rule: Rule, subProblems: List[Problem], onSuccess: List[Solution] => Option[Solution], description: String): RuleInstantiation = { val subTypes = subProblems.map(p => TupleType(p.xs.map(_.getType))) val builder = new SolutionBuilder(subProblems.size, subTypes) { @@ -47,7 +47,7 @@ object HeuristicInstantiation { } } - new RuleInstantiation(problem, rule, builder) { + new RuleInstantiation(problem, rule, builder, description) { def apply(sctx: SynthesisContext) = RuleDecomposed(subProblems) } diff --git a/src/main/scala/leon/synthesis/ParallelSearch.scala b/src/main/scala/leon/synthesis/ParallelSearch.scala index efe05182e1d329af67f7d744b7fa10348b941244..ca4346ade489ee110db92945452d891c822993df 100644 --- a/src/main/scala/leon/synthesis/ParallelSearch.scala +++ b/src/main/scala/leon/synthesis/ParallelSearch.scala @@ -12,6 +12,10 @@ class ParallelSearch(synth: Synthesizer, costModel: CostModel, nWorkers: Int) extends AndOrGraphParallelSearch[SynthesisContext, TaskRunRule, TaskTryRules, Solution](new AndOrGraph(TaskTryRules(problem), SearchCostModel(costModel)), nWorkers) { + def this(synth: Synthesizer, problem: Problem, nWorkers: Int) = { + this(synth, problem, synth.rules, synth.options.costModel, nWorkers) + } + import synth.reporter._ // This is HOT shared memory, used only in stop() for shutting down solvers! diff --git a/src/main/scala/leon/synthesis/Problem.scala b/src/main/scala/leon/synthesis/Problem.scala index e0ca64b78dff385aad774b86f44315b16dc7bb83..7810483affb6268979ba5f4d35f82b62db6905cf 100644 --- a/src/main/scala/leon/synthesis/Problem.scala +++ b/src/main/scala/leon/synthesis/Problem.scala @@ -12,11 +12,11 @@ case class Problem(as: List[Identifier], pc: Expr, phi: Expr, xs: List[Identifie } object Problem { - def fromChoose(ch: Choose): Problem = { + def fromChoose(ch: Choose, pc: Expr = BooleanLiteral(true)): Problem = { val xs = ch.vars val phi = ch.pred - val as = (variablesOf(phi)--xs).toList + val as = (variablesOf(And(pc, phi))--xs).toList - Problem(as, BooleanLiteral(true), phi, xs) + Problem(as, pc, phi, xs) } } diff --git a/src/main/scala/leon/synthesis/Rules.scala b/src/main/scala/leon/synthesis/Rules.scala index 7926efd69ac916f6678ec00b6a97fbddb7171864..ae46319400edf0efa255d31425f1e4df39b0da98 100644 --- a/src/main/scala/leon/synthesis/Rules.scala +++ b/src/main/scala/leon/synthesis/Rules.scala @@ -49,8 +49,10 @@ object SolutionBuilder { } } -abstract class RuleInstantiation(val problem: Problem, val rule: Rule, val onSuccess: SolutionBuilder) { +abstract class RuleInstantiation(val problem: Problem, val rule: Rule, val onSuccess: SolutionBuilder, val description: String) { def apply(sctx: SynthesisContext): RuleApplicationResult + + override def toString = description } sealed abstract class RuleApplicationResult @@ -59,16 +61,16 @@ case class RuleDecomposed(sub: List[Problem]) extends RuleApplicationResult case object RuleApplicationImpossible extends RuleApplicationResult object RuleInstantiation { - def immediateDecomp(problem: Problem, rule: Rule, sub: List[Problem], onSuccess: List[Solution] => Option[Solution]) = { + def immediateDecomp(problem: Problem, rule: Rule, sub: List[Problem], onSuccess: List[Solution] => Option[Solution], description: String) = { val subTypes = sub.map(p => TupleType(p.xs.map(_.getType))) - new RuleInstantiation(problem, rule, new SolutionCombiner(sub.size, subTypes, onSuccess)) { + new RuleInstantiation(problem, rule, new SolutionCombiner(sub.size, subTypes, onSuccess), description) { def apply(sctx: SynthesisContext) = RuleDecomposed(sub) } } def immediateSuccess(problem: Problem, rule: Rule, solution: Solution) = { - new RuleInstantiation(problem, rule, new SolutionCombiner(0, Seq(), ls => Some(solution))) { + new RuleInstantiation(problem, rule, new SolutionCombiner(0, Seq(), ls => Some(solution)), "Solve with "+solution) { def apply(sctx: SynthesisContext) = RuleSuccess(solution) } } diff --git a/src/main/scala/leon/synthesis/SimpleSearch.scala b/src/main/scala/leon/synthesis/SimpleSearch.scala index 32258cedda16cbb3da45c077b89f68d8e0cd074c..06fa7da537e3ed2143ac88ea9c05c498c3d11916 100644 --- a/src/main/scala/leon/synthesis/SimpleSearch.scala +++ b/src/main/scala/leon/synthesis/SimpleSearch.scala @@ -35,6 +35,10 @@ class SimpleSearch(synth: Synthesizer, rules: Seq[Rule], costModel: CostModel) extends AndOrGraphSearch[TaskRunRule, TaskTryRules, Solution](new AndOrGraph(TaskTryRules(problem), SearchCostModel(costModel))) { + def this(synth: Synthesizer, problem: Problem) = { + this(synth, problem, synth.rules, synth.options.costModel) + } + import synth.reporter._ val sctx = SynthesisContext.fromSynthesizer(synth) @@ -85,25 +89,29 @@ class SimpleSearch(synth: Synthesizer, var shouldStop = false + def searchStep() { + nextLeaf() match { + case Some(l) => + l match { + case al: g.AndLeaf => + val sub = expandAndTask(al.task) + onExpansion(al, sub) + case ol: g.OrLeaf => + val sub = expandOrTask(ol.task) + onExpansion(ol, sub) + } + case None => + stop() + } + } + def search(): Option[Solution] = { sctx.solver.init() shouldStop = false while (!g.tree.isSolved && !shouldStop) { - nextLeaf() match { - case Some(l) => - l match { - case al: g.AndLeaf => - val sub = expandAndTask(al.task) - onExpansion(al, sub) - case ol: g.OrLeaf => - val sub = expandOrTask(ol.task) - onExpansion(ol, sub) - } - case None => - stop() - } + searchStep() } g.tree.solution } diff --git a/src/main/scala/leon/synthesis/Solution.scala b/src/main/scala/leon/synthesis/Solution.scala index 6dbfc3844b52a0eb873ea7304c6436ee9c67ca89..965b1f74a7bde5c2c7c8d2bb18dc2919be7274e6 100644 --- a/src/main/scala/leon/synthesis/Solution.scala +++ b/src/main/scala/leon/synthesis/Solution.scala @@ -1,11 +1,13 @@ package leon package synthesis -import leon.purescala.Trees._ -import leon.purescala.TypeTrees.{TypeTree,TupleType} -import leon.purescala.Definitions._ -import leon.purescala.TreeOps._ -import leon.xlang.Trees.LetDef +import purescala.Trees._ +import purescala.TypeTrees.{TypeTree,TupleType} +import purescala.Definitions._ +import purescala.TreeOps._ +import xlang.Trees.LetDef +import xlang.TreeOps.{ScopeSimplifier => XLangScopeSimplifier} +import solvers.z3.UninterpretedZ3Solver // Defines a synthesis solution of the form: // ⟨ P | T ⟩ @@ -26,6 +28,26 @@ class Solution(val pre: Expr, val defs: Set[FunDef], val term: Expr) { def fullTerm = defs.foldLeft(term){ case (t, fd) => LetDef(fd, t) } + + def toSimplifiedExpr(ctx: LeonContext, p: Program): Expr = { + val uninterpretedZ3 = new UninterpretedZ3Solver(ctx.copy(reporter = new SilentReporter)) + uninterpretedZ3.setProgram(p) + + val simplifiers = List[Expr => Expr]( + simplifyTautologies(uninterpretedZ3)(_), + simplifyLets _, + decomposeIfs _, + matchToIfThenElse _, + simplifyPaths(uninterpretedZ3)(_), + patternMatchReconstruction _, + simplifyTautologies(uninterpretedZ3)(_), + simplifyLets _, + rewriteTuples _, + (new ScopeSimplifier with XLangScopeSimplifier).transform _ + ) + + simplifiers.foldLeft(toExpr){ (x, sim) => sim(x) } + } } object Solution { diff --git a/src/main/scala/leon/synthesis/SynthesisContext.scala b/src/main/scala/leon/synthesis/SynthesisContext.scala index 92ae31535579dfba4942f3ff6b55cc13ad5d774a..342e1766acc19e5e76def80d3e4161066b8e8210 100644 --- a/src/main/scala/leon/synthesis/SynthesisContext.scala +++ b/src/main/scala/leon/synthesis/SynthesisContext.scala @@ -10,10 +10,11 @@ import java.util.concurrent.atomic.AtomicBoolean case class SynthesisContext( context: LeonContext, - options: SynthesizerOptions, + options: SynthesisOptions, functionContext: Option[FunDef], program: Program, solver: Solver, + simpleSolver: Solver, reporter: Reporter, shouldStop: AtomicBoolean ) @@ -26,6 +27,7 @@ object SynthesisContext { synth.functionContext, synth.program, synth.solver, + synth.simpleSolver, synth.reporter, synth.shouldStop) } diff --git a/src/main/scala/leon/synthesis/SynthesizerOptions.scala b/src/main/scala/leon/synthesis/SynthesisOptions.scala similarity index 78% rename from src/main/scala/leon/synthesis/SynthesizerOptions.scala rename to src/main/scala/leon/synthesis/SynthesisOptions.scala index 647e4049da9e479d824621fc363426fbd6a4ae54..f221fb3aa82614874a0fc1a2da83467cf468a81c 100644 --- a/src/main/scala/leon/synthesis/SynthesizerOptions.scala +++ b/src/main/scala/leon/synthesis/SynthesisOptions.scala @@ -1,13 +1,15 @@ package leon package synthesis -case class SynthesizerOptions( +case class SynthesisOptions( + inPlace: Boolean = false, generateDerivationTrees: Boolean = false, filterFuns: Option[Set[String]] = None, searchWorkers: Int = 1, firstOnly: Boolean = false, timeoutMs: Option[Long] = None, costModel: CostModel = CostModel.default, + rules: Seq[Rule] = Rules.all ++ Heuristics.all, // Cegis related options cegisGenerateFunCalls: Boolean = false, diff --git a/src/main/scala/leon/synthesis/SynthesisPhase.scala b/src/main/scala/leon/synthesis/SynthesisPhase.scala index ed7cf742958023fa68b7f99cc6dfce984a763d2f..dc15b0b2ad828903690abd6a12fe7d5d52c0aeff 100644 --- a/src/main/scala/leon/synthesis/SynthesisPhase.scala +++ b/src/main/scala/leon/synthesis/SynthesisPhase.scala @@ -24,21 +24,12 @@ object SynthesisPhase extends LeonPhase[Program, Program] { LeonFlagOptionDef( "cegis:gencalls", "--cegis:gencalls", "Include function calls in CEGIS generators") ) - def run(ctx: LeonContext)(p: Program): Program = { - val silentContext : LeonContext = ctx.copy(reporter = new SilentReporter) - - val mainSolver = new FairZ3Solver(silentContext) - mainSolver.setProgram(p) - - val uninterpretedZ3 = new UninterpretedZ3Solver(silentContext) - uninterpretedZ3.setProgram(p) - - var options = SynthesizerOptions() - var inPlace = false + def processOptions(ctx: LeonContext): SynthesisOptions = { + var options = SynthesisOptions() for(opt <- ctx.options) opt match { case LeonFlagOption("inplace") => - inPlace = true + options = options.copy(inPlace = true) case LeonValueOption("functions", ListValue(fs)) => options = options.copy(filterFuns = Some(fs.toSet)) @@ -84,74 +75,36 @@ object SynthesisPhase extends LeonPhase[Program, Program] { case _ => } - def synthesizeAll(program: Program): Map[Choose, (FunDef, Solution)] = { - def noop(u:Expr, u2: Expr) = u - - var solutions = Map[Choose, (FunDef, Solution)]() - - def actOnChoose(f: FunDef)(e: Expr, a: Expr): Expr = e match { - case ch @ Choose(vars, pred) => - val problem = Problem.fromChoose(ch) - val synth = new Synthesizer(ctx, - Some(f), - mainSolver, - p, - problem, - Rules.all ++ Heuristics.all, - options) - val sol = synth.synthesize() + options + } - solutions += ch -> (f, sol._1) + def run(ctx: LeonContext)(p: Program): Program = { + val options = processOptions(ctx) - a - case _ => - a - } + def toProcess(ci: ChooseInfo) = { + options.filterFuns.isEmpty || options.filterFuns.get.contains(ci.fd.id.toString) + } - // Look for choose() - for (f <- program.definedFunctions.sortBy(_.id.toString) if f.body.isDefined) { - if (options.filterFuns.isEmpty || options.filterFuns.get.contains(f.id.toString)) { - treeCatamorphism(x => x, noop, actOnChoose(f), f.body.get) - } - } + var chooses = ChooseInfo.extractFromProgram(ctx, p, options).filter(toProcess) - solutions - } + val results = chooses.map { ci => + val (sol, isComplete) = ci.synthesizer.synthesize() - val solutions = synthesizeAll(p) - - // Simplify expressions - val simplifiers = List[Expr => Expr]( - simplifyTautologies(uninterpretedZ3)(_), - simplifyLets _, - decomposeIfs _, - matchToIfThenElse _, - simplifyPaths(uninterpretedZ3)(_), - patternMatchReconstruction _, - simplifyTautologies(uninterpretedZ3)(_), - simplifyLets _, - rewriteTuples _ - ) - - def simplify(e: Expr): Expr = simplifiers.foldLeft(e){ (x, sim) => sim(x) } - - val chooseToExprs = solutions.map { - case (ch, (fd, sol)) => - (ch, (fd, simplify(sol.toExpr))) - } + ci -> sol.toSimplifiedExpr(ctx, p) + }.toMap - if (inPlace) { + if (options.inPlace) { for (file <- ctx.files) { - new FileInterface(ctx.reporter, file).updateFile(chooseToExprs.mapValues(_._2)) + new FileInterface(ctx.reporter).updateFile(file, results) } } else { - for ((chs, (fd, ex)) <- chooseToExprs) { - val middle = " In "+fd.id.toString+", synthesis of: " + for ((ci, ex) <- results) { + val middle = " In "+ci.fd.id.toString+", synthesis of: " val remSize = (80-middle.length) ctx.reporter.info("-"*math.floor(remSize/2).toInt+middle+"-"*math.ceil(remSize/2).toInt) - ctx.reporter.info(chs) + ctx.reporter.info(ci.ch) ctx.reporter.info("-"*35+" Result: "+"-"*36) ctx.reporter.info(ScalaPrinter(ex)) ctx.reporter.info("") diff --git a/src/main/scala/leon/synthesis/Synthesizer.scala b/src/main/scala/leon/synthesis/Synthesizer.scala index 624b6a178e01c71090cb94823b56364900743676..6b792aea284d8765d46b7cee821ce78a624f5f07 100644 --- a/src/main/scala/leon/synthesis/Synthesizer.scala +++ b/src/main/scala/leon/synthesis/Synthesizer.scala @@ -6,6 +6,7 @@ import purescala.Definitions.{Program, FunDef} import purescala.TreeOps._ import purescala.Trees.{Expr, Not} import purescala.ScalaPrinter +import solvers.z3._ import sun.misc.{Signal, SignalHandler} import solvers.Solver @@ -19,25 +20,31 @@ import java.util.concurrent.atomic.AtomicBoolean class Synthesizer(val context : LeonContext, val functionContext: Option[FunDef], - val solver: Solver, val program: Program, val problem: Problem, - val rules: Seq[Rule], - val options: SynthesizerOptions) { + val options: SynthesisOptions) { - protected[synthesis] val reporter = context.reporter + val silentContext = context.copy(reporter = new SilentReporter) - import reporter.{error,warning,info,fatalError} + val rules: Seq[Rule] = options.rules + + val solver: FairZ3Solver = new FairZ3Solver(silentContext) + solver.setProgram(program) + + val simpleSolver: Solver = new UninterpretedZ3Solver(silentContext) + simpleSolver.setProgram(program) + + val reporter = context.reporter var shouldStop = new AtomicBoolean(false) def synthesize(): (Solution, Boolean) = { - val search = if (options.searchWorkers > 1) { - new ParallelSearch(this, problem, rules, options.costModel, options.searchWorkers) - } else { - new SimpleSearch(this, problem, rules, options.costModel) - } + val search = if (options.searchWorkers > 1) { + new ParallelSearch(this, problem, options.searchWorkers) + } else { + new SimpleSearch(this, problem) + } val sigINT = new Signal("INT") var oldHandler: SignalHandler = null @@ -72,7 +79,4 @@ class Synthesizer(val context : LeonContext, (new AndOrGraphPartialSolution(search.g, (task: TaskRunRule) => Solution.choose(task.problem)).getSolution, false) } } - } - - diff --git a/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala b/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala index b1f95c72644a81f0fa39364c1cbcc52522ce582a..39ff4d4c25cdc6e67424afe5d01ba33af6f671eb 100644 --- a/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala +++ b/src/main/scala/leon/synthesis/heuristics/ADTInduction.scala @@ -15,7 +15,7 @@ case object ADTInduction extends Rule("ADT Induction") with Heuristic { case IsTyped(origId, AbstractClassType(cd)) => (origId, cd) } - val steps = for (candidate <- candidates) yield { + val instances = for (candidate <- candidates) yield { val (origId, cd) = candidate val oas = p.as.filterNot(_ == origId) @@ -35,10 +35,13 @@ case object ADTInduction extends Rule("ADT Induction") with Heuristic { case _ => false } + // Map for getting a formula in the context of within the recursive function + val substMap = residualMap + (origId -> Variable(inductOn)) + if (isRecursive) { - val innerPhi = substAll(residualMap + (origId -> Variable(inductOn)), p.phi) - val innerPC = substAll(residualMap + (origId -> Variable(inductOn)), p.pc) + val innerPhi = substAll(substMap, p.phi) + val innerPC = substAll(substMap, p.pc) val subProblemsInfo = for (dcd <- cd.knownDescendents.sortBy(_.id.name)) yield dcd match { case ccd : CaseClassDef => @@ -92,11 +95,12 @@ case object ADTInduction extends Rule("ADT Induction") with Heuristic { // might only have to enforce it on solutions of base cases. None } else { - val funPre = subst(origId -> Variable(inductOn), Or(globalPre)) + val funPre = substAll(substMap, Or(globalPre)) + val funPost = substAll(substMap, p.phi) val outerPre = Or(globalPre) newFun.precondition = Some(funPre) - newFun.postcondition = Some(LetTuple(p.xs.toSeq, ResultVariable().setType(resType), p.phi)) + newFun.postcondition = Some(LetTuple(p.xs.toSeq, ResultVariable().setType(resType), funPost)) newFun.body = Some(MatchExpr(Variable(inductOn), cases)) @@ -107,12 +111,12 @@ case object ADTInduction extends Rule("ADT Induction") with Heuristic { } } - Some(HeuristicInstantiation(p, this, subProblemsInfo.map(_._1).toList, onSuccess)) + Some(HeuristicInstantiation(p, this, subProblemsInfo.map(_._1).toList, onSuccess, "ADT Induction on '"+origId+"'")) } else { None } } - steps.flatten + instances.flatten } } diff --git a/src/main/scala/leon/synthesis/heuristics/InnerCaseSplit.scala b/src/main/scala/leon/synthesis/heuristics/InnerCaseSplit.scala index b2782c5dc3d7dbb1d3a3979a06bf9065df0a68f0..8a76cd9f0d7a40e6700fcbe0446328514f8f8346 100644 --- a/src/main/scala/leon/synthesis/heuristics/InnerCaseSplit.scala +++ b/src/main/scala/leon/synthesis/heuristics/InnerCaseSplit.scala @@ -26,13 +26,13 @@ case object InnerCaseSplit extends Rule("Inner-Case-Split") with Heuristic { phi match { case Or(os) => - List(rules.CaseSplit.split(os, p)) + List(rules.CaseSplit.split(os, p, "Inner case-split")) case And(as) => val optapp = for ((a, i) <- as.zipWithIndex) yield { a match { case Or(os) => - Some(rules.CaseSplit.split(os.map(o => And(as.updated(i, o))), p)) + Some(rules.CaseSplit.split(os.map(o => And(as.updated(i, o))), p, "Inner case-split")) case _ => None diff --git a/src/main/scala/leon/synthesis/heuristics/IntInduction.scala b/src/main/scala/leon/synthesis/heuristics/IntInduction.scala index e59150394e2146f0ce6badcd99f44248e2d5d8d0..b94ceffd967931c7db640f42976a68353385734d 100644 --- a/src/main/scala/leon/synthesis/heuristics/IntInduction.scala +++ b/src/main/scala/leon/synthesis/heuristics/IntInduction.scala @@ -60,7 +60,7 @@ case object IntInduction extends Rule("Int Induction") with Heuristic { None } - Some(HeuristicInstantiation(p, this, List(subBase, subGT, subLT), onSuccess)) + Some(HeuristicInstantiation(p, this, List(subBase, subGT, subLT), onSuccess, "Int Induction on '"+origId+"'")) case _ => None } diff --git a/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala b/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala index 1a957e40250d7cab755d4657463d0a9f04aad9ed..a4308de79ec42d4ac50b24c0dcabae4291385698 100644 --- a/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala +++ b/src/main/scala/leon/synthesis/heuristics/OptimisticInjection.scala @@ -36,7 +36,7 @@ case object OptimisticInjection extends Rule("Opt. Injection") with Heuristic { val sub = p.copy(phi = And(newExprs)) - Some(HeuristicInstantiation(p, this, List(sub), forward)) + Some(HeuristicInstantiation(p, this, List(sub), forward, this.name)) } else { None } diff --git a/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala b/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala index 488df73556f61321eaf64ca21923b505087978b1..b906d37e9355e0a2ca4cb6572c3ca175c4f7ac51 100644 --- a/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala +++ b/src/main/scala/leon/synthesis/heuristics/SelectiveInlining.scala @@ -36,7 +36,7 @@ case object SelectiveInlining extends Rule("Sel. Inlining") with Heuristic { val sub = p.copy(phi = And(newExprs)) - Some(HeuristicInstantiation(p, this, List(sub), forward)) + Some(HeuristicInstantiation(p, this, List(sub), forward, this.name)) } else { None } diff --git a/src/main/scala/leon/synthesis/rules/ADTDual.scala b/src/main/scala/leon/synthesis/rules/ADTDual.scala index f1811f073e66e3fc69a7546d9d52be91428dea08..41202315d13fa2b9dc9bf8f1573c9fbd9365ebf7 100644 --- a/src/main/scala/leon/synthesis/rules/ADTDual.scala +++ b/src/main/scala/leon/synthesis/rules/ADTDual.scala @@ -24,7 +24,7 @@ case object ADTDual extends Rule("ADTDual") { if (!toRemove.isEmpty) { val sub = p.copy(phi = And((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq)) - List(RuleInstantiation.immediateDecomp(p, this, List(sub), forward)) + List(RuleInstantiation.immediateDecomp(p, this, List(sub), forward, "ADTDual")) } else { Nil } diff --git a/src/main/scala/leon/synthesis/rules/ADTSplit.scala b/src/main/scala/leon/synthesis/rules/ADTSplit.scala index 04cd58c5a70b6a2bbbec25ce9807e00457a746a8..228923bb49b4a3d3a6dd6f0374f09cff569fdae0 100644 --- a/src/main/scala/leon/synthesis/rules/ADTSplit.scala +++ b/src/main/scala/leon/synthesis/rules/ADTSplit.scala @@ -2,8 +2,6 @@ package leon package synthesis package rules -import solvers.TimeoutSolver - import purescala.Trees._ import purescala.Common._ import purescala.TypeTrees._ @@ -13,7 +11,7 @@ import purescala.Definitions._ case object ADTSplit extends Rule("ADT Split.") { def instantiateOn(sctx: SynthesisContext, p: Problem): Traversable[RuleInstantiation]= { - val solver = new TimeoutSolver(sctx.solver, 100L) // We give that 100ms + val solver = sctx.simpleSolver val candidates = p.as.collect { case IsTyped(id, AbstractClassType(cd)) => @@ -73,7 +71,7 @@ case object ADTSplit extends Rule("ADT Split.") { Some(Solution(Or(globalPre), sols.flatMap(_.defs).toSet, MatchExpr(Variable(id), cases))) } - Some(RuleInstantiation.immediateDecomp(p, this, subInfo.map(_._2).toList, onSuccess)) + Some(RuleInstantiation.immediateDecomp(p, this, subInfo.map(_._2).toList, onSuccess, "ADT Split on '"+id+"'")) case _ => None }}.flatten diff --git a/src/main/scala/leon/synthesis/rules/Assert.scala b/src/main/scala/leon/synthesis/rules/Assert.scala index cab8ea72c20094db0018dd2ad97f74c982a2c5a9..efafc204d318141d1cdef74a456269cd2cc56af0 100644 --- a/src/main/scala/leon/synthesis/rules/Assert.scala +++ b/src/main/scala/leon/synthesis/rules/Assert.scala @@ -23,7 +23,7 @@ case object Assert extends NormalizingRule("Assert") { List(RuleInstantiation.immediateDecomp(p, this, List(sub), { case Solution(pre, defs, term) :: Nil => Some(Solution(And(exprsA :+ pre), defs, term)) case _ => None - })) + }, "Assert "+And(exprsA))) } } else { Nil diff --git a/src/main/scala/leon/synthesis/rules/CaseSplit.scala b/src/main/scala/leon/synthesis/rules/CaseSplit.scala index 1e1ac7940967c3791060f1bf9ab58afe947fc30d..f22f97be439f1d084c5b1751042ec07337916e54 100644 --- a/src/main/scala/leon/synthesis/rules/CaseSplit.scala +++ b/src/main/scala/leon/synthesis/rules/CaseSplit.scala @@ -10,13 +10,13 @@ case object CaseSplit extends Rule("Case-Split") { def instantiateOn(sctx: SynthesisContext, p: Problem): Traversable[RuleInstantiation] = { p.phi match { case Or(os) => - List(split(os, p)) + List(split(os, p, "Split top-level Or")) case _ => Nil } } - def split(alts: Seq[Expr], p: Problem): RuleInstantiation = { + def split(alts: Seq[Expr], p: Problem, description: String): RuleInstantiation = { val subs = alts.map(a => Problem(p.as, p.pc, a, p.xs)).toList val onSuccess: List[Solution] => Option[Solution] = { @@ -34,7 +34,7 @@ case object CaseSplit extends Rule("Case-Split") { None } - RuleInstantiation.immediateDecomp(p, this, subs, onSuccess) + RuleInstantiation.immediateDecomp(p, this, subs, onSuccess, description) } } diff --git a/src/main/scala/leon/synthesis/rules/Cegis.scala b/src/main/scala/leon/synthesis/rules/Cegis.scala index 296d4ef401007d165f0f6e9d6b59790a3f6ba2e6..41527707f3150f6b2fb6bba0705b9deb37337a42 100644 --- a/src/main/scala/leon/synthesis/rules/Cegis.scala +++ b/src/main/scala/leon/synthesis/rules/Cegis.scala @@ -365,289 +365,278 @@ case object CEGIS extends Rule("CEGIS") { def css : Set[Identifier] = mappings.values.map(_._1).toSet ++ guardedTerms.flatMap(_._2) } - val TopLevelAnds(ands) = p.phi + List(new RuleInstantiation(p, this, SolutionBuilder.none, "CEGIS") { + def apply(sctx: SynthesisContext): RuleApplicationResult = { + var result: Option[RuleApplicationResult] = None - val xsSet = p.xs.toSet + var ass = p.as.toSet + var xss = p.xs.toSet + val initGuard = FreshIdentifier("START", true).setType(BooleanType) - val (exprsA, others) = ands.partition(e => (variablesOf(e) & xsSet).isEmpty) - if (exprsA.isEmpty) { - val res = new RuleInstantiation(p, this, SolutionBuilder.none) { - def apply(sctx: SynthesisContext): RuleApplicationResult = { - var result: Option[RuleApplicationResult] = None + val ndProgram = new NonDeterministicProgram(p, initGuard) + var unrolings = 0 + val maxUnrolings = 3 - var ass = p.as.toSet - var xss = p.xs.toSet + val mainSolver = new TimeoutSolver(sctx.solver, 10000L) // 10sec - val initGuard = FreshIdentifier("START", true).setType(BooleanType) + var exampleInputs = Set[Seq[Expr]]() - val ndProgram = new NonDeterministicProgram(p, initGuard) - var unrolings = 0 - val maxUnrolings = 3 - - val mainSolver = new TimeoutSolver(sctx.solver, 2000L) // 2sec - - var exampleInputs = Set[Seq[Expr]]() - - // We populate the list of examples with a predefined one - if (p.pc == BooleanLiteral(true)) { - exampleInputs += p.as.map(a => simplestValue(a.getType)) - } else { - val solver = mainSolver.getNewSolver + // We populate the list of examples with a predefined one + if (p.pc == BooleanLiteral(true)) { + exampleInputs += p.as.map(a => simplestValue(a.getType)) + } else { + val solver = mainSolver.getNewSolver - solver.assertCnstr(p.pc) + solver.assertCnstr(p.pc) - solver.check match { - case Some(true) => - val model = solver.getModel - exampleInputs += p.as.map(a => model.getOrElse(a, simplestValue(a.getType))) + solver.check match { + case Some(true) => + val model = solver.getModel + exampleInputs += p.as.map(a => model.getOrElse(a, simplestValue(a.getType))) - case Some(false) => - return RuleApplicationImpossible + case Some(false) => + return RuleApplicationImpossible - case None => - sctx.reporter.warning("Solver could not solve path-condition") - return RuleApplicationImpossible // This is not necessary though, but probably wanted - } + case None => + sctx.reporter.warning("Solver could not solve path-condition") + return RuleApplicationImpossible // This is not necessary though, but probably wanted } + } - // Keep track of collected cores to filter programs to test - var collectedCores = Set[Set[Identifier]]() - - // solver1 is used for the initial SAT queries - var solver1 = mainSolver.getNewSolver - solver1.assertCnstr(And(p.pc :: p.phi :: Variable(initGuard) :: Nil)) + // Keep track of collected cores to filter programs to test + var collectedCores = Set[Set[Identifier]]() - // solver2 is used for validating a candidate program, or finding new inputs - val solver2 = mainSolver.getNewSolver - solver2.assertCnstr(And(p.pc :: Not(p.phi) :: Variable(initGuard) :: Nil)) + // solver1 is used for the initial SAT queries + var solver1 = mainSolver.getNewSolver + solver1.assertCnstr(And(p.pc :: p.phi :: Variable(initGuard) :: Nil)) + // solver2 is used for validating a candidate program, or finding new inputs + val solver2 = mainSolver.getNewSolver + solver2.assertCnstr(And(p.pc :: Not(p.phi) :: Variable(initGuard) :: Nil)) - var allClauses = List[Expr]() - try { - do { - var needMoreUnrolling = false + var allClauses = List[Expr]() - // Compute all programs that have not been excluded yet - var allPrograms: Set[Set[Identifier]] = if (useCEPruning) { - ndProgram.allPrograms.filterNot(p => collectedCores.exists(c => c.subsetOf(p))) - } else { - Set() - } + try { + do { + var needMoreUnrolling = false - //println("Programs: "+allPrograms.size) - //println("CEs: "+exampleInputs.size) + // Compute all programs that have not been excluded yet + var allPrograms: Set[Set[Identifier]] = if (useCEPruning) { + ndProgram.allPrograms.filterNot(p => collectedCores.exists(c => c.subsetOf(p))) + } else { + Set() + } - // We further filter the set of working programs to remove those that fail on known examples - if (useCEPruning && !exampleInputs.isEmpty && ndProgram.canTest()) { - //for (ce <- exampleInputs) { - // println("CE: "+ce) - //} + //println("Programs: "+allPrograms.size) + //println("CEs: "+exampleInputs.size) - for (p <- allPrograms) { - if (!exampleInputs.forall(ndProgram.testForProgram(p))) { - // This program failed on at least one example - solver1.assertCnstr(Not(And(p.map(Variable(_)).toSeq))) - allPrograms -= p - } - } + // We further filter the set of working programs to remove those that fail on known examples + if (useCEPruning && !exampleInputs.isEmpty && ndProgram.canTest()) { + //for (ce <- exampleInputs) { + // println("CE: "+ce) + //} - if (allPrograms.isEmpty) { - needMoreUnrolling = true + for (p <- allPrograms) { + if (!exampleInputs.forall(ndProgram.testForProgram(p))) { + // This program failed on at least one example + solver1.assertCnstr(Not(And(p.map(Variable(_)).toSeq))) + allPrograms -= p } + } - //println("Passing tests: "+allPrograms.size) + if (allPrograms.isEmpty) { + needMoreUnrolling = true } - //allPrograms.foreach { p => - // println("PATH: "+p) - // println("CLAUSES: "+p.flatMap( b => ndProgram.mappings.get(b).map{ case (c, ex) => c+" = "+ex}).mkString(" && ")) - //} + //println("Passing tests: "+allPrograms.size) + } - val (clauses, closedBs) = ndProgram.unroll - //println("UNROLLING: ") - //for (c <- clauses) { - // println(" - " + c) - //} - //println("CLOSED Bs "+closedBs) + //allPrograms.foreach { p => + // println("PATH: "+p) + // println("CLAUSES: "+p.flatMap( b => ndProgram.mappings.get(b).map{ case (c, ex) => c+" = "+ex}).mkString(" && ")) + //} - val clause = And(clauses) - allClauses = clause :: allClauses + val (clauses, closedBs) = ndProgram.unroll + //println("UNROLLING: ") + //for (c <- clauses) { + // println(" - " + c) + //} + //println("CLOSED Bs "+closedBs) - solver1.assertCnstr(clause) - solver2.assertCnstr(clause) + val clause = And(clauses) + allClauses = clause :: allClauses - val tpe = TupleType(p.xs.map(_.getType)) - val bss = ndProgram.bss + solver1.assertCnstr(clause) + solver2.assertCnstr(clause) - if (clauses.isEmpty) { - needMoreUnrolling = true - } + val tpe = TupleType(p.xs.map(_.getType)) + val bss = ndProgram.bss - while (result.isEmpty && !needMoreUnrolling && !sctx.shouldStop.get) { + if (clauses.isEmpty) { + needMoreUnrolling = true + } - solver1.checkAssumptions(closedBs.map(id => Not(Variable(id)))) match { - case Some(true) => - val satModel = solver1.getModel + while (result.isEmpty && !needMoreUnrolling && !sctx.shouldStop.get) { - val bssAssumptions: Set[Expr] = bss.map(b => satModel(b) match { - case BooleanLiteral(true) => Variable(b) - case BooleanLiteral(false) => Not(Variable(b)) - }) + solver1.checkAssumptions(closedBs.map(id => Not(Variable(id)))) match { + case Some(true) => + val satModel = solver1.getModel - //println("CEGIS OUT!") - //println("Found solution: "+bssAssumptions) + val bssAssumptions: Set[Expr] = bss.map(b => satModel(b) match { + case BooleanLiteral(true) => Variable(b) + case BooleanLiteral(false) => Not(Variable(b)) + }) - //bssAssumptions.collect { case Variable(b) => ndProgram.mappings(b) }.foreach { - // case (c, ex) => - // println(". "+c+" = "+ex) - //} + //println("CEGIS OUT!") + //println("Found solution: "+bssAssumptions) - val validateWithZ3 = if (useCETests && !exampleInputs.isEmpty && ndProgram.canTest()) { + //bssAssumptions.collect { case Variable(b) => ndProgram.mappings(b) }.foreach { + // case (c, ex) => + // println(". "+c+" = "+ex) + //} - val p = bssAssumptions.collect { case Variable(b) => b } + val validateWithZ3 = if (useCETests && !exampleInputs.isEmpty && ndProgram.canTest()) { - if (exampleInputs.forall(ndProgram.testForProgram(p))) { - // All valid inputs also work with this, we need to - // make sure by validating this candidate with z3 - true - } else { - // One valid input failed with this candidate, we can skip - solver1.assertCnstr(Not(And(p.map(Variable(_)).toSeq))) - false - } - } else { - // No inputs or capability to test, we need to ask Z3 + val p = bssAssumptions.collect { case Variable(b) => b } + + if (exampleInputs.forall(ndProgram.testForProgram(p))) { + // All valid inputs also work with this, we need to + // make sure by validating this candidate with z3 true + } else { + // One valid input failed with this candidate, we can skip + solver1.assertCnstr(Not(And(p.map(Variable(_)).toSeq))) + false } + } else { + // No inputs or capability to test, we need to ask Z3 + true + } - if (validateWithZ3) { - solver2.checkAssumptions(bssAssumptions) match { - case Some(true) => - //println("#"*80) - val invalidModel = solver2.getModel + if (validateWithZ3) { + solver2.checkAssumptions(bssAssumptions) match { + case Some(true) => + //println("#"*80) + val invalidModel = solver2.getModel - val fixedAss = And(ass.collect { - case a if invalidModel contains a => Equals(Variable(a), invalidModel(a)) - }.toSeq) + val fixedAss = And(ass.collect { + case a if invalidModel contains a => Equals(Variable(a), invalidModel(a)) + }.toSeq) - val newCE = p.as.map(valuateWithModel(invalidModel)) + val newCE = p.as.map(valuateWithModel(invalidModel)) - exampleInputs += newCE + exampleInputs += newCE - //println("Found counter example: "+fixedAss) + //println("Found counter example: "+fixedAss) - // Retest whether the newly found C-E invalidates all programs - if (useCEPruning && ndProgram.canTest) { - if (allPrograms.forall(p => !ndProgram.testForProgram(p)(newCE))) { - // println("I found a killer example!") - needMoreUnrolling = true - } + // Retest whether the newly found C-E invalidates all programs + if (useCEPruning && ndProgram.canTest) { + if (allPrograms.forall(p => !ndProgram.testForProgram(p)(newCE))) { + // println("I found a killer example!") + needMoreUnrolling = true } + } - val unsatCore = if (useUnsatCores) { - solver1.push() - solver1.assertCnstr(fixedAss) + val unsatCore = if (useUnsatCores) { + solver1.push() + solver1.assertCnstr(fixedAss) - val core = solver1.checkAssumptions(bssAssumptions) match { - case Some(false) => - // Core might be empty if unrolling level is - // insufficient, it becomes unsat no matter what - // the assumptions are. - solver1.getUnsatCore + val core = solver1.checkAssumptions(bssAssumptions) match { + case Some(false) => + // Core might be empty if unrolling level is + // insufficient, it becomes unsat no matter what + // the assumptions are. + solver1.getUnsatCore - case Some(true) => - // Can't be! - bssAssumptions + case Some(true) => + // Can't be! + bssAssumptions - case None => - return RuleApplicationImpossible - } + case None => + return RuleApplicationImpossible + } - solver1.pop() + solver1.pop() - collectedCores += core.collect{ case Variable(id) => id } + collectedCores += core.collect{ case Variable(id) => id } - core - } else { - bssAssumptions - } + core + } else { + bssAssumptions + } - if (unsatCore.isEmpty) { - needMoreUnrolling = true - } else { - //if (useCEAsserts) { - // val freshCss = ndProgram.css.map(c => c -> Variable(FreshIdentifier(c.name, true).setType(c.getType))).toMap - // val ceIn = ass.collect { - // case id if invalidModel contains id => id -> invalidModel(id) - // } + if (unsatCore.isEmpty) { + needMoreUnrolling = true + } else { + //if (useCEAsserts) { + // val freshCss = ndProgram.css.map(c => c -> Variable(FreshIdentifier(c.name, true).setType(c.getType))).toMap + // val ceIn = ass.collect { + // case id if invalidModel contains id => id -> invalidModel(id) + // } - // val ceMap = (freshCss ++ ceIn) + // val ceMap = (freshCss ++ ceIn) - // val counterexample = substAll(ceMap, And(Seq(ndProgram.program, p.phi))) + // val counterexample = substAll(ceMap, And(Seq(ndProgram.program, p.phi))) - // //val And(ands) = counterexample - // //println("CE:") - // //for (a <- ands) { - // // println(" - "+a) - // //} + // //val And(ands) = counterexample + // //println("CE:") + // //for (a <- ands) { + // // println(" - "+a) + // //} - // solver1.assertCnstr(counterexample) - //} + // solver1.assertCnstr(counterexample) + //} - solver1.assertCnstr(Not(And(unsatCore.toSeq))) - } + solver1.assertCnstr(Not(And(unsatCore.toSeq))) + } - case Some(false) => + case Some(false) => - val expr = ndProgram.determinize(satModel.filter(_._2 == BooleanLiteral(true)).keySet) + val expr = ndProgram.determinize(satModel.filter(_._2 == BooleanLiteral(true)).keySet) - result = Some(RuleSuccess(Solution(BooleanLiteral(true), Set(), expr))) + result = Some(RuleSuccess(Solution(BooleanLiteral(true), Set(), expr))) - case _ => - return RuleApplicationImpossible - } + case _ => + return RuleApplicationImpossible } + } - case Some(false) => - //println("%%%% UNSAT") + case Some(false) => + //println("%%%% UNSAT") - if (useUninterpretedProbe) { - solver1.check match { - case Some(false) => - // Unsat even without blockers (under which fcalls are then uninterpreted) - return RuleApplicationImpossible + if (useUninterpretedProbe) { + solver1.check match { + case Some(false) => + // Unsat even without blockers (under which fcalls are then uninterpreted) + return RuleApplicationImpossible - case _ => - } + case _ => } + } - needMoreUnrolling = true + needMoreUnrolling = true - case _ => - //println("%%%% WOOPS") - return RuleApplicationImpossible - } + case _ => + //println("%%%% WOOPS") + return RuleApplicationImpossible } + } - unrolings += 1 - } while(unrolings < maxUnrolings && result.isEmpty && !sctx.shouldStop.get) + unrolings += 1 + } while(unrolings < maxUnrolings && result.isEmpty && !sctx.shouldStop.get) - result.getOrElse(RuleApplicationImpossible) + result.getOrElse(RuleApplicationImpossible) - } catch { - case e: Throwable => - sctx.reporter.warning("CEGIS crashed: "+e.getMessage) - e.printStackTrace - RuleApplicationImpossible - } + } catch { + case e: Throwable => + sctx.reporter.warning("CEGIS crashed: "+e.getMessage) + e.printStackTrace + RuleApplicationImpossible } } - List(res) - } else { - Nil - } + }) } } diff --git a/src/main/scala/leon/synthesis/rules/DetupleOutput.scala b/src/main/scala/leon/synthesis/rules/DetupleOutput.scala index b5746897e6cddd6900ceebc0cf09eafe4d61851c..3d475f336668bd80d6b4edbb0a89b3632ccba49a 100644 --- a/src/main/scala/leon/synthesis/rules/DetupleOutput.scala +++ b/src/main/scala/leon/synthesis/rules/DetupleOutput.scala @@ -49,7 +49,7 @@ case object DetupleOutput extends Rule("Detuple Out") { } - Some(RuleInstantiation.immediateDecomp(p, this, List(sub), onSuccess)) + Some(RuleInstantiation.immediateDecomp(p, this, List(sub), onSuccess, this.name)) } else { Nil } diff --git a/src/main/scala/leon/synthesis/rules/Disunification.scala b/src/main/scala/leon/synthesis/rules/Disunification.scala index 4925f60004acf4e1f604fc47cbfee5a72a3083dc..b496a4dd41b459ca195b76098ef09caa1c2e91ac 100644 --- a/src/main/scala/leon/synthesis/rules/Disunification.scala +++ b/src/main/scala/leon/synthesis/rules/Disunification.scala @@ -26,7 +26,7 @@ object Disunification { if (!toRemove.isEmpty) { val sub = p.copy(phi = Or((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq)) - List(RuleInstantiation.immediateDecomp(p, this, List(sub), forward)) + List(RuleInstantiation.immediateDecomp(p, this, List(sub), forward, this.name)) } else { Nil } diff --git a/src/main/scala/leon/synthesis/rules/EqualitySplit.scala b/src/main/scala/leon/synthesis/rules/EqualitySplit.scala index c59d37aa014af86df043465d4db59194fe86ff0e..ae6d6da241d570469541574515eff6850af462aa 100644 --- a/src/main/scala/leon/synthesis/rules/EqualitySplit.scala +++ b/src/main/scala/leon/synthesis/rules/EqualitySplit.scala @@ -2,16 +2,15 @@ package leon package synthesis package rules -import solvers.TimeoutSolver import purescala.Trees._ import purescala.Common._ import purescala.TypeTrees._ import purescala.TreeOps._ import purescala.Extractors._ -case object EqualitySplit extends Rule("Eq. Split.") { +case object EqualitySplit extends Rule("Eq. Split") { def instantiateOn(sctx: SynthesisContext, p: Problem): Traversable[RuleInstantiation] = { - val solver = new TimeoutSolver(sctx.solver, 100L) // We give that 100ms + val solver = sctx.simpleSolver val candidates = p.as.groupBy(_.getType).mapValues(_.combinations(2).filter { case List(a1, a2) => @@ -51,7 +50,7 @@ case object EqualitySplit extends Rule("Eq. Split.") { None } - Some(RuleInstantiation.immediateDecomp(p, this, List(sub1, sub2), onSuccess)) + Some(RuleInstantiation.immediateDecomp(p, this, List(sub1, sub2), onSuccess, "Eq. Split on '"+a1+"' and '"+a2+"'")) case _ => None }) diff --git a/src/main/scala/leon/synthesis/rules/Ground.scala b/src/main/scala/leon/synthesis/rules/Ground.scala index bb74de12aba2f3a80861261cc221e045edd3f11f..73b1a81a56441138f53f9f939919aa4fa8b86720 100644 --- a/src/main/scala/leon/synthesis/rules/Ground.scala +++ b/src/main/scala/leon/synthesis/rules/Ground.scala @@ -12,7 +12,7 @@ case object Ground extends Rule("Ground") { def instantiateOn(sctx: SynthesisContext, p: Problem): Traversable[RuleInstantiation] = { if (p.as.isEmpty) { - val solver = new TimeoutSolver(sctx.solver, 1000L) // We give that 1s + val solver = new TimeoutSolver(sctx.solver, 5000L) // We give that 1s val tpe = TupleType(p.xs.map(_.getType)) diff --git a/src/main/scala/leon/synthesis/rules/InequalitySplit.scala b/src/main/scala/leon/synthesis/rules/InequalitySplit.scala index 11040132becd8790fbfb93ebf84a5163d41e0a8a..6c9e07d73e95c07d259c0b34f274d8a06b0e844e 100644 --- a/src/main/scala/leon/synthesis/rules/InequalitySplit.scala +++ b/src/main/scala/leon/synthesis/rules/InequalitySplit.scala @@ -2,8 +2,6 @@ package leon package synthesis package rules -import solvers.TimeoutSolver - import purescala.Trees._ import purescala.TypeTrees._ import purescala.Common._ @@ -13,7 +11,7 @@ import purescala.Extractors._ case object InequalitySplit extends Rule("Ineq. Split.") { def instantiateOn(sctx: SynthesisContext, p: Problem): Traversable[RuleInstantiation] = { - val solver = new TimeoutSolver(sctx.solver, 100L) // We give that 100ms + val solver = sctx.simpleSolver val candidates = p.as.filter(_.getType == Int32Type).combinations(2).toList.filter { case List(a1, a2) => @@ -74,7 +72,7 @@ case object InequalitySplit extends Rule("Ineq. Split.") { None } - Some(RuleInstantiation.immediateDecomp(p, this, List(subLT, subEQ, subGT), onSuccess)) + Some(RuleInstantiation.immediateDecomp(p, this, List(subLT, subEQ, subGT), onSuccess, "Ineq. Split on '"+a1+"' and '"+a2+"'")) case _ => None }) diff --git a/src/main/scala/leon/synthesis/rules/IntegerEquation.scala b/src/main/scala/leon/synthesis/rules/IntegerEquation.scala index 1d357990756e3d36c847e3bfe6ff3ac424cb790e..60101558d83417f88ab7e864ffad59ff3ca57abb 100644 --- a/src/main/scala/leon/synthesis/rules/IntegerEquation.scala +++ b/src/main/scala/leon/synthesis/rules/IntegerEquation.scala @@ -60,7 +60,7 @@ case object IntegerEquation extends Rule("Integer Equation") { None } - List(RuleInstantiation.immediateDecomp(problem, this, List(newProblem), onSuccess)) + List(RuleInstantiation.immediateDecomp(problem, this, List(newProblem), onSuccess, this.name)) } else { val (eqPre0, eqWitness, freshxs) = elimVariable(eqas, normalizedEq) @@ -105,7 +105,7 @@ case object IntegerEquation extends Rule("Integer Equation") { None } - List(RuleInstantiation.immediateDecomp(problem, this, List(newProblem), onSuccess)) + List(RuleInstantiation.immediateDecomp(problem, this, List(newProblem), onSuccess, this.name)) } } } diff --git a/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala b/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala index 540dd6b947847be7477cb76b04458d5558d9b18d..3ed407bb9256732e025114bc2b240e7d009fb1a6 100644 --- a/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala +++ b/src/main/scala/leon/synthesis/rules/IntegerInequalities.scala @@ -131,7 +131,7 @@ case object IntegerInequalities extends Rule("Integer Inequalities") { val constraints: List[Expr] = for((ub, uc) <- upperBounds; (lb, lc) <- lowerBounds) yield LessEquals(ceilingDiv(lb, IntLiteral(lc)), floorDiv(ub, IntLiteral(uc))) val pre = And(exprNotUsed ++ constraints) - List(RuleInstantiation.immediateSuccess(problem, this, Solution(pre, Set(), witness))) + List(RuleInstantiation.immediateSuccess(problem, this, Solution(pre, Set(), Tuple(Seq(witness))))) } else { val involvedVariables = (upperBounds++lowerBounds).foldLeft(Set[Identifier]())((acc, t) => { @@ -201,7 +201,7 @@ case object IntegerInequalities extends Rule("Integer Inequalities") { None } - List(RuleInstantiation.immediateDecomp(problem, this, List(subProblem), onSuccess)) + List(RuleInstantiation.immediateDecomp(problem, this, List(subProblem), onSuccess, this.name)) } } } diff --git a/src/main/scala/leon/synthesis/rules/OnePoint.scala b/src/main/scala/leon/synthesis/rules/OnePoint.scala index d245e7bb2ee45030442ff0bf41beae33c066a28e..1b44bbd06369beabe1449b6eb58f4e6595141f4c 100644 --- a/src/main/scala/leon/synthesis/rules/OnePoint.scala +++ b/src/main/scala/leon/synthesis/rules/OnePoint.scala @@ -37,7 +37,7 @@ case object OnePoint extends NormalizingRule("One-point") { None } - List(RuleInstantiation.immediateDecomp(p, this, List(newProblem), onSuccess)) + List(RuleInstantiation.immediateDecomp(p, this, List(newProblem), onSuccess, this.name)) } else { Nil } diff --git a/src/main/scala/leon/synthesis/rules/OptimisticGround.scala b/src/main/scala/leon/synthesis/rules/OptimisticGround.scala index 7e65ed7970c6e9d5c67e25b997c8f9c8d3f48f16..aeefe055eeac384a596f3e87306557247b2e86bf 100644 --- a/src/main/scala/leon/synthesis/rules/OptimisticGround.scala +++ b/src/main/scala/leon/synthesis/rules/OptimisticGround.scala @@ -2,7 +2,6 @@ package leon package synthesis package rules -import solvers.TimeoutSolver import purescala.Trees._ import purescala.TypeTrees._ import purescala.TreeOps._ @@ -11,10 +10,10 @@ import purescala.Extractors._ case object OptimisticGround extends Rule("Optimistic Ground") { def instantiateOn(sctx: SynthesisContext, p: Problem): Traversable[RuleInstantiation] = { if (!p.as.isEmpty && !p.xs.isEmpty) { - val res = new RuleInstantiation(p, this, SolutionBuilder.none) { + val res = new RuleInstantiation(p, this, SolutionBuilder.none, this.name) { def apply(sctx: SynthesisContext) = { - val solver = new TimeoutSolver(sctx.solver, 100L) // We give that 100ms + val solver = sctx.simpleSolver // Optimistic ground is given a simple solver (uninterpreted) val xss = p.xs.toSet val ass = p.as.toSet diff --git a/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala b/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala index 2ae957da30dc98e0b5e5903c331a08a9ac089cd7..2572aebccb6712f17ddd6e867e306be6f3592a59 100644 --- a/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala +++ b/src/main/scala/leon/synthesis/rules/UnconstrainedOutput.scala @@ -20,7 +20,7 @@ case object UnconstrainedOutput extends NormalizingRule("Unconstr.Output") { None } - List(RuleInstantiation.immediateDecomp(p, this, List(sub), onSuccess)) + List(RuleInstantiation.immediateDecomp(p, this, List(sub), onSuccess, this.name)) } else { Nil } diff --git a/src/main/scala/leon/synthesis/rules/Unification.scala b/src/main/scala/leon/synthesis/rules/Unification.scala index 9b7ea42304e2f6271a888984c5cdb6375a7ab8b3..88b9c806efe92699934f6e7b61c6d5830596c3aa 100644 --- a/src/main/scala/leon/synthesis/rules/Unification.scala +++ b/src/main/scala/leon/synthesis/rules/Unification.scala @@ -27,7 +27,7 @@ object Unification { val sub = p.copy(phi = And((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq)) - List(RuleInstantiation.immediateDecomp(p, this, List(sub), forward)) + List(RuleInstantiation.immediateDecomp(p, this, List(sub), forward, this.name)) } else { Nil } diff --git a/src/main/scala/leon/synthesis/rules/UnusedInput.scala b/src/main/scala/leon/synthesis/rules/UnusedInput.scala index b97ff9033b44c7e926c86362ace2fb6497399e65..a3b21fc28a1128562276886e668c947a505e3f75 100644 --- a/src/main/scala/leon/synthesis/rules/UnusedInput.scala +++ b/src/main/scala/leon/synthesis/rules/UnusedInput.scala @@ -13,7 +13,7 @@ case object UnusedInput extends NormalizingRule("UnusedInput") { if (!unused.isEmpty) { val sub = p.copy(as = p.as.filterNot(unused)) - List(RuleInstantiation.immediateDecomp(p, this, List(sub), forward)) + List(RuleInstantiation.immediateDecomp(p, this, List(sub), forward, this.name)) } else { Nil } diff --git a/src/main/scala/leon/synthesis/search/AndOrGraph.scala b/src/main/scala/leon/synthesis/search/AndOrGraph.scala index 8227a0e9faaca71b7952383d4a1800c027d22623..a289e40ca469a08d9205a36212b2041e7b6849e8 100644 --- a/src/main/scala/leon/synthesis/search/AndOrGraph.scala +++ b/src/main/scala/leon/synthesis/search/AndOrGraph.scala @@ -201,5 +201,30 @@ class AndOrGraph[AT <: AOAndTask[S], OT <: AOOrTask[S], S](val root: OT, val cos parent.expandLeaf(this, succ) } } + + def getStatus: (Int, Int) = { + var total: Int = 0 + var closed: Int = 0 + + def tr(t: Tree, isParentClosed: Boolean) { + val isClosed = isParentClosed || t.isSolved || t.isUnsolvable + if (isClosed) { + closed += 1 + } + total += 1 + + t match { + case an: AndNode => + an.subProblems.values.foreach(tr(_, isClosed)) + case on: OrNode => + (on.alternatives.values ++ on.triedAlternatives.values).foreach(tr(_, isClosed)) + case _ => + } + } + + tr(tree, false) + + (closed, total) + } } diff --git a/src/main/scala/leon/synthesis/search/AndOrGraphSearch.scala b/src/main/scala/leon/synthesis/search/AndOrGraphSearch.scala index 79e70aecf1c6a9b58de398aaffa094c09bfc4632..d4f5e94168610634ff0b38e692b749502fe63e9c 100644 --- a/src/main/scala/leon/synthesis/search/AndOrGraphSearch.scala +++ b/src/main/scala/leon/synthesis/search/AndOrGraphSearch.scala @@ -103,4 +103,37 @@ abstract class AndOrGraphSearch[AT <: AOAndTask[S], processing -= ol } + + def traversePathFrom(n: g.Tree, path: List[Int]): Option[g.Tree] = { + n match { + case l: g.Leaf => + assert(path.isEmpty) + Some(l) + case an: g.AndNode => + path match { + case x :: xs => + traversePathFrom(an.subProblems(an.subTasks(x)), xs) + case Nil => + Some(an) + } + + case on: g.OrNode => + path match { + case x :: xs => + val t = on.altTasks(x) + if (on.triedAlternatives contains t) { + None + } else { + traversePathFrom(on.alternatives(t), xs) + } + + case Nil => + Some(on) + } + } + } + + def traversePath(path: List[Int]): Option[g.Tree] = { + traversePathFrom(g.tree, path) + } } diff --git a/src/main/scala/leon/synthesis/utils/Benchmarks.scala b/src/main/scala/leon/synthesis/utils/Benchmarks.scala index 8cb4c5fa741aaccafe7834c96b37a38ecebeb225..04998922b5d4b16e8f4ec38613121515774aa809 100644 --- a/src/main/scala/leon/synthesis/utils/Benchmarks.scala +++ b/src/main/scala/leon/synthesis/utils/Benchmarks.scala @@ -76,7 +76,7 @@ object Benchmarks extends App { for (file <- ctx.files) { val innerCtx = ctx.copy(files = List(file)) - val opts = SynthesizerOptions() + val opts = SynthesisOptions() val pipeline = leon.plugin.ExtractionPhase andThen SynthesisProblemExtractionPhase @@ -85,15 +85,19 @@ object Benchmarks extends App { val solver = new FairZ3Solver(ctx.copy(reporter = new SilentReporter)) solver.setProgram(program) + val simpleSolver = new UninterpretedZ3Solver(ctx.copy(reporter = new SilentReporter)) + simpleSolver.setProgram(program) + for ((f, ps) <- results.toSeq.sortBy(_._1.id.toString); p <- ps) { val sctx = SynthesisContext( - context = ctx, - options = opts, + context = ctx, + options = opts, functionContext = Some(f), - program = program, - solver = solver, - reporter = new DefaultReporter, - shouldStop = new java.util.concurrent.atomic.AtomicBoolean + program = program, + solver = solver, + simpleSolver = simpleSolver, + reporter = new DefaultReporter, + shouldStop = new java.util.concurrent.atomic.AtomicBoolean ) val ts = System.currentTimeMillis diff --git a/src/main/scala/leon/verification/AnalysisPhase.scala b/src/main/scala/leon/verification/AnalysisPhase.scala index 5ac64e8da43ca437c0fee36e1ad87f17fd10ad13..24b154cc1339a92683ad8def03b34231a12c6cc7 100644 --- a/src/main/scala/leon/verification/AnalysisPhase.scala +++ b/src/main/scala/leon/verification/AnalysisPhase.scala @@ -21,13 +21,116 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] { LeonValueOptionDef("timeout", "--timeout=T", "Timeout after T seconds when trying to prove a verification condition.") ) + def generateVerificationConditions(reporter: Reporter, program: Program, functionsToAnalyse: Set[String]) : List[VerificationCondition] = { + val defaultTactic = new DefaultTactic(reporter) + defaultTactic.setProgram(program) + val inductionTactic = new InductionTactic(reporter) + inductionTactic.setProgram(program) + + var allVCs: Seq[VerificationCondition] = Seq.empty + val analysedFunctions: MutableSet[String] = MutableSet.empty + + for(funDef <- program.definedFunctions.toList.sortWith((fd1, fd2) => fd1 < fd2) if (functionsToAnalyse.isEmpty || functionsToAnalyse.contains(funDef.id.name))) { + analysedFunctions += funDef.id.name + + val tactic: Tactic = + if(funDef.annotations.contains("induct")) { + inductionTactic + } else { + defaultTactic + } + + if(funDef.body.isDefined) { + allVCs ++= tactic.generatePreconditions(funDef) + allVCs ++= tactic.generatePatternMatchingExhaustivenessChecks(funDef) + allVCs ++= tactic.generatePostconditions(funDef) + allVCs ++= tactic.generateMiscCorrectnessConditions(funDef) + allVCs ++= tactic.generateArrayAccessChecks(funDef) + } + allVCs = allVCs.sortWith((vc1, vc2) => { + val id1 = vc1.funDef.id.name + val id2 = vc2.funDef.id.name + if(id1 != id2) id1 < id2 else vc1 < vc2 + }) + } + + val notFound = functionsToAnalyse -- analysedFunctions + notFound.foreach(fn => reporter.error("Did not find function \"" + fn + "\" though it was marked for analysis.")) + + allVCs.toList + } + + def checkVerificationConditions(reporter: Reporter, solvers: Seq[Solver], vcs: Seq[VerificationCondition]) : VerificationReport = { + for(vcInfo <- vcs) { + val funDef = vcInfo.funDef + val vc = vcInfo.condition + + reporter.info("Now considering '" + vcInfo.kind + "' VC for " + funDef.id + "...") + reporter.info("Verification condition (" + vcInfo.kind + ") for ==== " + funDef.id + " ====") + reporter.info(simplifyLets(vc)) + + // try all solvers until one returns a meaningful answer + var superseeded : Set[String] = Set.empty[String] + solvers.find(se => { + reporter.info("Trying with solver: " + se.name) + if(superseeded(se.name) || superseeded(se.description)) { + reporter.info("Solver was superseeded. Skipping.") + false + } else { + superseeded = superseeded ++ Set(se.superseeds: _*) + + val t1 = System.nanoTime + se.init() + val (satResult, counterexample) = se.solveSAT(Not(vc)) + val solverResult = satResult.map(!_) + + val t2 = System.nanoTime + val dt = ((t2 - t1) / 1000000) / 1000.0 + + solverResult match { + case None => false + case Some(true) => { + reporter.info("==== VALID ====") + + vcInfo.value = Some(true) + vcInfo.solvedWith = Some(se) + vcInfo.time = Some(dt) + + true + } + case Some(false) => { + reporter.error("Found counter-example : ") + reporter.error(counterexample.toSeq.sortBy(_._1.name).map(p => p._1 + " -> " + p._2).mkString("\n")) + reporter.error("==== INVALID ====") + vcInfo.value = Some(false) + vcInfo.solvedWith = Some(se) + vcInfo.counterExample = Some(counterexample) + vcInfo.time = Some(dt) + + true + } + } + } + }) match { + case None => { + reporter.warning("No solver could prove or disprove the verification condition.") + } + case _ => + } + + } + + val report = new VerificationReport(vcs) + report + } + def run(ctx: LeonContext)(program: Program) : VerificationReport = { - val functionsToAnalyse : MutableSet[String] = MutableSet.empty + var functionsToAnalyse = Set[String]() var timeout: Option[Int] = None for(opt <- ctx.options) opt match { case LeonValueOption("functions", ListValue(fs)) => - functionsToAnalyse ++= fs + functionsToAnalyse = Set() ++ fs case v @ LeonValueOption("timeout", _) => timeout = v.asInt(ctx) @@ -49,111 +152,10 @@ object AnalysisPhase extends LeonPhase[Program,VerificationReport] { solvers.foreach(_.setProgram(program)) - val defaultTactic = new DefaultTactic(reporter) - defaultTactic.setProgram(program) - val inductionTactic = new InductionTactic(reporter) - inductionTactic.setProgram(program) - - def generateVerificationConditions : List[VerificationCondition] = { - var allVCs: Seq[VerificationCondition] = Seq.empty - val analysedFunctions: MutableSet[String] = MutableSet.empty - - for(funDef <- program.definedFunctions.toList.sortWith((fd1, fd2) => fd1 < fd2) if (functionsToAnalyse.isEmpty || functionsToAnalyse.contains(funDef.id.name))) { - analysedFunctions += funDef.id.name - - val tactic: Tactic = - if(funDef.annotations.contains("induct")) { - inductionTactic - } else { - defaultTactic - } - - if(funDef.body.isDefined) { - allVCs ++= tactic.generatePreconditions(funDef) - allVCs ++= tactic.generatePatternMatchingExhaustivenessChecks(funDef) - allVCs ++= tactic.generatePostconditions(funDef) - allVCs ++= tactic.generateMiscCorrectnessConditions(funDef) - allVCs ++= tactic.generateArrayAccessChecks(funDef) - } - allVCs = allVCs.sortWith((vc1, vc2) => { - val id1 = vc1.funDef.id.name - val id2 = vc2.funDef.id.name - if(id1 != id2) id1 < id2 else vc1 < vc2 - }) - } - - val notFound = functionsToAnalyse -- analysedFunctions - notFound.foreach(fn => reporter.error("Did not find function \"" + fn + "\" though it was marked for analysis.")) - - allVCs.toList - } - - def checkVerificationConditions(vcs: Seq[VerificationCondition]) : VerificationReport = { - for(vcInfo <- vcs) { - val funDef = vcInfo.funDef - val vc = vcInfo.condition - - reporter.info("Now considering '" + vcInfo.kind + "' VC for " + funDef.id + "...") - reporter.info("Verification condition (" + vcInfo.kind + ") for ==== " + funDef.id + " ====") - reporter.info(simplifyLets(vc)) - - // try all solvers until one returns a meaningful answer - var superseeded : Set[String] = Set.empty[String] - solvers.find(se => { - reporter.info("Trying with solver: " + se.name) - if(superseeded(se.name) || superseeded(se.description)) { - reporter.info("Solver was superseeded. Skipping.") - false - } else { - superseeded = superseeded ++ Set(se.superseeds: _*) - - val t1 = System.nanoTime - se.init() - val (satResult, counterexample) = se.solveSAT(Not(vc)) - val solverResult = satResult.map(!_) - - val t2 = System.nanoTime - val dt = ((t2 - t1) / 1000000) / 1000.0 - - solverResult match { - case None => false - case Some(true) => { - reporter.info("==== VALID ====") - - vcInfo.value = Some(true) - vcInfo.solvedWith = Some(se) - vcInfo.time = Some(dt) - - true - } - case Some(false) => { - reporter.error("Found counter-example : ") - reporter.error(counterexample.toSeq.sortBy(_._1.name).map(p => p._1 + " -> " + p._2).mkString("\n")) - reporter.error("==== INVALID ====") - vcInfo.value = Some(false) - vcInfo.solvedWith = Some(se) - vcInfo.time = Some(dt) - - true - } - } - } - }) match { - case None => { - reporter.warning("No solver could prove or disprove the verification condition.") - } - case _ => - } - - } - - val report = new VerificationReport(vcs) - report - } - val report = if(solvers.size > 1) { reporter.info("Running verification condition generation...") - checkVerificationConditions(generateVerificationConditions) + val vcs = generateVerificationConditions(reporter, program, functionsToAnalyse) + checkVerificationConditions(reporter, solvers, vcs) } else { reporter.warning("No solver specified. Cannot test verification conditions.") VerificationReport.emptyReport diff --git a/src/main/scala/leon/verification/VerificationCondition.scala b/src/main/scala/leon/verification/VerificationCondition.scala index 893fb39b3a0cbd22485f1fb0d10230d8cb8fd2b9..a8cbfb51b041a367b2b63c27eba653a34b9f84a1 100644 --- a/src/main/scala/leon/verification/VerificationCondition.scala +++ b/src/main/scala/leon/verification/VerificationCondition.scala @@ -14,6 +14,7 @@ class VerificationCondition(val condition: Expr, val funDef: FunDef, val kind: V var value : Option[Boolean] = None var solvedWith : Option[Solver] = None var time : Option[Double] = None + var counterExample : Option[Map[Identifier, Expr]] = None def status : String = value match { case None => "unknown" diff --git a/src/main/scala/leon/xlang/TreeOps.scala b/src/main/scala/leon/xlang/TreeOps.scala index c567a85dcc98959c00c4821de45dbcf3ac8ffc85..5eb20b284178ad8009b1caad89c4b86e6240c23e 100644 --- a/src/main/scala/leon/xlang/TreeOps.scala +++ b/src/main/scala/leon/xlang/TreeOps.scala @@ -88,4 +88,32 @@ object TreeOps { treeCatamorphism(convert, combine, compute, expr) } + trait ScopeSimplifier extends purescala.TreeOps.ScopeSimplifier { + override def rec(e: Expr, scope: Scope) = e match { + case LetDef(fd: FunDef, body: Expr) => + val newId = genId(fd.id, scope) + var newScope = scope.register(fd.id -> newId) + + val newArgs = for(VarDecl(id, tpe) <- fd.args) yield { + val newArg = genId(id, newScope) + newScope = newScope.register(id -> newArg) + VarDecl(newArg, tpe) + } + + val newFd = new FunDef(newId, fd.returnType, newArgs) + + newScope = newScope.registerFunDef(fd -> newFd) + + newFd.body = fd.body.map(b => rec(b, newScope)) + newFd.precondition = fd.precondition.map(pre => rec(pre, newScope)) + newFd.postcondition = fd.postcondition.map(post => rec(post, newScope)) + + + LetDef(newFd, rec(body, newScope)) + + case _ => + super.rec(e, scope) + } + } + } diff --git a/src/main/scala/leon/xlang/Trees.scala b/src/main/scala/leon/xlang/Trees.scala index 13fded9dc87ac2b3635b1c045acda927ab01227e..0517d7ee3ae04aab8c889f4877410f87ae365b92 100644 --- a/src/main/scala/leon/xlang/Trees.scala +++ b/src/main/scala/leon/xlang/Trees.scala @@ -357,12 +357,13 @@ object Trees { tp: (TypeTree, StringBuffer, Int) => Unit, dp: (Definition, StringBuffer, Int) => Unit ): StringBuffer = { - sb.append("\n") + sb.append("{\n") dp(fd, sb, lvl+1) sb.append("\n") sb.append("\n") ind(sb, lvl) ep(body, sb, lvl) + sb.append("}\n") sb } diff --git a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala index 567d30abb14c99eaf01340fd38fd809d2d0c8f4b..686a968c450e2e2b0899634e571a416da0696f46 100644 --- a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala +++ b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala @@ -33,7 +33,7 @@ class SynthesisSuite extends FunSuite { reporter = new SilentReporter ) - val opts = SynthesizerOptions() + val opts = SynthesisOptions() val pipeline = leon.plugin.TemporaryInputPhase andThen leon.plugin.ExtractionPhase andThen SynthesisProblemExtractionPhase @@ -42,9 +42,19 @@ class SynthesisSuite extends FunSuite { val solver = new FairZ3Solver(ctx) solver.setProgram(program) + val simpleSolver = new FairZ3Solver(ctx) + simpleSolver.setProgram(program) + for ((f, ps) <- results; p <- ps) { test("Synthesizing %3d: %-20s [%s]".format(nextInt(), f.id.toString, title)) { - val sctx = SynthesisContext(ctx, opts, Some(f), program, solver, new DefaultReporter, new java.util.concurrent.atomic.AtomicBoolean) + val sctx = SynthesisContext(ctx, + opts, + Some(f), + program, + solver, + simpleSolver, + new DefaultReporter, + new java.util.concurrent.atomic.AtomicBoolean) block(sctx, f, p) } diff --git a/testcases/synthesis/ADTInduction.scala b/testcases/synthesis/ADTInduction.scala index bf7d8b16da99ee33830617c4e4bd6a4a71c0a67d..7487247cda198b0a6f51cf7a39f2f922f6736eda 100644 --- a/testcases/synthesis/ADTInduction.scala +++ b/testcases/synthesis/ADTInduction.scala @@ -25,12 +25,21 @@ object SortedList { // content(out) == (content(Nil()) -- Set(v)) //} - def deleteSynth(in: List, v: Int) = choose { - (out: List) => - // This spec is too weak. Maybe use later as bad example? - //!(content(out) contains v) && size(out)+1 >= size(in) - (content(out) == (content(in) -- Set(v))) - } + def deleteSynth(in: List, v: Int) = (in match { + case Cons(head22, tail21) => + (tail21 match { + case Cons(head23, tail23) => + (choose { (out: List) => + (content(out) == (content(Cons(head22, Cons(head23, tail23))) -- Set(v))) + }) + case _ => + (choose { (out: List) => + (content(out) == (content(Cons(head22, Nil())) -- Set(v))) + }) + }) + case _ => + Nil() + }) def concatSynth(in1: List, in2: List) = choose { (out : List) => diff --git a/testcases/synthesis/Spt.scala b/testcases/synthesis/Spt.scala index ad729b296b42ae5b301b8a70fe5273dbed9d945e..6fbac66487957764fbc9ab103ec80624618f1f8a 100644 --- a/testcases/synthesis/Spt.scala +++ b/testcases/synthesis/Spt.scala @@ -3,22 +3,54 @@ import leon.Utils._ // Examples taken from http://lara.epfl.ch/~psuter/spt/ object SynthesisProceduresToolkit { - def e1(a: Nat, b: Nat, c: Nat): Nat = choose( (x: Nat) => x != a && x != b && x != c) + def e1(a: Nat, b: Nat, c: Nat): Nat = if ((b == c)) { + if ((a == c)) { + (choose { (x: Nat) => + (x != a) + }) + } else { + (choose { (x: Nat) => + ((x != a) && (x != b)) + }) + } +} else { + if ((a == b)) { + (choose { (x: Nat) => + ((x != a) && (x != c)) + }) + } else { + (choose { (x: Nat) => + ((x != a) && (x != b) && (x != c)) + }) + } +} - def e2(): (Nat, Nat, Nat) = choose( (x: Nat, y: Nat, z: Nat) => x != y && y != z && x != z) + def e2(): (Nat, Nat, Nat) = (Z(), Succ(Z()), Succ(Succ(Succ(Z())))) - def e3(a1 : Nat, a2 : Nat, a3 : Nat, a4 : Nat): (Nat, Nat) = choose( (x1 : Nat, x2 : Nat) => Cons(x1, Cons(x2, Nil())) == Cons(a3, Cons(a4, Nil())) && Cons(a1, Cons(a2, Nil())) != Cons(x1, Cons(x2, Nil()))) + def e3(a1 : Nat, a2 : Nat, a3 : Nat, a4 : Nat): (Nat, Nat) = (a3, a4) - def e4(a1 : Nat, a2 : Nat, a3 : Nat, a4 : Nat): (Nat, Nat, NatList) = choose( (x1 : Nat, x2 : Nat, x3 : NatList) => Cons(x1, Cons(x2, Nil())) != Cons(a1, Cons(a2, Nil()))) + def e4(a1 : Nat, a2 : Nat, a3 : Nat, a4 : Nat): (Nat, Nat, NatList) = (Succ(a2), a1, Nil()) - def e5(a1 : NatList, a2 : Nat, a3 : NatList): (Nat, NatList, Nat, NatList) = choose( (x1 : Nat, x2 : NatList, x3 : Nat, x4 : NatList) => Cons(Succ(x1), x2) == a1 && Succ(x1) != a2 && a3 == Cons(x3, Cons(x3, x4))) + def e5(a1 : NatList, a2 : Nat, a3 : NatList): (Nat, NatList, Nat, NatList) = (choose { (x1: Nat, x2: NatList, x3: Nat, x4: NatList) => + ((Cons(Succ(x1), x2) == a1) && (Succ(x1) != a2) && (a3 == Cons(x3, Cons(x3, x4)))) +}) - def e6(a: Nat, b: Nat): (Nat, NatList) = choose( (x: Nat, y: NatList) => a == Succ(b)) + def e6(a: Nat, b: Nat): (Nat, NatList) = if ((a == Succ(b))) { + (Z(), Nil()) +} else { + leon.Utils.error[(Nat, NatList)]("Precondition failed") +} - def e7(a1 : NatList, a2 : Nat, a3 : NatList): (Nat, NatList, Nat, NatList) = choose( (x1 : Nat, x2 : NatList, x3 : Nat, x4 : NatList) => - Cons(Succ(x1), x2) == a1 && Succ(x1) != a2 && a3 == Cons(x3, Cons(x3, x4)) || (a1 == a3 && x1 == x3)) + def e7(a1 : NatList, a2 : Nat, a3 : NatList): (Nat, NatList, Nat, NatList) = (choose { (x1: Nat, x2: NatList, x3: Nat, x4: NatList) => + ((Cons(Succ(x1), x2) == a1) && (Succ(x1) != a2) && (a3 == Cons(x3, Cons(x3, x4)))) +}) - def e8(a : Nat) = choose { (x : Nat) => Succ(x) == a } + def e8(a : Nat) = (a match { + case Succ(n150) => + n150 + case _ => + leon.Utils.error[(Nat)]("Precondition failed") +}) abstract class Nat case class Z() extends Nat