cubical-transport-hott-lean4/CubicalTransport/Subst.lean
Maximus Gorog 31d19f655e
Some checks are pending
Lean Action CI / build (push) Waiting to run
Split: engine = cubical-transport HoTT only
Restructure to engine-only contents.  Application code (Topolei.*
namespace, canvas-rs / render Rust crates, Main / ProbeTest, naga IR
pipeline, Selection / Subobject / Trace / Obs.Ctx hypothesis stack,
cells-spec / HYPOTHESES / STATUS / NAGA_IR_PLAN docs) moves to the
sibling repo max/topolei.

What moved:
- `Topolei/Cubical/*.lean` (22 files) → `CubicalTransport/*.lean`
  with namespace `Topolei.Cubical.*` renamed to `CubicalTransport.*`.
  Fully-qualified test types `TopoleiCubical{FFI,Property}Test` →
  `CubicalTransport{FFI,Property}Test` for consistency.
- New root file `CubicalTransport.lean` re-exporting all 22 modules.
- Lakefile: package `cubicalTransport`; lib `CubicalTransport`; only
  `cubical-test` and `cubical-bench` exes (no GPU link path).

The split criterion: anything an AI shortcut could break that would
cascade-corrupt downstream proofs lives here.  Anything that would
only break the application stays in the topolei interface repo.

cubical-test passes 62/62 (smoke + properties) on the renamed engine.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 21:35:01 -06:00

224 lines
11 KiB
Text

