QUESTIONS Levels 1.5 + 2: full DecidableEq + simp routing
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>
This commit is contained in:
Maximus Gorog 2026-05-01 00:34:14 -06:00
parent 6adbce0c1b
commit 271b47102e
7 changed files with 524 additions and 10 deletions

View file

@ -1,6 +1,7 @@
import CubicalTransport.Interval
import CubicalTransport.Face
import CubicalTransport.Syntax
import CubicalTransport.DecEq
import CubicalTransport.Subst
import CubicalTransport.DimLine
import CubicalTransport.Typing

399
CubicalTransport/DecEq.lean Normal file
View file

@ -0,0 +1,399 @@
/-
CubicalTransport.DecEq
======================
Decidable equality for the 5-way mutual block (`CType`, `CTerm`,
`CTypeArg`, `CtorSpec`, `CTypeSchema`) plus the list/pair helper
shapes that appear inside it.
Lean 4's `deriving instance DecidableEq` does not currently support
mutual inductives — has to be written manually. The mutual `decEq`
block returns `Decidable (a = b)` directly; instances are then
declared post-hoc.
Imports `CubicalTransport.Syntax` and depends on the `DecidableEq`
instances for `DimVar`, `DimExpr`, and `FaceFormula` (added in
`Interval.lean` / `Face.lean`).
Used by `CubicalTransport.Question` for syntactic-classifier
decidability (`IsFullFace`, `IsEmptyFace`, `IsIntervalLine`,
`IsUnivLine`, `IsTransport`, `IsPathLine`, …). Cells-spec /
paideia downstream consumers also benefit (they want to compare
AST nodes when normalising / dispatching).
-/
import CubicalTransport.Syntax
namespace CubicalTransport.DecEq
-- ── Mutual decEq block ──────────────────────────────────────────────────────
-- Every Decidable (a = b) on the 5 AST types and their list/pair
-- helpers lives here. The block is structurally recursive on the AST
-- subterms and uses Lean's automatic recognition of OR-patterns to
-- collapse cross-constructor mismatches into a single `isFalse` arm.
mutual
def CType.decEq : (a b : CType) → Decidable (a = b)
| .univ, .univ => isTrue rfl
| .pi A B, .pi A' B' =>
match CType.decEq A A', CType.decEq B B' with
| isTrue hA, isTrue hB => isTrue (by rw [hA, hB])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .path A a b, .path A' a' b' =>
match CType.decEq A A', CTerm.decEq a a', CTerm.decEq b b' with
| isTrue hA, isTrue ha, isTrue hb => isTrue (by rw [hA, ha, hb])
| isFalse h, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .sigma A B, .sigma A' B' =>
match CType.decEq A A', CType.decEq B B' with
| isTrue hA, isTrue hB => isTrue (by rw [hA, hB])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .glue ψ T f fInv s r c A, .glue ψ' T' f' fInv' s' r' c' A' =>
if hψ : ψ = ψ' then
match CType.decEq T T', CTerm.decEq f f', CTerm.decEq fInv fInv',
CTerm.decEq s s', CTerm.decEq r r', CTerm.decEq c c',
CType.decEq A A' with
| isTrue hT, isTrue hf, isTrue hfI, isTrue hs, isTrue hr, isTrue hc, isTrue hA =>
isTrue (by rw [hψ, hT, hf, hfI, hs, hr, hc, hA])
| isFalse h, _, _, _, _, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h, _, _, _, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, isFalse h, _, _, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, _, isFalse h, _, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, _, _, isFalse h, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, _, _, _, isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, _, _, _, _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else
isFalse (fun heq => hψ (by cases heq; rfl))
| .ind S ps, .ind S' ps' =>
match CTypeSchema.decEq S S', decEqListCType ps ps' with
| isTrue hS, isTrue hp => isTrue (by rw [hS, hp])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .interval, .interval => isTrue rfl
-- Cross-constructor mismatches. Each row enumerates the (a, b)
-- shapes where the head constructors differ; `cases heq` discharges
-- because the eliminator treats them as inhabited-but-equal-only-
-- between-same-constructor.
| .univ, .pi _ _ | .univ, .path _ _ _ | .univ, .sigma _ _
| .univ, .glue _ _ _ _ _ _ _ _ | .univ, .ind _ _ | .univ, .interval
| .pi _ _, .univ | .pi _ _, .path _ _ _ | .pi _ _, .sigma _ _
| .pi _ _, .glue _ _ _ _ _ _ _ _ | .pi _ _, .ind _ _ | .pi _ _, .interval
| .path _ _ _, .univ | .path _ _ _, .pi _ _ | .path _ _ _, .sigma _ _
| .path _ _ _, .glue _ _ _ _ _ _ _ _ | .path _ _ _, .ind _ _ | .path _ _ _, .interval
| .sigma _ _, .univ | .sigma _ _, .pi _ _ | .sigma _ _, .path _ _ _
| .sigma _ _, .glue _ _ _ _ _ _ _ _ | .sigma _ _, .ind _ _ | .sigma _ _, .interval
| .glue _ _ _ _ _ _ _ _, .univ | .glue _ _ _ _ _ _ _ _, .pi _ _
| .glue _ _ _ _ _ _ _ _, .path _ _ _ | .glue _ _ _ _ _ _ _ _, .sigma _ _
| .glue _ _ _ _ _ _ _ _, .ind _ _ | .glue _ _ _ _ _ _ _ _, .interval
| .ind _ _, .univ | .ind _ _, .pi _ _ | .ind _ _, .path _ _ _
| .ind _ _, .sigma _ _ | .ind _ _, .glue _ _ _ _ _ _ _ _ | .ind _ _, .interval
| .interval, .univ | .interval, .pi _ _ | .interval, .path _ _ _
| .interval, .sigma _ _ | .interval, .glue _ _ _ _ _ _ _ _ | .interval, .ind _ _ =>
isFalse (fun heq => by cases heq)
def CTerm.decEq : (a b : CTerm) → Decidable (a = b)
| .var x, .var y =>
if h : x = y then isTrue (by rw [h]) else isFalse (fun heq => h (by cases heq; rfl))
| .lam x t, .lam y u =>
if hx : x = y then
match CTerm.decEq t u with
| isTrue ht => isTrue (by rw [hx, ht])
| isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else
isFalse (fun heq => hx (by cases heq; rfl))
| .app f a, .app f' a' =>
match CTerm.decEq f f', CTerm.decEq a a' with
| isTrue hf, isTrue ha => isTrue (by rw [hf, ha])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .plam i t, .plam j u =>
if hi : i = j then
match CTerm.decEq t u with
| isTrue ht => isTrue (by rw [hi, ht])
| isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else
isFalse (fun heq => hi (by cases heq; rfl))
| .papp t r, .papp u s =>
if hr : r = s then
match CTerm.decEq t u with
| isTrue ht => isTrue (by rw [hr, ht])
| isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else
isFalse (fun heq => hr (by cases heq; rfl))
| .transp i A φ t, .transp j B ψ u =>
if hi : i = j then if hφ : φ = ψ then
match CType.decEq A B, CTerm.decEq t u with
| isTrue hA, isTrue ht => isTrue (by rw [hi, hA, hφ, ht])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hφ (by cases heq; rfl))
else isFalse (fun heq => hi (by cases heq; rfl))
| .comp i A φ u t, .comp j B ψ u' t' =>
if hi : i = j then if hφ : φ = ψ then
match CType.decEq A B, CTerm.decEq u u', CTerm.decEq t t' with
| isTrue hA, isTrue hu, isTrue ht => isTrue (by rw [hi, hA, hφ, hu, ht])
| isFalse h, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hφ (by cases heq; rfl))
else isFalse (fun heq => hi (by cases heq; rfl))
| .compN i A cs t, .compN j B cs' t' =>
if hi : i = j then
match CType.decEq A B, decEqClauses cs cs', CTerm.decEq t t' with
| isTrue hA, isTrue hc, isTrue ht => isTrue (by rw [hi, hA, hc, ht])
| isFalse h, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hi (by cases heq; rfl))
| .glueIn φ t a, .glueIn ψ u b =>
if hφ : φ = ψ then
match CTerm.decEq t u, CTerm.decEq a b with
| isTrue ht, isTrue ha => isTrue (by rw [hφ, ht, ha])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hφ (by cases heq; rfl))
| .unglue φ f g, .unglue ψ f' g' =>
if hφ : φ = ψ then
match CTerm.decEq f f', CTerm.decEq g g' with
| isTrue hf, isTrue hg => isTrue (by rw [hφ, hf, hg])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hφ (by cases heq; rfl))
| .pair a b, .pair a' b' =>
match CTerm.decEq a a', CTerm.decEq b b' with
| isTrue ha, isTrue hb => isTrue (by rw [ha, hb])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .fst t, .fst u =>
match CTerm.decEq t u with
| isTrue ht => isTrue (by rw [ht])
| isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .snd t, .snd u =>
match CTerm.decEq t u with
| isTrue ht => isTrue (by rw [ht])
| isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .dimExpr r, .dimExpr s =>
if h : r = s then isTrue (by rw [h]) else isFalse (fun heq => h (by cases heq; rfl))
| .ctor S c ps as, .ctor S' c' ps' as' =>
if hc : c = c' then
match CTypeSchema.decEq S S', decEqListCType ps ps', decEqListCTerm as as' with
| isTrue hS, isTrue hp, isTrue ha => isTrue (by rw [hS, hc, hp, ha])
| isFalse h, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hc (by cases heq; rfl))
| .indElim S ps m bs t, .indElim S' ps' m' bs' t' =>
match CTypeSchema.decEq S S', decEqListCType ps ps',
CTerm.decEq m m', decEqBranches bs bs', CTerm.decEq t t' with
| isTrue hS, isTrue hp, isTrue hm, isTrue hb, isTrue ht =>
isTrue (by rw [hS, hp, hm, hb, ht])
| isFalse h, _, _, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h, _, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, isFalse h, _, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, _, isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, _, _, _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
-- Cross-constructor mismatches. Lean discharges via `cases heq`.
| .var _, .lam _ _ | .var _, .app _ _ | .var _, .plam _ _ | .var _, .papp _ _
| .var _, .transp _ _ _ _ | .var _, .comp _ _ _ _ _ | .var _, .compN _ _ _ _
| .var _, .glueIn _ _ _ | .var _, .unglue _ _ _ | .var _, .pair _ _
| .var _, .fst _ | .var _, .snd _ | .var _, .dimExpr _ | .var _, .ctor _ _ _ _
| .var _, .indElim _ _ _ _ _
| .lam _ _, .var _ | .lam _ _, .app _ _ | .lam _ _, .plam _ _ | .lam _ _, .papp _ _
| .lam _ _, .transp _ _ _ _ | .lam _ _, .comp _ _ _ _ _ | .lam _ _, .compN _ _ _ _
| .lam _ _, .glueIn _ _ _ | .lam _ _, .unglue _ _ _ | .lam _ _, .pair _ _
| .lam _ _, .fst _ | .lam _ _, .snd _ | .lam _ _, .dimExpr _ | .lam _ _, .ctor _ _ _ _
| .lam _ _, .indElim _ _ _ _ _
| .app _ _, .var _ | .app _ _, .lam _ _ | .app _ _, .plam _ _ | .app _ _, .papp _ _
| .app _ _, .transp _ _ _ _ | .app _ _, .comp _ _ _ _ _ | .app _ _, .compN _ _ _ _
| .app _ _, .glueIn _ _ _ | .app _ _, .unglue _ _ _ | .app _ _, .pair _ _
| .app _ _, .fst _ | .app _ _, .snd _ | .app _ _, .dimExpr _ | .app _ _, .ctor _ _ _ _
| .app _ _, .indElim _ _ _ _ _
| .plam _ _, .var _ | .plam _ _, .lam _ _ | .plam _ _, .app _ _ | .plam _ _, .papp _ _
| .plam _ _, .transp _ _ _ _ | .plam _ _, .comp _ _ _ _ _ | .plam _ _, .compN _ _ _ _
| .plam _ _, .glueIn _ _ _ | .plam _ _, .unglue _ _ _ | .plam _ _, .pair _ _
| .plam _ _, .fst _ | .plam _ _, .snd _ | .plam _ _, .dimExpr _ | .plam _ _, .ctor _ _ _ _
| .plam _ _, .indElim _ _ _ _ _
| .papp _ _, .var _ | .papp _ _, .lam _ _ | .papp _ _, .app _ _ | .papp _ _, .plam _ _
| .papp _ _, .transp _ _ _ _ | .papp _ _, .comp _ _ _ _ _ | .papp _ _, .compN _ _ _ _
| .papp _ _, .glueIn _ _ _ | .papp _ _, .unglue _ _ _ | .papp _ _, .pair _ _
| .papp _ _, .fst _ | .papp _ _, .snd _ | .papp _ _, .dimExpr _ | .papp _ _, .ctor _ _ _ _
| .papp _ _, .indElim _ _ _ _ _
| .transp _ _ _ _, .var _ | .transp _ _ _ _, .lam _ _ | .transp _ _ _ _, .app _ _
| .transp _ _ _ _, .plam _ _ | .transp _ _ _ _, .papp _ _ | .transp _ _ _ _, .comp _ _ _ _ _
| .transp _ _ _ _, .compN _ _ _ _ | .transp _ _ _ _, .glueIn _ _ _
| .transp _ _ _ _, .unglue _ _ _ | .transp _ _ _ _, .pair _ _ | .transp _ _ _ _, .fst _
| .transp _ _ _ _, .snd _ | .transp _ _ _ _, .dimExpr _ | .transp _ _ _ _, .ctor _ _ _ _
| .transp _ _ _ _, .indElim _ _ _ _ _
| .comp _ _ _ _ _, .var _ | .comp _ _ _ _ _, .lam _ _ | .comp _ _ _ _ _, .app _ _
| .comp _ _ _ _ _, .plam _ _ | .comp _ _ _ _ _, .papp _ _ | .comp _ _ _ _ _, .transp _ _ _ _
| .comp _ _ _ _ _, .compN _ _ _ _ | .comp _ _ _ _ _, .glueIn _ _ _
| .comp _ _ _ _ _, .unglue _ _ _ | .comp _ _ _ _ _, .pair _ _
| .comp _ _ _ _ _, .fst _ | .comp _ _ _ _ _, .snd _ | .comp _ _ _ _ _, .dimExpr _
| .comp _ _ _ _ _, .ctor _ _ _ _ | .comp _ _ _ _ _, .indElim _ _ _ _ _
| .compN _ _ _ _, .var _ | .compN _ _ _ _, .lam _ _ | .compN _ _ _ _, .app _ _
| .compN _ _ _ _, .plam _ _ | .compN _ _ _ _, .papp _ _ | .compN _ _ _ _, .transp _ _ _ _
| .compN _ _ _ _, .comp _ _ _ _ _ | .compN _ _ _ _, .glueIn _ _ _
| .compN _ _ _ _, .unglue _ _ _ | .compN _ _ _ _, .pair _ _ | .compN _ _ _ _, .fst _
| .compN _ _ _ _, .snd _ | .compN _ _ _ _, .dimExpr _ | .compN _ _ _ _, .ctor _ _ _ _
| .compN _ _ _ _, .indElim _ _ _ _ _
| .glueIn _ _ _, .var _ | .glueIn _ _ _, .lam _ _ | .glueIn _ _ _, .app _ _
| .glueIn _ _ _, .plam _ _ | .glueIn _ _ _, .papp _ _ | .glueIn _ _ _, .transp _ _ _ _
| .glueIn _ _ _, .comp _ _ _ _ _ | .glueIn _ _ _, .compN _ _ _ _
| .glueIn _ _ _, .unglue _ _ _ | .glueIn _ _ _, .pair _ _ | .glueIn _ _ _, .fst _
| .glueIn _ _ _, .snd _ | .glueIn _ _ _, .dimExpr _ | .glueIn _ _ _, .ctor _ _ _ _
| .glueIn _ _ _, .indElim _ _ _ _ _
| .unglue _ _ _, .var _ | .unglue _ _ _, .lam _ _ | .unglue _ _ _, .app _ _
| .unglue _ _ _, .plam _ _ | .unglue _ _ _, .papp _ _ | .unglue _ _ _, .transp _ _ _ _
| .unglue _ _ _, .comp _ _ _ _ _ | .unglue _ _ _, .compN _ _ _ _
| .unglue _ _ _, .glueIn _ _ _ | .unglue _ _ _, .pair _ _ | .unglue _ _ _, .fst _
| .unglue _ _ _, .snd _ | .unglue _ _ _, .dimExpr _ | .unglue _ _ _, .ctor _ _ _ _
| .unglue _ _ _, .indElim _ _ _ _ _
| .pair _ _, .var _ | .pair _ _, .lam _ _ | .pair _ _, .app _ _ | .pair _ _, .plam _ _
| .pair _ _, .papp _ _ | .pair _ _, .transp _ _ _ _ | .pair _ _, .comp _ _ _ _ _
| .pair _ _, .compN _ _ _ _ | .pair _ _, .glueIn _ _ _ | .pair _ _, .unglue _ _ _
| .pair _ _, .fst _ | .pair _ _, .snd _ | .pair _ _, .dimExpr _ | .pair _ _, .ctor _ _ _ _
| .pair _ _, .indElim _ _ _ _ _
| .fst _, .var _ | .fst _, .lam _ _ | .fst _, .app _ _ | .fst _, .plam _ _
| .fst _, .papp _ _ | .fst _, .transp _ _ _ _ | .fst _, .comp _ _ _ _ _
| .fst _, .compN _ _ _ _ | .fst _, .glueIn _ _ _ | .fst _, .unglue _ _ _
| .fst _, .pair _ _ | .fst _, .snd _ | .fst _, .dimExpr _ | .fst _, .ctor _ _ _ _
| .fst _, .indElim _ _ _ _ _
| .snd _, .var _ | .snd _, .lam _ _ | .snd _, .app _ _ | .snd _, .plam _ _
| .snd _, .papp _ _ | .snd _, .transp _ _ _ _ | .snd _, .comp _ _ _ _ _
| .snd _, .compN _ _ _ _ | .snd _, .glueIn _ _ _ | .snd _, .unglue _ _ _
| .snd _, .pair _ _ | .snd _, .fst _ | .snd _, .dimExpr _ | .snd _, .ctor _ _ _ _
| .snd _, .indElim _ _ _ _ _
| .dimExpr _, .var _ | .dimExpr _, .lam _ _ | .dimExpr _, .app _ _
| .dimExpr _, .plam _ _ | .dimExpr _, .papp _ _ | .dimExpr _, .transp _ _ _ _
| .dimExpr _, .comp _ _ _ _ _ | .dimExpr _, .compN _ _ _ _
| .dimExpr _, .glueIn _ _ _ | .dimExpr _, .unglue _ _ _ | .dimExpr _, .pair _ _
| .dimExpr _, .fst _ | .dimExpr _, .snd _ | .dimExpr _, .ctor _ _ _ _
| .dimExpr _, .indElim _ _ _ _ _
| .ctor _ _ _ _, .var _ | .ctor _ _ _ _, .lam _ _ | .ctor _ _ _ _, .app _ _
| .ctor _ _ _ _, .plam _ _ | .ctor _ _ _ _, .papp _ _ | .ctor _ _ _ _, .transp _ _ _ _
| .ctor _ _ _ _, .comp _ _ _ _ _ | .ctor _ _ _ _, .compN _ _ _ _
| .ctor _ _ _ _, .glueIn _ _ _ | .ctor _ _ _ _, .unglue _ _ _ | .ctor _ _ _ _, .pair _ _
| .ctor _ _ _ _, .fst _ | .ctor _ _ _ _, .snd _ | .ctor _ _ _ _, .dimExpr _
| .ctor _ _ _ _, .indElim _ _ _ _ _
| .indElim _ _ _ _ _, .var _ | .indElim _ _ _ _ _, .lam _ _ | .indElim _ _ _ _ _, .app _ _
| .indElim _ _ _ _ _, .plam _ _ | .indElim _ _ _ _ _, .papp _ _
| .indElim _ _ _ _ _, .transp _ _ _ _ | .indElim _ _ _ _ _, .comp _ _ _ _ _
| .indElim _ _ _ _ _, .compN _ _ _ _ | .indElim _ _ _ _ _, .glueIn _ _ _
| .indElim _ _ _ _ _, .unglue _ _ _ | .indElim _ _ _ _ _, .pair _ _
| .indElim _ _ _ _ _, .fst _ | .indElim _ _ _ _ _, .snd _ | .indElim _ _ _ _ _, .dimExpr _
| .indElim _ _ _ _ _, .ctor _ _ _ _ =>
isFalse (fun heq => by cases heq)
def CTypeArg.decEq : (a b : CTypeArg) → Decidable (a = b)
| .type A, .type B =>
match CType.decEq A B with
| isTrue h => isTrue (by rw [h])
| isFalse h => isFalse (fun heq => h (by cases heq; rfl))
| .param i, .param j =>
if h : i = j then isTrue (by rw [h]) else isFalse (fun heq => h (by cases heq; rfl))
| .self, .self => isTrue rfl
| .dim, .dim => isTrue rfl
| .type _, .param _ | .type _, .self | .type _, .dim
| .param _, .type _ | .param _, .self | .param _, .dim
| .self, .type _ | .self, .param _ | .self, .dim
| .dim, .type _ | .dim, .param _ | .dim, .self =>
isFalse (fun heq => by cases heq)
def CtorSpec.decEq : (a b : CtorSpec) → Decidable (a = b)
| .mk n as bs, .mk n' as' bs' =>
if hn : n = n' then
match decEqListCTypeArg as as', decEqClauses bs bs' with
| isTrue ha, isTrue hb => isTrue (by rw [hn, ha, hb])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hn (by cases heq; rfl))
def CTypeSchema.decEq : (a b : CTypeSchema) → Decidable (a = b)
| .mk n np cs, .mk n' np' cs' =>
if hn : n = n' then if hnp : np = np' then
match decEqListCtorSpec cs cs' with
| isTrue hc => isTrue (by rw [hn, hnp, hc])
| isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hnp (by cases heq; rfl))
else isFalse (fun heq => hn (by cases heq; rfl))
-- ── List / clause / branch helpers ──────────────────────────────────────────
def decEqListCType : (xs ys : List CType) → Decidable (xs = ys)
| [], [] => isTrue rfl
| [], _ :: _ => isFalse (fun heq => by cases heq)
| _ :: _, [] => isFalse (fun heq => by cases heq)
| x :: xs, y :: ys =>
match CType.decEq x y, decEqListCType xs ys with
| isTrue hx, isTrue hxs => isTrue (by rw [hx, hxs])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
def decEqListCTerm : (xs ys : List CTerm) → Decidable (xs = ys)
| [], [] => isTrue rfl
| [], _ :: _ => isFalse (fun heq => by cases heq)
| _ :: _, [] => isFalse (fun heq => by cases heq)
| x :: xs, y :: ys =>
match CTerm.decEq x y, decEqListCTerm xs ys with
| isTrue hx, isTrue hxs => isTrue (by rw [hx, hxs])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
def decEqListCTypeArg : (xs ys : List CTypeArg) → Decidable (xs = ys)
| [], [] => isTrue rfl
| [], _ :: _ => isFalse (fun heq => by cases heq)
| _ :: _, [] => isFalse (fun heq => by cases heq)
| x :: xs, y :: ys =>
match CTypeArg.decEq x y, decEqListCTypeArg xs ys with
| isTrue hx, isTrue hxs => isTrue (by rw [hx, hxs])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
def decEqListCtorSpec : (xs ys : List CtorSpec) → Decidable (xs = ys)
| [], [] => isTrue rfl
| [], _ :: _ => isFalse (fun heq => by cases heq)
| _ :: _, [] => isFalse (fun heq => by cases heq)
| x :: xs, y :: ys =>
match CtorSpec.decEq x y, decEqListCtorSpec xs ys with
| isTrue hx, isTrue hxs => isTrue (by rw [hx, hxs])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
def decEqClauses : (xs ys : List (FaceFormula × CTerm)) → Decidable (xs = ys)
| [], [] => isTrue rfl
| [], _ :: _ => isFalse (fun heq => by cases heq)
| _ :: _, [] => isFalse (fun heq => by cases heq)
| (φ, t) :: xs, (ψ, u) :: ys =>
if hφ : φ = ψ then
match CTerm.decEq t u, decEqClauses xs ys with
| isTrue ht, isTrue hxs => isTrue (by rw [hφ, ht, hxs])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hφ (by cases heq; rfl))
def decEqBranches : (xs ys : List (String × CTerm)) → Decidable (xs = ys)
| [], [] => isTrue rfl
| [], _ :: _ => isFalse (fun heq => by cases heq)
| _ :: _, [] => isFalse (fun heq => by cases heq)
| (n, t) :: xs, (n', u) :: ys =>
if hn : n = n' then
match CTerm.decEq t u, decEqBranches xs ys with
| isTrue ht, isTrue hxs => isTrue (by rw [hn, ht, hxs])
| isFalse h, _ => isFalse (fun heq => h (by cases heq; rfl))
| _, isFalse h => isFalse (fun heq => h (by cases heq; rfl))
else isFalse (fun heq => hn (by cases heq; rfl))
end
-- ── Instance declarations ───────────────────────────────────────────────────
instance : DecidableEq CType := CType.decEq
instance : DecidableEq CTerm := CTerm.decEq
instance : DecidableEq CTypeArg := CTypeArg.decEq
instance : DecidableEq CtorSpec := CtorSpec.decEq
instance : DecidableEq CTypeSchema := CTypeSchema.decEq
end CubicalTransport.DecEq

View file

@ -240,7 +240,37 @@ def tests : List (String × String × String) :=
{ env := .nil, binder := ⟨"i"⟩, body := .interval
, φ := .top, u := .var "u", t := .var "t" }
then "yes" else "no"),
"yes") ]
"yes"),
("Classifier IsFullFace decidable on .top face",
(if Question.IsFullFace
{ env := .nil, binder := ⟨"i"⟩, body := .univ
, φ := .top, u := .var "u", t := .var "t" }
then "yes" else "no"),
"yes"),
("Classifier IsTransport decidable when u = t",
(if Question.IsTransport
{ env := .nil, binder := ⟨"i"⟩, body := .univ
, φ := .top, u := .var "x", t := .var "x" }
then "yes" else "no"),
"yes"),
("Classifier IsTransport rejects when u ≠ t",
(if Question.IsTransport
{ env := .nil, binder := ⟨"i"⟩, body := .univ
, φ := .top, u := .var "x", t := .var "y" }
then "yes" else "no"),
"no"),
("Classifier IsPiLine decidable on .pi body",
(if Question.IsPiLine
{ env := .nil, binder := ⟨"i"⟩, body := .pi .univ .univ
, φ := .top, u := .var "u", t := .var "t" }
then "yes" else "no"),
"yes"),
("Classifier IsIntervalLine rejects on .univ",
(if Question.IsIntervalLine
{ env := .nil, binder := ⟨"i"⟩, body := .univ
, φ := .top, u := .var "u", t := .var "t" }
then "yes" else "no"),
"no") ]
/-- Run every smoke test, print its actual vs expected. Returns the
number of failures. -/

