From 31db0f47f268df4a08b75b52d1f4227a51e4c9ab Mon Sep 17 00:00:00 2001 From: Etienne Kneuss <ekneuss@gmail.com> Date: Fri, 28 Aug 2015 16:47:11 +0200 Subject: [PATCH] Enrich FreeableIterator's API, define GrowableIterable --- .../scala/leon/datagen/SolverDataGen.scala | 12 ++--- .../scala/leon/synthesis/ExamplesFinder.scala | 2 +- .../leon/synthesis/rules/CEGISLike.scala | 36 ++++++++------- .../leon/synthesis/rules/TEGISLike.scala | 46 +++++++++++++++---- .../scala/leon/utils/FreeableIterator.scala | 41 +++++++++++++++++ .../scala/leon/utils/GrowableIterable.scala | 25 ++++++++++ .../solvers/ModelEnumerationSuite.scala | 32 ++++--------- 7 files changed, 136 insertions(+), 58 deletions(-) create mode 100644 src/main/scala/leon/utils/GrowableIterable.scala diff --git a/src/main/scala/leon/datagen/SolverDataGen.scala b/src/main/scala/leon/datagen/SolverDataGen.scala index 027cc41e0..6f7479f48 100644 --- a/src/main/scala/leon/datagen/SolverDataGen.scala +++ b/src/main/scala/leon/datagen/SolverDataGen.scala @@ -15,13 +15,13 @@ import utils._ class SolverDataGen(ctx: LeonContext, pgm: Program, sff: ((LeonContext, Program) => SolverFactory[Solver])) extends DataGenerator { implicit val ctx0 = ctx - def generate(tpe: TypeTree): Iterator[Expr] = { + def generate(tpe: TypeTree): FreeableIterator[Expr] = { generateFor(Seq(FreshIdentifier("tmp", tpe)), BooleanLiteral(true), 20, 20).map(_.head) } - def generateFor(ins: Seq[Identifier], satisfying: Expr, maxValid: Int, maxEnumerated: Int): Iterator[Seq[Expr]] = { + def generateFor(ins: Seq[Identifier], satisfying: Expr, maxValid: Int, maxEnumerated: Int): FreeableIterator[Seq[Expr]] = { if (ins.isEmpty) { - Iterator.empty + FreeableIterator.empty } else { var fds = Map[ClassDef, FunDef]() @@ -81,11 +81,7 @@ class SolverDataGen(ctx: LeonContext, pgm: Program, sff: ((LeonContext, Program) val enum = modelEnum.enumVarying(ins, satisfying, sizeOf, 5) - try { - enum.take(maxValid).map(model => ins.map(model)).toList.iterator - } finally { - enum.free() - } + enum.take(maxValid).map(model => ins.map(model)) } } diff --git a/src/main/scala/leon/synthesis/ExamplesFinder.scala b/src/main/scala/leon/synthesis/ExamplesFinder.scala index 9ef46ec5a..45f399b1b 100644 --- a/src/main/scala/leon/synthesis/ExamplesFinder.scala +++ b/src/main/scala/leon/synthesis/ExamplesFinder.scala @@ -114,7 +114,7 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) { val solverExamples = solverDataGen.generateFor(ids, pc, maxValid, maxEnumerated).map(InExample(_)) - ExamplesBank(generatedExamples.toSeq ++ solverExamples.toSeq, Nil) + ExamplesBank(generatedExamples.toSeq ++ solverExamples.toList, Nil) } private def extractTestsOf(e: Expr): Set[Map[Identifier, Expr]] = { diff --git a/src/main/scala/leon/synthesis/rules/CEGISLike.scala b/src/main/scala/leon/synthesis/rules/CEGISLike.scala index 03feccf9e..480960777 100644 --- a/src/main/scala/leon/synthesis/rules/CEGISLike.scala +++ b/src/main/scala/leon/synthesis/rules/CEGISLike.scala @@ -20,6 +20,7 @@ import scala.collection.mutable.{HashMap=>MutableMap, ArrayBuffer} import evaluators._ import datagen._ +import leon.utils._ import codegen.CodeGenParams abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { @@ -698,36 +699,37 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { */ val nTests = if (p.pc == BooleanLiteral(true)) 50 else 20 - val inputIterator: Iterator[Seq[Expr]] = if (useVanuatoo) { - new VanuatooDataGen(sctx.context, sctx.program).generateFor(p.as, p.pc, nTests, 3000) + /* + val inputGenerator: FreeableIterator[Example] = { + val sff = { + (ctx: LeonContext, pgm: Program) => + SolverFactory.default(ctx, pgm).withTimeout(exSolverTo) + } + new SolverDataGen(sctx.context, sctx.program, sff).generateFor(p.as, p.pc, nTests, nTests).map { + InExample(_) + } + } */ + + val inputGenerator: Iterator[Example] = if (useVanuatoo) { + new VanuatooDataGen(sctx.context, sctx.program).generateFor(p.as, p.pc, nTests, 3000).map(InExample) } else { val evaluator = new DualEvaluator(sctx.context, sctx.program, CodeGenParams.default) - new GrammarDataGen(evaluator, ValueGrammar).generateFor(p.as, p.pc, nTests, 1000) + new GrammarDataGen(evaluator, ValueGrammar).generateFor(p.as, p.pc, nTests, 1000).map(InExample) } - val cachedInputIterator = new Iterator[Example] { - def next() = { - val i = InExample(inputIterator.next()) - baseExampleInputs += i - i - } - - def hasNext = { - inputIterator.hasNext - } - } + val gi = new GrowableIterable[Example](baseExampleInputs, inputGenerator) val failedTestsStats = new MutableMap[Example, Int]().withDefaultValue(0) - def hasInputExamples = baseExampleInputs.nonEmpty || cachedInputIterator.hasNext + def hasInputExamples = gi.nonEmpty var n = 1 def allInputExamples() = { if (n == 10 || n == 50 || n % 500 == 0) { - baseExampleInputs = baseExampleInputs.sortBy(e => -failedTestsStats(e)) + gi.sortBufferBy(e => -failedTestsStats(e)) } n += 1 - baseExampleInputs.iterator ++ cachedInputIterator + gi.iterator } try { diff --git a/src/main/scala/leon/synthesis/rules/TEGISLike.scala b/src/main/scala/leon/synthesis/rules/TEGISLike.scala index 2fe03e7af..9d9e360e2 100644 --- a/src/main/scala/leon/synthesis/rules/TEGISLike.scala +++ b/src/main/scala/leon/synthesis/rules/TEGISLike.scala @@ -5,16 +5,23 @@ package synthesis package rules import purescala.Expressions._ +import purescala.Definitions._ import purescala.Types._ import purescala.Constructors._ +import solvers._ +import datagen._ + import evaluators._ import codegen.CodeGenParams import utils._ +import leon.utils._ import grammars._ +import scala.collection.mutable.{HashMap => MutableMap} + import bonsai.enumerators._ abstract class TEGISLike[T <% Typed](name: String) extends Rule(name) { @@ -34,6 +41,8 @@ abstract class TEGISLike[T <% Typed](name: String) extends Rule(name) { def instantiateOn(implicit hctx: SearchContext, p: Problem): Traversable[RuleInstantiation] = { + return Nil; + List(new RuleInstantiation(this.name) { def apply(hctx: SearchContext): RuleApplication = { val sctx = hctx.sctx @@ -43,9 +52,35 @@ abstract class TEGISLike[T <% Typed](name: String) extends Rule(name) { val params = getParams(sctx, p) val grammar = params.grammar + val nTests = if (p.pc == BooleanLiteral(true)) 50 else 20 + + val useVanuatoo = sctx.settings.cegisUseVanuatoo.getOrElse(false) + + val inputGenerator: Iterator[Seq[Expr]] = if (useVanuatoo) { + new VanuatooDataGen(sctx.context, sctx.program).generateFor(p.as, p.pc, nTests, 3000) + } else { + val evaluator = new DualEvaluator(sctx.context, sctx.program, CodeGenParams.default) + new GrammarDataGen(evaluator, ValueGrammar).generateFor(p.as, p.pc, nTests, 1000) + } + + val gi = new GrowableIterable[Seq[Expr]](p.eb.examples.map(_.ins).distinct, inputGenerator) + + val failedTestsStats = new MutableMap[Seq[Expr], Int]().withDefaultValue(0) + + def hasInputExamples = gi.nonEmpty + + var n = 1 + def allInputExamples() = { + if (n == 10 || n == 50 || n % 500 == 0) { + gi.sortBufferBy(e => -failedTestsStats(e)) + } + n += 1 + gi.iterator + } + var tests = p.eb.valids.map(_.ins).distinct.map(t => (t, new Counter(0))) - if (tests.nonEmpty) { + if (gi.nonEmpty) { val evalParams = CodeGenParams.default.copy(maxFunctionInvocations = 2000) val evaluator = new DualEvaluator(sctx.context, sctx.program, evalParams) @@ -69,14 +104,14 @@ abstract class TEGISLike[T <% Typed](name: String) extends Rule(name) { //sctx.reporter.debug("Got expression "+e.asString) timers.testing.start() - if (tests.forall{ case (t, fc) => + if (allInputExamples().forall{ t => val res = evaluator.eval(exprToTest, p.as.zip(t).toMap) match { case EvaluationResults.Successful(BooleanLiteral(true)) => //sctx.reporter.debug("Test "+t.map(_.asString)+" passed!") true case _ => //sctx.reporter.debug("Test "+t.map(_.asString)+" failed!") - fc.inc() + failedTestsStats += t -> (failedTestsStats(t)+1) false } res @@ -84,11 +119,6 @@ abstract class TEGISLike[T <% Typed](name: String) extends Rule(name) { candidate = Some(tupleWrap(Seq(e))) } timers.testing.stop() - - if (n % params.reorderInterval == 0) { - tests = tests.sortBy { case (t, fc) => -fc.value } - } - n += 1 } timers.generating.stop() diff --git a/src/main/scala/leon/utils/FreeableIterator.scala b/src/main/scala/leon/utils/FreeableIterator.scala index f6ad42937..a790b2dbf 100644 --- a/src/main/scala/leon/utils/FreeableIterator.scala +++ b/src/main/scala/leon/utils/FreeableIterator.scala @@ -16,4 +16,45 @@ abstract class FreeableIterator[T] extends Iterator[T] { def computeNext(): Option[T] def free() + + override def map[B](f: T => B): FreeableIterator[B] = { + val orig = this + + new FreeableIterator[B] { + def computeNext() = orig.computeNext.map(f) + def free() = orig.free() + } + } + + override def take(n: Int): FreeableIterator[T] = { + val orig = this + + new FreeableIterator[T] { + private var c = 0; + + def computeNext() = { + if (c < n) { + c += 1 + orig.computeNext + } else { + None + } + } + + def free() = orig.free() + } + } + + override def toList: List[T] = { + val res = super.toList + free() + res + } +} + +object FreeableIterator { + def empty[T] = new FreeableIterator[T] { + def computeNext() = None + def free() = {} + } } diff --git a/src/main/scala/leon/utils/GrowableIterable.scala b/src/main/scala/leon/utils/GrowableIterable.scala new file mode 100644 index 000000000..d05a9f065 --- /dev/null +++ b/src/main/scala/leon/utils/GrowableIterable.scala @@ -0,0 +1,25 @@ +package leon + +import scala.collection.mutable.ArrayBuffer + +class GrowableIterable[T](init: Seq[T], growth: Iterator[T]) extends Iterable[T] { + var buffer = new ArrayBuffer[T]() ++ init + + val cachingIterator = new Iterator[T] { + def hasNext = growth.hasNext + + def next() = { + val res = growth.next() + buffer += res + res + } + } + + def iterator: Iterator[T] = { + buffer.iterator ++ cachingIterator + } + + def sortBufferBy[B](f: T => B)(implicit ord: math.Ordering[B]) = { + buffer = buffer.sortBy(f) + } +} diff --git a/src/test/scala/leon/integration/solvers/ModelEnumerationSuite.scala b/src/test/scala/leon/integration/solvers/ModelEnumerationSuite.scala index 9a23fd600..38553d171 100644 --- a/src/test/scala/leon/integration/solvers/ModelEnumerationSuite.scala +++ b/src/test/scala/leon/integration/solvers/ModelEnumerationSuite.scala @@ -53,14 +53,10 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL val me = getModelEnum val enum = me.enumSimple(Seq(l), cnstr) - try { - val models = enum.take(5).toList - - assert(models.size === 5, "We can enumerate at least 5 lists of size 3+") - assert(models.toSet.size === 5, "Models are distinct") - } finally { - enum.free() - } + val models = enum.take(5).toList + + assert(models.size === 5, "We can enumerate at least 5 lists of size 3+") + assert(models.toSet.size === 5, "Models are distinct") } test("Simple model enumeration 2") { implicit fix => @@ -72,10 +68,8 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL val me = getModelEnum val enum = me.enumSimple(Seq(l), cnstr) - val models = enum.take(5).toList - enum.free() - assert(models.size === 1, "We can only enumerate one list of size 0") + assert(enum.take(5).toList.size === 1, "We can only enumerate one list of size 0") } test("Varying model enumeration 1") { implicit fix => @@ -93,17 +87,13 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL // 1 model of each size val enum1 = me.enumVarying(Seq(l), cnstr, car) - val models1 = enum1.toList - enum1.free() - assert(models1.size === 2, "We can enumerate 2 lists of varying size 0 < .. < 3") + assert(enum1.toList.size === 2, "We can enumerate 2 lists of varying size 0 < .. < 3") // 3 models of each size val enum2 = me.enumVarying(Seq(l), cnstr, car, 3) - val models2 = enum2.toList - enum2.free() - assert(models2.size === 6, "We can enumerate 6 lists of varying size 0 < .. < 3 with 3 per size") + assert(enum2.toList.size === 6, "We can enumerate 6 lists of varying size 0 < .. < 3 with 3 per size") val car2 = fcall("List1.sum")(l.toVariable) @@ -111,7 +101,6 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL // 1 model of each sum val enum3 = me.enumVarying(Seq(l), cnstr, car2) val models3 = enum3.take(4).toList - enum3.free() assert(models3.size === 4, "We can enumerate >=4 lists of varying sum, with 0 < .. < 3") @@ -122,7 +111,6 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL // 2 model of each sum val enum4 = me.enumVarying(Seq(l), cnstr, car2, 2) val models4 = enum4.take(4).toList - enum4.free() assert(models4.size === 4, "We can enumerate >=4 lists of varying sum, with 0 < .. < 3") @@ -147,7 +135,6 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL // 1 model of each caracteristic (which is a boolean, so only two possibilities) val enum3 = me.enumVarying(Seq(l), cnstr, car) val models3 = enum3.take(10).toList - enum3.free() assert(models3.size === 2, "We can enumerate only 2 lists of varying size==0") @@ -155,7 +142,7 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL // 1 model of each caracteristic (which is a boolean, so only two possibilities) val enum4 = me.enumVarying(Seq(l), cnstr, car, 2) val models4 = enum4.take(10).toList - enum4.free() + assert(models4.size === 4, "We can enumerate only 4 lists of varying size==0 (2 each)") } @@ -172,14 +159,12 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL val enum1 = me.enumMaximizing(Seq(l), cnstr, car) val models1 = enum1.take(5).toList - enum1.free() assert(models1.size < 5, "It took less than 5 models to reach max") assert(evaluator.eval(car, models1.last).result === Some(bi(4)), "Max should be 4") val enum2 = me.enumMaximizing(Seq(l), BooleanLiteral(true), car) val models2 = enum2.take(4).toList - enum2.free() assert(models2.size == 4, "Unbounded search yields models") // in 4 steps, it should reach lists of size > 10 @@ -199,7 +184,6 @@ class ModelEnumeratorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL val enum1 = me.enumMinimizing(Seq(l), cnstr, car) val models1 = enum1.take(5).toList - enum1.free() assert(models1.size < 5, "It took less than 5 models to reach min") assert(evaluator.eval(car, models1.last).result === Some(bi(0)), "Min should be 0") -- GitLab