
gapt.provers.Session.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gapt_3 Show documentation
Show all versions of gapt_3 Show documentation
General Architecture for Proof Theory
The newest version!
package gapt.provers
import java.io.{BufferedReader, InputStreamReader, PrintWriter}
import java.lang.ProcessBuilder.Redirect
import gapt.expr._
import gapt.formats.StringInputFile
import gapt.formats.lisp._
import gapt.proofs.{HOLSequent, Sequent}
import gapt.provers.smtlib.ExternalSmtlibProgram
import gapt.utils.NameGenerator
import cats.free.Free.liftF
import cats.free._
import cats.implicits._
import cats.{Id, ~>}
import gapt.expr.formula.All
import gapt.expr.formula.And
import gapt.expr.formula.Bottom
import gapt.expr.formula.Eq
import gapt.expr.formula.Ex
import gapt.expr.formula.Formula
import gapt.expr.formula.Imp
import gapt.expr.formula.Neg
import gapt.expr.formula.Or
import gapt.expr.formula.Top
import gapt.expr.formula.constants.EqC
import gapt.expr.formula.constants.LogicalConstant
import gapt.expr.ty.{->:, FunctionType, TArr, TBase, To, Ty, baseTypes}
import gapt.expr.util.constants
import scala.collection.mutable
/**
* Implementation of proof sessions via the cats free monad. See [[http://typelevel.org/cats/datatypes/freemonad.html]].
*/
object Session {
/**
* Trait for individual session commands.
* @tparam A The return type of the command.
*/
sealed trait SessionCommand[A]
object SessionCommand {
/**
* Pushes the current assertions and declarations on the stack.
*/
case object Push extends SessionCommand[Unit]
/**
* Pops the stack, eliminating all assertions and declarations since the last push.
*/
case object Pop extends SessionCommand[Unit]
/**
* Declares a function.
*/
case class DeclareFun(fun: Const) extends SessionCommand[Unit]
/**
* Declares a sort.
*/
case class DeclareSort(sort: TBase) extends SessionCommand[Unit]
/**
* Asserts a formula.
*/
case class Assert(formula: Formula) extends SessionCommand[Unit]
/**
* Asserts a formula with a label.
*/
case class AssertLabelled(formula: Formula, label: String) extends SessionCommand[Unit]
/**
* Checks whether the current set of declarations and assertions is satisfiable.
*/
case object CheckSat extends SessionCommand[Either[SExpression, Boolean]]
/**
* Sets the logic to be used for the session.
*/
case class SetLogic(logic: String) extends SessionCommand[Unit]
/**
* Sets an option to a value.
*/
case class SetOption(option: String, args: List[String]) extends SessionCommand[Unit]
/**
* Submits an SExpression and receives one in return.
*/
case class Ask(input: SExpression) extends SessionCommand[SExpression]
/**
* Submits an SExpression without a return.
*/
case class Tell(input: SExpression) extends SessionCommand[Unit]
}
type Session[A] = Free[SessionCommand, A]
import SessionCommand._
/*
* Smart constructors that lift the operations of SessionCommand into the free monad.
*/
/**
* Pushes the current assertions and declarations on the stack.
*/
def push = liftF(Push)
/**
* Pops the stack, eliminating all assertions and declarations since the last push.
*/
def pop = liftF(Pop)
/**
* Asserts a formula.
*/
def assert(f: Formula) = liftF(Assert(f))
/**
* Asserts a formula with a label.
*/
def assert(f: Formula, label: String) = liftF(AssertLabelled(f, label))
/**
* Checks whether the current set of declarations and assertions is satisfiable.
*/
def checkSat: Session[Either[SExpression, Boolean]] = liftF(CheckSat)
/**
* Checks whether the current set of declarations and assertions is not satisfiable.
*/
def checkUnsat: Session[Either[SExpression, Boolean]] = checkSat.map(_.map(!_))
/**
* Sets the logic to be used for the session.
*/
def setLogic(logic: String) = liftF(SetLogic(logic))
/**
* Sets an option to a value.
*/
def setOption(option: String, args: String*) = liftF(SetOption(option, args.toList))
/**
* Declares a sort.
*/
def declareSort(sort: TBase) = liftF(DeclareSort(sort))
/**
* Declares a function.
*/
def declareFun(fun: Const) = liftF(DeclareFun(fun))
/**
* Submits an SExpression and receives one in return.
*/
def ask(input: SExpression) = liftF(Ask(input))
/**
* Submits an SExpression without a return.
*/
def tell(input: SExpression) = liftF(Tell(input))
def pure[T](t: T): Session[T] = Free.pure[SessionCommand, T](t)
def skip: Session[Unit] = pure(())
/**
* Asserts a list of formulas.
*/
def assert(formulas: List[Formula]): Session[Unit] = formulas.traverse_(assert)
/**
* Pushes the stack, then runs f, then pops the stack.
*/
def withScope[A](f: Session[A]): Session[A] = wrap(push, f, pop)
/**
* Encloses the session `f` between `before` and `after`.
*/
def wrap[A](before: Session[Unit], f: Session[A], after: Session[Unit]): Session[A] = for {
_ <- before
x <- f
_ <- after
} yield x
/**
* Declares all symbols (sorts and functions) in a list of Exprs.
*/
def declareSymbolsIn(expressions: IterableOnce[Expr]): Session[Unit] = {
val cs = expressions.iterator.to(Set) flatMap { constants.nonLogical(_) } filter {
case EqC(_) => false
case _: LogicalConstant => false
case _ => true
}
val ts = cs flatMap { c => baseTypes(c.ty) } filter {
case To => false
case _ => true
}
for {
_ <- ts.toList.traverse_(declareSort)
_ <- cs.toList.traverse_(declareFun)
} yield ()
}
/**
* Declares all symbols (sorts and functions) in a list of Exprs.
*/
def declareSymbolsIn(expressions: Expr*)(implicit d: DummyImplicit): Session[Unit] = declareSymbolsIn(expressions.toList)
def when(p: Boolean)(s: Session[Unit]) = if (p) s else skip
/**
* Contains various functions for interpreting a value of type Session.
*
* A "runner" contains a natural transformation from SessionCommand to Id; i.e. a function that can transform any
* SessionCommand[A] to an Id[A] (= A).
*
* Given such a transformation comp: SessionCommand ~> Id, you can use it to interpret a session program p via
* p.foldMap(comp).
*/
object Runners {
/**
* Runs a session. What that means, exactly, is up to the concrete implementation.
*/
abstract class SessionRunner {
/**
* Interprets a single session command.
*/
protected def interpretCommand[A](command: SessionCommand[A]): A
/**
* Runs a session by wrapping the `interpretCommand` function into a natural transformation
* trans: SessionCommand ~> Id and calling session.foldMap(trans).
*/
def run[A](session: Session[A]): A = {
val trans = new (SessionCommand ~> Id) {
def apply[B](command: SessionCommand[B]): B = interpretCommand(command)
}
session.foldMap(trans)
}
}
/**
* A runner that interprets a Session by communicating with an SMTLib process. The process is left abstract --
* it might not be an external program.
*
* Subclasses must implement the tell and ask functions.
*/
abstract class SMTLibSessionRunner extends SessionRunner {
protected def tell(input: SExpression): Unit
protected def ask(input: SExpression): SExpression
protected def interpretCommand[A](command: SessionCommand[A]): A = command match {
case Push => tell(LFun("push", LSymbol("1")))
case Pop => tell(LFun("pop", LSymbol("1")))
case DeclareSort(sort) => tell(LFun("declare-sort", LSymbol(typeRenaming(sort).name), LSymbol(0.toString)))
case DeclareFun(fun) => termRenaming(fun) match {
case Const(name, FunctionType(TBase(retType, Nil), argTypes), _) =>
tell(LFun("declare-fun", LSymbol(name), LList(argTypes map convert: _*), LSymbol(retType)))
case _ => () // do not declare applications or abstractions. TODO: check if we need to recurse into the term
}
case Assert(formula) => tell(LFun("assert", convert(formula)))
case AssertLabelled(formula, label) => tell(LFun("assert", LFun("!", convert(formula), LKeyword("named"), LSymbol(label))))
case CheckSat => ask(LFun("check-sat")) match {
case LSymbol("sat") => Right(true)
case LSymbol("unsat") => Right(false)
case unknown => Left(unknown)
}
case SetLogic(logic) => tell(LFun("set-logic", LSymbol(logic)))
case SetOption(option, args) => tell(LFun("set-option", LKeyword(option) +: args.map(LSymbol): _*))
case Ask(input) => ask(input)
case Tell(input) => tell(input)
}
protected val nameGen = new NameGenerator(Set()) // TODO: add reserved keywords?
def convert(ty: Ty): SExpression = (ty: @unchecked) match {
case TBase(argType, Nil) => LSymbol(argType)
case FunctionType(to, from) =>
val ts = (from :+ to) map convert
LFun("->", ts: _*)
}
object typeRenaming {
val map = mutable.Map[TBase, TBase]()
def apply(t: TBase): TBase = map.getOrElseUpdate(
t,
t match {
case To => TBase("Bool", Nil)
case TBase(n, _) => // TODO: polymorphic types
TBase(nameGen.fresh(mangleName(n, "t_")), Nil)
}
)
def apply(t: Ty): Ty = (t: @unchecked) match {
case base: TBase => apply(base)
case TArr(a, b) => apply(a) ->: apply(b)
}
}
object termRenaming {
val map = mutable.Map[Const, Const]()
def apply(c: Const): Const = map.getOrElseUpdate(
c,
Const(nameGen.fresh(mangleName(c.name)), typeRenaming(c.ty))
)
}
def convert(expr: Expr, boundVars: Map[Var, String] = Map()): SExpression = expr match {
case Top() => LSymbol("true")
case Bottom() => LSymbol("false")
case Neg(a) => LFun("not", convert(a, boundVars))
case And(a, b) => LFun("and", convert(a, boundVars), convert(b, boundVars))
case Or(a, b) => LFun("or", convert(a, boundVars), convert(b, boundVars))
case Imp(a, b) => LFun("=>", convert(a, boundVars), convert(b, boundVars))
case Eq(a, b) => LFun("=", convert(a, boundVars), convert(b, boundVars))
case c: Const => LSymbol(termRenaming(c).name)
case All(x @ Var(_, ty), a) =>
val smtVar = s"x${boundVars.size}"
LFun("forall", LList(LFun(smtVar, convert(typeRenaming(ty)))), convert(a, boundVars + (x -> smtVar)))
case Ex(x @ Var(_, ty), a) =>
val smtVar = s"x${boundVars.size}"
LFun("exists", LList(LFun(smtVar, convert(typeRenaming(ty)))), convert(a, boundVars + (x -> smtVar)))
case v: Var => LSymbol(boundVars(v))
case Abs(x @ Var(_, ty), a) =>
val smtVar = s"x${boundVars.size}"
LFun("lambda", LList(LFun(smtVar, convert(typeRenaming(ty)))), convert(a, boundVars + (x -> smtVar)))
case Apps(c, args) =>
LList((c :: args) map { convert(_, boundVars) }: _*)
}
}
/**
* An SMTLibSessionRunner that actually communicates with an external program.
* @param command The command & list of options used to start the external program.
*/
class ExternalSMTLibSessionRunner(command: String*) extends SMTLibSessionRunner {
val process = new ProcessBuilder(command: _*).redirectError(Redirect.INHERIT).start()
val in = new PrintWriter(process.getOutputStream)
val out = new BufferedReader(new InputStreamReader(process.getInputStream))
var debug = false
protected def tell(input: SExpression) = {
if (debug) println(input)
in println input.toDoc.render(Int.MaxValue)
}
protected def ask(input: SExpression) = {
if (debug) println(input)
in println input.toDoc.render(Int.MaxValue)
in.flush()
val res = out.readLine()
if (debug) println(s"-> $res")
if (res == null) throw new ExternalSmtlibProgram.UnexpectedTerminationException(input)
SExpressionParser.parse(StringInputFile(res)).head
}
}
/**
* An SMTLibSessionRunner that writes all commands sequentially to a string.
*
* Its `tell` simply writes to the string, while its `ask` writes
* to the string and receives dummy replies.
*/
class BenchmarkRecorder(lineWidth: Int = 80) extends SMTLibSessionRunner {
private val benchmark = new StringBuilder
def getBenchmark() = benchmark.result()
protected def tell(input: SExpression) =
benchmark append (input.toDoc <> "\n").render(lineWidth)
protected def ask(input: SExpression) = {
tell(input)
input match {
case LFun("check-sat") => LSymbol("unsat")
case _ => LList()
}
}
}
/**
* A runner that interprets a session by manipulating a stack of sets of formulas.
* @param checkValidity The function the runner uses to test validity of sequents.
*/
class StackSessionRunner(checkValidity: HOLSequent => Boolean) extends SessionRunner {
val formulaStack = mutable.Stack[Set[Formula]]()
var assertedFormulas = Set[Formula]()
protected def interpretCommand[A](command: SessionCommand[A]): Id[A] = command match {
case Push =>
formulaStack push assertedFormulas; ()
case Pop =>
assertedFormulas = formulaStack.pop(); ()
case Assert(formula) =>
assertedFormulas += formula; ()
case AssertLabelled(formula, _) =>
assertedFormulas += formula; ()
case CheckSat => Right(!checkValidity(assertedFormulas ++: Sequent()))
case Ask(input) => input
case DeclareFun(_) | DeclareSort(_) | SetLogic(_) | SetOption(_, _) | Tell(_) => ()
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy