Research source · Plain text

Basic.lean

formalizations/frontier-derivative/FrontierDerivative/Basic.lean

185 lines. Source is displayed for inspection; it is not executed by this page.

File fingerprint

SHA-256: c3751e225468bdde382a36f9d2b952d2a791a33abfabb6cabb7a47ff9e776d22

import Std

/-!
# Frontier derivatives for a normalized real-time pushdown machine

This file isolates the small theorem used by the FPRD forgotten-action note.
A transition consumes exactly one input symbol, inspects the leftmost (top)
stack symbol, and replaces it by a finite word.  The transition table is a
finite list, so `step`, `advance`, and `run` are executable.

The file does not formalize visibly pushdown automata, their determinization,
PEGs, scaffolding automata, or any theorem connecting those classes.
-/

namespace FrontierDerivative

universe uQ uInput uΓ

/-- A finite-control state paired with a stack whose top is the left end. -/
structure Config (Q : Type uQ) (Γ : Type uΓ) where
  state : Q
  stack : List Γ
  deriving DecidableEq, Repr

/-- One input-consuming, top-rewriting transition. -/
structure Transition (Q : Type uQ) (Input : Type uInput) (Γ : Type uΓ) where
  source : Q
  input : Input
  top : Γ
  target : Q
  replacement : List Γ
  deriving DecidableEq, Repr

/-- A normalized nondeterministic pushdown machine with final-state readout. -/
structure Machine (Q : Type uQ) (Input : Type uInput) (Γ : Type uΓ) where
  transitions : List (Transition Q Input Γ)
  initial : Config Q Γ
  final : Q → Bool

namespace Transition

variable {Q : Type uQ} {Input : Type uInput} {Γ : Type uΓ}

/-- Execute a transition when its source, input, and top symbol match. -/
def apply? [DecidableEq Q] [DecidableEq Input] [DecidableEq Γ]
    (t : Transition Q Input Γ) (a : Input) (c : Config Q Γ) : Option (Config Q Γ) :=
  match c.stack with
  | [] => none
  | X :: tail =>
      if t.source = c.state ∧ t.input = a ∧ t.top = X then
        some ⟨t.target, t.replacement ++ tail⟩
      else
        none

