From de8b1242ab1c8ea7f3a814166d8211d7cda542ae Mon Sep 17 00:00:00 2001
From: Manos Koukoutos <emmanouil.koukoutos@epfl.ch>
Date: Wed, 18 Nov 2015 08:56:46 +0100
Subject: [PATCH] Evaluator improvements

NDEvaluator now a trait
(Previous) NDEvaluator -> StreamEvaluator
AngelicEvaluator is parametrized to the underlying evaluator
Fix Choose and Minus in StreamEvaluator
---
 .../leon/evaluators/AngelicEvaluator.scala    | 19 ++++--
 .../scala/leon/evaluators/Evaluator.scala     |  4 ++
 ...DEvaluator.scala => StreamEvaluator.scala} | 68 ++++++++++---------
 src/main/scala/leon/repair/rules/Focus.scala  |  4 +-
 .../evaluators/EvaluatorSuite.scala           |  3 +-
 .../leon/unit/evaluators/EvaluatorSuite.scala |  3 +-
 6 files changed, 57 insertions(+), 44 deletions(-)
 rename src/main/scala/leon/evaluators/{NDEvaluator.scala => StreamEvaluator.scala} (93%)

diff --git a/src/main/scala/leon/evaluators/AngelicEvaluator.scala b/src/main/scala/leon/evaluators/AngelicEvaluator.scala
index 30926b890..c1374b54f 100644
--- a/src/main/scala/leon/evaluators/AngelicEvaluator.scala
+++ b/src/main/scala/leon/evaluators/AngelicEvaluator.scala
@@ -3,19 +3,24 @@
 package leon
 package evaluators
 
+import leon.solvers.Model
 import purescala.Definitions.Program
 import purescala.Expressions.Expr
+import EvaluationResults._
 
-class AngelicEvaluator(ctx: LeonContext, prog: Program)
-  extends ContextualEvaluator(ctx, prog, 50000)
+class AngelicEvaluator(ctx: LeonContext, prog: Program, underlying: NDEvaluator)
+  extends Evaluator(ctx, prog)
   with DeterministicEvaluator
-  with DefaultContexts
 {
-  private val underlying = new NDEvaluator(ctx, prog)
-
   val description: String = "angelic evaluator"
   val name: String = "Interpreter that returns the first solution of a non-deterministic program"
 
-  protected def e(expr: Expr)(implicit rctx: RC, gctx: GC): Expr =
-    underlying.e(expr)(rctx, gctx).headOption.getOrElse(throw RuntimeError("No solution!"))
+  def eval(expr: Expr, model: Model): EvaluationResult = underlying.eval(expr, model) match {
+    case Successful(Stream(h, _*)) =>
+      Successful(h)
+    case Successful(Stream()) =>
+      RuntimeError("Underlying ND-evaluator returned no solution")
+    case other@(RuntimeError(_) | EvaluatorError(_)) =>
+      other.asInstanceOf[Result[Nothing]]
+  }
 }
diff --git a/src/main/scala/leon/evaluators/Evaluator.scala b/src/main/scala/leon/evaluators/Evaluator.scala
index 609afd792..9843da81d 100644
--- a/src/main/scala/leon/evaluators/Evaluator.scala
+++ b/src/main/scala/leon/evaluators/Evaluator.scala
@@ -46,4 +46,8 @@ abstract class Evaluator(val context: LeonContext, val program: Program) extends
 
 trait DeterministicEvaluator extends Evaluator {
   type Value = Expr
+}
+
+trait NDEvaluator extends Evaluator {
+  type Value = Stream[Expr]
 }
\ No newline at end of file
diff --git a/src/main/scala/leon/evaluators/NDEvaluator.scala b/src/main/scala/leon/evaluators/StreamEvaluator.scala
similarity index 93%
rename from src/main/scala/leon/evaluators/NDEvaluator.scala
rename to src/main/scala/leon/evaluators/StreamEvaluator.scala
index 1937d46f7..5b387368b 100644
--- a/src/main/scala/leon/evaluators/NDEvaluator.scala
+++ b/src/main/scala/leon/evaluators/StreamEvaluator.scala
@@ -15,16 +15,15 @@ import purescala.Expressions._
 import leon.solvers.SolverFactory
 import leon.utils.StreamUtils.cartesianProduct
 
-class NDEvaluator(ctx: LeonContext, prog: Program)
+class StreamEvaluator(ctx: LeonContext, prog: Program)
   extends ContextualEvaluator(ctx, prog, 50000)
+  with NDEvaluator
   with DefaultContexts
 {
 
   val name = "ND-evaluator"
   val description = "Non-deterministic interpreter for Leon programs that returns a Stream of solutions"
 
-  type Value = Stream[Expr]
-
   case class NDValue(tp: TypeTree) extends Expr with Terminal {
     val getType = tp
   }
@@ -155,11 +154,7 @@ class NDEvaluator(ctx: LeonContext, prog: Program)
     case f @ Forall(fargs, TopLevelAnds(conjuncts)) =>
 
       def solveOne(conj: Expr) = {
-        val instantiations = try {
-          forallInstantiations(gctx, fargs, conj)
-        } catch {
-          case _: EvalError => Seq()
-        }
+        val instantiations = forallInstantiations(gctx, fargs, conj)
         for {
           es <- cartesianProduct(instantiations.map { case (enabler, mapping) =>
             e(Implies(enabler, conj))(rctx.withNewVars(mapping), gctx)
@@ -199,39 +194,46 @@ class NDEvaluator(ctx: LeonContext, prog: Program)
         val cnstr = andJoin(eqs ::: p.pc :: p.phi :: Nil)
         solver.assertCnstr(cnstr)
 
-        def getSolution = solver.check match {
-          case Some(true) =>
-            val model = solver.getModel
+        def getSolution = try {
+          solver.check match {
+            case Some(true) =>
+              val model = solver.getModel
 
-            val valModel = valuateWithModel(model) _
+              val valModel = valuateWithModel(model) _
 
-            val res = p.xs.map(valModel)
-            val leonRes = tupleWrap(res)
-            val total = System.currentTimeMillis-tStart
+              val res = p.xs.map(valModel)
+              val leonRes = tupleWrap(res)
+              val total = System.currentTimeMillis - tStart
 
-            ctx.reporter.debug("Synthesis took "+total+"ms")
-            ctx.reporter.debug("Finished synthesis with "+leonRes.asString)
+              ctx.reporter.debug("Synthesis took " + total + "ms")
+              ctx.reporter.debug("Finished synthesis with " + leonRes.asString)
 
-            Some(leonRes)
-          case _ =>
-            None
+              Some(leonRes)
+            case _ =>
+              None
+          }
+        } catch {
+          case _: Throwable => None
         }
 
         Stream.iterate(getSolution)(prev => {
-          val ensureDistinct = Not(Equals(p.xs.head.toVariable, prev.get))
+          val ensureDistinct = Not(Equals(tupleWrap(p.xs.map{ _.toVariable}), prev.get))
           solver.assertCnstr(ensureDistinct)
-          getSolution
+          val sol = getSolution
+          // Clean up when the stream ends
+          if (sol.isEmpty) {
+            solverf.reclaim(solver)
+            solverf.shutdown()
+          }
+          sol
         }).takeWhile(_.isDefined).map(_.get)
-
       } catch {
         case e: Throwable =>
+          solverf.reclaim(solver)
+          solverf.shutdown()
           Stream()
-      } finally {
-        solverf.reclaim(solver)
-        solverf.shutdown()
       }
 
-
     case MatchExpr(scrut, cases) =>
 
       def matchesCase(scrut: Expr, caze: MatchCase)(implicit rctx: RC, gctx: GC): Stream[(MatchCase, Map[Identifier, Expr])] = {
@@ -306,7 +308,8 @@ class NDEvaluator(ctx: LeonContext, prog: Program)
         try {
           Some(step(expr, es))
         } catch {
-          case _: EvalError | _: RuntimeError =>
+          case _: RuntimeError =>
+            // EvalErrors stop the computation alltogether
             None
         }
       }
@@ -370,7 +373,7 @@ class NDEvaluator(ctx: LeonContext, prog: Program)
     case (Plus(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) =>
       InfiniteIntegerLiteral(i1 + i2)
 
-    case (Minus(_, _) Seq (InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) =>
+    case (Minus(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) =>
       InfiniteIntegerLiteral(i1 - i2)
 
     case (Times(_, _), Seq(InfiniteIntegerLiteral(i1), InfiniteIntegerLiteral(i2))) =>
@@ -392,6 +395,9 @@ class NDEvaluator(ctx: LeonContext, prog: Program)
       else
         throw RuntimeError("Modulo of division by 0.")
 
+    case (UMinus(_), Seq(InfiniteIntegerLiteral(i))) =>
+      InfiniteIntegerLiteral(-i)
+
     case (RealPlus(_, _), Seq(FractionalLiteral(ln, ld), FractionalLiteral(rn, rd))) =>
       normalizeFraction(FractionalLiteral(ln * rd + rn * ld, ld * rd))
 
@@ -411,9 +417,6 @@ class NDEvaluator(ctx: LeonContext, prog: Program)
     case (BVMinus(_, _), Seq(IntLiteral(i1), IntLiteral(i2))) =>
       IntLiteral(i1 - i2)
 
-    case (UMinus(_), Seq(InfiniteIntegerLiteral(i))) =>
-      InfiniteIntegerLiteral(-i)
-
     case (BVUMinus(_), Seq(IntLiteral(i))) =>
       IntLiteral(-i)
 
@@ -578,6 +581,7 @@ class NDEvaluator(ctx: LeonContext, prog: Program)
       l
 
     case (other, _) =>
+      context.reporter.error(other.getPos, "Error: don't know how to handle " + other.asString + " in Evaluator ("+other.getClass+").")
       throw EvalError("Unhandled case in Evaluator: " + other.asString)
 
   }
diff --git a/src/main/scala/leon/repair/rules/Focus.scala b/src/main/scala/leon/repair/rules/Focus.scala
index 8c1694dbd..d6b4d874f 100644
--- a/src/main/scala/leon/repair/rules/Focus.scala
+++ b/src/main/scala/leon/repair/rules/Focus.scala
@@ -75,9 +75,7 @@ case object Focus extends PreprocessingRule("Focus") {
 
     val fdSpec = {
       val id = FreshIdentifier("res", fd.returnType)
-      Let(id, fd.body.get,
-        fd.postcondition.map(l => application(l, Seq(id.toVariable))).getOrElse(BooleanLiteral(true))
-      )
+      Let(id, fd.body.get, application(fd.postOrTrue, Seq(id.toVariable)))
     }
 
     val TopLevelAnds(clauses) = p.ws
diff --git a/src/test/scala/leon/integration/evaluators/EvaluatorSuite.scala b/src/test/scala/leon/integration/evaluators/EvaluatorSuite.scala
index 1643e4800..82c419b04 100644
--- a/src/test/scala/leon/integration/evaluators/EvaluatorSuite.scala
+++ b/src/test/scala/leon/integration/evaluators/EvaluatorSuite.scala
@@ -205,7 +205,8 @@ class EvaluatorSuite extends LeonTestSuiteWithProgram with ExpressionsDSL {
 
   def normalEvaluators(implicit ctx: LeonContext, pgm: Program): List[Evaluator] = {
     List(
-      new DefaultEvaluator(ctx, pgm)
+      new DefaultEvaluator(ctx, pgm),
+      new AngelicEvaluator(ctx, pgm, new StreamEvaluator(ctx, pgm))
     )
   }
 
diff --git a/src/test/scala/leon/unit/evaluators/EvaluatorSuite.scala b/src/test/scala/leon/unit/evaluators/EvaluatorSuite.scala
index 6c614a562..4002a0af0 100644
--- a/src/test/scala/leon/unit/evaluators/EvaluatorSuite.scala
+++ b/src/test/scala/leon/unit/evaluators/EvaluatorSuite.scala
@@ -19,7 +19,8 @@ class EvaluatorSuite extends LeonTestSuite with helpers.ExpressionsDSL {
 
   def normalEvaluators(implicit ctx: LeonContext, pgm: Program): List[DeterministicEvaluator] = {
     List(
-      new DefaultEvaluator(ctx, pgm)
+      new DefaultEvaluator(ctx, pgm),
+      new AngelicEvaluator(ctx, pgm, new StreamEvaluator(ctx, pgm))
     )
   }
 
-- 
GitLab