diff --git a/src/main/scala/leon/synthesis/ExamplesBank.scala b/src/main/scala/leon/synthesis/ExamplesBank.scala index 77b451094ae1451ce7e46c9e3312488c7c398058..1dfddd0ca025caf3c7faafce3904c304be362f43 100644 --- a/src/main/scala/leon/synthesis/ExamplesBank.scala +++ b/src/main/scala/leon/synthesis/ExamplesBank.scala @@ -4,7 +4,6 @@ package synthesis import purescala.Definitions._ import purescala.Expressions._ import purescala.Constructors._ -import evaluators._ import purescala.Common._ import repair._ import leon.utils.ASCIIHelpers._ @@ -175,9 +174,7 @@ case class QualifiedExamplesBank(as: List[Identifier], xs: List[Identifier], eb: } def filterIns(expr: Expr): ExamplesBank = { - val ev = new DefaultEvaluator(hctx.sctx.context, hctx.sctx.program) - - filterIns(m => ev.eval(expr, m).result == Some(BooleanLiteral(true))) + filterIns(m => hctx.sctx.defaultEvaluator.eval(expr, m).result == Some(BooleanLiteral(true))) } def filterIns(pred: Map[Identifier, Expr] => Boolean): ExamplesBank = { diff --git a/src/main/scala/leon/synthesis/SynthesisContext.scala b/src/main/scala/leon/synthesis/SynthesisContext.scala index bb90d2a268411c44e9dfdbde013886b7db5b56d9..b01077ce751f6b53e397b15eb3d5126e560d1b56 100644 --- a/src/main/scala/leon/synthesis/SynthesisContext.scala +++ b/src/main/scala/leon/synthesis/SynthesisContext.scala @@ -6,6 +6,7 @@ package synthesis import solvers._ import solvers.combinators._ import purescala.Definitions.{Program, FunDef} +import evaluators._ /** * This is global information per entire search, contains necessary information @@ -22,6 +23,10 @@ case class SynthesisContext( val rules = settings.rules val solverFactory = SolverFactory.getFromSettings(context, program) + + lazy val defaultEvaluator = { + new DefaultEvaluator(context, program) + } } object SynthesisContext { diff --git a/src/main/scala/leon/synthesis/rules/CEGISLike.scala b/src/main/scala/leon/synthesis/rules/CEGISLike.scala index f3fa5947d0c8cb8092f9d8fc032ca71c12cc2846..4b70832e97ec60123f58f2576f76f2e22725778e 100644 --- a/src/main/scala/leon/synthesis/rules/CEGISLike.scala +++ b/src/main/scala/leon/synthesis/rules/CEGISLike.scala @@ -43,14 +43,15 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { val sctx = hctx.sctx val ctx = sctx.context + val timings = ctx.timers.synthesis.rules.get(name) // CEGIS Flags to activate or deactivate features val useOptTimeout = sctx.settings.cegisUseOptTimeout.getOrElse(true) val useVanuatoo = sctx.settings.cegisUseVanuatoo.getOrElse(false) - val useShrink = sctx.settings.cegisUseShrink.getOrElse(true) + val useShrink = sctx.settings.cegisUseShrink.getOrElse(false) // Limits the number of programs CEGIS will specifically validate individually - val validateUpTo = 5 + val validateUpTo = 0 // Shrink the program when the ratio of passing cases is less than the threshold val shrinkThreshold = 1.0/2 @@ -513,19 +514,26 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { * First phase of CEGIS: solve for potential programs (that work on at least one input) */ def solveForTentativeProgram(): Option[Option[Set[Identifier]]] = { + timings.get("Solving for tentative").start(); val solverf = SolverFactory.default(ctx, programCTree).withTimeout(exSolverTo) val solver = solverf.getNewSolver() val cnstr = FunctionInvocation(phiFd.typed, phiFd.params.map(_.id.toVariable)) //debugCExpr(cTree) + println("Program: ") + println("-"*80) + println(programCTree.asString) + //println(" --- PhiFD ---") //println(phiFd.fullBody.asString(ctx)) val toFind = and(innerPc, cnstr) - //println(" --- Constraints ---") - //println(" - "+toFind) + println(" --- Constraints ---") + println(" - "+toFind.asString) try { - solver.assertCnstr(andJoin(bsOrdered.map(b => if (bs(b)) b.toVariable else Not(b.toVariable)))) + //TODO: WHAT THE F IS THIS? + //val bsOrNotBs = andJoin(bsOrdered.map(b => if (bs(b)) b.toVariable else Not(b.toVariable))) + //solver.assertCnstr(bsOrNotBs) solver.assertCnstr(toFind) for ((c, alts) <- cTree) { @@ -565,11 +573,12 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { val bModel = bs.filter(b => model.get(b).contains(BooleanLiteral(true))) - //println("Tentative expr: "+getExpr(bModel)) + println("Tentative model: "+bModel) + println("Tentative expr: "+getExpr(bModel)) Some(Some(bModel)) case Some(false) => - //println("UNSAT!") + println("UNSAT!") Some(None) case None => @@ -582,6 +591,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { Some(None) } } finally { + timings.get("Solving for tentative").stop(); solverf.reclaim(solver) solverf.shutdown() } @@ -591,6 +601,8 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { * Second phase of CEGIS: verify a given program by looking for CEX inputs */ def solveForCounterExample(bs: Set[Identifier]): Option[Option[Seq[Expr]]] = { + timings.get("Solving for cex").start(); + val solverf = SolverFactory.default(ctx, programCTree).withTimeout(cexSolverTo) val solver = solverf.getNewSolver() val cnstr = FunctionInvocation(phiFd.typed, phiFd.params.map(_.id.toVariable)) @@ -615,6 +627,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { None } } finally { + timings.get("Solving for cex").stop(); solverf.reclaim(solver) solverf.shutdown() } @@ -623,6 +636,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { List(new RuleInstantiation(this.name) { def apply(hctx: SearchContext): RuleApplication = { + timings.get("Prefix").start() var result: Option[RuleApplication] = None val sctx = hctx.sctx implicit val ctx = sctx.context @@ -718,6 +732,8 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { gi.iterator } + timings.get("Prefix").stop() + timings.get("Main Block").start(); try { do { var skipCESearch = false @@ -749,6 +765,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { for (bs <- prunedPrograms if !interruptManager.isInterrupted) { var valid = true val examples = allInputExamples() + timings.get("Testing").start() while(valid && examples.hasNext) { val e = examples.next() if (!ndProgram.testForProgram(bs)(e)) { @@ -760,6 +777,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { valid = false } } + timings.get("Testing").stop() if (wrongPrograms.size+1 % 1000 == 0) { sctx.reporter.debug("..."+wrongPrograms.size) @@ -795,6 +813,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { var doFilter = true if (validateUpTo > 0) { + timings.get("Validating first N").start() // Validate the first N programs individualy ndProgram.validatePrograms(prunedPrograms.take(validateUpTo)) match { case Left(sols) if sols.nonEmpty => @@ -810,18 +829,20 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { skipCESearch = true } } + timings.get("Validating first N").stop() } if (doFilter && !(nPassing < nInitial * shrinkThreshold && useShrink)) { + sctx.reporter.debug("Excluding "+wrongPrograms.size+" programs") wrongPrograms.foreach { ndProgram.excludeProgram } } } + timings.get("Loop").start() // CEGIS Loop at a given unfolding level while (result.isEmpty && !skipCESearch && !interruptManager.isInterrupted) { - ndProgram.solveForTentativeProgram() match { case Some(Some(bs)) => // Should we validate this program with Z3? @@ -833,6 +854,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { // make sure by validating this candidate with z3 true } else { + println("testing failed ?!") // One valid input failed with this candidate, we can skip ndProgram.excludeProgram(bs) false @@ -841,10 +863,12 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { // No inputs or capability to test, we need to ask Z3 true } + sctx.reporter.debug("Found tentative model (Validate="+validateWithZ3+")!") if (validateWithZ3) { ndProgram.solveForCounterExample(bs) match { case Some(Some(inputsCE)) => + sctx.reporter.debug("Found counter-example:"+inputsCE) val ce = InExample(inputsCE) // Found counter example! baseExampleInputs += ce @@ -863,6 +887,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { case None => // We are not sure + sctx.reporter.debug("Unknown") if (useOptTimeout) { // Interpret timeout in CE search as "the candidate is valid" sctx.reporter.info("CEGIS could not prove the validity of the resulting expression") @@ -881,6 +906,7 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { result = Some(RuleFailed()) } } + timings.get("Loop").stop() unfolding += 1 } while(unfolding <= maxUnfoldings && result.isEmpty && !interruptManager.isInterrupted) @@ -892,6 +918,8 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { sctx.reporter.warning("CEGIS crashed: "+e.getMessage) e.printStackTrace() RuleFailed() + } finally { + timings.get("Main Block").stop(); } } }) diff --git a/src/main/scala/leon/synthesis/rules/InequalitySplit.scala b/src/main/scala/leon/synthesis/rules/InequalitySplit.scala index 8925276af63069f78cab842142ab80b74b2f648c..ed8a285ff3f0a52a61760598ac742013d0e77f5d 100644 --- a/src/main/scala/leon/synthesis/rules/InequalitySplit.scala +++ b/src/main/scala/leon/synthesis/rules/InequalitySplit.scala @@ -43,7 +43,6 @@ case object InequalitySplit extends Rule("Ineq. Split.") { candidates.flatMap { case List(a1, a2) => - val subLT = p.copy(pc = and(LessThan(Variable(a1), Variable(a2)), p.pc), eb = p.qeb.filterIns(LessThan(Variable(a1), Variable(a2)))) val subEQ = p.copy(pc = and(Equals(Variable(a1), Variable(a2)), p.pc), diff --git a/testcases/synthesis/etienne-thesis/List/Delete.scala b/testcases/synthesis/etienne-thesis/List/Delete.scala new file mode 100644 index 0000000000000000000000000000000000000000..46f0710878d25a30e679f034ff07f985078950d1 --- /dev/null +++ b/testcases/synthesis/etienne-thesis/List/Delete.scala @@ -0,0 +1,41 @@ +import leon.annotation._ +import leon.lang._ +import leon.lang.synthesis._ + +object Delete { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case object Nil extends List + + def size(l: List) : BigInt = (l match { + case Nil => BigInt(0) + case Cons(_, t) => BigInt(1) + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[BigInt] = l match { + case Nil => Set.empty[BigInt] + case Cons(i, t) => Set(i) ++ content(t) + } + + def insert(in1: List, v: BigInt): List = { + Cons(v, in1) + } ensuring { content(_) == content(in1) ++ Set(v) } + + //def delete(in1: List, v: BigInt): List = { + // in1 match { + // case Cons(h,t) => + // if (h == v) { + // delete(t, v) + // } else { + // Cons(h, delete(t, v)) + // } + // case Nil => + // Nil + // } + //} ensuring { content(_) == content(in1) -- Set(v) } + + def delete(in1: List, v: BigInt) = choose { + (out : List) => + content(out) == content(in1) -- Set(v) + } +} diff --git a/testcases/synthesis/etienne-thesis/List/Diff.scala b/testcases/synthesis/etienne-thesis/List/Diff.scala new file mode 100644 index 0000000000000000000000000000000000000000..9b452ed7f7b8816d4e87b3d9044e40e1347d3492 --- /dev/null +++ b/testcases/synthesis/etienne-thesis/List/Diff.scala @@ -0,0 +1,59 @@ +import leon.annotation._ +import leon.lang._ +import leon.lang.synthesis._ + +object Diff { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case object Nil extends List + + def size(l: List) : BigInt = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[BigInt] = l match { + case Nil => Set.empty[BigInt] + case Cons(i, t) => Set(i) ++ content(t) + } + + def insert(in1: List, v: BigInt): List = { + Cons(v, in1) + } ensuring { content(_) == content(in1) ++ Set(v) } + + def delete(in1: List, v: BigInt): List = { + in1 match { + case Cons(h,t) => + if (h == v) { + delete(t, v) + } else { + Cons(h, delete(t, v)) + } + case Nil => + Nil + } + } ensuring { content(_) == content(in1) -- Set(v) } + + def union(in1: List, in2: List): List = { + in1 match { + case Cons(h, t) => + Cons(h, union(t, in2)) + case Nil => + in2 + } + } ensuring { content(_) == content(in1) ++ content(in2) } + + // def diff(in1: List, in2: List): List = { + // in2 match { + // case Nil => + // in1 + // case Cons(h, t) => + // diff(delete(in1, h), t) + // } + // } ensuring { content(_) == content(in1) -- content(in2) } + + def diff(in1: List, in2: List) = choose { + (out : List) => + content(out) == content(in1) -- content(in2) + } +} diff --git a/testcases/synthesis/etienne-thesis/List/Insert.scala b/testcases/synthesis/etienne-thesis/List/Insert.scala new file mode 100644 index 0000000000000000000000000000000000000000..6da044ca346e0cb95e194bdf42897f5f066ec23c --- /dev/null +++ b/testcases/synthesis/etienne-thesis/List/Insert.scala @@ -0,0 +1,28 @@ +import leon.annotation._ +import leon.lang._ +import leon.lang.synthesis._ + +object Insert { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case object Nil extends List + + def size(l: List) : BigInt = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[BigInt] = l match { + case Nil => Set.empty[BigInt] + case Cons(i, t) => Set(i) ++ content(t) + } + + //def insert(in1: List, v: BigInt): List = { + // Cons(v, in1) + //} ensuring { content(_) == content(in1) ++ Set(v) } + + def insert(in1: List, v: BigInt) = choose { + (out : List) => + content(out) == content(in1) ++ Set(v) + } +} diff --git a/testcases/synthesis/etienne-thesis/List/Split.scala b/testcases/synthesis/etienne-thesis/List/Split.scala new file mode 100644 index 0000000000000000000000000000000000000000..1f709a8669c047ecc300c0d78e558cb4bb720b90 --- /dev/null +++ b/testcases/synthesis/etienne-thesis/List/Split.scala @@ -0,0 +1,101 @@ +/* Copyright 2009-2015 EPFL, Lausanne */ + +import leon.annotation._ +import leon.lang._ +import leon.lang.synthesis._ + +object Complete { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case object Nil extends List + + def size(l: List) : BigInt = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[BigInt] = l match { + case Nil => Set.empty[BigInt] + case Cons(i, t) => Set(i) ++ content(t) + } + + def insert(in1: List, v: BigInt): List = { + Cons(v, in1) + } ensuring { content(_) == content(in1) ++ Set(v) } + + //def insert(in1: List, v: BigInt) = choose { + // (out : List) => + // content(out) == content(in1) ++ Set(v) + //} + + def delete(in1: List, v: BigInt): List = { + in1 match { + case Cons(h,t) => + if (h == v) { + delete(t, v) + } else { + Cons(h, delete(t, v)) + } + case Nil => + Nil + } + } ensuring { content(_) == content(in1) -- Set(v) } + + //def delete(in1: List, v: BigInt) = choose { + // (out : List) => + // content(out) == content(in1) -- Set(v) + //} + + def union(in1: List, in2: List): List = { + in1 match { + case Cons(h, t) => + Cons(h, union(t, in2)) + case Nil => + in2 + } + } ensuring { content(_) == content(in1) ++ content(in2) } + + //def union(in1: List, in2: List) = choose { + // (out : List) => + // content(out) == content(in1) ++ content(in2) + //} + + def diff(in1: List, in2: List): List = { + in2 match { + case Nil => + in1 + case Cons(h, t) => + diff(delete(in1, h), t) + } + } ensuring { content(_) == content(in1) -- content(in2) } + + //def diff(in1: List, in2: List) = choose { + // (out : List) => + // content(out) == content(in1) -- content(in2) + //} + + def splitSpec(list : List, res : (List,List)) : Boolean = { + + val s1 = size(res._1) + val s2 = size(res._2) + abs(s1 - s2) <= 1 && s1 + s2 == size(list) && + content(res._1) ++ content(res._2) == content(list) + } + + def abs(i : BigInt) : BigInt = { + if(i < 0) -i else i + } ensuring(_ >= 0) + + // def split(list : List) : (List,List) = (list match { + // case Nil => (Nil, Nil) + // case Cons(x, Nil) => (Cons(x, Nil), Nil) + // case Cons(x1, Cons(x2, xs)) => + // val (s1,s2) = split(xs) + // (Cons(x1, s1), Cons(x2, s2)) + // }) ensuring(res => splitSpec(list, res)) + + def split(list : List) : (List,List) = { + choose { (res : (List,List)) => splitSpec(list, res) } + } + +} diff --git a/testcases/synthesis/etienne-thesis/List/Union.scala b/testcases/synthesis/etienne-thesis/List/Union.scala new file mode 100644 index 0000000000000000000000000000000000000000..b170648665dad909abac2e3978b8675355195cde --- /dev/null +++ b/testcases/synthesis/etienne-thesis/List/Union.scala @@ -0,0 +1,50 @@ +import leon.annotation._ +import leon.lang._ +import leon.lang.synthesis._ + +object Union { + sealed abstract class List + case class Cons(head: BigInt, tail: List) extends List + case object Nil extends List + + def size(l: List) : BigInt = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[BigInt] = l match { + case Nil => Set.empty[BigInt] + case Cons(i, t) => Set(i) ++ content(t) + } + + def insert(in1: List, v: BigInt): List = { + Cons(v, in1) + } ensuring { content(_) == content(in1) ++ Set(v) } + + def delete(in1: List, v: BigInt): List = { + in1 match { + case Cons(h,t) => + if (h == v) { + delete(t, v) + } else { + Cons(h, delete(t, v)) + } + case Nil => + Nil + } + } ensuring { content(_) == content(in1) -- Set(v) } + + // def union(in1: List, in2: List): List = { + // in1 match { + // case Cons(h, t) => + // Cons(h, union(t, in2)) + // case Nil => + // in2 + // } + // } ensuring { content(_) == content(in1) ++ content(in2) } + + def union(in1: List, in2: List) = choose { + (out : List) => + content(out) == content(in1) ++ content(in2) + } +}