diff --git a/src/main/scala/leon/synthesis/rules/CegisLike.scala b/src/main/scala/leon/synthesis/rules/CegisLike.scala index 5cb20981e2cfb8873c4bf0f574a19b5591da2a65..bdcfb910c484dd5455b6f3a0cb8d06a3b06b3118 100644 --- a/src/main/scala/leon/synthesis/rules/CegisLike.scala +++ b/src/main/scala/leon/synthesis/rules/CegisLike.scala @@ -327,12 +327,12 @@ abstract class CEGISLike[T <% Typed](name: String) extends Rule(name) { val evalParams = CodeGenParams(maxFunctionInvocations = -1, doInstrument = false) + //println("-- "*30) + //println(programCTree) + //println(".. "*30) val evaluator = new DualEvaluator(sctx.context, programCTree, evalParams) - //println("-- "*30) - //println(debugPrinter(programCTree)) - //println(".. "*30) tester = { (ins: Seq[Expr], bValues: Set[Identifier]) => diff --git a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala index 5fe0a94ebe50136c32e1962e1696c3269fcfad8a..6d573a1477532256f3aa1d53383dc36366c9b928 100644 --- a/src/test/scala/leon/test/synthesis/SynthesisSuite.scala +++ b/src/test/scala/leon/test/synthesis/SynthesisSuite.scala @@ -24,8 +24,71 @@ class SynthesisSuite extends LeonTestSuite { counter } - def forProgram(title: String, opts: SynthesisSettings = SynthesisSettings())(content: String)(block: (SearchContext, FunDef, Problem) => Unit) { + abstract class SynStrat(val ruleName: String); + case class Decomp(n: String, sub: Seq[SynStrat]) extends SynStrat(n); + case class Close(n: String) extends SynStrat(n); + + + class TestSearch(ctx: LeonContext, + ci: ChooseInfo, + p: Problem, + strat: SynStrat) extends SimpleSearch(ctx, ci, p, CostModels.default, None) { + + def synthesizeFrom(sctx: SynthesisContext, from: Node, strat: SynStrat): Boolean = { + from match { + case an: AndNode => + an.expand(SearchContext(sctx, ci, an, this)) + + if (an.isSolved) { + true + } else if (an.isDeadEnd) { + false + } else { + strat match { + case Decomp(_, sub) => + (an.descendents zip sub).forall { + case (n, strat) => synthesizeFrom(sctx, n, strat) + } + case _ => + fail("Got non-conclusive reply without Decomp strat") + false + } + } + + case on: OrNode => + on.expand(SearchContext(sctx, ci, on, this)) + val ns = on.descendents.filter { + case an: AndNode => matchingDesc(an.ri, strat.ruleName) + case _ => false + } + + if (ns.isEmpty) { + fail("Failed to find rule name: "+strat.ruleName) + } else if (ns.size > 1) { + fail("Ambiguous rule name: "+strat.ruleName) + } else { + synthesizeFrom(sctx, ns.head, strat) + } + } + } + + override def search(sctx: SynthesisContext): Stream[Solution] = { + if (synthesizeFrom(sctx, g.root, strat)) { + g.root.generateSolutions() + } else { + Stream.empty + } + } + + def matchingDesc(app: RuleInstantiation, n: String): Boolean = { + import java.util.regex.Pattern; + val pattern = Pattern.quote(n).replace("*", "\\E.*\\Q") + app.toString.matches(pattern) + } + + } + def forProgram(title: String, opts: SynthesisSettings = SynthesisSettings())(content: String)(strats: PartialFunction[String, SynStrat]) { test("Synthesizing %3d: [%s]".format(nextInt(), title)) { val ctx = testContext.copy(settings = Settings( synthesis = true, @@ -40,6 +103,7 @@ class SynthesisSuite extends LeonTestSuite { for ((f,cis) <- results; ci <- cis) { info("%-20s".format(ci.fd.id.toString)) + val sctx = SynthesisContext(ctx, opts, ci.fd, @@ -47,77 +111,81 @@ class SynthesisSuite extends LeonTestSuite { ctx.reporter) val p = ci.problem - val search = new SimpleSearch(ctx, ci, p, opts.costModel, None) - val hctx = SearchContext(sctx, ci, search.g.root, search) - - block(hctx, f, p) - } - } - } - case class Apply(desc: String, andThen: List[Apply] = Nil) + if (strats.isDefinedAt(f.id.name)) { + val search = new TestSearch(ctx, ci, p, strats(f.id.name)) - def synthesizeWith(hctx: SearchContext, p: Problem, ss: Apply): Solution = { - val apps = hctx.sctx.rules flatMap { _.instantiateOn(hctx, p)} + val sols = search.search(sctx) - def matchingDesc(app: RuleInstantiation, ss: Apply): Boolean = { - import java.util.regex.Pattern; - val pattern = Pattern.quote(ss.desc).replace("*", "\\E.*\\Q") - app.toString.matches(pattern) - } - - apps.filter(matchingDesc(_, ss)) match { - case app :: Nil => - app.apply(hctx) match { - case RuleClosed(sols) => - assert(sols.nonEmpty) - assert(ss.andThen.isEmpty) - sols.head - - case RuleExpanded(sub) => - val subSols = (sub zip ss.andThen) map { case (p, ss) => synthesizeWith(hctx, p, ss) } - app.onSuccess(subSols).get - } - - case Nil => - throw new AssertionError("Failed to find "+ss.desc+". Available: "+apps.mkString(", ")) - - case xs => - throw new AssertionError("Ambiguous "+ss.desc+". Found: "+xs.mkString(", ")) - } - } - - def synthesize(title: String)(program: String)(strategies: PartialFunction[String, Apply]) { - forProgram(title)(program) { - case (hctx, fd, p) => - strategies.lift.apply(fd.id.toString) match { - case Some(ss) => - synthesizeWith(hctx, p, ss) - - case None => - assert(false, "Function "+fd.id.toString+" not found") + if(sols.isEmpty) { + fail("No solution") + } + } } - } - } - - - def assertAllAlternativesSucceed(hctx: SearchContext, rr: Traversable[RuleInstantiation]) { - assert(!rr.isEmpty) - - for (r <- rr) { - assertRuleSuccess(hctx, r) - } + } } - def assertRuleSuccess(hctx: SearchContext, rr: RuleInstantiation): Option[Solution] = { - rr.apply(hctx) match { - case RuleClosed(sols) if sols.nonEmpty => - sols.headOption - case _ => - assert(false, "Rule did not succeed") - None - } - } +// def synthesizeWith(search: SearchContext, p: Problem, ss: Apply): Solution = { +// val apps = hctx.sctx.rules flatMap { _.instantiateOn(hctx, p)} +// +// def matchingDesc(app: RuleInstantiation, ss: Apply): Boolean = { +// import java.util.regex.Pattern; +// val pattern = Pattern.quote(ss.desc).replace("*", "\\E.*\\Q") +// app.toString.matches(pattern) +// } +// +// apps.filter(matchingDesc(_, ss)) match { +// case app :: Nil => +// app.apply(hctx) match { +// case RuleClosed(sols) => +// assert(sols.nonEmpty) +// assert(ss.andThen.isEmpty) +// sols.head +// +// case RuleExpanded(sub) => +// val subSols = (sub zip ss.andThen) map { case (p, ss) => synthesizeWith(hctx, p, ss) } +// app.onSuccess(subSols).get +// } +// +// case Nil => +// throw new AssertionError("Failed to find "+ss.desc+". Available: "+apps.mkString(", ")) +// +// case xs => +// throw new AssertionError("Ambiguous "+ss.desc+". Found: "+xs.mkString(", ")) +// } +// } +// +// def synthesize(title: String)(program: String)(strategies: PartialFunction[String, Apply]) { +// forProgram(title)(program) { +// case (hctx, fd, p) => +// strategies.lift.apply(fd.id.toString) match { +// case Some(ss) => +// synthesizeWith(hctx, p, ss) +// +// case None => +// assert(false, "Function "+fd.id.toString+" not found") +// } +// } +// } +// +// +// def assertAllAlternativesSucceed(hctx: SearchContext, rr: Traversable[RuleInstantiation]) { +// assert(!rr.isEmpty) +// +// for (r <- rr) { +// assertRuleSuccess(hctx, r) +// } +// } +// +// def assertRuleSuccess(hctx: SearchContext, rr: RuleInstantiation): Option[Solution] = { +// rr.apply(hctx) match { +// case RuleClosed(sols) if sols.nonEmpty => +// sols.headOption +// case _ => +// assert(false, "Rule did not succeed") +// None +// } +// } forProgram("Ground Enum", SynthesisSettings(selectedSolvers = Set("enum")))( """ @@ -140,8 +208,7 @@ object Injection { } """ ) { - case (hctx, fd, p) => - assertAllAlternativesSucceed(hctx, rules.Ground.instantiateOn(hctx, p)) + case "simple" => Close("Ground") } forProgram("Cegis 1")( @@ -165,8 +232,7 @@ object Injection { } """ ) { - case (hctx, fd, p) => - assertAllAlternativesSucceed(hctx, rules.CEGIS.instantiateOn(hctx, p)) + case "simple" => Close("CEGIS") } forProgram("Cegis 2")( @@ -190,25 +256,15 @@ object Injection { } """ ) { - case (hctx, fd, p) => - rules.CEGIS.instantiateOn(hctx, p).head.apply(hctx) match { - case RuleClosed(sols) if sols.nonEmpty => - assert(false, "CEGIS should have failed, but found : %s".format(sols.head)) - case _ => - } - - rules.ADTSplit.instantiateOn(hctx, p).head.apply(hctx) match { - case RuleExpanded(subs) => - for (sub <- subs; alt <- rules.CEGIS.instantiateOn(hctx, sub)) { - assertRuleSuccess(hctx, alt) - } - case _ => - assert(false, "Woot?") - } + case "tail" => + Decomp("ADT Split on 'in*'", Seq( + Close("CEGIS"), + Close("CEGIS") + )) } - synthesize("Lists")(""" + forProgram("Lists")(""" import leon.lang._ import leon.lang.synthesis._ @@ -250,31 +306,35 @@ object SortedList { } """) { case "concat" => - Apply("ADT Induction on 'in1'", List( - Apply("CEGIS"), - Apply("CEGIS") + Decomp("ADT Induction on 'in1'", List( + Close("CEGIS"), + Close("CEGIS") )) case "insert" => - Apply("ADT Induction on 'in1'", List( - Apply("CEGIS"), - Apply("CEGIS") + Decomp("ADT Induction on 'in1'", List( + Close("CEGIS"), + Close("CEGIS") )) case "insertSorted" => - Apply("Assert isSorted(in1)", List( - Apply("ADT Induction on 'in1'", List( - Apply("Ineq. Split on 'head*' and 'v*'", List( - Apply("CEGIS"), - Apply("CEGIS"), - Apply("CEGIS") + Decomp("Assert isSorted(in1)", List( + Decomp("ADT Induction on 'in1'", List( + Decomp("Ineq. Split on 'head*' and 'v*'", List( + Close("CEGIS"), + Decomp("Equivalent Inputs *", List( + Decomp("Unused Inputs *", List( + Close("CEGIS") + )) + )), + Close("CEGIS") )), - Apply("CEGIS") + Close("CEGIS") )) )) } - synthesize("Church")(""" + forProgram("Church")(""" import leon.lang._ import leon.lang.synthesis._ @@ -303,13 +363,13 @@ object ChurchNumerals { } """) { case "add" => - Apply("ADT Induction on 'y'", List( - Apply("CEGIS"), - Apply("CEGIS") + Decomp("ADT Induction on 'y'", List( + Close("CEGIS"), + Close("CEGIS") )) } - synthesize("Church")(""" + forProgram("Church")(""" import leon.lang._ import leon.lang.synthesis._ @@ -351,6 +411,6 @@ object ChurchNumerals { } """) { case "distinct" => - Apply("CEGIS") + Close("CEGIS") } }