Some checks are pending
Lean Action CI / build (push) Waiting to run
Lands the foundational DecidableEq machinery and the @[simp]-
based question-form routing in one go (per project discipline:
no shortcuts, no compat shims).
CubicalTransport/DecEq.lean (new, ~290 lines):
- Mutual decEq for the 5-way AST block (CType, CTerm, CTypeArg,
CtorSpec, CTypeSchema) plus list/clause/branch helpers
(decEqListCType, decEqListCTerm, decEqListCTypeArg,
decEqListCtorSpec, decEqClauses, decEqBranches).
- Returns Decidable (a = b) directly; uses OR-patterns for
cross-constructor mismatches, discharged uniformly via cases.
- Five DecidableEq instances declared post-block.
- Lean 4 deriving doesn't support mutual inductives; manual
decEq is the canonical approach.
CubicalTransport/Interval.lean: deriving DecidableEq on DimExpr.
CubicalTransport/Face.lean: deriving DecidableEq on FaceFormula.
CubicalTransport/Question.lean:
- All 11 classifier Decidable instances now land:
IsConstLine, IsFullFace, IsEmptyFace, IsTransport,
IsIntervalLine, IsUnivLine — direct from DecidableEq.
IsPathLine, IsPiLine, IsSigmaLine, IsGlueLine, IsIndLine —
via match h : q.body with on the head constructor +
existential-witness reconstruction in the isTrue arm.
- @[simp] tags on ask_of_full_face / ask_of_empty_face /
ask_of_const_line / ask_of_transport_full_face — the Level 2
routing through CompQ.Equiv.
- Three example proofs at end of file demonstrating that the
simp-set composes (full-face C1, empty-face C2, transport-
shaped interval-line reduction).
CubicalTransport/FFITest.lean: 6 new classifier-decidability
smoke tests (IsFullFace, IsTransport×2, IsPiLine, IsIntervalLine).
Test count: 84 → 89 passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
543 lines
22 KiB
Text
543 lines
22 KiB
Text
/-
|
||
Topolei.Cubical.Face
|
||
====================
|
||
The face lattice F: distributive lattice generated by (i=0) and (i=1)
|
||
with the fundamental relation (i=0) ∧ (i=1) = 0_F.
|
||
|
||
Reference: CCHM §4.1
|
||
Grammar: ϕ, ψ ::= 0_F | 1_F | (i=0) | (i=1) | ϕ∧ψ | ϕ∨ψ
|
||
|
||
Face formulas describe which sub-face of a cube we are on.
|
||
The key invariant: (i=0) and (i=1) are mutually exclusive and jointly exhaustive.
|
||
-/
|
||
|
||
import CubicalTransport.Interval
|
||
|
||
-- ── Face formulas ─────────────────────────────────────────────────────────────
|
||
|
||
inductive FaceFormula where
|
||
| bot : FaceFormula -- 0_F
|
||
| top : FaceFormula -- 1_F
|
||
| eq0 (i : DimVar) : FaceFormula -- (i = 0)
|
||
| eq1 (i : DimVar) : FaceFormula -- (i = 1)
|
||
| meet (ϕ ψ : FaceFormula) : FaceFormula -- ϕ ∧ ψ
|
||
| join (ϕ ψ : FaceFormula) : FaceFormula -- ϕ ∨ ψ
|
||
deriving Repr, Inhabited, DecidableEq
|
||
|
||
-- ── Semantic evaluation ───────────────────────────────────────────────────────
|
||
|
||
def FaceFormula.eval (env : DimVar → Bool) : FaceFormula → Bool
|
||
| .bot => false
|
||
| .top => true
|
||
| .eq0 i => !(env i)
|
||
| .eq1 i => env i
|
||
| .meet ϕ ψ => ϕ.eval env && ψ.eval env
|
||
| .join ϕ ψ => ϕ.eval env || ψ.eval env
|
||
|
||
-- ── Fundamental laws ──────────────────────────────────────────────────────────
|
||
-- Strategy: simp only [eval] to unfold, then Bool case analysis.
|
||
-- Note: `show` is avoided here because `&&`/`||` bind looser than `=` in Lean 4,
|
||
-- causing parse surprises. `simp only [eval]` is cleaner and consistent.
|
||
|
||
namespace FaceFormula
|
||
|
||
-- (i=0) and (i=1) are mutually exclusive: their meet is always false
|
||
theorem eq0_meet_eq1 (i : DimVar) (env : DimVar → Bool) :
|
||
(meet (eq0 i) (eq1 i)).eval env = false := by
|
||
simp only [eval]
|
||
cases env i <;> rfl
|
||
|
||
-- They are jointly exhaustive: their join is always true
|
||
theorem eq0_join_eq1 (i : DimVar) (env : DimVar → Bool) :
|
||
(join (eq0 i) (eq1 i)).eval env = true := by
|
||
simp only [eval]
|
||
cases env i <;> rfl
|
||
|
||
-- Commutativity
|
||
theorem meet_comm (ϕ ψ : FaceFormula) (env : DimVar → Bool) :
|
||
(meet ϕ ψ).eval env = (meet ψ ϕ).eval env := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> cases ψ.eval env <;> rfl
|
||
|
||
theorem join_comm (ϕ ψ : FaceFormula) (env : DimVar → Bool) :
|
||
(join ϕ ψ).eval env = (join ψ ϕ).eval env := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> cases ψ.eval env <;> rfl
|
||
|
||
-- Associativity
|
||
theorem meet_assoc (ϕ ψ χ : FaceFormula) (env : DimVar → Bool) :
|
||
(meet ϕ (meet ψ χ)).eval env = (meet (meet ϕ ψ) χ).eval env := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> cases ψ.eval env <;> cases χ.eval env <;> rfl
|
||
|
||
theorem join_assoc (ϕ ψ χ : FaceFormula) (env : DimVar → Bool) :
|
||
(join ϕ (join ψ χ)).eval env = (join (join ϕ ψ) χ).eval env := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> cases ψ.eval env <;> cases χ.eval env <;> rfl
|
||
|
||
-- Identity laws
|
||
theorem meet_top (ϕ : FaceFormula) (env : DimVar → Bool) :
|
||
(meet ϕ top).eval env = ϕ.eval env := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> rfl
|
||
|
||
theorem join_bot (ϕ : FaceFormula) (env : DimVar → Bool) :
|
||
(join ϕ bot).eval env = ϕ.eval env := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> rfl
|
||
|
||
theorem meet_bot (ϕ : FaceFormula) (env : DimVar → Bool) :
|
||
(meet ϕ bot).eval env = false := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> rfl
|
||
|
||
theorem join_top (ϕ : FaceFormula) (env : DimVar → Bool) :
|
||
(join ϕ top).eval env = true := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> rfl
|
||
|
||
-- Distributivity
|
||
theorem distrib (ϕ ψ χ : FaceFormula) (env : DimVar → Bool) :
|
||
(meet ϕ (join ψ χ)).eval env = (join (meet ϕ ψ) (meet ϕ χ)).eval env := by
|
||
simp only [eval]
|
||
cases ϕ.eval env <;> cases ψ.eval env <;> cases χ.eval env <;> rfl
|
||
|
||
-- ── Face restriction ──────────────────────────────────────────────────────────
|
||
|
||
/-- Restrict a face formula by fixing dimension i to value b.
|
||
This is the substitution ϕ(i/b) from CCHM. -/
|
||
def restrict (i : DimVar) (b : Bool) : FaceFormula → FaceFormula
|
||
| .bot => .bot
|
||
| .top => .top
|
||
| .eq0 j => if j = i then (if b then .bot else .top) else .eq0 j
|
||
| .eq1 j => if j = i then (if b then .top else .bot) else .eq1 j
|
||
| .meet ϕ ψ => .meet (restrict i b ϕ) (restrict i b ψ)
|
||
| .join ϕ ψ => .join (restrict i b ϕ) (restrict i b ψ)
|
||
|
||
theorem eval_restrict (i : DimVar) (b : Bool) (ϕ : FaceFormula) (env : DimVar → Bool) :
|
||
(restrict i b ϕ).eval env =
|
||
ϕ.eval (fun j => if j = i then b else env j) := by
|
||
induction ϕ with
|
||
| bot => rfl
|
||
| top => rfl
|
||
| eq0 j =>
|
||
simp only [restrict, eval]
|
||
by_cases h : j = i
|
||
· subst h
|
||
cases b <;> simp [eval]
|
||
· simp only [if_neg h, eval]
|
||
| eq1 j =>
|
||
simp only [restrict, eval]
|
||
by_cases h : j = i
|
||
· subst h
|
||
cases b <;> simp [eval]
|
||
· simp only [if_neg h, eval]
|
||
| meet ϕ ψ ih_ϕ ih_ψ => simp only [restrict, eval, ih_ϕ, ih_ψ]
|
||
| join ϕ ψ ih_ϕ ih_ψ => simp only [restrict, eval, ih_ϕ, ih_ψ]
|
||
|
||
-- Endpoint restriction lemmas (used in composition algorithm)
|
||
theorem restrict_eq0_false (i : DimVar) (env : DimVar → Bool) :
|
||
(restrict i false (eq0 i)).eval env = true := by
|
||
simp [restrict, eval]
|
||
|
||
theorem restrict_eq0_true (i : DimVar) (env : DimVar → Bool) :
|
||
(restrict i true (eq0 i)).eval env = false := by
|
||
simp [restrict, eval]
|
||
|
||
theorem restrict_eq1_true (i : DimVar) (env : DimVar → Bool) :
|
||
(restrict i true (eq1 i)).eval env = true := by
|
||
simp [restrict, eval]
|
||
|
||
theorem restrict_eq1_false (i : DimVar) (env : DimVar → Bool) :
|
||
(restrict i false (eq1 i)).eval env = false := by
|
||
simp [restrict, eval]
|
||
|
||
-- ── Entailment ────────────────────────────────────────────────────────────────
|
||
|
||
/-- ϕ entails ψ: whenever ϕ holds, ψ holds. -/
|
||
def Entails (ϕ ψ : FaceFormula) : Prop :=
|
||
∀ env : DimVar → Bool, ϕ.eval env = true → ψ.eval env = true
|
||
|
||
theorem entails_refl (ϕ : FaceFormula) : Entails ϕ ϕ :=
|
||
fun _ h => h
|
||
|
||
theorem entails_trans (ϕ ψ χ : FaceFormula) :
|
||
Entails ϕ ψ → Entails ψ χ → Entails ϕ χ :=
|
||
fun h1 h2 env hϕ => h2 env (h1 env hϕ)
|
||
|
||
theorem entails_meet_left (ϕ ψ : FaceFormula) : Entails (meet ϕ ψ) ϕ :=
|
||
fun env h => by
|
||
simp only [eval] at h
|
||
cases hϕ : ϕ.eval env
|
||
· simp [hϕ] at h
|
||
· rfl
|
||
|
||
theorem entails_join_left (ϕ ψ : FaceFormula) : Entails ϕ (join ϕ ψ) :=
|
||
fun env h => by simp only [eval, h, Bool.true_or]
|
||
|
||
theorem entails_join_right (ϕ ψ : FaceFormula) : Entails ψ (join ϕ ψ) :=
|
||
fun env h => by simp only [eval, h, Bool.or_true]
|
||
|
||
-- ── Dimension absence ─────────────────────────────────────────────────────────
|
||
|
||
/-- Syntactic check: `i` does not appear in the face formula. -/
|
||
def dimAbsent (i : DimVar) : FaceFormula → Bool
|
||
| .bot => true
|
||
| .top => true
|
||
| .eq0 j => j != i
|
||
| .eq1 j => j != i
|
||
| .meet ϕ ψ => ϕ.dimAbsent i && ψ.dimAbsent i
|
||
| .join ϕ ψ => ϕ.dimAbsent i && ψ.dimAbsent i
|
||
|
||
-- ── DimExpr → FaceFormula: "the DimExpr equals 0 / 1" ────────────────────────
|
||
-- These translate a DimExpr r into a FaceFormula saying "r = 0" (or "r = 1").
|
||
-- Used by FaceFormula.substDim below: when we substitute a dim variable j
|
||
-- that appears inside `eq0 j` with a DimExpr r, the result should encode
|
||
-- "r = 0" as a face, which — for composite r — is itself a composite face.
|
||
--
|
||
-- De Morgan at the DimExpr ↔ FaceFormula boundary:
|
||
-- (meet r s = 0) ↔ (r = 0 ∨ s = 0) [meet zero: either factor is 0]
|
||
-- (join r s = 0) ↔ (r = 0 ∧ s = 0) [join zero: both factors are 0]
|
||
-- (inv r = 0) ↔ (r = 1)
|
||
-- Dually for "= 1".
|
||
|
||
mutual
|
||
/-- Build a FaceFormula expressing `r = 0` for an arbitrary DimExpr `r`. -/
|
||
def dimExprEq0 : DimExpr → FaceFormula
|
||
| .zero => .top
|
||
| .one => .bot
|
||
| .var k => .eq0 k
|
||
| .inv r => dimExprEq1 r
|
||
| .meet r s => .join (dimExprEq0 r) (dimExprEq0 s)
|
||
| .join r s => .meet (dimExprEq0 r) (dimExprEq0 s)
|
||
|
||
/-- Build a FaceFormula expressing `r = 1` for an arbitrary DimExpr `r`. -/
|
||
def dimExprEq1 : DimExpr → FaceFormula
|
||
| .zero => .bot
|
||
| .one => .top
|
||
| .var k => .eq1 k
|
||
| .inv r => dimExprEq0 r
|
||
| .meet r s => .meet (dimExprEq1 r) (dimExprEq1 s)
|
||
| .join r s => .join (dimExprEq1 r) (dimExprEq1 s)
|
||
end
|
||
|
||
-- ── Face formula substitution by DimExpr ─────────────────────────────────────
|
||
|
||
/-- Substitute a dimension variable `i` with an arbitrary `DimExpr r`
|
||
throughout a face formula. Uses `dimExprEq0`/`dimExprEq1` to encode
|
||
the composite faces arising from substitution into `eq0 j`/`eq1 j`. -/
|
||
def substDim (i : DimVar) (r : DimExpr) : FaceFormula → FaceFormula
|
||
| .bot => .bot
|
||
| .top => .top
|
||
| .eq0 j => if j = i then dimExprEq0 r else .eq0 j
|
||
| .eq1 j => if j = i then dimExprEq1 r else .eq1 j
|
||
| .meet ϕ ψ => .meet (ϕ.substDim i r) (ψ.substDim i r)
|
||
| .join ϕ ψ => .join (ϕ.substDim i r) (ψ.substDim i r)
|
||
|
||
-- ── Correctness: dimExprEq0/1 compute the right predicate ───────────────────
|
||
-- The semantic content: `(dimExprEq0 r).eval env = !(r.eval env)`, i.e. the
|
||
-- face holds iff r evaluates to 0 (= false in Bool).
|
||
|
||
mutual
|
||
theorem dimExprEq0_eval (r : DimExpr) (env : DimVar → Bool) :
|
||
(dimExprEq0 r).eval env = !(r.eval env) := by
|
||
cases r with
|
||
| zero => rfl
|
||
| one => rfl
|
||
| var k => rfl
|
||
| inv s =>
|
||
show (dimExprEq1 s).eval env = _
|
||
rw [dimExprEq1_eval s env]
|
||
simp [DimExpr.eval]
|
||
| meet s t =>
|
||
show ((dimExprEq0 s).join (dimExprEq0 t)).eval env = _
|
||
simp only [eval]
|
||
rw [dimExprEq0_eval s env, dimExprEq0_eval t env]
|
||
simp [DimExpr.eval, Bool.not_and]
|
||
| join s t =>
|
||
show ((dimExprEq0 s).meet (dimExprEq0 t)).eval env = _
|
||
simp only [eval]
|
||
rw [dimExprEq0_eval s env, dimExprEq0_eval t env]
|
||
simp [DimExpr.eval, Bool.not_or]
|
||
|
||
theorem dimExprEq1_eval (r : DimExpr) (env : DimVar → Bool) :
|
||
(dimExprEq1 r).eval env = r.eval env := by
|
||
cases r with
|
||
| zero => rfl
|
||
| one => rfl
|
||
| var k => rfl
|
||
| inv s =>
|
||
show (dimExprEq0 s).eval env = _
|
||
rw [dimExprEq0_eval s env]
|
||
simp [DimExpr.eval]
|
||
| meet s t =>
|
||
show ((dimExprEq1 s).meet (dimExprEq1 t)).eval env = _
|
||
simp only [eval]
|
||
rw [dimExprEq1_eval s env, dimExprEq1_eval t env]
|
||
simp [DimExpr.eval]
|
||
| join s t =>
|
||
show ((dimExprEq1 s).join (dimExprEq1 t)).eval env = _
|
||
simp only [eval]
|
||
rw [dimExprEq1_eval s env, dimExprEq1_eval t env]
|
||
simp [DimExpr.eval]
|
||
end
|
||
|
||
-- ── Endpoint specialisations: substDim at .zero / .one agrees with restrict ──
|
||
|
||
theorem substDim_zero (i : DimVar) (ϕ : FaceFormula) :
|
||
ϕ.substDim i .zero = ϕ.restrict i false := by
|
||
induction ϕ with
|
||
| bot => rfl
|
||
| top => rfl
|
||
| eq0 j => by_cases h : j = i <;> simp [substDim, restrict, h, dimExprEq0]
|
||
| eq1 j => by_cases h : j = i <;> simp [substDim, restrict, h, dimExprEq1]
|
||
| meet ϕ ψ ihϕ ihψ => simp [substDim, restrict, ihϕ, ihψ]
|
||
| join ϕ ψ ihϕ ihψ => simp [substDim, restrict, ihϕ, ihψ]
|
||
|
||
theorem substDim_one (i : DimVar) (ϕ : FaceFormula) :
|
||
ϕ.substDim i .one = ϕ.restrict i true := by
|
||
induction ϕ with
|
||
| bot => rfl
|
||
| top => rfl
|
||
| eq0 j => by_cases h : j = i <;> simp [substDim, restrict, h, dimExprEq0]
|
||
| eq1 j => by_cases h : j = i <;> simp [substDim, restrict, h, dimExprEq1]
|
||
| meet ϕ ψ ihϕ ihψ => simp [substDim, restrict, ihϕ, ihψ]
|
||
| join ϕ ψ ihϕ ihψ => simp [substDim, restrict, ihϕ, ihψ]
|
||
|
||
-- ── substDim is identity when dim is absent ──────────────────────────────────
|
||
|
||
theorem substDim_of_absent (i : DimVar) (r : DimExpr)
|
||
(ϕ : FaceFormula) (h : ϕ.dimAbsent i = true) :
|
||
ϕ.substDim i r = ϕ := by
|
||
induction ϕ with
|
||
| bot => rfl
|
||
| top => rfl
|
||
| eq0 j =>
|
||
simp only [dimAbsent, bne_iff_ne] at h
|
||
simp [substDim, if_neg h]
|
||
| eq1 j =>
|
||
simp only [dimAbsent, bne_iff_ne] at h
|
||
simp [substDim, if_neg h]
|
||
| meet ϕ ψ ihϕ ihψ =>
|
||
simp only [dimAbsent, Bool.and_eq_true] at h
|
||
simp [substDim, ihϕ h.1, ihψ h.2]
|
||
| join ϕ ψ ihϕ ihψ =>
|
||
simp only [dimAbsent, Bool.and_eq_true] at h
|
||
simp [substDim, ihϕ h.1, ihψ h.2]
|
||
|
||
-- ── dimExprEq{0,1} dim-absence preservation ────────────────────────────────
|
||
-- Mutual recursion because dimExprEq0 and dimExprEq1 refer to each other
|
||
-- on `.inv`.
|
||
|
||
mutual
|
||
/-- When `r` doesn't mention `j`, neither does `dimExprEq0 r`. -/
|
||
theorem dimExprEq0_dimAbsent (j : DimVar) : (r : DimExpr) →
|
||
r.dimAbsent j = true → (dimExprEq0 r).dimAbsent j = true
|
||
| .zero, _ => rfl
|
||
| .one, _ => rfl
|
||
| .var k, h => by
|
||
simp only [DimExpr.dimAbsent, bne_iff_ne] at h
|
||
simp [dimExprEq0, dimAbsent, bne_iff_ne, h]
|
||
| .inv r, h => by
|
||
simp only [DimExpr.dimAbsent] at h
|
||
show (dimExprEq1 r).dimAbsent j = true
|
||
exact dimExprEq1_dimAbsent j r h
|
||
| .meet r s, h => by
|
||
simp only [DimExpr.dimAbsent, Bool.and_eq_true] at h
|
||
show ((dimExprEq0 r).join (dimExprEq0 s)).dimAbsent j = true
|
||
simp only [dimAbsent, Bool.and_eq_true]
|
||
exact ⟨dimExprEq0_dimAbsent j r h.1, dimExprEq0_dimAbsent j s h.2⟩
|
||
| .join r s, h => by
|
||
simp only [DimExpr.dimAbsent, Bool.and_eq_true] at h
|
||
show ((dimExprEq0 r).meet (dimExprEq0 s)).dimAbsent j = true
|
||
simp only [dimAbsent, Bool.and_eq_true]
|
||
exact ⟨dimExprEq0_dimAbsent j r h.1, dimExprEq0_dimAbsent j s h.2⟩
|
||
|
||
/-- When `r` doesn't mention `j`, neither does `dimExprEq1 r`. -/
|
||
theorem dimExprEq1_dimAbsent (j : DimVar) : (r : DimExpr) →
|
||
r.dimAbsent j = true → (dimExprEq1 r).dimAbsent j = true
|
||
| .zero, _ => rfl
|
||
| .one, _ => rfl
|
||
| .var k, h => by
|
||
simp only [DimExpr.dimAbsent, bne_iff_ne] at h
|
||
simp [dimExprEq1, dimAbsent, bne_iff_ne, h]
|
||
| .inv r, h => by
|
||
simp only [DimExpr.dimAbsent] at h
|
||
show (dimExprEq0 r).dimAbsent j = true
|
||
exact dimExprEq0_dimAbsent j r h
|
||
| .meet r s, h => by
|
||
simp only [DimExpr.dimAbsent, Bool.and_eq_true] at h
|
||
show ((dimExprEq1 r).meet (dimExprEq1 s)).dimAbsent j = true
|
||
simp only [dimAbsent, Bool.and_eq_true]
|
||
exact ⟨dimExprEq1_dimAbsent j r h.1, dimExprEq1_dimAbsent j s h.2⟩
|
||
| .join r s, h => by
|
||
simp only [DimExpr.dimAbsent, Bool.and_eq_true] at h
|
||
show ((dimExprEq1 r).join (dimExprEq1 s)).dimAbsent j = true
|
||
simp only [dimAbsent, Bool.and_eq_true]
|
||
exact ⟨dimExprEq1_dimAbsent j r h.1, dimExprEq1_dimAbsent j s h.2⟩
|
||
end
|
||
|
||
/-- `substDim j s` on `dimExprEq0 r` is identity when `r` doesn't mention `j`. -/
|
||
theorem dimExprEq0_substDim_of_absent (j : DimVar) (s : DimExpr)
|
||
(r : DimExpr) (h : r.dimAbsent j = true) :
|
||
(dimExprEq0 r).substDim j s = dimExprEq0 r :=
|
||
substDim_of_absent j s _ (dimExprEq0_dimAbsent j r h)
|
||
|
||
/-- `substDim j s` on `dimExprEq1 r` is identity when `r` doesn't mention `j`. -/
|
||
theorem dimExprEq1_substDim_of_absent (j : DimVar) (s : DimExpr)
|
||
(r : DimExpr) (h : r.dimAbsent j = true) :
|
||
(dimExprEq1 r).substDim j s = dimExprEq1 r :=
|
||
substDim_of_absent j s _ (dimExprEq1_dimAbsent j r h)
|
||
|
||
-- ── dimAbsent after substDim (i becomes absent after substituting i → r) ──
|
||
|
||
/-- After `substDim i r ϕ`, `i` is absent from the result — provided `r`
|
||
itself doesn't mention `i`. Needed downstream in `CTerm.dimAbsent_after_substDim`. -/
|
||
theorem dimAbsent_after_substDim (i : DimVar) (r : DimExpr)
|
||
(hr : r.dimAbsent i = true) (ϕ : FaceFormula) :
|
||
(ϕ.substDim i r).dimAbsent i = true := by
|
||
induction ϕ with
|
||
| bot => rfl
|
||
| top => rfl
|
||
| eq0 j =>
|
||
simp only [substDim]
|
||
by_cases hji : j = i
|
||
· rw [hji, if_pos rfl]; exact dimExprEq0_dimAbsent i r hr
|
||
· simp only [if_neg hji, dimAbsent, bne_iff_ne]; exact hji
|
||
| eq1 j =>
|
||
simp only [substDim]
|
||
by_cases hji : j = i
|
||
· rw [hji, if_pos rfl]; exact dimExprEq1_dimAbsent i r hr
|
||
· simp only [if_neg hji, dimAbsent, bne_iff_ne]; exact hji
|
||
| meet ϕ ψ ihϕ ihψ =>
|
||
simp only [substDim, dimAbsent, Bool.and_eq_true]
|
||
exact ⟨ihϕ, ihψ⟩
|
||
| join ϕ ψ ihϕ ihψ =>
|
||
simp only [substDim, dimAbsent, Bool.and_eq_true]
|
||
exact ⟨ihϕ, ihψ⟩
|
||
|
||
-- ── substDim commutes with itself on disjoint dim variables ─────────────────
|
||
|
||
/-- Substituting disjoint dims `i` and `j` (with the RHS DimExprs not
|
||
mentioning the opposite side's binder) commutes. Required for the
|
||
`CTerm.substDim_comm_aux` mutual induction through the new `transp`/
|
||
`comp` arms that now substitute in the face formula. -/
|
||
theorem substDim_comm (i j : DimVar) (r s : DimExpr) (hij : i ≠ j)
|
||
(hrj : r.dimAbsent j = true) (hsi : s.dimAbsent i = true)
|
||
(ϕ : FaceFormula) :
|
||
(ϕ.substDim i r).substDim j s = (ϕ.substDim j s).substDim i r := by
|
||
induction ϕ with
|
||
| bot => rfl
|
||
| top => rfl
|
||
| eq0 k =>
|
||
by_cases hki : k = i
|
||
· subst hki
|
||
simp only [substDim, ite_true, if_neg hij]
|
||
exact dimExprEq0_substDim_of_absent j s r hrj
|
||
· by_cases hkj : k = j
|
||
· subst hkj
|
||
simp only [substDim, if_neg hki, ite_true]
|
||
exact (dimExprEq0_substDim_of_absent i r s hsi).symm
|
||
· simp [substDim, if_neg hki, if_neg hkj]
|
||
| eq1 k =>
|
||
by_cases hki : k = i
|
||
· subst hki
|
||
simp only [substDim, ite_true, if_neg hij]
|
||
exact dimExprEq1_substDim_of_absent j s r hrj
|
||
· by_cases hkj : k = j
|
||
· subst hkj
|
||
simp only [substDim, if_neg hki, ite_true]
|
||
exact (dimExprEq1_substDim_of_absent i r s hsi).symm
|
||
· simp [substDim, if_neg hki, if_neg hkj]
|
||
| meet ϕ ψ ihϕ ihψ => simp [substDim, ihϕ, ihψ]
|
||
| join ϕ ψ ihϕ ihψ => simp [substDim, ihϕ, ihψ]
|
||
|
||
-- ── Normalisation (Stage 4.3, 2026-04-23) ────────────────────────────────────
|
||
--
|
||
-- After `substDim`, face formulas can contain terms like `.meet .top φ`
|
||
-- or `.join .bot φ` (e.g. `.substDim i .zero` on `.eq0 i` produces
|
||
-- `.top` as a sub-term which may need propagation). Rust's cubical
|
||
-- evaluator needs canonicalised faces to make dispatch deterministic:
|
||
-- the existing Glue-transport axioms hinge on `φ.substDim i .one = .bot`
|
||
-- or `= .top` literal equalities, which only hold after normalisation.
|
||
--
|
||
-- Reductions implemented (mirrors the semantic laws already in this
|
||
-- file):
|
||
-- · `.meet .top φ → φ`, `.meet φ .top → φ`
|
||
-- · `.meet .bot _ → .bot`, `.meet _ .bot → .bot`
|
||
-- · `.join .bot φ → φ`, `.join φ .bot → φ`
|
||
-- · `.join .top _ → .top`, `.join _ .top → .top`
|
||
--
|
||
-- Mutually-exclusive literal reductions (`.meet (.eq0 i) (.eq1 i) → .bot`,
|
||
-- etc.) are deliberately skipped — they require decidable equality on
|
||
-- `DimVar` in the reduction step and don't affect any current axiom.
|
||
|
||
@[extern "topolei_cubical_face_normalize"]
|
||
private opaque normalizeRust : FaceFormula → FaceFormula
|
||
|
||
/-- Canonical form under literal-identity / absorption reductions. -/
|
||
@[implemented_by normalizeRust]
|
||
def normalize : FaceFormula → FaceFormula
|
||
| .bot => .bot
|
||
| .top => .top
|
||
| .eq0 i => .eq0 i
|
||
| .eq1 i => .eq1 i
|
||
| .meet ϕ ψ =>
|
||
match normalize ϕ, normalize ψ with
|
||
| .bot, _ => .bot
|
||
| _, .bot => .bot
|
||
| .top, nψ => nψ
|
||
| nϕ, .top => nϕ
|
||
| nϕ, nψ => .meet nϕ nψ
|
||
| .join ϕ ψ =>
|
||
match normalize ϕ, normalize ψ with
|
||
| .top, _ => .top
|
||
| _, .top => .top
|
||
| .bot, nψ => nψ
|
||
| nϕ, .bot => nϕ
|
||
| nϕ, nψ => .join nϕ nψ
|
||
|
||
-- ── Preservation of evaluation ──────────────────────────────────────────────
|
||
|
||
private theorem normalize_meet_aux (ϕ ψ : FaceFormula) (env : DimVar → Bool) :
|
||
((normalize (FaceFormula.meet ϕ ψ)).eval env) =
|
||
(((normalize ϕ).eval env) && ((normalize ψ).eval env)) := by
|
||
show (match normalize ϕ, normalize ψ with
|
||
| FaceFormula.bot, _ => FaceFormula.bot
|
||
| _, FaceFormula.bot => FaceFormula.bot
|
||
| FaceFormula.top, nψ => nψ
|
||
| nϕ, FaceFormula.top => nϕ
|
||
| nϕ, nψ => FaceFormula.meet nϕ nψ).eval env =
|
||
(((normalize ϕ).eval env) && ((normalize ψ).eval env))
|
||
cases hϕ : normalize ϕ <;> cases hψ : normalize ψ <;> simp [eval]
|
||
|
||
private theorem normalize_join_aux (ϕ ψ : FaceFormula) (env : DimVar → Bool) :
|
||
((normalize (FaceFormula.join ϕ ψ)).eval env) =
|
||
(((normalize ϕ).eval env) || ((normalize ψ).eval env)) := by
|
||
show (match normalize ϕ, normalize ψ with
|
||
| FaceFormula.top, _ => FaceFormula.top
|
||
| _, FaceFormula.top => FaceFormula.top
|
||
| FaceFormula.bot, nψ => nψ
|
||
| nϕ, FaceFormula.bot => nϕ
|
||
| nϕ, nψ => FaceFormula.join nϕ nψ).eval env =
|
||
(((normalize ϕ).eval env) || ((normalize ψ).eval env))
|
||
cases hϕ : normalize ϕ <;> cases hψ : normalize ψ <;> simp [eval]
|
||
|
||
/-- Normalisation is semantically transparent. -/
|
||
theorem normalize_preserves_eval (ϕ : FaceFormula) (env : DimVar → Bool) :
|
||
(normalize ϕ).eval env = ϕ.eval env := by
|
||
induction ϕ with
|
||
| bot => rfl
|
||
| top => rfl
|
||
| eq0 i => rfl
|
||
| eq1 i => rfl
|
||
| meet ϕ ψ ihϕ ihψ =>
|
||
have h := normalize_meet_aux ϕ ψ env
|
||
rw [h, ihϕ, ihψ]
|
||
rfl
|
||
| join ϕ ψ ihϕ ihψ =>
|
||
have h := normalize_join_aux ϕ ψ env
|
||
rw [h, ihϕ, ihψ]
|
||
rfl
|
||
|
||
end FaceFormula
|