Files
agda-spa/lean/Spa/Language/Graphs.lean
Danila Fedorin 2cfd0a2fb7 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>
2026-06-09 19:30:42 -07:00

170 lines
5.8 KiB
Lean4
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/-
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