/-
Topolei.Cubical.Subst
=====================
Dimension substitution for CType (Step 1 of the transport plan).
CTerm already has substDim : DimVar → DimExpr → CTerm → CTerm (Syntax.lean).
Here we add:
CTerm.substDimBool : DimVar → Bool → CTerm → CTerm
— specialises substDim to the two canonical endpoints (false = 0, true = 1).
— Defined as a thin wrapper; no new recursion.
CType.substDim : DimVar → Bool → CType → CType
— Substitutes a dimension variable with a Bool endpoint throughout a type.
— CType.path recurses into its CTerm endpoints via substDimBool.
Key theorems:
· Reduction lemmas (univ, pi, path) — proved by rfl.
· substDimBool_eq_substDim — the wrapper unfolds correctly.
· substDim_false_of_env / substDim_true_of_env — face connection:
the Bool environment value at i selects which endpoint substitution applies.
· substDim_idem — substituting twice at the same Bool is idempotent.
Note: substDim_comm (disjoint dimensions commute) is deferred;
it requires DimExpr.subst commutativity, which needs its own treatment.
-/
import CubicalTransport.Syntax
-- ── CTerm.substDimBool ────────────────────────────────────────────────────────
/-- Specialise CTerm.substDim to a Bool endpoint.
false → substitute i with DimExpr.zero (the i=0 face)
true → substitute i with DimExpr.one (the i=1 face) -/
def CTerm.substDimBool (i : DimVar) (b : Bool) (t : CTerm) : CTerm :=
t.substDim i (if b then .one else .zero)
-- Unfolds to substDim by definition.
theorem CTerm.substDimBool_eq_substDim (i : DimVar) (b : Bool) (t : CTerm) :
t.substDimBool i b = t.substDim i (if b then .one else .zero) := rfl
theorem CTerm.substDimBool_false (i : DimVar) (t : CTerm) :
t.substDimBool i false = t.substDim i .zero := rfl
theorem CTerm.substDimBool_true (i : DimVar) (t : CTerm) :
t.substDimBool i true = t.substDim i .one := rfl
-- ── CType.substDim ────────────────────────────────────────────────────────────
/-- Substitute dimension variable i with Bool endpoint b throughout a type.
Path type endpoints are terms, so we delegate to CTerm.substDimBool. -/
def CType.substDim (i : DimVar) (b : Bool) : CType → CType
| .univ => .univ
| .pi A B => .pi (A.substDim i b) (B.substDim i b)
| .path A a t => .path (A.substDim i b) (a.substDimBool i b) (t.substDimBool i b)
| .sigma A B => .sigma (A.substDim i b) (B.substDim i b)
| .glue φ T f fInv sec ret coh A =>
.glue (φ.substDim i (if b then .one else .zero))
(T.substDim i b)
(f.substDimBool i b) (fInv.substDimBool i b)
(sec.substDimBool i b) (ret.substDimBool i b) (coh.substDimBool i b)
(A.substDim i b)
-- ── CType.substDimExpr ────────────────────────────────────────────────────────
/-- Substitute dimension variable `i` with an arbitrary `DimExpr r` throughout
a type. Generalises `CType.substDim`, which fixes `r` to a Bool endpoint.
Used for *line reversal* in transport: the reversed line is
`A[i := inv i]`, which cannot be expressed as a Bool-endpoint
substitution because `inv i` is an open DimExpr. -/
def CType.substDimExpr (i : DimVar) (r : DimExpr) : CType → CType
| .univ => .univ
| .pi A B => .pi (A.substDimExpr i r) (B.substDimExpr i r)
| .path A a t => .path (A.substDimExpr i r) (a.substDim i r) (t.substDim i r)
| .sigma A B => .sigma (A.substDimExpr i r) (B.substDimExpr i r)
| .glue φ T f fInv sec ret coh A =>
.glue (φ.substDim i r)
(T.substDimExpr i r)
(f.substDim i r) (fInv.substDim i r)
(sec.substDim i r) (ret.substDim i r) (coh.substDim i r)
(A.substDimExpr i r)
-- ── Reduction lemmas ──────────────────────────────────────────────────────────
-- All proved by rfl: substDim is defined by pattern matching, so these
-- hold definitionally.
namespace CType
theorem substDim_univ (i : DimVar) (b : Bool) :
(univ).substDim i b = .univ := rfl
theorem substDim_pi (i : DimVar) (b : Bool) (A B : CType) :
(pi A B).substDim i b = .pi (A.substDim i b) (B.substDim i b) := rfl
theorem substDim_path (i : DimVar) (b : Bool) (A : CType) (a t : CTerm) :
(path A a t).substDim i b =
.path (A.substDim i b) (a.substDimBool i b) (t.substDimBool i b) := rfl
theorem substDim_sigma (i : DimVar) (b : Bool) (A B : CType) :
(sigma A B).substDim i b = .sigma (A.substDim i b) (B.substDim i b) := rfl
theorem substDim_glue (i : DimVar) (b : Bool) (φ : FaceFormula) (T : CType)
(f fInv sec ret coh : CTerm) (A : CType) :
(glue φ T f fInv sec ret coh A).substDim i b =
.glue (φ.substDim i (if b then .one else .zero))
(T.substDim i b)
(f.substDimBool i b) (fInv.substDimBool i b)
(sec.substDimBool i b) (ret.substDimBool i b) (coh.substDimBool i b)
(A.substDim i b) := rfl
-- ── substDimExpr reduction lemmas ─────────────────────────────────────────────
theorem substDimExpr_univ (i : DimVar) (r : DimExpr) :
(univ).substDimExpr i r = .univ := rfl
theorem substDimExpr_pi (i : DimVar) (r : DimExpr) (A B : CType) :
(pi A B).substDimExpr i r =
.pi (A.substDimExpr i r) (B.substDimExpr i r) := rfl
theorem substDimExpr_path (i : DimVar) (r : DimExpr) (A : CType) (a t : CTerm) :
(path A a t).substDimExpr i r =
.path (A.substDimExpr i r) (a.substDim i r) (t.substDim i r) := rfl
theorem substDimExpr_sigma (i : DimVar) (r : DimExpr) (A B : CType) :
(sigma A B).substDimExpr i r =
.sigma (A.substDimExpr i r) (B.substDimExpr i r) := rfl
theorem substDimExpr_glue (i : DimVar) (r : DimExpr) (φ : FaceFormula) (T : CType)
(f fInv sec ret coh : CTerm) (A : CType) :
(glue φ T f fInv sec ret coh A).substDimExpr i r =
.glue (φ.substDim i r)
(T.substDimExpr i r)
(f.substDim i r) (fInv.substDim i r)
(sec.substDim i r) (ret.substDim i r) (coh.substDim i r)
(A.substDimExpr i r) := rfl
/-- The Bool-endpoint `substDim` is exactly `substDimExpr` at the canonical
endpoint `DimExpr`. Proof is by pattern-matching `def` (rather than the
`induction` tactic) because `CType` is mutually inductive with `CTerm`.
`CTerm.substDimBool` is defined as `CTerm.substDim` at the same DimExpr,
so the path case closes by unfolding both. -/
def substDim_eq_substDimExpr (i : DimVar) (b : Bool) :
(A : CType) →
A.substDim i b = A.substDimExpr i (if b then DimExpr.one else DimExpr.zero)
| .univ => rfl
| .pi A B => by
show CType.pi (A.substDim i b) (B.substDim i b) =
CType.pi (A.substDimExpr i _) (B.substDimExpr i _)
rw [substDim_eq_substDimExpr i b A, substDim_eq_substDimExpr i b B]
| .path A a t => by
show CType.path (A.substDim i b) (a.substDimBool i b) (t.substDimBool i b) =
CType.path (A.substDimExpr i _) (a.substDim i _) (t.substDim i _)
rw [substDim_eq_substDimExpr i b A,
CTerm.substDimBool_eq_substDim,
CTerm.substDimBool_eq_substDim]
| .sigma A B => by
show CType.sigma (A.substDim i b) (B.substDim i b) =
CType.sigma (A.substDimExpr i _) (B.substDimExpr i _)
rw [substDim_eq_substDimExpr i b A, substDim_eq_substDimExpr i b B]
| .glue φ T f fInv sec ret coh A => by
show CType.glue
(φ.substDim i (if b then DimExpr.one else DimExpr.zero))
(T.substDim i b)
(f.substDimBool i b) (fInv.substDimBool i b)
(sec.substDimBool i b) (ret.substDimBool i b) (coh.substDimBool i b)
(A.substDim i b)
= CType.glue
(φ.substDim i _)
(T.substDimExpr i _)
(f.substDim i _) (fInv.substDim i _)
(sec.substDim i _) (ret.substDim i _) (coh.substDim i _)
(A.substDimExpr i _)
rw [substDim_eq_substDimExpr i b T,
substDim_eq_substDimExpr i b A,
CTerm.substDimBool_eq_substDim,
CTerm.substDimBool_eq_substDim,
CTerm.substDimBool_eq_substDim,
CTerm.substDimBool_eq_substDim,
CTerm.substDimBool_eq_substDim]
-- ── Face connection ───────────────────────────────────────────────────────────
-- These lemmas state the relationship between the Bool dimension environment
-- and which endpoint substitution applies.
--
-- Semantics: env i = false means we are at the i=0 face (eq0 i holds).
-- env i = true means we are at the i=1 face (eq1 i holds).
-- The correct substitution to apply is therefore substDim i (env i).
/-- At the i=0 face (env i = false), substDim i (env i) is substDim i false. -/
theorem substDim_at_false (i : DimVar) (A : CType) (env : DimVar → Bool)
(h : env i = false) :
A.substDim i (env i) = A.substDim i false := by
rw [h]
/-- At the i=1 face (env i = true), substDim i (env i) is substDim i true. -/
theorem substDim_at_true (i : DimVar) (A : CType) (env : DimVar → Bool)
(h : env i = true) :
A.substDim i (env i) = A.substDim i true := by
rw [h]
-- ── Deferred: idempotence and commutativity ───────────────────────────────────
-- substDim_idem : (A.substDim i b).substDim i b = A.substDim i b
-- substDim_comm : for i ≠ j, (A.substDim i b).substDim j c = (A.substDim j c).substDim i b
--
-- Both require simultaneous induction over the CType/CTerm mutual inductive,
-- which needs a `mutual` proof block or a size-indexed recursor.
-- Deferred to a later pass after the DimLine and transport layers are in place.
--
-- Correctness argument (informal):
-- · After substDim i b, every DimExpr referencing i becomes zero or one
-- (neither of which contains free variables), so a second substDim i b
-- finds nothing left to substitute. Idempotence follows.
-- · Substituting disjoint dimensions i ≠ j affects non-overlapping parts
-- of every DimExpr (DimExpr.subst is capture-avoiding), so order is irrelevant.
theorem substDim_comm_univ (i j : DimVar) (b c : Bool) :
((univ : CType).substDim i b).substDim j c =
((univ : CType).substDim j c).substDim i b := rfl
end CType
-- Note: dimAbsent, substDimBool_idem, and substDim_idem are proved in DimLine.lean,
-- which is downstream in the import chain and has access to dimAbsent predicates.