View file

@ -22,7 +22,7 @@ inductive FaceFormula where
| eq1 (i : DimVar) : FaceFormula -- (i = 1)
| meet (ϕ ψ : FaceFormula) : FaceFormula -- ϕ ∧ ψ
| join (ϕ ψ : FaceFormula) : FaceFormula -- ϕ ψ
deriving Repr, Inhabited
deriving Repr, Inhabited, DecidableEq
-- ── Semantic evaluation ───────────────────────────────────────────────────────

View file

@ -26,7 +26,7 @@ inductive DimExpr where
| inv (r : DimExpr) : DimExpr -- 1 r
| meet (r s : DimExpr) : DimExpr -- r ∧ s
| join (r s : DimExpr) : DimExpr -- r s
deriving Repr, Inhabited
deriving Repr, Inhabited, DecidableEq
-- ── Semantic evaluation ───────────────────────────────────────────────────────

View file

@ -24,6 +24,7 @@
import CubicalTransport.TransportLaws
import CubicalTransport.CompLaws
import CubicalTransport.DecEq
namespace Question
@ -138,17 +139,63 @@ def IsIntervalLine (q : CompQ) : Prop :=
def IsUnivLine (q : CompQ) : Prop :=
q.body = .univ
-- ── Decidability for the syntactic classifiers ───────────────────────────────
-- Only `IsConstLine` is automatically `Decidable` at this layer (it
-- reduces to a Bool equality). The face- and body-shape classifiers
-- need `DecidableEq FaceFormula` / `DecidableEq CType` from
-- `Topolei.Cubical.DecEq` — registering instances cross-package is
-- left for Level 1.5 once cells-spec / paideia starts dispatching on
-- them.
-- ── Decidability for every classifier ───────────────────────────────────────
-- All classifiers are `Decidable`. Syntactic ones (face / body-tag /
-- u=t equality) reduce directly to `DecidableEq` on existing types
-- (`FaceFormula`, `CType`, `CTerm` — the latter via the mutual
-- `decEq` block in `DecEq.lean`). Existential body-shape classifiers
-- (`IsPathLine`, `IsGlueLine`, `IsPiLine`, `IsSigmaLine`, `IsIndLine`)
-- use `match h : q.body with` to inspect the head constructor and
-- reconstruct the existential witness.
instance (q : CompQ) : Decidable (IsConstLine q) :=
inferInstanceAs (Decidable (q.body.dimAbsent q.binder = true))
instance (q : CompQ) : Decidable (IsFullFace q) :=
inferInstanceAs (Decidable (q.φ = .top))
instance (q : CompQ) : Decidable (IsEmptyFace q) :=
inferInstanceAs (Decidable (q.φ = .bot))
instance (q : CompQ) : Decidable (IsTransport q) :=
inferInstanceAs (Decidable (q.u = q.t))
instance (q : CompQ) : Decidable (IsIntervalLine q) :=
inferInstanceAs (Decidable (q.body = .interval))
instance (q : CompQ) : Decidable (IsUnivLine q) :=
inferInstanceAs (Decidable (q.body = .univ))
instance (q : CompQ) : Decidable (IsPathLine q) :=
match h : q.body with
| .path A₀ a b => isTrue ⟨A₀, a, b, h⟩
| .univ | .pi _ _ | .sigma _ _ | .glue _ _ _ _ _ _ _ _ | .ind _ _ | .interval =>
isFalse (fun ⟨_, _, _, hp⟩ => by rw [hp] at h; cases h)
instance (q : CompQ) : Decidable (IsPiLine q) :=
match h : q.body with
| .pi domA codA => isTrue ⟨domA, codA, h⟩
| .univ | .path _ _ _ | .sigma _ _ | .glue _ _ _ _ _ _ _ _ | .ind _ _ | .interval =>
isFalse (fun ⟨_, _, hp⟩ => by rw [hp] at h; cases h)
instance (q : CompQ) : Decidable (IsSigmaLine q) :=
match h : q.body with
| .sigma A B => isTrue ⟨A, B, h⟩
| .univ | .pi _ _ | .path _ _ _ | .glue _ _ _ _ _ _ _ _ | .ind _ _ | .interval =>
isFalse (fun ⟨_, _, hp⟩ => by rw [hp] at h; cases h)
instance (q : CompQ) : Decidable (IsGlueLine q) :=
match h : q.body with
| .glue ψ T f fInv s r c A => isTrue ⟨ψ, T, f, fInv, s, r, c, A, h⟩
| .univ | .pi _ _ | .path _ _ _ | .sigma _ _ | .ind _ _ | .interval =>
isFalse (fun ⟨_, _, _, _, _, _, _, _, hp⟩ => by rw [hp] at h; cases h)
instance (q : CompQ) : Decidable (IsIndLine q) :=
match h : q.body with
| .ind S params => isTrue ⟨S, params, h⟩
| .univ | .pi _ _ | .path _ _ _ | .sigma _ _ | .glue _ _ _ _ _ _ _ _ | .interval =>
isFalse (fun ⟨_, _, hp⟩ => by rw [hp] at h; cases h)
-- ── Restated axioms — classifier-conditioned question equivalence ─────────────
-- Level 1: each existing `eval_comp_*` axiom is restated as a
-- theorem about `CompQ.ask`. These are derived (not new axioms);
@ -163,6 +210,7 @@ namespace CompQ
/-- C1 in question form: full-face question's answer is the
partial element substituted at `binder := 1`. -/
@[simp]
theorem ask_of_full_face (q : CompQ) (h : IsFullFace q) :
q.ask = eval q.env (q.u.substDim q.binder .one) := by
unfold ask
@ -171,6 +219,7 @@ theorem ask_of_full_face (q : CompQ) (h : IsFullFace q) :
/-- C2 in question form: empty-face question's answer is plain
transport on the base. -/
@[simp]
theorem ask_of_empty_face (q : CompQ) (h : IsEmptyFace q) :
q.ask = eval q.env (.transp q.binder q.body .bot q.t) := by
unfold ask
@ -181,6 +230,7 @@ theorem ask_of_empty_face (q : CompQ) (h : IsEmptyFace q) :
`binder`, heterogeneous comp reduces to `vHCompValue` (hcomp).
Requires the face is neither full nor empty (those have their
own theorems above). -/
@[simp]
theorem ask_of_const_line (q : CompQ)
(hC : IsConstLine q)
(hφ₁ : ¬ IsFullFace q) (hφ₂ : ¬ IsEmptyFace q) :
@ -239,6 +289,7 @@ theorem ask_of_stuck (q : CompQ)
base is constant in the line binder (the cubical typing premise),
the answer is `eval env t`. This is the bridge between
`CompQ.ofTransp` and the legacy `eval_transp_top` axiom. -/
@[simp]
theorem ask_of_transport_full_face (q : CompQ)
(hT : IsTransport q) (hφ : IsFullFace q)
(hi : q.t.dimAbsent q.binder = true) :
@ -248,4 +299,33 @@ theorem ask_of_transport_full_face (q : CompQ)
end CompQ
-- ── Level 2 — automated routing through `simp` ──────────────────────────────
-- The `@[simp]` attribute on the classifier-conditioned theorems above
-- lets Lean's `simp` rewrite `q.ask` automatically when a syntactic
-- classifier hypothesis is in scope. These examples exercise the
-- routing on representative shapes; they also serve as sanity checks
-- that the simp-set composes (none of the lemmas mask each other).
example (env : CEnv) (i : DimVar) (A : CType) (u t : CTerm) :
let q : CompQ := ⟨env, i, A, .top, u, t⟩
q.ask = eval env (u.substDim i .one) := by
simp [CompQ.ask_of_full_face, IsFullFace]
example (env : CEnv) (i : DimVar) (A : CType) (u t : CTerm) :
let q : CompQ := ⟨env, i, A, .bot, u, t⟩
q.ask = eval env (.transp i A .bot t) := by
simp [CompQ.ask_of_empty_face, IsEmptyFace]
/-- The interval-line question with `u = t` (transport-shaped) and
a base that doesn't depend on the binder reduces to `eval env t`.
Demonstrates Level 2 chaining: full-face ⊕ transport ⊕ dim-absent
base composed automatically by `simp`. -/
example (env : CEnv) (i : DimVar) (t : CTerm)
(hi : t.dimAbsent i = true) :
(CompQ.ofTransp env i .interval .top t).ask = eval env t := by
apply CompQ.ask_of_transport_full_face
· rfl
· rfl
· exact hi
end Question

View file

@ -229,6 +229,10 @@ deriving instance Repr for CTypeArg
deriving instance Repr for CtorSpec
deriving instance Repr for CTypeSchema
-- DecidableEq for the 5-way mutual block lives in `CubicalTransport.DecEq`
-- (Lean's `deriving instance DecidableEq` doesn't currently support mutual
-- inductives — has to be written manually).
-- ── Dimension substitution ────────────────────────────────────────────────────
-- Substitute dimension variable i with DimExpr r throughout a term.
--