Documentation

Pdl.Completeness.BuildTree

From winning strategies to model graphs, part 1: BuildTree and PreState (Section 6.3) #

The uniform open local tableau #

Open local tableaux for X that are the uniform one, i.e. uniLocalTab X. This type has at most one element, and it is inhabited iff uniLocalTab X has an end node.

Equations
Instances For
    @[instance_reducible]
    Equations

    All uniform open local tableaux for X: the singleton list containing uniLocalTab X if that has an end node, and the empty list otherwise. Analogue of OpenLocalTableau.all.

    Equations
    Instances For
      theorem UniOpenLT.all_spec {X : Sequent} {ltX : UniOpenLT X} :
      ltX all X

      Analogue of OpenLocalTableau.all_spec.

      Builder Strategy Tree #

      inductive BuildTree :

      Winning Strategy Tree for Builder. At each step, we consider

      • ALL rules R that prover may choose, followed immediately by
      • ONE of the children then chosen by Builder

      The type is actually similar to Tableau, as it also uses a history, but it does allow open leaves. For choosing a local tableau end node the mutual RuleChoice is needed to avoid the error "nested inductive datatypes parameters cannot contain local variables". Instead of the .lpr constructor here we have .fpr because we only make a RuleTree when Builder wins and thus we can never reach an lpr where Prover would win, but do allow free repeats. As in Tableau note that the history is stored in reverse.

      Instances For
        inductive BuildChoice :
        Instances For
          noncomputable def BuildTree.size {H : History} {X : Sequent} :
          BuildTree H X

          Manual replacement for sizeOf (bt : BuildTree) so we also count the next parts.

          Equations
          Instances For
            noncomputable def BuildChoice.size {H : History} {X : Sequent} {YS : Finset Sequent} :
            BuildChoice H X YS
            Equations
            Instances For
              theorem BuildTree.size_lt_loc (H : History) (X : Sequent) (nbas : ¬X.basic) (next : (lt : UniOpenLT X) → BuildChoice H X (endNodesOf lt)) (ltX : UniOpenLT X) (someLT : UniOpenLT.all X []) :
              (next ltX).6.size < (loc nbas someLT next).size
              theorem BuildTree.size_lt_pdl (H : History) (X : Sequent) (bas : X.basic) (someR : PdlRule.all X []) (next : (Y : Sequent) → PdlRule X YBuildTree (X :: H) Y) (Y : Sequent) (r : PdlRule X Y) :
              (next Y r).size < (pdl bas someR next).size
              @[simp]
              theorem BuildChoice.fst_eq {H : History} {X : Sequent} {YS : Finset Sequent} {bc : BuildChoice H X YS} :
              bc.1 = H
              @[simp]
              theorem BuildChoice.snd_eq {H : History} {X : Sequent} {YS : Finset Sequent} {bc : BuildChoice H X YS} :
              bc.2 = X
              @[simp]
              theorem BuildChoice.thrd_eq {H : History} {X : Sequent} {YS : Finset Sequent} {bc : BuildChoice H X YS} :
              bc.3 = YS
              theorem BuildChoice.frth_mem {H : History} {X : Sequent} {YS : Finset Sequent} {bc : BuildChoice H X YS} :
              bc.4 YS

              The node picked by Builder is one of the given ones.

              @[instance_reducible]
              Equations
              • One or more equations did not get rendered due to their size.
              def BuildTree.getFreeRepeat {H : History} {X : Sequent} {bt : BuildTree H X} (h : bt.isFreeRepeat) :
              Equations
              • One or more equations did not get rendered due to their size.
              Instances For
                def FreeRepeat.of_rep_free {H : History} {X : Sequent} (rp : rep H X) (free : ¬X.isLoaded) :

                Given the proof rep H X and that X is free, find a FreeRepeat value / data.

                (Previously here we tried to go from rep H X and ¬Nonempty (LoadedPathRepeat H X) to FreeRepeat which does not work as there might still be loaded non-lpr repeats.)

                Equations
                Instances For
                  @[irreducible]
                  noncomputable def buildTree (s : Strategy tableauGame Builder) {H : History} {X : Sequent} {p : ProverPos H X} (h : winning s H, X, Sum.inl p) :

                  Given a winning Builder strategy, compute its BuildTree. NEW: note the Sum.inl p here. This ensure we start tree building from a Prover position, i.e.

                  • not allowing BuilderPos.lpr here (easy, was forbidden already anyway as prover wins there.)
                  • not allowing BuilderPos.ltab because we cannot use BuildTree.loc for a single fixed local tab.
                  Equations
                  Instances For

                    Matches #

                    inductive Match {H : History} {X : Sequent} :
                    BuildTree H XType

                    A match is a path inside a BuildTree. Analogous to PathIn for Tableau. In Game Theory this could be called a "rollout", but note that it stays within the given Builder strategy tree and it is not tracking all intermediate game positions.

                    Instances For
                      @[instance_reducible]
                      instance instDecidableEqMatch {H✝ : History} {X✝ : Sequent} {a✝ : BuildTree H✝ X✝} :
                      Equations
                      def instDecidableEqMatch.decEq {H✝ : History} {X✝ : Sequent} {a✝ : BuildTree H✝ X✝} (x✝ x✝¹ : Match a✝) :
                      Decidable (x✝ = x✝¹)
                      Equations
                      Instances For
                        @[implicit_reducible]
                        def Match.length {H : History} {X : Sequent} {bt : BuildTree H X} :
                        Match bt

                        Inspired by PathIn.length. Counting the steps made by a Match in a BuildTree. Note that such a step is a combination of a prover and a builder move.

                        Equations
                        Instances For
                          @[implicit_reducible]
                          def Match.btAt {H : History} {X : Sequent} {bt : BuildTree H X} :
                          Match bt(H' : History) × (Y : Sequent) × BuildTree H' Y
                          Equations
                          Instances For
                            def Match.endSeq {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) :

                            The sequent reached at the end of a match.

                            Equations
                            Instances For
                              @[irreducible]
                              noncomputable def Match.all {H : History} {X : Sequent} (bt : BuildTree H X) :
                              List (Match bt)
                              Equations
                              Instances For
                                theorem Match.all_spec {H : History} {X : Sequent} {bt : BuildTree H X} {m : Match bt} :
                                m all bt
                                def Match.isOpenLeaf {H : History} {X : Sequent} {bt : BuildTree H X} {m : Match bt} :
                                Equations
                                Instances For
                                  @[instance_reducible]
                                  instance instDecidableIsOpenLeaf {a✝ : History} {a✝¹ : Sequent} {bt : BuildTree a✝ a✝¹} {m : Match bt} :
                                  Equations
                                  • One or more equations did not get rendered due to their size.
                                  def Match.isFreeRepeat {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) :
                                  Equations
                                  Instances For
                                    @[instance_reducible]
                                    Equations
                                    • One or more equations did not get rendered due to their size.

                                    Get the FreeRepeat (rewind-index and same-sequent proof) of a Match.

                                    Equations
                                    Instances For
                                      def Match.append {H : History} {X : Sequent} {bt : BuildTree H X} (m1 : Match bt) (m2 : Match m1.btAt.snd.snd) :
                                      Equations
                                      Instances For
                                        theorem Match.btAt_append {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) (c : Match m.btAt.snd.snd) :
                                        (m.append c).btAt = c.btAt

                                        Appending matches: the node reached is the one reached by the second match.

                                        theorem Match.endSeq_append {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) (c : Match m.btAt.snd.snd) :

                                        Appending matches: the sequent reached is the one reached by the second match.

                                        def Match.rewind {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) (k : Fin (m.length + 1)) :

                                        Rewind a Match, i.e. go back up inside bt by k steps. The + 1 is there because going back 0 steps does nothing.

                                        Equations
                                        Instances For
                                          @[simp]
                                          theorem Match.rewind_zero {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) :
                                          m.rewind 0 = m

                                          Rewinding 0 steps does nothing.

                                          theorem Match.rewind_length_lt_length_of_pos {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) (k : Fin (m.length + 1)) (k_pos : 0 < k) :

                                          Inspired by PathIn.rewind_length_lt_length_of_gt_zero.

                                          def Match.companionOf {X : Sequent} {bt : BuildTree [] X} (m : Match bt) (h : m.isFreeRepeat) :

                                          Roll back to the companion. Only possibe if we started with H=[] so we know the root. The + 1 is there because the FreeRepeat values are indices of the history starting with 0, but Match.rewind 0 would do nothing. (Same as the .succ in companionOf for PathIn.)

                                          Equations
                                          Instances For
                                            @[implicit_reducible]
                                            def Match.toHistory {H : History} {X : Sequent} {bt : BuildTree H X} :
                                            Match btHistory

                                            The sequents visited by a Match, in reverse order and not including the last one. Analogous to PathIn.toHistory.

                                            Equations
                                            Instances For
                                              @[simp]
                                              theorem Match.toHistory_length {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) :
                                              theorem Match.toHistory_append_eq_btAt_fst {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) :

                                              The history reached by a Match consists of the sequents visited, then the old history.

                                              @[simp]
                                              theorem Match.rewind_last {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) :
                                              theorem Match.btAt_rewind_eq_toHistory_get {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) (k : Fin (m.length + 1)) :

                                              Rewinding a Match by k steps gives the k-th element of the history, where the end sequent of the match itself is counted as the 0-th element. Inspired by PathIn.nodeAt_rewind_eq_toHistory_get.

                                              def Match.companion {X : Sequent} {bt : BuildTree [] X} (m n : Match bt) :

                                              The repeat ♥ companion relation on Match.

                                              Equations
                                              Instances For
                                                theorem Match.companionOf_setEqTo_sequent {a✝ : Sequent} {bt : BuildTree [] a✝} (m : Match bt) (h : m.isFreeRepeat) :

                                                The sequent at the companion is the same as the sequent at the repeat. Similar to nodeAt_companionOf_setEq.

                                                theorem Match.companionOf_length_lt {X : Sequent} {bt : BuildTree [] X} (m : Match bt) (h : m.isFreeRepeat) :

                                                Going to the companion of a free repeat gives a strictly shorter Match.

                                                Collecting Sequents for Pre-states #

                                                As possible worlds for the model graph we want to define maximal paths inside the build tree that do not contain (M), (L+) or (L-) steps.

                                                We collect the sequents along such paths directly by induction on the BuildTree.

                                                @[irreducible]

                                                Collect pre-states in the whole BuildTree. The local pre-states come from paths in a local tableau, and PDL pre-states each consist of just a single node.

                                                Equations
                                                Instances For

                                                  The following four simp lemmas describe membership in BuildTree.collect. They are the Finset replacements of the old list-based unfolding of collect.

                                                  @[simp]
                                                  theorem BuildTree.mem_collect_loc {H : History} {X : Sequent} {nbas : ¬X.basic} {someLT : UniOpenLT.all X []} {next : (lt : UniOpenLT X) → BuildChoice H X (endNodesOf lt)} {p : List Sequent} :
                                                  p (loc nbas someLT next).collect ltUniOpenLT.all X, p (↑lt).pathsTo (next lt).4 p (next lt).6.collect
                                                  @[simp]
                                                  theorem BuildTree.mem_collect_pdl {H : List Sequent} {X : Sequent} {bas : X.basic} {someR : PdlRule.all X []} {next : (Y : Sequent) → PdlRule X YBuildTree (X :: H) Y} {p : List Sequent} :
                                                  p (pdl bas someR next).collect p = [X] ∃ (Y : Sequent) (r : PdlRule X Y), Y, r PdlRule.all X p (next Y r).collect
                                                  @[simp]
                                                  theorem BuildTree.mem_collect_openLeaf {H : History} {X : Sequent} {bas : X.basic} {noRule : PdlRule.all X = []} {p : List Sequent} :
                                                  p (openLeaf bas noRule).collect p = [X]
                                                  theorem BuildTree.collect_contains_root_of_not_freeRepeat {H : History} {X : Sequent} (bt : BuildTree H X) (h : ¬bt.isFreeRepeat) :
                                                  πbt.collect, X π

                                                  Any BuildTree that is not a free repeat collects at least one list containing its root. Generalisation of BuildTree.collect_contains_root to non-empty histories.

                                                  theorem BuildTree.collect_contains_root {X : Sequent} (bt : BuildTree [] X) :
                                                  πbt.collect, X π

                                                  Pre-states (Def 6.13) #

                                                  def PreState {H : History} {X : Sequent} (bt : BuildTree H X) :

                                                  A pre-state is a list of sequents collected from a BuildTree.

                                                  Equations
                                                  Instances For
                                                    theorem PreState.nonempty {H : History} {X : Sequent} {bt : BuildTree H X} {π : PreState bt} :
                                                    π []

                                                    Collecting Formulas in Pre-state Sequents #

                                                    Λ(π) gets all formulas for a pre-state but keep the information what is loaded. Returns the WhateverFormula type so that lemmas like 6.15 and 6.18 are sayable.

                                                    Equations
                                                    Instances For
                                                      def PreState.forms {H : History} {X : Sequent} {bt : BuildTree H X} (π : PreState bt) :

                                                      Λ⁻(π) gets all formulas from a pre-state π, via unloading if needed.

                                                      Equations
                                                      Instances For
                                                        @[simp]
                                                        theorem PreState.mem_wForms {H : History} {X : Sequent} {bt : BuildTree H X} {π : PreState bt} {f : WhateverFormula} :
                                                        f π.wForms Zπ, f Z.wForms
                                                        @[simp]
                                                        theorem PreState.mem_forms {H : History} {X : Sequent} {bt : BuildTree H X} {π : PreState bt} {f : Formula} :
                                                        f π.forms Zπ, f Z.toFinset

                                                        Characterizing three different ways in which a formula can be in PreState.forms.

                                                        theorem PreState.forms_saturated {H : History} {X : Sequent} {bt : BuildTree H X} {π : PreState bt} :
                                                        theorem PreState.forms_last_basic {H : History} {X : Sequent} {bt : BuildTree H X} {π : PreState bt} :
                                                        ((↑π).getLast ).basic

                                                        PreStates to Matches and back again #

                                                        To prove the existence lemmas we first make some helper definitions and lemmas that allow us to switch between PreStates & Matches.

                                                        theorem Match.collect_btAt_subset {H : History} {X : Sequent} {bt : BuildTree H X} (m : Match bt) (π : List Sequent) :
                                                        π m.btAt.snd.snd.collectπ bt.collect

                                                        The result of BuildTree.collect in any sub-BuildTree reached by a Match is also part of BuildTree.collect applied to the bigger BuildTree.

                                                        theorem Match.existsPreState {X : Sequent} {bt : BuildTree [] X} (m : Match bt) :
                                                        ∃ (π : PreState bt), Zπ, m.btAt.snd.fst = Z

                                                        For any Match there exists a PreState containing the sequent at the end of the Match.

                                                        def Match.fitsPreState {X : Sequent} {bt : BuildTree [] X} (m : Match bt) (π : List Sequent) :

                                                        The Boolean predicate used by Match.toPreState: does the given list of sequents contain the sequent at the end of the given Match?

                                                        Equations
                                                        Instances For
                                                          theorem Match.fitsPreState_iff {X : Sequent} {bt : BuildTree [] X} {m : Match bt} {π : List Sequent} :
                                                          m.fitsPreState π = true Zπ, m.btAt.snd.fst = Z
                                                          theorem Match.exists_fitsPreState {X : Sequent} {bt : BuildTree [] X} (m : Match bt) :
                                                          πbt.collect.toList, m.fitsPreState π = true

                                                          Reformulation of Match.existsPreState using Match.fitsPreState. Note that we use Finset.toList here to be able to use List.find? below.

                                                          Thanks to Match.existsPreState the search for a fitting pre-state succeeds.

                                                          noncomputable def Match.toPreState {X : Sequent} {bt : BuildTree [] X} (m : Match bt) :

                                                          Pick a PreState for a given Match, using Match.existsPreState and List.find?. Noncomputable because Finset.toList is.

                                                          Equations
                                                          Instances For
                                                            theorem Match.toPreState_spec {X : Sequent} {bt : BuildTree [] X} (m : Match bt) :
                                                            Zm.toPreState, m.btAt.snd.fst = Z

                                                            The result of Match.toPreState indeed contains the sequent at the end of the Match.

                                                            @[irreducible]
                                                            def BuildTree.toMatchAux {H : History} {X : Sequent} (bt : BuildTree H X) (p : List Sequent) :

                                                            Search for the node in bt at which the list p of sequents is collected, and return the Match leading to that node. Auxiliary function for PreState.toMatch, defined for all lists p of sequents. If p is not collected anywhere, then we return Match.nil as a dummy value.

                                                            Equations
                                                            Instances For

                                                              A collected list of sequents is still collected in the sub-BuildTree found for it.

                                                              theorem BuildTree.toMatchAux_head? {H : History} {X : Sequent} (bt : BuildTree H X) (p : List Sequent) :

                                                              A collected list of sequents starts with the sequent of the node where it is collected.

                                                              def PreState.toMatch {H : History} {X : Sequent} {bt : BuildTree H X} (π : PreState bt) :

                                                              Every pre-state comes from a Match: this is the Match that leads to the node of the BuildTree at which the pre-state π was collected. (Defined for any history, not only for H = [].)

                                                              Equations
                                                              Instances For
                                                                theorem PreState.toMatch_mem_collect {H : History} {X : Sequent} {bt : BuildTree H X} (π : PreState bt) :

                                                                Specification of PreState.toMatch, part one: the pre-state is collected already in the sub-BuildTree reached by the match.

                                                                theorem PreState.toMatch_head {H : History} {X : Sequent} {bt : BuildTree H X} (π : PreState bt) :
                                                                (↑π).head = π.toMatch.btAt.snd.fst

                                                                Specification of PreState.toMatch, part two: the pre-state starts with the sequent at the node reached by the match.

                                                                theorem PreState.toMatch_endSeq {H : History} {X : Sequent} {bt : BuildTree H X} (π : PreState bt) :
                                                                π.toMatch.endSeq = (↑π).head

                                                                Specification of PreState.toMatch, part three: the sequent at the node reached by the match is the head of the pre-state. (Reformulation of PreState.toMatch_head.)

                                                                theorem PreState.exists_match_endSeq_eq_last {H : History} {X : Sequent} {bt : BuildTree H X} (π : PreState bt) :
                                                                ∃ (m : Match bt), m.endSeq = (↑π).getLast

                                                                Not only the first sequent of a pre-state is reached by a Match (this is PreState.toMatch), also the last sequent of a pre-state is reached by some Match.

                                                                theorem PreState.exists_endMatch {H : History} {X : Sequent} {bt : BuildTree H X} (π : PreState bt) :
                                                                ∃ (c : Match π.toMatch.btAt.snd.snd), π.toMatch.endSeq = (↑π).head (π.toMatch.append c).endSeq = (↑π).getLast

                                                                Both ends of a pre-state are reached by matches, and the match reaching the last sequent extends the one reaching the first sequent: the continuation c is a Match inside the sub-BuildTree at which π was collected, and appending it to π.toMatch gives a Match in the whole tree that ends at the last sequent of π.

                                                                theorem PreState.setEqTo_mem_toMatch_toPreState {X : Sequent} {bt : BuildTree [] X} (π : PreState bt) :
                                                                Zπ.toMatch.toPreState, (↑π).head = Z

                                                                Weak round-trip that always holds: going from a pre-state to a match and back gives a pre-state that contains the first sequent of π.

                                                                theorem PreState.toMatch_toPreState {X : Sequent} {bt : BuildTree [] X} (π : PreState bt) (uniq : ρbt.collect, (∃ Zρ, (↑π).head = Z)ρ = π) :

                                                                Round-trip: under the assumption that π is the only collected list that contains a sequent set-equal to the sequent at the node where π is collected, going to the match and back gives π again.

                                                                theorem PreState.toMatch_toPreState_openLeaf {X : Sequent} (bas : X.basic) (noRule : PdlRule.all X = []) (π : PreState (BuildTree.openLeaf bas noRule)) :

                                                                Example where the uniq assumption of PreState.toMatch_toPreState is satisfied: an open leaf collects only one pre-state, so there the round-trip does hold.

                                                                Properties of Formula (Sets? Lists?) obtained from Pre-States #

                                                                theorem PreState.mem_bothSides_getLast_of_basic {H : History} {X : Sequent} {bt : BuildTree H X} {π : PreState bt} {φ : Formula} (φ_basic : φ.basic = true) (φ_in : φ π.forms) :
                                                                φ ((↑π).getLast ).toFinset

                                                                Every basic formula of a pre-state already occurs in the last (basic) sequent of that pre-state. Note that bothSides is used here, so this also covers formulas from the loaded part of a sequent.

                                                                theorem PreState.formsCases {a✝ : History} {a✝¹ : Sequent} {bt : BuildTree a✝ a✝¹} {φ : Formula} {π : PreState bt} (φ_in : φ π.forms) :
                                                                φ.basic = true φ ((↑π).getLast ).toFinset ¬φ.basic = true

                                                                Lemma 6.14, weakened version. The original statement says that φ is principal in a rule applied later on. We do not have the rule applications available along a pre-state, so instead we make the case distinction on whether φ is basic, and give the actual content for the first case: any basic formula of a pre-state occurs already in its last sequent. (We also use Sequent.toFinset instead of to include the loaded formula.)

                                                                Lemma 6.15 free case. #

                                                                The helper lemmas needed for it are in Pdl/Sequent.lean, Pdl/LocalRules.lean and Pdl/LocalTableauPaths.lean.

                                                                theorem PreState.freeUnfoldDiaMem_of_nonAtom {H : History} {X : Sequent} {bt : BuildTree H X} {π : PreState bt} {α : Program} {φ : Formula} :

                                                                Lemma 6.15 free case. (Generalised from bt : BuildTree [] X to an arbitrary history H, as needed for the recursion into sub-BuildTrees.)

                                                                Lemma 6.15 loaded cases. #

                                                                The helper lemmas needed for these are in Pdl/Sequent.lean, Pdl/LocalRules.lean and Pdl/LocalTableauPaths.lean.

                                                                theorem PreState.loadUnfoldMem_of_nonAtom {H : History} {X : Sequent} {bt : BuildTree H X} {π : PreState bt} {α : Program} {ξ : AnyFormula} :
                                                                ¬α.isAtomicWhateverFormula.negLoad (~'αξ) π.wForms∃ (ress : Finset (Finset Formula × Option NegLoadFormula)), Nonempty (LoadRule (~'αξ) ress) Foress, ((Fo.1.sort fun (a b : Formula) => a b).all fun (f : Formula) => decide ((WhateverFormula.any AnyFormula.normal) f π.wForms)) = true (Fo.2.toList.all fun (nl : NegLoadFormula) => decide (WhateverFormula.negLoad nl π.wForms)) = true

                                                                Generic version of the loaded case of Lemma 6.15: a non-atomic loaded diamond in a pre-state must have been unfolded by a LoadRule somewhere in the pre-state. The two versions below are the special cases for AnyFormula.loaded and AnyFormula.normal. (Generalised from bt : BuildTree [] X to an arbitrary history H, as needed for the recursion into sub-BuildTrees.)

                                                                Lemma 6.15 loaded case with more than one loaded box

                                                                Lemma 6.15 loaded case with only one loaded box.

                                                                Lemma 6.16: pre-states are saturated and locally consistent, their last node is basic.