From e40e99cdf38b2b6a73efdf5c7484fd7d042f91ff Mon Sep 17 00:00:00 2001 From: Etienne Kneuss <colder@php.net> Date: Thu, 29 Aug 2013 18:43:03 +0200 Subject: [PATCH] Add synthesis regression test suite For now only synthesis tests that 100% pass. The rest of benchmarks need to be investigated due to memory consumption issues --- .../scala/leon/synthesis/BoundedSearch.scala | 24 +++++ .../leon/synthesis/SynthesisOptions.scala | 1 + .../scala/leon/synthesis/Synthesizer.scala | 8 +- .../regression/synthesis/Church/Add.scala | 24 +++++ .../synthesis/Church/Distinct.scala | 37 +++++++ .../regression/synthesis/Church/Mult.scala | 37 +++++++ .../regression/synthesis/Church/Squared.scala | 44 +++++++++ .../regression/synthesis/List/Delete.scala | 40 ++++++++ .../regression/synthesis/List/Diff.scala | 58 +++++++++++ .../regression/synthesis/List/Insert.scala | 27 +++++ .../regression/synthesis/List/Split.scala | 98 +++++++++++++++++++ .../regression/synthesis/List/Union.scala | 49 ++++++++++ .../synthesis/SynthesisRegressionSuite.scala | 73 ++++++++++++++ .../leon/test/synthesis/SynthesisSuite.scala | 15 --- 14 files changed, 519 insertions(+), 16 deletions(-) create mode 100644 src/main/scala/leon/synthesis/BoundedSearch.scala create mode 100644 src/test/resources/regression/synthesis/Church/Add.scala create mode 100644 src/test/resources/regression/synthesis/Church/Distinct.scala create mode 100644 src/test/resources/regression/synthesis/Church/Mult.scala create mode 100644 src/test/resources/regression/synthesis/Church/Squared.scala create mode 100644 src/test/resources/regression/synthesis/List/Delete.scala create mode 100644 src/test/resources/regression/synthesis/List/Diff.scala create mode 100644 src/test/resources/regression/synthesis/List/Insert.scala create mode 100644 src/test/resources/regression/synthesis/List/Split.scala create mode 100644 src/test/resources/regression/synthesis/List/Union.scala create mode 100644 src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala diff --git a/src/main/scala/leon/synthesis/BoundedSearch.scala b/src/main/scala/leon/synthesis/BoundedSearch.scala new file mode 100644 index 000000000..5f7785225 --- /dev/null +++ b/src/main/scala/leon/synthesis/BoundedSearch.scala @@ -0,0 +1,24 @@ +/* Copyright 2009-2013 EPFL, Lausanne */ + +package leon +package synthesis + +class BoundedSearch(synth: Synthesizer, + problem: Problem, + rules: Seq[Rule], + costModel: CostModel, + searchBound: Int) extends SimpleSearch(synth, problem, rules, costModel) { + + def this(synth: Synthesizer, problem: Problem, searchBound: Int) = { + this(synth, problem, synth.rules, synth.options.costModel, searchBound) + } + + override def searchStep() { + val (closed, total) = g.getStatus + if (total > searchBound) { + stop() + } else { + super.searchStep() + } + } +} diff --git a/src/main/scala/leon/synthesis/SynthesisOptions.scala b/src/main/scala/leon/synthesis/SynthesisOptions.scala index c776ab044..9f867cabd 100644 --- a/src/main/scala/leon/synthesis/SynthesisOptions.scala +++ b/src/main/scala/leon/synthesis/SynthesisOptions.scala @@ -13,6 +13,7 @@ case class SynthesisOptions( costModel: CostModel = CostModel.default, rules: Seq[Rule] = Rules.all ++ Heuristics.all, manualSearch: Boolean = false, + searchBound: Option[Int] = None, // Cegis related options cegisUseUninterpretedProbe: Boolean = false, diff --git a/src/main/scala/leon/synthesis/Synthesizer.scala b/src/main/scala/leon/synthesis/Synthesizer.scala index a0232558e..a0d1e19a7 100644 --- a/src/main/scala/leon/synthesis/Synthesizer.scala +++ b/src/main/scala/leon/synthesis/Synthesizer.scala @@ -46,7 +46,13 @@ class Synthesizer(val context : LeonContext, } else if (options.searchWorkers > 1) { new ParallelSearch(this, problem, options.searchWorkers) } else { - new SimpleSearch(this, problem) + options.searchBound match { + case Some(b) => + new BoundedSearch(this, problem, b) + + case None => + new SimpleSearch(this, problem) + } } val sigINT = new Signal("INT") diff --git a/src/test/resources/regression/synthesis/Church/Add.scala b/src/test/resources/regression/synthesis/Church/Add.scala new file mode 100644 index 000000000..7b1b6b95f --- /dev/null +++ b/src/test/resources/regression/synthesis/Church/Add.scala @@ -0,0 +1,24 @@ +import leon.Utils._ +object ChurchNumerals { + sealed abstract class Num + case object Z extends Num + case class S(pred: Num) extends Num + + def value(n:Num) : Int = { + n match { + case Z => 0 + case S(p) => 1 + value(p) + } + } ensuring (_ >= 0) + + // def add(x : Num, y : Num) : Num = (x match { + // case Z => y + // case S(p) => add(p, S(y)) + // }) ensuring (value(_) == value(x) + value(y)) + + def add(x: Num, y: Num): Num = { + choose { (r : Num) => + value(r) == value(x) + value(y) + } + } +} diff --git a/src/test/resources/regression/synthesis/Church/Distinct.scala b/src/test/resources/regression/synthesis/Church/Distinct.scala new file mode 100644 index 000000000..a7a0189c2 --- /dev/null +++ b/src/test/resources/regression/synthesis/Church/Distinct.scala @@ -0,0 +1,37 @@ +import leon.Utils._ +object ChurchNumerals { + sealed abstract class Num + case object Z extends Num + case class S(pred: Num) extends Num + + def value(n:Num) : Int = { + n match { + case Z => 0 + case S(p) => 1 + value(p) + } + } ensuring (_ >= 0) + + def add(x : Num, y : Num) : Num = (x match { + case Z => y + case S(p) => add(p, S(y)) + }) ensuring (value(_) == value(x) + value(y)) + + //def distinct(x : Num, y : Num) : Num = (x match { + // case Z => + // S(y) + + // case S(p) => + // y match { + // case Z => + // S(x) + // case S(p) => + // Z + // } + //}) ensuring (res => res != x && res != y) + + def distinct(x: Num, y: Num): Num = { + choose { (r : Num) => + r != x && r != y + } + } +} diff --git a/src/test/resources/regression/synthesis/Church/Mult.scala b/src/test/resources/regression/synthesis/Church/Mult.scala new file mode 100644 index 000000000..42519e663 --- /dev/null +++ b/src/test/resources/regression/synthesis/Church/Mult.scala @@ -0,0 +1,37 @@ +import leon.Utils._ +object ChurchNumerals { + sealed abstract class Num + case object Z extends Num + case class S(pred: Num) extends Num + + def value(n:Num) : Int = { + n match { + case Z => 0 + case S(p) => 1 + value(p) + } + } ensuring (_ >= 0) + + def add(x : Num, y : Num) : Num = (x match { + case Z => y + case S(p) => add(p, S(y)) + }) ensuring (value(_) == value(x) + value(y)) + + //def distinct(x : Num, y : Num) : Num = (x match { + // case Z => + // S(y) + + // case S(p) => + // y match { + // case Z => + // S(x) + // case S(p) => + // Z + // } + //}) ensuring (res => res != x && res != y) + + def mult(x: Num, y: Num): Num = { + choose { (r : Num) => + value(r) == value(x) * value(y) + } + } +} diff --git a/src/test/resources/regression/synthesis/Church/Squared.scala b/src/test/resources/regression/synthesis/Church/Squared.scala new file mode 100644 index 000000000..096f212a6 --- /dev/null +++ b/src/test/resources/regression/synthesis/Church/Squared.scala @@ -0,0 +1,44 @@ +import leon.Utils._ +object ChurchNumerals { + sealed abstract class Num + case object Z extends Num + case class S(pred: Num) extends Num + + def value(n:Num) : Int = { + n match { + case Z => 0 + case S(p) => 1 + value(p) + } + } ensuring (_ >= 0) + + def add(x : Num, y : Num) : Num = (x match { + case Z => y + case S(p) => add(p, S(y)) + }) ensuring (value(_) == value(x) + value(y)) + + //def distinct(x : Num, y : Num) : Num = (x match { + // case Z => + // S(y) + + // case S(p) => + // y match { + // case Z => + // S(x) + // case S(p) => + // Z + // } + //}) ensuring (res => res != x && res != y) + + def mult(x: Num, y: Num): Num = (y match { + case S(p) => + add(mult(x, p), x) + case Z => + Z + }) ensuring { value(_) == value(x) * value(y) } + + def squared(x: Num): Num = { + choose { (r : Num) => + value(r) == value(x) * value(x) + } + } +} diff --git a/src/test/resources/regression/synthesis/List/Delete.scala b/src/test/resources/regression/synthesis/List/Delete.scala new file mode 100644 index 000000000..9fdef981b --- /dev/null +++ b/src/test/resources/regression/synthesis/List/Delete.scala @@ -0,0 +1,40 @@ +import leon.Annotations._ +import leon.Utils._ + +object Delete { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case object Nil extends List + + def size(l: List) : Int = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[Int] = l match { + case Nil => Set.empty[Int] + case Cons(i, t) => Set(i) ++ content(t) + } + + def insert(in1: List, v: Int): List = { + Cons(v, in1) + } ensuring { content(_) == content(in1) ++ Set(v) } + + //def delete(in1: List, v: Int): List = { + // in1 match { + // case Cons(h,t) => + // if (h == v) { + // delete(t, v) + // } else { + // Cons(h, delete(t, v)) + // } + // case Nil => + // Nil + // } + //} ensuring { content(_) == content(in1) -- Set(v) } + + def delete(in1: List, v: Int) = choose { + (out : List) => + content(out) == content(in1) -- Set(v) + } +} diff --git a/src/test/resources/regression/synthesis/List/Diff.scala b/src/test/resources/regression/synthesis/List/Diff.scala new file mode 100644 index 000000000..3e7121eb1 --- /dev/null +++ b/src/test/resources/regression/synthesis/List/Diff.scala @@ -0,0 +1,58 @@ +import leon.Annotations._ +import leon.Utils._ + +object Diff { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case object Nil extends List + + def size(l: List) : Int = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[Int] = l match { + case Nil => Set.empty[Int] + case Cons(i, t) => Set(i) ++ content(t) + } + + def insert(in1: List, v: Int): List = { + Cons(v, in1) + } ensuring { content(_) == content(in1) ++ Set(v) } + + def delete(in1: List, v: Int): List = { + in1 match { + case Cons(h,t) => + if (h == v) { + delete(t, v) + } else { + Cons(h, delete(t, v)) + } + case Nil => + Nil + } + } ensuring { content(_) == content(in1) -- Set(v) } + + def union(in1: List, in2: List): List = { + in1 match { + case Cons(h, t) => + Cons(h, union(t, in2)) + case Nil => + in2 + } + } ensuring { content(_) == content(in1) ++ content(in2) } + + // def diff(in1: List, in2: List): List = { + // in2 match { + // case Nil => + // in1 + // case Cons(h, t) => + // diff(delete(in1, h), t) + // } + // } ensuring { content(_) == content(in1) -- content(in2) } + + def diff(in1: List, in2: List) = choose { + (out : List) => + content(out) == content(in1) -- content(in2) + } +} diff --git a/src/test/resources/regression/synthesis/List/Insert.scala b/src/test/resources/regression/synthesis/List/Insert.scala new file mode 100644 index 000000000..e5b21b45d --- /dev/null +++ b/src/test/resources/regression/synthesis/List/Insert.scala @@ -0,0 +1,27 @@ +import leon.Annotations._ +import leon.Utils._ + +object Insert { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case object Nil extends List + + def size(l: List) : Int = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[Int] = l match { + case Nil => Set.empty[Int] + case Cons(i, t) => Set(i) ++ content(t) + } + + //def insert(in1: List, v: Int): List = { + // Cons(v, in1) + //} ensuring { content(_) == content(in1) ++ Set(v) } + + def insert(in1: List, v: Int) = choose { + (out : List) => + content(out) == content(in1) ++ Set(v) + } +} diff --git a/src/test/resources/regression/synthesis/List/Split.scala b/src/test/resources/regression/synthesis/List/Split.scala new file mode 100644 index 000000000..5aa4a8f1f --- /dev/null +++ b/src/test/resources/regression/synthesis/List/Split.scala @@ -0,0 +1,98 @@ +import leon.Annotations._ +import leon.Utils._ + +object Complete { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case object Nil extends List + + def size(l: List) : Int = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[Int] = l match { + case Nil => Set.empty[Int] + case Cons(i, t) => Set(i) ++ content(t) + } + + def insert(in1: List, v: Int): List = { + Cons(v, in1) + } ensuring { content(_) == content(in1) ++ Set(v) } + + //def insert(in1: List, v: Int) = choose { + // (out : List) => + // content(out) == content(in1) ++ Set(v) + //} + + def delete(in1: List, v: Int): List = { + in1 match { + case Cons(h,t) => + if (h == v) { + delete(t, v) + } else { + Cons(h, delete(t, v)) + } + case Nil => + Nil + } + } ensuring { content(_) == content(in1) -- Set(v) } + + //def delete(in1: List, v: Int) = choose { + // (out : List) => + // content(out) == content(in1) -- Set(v) + //} + + def union(in1: List, in2: List): List = { + in1 match { + case Cons(h, t) => + Cons(h, union(t, in2)) + case Nil => + in2 + } + } ensuring { content(_) == content(in1) ++ content(in2) } + + //def union(in1: List, in2: List) = choose { + // (out : List) => + // content(out) == content(in1) ++ content(in2) + //} + + def diff(in1: List, in2: List): List = { + in2 match { + case Nil => + in1 + case Cons(h, t) => + diff(delete(in1, h), t) + } + } ensuring { content(_) == content(in1) -- content(in2) } + + //def diff(in1: List, in2: List) = choose { + // (out : List) => + // content(out) == content(in1) -- content(in2) + //} + + def splitSpec(list : List, res : (List,List)) : Boolean = { + + val s1 = size(res._1) + val s2 = size(res._2) + abs(s1 - s2) <= 1 && s1 + s2 == size(list) && + content(res._1) ++ content(res._2) == content(list) + } + + def abs(i : Int) : Int = { + if(i < 0) -i else i + } ensuring(_ >= 0) + + // def split(list : List) : (List,List) = (list match { + // case Nil => (Nil, Nil) + // case Cons(x, Nil) => (Cons(x, Nil), Nil) + // case Cons(x1, Cons(x2, xs)) => + // val (s1,s2) = split(xs) + // (Cons(x1, s1), Cons(x2, s2)) + // }) ensuring(res => splitSpec(list, res)) + + def split(list : List) : (List,List) = { + choose { (res : (List,List)) => splitSpec(list, res) } + } + +} diff --git a/src/test/resources/regression/synthesis/List/Union.scala b/src/test/resources/regression/synthesis/List/Union.scala new file mode 100644 index 000000000..adcc943fd --- /dev/null +++ b/src/test/resources/regression/synthesis/List/Union.scala @@ -0,0 +1,49 @@ +import leon.Annotations._ +import leon.Utils._ + +object Union { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case object Nil extends List + + def size(l: List) : Int = (l match { + case Nil => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def content(l: List): Set[Int] = l match { + case Nil => Set.empty[Int] + case Cons(i, t) => Set(i) ++ content(t) + } + + def insert(in1: List, v: Int): List = { + Cons(v, in1) + } ensuring { content(_) == content(in1) ++ Set(v) } + + def delete(in1: List, v: Int): List = { + in1 match { + case Cons(h,t) => + if (h == v) { + delete(t, v) + } else { + Cons(h, delete(t, v)) + } + case Nil => + Nil + } + } ensuring { content(_) == content(in1) -- Set(v) } + + // def union(in1: List, in2: List): List = { + // in1 match { + // case Cons(h, t) => + // Cons(h, union(t, in2)) + // case Nil => + // in2 + // } + // } ensuring { content(_) == content(in1) ++ content(in2) } + + def union(in1: List, in2: List) = choose { + (out : List) => + content(out) == content(in1) ++ content(in2) + } +} diff --git a/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala b/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala new file mode 100644 index 000000000..eda49bbcf --- /dev/null +++ b/src/test/scala/leon/test/synthesis/SynthesisRegressionSuite.scala @@ -0,0 +1,73 @@ +/* Copyright 2009-2013 EPFL, Lausanne */ + +package leon +package test +package synthesis + +import leon._ +import leon.purescala.Definitions._ +import leon.purescala.Trees._ +import leon.purescala.TreeOps._ +import leon.solvers.z3._ +import leon.solvers.Solver +import leon.synthesis._ +import leon.synthesis.utils._ + +import java.io.File + +import TestUtils._ + +class SynthesisRegressionSuite extends LeonTestSuite { + private def forEachFileIn(path : String)(block : File => Unit) { + val fs = filesInResourceDir(path, _.endsWith(".scala")) + + for(f <- fs) { + block(f) + } + } + + private def testSynthesis(cat: String, f: File, bound: Int) { + val ctx = LeonContext( + settings = Settings( + synthesis = true, + xlang = false, + verify = false + ), + files = List(f), + reporter = new TestSilentReporter + ) + + val opts = SynthesisOptions(searchBound = Some(bound)) + + val pipeline = plugin.ExtractionPhase andThen SubtypingPhase + + val program = pipeline.run(ctx)(f.getAbsolutePath :: Nil) + + var chooses = ChooseInfo.extractFromProgram(ctx, program, opts) + + for (ci <- chooses) { + test(cat+": "+f.getName()+" - "+ci.fd.id.name) { + val (sol, isComplete) = ci.synthesizer.synthesize() + if (!isComplete) { + fail("Solution was not found. (Search bound: "+bound+")") + } + } + } + } + + forEachFileIn("regression/synthesis/Church/") { f => + testSynthesis("Church", f, 200) + } + + forEachFileIn("regression/synthesis/List/") { f => + testSynthesis("List", f, 200) + } + + forEachFileIn("regression/synthesis/SortedList/") { f => + testSynthesis("SortedList", f, 400) + } + + forEachFileIn("regression/synthesis/StrictSortedList/") { f => + testSynthesis("StrictSortedList", f, 400) + } +} diff --git a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala index d134c980e..574efa3fe 100644 --- a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala +++ b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala @@ -136,20 +136,6 @@ class SynthesisSuite extends LeonTestSuite { } } - - def assertFastEnough(sctx: SynthesisContext, rr: Traversable[RuleInstantiation], timeoutMs: Long) { - for (alt <- rr) { - val ts = System.currentTimeMillis - - val res = alt.apply(sctx) - - val t = System.currentTimeMillis - ts - - t should be <= timeoutMs - } - } - - forProgram("Cegis 1")( """ import scala.collection.immutable.Set @@ -173,7 +159,6 @@ object Injection { ) { case (sctx, fd, p) => assertAllAlternativesSucceed(sctx, rules.CEGIS.instantiateOn(sctx, p)) - assertFastEnough(sctx, rules.CEGIS.instantiateOn(sctx, p), 100) } forProgram("Cegis 2")( -- GitLab