gapt.proofs.resolution.resolution.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.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:
*
* - [[Input]] rules for formulas of the end-sequent
*
- [[AndL]], [[AllR]], etc. for the clausification. A combination of
* [[DefIntro]] and [[Taut]] is used to introduce definitions for subformulas.
*
- [[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]].
*
- Each empty clause of a splitting branch is followed by an
* [[AvatarContradiction]] inference, moving the assertion back to the clause.
*
- 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
)
}
}
}