All Downloads are FREE. Search and download functionalities are using the official Maven repository.

gapt.expr.formula.fol.hol2fol.scala Maven / Gradle / Ivy

The newest version!
package gapt.expr.formula.fol

import gapt.expr._
import gapt.expr.formula.All
import gapt.expr.formula.And
import gapt.expr.formula.Atom
import gapt.expr.formula.Bottom
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.hol.HOLFunction
import gapt.expr.formula.hol._
import gapt.expr.subst.Substitution
import gapt.expr.ty.FunctionType
import gapt.expr.ty.To
import gapt.expr.ty.Ty
import gapt.expr.util
import gapt.expr.util.freeVariables
import gapt.proofs.HOLSequent
import gapt.proofs.context.Context

/**
 * Creates a FOL formula from a HOL formula, but applies transformations which do _not_ preserve validity!
 * Transformations applied:
 *
 *  - Replace all subterms (\x.t[v]) by a function q_n(v). The scope parameter is needed to pass existing term-constant
 *    mappings.
 *  - Change the type of constants and variables s.t. they are first order (i.e. Const("c", To->Ti) is mapped to
 *    FOLConst("c",Ti)
 *  - Logical operators inside the term structure are replaced by first order terms
 *
 * @note Make sure you need all of these tricks. To only replace abstraction subterms, use [[replaceAbstractions]].
 *
 */
object reduceHolToFol {

  /**
   * Apply method for a formula when scope needs to passed on in a recursion.
   * @param formula the formula to convert
   * @return a pair of the reduced formula and the updated scope
   */
  def apply(formula: Formula)(implicit definition: Hol2FolDefinitions): FOLFormula =
    convertHolFormulaToFolFormula(replaceAbstractions(formula))

  /**
   * Apply method for a an expression when scope needs to passed on in a recursion.
   * @param term the expression to convert
   * @return a pair of the reduced expression and the updated scope
   */
  def apply(term: Expr)(implicit definition: Hol2FolDefinitions): FOLExpression =
    (replaceAbstractions(term)) match {
      case f: Formula => convertHolFormulaToFolFormula(f)
      case t          => convertHolTermToFolTerm(t)
    }

  /**
   * Assumes we are on the logical level of the hol formula - all types are
   * mapped to i, i>o or i>i>o respectively
   */
  private def convertHolFormulaToFolFormula(formula: Formula): FOLFormula = {
    formula match {
      case Top()    => Top()
      case Bottom() => Bottom()
      case Neg(f) =>
        Neg(convertHolFormulaToFolFormula(f))
      case And(f1, f2) =>
        And(
          convertHolFormulaToFolFormula(f1),
          convertHolFormulaToFolFormula(f2)
        )
      case Or(f1, f2) =>
        Or(
          convertHolFormulaToFolFormula(f1),
          convertHolFormulaToFolFormula(f2)
        )
      case Imp(f1, f2) =>
        Imp(
          convertHolFormulaToFolFormula(f1),
          convertHolFormulaToFolFormula(f2)
        )
      case All(v: Var, f) =>
        All(
          convertHolVariableToFolVariable(v),
          convertHolFormulaToFolFormula(f)
        )
      case Ex(v: Var, f) =>
        Ex(
          convertHolVariableToFolVariable(v),
          convertHolFormulaToFolFormula(f)
        )
      case Const(n, To, _) =>
        FOLAtom(n, Nil)
      case Atom(Const(n, _, _), as) =>
        FOLAtom(n, as.map { convertHolTermToFolTerm })
      case Atom(Var(n, _), as) =>
        FOLAtom(n, as.map { convertHolTermToFolTerm })
      case HOLFunction(Const(n, _, _), as) =>
        FOLAtom(n, as.map { convertHolTermToFolTerm })
      case HOLFunction(Var(n, _), as) =>
        FOLAtom(n, as.map { convertHolTermToFolTerm })
    }
  }

  private def convertHolVariableToFolVariable(v: Var): FOLVar =
    FOLVar(v.name)

