diff --git a/src/main/scala/leon/solvers/templates/QuantificationManager.scala b/src/main/scala/leon/solvers/templates/QuantificationManager.scala index 3503033f577a4c19ddadc3dfea835d68aae81c4c..58136e7e5b46fb0d569a9dbc3a4cd1482c7cb1f9 100644 --- a/src/main/scala/leon/solvers/templates/QuantificationManager.scala +++ b/src/main/scala/leon/solvers/templates/QuantificationManager.scala @@ -12,21 +12,32 @@ import Instantiation._ trait QuantificationManager[T] { self : LambdaManager[T] => - lazy val startId = FreshIdentifier("q", BooleanType) - lazy val start = encoder.encodeId(startId) - private class QuantificationScope( - val quantifiedApps: List[(T, App[T])], - val quantifiedID: Map[Identifier, T], + val quantifiedApps: List[App[T]], + val quantifiers: Map[Identifier, T], val condVars: Map[Identifier, T], val exprVars: Map[Identifier, T], val clauses: Seq[T], val blockers: Map[T, Set[TemplateCallInfo[T]]], val applications: Map[T, Set[App[T]]], - val lambdas: Map[T, LambdaTemplate[T]] + val lambdas: Map[T, LambdaTemplate[T]], + val res: Set[T] ) { - def this() = this(Nil, Map.empty, Map.empty, Map.empty, Seq.empty, Map.empty, Map.empty, Map.empty) - lazy val quantified: Set[T] = quantifiedID.values.toSet + def this() = this(Nil, Map.empty, Map.empty, Map.empty, Seq.empty, Map.empty, Map.empty, Map.empty, Set.empty) + def this(scope: QuantificationScope) = this( + scope.quantifiedApps, + scope.quantifiers, + scope.condVars, + scope.exprVars, + scope.clauses, + scope.blockers, + scope.applications, + scope.lambdas, + scope.res + ) + + lazy val quantified: Set[T] = quantifiers.values.toSet + def free(tpe: FunctionType, idT: T): Boolean = res(idT) || freeLambdas(tpe)(idT) } private var scopes: List[QuantificationScope] = List(new QuantificationScope) @@ -36,16 +47,7 @@ trait QuantificationManager[T] { self : LambdaManager[T] => self.push() val currentScope = scope - scopes = new QuantificationScope( - currentScope.quantifiedApps, - currentScope.quantifiedID, - currentScope.condVars, - currentScope.exprVars, - currentScope.clauses, - currentScope.blockers, - currentScope.applications, - currentScope.lambdas - ) :: scopes.tail + scopes = new QuantificationScope(currentScope) :: scopes.tail } override def pop(lvl: Int): Unit = { @@ -53,16 +55,36 @@ trait QuantificationManager[T] { self : LambdaManager[T] => scopes = scopes.drop(lvl) } - def registerQuantified(start: T, condVars: Map[Identifier, T], exprVars: Map[Identifier, T], clauses: Seq[T], blockers: Map[T, Set[TemplateCallInfo[T]]], apps: Set[(T, App[T])], quantified: Set[T]): Unit = { + def registerQuantified( + startVar: Identifier, + quantifierVars: Seq[Identifier], + condVars: Map[Identifier, T], + exprVars: Map[Identifier, T], + guardedExprs: Map[Identifier, Seq[Expr]], + lambdas: Map[T, LambdaTemplate[T]], + subst: Map[Identifier, T], + res: Option[T] + ): Unit = { + val currentScope = scope + def free(tpe: FunctionType, idT: T): Boolean = currentScope.free(tpe, idT) || res.exists(_ == idT) + + val quantifiers: Seq[(Identifier, T)] = quantifierVars.map(id => id -> encoder.encodeId(id)) + val quantified: Set[T] = quantifiers.map(_._2).toSet + val pathVar = startVar -> encoder.encodeId(startVar) + val (clauses, blockers, apps, _) = Template.encode(encoder, pathVar, quantifiers, condVars, exprVars, guardedExprs, lambdas, subst) - val bindingCalls: Set[App[T]] = apps.collect { - case (b, app @ App(caller, tpe, args)) if args exists quantified => app + val bindingApps: Set[App[T]] = { + def rec(templates: Map[T, LambdaTemplate[T]]): Set[App[T]] = templates.flatMap { + case (_, template) => template.applications.flatMap(_._2).toSet ++ rec(template.lambdas) + }.toSet + + val allApps = apps.flatMap(_._2).toSet + for (app @ App(caller, tpe, args) <- (allApps ++ rec(lambdas)) if args exists quantified) yield app } - // TODO: postcondition res ?? - val argumentBindings: Set[(Option[T], FunctionType, Int, T)] = bindingCalls.flatMap { + val argumentBindings: Set[(Option[T], FunctionType, Int, T)] = bindingApps.flatMap { case App(caller, tpe, args) => args.zipWithIndex.collect { - case (qid, idx) if quantified(qid) => (if (freeLambdas(tpe)(caller)) Some(caller) else None, tpe, idx, qid) + case (qid, idx) if quantified(qid) => (if (free(tpe, caller)) Some(caller) else None, tpe, idx, qid) } } @@ -103,12 +125,48 @@ trait QuantificationManager[T] { self : LambdaManager[T] => case (mappings, (_, pairs)) => pairs.toList.flatMap(p => mappings.map(mapping => mapping + p)) } - val expandedClauses = mappings.flatMap { mapping => - val substituter = encoder.substitute(mapping) - clauses map substituter + val (allClauses, allBlockers, allApps) = { + var allClauses : Seq[T] = Seq.empty + var allBlockers : Map[T, Set[TemplateCallInfo[T]]] = Map.empty.withDefaultValue(Set.empty) + var allApps : Map[T, Set[App[T]]] = Map.empty.withDefaultValue(Set.empty) + + for (mapping <- mappings) { + val substituter = encoder.substitute(mapping) + allClauses ++= clauses.map(substituter) + + blockers.foreach { case (b, fis) => + val bp = substituter(b) + val fisp = fis.map(fi => fi.copy(args = fi.args.map(substituter))) + allBlockers += bp -> (allBlockers(bp) ++ fisp) + } + + apps.foreach { case (b, fas) => + val bp = substituter(b) + val fasp = fas.map(fa => fa.copy(caller = substituter(fa.caller), args = fa.args.map(substituter))) + allApps += bp -> (allApps(bp) ++ fasp) + } + } + + (allClauses, allBlockers, allApps) } - + val quantifiedApps: Set[(T, App[T])] = for { + (b, bApps) <- allApps.toSet + app @ App(caller, tpe, args) <- bApps + if args exists quantified + } yield (b, app) + + scopes = new QuantificationScope( + currentScope.quantifiedApps ++ quantifiedApps, + currentScope.quantifiers ++ quantifiers, + currentScope.condVars ++ condVars, + currentScope.exprVars ++ exprVars, + currentScope.clauses ++ allClauses, + currentScope.blockers merge allBlockers, + currentScope.applications merge allApps, + currentScope.lambdas ++ lambdas, + currentScope.res ++ res + ) :: scopes.tail } override def instantiateApp(blocker: T, app: App[T]): Instantiation[T] = { @@ -120,24 +178,24 @@ trait QuantificationManager[T] { self : LambdaManager[T] => // applications previously encountered. Also make sure the current `app` is in the mapping // as other instantiations have been performed previously when the associated applications // were first encountered. - val appMappings: List[List[((T, App[T]), (T, App[T]))]] = quantifiedApps + val appMappings: List[List[(App[T], App[T])]] = quantifiedApps // 1. select an application in the quantified proposition for which the current app can // be bound when generating the new constraints - .filter { case (qb, qapp) => - qapp.caller == caller || (qapp.tpe == tpe && !freeLambdas(qapp.tpe)(qapp.caller)) - } + .filter(qapp => qapp.caller == caller || (qapp.tpe == tpe && !free(qapp.tpe, qapp.caller))) // 2. build the instantiation mapping associated to the chosen current application binding .flatMap { bindingApp => quantifiedApps // 2.1. select all potential matches for each quantified application - .map { case qapp @ (qb, App(qcaller, qtpe, qargs)) => + .map { case qapp @ App(qcaller, qtpe, qargs) => if (qapp == bindingApp) { - bindingApp -> Set(blocker -> app) + bindingApp -> Set(app) } else { - val instances = self.applications(qtpe).filter(p => qcaller == p._2.caller || !freeLambdas(qtpe)(p._2.caller)) + val instances = self.applications(qtpe).collect { + case (_, app @ App(caller, _, _)) if qcaller == caller || !free(qtpe, caller) => app + } // 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 withApp = if (qcaller == caller || !freeLambdas(tpe)(caller)) instances + (blocker -> app) else instances + val withApp = if (qcaller == caller || !free(tpe, caller)) instances + app 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 @@ -148,7 +206,7 @@ trait QuantificationManager[T] { self : LambdaManager[T] => } // 2.2. based on the possible bindings for each quantified application, build a set of // instantiation mappings that can be used to instantiate all necessary constraints - .foldLeft[List[List[((T, App[T]), (T, App[T]))]]] (List(Nil)) { + .foldLeft[List[List[(App[T], App[T])]]] (List(Nil)) { case (mappings, (qapp, instances)) => instances.toList.flatMap(app => mappings.map(mapping => mapping :+ (app -> qapp))) } @@ -161,8 +219,7 @@ trait QuantificationManager[T] { self : LambdaManager[T] => var subst: Map[T, T] = Map.empty for { - ((qb, App(qcaller, _, qargs)), (b, App(caller, _, args))) <- mapping - _ = constraints ++= Seq(qb, b) + (App(qcaller, _, qargs), App(caller, _, args)) <- mapping _ = if (qcaller != caller) constraints :+= encoder.mkEquals(qcaller, caller) (qarg, arg) <- (qargs zip args) } if (subst.isDefinedAt(qarg) || !quantified(qarg)) { @@ -172,7 +229,7 @@ trait QuantificationManager[T] { self : LambdaManager[T] => } // make sure we freshen quantified variables that haven't been bound by concrete calls - subst ++= quantifiedID.collect { + subst ++= quantifiers.collect { case (id, idT) if !subst.isDefinedAt(idT) => idT -> encoder.encodeId(id) } diff --git a/src/main/scala/leon/solvers/templates/Templates.scala b/src/main/scala/leon/solvers/templates/Templates.scala index fddce8a08e50c3a6083edf0ae136aaf2b1b8fb3e..f19357a1ce4c174c1c2a42bcf13019c49933b301 100644 --- a/src/main/scala/leon/solvers/templates/Templates.scala +++ b/src/main/scala/leon/solvers/templates/Templates.scala @@ -25,16 +25,18 @@ object Instantiation { def empty[T] = (Seq.empty[T], Map.empty[T, Set[TemplateCallInfo[T]]], Map.empty[(T, App[T]), Set[TemplateAppInfo[T]]]) + implicit class MapWrapper[A,B](map: Map[A,Set[B]]) { + def merge(that: Map[A,Set[B]]): Map[A,Set[B]] = (map.keys ++ that.keys).map { k => + k -> (map.getOrElse(k, Set.empty) ++ that.getOrElse(k, Set.empty)) + }.toMap + } + implicit class InstantiationWrapper[T](i: Instantiation[T]) { def ++(that: Instantiation[T]): Instantiation[T] = { val (thisClauses, thisBlockers, thisApps) = i val (thatClauses, thatBlockers, thatApps) = that - ( - thisClauses ++ thatClauses, - (thisBlockers.keys ++ thatBlockers.keys).map(k => k -> (thisBlockers.getOrElse(k, Set.empty) ++ thatBlockers.getOrElse(k, Set.empty))).toMap, - (thisApps.keys ++ thatApps.keys).map(k => k -> (thisApps.getOrElse(k, Set.empty) ++ thatApps.getOrElse(k, Set.empty))).toMap - ) + (thisClauses ++ thatClauses, thisBlockers merge thatBlockers, thisApps merge thatApps) } } }