diff --git a/src/main/scala/leon/solvers/templates/QuantificationManager.scala b/src/main/scala/leon/solvers/templates/QuantificationManager.scala index ae69427dceeec55049c8158437266720b3ed4c62..57dc368eb2bc5d467e56129cd91c45f21dccb999 100644 --- a/src/main/scala/leon/solvers/templates/QuantificationManager.scala +++ b/src/main/scala/leon/solvers/templates/QuantificationManager.scala @@ -67,27 +67,6 @@ object QuantificationTemplate { class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManager[T](encoder) { - private val stdQuantifiers: MutableMap[TypeTree, Seq[T]] = MutableMap.empty - private val quantified: MutableSet[T] = MutableSet.empty - - private def standardQuantifiers(tpe: TypeTree, count: Int): Seq[T] = { - val quantifiers = stdQuantifiers.getOrElse(tpe, Seq.empty) - val currentCount = quantifiers.size - - if (currentCount >= count) quantifiers.take(count) else { - val newQuantifiers = List.range(0, count - currentCount).map(_ => encoder.encodeId(FreshIdentifier("x", tpe))) - quantified ++= newQuantifiers - - val allQuantifiers = quantifiers ++ newQuantifiers - stdQuantifiers(tpe) = allQuantifiers - allQuantifiers - } - } - - private def freshQuantifierSubst: Map[T, T] = stdQuantifiers.flatMap { case (tpe, ids) => - ids zip List.range(0, ids.size).map(_ => encoder.encodeId(FreshIdentifier("x", tpe))) - }.toMap - private val nextHolder: () => T = { val id: Identifier = FreshIdentifier("ph", BooleanType) () => encoder.encodeId(id) @@ -98,47 +77,30 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage () => encoder.encodeId(id) } - private type Bindings = Set[(Option[T], TypeTree, Int, T)] - private var bindingsStack: List[Bindings] = List(Set.empty) - private def bindings: Bindings = bindingsStack.head - private def bindings_=(bs: Bindings): Unit = { - bindingsStack = bs :: bindingsStack.tail - } - private var quantificationsStack: List[Seq[Quantification]] = List(Seq.empty) private def quantifications: Seq[Quantification] = quantificationsStack.head private def quantifications_=(qs: Seq[Quantification]): Unit = { quantificationsStack = qs :: quantificationsStack.tail } - private var instantiatedMatchersStack: List[Seq[(T, Matcher[T], Map[T,T])]] = List(Seq.empty) - private def instantiatedMatchers: Seq[(T, Matcher[T], Map[T,T])] = instantiatedMatchersStack.head - private def instantiatedMatchers_=(ias: Seq[(T, Matcher[T], Map[T,T])]): Unit = { - instantiatedMatchersStack = ias :: instantiatedMatchersStack.tail - } - - private var boundMatchersStack: List[Set[(T, Matcher[T])]] = List(Set.empty) - private def boundMatchers: Set[(T, Matcher[T])] = boundMatchersStack.head - private def boundMatchers_=(bas: Set[(T, Matcher[T])]): Unit = { - boundMatchersStack = bas :: boundMatchersStack.tail + private var instantiatedStack: List[Set[(T, Matcher[T])]] = List(Set.empty) + private def instantiated: Set[(T, Matcher[T])] = instantiatedStack.head + private def instantiated_=(ias: Set[(T, Matcher[T])]): Unit = { + instantiatedStack = ias :: instantiatedStack.tail } private var knownStack: List[Set[T]] = List(Set.empty) private def known(idT: T): Boolean = knownStack.head(idT) || byID.isDefinedAt(idT) override def push(): Unit = { - bindingsStack = bindings :: bindingsStack quantificationsStack = quantifications :: quantificationsStack - instantiatedMatchersStack = instantiatedMatchers :: instantiatedMatchersStack - boundMatchersStack = boundMatchers :: boundMatchersStack + instantiatedStack = instantiated :: instantiatedStack knownStack = knownStack.head :: knownStack } override def pop(lvl: Int): Unit = { - bindingsStack = bindingsStack.drop(lvl) quantificationsStack = quantificationsStack.drop(lvl) - instantiatedMatchersStack = instantiatedMatchersStack.drop(lvl) - boundMatchersStack = boundMatchersStack.drop(lvl) + instantiatedStack = instantiatedStack.drop(lvl) knownStack = knownStack.drop(lvl) } @@ -154,6 +116,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage val holdVar: T, val guardVar: T, var currentHolder: T, + val quantified: Set[T], val condVars: Map[Identifier, T], val exprVars: Map[Identifier, T], val clauses: Seq[T], @@ -163,11 +126,11 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage val lambdas: Map[T, LambdaTemplate[T]]) { def this( - qVar: T, holdVar: T, guardVar: T, + qVar: T, holdVar: T, guardVar: T, quantified: Set[T], condVars: Map[Identifier, T], exprVars: Map[Identifier, T], clauses: Seq[T], blockers: Map[T, Set[TemplateCallInfo[T]]], applications: Map[T, Set[App[T]]], matchers: Set[Matcher[T]], lambdas: Map[T, LambdaTemplate[T]]) = { - this(qVar, holdVar, guardVar, nextHolder(), + this(qVar, holdVar, guardVar, nextHolder(), quantified, condVars, exprVars, clauses, blockers, applications, matchers, lambdas) } @@ -176,7 +139,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage new Quantification ( qVar, holdVar, guardVar, currentHolder, - condVars, exprVars, + quantified, condVars, exprVars, clauses map substituter, blockers map { case (b, fis) => substituter(b) -> fis.map(fi => fi.copy(args = fi.args.map(substituter))) @@ -191,7 +154,7 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage ) } - def instantiate(blocker: T, matcher: Matcher[T], quantifierSubst: Map[T, T]): Instantiation[T] = { + def instantiate(blocker: T, matcher: Matcher[T]): Instantiation[T] = { val Matcher(caller, tpe, args) = matcher // Build a mapping from applications in the quantified statement to all potential concrete @@ -209,22 +172,19 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage if (qm == bindingMatcher) { bindingMatcher -> Set(blocker -> matcher) } else { - val instances: Seq[(T, Matcher[T])] = instantiatedMatchers.collect { - case (b, m @ Matcher(caller, tpe, _), _) if tpe == qtpe && (qcaller == caller || !known(caller)) => (b, m) + val instances: Set[(T, Matcher[T])] = instantiated.filter { + case (b, m @ Matcher(caller, tpe, _)) => tpe == qtpe && (qcaller == caller || !known(caller)) } // concrete applications can appear multiple times in the constraint, and this is also the case // for the current application for which we are generating the constraints val withCurrent = if (tpe == qtpe && (qcaller == caller || !known(caller))) { - instances :+ (blocker -> matcher) + instances + (blocker -> matcher) } else instances - // add quantified application to instances for constraints that depend on free variables - // this also make sure that constraints that don't depend on all variables will also be instantiated - // Note: we can use `blocker` here as the blocker since it is guaranteed true in this branch - val withAll = withCurrent :+ (blocker -> qm.copy(args = qm.args.map(a => quantifierSubst.getOrElse(a, a)))) + println(qm -> withCurrent) - qm -> withAll + qm -> withCurrent } } // 2.2. based on the possible bindings for each quantified application, build a set of @@ -247,41 +207,24 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage _ = constraints :+= b _ = if (qcaller != caller) constraints :+= encoder.mkEquals(qcaller, caller) (qarg, arg) <- (qargs zip args) - } if (subst.isDefinedAt(qarg) || !quantified(qarg)) { + } if (subst.isDefinedAt(qarg)) { + constraints :+= encoder.mkEquals(subst(qarg), arg) + } else if (!quantified(qarg)) { constraints :+= encoder.mkEquals(qarg, arg) } else { subst += qarg -> arg } - for ((q,nq) <- quantifierSubst if !subst.isDefinedAt(q)) { - subst += q -> nq - } - val enabler = if (constraints.size == 1) constraints.head else encoder.mkAnd(constraints : _*) val newHolder = nextHolder() + val baseSubstMap = (condVars ++ exprVars).map { case (id, idT) => idT -> encoder.encodeId(id) } val lambdaSubstMap = lambdas map { case (idT, lambda) => idT -> encoder.encodeId(lambda.id) } - val substMap = subst ++ lambdaSubstMap + (qVar -> currentHolder) + (guardVar -> enabler) + (holdVar -> newHolder) - val substituter = encoder.substitute(substMap) - - val newClauses = clauses map substituter - val newBlockers = blockers map { case (b, fis) => - substituter(b) -> fis.map(fi => fi.copy(args = fi.args.map(substituter))) - } - - instantiation ++= (newClauses, newBlockers, Map.empty) - - for ((idT, lambda) <- lambdas) { - val newIdT = substituter(idT) - val newTemplate = lambda.substitute(substMap) - instantiation ++= instantiateLambda(newIdT, newTemplate) - } - - for ((b, apps) <- applications; bp = substituter(b); app <- apps) { - val newApp = app.copy(caller = substituter(app.caller), args = app.args.map(substituter)) - instantiation ++= instantiateApp(bp, newApp) - } + val substMap = subst ++ baseSubstMap ++ lambdaSubstMap + + (qVar -> currentHolder) + (guardVar -> enabler) + (holdVar -> newHolder) + instantiation ++= Template.instantiate(encoder, QuantificationManager.this, + clauses, blockers, applications, Seq.empty, Map.empty, lambdas, substMap) currentHolder = newHolder } @@ -291,106 +234,56 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage def instantiateQuantification(template: QuantificationTemplate[T], substMap: Map[T, T]): Instantiation[T] = { - val (quantification, matchers) = { - val quantified = template.quantifiers.map(_._2).toSet - - val allMatchers: Set[Matcher[T]] = { - def rec(templates: Map[T, LambdaTemplate[T]]): Set[Matcher[T]] = templates.flatMap { - case (_, template) => template.matchers.flatMap(_._2).toSet ++ rec(template.lambdas) - }.toSet - - val allMatchers = template.matchers.flatMap(_._2).toSet ++ rec(template.lambdas) - for (m @ Matcher(caller, tpe, args) <- allMatchers if args exists quantified) yield m - } + val instantiationSubst: Map[T, T] = substMap + + (template.guardVar -> encoder.encodeExpr(Map.empty)(BooleanLiteral(true))) - val q = new Quantification( - template.qs._2, - template.holdVar, - template.guardVar, - template.qs._2, // this Quantification will never be instantiated, so this increases sanity - template.condVars, - template.exprVars, - template.clauses, - template.blockers, - template.applications, - allMatchers, - template.lambdas - ) - - val tpeCounts = template.quantifiers.groupBy(_._1.getType).mapValues(_.map(_._2).toSeq) - val qSubst = tpeCounts.flatMap { case (tpe, idTs) => idTs zip standardQuantifiers(tpe, idTs.size) }.toMap - val subst = substMap ++ qSubst - - val quantification = q.substitute(subst) - - val substituter = encoder.substitute(subst) - val matchers = template.matchers.map { case (b, ms) => - substituter(b) -> ms.map(m => m.copy(caller = substituter(m.caller), args = m.args.map(substituter))) - } - - (quantification, matchers) + val substituter = encoder.substitute(instantiationSubst) + val matchers = template.matchers.map { case (b, ms) => + substituter(b) -> ms.map(m => m.copy(caller = substituter(m.caller), args = m.args.map(substituter))) } - val quantifierSubst: Map[T,T] = freshQuantifierSubst - var instantiation: Instantiation[T] = Template.instantiate(encoder, this, - quantification.clauses, quantification.blockers, quantification.applications, - Seq.empty, matchers, quantification.lambdas, - quantifierSubst + (quantification.guardVar -> encoder.encodeExpr(Map.empty)(BooleanLiteral(true))) - ) - - val qBindings: Bindings = quantification.matchers.flatMap { - case Matcher(caller, tpe, args) => args.zipWithIndex.collect { - case (qid, idx) if quantified(qid) => - (if (known(caller)) Some(caller) else None, tpe, idx, qid) - } - } + template.clauses, template.blockers, template.applications, + Seq.empty, Map.empty, template.lambdas, instantiationSubst) - val (callerBindings, typeBindings) = (bindings ++ qBindings).partition(_._1.isDefined) + val quantified = template.quantifiers.map(_._2).toSet - val callerMap: Map[(T, Int), Set[T]] = callerBindings.groupBy(p => (p._1.get, p._3)).mapValues(_.map(_._4)) - val typeMap: Map[(TypeTree, Int), Set[T]] = typeBindings.groupBy(p => (p._2, p._3)).mapValues(_.map(_._4)) + val allMatchers: Set[Matcher[T]] = { + def rec(templates: Map[T, LambdaTemplate[T]]): Set[Matcher[T]] = templates.flatMap { + case (_, template) => template.matchers.flatMap(_._2).toSet ++ rec(template.lambdas) + }.toSet - val pairs: Set[(T, T)] = qBindings.flatMap { case (optIdT, tpe, idx, q) => - val matches = typeMap.getOrElse(tpe -> idx, Set.empty) ++ optIdT.toSeq.flatMap(idT => callerMap(idT -> idx)) - matches.map(q2 => q -> q2) + val allMatchers = template.matchers.flatMap(_._2).toSet ++ rec(template.lambdas) + for (m @ Matcher(caller, tpe, args) <- allMatchers if args exists quantified) yield m } - val mappings: List[Map[T, T]] = - pairs.groupBy(_._1).toSeq.foldLeft(List(Map.empty[T, T])) { - case (mappings, (_, pairs)) => pairs.toList.flatMap(p => mappings.map(mapping => mapping + p)) + val matchQuorums: Seq[Set[Matcher[T]]] = allMatchers.subsets.filter { ms => + var doubled: Boolean = false + var qs: Set[T] = Set.empty + for (m @ Matcher(_, _, args) <- ms) { + val qargs = (args filter quantified).toSet + if ((qs & qargs).nonEmpty) doubled = true + qs ++= qargs } - val newQuantifications = for (mapping <- mappings) yield { - val newQ = encoder.encodeId(FreshIdentifier("q", BooleanType)) - - val freshConds = quantification.condVars.map(p => p._1.freshen -> p._2) - val freshExprs = quantification.exprVars.map(p => p._1.freshen -> p._2) + !doubled && (qs == quantified) + }.toSeq - val substMap: Map[T, T] = mapping ++ - (freshConds ++ freshExprs).map { case (id, idT) => idT -> encoder.encodeId(id) } ++ - quantification.lambdas.map { case (idT, template) => idT -> encoder.encodeId(template.id) } + - (quantification.qVar -> newQ) - - val substituter = encoder.substitute(substMap) + val newQuantifications = for (matchers <- matchQuorums) yield { + val newQ = nextQ() new Quantification(newQ, - quantification.holdVar, quantification.guardVar, - newQ, // set the fresh qVar as `currentHolder` to ensure blocking before first unfolding - freshConds mapValues substituter, - freshExprs mapValues substituter, - quantification.clauses map substituter, - quantification.blockers map { case (b, fis) => - substituter(b) -> fis.map(fi => fi.copy(args = fi.args.map(substituter))) - }, - quantification.applications map { case (b, fas) => - substituter(b) -> fas.map(fa => fa.copy(caller = substituter(fa.caller), args = fa.args.map(substituter))) - }, - quantification.matchers map { case m @ Matcher(caller, _, args) => - m.copy(caller = substituter(caller), args = args.map(substituter)) - }, - quantification.lambdas map { case (idT, template) => substituter(idT) -> template.substitute(mapping) } - ) + template.holdVar, template.guardVar, + newQ, // set template `qVar` as `currentHolder` to ensure blocking before first unfolding + quantified, + template.condVars, + template.exprVars, + template.clauses, + template.blockers, + template.applications, + matchers, + template.lambdas + ).substitute(substMap) } val eqClause = { @@ -401,39 +294,30 @@ class QuantificationManager[T](encoder: TemplateEncoder[T]) extends LambdaManage instantiation = (instantiation._1 :+ eqClause, instantiation._2, instantiation._3) - // ensure the `quantifierSubst` associated to each instantiated app covers all necessary - // parameters for the current new quantifications - instantiatedMatchers = for ((b, m, qSubst) <- instantiatedMatchers) yield { - val nqSubst = if (qSubst.size == stdQuantifiers.map(_._2.size).sum) qSubst else { - stdQuantifiers.flatMap { case (tpe, ids) => - val recent = ids.dropWhile(qSubst.isDefinedAt) - recent zip recent.map(_ => encoder.encodeId(FreshIdentifier("x", tpe))) - }.toMap - } + val saved = instantiated - (b, m, nqSubst) + for ((b, ms) <- matchers; bp = substituter(b); m <- ms) { + val newM = m.copy(caller = substituter(m.caller), args = m.args.map(substituter)) + instantiation ++= instantiateMatcher(bp, newM) } - quantifications ++= newQuantifications - - for ((b, m, qSubst) <- instantiatedMatchers; q <- newQuantifications) { - instantiation ++= q.instantiate(b, m, qSubst) + for ((b, m) <- saved; q <- newQuantifications) { + instantiation ++= q.instantiate(b, m) } + quantifications ++= newQuantifications + instantiation } def instantiateMatcher(blocker: T, matcher: Matcher[T]): Instantiation[T] = { - val qInst = if (boundMatchers(blocker -> matcher)) Instantiation.empty[T] else { - val quantifierSubst: Map[T, T] = freshQuantifierSubst - + val qInst = if (instantiated(blocker -> matcher)) Instantiation.empty[T] else { var instantiation = Instantiation.empty[T] for (q <- quantifications) { - instantiation ++= q.instantiate(blocker, matcher, quantifierSubst) + instantiation ++= q.instantiate(blocker, matcher) } - instantiatedMatchers :+= (blocker, matcher, quantifierSubst) - boundMatchers += (blocker -> matcher) + instantiated += (blocker -> matcher) instantiation } diff --git a/src/main/scala/leon/solvers/templates/Templates.scala b/src/main/scala/leon/solvers/templates/Templates.scala index ab63924ef3918cbd664d72261dabf31d6edb1d3e..376c411c2fcbb9fbfc53be6549ae60f624c34cc0 100644 --- a/src/main/scala/leon/solvers/templates/Templates.scala +++ b/src/main/scala/leon/solvers/templates/Templates.scala @@ -70,7 +70,8 @@ trait Template[T] { self => } val lambdaSubstMap = lambdas.map { case (idT, lambda) => idT -> encoder.encodeId(lambda.id) } - val substMap : Map[T,T] = baseSubstMap ++ lambdaSubstMap + (start -> aVar) + val quantificationSubstMap = quantifications.map(q => q.qs._2 -> encoder.encodeId(q.qs._1)) + val substMap : Map[T,T] = baseSubstMap ++ lambdaSubstMap ++ quantificationSubstMap + (start -> aVar) Template.instantiate(encoder, manager, clauses, blockers, applications, quantifications, matchers, lambdas, substMap) @@ -273,7 +274,7 @@ object FunctionTemplate { lambdas: Map[T, LambdaTemplate[T]], isRealFunDef: Boolean ) : FunctionTemplate[T] = { - + val (clauses, blockers, applications, matchers, templateString) = Template.encode(encoder, pathVar, arguments, condVars, exprVars, guardedExprs, lambdas, substMap = quantifications.map(q => q.qs).toMap,