
gapt.proofs.gaptic.tactics.lkTactics.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.gaptic.tactics
import gapt.expr._
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.hol.HOLPosition
import gapt.expr.formula.hol.instantiate
import gapt.expr.util.freeVariables
import gapt.expr.util.rename
import gapt.logic.clauseSubsumption
import gapt.proofs._
import gapt.proofs.context.Context
import gapt.proofs.context.facet.ProofNames
import gapt.proofs.gaptic._
import gapt.proofs.lk.rules.AndLeftRule
import gapt.proofs.lk.rules.AndRightRule
import gapt.proofs.lk.rules.BottomAxiom
import gapt.proofs.lk.rules.CutRule
import gapt.proofs.lk.rules.EqualityLeftRule
import gapt.proofs.lk.rules.EqualityRightRule
import gapt.proofs.lk.rules.ExistsLeftRule
import gapt.proofs.lk.rules.ForallRightRule
import gapt.proofs.lk.rules.ImpLeftRule
import gapt.proofs.lk.rules.ImpRightRule
import gapt.proofs.lk.rules.LogicalAxiom
import gapt.proofs.lk.rules.NegLeftRule
import gapt.proofs.lk.rules.NegRightRule
import gapt.proofs.lk.rules.OrLeftRule
import gapt.proofs.lk.rules.OrRightRule
import gapt.proofs.lk.rules.ProofLink
import gapt.proofs.lk.rules.ReflexivityAxiom
import gapt.proofs.lk.rules.TopAxiom
import gapt.proofs.lk.rules.macros.ExistsRightBlock
import gapt.proofs.lk.rules.macros.ForallLeftBlock
/**
* Closes a goal with a proof link
*
* @param proofName The name of the proof proving the goal.
*/
case class ProofLinkTactic(proofName: String)(implicit ctx: Context) extends Tactical1[Unit] {
def apply(goal: OpenAssumption) = ctx.get[ProofNames].names.get(proofName) match {
case Some((term, refSeq)) => clauseSubsumption(refSeq, goal.conclusion, multisetSubsumption = true) match {
case Some(sub) => replace(ProofLink(sub(term), sub(refSeq)))
case None => TacticFailure(this, "Mismatch between goal " + goal.toString + " and Referenced Sequent " + refSeq.toString)
}
case None => TacticFailure(this, "Proof " + proofName + " not defined in context")
}
}
/**
* Closes a goal of the form A, Γ :- Γ, Δ
*/
case object LogicalAxiomTactic extends Tactical1[Unit] {
def apply(goal: OpenAssumption) = {
val candidates = goal.conclusion.antecedent intersect goal.conclusion.succedent
candidates match {
case Seq(formula, _*) => replace(LogicalAxiom(formula))
case _ => TacticFailure(this, "not a logical axiom")
}
}
}
/**
* Closes a goal of the form Γ :- Δ, ⊤
*/
case object TopAxiomTactic extends Tactical1[Unit] {
def apply(goal: OpenAssumption): Tactic[Unit] =
for {
case (_, Top(), _: Suc) <- findFormula(goal, AnyFormula)
_ <- replace(TopAxiom)
} yield ()
}
/**
* Closes a goal of the form ⊥, Γ :- Δ
*/
case object BottomAxiomTactic extends Tactical1[Unit] {
def apply(goal: OpenAssumption): Tactic[Unit] =
for {
case (_, Bottom(), _: Ant) <- findFormula(goal, AnyFormula)
_ <- replace(BottomAxiom)
} yield ()
}
/**
* Closes a goal of the form Γ :- Δ, s = s
*/
case object ReflexivityAxiomTactic extends Tactical1[Unit] {
object Refl {
def unapply(f: Formula): Option[Expr] = f match {
case Eq(t, t_) if t == t_ => Some(t)
case _ => None
}
}
def apply(goal: OpenAssumption) =
for {
case (_, Refl(t), _: Suc) <- findFormula(goal, AnyFormula)
_ <- replace(ReflexivityAxiom(t))
} yield ()
}
/**
* Decomposes a negation in the antecedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
*/
case class NegLeftTactic(mode: TacticApplyMode = UniqueFormula) extends Tactical1[String] {
def apply(goal: OpenAssumption) =
for {
case (existingLabel: String, Neg(f), i: Ant) <- findFormula(goal, mode)
newGoal = goal.labelledSequent.delete(i) :+ (existingLabel -> f)
_ <- replace(NegLeftRule(OpenAssumption(newGoal), newGoal.indices.last))
} yield existingLabel
}
/**
* Decomposes a negation in the succedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
*/
case class NegRightTactic(mode: TacticApplyMode = UniqueFormula) extends Tactical1[String] {
def apply(goal: OpenAssumption) =
for {
case (existingLabel: String, Neg(f), i: Suc) <- findFormula(goal, mode)
newGoal = (existingLabel -> f) +: goal.labelledSequent.delete(i)
_ <- replace(NegRightRule(OpenAssumption(newGoal), newGoal.indices.head))
} yield existingLabel
}
/**
* Removes a formula from the antecedent of a goal.
*
* @param applyToLabel The label of the formula to be removed.
*/
case class WeakeningLeftTactic(applyToLabel: String) extends Tactical1[Unit] {
def apply(goal: OpenAssumption) =
for {
case (_, _, i: Ant) <- findFormula(goal, OnLabel(applyToLabel))
_ <- replace(OpenAssumption(goal.labelledSequent delete i))
} yield ()
}
/**
* Removes a formula from the succedent of a goal.
*
* @param applyToLabel The label of the formula to be removed.
*/
case class WeakeningRightTactic(applyToLabel: String) extends Tactical1[Unit] {
def apply(goal: OpenAssumption) =
for {
case (_, _, i: Suc) <- findFormula(goal, OnLabel(applyToLabel))
_ <- replace(OpenAssumption(goal.labelledSequent delete i))
} yield ()
}
/**
* Decomposes a conjunction in the antecedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
*/
case class AndLeftTactic(mode: TacticApplyMode = UniqueFormula) extends Tactical1[(String, String)] {
def apply(goal: OpenAssumption) =
for {
case (label, And(lhs, rhs), idx: Ant) <- findFormula(goal, mode)
newLabel1 #:: newLabel2 #:: _ = NewLabels(goal.labelledSequent, label): @unchecked
newGoal = (newLabel1 -> lhs) +: (newLabel2 -> rhs) +: goal.labelledSequent.delete(idx)
_ <- replace(AndLeftRule(OpenAssumption(newGoal), Ant(0), Ant(1)))
} yield (newLabel1, newLabel2)
}
/**
* Decomposes a conjunction in the succedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
*/
case class AndRightTactic(mode: TacticApplyMode = UniqueFormula) extends Tactical1[Unit] with BinaryTactic[Unit] {
def apply(goal: OpenAssumption) =
for {
case (label, And(lhs, rhs), idx: Suc) <- findFormula(goal, mode)
_ <- replace(
AndRightRule(OpenAssumption(goal.labelledSequent.updated(idx, label -> lhs)), idx, OpenAssumption(goal.labelledSequent.updated(idx, label -> rhs)), idx)
)
} yield ()
}
/**
* Decomposes a disjunction in the antecedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
*/
case class OrLeftTactic(mode: TacticApplyMode = UniqueFormula) extends Tactical1[Unit] with BinaryTactic[Unit] {
def apply(goal: OpenAssumption) =
for {
case (label, Or(lhs, rhs), idx: Ant) <- findFormula(goal, mode)
_ <- replace(
OrLeftRule(OpenAssumption(goal.labelledSequent.updated(idx, label -> lhs)), idx, OpenAssumption(goal.labelledSequent.updated(idx, label -> rhs)), idx)
)
} yield ()
}
/**
* Decomposes a disjunction in the succedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
*/
case class OrRightTactic(mode: TacticApplyMode = UniqueFormula) extends Tactical1[(String, String)] {
def apply(goal: OpenAssumption) =
for {
case (label, Or(lhs, rhs), idx: Suc) <- findFormula(goal, mode)
newLabel1 #:: newLabel2 #:: _ = NewLabels(goal.labelledSequent, label): @unchecked
newGoal = goal.labelledSequent.delete(idx) :+ (newLabel1 -> lhs) :+ (newLabel2 -> rhs)
Seq(rhsIdx, lhsIdx) = newGoal.indices.reverse.take(2)
_ <- replace(OrRightRule(OpenAssumption(newGoal), lhsIdx, rhsIdx))
} yield (newLabel1, newLabel2)
}
/**
* Decomposes an implication in the antecedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
*/
case class ImpLeftTactic(mode: TacticApplyMode = UniqueFormula) extends Tactical1[Unit] with BinaryTactic[Unit] {
def apply(goal: OpenAssumption) =
for {
case (label, Imp(lhs, rhs), idx: Ant) <- findFormula(goal, mode)
_ <- replace(
ImpLeftRule(
OpenAssumption(goal.labelledSequent.delete(idx) :+ (label -> lhs)),
Suc(goal.labelledSequent.succedent.size),
OpenAssumption(goal.labelledSequent.updated(idx, label -> rhs)),
idx
)
)
} yield ()
}
/**
* Decomposes an implication in the succedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
*/
case class ImpRightTactic(mode: TacticApplyMode = UniqueFormula) extends Tactical1[(String, String)] {
// TODO: keep label for rhs?
def apply(goal: OpenAssumption) =
for {
case (label, Imp(lhs, rhs), idx: Suc) <- findFormula(goal, mode)
newLabel1 #:: newLabel2 #:: _ = NewLabels(goal.labelledSequent, label): @unchecked
newGoal = (newLabel1 -> lhs) +: goal.labelledSequent.updated(idx, newLabel2 -> rhs)
_ <- replace(ImpRightRule(OpenAssumption(newGoal), Ant(0), idx))
} yield (newLabel1, newLabel2)
}
abstract class StrongQuantTactic extends Tactical1[Var] {
def eigenVariable: Option[Var]
protected def pickEigenvariable(bound: Var, goal: OpenAssumption): Tactic[Var] =
eigenVariable match {
case Some(ev) =>
if (freeVariables(goal.conclusion) contains ev)
TacticFailure(this, "Provided eigenvariable would violate eigenvariable condition.")
else
Tactic.pure(ev)
case None =>
Tactic.pure(rename(bound, freeVariables(goal.conclusion)))
}
}
/**
* Decomposes an existential quantifier in the antecedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
* @param eigenVariable If Some(v), the rule will attempt to use v as the eigenvariable. Otherwise it will automatically pick one.
*/
case class ExistsLeftTactic(mode: TacticApplyMode = UniqueFormula, eigenVariable: Option[Var] = None) extends StrongQuantTactic {
def apply(goal: OpenAssumption) =
for {
case (label, f @ Ex(bound, _), idx: Ant) <- findFormula(goal, mode)
ev <- pickEigenvariable(bound, goal)
_ <- replace(ExistsLeftRule(OpenAssumption(goal.labelledSequent.updated(idx, label -> instantiate(f, ev))), f, ev))
} yield ev
}
/**
* Decomposes a block of existential quantifiers in the antecedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
* @param terms Instantiations for the quantifiers in the block.
* @param instantiateOnce Whether the quantified formula should be forgotten after instantiating.
*/
case class ExistsRightTactic(mode: TacticApplyMode = UniqueFormula, terms: Seq[Expr], instantiateOnce: Boolean) extends Tactical1[String] {
def apply(goal: OpenAssumption) =
for {
case (label: String, f @ Ex(_, _), idx: Suc) <- findFormula(goal, mode)
newLabel = NewLabel(goal.labelledSequent, label)
instantiatedFormula = BetaReduction.betaNormalize(instantiate(f, terms))
newLS = if (instantiateOnce) goal.labelledSequent.updated(idx, (label, instantiatedFormula))
else goal.labelledSequent :+ (newLabel -> instantiatedFormula)
_ <- replace(ExistsRightBlock(OpenAssumption(newLS), f, terms))
} yield if (instantiateOnce) label else newLabel
def forget = ExistsRightTactic(mode, terms, instantiateOnce = true)
}
/**
* Decomposes a block of universal quantifiers in the succedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
* @param terms Instantiations for the quantifiers in the block.
* @param instantiateOnce Whether the quantified formula should be forgotten after instantiating.
*/
case class ForallLeftTactic(mode: TacticApplyMode = UniqueFormula, terms: Seq[Expr], instantiateOnce: Boolean) extends Tactical1[String] {
def apply(goal: OpenAssumption) =
for {
case (label: String, f @ All(_, _), idx: Ant) <- findFormula(goal, mode)
newLabel = NewLabel(goal.labelledSequent, label)
instantiatedFormula = BetaReduction.betaNormalize(instantiate(f, terms))
newLS = if (instantiateOnce) goal.labelledSequent.updated(idx, (label, instantiatedFormula))
else (newLabel -> instantiatedFormula) +: goal.labelledSequent
_ <- replace(ForallLeftBlock(OpenAssumption(newLS), f, terms))
} yield if (instantiateOnce) label else newLabel
def forget = ForallLeftTactic(mode, terms, instantiateOnce = true)
}
/**
* Decomposes a universal quantifier in the succedent of a goal.
*
* @param mode How to apply the tactic: To a specific label, to the only fitting formula, or to any fitting formula.
* @param eigenVariable If Some(v), the rule will attempt to use v as the eigenvariable. Otherwise it will automatically pick one.
*/
case class ForallRightTactic(mode: TacticApplyMode = UniqueFormula, eigenVariable: Option[Var] = None) extends StrongQuantTactic {
def apply(goal: OpenAssumption) =
for {
case (label, f @ All(bound, _), idx: Suc) <- findFormula(goal, mode)
ev <- pickEigenvariable(bound, goal)
_ <- replace(ForallRightRule(OpenAssumption(goal.labelledSequent.updated(idx, label -> instantiate(f, ev))), f, ev))
} yield ev
}
/**
* Introduces a cut, creating two new subgoals.
*
* @param cutFormula The cut formula.
* @param cutLabel The label for the cut formula.
*/
case class CutTactic(cutLabel: String, cutFormula: Formula) extends Tactical1[Unit] with BinaryTactic[Unit] {
override def apply(goal: OpenAssumption) = {
val goalSequent = goal.labelledSequent
val leftPremise = OpenAssumption(goalSequent :+ (cutLabel, cutFormula))
val rightPremise = OpenAssumption((cutLabel, cutFormula) +: goalSequent)
val auxProof = CutRule(leftPremise, Suc(leftPremise.labelledSequent.succedent.length - 1), rightPremise, Ant(0))
replace(auxProof)
}
}
/**
* Applies an equation in a goal.
*
* @param equationLabel The label of the equation.
* @param formulaLabel The label of the formula the equation is to be used on.
* @param leftToRight If `Some(true)`, the equation `s = t` will be used to rewrite `s` to `t`, and the other way around
* for Some(false). If `None`, the tactic will attempt to decide the direction automatically.
* @param targetFormula If `Some(f)`, the tactic will attempt to produce `f` through application of the equality. Otherwise
* it will replace as many occurrences as possible according to `leftToRight`.
*/
case class EqualityTactic(equationLabel: String, formulaLabel: String, private val leftToRight: Option[Boolean] = None, private val targetFormula: Option[Formula] = None) extends Tactical1[Unit] {
override def apply(goal: OpenAssumption) = {
val goalSequent = goal.labelledSequent
val indices = for (
case ((`equationLabel`, Eq(_, _)), eqIndex) <- goalSequent.zipWithIndex.antecedent;
case ((`formulaLabel`, _), formulaIndex) <- goalSequent.zipWithIndex.elements
) yield (eqIndex, formulaIndex)
indices.headOption match {
case None => TacticFailure(this, "label not found")
case Some((equalityIndex, formulaIndex)) =>
val (_, Eq(s, t)) = goalSequent(equalityIndex): @unchecked
val (_, auxFormula) = goalSequent(formulaIndex)
def f(l: List[HOLPosition], h: Formula, r: Expr): Formula = l match {
case x :: xs => f(xs, h.replace(x, r), r)
case Nil => h
}
def testValidity(mainFormula: Formula): Boolean = {
if (s == t && auxFormula == mainFormula) {
val sAux = auxFormula.find(s)
if (sAux.isEmpty)
false
else
true
} else if (s == t && auxFormula != mainFormula)
false
else if (s != t && auxFormula == mainFormula)
false
else {
val sAux = auxFormula.find(s)
val sMain = mainFormula.find(s)
val tAux = auxFormula.find(t)
val tMain = mainFormula.find(t)
if (sAux.isEmpty && tAux.isEmpty)
false
else {
val tToS = sMain intersect tAux
val sToT = tMain intersect sAux
if (tToS.isEmpty) {
val mainNew = sToT.foldLeft(auxFormula) { (acc, p) => HOLPosition.replace(acc, p, t) }
if (mainNew == mainFormula)
true
else
false
} else if (sToT.isEmpty) {
val mainNew = tToS.foldLeft(auxFormula) { (acc, p) => HOLPosition.replace(acc, p, s) }
if (mainNew == mainFormula)
true
else
false
} else
false
}
}
}
val replacement = targetFormula match {
case Some(mainFormula) =>
if (testValidity(mainFormula))
targetFormula
else None
case None =>
val r = leftToRight match {
case Some(true) =>
f(auxFormula.find(s), auxFormula, t)
case Some(false) =>
f(auxFormula.find(t), auxFormula, s)
case None =>
((auxFormula.find(s), auxFormula.find(t)): @unchecked) match {
case (Nil, ps) if ps.nonEmpty =>
f(ps, auxFormula, s)
case (ps, Nil) if ps.nonEmpty =>
f(ps, auxFormula, t)
}
}
Option(r)
case null => None
}
replacement match {
case Some(x) if x != auxFormula =>
val newGoal = goalSequent.updated(formulaIndex, formulaLabel -> x)
val premise = OpenAssumption(newGoal)
replace(if (formulaIndex.isAnt) EqualityLeftRule(premise, equalityIndex, formulaIndex, auxFormula)
else EqualityRightRule(premise, equalityIndex, formulaIndex, auxFormula))
case _ => TacticFailure(this, "FIXME")
}
}
}
def fromLeftToRight = EqualityTactic(equationLabel, formulaLabel, leftToRight = Some(true))
def fromRightToLeft = EqualityTactic(equationLabel, formulaLabel, leftToRight = Some(false))
def yielding(targetFormula: Formula) = EqualityTactic(equationLabel, formulaLabel, targetFormula = Some(targetFormula))
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy