diff --git a/src/main/scala/lesynth/ExampleRunner.scala b/src/main/scala/lesynth/ExampleRunner.scala deleted file mode 100644 index 3d82d3c037faa8cc2cfbe1531ffaf83c73d40186..0000000000000000000000000000000000000000 --- a/src/main/scala/lesynth/ExampleRunner.scala +++ /dev/null @@ -1,134 +0,0 @@ -package lesynth - -import scala.collection.mutable.{ Map => MutableMap } -import scala.collection.mutable.{ Set => MutableSet } -import scala.collection.mutable.{ LinkedList => MutableList } - -import leon.{ Main => LeonMain, DefaultReporter, Settings, LeonContext } -import leon.evaluators._ -import leon.evaluators.EvaluationResults._ -import leon.solvers.Solver -import leon.solvers.z3.{ FairZ3Solver } -import leon.verification.AnalysisPhase -import leon.purescala.TypeTrees.{ TypeTree => LeonType, _ } -import leon.purescala.Trees._ -import leon.purescala.Definitions.{ FunDef, VarDecl, Program, ObjectDef } -import leon.purescala.Common.{ Identifier, FreshIdentifier } -import leon.purescala.TreeOps - -import insynth.util.logging.HasLogger - -import EvaluationResults._ - -class ExampleRunner(program: Program, maxSteps: Int = 2000) extends HasLogger { - - import TreeOps._ - - var counterExamples = MutableList[Map[Identifier, Expr]]() - - val leonEmptyContext = LeonContext() - - // its okay to construct just one, prog is not use in the default evaluator - lazy val defaultEvaluator = { - val ev = new DefaultEvaluator(leonEmptyContext, program) - ev.maxSteps = maxSteps - ev - } - - def evaluate(expr: Expr, mapping: Map[Identifier, Expr]) = { - fine("to evaluate: " + expr + " for mapping: " + mapping) - defaultEvaluator.eval(expr, mapping) match { - case Successful(BooleanLiteral(true)) => - fine("Eval succeded: EvaluationSuccessful(true)") - true - case m => - fine("Eval failed: " + m) - false - } - } - - def evaluateToResult(expr: Expr, mapping: Map[Identifier, Expr]) = { - fine("to evaluate: " + expr + " for mapping: " + mapping) - defaultEvaluator.eval(expr, mapping) - } - - /** filter counterexamples according to an expression (precondition) */ - def filter(prec: Expr) = { - entering("filter(" + prec + ")") - fine("Old counterExamples.size: " + counterExamples.size) - counterExamples = counterExamples filter { - evaluate(prec, _) - } - fine("New counterExamples.size: " + counterExamples.size) - } - - /** count how many examples pass */ - def countPassed(expressionToCheck: Expr) = { - // TODO body dont have set type in synthesizer -// val expressionToCheck = -// //Globals.bodyAndPostPlug(exp) -// { -// val resFresh = FreshIdentifier("result", true).setType(body.getType) -// Let( -// resFresh, body, -// replace(Map(ResultVariable() -> Variable(resFresh)), matchToIfThenElse(holeFunDef.getPostcondition))) -// } - fine("expressionToCheck: " + expressionToCheck) - finest("program: " + program) - - (0 /: counterExamples) { - (res, ce) => - { - if (evaluate(expressionToCheck, ce)) res + 1 - else res - } - } - } - -// def countPassedAndTerminating(body: Expr): Int = { -// // TODO body dont have set type in synthesizer -// val expressionToCheck = -// //Globals.bodyAndPostPlug(exp) -// { -// val resFresh = FreshIdentifier("result", true).setType(body.getType) -// Let( -// resFresh, body, -// replace(Map(ResultVariable() -> Variable(resFresh)), matchToIfThenElse(holeFunDef.getPostcondition))) -// } -// fine("expressionToCheck: " + expressionToCheck) -// -// (0 /: counterExamples) { -// (res, ce) => -// { -// evaluateToResult(expressionToCheck, ce) match { -// case EvaluationSuccessful(BooleanLiteral(true)) => -// res + 1 -// case EvaluationError("Stack overflow") => -// return -counterExamples.size -// case m => -// res -// } -// } -// } -// } - - // check if this body passes all examples -// def check(body: Expr): Boolean = { -// val examplesToCheck = counterExamples -// val expressionToCheck = Globals.bodyAndPostPlug(body) -// fine("Expression to check: " + expressionToCheck) -// -// var res = true -// val iterator = counterExamples.iterator -// -// while (res && iterator.hasNext) { -// val currentExample = iterator.next -// res = evaluate(expressionToCheck, currentExample) -// -// if (!res) fine("Failed example: " + currentExample) -// } -// -// return res -// } - -} \ No newline at end of file diff --git a/src/main/scala/lesynth/SynthesisInfo.scala b/src/main/scala/lesynth/SynthesisInfo.scala index 82a5487d8972817338283af882d57c2a5236879d..e438d247173037cb93e6f170b5c54f55bf3bdd55 100644 --- a/src/main/scala/lesynth/SynthesisInfo.scala +++ b/src/main/scala/lesynth/SynthesisInfo.scala @@ -1,5 +1,7 @@ package lesynth +import leon.StopwatchCollections + /** * Contains information about the synthesis process */ @@ -7,6 +9,17 @@ class SynthesisInfo { import SynthesisInfo.Action + // synthesis information + private var _numberOfEnumeratedExpressions: Long = 0 + def numberOfEnumeratedExpressions = _numberOfEnumeratedExpressions + def numberOfEnumeratedExpressions_=(numberOfEnumeratedExpressionsIn: Long) = + _numberOfEnumeratedExpressions = numberOfEnumeratedExpressionsIn + + private var _iterations: Int = 0 + def iterations = _iterations + def iterations_=(iterationsIn: Int) = + _iterations = iterationsIn + // times private var times = new Array[Long](Action.values.size) private var startTimes = new Array[Long](Action.values.size) diff --git a/src/main/scala/lesynth/SynthesizerExamples.scala b/src/main/scala/lesynth/SynthesizerExamples.scala index 7f5cd95ac9c9a3b9f6a14b3e8f740488eedb10d3..1a3037f86d678a8bfd442125b4e208b4318b8d08 100755 --- a/src/main/scala/lesynth/SynthesizerExamples.scala +++ b/src/main/scala/lesynth/SynthesizerExamples.scala @@ -2,42 +2,45 @@ package lesynth import scala.collection.mutable.{ Map => MutableMap } import scala.collection.mutable.{ Set => MutableSet } -import scala.collection.mutable.PriorityQueue +import scala.collection.mutable.{ ArrayBuffer, PriorityQueue } + +import leon.{ Reporter, DefaultReporter, SilentReporter, LeonContext } +import leon.Main.processOptions -import leon.{Reporter, DefaultReporter, SilentReporter, LeonContext} import leon.solvers.{ Solver, TimeoutSolver } import leon.solvers.z3.{ FairZ3Solver } import leon.verification.AnalysisPhase + import leon.purescala.TypeTrees.{ TypeTree => LeonType, _ } import leon.purescala.Trees.{ Variable => LeonVariable, _ } -import leon.purescala.Definitions.{FunDef, Program} +import leon.purescala.Definitions.{ FunDef, Program } import leon.purescala.Common.{ Identifier, FreshIdentifier } import leon.purescala.TreeOps -import insynth.util.logging.HasLogger +import leon.evaluators.EvaluationResults +import leon.evaluators._ +import leon.synthesis.{ Problem, SynthesisContext } + import insynth.interfaces.Declaration import insynth.InSynth -import insynth.leon.loader.LeonLoader -import insynth.leon.LeonDeclaration -import insynth.leon.ImmediateExpression -import insynth.engine.InitialEnvironmentBuilder -import insynth.interfaces.Declaration -import insynth.engine.InitialEnvironmentBuilder -import insynth.leon.TypeTransformer +import insynth.leon.loader._ +import insynth.leon._ +import insynth.engine._ import insynth.reconstruction.Output -import leon.synthesis.{ Problem, SynthesisContext } -import leon.Main.processOptions -import leon.purescala.TypeTrees._ +import insynth.util.logging.HasLogger + +import scala.collection.mutable.{ Map => MutableMap, Set => MutableSet } +import scala.util.control.Breaks._ -import scala.collection.mutable.{Map => MutableMap} -import scala.collection.mutable.{Set => MutableSet} -import scala.util.control.Breaks.break -import scala.util.control.Breaks.breakable +import lesynth.examples._ +import lesynth.evaluation._ +import lesynth.ranking._ +import lesynth.refinement._ import SynthesisInfo._ import SynthesisInfo.Action._ -class SynthesizerForRuleExamples( +class SynthesizerForRuleExamples( // some synthesis instance information val solver: Solver, val program: Program, @@ -46,23 +49,24 @@ class SynthesizerForRuleExamples( val problem: Problem, val synthesisContext: SynthesisContext, val freshResVar: LeonVariable, + val evaluationStrategy: EvaluationStrategy, // = DefaultEvaluationStrategy(program, holeFunDef, synthesisContext.context), // number of condition expressions to try before giving up on that branch expression numberOfBooleanSnippets: Int = 5, numberOfCounterExamplesToGenerate: Int = 5, - leonTimeout: Int = 2, // seconds + leonTimeout: Int = 1, // seconds analyzeSynthesizedFunctionOnly: Boolean = false, showLeonOutput: Boolean = false, reporter: Reporter = new DefaultReporter, //examples: List[Map[Identifier, Expr]] = Nil, // we need the holeDef to know the right ids - introduceExamples: ((Seq[Identifier], LeonLoader) => List[Map[Identifier, Expr]]) = { (_, _) => Nil }, + introduceExamples: (() => List[Map[Identifier, Expr]]) = { () => Nil }, collectCounterExamplesFromLeon: Boolean = false, filterOutAlreadySeenBranchExpressions: Boolean = true, - useStringSetForFilterOutAlreadySeenBranchExpressions: Boolean = true, - numberOfTestsInIteration: Int = 50, - numberOfCheckInIteration: Int = 5 -) extends HasLogger { - + useStringSetForFilterOutAlreadySeenBranchExpressions: Boolean = true, + numberOfTestsInIteration: Int = 25, + numberOfCheckInIteration: Int = 5, + exampleRunnerSteps: Int = 4000) extends HasLogger { + val fileName: String = "noFileName" info("Synthesizer:") @@ -71,7 +75,7 @@ class SynthesizerForRuleExamples( info("numberOfCounterExamplesToGenerate: %d".format(numberOfCounterExamplesToGenerate)) info("leonTimeout: %d".format(leonTimeout)) //info("examples: " + examples.mkString(", ")) - + info("holeFunDef: %s".format(holeFunDef)) info("problem: %s".format(problem.toString)) @@ -82,17 +86,17 @@ class SynthesizerForRuleExamples( private var loader: LeonLoader = _ private var inSynth: InSynth = _ private var inSynthBoolean: InSynth = _ - private var refiner: Refiner = _ + private var refiner: Filter = _ //private var solver: Solver = _ private var ctx: LeonContext = _ private var initialPrecondition: Expr = _ - + private var variableRefiner: VariableRefiner = _ // can be used to unnecessary syntheses private var variableRefinedBranch = false private var variableRefinedCondition = true // assure initial synthesis private var booleanExpressionsSaved: Stream[Output] = _ - + private var seenBranchExpressions: Set[String] = Set.empty // flag denoting if a correct body has been synthesized @@ -109,24 +113,24 @@ class SynthesizerForRuleExamples( // filtering/ranking with examples support var exampleRunner: ExampleRunner = _ + var gatheredExamples: ArrayBuffer[Example] = _ def analyzeProgram = { - + synthInfo.start(Verification) - Globals.allSolved = Some(true) + Globals.allSolved = None import TreeOps._ - + val body = holeFunDef.getBody val theExpr = { - val resFresh = FreshIdentifier("result", true).setType(body.getType) - val bodyAndPost = - Let( - resFresh, body, - replace(Map(ResultVariable() -> LeonVariable(resFresh)), matchToIfThenElse(holeFunDef.getPostcondition)) - ) - - val withPrec = if( holeFunDef.precondition.isEmpty) { + val resFresh = FreshIdentifier("result", true).setType(body.getType) + val bodyAndPost = + Let( + resFresh, body, + replace(Map(ResultVariable() -> LeonVariable(resFresh)), matchToIfThenElse(holeFunDef.getPostcondition))) + + val withPrec = if (holeFunDef.precondition.isEmpty) { bodyAndPost } else { Implies(matchToIfThenElse(holeFunDef.precondition.get), bodyAndPost) @@ -134,7 +138,7 @@ class SynthesizerForRuleExamples( withPrec } - + val reporter = //new DefaultReporter new SilentReporter val args = @@ -142,14 +146,15 @@ class SynthesizerForRuleExamples( Array(fileName, "--timeout=" + leonTimeout, "--functions=" + holeFunDef.id.name) else Array(fileName, "--timeout=" + leonTimeout) - info("Leon context array: " + args.mkString(",")) + finest("Leon context array: " + args.mkString(",")) ctx = processOptions(reporter, args.toList) - val solver = new TimeoutSolver(new FairZ3Solver(ctx), 4000L) - //new TimeoutSolver(synthesisContext.solver.getNewSolver, 2000L) + val solver = new TimeoutSolver(new FairZ3Solver(ctx), 1000L * leonTimeout) + //new TimeoutSolver(synthesisContext.solver.getNewSolver, 2000L) solver.setProgram(program) - + + Globals.asMap = Map.empty Globals.allSolved = solver.solve(theExpr) - fine("solver said " + Globals.allSolved + " for " + theExpr) + finest("Solver said " + Globals.allSolved + " for " + theExpr) //interactivePause // measure time @@ -160,8 +165,8 @@ class SynthesizerForRuleExamples( // TODO return boolean (do not do unecessary analyze) def generateCounterexamples(program: Program, funDef: FunDef, number: Int): (Seq[Map[Identifier, Expr]], Expr) = { - fine("generate counter examples with funDef.prec= " + funDef.precondition.getOrElse(BooleanLiteral(true))) - + info("Generate counter examples with precondition " + funDef.precondition.getOrElse(BooleanLiteral(true))) + // get current precondition var precondition = funDef.precondition.getOrElse(BooleanLiteral(true)) // where we will accumulate counterexamples as sequence of maps @@ -178,7 +183,7 @@ class SynthesizerForRuleExamples( // check if solver could solved this instance if (Globals.allSolved == Some(false) && !Globals.asMap.isEmpty) { - fine("found coounterexample: " + Globals.asMap) + info("Found counterexample: " + Globals.asMap) // add current counterexample maps :+= Globals.asMap @@ -193,9 +198,9 @@ class SynthesizerForRuleExamples( ind += 1 } -// val temptime = System.currentTimeMillis - temp -// fine("Generation of counter-examples took: " + temptime) -// verTime += temptime + // val temptime = System.currentTimeMillis - temp + // fine("Generation of counter-examples took: " + temptime) + // verTime += temptime // return found counterexamples and the formed precondition (maps, precondition) @@ -203,21 +208,23 @@ class SynthesizerForRuleExamples( def getCurrentBuilder = new InitialEnvironmentBuilder(allDeclarations) - def synthesizeBranchExpressions = { - synthInfo.profile(Generation) { inSynth.getExpressions(getCurrentBuilder) } + def synthesizeBranchExpressions = { + info("Invoking synthesis for branch expressions") + synthInfo.profile(Generation) { inSynth.getExpressions(getCurrentBuilder) } } def synthesizeBooleanExpressions = { + info("Invoking synthesis for condition expressions") synthInfo.start(Generation) - if ( variableRefinedCondition ) { + if (variableRefinedCondition) { // store for later fetch (will memoize values) - booleanExpressionsSaved = - inSynthBoolean.getExpressions(getCurrentBuilder). - filterNot(expr => refiner.isAvoidable(expr.getSnippet, problem.as)) take numberOfBooleanSnippets - // reset flag - variableRefinedCondition = false + booleanExpressionsSaved = + inSynthBoolean.getExpressions(getCurrentBuilder). + filterNot(expr => refiner.isAvoidable(expr.getSnippet, problem.as)) take numberOfBooleanSnippets + // reset flag + variableRefinedCondition = false } - + synthInfo end booleanExpressionsSaved } @@ -225,7 +232,7 @@ class SynthesizerForRuleExamples( System.out.println("Press Any Key To Continue..."); new java.util.Scanner(System.in).nextLine(); } - + def getNewExampleQueue = PriorityQueue[(Expr, Int)]()( new Ordering[(Expr, Int)] { def compare(pair1: (Expr, Int), pair2: (Expr, Int)) = @@ -235,27 +242,10 @@ class SynthesizerForRuleExamples( def initialize = { hole = Hole(desiredType) - + // TODO lose this - globals are bad Globals.allSolved = None - // context needed for verification - import leon.Main._ -// val reporter = -// if (showLeonOutput) new DefaultReporter -// else new SilentReporter -// -// val args = -// if (analyzeSynthesizedFunctionOnly) -// Array(fileName, "--timeout=" + leonTimeout, "--functions=" + holeFunDef.id.name) -// else -// Array(fileName, "--timeout=" + leonTimeout) -// info("Leon context array: " + args.mkString(",")) -// ctx = processOptions(reporter, args.toList) - - //solver = //new FairZ3Solver(ctx) - // new TimeoutSolver(new FairZ3Solver(ctx), leonTimeout) - // create new insynth object loader = new LeonLoader(program, hole, problem.as, false) inSynth = new InSynth(loader, true) @@ -267,7 +257,7 @@ class SynthesizerForRuleExamples( // funDef of the hole fine("postcondition is: " + holeFunDef.getPostcondition) fine("declarations we see: " + allDeclarations.map(_.toString).mkString("\n")) - interactivePause + // interactivePause // accumulate precondition for the remaining branch to synthesize accumulatingPrecondition = holeFunDef.precondition.getOrElse(BooleanLiteral(true)) @@ -283,92 +273,10 @@ class SynthesizerForRuleExamples( loader.variableDeclarations, loader.classMap, reporter) // calculate cases that should not happen - refiner = new Refiner(program, hole, holeFunDef) - fine("Refiner initialized. Recursive call: " + refiner.recurentExpression) - - exampleRunner = new ExampleRunner(program, 4000) - exampleRunner.counterExamples ++= //examples - introduceExamples(holeFunDef.args.map(_.id), loader) - - fine("Introduced examples: " + exampleRunner.counterExamples.mkString(", ")) - } - - def countPassedExamples(snippet: Expr) = { - synthInfo.start(Action.Evaluation) - - val oldPreconditionSaved = holeFunDef.precondition - val oldBodySaved = holeFunDef.body - - // restore initial precondition - holeFunDef.precondition = Some(initialPrecondition) - - // get the whole body (if else...) - val accumulatedExpression = accumulatingExpression(snippet) - // set appropriate body to the function for the correct evaluation - holeFunDef.body = Some(accumulatedExpression) - - - import TreeOps._ - val expressionToCheck = - //Globals.bodyAndPostPlug(exp) - { - val resFresh = FreshIdentifier("result", true).setType(accumulatedExpression.getType) - Let( - resFresh, accumulatedExpression, - replace(Map(ResultVariable() -> LeonVariable(resFresh)), matchToIfThenElse(holeFunDef.getPostcondition))) - } - - finest("going to count passed for: " + holeFunDef) - finest("going to count passed for: " + expressionToCheck) - - val count = exampleRunner.countPassed(expressionToCheck) -// if (snippet.toString == "Cons(l1.head, concat(l1.tail, l2))") -// interactivePause - - holeFunDef.precondition = oldPreconditionSaved - holeFunDef.body = oldBodySaved - - synthInfo end count - } - - - def evaluateCandidate(snippet: Expr, mapping: Map[Identifier, Expr]) = { - - synthInfo.start(Action.Evaluation) + refiner = new Filter(program, holeFunDef, variableRefiner) - val oldPreconditionSaved = holeFunDef.precondition - val oldBodySaved = holeFunDef.body - - // restore initial precondition - holeFunDef.precondition = Some(initialPrecondition) - - // get the whole body (if else...) - val accumulatedExpression = accumulatingExpression(snippet) - // set appropriate body to the function for the correct evaluation - holeFunDef.body = Some(accumulatedExpression) - - import TreeOps._ - val expressionToCheck = - //Globals.bodyAndPostPlug(exp) - { - val resFresh = FreshIdentifier("result", true).setType(accumulatedExpression.getType) - Let( - resFresh, accumulatedExpression, - replace(Map(ResultVariable() -> LeonVariable(resFresh)), matchToIfThenElse(holeFunDef.getPostcondition))) - } - - finest("going to evaluate candidate for: " + holeFunDef) - finest("going to evaluate candidate for: " + expressionToCheck) - - val count = exampleRunner.evaluate(expressionToCheck, mapping) - - holeFunDef.precondition = oldPreconditionSaved - holeFunDef.body = oldBodySaved - -// if(snippet.toString == "checkf(f2, reverse(r2))") -// interactivePause - - synthInfo end count + gatheredExamples = ArrayBuffer(introduceExamples().map(Example(_)): _*) + info("Introduced examples: " + gatheredExamples.mkString("\t")) } def synthesize: Report = { @@ -389,23 +297,24 @@ class SynthesizerForRuleExamples( case Some(true) => false case _ => true } - // update flag in case of time limit overdue - def checkTimeout = - if (synthesisContext.shouldStop.get) { - keepGoing = false - true - } else - false + // update flag in case of time limit overdue + def checkTimeout = + if (synthesisContext.shouldStop.get) { + keepGoing = false + true + } else + false // initial snippets (will update in the loop) var snippets = synthesizeBranchExpressions var snippetsIterator = snippets.iterator - + // ordering of expressions according to passed examples var pq = getNewExampleQueue - + // iterate while the program is not valid import scala.util.control.Breaks._ + var totalExpressionsTested = 0 var iteration = 0 var noBranchFoundIteration = 0 breakable { @@ -425,101 +334,119 @@ class SynthesizerForRuleExamples( var numberOfTested = 0 reporter.info("Going into a enumeration/testing phase.") - fine("evaluating examples: " + exampleRunner.counterExamples.mkString("\n")) - + fine("evaluating examples: " + gatheredExamples.mkString("\n")) + breakable { - while(true) { + while (true) { + if (checkTimeout) break val batchSize = numberOfTestsInIteration * (1 << noBranchFoundIteration) - - reporter.info("numberOfTested: " + numberOfTested) - // ranking of candidates - val candidates = { - val (it1, it2) = snippetsIterator.duplicate - snippetsIterator = it2.drop(batchSize) - it1.take(batchSize). - map(_.getSnippet).filterNot( - snip => { - (seenBranchExpressions contains snip.toString) || refiner.isAvoidable(snip, problem.as) - } - ).toSeq - } - info("got candidates of size: " + candidates.size) - //interactivePause - - // printing candidates and pass counts - fine( { - val logString = ((candidates.zipWithIndex) map { - case ((snippet: Expr, ind: Int)) => ind + ": snippet is " + snippet.toString + - " pass count is " + countPassedExamples(snippet) - }).mkString("\n") - logString - }) - //interactivePause - - if (candidates.size > 0) { - val ranker = new Ranker(candidates, - Evaluation(exampleRunner.counterExamples, this.evaluateCandidate _, candidates, exampleRunner), - false) - - val maxCandidate = ranker.getMax - info("maxCandidate is: " + maxCandidate) - numberOfTested += batchSize - - if ( - candidates.exists(_.toString contains "checkf(f2, Cons(x, r2))") - ) { - println("maxCandidate is: " + maxCandidate) - println(ranker.printTuples) - println("AAA2") - println("Candidates: " + candidates.zipWithIndex.map({ - case (cand, ind) => "[" + ind + "]" + cand.toString - }).mkString("\n")) - println("Examples: " + exampleRunner.counterExamples.zipWithIndex.map({ - case (example, ind) => "[" + ind + "]" + example.toString - }).mkString("\n")) - interactivePause - } - - interactivePause - if (tryToSynthesizeBranch(maxCandidate)) { - noBranchFoundIteration = 0 - break - } - interactivePause - - noBranchFoundIteration += 1 - } - } - } - - // add to seen if branch was not found for it - //seenBranchExpressions += snippetTree.toString + + reporter.info("numberOfTested: " + numberOfTested) + // ranking of candidates + val candidates = { + val (it1, it2) = snippetsIterator.duplicate + snippetsIterator = it2.drop(batchSize) + it1.take(batchSize). + filterNot( + out => { + val snip = out.getSnippet + fine("enumerated: " + snip) + (seenBranchExpressions contains snip.toString) || refiner.isAvoidable(snip, problem.as) + }).toIndexedSeq + } + info("Got candidates of size: " + candidates.size + + " , first 10 of them are: " + candidates.take(10).map(_.getSnippet.toString).mkString(",\t")) + //interactivePause + + + // val count = exampleRunner.evaluate(expressionToCheck, mapping) + + if (candidates.size > 0) { + // save current precondition and the old body since it will can be mutated during evaluation + val oldPreconditionSaved = holeFunDef.precondition + val oldBodySaved = holeFunDef.body + // set initial precondition + holeFunDef.precondition = Some(initialPrecondition) + + val ranker = evaluationStrategy.getRanker(candidates, accumulatingExpression, gatheredExamples) + exampleRunner = evaluationStrategy.getExampleRunner + + info("Ranking candidates...") + synthInfo.start(Action.Evaluation) + val maxCandidate = ranker.getMax + synthInfo.end + + // restore original precondition and body + holeFunDef.precondition = oldPreconditionSaved + holeFunDef.body = oldBodySaved + + // check for timeouts + if (!keepGoing) break + + info("Candidate with the most successfull evaluations is: " + maxCandidate) +// interactivePause + numberOfTested += batchSize + + if (false //candidates.exists(_.toString contains "merge(sort(split(list") + ) { + println("maxCandidate is: " + maxCandidate) + println(ranker.printTuples) + println("AAA2") + println("Candidates: " + candidates.zipWithIndex.map({ + case (cand, ind) => "[" + ind + "]" + cand.toString + }).mkString("\n")) + println("Examples: " + gatheredExamples.zipWithIndex.map({ + case (example, ind) => "[" + ind + "]" + example.toString + }).mkString("\n")) + interactivePause + } + + // interactivePause + if (tryToSynthesizeBranch(maxCandidate.getExpr)) { + noBranchFoundIteration = 0 + break + } + // interactivePause + + totalExpressionsTested += numberOfTested + noBranchFoundIteration += 1 + } + } // while(true) + } // breakable + + // add to seen if branch was not found for it + //seenBranchExpressions += snippetTree.toString + + if (!keepGoing) break // if did not found for any of the branch expressions if (found) { synthInfo end Synthesis + synthInfo.iterations = iteration + synthInfo.numberOfEnumeratedExpressions = numberOfTested reporter.info("We are done, in time: " + synthInfo.last) return new FullReport(holeFunDef, synthInfo) } - if ( variableRefinedBranch ) { - fine("Variable refined, doing branch synthesis again") - // get new snippets - snippets = synthesizeBranchExpressions - snippetsIterator = snippets.iterator - pq = getNewExampleQueue - + if (variableRefinedBranch) { + info("Variable refined, doing branch synthesis again") + // get new snippets + snippets = synthesizeBranchExpressions + snippetsIterator = snippets.iterator + pq = getNewExampleQueue + // reset flag variableRefinedBranch = false } else // reseting iterator needed because we may have some expressions that previously did not work - snippetsIterator = snippets.iterator + snippetsIterator = snippets.iterator - fine("filtering based on: " + holeFunDef.precondition.get) - fine("counterexamples before filter: " + exampleRunner.counterExamples.size) + info("Filtering based on precondition: " + holeFunDef.precondition.get) + fine("counterexamples before filter: " + gatheredExamples.size) exampleRunner.filter(holeFunDef.precondition.get) - fine("counterexamples after filter: " + exampleRunner.counterExamples.size) - fine("counterexamples after filter: " + exampleRunner.counterExamples.mkString("\n")) + gatheredExamples = exampleRunner.examples + fine("counterexamples after filter: " + gatheredExamples.size) + fine("counterexamples after filter: " + gatheredExamples.mkString("\n")) } } //breakable { while (!keepGoing) { @@ -541,10 +468,12 @@ class SynthesizerForRuleExamples( // TODO spare one analyzing step // analyze the program - fine("analyzing program for funDef:" + holeFunDef) -// solver.setProgram(program) + info("Current candidate solution is:\n" + holeFunDef) + fine("Analyzing program for funDef:" + holeFunDef) + // solver.setProgram(program) analyzeProgram + info("Solver returned: " + Globals.allSolved) // check if solver could solved this instance if (Globals.allSolved == Some(true)) { // mark the branch found @@ -572,7 +501,7 @@ class SynthesizerForRuleExamples( // collect (add) counterexamples from leon if (collectCounterExamplesFromLeon) - exampleRunner.counterExamples ++= maps + gatheredExamples ++= maps.map(Example(_)) // this should not be possible // val keepGoing = Globals.allSolved match { @@ -594,15 +523,13 @@ class SynthesizerForRuleExamples( // return true // } - //if(maps.isEmpty) throw new RuntimeException("asdasdasd") - + //if(maps.isEmpty) throw new RuntimeException("asdasdasd") - // will modify funDef body and precondition, restore it later try { { //if (!maps.isEmpty) { // proceed with synthesizing boolean expressions - solver.setProgram(program) + //solver.setProgram(program) // reconstruct (only defined number of boolean expressions) val innerSnippets = synthesizeBooleanExpressions @@ -613,16 +540,18 @@ class SynthesizerForRuleExamples( }).mkString("\n")) for ( - innerSnippetTree <- innerSnippets map { _.getSnippet }; - if ( - { val flag = !refiner.isAvoidable(innerSnippetTree, problem.as) - if (!flag) fine("Refiner filtered this snippet: " + innerSnippetTree) - flag } - ) - ) { + innerSnippetTree <- innerSnippets map { _.getSnippet }; + if ( + { + val flag = !refiner.isAvoidable(innerSnippetTree, problem.as) + if (!flag) fine("Refiner filtered this snippet: " + innerSnippetTree) + flag + }) + ) { fine("boolean snippet is: " + innerSnippetTree) + info("Trying: " + innerSnippetTree + " as a condition.") - val (innerFound, innerPrec) = tryToSynthesizeBooleanCondition(snippetTree, innerSnippetTree, precondition) + val (innerFound, innerPrec) = tryToSynthesizeBooleanCondition(snippetTree, innerSnippetTree, maps) // if precondition found if (innerFound) { @@ -654,8 +583,8 @@ class SynthesizerForRuleExamples( } } - def tryToSynthesizeBooleanCondition(snippetTree: Expr, innerSnippetTree: Expr, precondition: Expr): (Boolean, Option[Expr]) = { - + def tryToSynthesizeBooleanCondition(snippetTree: Expr, innerSnippetTree: Expr, counterExamples: Seq[Map[Identifier, Expr]]): (Boolean, Option[Expr]) = { + // new condition together with existing precondition val newCondition = And(Seq(accumulatingPrecondition, innerSnippetTree)) @@ -672,69 +601,73 @@ class SynthesizerForRuleExamples( // nothing here // here, our expression is not contradictory, continue case Some(false) => - // check if synthesized boolean expression implies precondition (counterexamples) - val implyExpression = Implies(newCondition, precondition) - fine("implyExpression is: " + implyExpression) - - // check if synthesized condition implies counter-examples - val solveReturn = solver.solve(implyExpression) - fine("solve returned: " + solveReturn) - - solveReturn match { - case Some(true) => - // if expression implies counterexamples add it to the precondition and try to validate program - holeFunDef.precondition = Some(newCondition) - // do analysis -// solver.setProgram(program) - analyzeProgram - // program is valid, we have a branch - if (Globals.allSolved == Some(true)) { - // we found a branch - reporter.info("We found a branch, for expression %s, with condition %s.".format(snippetTree, innerSnippetTree)) - - // update accumulating expression - val oldAccumulatingExpression = accumulatingExpression - val newAccumulatingExpression = - (finalExpr: Expr) => - oldAccumulatingExpression({ - val innerIf = IfExpr(innerSnippetTree, snippetTree, finalExpr) - innerIf.setType(snippetTree.getType) - innerIf - }) - - accumulatingExpression = newAccumulatingExpression - - // update accumulating precondition - fine("updating accumulatingPrecondition") - accumulatingPrecondition = And(Seq(accumulatingPrecondition, Not(innerSnippetTree))) - fine("updating hole fun precondition and body (to be hole)") - - // set to set new precondition - val preconditionToRestore = Some(accumulatingPrecondition) - - val variableRefinementResult = variableRefiner.checkRefinements(innerSnippetTree, allDeclarations) - if (variableRefinementResult._1) { - allDeclarations = variableRefinementResult._2 - - // the reason for two flags is for easier management of re-syntheses only if needed - variableRefinedBranch = true - variableRefinedCondition = true - } - - // found a boolean snippet, break - (true, preconditionToRestore) - } else { - // reset funDef and continue with next boolean snippet - val preconditionToRestore = Some(accumulatingPrecondition) - (false, preconditionToRestore) + // check if synthesized boolean expression implies precondition (counterexamples) + val implyCounterExamples = (false /: counterExamples) { + case (false, exMapping) => + exampleRunner.evaluateToResult(newCondition, exMapping) match { + case EvaluationResults.Successful(BooleanLiteral(false)) => false + case r => + fine("Evaluation result for " + newCondition + " on " + exMapping + " is " + r) + true + } + case _ => true + } + fine("implyCounterExamples: " + implyCounterExamples) + + if (!implyCounterExamples) { + // if expression implies counterexamples add it to the precondition and try to validate program + holeFunDef.precondition = Some(newCondition) + // do analysis + solver.setProgram(program) + analyzeProgram + // program is valid, we have a branch + if (Globals.allSolved == Some(true)) { + // we found a branch + reporter.info("We found a branch, for expression %s, with condition %s.".format(snippetTree, innerSnippetTree)) + + // update accumulating expression + val oldAccumulatingExpression = accumulatingExpression + val newAccumulatingExpression = + (finalExpr: Expr) => + oldAccumulatingExpression({ + val innerIf = IfExpr(innerSnippetTree, snippetTree, finalExpr) + innerIf.setType(snippetTree.getType) + innerIf + }) + + accumulatingExpression = newAccumulatingExpression + + // update accumulating precondition + fine("updating accumulatingPrecondition") + accumulatingPrecondition = And(Seq(accumulatingPrecondition, Not(innerSnippetTree))) + fine("updating hole fun precondition and body (to be hole)") + + // set to set new precondition + val preconditionToRestore = Some(accumulatingPrecondition) + + val variableRefinementResult = variableRefiner.checkRefinements(innerSnippetTree, allDeclarations) + if (variableRefinementResult._1) { + info("Variable is refined.") + allDeclarations = variableRefinementResult._2 + + // the reason for two flags is for easier management of re-syntheses only if needed + variableRefinedBranch = true + variableRefinedCondition = true } - case _ => - - fine("solver filtered out the precondition (does not imply counterexamples)") - (false, None) - } //solveReturn match { (for implying counterexamples) + // found a boolean snippet, break + (true, preconditionToRestore) + } else { + // reset funDef and continue with next boolean snippet + val preconditionToRestore = Some(accumulatingPrecondition) + (false, preconditionToRestore) + } + } else { + fine("solver filtered out the precondition (does not imply counterexamples)") + (false, None) + } + //solveReturn match { (for implying counterexamples) } // notFalseSolveReturn match { } -} \ No newline at end of file +} diff --git a/src/main/scala/lesynth/evaluation/CodeGenExampleRunner.scala b/src/main/scala/lesynth/evaluation/CodeGenExampleRunner.scala new file mode 100644 index 0000000000000000000000000000000000000000..3da1dc0bcace4c65996d175bc11c41b0e9597182 --- /dev/null +++ b/src/main/scala/lesynth/evaluation/CodeGenExampleRunner.scala @@ -0,0 +1,128 @@ +package lesynth +package evaluation + +import scala.collection.mutable.ArrayBuffer + +import leon._ +import leon.evaluators._ +import leon.evaluators.EvaluationResults._ +import leon.purescala.Trees._ +import leon.purescala.Definitions.{ FunDef, VarDecl, Program, ObjectDef } +import leon.purescala.Common.{ Identifier, FreshIdentifier } +import leon.purescala.TreeOps +import leon.codegen.CodeGenEvalParams + +import lesynth.examples._ +import lesynth.ranking._ + +import insynth.util.logging.HasLogger + +case class CodeGenExampleRunner(program: Program, funDef: FunDef, ctx: LeonContext, + candidates: Seq[Candidate], inputExamples: Seq[Example], + params: CodeGenEvalParams = CodeGenEvalParams(maxFunctionInvocations = 200, checkContracts = true)) extends ExampleRunner(inputExamples) with HasLogger { + + private var _examples = ArrayBuffer(transformExamples(inputExamples): _*) + + val evaluationContext = ctx.copy(reporter = new SilentReporter) + + lazy val _evaluator = new CodeGenEvaluator(evaluationContext, program, params) + override def getEvaluator = _evaluator + + def transformExamples(examples: Seq[Example]) = + examples.map( + ex => { + val map = ex.map + for(id <- funDef.args.map(_.id)) yield + map(id) + } + ) + + def compile(expr: Expr, ids: Seq[Identifier]) = { + // this get is dubious + StopwatchCollections.get("Compilation").newStopwatch profile getEvaluator.compile(expr, ids).get + } + + val candidateClosures = candidates.map(cand => compile(cand.prepareExpression, funDef.args.map(_.id))) + + override def evaluate(candidateInd: Int, exampleInd: Int) = { + val closure = candidateClosures(candidateInd) + + fine("Index evaluate candidate [%d]%s, for [%d]%s.".format( + candidateInd, candidates(candidateInd).prepareExpression, exampleInd, examples(exampleInd) + )) + + evaluate(closure, _examples(exampleInd)) + } + + override def addExamples(newExamples: Seq[Example]) = { + super.addExamples(newExamples) + _examples ++= transformExamples(examples) + } + + def evaluate(expr: Expr, args: Seq[Expr]) { + fine("to evaluate: " + expr + " for: " + args) + + val closure = compile(expr, funDef.args.map(_.id)) + evaluate(closure, args) + } + + override def evaluate(expr: Expr, mapping: Map[Identifier, Expr]) = { + fine("to evaluate: " + expr + " for mapping: " + mapping) + + val closure = compile(expr, funDef.args.map(_.id)) + + evaluate(closure, funDef.args.map(arg => mapping(arg.id))) + } + + override def evaluateToResult(expr: Expr, mapping: Map[Identifier, Expr]): Result = { + fine("to evaluate: " + expr + " for mapping: " + mapping) + + val closure = compile(expr, funDef.args.map(_.id)) + + closure(funDef.args.map(arg => mapping(arg.id))) + } + + def evaluate(evalClosure: Seq[Expr] => Result, args: Seq[Expr]) = { + try { + evalClosure(args) match { + case Successful(BooleanLiteral(true)) => + fine("Eval succeded: EvaluationSuccessful(true)") + true + case m => + fine("Eval failed: " + m) + false + } + } catch { + case e: StackOverflowError => + fine("Eval failed: " + e) + false + } + } + + /** filter counterexamples according to an expression (precondition) */ + override def filter(prec: Expr) = { + entering("filter(" + prec + ")") + fine("Old counterExamples.size: " + examples.size) + + val closure = compile(prec, funDef.args.map(_.id)) + + _examples = _examples filter { + evaluate(closure, _) + } + fine("New counterExamples.size: " + examples.size) + } + + /** count how many examples pass */ + override def countPassed(expressionToCheck: Expr) = { + fine("expressionToCheck: " + expressionToCheck) + + val closure = compile(expressionToCheck, funDef.args.map(_.id)) + + val (passed, failed) = (_examples zip examples).partition( + pair => evaluate(closure, pair._1) + ) + + (passed.size, passed map (_._2)) + } + +} \ No newline at end of file diff --git a/src/main/scala/lesynth/evaluation/DefaultExampleRunner.scala b/src/main/scala/lesynth/evaluation/DefaultExampleRunner.scala new file mode 100644 index 0000000000000000000000000000000000000000..c11570107d4b8910bcd88ca49e6185c0a14a6202 --- /dev/null +++ b/src/main/scala/lesynth/evaluation/DefaultExampleRunner.scala @@ -0,0 +1,82 @@ +package lesynth +package evaluation + +import scala.collection.mutable.ArrayBuffer + +import leon._ +import leon.evaluators._ +import leon.evaluators.EvaluationResults._ +import leon.purescala.Trees._ +import leon.purescala.Definitions.{ FunDef, VarDecl, Program, ObjectDef } +import leon.purescala.Common.{ Identifier, FreshIdentifier } +import leon.purescala.TreeOps + +import lesynth.examples._ +import lesynth.ranking._ + +import insynth.util.logging.HasLogger + +case class DefaultExampleRunner(program: Program, funDef: FunDef, ctx: LeonContext, + candidates: Seq[Candidate], inputExamples: Seq[Example], + maxSteps: Int = 2000) extends ExampleRunner(inputExamples) with HasLogger { + + private var _examples = ArrayBuffer(inputExamples.map(_.map): _*) + + override def addExamples(newExamples: Seq[Example]) = { + super.addExamples(newExamples) + _examples ++= newExamples.map(_.map) + } + + val evaluationContext = ctx.copy(reporter = new SilentReporter) + + lazy val _evaluator = new DefaultEvaluator(evaluationContext, program) + override def getEvaluator = _evaluator + + override def evaluate(expr: Expr, mapping: Map[Identifier, Expr]): Boolean = { + finest("to evaluate: " + expr + " for mapping: " + mapping) + getEvaluator.eval(expr, mapping) match { + case Successful(BooleanLiteral(true)) => + finest("Eval succeded: EvaluationSuccessful(true)") + true + case m => + finest("Eval failed: " + m) + false + } + } + + def evaluate(expr: Expr, args: Seq[Expr]): Boolean = { + evaluate(expr, funDef.args.map(_.id).zip(args).toMap) + } + + override def evaluateToResult(expr: Expr, mapping: Map[Identifier, Expr]) = { + finest("to evaluate: " + expr + " for mapping: " + mapping) + getEvaluator.eval(expr, mapping) + } + + + /** if the example runner knows about candidates and input examples, run by their indexes */ + def evaluate(candidateInd: Int, exampleInd: Int) = + evaluate(candidates(candidateInd).prepareExpression, _examples(exampleInd)) + + /** filter counterexamples according to an expression (precondition) */ + def filter(prec: Expr) = { + entering("filter(" + prec + ")") + finest("Old counterExamples.size: " + examples.size) + _examples = _examples filter { + evaluate(prec, _) + } + finest("New counterExamples.size: " + examples.size) + } + + /** count how many examples pass */ + def countPassed(expressionToCheck: Expr) = { + finest("count passed for expression to check: " + expressionToCheck) + + val (passed, failed) = examples.partition( + ex => evaluate(expressionToCheck, ex.map) + ) + + (passed.size, passed) + } + +} \ No newline at end of file diff --git a/src/main/scala/lesynth/evaluation/EvaluationStrategy.scala b/src/main/scala/lesynth/evaluation/EvaluationStrategy.scala new file mode 100644 index 0000000000000000000000000000000000000000..55780f6283451e8e47fc2dc3839078f6c52d93fd --- /dev/null +++ b/src/main/scala/lesynth/evaluation/EvaluationStrategy.scala @@ -0,0 +1,94 @@ +package lesynth +package evaluation + +import leon._ +import leon.evaluators._ +import leon.evaluators.EvaluationResults._ +import leon.purescala.Trees._ +import leon.purescala.Definitions.{ FunDef, VarDecl, Program, ObjectDef } +import leon.purescala.Common.{ Identifier, FreshIdentifier } +import leon.purescala.TreeOps +import leon.codegen.CodeGenEvalParams + +import insynth.reconstruction.Output +import insynth.util.logging._ + +import lesynth.ranking._ +import lesynth.examples._ + +trait EvaluationStrategy { + def getRanker(candidatePairs: IndexedSeq[(Output)], bodyBuilder: (Expr) => Expr, inputExamples: Seq[Example]): Ranker + + def getExampleRunner: ExampleRunner +} + +case class DefaultEvaluationStrategy(program: Program, funDef: FunDef, ctx: LeonContext, + maxSteps: Int = 2000) extends EvaluationStrategy with HasLogger { + + var exampleRunner: ExampleRunner = _ + + override def getRanker(candidatePairs: IndexedSeq[Output], bodyBuilder: (Expr) => Expr, inputExamples: Seq[Example]) = { + + val candidates = Candidate.makeDefaultCandidates(candidatePairs, bodyBuilder, funDef) + + exampleRunner = DefaultExampleRunner(program, funDef, ctx, candidates, inputExamples) + + // printing candidates and pass counts + fine("Ranking with examples: " + inputExamples.mkString(", ")) + fine( { + val logString = ((candidates.zipWithIndex) map { + case (cand: Candidate, ind: Int) => { + val result = exampleRunner.countPassed(cand.prepareExpression) + ind + ": snippet is " + cand.expr + + " pass count is " + result._1 + " (" + result._2.mkString(", ") + ")" + } + }).mkString("\n") + logString + }) + + val evaluation = Evaluation(exampleRunner) + + new Ranker(candidates, evaluation) + } + + override def getExampleRunner = exampleRunner +} + +case class CodeGenEvaluationStrategy(program: Program, funDef: FunDef, ctx: LeonContext, + maxSteps: Int = 200) extends EvaluationStrategy with HasLogger { + + var exampleRunner: ExampleRunner = _ + + override def getRanker(candidatePairs: IndexedSeq[Output], bodyBuilder: (Expr) => Expr, inputExamples: Seq[Example]) = { + + val candidates = Candidate.makeCodeGenCandidates(candidatePairs, bodyBuilder, funDef) + + val newProgram = program.copy(mainObject = program.mainObject.copy(defs = program.mainObject.defs ++ candidates.map(_.newFunDef))) + + finest("New program looks like: " + newProgram) + finest("Candidates look like: " + candidates.map(_.prepareExpression).mkString("\n")) + + val params = CodeGenEvalParams(maxFunctionInvocations = maxSteps, checkContracts = true) + + exampleRunner = CodeGenExampleRunner(newProgram, funDef, ctx, candidates, inputExamples, params) + + // printing candidates and pass counts + fine("Ranking with examples: " + inputExamples.mkString(", ")) + fine( { + val logString = ((candidates.zipWithIndex) map { + case (cand: Candidate, ind: Int) => { + val result = exampleRunner.countPassed(cand.prepareExpression) + ind + ": snippet is " + cand.expr + + " pass count is " + result._1 + " (" + result._2.mkString(", ") + ")" + } + }).mkString("\n") + logString + }) + + val evaluation = Evaluation(exampleRunner) + + new Ranker(candidates, evaluation) + } + + override def getExampleRunner = exampleRunner +} \ No newline at end of file diff --git a/src/main/scala/lesynth/evaluation/ExampleRunner.scala b/src/main/scala/lesynth/evaluation/ExampleRunner.scala new file mode 100644 index 0000000000000000000000000000000000000000..bb78d02ca92398e4ec9d3a56125f59ae5346da3f --- /dev/null +++ b/src/main/scala/lesynth/evaluation/ExampleRunner.scala @@ -0,0 +1,40 @@ +package lesynth +package evaluation + +import scala.collection.mutable.ArrayBuffer + +import leon.purescala.Trees._ +import leon.purescala.Common.Identifier +import leon.evaluators._ +import leon.evaluators.EvaluationResults.Result + +import insynth.util.logging.HasLogger + +import lesynth.examples.Example + +abstract class ExampleRunner(inputExamples: Seq[Example]) extends HasLogger { + + private var _examples = ArrayBuffer(inputExamples: _*) + def examples = _examples + + def addExamples(newExamples: Seq[Example]): Unit = { + _examples ++= newExamples + } + + def getEvaluator: Evaluator + + /** evaluate given expression */ + def evaluate(expr: Expr, mapping: Map[Identifier, Expr]): Boolean + + def evaluateToResult(expr: Expr, mapping: Map[Identifier, Expr]): Result + + /** if the example runner knows about candidates and input examples, run by their indexes */ + def evaluate(candidateInd: Int, exampleInd: Int): Boolean + + /** filter counterexamples according to an expression (precondition) */ + def filter(prec: Expr): Unit + + /** count how many examples pass */ + def countPassed(expressionToCheck: Expr): (Int, Seq[Example]) + +} \ No newline at end of file diff --git a/src/main/scala/lesynth/examples/Example.scala b/src/main/scala/lesynth/examples/Example.scala new file mode 100644 index 0000000000000000000000000000000000000000..10a47e48c8b75512acf6ca74105769b0cf112c6d --- /dev/null +++ b/src/main/scala/lesynth/examples/Example.scala @@ -0,0 +1,13 @@ +package lesynth +package examples + +import leon.purescala.Trees._ +import leon.purescala.Common.{ Identifier, FreshIdentifier } + +case class Example(map: Map[Identifier, Expr]) { + + override def toString = { + map.toString + } + +} \ No newline at end of file diff --git a/src/main/scala/lesynth/InputExamples.scala b/src/main/scala/lesynth/examples/InputExamples.scala similarity index 97% rename from src/main/scala/lesynth/InputExamples.scala rename to src/main/scala/lesynth/examples/InputExamples.scala index 71c9db244bb0374c268e2a28c604ebcb0335f1b8..8d0f314ab7c849a20c21161f4b6a67637ef3855f 100755 --- a/src/main/scala/lesynth/InputExamples.scala +++ b/src/main/scala/lesynth/examples/InputExamples.scala @@ -1,4 +1,5 @@ package lesynth +package examples import leon.{ Main => LeonMain, Reporter, DefaultReporter, SilentReporter, Settings, LeonContext } import leon.solvers.{ Solver, TimeoutSolver } @@ -18,14 +19,11 @@ import insynth.leon.loader.{ HoleExtractor, LeonLoader } object InputExamples { def getDataGenInputExamples(evaluator: Evaluator, p: Problem, - numOfModels: Int, numOfTries: Int, _forcedFreeVars: Option[Seq[Identifier]]) - (argumentIds: Seq[Identifier], loader: LeonLoader) = { - + numOfModels: Int, numOfTries: Int, _forcedFreeVars: Option[Seq[Identifier]]) = { val models = findModels(p.pc, evaluator, numOfModels, numOfTries, forcedFreeVars = _forcedFreeVars) models.toList - } def getFixedInputExamples(argumentIds: Seq[Identifier], loader: LeonLoader) = { diff --git a/src/main/scala/lesynth/ranking/Candidate.scala b/src/main/scala/lesynth/ranking/Candidate.scala new file mode 100644 index 0000000000000000000000000000000000000000..3305b1832e2d4ccb9df515a79950d456ad041981 --- /dev/null +++ b/src/main/scala/lesynth/ranking/Candidate.scala @@ -0,0 +1,112 @@ +package lesynth +package ranking + +import leon.purescala.Trees._ +import leon.purescala.Common._ +import leon.purescala.Definitions._ +import leon.purescala.TypeTrees._ +import leon.purescala.TreeOps + +import insynth.reconstruction.Output +import insynth.structures.Weight._ +import insynth.util.logging.HasLogger + +object Candidate { + private var _freshResultVariable: Identifier = _ + def freshResultVariable = _freshResultVariable + + def getFreshResultVariable(tpe: TypeTree) = _freshResultVariable = FreshIdentifier("result", true).setType(tpe) + + def makeDefaultCandidates(candidatePairs: IndexedSeq[Output], bodyBuilder: Expr => Expr, funDef: FunDef) = { + getFreshResultVariable(funDef.getBody.getType) + candidatePairs map { + pair => + DefaultCandidate(pair.getSnippet, bodyBuilder(pair.getSnippet), pair.getWeight, funDef) + } + } + + def makeCodeGenCandidates(candidatePairs: IndexedSeq[Output], bodyBuilder: Expr => Expr, funDef: FunDef) = { + getFreshResultVariable(funDef.getBody.getType) + candidatePairs map { + pair => + CodeGenCandidate(pair.getSnippet, bodyBuilder(pair.getSnippet), pair.getWeight, funDef) + } + } +} + +abstract class Candidate(weight: Weight) { + def getExpr: Expr + + def prepareExpression: Expr + + def getWeight = weight +} + +case class DefaultCandidate(expr: Expr, bodyExpr: Expr, weight: Weight, holeFunDef: FunDef) + extends Candidate(weight) with HasLogger { + import Candidate._ + + override def getExpr = expr + + lazy val expressionToEvaluate = { + import TreeOps._ + + assert(bodyExpr.getType != Untyped) + val resFresh = freshResultVariable//.setType(expr.getType) + + // body can contain (self-)recursive calls + Let(resFresh, bodyExpr, + replace(Map(ResultVariable() -> Variable(resFresh)), + matchToIfThenElse(holeFunDef.getPostcondition))) + } + + override def prepareExpression = { + // set appropriate body to the function for the correct evaluation due to recursive calls + holeFunDef.body = Some(bodyExpr) + +// finest("going to evaluate candidate for: " + holeFunDef) +// finest("going to evaluate candidate for: " + expressionToEvaluate) + expressionToEvaluate + } +} + +case class CodeGenCandidate(expr: Expr, bodyExpr: Expr, weight: Weight, holeFunDef: FunDef) + extends Candidate(weight) with HasLogger { + import Candidate._ + + override def getExpr = expr + + lazy val (expressionToEvaluate, newFunDef) = { + import TreeOps._ + + val newFunId = FreshIdentifier("tempIntroducedFunction") + val newFun = new FunDef(newFunId, holeFunDef.returnType, holeFunDef.args) + newFun.precondition = holeFunDef.precondition + newFun.postcondition = holeFunDef.postcondition + + def replaceFunDef(expr: Expr) = expr match { + case FunctionInvocation(`holeFunDef`, args) => + Some(FunctionInvocation(newFun, args)) + case _ => None + } + val newBody = searchAndReplace(replaceFunDef)(bodyExpr) + + newFun.body = Some(newBody) + + assert(bodyExpr.getType != Untyped) + val resFresh = freshResultVariable//.setType(expr.getType) + + // body can contain (self-)recursive calls + (Let(resFresh, newBody, + replace(Map(ResultVariable() -> Variable(resFresh)), + matchToIfThenElse(newFun.getPostcondition))) + , + newFun) + } + + override def prepareExpression = { +// finest("going to evaluate candidate for: " + holeFunDef) +// finest("going to evaluate candidate for: " + expressionToEvaluate) + expressionToEvaluate + } +} \ No newline at end of file diff --git a/src/main/scala/lesynth/Evaluation.scala b/src/main/scala/lesynth/ranking/Evaluation.scala similarity index 59% rename from src/main/scala/lesynth/Evaluation.scala rename to src/main/scala/lesynth/ranking/Evaluation.scala index 93093979d158648310f749d733b33c9009862ba6..fb044ab9cf9620ad8b5078c21d899e1be227e75b 100644 --- a/src/main/scala/lesynth/Evaluation.scala +++ b/src/main/scala/lesynth/ranking/Evaluation.scala @@ -1,44 +1,40 @@ package lesynth +package ranking -import scala.util.Random +import lesynth.evaluation._ -import leon.purescala.Trees.{ Variable => LeonVariable, _ } -import leon.purescala.Common.Identifier - -case class Evaluation(examples: Seq[Map[Identifier, Expr]], exampleFun: (Expr, Map[Identifier, Expr])=>Boolean, candidates: Seq[Expr], - exampleRunner: ExampleRunner) { +case class Evaluation(exampleRunner: ExampleRunner) { - val random: Random = new Random(System.currentTimeMillis) - - // keep track of evaluations + // keep track of which examples to evaluate next var nextExamples: Map[Int, Int] = Map() + // keep track of evaluation results var evaluations = Map[Int, Array[Boolean]]() -// def evalAvailable(expr: Int) = { -// val nextExample = nextExamples.getOrElse(expr, 0) -// if (nextExample >= examples.size) false -// else true -// } - + def numberOfExamples = exampleRunner.examples.size + def evaluate(exprInd: Int) = { numberOfEvaluationCalls += 1 + // get next example index and update val nextExample = nextExamples.getOrElse(exprInd, 0) - if (nextExample >= examples.size) throw new RuntimeException("Exhausted examples for " + exprInd) - + if (nextExample >= numberOfExamples) throw new RuntimeException("Exhausted examples for " + exprInd) nextExamples += (exprInd -> (nextExample + 1)) - - val example = examples(nextExample) - val expressionToCheck = candidates(exprInd) - val result = exampleFun(expressionToCheck, example) - val evalArray = evaluations.getOrElse(exprInd, Array.ofDim[Boolean](examples.size)) + val result = exampleRunner.evaluate(exprInd, nextExample) + + // update evaluation results + val evalArray = evaluations.getOrElse(exprInd, Array.ofDim[Boolean](numberOfExamples)) evalArray(nextExample) = result evaluations += (exprInd -> evalArray) + result } + // for measurements + var numberOfEvaluationCalls = 0 + def getEfficiencyRatio = numberOfEvaluationCalls.toFloat / (numberOfExamples * evaluations.size) + // def evaluate(expr: Int, exampleInd: Int) = { // val nextExample = nextExamples.getOrElse(expr, 0) // assert(exampleInd <= nextExample) @@ -57,13 +53,10 @@ case class Evaluation(examples: Seq[Map[Identifier, Expr]], exampleFun: (Expr, M // } // } - def evaluate(expression: Int, example: Int => Boolean) = { - example(expression) - } - - def getNumberOfExamples = examples.size - - var numberOfEvaluationCalls = 0 - def getEfficiencyRatio = numberOfEvaluationCalls.toFloat / (examples.size * evaluations.size) - +// def evalAvailable(expr: Int) = { +// val nextExample = nextExamples.getOrElse(expr, 0) +// if (nextExample >= examples.size) false +// else true +// } + } \ No newline at end of file diff --git a/src/main/scala/lesynth/Ranker.scala b/src/main/scala/lesynth/ranking/Ranker.scala similarity index 76% rename from src/main/scala/lesynth/Ranker.scala rename to src/main/scala/lesynth/ranking/Ranker.scala index 13742f2c2a94c8e800519a0ca7a7cf56e928507e..5f8e969f7fdb8bcb7550c38f449669a90720ae4d 100644 --- a/src/main/scala/lesynth/Ranker.scala +++ b/src/main/scala/lesynth/ranking/Ranker.scala @@ -1,18 +1,21 @@ package lesynth +package ranking import util.control.Breaks._ import scala.collection._ import leon.purescala.Trees.{ Variable => LeonVariable, _ } -class Ranker(candidates: Seq[Expr], evaluation: Evaluation, printStep: Boolean = false) { +class Ranker(candidates: IndexedSeq[Candidate], evaluation: Evaluation, checkTimeout: (() => Boolean) = { () => false }, printStep: Boolean = false) { - var rankings: Array[Int] = (0 until candidates.size).toArray + val candidatesSize = candidates.size + + var rankings: Array[Int] = (0 until candidatesSize).toArray // keep track of intervals var tuples: Map[Int, (Int, Int)] = - (for (i <- 0 until candidates.size) - yield (i, (0, evaluation.getNumberOfExamples))) toMap + (for (i <- 0 until candidatesSize) + yield (i, (0, evaluation.numberOfExamples))) toMap def getKMax(k: Int) = { @@ -43,18 +46,18 @@ class Ranker(candidates: Seq[Expr], evaluation: Evaluation, printStep: Boolean = def bubbleDown(ind: Int): Unit = { if (compare(rankings(ind), rankings(ind + 1))) { swap(ind, ind + 1) - if (ind < candidates.size-2) + if (ind < candidatesSize-2) bubbleDown(ind + 1) } } - var numberLeft = candidates.size + var numberLeft = candidatesSize def getMax = { - numberLeft = candidates.size + numberLeft = candidatesSize - while (numberLeft > 1) { + while (numberLeft > 1 && !checkTimeout()) { evaluate(rankings(0)) @@ -81,8 +84,7 @@ class Ranker(candidates: Seq[Expr], evaluation: Evaluation, printStep: Boolean = println("***: " + numberLeft) } - candidates(rankings(0)) - + candidates(rankings(0)) } // def getVerifiedMax = { @@ -109,6 +111,9 @@ class Ranker(candidates: Seq[Expr], evaluation: Evaluation, printStep: Boolean = tuple1._2 <= tuple2._1 } + // candidate x <= candidate y heuristically (compare their sizes) + def heurCompare(x: Int, y: Int) = candidates(x).getWeight >= candidates(y).getWeight + def compare(x: Int, y: Int) = { val tuple1 = tuples(x) val tuple2 = tuples(y) @@ -117,7 +122,8 @@ class Ranker(candidates: Seq[Expr], evaluation: Evaluation, printStep: Boolean = val median2 = (tuple2._1 + tuple2._2).toFloat/2 /*median1 < median2 || median1 == median2 && */ - tuple1._2 < tuple2._2 || tuple1._2 == tuple2._2 && median1 < median2 + tuple1._2 < tuple2._2 || tuple1._2 == tuple2._2 && + (heurCompare(x, y) || median1 < median2) } def rankOf(expr: Int) = @@ -128,7 +134,7 @@ class Ranker(candidates: Seq[Expr], evaluation: Evaluation, printStep: Boolean = tuples.toList.sortWith((tp1, tp2) => rankOf(tp1._1) <= rankOf(tp2._1)).zipWithIndex) yield (if (tuple._1 == rankings(0)) "->" else if (ind >= numberLeft) "/\\" else " ") + tuple._1 + ": " + - ((0 until evaluation.getNumberOfExamples) map { + ((0 until evaluation.numberOfExamples) map { x => if (x < tuple._2._1) '+' else if (x >= tuple._2._2) '-' else '_' }).mkString).mkString("\n") diff --git a/src/main/scala/lesynth/Refiner.scala b/src/main/scala/lesynth/refinement/Refiner.scala similarity index 65% rename from src/main/scala/lesynth/Refiner.scala rename to src/main/scala/lesynth/refinement/Refiner.scala index 2fc2cbd4424546dffeeaa4c61a268897ea150788..3c385858b9dee01ff908dfa0c92c910fdfb69b15 100755 --- a/src/main/scala/lesynth/Refiner.scala +++ b/src/main/scala/lesynth/refinement/Refiner.scala @@ -1,7 +1,9 @@ package lesynth +package refinement import leon.purescala.Trees._ -import leon.purescala.Definitions.{ Program, VarDecl, FunDef, VarDecls } +import leon.purescala.TypeTrees._ +import leon.purescala.Definitions._ import leon.purescala.Common.{ Identifier, FreshIdentifier } import leon.purescala.TreeOps import leon.plugin.ExtractionPhase @@ -10,42 +12,47 @@ import insynth.leon.loader.LeonLoader import insynth.util.logging.HasLogger -class Refiner(program: Program, hole: Hole, holeFunDef: FunDef) extends HasLogger { +class Filter(program: Program, holeFunDef: FunDef, refiner: VariableRefiner) extends HasLogger { import Globals._ def isAvoidable(expr: Expr, funDefArgs: List[Identifier]) = { fine( "Results for refining " + expr + ", are: " + - " ,recurentExpression == expr " + (recurentExpression == expr) + " ,isCallAvoidableBySize(expr) " + isCallAvoidableBySize(expr, funDefArgs) + " ,hasDoubleRecursion(expr) " + hasDoubleRecursion(expr) + - " ,isOperatorAvoidable(expr) " + isOperatorAvoidable(expr) + " ,isOperatorAvoidable(expr) " + isOperatorAvoidable(expr) + + " ,isUnecessaryInstanceOf(expr) " + isUnecessaryInstanceOf(expr) ) - recurentExpression == expr || isCallAvoidableBySize(expr, funDefArgs) || hasDoubleRecursion(expr) || isOperatorAvoidable(expr) + isCallAvoidableBySize(expr, funDefArgs) || hasDoubleRecursion(expr) || + isOperatorAvoidable(expr) || isUnecessaryInstanceOf(expr) } //val holeFunDef = Globals.holeFunDef - val recurentExpression: Expr = + val pureRecurentExpression: Expr = if (holeFunDef.hasBody) { - FunctionInvocation(holeFunDef, holeFunDef.args map { varDecl => Variable(varDecl.id) }) + FunctionInvocation(holeFunDef, holeFunDef.args map { _.toVariable }) } else Error("Hole FunDef should have a body") + fine("Refiner initialized. Recursive call: " + pureRecurentExpression) + def isBadInvocation(expr2: Expr) = expr2 match { + case `pureRecurentExpression` => + fine("pure recurrent expression detected") + true + case FunctionInvocation(`holeFunDef`, args) => + (0 /: (args zip holeFunDef.args.map(_.id))) { + case (res, (arg, par)) if res == 0 => isLess(arg, par) + case (res, _) => res + } >= 0 + case _ => false + } + // check according to size // true - YES, false - NO or don't know // basically a lexicographic (well-founded) ordering def isCallAvoidableBySize(expr: Expr, funDefArgs: List[Identifier]) = { - - def isBadInvocation(expr2: Expr) = expr2 match { - case FunctionInvocation(`holeFunDef`, args) => - (0 /: (args zip holeFunDef.args.map(_.id))) { - case (res, (arg, par)) if res == 0 => isLess(arg, par) - case (res, _) => res - } >= 0 - case _ => false - } - + import TreeOps.treeCatamorphism treeCatamorphism( @@ -73,7 +80,7 @@ class Refiner(program: Program, hole: Hole, holeFunDef: FunDef) extends HasLogge getSize(arg, 0) } - + def hasDoubleRecursion(expr: Expr) = { var found = false @@ -107,4 +114,25 @@ class Refiner(program: Program, hole: Hole, holeFunDef: FunDef) extends HasLogge case _ => false } + def isUnecessaryInstanceOf(expr: Expr) = { + def isOfClassType(exp: Expr, classDef: ClassTypeDef) = + expr.getType match { + case tpe: ClassType => tpe.classDef == classDef + case _ => false + } + expr match { + case CaseClassInstanceOf(classDef, innerExpr) + if isOfClassType(innerExpr, classDef) => + true + case CaseClassInstanceOf(classDef, v@Variable(id)) => { + val possibleTypes = refiner.getPossibleTypes(id) + if (possibleTypes.size == 1) + possibleTypes.head.classDef == classDef + else + false + } + case _ => false + } + } + } \ No newline at end of file diff --git a/src/main/scala/lesynth/VariableRefiner.scala b/src/main/scala/lesynth/refinement/VariableRefiner.scala similarity index 97% rename from src/main/scala/lesynth/VariableRefiner.scala rename to src/main/scala/lesynth/refinement/VariableRefiner.scala index cc3b664cdcc55776ba82e799926b2e5901840fc8..42fc251cb60f2e9f63f955786c24f2012cf5922c 100755 --- a/src/main/scala/lesynth/VariableRefiner.scala +++ b/src/main/scala/lesynth/refinement/VariableRefiner.scala @@ -1,4 +1,5 @@ package lesynth +package refinement import scala.collection.mutable.{Map => MutableMap} import scala.collection.mutable.{Set => MutableSet} @@ -30,6 +31,8 @@ class VariableRefiner(directSubclassMap: Map[ClassType, Set[ClassType]], variabl } } + def getPossibleTypes(id: Identifier) = variableRefinements(id) + def checkRefinements(expr: Expr, allDeclarations: List[Declaration]) = // check for refinements getIdAndClassDef(expr) match { diff --git a/src/main/scala/lesynth/rules/ConditionAbductionSynthesisTwoPhase.scala b/src/main/scala/lesynth/rules/ConditionAbductionSynthesisTwoPhase.scala index 0b28a99862e21c238eb87eb85ed8166d39944bb0..f69216abe431699b2ee54711722d8477204c26e4 100755 --- a/src/main/scala/lesynth/rules/ConditionAbductionSynthesisTwoPhase.scala +++ b/src/main/scala/lesynth/rules/ConditionAbductionSynthesisTwoPhase.scala @@ -7,12 +7,14 @@ import leon.purescala.TypeTrees._ import leon.purescala.TreeOps._ import leon.purescala.Extractors._ import leon.purescala.Definitions._ -import leon.purescala.DataGen.findModels import leon.synthesis._ import leon.solvers.{ Solver, TimeoutSolver } import leon.evaluators.CodeGenEvaluator -import InputExamples._ +import lesynth.examples.InputExamples._ +import lesynth.evaluation._ + +import leon.StopwatchCollections case object ConditionAbductionSynthesisTwoPhase extends Rule("Condition abduction synthesis (two phase).") { def instantiateOn(sctx: SynthesisContext, p: Problem): Traversable[RuleInstantiation] = { @@ -38,21 +40,24 @@ case object ConditionAbductionSynthesisTwoPhase extends Rule("Condition abductio val freshResVar = Variable(freshResID) val codeGenEval = new CodeGenEvaluator(sctx.context, sctx.program) - def getInputExamples = - getDataGenInputExamples(codeGenEval, p, - 40, 2000, Some(holeFunDef.args.map(_.id)) - ) _ + def getInputExamples = { + () => getDataGenInputExamples(codeGenEval, p, + 20, 2000, Some(p.as) + ) + } + val evaluationStrategy = new CodeGenEvaluationStrategy(program, holeFunDef, sctx.context, 500) + holeFunDef.postcondition = Some(replace( Map(givenVariable.toVariable -> ResultVariable().setType(holeFunDef.returnType)), p.phi)) holeFunDef.precondition = Some(p.pc) val synthesizer = new SynthesizerForRuleExamples( - solver, program, desiredType, holeFunDef, p, sctx, freshResVar, - 40, 2, 1, + solver, program, desiredType, holeFunDef, p, sctx, freshResVar, evaluationStrategy, + 20, 1, 1, reporter = reporter, introduceExamples = getInputExamples, - numberOfTestsInIteration = 50, + numberOfTestsInIteration = 25, numberOfCheckInIteration = 2 ) @@ -60,6 +65,7 @@ case object ConditionAbductionSynthesisTwoPhase extends Rule("Condition abductio case EmptyReport => RuleApplicationImpossible case fr@FullReport(resFunDef, _) => println(fr.summaryString) + println("Compilation time: " + StopwatchCollections.get("Compilation").getMillis) RuleSuccess(Solution(resFunDef.getPrecondition, Set.empty, resFunDef.body.get)) } } catch {