Documentation

DescriptiveComplexity.Machines

Turing machines over a universe, without a vocabulary #

The semantics half of the machine bridge: what it means for a nondeterministic Turing machine, presented as relations on a universe, to accept. No vocabulary appears here – DescriptiveComplexity.Problems.Machine.Defs supplies one and reads these definitions off a structure – so that the reductions, which build machines rather than read them, can reason about runs without unfolding any RelMap.

The model #

A machine is DescriptiveComplexity.TMData: the sorts (Posn, Tr), the marks (Start, Acc, Blank, Right), the binary attributes of a transition (Src, Read, Dst, Write), the initial tape Inp, and a linear order Le. Two decisions are visible in the types.

A run is read in three ways, and which one a construction uses is what its resource bound can see. DescriptiveComplexity.TMData.StepsIn counts exactly, so two phases of unknown length do not compose; Relation.ReflTransGen composes but carries no count, so DescriptiveComplexity.TMData.Accepts cannot read it; and DescriptiveComplexity.TMData.ReachesIn – a run of at most n steps – does both, composing by adding budgets and weakening upwards. A clocked program is written with the third and a space-bounded one with its erasure, which is how the two share their phase lemmas.

The tape is a total function A → A, so the semantics is total: reading a cell never fails. Which functions count as initial tapes is DescriptiveComplexity.TMData.InitTape – the input where it is defined, blank elsewhere – stated as a relation so that no choice is needed and so that the first-order kernel of the membership proof can check it literally.

Transport #

DescriptiveComplexity.TMData.Agree records that two machines over different universes correspond along an equivalence, fieldwise; DescriptiveComplexity.TMData.Agree.accepts transports acceptance along it. This is all the isomorphism-invariance proof of the decision problems needs.

A configuration: the current state, the position of the head, and the contents of the tape.

  • state : A

    The current state.

  • head : A

    The cell the head is on.

  • tape : AA

    The symbol in each cell.

Instances For
    Dependency graph
    theorem DescriptiveComplexity.Config.ext {A : Type} {x y : Config A} (state : x.state = y.state) (head : x.head = y.head) (tape : x.tape = y.tape) :
    x = y
    Dependency graph
    Dependency graph

    A Turing machine presented as relations on a universe: the sorts, the marks, the attributes of the transitions, the initial tape and the order along which the head moves.

    • Posn : AProp

      Being a position – a tape cell, and equally a time step.

    • Le : AAProp

      The order on positions, along which the head moves.

    • Tr : AProp

      Being a transition.

    • Start : AProp

      Being a start state.

    • Acc : AProp

      Being an accepting state.

    • Blank : AProp

      Being the blank symbol.

    • Right : AProp

      This transition moves the head right (rather than left).

    • Src : AAProp

      The state a transition applies in.

    • Read : AAProp

      The symbol a transition reads.

    • Dst : AAProp

      The state a transition moves to.

    • Write : AAProp

      The symbol a transition writes.

    • Inp : AAProp

      The input: the symbol initially in a cell.

    Instances For
      Dependency graph

      The symbols a cell may initially hold: the input symbol where the input is defined, the blank elsewhere.

      Equations
      Instances For
        Dependency graph

        Being an initial configuration: a start state, the head on the lowest position, and an initial tape.

        Equations
        Instances For
          Dependency graph

          One step. Some transition applies in the current state to the symbol under the head: it writes in that cell, changes state, and moves the head to the neighboring position in the direction it names. Cells other than the one under the head are unchanged, and a move off the end of the tape is impossible – there being no such neighbor, no step is available and the run stops.

          Equations
          • One or more equations did not get rendered due to their size.
          Instances For
            Dependency graph

            Reaching one configuration from another in exactly n steps.

            Equations
            Instances For
              Dependency graph

              Acceptance: some run from an initial configuration reaches an accepting state within as many steps as there are positions.

              The bound is unary by construction – it counts elements of the universe – which is what makes this an NP problem rather than an NEXP one, with no arithmetic anywhere. A reduction buys itself |Tag| · nᵈ steps by choosing the dimension d of its interpretation.

              The bound is strict because the positions index the time points of the run: N of them leave N - 1 steps between them, which is what makes DescriptiveComplexity.TMData.accepts_iff_exists_walk an equivalence and not merely an implication.

              Equations
              Instances For
                Dependency graph

                Acceptance in bounded space: some run from an initial configuration reaches an accepting state, with no bound on its length.

                This is DescriptiveComplexity.TMData.Accepts with the step bound dropped, and that is all the difference between the time-bounded and the space-bounded model: the space is already bounded by construction, since the tape is indexed by the positions and a reduction of dimension d therefore buys nᵈ cells exactly as it buys nᵈ steps. What changes is that a run no longer fits inside the structure – it may visit exponentially many configurations – so acceptance is reachability in the configuration graph rather than a walk indexed by the positions, which is why the membership proof for the space-bounded problems is an SO(TC) specification rather than a Σ₁ definition.

                Equations
                Instances For
                  Dependency graph
                  def DescriptiveComplexity.TMData.ReachesIn {A : Type} (M : TMData A) (n : ) (c c' : Config A) :

                  Reaching one configuration from another within a budget: some run of at most n steps.

                  This is the form in which a program's phases are stated when the machine is on a clock. DescriptiveComplexity.TMData.StepsIn counts exactly, so two phases whose lengths are not known separately do not compose; Relation.ReflTransGen composes but carries no count, and DescriptiveComplexity.TMData.Accepts cannot read it. A budget is what does both: it composes by adding (DescriptiveComplexity.TMData.ReachesIn.trans), it weakens upwards (DescriptiveComplexity.TMData.ReachesIn.mono), so a phase may be charged more than it spends and the arithmetic done once at the end, and it still gives the witness Accepts asks for. The space-bounded reading is its erasure (DescriptiveComplexity.TMData.ReachesIn.reflTransGen).

                  Equations
                  Instances For
                    Dependency graph
                    theorem DescriptiveComplexity.TMData.StepsIn.trans_step {A : Type} {M : TMData A} {n : } {c d e : Config A} :
                    M.StepsIn n c dM.Step d eM.StepsIn (n + 1) c e

                    A run extended by one more step at its end.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.stepsIn_succ_iff {A : Type} {M : TMData A} {n : } {c e : Config A} :
                    M.StepsIn (n + 1) c e ∃ (d : Config A), M.StepsIn n c d M.Step d e

                    A run decomposed at its far end: n + 1 steps are n steps and then one. DescriptiveComplexity.TMData.StepsIn recurses at the near end; inductions along the time order – the fixed-point description of a deterministic run – need this reading.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.stepsIn_split {A : Type} {M : TMData A} {m k : } {c e : Config A} :
                    M.StepsIn (m + k) c e∃ (d : Config A), M.StepsIn m c d M.StepsIn k d e

                    A run splits anywhere: m + k steps decompose into m and then k, through the configuration reached halfway. This is what lets two runs from the same configuration be compared at a common time.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.StepsIn.trans {A : Type} {M : TMData A} {n m : } {c d e : Config A} :
                    M.StepsIn n c dM.StepsIn m d eM.StepsIn (n + m) c e

                    Runs compose. Phases of a constructed machine are proved one at a time and chained with this; the step counts add, which is the form the budget obligation of a reduction takes.

                    Dependency graph

                    A budgeted run is a run: the space-bounded reading of acceptance forgets the count, so every lemma stated with DescriptiveComplexity.TMData.StepsIn – the sweep primitives of DescriptiveComplexity.Problems.Machine.Program, in particular – feeds straight into it.

                    Dependency graph

                    Every run has a length: the converse of DescriptiveComplexity.TMData.reflTransGen_of_stepsIn. A reachability claim carries no count, but a chain of steps has one, so a phase proved without a budget can still be handed to a caller that needs one – the count is whatever the chain happens to be.

                    Dependency graph

                    The algebra of budgets #

                    theorem DescriptiveComplexity.TMData.StepsIn.reachesIn {A : Type} {M : TMData A} {n : } {c d : Config A} (h : M.StepsIn n c d) :
                    M.ReachesIn n c d

                    A run of exactly n steps is a run within n.

                    Dependency graph

                    Every run has a budget, the same fact stated for DescriptiveComplexity.TMData.ReachesIn.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.ReachesIn.mono {A : Type} {M : TMData A} {n m : } {c d : Config A} (hnm : n m) (h : M.ReachesIn n c d) :
                    M.ReachesIn m c d

                    A budget may be raised, which is what makes phases of different lengths composable: each is charged a bound it need not meet.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.reachesIn_refl {A : Type} {M : TMData A} {n : } {c : Config A} :
                    M.ReachesIn n c c

                    Standing still costs nothing.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.reachesIn_of_step {A : Type} {M : TMData A} {c d : Config A} (h : M.Step c d) :
                    M.ReachesIn 1 c d

                    One step costs one.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.ReachesIn.trans {A : Type} {M : TMData A} {n m : } {c d e : Config A} (hcd : M.ReachesIn n c d) (hde : M.ReachesIn m d e) :
                    M.ReachesIn (n + m) c e

                    Budgeted runs compose, and their budgets add. This is the whole reason for the notion: a program's cost is read off its phases as a sum, and compared with the clock once.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.ReachesIn.tail {A : Type} {M : TMData A} {n : } {c d e : Config A} (h : M.ReachesIn n c d) (hstep : M.Step d e) :
                    M.ReachesIn (n + 1) c e

                    A budgeted run extended by one step at its end.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.ReachesIn.head {A : Type} {M : TMData A} {n : } {c d e : Config A} (hstep : M.Step c d) (h : M.ReachesIn n d e) :
                    M.ReachesIn (n + 1) c e

                    A budgeted run extended by one step at its start.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.reachesIn_five {A : Type} {M : TMData A} {n : } {c₀ c₁ c₂ c₃ c₄ c₅ : Config A} (h₁ : M.Step c₀ c₁) (h₂ : M.ReachesIn n c₁ c₂) (h₃ : M.Step c₂ c₃) (h₄ : M.ReachesIn n c₃ c₄) (h₅ : M.Step c₄ c₅) :
                    M.ReachesIn (2 * n + 3) c₀ c₅

                    Five legs, added up: two runs of the same length with a step before, between and after them. This is the shape a program's opening has – enter, a sweep, turn round, a sweep, enter the next phase – and the addition is all there is to its cost.

                    Dependency graph

                    A budgeted run is a run. The erasure that turns every statement of a clocked program into the corresponding statement of a space-bounded one, which is why the two models share their phase lemmas.

                    Dependency graph
                    theorem DescriptiveComplexity.TMData.accepts_of_reachesIn {A : Type} {M : TMData A} {n : } {c₀ c : Config A} (hinit : M.IsInit c₀) (hrun : M.ReachesIn n c₀ c) (hlt : n < Nat.card { p : A // M.Posn p }) (hacc : M.Acc c.state) :

                    A budgeted run that ends accepting makes the machine accept, provided the budget is below the clock. The one place a program's arithmetic meets DescriptiveComplexity.TMData.Accepts.

                    Dependency graph

                    Well-formedness, folded into the yes-instances in the style of DescriptiveComplexity.IsLinOrd for Knapsack: the order is linear, there is a position to start on, the input is functional, and there is exactly one blank symbol. Every conjunct is first-order, so the Σ₁ kernel can check it.

                    Equations
                    • One or more equations did not get rendered due to their size.
                    Instances For
                      Dependency graph

                      Determinism, folded into the yes-instances of DescriptiveComplexity.DTMAccept: one start state, at most one transition applicable in a given state on a given symbol, and at most one destination and written symbol per transition. Together with well-formedness this leaves at most one step from any configuration (DescriptiveComplexity.TMData.step_functional) and at most one initial configuration (DescriptiveComplexity.TMData.isInit_unique), so the run is unique – the shape a least fixed point can compute. Every conjunct is first-order.

                      Equations
                      • One or more equations did not get rendered due to their size.
                      Instances For
                        Dependency graph
                        theorem DescriptiveComplexity.TMData.initTape_functional {A : Type} {M : TMData A} (hwf : M.WellFormed) {p a b : A} (ha : M.InitTape p a) (hb : M.InitTape p b) :
                        a = b

                        A well-formed initial tape is functional: a cell holds either its unique input symbol or the unique blank, and the two cases exclude each other.

                        Dependency graph
                        theorem DescriptiveComplexity.TMData.isInit_unique {A : Type} {M : TMData A} (hwf : M.WellFormed) (hstart : ∀ (q q' : A), M.Start qM.Start q'q = q') {c c' : Config A} (h : M.IsInit c) (h' : M.IsInit c') :
                        c = c'

                        A machine with one start state has at most one initial configuration: the start state and the lowest position are pinned, and well-formedness makes the initial tape functional.

                        Dependency graph
                        theorem DescriptiveComplexity.TMData.exists_isInit {A : Type} {M : TMData A} [Finite A] (hwf : M.WellFormed) {q₀ : A} (hq : M.Start q₀) :
                        ∃ (c₀ : Config A), M.IsInit c₀ c₀.state = q₀

                        A well-formed machine with a start state has an initial configuration: put the head on the lowest position and read the tape off the input, filling the unwritten cells with the blank.

                        Dependency graph

                        Transport along an equivalence of universes #

                        structure DescriptiveComplexity.TMData.Agree {A B : Type} (u : B A) (N : TMData B) (M : TMData A) :

                        Two machines over different universes agree along an equivalence when every relation of one is the pullback of the other's.

                        • posn (b : B) : N.Posn b M.Posn (u b)

                          The positions correspond.

                        • le (b b' : B) : N.Le b b' M.Le (u b) (u b')

                          The orders correspond.

                        • tr (b : B) : N.Tr b M.Tr (u b)

                          The transitions correspond.

                        • start (b : B) : N.Start b M.Start (u b)

                          The start states correspond.

                        • acc (b : B) : N.Acc b M.Acc (u b)

                          The accepting states correspond.

                        • blank (b : B) : N.Blank b M.Blank (u b)

                          The blanks correspond.

                        • right (b : B) : N.Right b M.Right (u b)

                          The directions correspond.

                        • src (b b' : B) : N.Src b b' M.Src (u b) (u b')

                          The sources correspond.

                        • read (b b' : B) : N.Read b b' M.Read (u b) (u b')

                          The read symbols correspond.

                        • dst (b b' : B) : N.Dst b b' M.Dst (u b) (u b')

                          The destinations correspond.

                        • write (b b' : B) : N.Write b b' M.Write (u b) (u b')

                          The written symbols correspond.

                        • inp (b b' : B) : N.Inp b b' M.Inp (u b) (u b')

                          The inputs correspond.

                        Instances For
                          Dependency graph
                          def DescriptiveComplexity.Config.map {A B : Type} (u : B A) (c : Config B) :

                          Transport of a configuration along an equivalence.

                          Equations
                          Instances For
                            Dependency graph
                            @[simp]
                            theorem DescriptiveComplexity.Config.map_state {A B : Type} (u : B A) (c : Config B) :
                            (map u c).state = u c.state
                            Dependency graph
                            @[simp]
                            theorem DescriptiveComplexity.Config.map_head {A B : Type} (u : B A) (c : Config B) :
                            (map u c).head = u c.head
                            Dependency graph
                            @[simp]
                            theorem DescriptiveComplexity.Config.map_tape {A B : Type} (u : B A) (c : Config B) (p : A) :
                            (map u c).tape p = u (c.tape (u.symm p))
                            Dependency graph

                            Every configuration over A is the transport of one over B.

                            Dependency graph
                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.minPos {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) {b : B} :
                            MinPos N.Le N.Posn b MinPos M.Le M.Posn (u b)
                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.succPos {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) {b b' : B} :
                            SuccPos N.Le N.Posn b b' SuccPos M.Le M.Posn (u b) (u b')
                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.initTape {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) {b b' : B} :
                            N.InitTape b b' M.InitTape (u b) (u b')
                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.isInit {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) {c : Config B} :
                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.step {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) {c c' : Config B} :
                            N.Step c c' M.Step (Config.map u c) (Config.map u c')
                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.stepsIn {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) (n : ) (c c' : Config B) :
                            N.StepsIn n c c' M.StepsIn n (Config.map u c) (Config.map u c')
                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.accepts {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) :

                            Acceptance transports along an equivalence.

                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.reach {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) (c c' : Config B) :

                            Reachability in the configuration graph transports along an equivalence.

                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.acceptsSpace {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) :

                            Acceptance in bounded space transports along an equivalence.

                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.deterministic {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) :

                            Determinism transports along an equivalence.

                            Dependency graph
                            theorem DescriptiveComplexity.TMData.Agree.wellFormed {A : Type} {M : TMData A} {B : Type} {u : B A} {N : TMData B} (h : Agree u N M) :

                            Well-formedness transports along an equivalence.

                            Dependency graph