From 7c9386d5cfdd032df0028740a1e8e1970d025bbc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mika=C3=ABl=20Mayer?= <mikael.mayer@epfl.ch>
Date: Fri, 15 Jan 2016 12:48:08 +0100
Subject: [PATCH] Leon can handle abstract examples if the flag
 `keepAbstractExamples` is set to true in ExamplesFinder. (It means that
 examples will not be enumerated if they have free unassigned variables, but
 kept as it.) input/output examples can now match only a part of the inputs,
 but the output is still compulsory This is now used in StringRender.scala.
 The functional example works.

---
 .../evaluators/StringTracingEvaluator.scala   | 10 +++
 .../scala/leon/synthesis/ExamplesFinder.scala | 80 +++++++++++--------
 .../leon/synthesis/rules/StringRender.scala   | 12 +--
 3 files changed, 64 insertions(+), 38 deletions(-)

diff --git a/src/main/scala/leon/evaluators/StringTracingEvaluator.scala b/src/main/scala/leon/evaluators/StringTracingEvaluator.scala
index 62cde913f..ab8b14055 100644
--- a/src/main/scala/leon/evaluators/StringTracingEvaluator.scala
+++ b/src/main/scala/leon/evaluators/StringTracingEvaluator.scala
@@ -66,6 +66,16 @@ class StringTracingEvaluator(ctx: LeonContext, prog: Program) extends Contextual
           case _ =>
             StringConcat(es1, es2)
         }
+      case Application(caller, args) =>
+        val ecaller = e(caller)
+        ecaller match {
+          case l @ Lambda(params, body) =>
+            super.e(Application(ecaller, args))
+          case PartialLambda(mapping, dflt, _) =>
+            super.e(Application(ecaller, args))
+          case f =>
+            Application(f, args.map(e))
+        }
       case expr =>
         super.e(expr)
     }
diff --git a/src/main/scala/leon/synthesis/ExamplesFinder.scala b/src/main/scala/leon/synthesis/ExamplesFinder.scala
index 3cc97f4f5..524642874 100644
--- a/src/main/scala/leon/synthesis/ExamplesFinder.scala
+++ b/src/main/scala/leon/synthesis/ExamplesFinder.scala
@@ -25,6 +25,10 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) {
   implicit val ctx = ctx0
 
   val reporter = ctx.reporter
+  
+  private var keepAbstractExamples = false
+  
+  def setKeepAbstractExamples(b: Boolean) = { this.keepAbstractExamples = b; this}
 
   def extractFromFunDef(fd: FunDef, partition: Boolean): ExamplesBank = fd.postcondition match {
     case Some(Lambda(Seq(ValDef(id, _)), post)) =>
@@ -41,6 +45,8 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) {
           Some(InOutExample(fd.params.map(p => t(p.id)), Seq(t(id))))
         } else if ((ids & insIds) == insIds) {
           Some(InExample(fd.params.map(p => t(p.id))))
+        } else if((ids & outsIds) == outsIds) { // Examples provided on a part of the inputs.
+          Some(InOutExample(fd.params.map(p => t.getOrElse(p.id, Variable(p.id))), Seq(t(id))))
         } else {
           None
         }
@@ -67,27 +73,31 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) {
     case None =>
       ExamplesBank(Nil, Nil)
   }
