From 7cc4e4fc50eb62b1b938f2354ce668b49a200f0f Mon Sep 17 00:00:00 2001 From: Manos Koukoutos <emmanouil.koukoutos@epfl.ch> Date: Mon, 25 Apr 2016 15:23:50 +0200 Subject: [PATCH] Abduction rule --- .../leon/synthesis/rules/Abduction.scala | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 src/main/scala/leon/synthesis/rules/Abduction.scala diff --git a/src/main/scala/leon/synthesis/rules/Abduction.scala b/src/main/scala/leon/synthesis/rules/Abduction.scala new file mode 100644 index 000000000..da32100f3 --- /dev/null +++ b/src/main/scala/leon/synthesis/rules/Abduction.scala @@ -0,0 +1,104 @@ +/* Copyright 2009-2016 EPFL, Lausanne */ + +package leon +package synthesis +package rules + +import purescala.Common._ +import purescala.DefOps._ +import purescala.Expressions._ +import purescala.TypeOps.canBeSubtypeOf +import purescala.Constructors._ +import purescala.ExprOps._ +import purescala.Definitions._ +import purescala.Extractors._ +import leon.solvers.{SolverFactory, SimpleSolverAPI} +import leon.utils.Simplifiers + +object Abduction extends Rule("Abduction") { + override def instantiateOn(implicit hctx: SearchContext, p: Problem): Traversable[RuleInstantiation] = { + + // Let ⟦ as ⟨ ws && pc | phi ⟩ xs ⟧ be the original problem + def processFd(tfd: TypedFunDef): Option[RuleInstantiation] = { + // We assign xs = tfd(newXs), newXs fresh + val newXs = tfd.paramIds map (_.freshen) + val args = newXs map Variable + val call = FunctionInvocation(tfd, args) + // prec. of tfd(newXs) (newXs have to satisfy it) + val pre = replaceFromIDs(tfd.paramIds.zip(args).toMap, tfd.precOrTrue) + + // Fail if pre is not SAT + val solver = SimpleSolverAPI(SolverFactory.getFromSettings(hctx, hctx.program)) + if (!solver.solveSAT(p.pc and pre)._1.contains(true)) return None + + // postc. of tfd(newXs) will hold for xs + val post = application( + replaceFromIDs(tfd.paramIds.zip(args).toMap, tfd.postOrTrue), + Seq(tupleWrap(p.xs map Variable)) + ) + + // Conceptually, the new problem is + // ⟦ as ⟨ ws && pc && xs <- tfd(newXs) && post | pre && phi ⟩ newXs ⟧ + // But we will only accept this problem if xs is simplifiable in phi under the new assumptions + + def containsXs(e: Expr) = (variablesOf(e) & p.xs.toSet).nonEmpty + + val newPhi = { + + val newPhi0 = { + // Try to eliminate xs in trivial cases + val TopLevelAnds(newPhis) = and(pre, post) + val equalities = newPhis.collect { + case Equals(e1, e2) if containsXs(e1) && !containsXs(e2) => e1 -> e2 + case Equals(e1, e2) if containsXs(e2) && !containsXs(e1) => e2 -> e1 + }.toMap + + replace(equalities, p.phi) + } + + val bigX = FreshIdentifier("x", p.outType, alwaysShowUniqueID = true) + + val projections = unwrapTuple(call, p.xs.size).zipWithIndex.map{ + case (e, ind) => tupleSelect(e, ind + 1, p.xs.size) + } + + val pc = p.pc + .withCond(pre) + .withBinding(bigX, call) + .withBindings(newXs zip projections) + .withCond(post) + + Simplifiers.bestEffort(hctx, hctx.program)(newPhi0, pc) + } + + if (containsXs(newPhi)) { + None + } else { + // We do not need xs anymore, so we accept the decomposition. + // We can remove xs-related cluases from the PC. + // Notice however we need to synthesize newXs such that pre is satisfied as well. + // Final problem is: + // ⟦ as ⟨ ws && pc | pre && phi ⟩ newXs ⟧ + + val newP = p.copy(phi = newPhi, xs = newXs.toList) + + val onSuccess = forwardMap(letTuple(p.xs, call, _)) + + Some(decomp(List(newP), onSuccess, "Blah")) + } + } + + val filter = (fd: FunDef) => fd.isSynthetic || fd.isInner + + val funcs = visibleFunDefsFromMain(hctx.program).toSeq.sortBy(_.id).filterNot(filter) + + // For now, let's handle all outputs at once only + for { + fd <- funcs + inst <- canBeSubtypeOf(p.outType, fd.tparams.map(_.tp), fd.returnType) + decomp <- processFd(fd.typed(fd.tparams map (tpar => inst(tpar.tp)))) + } yield decomp + + } + +} -- GitLab