Documentation

Foundation.FirstOrder.Syntax.Classical.PrimrecCoding

Primcodable for Semiterm and Semiformula (issue #506) #

Semiterm.ofNat/Semiformula.ofNat are strong recursions on the code: the func branch reads an argument vector out of the code and calls itself on each entry. Their Primrec versions go through Primrec.nat_strong_rec (terms) and Primrec.nat_omega_rec' (formulas), whose step functions this file builds. Two design points:

Everything happens at the level of codes. The table entry for a sub-code j is encode (ofNat n j), and encode on Option is 0 for none and encode t + 1 for some t, so a successful entry already contains the argument's toNat, one less than itself, and the argument-vector code is rebuilt by arithmetic alone. No term is ever reconstructed, and nothing here needs L or ξ decidable.

Option is kept out of the folds. stepVec returns ℕ with 0 for failure and r + 1 for success with value r: with Option ℕ as the fold's accumulator, the Primrec combinators must synthesise Primcodable for towers of products ending in Option ℕ, and elaboration diverges. The ℕ encoding costs one - 1 per lookup and elaborates immediately.

Nat.natToList (Vorspiel/Nat/Matrix.lean) is used throughout for the same reason: Nat.natToVec produces a Fin k → ℕ whose length lives in its type, which no Primrec combinator can consume, so natToList is the same decoding without the dependency, and natToVec_eq_some_iff lets the correctness proofs move between them.

theorem Matrix.getM_option_succ {n : ℕ} {β : Type u_1} (f : Fin (n + 1) → Option β) :
getM f = (f 0).bind fun (a : β) => Option.map (fun (w : Fin n → β) (i : Fin (n + 1)) => Fin.cases a w i) (getM fun (j : Fin n) => f j.succ)

The equation lemma that keeps reasoning out of Option's applicative layer: at Option, getM's recursive step is a bind of a map.

theorem Matrix.getM_option_eq_some_iff {n : ℕ} {β : Type u_1} {f : Fin n → Option β} {v : Fin n → β} :
getM f = some v ↔ ∀ (i : Fin n), f i = some (v i)

getM at Option succeeds exactly when every component does. This is what turns the func branch of ofNat into a statement about each argument separately.

Primitive-recursiveness of a language's symbol encodings, uniformly in the arity.

This is strictly stronger than [(k : ℕ) → Primcodable (L.Func k)], necessarily so: Semiterm.ofNat reads the arity k out of the code being decoded and then calls decode : ℕ → Option (L.Func k) at that k, so simulating it needs (k, e) ↦ encode (decode e : Option (L.Func k)) to be primitive recursive as a function of the pair. A per-arity family gives primitive recursiveness only at each fixed k, with no uniformity across k.

Instances
    @[instance_reducible]
    Equations
    @[instance_reducible]
    Equations

    ℒₒᵣ satisfies the uniform condition: encode ∘ decode is fun k e ↦ if (k = 0 ∨ k = 2) ∧ e < 2 then e + 1 else 0.

    The argument-vector fold of the func branch. T is the table of values already computed by the strong recursion, l the list of argument codes.

    The result is 0 for failure and r + 1 for success with vector code r; see the module docstring for why this is ℕ and not Option ℕ. A lookup that is absent or 0 — that is, an argument whose own decoding failed — fails the whole fold.

    Equations
    Instances For
      theorem FFL.FirstOrder.Semiterm.stepVec_cons (T : List ℕ) (j : ℕ) (l : List ℕ) :
      stepVec T (j :: l) = if T.getD j 0 = 0 ∨ stepVec T l = 0 then 0 else Nat.pair (T.getD j 0 - 1) (stepVec T l - 1) + 2
      theorem FFL.FirstOrder.Semiterm.stepVec_ofFn {T : List ℕ} {a : ℕ} {w u : Fin a → ℕ} (h : ∀ (i : Fin a), T.getD (w i) 0 = u i + 1) :

      With every lookup present and non-zero, the fold rebuilds the vecToNat code of the arguments.

      theorem FFL.FirstOrder.Semiterm.stepVec_eq_zero {T l : List ℕ} {j : ℕ} (hj : j ∈ l) (h : T.getD j 0 = 0) :
      stepVec T l = 0

      One absent-or-zero lookup anywhere kills the fold.

      def FFL.FirstOrder.Semiterm.stepBody {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] (n : ℕ) (T : List ℕ) (d : ℕ) :

      The four branches of Semiterm.ofNat at the level of codes, with d + 1 the code being decoded. Branch 2 is the func case: it checks that the argument vector decodes to the recorded arity, that the function symbol decodes, and that every argument's own decoding succeeded — the last through stepVec, reading the table.

      Equations
      • One or more equations did not get rendered due to their size.
      Instances For
        def FFL.FirstOrder.Semiterm.step {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] (n : ℕ) (T : List ℕ) :

        One step of the course-of-values recursion, at the level of codes: given the table T of the values encode (ofNat n j) for every j < T.length, it returns encode (ofNat n T.length).

        Equations
        Instances For
          @[reducible, inline]
          abbrev FFL.FirstOrder.Semiterm.table {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] (n e : ℕ) :
          Equations
          Instances For
            @[simp]
            theorem FFL.FirstOrder.Semiterm.table_length {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] (n e : ℕ) :
            (table n e).length = e
            theorem FFL.FirstOrder.Semiterm.table_getD {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] {n e j : ℕ} (h : j < e) :
            theorem FFL.FirstOrder.Semiterm.step_table {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] (n e : ℕ) :

            On the table of all previously computed values, the step function returns the next one — the obligation Primrec.nat_strong_rec asks for.

            Four branches, matching ofNat's; the two atomic ones are arithmetic. In the func branch, natToVec succeeding is tested as natToList having the recorded length (natToVec_isSome_of_length/natToVec_eq_none_of_length), the function symbol's decoding is read off encode ∘ decode being non-zero, and each argument's decoding is read out of the table, so the branch never reconstructs a term; the argument-vector code comes from stepVec_ofFn, and when an argument fails to decode, its table entry is 0 and stepVec_eq_zero collapses the fold, matching Matrix.getM returning none on the other side.

            fun n e ↦ encode (Semiterm.ofNat n e) is primitive recursive: the term half of issue #506.

            @[instance_reducible]

            The instance sits on the Encodable instance already in Basic/Coding.lean, so nothing downstream sees a new encode; the examples below check that, and that decode is still ofNat.

            Equations

            The rel/nrel argument fold, the term-decoding sibling of Semiterm.stepVec. Where the term version reads each argument out of the strong-recursion table, this decodes each argument code as a term of Semiterm L ξ n — the arguments of a relation are terms, not sub-formulas, so they are in no table. 0 is failure and r + 1 success, as there.

            Equations
            • One or more equations did not get rendered due to their size.
            Instances For
              @[simp]
              theorem FFL.FirstOrder.Semiformula.stepVecT_ofFn {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] {n a : ℕ} {w u : Fin a → ℕ} (h : ∀ (i : Fin a), Encodable.encode (Encodable.decode (w i)) = u i + 1) :
              theorem FFL.FirstOrder.Semiformula.stepVecT_eq_zero {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] {n : ℕ} {l : List ℕ} {j : ℕ} (hj : j ∈ l) (h : Encodable.encode (Encodable.decode j) = 0) :
              stepVecT n l = 0

              The formula step function #

              Semiformula.ofNat cannot use Primrec.nat_strong_rec: the ∀/∃ branches recurse at ofNat (n + 1), so the arity changes as the recursion descends, while nat_strong_rec holds its parameter fixed and tabulates one arity only. The recursion is therefore run by Primrec.nat_omega_rec' over pairs (n, e), with e as the measure and subArgs naming the immediate sub-formulas — two at the same arity for ⋏/⋎, one at the successor arity for ∀/∃, and none elsewhere. The measure obligation subArgs_ord is ofNat's own internal < e + 1 bounds read back.

              step is written with an if rather than a Nat.casesOn: a casesOn adds a level to the tower of products the ambient argument lives in (((ℕ × ℕ) × List ℕ) × ℕ instead of (ℕ × ℕ) × List ℕ), and elaboration of the Primrec combinators diverges on it, exactly as it did on an Option-valued fold in the term half.

              def FFL.FirstOrder.Semiformula.stepBody {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] (n d : ℕ) (vs : List ℕ) :
              Equations
              • One or more equations did not get rendered due to their size.
              Instances For
                Equations
                • One or more equations did not get rendered due to their size.
                Instances For
                  theorem FFL.FirstOrder.Semiformula.subArgs_ord (b b' : ℕ × ℕ) :
                  b' ∈ subArgs b → b'.2 < b.2
                  theorem FFL.FirstOrder.Semiformula.step_correct {L : Language} [L.Encodable] {ξ : Type u_1} [Primcodable ξ] (n e : ℕ) :
                  step n e (List.map (fun (b : ℕ × ℕ) => Encodable.encode (ofNat b.1 b.2)) (subArgs (n, e))) = Encodable.encode (ofNat n e)
                  theorem FFL.FirstOrder.Semiformula.primrec_step {L : Language} [L.Encodable] [L.Primcodable] {ξ : Type u_1} [Primcodable ξ] :
                  Primrec fun (q : (ℕ × ℕ) × List ℕ) => step q.1.1 q.1.2 q.2

                  fun n e ↦ encode (Semiformula.ofNat n e) is primitive recursive. With Semiterm.encode_ofNat_primrec this closes issue #506: Primcodable instances for both Semiterm L ξ n and Semiformula L ξ n, for every language satisfying the uniform-in-arity condition Language.Primcodable.

                  @[instance_reducible]

                  As with the term instance, this sits on Semiformula.encodable rather than introducing a second Encodable; the examples below check that, and that encode and decode are still toNat and ofNat definitionally.

                  Equations
                  theorem FFL.FirstOrder.Semiformula.primrec₂_and {L : Language} [L.Encodable] [L.Primcodable] {ξ : Type u_1} [Primcodable ξ] {n : ℕ} :
                  Primrec₂ fun (φ ψ : Semiformula L ξ n) => φ ⋏ ψ
                  theorem FFL.FirstOrder.Semiformula.primrec₂_or {L : Language} [L.Encodable] [L.Primcodable] {ξ : Type u_1} [Primcodable ξ] {n : ℕ} :
                  Primrec₂ fun (φ ψ : Semiformula L ξ n) => φ ⋎ ψ