  /**
   * Converts a term to a first-order term.
   *
   * @param term The term to be converted to a first-order term.
   * @return A first-order term obtained from `term` by erasing types of
   * constants and variables in leaf position by `i` and by replacing types of
   * inner constants by i > i > ... > i of adequate arity. In particular logical
   * constants are replaced in this way e.g. ¬ : o > o occurring in a term of
   * the form (¬ f) is replaced by ¬ : i > i.
   */
  private def convertHolTermToFolTerm(term: Expr): FOLTerm = {
    term match {
      case v: Var         => convertHolVariableToFolVariable(v)
      case Const(n, _, _) => FOLConst(n)
      case Apps(h: VarOrConst, as) =>
        FOLFunction(h.name, as.map { convertHolTermToFolTerm })
    }
  }
}

object invertBijectiveMap {
  def apply[A, B](map: Map[A, B]): Map[B, A] =
    map.map[B, A] { _.swap }
}

/**
 * Definitions of the form cₜ(x₁,...,xₙ) := t(x₁,...,xₙ), where c is a constant
 * and t is a term having as only free variables x₁,...,xₙ.
 * @param context The context with respect to which these definitions are made.
 * In particular the context defines the already used names.
 */
class Hol2FolDefinitions(implicit val context: Context = Context.default) {

  type Definiendum = Expr
  type Definiens = Expr

  private var definitions: Map[Definiendum, Definiens] = Map()

  private val nameGenerator = context.newNameGenerator

  /**
   * Looks up the defined expression.
   * @param expression An expression t(s₁,...,sₙ).
   * @return The defined expression cₜ(s₁,...,sₙ).
   * If there is no definition of the form cₜ(x₁,...,xₙ) ↔ t(x₁,...,xₙ),
   * then such a definition is created where cₜ is a new constant that does
   * not intersect the context.
   */
  def getDefinedExpression(expression: Definiens): Definiendum = {
    addDefinitionIfNecessary(expression)
    val Some((e, m)) = getMatchingDefiniens(expression): @unchecked
    m(invertBijectiveMap(definitions)(e))
  }

  private def addDefinitionIfNecessary(expression: Definiens): Unit = {
    getMatchingDefiniens(expression) match {
      case None =>
        val freeVars = freeVariables(expression).toList
        val constant = Const(
          freshConstantName,
          Abs.Block(freeVars, expression).ty
        )
        definitions += Apps(constant, freeVars) -> expression
      case _ =>
    }
  }

  def getDefiningExpression(expression: Definiendum): Option[Definiens] = {
    for {
      (c, m) <- getMatchingDefiniendum(expression)
      d <- definitions.get(c)
    } yield {
      m(d)
    }
  }

  private def getMatchingDefiniendum(expression: Expr): Option[(Expr, Substitution)] = {
    definitions.keys.view
      .map { e => e -> util.syntacticMatching(e, expression) }
      .collectFirst { case (e, Some(m)) => (e, m) }
  }

  private def getMatchingDefiniens(expression: Expr): Option[(Expr, Substitution)] = {
    definitions.values.view
      .map { e => e -> util.syntacticMatching(e, expression) }
      .collectFirst { case (e, Some(m)) => (e, m) }
  }

  override def equals(obj: Any): Boolean =
    obj match {
      case o: Hol2FolDefinitions => isEqualTo(o)
      case _                     => false
    }

  private def isEqualTo(otherDefinitions: Hol2FolDefinitions): Boolean =
    lambdaClosedDefinitionsPairs == otherDefinitions.lambdaClosedDefinitionsPairs

  private def lambdaClosedDefinitionsPairs: Set[(Expr, Expr)] =
    definitions.toSet.map { lambdaCloseDefinitionPair }

  private def lambdaCloseDefinitionPair(definitionPair: (Expr, Expr)): (Expr, Expr) =
    definitionPair match {
      case (d @ Apps(_: Const, xs), e) =>
        Abs.Block(xs.asInstanceOf[List[Var]], d) -> Abs.Block(xs.asInstanceOf[List[Var]], e)
    }