-
-  // Extract examples from the passes found in expression
+  
+  /** Extract examples from the passes found in expression */
   def extractFromProblem(p: Problem): ExamplesBank = {
     val testClusters = extractTestsOf(and(p.pc, p.phi))
 
     // Finally, we keep complete tests covering all as++xs
     val allIds  = (p.as ++ p.xs).toSet
     val insIds  = p.as.toSet
-
+    val outsIds = p.xs.toSet
+    
     val examples = testClusters.toSeq.flatMap { t =>
       val ids = t.keySet
       if ((ids & allIds) == allIds) {
         Some(InOutExample(p.as.map(t), p.xs.map(t)))
       } else if ((ids & insIds) == insIds) {
         Some(InExample(p.as.map(t)))
+      } else if((ids & outsIds) == outsIds) { // Examples provided on a part of the inputs.
+        Some(InOutExample(p.as.map(p => t.getOrElse(p, Variable(p))), p.xs.map(t)))
       } else {
         None
       }
     }
 
     def isValidExample(ex: Example): Boolean = {
+      if(this.keepAbstractExamples) return true // TODO: Abstract interpretation here ?
       val (mapping, cond) = ex match {
         case io: InOutExample =>
           (Map((p.as zip io.ins) ++ (p.xs zip io.outs): _*), And(p.pc, p.phi))
@@ -117,6 +127,7 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) {
     ExamplesBank(generatedExamples.toSeq ++ solverExamples.toList, Nil)
   }
 
+  /** Extracts all passes constructs from the given postcondition, merges them if needed */
   private def extractTestsOf(e: Expr): Set[Map[Identifier, Expr]] = {
     val allTests = collect[Map[Identifier, Expr]] {
       case Passes(ins, outs, cases) =>
@@ -135,7 +146,7 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) {
         }
 
         // Check whether we can extract all ids from example
-        val results = exs.collect { case e if infos.forall(_._2.isDefinedAt(e)) =>
+        val results = exs.collect { case e if infos.forall(_._2.isDefinedAt(e)) || this.keepAbstractExamples =>
           infos.map{ case (id, f) => id -> f(e) }.toMap
         }
 
@@ -149,7 +160,8 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) {
     consolidateTests(allTests)
   }
 
-
+  /** Processes ((in, out) passes {
+    * cs[=>Case pattExpr if guard => outR]*/
   private def caseToExamples(in: Expr, out: Expr, cs: MatchCase, examplesPerCase: Int = 5): Seq[(Expr,Expr)] = {
 
     def doSubstitute(subs : Seq[(Identifier, Expr)], e : Expr) = 
@@ -180,34 +192,38 @@ class ExamplesFinder(ctx0: LeonContext, program: Program) {
           case (a, b, c) =>
             None
         }) getOrElse {
-          // If the input contains free variables, it does not provide concrete examples. 
-          // We will instantiate them according to a simple grammar to get them.
-          val enum = new MemoizedEnumerator[TypeTree, Expr](ValueGrammar.getProductions)
-          val values = enum.iterator(tupleTypeWrap(freeVars.map { _.getType }))
-          val instantiations = values.map {
-            v => freeVars.zip(unwrapTuple(v, freeVars.size)).toMap
-          }
-
-          def filterGuard(e: Expr, mapping: Map[Identifier, Expr]): Boolean = cs.optGuard match {
-            case Some(guard) =>
-              // in -> e should be enough. We shouldn't find any subexpressions of in.
-              evaluator.eval(replace(Map(in -> e), guard), mapping) match {
-                case EvaluationResults.Successful(BooleanLiteral(true)) => true
-                case _ => false
-              }
-
-            case None =>
-              true
+          if(this.keepAbstractExamples) {
+            Seq((pattExpr, cs.rhs))
+          } else {
+            // If the input contains free variables, it does not provide concrete examples. 
+            // We will instantiate them according to a simple grammar to get them.
+            val enum = new MemoizedEnumerator[TypeTree, Expr](ValueGrammar.getProductions)
+            val values = enum.iterator(tupleTypeWrap(freeVars.map { _.getType }))
+            val instantiations = values.map {
+              v => freeVars.zip(unwrapTuple(v, freeVars.size)).toMap
+            }
+  
+            def filterGuard(e: Expr, mapping: Map[Identifier, Expr]): Boolean = cs.optGuard match {
+              case Some(guard) =>
+                // in -> e should be enough. We shouldn't find any subexpressions of in.
+                evaluator.eval(replace(Map(in -> e), guard), mapping) match {
+                  case EvaluationResults.Successful(BooleanLiteral(true)) => true
+                  case _ => false
+                }
+  
+              case None =>
+                true
+            }
+            
+            if(cs.optGuard == Some(BooleanLiteral(false))) {
+              Nil
+            } else (for {
+              inst <- instantiations.toSeq
+              inR = replaceFromIDs(inst, pattExpr)
+              outR = replaceFromIDs(inst, doSubstitute(ieMap, cs.rhs))
+              if filterGuard(inR, inst)
+            } yield (inR, outR)).take(examplesPerCase)
           }
-          
-          if(cs.optGuard == Some(BooleanLiteral(false))) {
-            Nil
-          } else (for {
-            inst <- instantiations.toSeq
-            inR = replaceFromIDs(inst, pattExpr)
-            outR = replaceFromIDs(inst, doSubstitute(ieMap, cs.rhs))
-            if filterGuard(inR, inst)
-          } yield (inR, outR)).take(examplesPerCase)
         }
       }
     }
diff --git a/src/main/scala/leon/synthesis/rules/StringRender.scala b/src/main/scala/leon/synthesis/rules/StringRender.scala
index 1f62ec67b..e8a78e143 100644
--- a/src/main/scala/leon/synthesis/rules/StringRender.scala
+++ b/src/main/scala/leon/synthesis/rules/StringRender.scala
@@ -190,7 +190,7 @@ case object StringRender extends Rule("StringRender") {
           val evalResult =  e.eval(template, modelResult)
           evalResult.result match {
             case None =>
-              hctx.reporter.debug("Eval = None : ["+template+"] in ["+inputs.zip(in)+"]")
+              hctx.reporter.info("Eval = None : ["+template+"] in ["+inputs.zip(in)+"]")
               None
             case Some((sfExpr, abstractSfExpr)) =>
               //ctx.reporter.debug("Eval = ["+sfExpr+"] (from "+abstractSfExpr+")")
@@ -202,16 +202,16 @@ case object StringRender extends Rule("StringRender") {
                     case Some(equations) =>
                       gatherEquations(q, acc ++= equations)
                     case None =>
-                      hctx.reporter.debug("Could not extract equations from ["+sfget+"] == ["+rhsget+"]")
+                      hctx.reporter.info("Could not extract equations from ["+sfget+"] == ["+rhsget+"]")
                     None
                   }
                 case _ =>
-                  hctx.reporter.debug("sf empty or rhs empty ["+sfExpr+"] => ["+sf+"] in ["+rhs+"]")
+                  hctx.reporter.info("sf empty or rhs empty ["+sfExpr+"] => ["+sf+"] in ["+rhs+"]")
                   None
               }
           }
         } else {
-          hctx.reporter.debug("RHS.length != 1 : ["+rhExpr+"]")
+          hctx.reporter.info("RHS.length != 1 : ["+rhExpr+"]")
           None 
         }
     }
@@ -237,7 +237,7 @@ case object StringRender extends Rule("StringRender") {
     def computeSolutions(funDefsBodies: Seq[(FunDef, WithIds[Expr])], template: WithIds[Expr]): Stream[Assignment] = {
       val funDefs = for((funDef, body) <- funDefsBodies) yield  { funDef.body = Some(body._1); funDef }
       val newProgram = DefOps.addFunDefs(hctx.program, funDefs, hctx.sctx.functionContext)
-      findAssignments(newProgram, p.as, examples, template._1)
+      findAssignments(newProgram, p.as.filter{ x => !x.getType.isInstanceOf[FunctionType] }, examples, template._1)
     }
     
     val tagged_solutions =
@@ -573,7 +573,7 @@ case object StringRender extends Rule("StringRender") {
         
         val defaultToStringFunctions = defaultMapTypeToString()
         
-        val examplesFinder = new ExamplesFinder(hctx.context, hctx.program)
+        val examplesFinder = new ExamplesFinder(hctx.context, hctx.program).setKeepAbstractExamples(true)
         val examples = examplesFinder.extractFromProblem(p)
         
         val definedStringConverters: StringConverters =
-- 
GitLab