/-- The executable transition agrees with the normalized top-rewrite rule. -/
theorem apply?_eq_some_iff [DecidableEq Q] [DecidableEq Input] [DecidableEq Γ]
    (t : Transition Q Input Γ) (a : Input) (c c' : Config Q Γ) :
    t.apply? a c = some c' ↔
      ∃ tail,
        c.state = t.source ∧
        c.stack = t.top :: tail ∧
        a = t.input ∧
        c' = ⟨t.target, t.replacement ++ tail⟩ := by
  cases hstack : c.stack with
  | nil => simp [apply?, hstack]
  | cons X tail =>
      simp [apply?, hstack]
      constructor
      · rintro ⟨⟨hsource, hinput, htop⟩, hout⟩
        exact ⟨hsource.symm, tail, ⟨htop.symm, rfl⟩, hinput.symm, hout.symm⟩
      · rintro ⟨hsource, tail', ⟨htop, htail⟩, hinput, hout⟩
        subst tail'
        exact ⟨⟨hsource.symm, hinput.symm, htop.symm⟩, hout.symm⟩

end Transition

namespace Machine

variable {Q : Type uQ} {Input : Type uInput} {Γ : Type uΓ}
variable [DecidableEq Q] [DecidableEq Input] [DecidableEq Γ]

/-- All successors of one configuration on one arriving symbol. -/
def step (M : Machine Q Input Γ) (a : Input) (c : Config Q Γ) : List (Config Q Γ) :=
  M.transitions.filterMap fun t => t.apply? a c

/-- Apply one arriving symbol to every live configuration. -/
def advance (M : Machine Q Input Γ) (cs : List (Config Q Γ)) (a : Input) :
    List (Config Q Γ) :=
  cs.flatMap (M.step a)

/-- Execute a word from an arbitrary finite frontier. -/
def runFrom (M : Machine Q Input Γ) (cs : List (Config Q Γ)) (w : List Input) :
    List (Config Q Γ) :=
  w.foldl M.advance cs

/-- Execute a word from the singleton initial frontier. -/
def run (M : Machine Q Input Γ) (w : List Input) : List (Config Q Γ) :=
  M.runFrom [M.initial] w

/-- The semantic frontier component at control state `q` after prefix `u`. -/
def Frontier (M : Machine Q Input Γ) (u : List Input) (q : Q) (z : List Γ) : Prop :=
  (⟨q, z⟩ : Config Q Γ) ∈ M.run u

/-- Final-state acceptance from the finite frontier. -/
def Accepts (M : Machine Q Input Γ) (u : List Input) : Prop :=
  ∃ c ∈ M.run u, M.final c.state = true

theorem mem_step_iff (M : Machine Q Input Γ) (a : Input) (c c' : Config Q Γ) :
    c' ∈ M.step a c ↔
      ∃ t ∈ M.transitions, t.apply? a c = some c' := by
  simp [step]

theorem mem_advance_iff (M : Machine Q Input Γ) (cs : List (Config Q Γ))
    (a : Input) (c' : Config Q Γ) :
    c' ∈ M.advance cs a ↔
      ∃ c ∈ cs, ∃ t ∈ M.transitions, t.apply? a c = some c' := by
  simp [advance, mem_step_iff]

theorem runFrom_append (M : Machine Q Input Γ) (cs : List (Config Q Γ))
    (u v : List Input) :
    M.runFrom cs (u ++ v) = M.runFrom (M.runFrom cs u) v := by
  simp [runFrom, List.foldl_append]

theorem run_append_singleton (M : Machine Q Input Γ) (u : List Input) (a : Input) :
    M.run (u ++ [a]) = M.advance (M.run u) a := by
  simp [run, runFrom, List.foldl_append]

/--
The exact frontier derivative recurrence.  A stack word is in the frontier
after `u ++ [a]` exactly when a stack in the frontier after `u` has top `X`
and one listed transition replaces `X` by `γ`.  In language notation this is
`S_{ua}(q') = ⋃ γ (X⁻¹ S_u(q))`.
-/
theorem frontier_append_singleton_iff (M : Machine Q Input Γ) (u : List Input)
    (a : Input) (q' : Q) (z' : List Γ) :
    M.Frontier (u ++ [a]) q' z' ↔
      ∃ q X tail t,
        M.Frontier u q (X :: tail) ∧
        t ∈ M.transitions ∧
        t.source = q ∧
        t.input = a ∧
        t.top = X ∧
        q' = t.target ∧
        z' = t.replacement ++ tail := by
  rw [Frontier, run_append_singleton, mem_advance_iff]
  constructor
  · rintro ⟨⟨q, z⟩, hc, t, ht, happly⟩
    rw [Transition.apply?_eq_some_iff] at happly
    obtain ⟨tail, hstate, hstack, hinput, hout⟩ := happly
    change q = t.source at hstate
    change z = t.top :: tail at hstack
    subst z
    refine ⟨q, t.top, tail, t, hc, ht, ?_, ?_, rfl, ?_, ?_⟩
    · exact hstate.symm
    · exact hinput.symm
    · exact congrArg Config.state hout
    · exact congrArg Config.stack hout
  · rintro ⟨q, X, tail, t, hfrontier, ht, hsource, hinput, htop,
      htarget, hstack⟩
    refine ⟨⟨q, X :: tail⟩, hfrontier, t, ht, ?_⟩
    rw [Transition.apply?_eq_some_iff]
    refine ⟨tail, hsource.symm, ?_, hinput.symm, ?_⟩
    · simp [htop]
    · cases htarget
      cases hstack
      rfl

/-- Acceptance is exactly nonemptiness of some final frontier component. -/
theorem accepts_iff_final_frontier (M : Machine Q Input Γ) (u : List Input) :
    M.Accepts u ↔
      ∃ q z, M.Frontier u q z ∧ M.final q = true := by
  constructor
  · rintro ⟨c, hc, hfinal⟩
    exact ⟨c.state, c.stack, hc, hfinal⟩
  · rintro ⟨q, z, hfrontier, hfinal⟩
    exact ⟨⟨q, z⟩, hfrontier, hfinal⟩

/-- Empty-input behavior is the initial configuration, including its report. -/
theorem accepts_nil_iff (M : Machine Q Input Γ) :
    M.Accepts [] ↔ M.final M.initial.state = true := by
  simp [Accepts, run, runFrom]

end Machine

end FrontierDerivative