From 5ea6796fa4c3f3d09aa90f2fe9bc5015bc77da33 Mon Sep 17 00:00:00 2001
From: "Emmanouil (Manos) Koukoutos" <emmanouil.koukoutos@epfl.ch>
Date: Tue, 2 Dec 2014 17:58:16 +0100
Subject: [PATCH] Synthesis from examples

---
 .../scala/leon/purescala/Extractors.scala     |  6 ++
 .../scala/leon/purescala/PrettyPrinter.scala  |  1 -
 src/main/scala/leon/synthesis/Problem.scala   | 64 +++++++++++++++++--
 .../synthesis/SynthesisRegressionSuite.scala  |  4 ++
 4 files changed, 70 insertions(+), 5 deletions(-)

diff --git a/src/main/scala/leon/purescala/Extractors.scala b/src/main/scala/leon/purescala/Extractors.scala
index 439d0888c..bfbcf0267 100644
--- a/src/main/scala/leon/purescala/Extractors.scala
+++ b/src/main/scala/leon/purescala/Extractors.scala
@@ -262,4 +262,10 @@ object Extractors {
     }
   }
 
+  object UnwrapTuplePattern {
+    def unapply(p : Pattern) : Option[Seq[Pattern]] = Option(p) map {
+      case TuplePattern(_,subs) => subs
+      case other => Seq(other)
+    }
+  }
 }
diff --git a/src/main/scala/leon/purescala/PrettyPrinter.scala b/src/main/scala/leon/purescala/PrettyPrinter.scala
index b8ebf7c95..08dee1c71 100644
--- a/src/main/scala/leon/purescala/PrettyPrinter.scala
+++ b/src/main/scala/leon/purescala/PrettyPrinter.scala
@@ -321,7 +321,6 @@ class PrettyPrinter(opts: PrinterOptions, val sb: StringBuffer = new StringBuffe
           p"($args)"
         }
 
-
       case FunctionInvocation(tfd, args) =>
         p"${tfd.id}"
 
diff --git a/src/main/scala/leon/synthesis/Problem.scala b/src/main/scala/leon/synthesis/Problem.scala
index d13b70d44..e56eb59a8 100644
--- a/src/main/scala/leon/synthesis/Problem.scala
+++ b/src/main/scala/leon/synthesis/Problem.scala
@@ -5,13 +5,15 @@ package synthesis
 
 import leon.purescala.Trees._
 import leon.purescala.TreeOps._
+import leon.purescala.TypeTrees.TypeTree
 import leon.purescala.Common._
 import leon.purescala.Constructors._
 
 // Defines a synthesis triple of the form:
 // ⟦ as ⟨ C | phi ⟩ xs ⟧
 case class Problem(as: List[Identifier], pc: Expr, phi: Expr, xs: List[Identifier]) {
-  override def toString = "⟦ "+as.mkString(";")+", "+(if (pc != BooleanLiteral(true)) pc+" ≺ " else "")+" ⟨ "+phi+" ⟩ "+xs.mkString(";")+" ⟧ "
+  override def toString = 
+    "⟦ "+as.mkString(";")+", " + (if (pc != BooleanLiteral(true)) pc+" ≺ " else "") + " ⟨ "+phi+" ⟩ " + xs.mkString(";") + " ⟧ "
 
   def getTests(sctx: SynthesisContext): Seq[Example] = {
     import purescala.Extractors._
@@ -81,13 +83,67 @@ case class Problem(as: List[Identifier], pc: Expr, phi: Expr, xs: List[Identifie
       case Tuple(is) => is.collect { case Variable(i) => i }.toList
       case _ => Nil
     }
+  
+
+    def toIOExamples(in: Expr, out : Expr, cs : MatchCase) : Seq[(Expr,Expr)] = {
+      import utils.ExpressionGrammars
+      import bonsai._
+      import bonsai.enumerators._
+
+      val examplesPerVariable = 5
+     
+      def doSubstitute(subs : Seq[(Identifier, Expr)], e : Expr) = 
+        subs.foldLeft(e) { 
+          case (from, (id, to)) => replaceFromIDs(Map(id -> to), from)
+        }
+
+      // The trivial example
+      if (cs.rhs == out)
+        Seq()
+      else {
+        // The pattern as expression (input expression)(may contain free variables)
+        val (pattExpr, ieMap) = patternToExpression(cs.pattern, in.getType)
+        val freeVars = variablesOf(pattExpr).toSeq
+
+        if (freeVars.isEmpty) {
+          // The input contains no free vars. Trivially return input-output pair
+          Seq((pattExpr, doSubstitute(ieMap,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 grammar = ExpressionGrammars.BaseGrammar
+          val enum = new MemoizedEnumerator[TypeTree, Expr](grammar.getProductions _)
+          val types = freeVars.map{ _.getType }
+          val typesWithValues = types.map { tp => (tp, enum.iterator(tp).take(examplesPerVariable).toSeq) }.toMap
+          val values = freeVars map { v => typesWithValues(v.getType) }
+          // Make all combinations of all possible instantiations
+          def combinations[A](s : Seq[Seq[A]]) : Seq[Seq[A]] = {
+            if (s.isEmpty) Seq(Seq())
+            else for {
+              h <- s.head
+              t <- combinations(s.tail)
+            } yield (h +: t)
+          }
+          val instantiations = combinations(values) map { freeVars.zip(_).toMap }
+          instantiations map { inst =>
+            (replaceFromIDs(inst, pattExpr), replaceFromIDs(inst, doSubstitute(ieMap, cs.rhs)))  
+          }
+        }
+        
+      }
+    }
+
 
     val evaluator = new DefaultEvaluator(sctx.context, sctx.program)
 
     val testClusters = collect[Map[Identifier, Expr]] {
-      case FunctionInvocation(tfd, List(in, out, FiniteMap(inouts))) if tfd.id.name == "passes" =>
-        val infos = extractIds(Tuple(Seq(in, out)))
-        val exs   = inouts.map{ case (i, o) =>
+
+      //case FunctionInvocation(tfd, List(in, out, FiniteMap(inouts))) if tfd.id.name == "passes" =>
+      case p@Passes(ins, out, cases) =>
+        
+        val ioPairs = cases flatMap { toIOExamples(ins,out,_) }
+        val infos = extractIds(p.scrutinee)
+        val exs   = ioPairs.map{ case (i, o) =>
           val test = Tuple(Seq(i, o))
           val ids = variablesOf(test)
           evaluator.eval(test, ids.map { (i: Identifier) => i -> i.toVariable }.toMap) match {
diff --git a/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala b/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala
index eeeadde94..89fe46ea0 100644
--- a/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala
+++ b/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala
@@ -53,6 +53,10 @@ class SynthesisRegressionSuite extends LeonTestSuite {
     testSynthesis("Church", f, 200)
   }
 
+  forEachFileIn("regression/synthesis/Examples/") { f =>
+    testSynthesis("IOExamples", f, 200)
+  }
+
   forEachFileIn("regression/synthesis/List/") { f =>
     testSynthesis("List", f, 200)
   }
-- 
GitLab