diff --git a/src/main/scala/leon/synthesis/DerivationTree.scala b/src/main/scala/leon/synthesis/DerivationTree.scala index 62fea60b1341ed39acd4248b56782c4769987e7e..370b10f79590ed031d6fcf987fb23e917d0e1b7e 100644 --- a/src/main/scala/leon/synthesis/DerivationTree.scala +++ b/src/main/scala/leon/synthesis/DerivationTree.scala @@ -2,17 +2,20 @@ package leon package synthesis class DerivationTree(root: RootTask) { - var store = Map[DecomposedTask, Map[Problem, DecomposedTask]]().withDefaultValue(Map()) + var store = Map[SimpleTask, Map[Problem, SimpleTask]]().withDefaultValue(Map()) var solutions = Map[Task, Solution]() def recordSolutionFor(task: Task, solution: Solution) = task match { - case dt: DecomposedTask => + /* + case dt: SimpleTask => if (dt.parent ne null) { store += dt.parent -> (store(dt.parent) + (task.problem -> dt)) } solutions += dt -> solution case _ => + */ + case _ => } private[this] var _nextID = 0 @@ -41,12 +44,13 @@ class DerivationTree(root: RootTask) { } } - def printTask(t: DecomposedTask) { + def printTask(t: SimpleTask) { val node = nameFor(t, "task"); + /* res append " "+node+" [ label = <<TABLE BORDER=\"0\" CELLBORDER=\"1\" CELLSPACING=\"0\"><TR><TD BORDER=\"0\">"+t.rule.name+"</TD></TR><TR><TD BGCOLOR=\"indianred1\">"+t.problem+"</TD></TR><TR><TD BGCOLOR=\"greenyellow\">"+solutions.getOrElse(t, "?")+"</TD></TR></TABLE>> shape = \"none\" ];\n" - + */ for ((_, task) <- store(t)) { res append nameFor(task, "task") +" -> " + " "+node+";\n" diff --git a/src/main/scala/leon/synthesis/Rules.scala b/src/main/scala/leon/synthesis/Rules.scala index b8ea78b0dcbe786e8d649afdae9b62a441250de9..ea1220929dd39531aeffe8d18942498d50d4ebf4 100644 --- a/src/main/scala/leon/synthesis/Rules.scala +++ b/src/main/scala/leon/synthesis/Rules.scala @@ -21,9 +21,14 @@ object Rules { ) } +abstract class RuleResult +case object RuleInapplicable extends RuleResult +case class RuleSuccess(solution: Solution) extends RuleResult +case class RuleDecomposed(subProblems: List[Problem], onSuccess: List[Solution] => Solution) extends RuleResult + abstract class Rule(val name: String, val synth: Synthesizer) { - def isApplicable(task: Task): List[DecomposedTask] + def applyOn(task: Task): RuleResult def subst(what: Tuple2[Identifier, Expr], in: Expr): Expr = replace(Map(Variable(what._1) -> what._2), in) @@ -36,7 +41,7 @@ abstract class Rule(val name: String, val synth: Synthesizer) { } class OnePoint(synth: Synthesizer) extends Rule("One-point", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem @@ -67,15 +72,15 @@ class OnePoint(synth: Synthesizer) extends Rule("One-point", synth) { case _ => Solution.none } - List(task.decompose(this, List(newProblem), onSuccess, 100)) + RuleDecomposed(List(newProblem), onSuccess) } else { - Nil + RuleInapplicable } } } class Ground(synth: Synthesizer) extends Rule("Ground", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem if (p.as.isEmpty) { @@ -84,20 +89,20 @@ class Ground(synth: Synthesizer) extends Rule("Ground", synth) { synth.solveSAT(p.phi) match { case (Some(true), model) => - List(task.solveUsing(this, Solution(BooleanLiteral(true), Tuple(p.xs.map(id => model.getOrElse(id, simplestValue(Variable(id))))).setType(tpe), 200), 200)) + RuleSuccess(Solution(BooleanLiteral(true), Tuple(p.xs.map(id => model.getOrElse(id, simplestValue(Variable(id))))).setType(tpe))) case (Some(false), model) => - List(task.solveUsing(this, Solution(BooleanLiteral(false), Error(p.phi+" is UNSAT!").setType(tpe), 200), 200)) + RuleSuccess(Solution(BooleanLiteral(false), Error(p.phi+" is UNSAT!").setType(tpe))) case _ => - Nil + RuleInapplicable } } else { - Nil + RuleInapplicable } } } class CaseSplit(synth: Synthesizer) extends Rule("Case-Split", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem p.phi match { case Or(Seq(o1, o2)) => @@ -109,15 +114,15 @@ class CaseSplit(synth: Synthesizer) extends Rule("Case-Split", synth) { case _ => Solution.none } - List(task.decompose(this, List(sub1, sub2), onSuccess, 100)) + RuleDecomposed(List(sub1, sub2), onSuccess) case _ => - Nil + RuleInapplicable } } } class Assert(synth: Synthesizer) extends Rule("Assert", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem p.phi match { @@ -128,7 +133,7 @@ class Assert(synth: Synthesizer) extends Rule("Assert", synth) { if (!exprsA.isEmpty) { if (others.isEmpty) { - List(task.solveUsing(this, Solution(And(exprsA), Tuple(p.xs.map(id => simplestValue(Variable(id))))), 150)) + RuleSuccess(Solution(And(exprsA), Tuple(p.xs.map(id => simplestValue(Variable(id)))))) } else { val onSuccess: List[Solution] => Solution = { case List(s) => Solution(And(s.pre +: exprsA), s.term, 150) @@ -137,34 +142,34 @@ class Assert(synth: Synthesizer) extends Rule("Assert", synth) { val sub = p.copy(phi = And(others)) - List(task.decompose(this, List(sub), onSuccess, 150)) + RuleDecomposed(List(sub), onSuccess) } } else { - Nil + RuleInapplicable } case _ => - Nil + RuleInapplicable } } } class UnusedInput(synth: Synthesizer) extends Rule("UnusedInput", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem val unused = p.as.toSet -- variablesOf(p.phi) if (!unused.isEmpty) { val sub = p.copy(as = p.as.filterNot(unused)) - List(task.decompose(this, List(sub), forward, 300)) + RuleDecomposed(List(sub), forward) } else { - Nil + RuleInapplicable } } } class UnconstrainedOutput(synth: Synthesizer) extends Rule("Unconstr.Output", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem val unconstr = p.xs.toSet -- variablesOf(p.phi) @@ -178,10 +183,9 @@ class UnconstrainedOutput(synth: Synthesizer) extends Rule("Unconstr.Output", sy Solution.none } - List(task.decompose(this, List(sub), onSuccess, 300)) - + RuleDecomposed(List(sub), onSuccess) } else { - Nil + RuleInapplicable } } @@ -189,7 +193,7 @@ class UnconstrainedOutput(synth: Synthesizer) extends Rule("Unconstr.Output", sy object Unification { class DecompTrivialClash(synth: Synthesizer) extends Rule("Unif Dec./Clash/Triv.", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem val TopLevelAnds(exprs) = p.phi @@ -209,15 +213,15 @@ object Unification { val sub = p.copy(phi = And((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq)) - List(task.decompose(this, List(sub), forward, 100)) + RuleDecomposed(List(sub), forward) } else { - Nil + RuleInapplicable } } } class OccursCheck(synth: Synthesizer) extends Rule("Unif OccursCheck", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem val TopLevelAnds(exprs) = p.phi @@ -234,9 +238,9 @@ object Unification { if (isImpossible) { val tpe = TupleType(p.xs.map(_.getType)) - List(task.solveUsing(this, Solution(BooleanLiteral(false), Error(p.phi+" is UNSAT!").setType(tpe), 200), 200)) + RuleSuccess(Solution(BooleanLiteral(false), Error(p.phi+" is UNSAT!").setType(tpe))) } else { - Nil + RuleInapplicable } } } @@ -244,7 +248,7 @@ object Unification { class ADTDual(synth: Synthesizer) extends Rule("ADTDual", synth) { - def isApplicable(task: Task): List[DecomposedTask] = { + def applyOn(task: Task): RuleResult = { val p = task.problem val xs = p.xs.toSet @@ -263,9 +267,9 @@ class ADTDual(synth: Synthesizer) extends Rule("ADTDual", synth) { if (!toRemove.isEmpty) { val sub = p.copy(phi = And((exprs.toSet -- toRemove ++ toAdd.flatten).toSeq)) - List(task.decompose(this, List(sub), forward, 80)) + RuleDecomposed(List(sub), forward) } else { - Nil + RuleInapplicable } } } diff --git a/src/main/scala/leon/synthesis/Synthesizer.scala b/src/main/scala/leon/synthesis/Synthesizer.scala index 295b6e143516deae9dbe3b2d85d262644facc1d2..b1c443494e44e849fa02b8375663014680f7fb19 100644 --- a/src/main/scala/leon/synthesis/Synthesizer.scala +++ b/src/main/scala/leon/synthesis/Synthesizer.scala @@ -28,6 +28,7 @@ class Synthesizer(val r: Reporter, val solvers: List[Solver], generateDerivation workList += rootTask solution = None + if (generateDerivationTrees) { derivationTree = new DerivationTree(rootTask) } @@ -35,26 +36,11 @@ class Synthesizer(val r: Reporter, val solvers: List[Solver], generateDerivation while (!workList.isEmpty && solution.isEmpty) { val task = workList.dequeue() - info("Now handling: "+task.problem) - - val alternatives = rules.flatMap(_.isApplicable(task)) - - alternatives.find(_.isSuccess) match { - case Some(ss) => - info(" => Rule "+ss.rule+" succeeded") - ss.succeeded() - case None => - info(" => Possible Next Steps:") - alternatives.foreach(t => info(" - "+t.description)) - workList ++= alternatives.flatMap(_.subTasks) - } + println("Running "+task+"...") + val subtasks = task.run + println(subtasks) - // We are stuck - if (alternatives.isEmpty) { - val sol = Solution.choose(task.problem) - warning(" => I give up: "+task.problem+" ⊢ "+sol) - onTaskSucceeded(task, sol) - } + workList ++= subtasks } @@ -68,15 +54,19 @@ class Synthesizer(val r: Reporter, val solvers: List[Solver], generateDerivation def onTaskSucceeded(task: Task, solution: Solution) { info(" => Solved "+task.problem+" ⊢ "+solution) + if (generateDerivationTrees) { derivationTree.recordSolutionFor(task, solution) } - if (task.parent eq null) { - info(" SUCCESS!") - this.solution = Some(solution) - } else { - task.parent.subSucceeded(task.problem, solution) + task match { + case rt: RootTask => + info(" SUCCESS!") + this.solution = Some(solution) + case d: ApplyRuleTask => + d.parent.succeeded(solution) + case s: SimpleTask => + s.parent.subSucceeded(task.problem, solution) } } @@ -93,12 +83,13 @@ class Synthesizer(val r: Reporter, val solvers: List[Solver], generateDerivation (None, Map()) } + val rules = Rules.all(this) + import purescala.Trees._ def synthesizeAll(program: Program): Map[Choose, Solution] = { solvers.foreach(_.setProgram(program)) - val rules = Rules.all(this) def noop(u:Expr, u2: Expr) = u diff --git a/src/main/scala/leon/synthesis/Task.scala b/src/main/scala/leon/synthesis/Task.scala index d73a7f58a93b47e7663bbd46b85ae698845c7450..d971b8ac00140abeed0ba8296f0b92db8474cdae 100644 --- a/src/main/scala/leon/synthesis/Task.scala +++ b/src/main/scala/leon/synthesis/Task.scala @@ -1,13 +1,14 @@ package leon package synthesis -class Task(val synth: Synthesizer, - val parent: DecomposedTask, +abstract class Task(val synth: Synthesizer, + val parent: Task, val problem: Problem, - val score: Score) extends Ordered[Task] { + val priority: Priority) extends Ordered[Task] { - def compare(that: Task) = this.score - that.score + def compare(that: Task) = this.priority - that.priority + /* def decompose(rule: Rule, subProblems: List[Problem], onSuccess: List[Solution] => Solution, score: Score): DecomposedTask = { new DecomposedTask(this.synth, this.parent, this.problem, score, rule, subProblems, onSuccess) } @@ -15,31 +16,46 @@ class Task(val synth: Synthesizer, def solveUsing(rule: Rule, onSuccess: => Solution, score: Score): DecomposedTask = { new DecomposedTask(this.synth, this.parent, this.problem, 1000, rule, Nil, { case _ => onSuccess }) } + */ - override def toString = " Task("+score+"): " +problem + def run: List[Task] + + override def toString = " Task("+priority+"): " +problem } -class DecomposedTask(synth: Synthesizer, - parent: DecomposedTask, - problem: Problem, - score: Score, - val rule: Rule, - val subProblems: List[Problem], - val onSuccess: List[Solution] => Solution) extends Task(synth, parent, problem, score) { +class SimpleTask(synth: Synthesizer, + override val parent: ApplyRuleTask, + problem: Problem, + priority: Priority) extends Task(synth, parent, problem, priority) { - def subTasks = subProblems.map(new Task(synth, this, _, score)) + def succeeded(solution: Solution) { + synth.onTaskSucceeded(this, solution) + } - var subSolutions = Map[Problem, Solution]() + def run: List[Task] = { + synth.rules.map(r => new ApplyRuleTask(synth, this, problem, priority, r)) + } - def isSuccess = subProblems.isEmpty + var failed = Set[Rule]() + def notifyInapplicable(r: Rule) = { + failed += r + if (failed.size == synth.rules.size) { + synth.onTaskSucceeded(this, Solution.choose(problem)) + } + } +} - def succeeded() { - assert(isSuccess) +class RootTask(synth: Synthesizer, problem: Problem) extends SimpleTask(synth, null, problem, 0) - val solution = onSuccess(Nil) +class ApplyRuleTask(synth: Synthesizer, + override val parent: SimpleTask, + problem: Problem, + priority: Priority, + val rule: Rule) extends Task(synth, parent, problem, priority) { - synth.onTaskSucceeded(this, solution) - } + var subProblems: List[Problem] = _ + var onSuccess: List[Solution] => Solution = _ + var subSolutions : Map[Problem, Solution] = _ def subSucceeded(p: Problem, s: Solution) { assert(subProblems contains p, "Problem "+p+" is unknown to me ?!?") @@ -49,14 +65,30 @@ class DecomposedTask(synth: Synthesizer, if (subSolutions.size == subProblems.size) { - val solution = onSuccess(subProblems map subSolutions) + val solution = onSuccess(subProblems map subSolutions) synth.onTaskSucceeded(this, solution) } } } - def description = "by "+rule.name+"("+score+"): "+problem +" ⟿ "+subProblems.mkString(" ; ") -} + def run: List[Task] = { + rule.applyOn(this) match { + case RuleSuccess(solution) => + // Solved + synth.onTaskSucceeded(this, solution) + Nil + case RuleDecomposed(subProblems, onSuccess) => + this.subProblems = subProblems + this.onSuccess = onSuccess + this.subSolutions = Map() + + subProblems.map(new SimpleTask(synth, this, _, 42)) + case RuleInapplicable => + parent.notifyInapplicable(rule) + Nil + } + } -class RootTask(synth: Synthesizer, p: Problem) extends Task(synth, null, p, 0) + override def toString = "Trying "+rule+" on "+problem +} diff --git a/src/main/scala/leon/synthesis/package.scala b/src/main/scala/leon/synthesis/package.scala index 30c04b2f5f3eaaabcde5f3765c7000597fccbd3e..fbef6f0ea621521aaec07596f274740c78dda956 100644 --- a/src/main/scala/leon/synthesis/package.scala +++ b/src/main/scala/leon/synthesis/package.scala @@ -2,4 +2,5 @@ package leon package object synthesis { type Score = Int + type Priority = Int }