Lean migration: Phase 5 (language, CFGs, traces, Program)
- Spa.Language.Base: Expr/BasicStmt/Stmt + HasVar relations; StringSet lifts to Finset String - Spa.Language.Semantics: Value/Env/Env.Mem, big-step relations, LatticeInterpretation (respects-≈ field drops out with =) - Spa.Language.Graphs: Graph with nodes : Fin size → List BasicStmt (Vec lookup lemmas lift to Fin.append_left/right), comp/link/loop/ skipto/singleton/wrap/buildCfg, predecessors via List.finRange - Spa.Language.Traces: Trace + EndToEndTrace (Prop-valued) - Spa.Language.Properties: trace embeddings, loop lemmas, buildCfg_sufficient; the 80-line Fin-disjointness block reduces to castAdd_ne_natAdd + mathlib list lemmas - Spa.Language: Program (vars via Finset.sort — toList is noncomputable) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
169
lean/Spa/Language/Graphs.lean
Normal file
169
lean/Spa/Language/Graphs.lean
Normal file
@@ -0,0 +1,169 @@
|
||||
/-
|
||||
Port of `Language/Graphs.agda`.
|
||||
|
||||
Representation note: `nodes : Vec (List BasicStmt) size` becomes
|
||||
`nodes : Fin size → List BasicStmt`. With that, the `Data.Vec` lookup/append
|
||||
lemma stack (`lookup-++ˡ/ʳ`, `cast-is-id`, …) lifts into mathlib's
|
||||
`Fin.append` with `Fin.append_left` / `Fin.append_right`.
|
||||
|
||||
Correspondence:
|
||||
_↑ˡ_/_↑ʳ_ (on Fin) ↦ Fin.castAdd / Fin.natAdd (mathlib)
|
||||
_↑ˡⁱ_/_↑ʳⁱ_ ↦ liftIdxL / liftIdxR
|
||||
_↑ˡᵉ_/_↑ʳᵉ_ ↦ liftEdgeL / liftEdgeR
|
||||
_∙_ ↦ Graph.comp (scoped notation ∙)
|
||||
_↦_ ↦ Graph.link (scoped notation ⤳)
|
||||
loop ↦ Graph.loop
|
||||
_skipto_ ↦ Graph.skipto
|
||||
_[_] ↦ Graph.nodes (plain application)
|
||||
singleton, wrap ↦ Graph.singleton, Graph.wrap
|
||||
buildCfg ↦ buildCfg
|
||||
indices ↦ List.finRange (mathlib; `fins` from Utils.agda)
|
||||
indices-complete ↦ List.mem_finRange
|
||||
indices-Unique ↦ List.nodup_finRange
|
||||
predecessors ↦ Graph.predecessors
|
||||
edge⇒predecessor ↦ Graph.mem_predecessors_of_edge
|
||||
predecessor⇒edge ↦ Graph.edge_of_mem_predecessors
|
||||
-/
|
||||
import Spa.Language.Base
|
||||
import Mathlib.Data.Fin.Tuple.Basic
|
||||
import Mathlib.Data.List.ProdSigma
|
||||
import Mathlib.Data.List.FinRange
|
||||
|
||||
namespace Spa
|
||||
|
||||
structure Graph where
|
||||
size : ℕ
|
||||
nodes : Fin size → List BasicStmt
|
||||
edges : List (Fin size × Fin size)
|
||||
inputs : List (Fin size)
|
||||
outputs : List (Fin size)
|
||||
|
||||
namespace Graph
|
||||
|
||||
abbrev Index (g : Graph) : Type := Fin g.size
|
||||
|
||||
abbrev Edge (g : Graph) : Type := g.Index × g.Index
|
||||
|
||||
/-- Agda: `_↑ˡⁱ_`. -/
|
||||
def liftIdxL {n : ℕ} (l : List (Fin n)) (m : ℕ) : List (Fin (n + m)) :=
|
||||
l.map (Fin.castAdd m)
|
||||
|
||||
/-- Agda: `_↑ʳⁱ_`. -/
|
||||
def liftIdxR (n : ℕ) {m : ℕ} (l : List (Fin m)) : List (Fin (n + m)) :=
|
||||
l.map (Fin.natAdd n)
|
||||
|
||||
/-- Agda: `_↑ˡᵉ_` (with `_↑ˡ_` on pairs inlined). -/
|
||||
def liftEdgeL {n : ℕ} (l : List (Fin n × Fin n)) (m : ℕ) :
|
||||
List (Fin (n + m) × Fin (n + m)) :=
|
||||
l.map (fun e => (e.1.castAdd m, e.2.castAdd m))
|
||||
|
||||
/-- Agda: `_↑ʳᵉ_` (with `_↑ʳ_` on pairs inlined). -/
|
||||
def liftEdgeR (n : ℕ) {m : ℕ} (l : List (Fin m × Fin m)) :
|
||||
List (Fin (n + m) × Fin (n + m)) :=
|
||||
l.map (fun e => (e.1.natAdd n, e.2.natAdd n))
|
||||
|
||||
/-- Agda: `_∙_` — disjoint union. -/
|
||||
def comp (g₁ g₂ : Graph) : Graph where
|
||||
size := g₁.size + g₂.size
|
||||
nodes := Fin.append g₁.nodes g₂.nodes
|
||||
edges := liftEdgeL g₁.edges g₂.size ++ liftEdgeR g₁.size g₂.edges
|
||||
inputs := liftIdxL g₁.inputs g₂.size ++ liftIdxR g₁.size g₂.inputs
|
||||
outputs := liftIdxL g₁.outputs g₂.size ++ liftIdxR g₁.size g₂.outputs
|
||||
|
||||
@[inherit_doc] scoped infixr:70 " ∙ " => Graph.comp
|
||||
|
||||
/-- Agda: `_↦_` — sequencing: all outputs of `g₁` feed all inputs of `g₂`. -/
|
||||
def link (g₁ g₂ : Graph) : Graph where
|
||||
size := g₁.size + g₂.size
|
||||
nodes := Fin.append g₁.nodes g₂.nodes
|
||||
edges := liftEdgeL g₁.edges g₂.size ++ liftEdgeR g₁.size g₂.edges ++
|
||||
(liftIdxL g₁.outputs g₂.size).product (liftIdxR g₁.size g₂.inputs)
|
||||
inputs := liftIdxL g₁.inputs g₂.size
|
||||
outputs := liftIdxR g₁.size g₂.outputs
|
||||
|
||||
@[inherit_doc] scoped infixr:70 " ⤳ " => Graph.link
|
||||
|
||||
/-- The entry node of a `loop` graph. -/
|
||||
def loopIn (g : Graph) : Fin (2 + g.size) := (0 : Fin 2).castAdd g.size
|
||||
|
||||
/-- The exit node of a `loop` graph. -/
|
||||
def loopOut (g : Graph) : Fin (2 + g.size) := (1 : Fin 2).castAdd g.size
|
||||
|
||||
/-- Agda: `loop`. -/
|
||||
def loop (g : Graph) : Graph where
|
||||
size := 2 + g.size
|
||||
nodes := Fin.append (fun _ : Fin 2 => []) g.nodes
|
||||
edges := liftEdgeR 2 g.edges ++
|
||||
(liftIdxR 2 g.inputs).map (g.loopIn, ·) ++
|
||||
(liftIdxR 2 g.outputs).map (·, g.loopOut) ++
|
||||
[(g.loopOut, g.loopIn), (g.loopIn, g.loopOut)]
|
||||
inputs := [g.loopIn]
|
||||
outputs := [g.loopOut]
|
||||
|
||||
@[simp] theorem loop_inputs (g : Graph) : (loop g).inputs = [g.loopIn] := rfl
|
||||
|
||||
@[simp] theorem loop_outputs (g : Graph) : (loop g).outputs = [g.loopOut] := rfl
|
||||
|
||||
/-- Agda: `_skipto_` (unused by `buildCfg`, ported for parity). -/
|
||||
def skipto (g₁ g₂ : Graph) : Graph where
|
||||
size := g₁.size + g₂.size
|
||||
nodes := Fin.append g₁.nodes g₂.nodes
|
||||
edges := liftEdgeL g₁.edges g₂.size ++ liftEdgeR g₁.size g₂.edges ++
|
||||
(liftIdxL g₁.inputs g₂.size).product (liftIdxR g₁.size g₂.inputs)
|
||||
inputs := liftIdxL g₁.inputs g₂.size
|
||||
outputs := liftIdxR g₁.size g₂.inputs
|
||||
|
||||
/-- Agda: `singleton`. -/
|
||||
def singleton (bss : List BasicStmt) : Graph where
|
||||
size := 1
|
||||
nodes := fun _ => bss
|
||||
edges := []
|
||||
inputs := [0]
|
||||
outputs := [0]
|
||||
|
||||
/-- Agda: `wrap`. -/
|
||||
def wrap (g : Graph) : Graph :=
|
||||
singleton [] ⤳ g ⤳ singleton []
|
||||
|
||||
end Graph
|
||||
|
||||
open Graph in
|
||||
/-- Agda: `buildCfg`. -/
|
||||
def buildCfg : Stmt → Graph
|
||||
| .basic bs => Graph.singleton [bs]
|
||||
| .andThen s₁ s₂ => buildCfg s₁ ⤳ buildCfg s₂
|
||||
| .ifElse _ s₁ s₂ => buildCfg s₁ ∙ buildCfg s₂
|
||||
| .whileLoop _ s => Graph.loop (buildCfg s)
|
||||
|
||||
namespace Graph
|
||||
|
||||
variable (g : Graph)
|
||||
|
||||
/-- Agda: `indices` (`fins` is mathlib's `List.finRange`). -/
|
||||
def indices : List g.Index := List.finRange g.size
|
||||
|
||||
/-- Agda: `indices-complete`. -/
|
||||
theorem mem_indices (idx : g.Index) : idx ∈ g.indices :=
|
||||
List.mem_finRange idx
|
||||
|
||||
/-- Agda: `indices-Unique`. -/
|
||||
theorem nodup_indices : g.indices.Nodup :=
|
||||
List.nodup_finRange g.size
|
||||
|
||||
/-- Agda: `predecessors`. -/
|
||||
def predecessors (idx : g.Index) : List g.Index :=
|
||||
g.indices.filter (fun idx' => (idx', idx) ∈ g.edges)
|
||||
|
||||
/-- Agda: `edge⇒predecessor`. -/
|
||||
theorem mem_predecessors_of_edge {idx₁ idx₂ : g.Index}
|
||||
(h : (idx₁, idx₂) ∈ g.edges) : idx₁ ∈ g.predecessors idx₂ :=
|
||||
List.mem_filter.mpr ⟨g.mem_indices idx₁, by simpa using h⟩
|
||||
|
||||
/-- Agda: `predecessor⇒edge`. -/
|
||||
theorem edge_of_mem_predecessors {idx₁ idx₂ : g.Index}
|
||||
(h : idx₁ ∈ g.predecessors idx₂) : (idx₁, idx₂) ∈ g.edges := by
|
||||
simpa using (List.mem_filter.mp h).2
|
||||
|
||||
end Graph
|
||||
|
||||
end Spa
|
||||
Reference in New Issue
Block a user