From 7366462beeb70f568fc68e4668469a1a03245f22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mika=C3=ABl=20Mayer?= <mikael.mayer@epfl.ch> Date: Wed, 27 Jan 2016 16:31:26 +0100 Subject: [PATCH] Homomorphic to work on lambdas + tests. Ability to keep abstract term on the RHS of examples without trivially evaluating the choose construct. --- .../leon/evaluators/RecursiveEvaluator.scala | 7 +++++ src/main/scala/leon/purescala/ExprOps.scala | 7 +++++ .../scala/leon/synthesis/ExamplesFinder.scala | 5 +++- .../leon/synthesis/rules/StringRender.scala | 6 +++-- .../integration/purescala/ExprOpsSuite.scala | 26 +++++++++++++++---- 5 files changed, 43 insertions(+), 8 deletions(-) diff --git a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala index b57383e8b..62e69fa62 100644 --- a/src/main/scala/leon/evaluators/RecursiveEvaluator.scala +++ b/src/main/scala/leon/evaluators/RecursiveEvaluator.scala @@ -28,6 +28,10 @@ abstract class RecursiveEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int lazy val scalaEv = new ScalacEvaluator(this, ctx, prog) protected var clpCache = Map[(Choose, Seq[Expr]), Expr]() + + private var evaluationFailsOnChoose = false + /** Sets the flag if when encountering a Choose, it should fail instead of solving it. */ + def setEvaluationFailOnChoose(b: Boolean) = { this.evaluationFailsOnChoose = b; this } protected[evaluators] def e(expr: Expr)(implicit rctx: RC, gctx: GC): Expr = expr match { case Variable(id) => @@ -577,6 +581,9 @@ abstract class RecursiveEvaluator(ctx: LeonContext, prog: Program, maxSteps: Int e(p.asConstraint) case choose: Choose => + if(evaluationFailsOnChoose) { + throw EvalError("Evaluator set to not solve choose constructs") + } implicit val debugSection = utils.DebugSectionSynthesis diff --git a/src/main/scala/leon/purescala/ExprOps.scala b/src/main/scala/leon/purescala/ExprOps.scala index c651d3880..e03ac549e 100644 --- a/src/main/scala/leon/purescala/ExprOps.scala +++ b/src/main/scala/leon/purescala/ExprOps.scala @@ -1560,6 +1560,8 @@ object ExprOps { } implicit class AugmentedContext(c: Option[Map[Identifier, Identifier]]) { def &&(other: => Option[Map[Identifier, Identifier]]) = mergeContexts(c, other) + def --(other: Seq[Identifier]) = + c.map(_ -- other) } implicit class AugmentedBooleant(c: Boolean) { def &&(other: => Option[Map[Identifier, Identifier]]) = if(c) other else None @@ -1685,6 +1687,11 @@ object ExprOps { // TODO: Seems a lot is missing, like Literals + case (Lambda(defs, body), Lambda(defs2, body2)) => + // We remove variables introduced by lambdas. + (isHomo(body, body2) && + (defs zip defs2).mergeall{ case (ValDef(a1, _), ValDef(a2, _)) => Option(Map(a1 -> a2)) } + ) -- (defs.map(_.id)) case Same(Operator(es1, _), Operator(es2, _)) => (es1.size == es2.size) && (es1 zip es2).mergeall{ case (e1, e2) => isHomo(e1, e2) } diff --git a/src/main/scala/leon/synthesis/ExamplesFinder.scala b/src/main/scala/leon/synthesis/ExamplesFinder.scala index 0110f7128..1fa660b55 100644 --- a/src/main/scala/leon/synthesis/ExamplesFinder.scala +++ b/src/main/scala/leon/synthesis/ExamplesFinder.scala @@ -27,8 +27,11 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) { val reporter = ctx.reporter private var keepAbstractExamples = false - + /** If true, will not evaluate examples to check them. */ def setKeepAbstractExamples(b: Boolean) = { this.keepAbstractExamples = b; this} + /** Sets if evalution of the result of tests should stop on choose statements. + * Useful for programming by Example */ + def setEvaluationFailOnChoose(b: Boolean) = { evaluator.setEvaluationFailOnChoose(b); this } def extractFromFunDef(fd: FunDef, partition: Boolean): ExamplesBank = fd.postcondition match { case Some(Lambda(Seq(ValDef(id, _)), post)) => diff --git a/src/main/scala/leon/synthesis/rules/StringRender.scala b/src/main/scala/leon/synthesis/rules/StringRender.scala index b3172538e..cce501ac1 100644 --- a/src/main/scala/leon/synthesis/rules/StringRender.scala +++ b/src/main/scala/leon/synthesis/rules/StringRender.scala @@ -176,7 +176,7 @@ case object StringRender extends Rule("StringRender") { case Some(equations) => gatherEquations(q, acc ++= equations) case None => - hctx.reporter.info("Could not extract equations from ["+sfget+"] == ["+rhsget+"]") + hctx.reporter.info("Could not extract equations from ["+sfget+"] == ["+rhsget+"]\n coming from ... == " + rhExpr) None } case _ => @@ -547,7 +547,9 @@ case object StringRender extends Rule("StringRender") { case List(IsTyped(v, StringType)) => val description = "Creates a standard string conversion function" - val examplesFinder = new ExamplesFinder(hctx.context, hctx.program).setKeepAbstractExamples(true) + val examplesFinder = new ExamplesFinder(hctx.context, hctx.program) + .setKeepAbstractExamples(true) + .setEvaluationFailOnChoose(true) val examples = examplesFinder.extractFromProblem(p) val abstractStringConverters: StringConverters = diff --git a/src/test/scala/leon/integration/purescala/ExprOpsSuite.scala b/src/test/scala/leon/integration/purescala/ExprOpsSuite.scala index 2512f87ce..b8c0dbd8a 100644 --- a/src/test/scala/leon/integration/purescala/ExprOpsSuite.scala +++ b/src/test/scala/leon/integration/purescala/ExprOpsSuite.scala @@ -7,6 +7,7 @@ import leon.test._ import leon.purescala.Constructors._ import leon.purescala.Expressions._ import leon.purescala.ExprOps._ +import leon.purescala.Definitions._ import leon.purescala.Common._ class ExprOpsSuite extends LeonTestSuiteWithProgram with helpers.ExpressionsDSL { @@ -106,12 +107,27 @@ class ExprOpsSuite extends LeonTestSuiteWithProgram with helpers.ExpressionsDSL import leon.purescala.ExprOps.canBeHomomorphic import leon.purescala.Types._ import leon.purescala.Definitions._ - val d = FreshIdentifier("x", IntegerType) - val e = FreshIdentifier("y", IntegerType) - assert(canBeHomomorphic(Variable(d), Variable(e)).isEmpty) - val l1 = Lambda(Seq(ValDef(d)), Variable(d)) - val l2 = Lambda(Seq(ValDef(e)), Variable(e)) + val d = FreshIdentifier("d", IntegerType) + val x = FreshIdentifier("x", IntegerType) + val y = FreshIdentifier("y", IntegerType) + assert(canBeHomomorphic(Variable(d), Variable(x)).isEmpty) + val l1 = Lambda(Seq(ValDef(x)), Variable(x)) + val l2 = Lambda(Seq(ValDef(y)), Variable(y)) assert(canBeHomomorphic(l1, l2).nonEmpty) + val fType = FunctionType(Seq(IntegerType), IntegerType) + val f = FreshIdentifier("f", + FunctionType(Seq(IntegerType, fType, fType), IntegerType)) + val farg1 = FreshIdentifier("arg1", IntegerType) + val farg2 = FreshIdentifier("arg2", fType) + val farg3 = FreshIdentifier("arg3", fType) + + val fdef = new FunDef(f, Seq(), Seq(ValDef(farg1), ValDef(farg2), ValDef(farg3)), IntegerType) + + // Captured variables should be silent, even if they share the same identifier in two places of the code. + assert(canBeHomomorphic( + FunctionInvocation(fdef.typed, Seq(Variable(d), l1, l2)), + FunctionInvocation(fdef.typed, Seq(Variable(d), l1, l1))).nonEmpty) + } } -- GitLab