diff --git a/src/main/scala/leon/purescala/TreeOps.scala b/src/main/scala/leon/purescala/TreeOps.scala index dc2352d4adeda982c03b73592265e51970435f0b..91999a0143ca394f3047a48201fda2c0eb9498bb 100644 --- a/src/main/scala/leon/purescala/TreeOps.scala +++ b/src/main/scala/leon/purescala/TreeOps.scala @@ -1078,18 +1078,22 @@ object TreeOps { case IfExpr(cond, then, elze) => val TopLevelOrs(orcases) = toDNF(cond) - if (!orcases.tail.isEmpty) { - pre(IfExpr(orcases.head, then, IfExpr(Or(orcases.tail), then, elze))) - } else { - val TopLevelAnds(andcases) = orcases.head + if (orcases.exists{ case TopLevelAnds(ands) => ands.exists(_.isInstanceOf[CaseClassInstanceOf]) } ) { + if (!orcases.tail.isEmpty) { + pre(IfExpr(orcases.head, then, IfExpr(Or(orcases.tail), then, elze))) + } else { + val TopLevelAnds(andcases) = orcases.head - val (andis, andnotis) = andcases.partition(_.isInstanceOf[CaseClassInstanceOf]) + val (andis, andnotis) = andcases.partition(_.isInstanceOf[CaseClassInstanceOf]) - if (andis.isEmpty || andnotis.isEmpty) { - e - } else { - IfExpr(And(andis), IfExpr(And(andnotis), then, elze), elze) + if (andis.isEmpty || andnotis.isEmpty) { + e + } else { + IfExpr(And(andis), IfExpr(And(andnotis), then, elze), elze) + } } + } else { + e } case _ => e diff --git a/src/main/scala/leon/purescala/TypeTrees.scala b/src/main/scala/leon/purescala/TypeTrees.scala index 01aea89f986c7263cb312763864006714581057c..03e29dfcdaf39353d9458daed39a130e0b348c86 100644 --- a/src/main/scala/leon/purescala/TypeTrees.scala +++ b/src/main/scala/leon/purescala/TypeTrees.scala @@ -87,6 +87,10 @@ object TypeTrees { case _ => None } + def isSubtypeOf(t1: TypeTree, t2: TypeTree): Boolean = { + leastUpperBound(t1, t2) == Some(t2) + } + // returns the number of distinct values that inhabit a type sealed abstract class TypeSize extends Serializable case class FiniteSize(size: Int) extends TypeSize diff --git a/src/main/scala/leon/synthesis/Complexity.scala b/src/main/scala/leon/synthesis/Complexity.scala index c12f3c3f9b94403a7a739f02c1d6b29e68f921eb..2f09637d4738ad3b6eea8bc632487e96b561cec1 100644 --- a/src/main/scala/leon/synthesis/Complexity.scala +++ b/src/main/scala/leon/synthesis/Complexity.scala @@ -16,10 +16,10 @@ abstract class AbsSolComplexity extends Complexity[AbsSolComplexity] { case class SolComplexity(s: Solution) extends AbsSolComplexity { lazy val value = { - val chooses = collectChooses(s.term) + val chooses = collectChooses(s.toExpr) val chooseCost = chooses.foldLeft(0)((i, c) => i + (1000 * math.pow(2, c.vars.size).toInt + formulaSize(c.pred))) - formulaSize(s.pre) + formulaSize(s.term) + chooseCost + formulaSize(s.toExpr) + chooseCost } } diff --git a/src/main/scala/leon/synthesis/Heuristics.scala b/src/main/scala/leon/synthesis/Heuristics.scala index ad716afe102a94e38eec7eb23c58886e3f8517d6..21c0a408c40d78b575f70483a84ed325275c4674 100644 --- a/src/main/scala/leon/synthesis/Heuristics.scala +++ b/src/main/scala/leon/synthesis/Heuristics.scala @@ -10,7 +10,6 @@ import purescala.Definitions._ object Heuristics { def all = Set[Synthesizer => Rule]( - new OptimisticGround(_), new IntInduction(_), new OptimisticInjection(_) ) @@ -22,62 +21,6 @@ trait Heuristic { override def toString = "H: "+name } -class OptimisticGround(synth: Synthesizer) extends Rule("Optimistic Ground", synth, 90) with Heuristic { - def applyOn(task: Task): RuleResult = { - val p = task.problem - - if (!p.as.isEmpty && !p.xs.isEmpty) { - val xss = p.xs.toSet - val ass = p.as.toSet - - val tpe = TupleType(p.xs.map(_.getType)) - - var i = 0; - var maxTries = 3; - - var result: Option[RuleResult] = None - var predicates: Seq[Expr] = Seq() - - while (result.isEmpty && i < maxTries) { - val phi = And(p.phi +: predicates) - synth.solver.solveSAT(phi) match { - case (Some(true), satModel) => - val satXsModel = satModel.filterKeys(xss) - - val newPhi = valuateWithModelIn(phi, xss, satModel) - - synth.solver.solveSAT(Not(newPhi)) match { - case (Some(true), invalidModel) => - // Found as such as the xs break, refine predicates - predicates = valuateWithModelIn(phi, ass, invalidModel) +: predicates - - case (Some(false), _) => - result = Some(RuleSuccess(Solution(BooleanLiteral(true), Tuple(p.xs.map(valuateWithModel(satModel))).setType(tpe)))) - - case _ => - result = Some(RuleInapplicable) - } - - case (Some(false), _) => - if (predicates.isEmpty) { - result = Some(RuleSuccess(Solution(BooleanLiteral(false), Error(p.phi+" is UNSAT!").setType(tpe)))) - } else { - result = Some(RuleInapplicable) - } - case _ => - result = Some(RuleInapplicable) - } - - i += 1 - } - - result.getOrElse(RuleInapplicable) - } else { - RuleInapplicable - } - } -} - class IntInduction(synth: Synthesizer) extends Rule("Int Induction", synth, 80) with Heuristic { def applyOn(task: Task): RuleResult = { @@ -103,8 +46,16 @@ class IntInduction(synth: Synthesizer) extends Rule("Int Induction", synth, 80) val onSuccess: List[Solution] => Solution = { case List(base, gt, lt) => + val preIn = Or(Seq(And(Equals(Variable(inductOn), IntLiteral(0)), base.pre), + And(GreaterThan(Variable(inductOn), IntLiteral(0)), gt.pre), + And(LessThan(Variable(inductOn), IntLiteral(0)), lt.pre))) + val preOut = subst(inductOn -> Variable(origId), preIn) + val newFun = new FunDef(FreshIdentifier("rec", true), tpe, Seq(VarDecl(inductOn, inductOn.getType))) - newFun.body = Some( + + newFun.precondition = Some(preIn) + + newFun.body = Some( IfExpr(Equals(Variable(inductOn), IntLiteral(0)), base.toExpr, IfExpr(GreaterThan(Variable(inductOn), IntLiteral(0)), @@ -112,7 +63,8 @@ class IntInduction(synth: Synthesizer) extends Rule("Int Induction", synth, 80) , LetTuple(postXs, FunctionInvocation(newFun, Seq(Plus(Variable(inductOn), IntLiteral(1)))), lt.toExpr))) ) - Solution(BooleanLiteral(true), LetDef(newFun, FunctionInvocation(newFun, Seq(Variable(origId))))) + + Solution(preOut, base.defs++gt.defs++lt.defs+newFun, FunctionInvocation(newFun, Seq(Variable(origId)))) case _ => Solution.none } @@ -195,3 +147,4 @@ class SelectiveInlining(synth: Synthesizer) extends Rule("Sel. Inlining", synth, } } } + diff --git a/src/main/scala/leon/synthesis/Rules.scala b/src/main/scala/leon/synthesis/Rules.scala index fc711b1e717eb570da1e25023e9db4cac55deb03..af3e7b8053f8c2f47b52a17f517f31dfe3be341c 100644 --- a/src/main/scala/leon/synthesis/Rules.scala +++ b/src/main/scala/leon/synthesis/Rules.scala @@ -7,6 +7,7 @@ import purescala.Trees._ import purescala.Extractors._ import purescala.TreeOps._ import purescala.TypeTrees._ +import purescala.Definitions._ object Rules { def all = Set[Synthesizer => Rule]( @@ -18,6 +19,8 @@ object Rules { new CaseSplit(_), new UnusedInput(_), new UnconstrainedOutput(_), + new OptimisticGround(_), + new CEGIS(_), new Assert(_) ) } @@ -42,7 +45,7 @@ abstract class Rule(val name: String, val synth: Synthesizer, val priority: Prio def substAll(what: Map[Identifier, Expr], in: Expr): Expr = replace(what.map(w => Variable(w._1) -> w._2), in) val forward: List[Solution] => Solution = { - case List(s) => Solution(s.pre, s.term) + case List(s) => Solution(s.pre, s.defs, s.term) case _ => Solution.none } @@ -72,11 +75,11 @@ class OnePoint(synth: Synthesizer) extends Rule("One-point", synth, 300) { val newProblem = Problem(p.as, p.c, subst(x -> e, And(others)), oxs) val onSuccess: List[Solution] => Solution = { - case List(Solution(pre, term)) => + case List(Solution(pre, defs, term)) => if (oxs.isEmpty) { - Solution(pre, Tuple(e :: Nil)) + Solution(pre, defs, Tuple(e :: Nil)) } else { - Solution(pre, LetTuple(oxs, term, subst(x -> e, Tuple(p.xs.map(Variable(_)))))) + Solution(pre, defs, LetTuple(oxs, term, subst(x -> e, Tuple(p.xs.map(Variable(_)))))) } case _ => Solution.none } @@ -98,9 +101,9 @@ class Ground(synth: Synthesizer) extends Rule("Ground", synth, 500) { synth.solver.solveSAT(p.phi) match { case (Some(true), model) => - RuleSuccess(Solution(BooleanLiteral(true), Tuple(p.xs.map(valuateWithModel(model))).setType(tpe))) + RuleSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(model))).setType(tpe))) case (Some(false), model) => - RuleSuccess(Solution(BooleanLiteral(false), Error(p.phi+" is UNSAT!").setType(tpe))) + RuleSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe))) case _ => RuleInapplicable } @@ -119,7 +122,7 @@ class CaseSplit(synth: Synthesizer) extends Rule("Case-Split", synth, 200) { val sub2 = Problem(p.as, p.c, o2, p.xs) val onSuccess: List[Solution] => Solution = { - case List(Solution(p1, t1), Solution(p2, t2)) => Solution(Or(p1, p2), IfExpr(p1, t1, t2)) + case List(Solution(p1, d1, t1), Solution(p2, d2, t2)) => Solution(Or(p1, p2), d1++d2, IfExpr(p1, t1, t2)) case _ => Solution.none } @@ -142,7 +145,7 @@ class Assert(synth: Synthesizer) extends Rule("Assert", synth, 200) { if (!exprsA.isEmpty) { if (others.isEmpty) { - RuleSuccess(Solution(And(exprsA), Tuple(p.xs.map(id => simplestValue(Variable(id)))))) + RuleSuccess(Solution(And(exprsA), Set(), Tuple(p.xs.map(id => simplestValue(Variable(id)))))) } else { val sub = p.copy(c = And(p.c +: exprsA), phi = And(others)) @@ -160,7 +163,7 @@ class Assert(synth: Synthesizer) extends Rule("Assert", synth, 200) { class UnusedInput(synth: Synthesizer) extends Rule("UnusedInput", synth, 100) { def applyOn(task: Task): RuleResult = { val p = task.problem - val unused = p.as.toSet -- variablesOf(p.phi) + val unused = p.as.toSet -- variablesOf(p.phi) -- variablesOf(p.c) if (!unused.isEmpty) { val sub = p.copy(as = p.as.filterNot(unused)) @@ -182,7 +185,7 @@ class UnconstrainedOutput(synth: Synthesizer) extends Rule("Unconstr.Output", sy val onSuccess: List[Solution] => Solution = { case List(s) => - Solution(s.pre, LetTuple(sub.xs, s.term, Tuple(p.xs.map(id => if (unconstr(id)) simplestValue(Variable(id)) else Variable(id))))) + Solution(s.pre, s.defs, LetTuple(sub.xs, s.term, Tuple(p.xs.map(id => if (unconstr(id)) simplestValue(Variable(id)) else Variable(id))))) case _ => Solution.none } @@ -242,7 +245,7 @@ object Unification { if (isImpossible) { val tpe = TupleType(p.xs.map(_.getType)) - RuleSuccess(Solution(BooleanLiteral(false), Error(p.phi+" is UNSAT!").setType(tpe))) + RuleSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe))) } else { RuleInapplicable } @@ -278,3 +281,234 @@ class ADTDual(synth: Synthesizer) extends Rule("ADTDual", synth, 200) { } } +class CEGIS(synth: Synthesizer) extends Rule("CEGIS", synth, 50) { + def applyOn(task: Task): RuleResult = { + val p = task.problem + + case class Generator(tpe: TypeTree, altBuilder: () => List[(Expr, Set[Identifier])]); + + var generators = Map[TypeTree, Generator]() + def getGenerator(t: TypeTree): Generator = generators.get(t) match { + case Some(g) => g + case None => + val alternatives: () => List[(Expr, Set[Identifier])] = t match { + case BooleanType => + { () => List((BooleanLiteral(true), Set()), (BooleanLiteral(false), Set())) } + + case Int32Type => + { () => List((IntLiteral(0), Set()), (IntLiteral(1), Set())) } + + case TupleType(tps) => + { () => + val ids = tps.map(t => FreshIdentifier("t", true).setType(t)) + List((Tuple(ids.map(Variable(_))), ids.toSet)) + } + + case CaseClassType(cd) => + { () => + val ids = cd.fieldsIds.map(i => FreshIdentifier("c", true).setType(i.getType)) + List((CaseClass(cd, ids.map(Variable(_))), ids.toSet)) + } + + case AbstractClassType(cd) => + { () => + val alts: Seq[(Expr, Set[Identifier])] = cd.knownDescendents.flatMap(i => i match { + case acd: AbstractClassDef => + synth.reporter.error("Unnexpected abstract class in descendants!") + None + case cd: CaseClassDef => + val ids = cd.fieldsIds.map(i => FreshIdentifier("c", true).setType(i.getType)) + Some((CaseClass(cd, ids.map(Variable(_))), ids.toSet)) + }) + alts.toList + } + + case _ => + synth.reporter.error("Can't construct generator. Unsupported type: "+t+"["+t.getClass+"]"); + { () => Nil } + } + val g = Generator(t, alternatives) + generators += t -> g + g + } + + def inputAlternatives(t: TypeTree): List[(Expr, Set[Identifier])] = { + p.as.filter(a => isSubtypeOf(a.getType, t)).map(id => (Variable(id) : Expr, Set[Identifier]())) + } + + case class TentativeFormula(phi: Expr, + program: Expr, + mappings: Map[Identifier, (Identifier, Expr)], + recTerms: Map[Identifier, Set[Identifier]]) { + def unroll: TentativeFormula = { + var newProgram = List[Expr]() + var newRecTerms = Map[Identifier, Set[Identifier]]() + var newMappings = Map[Identifier, (Identifier, Expr)]() + + for ((_, recIds) <- recTerms; recId <- recIds) { + val gen = getGenerator(recId.getType) + val alts = gen.altBuilder() ::: inputAlternatives(recId.getType) + + val altsWithBranches = alts.map(alt => FreshIdentifier("b", true).setType(BooleanType) -> alt) + + val bvs = altsWithBranches.map(alt => Variable(alt._1)) + val distinct = if (bvs.size > 1) { + (for (i <- (1 to bvs.size-1); j <- 0 to i-1) yield { + Or(Not(bvs(i)), Not(bvs(j))) + }).toList + } else { + List(BooleanLiteral(true)) + } + val pre = And(Or(bvs) :: distinct) // (b1 OR b2) AND (Not(b1) OR Not(b2)) + val cases = for((bid, (ex, rec)) <- altsWithBranches.toList) yield { // b1 => E(gen1, gen2) [b1 -> {gen1, gen2}] + if (!rec.isEmpty) { + newRecTerms += bid -> rec + } + newMappings += bid -> (recId -> ex) + + Implies(Variable(bid), Equals(Variable(recId), ex)) + } + + newProgram = newProgram ::: pre :: cases + } + + TentativeFormula(phi, And(program :: newProgram), mappings ++ newMappings, newRecTerms) + } + + def bounds = recTerms.keySet.map(id => Not(Variable(id))).toList + def bss = mappings.keySet + + def entireFormula = And(phi :: program :: bounds) + } + + var result: Option[RuleResult] = None + + var ass = p.as.toSet + var xss = p.xs.toSet + + var lastF = TentativeFormula(Implies(p.c, p.phi), BooleanLiteral(true), Map(), Map() ++ p.xs.map(x => x -> Set(x))) + var currentF = lastF.unroll + var unrolings = 0 + val maxUnrolings = 2 + do { + //println("Was: "+lastF.entireFormula) + //println("Now Trying : "+currentF.entireFormula) + + val tpe = TupleType(p.xs.map(_.getType)) + val bss = currentF.bss + + var predicates: Seq[Expr] = Seq() + var continue = true + + while (result.isEmpty && continue) { + val basePhi = currentF.entireFormula + val constrainedPhi = And(basePhi +: predicates) + //println("-"*80) + //println("To satisfy: "+constrainedPhi) + synth.solver.solveSAT(constrainedPhi) match { + case (Some(true), satModel) => + //println("Found candidate!: "+satModel.filterKeys(bss)) + + //println("Corresponding program: "+simplifyTautologies(synth.solver)(valuateWithModelIn(currentF.program, bss, satModel))) + val fixedBss = And(bss.map(b => Equals(Variable(b), satModel(b))).toSeq) + //println("Phi with fixed sat bss: "+fixedBss) + + val counterPhi = Implies(And(fixedBss, currentF.program), currentF.phi) + //println("Formula to validate: "+counterPhi) + + synth.solver.solveSAT(Not(counterPhi)) match { + case (Some(true), invalidModel) => + // Found as such as the xs break, refine predicates + //println("Found counter EX: "+invalidModel) + predicates = Not(And(bss.map(b => Equals(Variable(b), satModel(b))).toSeq)) +: predicates + //println("Let's avoid this case: "+bss.map(b => Equals(Variable(b), satModel(b))).mkString(" ")) + + case (Some(false), _) => + //println("Sat model: "+satModel.toSeq.sortBy(_._1.toString).map{ case (id, v) => id+" -> "+v }.mkString(", ")) + var mapping = currentF.mappings.filterKeys(satModel.mapValues(_ == BooleanLiteral(true))).values.toMap + + //println("Mapping: "+mapping) + + // Resolve mapping + for ((c, e) <- mapping) { + mapping += c -> substAll(mapping, e) + } + + result = Some(RuleSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(mapping))).setType(tpe)))) + + case _ => + } + + case (Some(false), _) => + //println("%%%% UNSAT") + continue = false + case _ => + //println("%%%% WOOPS") + continue = false + } + } + + lastF = currentF + currentF = currentF.unroll + unrolings += 1 + } while(unrolings < maxUnrolings && lastF != currentF && result.isEmpty) + + result.getOrElse(RuleInapplicable) + } +} + +class OptimisticGround(synth: Synthesizer) extends Rule("Optimistic Ground", synth, 90) { + def applyOn(task: Task): RuleResult = { + val p = task.problem + + if (!p.as.isEmpty && !p.xs.isEmpty) { + val xss = p.xs.toSet + val ass = p.as.toSet + + val tpe = TupleType(p.xs.map(_.getType)) + + var i = 0; + var maxTries = 3; + + var result: Option[RuleResult] = None + var predicates: Seq[Expr] = Seq() + + while (result.isEmpty && i < maxTries) { + val phi = And(p.phi +: predicates) + synth.solver.solveSAT(phi) match { + case (Some(true), satModel) => + val satXsModel = satModel.filterKeys(xss) + + val newPhi = valuateWithModelIn(phi, xss, satModel) + + synth.solver.solveSAT(Not(newPhi)) match { + case (Some(true), invalidModel) => + // Found as such as the xs break, refine predicates + predicates = valuateWithModelIn(phi, ass, invalidModel) +: predicates + + case (Some(false), _) => + result = Some(RuleSuccess(Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(valuateWithModel(satModel))).setType(tpe)))) + + case _ => + result = Some(RuleInapplicable) + } + + case (Some(false), _) => + if (predicates.isEmpty) { + result = Some(RuleSuccess(Solution(BooleanLiteral(false), Set(), Error(p.phi+" is UNSAT!").setType(tpe)))) + } else { + result = Some(RuleInapplicable) + } + case _ => + result = Some(RuleInapplicable) + } + + i += 1 + } + + result.getOrElse(RuleInapplicable) + } else { + RuleInapplicable + } + } +} diff --git a/src/main/scala/leon/synthesis/Solution.scala b/src/main/scala/leon/synthesis/Solution.scala index a1ecacaa28368eb211db8e0a1edd50095c659bd4..cf2561de50e05f13613afedbf89a193b03092e34 100644 --- a/src/main/scala/leon/synthesis/Solution.scala +++ b/src/main/scala/leon/synthesis/Solution.scala @@ -2,23 +2,26 @@ package leon package synthesis import leon.purescala.Trees._ +import leon.purescala.Definitions._ import leon.purescala.TreeOps._ // Defines a synthesis solution of the form: // ⟨ P | T ⟩ -class Solution(val pre: Expr, val term: Expr) { - override def toString = "⟨ "+pre+" | "+term+" ⟩" +class Solution(val pre: Expr, val defs: Set[FunDef], val term: Expr) { + override def toString = "⟨ "+pre+" | "+defs.mkString(" ")+" "+term+" ⟩" lazy val complexity: AbsSolComplexity = new SolComplexity(this) def toExpr = { - if (pre == BooleanLiteral(true)) { + val result = if (pre == BooleanLiteral(true)) { term } else if (pre == BooleanLiteral(false)) { Error("Impossible program").setType(term.getType) } else { IfExpr(pre, term, Error("Precondition failed").setType(term.getType)) } + + defs.foldLeft(result){ case (t, fd) => LetDef(fd, t) } } } @@ -26,19 +29,19 @@ object Solution { def simplify(e: Expr) = simplifyLets(e) - def apply(pre: Expr, term: Expr) = { - new Solution(simplify(pre), simplify(term)) + def apply(pre: Expr, defs: Set[FunDef], term: Expr) = { + new Solution(simplify(pre), defs, simplify(term)) } - def unapply(s: Solution): Option[(Expr, Expr)] = if (s eq null) None else Some((s.pre, s.term)) + def unapply(s: Solution): Option[(Expr, Set[FunDef], Expr)] = if (s eq null) None else Some((s.pre, s.defs, s.term)) def choose(p: Problem): Solution = { - new Solution(BooleanLiteral(true), Choose(p.xs, p.phi)) + new Solution(BooleanLiteral(true), Set(), Choose(p.xs, p.phi)) } // Generate the simplest, wrongest solution, used for complexity lowerbound def basic(p: Problem): Solution = { - new Solution(BooleanLiteral(true), Tuple(p.xs.map(id => simplestValue(id.getType)))) + new Solution(BooleanLiteral(true), Set(), Tuple(p.xs.map(id => simplestValue(id.getType)))) } def none: Solution = { diff --git a/src/main/scala/leon/synthesis/Synthesizer.scala b/src/main/scala/leon/synthesis/Synthesizer.scala index ba8ec79c42f157b67381e56eb16611bad3af23e8..a811fe6b72c00cd89e068b086d76cb645632d336 100644 --- a/src/main/scala/leon/synthesis/Synthesizer.scala +++ b/src/main/scala/leon/synthesis/Synthesizer.scala @@ -42,9 +42,11 @@ class Synthesizer(val reporter: Reporter, val ts = System.currentTimeMillis + def currentDurationMs = System.currentTimeMillis-ts + def timeoutExpired(): Boolean = { timeoutMs match { - case Some(t) if (System.currentTimeMillis-ts)/1000 > t => true + case Some(t) if currentDurationMs/1000 > t => true case _ => false } } @@ -64,6 +66,8 @@ class Synthesizer(val reporter: Reporter, } } + info("Finished in "+currentDurationMs+"ms") + if (generateDerivationTrees) { val deriv = new DerivationTree(rootTask) deriv.toDotFile("derivation"+derivationCounter+".dot") diff --git a/src/main/scala/leon/synthesis/Task.scala b/src/main/scala/leon/synthesis/Task.scala index ed3bd980d09ba83c4bb668bf61bfbdf2a3b41622..1176c19a7ebe56facba15555817f6804e719d3c2 100644 --- a/src/main/scala/leon/synthesis/Task.scala +++ b/src/main/scala/leon/synthesis/Task.scala @@ -56,6 +56,7 @@ class Task(synth: Synthesizer, steps = new TaskStep(newProblems) :: steps } else { solution = Some(onSuccess(solutions)) + parent.partlySolvedBy(this, solution.get) } } @@ -80,8 +81,8 @@ class Task(synth: Synthesizer, parent.partlySolvedBy(this, solution) val prefix = "[%-20s] ".format(Option(rule).map(_.toString).getOrElse("root")) - println(prefix+"Got: "+problem) - println(prefix+"Solved with: "+solution) + synth.reporter.info(prefix+"Got: "+problem) + synth.reporter.info(prefix+"Solved with: "+solution) Nil case RuleMultiSteps(subProblems, interSteps, onSuccess) => @@ -96,10 +97,10 @@ class Task(synth: Synthesizer, val simplestSolution = onSuccess(simplestSubSolutions) minComplexity = new FixedSolComplexity(parent.minComplexity.value + simplestSolution.complexity.value) val prefix = "[%-20s] ".format(Option(rule).map(_.toString).getOrElse("root")) - println(prefix+"Got: "+problem) - println(prefix+"Decomposed into:") + synth.reporter.info(prefix+"Got: "+problem) + synth.reporter.info(prefix+"Decomposed into:") for(p <- subProblems) { - println(prefix+" - "+p) + synth.reporter.info(prefix+" - "+p) } subProblems @@ -121,8 +122,10 @@ class RootTask(synth: Synthesizer, problem: Problem) extends Task(synth, null, p } override def partlySolvedBy(t: Task, s: Solution) { - solution = Some(s) - solver = Some(t) + if (isBetterSolutionThan(s, solution)) { + solution = Some(s) + solver = Some(t) + } } override def unsolvedBy(t: Task) { diff --git a/testcases/synthesis/Injection.scala b/testcases/synthesis/Injection.scala new file mode 100644 index 0000000000000000000000000000000000000000..32c91ccad03674a50cd5cfb38f745fb091a046e1 --- /dev/null +++ b/testcases/synthesis/Injection.scala @@ -0,0 +1,24 @@ +import scala.collection.immutable.Set +import leon.Annotations._ +import leon.Utils._ + +object Injection { + sealed abstract class List + case class Cons(head: Int, tail: List) extends List + case class Nil() extends List + + // proved with unrolling=0 + def size(l: List) : Int = (l match { + case Nil() => 0 + case Cons(_, t) => 1 + size(t) + }) ensuring(res => res >= 0) + + + def listOfSizeSynth(in: List, i: Int) = choose{out: List => (size(out) == i) && (size(in) == i) } + def listOfSizeSynth2(i: Int) = choose{out: List => size(out) == i } + + def subProblem(in: List, i: Int) = choose{out: List => (size(out) == i) && (size(in)+1 == i) } + def simple(in: List) = choose{out: List => size(out) == size(in) } + + def invalidInjection(in: List, i: Int) = choose{out: List => (size(out) == i) && (size(in) == i) && in != out } +} diff --git a/testcases/synthesis/SimplestCegis.scala b/testcases/synthesis/SimplestCegis.scala new file mode 100644 index 0000000000000000000000000000000000000000..d0c5c44d8555c4e50c463df5f921762fe9242b8f --- /dev/null +++ b/testcases/synthesis/SimplestCegis.scala @@ -0,0 +1,17 @@ +import scala.collection.immutable.Set +import leon.Annotations._ +import leon.Utils._ + +object Injection { + sealed abstract class List + case class Cons(tail: List) extends List + case class Nil() extends List + + // proved with unrolling=0 + def size(l: List) : Int = (l match { + case Nil() => 0 + case Cons(t) => 1 + size(t) + }) ensuring(res => res >= 0) + + def simple(in: List) = choose{out: List => size(out) == size(in) } +}