  private def freshConstantName: String =
    nameGenerator.freshWithIndex { n => s"q_{${n + 1}}" }

  // FIXME These methods only exist to maintain compatibility with legacy

  def toMap: Map[Expr, Expr] = definitions

  def toLegacyMap: Map[Expr, String] = definitions.map {
    case (Apps(Const(n, _, _), _), e) => e -> n
  }

  def lookupByName(name: String): Option[Expr] =
    definitions
      .find { case (Apps(c: Const, _), _) => c.name == name }
      .map { _._2 }
}

/**
 * Replace lambda-abstractions by constants.
 *
 * Each abstraction in an [[gapt.proofs.HOLSequent]] is replaced by a separate constant symbol; the used
 * constants are returned in a Map.
 */
object replaceAbstractions {

  def apply(expression: Expr)(implicit definitions: Hol2FolDefinitions): Expr =
    new NonQuantifierAbstractionLifter(definitions).lift(expression)

  def apply(formula: Formula)(implicit definitions: Hol2FolDefinitions): Formula =
    new NonQuantifierAbstractionLifter(definitions).lift(formula)

  private class NonQuantifierAbstractionLifter(
      private val definitions: Hol2FolDefinitions
  ) {

    def lift(formula: Formula): Formula =
      lift(formula.asInstanceOf[Expr]).asInstanceOf[Formula]

    def lift(expression: Expr): Expr = {
      defineOutermostNonQuantifierAbstractions(expression)
      liftOutermostNonQuantifierAbstractions(expression)
    }

    private def getOutermostNonQuantifierAbstractions(expression: Expr): Seq[Expr] =
      expression match {
        case Ex(_, f)  => getOutermostNonQuantifierAbstractions(f)
        case All(_, f) => getOutermostNonQuantifierAbstractions(f)
        case App(e1, e2) =>
          getOutermostNonQuantifierAbstractions(e1) ++
            getOutermostNonQuantifierAbstractions(e2)
        case _: Abs => Seq(expression)
        case _      => Seq()
      }

    private def defineOutermostNonQuantifierAbstractions(expression: Expr): Unit = {
      defineExpressions(getOutermostNonQuantifierAbstractions(expression))
    }

    private def defineExpressions(expressions: Seq[Expr]): Unit =
      expressions.foreach {
        definitions.getDefiningExpression
      }

    private def liftOutermostNonQuantifierAbstractions(expression: Expr): Expr =
      expression match {
        case Ex(v, f) =>
          Ex(v, liftOutermostNonQuantifierAbstractions(f))
        case All(v, f) =>
          All(v, liftOutermostNonQuantifierAbstractions(f))
        case App(e1, e2) =>
          App(
            liftOutermostNonQuantifierAbstractions(e1),
            liftOutermostNonQuantifierAbstractions(e2)
          )
        case _: Abs =>
          definitions.getDefinedExpression(expression)
        case _ => expression
      }
  }
}

/**
 * Replaces the constants introduced by [[replaceAbstractions]] with the
 * original lambda-abstractions.
 *
 * Two lambda abstractions that are matching may have the same abstracting
 * constant. However no effort is made to detect matching lambda abstractions in
 * order to minimize the number of definitions.
 */
object undoReplaceAbstractions {

  def apply(sequent: HOLSequent, definitions: Hol2FolDefinitions): HOLSequent =
    sequent.map { undoReplaceAbstractions(_, definitions) }

  def apply(f: Formula, definitions: Hol2FolDefinitions): Formula =
    apply(f.asInstanceOf[Expr], definitions).asInstanceOf[Formula]

  /**
   * Replace all occurrences of defined constants by their abstractions.
   *
   * @param expression The expression in which definitions are unfolded.
   * @param definitions The definition to be be unfolded.
   * @return An expression obtained from `expression` by unfolding all the
   * constants defined in `h2fDefinitions` by their defining term.
   */
  def apply(expression: Expr, definitions: Hol2FolDefinitions): Expr = {
    HOLPosition.getPositions(expression).foldLeft(expression) {
      (e, p) =>
        expression(p) match {
          case Apps(_: Const, _) =>
            definitions.getDefiningExpression(expression(p)).map {
              e.replace(p, _)
            }.getOrElse(e)
          case _ => e
        }
    }
  }
}

