diff --git a/library/lang/package.scala b/library/lang/package.scala index df7c29d010d540b60e743c099a45ab6546b3791f..44351d02ca9328bbbf86063745d233f2db17cd7c 100644 --- a/library/lang/package.scala +++ b/library/lang/package.scala @@ -30,6 +30,12 @@ package object lang { } } + @ignore def forall[A](p: A => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore def forall[A,B](p: (A,B) => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore def forall[A,B,C](p: (A,B,C) => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore def forall[A,B,C,D](p: (A,B,C,D) => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore def forall[A,B,C,D,E](p: (A,B,C,D,E) => Boolean): Boolean = sys.error("Can't execute quantified proposition") + @ignore object InvariantFunction { def invariant(x: Boolean): Unit = () diff --git a/src/main/scala/leon/solvers/templates/LambdaManager.scala b/src/main/scala/leon/solvers/templates/LambdaManager.scala index 37de1a88284a5659953da4a6c3bc77cb8a833046..3b355a4b7a960279f4dfd9d65ebfbdec4337147e 100644 --- a/src/main/scala/leon/solvers/templates/LambdaManager.scala +++ b/src/main/scala/leon/solvers/templates/LambdaManager.scala @@ -11,31 +11,32 @@ import purescala.Types._ import Instantiation._ -class LambdaManager[T](encoder: TemplateEncoder[T]) { - private type IdMap = Map[T, LambdaTemplate[T]] +class LambdaManager[T](protected val encoder: TemplateEncoder[T]) { + + protected type IdMap = Map[T, LambdaTemplate[T]] + protected def byID : IdMap = byIDStack.head private var byIDStack : List[IdMap] = List(Map.empty) - private def byID : IdMap = byIDStack.head private def byID_=(map: IdMap) : Unit = { byIDStack = map :: byIDStack.tail } - private type TypeMap = Map[TypeTree, Set[(T, LambdaTemplate[T])]] + protected type TypeMap = Map[FunctionType, Set[(T, LambdaTemplate[T])]] + protected def byType : TypeMap = byTypeStack.head private var byTypeStack : List[TypeMap] = List(Map.empty.withDefaultValue(Set.empty)) - private def byType : TypeMap = byTypeStack.head private def byType_=(map: TypeMap) : Unit = { byTypeStack = map :: byTypeStack.tail } - private type ApplicationMap = Map[TypeTree, Set[(T, App[T])]] + protected type ApplicationMap = Map[FunctionType, Set[(T, App[T])]] + protected def applications : ApplicationMap = applicationsStack.head private var applicationsStack : List[ApplicationMap] = List(Map.empty.withDefaultValue(Set.empty)) - private def applications : ApplicationMap = applicationsStack.head private def applications_=(map: ApplicationMap) : Unit = { applicationsStack = map :: applicationsStack.tail } - private type FreeMap = Map[TypeTree, Set[T]] + protected type FreeMap = Map[FunctionType, Set[T]] + protected def freeLambdas : FreeMap = freeLambdasStack.head private var freeLambdasStack : List[FreeMap] = List(Map.empty.withDefaultValue(Set.empty)) - private def freeLambdas : FreeMap = freeLambdasStack.head private def freeLambdas_=(map: FreeMap) : Unit = { freeLambdasStack = map :: freeLambdasStack.tail } @@ -55,67 +56,61 @@ class LambdaManager[T](encoder: TemplateEncoder[T]) { } def registerFree(lambdas: Seq[(TypeTree, T)]): Unit = { - lambdas.foreach(p => freeLambdas += p._1 -> (freeLambdas(p._1) + p._2)) + for ((tpe, idT) <- lambdas) tpe match { + case ft: FunctionType => + freeLambdas += ft -> (freeLambdas(ft) + idT) + case _ => + } } - private def instantiate(apps: Map[T, Set[App[T]]], lambdas: Map[T, LambdaTemplate[T]]) : Instantiation[T] = { - var clauses : Clauses[T] = Seq.empty - var callBlockers : CallBlockers[T] = Map.empty.withDefaultValue(Set.empty) + def instantiateLambda(idT: T, template: LambdaTemplate[T]): Instantiation[T] = { + var clauses : Clauses[T] = equalityClauses(idT, template) var appBlockers : AppBlockers[T] = Map.empty.withDefaultValue(Set.empty) - def mkBlocker(blockedApp: (T, App[T]), lambda: (T, LambdaTemplate[T])) : Unit = { - val (_, App(caller, tpe, args)) = blockedApp - val (idT, template) = lambda + // make sure the new lambda isn't equal to any free lambda var + clauses ++= freeLambdas(template.tpe).map(pIdT => encoder.mkNot(encoder.mkEquals(pIdT, idT))) + byID += idT -> template + byType += template.tpe -> (byType(template.tpe) + (idT -> template)) + + for (blockedApp @ (_, App(caller, tpe, args)) <- applications(template.tpe)) { val equals = encoder.mkEquals(idT, caller) appBlockers += (blockedApp -> (appBlockers(blockedApp) + TemplateAppInfo(template, equals, args))) } - for (lambda @ (idT, template) <- lambdas) { - // make sure the new lambda isn't equal to any free lambda var - clauses ++= freeLambdas(template.tpe).map(pIdT => encoder.mkNot(encoder.mkEquals(pIdT, idT))) - - byID += idT -> template - byType += template.tpe -> (byType(template.tpe) + (idT -> template)) - - for (guardedApp <- applications(template.tpe)) { - mkBlocker(guardedApp, lambda) - } - } + (clauses, Map.empty, appBlockers) + } - for ((b, fas) <- apps; app @ App(caller, tpe, args) <- fas) { - if (byID contains caller) { - val (newClauses, newCalls, newApps) = byID(caller).instantiate(b, args) + def instantiateApp(blocker: T, app: App[T]): Instantiation[T] = { + val App(caller, tpe, args) = app + var clauses : Clauses[T] = Seq.empty + var callBlockers : CallBlockers[T] = Map.empty.withDefaultValue(Set.empty) + var appBlockers : AppBlockers[T] = Map.empty.withDefaultValue(Set.empty) - clauses ++= newClauses - newCalls.foreach(p => callBlockers += p._1 -> (callBlockers(p._1) ++ p._2)) - newApps.foreach(p => appBlockers += p._1 -> (appBlockers(p._1) ++ p._2)) - } else if (!freeLambdas(tpe).contains(caller)) { - val key = b -> app + if (byID contains caller) { + val (newClauses, newCalls, newApps) = byID(caller).instantiate(blocker, args) - // make sure that even if byType(tpe) is empty, app is recorded in blockers - // so that UnrollingBank will generate the initial block! - if (!(appBlockers contains key)) appBlockers += key -> Set.empty + clauses ++= newClauses + newCalls.foreach(p => callBlockers += p._1 -> (callBlockers(p._1) ++ p._2)) + newApps.foreach(p => appBlockers += p._1 -> (appBlockers(p._1) ++ p._2)) + } else if (!freeLambdas(tpe).contains(caller)) { + val key = blocker -> app - for (lambda <- byType(tpe)) { - mkBlocker(key, lambda) - } + // make sure that even if byType(tpe) is empty, app is recorded in blockers + // so that UnrollingBank will generate the initial block! + if (!(appBlockers contains key)) appBlockers += key -> Set.empty - applications += tpe -> (applications(tpe) + key) + for ((idT,template) <- byType(tpe)) { + val equals = encoder.mkEquals(idT, caller) + appBlockers += (key -> (appBlockers(key) + TemplateAppInfo(template, equals, args))) } + + applications += tpe -> (applications(tpe) + key) } (clauses, callBlockers, appBlockers) } - def instantiateLambda(idT: T, template: LambdaTemplate[T]): Instantiation[T] = { - val eqClauses = equalityClauses(idT, template) - val (clauses, blockers, apps) = instantiate(Map.empty, Map(idT -> template)) - (eqClauses ++ clauses, blockers, apps) - } - - def instantiateApps(apps: Map[T, Set[App[T]]]): Instantiation[T] = instantiate(apps, Map.empty) - private def equalityClauses(idT: T, template: LambdaTemplate[T]): Seq[T] = { byType(template.tpe).map { case (thatIdT, that) => val equals = encoder.mkEquals(idT, thatIdT) diff --git a/src/main/scala/leon/solvers/templates/QuantificationManager.scala b/src/main/scala/leon/solvers/templates/QuantificationManager.scala new file mode 100644 index 0000000000000000000000000000000000000000..3503033f577a4c19ddadc3dfea835d68aae81c4c --- /dev/null +++ b/src/main/scala/leon/solvers/templates/QuantificationManager.scala @@ -0,0 +1,210 @@ +package leon +package solvers +package templates + +import purescala.Common._ +import purescala.Extractors._ +import purescala.Expressions._ +import purescala.ExprOps._ +import purescala.Types._ + +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 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]] + ) { + 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 + } + + private var scopes: List[QuantificationScope] = List(new QuantificationScope) + private def scope: QuantificationScope = scopes.head + + override def push(): Unit = { + 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 + } + + override def pop(lvl: Int): Unit = { + self.pop(lvl) + 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 = { + + val bindingCalls: Set[App[T]] = apps.collect { + case (b, app @ App(caller, tpe, args)) if args exists quantified => app + } + + // TODO: postcondition res ?? + val argumentBindings: Set[(Option[T], FunctionType, Int, T)] = bindingCalls.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) + } + } + + val (callerBindings, typeBindings) = argumentBindings.partition(_._1.isDefined) + + val typeMap: Map[FunctionType, Set[(Int, T)]] = typeBindings.groupBy(_._2).mapValues(_.map(p => (p._3, p._4))) + val typePairs: Seq[(T, T)] = typeMap.toSeq.flatMap { + case (_, argBinds) => argBinds.groupBy(_._1).mapValues(_.map(_._2)).toSeq.flatMap { + case (_, options) => options.flatMap(o1 => options.map(o2 => o1 -> o2)) + } + } + + val callerPairs: Seq[(T, T)] = callerBindings.groupBy(p => (p._1.get, p._2)).toSeq.flatMap { + case ((freeFunction, tpe), arguments) => + val callerIdentified: Set[(Int, T)] = arguments.map(p => (p._3, p._4)) + val typeIdentified: Set[(Int, T)] = typeMap.getOrElse(tpe, Set.empty) + + (callerIdentified ++ typeIdentified).groupBy(_._1).mapValues(_.map(_._2)).toSeq.flatMap { + case (_, options) => options.flatMap(o1 => options.map(o2 => o1 -> o2)) + } + } + + val filteredPairs: Set[(T, T)] = { + def rec( + mappings: Seq[(T, T)], + result: Set[(T, T)] + ): Set[(T, T)] = mappings match { + case (p @ (x, y)) +: tail if !result(p) && !result(y -> x) => rec(tail, result + p) + case p +: tail => rec(tail, result) + case Seq() => result + } + + rec(callerPairs ++ typePairs, Set.empty) + } + + val mappings: List[Map[T, T]] = + filteredPairs.groupBy(_._1).toSeq.foldLeft(List(Map.empty[T, 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 + } + + + } + + override def instantiateApp(blocker: T, app: App[T]): Instantiation[T] = { + val App(caller, tpe, args) = app + val currentScope = scope + import currentScope._ + + // Build a mapping from applications in the quantified statement to all potential concrete + // 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 + // 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)) + } + // 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)) => + if (qapp == bindingApp) { + bindingApp -> Set(blocker -> app) + } else { + val instances = self.applications(qtpe).filter(p => qcaller == p._2.caller || !freeLambdas(qtpe)(p._2.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 withApp = if (qcaller == caller || !freeLambdas(tpe)(caller)) instances + (blocker -> 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 + val withAll = withApp + qapp + + qapp -> withAll + } + } + // 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)) { + case (mappings, (qapp, instances)) => + instances.toList.flatMap(app => mappings.map(mapping => mapping :+ (app -> qapp))) + } + } + + var instantiation = Instantiation.empty[T] + + for (mapping <- appMappings) { + var constraints: List[T] = Nil + var subst: Map[T, T] = Map.empty + + for { + ((qb, App(qcaller, _, qargs)), (b, App(caller, _, args))) <- mapping + _ = constraints ++= Seq(qb, b) + _ = if (qcaller != caller) constraints :+= encoder.mkEquals(qcaller, caller) + (qarg, arg) <- (qargs zip args) + } if (subst.isDefinedAt(qarg) || !quantified(qarg)) { + constraints :+= encoder.mkEquals(qarg, arg) + } else { + subst += qarg -> arg + } + + // make sure we freshen quantified variables that haven't been bound by concrete calls + subst ++= quantifiedID.collect { + case (id, idT) if !subst.isDefinedAt(idT) => idT -> encoder.encodeId(id) + } + + val bp = encoder.encodeId(startId) + val enabler = encoder.mkEquals(bp, encoder.mkAnd(constraints : _*)) + + val lambdaSubstMap = lambdas.map { case (idT, lambda) => idT -> encoder.encodeId(lambda.id) } + val substMap = subst ++ lambdaSubstMap + (start -> enabler) + val substituter = encoder.substitute(subst) + + val newClauses = clauses.map(substituter) + val newBlockers = blockers.map { case (b, fis) => + substituter(b) -> fis.map(fi => fi.copy(args = fi.args.map(substituter))) + } + + val newApplications = currentScope.applications.map { case (b, fas) => + substituter(b) -> fas.map(fa => fa.copy(caller = substituter(fa.caller), args = fa.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) <- newApplications; app <- apps) { + instantiation ++= instantiateApp(b, app) + } + } + + instantiation + } +} diff --git a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala index 0e0e7f063daebff985beda49f198deae8edbcde3..1eac5171272810efc23750e3745a7b03ce5fbf46 100644 --- a/src/main/scala/leon/solvers/templates/TemplateGenerator.scala +++ b/src/main/scala/leon/solvers/templates/TemplateGenerator.scala @@ -17,7 +17,7 @@ class TemplateGenerator[T](val encoder: TemplateEncoder[T], private var cache = Map[TypedFunDef, FunctionTemplate[T]]() private var cacheExpr = Map[Expr, FunctionTemplate[T]]() - private val lambdaManager = new LambdaManager[T](encoder) + private val lambdaManager = new LambdaManager[T](encoder) with QuantificationManager[T] def mkTemplate(body: Expr): FunctionTemplate[T] = { if (cacheExpr contains body) { diff --git a/src/main/scala/leon/solvers/templates/Templates.scala b/src/main/scala/leon/solvers/templates/Templates.scala index 38821e2a0a73c98fe45c05df0ff37b265d307aa9..fddce8a08e50c3a6083edf0ae136aaf2b1b8fb3e 100644 --- a/src/main/scala/leon/solvers/templates/Templates.scala +++ b/src/main/scala/leon/solvers/templates/Templates.scala @@ -11,7 +11,7 @@ import purescala.ExprOps._ import purescala.Types._ import purescala.Definitions._ -case class App[T](caller: T, tpe: TypeTree, args: Seq[T]) { +case class App[T](caller: T, tpe: FunctionType, args: Seq[T]) { override def toString = { "(" + caller + " : " + tpe + ")" + args.mkString("(", ",", ")") } @@ -26,7 +26,7 @@ object Instantiation { def empty[T] = (Seq.empty[T], Map.empty[T, Set[TemplateCallInfo[T]]], Map.empty[(T, App[T]), Set[TemplateAppInfo[T]]]) implicit class InstantiationWrapper[T](i: Instantiation[T]) { - def merge(that: Instantiation[T]): Instantiation[T] = { + def ++(that: Instantiation[T]): Instantiation[T] = { val (thisClauses, thisBlockers, thisApps) = i val (thatClauses, thatBlockers, thatApps) = that @@ -81,17 +81,19 @@ trait Template[T] { self => substituter(b) -> fas.map(fa => fa.copy(caller = substituter(fa.caller), args = fa.args.map(substituter))) } - val lambdaInstantiation = lambdas.foldLeft(Instantiation.empty[T]) { - case (acc, (idT, lambda)) => - val newIdT = substituter(idT) - val newTemplate = lambda.substitute(substMap) - val instantiation = lambdaManager.instantiateLambda(newIdT, newTemplate) - acc merge instantiation + var instantiation: Instantiation[T] = (newClauses, newBlockers, Map.empty) + + for ((idT, lambda) <- lambdas) { + val newIdT = substituter(idT) + val newTemplate = lambda.substitute(substMap) + instantiation ++= lambdaManager.instantiateLambda(newIdT, newTemplate) } - val appInstantiation = lambdaManager.instantiateApps(newApplications) + for ((b,apps) <- newApplications; app <- apps) { + instantiation ++= lambdaManager.instantiateApp(b, app) + } - (newClauses, newBlockers, Map.empty[(T, App[T]), Set[TemplateAppInfo[T]]]) merge lambdaInstantiation merge appInstantiation + instantiation } override def toString : String = "Instantiated template" @@ -132,7 +134,7 @@ object Template { val App(c, tpe, prevArgs) = applicationTemplate(caller) App(c, tpe, prevArgs ++ args.map(encodeExpr)) case Application(c, args) => - App(encodeExpr(c), c.getType, args.map(encodeExpr)) + App(encodeExpr(c), c.getType.asInstanceOf[FunctionType], args.map(encodeExpr)) case _ => scala.sys.error("Should never happen!") } @@ -151,7 +153,7 @@ object Template { lambdas: Map[T, LambdaTemplate[T]], substMap: Map[Identifier, T] = Map.empty[Identifier, T], optCall: Option[TypedFunDef] = None, - optApp: Option[(T, TypeTree)] = None + optApp: Option[(T, FunctionType)] = None ) : (Seq[T], Map[T, Set[TemplateCallInfo[T]]], Map[T, Set[App[T]]], () => String) = { val idToTrId : Map[Identifier, T] = { @@ -258,7 +260,6 @@ object FunctionTemplate { funString ) } - } class FunctionTemplate[T] private( @@ -344,7 +345,7 @@ object LambdaTemplate { ) : LambdaTemplate[T] = { val id = ids._2 - val tpe = ids._1.getType + val tpe = ids._1.getType.asInstanceOf[FunctionType] val (clauses, blockers, applications, templateString) = Template.encode(encoder, pathVar, arguments, condVars, exprVars, guardedExprs, lambdas, substMap = baseSubstMap + ids, optApp = Some(id -> tpe)) @@ -390,7 +391,7 @@ class LambdaTemplate[T] private ( private[templates] val structuralKey: Lambda, stringRepr: () => String) extends Template[T] { - val tpe = id.getType + val tpe = id.getType.asInstanceOf[FunctionType] def substitute(substMap: Map[T,T]): LambdaTemplate[T] = { val substituter : T => T = encoder.substitute(substMap)