
gapt.proofs.resolution.ResolutionToExpansionProof.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.Formula
import gapt.expr.subst.Substitution
import gapt.expr.util.freeVariables
import gapt.expr.util.rename
import gapt.logic.Polarity
import gapt.proofs.context.Context
import gapt.proofs.context.mutable.MutableContext
import gapt.proofs.{HOLSequent, Sequent, RichFormulaSequent}
import gapt.proofs.expansion._
import gapt.provers.sat.Sat4j
import gapt.utils.Maybe
import scala.collection.mutable
/**
* Converts a resolution proof to an expansion proof.
* ResolutionToExpansionProof( rp ) will return the expansion proof of rp and ResolutionToExpansionProof( rp, input )
* is used by the CERES method to return the expansion proof of the ACNF, which is extracted from the proof projections.
*
* Let us first ignore splitting and subformula definitions for simplicity.
* However we may still have clausification inferences.
* The conversion then proceeds bottom-up through the resolution proof (to be precise: in a reverse post-order traversal).
*
* At each point in the algorithm, we associate with every subproof a set of expansion sequents and associated substitutions,
* i.e. we keep a `Map[ResolutionProof, Set[(Substitution, ExpansionSequent)] ]` with the following properties:
* 1. the conjunction of all deep sequents (each interpreted as a disjunction) is unsatisfiable, and
* 2. the substitution applied to the shallow sequent is always equal to the conclusion of the subproof.
*
* In every step we consider a subproof, empty its `Set[(Substitution, ExpansionSequent)]`,
* and add in exchange the appropriate sets to its premises, while keeping the invariant. In this manner,
* these sets propagate upwards through the proof to the input sequents.
*
* Let us first consider the common case that the input sequents are ground unit sequents: i.e. if we obtained a resolution
* proof for `∀x A(x), ∀x B(x) :- ∀x C(x)`, then we would have the input sequents `:- ∀x A(x)`, `:- ∀x B(x)`, and `∀x C(x) :-`.
* When we have finally propagated all the sets up to the input sequents, the invariant guarantees the following:
* we have expansion sequents with the input sequents as shallow sequents (at this point we use the
* requirement that the input sequents are ground), such that the conjunction of the deep sequents
* (interpreted as disjunctions) is unsatisfiable.
* This immediately implies that if we combine the expansions of the input sequents into a sequent, we get an
* expansion proof of `∀x A(x), ∀x B(x) :- ∀x C(x)`.
*
* In the unlikely case that the resolution proof starts from clauses, we just pretend that the clauses are derived
* from formulas--that is, we instead of beginning with `D(x), E(x,y) :- F(y)` we just imagine the proof begins with
* `:- ∀x ∀y (¬D(x) ∨ ¬E(x,y) ∨ F(y))`.
*
* Splitting inferences are converted to cuts in the expansion proof: formally, we can first skip all the splitting
* inferences in the resoltion proof; the assertions now become part of the clauses, and we get additional input sequents.
* For example, consider the clauses `:- A(x), B(y)`, `A(c) :-`, and `B(c) :-`. The natural way to refute these clauses
* is to split the first clause into `:- A(x) <-- s1` and `:- B(y) <-- s2`, then perform unit resolution twice, and
* then have a propositional contradiction. After skipping the splitting inferences we would get a proof starting from
* `:- s1, s2`, `s1 :- A(x)`, `s2 :- B(x)`, `A(c) :-`, and `B(c) :-`. If we replace `s1 := ∀x A(x)` and `s2 := ∀x B(x)`
* everywhere in the resulting expansion proof, then we can package up the expansions of `s1 :- A(x)` and `s2 :- B(x)` as cuts.
* The other clauses are then precisely the clause we had before splitting.
*
* Subformula definitions are eliminated after the conversion to expansion proofs, see
* [[gapt.proofs.expansion.eliminateDefsET]].
*/
object ResolutionToExpansionProof {
def apply(proof: ResolutionProof)(implicit ctx: Maybe[Context]): ExpansionProof = {
apply(proof, inputsAsExpansionSequent)
}
def inputsAsExpansionSequent(input: Input, set: Set[(Substitution, ExpansionSequent)]): ExpansionSequent = {
input match {
case Input(Sequent(Seq(f), Seq())) if freeVariables(f).isEmpty =>
Sequent() :+ ETMerge(f, Polarity.InSuccedent, set.map(_._2.elements.head))
case Input(Sequent(Seq(), Seq(f))) if freeVariables(f).isEmpty =>
ETMerge(f, Polarity.InAntecedent, set.map(_._2.elements.head)) +: Sequent()
case Input(seq) =>
val fvs = freeVariables(seq).toSeq
val sh = All.Block(fvs, seq.toDisjunction)
ETWeakQuantifierBlock(sh, fvs.size, for ((subst, es) <- set) yield subst(fvs) -> es.toDisjunction(Polarity.Negative)) +: Sequent()
}
}
def apply(
proof: ResolutionProof,
input: (Input, Set[(Substitution, ExpansionSequent)]) => ExpansionSequent
)(
implicit ctx: Maybe[Context]
): ExpansionProof = {
implicit val ctx1: Context = ctx.getOrElse(MutableContext.guess(proof))
val expansionWithDefs = withDefs(proof, input)
val defConsts = proof.subProofs collect { case d: DefIntro => d.defConst: Const }
eliminateCutsET(eliminateDefsET(eliminateCutsET(expansionWithDefs), !containsEquationalReasoning(proof), defConsts))
}
private implicit class RichPair[A, B](private val pair: (A, B)) extends AnyVal {
def map1[A_](f: A => A_): (A_, B) = (f(pair._1), pair._2)
def map2[B_](f: B => B_): (A, B_) = (pair._1, f(pair._2))
}
private val debugCheckTyping = false
private val debugCheckTautology = false
/**
* Performs the conversion without eliminating the definitions
* introduced by structural clausification.
*/
def withDefs(
proof: ResolutionProof,
input: (Input, Set[(Substitution, ExpansionSequent)]) => ExpansionSequent,
addConclusion: Boolean = true
): ExpansionProof = {
val nameGen = rename.awayFrom(containedNames(proof))
val expansions = mutable.Map[ResolutionProof, Set[(Substitution, Sequent[ETt])]]().withDefaultValue(Set())
val cuts = mutable.Buffer[ETCut.Cut]()
var expansionSequent: ExpansionSequent =
if (!addConclusion) Sequent()
else proof.conclusion.zipWithIndex.map { case (a, i) => ETAtom(a.asInstanceOf[Atom], !i.polarity) }
val splitDefn = mutable.Map[Atom, Formula]()
val splitCutL = mutable.Map[Atom, List[ExpansionTree]]().withDefaultValue(Nil)
val splitCutR = mutable.Map[Atom, List[ExpansionTree]]().withDefaultValue(Nil)
def mkExpSeq(p: ResolutionProof, s: Substitution, ts: Sequent[ETt]): ExpansionSequent =
for (((f, t), i) <- p.conclusion.zip(ts).zipWithIndex)
yield ExpansionTree(BetaReduction.betaNormalize(s(f)), !i.polarity, t)
def propg_(p: ResolutionProof, q: ResolutionProof, f: Set[(Substitution, Sequent[ETt])] => Set[(Substitution, Sequent[ETt])]) = {
val fvsQ = freeVariables(q.conclusion)
val newEs = f(expansions(p)).map(_.map1(_.restrict(fvsQ)))
if (debugCheckTyping) for ((s, e) <- newEs) mkExpSeq(q, s, e).foreach(_.check())
expansions(q) = expansions(q) union newEs
}
def propg(p: ResolutionProof, q: ResolutionProof, f: Set[(Substitution, Sequent[ETt])] => Set[(Substitution, Sequent[ETt])]) = {
propg_(p, q, f)
clear(p)
}
def clear(p: ResolutionProof) = {
expansions -= p
if (debugCheckTautology) { // expensive invariant check
val deepExpansions = for {
(q, exp) <- expansions
(subst, es) <- exp
} yield (q.assertions ++ mkExpSeq(q, subst, es).deep).toDisjunction
val splitL = for ((a, es) <- splitCutL if splitDefn.contains(a); e <- es) yield e.deep --> a
val splitR = for ((a, es) <- splitCutR if splitDefn.contains(a); e <- es) yield a --> e.deep
val deep = And(cuts.map(_.deep)) & And(deepExpansions) & And(splitL) & And(splitR) &
expansionSequent.deep.toNegConjunction
require(Sat4j isUnsat deep)
}
}
def propgm2(p: ResolutionProof, q: ResolutionProof, f: Sequent[ETt] => Sequent[ETt]) =
propg(p, q, _.map(_.map2(f)))
def prop1(p: PropositionalResolutionRule, f: ETt => ETt) = {
val Seq(oc) = p.occConnectors
propgm2(p, p.subProof, es => oc.parent(es).updated(p.idx, f(es(p.mainIndices.head))))
}
def prop1s(p: PropositionalResolutionRule, f: (Substitution, ETt) => ETt) = {
val Seq(oc) = p.occConnectors
propg(p, p.subProof, _.map(es => es._1 -> oc.parent(es._2).updated(p.idx, f(es._1, es._2(p.mainIndices.head)))))
}
def prop2(p: PropositionalResolutionRule, f: (ETt, ETt) => ETt) = {
val Seq(oc) = p.occConnectors
propgm2(p, p.subProof, es => oc.parent(es, f(es(p.mainIndices(0)), es(p.mainIndices(1)))))
}
def sequent2expansions(sequent: HOLSequent): Set[(Substitution, Sequent[ETt])] =
Set(Substitution() -> sequent.map(_ => ETtAtom))
expansions(proof) = sequent2expansions(proof.conclusion)
proof.dagLike.postOrder.reverse.foreach {
case p @ Input(seq) =>
expansionSequent ++= input(Input(seq), expansions(p).map { case (s, ts) => s -> mkExpSeq(p, s, ts) })
clear(p)
case p @ Defn(_, _) =>
expansionSequent +:= ExpansionTree(p.definitionFormula, Polarity.InAntecedent, ETtMerge(expansions(p).map(_._2.elements.head)))
clear(p)
case p @ Taut(f) =>
for {
(s, Sequent(Seq(l), Seq(r))) <- expansions(p)
if l != ETtAtom || r != ETtAtom
} cuts += ETCut.Cut(BetaReduction.betaNormalize(s(f)), l, r)
clear(p)
case p @ Refl(_) =>
clear(p)
case p @ Factor(q, _, _) =>
val Seq(oc) = p.occConnectors
propgm2(p, q, oc.parent(_))
case p @ Subst(q, subst) =>
val subFVs = freeVariables(q.conclusion)
propg(p, q, _.map(_.map1(_ compose subst restrict subFVs)))
case p @ Resolution(q1, _, q2, _) =>
val Seq(oc1, oc2) = p.occConnectors
propg_(p, q1, _.map(es => es._1 -> oc1.parent(es._2, ETtAtom)))
propg(p, q2, _.map(es => es._1 -> oc2.parent(es._2, ETtAtom)))
case p @ DefIntro(q, i, _, _) =>
val Seq(oc) = p.occConnectors
propg(
p,
q,
_.map(es =>
es._1 -> oc.parent(es._2).updated(
i,
ETtDef(es._1(p.defAtom), ETtAtom)
)
)
)
case p @ Paramod(q1, i1, _, q2, _, _) =>
val Seq(oc1, oc2) = p.occConnectors
propg_(p, q1, _.map(es => es._1 -> oc1.parent(es._2).updated(i1, ETtAtom)))
propg(p, q2, _.map(es => es._1 -> oc2.parent(es._2)))
case p @ AvatarSplit(q, _, AvatarGroundComp(_, _)) =>
val Seq(oc) = p.occConnectors
propg(p, q, _.map(_.map2(es => oc.parent(es, ETtAtom))))
case p @ AvatarSplit(q, _, comp @ AvatarNonGroundComp(splAtom, definition, vars)) =>
val renaming = Substitution(for (v <- freeVariables(comp.clause)) yield v -> nameGen.fresh(v))
splitDefn(splAtom) = definition
splitCutL(splAtom) ::= ETStrongQuantifierBlock(
definition,
renaming(vars).map(_.asInstanceOf[Var]),
formulaToExpansionTree(renaming(comp.disjunction), Polarity.InSuccedent)
)
val Seq(oc) = p.occConnectors
propg(
p,
q,
_.map(es =>
renaming.compose(es._1) -> oc.parents(es._2).zipWithIndex.map {
x =>
(x: @unchecked) match {
case (Seq(et), _) => et
case (Seq(), _) => ETtAtom
}
}
)
)
case p @ AvatarComponent(AvatarGroundComp(_, _)) =>
clear(p)
case p @ AvatarComponent(AvatarNegNonGroundComp(_, _, _, _)) =>
clear(p)
case p @ AvatarComponent(AvatarNonGroundComp(splAtom, definition, vars)) =>
splitDefn(splAtom) = definition
splitCutR(splAtom) ::= ETWeakQuantifierBlock(
definition,
vars.size,
for ((s, es0) <- expansions(p); es = mkExpSeq(p, s, es0))
yield s(vars) -> es.toDisjunction(Polarity.Negative)
)
clear(p)
case p @ AvatarContradiction(q) =>
propg(p, q, _ => sequent2expansions(q.conclusion))
case p: Flip =>
prop1(
p,
{
case ETtAtom => ETtAtom
case _ => throw new IllegalArgumentException
}
)
case p @ TopL(q, _) =>
val Seq(oc) = p.occConnectors
propgm2(p, q, oc.parent(_, ETtNullary))
case p @ BottomR(q, _) =>
val Seq(oc) = p.occConnectors
propgm2(p, q, oc.parent(_, ETtNullary))
case p: NegL => prop1(p, ETtUnary.apply)
case p: NegR => prop1(p, ETtUnary.apply)
case p: AndL => prop2(p, ETtBinary.apply)
case p: OrR => prop2(p, ETtBinary.apply)
case p: ImpR => prop2(p, ETtBinary.apply)
case p: AndR1 => prop1(p, ETtBinary(_, ETtWeakening))
case p: OrL1 => prop1(p, ETtBinary(_, ETtWeakening))
case p: ImpL1 => prop1(p, ETtBinary(_, ETtWeakening))
case p: AndR2 => prop1(p, ETtBinary(ETtWeakening, _))
case p: OrL2 => prop1(p, ETtBinary(ETtWeakening, _))
case p: ImpL2 => prop1(p, ETtBinary(ETtWeakening, _))
case p: WeakQuantResolutionRule =>
val Seq(oc) = p.occConnectors
val subFVs = freeVariables(p.subProof.conclusion)
propg(
p,
p.subProof,
_.groupBy(_._1.restrict(subFVs)).view.mapValues(ess =>
for (i <- p.subProof.conclusion.indicesSequent; j = oc.child(i))
yield
if (i == p.idx)
ETtWeak(Map() ++ ess.groupBy(_._1(p.variable)).view.mapValues(_.map(_._2(j))).mapValues(ETtMerge(_)).toMap)
else
ETtMerge(ess.map(_._2(j)))
).toSet
)
case p: SkolemQuantResolutionRule =>
prop1s(p, (s, et) => ETtSkolem(s(p.skolemTerm), et))
}
for ((splAtom, defn) <- splitDefn)
cuts += (
ETMerge(defn, Polarity.InSuccedent, splitCutL(splAtom)) ->
ETMerge(defn, Polarity.InAntecedent, splitCutR(splAtom))
)
ExpansionProof(eliminateMerges(ETMerge(ETCut(cuts) +: expansionSequent)))
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy