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

gapt.proofs.resolution.resolution.scala Maven / Gradle / Ivy

The newest version!
package gapt.proofs.resolution

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.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.hol.HOLAtomConst
import gapt.expr.subst.Substitution
import gapt.expr.ty.->:
import gapt.expr.ty.FunctionType
import gapt.expr.ty.To
import gapt.expr.util.freeVariables
import gapt.expr.util.rename
import gapt.expr.util.replacementContext
import gapt.expr.util.syntacticMGU
import gapt.formats.babel.BabelSignature
import gapt.logic.hol.SkolemFunctions
import gapt.proofs._
import gapt.proofs.context.Context
import gapt.proofs.context.update.Definition

import scala.collection.mutable

/**
 * Proof using resolution.
 *
 * Our resolution calculus integrates higher-order reasoning,
 * structural clausification, and Avatar-style splitting as in [Vor14].
 *
 * The judgments of this calculus are A-sequents.  An A-sequent is a pair
 * of a sequent of HOL formulas, and a conjunction of propositional
 * literals---the assertion (that's where the "A" comes from).
 * {{{
 *   Γ :- Δ <- A
 * }}}
 *
 * We store the sequent as a HOLSequent in [[ResolutionProof#conclusion]],
 * and the negation of the assertion as a HOLClause in
 * [[ResolutionProof#assertions]] (as the negation of a conjunction of
 * literals is a clause).  The judgment is interpreted as one of the following
 * equivalent formulas:
 * {{{
 *   assertions.toNegConjunction --> conclusion.toDisjunction
 *   (conclusion ++ assertions).toDisjunction   // equivalent to the first one
 * }}}
 *
 * Inferences such as [[Resolution]] or [[Paramod]] do not operate on the assertions.
 * Unless specified otherwise, assertions are inherited by default:
 * {{{
 *   Γ :- Δ, a <- A       a, Π :- Λ <- B
 *   --------------------------------
 *         Γ, Π :- Δ, Λ <- A ∧ B
 * }}}
 *
 * There is no factoring on assertions, duplicate assertions are automatically removed.
 *
 * Reading from top to bottom, a resolution proof is usually structured as follows:
 * 
    *
  1. [[Input]] rules for formulas of the end-sequent *
  2. [[AndL]], [[AllR]], etc. for the clausification. A combination of * [[DefIntro]] and [[Taut]] is used to introduce definitions for subformulas. *
  3. [[Resolution]], [[Paramod]], [[Subst]]. [[Factor]], [[Refl]], [[Taut]] for the * refutation of the resulting clauses. Clauses are split by successively * applying [[AvatarSplit]], reducing the clause to an empty clause. * The clause components can then be used using [[AvatarComponent]]. *
  4. Each empty clause of a splitting branch is followed by an * [[AvatarContradiction]] inference, moving the assertion back to the clause. *
  5. A [[Resolution]] refutation of the [[AvatarContradiction]] clauses. *
* * Substitutions are not absorbed into resolution, factoring, and * paramodulation; they are explicitly represented using [[Subst]]. * * [Vor14] Andrei Voronkov, AVATAR: The Architecture for First-Order Theorem Provers. CAV 2014: pp. 696-710 */ trait ResolutionProof extends SequentProof[Formula, ResolutionProof] with DagProof[ResolutionProof] { /** * Assertions of the proof. * * Attention: this is interpreted as assertions.toNegConjunction --> conclusion.toDisjunction * * These assertions indicate the splitting assumptions this proof depends on. */ val assertions: HOLClause = immediateSubProofs.flatMapS(_.assertions).distinct /** Definitions introduced by the bottom-most inference rule. */ def introducedDefinitions: Map[HOLAtomConst, Expr] = Map() /** * All definitions introduced by any subproof. * * throws java.lang.Exception if inconsistent definitions are used */ def definitions = { val builder = mutable.Map[HOLAtomConst, Expr]() for { p <- subProofs (defConst, definition) <- p.introducedDefinitions } if (builder contains defConst) requireEq(builder(defConst), definition) else builder(defConst) = definition builder.toMap } def skolemSymbols: Set[Const] = subProofs.collect { case p: SkolemQuantResolutionRule => p.skolemConst } def skolemFunctions(implicit ctx: Context) = SkolemFunctions(skolemSymbols.map(skC => skC -> ctx.skolemDef(skC).get)) def stringifiedConclusion(implicit sig: BabelSignature): String = { val assertionString = if (assertions.isEmpty) "" else s" <- ${assertions.map(identity, -_).map(_.toSigRelativeString).elements.mkString(",")} " conclusion.toSigRelativeString + assertionString } override protected def stepString(subProofLabels: Map[Any, String]) = s"$stringifiedConclusion (${super[DagProof].stepString(subProofLabels)})" /** Is this a proof of the empty clause with empty assertions, and consistent definitions? */ def isProof = { definitions conclusion.isEmpty && assertions.isEmpty } private[resolution] def requireEq[T](a: T, b: T) = require(a == b, s"\n$a ==\n$b") } abstract class LocalResolutionRule extends ResolutionProof with ContextRule[Formula, ResolutionProof] abstract class InitialClause extends LocalResolutionRule { override def auxIndices = Seq() override def immediateSubProofs = Seq() } /** * Input sequent. * {{{ * ------- * sequent * }}} */ case class Input(sequent: HOLSequent) extends InitialClause { override def mainFormulaSequent = sequent } /** * Tautology. * {{{ * ------------------- * formula :- formula * }}} */ case class Taut(formula: Formula) extends InitialClause { override def mainFormulaSequent = formula +: Sequent() :+ formula } /** * Reflexivity. * {{{ * -------------- * :- term = term * }}} */ case class Refl(term: Expr) extends InitialClause { override def mainFormulaSequent = Sequent() :+ (term === term) } /** * Definition. * {{{ * --------------------- * :- ∀x (D(x) <-> φ(x)) * }}} */ case class Defn(defConst: HOLAtomConst, definition: Expr) extends InitialClause { val vars = (defConst.ty: @unchecked) match { case FunctionType(To, argTypes) => definition match { case Abs.Block(vs, _) if vs.size == argTypes.size && vs == vs.distinct => vs case _ => for ((t, i) <- argTypes.zipWithIndex) yield Var(s"x_$i", t) } } val definitionFormula = All.Block(vars, defConst(vars) <-> BetaReduction.betaNormalize(definition(vars))) override def mainFormulaSequent = Sequent() :+ definitionFormula override def introducedDefinitions = Map(defConst -> definition) } /** * Factoring. * {{{ * l, l, Γ :- Δ * ----------- * l, Γ :- Δ * }}} */ case class Factor(subProof: ResolutionProof, idx1: SequentIndex, idx2: SequentIndex) extends LocalResolutionRule { require(idx1 sameSideAs idx2) require(idx1 < idx2) requireEq(subProof.conclusion(idx1), subProof.conclusion(idx2)) override def mainFormulaSequent = if (idx1 isAnt) subProof.conclusion(idx1) +: Sequent() else Sequent() :+ subProof.conclusion(idx1) override def immediateSubProofs = Seq(subProof) override def auxIndices = Seq(Seq(idx1, idx2)) } object Factor { def apply(p: ResolutionProof): ResolutionProof = apply(p, p.conclusion.distinct) def apply(p: ResolutionProof, newConclusion: HOLSequent): ResolutionProof = { require( newConclusion setEquals p.conclusion, s"Proposed conclusion $newConclusion has fewer formulas than ${p.conclusion}" ) require( newConclusion isSubMultisetOf p.conclusion, s"Proposed conclusion $newConclusion is not a submultiset of ${p.conclusion}" ) var p_ = p for ((a, i) <- p.conclusion.diff(newConclusion).zipWithIndex) { val Seq(j1, j2, _*) = p_.conclusion.zipWithIndex.elements.filter(_._2 sameSideAs i).filter(_._1 == a).map(_._2): @unchecked p_ = Factor(p_, j1, j2) } p_ } def withOccConn(p: ResolutionProof): (ResolutionProof, SequentConnector) = { var p_ = p var conn = SequentConnector(p_.conclusion) for ((a, i) <- p.conclusion.diff(p.conclusion.distinct).zipWithIndex) { val Seq(j1, j2, _*) = p_.conclusion.zipWithIndex.elements.filter(_._2 sameSideAs i).filter(_._1 == a).map(_._2): @unchecked p_ = Factor(p_, j1, j2) conn = p_.occConnectors.head * conn } p_ -> conn } object Block { def unapply(p: ResolutionProof): Some[ResolutionProof] = p match { case Factor(Factor.Block(q), _, _) => Some(q) case _ => Some(p) } } } object MguFactor { def apply(p: ResolutionProof, i1: SequentIndex, i2: SequentIndex): ResolutionProof = { val Some(mgu) = syntacticMGU(p.conclusion(i1), p.conclusion(i2)): @unchecked Factor(Subst(p, mgu), i1, i2) } } /** * Substitution. * {{{ * Γ :- Δ * ---------------------- * substitution(Γ :- Δ) * }}} */ case class Subst(subProof: ResolutionProof, substitution: Substitution) extends ResolutionProof { import gapt.expr.subst.ExprSubstWithβ._ override val conclusion: Sequent[Formula] = subProof.conclusion.map(substitution(_)) override def mainIndices: Seq[SequentIndex] = subProof.conclusion.indices override def auxIndices: Seq[Seq[SequentIndex]] = Seq(subProof.conclusion.indices) override def occConnectors: Seq[SequentConnector] = Seq(SequentConnector(conclusion, subProof.conclusion, subProof.conclusion.indicesSequent.map(Seq(_)))) override def immediateSubProofs: Seq[ResolutionProof] = Seq(subProof) } object Subst { def ifNecessary(subProof: ResolutionProof, substitution: Substitution): ResolutionProof = subProof match { case Subst(subProof2, substitution2) => Subst.ifNecessary(subProof2, substitution compose substitution2) case _ if substitution(subProof.conclusion) == subProof.conclusion => subProof case _ => Subst(subProof, substitution) } } /** * Resolution. * {{{ * Γ :- Δ, a a, Π :- Λ * ---------------------- * Γ, Π :- Δ, Λ * }}} */ case class Resolution(subProof1: ResolutionProof, idx1: SequentIndex, subProof2: ResolutionProof, idx2: SequentIndex) extends LocalResolutionRule { require(idx1 isSuc) require(idx2 isAnt) requireEq(subProof1.conclusion(idx1), subProof2.conclusion(idx2)) def resolvedLiteral = subProof1.conclusion(idx1) def mainFormulaSequent = Sequent() def immediateSubProofs = Seq(subProof1, subProof2) def auxIndices = Seq(Seq(idx1), Seq(idx2)) } object Resolution { def apply(subProof1: ResolutionProof, subProof2: ResolutionProof, atom: Formula): ResolutionProof = Resolution(subProof1, subProof1.conclusion.indexOfInSuc(atom), subProof2, subProof2.conclusion.indexOfInAnt(atom)) def maybe(subProof1: ResolutionProof, subProof2: ResolutionProof, atom: Formula): ResolutionProof = if (!subProof1.conclusion.succedent.contains(atom)) subProof1 else if (!subProof2.conclusion.antecedent.contains(atom)) subProof2 else Resolution(subProof1, subProof2, atom) } object MguResolution { def apply(subProof1: ResolutionProof, idx1: SequentIndex, subProof2: ResolutionProof, idx2: SequentIndex): ResolutionProof = { val renaming = Substitution(rename(freeVariables(subProof1.conclusion), freeVariables(subProof2.conclusion))) val Some(mgu) = syntacticMGU(renaming(subProof1.conclusion(idx1)), subProof2.conclusion(idx2)): @unchecked Resolution(Subst(subProof1, mgu compose renaming), idx1, Subst(subProof2, mgu), idx2) } } /** * Paramodulation. * {{{ * Γ :- Δ, t=s Π :- Λ, l[t] * ---------------------------- * Γ, Π :- Δ, Λ, l[s] * }}} */ case class Paramod(subProof1: ResolutionProof, eqIdx: SequentIndex, leftToRight: Boolean, subProof2: ResolutionProof, auxIdx: SequentIndex, context: Expr) extends LocalResolutionRule { require(eqIdx isSuc) val (t, s) = subProof1.conclusion(eqIdx) match { case Eq(t_, s_) => if (leftToRight) (t_, s_) else (s_, t_) } private def instCtx(t: Expr): Formula = (context match { case Abs(v, c) => v.ty match { case _ ->: _ => BetaReduction.betaNormalize(context(t)) case _ => Substitution(v -> t)(c) } case _ => context(t) }).asInstanceOf[Formula] def auxFormula = subProof2.conclusion(auxIdx) require( auxFormula == instCtx(t), s"$auxFormula == ${instCtx(t)}" ) val rewrittenAuxFormula = instCtx(s) def mainFormulaSequent = if (auxIdx isAnt) rewrittenAuxFormula +: Sequent() else Sequent() :+ rewrittenAuxFormula def immediateSubProofs = Seq(subProof1, subProof2) def auxIndices = Seq(Seq(eqIdx), Seq(auxIdx)) } object Paramod { def withMain(subProof1: ResolutionProof, eqIdx: SequentIndex, subProof2: ResolutionProof, auxIdx: SequentIndex, main: Formula): ResolutionProof = { val Eq(t, s) = subProof1.conclusion(eqIdx): @unchecked val ctxLTR = replacementContext(t.ty, main, subProof2.conclusion(auxIdx).find(t).filter(main get _ contains s)) val ctxRTL = replacementContext(t.ty, main, subProof2.conclusion(auxIdx).find(s).filter(main get _ contains t)) if ( BetaReduction.betaNormalize(ctxLTR(t)) == subProof2.conclusion(auxIdx) && BetaReduction.betaNormalize(ctxLTR(s)) == main ) { Paramod(subProof1, eqIdx, true, subProof2, auxIdx, ctxLTR) } else { Paramod(subProof1, eqIdx, false, subProof2, auxIdx, ctxRTL) } } } abstract class PropositionalResolutionRule extends LocalResolutionRule { def subProof: ResolutionProof def idx: SequentIndex override def immediateSubProofs = Seq(subProof) override def auxIndices = Seq(Seq(idx)) def occConn = occConnectors.head } /** * Definition introduction. * {{{ * Γ :- Δ, φ(x) * -------- * Γ :- Δ, D(x) * }}} * * This inference should only be used on descendants of Input inferences. */ case class DefIntro(subProof: ResolutionProof, idx: SequentIndex, definition: Definition, args: Seq[Expr]) extends PropositionalResolutionRule { val Definition(defConst: HOLAtomConst, by) = definition: @unchecked val defAtom = defConst(args).asInstanceOf[Atom] val expandedFormula = BetaReduction.betaNormalize(Apps(by, args)) requireEq(subProof.conclusion(idx), expandedFormula) def auxFormula = expandedFormula.asInstanceOf[Formula] override def introducedDefinitions = Map(defConst -> by) override def mainFormulaSequent = if (idx isAnt) defAtom +: Sequent() else Sequent() :+ defAtom } case class TopL(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isAnt) locally { val Top() = subProof.conclusion(idx): @unchecked } def mainFormulaSequent = Sequent() } case class BottomR(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isSuc) locally { val Bottom() = subProof.conclusion(idx): @unchecked } def mainFormulaSequent = Sequent() } case class NegL(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isAnt) val Neg(sub) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = Sequent() :+ sub } case class NegR(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isSuc) val Neg(sub) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = sub +: Sequent() } case class AndL(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isAnt) val And(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = sub1 +: sub2 +: Sequent() } case class OrR(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isSuc) val Or(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = Sequent() :+ sub1 :+ sub2 } case class ImpR(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isSuc) val Imp(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = sub1 +: Sequent() :+ sub2 } case class AndR1(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isSuc) val And(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = Sequent() :+ sub1 } case class OrL1(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isAnt) val Or(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = sub1 +: Sequent() } case class ImpL1(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isAnt) val Imp(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = Sequent() :+ sub1 } case class AndR2(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isSuc) val And(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = Sequent() :+ sub2 } case class OrL2(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isAnt) val Or(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = sub2 +: Sequent() } case class ImpL2(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { require(idx isAnt) val Imp(sub1, sub2) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = sub2 +: Sequent() } abstract class WeakQuantResolutionRule extends PropositionalResolutionRule { def variable: Var def bound: Var def sub: Formula def instFormula = Substitution(bound -> variable)(sub) // FIXME: is eigenvariable condition a good idea? require(!freeVariables(subProof.conclusion).contains(variable)) } case class AllR(subProof: ResolutionProof, idx: SequentIndex, variable: Var) extends WeakQuantResolutionRule { require(idx isSuc) val All(bound, sub) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = Sequent() :+ instFormula } object AllR { object Block { def apply(subProof: ResolutionProof, idx: SequentIndex, vars: Seq[Var]): ResolutionProof = vars match { case v +: vs => apply(AllR(subProof, idx, v), subProof.conclusion.indices.last, vs) case Seq() => subProof } def apply(subProof: ResolutionProof, idx: SequentIndex): ResolutionProof = { val All.Block(vs, _) = subProof.conclusion(idx) apply(subProof, idx, vs) } } } case class ExL(subProof: ResolutionProof, idx: SequentIndex, variable: Var) extends WeakQuantResolutionRule { require(idx isAnt) val Ex(bound, sub) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = instFormula +: Sequent() } abstract class SkolemQuantResolutionRule extends PropositionalResolutionRule { def skolemTerm: Expr def bound: Var def sub: Formula val Apps(skolemConst: Const, skolemArgs) = skolemTerm: @unchecked def instFormula = Substitution(bound -> skolemTerm)(sub) } trait SkolemQuantResolutionRuleCompanion { type R def apply(subProof: ResolutionProof, idx: SequentIndex, skolemTerm: Expr): R } case class AllL(subProof: ResolutionProof, idx: SequentIndex, skolemTerm: Expr) extends SkolemQuantResolutionRule { require(idx isAnt) val All(bound, sub) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = instFormula +: Sequent() } object AllL extends SkolemQuantResolutionRuleCompanion { type R = AllL } case class ExR(subProof: ResolutionProof, idx: SequentIndex, skolemTerm: Expr) extends SkolemQuantResolutionRule { require(idx isSuc) val Ex(bound, sub) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = Sequent() :+ instFormula } object ExR extends SkolemQuantResolutionRuleCompanion { type R = ExR } case class Flip(subProof: ResolutionProof, idx: SequentIndex) extends PropositionalResolutionRule { val Eq(t, s) = subProof.conclusion(idx): @unchecked def mainFormulaSequent = if (idx isSuc) Sequent() :+ Eq(s, t) else Eq(s, t) +: Sequent() } object Flip { def simulate(subProof: ResolutionProof, equation: SequentIndex): ResolutionProof = { val Eq(s, t) = subProof.conclusion(equation): @unchecked val x = rename(Var("x", s.ty), freeVariables(subProof.conclusion)) if (equation isSuc) { Paramod(subProof, equation, true, Refl(s), Suc(0), Abs(x, Eq(x, s))) } else { Resolution( Paramod(Taut(Eq(t, s)), Suc(0), false, Refl(s), Suc(0), Abs(x, Eq(s, x))), Suc(0), subProof, equation ) } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy