Documentation

DescriptiveComplexity.HeadEval

Evaluating a first-order formula with heads #

The half of the capture theorem that has content: a two-way multi-head automaton can decide any fixed first-order formula of its head positions, spending two extra heads per quantifier.

The guards of a DescriptiveComplexity.HeadProgram are quantifier-free by fiat – a control that could read a quantified fact would be first-order logic in disguise. This file removes the apparent limitation: quantifiers are not read, they are walked. To decide ∀ x, φ(x) the program parks a head at the greatest element, sweeps another head from the least element upwards, and evaluates φ at each stop; it answers false at the first stop where φ fails, and true when the sweep reaches the parked head. Existential quantification is ∀ with → ⊥ around it, which the imp case already handles, so a single sweep serves both.

The two combinators #

Both are DescriptiveComplexity.HeadProgram.wireP over a four- or three-node control graph, and both are proved by DescriptiveComplexity.HeadProgram.runs_wireP: the sweep's proof is an induction downwards along the order (DescriptiveComplexity.order_induction_down), which is the direction that knows about the elements a walker has not yet reached.

The evaluator #

DescriptiveComplexity.HeadProgram.evalP is defined by structural recursion on a BoundedFormula, with a fixed layout of the heads:

The recursion never inspects a state: each case is a combinator, and its correctness is the corresponding combinator lemma applied to the inductive hypothesis. The result, DescriptiveComplexity.HeadProgram.decides_evalP, is a DescriptiveComplexity.HeadProgram.Decides, and DescriptiveComplexity.HeadProgram.deterministic_evalP records that the evaluator is deterministic – a sweep is not a guess – which is what lets the same evaluator serve the deterministic capture.

Moving a single head #

The moves that send head h along mv and leave every other head where it is.

Equations
Instances For
    Dependency graph
    @[simp]
    theorem DescriptiveComplexity.HeadProgram.setHead_self {K : ℕ} (h : Fin K) (mv : HeadMove K) :
    setHead h mv h = mv
    Dependency graph
    @[simp]
    theorem DescriptiveComplexity.HeadProgram.setHead_of_ne {K : ℕ} {h j : Fin K} (mv : HeadMove K) (hj : j ≠ h) :
    Dependency graph

    Branching #

    The control nodes of a branch: the test, and its two continuations.

    • test : IteNode

      Running the test.

    • thenB : IteNode

      Running the continuation taken when the test says true.

    • elseB : IteNode

      Running the continuation taken when the test says false.

    Instances For
      Dependency graph
      Dependency graph
      Dependency graph
      Dependency graph
      Dependency graph

      Branching: run F; continue with G if it says true and with H if it says false, and answer what the continuation answers.

      Equations
      Instances For
        Dependency graph

        Sweeping a head along the order #

        The control nodes of a sweep.

        • reset : ScanNode

          Putting the walking head at the least element and the marker at the greatest.

        • body : ScanNode

          Evaluating the body at the current value of the walking head.

        • atMax : ScanNode

          Asking whether the walking head has reached the marker.

        • bump : ScanNode

          Stepping the walking head to the next element.

        Instances For
          Dependency graph
          Dependency graph
          Dependency graph
          Dependency graph

          The moves that start a sweep: the walking head to the least element, the marker to the greatest.

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

            The sweep: run F at every value of head h in turn, from the least element up to the marker head hm; answer false at the first value where F does, and true if it never does.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For
              Dependency graph
              theorem DescriptiveComplexity.HeadProgram.Decides.congr {L : FirstOrder.Language} {K : ℕ} {A : Type} [L.Structure A] [LinearOrder A] {F : HeadProgram L K} {m : ℕ} {P P' : (Fin K → A) → Prop} (h : F.Decides A m P) (hPP : ∀ (x : Fin K → A), P x ↔ P' x) :
              F.Decides A m P'

              Restating what a fragment decides.

              Dependency graph
              theorem DescriptiveComplexity.HeadProgram.decides_scanP {L : FirstOrder.Language} {K : ℕ} {A : Type} [L.Structure A] [LinearOrder A] [Finite A] {d : ℕ} {h hm : Fin K} (hh : ↑h = d) (hhm : ↑hm = d + 1) {F : HeadProgram L K} {P : (Fin K → A) → Prop} (hF : F.Decides A (d + 2) P) (hP : HeadLocal (d + 1) P) :
              (scanP h hm F).Decides A d fun (x : Fin K → A) => ∀ (a : A), P (Function.update x h a)

              What a sweep decides: the body, at every value of the walking head.

              Dependency graph

              Atoms as guards #

              An atomic formula, read as a guard on the heads: its variables are sent to the heads that hold them.

              Equations
              Instances For
                Dependency graph

                A guard read off a quantifier-free formula is quantifier-free, as a guard must be.

                Dependency graph

                The evaluator #

                Dependency graph
                def DescriptiveComplexity.HeadProgram.evalP {L : FirstOrder.Language} {K : ℕ} {α : Type} (sh : ℕ → Fin K) {n : ℕ} :
                ℕ → (α ⊕ Fin n → Fin K) → (L.sum FirstOrder.Language.order).BoundedFormula α n → HeadProgram L K

                The evaluator: a program deciding a fixed first-order formula of the heads. Atoms are guards, implication is a branch, and a quantifier is a sweep of two fresh heads – sh d walking, sh (d + 1) marking the greatest element.

                Equations
                Instances For
                  Dependency graph
                  theorem DescriptiveComplexity.HeadProgram.decides_evalP {L : FirstOrder.Language} {K : ℕ} {α A : Type} [L.Structure A] [LinearOrder A] [Finite A] (sh : ℕ → Fin K) (hsh : ∀ i < K, ↑(sh i) = i) {n : ℕ} (ψ : (L.sum FirstOrder.Language.order).BoundedFormula α n) (d : ℕ) (hv : α ⊕ Fin n → Fin K) :
                  (∀ (v : α ⊕ Fin n), ↑(hv v) < d) → d + qdepth ψ ≤ K → (evalP sh d hv ψ).Decides A d fun (x : Fin K → A) => ψ.Realize (fun (a : α) => x (hv (Sum.inl a))) fun (i : Fin n) => x (hv (Sum.inr i))

                  The evaluator is correct: it decides the formula, reading its variables off the heads they live in, and gives those heads back untouched.

                  Dependency graph
                  theorem DescriptiveComplexity.HeadProgram.deterministic_evalP {L : FirstOrder.Language} {K : ℕ} {α A : Type} [L.Structure A] [LinearOrder A] (sh : ℕ → Fin K) {n : ℕ} (ψ : (L.sum FirstOrder.Language.order).BoundedFormula α n) (d : ℕ) (hv : α ⊕ Fin n → Fin K) :
                  (evalP sh d hv ψ).Deterministic A

                  The evaluator is deterministic: it sweeps, it does not guess. This is what lets the same programs serve the deterministic capture.

                  Dependency graph