/**
 * Introducing abstractions and converting to fol changes more complex types to fol compatible ones. With changeTypeIn
 * you can change them back.
 */
object changeTypeIn {

  type TypeMap = Map[String, Ty]

  /**
   * Maps types of constants and variables to the given types.
   *
   * @param expression The expression in which the types are to be replaced.
   * @param typeMap Specifies the names of constants and variables whose type
   *                is to be replaced by the associated type.
   * @return An expression obtained from `expression` by replacing the types of leaf-occurrences of variables and
   *         constants by the given types. The types of inner constants and variables are changed according
   *         to the new types of their arguments.
   */
  def apply(expression: Expr, typeMap: TypeMap): Expr = expression match {
    case v @ Var(n, _) =>
      typeMap.get(n).map { Var(n, _) }.getOrElse(v)
    case c @ Const(n, _, _) =>
      typeMap.get(n).map { Const(n, _) }.getOrElse(c)
    case HOLFunction(Const(f, FunctionType(r, _), _), as) =>
      val newAs = as.map { changeTypeIn(_, typeMap) }
      val newTs = newAs.map { _.ty }
      HOLFunction(Const(f, FunctionType(r, newTs)), newAs)
    case HOLFunction(Var(x, FunctionType(r, _)), as) =>
      val newAs = as.map { changeTypeIn(_, typeMap) }
      val newTs = newAs.map { _.ty }
      HOLFunction(Var(x, FunctionType(r, newTs)), newAs)
    case Atom(Const(f, FunctionType(r, _), _), as) =>
      val newAs = as.map { changeTypeIn(_, typeMap) }
      val newTs = newAs.map { _.ty }
      Atom(Const(f, FunctionType(r, newTs)), newAs)
    case Atom(Var(f, FunctionType(r, _)), as) =>
      val newAs = as.map { changeTypeIn(_, typeMap) }
      val newTs = newAs.map { _.ty }
      Atom(Const(f, FunctionType(r, newTs)), newAs)
    case Neg(x) =>
      Neg(changeTypeIn(x, typeMap))
    case And(s, t) =>
      And(changeTypeIn(s, typeMap), changeTypeIn(t, typeMap))
    case Or(s, t) =>
      Or(changeTypeIn(s, typeMap), changeTypeIn(t, typeMap))
    case Imp(s, t) =>
      Imp(changeTypeIn(s, typeMap), changeTypeIn(t, typeMap))
    case All(x, f) =>
      val newX = typeMap.get(x.name).map { Var(x.name, _) }.getOrElse(x)
      All(newX, changeTypeIn(f, typeMap))
    case Ex(x, f) =>
      val newX = typeMap.get(x.name).map { Var(x.name, _) }.getOrElse(x)
      Ex(newX, changeTypeIn(f, typeMap))
    case Abs(x, t) =>
      val newX = typeMap.get(x.name).map { Var(x.name, _) }.getOrElse(x)
      Abs(newX, changeTypeIn(t, typeMap))
    case App(s, t) =>
      App(changeTypeIn(s, typeMap), changeTypeIn(t, typeMap))
    case _ =>
      throw new Exception("Unhandled case of a HOL Formula! " + expression)
  }

  /**
   * @see `changeTypeIn.apply( Expr, TypeMap )`.
   */
  def apply(e: Formula, tmap: TypeMap): Formula =
    changeTypeIn(e.asInstanceOf[Expr], tmap).asInstanceOf[Formula]

  def apply(fs: HOLSequent, tmap: TypeMap): HOLSequent = HOLSequent(
    fs.antecedent.map(x => changeTypeIn(x, tmap)),
    fs.succedent.map(x => changeTypeIn(x, tmap))
  )
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy