Add function back in to Embedding

This commit is contained in:
2026-08-09 21:43:20 -05:00
parent fd371ba175
commit 53f8bd47dc
2 changed files with 25 additions and 50 deletions

View File

@@ -229,55 +229,47 @@ because of the offset-based embedding, we can determine whether a node
came from a particular subgraph simply by examining its offset and sub-graph came from a particular subgraph simply by examining its offset and sub-graph
size. This is captured by `Embed.mem_range_iff`. -/ size. This is captured by `Embed.mem_range_iff`. -/
/-- Translate an index of `g` into `h` by a constant offset. -/
private def shift {g h : GGraph α} (off : ) (hle : g.size + off h.size) (i : g.Index) :
h.Index := off + i.val, by omega
@[simp] lemma shift_val {g h : GGraph α} {off : } (hle : g.size + off h.size)
(i : g.Index) : (shift (h := h) off hle i).val = off + i.val := rfl
/-- A special-case embedding of `g` into `h` in which all edges and nodes /-- A special-case embedding of `g` into `h` in which all edges and nodes
of `g` are present in `h` at a given offset `off`. -/ of `g` are present in `h` at a given offset `off`. -/
structure Embed (g h : GGraph α) where structure Embed (g h : GGraph α) where
f : g.Index h.Index
off : off :
size_le : g.size + off h.size f_val : i, (f i).val = off + i.val
nodes_eq : i, h.nodes (shift off size_le i) = g.nodes i nodes_eq : i, h.nodes (f i) = g.nodes i
edges_mem : {e : g.Edge}, e g.edges edges_mem : {e : g.Edge}, e g.edges (f e.1, f e.2) h.edges
(shift off size_le e.1, shift off size_le e.2) h.edges
/-- The index translation of an embedding. -/ lemma Embed.f_inj {g h : GGraph α} (e : Embed g h) : Function.Injective e.f := by
abbrev Embed.f {g h : GGraph α} (e : Embed g h) (i : g.Index) : h.Index := intro i j hij
shift e.off e.size_le i have := congrArg Fin.val hij
rw [e.f_val, e.f_val] at this
lemma Embed.f_inj {g h : GGraph α} (e : Embed g h) : Function.Injective e.f := exact Fin.ext (by omega)
fun _ _ hij => Fin.ext (by simpa using congrArg Fin.val hij)
/-- An embedding's range is the interval `[off, off + g.size)`. -/ /-- An embedding's range is the interval `[off, off + g.size)`. -/
lemma Embed.mem_range_iff {g h : GGraph α} (e : Embed g h) (j : h.Index) : lemma Embed.mem_range_iff {g h : GGraph α} (e : Embed g h) (j : h.Index) :
( i, e.f i = j) e.off j.val j.val < e.off + g.size := by ( i, e.f i = j) e.off j.val j.val < e.off + g.size := by
constructor constructor
· rintro i, rfl; have := i.isLt; simp only [Embed.f, shift_val]; omega · rintro i, rfl; have := i.isLt; rw [e.f_val]; omega
· rintro hlo, hhi · rintro hlo, hhi
exact j.val - e.off, by omega, Fin.ext (by simp only [Embed.f, shift_val]; omega) refine j.val - e.off, by omega, Fin.ext ?_
rw [e.f_val]
show e.off + (j.val - e.off) = j.val
omega
/-- Build an embedding from an index map that is pointwise the shift. The five /-- Build an embedding from an index map that is pointwise the shift. The five
inclusions below are naturally written with `Fin.castAdd`/`Fin.natAdd` — the form inclusions below are naturally written with `Fin.castAdd`/`Fin.natAdd` — the form
the `Fin.append` lemmas are stated in — so this lets them keep those proofs the `Fin.append` lemmas are stated in — so this lets them keep those proofs
verbatim while `Embed` stores only the offset. The trailing two arguments are verbatim. The trailing argument is boilerplate at every call site and defaults
boilerplate at every call site and default to discharging themselves. -/ to discharging itself. -/
private def Embed.ofIndexMap {g h : GGraph α} (off : ) (k : g.Index h.Index) private def Embed.ofIndexMap {g h : GGraph α} (off : ) (k : g.Index h.Index)
(hn : i, h.nodes (k i) = g.nodes i) (hn : i, h.nodes (k i) = g.nodes i)
(hem : {e : g.Edge}, e g.edges (k e.1, k e.2) h.edges) (hem : {e : g.Edge}, e g.edges (k e.1, k e.2) h.edges)
(hle : g.size + off h.size := by
first | omega | (simp [GGraph.sequence, GGraph.overlay, GGraph.loop] <;> omega))
(hk : i, (k i).val = off + i.val := by intro i; simp) : (hk : i, (k i).val = off + i.val := by intro i; simp) :
Embed g h where Embed g h where
f := k
off := off off := off
size_le := hle f_val := hk
nodes_eq i := by rw [show shift off hle i = k i from Fin.ext (by simp [hk])]; exact hn i nodes_eq := hn
edges_mem hmem := by edges_mem := hem
have hs : i, shift (h := h) off hle i = k i := fun i => Fin.ext (by simp [hk])
rw [hs, hs]; exact hem hmem
/-- Embeddings compose (offsets add). -/ /-- Embeddings compose (offsets add). -/
def Embed.trans {g₁ g₂ g₃ : GGraph α} (e₁ : Embed g₁ g₂) (e₂ : Embed g₂ g₃) : def Embed.trans {g₁ g₂ g₃ : GGraph α} (e₁ : Embed g₁ g₂) (e₂ : Embed g₂ g₃) :
@@ -285,8 +277,7 @@ def Embed.trans {g₁ g₂ g₃ : GGraph α} (e₁ : Embed g₁ g₂) (e₂ : Em
ofIndexMap (e₂.off + e₁.off) (fun i => e₂.f (e₁.f i)) ofIndexMap (e₂.off + e₁.off) (fun i => e₂.f (e₁.f i))
(fun i => (e₂.nodes_eq (e₁.f i)).trans (e₁.nodes_eq i)) (fun i => (e₂.nodes_eq (e₁.f i)).trans (e₁.nodes_eq i))
(fun he => e₂.edges_mem (e₁.edges_mem he)) (fun he => e₂.edges_mem (e₁.edges_mem he))
(hle := by have := e₁.size_le; have := e.size_le; omega) (hk := fun i => by rw [e₂.f_val, e.f_val]; omega)
(hk := fun i => by simp; omega)
/-- The left operand's inclusion into a sequenced graph. -/ /-- The left operand's inclusion into a sequenced graph. -/
def Embed.sequenceLeft (g₁ g₂ : GGraph α) : Embed g₁ (g₁ g₂) := def Embed.sequenceLeft (g₁ g₂ : GGraph α) : Embed g₁ (g₁ g₂) :=
@@ -322,20 +313,6 @@ def Embed.singletonIndex {a : α} {h : GGraph α} (e : Embed (singleton a) h) :
(e : Embed (singleton a) h) : h.nodes e.singletonIndex = a := (e : Embed (singleton a) h) : h.nodes e.singletonIndex = a :=
e.nodes_eq 0, Nat.zero_lt_one e.nodes_eq 0, Nat.zero_lt_one
/-! `shift` puts the offset on the left (`off + i.val`), matching `Fin.natAdd`, so
every right inclusion is *definitionally* the form `Spa/Language/Properties.lean`
states its trace indices in. Only the left inclusions need a bridge, since their
offset is `0` and `0 + i.val` does not reduce.
Deliberately not `@[simp]`: they rewrite `Embed.f` back into `Fin.castAdd` form,
discarding the offset that `Embed.mem_range_iff` — and the subgraph-containment
tests built on it — reason with. -/
lemma Embed.sequenceLeft_f (g₁ g₂ : GGraph α) (i : g₁.Index) :
(Embed.sequenceLeft g₁ g₂).f i = i.castAdd g₂.size := Fin.ext (Nat.zero_add _)
lemma Embed.overlayLeft_f (g₁ g₂ : GGraph α) (i : g₁.Index) :
(Embed.overlayLeft g₁ g₂).f i = i.castAdd g₂.size := Fin.ext (Nat.zero_add _)
variable (g : GGraph α) variable (g : GGraph α)
/-- All the nodes in the graph. -/ /-- All the nodes in the graph. -/

View File

@@ -41,9 +41,8 @@ noncomputable def Trace.embed {g h : Graph} (e : GGraph.Embed g h)
a corresponding trace exists in the combined graph. -/ a corresponding trace exists in the combined graph. -/
noncomputable def Trace.overlay_left {idx₁ idx₂ : g₁.Index} noncomputable def Trace.overlay_left {idx₁ idx₂ : g₁.Index}
(tr : Trace g₁ idx₁ idx₂ ρ₁ ρ₂) : (tr : Trace g₁ idx₁ idx₂ ρ₁ ρ₂) :
Trace (g₁ g₂) (idx₁.castAdd g₂.size) (idx₂.castAdd g₂.size) ρ₁ ρ₂ := by Trace (g₁ g₂) (idx₁.castAdd g₂.size) (idx₂.castAdd g₂.size) ρ₁ ρ₂ :=
have h := tr.embed (GGraph.Embed.overlayLeft g₁ g₂) tr.embed (GGraph.Embed.overlayLeft g₁ g₂)
rwa [GGraph.Embed.overlayLeft_f, GGraph.Embed.overlayLeft_f] at h
/-- When two graphs are overlaid, for each trace in the right graph, /-- When two graphs are overlaid, for each trace in the right graph,
a corresponding trace exists in the combined graph. -/ a corresponding trace exists in the combined graph. -/
@@ -56,9 +55,8 @@ noncomputable def Trace.overlay_right {idx₁ idx₂ : g₂.Index}
a corresponding trace exists in the combined graph. -/ a corresponding trace exists in the combined graph. -/
noncomputable def Trace.sequence_left {idx₁ idx₂ : g₁.Index} noncomputable def Trace.sequence_left {idx₁ idx₂ : g₁.Index}
(tr : Trace g₁ idx₁ idx₂ ρ₁ ρ₂) : (tr : Trace g₁ idx₁ idx₂ ρ₁ ρ₂) :
Trace (g₁ g₂) (idx₁.castAdd g₂.size) (idx₂.castAdd g₂.size) ρ₁ ρ₂ := by Trace (g₁ g₂) (idx₁.castAdd g₂.size) (idx₂.castAdd g₂.size) ρ₁ ρ₂ :=
have h := tr.embed (GGraph.Embed.sequenceLeft g₁ g₂) tr.embed (GGraph.Embed.sequenceLeft g₁ g₂)
rwa [GGraph.Embed.sequenceLeft_f, GGraph.Embed.sequenceLeft_f] at h
/-- When two graphs are sequenced, for each trace in the second graph, /-- When two graphs are sequenced, for each trace in the second graph,
a corresponding trace exists in the combined graph. -/ a corresponding trace exists in the combined graph. -/